Compare commits

...

17 Commits

Author SHA1 Message Date
Andy
444b3521eb 2024.5.18.0
YT
YouTubeSettings: add 'DefaultVideoHighlightFPS_H' and 'DefaultVideoHighlightFPS_L' properties
VideoOption: highlight frame rates higher/lower than this value

SCrawler
SiteSettingsBase: add 'UserAgentDefault' property
API.Facebook, API.Instagram, API.Mastodon, API.OnlyFans, API.ThreadsNet, API.Twitter: add categories
API.Instagram.SiteSettings: add 'DownDetector' validation; remove wrong header
API.Instagram.UserData: fix incorrect definition of pinned posts; add 'DefaultParser_Pinned' and 'DefaultParser_SkipPost' func (for Threads)
API.Threads: fix pinned posts processing
API.Reddit: add 429 bypass; change the naming method of video files (hosted on Reddit) to the 'YYYYMMDD_HHMMSS' pattern; add 'UserAgent' property
API.RedGifs: hide credential controls
API.Twitter: add 'Likes' downloading; change domain from twitter.com to x.com;
API.OnlyFans: set '_AllowUserAgentUpdate' to false
SiteEditorForm: group options by category
GroupListForm: enable 'OK' if it is filter
DownloadGroup: add 'FilterShowAllUsers' property
PropertyValueHost: add 'Category' property
MainFrame: the 'ALL' filter isn't unchecked when loading a filter from a saved one
Update user paths when global paths change
Scheduler: add the ability to clone the scheduler

PluginProvider
PropertyOption attribute: set category name when `IsAuth = True`
ISiteSettings: add 'UserAgentDefault' property
2024-05-18 01:17:29 +03:00
Andy
ec2266f1bf 2024.5.4.0
YT
remove dots from the end of the file name; add a setting to remove specific characters

SCrawler
API.Instagram: simplify the 'Connection closed' error
API.Reddit: update token refresh request; add 'BearerTokenUseCurl' hidden property
API.Threads: fix frong header name ('dnt'); update 'UpdateCredentials' function
AutoDownloader: change 'IndexOutOfRangeException' to 'Exception' in the 'Download' function
TDownloader: fix 'FilesUpdatePendingUsers' function (freeze)
UserSearchForm: add 'FriendlyName' to search results
2024-05-04 07:04:26 +03:00
Andy
7d9255c916 2024.4.26.0
Add 'CookieValueExtractorAttribute' and the ability to immediately populate fields with values that can be extracted from cookies
Feed: add the ability to load the last session of the current day (if it exists) as the current session after restarting SCrawler
UserSearchForm: include friendly name matches in search result
API.Xhamster: saved posts aren't downloading
2024-04-26 17:18:31 +03:00
Andy
5b5857e31d 2024.4.14.0
Delete old notes and comments
API.Facebook: add app-id extraction from page; remove app-id from site requirements; update tokens parsing; update tokens regex
API.Instagram: add default function to parse tokens
2024-04-14 07:44:05 +03:00
Andy
46372ec9fb 2024.4.13.0
YT
Add subtitles to information about downloaded files

PluginProvider
IPluginContentProvider: add 'ResetHistoryData' function

SCrawler
UserDataBase: call 'UpdateUsersList' when 'UpdateUserInformation' with argument 'OnlyDiff'; implement 'ResetHistoryData' function; set 'LastUpdated' to null and 'UpdateUserInformation' when erasing history data
API.Instagram: set 'FirstLoadingDone' to false when erasing history data; fix broken saved posts downloading
API.TikTok: set 'LastDownloadDate' to null when erasing history data
API.YouTube: set last download dates to null when erasing history data
GroupUsersViewer: add the number of users, object type and object name to the form title; add 'F1' to help hint; add '(Alt+)F3' to edit user
AutoDownloaderEditorForm, SchedulerEditorForm, GroupEditorForm, GroupListForm: update to 'GroupUsersViewer'
MainFrame: make 'EditSelectedUser' friend
UserDataHost: implement 'ResetHistoryData' function
SettingsCLS: add 'OnlyDiff' to the 'UpdateUsersList' function
UserInfo: add 'ExactEquals' shared function
2024-04-13 10:10:27 +03:00
Andy
7296fda977 2024.4.10.0 2024-04-10 06:25:39 +03:00
Andy
5f90bf6a99 2024.4.8.0
YT
MusicPlaylistsForm, VideoOptionsForm: add audio bitrate option
MediaItem: update type icon; update confirmation dialog for deleting non-single object
Track: update extension
PlayList: update 'ToString' information for 'MediaItem'
YouTubeMediaContainerBase: add size recalculation; add audio bitrate change; embed thumbnail in the extracted 'mp3' as cover art; update 'DownloadCommand' function; include elements' files in XML for non-single items; update 'Delete' function; update files handling; include generated playlists and cover file in the file list
YouTubeSettings: add properties 'DefaultAudioEmbedThumbnail_ExtractedFiles', 'DefaultAudioBitrate', 'DefaultAudioBitrate_crf'
Exclude 'drc' from parsing results
Fix incorrect file reference when the yt-dlp.exe has a different name

SCrawler
Base.Declarations: hide 'TokenRefreshIntervalProvider' error
Base.DeclaredNames: remove 'Header_FB_FRIENDLY_NAME' const (use 'API.Instagram.UserData.GQL')
Base.M3U8Base: add 'SkipBroken' argument
Base.UserDataBase: add size recalculation (STD)
Base.SiteSettingsBase: add 'SettingsVersion' property
TDownloader: delete 'RenameOldFileNames' function
SiteEditorForm: remove begin/end update of global settings when updating
MainFrame: update 'BTT_DOWN_SPEC' tooltip
SettingsHostCollection, SettingsHost: move site settings to a personal setting file (delete these settings from the global settings file)
DownloadGroupCollection: remove data update during initialization and reindexing
SettingsCLS: add 'SettingsVersion' property
Add hidden controls
API.JustForFans: change m3u8 parsing and downloading algo; remove 'CancellationToken' from m3u8 (replace with 'IThrower')
API.Facebook: add option 'RequestsWaitTimer_Any'; add internal option 'DownloadData_Impl'; update GDL names and tokens references; add wait timers
API.Threads: add option 'RequestsWaitTimer_Any'; add internal option 'DownloadData_Impl'; update GDL names and tokens references; add wait timers
API.Instagram: ADD 'GDL' SUPPORT; add 'UpdateWwwClaim' to 'Declarations.UpdateResponser' and 'UserData'; add additional 'HH_IG_WWW_CLAIM' properties; add 'RequestsWaitTimer_Any' property; add tooltips for timer controls; update 'LastRequests' environment; update information about requests on the label in the settings form; update reels downloading function
2024-04-09 21:39:09 +03:00
Andy
718eccc3c3 2024.3.31.0
Plugins
ISiteSettings: add 'CMDEncoding', 'EnvironmentPrograms' properties; add 'EnvironmentProgramsUpdated' function

SCrawler
GlobalSettingsForm: add the ability to find the program environment
SettingsCLS: update 'ProgramFile' class; add 'EnvironmentProgramsList' property and 'UpdateEnvironmentPrograms' function
Add the ability to display group users
Add 'GroupUsersViewer'
2024-03-31 13:18:20 +03:00
Andy
efa09fb457 2024.3.30.0
UPDATE DOWNLOAD GROUP ENVIRONMENT

Add the ability to filter users who have been (not)downloaded in the last x days.
DownloadedInfoForm: fix possible bug
Feed: fix scrolling bug
IUserData, UserDataBase, UserDataBind: remove 'FitToAddParams'
UserDataBase: update 'GetLVIGroup' function; wrong decision to set 'LastUpdated' date
AutoDownloader: remove 'All' and 'Default' options
SettingsCLS: refactoring the code and XML file
2024-03-30 15:42:30 +03:00
Andy
b252d32a7e 2024.3.26.0
API.Instagram: extract image from video
API.Reddit: add 'TryImage' bypass
Feed: add hotkeys: 'Home', 'End', 'Up', 'Page Up', 'Down', 'Page Down'; fix form deactivating; add ability to save/load view
MainFrame: update the background picture if it has changed
2024-03-26 14:13:01 +03:00
Andy
34cd510507 2024.3.24.0
YT
YouTubeMediaContainerBase: add item index to playlist row only if it is > 0

SCrawler
API.Instagram: handle 'JsonNull' for saved posts
GlobalSettingsForm: move the 'GroupUser' option to MainFrame
MainFrame: add 'GroupUser' option and 'ViewFilter' options
SettingsHostCollection: update the 'Silent' parameter for single instance
2024-03-24 02:56:08 +03:00
Andy
f5dd791941 2024.3.22.0
TDownloader: exclude the last 3 days from deleting session files
UserDownloadQueueForm: fix bug
2024-03-22 01:28:49 +03:00
Andy
2a2c12c651 2024.3.19.0
API.XHamster: some videos are missing when downloading creators; user videos aren't downloading
2024-03-19 01:09:54 +03:00
Andy
2bacc17ac4 2024.3.17.0
API.OnlyFans: add stories download
API.PornHub: fix list trim
2024-03-18 02:27:01 +03:00
Andy
7a68067d77 2024.3.15.0
PluginProvider
Add 'Checked' property
Add 'PropertyValueEventArgs'

SCrawler
Add default headers
Add predefined colors to the color picker
API.Instagram: automatically reset download options after updating credentials
2024-03-15 21:38:55 +03:00
Andy
d1eacc2db2 2024.3.13.0
YT
Add the ability to add downloaded item(s) to a playlist
Add the ability to create a playlist from downloaded music

SCrawler
Fix resetting of 'Available' indexes when the exiting a job
Allow thumbnail to be saved with file
API.TikTok: add more settings for standalone downloader; fix files with long names aren't downloaded (STD)
Feed: add moved/copied files to message box
2024-03-13 23:34:00 +03:00
Andy
3f38803643 2024.3.4.0
YT
Add the ability to add downloaded item(s) to a playlist
Add the ability to create a playlist from downloaded music

SCrawler
Fix resetting of 'Available' indexes when the exiting a job
2024-03-04 21:09:38 +03:00
154 changed files with 9801 additions and 3067 deletions

View File

@@ -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.--> 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 # 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 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 # How to build from source
1. Delete the `PersonalUtilities` project from the solution. 1. Delete the `PersonalUtilities` project from the solution.

View File

@@ -1,3 +1,147 @@
# 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.2.25.0
*2024-02-25* *2024-02-25*

Binary file not shown.

Before

Width:  |  Height:  |  Size: 56 KiB

After

Width:  |  Height:  |  Size: 59 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 78 KiB

After

Width:  |  Height:  |  Size: 101 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 11 KiB

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

After

Width:  |  Height:  |  Size: 6.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 25 KiB

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 35 KiB

After

Width:  |  Height:  |  Size: 40 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -36,8 +36,24 @@ Namespace Plugin.Attributes
Public Property IsInformationLabel As Boolean = False Public Property IsInformationLabel As Boolean = False
''' <summary>Label text alignment.<br/>Default: <see cref="Drawing.ContentAlignment.TopCenter"/></summary> ''' <summary>Label text alignment.<br/>Default: <see cref="Drawing.ContentAlignment.TopCenter"/></summary>
Public Property LabelTextAlign As Drawing.ContentAlignment = Drawing.ContentAlignment.TopCenter Public Property LabelTextAlign As Drawing.ContentAlignment = Drawing.ContentAlignment.TopCenter
Private _IsAuth As Boolean = False
''' <summary>This is an authorization property</summary> ''' <summary>This is an authorization property</summary>
Public Property IsAuth As Boolean = False Public Property IsAuth As Boolean
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> ''' <summary>Initialize a new property option attribute</summary>
''' <param name="PropertyName">Property name</param> ''' <param name="PropertyName">Property name</param>
Public Sub New(<CallerMemberName()> Optional ByVal PropertyName As String = Nothing) Public Sub New(<CallerMemberName()> Optional ByVal PropertyName As String = Nothing)
@@ -57,6 +73,7 @@ Namespace Plugin.Attributes
''' <summary>Store property value in settings XML file</summary> ''' <summary>Store property value in settings XML file</summary>
<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class PXML : Inherits Attribute <AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class PXML : Inherits Attribute
Public ReadOnly ElementName As String Public ReadOnly ElementName As String
Public Property OnlyForChecked As Boolean = False
''' <summary>Initialize a new XML attribute</summary> ''' <summary>Initialize a new XML attribute</summary>
''' <param name="XMLElementName">XML element name</param> ''' <param name="XMLElementName">XML element name</param>
Public Sub New(<CallerMemberName()> Optional ByVal XMLElementName As String = Nothing) Public Sub New(<CallerMemberName()> Optional ByVal XMLElementName As String = Nothing)

View File

@@ -40,5 +40,6 @@ Namespace Plugin
Sub GetMedia(ByVal Token As Threading.CancellationToken) Sub GetMedia(ByVal Token As Threading.CancellationToken)
Sub Download(ByVal Token As Threading.CancellationToken) Sub Download(ByVal Token As Threading.CancellationToken)
Sub DownloadSingleObject(ByVal Data As IDownloadableMedia, ByVal Token As Threading.CancellationToken) Sub DownloadSingleObject(ByVal Data As IDownloadableMedia, ByVal Token As Threading.CancellationToken)
Sub ResetHistoryData()
End Interface End Interface
End Namespace End Namespace

View File

@@ -17,6 +17,10 @@ Namespace Plugin
ReadOnly Property Icon As Icon ReadOnly Property Icon As Icon
ReadOnly Property Image As Image ReadOnly Property Image As Image
ReadOnly Property Site As String ReadOnly Property Site As String
Property CMDEncoding As String
Property EnvironmentPrograms As IEnumerable(Of String)
Property UserAgentDefault As String
Sub EnvironmentProgramsUpdated()
Property AccountName As String Property AccountName As String
Property Temporary As Boolean Property Temporary As Boolean
Property DefaultInstance As ISiteSettings Property DefaultInstance As ISiteSettings

View File

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

View File

@@ -9,8 +9,23 @@
Namespace Plugin Namespace Plugin
Public NotInheritable Class PropertyValue : Implements IPropertyValue Public NotInheritable Class PropertyValue : Implements IPropertyValue
Public Event ValueChanged As IPropertyValue.ValueChangedEventHandler Implements IPropertyValue.ValueChanged Public Event ValueChanged As IPropertyValue.ValueChangedEventHandler Implements IPropertyValue.ValueChanged
Public Event CheckedChanged As IPropertyValue.ValueChangedEventHandler
Public Property [Type] As Type Implements IPropertyValue.Type Public Property [Type] As Type Implements IPropertyValue.Type
Public Property OnChangeFunction As IPropertyValue.ValueChangedEventHandler Public Property OnChangeFunction As IPropertyValue.ValueChangedEventHandler
Public Property OnCheckboxCheckedChange As EventHandler(Of PropertyValueEventArgs)
Private _Checked As Boolean = False
Public Property Checked As Boolean
Get
Return _Checked
End Get
Set(ByVal IsChecked As Boolean)
_Checked = IsChecked
If Not _Initialization Then
If Not OnCheckboxCheckedChange Is Nothing Then OnCheckboxCheckedChange.Invoke(Me, EventArgs.Empty)
RaiseEvent CheckedChanged(_Checked)
End If
End Set
End Property
Private _Initialization As Boolean = False Private _Initialization As Boolean = False
''' <inheritdoc cref="PropertyValue.New(Object, Type, ByRef IPropertyValue.ValueChangedEventHandler)"/> ''' <inheritdoc cref="PropertyValue.New(Object, Type, ByRef IPropertyValue.ValueChangedEventHandler)"/>
''' <exception cref="ArgumentNullException"></exception> ''' <exception cref="ArgumentNullException"></exception>
@@ -59,6 +74,7 @@ Namespace Plugin
Type = Source.Type Type = Source.Type
OnChangeFunction = Source.OnChangeFunction OnChangeFunction = Source.OnChangeFunction
_Value = Source._Value _Value = Source._Value
_Checked = Source._Checked
_Initialization = False _Initialization = False
End Sub End Sub
End Class End Class
@@ -71,4 +87,8 @@ Namespace Plugin
''' <summary>Property value</summary> ''' <summary>Property value</summary>
Property Value As Object Property Value As Object
End Interface End Interface
Public Class PropertyValueEventArgs : Inherits EventArgs
Public Property Checked As Boolean = False
Public Property ControlEnabled As Boolean = True
End Class
End Namespace End Namespace

View File

@@ -81,6 +81,7 @@ Namespace API.YouTube.Base
Public Structure MediaObject : Implements IIndexable, IComparable(Of MediaObject) Public Structure MediaObject : Implements IIndexable, IComparable(Of MediaObject)
Public Type As Plugin.UserMediaTypes Public Type As Plugin.UserMediaTypes
Public ID As String Public ID As String
Public ID_DRC As Boolean
Public Extension As String Public Extension As String
Public Width As Integer Public Width As Integer
Public Height As Integer Public Height As Integer
@@ -110,11 +111,15 @@ Namespace API.YouTube.Base
End Function End Function
Private Function CompareTo(ByVal Other As MediaObject) As Integer Implements IComparable(Of MediaObject).CompareTo Private Function CompareTo(ByVal Other As MediaObject) As Integer Implements IComparable(Of MediaObject).CompareTo
If Type = Other.Type Then If Type = Other.Type Then
If ID_DRC.CompareTo(Other.ID_DRC) = 0 Then
If Width.CompareTo(Other.Width) = 0 Then If Width.CompareTo(Other.Width) = 0 Then
Return Size.CompareTo(Other.Size) * -1 Return Size.CompareTo(Other.Size) * -1
Else Else
Return Width.CompareTo(Other.Width) * -1 Return Width.CompareTo(Other.Width) * -1
End If End If
Else
Return ID_DRC.CompareTo(Other.ID_DRC)
End If
Else Else
Return CInt(Type).CompareTo(CInt(Other.Type)) Return CInt(Type).CompareTo(CInt(Other.Type))
End If End If

View File

@@ -174,7 +174,7 @@ Namespace API.YouTube.Base
ByVal ObjType As YouTubeMediaType, ByVal ChannelTab As YouTubeChannelTab, ByVal ObjType As YouTubeMediaType, ByVal ChannelTab As YouTubeChannelTab,
ByVal IsMusic As Boolean, ByVal UrlAsIs As Boolean) As Boolean ByVal IsMusic As Boolean, ByVal UrlAsIs As Boolean) As Boolean
Try Try
Dim command$ = "yt-dlp --write-info-json --skip-download" Dim command$ = $"{YTDLP_NAME} --write-info-json --skip-download"
command.StringAppend(GetCookiesCommand(UseCookies, CookiesFile), " ") command.StringAppend(GetCookiesCommand(UseCookies, CookiesFile), " ")
If DateAfter.HasValue Then command.StringAppend($"--dateafter {DateAfter.Value:yyyyMMdd}", " ") If DateAfter.HasValue Then command.StringAppend($"--dateafter {DateAfter.Value:yyyyMMdd}", " ")
If DateBefore.HasValue Then command.StringAppend($"--datebefore {DateBefore.Value:yyyyMMdd}", " ") If DateBefore.HasValue Then command.StringAppend($"--datebefore {DateBefore.Value:yyyyMMdd}", " ")

View File

@@ -36,7 +36,9 @@ Namespace API.YouTube.Base
<Browsable(False)> Private Property Mode As GridUpdateModes = GridUpdateModes.OnConfirm Implements IGridValuesContainer.Mode <Browsable(False)> Private Property Mode As GridUpdateModes = GridUpdateModes.OnConfirm Implements IGridValuesContainer.Mode
<Browsable(False), XMLVV(-1)> Friend ReadOnly Property PlaylistFormSplitterDistance As XMLValue(Of Integer) <Browsable(False), XMLVV(-1)> Friend ReadOnly Property PlaylistFormSplitterDistance As XMLValue(Of Integer)
<Browsable(False)> Friend ReadOnly Property DownloadLocations As DownloadLocationsCollection <Browsable(False)> Friend ReadOnly Property DownloadLocations As DownloadLocationsCollection
<Browsable(False)> Friend ReadOnly Property PlaylistsLocations As DownloadLocationsCollection
<Browsable(False)> Public Overridable Property AccountName As String <Browsable(False)> Public Overridable Property AccountName As String
<Browsable(False), XMLVV(0)> Private ReadOnly Property SettingsVersion As XMLValue(Of Integer)
#Region "Environment" #Region "Environment"
#Region "Programs" #Region "Programs"
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment programs"), DisplayName("Path to yt-dlp.exe"), <Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment programs"), DisplayName("Path to yt-dlp.exe"),
@@ -84,6 +86,18 @@ Namespace API.YouTube.Base
Description("The default output path where files should be downloaded."), Description("The default output path where files should be downloaded."),
Editor(GetType(GridSFileTypeEditorPath), GetType(UITypeEditor))> Editor(GetType(GridSFileTypeEditorPath), GetType(UITypeEditor))>
Public ReadOnly Property OutputPath As XMLValue(Of SFile) Public ReadOnly Property OutputPath As XMLValue(Of SFile)
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment"), DisplayName("Playlist file"),
Description("Last selected playlist file"),
Editor(GetType(GridSFileTypeEditor_M3U8), GetType(UITypeEditor))>
Public ReadOnly Property LatestPlaylistFile As XMLValue(Of SFile)
Private Class GridSFileTypeEditor_M3U8 : Inherits GridSFileTypeEditor
Public Overrides Function EditValue(ByVal Context As ITypeDescriptorContext, ByVal Provider As IServiceProvider, ByVal Value As Object) As Object
Dim f As SFile = SFile.SelectFiles(New SFile(CStr(AConvert(Of String)(Value, AModes.Var, String.Empty))), False,
"Select playlist file", "Playlists|*.m3u;*.m3u8|All files|*.*", EDP.ReturnValue).FirstOrDefault()
If Not f.IsEmptyString() Then Value = f
Return Value
End Function
End Class
<Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment"), DisplayName("Output path auto change"), <Browsable(True), GridVisible(False), XMLVN({"Environment"}), Category("Environment"), DisplayName("Output path auto change"),
Description("Automatically change the output path when a new destination is selected in the opening forms.")> Description("Automatically change the output path when a new destination is selected in the opening forms.")>
Public ReadOnly Property OutputPathAutoChange As XMLValue(Of Boolean) Public ReadOnly Property OutputPathAutoChange As XMLValue(Of Boolean)
@@ -229,6 +243,9 @@ Namespace API.YouTube.Base
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Program description"), <Browsable(True), GridVisible(False), XMLVN({"Defaults"}), Category("Defaults"), DisplayName("Program description"),
Description("Add some additional info to the program info if you need")> Description("Add some additional info to the program info if you need")>
Friend ReadOnly Property ProgramDescription As XMLValue(Of String) Friend ReadOnly Property ProgramDescription As XMLValue(Of String)
<Browsable(True), GridVisible(False), XMLVN({"Defaults"}, "%"""), Category("Defaults"), DisplayName("Remove characters"),
Description("Remove specific characters from a file name")>
Friend ReadOnly Property FileRemoveCharacters As XMLValue(Of String)
#End Region #End Region
#Region "Defaults ChannelsDownload" #Region "Defaults ChannelsDownload"
<Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"), <Browsable(True), GridVisible, XMLVN({"Defaults", "Channels"}), Category("Defaults"), DisplayName("Default download tabs for channels"),
@@ -344,6 +361,12 @@ Namespace API.YouTube.Base
Throw New NotImplementedException("'GetFormat' is not available in 'FpsFormatProvider'") Throw New NotImplementedException("'GetFormat' is not available in 'FpsFormatProvider'")
End Function End Function
End Class 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)
#End Region #End Region
#Region "Defaults Audio" #Region "Defaults Audio"
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, "AAC"), Category("Defaults Audio"), DisplayName("Default codec"), <Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, "AAC"), Category("Defaults Audio"), DisplayName("Default codec"),
@@ -366,6 +389,32 @@ Namespace API.YouTube.Base
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, True), Category("Defaults Audio"), DisplayName("Embed thumbnail"), <Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, True), Category("Defaults Audio"), DisplayName("Embed thumbnail"),
Description("Embed thumbnail in the audio as cover art. Default: true.")> Description("Embed thumbnail in the audio as cover art. Default: true.")>
Public ReadOnly Property DefaultAudioEmbedThumbnail As XMLValue(Of Boolean) Public ReadOnly Property DefaultAudioEmbedThumbnail As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, True), Category("Defaults Audio"), DisplayName("Embed thumbnail (extracted files)"),
Description("Embed thumbnail in the extracted (additional file ('mp3' only)) audio as cover art. Default: true.")>
Public ReadOnly Property DefaultAudioEmbedThumbnail_ExtractedFiles As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, -1), Category("Defaults Audio"), DisplayName("Bitrate"),
Description("Default audio bitrate if you want to change it during download. -1 to disable. Default: -1.")>
Public ReadOnly Property DefaultAudioBitrate As XMLValue(Of Integer)
<Browsable(True), GridVisible, XMLVN({"DefaultsAudio"}, 20), Category("Defaults Audio"), DisplayName("Bitrate: ffmpeg crf"),
Description("This is the ffmpeg argument. Change it only if you know what you're doing. Default: 20.")>
Public ReadOnly Property DefaultAudioBitrate_crf As XMLValue(Of Integer)
#Region "Music"
<Browsable(True), GridVisible, XMLVN({"Playlists"}, True), Category("Music"), DisplayName("Create M3U8"),
Description("Create M3U8 playlist for music. Default: true.")>
Public ReadOnly Property MusicPlaylistCreate_M3U8 As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Playlists"}), Category("Music"), DisplayName("Create M3U"),
Description("Create M3U playlist for music. Default: false.")>
Public ReadOnly Property MusicPlaylistCreate_M3U As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Playlists"}), Category("Music"), DisplayName("M3U8 Append artist"),
Description("Add artist to file name. Default: false.")>
Public ReadOnly Property MusicPlaylistCreate_M3U8_AppendArtist As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Playlists"}), Category("Music"), DisplayName("M3U8 Append file extension"),
Description("Add file extension to file name. Default: false.")>
Public ReadOnly Property MusicPlaylistCreate_M3U8_AppendExt As XMLValue(Of Boolean)
<Browsable(True), GridVisible, XMLVN({"Playlists"}), Category("Music"), DisplayName("M3U8 Append file number"),
Description("Add file number to file name. Default: false.")>
Public ReadOnly Property MusicPlaylistCreate_M3U8_AppendNumber As XMLValue(Of Boolean)
#End Region
#End Region #End Region
#Region "Defaults Subtitles" #Region "Defaults Subtitles"
<XMLVN({"DefaultsSubtitles"}, {"en"}, CollectionMode:=IXMLValuesCollection.Modes.String)> <XMLVN({"DefaultsSubtitles"}, {"en"}, CollectionMode:=IXMLValuesCollection.Modes.String)>
@@ -418,7 +467,9 @@ Namespace API.YouTube.Base
Public Sub New(ByVal AccountName As String) Public Sub New(ByVal AccountName As String)
Me.AccountName = AccountName Me.AccountName = AccountName
DownloadLocations = New DownloadLocationsCollection DownloadLocations = New DownloadLocationsCollection
PlaylistsLocations = New DownloadLocationsCollection
DownloadLocations.Load(False, True) DownloadLocations.Load(False, True)
PlaylistsLocations.Load(True, True, $"{XmlFile.SettingsFolder}\DownloadLocations_Playlists.xml")
Dim acc$ = String.Empty Dim acc$ = String.Empty
If Not AccountName.IsEmptyString Then acc = $"_{AccountName}" If Not AccountName.IsEmptyString Then acc = $"_{AccountName}"
Dim f As SFile = YouTubeSettingsFile Dim f As SFile = YouTubeSettingsFile

View File

@@ -43,8 +43,16 @@ Namespace API.YouTube.Controls
Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton11 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
Dim ActionButton12 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton13 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton14 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton15 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton16 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton17 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton18 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim TT_MAIN As System.Windows.Forms.ToolTip Dim TT_MAIN As System.Windows.Forms.ToolTip
Me.BTT_DOWN = New System.Windows.Forms.Button() Me.BTT_DOWN = New System.Windows.Forms.Button()
Me.BTT_CANCEL = New System.Windows.Forms.Button() Me.BTT_CANCEL = New System.Windows.Forms.Button()
@@ -58,6 +66,8 @@ Namespace API.YouTube.Controls
Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.CH_DOWN_LYRICS = New System.Windows.Forms.CheckBox() Me.CH_DOWN_LYRICS = New System.Windows.Forms.CheckBox()
Me.TXT_OUTPUT_PATH = New PersonalUtilities.Forms.Controls.ComboBoxExtended() Me.TXT_OUTPUT_PATH = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
Me.CMB_PLS = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
Me.TXT_AUDIO_BITRATE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
TP_MAIN = New System.Windows.Forms.TableLayoutPanel() TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel() TP_BUTTONS = New System.Windows.Forms.TableLayoutPanel()
TP_PLS = New System.Windows.Forms.TableLayoutPanel() TP_PLS = New System.Windows.Forms.TableLayoutPanel()
@@ -83,6 +93,8 @@ Namespace API.YouTube.Controls
TP_LYRICS.SuspendLayout() TP_LYRICS.SuspendLayout()
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_OUTPUT_PATH, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_OUTPUT_PATH, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_AUDIO_BITRATE, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout() Me.SuspendLayout()
' '
'TP_MAIN 'TP_MAIN
@@ -97,10 +109,10 @@ Namespace API.YouTube.Controls
TP_MAIN.Margin = New System.Windows.Forms.Padding(0) TP_MAIN.Margin = New System.Windows.Forms.Padding(0)
TP_MAIN.Name = "TP_MAIN" TP_MAIN.Name = "TP_MAIN"
TP_MAIN.RowCount = 3 TP_MAIN.RowCount = 3
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 84.0!)) TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 140.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_MAIN.Size = New System.Drawing.Size(434, 261) TP_MAIN.Size = New System.Drawing.Size(434, 317)
TP_MAIN.TabIndex = 0 TP_MAIN.TabIndex = 0
' '
'TP_BUTTONS 'TP_BUTTONS
@@ -112,14 +124,14 @@ Namespace API.YouTube.Controls
TP_BUTTONS.Controls.Add(Me.BTT_DOWN, 1, 0) TP_BUTTONS.Controls.Add(Me.BTT_DOWN, 1, 0)
TP_BUTTONS.Controls.Add(Me.BTT_CANCEL, 2, 0) TP_BUTTONS.Controls.Add(Me.BTT_CANCEL, 2, 0)
TP_BUTTONS.Dock = System.Windows.Forms.DockStyle.Fill TP_BUTTONS.Dock = System.Windows.Forms.DockStyle.Fill
TP_BUTTONS.Location = New System.Drawing.Point(0, 236) TP_BUTTONS.Location = New System.Drawing.Point(0, 292)
TP_BUTTONS.Margin = New System.Windows.Forms.Padding(0) TP_BUTTONS.Margin = New System.Windows.Forms.Padding(0)
TP_BUTTONS.Name = "TP_BUTTONS" TP_BUTTONS.Name = "TP_BUTTONS"
TP_BUTTONS.RowCount = 1 TP_BUTTONS.RowCount = 1
TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) TP_BUTTONS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_BUTTONS.Size = New System.Drawing.Size(434, 25) TP_BUTTONS.Size = New System.Drawing.Size(434, 25)
TP_BUTTONS.TabIndex = 2 TP_BUTTONS.TabIndex = 1
' '
'BTT_DOWN 'BTT_DOWN
' '
@@ -147,7 +159,7 @@ Namespace API.YouTube.Controls
'SPLITTER_MAIN 'SPLITTER_MAIN
' '
Me.SPLITTER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill Me.SPLITTER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
Me.SPLITTER_MAIN.Location = New System.Drawing.Point(3, 87) Me.SPLITTER_MAIN.Location = New System.Drawing.Point(3, 143)
Me.SPLITTER_MAIN.Name = "SPLITTER_MAIN" Me.SPLITTER_MAIN.Name = "SPLITTER_MAIN"
' '
'SPLITTER_MAIN.Panel1 'SPLITTER_MAIN.Panel1
@@ -263,16 +275,20 @@ Namespace API.YouTube.Controls
TP_SETTINGS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_SETTINGS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_SETTINGS.Controls.Add(TP_FORMATS, 0, 1) TP_SETTINGS.Controls.Add(TP_FORMATS, 0, 1)
TP_SETTINGS.Controls.Add(TP_LYRICS, 0, 0) TP_SETTINGS.Controls.Add(TP_LYRICS, 0, 0)
TP_SETTINGS.Controls.Add(Me.TXT_OUTPUT_PATH, 0, 2) TP_SETTINGS.Controls.Add(Me.TXT_OUTPUT_PATH, 0, 3)
TP_SETTINGS.Controls.Add(Me.CMB_PLS, 0, 4)
TP_SETTINGS.Controls.Add(Me.TXT_AUDIO_BITRATE, 0, 2)
TP_SETTINGS.Dock = System.Windows.Forms.DockStyle.Fill TP_SETTINGS.Dock = System.Windows.Forms.DockStyle.Fill
TP_SETTINGS.Location = New System.Drawing.Point(0, 0) TP_SETTINGS.Location = New System.Drawing.Point(0, 0)
TP_SETTINGS.Margin = New System.Windows.Forms.Padding(0) TP_SETTINGS.Margin = New System.Windows.Forms.Padding(0)
TP_SETTINGS.Name = "TP_SETTINGS" TP_SETTINGS.Name = "TP_SETTINGS"
TP_SETTINGS.RowCount = 3 TP_SETTINGS.RowCount = 5
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!)) TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!)) TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!)) TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
TP_SETTINGS.Size = New System.Drawing.Size(434, 84) TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
TP_SETTINGS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 20.0!))
TP_SETTINGS.Size = New System.Drawing.Size(434, 140)
TP_SETTINGS.TabIndex = 1 TP_SETTINGS.TabIndex = 1
' '
'TP_FORMATS 'TP_FORMATS
@@ -291,7 +307,7 @@ Namespace API.YouTube.Controls
TP_FORMATS.RowCount = 1 TP_FORMATS.RowCount = 1
TP_FORMATS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_FORMATS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_FORMATS.Size = New System.Drawing.Size(434, 28) TP_FORMATS.Size = New System.Drawing.Size(434, 28)
TP_FORMATS.TabIndex = 1 TP_FORMATS.TabIndex = 5
' '
'TXT_FORMATS_ADDIT 'TXT_FORMATS_ADDIT
' '
@@ -360,7 +376,7 @@ Namespace API.YouTube.Controls
TP_LYRICS.RowCount = 1 TP_LYRICS.RowCount = 1
TP_LYRICS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_LYRICS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_LYRICS.Size = New System.Drawing.Size(434, 28) TP_LYRICS.Size = New System.Drawing.Size(434, 28)
TP_LYRICS.TabIndex = 0 TP_LYRICS.TabIndex = 6
' '
'TXT_SUBS 'TXT_SUBS
' '
@@ -410,23 +426,28 @@ Namespace API.YouTube.Controls
'TXT_OUTPUT_PATH 'TXT_OUTPUT_PATH
' '
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
ActionButton7.Name = "Open" ActionButton7.Name = "Save"
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
ActionButton7.ToolTipText = "Choose a new location (Ctrl+O)" ActionButton7.ToolTipText = "Save destination"
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
ActionButton8.Name = "Add" ActionButton8.Name = "Open"
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
ActionButton8.ToolTipText = "Choose a new location and add it to the list (Alt+O)" ActionButton8.ToolTipText = "Choose a new location (Ctrl+O)"
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image) ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
ActionButton9.Name = "Clear" ActionButton9.Name = "Add"
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add
ActionButton9.ToolTipText = "Choose a new location and add it to the list (Alt+O)"
ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image) ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
ActionButton10.Name = "ArrowDown" ActionButton10.Name = "Clear"
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image)
ActionButton11.Name = "ArrowDown"
ActionButton11.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton7) Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton7)
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton8) Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton8)
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton9) Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton9)
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton10) Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton10)
Me.TXT_OUTPUT_PATH.Buttons.Add(ActionButton11)
Me.TXT_OUTPUT_PATH.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_OUTPUT_PATH.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
Me.TXT_OUTPUT_PATH.CaptionText = "Output path" Me.TXT_OUTPUT_PATH.CaptionText = "Output path"
Me.TXT_OUTPUT_PATH.CaptionToolTipEnabled = True Me.TXT_OUTPUT_PATH.CaptionToolTipEnabled = True
@@ -446,23 +467,82 @@ Namespace API.YouTube.Controls
Me.TXT_OUTPUT_PATH.Columns.Add(ListColumn1) Me.TXT_OUTPUT_PATH.Columns.Add(ListColumn1)
Me.TXT_OUTPUT_PATH.Columns.Add(ListColumn2) Me.TXT_OUTPUT_PATH.Columns.Add(ListColumn2)
Me.TXT_OUTPUT_PATH.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_OUTPUT_PATH.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_OUTPUT_PATH.Location = New System.Drawing.Point(3, 59) Me.TXT_OUTPUT_PATH.Location = New System.Drawing.Point(3, 87)
Me.TXT_OUTPUT_PATH.Name = "TXT_OUTPUT_PATH" Me.TXT_OUTPUT_PATH.Name = "TXT_OUTPUT_PATH"
Me.TXT_OUTPUT_PATH.Size = New System.Drawing.Size(428, 22) Me.TXT_OUTPUT_PATH.Size = New System.Drawing.Size(428, 22)
Me.TXT_OUTPUT_PATH.TabIndex = 2 Me.TXT_OUTPUT_PATH.TabIndex = 2
Me.TXT_OUTPUT_PATH.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle Me.TXT_OUTPUT_PATH.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
' '
'CMB_PLS
'
ActionButton12.BackgroundImage = CType(resources.GetObject("ActionButton12.BackgroundImage"), System.Drawing.Image)
ActionButton12.Name = "Save"
ActionButton12.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
ActionButton12.ToolTipText = "Save playlist"
ActionButton13.BackgroundImage = CType(resources.GetObject("ActionButton13.BackgroundImage"), System.Drawing.Image)
ActionButton13.Name = "List"
ActionButton13.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.List
ActionButton13.ToolTipText = "Select multiple playlists"
ActionButton14.BackgroundImage = CType(resources.GetObject("ActionButton14.BackgroundImage"), System.Drawing.Image)
ActionButton14.Name = "Open"
ActionButton14.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
ActionButton14.ToolTipText = "Choose an output file"
ActionButton15.BackgroundImage = CType(resources.GetObject("ActionButton15.BackgroundImage"), System.Drawing.Image)
ActionButton15.Name = "Add"
ActionButton15.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add
ActionButton15.ToolTipText = "Choose an output file (add a new location to the list)"
ActionButton16.BackgroundImage = CType(resources.GetObject("ActionButton16.BackgroundImage"), System.Drawing.Image)
ActionButton16.Name = "Clear"
ActionButton16.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
ActionButton17.BackgroundImage = CType(resources.GetObject("ActionButton17.BackgroundImage"), System.Drawing.Image)
ActionButton17.Name = "ArrowDown"
ActionButton17.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
Me.CMB_PLS.Buttons.Add(ActionButton12)
Me.CMB_PLS.Buttons.Add(ActionButton13)
Me.CMB_PLS.Buttons.Add(ActionButton14)
Me.CMB_PLS.Buttons.Add(ActionButton15)
Me.CMB_PLS.Buttons.Add(ActionButton16)
Me.CMB_PLS.Buttons.Add(ActionButton17)
Me.CMB_PLS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label
Me.CMB_PLS.CaptionText = "Playlist"
Me.CMB_PLS.CaptionToolTipEnabled = True
Me.CMB_PLS.CaptionToolTipText = "Add downloaded item(s) to playlist"
Me.CMB_PLS.CaptionVisible = True
Me.CMB_PLS.CaptionWidth = 50.0R
Me.CMB_PLS.Dock = System.Windows.Forms.DockStyle.Fill
Me.CMB_PLS.Location = New System.Drawing.Point(3, 115)
Me.CMB_PLS.Name = "CMB_PLS"
Me.CMB_PLS.Size = New System.Drawing.Size(428, 22)
Me.CMB_PLS.TabIndex = 3
Me.CMB_PLS.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
'
'TXT_AUDIO_BITRATE
'
ActionButton18.BackgroundImage = CType(resources.GetObject("ActionButton18.BackgroundImage"), System.Drawing.Image)
ActionButton18.Name = "Clear"
ActionButton18.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_AUDIO_BITRATE.Buttons.Add(ActionButton18)
Me.TXT_AUDIO_BITRATE.CaptionText = "Audio bitrate"
Me.TXT_AUDIO_BITRATE.CaptionToolTipEnabled = True
Me.TXT_AUDIO_BITRATE.CaptionToolTipText = "Default audio bitrate if you want to change it during download"
Me.TXT_AUDIO_BITRATE.CaptionWidth = 112.0R
Me.TXT_AUDIO_BITRATE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_AUDIO_BITRATE.Location = New System.Drawing.Point(3, 59)
Me.TXT_AUDIO_BITRATE.Name = "TXT_AUDIO_BITRATE"
Me.TXT_AUDIO_BITRATE.Size = New System.Drawing.Size(428, 22)
Me.TXT_AUDIO_BITRATE.TabIndex = 4
'
'MusicPlaylistsForm 'MusicPlaylistsForm
' '
Me.AcceptButton = Me.BTT_DOWN Me.AcceptButton = Me.BTT_DOWN
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.CancelButton = Me.BTT_CANCEL Me.CancelButton = Me.BTT_CANCEL
Me.ClientSize = New System.Drawing.Size(434, 261) Me.ClientSize = New System.Drawing.Size(434, 317)
Me.Controls.Add(TP_MAIN) Me.Controls.Add(TP_MAIN)
Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeMusicIcon_32 Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeMusicIcon_32
Me.KeyPreview = True Me.KeyPreview = True
Me.MinimumSize = New System.Drawing.Size(450, 300) Me.MinimumSize = New System.Drawing.Size(450, 356)
Me.Name = "MusicPlaylistsForm" Me.Name = "MusicPlaylistsForm"
Me.Text = "Albums" Me.Text = "Albums"
TP_MAIN.ResumeLayout(False) TP_MAIN.ResumeLayout(False)
@@ -481,6 +561,8 @@ Namespace API.YouTube.Controls
TP_LYRICS.PerformLayout() TP_LYRICS.PerformLayout()
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).EndInit() CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_OUTPUT_PATH, System.ComponentModel.ISupportInitialize).EndInit() CType(Me.TXT_OUTPUT_PATH, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_AUDIO_BITRATE, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False) Me.ResumeLayout(False)
End Sub End Sub
@@ -496,5 +578,7 @@ Namespace API.YouTube.Controls
Private WithEvents SPLITTER_MAIN As SplitContainer Private WithEvents SPLITTER_MAIN As SplitContainer
Private WithEvents CH_DOWN_LYRICS As CheckBox Private WithEvents CH_DOWN_LYRICS As CheckBox
Private WithEvents TXT_OUTPUT_PATH As PersonalUtilities.Forms.Controls.ComboBoxExtended Private WithEvents TXT_OUTPUT_PATH As PersonalUtilities.Forms.Controls.ComboBoxExtended
Private WithEvents CMB_PLS As PersonalUtilities.Forms.Controls.ComboBoxExtended
Private WithEvents TXT_AUDIO_BITRATE As PersonalUtilities.Forms.Controls.TextBoxExtended
End Class End Class
End Namespace End Namespace

View File

@@ -222,6 +222,13 @@
</value> </value>
</data> </data>
<data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
</value>
</data>
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
@@ -232,7 +239,7 @@
cMaRN0UdBBkAAAAASUVORK5CYII= cMaRN0UdBBkAAAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton9.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE
@@ -254,7 +261,7 @@
0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC 0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton9.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton10.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -262,7 +269,7 @@
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
<data name="ActionButton10.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton11.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL 3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
@@ -350,6 +357,161 @@
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
AAAASUVORK5CYII= AAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton12.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
</value>
</data>
<data name="ActionButton13.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeElE
QVQ4T2P4//8/RRhMFHQfKgDi/yAaXQEhDCZAmkNbnvyXta4CciESLEws//FhmDqYAQUgzUBMngsowVgF
ScFgYjQQsUsQi8FEYsXyAiD+D6LRFRDCYAKk2bPo6H9J40wgFyKBLeCQMUwdzIACkGYgHnKB+J8BAD5Q
tqhi4tzWAAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton14.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
cMaRN0UdBBkAAAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton15.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE
QVRIS62WWWxMURjHL220JW1HausmlFrDFKUhnUGH6bRFzJ2idImlC0Vp2mlji1A8iNhCPIjIRES8EU+W
h2oEtbSDTk3HNNM7S01VKsXjkb/vXBo3k1Ee7sMvmZzzzf//ne/+z50RAAxL1MUIG4G/YAv3HSVhF5Vw
IYNdz3LadVj9RgdTB+HQYYPHIJuE1ocSdlEJFzG+1bPRLQLinglIeCkg+XUkKvz56hnkOfQs/rmA8S9H
YEp7FDI64tAQtKhnsMapZ7zzNHsUFnbGY4VzIk70l6hnIH4wsDR7NBZ3apDrSqL5T8eFgUr1DLZ78lim
Q4N8VzK29MxEpZSBa4M16hnU+c3M9CEFpdJsVHsXos63DDcHrf9nQEXD5VymwW/5USLNwl5vJhp7dTgW
NML2pR7jbsUMS+KdMTa5Q8NQxinfBU4dRFcOyjy52OtbhwOBDTgZLKPPmTgY0ON4MBdNfSbYBupxY8Aq
G10dqMG5/nIc7ytGQ6CQRliAamkTN/g1Ai4e95Qy3iogpX0UtBRDnhRzdxq2SXOxz5eFQ70rScCEU335
ssGxj0YS06HSm4GN3ekwdE2C1hGH1LZR0JDOJof5jwHvnIvzTa0jlooTYfktvt+fhcOBHDQFTWRgxJGP
ObAGsulZLMLWnjlY756K5c4JmNcRi6T2SGheCIihS2l5ozAo6NRhMolnUAcGV6IcwwqvFrX+JTjYuwKH
SfRAYDms/mzs9y1GFe2VSnOw1j0FejqpLN4WCX4ZufiIBwLMLxQGm12rsLQzgWKYgmLPLNTQw6ynpDSS
IBet8y+TqaVRVdFIeJrWuCcj+/0EzH43BomvIhBLI45uFiDcJ+6QwROFwa6+Amb9bGFNg6Xs9Ncd7Oy3
Knb2eyU7/20nu9y/m136tIvEl6BC0qKoZwby3alo9JVhj7T5R7m/kJVIIityi8zyXmTiW+I10SqyIQNb
uIgNwYuuf25kFd75KPKkI49OmUWnrfYWyXv/wBb2cijhhVf6a9lGei65XclYRDd6mj0GWz2iLBJaH0rY
RSVc5Eywmhm7kuQXHX+bJlBStrh+zTi0PpSwi0q4yNFAOVvgiEcKJWUsxZn/NhT+znlofShhF5VwkRpv
MUtti4KGYjj6sYCIh5QSu4oG27stjItHU+cjeQzvkcFzFQ2KnSKLoc4FukDCXeI2GbSoaFD4ziyPxNxK
0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton16.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton17.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
GlAKCkhEC4KgQlsLQkqhKi/lrYWWlxaw3dLddrerz/Q89+7dc2fbfTn3npf5fJJv2rS758z85nnOzJz5
nZktAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMK3O3r79wVUIz65jfGNVxI/VIX69CvGO9M//a9P+e8o3B/8v
vKn9s+3fyX8dAJgmaWd+fl3E96Wd/E9XdvZHkfbvXNa+Rn45AGCS3bvjj/E/h3box5OrmxjPyy8PAEyS
XXO7zqhCeH/HDnwUOdCE+J6zdux4eH47YIrEGE8uy/Ls9Bnx/LooL0oH9b9Th/I1TVG+rCqKC+q6Xsh/
FJgmO8vy6WknfdPQTnsjckMdwlPy2wITLO3wF6si/lGas1ekuXvX0Fzuyg9S3psOCl6qDwimQB3ji9Ok
3btmEm907kpnEa/Mbw9Mlq1pB/6cdHZ/ZcfcPZrcXoXyrVVVFfl1gUmSdsS/libqPUMTd5NSvjktwrbB
kgDjVi1UT26K+Nnu+XrMuaud60uPWHpIfhtg3JqyfEaanHcPTdZNTRPCPy4uLj40LxIwBudt2fKAtOP/
0zQnN+5koIg3tpca81sC49J+LZcm5a3rJulYEq6LSV40YBOFEB6V5uFV6+flRiTsSwf9r81vDYzBCSO4
vjfq/KAuiqfm5QM2QRPjuWnubUbz71DCn6W33zpYCmDT1EX5m92Tcuy5q47xFXkxgQ3UduqnOXfn0Bzc
xJSvz4sCbIb2pzlp8v1w/WScnKSzkjekRT1hsMTAKC0vL5/Ydud3zb1NT1FelBcL2GiDm3d0TMTJy0ea
pjk1LzYwAu3NvtLc+uTQXBtn7tYYCJtja/vQno5JOJFpQrzWb4hhNJoQnpjm1Q3D82wCcnNRFKfnxQQ2
Qttk1zH5JjzhFmcIcHzyzb6O5aFem5J0sP/OvKjARmg7b7sm3xRkT3vDorwawJHb1t6Ep2NOTVoOtDch
yssMjFr6IPh8x8SbnsT4lrQamgPhCMzPzz+sifHjnXNpMnN5XnRglJaWlk5KE2z/0ISbxnzQQ0bgvlXz
1ePSXPnG0NyZ+DRF8Zi8CsCo7Azh0V0TbkrzRc2B0G3wIJ9429CcmZLce4MgYJTyff87JtzU5uayLM/J
qwcM7vD5+jQ3DgzNlWnKDXldgFFJZwW/2jHZpj1727uZ5VWE3mofqJXmw4eG5sdUpqqqXXm1gFGoQnhJ
12SbgRxoYvzjtIruK04vxRjPSvPgK0PzYmqTPqtemVcNGIU6xgu7JtusJH1ovH9ubu6UvLrQC2ncPyuN
/58Mz4fpTvnmvHrAKJQL5dO6J9ssJXxucWFhLq8yzLKtaUf5h2ncb9zz+8eUKsYP53UERmHX/PyOrsk2
g7nJDUWYZUuPWHpIE8oPdIz92UiMn86rCoxIOmOYta8KD5uftk2Peb1hZtTzdVOHcF3HmJ+ZVCF+Ia8u
MCppcl0+PNlmOG1zYPtYYc2BzIQ0np+ZxvWPh8b5LObqvMrAqEzRo4BHmctijCfnEsBUqkP5u2ksz8Kd
PI8g5SfyagOj0jbIpQk2c01DR5Brmh3NfC4DTI324LWO8V0dY3pm48mAsEGm7OEgo0sRb9wZ4+NzGWDi
lWUZ0ti9Zt1YnvUU8fdyCYBRmsFbAh9xqhDvqEN4Xi4FTKz8s93vD4/hPiSdpJyXywCMWPtrgKuGJ12P
ck/6gPmDXAuYOHVR/lY6UN3XMXb7kDv17MAGqhaqJ6WJ1sdegDUJ726a5oG5JDB2917vL+Kl3eO1N/lQ
LgewUdIO8E0dk69vubosy+25JDA2bYNuFeJnOsZovxLjhbkkwEZZXl4+0QfOvfl2Ogg4O5cFNl1dFE9N
4/B7Q+Oyj7mh/VzKZQE2UtM0j6iL+LWOidizhN3OPBiHuigvSmPwrvVjsn9pQnh1LguwGQa3Fo3fHp6M
Pcw97c1WcllgQy0tLZ2UDr7/qmMc9jJNiF/WkwNjMHhQ0GzfX/yIU8RLfRCxkdq+kzTfrugcf/3MgZ1l
+fRcHmCztU8Yq2P8h47J2cdcpTmQjdCE8IQ0vnzjdkjKP8nlAcZoWxXin3dP0n4l1eGb9UL92FwXOG51
Ub48ja09w2Otz2nvTJpKs21QIWDs0lnKb6TJqTEphN3NQvncXBY4VtvSju4N3WOs17l6cXHxoblGwKRo
r8mlHeAtHZO2b9mfDohem8sCR2XX3K4z0hj65NCYklSTGONpuUzApNlVFFWaqP81NHF7mvD2tnM7lwbu
V/vwqTR2vrV+LPU7VSjf4ff+MAU0B65NeWVd12fm0sBhpTnzosHDp7rGUV8T9lVFvDiXCJgSrmEezDea
onhMrgsM25rmyuvSODkwNG56nvZyYvi5XCNg2mgOXM3tVVH9ci4L3KtpmlN9W7Y+VYhfiEkuEzCt8n3L
fzA8yXuY/b7OZEVZlovt3ew6xknf8965ublTcpmAaac5cG3C2zQ09Vv7bVAaC/+7fmz0Og6QYVZpDlyT
GD/dPlgpl4b+2Nru5NIYuGfdmOhxmhB/VBblL+QaATNKc+DBfH1nCI/OdWHGtTewSdv874fGgIT4xfYb
wlwmYNZpDlzNbVUIz85lYUblJ2i6BDacGP/u7O3bH5zLBPSF5sDV7K+L+Nu5LMyYtJP7xbSNfzy0zfue
A+03gak8WwdVAnpHc+CaxHiJ5sCZsnK9f/+6bd3v3JZ2/r+SawT0mebAg0kfjB93v/Pp136t3X693bWN
e56v6nsBhmkOXE24Ph0EnJXrwpSp63qhDuXnu7dtn1P+U1VVP5PLBHAozYGDtD+LchvU6TN4Iqa+lqGs
XO8/YVAlgMPQHLiSsC+dNb0ml4UJVxflRWm73b1+O/Y5YXcVwvNziQDun+bANYnxkvO2bHlALg0TJsZ4
cl3ESzu3Xa8Trm+KYimXCeDIaQ48mKqIH9McOHl2zc/vaIr42a5t1vN8tCiK03OZAI6J5sCVFPFr7QNk
cl0Ys3yp6nvrtlO/s3K9f9ugSgDHSXPgILk58PxcFsYkX+93J8s1qUK8oynKF+YSAYyO5sCVhH3pgOjV
uSxsoqZpHpjq//bu7dLjFPHGND+Xc5kARk9z4JrE+JZUEl+1bpLFhYW5VPf/WLcd5N/ruj4zlwlg42gO
PCQfdXOVjdeE8MRU6xuGai9uXw2MgebA1YTrFkMoc10YsaYoX5rqfOf6uvc6e9LO/xW5RACbT3Pgam5N
B0Q/m8vCCLT3XnCQ2ZXwnWqhenIuE8D4aA5czV3OykZj19yuM1I9PzlUXwnhirIst+cyAYyf5sA1GTQH
uu/6MdoZ4+NTHb+1rq59j+v9wKTSHHhIPtI0zam5NByhNH5enGr306Fa9j1720ttuUQAE0tz4Epi/FJM
cl24b8ZNd25KdTk31whg8mkOXEm4pX1EbS4LHebn5x+WdnIf765fr3NVCOFRuUwA00Nz4Gr21kX58lwW
1qjmq8el+nxjqF4S4yVLS0sn5TIBTB/NgWuiOfAQTVE+J9XltnV16nXCvqqIF+cSAUw3zYGH5INnb9/+
4Fyavtra7uRSLe4Zqk3f88MmxvNyjQBmhiavg/liVVVFrkuvLC4uPjSt/4eG6iEhXlOWZchlApg9mgNX
c3P6wD8nl6UXqvlqZ1rv/xmqQ+/ThPJv5ufnH5TLBDC7NAeuZm97n/tclplWhfCstL4/GVr/nsf1fqCH
NAeu5kB7aSSVZFabA13v786tVVFckGsE0C+aAw8mnSG/f25u7pRcmpnQbt8mlB/oWt8+pwnxWk+PBNAc
uJoqxC/MSnNgs7BQ1yFc17WePc97Z+1AD+C4aA5czU3T/qjXdED3zLQePx5ar75nf77ev3VQJQBWaQ5c
zZ4qhJfkskyVuigvapvbOtapt2lC/FFTls/IJQKgi+bA1aw0B07FGWOM8eQ6xnd1rEe/E+OX2jGdywTA
fdEcuCYx/u2kXzNudjTz6az/c53L3+NUMX7YI6EBjp7mwIO5pt3J5rpMlHKhfFpavu8PLW/fM+s/7QTY
eJoDV/PduiiWc1kmwuB6f7x7aDn7ntvrGC/MJQLgeGgOHKQK8Y46hOflsoxN0zQPbEJ8Z9cy9jxf3RnC
o3OZABgFzYGrab9efl0qyViaAxcXFubSgchnOpar77k8xnhaLhMAo6Q5cE2K+L7NfoBMHcJT0nvfvG5Z
+h3X+wE2iebAg7m6LMvtuS4bKl/v14txSMLuKoQX5BIBsBk0B64kfCfV4om5LCN33pYtD3DA1ZVwfb1Q
PzaXCYDNpDlwJWH3RnSe75rbdUZ6/X9b/369z0eLojg9lwmAcdAcuJqV5sCRaEJ4QnrNbw+9R9+zcr1/
26BKAIyV5sBDcll7W95cmmPSPocgvc6dQ6/b9+xpivJluUQATBDNgQdz1TE2B6phV4p446TdhAmAIens
9ZXpQ1tz4KA58Am5LPfrrB07Hp7+zr90v1Z/UxXxU3Vdn5nLBMAk0xy4krC7WSifm8tyWGVZnl2F+M3u
1+hxYrxkeXn5xFwmAKaB5sDV7E9nsRfnsqyTdnIvGtxiuPPv9jV7Ul1ekUsEwLTRHHgwVSjfsbS0dFIu
TWtr+6uB9P8ODP/Znue7ZVmek2sEwBTT2Laa8sr2enb7jHoHRl0JV8QYH5nHDQCzwJ0DV/P1tKO7vuO/
9zpVKN/qej/AjNIcKB3Z24TyVXmIADCrNAfKmtzUxHhuHhoAzDrNgZJyVQjhUXlIANAjmgN7m/Du471d
MgBTzp0D+5Sw777uiQBAz2gO7EPCLSnn500OAAOaA2c615RlGfKmBoBDaQ6cvTQhvmd+fv5BeRMDwGFp
DpyJuN4PwDHQHDjVubUqigvypgSAo6M5cPrShHjtYghl3oQAcGw0B05Rivi+ubm5U/KmA4Djozlw4rM/
X+/fOthiADA6mgMnME2IP2rK8hl5GwHAxtAcOFH5SozxrLxpAGBjaQ4cf6oYP9w0zal5kwDA5tAcOLYc
aC/FpE1wwmBLAMAm0xy46bk91fvCXH4AGCvNgZuRIn6tKYrH5JoDwGTQHLihuTzGeFouNQBMFs2BI4/r
/QBMB82Bo0rYXYXwglxWAJh8mgOPN+H6eqF+bC4nAEwVzYHHkiL+c1EUp+caAsB00hx4FInxLalk2waV
A4AppznwfrOnLsqX53IBwOzQHHiYFPHGaqF6Ui4TAMwezYGHpirip+q6PjOXBwBmmubANjFesry8fGKu
CQD0Q4+bA/dWMf56LgMA9E8PmwO/W5blOXn1AaC/+tMcWF4ZY3xkXm0AYOabA2O8ZGlp6aS8ugDAGrPY
HLi3CeWr8voBAIczQ82BN6UDmnPzagEA92f6mwPLz1dVVeTVAQCO1LQ2B1Yh/PX8/PyD8moAAEdrupoD
w76qiBfnRQcAjtMUNAeGW1LOz8sLAIzKBDcHXlOWZciLCQCM2gQ2B142Nzd3Sl48AGCjTEhz4H7X+wFg
k425OfDWqqh+Pi8KALDJtqWDgDemHfKBoR30hqUJ8dqY5PcHAMalKcrnpJ3z94Z31qNO+/t+1/sBYIKk
k/LT6hD+Mu2oR/4rgXTW/+X02r+U3woAmDTtz/GaIv5F2nH/ZHhHfpS5J+Vf01n/S9LLbhu8OgAw0dpb
8TYL5XPTmfvb0o78v/MOvWtHvybtzXzKT1Qx/n5d1wv5pQCAaXXvAUFRLLXd+3WMFzZF+cKUl7X/rIri
gsWFhbn8RwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
AAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton18.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value> </value>
</data> </data>
</root> </root>

View File

@@ -19,10 +19,17 @@ Namespace API.YouTube.Controls
Friend Class MusicPlaylistsForm : Implements IDesignXMLContainer Friend Class MusicPlaylistsForm : Implements IDesignXMLContainer
#Region "Declarations" #Region "Declarations"
Private MyView As FormView Private MyView As FormView
Private ReadOnly MyFieldsChecker As FieldsChecker
Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML
Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes
Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName
Private ReadOnly MyContainer As IYouTubeMediaContainer Private ReadOnly MyContainer As IYouTubeMediaContainer
Private ReadOnly M3U8Files As List(Of SFile)
Private ReadOnly Property M3U8FilesFull As List(Of SFile)
Get
Return ListAddList(Nothing, M3U8Files, LAP.NotContainsOnly).ListAddValue(CMB_PLS.Text, LAP.NotContainsOnly)
End Get
End Property
Private Initializing As Boolean = True Private Initializing As Boolean = True
Private ReadOnly Property Current As IYouTubeMediaContainer Private ReadOnly Property Current As IYouTubeMediaContainer
Get Get
@@ -40,7 +47,9 @@ Namespace API.YouTube.Controls
#Region "Initializer" #Region "Initializer"
Friend Sub New(ByVal Container As IYouTubeMediaContainer) Friend Sub New(ByVal Container As IYouTubeMediaContainer)
InitializeComponent() InitializeComponent()
M3U8Files = New List(Of SFile)
MyContainer = Container MyContainer = Container
MyFieldsChecker = New FieldsChecker
End Sub End Sub
#End Region #End Region
#Region "Form handlers" #Region "Form handlers"
@@ -52,6 +61,9 @@ Namespace API.YouTube.Controls
End If End If
MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_OUTPUT_PATH) MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_OUTPUT_PATH)
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS,, True)
CMB_PLS.Text = MyYouTubeSettings.LatestPlaylistFile.Value
If MyYouTubeSettings.DefaultAudioBitrate > 0 Then TXT_AUDIO_BITRATE.Text = MyYouTubeSettings.DefaultAudioBitrate.Value
CMB_FORMATS.Items.AddRange(AvailableAudioFormats) CMB_FORMATS.Items.AddRange(AvailableAudioFormats)
If MyYouTubeSettings.PlaylistFormSplitterDistance > 0 Then SPLITTER_MAIN.SplitterDistancePercentageSet(MyYouTubeSettings.PlaylistFormSplitterDistance) If MyYouTubeSettings.PlaylistFormSplitterDistance > 0 Then SPLITTER_MAIN.SplitterDistancePercentageSet(MyYouTubeSettings.PlaylistFormSplitterDistance)
@@ -104,6 +116,9 @@ Namespace API.YouTube.Controls
Text = .PlaylistTitle Text = .PlaylistTitle
End If End If
MyFieldsChecker.AddControl(Of Integer)(TXT_AUDIO_BITRATE, TXT_AUDIO_BITRATE.CaptionText, True)
MyFieldsChecker.EndLoaderOperations()
UpdateSizeText() UpdateSizeText()
End With End With
RefillAddit() RefillAddit()
@@ -111,7 +126,9 @@ Namespace API.YouTube.Controls
End Sub End Sub
Private Sub MusicPlaylistsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing Private Sub MusicPlaylistsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
MyYouTubeSettings.PlaylistFormSplitterDistance.Value = SPLITTER_MAIN.SplitterDistancePercentageGet MyYouTubeSettings.PlaylistFormSplitterDistance.Value = SPLITTER_MAIN.SplitterDistancePercentageGet
MyView.DisposeIfReady() MyView.DisposeIfReady
MyFieldsChecker.DisposeIfReady
M3U8Files.Clear()
End Sub End Sub
Private Sub MusicPlaylistsForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown Private Sub MusicPlaylistsForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
Dim b As Boolean = True Dim b As Boolean = True
@@ -181,8 +198,52 @@ Namespace API.YouTube.Controls
End With End With
End Sub End Sub
Private Sub TXT_OUTPUT_PATH_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_OUTPUT_PATH.ActionOnButtonClick Private Sub TXT_OUTPUT_PATH_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles TXT_OUTPUT_PATH.ActionOnButtonClick
If Sender.DefaultButton = ADB.Open Or Sender.DefaultButton = ADB.Add Then _ Select Case e.DefaultButton
MyYouTubeSettings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT_PATH, Sender.DefaultButton = ADB.Add, MyDownloaderSettings.OutputPathAskForName) Case ADB.Open, ADB.Add
MyYouTubeSettings.DownloadLocations.ChooseNewLocation(TXT_OUTPUT_PATH, e.DefaultButton = ADB.Add, MyDownloaderSettings.OutputPathAskForName)
Case ADB.Save
If Not TXT_OUTPUT_PATH.Text.IsEmptyString Then
With MyYouTubeSettings.PlaylistsLocations
.Add(TXT_OUTPUT_PATH.Text, True)
.PopulateComboBox(TXT_OUTPUT_PATH, TXT_OUTPUT_PATH.Text)
End With
End If
End Select
End Sub
Private Sub CMB_PLS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles CMB_PLS.ActionOnButtonClick
Try
Select Case e.DefaultButton
Case ADB.Add, ADB.Open
Dim f As SFile = Nothing
If Not CMB_PLS.Text.IsEmptyString Then
f = CMB_PLS.Text
ElseIf Not TXT_OUTPUT_PATH.Text.IsEmptyString Then
f = TXT_OUTPUT_PATH.Text
End If
f = SFile.SelectFiles(f, False, "Select a playlist...", "Playlists|*.m3u;*.m3u8|All files|*.*", EDP.ReturnValue).FirstOrDefault
If Not f.IsEmptyString Then
If Sender.DefaultButton = ADB.Add Then
MyYouTubeSettings.PlaylistsLocations.Add(f.ToString, True, True)
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS, f, True)
End If
CMB_PLS.Text = f
End If
Case ADB.List
Dim result As Boolean = False
Dim selectedFiles As IEnumerable(Of SFile) = MyYouTubeSettings.PlaylistsLocations.ChooseNewPlaylistArray(CMB_PLS, result)
If result Then M3U8Files.ListAddList(selectedFiles, LAP.NotContainsOnly, LAP.ClearBeforeAdd)
Case ADB.Save
With MyYouTubeSettings.PlaylistsLocations
If Not CMB_PLS.Text.IsEmptyString AndAlso .IndexOf(CMB_PLS.Text,, True) = -1 Then
.Add(CMB_PLS.Text, True, True)
.PopulateComboBox(CMB_PLS, CMB_PLS.Text, True)
End If
End With
Case ADB.Clear : M3U8Files.Clear()
End Select
Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.YouTube.Controls.MusicPlaylistsForm.SelectPlaylist]")
End Try
End Sub End Sub
#End Region #End Region
#Region "Lists' handlers" #Region "Lists' handlers"
@@ -268,7 +329,7 @@ Namespace API.YouTube.Controls
Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
If TXT_OUTPUT_PATH.IsEmptyString Then If TXT_OUTPUT_PATH.IsEmptyString Then
MsgBoxE({"The output path cannot be null.", "Download music"}, vbCritical) MsgBoxE({"The output path cannot be null.", "Download music"}, vbCritical)
Else ElseIf MyFieldsChecker.AllParamsOK Then
With DirectCast(MyContainer, YouTubeMediaContainerBase) With DirectCast(MyContainer, YouTubeMediaContainerBase)
.OutputSubtitlesFormat = IIf(CH_DOWN_LYRICS.Checked, "LRC", String.Empty) .OutputSubtitlesFormat = IIf(CH_DOWN_LYRICS.Checked, "LRC", String.Empty)
If Not TXT_SUBS.Checked Then .PostProcessing_OutputSubtitlesFormats.Clear() If Not TXT_SUBS.Checked Then .PostProcessing_OutputSubtitlesFormats.Clear()
@@ -276,8 +337,12 @@ Namespace API.YouTube.Controls
If Not TXT_FORMATS_ADDIT.Checked Then .PostProcessing_OutputAudioFormats.Clear() If Not TXT_FORMATS_ADDIT.Checked Then .PostProcessing_OutputAudioFormats.Clear()
.AbsolutePath = TXT_OUTPUT_PATH.Checked .AbsolutePath = TXT_OUTPUT_PATH.Checked
.File = TXT_OUTPUT_PATH.Text.CSFileP .File = TXT_OUTPUT_PATH.Text.CSFileP
.M3U8_PlaylistFiles = M3U8FilesFull
.OutputAudioBitrate = AConvert(Of Integer)(TXT_AUDIO_BITRATE.Text, -1)
If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = .File If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = .File
If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(.File, False) If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(.File, False)
If Not CMB_PLS.Text.IsEmptyString Then MyYouTubeSettings.PlaylistsLocations.Add(CMB_PLS.Text, False, True)
MyYouTubeSettings.LatestPlaylistFile.Value = CMB_PLS.Text
End With End With
DialogResult = DialogResult.OK DialogResult = DialogResult.OK
Close() Close()

View File

@@ -27,6 +27,7 @@ Namespace API.YouTube.Controls
Friend Sub New(ByVal m As MediaObject, Optional ByVal SelectedAudio As MediaObject = Nothing) Friend Sub New(ByVal m As MediaObject, Optional ByVal SelectedAudio As MediaObject = Nothing)
Me.New Me.New
Const d$ = " " & ChrW(183) & " " Const d$ = " " & ChrW(183) & " "
Const DRC$ = Objects.YouTubeMediaContainerBase.DRC
MyMedia = m MyMedia = m
If m.Type = Plugin.UserMediaTypes.Audio Then If m.Type = Plugin.UserMediaTypes.Audio Then
If m.Bitrate >= 320 Then If m.Bitrate >= 320 Then
@@ -38,6 +39,7 @@ Namespace API.YouTube.Controls
End If End If
LBL_DEFINITION.Text = $"{m.Bitrate}k" LBL_DEFINITION.Text = $"{m.Bitrate}k"
LBL_CODECS.Text = $"{m.Extension} {d} {m.Codec} {d} {m.Bitrate}k" LBL_CODECS.Text = $"{m.Extension} {d} {m.Codec} {d} {m.Bitrate}k"
If Not m.ID.IsEmptyString AndAlso m.ID.StringToLower.Contains(DRC) Then LBL_CODECS.Text &= $" {d} DRC"
Else Else
If m.Height >= 1440 Then If m.Height >= 1440 Then
LBL_DEFINITION_INFO.Text = "Ultra High Definition" LBL_DEFINITION_INFO.Text = "Ultra High Definition"
@@ -53,7 +55,14 @@ Namespace API.YouTube.Controls
LBL_DEFINITION.Text = $"{m.Height}p" LBL_DEFINITION.Text = $"{m.Height}p"
LBL_CODECS.Text = $"{m.Extension.StringToUpper}{d}{m.Codec.StringToUpper}{d}{m.FPS}fps{d}{m.Bitrate}k" LBL_CODECS.Text = $"{m.Extension.StringToUpper}{d}{m.Codec.StringToUpper}{d}{m.FPS}fps{d}{m.Bitrate}k"
If Not m.Protocol.IsEmptyString Then LBL_CODECS.Text &= $" ({m.Protocol})" If Not m.Protocol.IsEmptyString Then LBL_CODECS.Text &= $" ({m.Protocol})"
If Not m.ID.IsEmptyString AndAlso m.ID.StringToLower.Contains(DRC) Then LBL_CODECS.Text &= $"{d}DRC"
If Not SelectedAudio.ID.IsEmptyString Then LBL_CODECS.Text &= $" / {SelectedAudio.Extension}{d}{SelectedAudio.Codec}{d}{SelectedAudio.Bitrate}k" If Not SelectedAudio.ID.IsEmptyString Then LBL_CODECS.Text &= $" / {SelectedAudio.Extension}{d}{SelectedAudio.Codec}{d}{SelectedAudio.Bitrate}k"
If Not SelectedAudio.ID.IsEmptyString AndAlso SelectedAudio.ID.StringToLower.Contains(DRC) Then LBL_CODECS.Text &= $"{d}DRC"
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 End If
Dim sv% = m.Size / 1024 Dim sv% = m.Size / 1024

View File

@@ -31,9 +31,15 @@ Namespace API.YouTube.Controls
Dim TP_DESTINATION As System.Windows.Forms.TableLayoutPanel Dim TP_DESTINATION As System.Windows.Forms.TableLayoutPanel
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(VideoOptionsForm)) Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(VideoOptionsForm))
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Dim ListColumn1 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn() Dim ListColumn2 As PersonalUtilities.Forms.Controls.Base.ListColumn = New PersonalUtilities.Forms.Controls.Base.ListColumn()
Dim TP_OK_CANCEL As System.Windows.Forms.TableLayoutPanel Dim TP_OK_CANCEL As System.Windows.Forms.TableLayoutPanel
Dim TP_PLS As System.Windows.Forms.TableLayoutPanel
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim LB_SEP_1 As System.Windows.Forms.Label Dim LB_SEP_1 As System.Windows.Forms.Label
Dim LB_SEP_2 As System.Windows.Forms.Label Dim LB_SEP_2 As System.Windows.Forms.Label
Dim TP_WHAT As System.Windows.Forms.TableLayoutPanel Dim TP_WHAT As System.Windows.Forms.TableLayoutPanel
@@ -41,16 +47,18 @@ Namespace API.YouTube.Controls
Dim LBL_FORMAT As System.Windows.Forms.Label Dim LBL_FORMAT As System.Windows.Forms.Label
Dim LBL_SUBS_FORMAT As System.Windows.Forms.Label Dim LBL_SUBS_FORMAT As System.Windows.Forms.Label
Dim TT_MAIN As System.Windows.Forms.ToolTip Dim TT_MAIN As System.Windows.Forms.ToolTip
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim TP_FPS_BITRATE As System.Windows.Forms.TableLayoutPanel
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton7 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton8 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton9 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton10 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton11 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton11 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton12 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton13 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton14 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton15 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton16 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton17 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Me.ICON_VIDEO = New System.Windows.Forms.PictureBox() Me.ICON_VIDEO = New System.Windows.Forms.PictureBox()
Me.LBL_TITLE = New System.Windows.Forms.Label() Me.LBL_TITLE = New System.Windows.Forms.Label()
Me.TP_HEADER_INFO_2 = New System.Windows.Forms.TableLayoutPanel() Me.TP_HEADER_INFO_2 = New System.Windows.Forms.TableLayoutPanel()
@@ -60,9 +68,13 @@ Namespace API.YouTube.Controls
Me.BTT_BROWSE = New System.Windows.Forms.Button() Me.BTT_BROWSE = New System.Windows.Forms.Button()
Me.BTT_DOWN = New System.Windows.Forms.Button() Me.BTT_DOWN = New System.Windows.Forms.Button()
Me.BTT_CANCEL = New System.Windows.Forms.Button() Me.BTT_CANCEL = New System.Windows.Forms.Button()
Me.CMB_PLS = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
Me.BTT_PLS_BROWSE = New System.Windows.Forms.Button()
Me.OPT_VIDEO = New System.Windows.Forms.RadioButton() Me.OPT_VIDEO = New System.Windows.Forms.RadioButton()
Me.OPT_AUDIO = New System.Windows.Forms.RadioButton() Me.OPT_AUDIO = New System.Windows.Forms.RadioButton()
Me.LBL_AUDIO_CODEC = New System.Windows.Forms.Label() Me.LBL_AUDIO_CODEC = New System.Windows.Forms.Label()
Me.TXT_FPS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_AUDIO_BITRATE = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TP_HEADER_BASE = New System.Windows.Forms.TableLayoutPanel() Me.TP_HEADER_BASE = New System.Windows.Forms.TableLayoutPanel()
Me.TP_SUBS = New System.Windows.Forms.TableLayoutPanel() Me.TP_SUBS = New System.Windows.Forms.TableLayoutPanel()
Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_SUBS = New PersonalUtilities.Forms.Controls.TextBoxExtended()
@@ -72,7 +84,6 @@ Namespace API.YouTube.Controls
Me.CMB_FORMAT = New System.Windows.Forms.ComboBox() Me.CMB_FORMAT = New System.Windows.Forms.ComboBox()
Me.CMB_AUDIO_CODEC = New System.Windows.Forms.ComboBox() Me.CMB_AUDIO_CODEC = New System.Windows.Forms.ComboBox()
Me.NUM_RES = New System.Windows.Forms.NumericUpDown() 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.TP_CONTROLS = New System.Windows.Forms.TableLayoutPanel()
Me.TXT_SUBS_ADDIT = New PersonalUtilities.Forms.Controls.TextBoxExtended() Me.TXT_SUBS_ADDIT = New PersonalUtilities.Forms.Controls.TextBoxExtended()
Me.TXT_EXTRA_AUDIO_FORMATS = 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_FOOTER = New System.Windows.Forms.TableLayoutPanel()
TP_DESTINATION = New System.Windows.Forms.TableLayoutPanel() TP_DESTINATION = New System.Windows.Forms.TableLayoutPanel()
TP_OK_CANCEL = New System.Windows.Forms.TableLayoutPanel() TP_OK_CANCEL = New System.Windows.Forms.TableLayoutPanel()
TP_PLS = New System.Windows.Forms.TableLayoutPanel()
LB_SEP_1 = New System.Windows.Forms.Label() LB_SEP_1 = New System.Windows.Forms.Label()
LB_SEP_2 = New System.Windows.Forms.Label() LB_SEP_2 = New System.Windows.Forms.Label()
TP_WHAT = New System.Windows.Forms.TableLayoutPanel() TP_WHAT = New System.Windows.Forms.TableLayoutPanel()
@@ -90,6 +102,7 @@ Namespace API.YouTube.Controls
LBL_FORMAT = New System.Windows.Forms.Label() LBL_FORMAT = New System.Windows.Forms.Label()
LBL_SUBS_FORMAT = New System.Windows.Forms.Label() LBL_SUBS_FORMAT = New System.Windows.Forms.Label()
TT_MAIN = New System.Windows.Forms.ToolTip(Me.components) TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
TP_FPS_BITRATE = New System.Windows.Forms.TableLayoutPanel()
TP_HEADER.SuspendLayout() TP_HEADER.SuspendLayout()
CType(Me.ICON_VIDEO, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.ICON_VIDEO, System.ComponentModel.ISupportInitialize).BeginInit()
TP_HEADER_INFO.SuspendLayout() TP_HEADER_INFO.SuspendLayout()
@@ -100,14 +113,18 @@ Namespace API.YouTube.Controls
TP_DESTINATION.SuspendLayout() TP_DESTINATION.SuspendLayout()
CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).BeginInit()
TP_OK_CANCEL.SuspendLayout() TP_OK_CANCEL.SuspendLayout()
TP_PLS.SuspendLayout()
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).BeginInit()
TP_WHAT.SuspendLayout() TP_WHAT.SuspendLayout()
TP_FPS_BITRATE.SuspendLayout()
CType(Me.TXT_FPS, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_AUDIO_BITRATE, System.ComponentModel.ISupportInitialize).BeginInit()
Me.TP_HEADER_BASE.SuspendLayout() Me.TP_HEADER_BASE.SuspendLayout()
Me.TP_SUBS.SuspendLayout() Me.TP_SUBS.SuspendLayout()
CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_SUBS, System.ComponentModel.ISupportInitialize).BeginInit()
Me.TP_MAIN.SuspendLayout() Me.TP_MAIN.SuspendLayout()
Me.TP_OPTIONS.SuspendLayout() Me.TP_OPTIONS.SuspendLayout()
CType(Me.NUM_RES, System.ComponentModel.ISupportInitialize).BeginInit() 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_SUBS_ADDIT, System.ComponentModel.ISupportInitialize).BeginInit()
CType(Me.TXT_EXTRA_AUDIO_FORMATS, System.ComponentModel.ISupportInitialize).BeginInit() CType(Me.TXT_EXTRA_AUDIO_FORMATS, System.ComponentModel.ISupportInitialize).BeginInit()
Me.SuspendLayout() Me.SuspendLayout()
@@ -126,7 +143,7 @@ Namespace API.YouTube.Controls
TP_HEADER.Name = "TP_HEADER" TP_HEADER.Name = "TP_HEADER"
TP_HEADER.RowCount = 1 TP_HEADER.RowCount = 1
TP_HEADER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) 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 TP_HEADER.TabIndex = 0
' '
'ICON_VIDEO 'ICON_VIDEO
@@ -155,7 +172,7 @@ Namespace API.YouTube.Controls
TP_HEADER_INFO.RowCount = 2 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.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 TP_HEADER_INFO.TabIndex = 0
' '
'LBL_TITLE '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.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.Location = New System.Drawing.Point(3, 0)
Me.LBL_TITLE.Name = "LBL_TITLE" 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.TabIndex = 0
Me.LBL_TITLE.Text = "Video title" Me.LBL_TITLE.Text = "Video title"
Me.LBL_TITLE.TextAlign = System.Drawing.ContentAlignment.MiddleLeft 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.Name = "TP_HEADER_INFO_2"
Me.TP_HEADER_INFO_2.RowCount = 1 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.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 Me.TP_HEADER_INFO_2.TabIndex = 1
' '
'ICON_CLOCK '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.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.Location = New System.Drawing.Point(115, 0)
Me.LBL_URL.Name = "LBL_URL" 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.TabIndex = 1
Me.LBL_URL.TabStop = True Me.LBL_URL.TabStop = True
Me.LBL_URL.Text = "https://www.youtube.com/watch?v=abcdefghijk" 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.ColumnCount = 1
TP_FOOTER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_FOOTER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_FOOTER.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!)) TP_FOOTER.Controls.Add(TP_DESTINATION, 0, 1)
TP_FOOTER.Controls.Add(TP_DESTINATION, 0, 0) TP_FOOTER.Controls.Add(TP_OK_CANCEL, 0, 2)
TP_FOOTER.Controls.Add(TP_OK_CANCEL, 0, 1) TP_FOOTER.Controls.Add(TP_PLS, 0, 0)
TP_FOOTER.Dock = System.Windows.Forms.DockStyle.Fill TP_FOOTER.Dock = System.Windows.Forms.DockStyle.Fill
TP_FOOTER.Location = New System.Drawing.Point(6, 215) TP_FOOTER.Location = New System.Drawing.Point(6, 243)
TP_FOOTER.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3) TP_FOOTER.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
TP_FOOTER.Name = "TP_FOOTER" TP_FOOTER.Name = "TP_FOOTER"
TP_FOOTER.RowCount = 2 TP_FOOTER.RowCount = 3
TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 50.0!)) TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_FOOTER.Size = New System.Drawing.Size(709, 52) TP_FOOTER.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_FOOTER.Size = New System.Drawing.Size(589, 81)
TP_FOOTER.TabIndex = 5 TP_FOOTER.TabIndex = 5
' '
'TP_DESTINATION 'TP_DESTINATION
@@ -264,20 +282,25 @@ Namespace API.YouTube.Controls
TP_DESTINATION.Controls.Add(Me.TXT_FILE, 0, 0) TP_DESTINATION.Controls.Add(Me.TXT_FILE, 0, 0)
TP_DESTINATION.Controls.Add(Me.BTT_BROWSE, 1, 0) TP_DESTINATION.Controls.Add(Me.BTT_BROWSE, 1, 0)
TP_DESTINATION.Dock = System.Windows.Forms.DockStyle.Fill TP_DESTINATION.Dock = System.Windows.Forms.DockStyle.Fill
TP_DESTINATION.Location = New System.Drawing.Point(0, 0) TP_DESTINATION.Location = New System.Drawing.Point(0, 27)
TP_DESTINATION.Margin = New System.Windows.Forms.Padding(0) TP_DESTINATION.Margin = New System.Windows.Forms.Padding(0)
TP_DESTINATION.Name = "TP_DESTINATION" TP_DESTINATION.Name = "TP_DESTINATION"
TP_DESTINATION.RowCount = 1 TP_DESTINATION.RowCount = 1
TP_DESTINATION.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_DESTINATION.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_DESTINATION.Size = New System.Drawing.Size(709, 26) TP_DESTINATION.Size = New System.Drawing.Size(589, 27)
TP_DESTINATION.TabIndex = 0 TP_DESTINATION.TabIndex = 0
' '
'TXT_FILE 'TXT_FILE
' '
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image) ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
ActionButton1.Name = "ArrowDown" ActionButton1.Name = "Save"
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
ActionButton1.ToolTipText = "Save destination"
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
ActionButton2.Name = "ArrowDown"
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
Me.TXT_FILE.Buttons.Add(ActionButton1) Me.TXT_FILE.Buttons.Add(ActionButton1)
Me.TXT_FILE.Buttons.Add(ActionButton2)
Me.TXT_FILE.ChangeControlsEnableOnCheckedChange = False Me.TXT_FILE.ChangeControlsEnableOnCheckedChange = False
ListColumn1.Name = "COL_NAME" ListColumn1.Name = "COL_NAME"
ListColumn1.Text = "Name" ListColumn1.Text = "Name"
@@ -293,17 +316,17 @@ Namespace API.YouTube.Controls
Me.TXT_FILE.Location = New System.Drawing.Point(1, 1) Me.TXT_FILE.Location = New System.Drawing.Point(1, 1)
Me.TXT_FILE.Margin = New System.Windows.Forms.Padding(1) Me.TXT_FILE.Margin = New System.Windows.Forms.Padding(1)
Me.TXT_FILE.Name = "TXT_FILE" 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.TabIndex = 0
Me.TXT_FILE.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle Me.TXT_FILE.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
' '
'BTT_BROWSE 'BTT_BROWSE
' '
Me.BTT_BROWSE.Dock = System.Windows.Forms.DockStyle.Fill 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.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
Me.BTT_BROWSE.Name = "BTT_BROWSE" Me.BTT_BROWSE.Name = "BTT_BROWSE"
Me.BTT_BROWSE.Size = New System.Drawing.Size(74, 22) Me.BTT_BROWSE.Size = New System.Drawing.Size(74, 23)
Me.BTT_BROWSE.TabIndex = 1 Me.BTT_BROWSE.TabIndex = 1
Me.BTT_BROWSE.Text = "Browse" Me.BTT_BROWSE.Text = "Browse"
TT_MAIN.SetToolTip(Me.BTT_BROWSE, "Choose an output file (Right click for add a new location to the list)") TT_MAIN.SetToolTip(Me.BTT_BROWSE, "Choose an output file (Right click for add a new location to the list)")
@@ -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_DOWN, 1, 0)
TP_OK_CANCEL.Controls.Add(Me.BTT_CANCEL, 2, 0) TP_OK_CANCEL.Controls.Add(Me.BTT_CANCEL, 2, 0)
TP_OK_CANCEL.Dock = System.Windows.Forms.DockStyle.Fill TP_OK_CANCEL.Dock = System.Windows.Forms.DockStyle.Fill
TP_OK_CANCEL.Location = New System.Drawing.Point(0, 26) TP_OK_CANCEL.Location = New System.Drawing.Point(0, 54)
TP_OK_CANCEL.Margin = New System.Windows.Forms.Padding(0) TP_OK_CANCEL.Margin = New System.Windows.Forms.Padding(0)
TP_OK_CANCEL.Name = "TP_OK_CANCEL" TP_OK_CANCEL.Name = "TP_OK_CANCEL"
TP_OK_CANCEL.RowCount = 1 TP_OK_CANCEL.RowCount = 1
TP_OK_CANCEL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) TP_OK_CANCEL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_OK_CANCEL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 26.0!)) TP_OK_CANCEL.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27.0!))
TP_OK_CANCEL.Size = New System.Drawing.Size(709, 26) TP_OK_CANCEL.Size = New System.Drawing.Size(589, 27)
TP_OK_CANCEL.TabIndex = 1 TP_OK_CANCEL.TabIndex = 1
' '
'BTT_DOWN 'BTT_DOWN
' '
Me.BTT_DOWN.Dock = System.Windows.Forms.DockStyle.Fill 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.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
Me.BTT_DOWN.Name = "BTT_DOWN" Me.BTT_DOWN.Name = "BTT_DOWN"
Me.BTT_DOWN.Size = New System.Drawing.Size(74, 22) Me.BTT_DOWN.Size = New System.Drawing.Size(74, 23)
Me.BTT_DOWN.TabIndex = 0 Me.BTT_DOWN.TabIndex = 0
Me.BTT_DOWN.Text = "Download" Me.BTT_DOWN.Text = "Download"
Me.BTT_DOWN.UseVisualStyleBackColor = True Me.BTT_DOWN.UseVisualStyleBackColor = True
@@ -342,32 +365,95 @@ Namespace API.YouTube.Controls
' '
Me.BTT_CANCEL.DialogResult = System.Windows.Forms.DialogResult.Cancel Me.BTT_CANCEL.DialogResult = System.Windows.Forms.DialogResult.Cancel
Me.BTT_CANCEL.Dock = System.Windows.Forms.DockStyle.Fill 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.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
Me.BTT_CANCEL.Name = "BTT_CANCEL" Me.BTT_CANCEL.Name = "BTT_CANCEL"
Me.BTT_CANCEL.Size = New System.Drawing.Size(74, 22) Me.BTT_CANCEL.Size = New System.Drawing.Size(74, 23)
Me.BTT_CANCEL.TabIndex = 1 Me.BTT_CANCEL.TabIndex = 1
Me.BTT_CANCEL.Text = "Cancel" Me.BTT_CANCEL.Text = "Cancel"
Me.BTT_CANCEL.UseVisualStyleBackColor = True Me.BTT_CANCEL.UseVisualStyleBackColor = True
' '
'TP_PLS
'
TP_PLS.ColumnCount = 2
TP_PLS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_PLS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
TP_PLS.Controls.Add(Me.CMB_PLS, 0, 0)
TP_PLS.Controls.Add(Me.BTT_PLS_BROWSE, 1, 0)
TP_PLS.Dock = System.Windows.Forms.DockStyle.Fill
TP_PLS.Location = New System.Drawing.Point(0, 0)
TP_PLS.Margin = New System.Windows.Forms.Padding(0)
TP_PLS.Name = "TP_PLS"
TP_PLS.RowCount = 1
TP_PLS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_PLS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 27.0!))
TP_PLS.Size = New System.Drawing.Size(589, 27)
TP_PLS.TabIndex = 2
'
'CMB_PLS
'
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
ActionButton3.Name = "Save"
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Save
ActionButton3.ToolTipText = "Save playlist"
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
ActionButton4.Name = "List"
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.List
ActionButton4.ToolTipText = "Select multiple playlists"
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
ActionButton5.Name = "Clear"
ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
ActionButton6.Name = "ArrowDown"
ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
Me.CMB_PLS.Buttons.Add(ActionButton3)
Me.CMB_PLS.Buttons.Add(ActionButton4)
Me.CMB_PLS.Buttons.Add(ActionButton5)
Me.CMB_PLS.Buttons.Add(ActionButton6)
Me.CMB_PLS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.Label
Me.CMB_PLS.CaptionText = "Playlist"
Me.CMB_PLS.CaptionToolTipEnabled = True
Me.CMB_PLS.CaptionToolTipText = "Add downloaded item(s) to playlist"
Me.CMB_PLS.CaptionVisible = True
Me.CMB_PLS.CaptionWidth = 50.0R
Me.CMB_PLS.Dock = System.Windows.Forms.DockStyle.Fill
Me.CMB_PLS.Location = New System.Drawing.Point(1, 1)
Me.CMB_PLS.Margin = New System.Windows.Forms.Padding(1)
Me.CMB_PLS.Name = "CMB_PLS"
Me.CMB_PLS.Size = New System.Drawing.Size(507, 22)
Me.CMB_PLS.TabIndex = 0
Me.CMB_PLS.TextBoxBorderStyle = System.Windows.Forms.BorderStyle.FixedSingle
'
'BTT_PLS_BROWSE
'
Me.BTT_PLS_BROWSE.Dock = System.Windows.Forms.DockStyle.Fill
Me.BTT_PLS_BROWSE.Location = New System.Drawing.Point(512, 2)
Me.BTT_PLS_BROWSE.Margin = New System.Windows.Forms.Padding(3, 2, 3, 2)
Me.BTT_PLS_BROWSE.Name = "BTT_PLS_BROWSE"
Me.BTT_PLS_BROWSE.Size = New System.Drawing.Size(74, 23)
Me.BTT_PLS_BROWSE.TabIndex = 1
Me.BTT_PLS_BROWSE.Text = "Browse"
TT_MAIN.SetToolTip(Me.BTT_PLS_BROWSE, "Choose an output file (Right click for add a new location to the list)")
Me.BTT_PLS_BROWSE.UseVisualStyleBackColor = True
'
'LB_SEP_1 'LB_SEP_1
' '
LB_SEP_1.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles) LB_SEP_1.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
LB_SEP_1.BackColor = System.Drawing.SystemColors.ControlDark LB_SEP_1.BackColor = System.Drawing.SystemColors.ControlDark
LB_SEP_1.Location = New System.Drawing.Point(6, 179) LB_SEP_1.Location = New System.Drawing.Point(6, 207)
LB_SEP_1.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0) LB_SEP_1.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
LB_SEP_1.Name = "LB_SEP_1" LB_SEP_1.Name = "LB_SEP_1"
LB_SEP_1.Size = New System.Drawing.Size(709, 1) LB_SEP_1.Size = New System.Drawing.Size(589, 1)
LB_SEP_1.TabIndex = 3 LB_SEP_1.TabIndex = 3
' '
'LB_SEP_2 '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.Anchor = CType((System.Windows.Forms.AnchorStyles.Left Or System.Windows.Forms.AnchorStyles.Right), System.Windows.Forms.AnchorStyles)
LB_SEP_2.BackColor = System.Drawing.SystemColors.ControlDark LB_SEP_2.BackColor = System.Drawing.SystemColors.ControlDark
LB_SEP_2.Location = New System.Drawing.Point(6, 209) LB_SEP_2.Location = New System.Drawing.Point(6, 237)
LB_SEP_2.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0) LB_SEP_2.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
LB_SEP_2.Name = "LB_SEP_2" LB_SEP_2.Name = "LB_SEP_2"
LB_SEP_2.Size = New System.Drawing.Size(709, 1) LB_SEP_2.Size = New System.Drawing.Size(589, 1)
LB_SEP_2.TabIndex = 5 LB_SEP_2.TabIndex = 5
' '
'TP_WHAT 'TP_WHAT
@@ -439,7 +525,7 @@ Namespace API.YouTube.Controls
' '
LBL_SUBS_FORMAT.AutoSize = True LBL_SUBS_FORMAT.AutoSize = True
LBL_SUBS_FORMAT.Dock = System.Windows.Forms.DockStyle.Fill 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.Name = "LBL_SUBS_FORMAT"
LBL_SUBS_FORMAT.Size = New System.Drawing.Size(74, 28) LBL_SUBS_FORMAT.Size = New System.Drawing.Size(74, 28)
LBL_SUBS_FORMAT.TabIndex = 2 LBL_SUBS_FORMAT.TabIndex = 2
@@ -451,7 +537,7 @@ Namespace API.YouTube.Controls
' '
Me.LBL_AUDIO_CODEC.AutoSize = True Me.LBL_AUDIO_CODEC.AutoSize = True
Me.LBL_AUDIO_CODEC.Dock = System.Windows.Forms.DockStyle.Fill 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.Name = "LBL_AUDIO_CODEC"
Me.LBL_AUDIO_CODEC.Size = New System.Drawing.Size(74, 28) Me.LBL_AUDIO_CODEC.Size = New System.Drawing.Size(74, 28)
Me.LBL_AUDIO_CODEC.TabIndex = 5 Me.LBL_AUDIO_CODEC.TabIndex = 5
@@ -459,6 +545,59 @@ Namespace API.YouTube.Controls
Me.LBL_AUDIO_CODEC.TextAlign = System.Drawing.ContentAlignment.MiddleRight Me.LBL_AUDIO_CODEC.TextAlign = System.Drawing.ContentAlignment.MiddleRight
TT_MAIN.SetToolTip(Me.LBL_AUDIO_CODEC, "Output Audio Codec") TT_MAIN.SetToolTip(Me.LBL_AUDIO_CODEC, "Output Audio Codec")
' '
'TP_FPS_BITRATE
'
TP_FPS_BITRATE.ColumnCount = 2
TP_FPS_BITRATE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
TP_FPS_BITRATE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 50.0!))
TP_FPS_BITRATE.Controls.Add(Me.TXT_FPS, 0, 0)
TP_FPS_BITRATE.Controls.Add(Me.TXT_AUDIO_BITRATE, 1, 0)
TP_FPS_BITRATE.Dock = System.Windows.Forms.DockStyle.Fill
TP_FPS_BITRATE.Location = New System.Drawing.Point(6, 93)
TP_FPS_BITRATE.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
TP_FPS_BITRATE.Name = "TP_FPS_BITRATE"
TP_FPS_BITRATE.RowCount = 1
TP_FPS_BITRATE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_FPS_BITRATE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
TP_FPS_BITRATE.Size = New System.Drawing.Size(589, 28)
TP_FPS_BITRATE.TabIndex = 6
'
'TXT_FPS
'
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image)
ActionButton7.Name = "Clear"
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_FPS.Buttons.Add(ActionButton7)
Me.TXT_FPS.CaptionText = "Video FPS"
Me.TXT_FPS.CaptionToolTipEnabled = True
Me.TXT_FPS.CaptionToolTipText = "Set the video FPS by setting the FPS value in this field. Leave blank so as not t" &
"o change"
Me.TXT_FPS.CaptionWidth = 60.0R
Me.TXT_FPS.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_FPS.Location = New System.Drawing.Point(3, 2)
Me.TXT_FPS.Margin = New System.Windows.Forms.Padding(3, 2, 3, 3)
Me.TXT_FPS.Name = "TXT_FPS"
Me.TXT_FPS.Size = New System.Drawing.Size(288, 22)
Me.TXT_FPS.TabIndex = 0
Me.TXT_FPS.TextBoxWidthMinimal = 30
'
'TXT_AUDIO_BITRATE
'
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image)
ActionButton8.Name = "Clear"
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
Me.TXT_AUDIO_BITRATE.Buttons.Add(ActionButton8)
Me.TXT_AUDIO_BITRATE.CaptionText = "Audio bitrate"
Me.TXT_AUDIO_BITRATE.CaptionToolTipEnabled = True
Me.TXT_AUDIO_BITRATE.CaptionToolTipText = "Set the video FPS if you want to change it during download. Leave blank so as not" &
" to change."
Me.TXT_AUDIO_BITRATE.CaptionWidth = 75.0R
Me.TXT_AUDIO_BITRATE.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_AUDIO_BITRATE.Location = New System.Drawing.Point(297, 3)
Me.TXT_AUDIO_BITRATE.Name = "TXT_AUDIO_BITRATE"
Me.TXT_AUDIO_BITRATE.Size = New System.Drawing.Size(289, 22)
Me.TXT_AUDIO_BITRATE.TabIndex = 1
'
'TP_HEADER_BASE 'TP_HEADER_BASE
' '
Me.TP_HEADER_BASE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] Me.TP_HEADER_BASE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
@@ -473,8 +612,8 @@ Namespace API.YouTube.Controls
Me.TP_HEADER_BASE.RowCount = 1 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.Percent, 100.0!))
Me.TP_HEADER_BASE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 64.0!)) Me.TP_HEADER_BASE.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 64.0!))
Me.TP_HEADER_BASE.Size = New System.Drawing.Size(721, 65) Me.TP_HEADER_BASE.Size = New System.Drawing.Size(601, 65)
Me.TP_HEADER_BASE.TabIndex = 6 Me.TP_HEADER_BASE.TabIndex = 7
' '
'TP_SUBS 'TP_SUBS
' '
@@ -486,31 +625,31 @@ Namespace API.YouTube.Controls
Me.TP_SUBS.Controls.Add(LBL_SUBS_FORMAT, 1, 0) Me.TP_SUBS.Controls.Add(LBL_SUBS_FORMAT, 1, 0)
Me.TP_SUBS.Controls.Add(Me.CMB_SUBS_FORMAT, 2, 0) Me.TP_SUBS.Controls.Add(Me.CMB_SUBS_FORMAT, 2, 0)
Me.TP_SUBS.Dock = System.Windows.Forms.DockStyle.Fill Me.TP_SUBS.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_SUBS.Location = New System.Drawing.Point(6, 93) Me.TP_SUBS.Location = New System.Drawing.Point(6, 121)
Me.TP_SUBS.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0) Me.TP_SUBS.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
Me.TP_SUBS.Name = "TP_SUBS" Me.TP_SUBS.Name = "TP_SUBS"
Me.TP_SUBS.RowCount = 1 Me.TP_SUBS.RowCount = 1
Me.TP_SUBS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) 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 Me.TP_SUBS.TabIndex = 2
' '
'TXT_SUBS 'TXT_SUBS
' '
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image) ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image)
ActionButton2.Name = "Open" ActionButton9.Name = "Open"
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
ActionButton2.ToolTipText = "Choose subtitles" ActionButton9.ToolTipText = "Choose subtitles"
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image) ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image)
ActionButton3.Name = "Refresh" ActionButton10.Name = "Refresh"
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
ActionButton3.ToolTipText = "Reset subtitles to initial selected" ActionButton10.ToolTipText = "Reset subtitles to initial selected"
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image) ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image)
ActionButton4.Name = "Clear" ActionButton11.Name = "Clear"
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear ActionButton11.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
ActionButton4.ToolTipText = "Clear subtitles selection (don't download subtitles)" ActionButton11.ToolTipText = "Clear subtitles selection (don't download subtitles)"
Me.TXT_SUBS.Buttons.Add(ActionButton2) Me.TXT_SUBS.Buttons.Add(ActionButton9)
Me.TXT_SUBS.Buttons.Add(ActionButton3) Me.TXT_SUBS.Buttons.Add(ActionButton10)
Me.TXT_SUBS.Buttons.Add(ActionButton4) Me.TXT_SUBS.Buttons.Add(ActionButton11)
Me.TXT_SUBS.CaptionText = "Subtitles" Me.TXT_SUBS.CaptionText = "Subtitles"
Me.TXT_SUBS.CaptionToolTipEnabled = True Me.TXT_SUBS.CaptionToolTipEnabled = True
Me.TXT_SUBS.CaptionToolTipText = "The selected subtitles will also be downloaded" Me.TXT_SUBS.CaptionToolTipText = "The selected subtitles will also be downloaded"
@@ -519,7 +658,7 @@ Namespace API.YouTube.Controls
Me.TXT_SUBS.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_SUBS.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_SUBS.Location = New System.Drawing.Point(3, 3) Me.TXT_SUBS.Location = New System.Drawing.Point(3, 3)
Me.TXT_SUBS.Name = "TXT_SUBS" 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.TabIndex = 0
Me.TXT_SUBS.TextBoxReadOnly = True 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.Dock = System.Windows.Forms.DockStyle.Fill
Me.CMB_SUBS_FORMAT.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList Me.CMB_SUBS_FORMAT.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
Me.CMB_SUBS_FORMAT.FormattingEnabled = True 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.Name = "CMB_SUBS_FORMAT"
Me.CMB_SUBS_FORMAT.Size = New System.Drawing.Size(74, 21) Me.CMB_SUBS_FORMAT.Size = New System.Drawing.Size(74, 21)
Me.CMB_SUBS_FORMAT.TabIndex = 1 Me.CMB_SUBS_FORMAT.TabIndex = 1
@@ -538,55 +677,56 @@ Namespace API.YouTube.Controls
Me.TP_MAIN.ColumnCount = 1 Me.TP_MAIN.ColumnCount = 1
Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.TP_MAIN.Controls.Add(Me.TP_HEADER_BASE, 0, 0) Me.TP_MAIN.Controls.Add(Me.TP_HEADER_BASE, 0, 0)
Me.TP_MAIN.Controls.Add(TP_FOOTER, 0, 8) Me.TP_MAIN.Controls.Add(TP_FOOTER, 0, 9)
Me.TP_MAIN.Controls.Add(Me.TP_OPTIONS, 0, 1) Me.TP_MAIN.Controls.Add(Me.TP_OPTIONS, 0, 1)
Me.TP_MAIN.Controls.Add(Me.TP_CONTROLS, 0, 6) Me.TP_MAIN.Controls.Add(Me.TP_CONTROLS, 0, 7)
Me.TP_MAIN.Controls.Add(LB_SEP_1, 0, 5) Me.TP_MAIN.Controls.Add(LB_SEP_1, 0, 6)
Me.TP_MAIN.Controls.Add(LB_SEP_2, 0, 7) Me.TP_MAIN.Controls.Add(LB_SEP_2, 0, 8)
Me.TP_MAIN.Controls.Add(Me.TP_SUBS, 0, 2) Me.TP_MAIN.Controls.Add(Me.TP_SUBS, 0, 3)
Me.TP_MAIN.Controls.Add(Me.TXT_SUBS_ADDIT, 0, 3) Me.TP_MAIN.Controls.Add(Me.TXT_SUBS_ADDIT, 0, 4)
Me.TP_MAIN.Controls.Add(Me.TXT_EXTRA_AUDIO_FORMATS, 0, 4) Me.TP_MAIN.Controls.Add(Me.TXT_EXTRA_AUDIO_FORMATS, 0, 5)
Me.TP_MAIN.Controls.Add(TP_FPS_BITRATE, 0, 2)
Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_MAIN.Location = New System.Drawing.Point(0, 0) Me.TP_MAIN.Location = New System.Drawing.Point(0, 0)
Me.TP_MAIN.Name = "TP_MAIN" Me.TP_MAIN.Name = "TP_MAIN"
Me.TP_MAIN.RowCount = 10 Me.TP_MAIN.RowCount = 11
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 65.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 65.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 5.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 58.0!)) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 87.0!))
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle()) Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle())
Me.TP_MAIN.Size = New System.Drawing.Size(721, 271) Me.TP_MAIN.Size = New System.Drawing.Size(601, 328)
Me.TP_MAIN.TabIndex = 0 Me.TP_MAIN.TabIndex = 0
' '
'TP_OPTIONS 'TP_OPTIONS
' '
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.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, 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, 80.0!)) Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 80.0!))
Me.TP_OPTIONS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
Me.TP_OPTIONS.Controls.Add(LBL_FORMAT, 1, 0) Me.TP_OPTIONS.Controls.Add(LBL_FORMAT, 1, 0)
Me.TP_OPTIONS.Controls.Add(TP_WHAT, 0, 0) Me.TP_OPTIONS.Controls.Add(TP_WHAT, 0, 0)
Me.TP_OPTIONS.Controls.Add(Me.CMB_FORMAT, 2, 0) Me.TP_OPTIONS.Controls.Add(Me.CMB_FORMAT, 2, 0)
Me.TP_OPTIONS.Controls.Add(Me.LBL_AUDIO_CODEC, 5, 0) Me.TP_OPTIONS.Controls.Add(Me.LBL_AUDIO_CODEC, 4, 0)
Me.TP_OPTIONS.Controls.Add(Me.CMB_AUDIO_CODEC, 6, 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.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.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_OPTIONS.Location = New System.Drawing.Point(6, 65) 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.Margin = New System.Windows.Forms.Padding(6, 0, 6, 0)
Me.TP_OPTIONS.Name = "TP_OPTIONS" Me.TP_OPTIONS.Name = "TP_OPTIONS"
Me.TP_OPTIONS.RowCount = 1 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.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 Me.TP_OPTIONS.TabIndex = 1
' '
'CMB_FORMAT 'CMB_FORMAT
@@ -604,7 +744,7 @@ Namespace API.YouTube.Controls
Me.CMB_AUDIO_CODEC.Dock = System.Windows.Forms.DockStyle.Fill Me.CMB_AUDIO_CODEC.Dock = System.Windows.Forms.DockStyle.Fill
Me.CMB_AUDIO_CODEC.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList Me.CMB_AUDIO_CODEC.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList
Me.CMB_AUDIO_CODEC.FormattingEnabled = True 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.Name = "CMB_AUDIO_CODEC"
Me.CMB_AUDIO_CODEC.Size = New System.Drawing.Size(74, 21) Me.CMB_AUDIO_CODEC.Size = New System.Drawing.Size(74, 21)
Me.CMB_AUDIO_CODEC.TabIndex = 3 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.TextAlign = System.Windows.Forms.HorizontalAlignment.Center
Me.NUM_RES.Value = New Decimal(New Integer() {1080, 0, 0, 0}) 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 'TP_CONTROLS
' '
Me.TP_CONTROLS.ColumnCount = 1 Me.TP_CONTROLS.ColumnCount = 1
Me.TP_CONTROLS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) Me.TP_CONTROLS.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.TP_CONTROLS.Dock = System.Windows.Forms.DockStyle.Fill Me.TP_CONTROLS.Dock = System.Windows.Forms.DockStyle.Fill
Me.TP_CONTROLS.Location = New System.Drawing.Point(3, 182) Me.TP_CONTROLS.Location = New System.Drawing.Point(3, 210)
Me.TP_CONTROLS.Margin = New System.Windows.Forms.Padding(3, 0, 3, 0) Me.TP_CONTROLS.Margin = New System.Windows.Forms.Padding(3, 0, 3, 0)
Me.TP_CONTROLS.Name = "TP_CONTROLS" Me.TP_CONTROLS.Name = "TP_CONTROLS"
Me.TP_CONTROLS.RowCount = 1 Me.TP_CONTROLS.RowCount = 1
Me.TP_CONTROLS.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) 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 Me.TP_CONTROLS.TabIndex = 0
' '
'TXT_SUBS_ADDIT 'TXT_SUBS_ADDIT
' '
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image) ActionButton12.BackgroundImage = CType(resources.GetObject("ActionButton12.BackgroundImage"), System.Drawing.Image)
ActionButton6.Enabled = False ActionButton12.Enabled = False
ActionButton6.Name = "Open" ActionButton12.Name = "Open"
ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open ActionButton12.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
ActionButton6.ToolTipText = "Choose additional formats" ActionButton12.ToolTipText = "Choose additional formats"
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) ActionButton13.BackgroundImage = CType(resources.GetObject("ActionButton13.BackgroundImage"), System.Drawing.Image)
ActionButton7.Enabled = False ActionButton13.Enabled = False
ActionButton7.Name = "Refresh" ActionButton13.Name = "Refresh"
ActionButton7.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh ActionButton13.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
ActionButton7.ToolTipText = "Fill in additional formats from the defaults" ActionButton13.ToolTipText = "Fill in additional formats from the defaults"
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) ActionButton14.BackgroundImage = CType(resources.GetObject("ActionButton14.BackgroundImage"), System.Drawing.Image)
ActionButton8.Enabled = False ActionButton14.Enabled = False
ActionButton8.Name = "Clear" ActionButton14.Name = "Clear"
ActionButton8.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear ActionButton14.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
ActionButton8.ToolTipText = "Remove all additional formats" ActionButton14.ToolTipText = "Remove all additional formats"
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton6) Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton12)
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton7) Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton13)
Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton8) Me.TXT_SUBS_ADDIT.Buttons.Add(ActionButton14)
Me.TXT_SUBS_ADDIT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_SUBS_ADDIT.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
Me.TXT_SUBS_ADDIT.CaptionText = "Additional subtitle formats" Me.TXT_SUBS_ADDIT.CaptionText = "Additional subtitle formats"
Me.TXT_SUBS_ADDIT.CaptionToolTipEnabled = True Me.TXT_SUBS_ADDIT.CaptionToolTipEnabled = True
@@ -679,44 +801,44 @@ Namespace API.YouTube.Controls
Me.TXT_SUBS_ADDIT.CaptionWidth = 150.0R Me.TXT_SUBS_ADDIT.CaptionWidth = 150.0R
Me.TXT_SUBS_ADDIT.ClearTextByButtonClear = False Me.TXT_SUBS_ADDIT.ClearTextByButtonClear = False
Me.TXT_SUBS_ADDIT.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_SUBS_ADDIT.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_SUBS_ADDIT.Location = New System.Drawing.Point(6, 124) Me.TXT_SUBS_ADDIT.Location = New System.Drawing.Point(6, 152)
Me.TXT_SUBS_ADDIT.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3) Me.TXT_SUBS_ADDIT.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
Me.TXT_SUBS_ADDIT.Name = "TXT_SUBS_ADDIT" Me.TXT_SUBS_ADDIT.Name = "TXT_SUBS_ADDIT"
Me.TXT_SUBS_ADDIT.Size = New System.Drawing.Size(709, 22) Me.TXT_SUBS_ADDIT.Size = New System.Drawing.Size(589, 22)
Me.TXT_SUBS_ADDIT.TabIndex = 3 Me.TXT_SUBS_ADDIT.TabIndex = 3
Me.TXT_SUBS_ADDIT.Tag = "s" Me.TXT_SUBS_ADDIT.Tag = "s"
Me.TXT_SUBS_ADDIT.TextBoxReadOnly = True Me.TXT_SUBS_ADDIT.TextBoxReadOnly = True
' '
'TXT_EXTRA_AUDIO_FORMATS 'TXT_EXTRA_AUDIO_FORMATS
' '
ActionButton9.BackgroundImage = CType(resources.GetObject("ActionButton9.BackgroundImage"), System.Drawing.Image) ActionButton15.BackgroundImage = CType(resources.GetObject("ActionButton15.BackgroundImage"), System.Drawing.Image)
ActionButton9.Enabled = False ActionButton15.Enabled = False
ActionButton9.Name = "Open" ActionButton15.Name = "Open"
ActionButton9.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open ActionButton15.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Open
ActionButton9.ToolTipText = "Choose additional formats" ActionButton15.ToolTipText = "Choose additional formats"
ActionButton10.BackgroundImage = CType(resources.GetObject("ActionButton10.BackgroundImage"), System.Drawing.Image) ActionButton16.BackgroundImage = CType(resources.GetObject("ActionButton16.BackgroundImage"), System.Drawing.Image)
ActionButton10.Enabled = False ActionButton16.Enabled = False
ActionButton10.Name = "Refresh" ActionButton16.Name = "Refresh"
ActionButton10.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh ActionButton16.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Refresh
ActionButton10.ToolTipText = "Fill in additional formats from the defaults" ActionButton16.ToolTipText = "Fill in additional formats from the defaults"
ActionButton11.BackgroundImage = CType(resources.GetObject("ActionButton11.BackgroundImage"), System.Drawing.Image) ActionButton17.BackgroundImage = CType(resources.GetObject("ActionButton17.BackgroundImage"), System.Drawing.Image)
ActionButton11.Enabled = False ActionButton17.Enabled = False
ActionButton11.Name = "Clear" ActionButton17.Name = "Clear"
ActionButton11.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear ActionButton17.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
ActionButton11.ToolTipText = "Choose additional formats" ActionButton17.ToolTipText = "Choose additional formats"
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton9) Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton15)
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton10) Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton16)
Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton11) Me.TXT_EXTRA_AUDIO_FORMATS.Buttons.Add(ActionButton17)
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox Me.TXT_EXTRA_AUDIO_FORMATS.CaptionMode = PersonalUtilities.Forms.Controls.Base.ICaptionControl.Modes.CheckBox
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionText = "Additional audio formats" Me.TXT_EXTRA_AUDIO_FORMATS.CaptionText = "Additional audio formats"
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionToolTipEnabled = True Me.TXT_EXTRA_AUDIO_FORMATS.CaptionToolTipEnabled = True
Me.TXT_EXTRA_AUDIO_FORMATS.CaptionWidth = 150.0R Me.TXT_EXTRA_AUDIO_FORMATS.CaptionWidth = 150.0R
Me.TXT_EXTRA_AUDIO_FORMATS.ClearTextByButtonClear = False Me.TXT_EXTRA_AUDIO_FORMATS.ClearTextByButtonClear = False
Me.TXT_EXTRA_AUDIO_FORMATS.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_EXTRA_AUDIO_FORMATS.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_EXTRA_AUDIO_FORMATS.Location = New System.Drawing.Point(6, 152) Me.TXT_EXTRA_AUDIO_FORMATS.Location = New System.Drawing.Point(6, 180)
Me.TXT_EXTRA_AUDIO_FORMATS.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3) Me.TXT_EXTRA_AUDIO_FORMATS.Margin = New System.Windows.Forms.Padding(6, 3, 6, 3)
Me.TXT_EXTRA_AUDIO_FORMATS.Name = "TXT_EXTRA_AUDIO_FORMATS" Me.TXT_EXTRA_AUDIO_FORMATS.Name = "TXT_EXTRA_AUDIO_FORMATS"
Me.TXT_EXTRA_AUDIO_FORMATS.Size = New System.Drawing.Size(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.TabIndex = 4
Me.TXT_EXTRA_AUDIO_FORMATS.Tag = "a" Me.TXT_EXTRA_AUDIO_FORMATS.Tag = "a"
Me.TXT_EXTRA_AUDIO_FORMATS.TextBoxReadOnly = True 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.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.CancelButton = Me.BTT_CANCEL Me.CancelButton = Me.BTT_CANCEL
Me.ClientSize = New System.Drawing.Size(721, 271) Me.ClientSize = New System.Drawing.Size(601, 328)
Me.Controls.Add(Me.TP_MAIN) Me.Controls.Add(Me.TP_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeIcon_32 Me.Icon = Global.SCrawler.My.Resources.SiteYouTube.YouTubeIcon_32
Me.KeyPreview = True Me.KeyPreview = True
Me.MaximizeBox = False Me.MaximizeBox = False
Me.MinimizeBox = False Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(737, 310) Me.MinimumSize = New System.Drawing.Size(617, 367)
Me.Name = "VideoOptionsForm" Me.Name = "VideoOptionsForm"
Me.ShowInTaskbar = False Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
@@ -750,8 +872,13 @@ Namespace API.YouTube.Controls
TP_DESTINATION.ResumeLayout(False) TP_DESTINATION.ResumeLayout(False)
CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).EndInit() CType(Me.TXT_FILE, System.ComponentModel.ISupportInitialize).EndInit()
TP_OK_CANCEL.ResumeLayout(False) TP_OK_CANCEL.ResumeLayout(False)
TP_PLS.ResumeLayout(False)
CType(Me.CMB_PLS, System.ComponentModel.ISupportInitialize).EndInit()
TP_WHAT.ResumeLayout(False) TP_WHAT.ResumeLayout(False)
TP_WHAT.PerformLayout() TP_WHAT.PerformLayout()
TP_FPS_BITRATE.ResumeLayout(False)
CType(Me.TXT_FPS, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_AUDIO_BITRATE, System.ComponentModel.ISupportInitialize).EndInit()
Me.TP_HEADER_BASE.ResumeLayout(False) Me.TP_HEADER_BASE.ResumeLayout(False)
Me.TP_SUBS.ResumeLayout(False) Me.TP_SUBS.ResumeLayout(False)
Me.TP_SUBS.PerformLayout() Me.TP_SUBS.PerformLayout()
@@ -760,7 +887,6 @@ Namespace API.YouTube.Controls
Me.TP_OPTIONS.ResumeLayout(False) Me.TP_OPTIONS.ResumeLayout(False)
Me.TP_OPTIONS.PerformLayout() Me.TP_OPTIONS.PerformLayout()
CType(Me.NUM_RES, System.ComponentModel.ISupportInitialize).EndInit() 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_SUBS_ADDIT, System.ComponentModel.ISupportInitialize).EndInit()
CType(Me.TXT_EXTRA_AUDIO_FORMATS, System.ComponentModel.ISupportInitialize).EndInit() CType(Me.TXT_EXTRA_AUDIO_FORMATS, System.ComponentModel.ISupportInitialize).EndInit()
Me.ResumeLayout(False) Me.ResumeLayout(False)
@@ -791,5 +917,8 @@ Namespace API.YouTube.Controls
Private WithEvents BTT_CANCEL As Button Private WithEvents BTT_CANCEL As Button
Private WithEvents TP_HEADER_INFO_2 As TableLayoutPanel Private WithEvents TP_HEADER_INFO_2 As TableLayoutPanel
Private WithEvents TXT_FPS As PersonalUtilities.Forms.Controls.TextBoxExtended Private WithEvents TXT_FPS As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents CMB_PLS As PersonalUtilities.Forms.Controls.ComboBoxExtended
Private WithEvents BTT_PLS_BROWSE As Button
Private WithEvents TXT_AUDIO_BITRATE As PersonalUtilities.Forms.Controls.TextBoxExtended
End Class End Class
End Namespace End Namespace

View File

@@ -137,6 +137,13 @@
</metadata> </metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
</value>
</data>
<data name="ActionButton2.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL 3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
@@ -235,6 +242,123 @@
<metadata name="TP_OK_CANCEL.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_OK_CANCEL.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<metadata name="TP_PLS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<data name="ActionButton3.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
wwAADsMBx2+oZAAAAFFJREFUOE9joAr49u3bf1Lw169f50O1QgBI0MnJCY4/vP8Ix8hiILqtrQ3TEFIM
AGGYIVDtpBsAwkQbgIyR1dDWAGLwqAGD0gByMFQ7JYCBAQChNviRiQ8ETwAAAABJRU5ErkJggg==
</value>
</data>
<data name="ActionButton4.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeElE
QVQ4T2P4//8/RRhMFHQfKgDi/yAaXQEhDCZAmkNbnvyXta4CciESLEws//FhmDqYAQUgzUBMngsowVgF
ScFgYjQQsUsQi8FEYsXyAiD+D6LRFRDCYAKk2bPo6H9J40wgFyKBLeCQMUwdzIACkGYgHnKB+J8BAD5Q
tqhi4tzWAAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton5.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton6.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
GlAKCkhEC4KgQlsLQkqhKi/lrYWWlxaw3dLddrerz/Q89+7dc2fbfTn3npf5fJJv2rS758z85nnOzJz5
nZktAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMK3O3r79wVUIz65jfGNVxI/VIX69CvGO9M//a9P+e8o3B/8v
vKn9s+3fyX8dAJgmaWd+fl3E96Wd/E9XdvZHkfbvXNa+Rn45AGCS3bvjj/E/h3box5OrmxjPyy8PAEyS
XXO7zqhCeH/HDnwUOdCE+J6zdux4eH47YIrEGE8uy/Ls9Bnx/LooL0oH9b9Th/I1TVG+rCqKC+q6Xsh/
FJgmO8vy6WknfdPQTnsjckMdwlPy2wITLO3wF6si/lGas1ekuXvX0Fzuyg9S3psOCl6qDwimQB3ji9Ok
3btmEm907kpnEa/Mbw9Mlq1pB/6cdHZ/ZcfcPZrcXoXyrVVVFfl1gUmSdsS/libqPUMTd5NSvjktwrbB
kgDjVi1UT26K+Nnu+XrMuaud60uPWHpIfhtg3JqyfEaanHcPTdZNTRPCPy4uLj40LxIwBudt2fKAtOP/
0zQnN+5koIg3tpca81sC49J+LZcm5a3rJulYEq6LSV40YBOFEB6V5uFV6+flRiTsSwf9r81vDYzBCSO4
vjfq/KAuiqfm5QM2QRPjuWnubUbz71DCn6W33zpYCmDT1EX5m92Tcuy5q47xFXkxgQ3UduqnOXfn0Bzc
xJSvz4sCbIb2pzlp8v1w/WScnKSzkjekRT1hsMTAKC0vL5/Ydud3zb1NT1FelBcL2GiDm3d0TMTJy0ea
pjk1LzYwAu3NvtLc+uTQXBtn7tYYCJtja/vQno5JOJFpQrzWb4hhNJoQnpjm1Q3D82wCcnNRFKfnxQQ2
Qttk1zH5JjzhFmcIcHzyzb6O5aFem5J0sP/OvKjARmg7b7sm3xRkT3vDorwawJHb1t6Ep2NOTVoOtDch
yssMjFr6IPh8x8SbnsT4lrQamgPhCMzPzz+sifHjnXNpMnN5XnRglJaWlk5KE2z/0ISbxnzQQ0bgvlXz
1ePSXPnG0NyZ+DRF8Zi8CsCo7Azh0V0TbkrzRc2B0G3wIJ9429CcmZLce4MgYJTyff87JtzU5uayLM/J
qwcM7vD5+jQ3DgzNlWnKDXldgFFJZwW/2jHZpj1727uZ5VWE3mofqJXmw4eG5sdUpqqqXXm1gFGoQnhJ
12SbgRxoYvzjtIruK04vxRjPSvPgK0PzYmqTPqtemVcNGIU6xgu7JtusJH1ovH9ubu6UvLrQC2ncPyuN
/58Mz4fpTvnmvHrAKJQL5dO6J9ssJXxucWFhLq8yzLKtaUf5h2ncb9zz+8eUKsYP53UERmHX/PyOrsk2
g7nJDUWYZUuPWHpIE8oPdIz92UiMn86rCoxIOmOYta8KD5uftk2Peb1hZtTzdVOHcF3HmJ+ZVCF+Ia8u
MCppcl0+PNlmOG1zYPtYYc2BzIQ0np+ZxvWPh8b5LObqvMrAqEzRo4BHmctijCfnEsBUqkP5u2ksz8Kd
PI8g5SfyagOj0jbIpQk2c01DR5Brmh3NfC4DTI324LWO8V0dY3pm48mAsEGm7OEgo0sRb9wZ4+NzGWDi
lWUZ0ti9Zt1YnvUU8fdyCYBRmsFbAh9xqhDvqEN4Xi4FTKz8s93vD4/hPiSdpJyXywCMWPtrgKuGJ12P
ck/6gPmDXAuYOHVR/lY6UN3XMXb7kDv17MAGqhaqJ6WJ1sdegDUJ726a5oG5JDB2917vL+Kl3eO1N/lQ
LgewUdIO8E0dk69vubosy+25JDA2bYNuFeJnOsZovxLjhbkkwEZZXl4+0QfOvfl2Ogg4O5cFNl1dFE9N
4/B7Q+Oyj7mh/VzKZQE2UtM0j6iL+LWOidizhN3OPBiHuigvSmPwrvVjsn9pQnh1LguwGQa3Fo3fHp6M
Pcw97c1WcllgQy0tLZ2UDr7/qmMc9jJNiF/WkwNjMHhQ0GzfX/yIU8RLfRCxkdq+kzTfrugcf/3MgZ1l
+fRcHmCztU8Yq2P8h47J2cdcpTmQjdCE8IQ0vnzjdkjKP8nlAcZoWxXin3dP0n4l1eGb9UL92FwXOG51
Ub48ja09w2Otz2nvTJpKs21QIWDs0lnKb6TJqTEphN3NQvncXBY4VtvSju4N3WOs17l6cXHxoblGwKRo
r8mlHeAtHZO2b9mfDohem8sCR2XX3K4z0hj65NCYklSTGONpuUzApNlVFFWaqP81NHF7mvD2tnM7lwbu
V/vwqTR2vrV+LPU7VSjf4ff+MAU0B65NeWVd12fm0sBhpTnzosHDp7rGUV8T9lVFvDiXCJgSrmEezDea
onhMrgsM25rmyuvSODkwNG56nvZyYvi5XCNg2mgOXM3tVVH9ci4L3KtpmlN9W7Y+VYhfiEkuEzCt8n3L
fzA8yXuY/b7OZEVZlovt3ew6xknf8965ublTcpmAaac5cG3C2zQ09Vv7bVAaC/+7fmz0Og6QYVZpDlyT
GD/dPlgpl4b+2Nru5NIYuGfdmOhxmhB/VBblL+QaATNKc+DBfH1nCI/OdWHGtTewSdv874fGgIT4xfYb
wlwmYNZpDlzNbVUIz85lYUblJ2i6BDacGP/u7O3bH5zLBPSF5sDV7K+L+Nu5LMyYtJP7xbSNfzy0zfue
A+03gak8WwdVAnpHc+CaxHiJ5sCZsnK9f/+6bd3v3JZ2/r+SawT0mebAg0kfjB93v/Pp136t3X693bWN
e56v6nsBhmkOXE24Ph0EnJXrwpSp63qhDuXnu7dtn1P+U1VVP5PLBHAozYGDtD+LchvU6TN4Iqa+lqGs
XO8/YVAlgMPQHLiSsC+dNb0ml4UJVxflRWm73b1+O/Y5YXcVwvNziQDun+bANYnxkvO2bHlALg0TJsZ4
cl3ESzu3Xa8Trm+KYimXCeDIaQ48mKqIH9McOHl2zc/vaIr42a5t1vN8tCiK03OZAI6J5sCVFPFr7QNk
cl0Ys3yp6nvrtlO/s3K9f9ugSgDHSXPgILk58PxcFsYkX+93J8s1qUK8oynKF+YSAYyO5sCVhH3pgOjV
uSxsoqZpHpjq//bu7dLjFPHGND+Xc5kARk9z4JrE+JZUEl+1bpLFhYW5VPf/WLcd5N/ruj4zlwlg42gO
PCQfdXOVjdeE8MRU6xuGai9uXw2MgebA1YTrFkMoc10YsaYoX5rqfOf6uvc6e9LO/xW5RACbT3Pgam5N
B0Q/m8vCCLT3XnCQ2ZXwnWqhenIuE8D4aA5czV3OykZj19yuM1I9PzlUXwnhirIst+cyAYyf5sA1GTQH
uu/6MdoZ4+NTHb+1rq59j+v9wKTSHHhIPtI0zam5NByhNH5enGr306Fa9j1720ttuUQAE0tz4Epi/FJM
cl24b8ZNd25KdTk31whg8mkOXEm4pX1EbS4LHebn5x+WdnIf765fr3NVCOFRuUwA00Nz4Gr21kX58lwW
1qjmq8el+nxjqF4S4yVLS0sn5TIBTB/NgWuiOfAQTVE+J9XltnV16nXCvqqIF+cSAUw3zYGH5INnb9/+
4Fyavtra7uRSLe4Zqk3f88MmxvNyjQBmhiavg/liVVVFrkuvLC4uPjSt/4eG6iEhXlOWZchlApg9mgNX
c3P6wD8nl6UXqvlqZ1rv/xmqQ+/ThPJv5ufnH5TLBDC7NAeuZm97n/tclplWhfCstL4/GVr/nsf1fqCH
NAeu5kB7aSSVZFabA13v786tVVFckGsE0C+aAw8mnSG/f25u7pRcmpnQbt8mlB/oWt8+pwnxWk+PBNAc
uJoqxC/MSnNgs7BQ1yFc17WePc97Z+1AD+C4aA5czU3T/qjXdED3zLQePx5ar75nf77ev3VQJQBWaQ5c
zZ4qhJfkskyVuigvapvbOtapt2lC/FFTls/IJQKgi+bA1aw0B07FGWOM8eQ6xnd1rEe/E+OX2jGdywTA
fdEcuCYx/u2kXzNudjTz6az/c53L3+NUMX7YI6EBjp7mwIO5pt3J5rpMlHKhfFpavu8PLW/fM+s/7QTY
eJoDV/PduiiWc1kmwuB6f7x7aDn7ntvrGC/MJQLgeGgOHKQK8Y46hOflsoxN0zQPbEJ8Z9cy9jxf3RnC
o3OZABgFzYGrab9efl0qyViaAxcXFubSgchnOpar77k8xnhaLhMAo6Q5cE2K+L7NfoBMHcJT0nvfvG5Z
+h3X+wE2iebAg7m6LMvtuS4bKl/v14txSMLuKoQX5BIBsBk0B64kfCfV4om5LCN33pYtD3DA1ZVwfb1Q
PzaXCYDNpDlwJWH3RnSe75rbdUZ6/X9b/369z0eLojg9lwmAcdAcuJqV5sCRaEJ4QnrNbw+9R9+zcr1/
26BKAIyV5sBDcll7W95cmmPSPocgvc6dQ6/b9+xpivJluUQATBDNgQdz1TE2B6phV4p446TdhAmAIens
9ZXpQ1tz4KA58Am5LPfrrB07Hp7+zr90v1Z/UxXxU3Vdn5nLBMAk0xy4krC7WSifm8tyWGVZnl2F+M3u
1+hxYrxkeXn5xFwmAKaB5sDV7E9nsRfnsqyTdnIvGtxiuPPv9jV7Ul1ekUsEwLTRHHgwVSjfsbS0dFIu
TWtr+6uB9P8ODP/Znue7ZVmek2sEwBTT2Laa8sr2enb7jHoHRl0JV8QYH5nHDQCzwJ0DV/P1tKO7vuO/
9zpVKN/qej/AjNIcKB3Z24TyVXmIADCrNAfKmtzUxHhuHhoAzDrNgZJyVQjhUXlIANAjmgN7m/Du471d
MgBTzp0D+5Sw777uiQBAz2gO7EPCLSnn500OAAOaA2c615RlGfKmBoBDaQ6cvTQhvmd+fv5BeRMDwGFp
DpyJuN4PwDHQHDjVubUqigvypgSAo6M5cPrShHjtYghl3oQAcGw0B05Rivi+ubm5U/KmA4Djozlw4rM/
X+/fOthiADA6mgMnME2IP2rK8hl5GwHAxtAcOFH5SozxrLxpAGBjaQ4cf6oYP9w0zal5kwDA5tAcOLYc
aC/FpE1wwmBLAMAm0xy46bk91fvCXH4AGCvNgZuRIn6tKYrH5JoDwGTQHLihuTzGeFouNQBMFs2BI4/r
/QBMB82Bo0rYXYXwglxWAJh8mgOPN+H6eqF+bC4nAEwVzYHHkiL+c1EUp+caAsB00hx4FInxLalk2waV
A4AppznwfrOnLsqX53IBwOzQHHiYFPHGaqF6Ui4TAMwezYGHpirip+q6PjOXBwBmmubANjFesry8fGKu
CQD0Q4+bA/dWMf56LgMA9E8PmwO/W5blOXn1AaC/+tMcWF4ZY3xkXm0AYOabA2O8ZGlp6aS8ugDAGrPY
HLi3CeWr8voBAIczQ82BN6UDmnPzagEA92f6mwPLz1dVVeTVAQCO1LQ2B1Yh/PX8/PyD8moAAEdrupoD
w76qiBfnRQcAjtMUNAeGW1LOz8sLAIzKBDcHXlOWZciLCQCM2gQ2B142Nzd3Sl48AGCjTEhz4H7X+wFg
k425OfDWqqh+Pi8KALDJtqWDgDemHfKBoR30hqUJ8dqY5PcHAMalKcrnpJ3z94Z31qNO+/t+1/sBYIKk
k/LT6hD+Mu2oR/4rgXTW/+X02r+U3woAmDTtz/GaIv5F2nH/ZHhHfpS5J+Vf01n/S9LLbhu8OgAw0dpb
8TYL5XPTmfvb0o78v/MOvWtHvybtzXzKT1Qx/n5d1wv5pQCAaXXvAUFRLLXd+3WMFzZF+cKUl7X/rIri
gsWFhbn8RwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
AAAASUVORK5CYII=
</value>
</data>
<metadata name="LB_SEP_1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="LB_SEP_1.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
@@ -253,74 +377,15 @@
<metadata name="LBL_SUBS_FORMAT.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="LBL_SUBS_FORMAT.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </metadata>
<data name="ActionButton2.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <metadata name="TP_FPS_BITRATE.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value> <value>False</value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO </metadata>
wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
cMaRN0UdBBkAAAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton3.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
QVQ4T2P4//8/QczOJyyqHpzfiE0OQwAZC8iqszAzs7CJ69o4BR768V/W2jcGXQ0KB4aFNS3dDQtnrbCb
ePCK48wTN1wXXXzge/jXf/clV55zC4hIIatF0cjIyMikElzc57z0wX+XHd/+2+//99/ywP//xlu//tdb
+eK/4Zp3/1WTOhYzARViNUAluKjTdf37/0ZTTn9TbdhwXblhwwW1/qOP1Ja9+K8w+95/6cm3/6v2Xvkv
qKjniGGAoIqRpW3/4e8S9uGdzFz82gwMDFxAzCxm4ZegtuLDf+VJ1/8rZM25IqLvnM/CximCYYCic1QN
v7x2JIwPwyrJ3XNUylddE9G2TWNmZOBDl4czmJiZMSRBmFdSyYyJgUEQmxwIYxWEYXZBCUls4sgYq6CA
prWNbtG8nXKeaVPR5XiVjSxEzf0yYXy4BBMLO6eQjoOXZvrkbbazrv53Xf/2v4CSbjBMXkhBl1/CMyNZ
qWnvGy5pNQ+YONwAfjXzAOupl/47LLr333L50/96q9/8l23YdES6cO5KuYqVW+R7Tj6SnfP0v4hryjyY
HhQDmFjYeHVKFp7WX/Xuv9Kq9/+Vd/z7r7rv/3+l7f//y676DEwDN/9L+BVvYkKLCTgDhNkkVUyVlr74
qbbz73/VOTc/qsy89kWx+9h7qbQpJwS1bbOAscGGrB6EUTggLOqf16C55ft/HlnNAFZOXgVWdi4FRgYG
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
</value>
</data>
<data name="ActionButton4.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
</value>
</data>
<data name="ActionButton5.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
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>
<data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton7.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6 iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
QVQ4T2P4//8/QczOJyyqHpzfiE0OQwAZC8iqszAzs7CJ69o4BR768V/W2jcGXQ0KB4aFNS3dDQtnrbCb tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
ePCK48wTN1wXXXzge/jXf/clV55zC4hIIatF0cjIyMikElzc57z0wX+XHd/+2+//99/ywP//xlu//tdb AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
+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> </value>
</data> </data>
<data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton8.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
@@ -364,6 +429,76 @@
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC 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> </value>
</data> </data>
</root> </root>

View File

@@ -26,11 +26,17 @@ Namespace API.YouTube.Controls
Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML Friend Property DesignXML As EContainer Implements IDesignXMLContainer.DesignXML
Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes Private Property DesignXMLNodes As String() Implements IDesignXMLContainer.DesignXMLNodes
Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName Private Property DesignXMLNodeName As String Implements IDesignXMLContainer.DesignXMLNodeName
Private Const ControlsRow As Integer = 6 Private Const ControlsRow As Integer = 7
Private ReadOnly Property CNT_PROCESSOR As TableControlsProcessor Private ReadOnly Property CNT_PROCESSOR As TableControlsProcessor
Friend Property MyContainer As YouTubeMediaContainerBase Friend Property MyContainer As YouTubeMediaContainerBase
Private Initialization As Boolean = True Private Initialization As Boolean = True
Private ReadOnly InheritsFromContainer As Boolean Private ReadOnly InheritsFromContainer As Boolean
Private ReadOnly M3U8Files As List(Of SFile)
Private ReadOnly Property M3U8FilesFull As List(Of SFile)
Get
Return ListAddList(Nothing, M3U8Files, LAP.NotContainsOnly).ListAddValue(CMB_PLS.Text, LAP.NotContainsOnly)
End Get
End Property
Private Class FpsFieldChecker : Inherits FieldsCheckerProviderBase Private Class FpsFieldChecker : Inherits FieldsCheckerProviderBase
Private ReadOnly MyProvider As ANumbers = YouTubeSettings.FpsFormatProvider.MyProviderDefault Private ReadOnly MyProvider As ANumbers = YouTubeSettings.FpsFormatProvider.MyProviderDefault
Public Overrides Property ErrorMessage As String Public Overrides Property ErrorMessage As String
@@ -54,6 +60,7 @@ Namespace API.YouTube.Controls
#Region "Initializers" #Region "Initializers"
Friend Sub New(ByVal Container As YouTubeMediaContainerBase, Optional ByVal InheritsFromContainer As Boolean = False) Friend Sub New(ByVal Container As YouTubeMediaContainerBase, Optional ByVal InheritsFromContainer As Boolean = False)
InitializeComponent() InitializeComponent()
M3U8Files = New List(Of SFile)
MyContainer = Container MyContainer = Container
CNT_PROCESSOR = New TableControlsProcessor(TP_CONTROLS) CNT_PROCESSOR = New TableControlsProcessor(TP_CONTROLS)
Me.InheritsFromContainer = InheritsFromContainer Me.InheritsFromContainer = InheritsFromContainer
@@ -69,6 +76,8 @@ Namespace API.YouTube.Controls
End If End If
MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_FILE) MyYouTubeSettings.DownloadLocations.PopulateComboBox(TXT_FILE)
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS,, True)
CMB_PLS.Text = MyYouTubeSettings.LatestPlaylistFile.Value
If Not MyContainer Is Nothing Then If Not MyContainer Is Nothing Then
With MyContainer With MyContainer
@@ -155,11 +164,16 @@ Namespace API.YouTube.Controls
If InheritsFromContainer Then If InheritsFromContainer Then
If .OutputVideoFPS > 0 Then TXT_FPS.Text = .OutputVideoFPS If .OutputVideoFPS > 0 Then TXT_FPS.Text = .OutputVideoFPS
If .OutputAudioBitrate > 0 Then TXT_AUDIO_BITRATE.Text = .OutputAudioBitrate
Else Else
If MyYouTubeSettings.DefaultVideoFPS > 0 Then TXT_FPS.Text = MyYouTubeSettings.DefaultVideoFPS If MyYouTubeSettings.DefaultVideoFPS > 0 Then TXT_FPS.Text = MyYouTubeSettings.DefaultVideoFPS
If MyYouTubeSettings.DefaultAudioBitrate > 0 Then TXT_AUDIO_BITRATE.Text = MyYouTubeSettings.DefaultAudioBitrate.Value
End If End If
MyFieldsChecker.AddControl(Of Double)(TXT_FPS, TXT_FPS.CaptionText, True, New FpsFieldChecker) With MyFieldsChecker
MyFieldsChecker.EndLoaderOperations() .AddControl(Of Double)(TXT_FPS, TXT_FPS.CaptionText, True, New FpsFieldChecker)
.AddControl(Of Integer)(TXT_AUDIO_BITRATE, TXT_AUDIO_BITRATE.CaptionText, True)
.EndLoaderOperations()
End With
TP_SUBS.Enabled = .Subtitles.Count > 0 TP_SUBS.Enabled = .Subtitles.Count > 0
TXT_SUBS_ADDIT.Enabled = .Subtitles.Count > 0 TXT_SUBS_ADDIT.Enabled = .Subtitles.Count > 0
RefillTextBoxes() RefillTextBoxes()
@@ -180,6 +194,7 @@ Namespace API.YouTube.Controls
Private Sub VideoOptionsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing Private Sub VideoOptionsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
MyView.DisposeIfReady() MyView.DisposeIfReady()
MyFieldsChecker.DisposeIfReady() MyFieldsChecker.DisposeIfReady()
M3U8Files.Clear()
End Sub End Sub
#End Region #End Region
#Region "Refill" #Region "Refill"
@@ -313,9 +328,11 @@ Namespace API.YouTube.Controls
ControlInvokeFast(TP_CONTROLS, Sub() ControlInvokeFast(TP_CONTROLS, Sub()
With DirectCast(Container, YouTubeMediaContainerBase) With DirectCast(Container, YouTubeMediaContainerBase)
.File = $"{TXT_FILE.Text.CSFilePS}{ .File.File}" .File = $"{TXT_FILE.Text.CSFilePS}{ .File.File}"
.M3U8_PlaylistFiles = M3U8FilesFull
If Full Then If Full Then
.OutputVideoExtension = CMB_FORMAT.Text.StringToLower .OutputVideoExtension = CMB_FORMAT.Text.StringToLower
.OutputVideoFPS = AConvert(Of Double)(TXT_FPS.Text, YouTubeSettings.FpsFormatProvider.MyProviderDefault, -1) .OutputVideoFPS = AConvert(Of Double)(TXT_FPS.Text, YouTubeSettings.FpsFormatProvider.MyProviderDefault, -1)
.OutputAudioBitrate = AConvert(Of Integer)(TXT_AUDIO_BITRATE.Text, -1)
.OutputAudioCodec = CMB_AUDIO_CODEC.Text.StringToLower .OutputAudioCodec = CMB_AUDIO_CODEC.Text.StringToLower
.OutputSubtitlesFormat = CMB_SUBS_FORMAT.Text.StringToLower .OutputSubtitlesFormat = CMB_SUBS_FORMAT.Text.StringToLower
.IsAudioSelected = OPT_AUDIO.Checked .IsAudioSelected = OPT_AUDIO.Checked
@@ -335,12 +352,15 @@ Namespace API.YouTube.Controls
Else Else
f = TXT_FILE.Text f = TXT_FILE.Text
End If End If
f = CleanFileName(f)
If f.IsEmptyString Then Throw New ArgumentNullException("File", "The output file cannot be null") If f.IsEmptyString Then Throw New ArgumentNullException("File", "The output file cannot be null")
With MyContainer With MyContainer
.OutputVideoExtension = CMB_FORMAT.Text.StringToLower .OutputVideoExtension = CMB_FORMAT.Text.StringToLower
.OutputVideoFPS = AConvert(Of Double)(TXT_FPS.Text, YouTubeSettings.FpsFormatProvider.MyProviderDefault, -1) .OutputVideoFPS = AConvert(Of Double)(TXT_FPS.Text, YouTubeSettings.FpsFormatProvider.MyProviderDefault, -1)
.OutputAudioBitrate = AConvert(Of Integer)(TXT_AUDIO_BITRATE.Text, -1)
.OutputAudioCodec = CMB_AUDIO_CODEC.Text.StringToLower .OutputAudioCodec = CMB_AUDIO_CODEC.Text.StringToLower
.OutputSubtitlesFormat = CMB_SUBS_FORMAT.Text.StringToLower .OutputSubtitlesFormat = CMB_SUBS_FORMAT.Text.StringToLower
.M3U8_PlaylistFiles = M3U8FilesFull
If Not .HasElements Then If Not .HasElements Then
Dim cntIndex% = -1 Dim cntIndex% = -1
@@ -357,6 +377,7 @@ Namespace API.YouTube.Controls
Else Else
.SelectedVideoIndex = -1 .SelectedVideoIndex = -1
.SelectedAudioIndex = cntIndex .SelectedAudioIndex = cntIndex
.MediaType = UMTypes.Audio
End If End If
.FileSetManually = True .FileSetManually = True
.File = f .File = f
@@ -367,6 +388,7 @@ Namespace API.YouTube.Controls
Else Else
If OPT_AUDIO.Checked Then If OPT_AUDIO.Checked Then
.SetMaxResolution(-2) .SetMaxResolution(-2)
.MediaType = UMTypes.Audio
Else Else
.SetMaxResolution(NUM_RES.Value) .SetMaxResolution(NUM_RES.Value)
End If End If
@@ -377,6 +399,8 @@ Namespace API.YouTube.Controls
If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = f If MyYouTubeSettings.OutputPathAutoChange Then MyYouTubeSettings.OutputPath.Value = f
If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(f, False) If MyDownloaderSettings.OutputPathAutoAddPaths Then MyYouTubeSettings.DownloadLocations.Add(f, False)
If Not CMB_PLS.Text.IsEmptyString Then MyYouTubeSettings.PlaylistsLocations.Add(CMB_PLS.Text, False, True)
MyYouTubeSettings.LatestPlaylistFile.Value = CMB_PLS.Text
DialogResult = DialogResult.OK DialogResult = DialogResult.OK
Close() Close()
@@ -508,6 +532,42 @@ Namespace API.YouTube.Controls
End Sub End Sub
#End Region #End Region
#Region "Footer" #Region "Footer"
Private Sub CMB_PLS_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles CMB_PLS.ActionOnButtonClick
Select Case e.DefaultButton
Case ADB.List
Dim result As Boolean = False
Dim selectedFiles As IEnumerable(Of SFile) = MyYouTubeSettings.PlaylistsLocations.ChooseNewPlaylistArray(CMB_PLS, result)
If result And selectedFiles.ListExists Then M3U8Files.ListAddList(selectedFiles, LAP.NotContainsOnly, LAP.ClearBeforeAdd)
Case ADB.Save
With MyYouTubeSettings.PlaylistsLocations
If Not CMB_PLS.Text.IsEmptyString AndAlso .IndexOf(CMB_PLS.Text,, True) = -1 Then
.Add(CMB_PLS.Text, True, True)
.PopulateComboBox(CMB_PLS, CMB_PLS.Text, True)
End If
End With
Case ADB.Clear : M3U8Files.Clear()
End Select
End Sub
Private Sub BTT_PLS_BROWSE_MouseDown(sender As Object, e As MouseEventArgs) Handles BTT_PLS_BROWSE.MouseDown
Try
Dim f As SFile = Nothing
If Not CMB_PLS.Text.IsEmptyString Then
f = CMB_PLS.Text
ElseIf Not TXT_FILE.Text.IsEmptyString Then
f = TXT_FILE.Text
End If
f = SFile.SelectFiles(f, False, "Select a playlist...", "Playlists|*.m3u;*.m3u8|All files|*.*", EDP.ReturnValue).FirstOrDefault
If Not f.IsEmptyString Then
If e.Button = MouseButtons.Right Then
MyYouTubeSettings.PlaylistsLocations.Add(f.ToString, True, True)
MyYouTubeSettings.PlaylistsLocations.PopulateComboBox(CMB_PLS, f, True)
End If
CMB_PLS.Text = f
End If
Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.YouTube.Controls.VideoOptionsForm.SelectPlaylist]")
End Try
End Sub
Private _FilePathBeforeItemChange As SFile = Nothing Private _FilePathBeforeItemChange As SFile = Nothing
Private Sub TXT_FILE_ActionSelectedItemBeforeChanged(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles TXT_FILE.ActionSelectedItemBeforeChanged Private Sub TXT_FILE_ActionSelectedItemBeforeChanged(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles TXT_FILE.ActionSelectedItemBeforeChanged
If Not TXT_FILE.Text.IsEmptyString Then _FilePathBeforeItemChange = TXT_FILE.Text Else _FilePathBeforeItemChange = Nothing If Not TXT_FILE.Text.IsEmptyString Then _FilePathBeforeItemChange = TXT_FILE.Text Else _FilePathBeforeItemChange = Nothing
@@ -529,6 +589,14 @@ Namespace API.YouTube.Controls
_FilePathBeforeItemChange = Nothing _FilePathBeforeItemChange = Nothing
End Try End Try
End Sub End Sub
Private Sub TXT_FILE_ActionOnButtonClick(ByVal Sender As Object, ByVal e As ActionButtonEventArgs) Handles TXT_FILE.ActionOnButtonClick
If e.DefaultButton = ADB.Save And Not TXT_FILE.Text.IsEmptyString Then
With MyYouTubeSettings.PlaylistsLocations
.Add(TXT_FILE.Text, True)
.PopulateComboBox(TXT_FILE, TXT_FILE.Text)
End With
End If
End Sub
Private Sub BTT_BROWSE_MouseDown(sender As Object, e As MouseEventArgs) Handles BTT_BROWSE.MouseDown Private Sub BTT_BROWSE_MouseDown(sender As Object, e As MouseEventArgs) Handles BTT_BROWSE.MouseDown
Dim f As SFile Dim f As SFile
#Disable Warning BC40000 #Disable Warning BC40000
@@ -537,12 +605,15 @@ Namespace API.YouTube.Controls
f = SFile.SelectPath(f, "Select the destination of the video files", EDP.ReturnValue) f = SFile.SelectPath(f, "Select the destination of the video files", EDP.ReturnValue)
Else Else
f = TXT_FILE.Text f = TXT_FILE.Text
Dim sPattern$ = $"Video|{AvailableVideoFormats.Select(Function(vf) $"*.{vf.ToLower}").ListToString(";")}" & Dim ext$ = f.Extension
$"|Audio|{AvailableAudioFormats.Select(Function(af) $"*.{af.ToLower}").ListToString(";")}" & Dim sPattern$ = "All Files|*.*|" &
"|All Files|*.*" $"Video|{AvailableVideoFormats.Select(Function(vf) $"*.{vf.ToLower}").ListToString(";")}" &
f = SFile.SaveAs(f, "Select the destination of the video file",,, sPattern, EDP.ReturnValue) $"|Audio|{AvailableAudioFormats.Select(Function(af) $"*.{af.ToLower}").ListToString(";")}"
f = SFile.SaveAs(f, "Select the destination of the video file",, ext, sPattern, EDP.ReturnValue)
f.Extension = ext
End If End If
#Enable Warning #Enable Warning
f = CleanFileName(f)
If Not f.IsEmptyString Then If Not f.IsEmptyString Then
If e.Button = MouseButtons.Right Then If e.Button = MouseButtons.Right Then
MyYouTubeSettings.DownloadLocations.Add(f, MyDownloaderSettings.OutputPathAskForName) MyYouTubeSettings.DownloadLocations.Add(f, MyDownloaderSettings.OutputPathAskForName)

View File

@@ -17,10 +17,21 @@ Namespace API.YouTube
Public Const DownloaderDataFolderYouTube As String = DownloadObjects.STDownloader.DownloaderDataFolder & "YouTube\" Public Const DownloaderDataFolderYouTube As String = DownloadObjects.STDownloader.DownloaderDataFolder & "YouTube\"
Friend Const YouTubeDownloadPathDefault As String = "YouTubeDownloads\" Friend Const YouTubeDownloadPathDefault As String = "YouTubeDownloads\"
Friend Const SimpleArraysFormNode As String = "SimpleFormatsChooserForm" Friend Const SimpleArraysFormNode As String = "SimpleFormatsChooserForm"
Private Const YTDLP_DefaultName As String = "yt-dlp"
Public Property MyYouTubeSettings As Base.YouTubeSettings Public Property MyYouTubeSettings As Base.YouTubeSettings
Public Property MyCache As CacheKeeper Public Property MyCache As CacheKeeper
Friend ReadOnly Property MyCacheSettings As New CacheKeeper(DownloaderDataFolderYouTube) With {.DeleteCacheOnDispose = False, .DeleteRootOnDispose = False} Friend ReadOnly Property MyCacheSettings As New CacheKeeper(DownloaderDataFolderYouTube) With {.DeleteCacheOnDispose = False, .DeleteRootOnDispose = False}
Public ReadOnly Property YouTubeCookieNetscapeFile As New SFile($"Settings\Responser_{YouTubeSite}_Cookies_Netscape.txt") Public ReadOnly Property YouTubeCookieNetscapeFile As New SFile($"Settings\Responser_{YouTubeSite}_Cookies_Netscape.txt")
Friend ReadOnly Property YTDLP_NAME As String
Get
Dim n$ = MyYouTubeSettings.YTDLP.Value.Name
If Not n.IsEmptyString Then
Return If(n.ToLower = YTDLP_DefaultName, n, $"""{n}""")
Else
Return YTDLP_DefaultName
End If
End Get
End Property
Friend ReadOnly Property AvailableSubtitlesFormats As String() Friend ReadOnly Property AvailableSubtitlesFormats As String()
Get Get
Return {"ASS", "LRC", "SRT", "VTT"} Return {"ASS", "LRC", "SRT", "VTT"}
@@ -45,6 +56,19 @@ Namespace API.YouTube
Friend ReadOnly TitleHtmlConverter As Func(Of String, String) = Function(Input) Input.StringRemoveWinForbiddenSymbols().StringTrim() Friend ReadOnly TitleHtmlConverter As Func(Of String, String) = Function(Input) Input.StringRemoveWinForbiddenSymbols().StringTrim()
Friend ReadOnly ProgressProvider As IMyProgressNumberProvider = MyProgressNumberProvider.Percentage Friend ReadOnly ProgressProvider As IMyProgressNumberProvider = MyProgressNumberProvider.Percentage
Public ReadOnly TrueUrlRegEx As RParams = RParams.DM(Base.YouTubeFunctions.TrueUrlPattern, 0, EDP.ReturnValue) Public ReadOnly TrueUrlRegEx As RParams = RParams.DM(Base.YouTubeFunctions.TrueUrlPattern, 0, EDP.ReturnValue)
Friend Function CleanFileName(ByVal f As SFile) As SFile
If Not f.IsEmptyString And Not f.Name.IsEmptyString Then
Dim ff As SFile = f
ff.Name = ff.Name.StringRemoveWinForbiddenSymbols.StringTrim
ff.Name = ff.Name.StringTrimEnd(".")
If Not ff.Name.IsEmptyString And Not MyYouTubeSettings.FileRemoveCharacters.IsEmptyString Then _
ff.Name = ff.Name.StringReplaceSymbols(MyYouTubeSettings.FileRemoveCharacters.Value.AsList.ListCast(Of String).ToArray, String.Empty, EDP.ReturnValue)
If ff.Name.IsEmptyString Then ff.Name = "file"
Return ff
Else
Return f
End If
End Function
Private Class TimeToStringConverter : Implements ICustomProvider Private Class TimeToStringConverter : Implements ICustomProvider
Private ReadOnly _Provider As New ADateTime("mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan} Private ReadOnly _Provider As New ADateTime("mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan}
Private ReadOnly _ProviderWithHours As New ADateTime("h\:mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan} Private ReadOnly _ProviderWithHours As New ADateTime("h\:mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan}

View File

@@ -9,6 +9,7 @@
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.XML.Base Imports PersonalUtilities.Functions.XML.Base
Imports PersonalUtilities.Functions.XML.Attributes Imports PersonalUtilities.Functions.XML.Attributes
Imports PersonalUtilities.Forms
Imports PersonalUtilities.Forms.Controls Imports PersonalUtilities.Forms.Controls
Imports PersonalUtilities.Forms.Controls.Base Imports PersonalUtilities.Forms.Controls.Base
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
@@ -103,7 +104,7 @@ Namespace DownloadObjects.STDownloader
If UseUpdate Then .EndUpdate(True) If UseUpdate Then .EndUpdate(True)
End With End With
End Sub End Sub
Public Sub PopulateComboBox(ByRef CMB As ComboBoxExtended, Optional ByVal Current As SFile = Nothing) Public Sub PopulateComboBox(ByRef CMB As ComboBoxExtended, Optional ByVal Current As SFile = Nothing, Optional ByVal IsFile As Boolean = False)
Locations.Sort() Locations.Sort()
With CMB With CMB
.BeginUpdate() .BeginUpdate()
@@ -124,7 +125,7 @@ Namespace DownloadObjects.STDownloader
.EndUpdate() .EndUpdate()
If Not Current.IsEmptyString And Locations.Count > 0 Then If Not Current.IsEmptyString And Locations.Count > 0 Then
Dim i% = IndexOf(Current.PathWithSeparator) Dim i% = IndexOf(If(IsFile, Current.ToString, Current.PathWithSeparator),, IsFile)
If i.ValueBetween(0, .Items.Count - 1) Then .SelectedIndex = i If i.ValueBetween(0, .Items.Count - 1) Then .SelectedIndex = i
If Current.File.IsEmptyString Then CMB.Text = Current.PathWithSeparator Else CMB.Text = Current If Current.File.IsEmptyString Then CMB.Text = Current.PathWithSeparator Else CMB.Text = Current
End If End If
@@ -141,6 +142,44 @@ Namespace DownloadObjects.STDownloader
End If End If
Return f Return f
End Function End Function
Friend Function ChooseNewPlaylistArray(ByRef CMB As ComboBoxExtended, ByRef Result As Boolean) As IEnumerable(Of SFile)
Try
Dim initFiles As IEnumerable(Of SFile) = Nothing
Dim selectedFiles As IEnumerable(Of SFile) = Nothing
If Count > 0 Then initFiles = Me.Select(Function(l) l.Path.CSFile)
Dim addh As New EventHandler(Of SimpleListFormEventArgs)(Sub(ByVal s As Object, ByVal ee As SimpleListFormEventArgs)
Dim ff As List(Of SFile) = SFile.SelectFiles(,, "Select playlist files", "Playlist|*.m3u;*.m3u8|AllFiles|*.*", EDP.ReturnValue)
If ff.ListExists Then
ee.AddItem(ff.Cast(Of Object))
ee.Result = True
Else
ee.Result = False
End If
End Sub)
Using f As New SimpleListForm(Of SFile)(initFiles, API.YouTube.MyYouTubeSettings.DesignXml) With {
.DesignXMLNodeName = "M3U8SelectorForm",
.FormText = "Playlists",
.Buttons = {ActionButton.DefaultButtons.Add},
.Icon = ImageRenderer.GetIcon(My.Resources.StartPic_Green_16, EDP.ReturnValue),
.AddFunction = addh
}
If f.ShowDialog = DialogResult.OK Then Result = True : selectedFiles = ListAddList(Nothing, f.DataResult, LAP.NotContainsOnly)
End Using
If selectedFiles.ListExists Then
Dim added As Boolean = False
selectedFiles.ListForEach(Sub(ByVal plsFile As SFile, ByVal ii As Integer)
If IndexOf(plsFile.ToString,, True) = -1 Then Add(plsFile.ToString, True, True) : added = True
End Sub)
If added Then PopulateComboBox(CMB, selectedFiles(0).ToString, True)
CMB.Text = selectedFiles(0)
Return selectedFiles
Else
Return Nothing
End If
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Select playlist array")
End Try
End Function
Private Sub Update() Private Sub Update()
If Locations.Count > 0 Then If Locations.Count > 0 Then
Using x As New XmlFile With {.AllowSameNames = True} Using x As New XmlFile With {.AllowSameNames = True}
@@ -158,9 +197,9 @@ Namespace DownloadObjects.STDownloader
Public Overloads Sub Add(ByVal Item As DownloadLocation) Implements ICollection(Of DownloadLocation).Add Public Overloads Sub Add(ByVal Item As DownloadLocation) Implements ICollection(Of DownloadLocation).Add
Add(Item, True) Add(Item, True)
End Sub End Sub
Public Overloads Sub Add(ByVal Item As DownloadLocation, ByVal AskForName As Boolean) Public Overloads Sub Add(ByVal Item As DownloadLocation, ByVal AskForName As Boolean, Optional ByVal IsFile As Boolean = False)
If Not Item.Path.IsEmptyString Then If Not Item.Path.IsEmptyString Then
Dim i% = IndexOf(Item) Dim i% = IndexOf(Item,, IsFile)
Dim processUpdate As Boolean = True Dim processUpdate As Boolean = True
If i >= 0 Then If i >= 0 Then
If Locations(i).Model = Item.Model Then If Locations(i).Model = Item.Model Then
@@ -183,8 +222,12 @@ Namespace DownloadObjects.STDownloader
Public Function Contains(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Contains Public Function Contains(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Contains
Return Not Item.Path.IsEmptyString AndAlso Locations.Contains(Item) Return Not Item.Path.IsEmptyString AndAlso Locations.Contains(Item)
End Function End Function
Public Function IndexOf(ByVal Item As DownloadLocation, Optional ByVal IgnoreModel As Boolean = False) As Integer Public Function IndexOf(ByVal Item As DownloadLocation, Optional ByVal IgnoreModel As Boolean = False, Optional ByVal IsFile As Boolean = False) As Integer
If Not IsFile Then
Return Locations.FindIndex(Function(d) d.Path = Item.Path And (d.Model = Item.Model Or IgnoreModel)) Return Locations.FindIndex(Function(d) d.Path = Item.Path And (d.Model = Item.Model Or IgnoreModel))
Else
Return Locations.FindIndex(Function(d) d.Path = Item.Path)
End If
End Function End Function
Public Function Remove(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Remove Public Function Remove(ByVal Item As DownloadLocation) As Boolean Implements ICollection(Of DownloadLocation).Remove
If Locations.Remove(Item) Then If Locations.Remove(Item) Then

View File

@@ -12,6 +12,7 @@ Imports SCrawler.API.YouTube.Objects
Imports SCrawler.API.YouTube.Controls Imports SCrawler.API.YouTube.Controls
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Functions.Messaging
Namespace DownloadObjects.STDownloader Namespace DownloadObjects.STDownloader
Public Delegate Sub MediaItemEventHandler(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer) Public Delegate Sub MediaItemEventHandler(ByVal Sender As MediaItem, ByVal Container As IYouTubeMediaContainer)
<DefaultEvent("DoubleClick"), DesignTimeVisible(False), ToolboxItem(False)> <DefaultEvent("DoubleClick"), DesignTimeVisible(False), ToolboxItem(False)>
@@ -135,7 +136,7 @@ Namespace DownloadObjects.STDownloader
LBL_TITLE.Text = .ToString(True) LBL_TITLE.Text = .ToString(True)
If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then
LBL_INFO.Text = .File.Extension.StringToUpper LBL_INFO.Text = .File.Extension.StringToUpper
ElseIf Not .IsMusic Then ElseIf Not .IsMusic And Not (.MediaType = Plugin.UserMediaTypes.Audio Or .MediaType = Plugin.UserMediaTypes.AudioPre) Then
If .Height > 0 Then If .Height > 0 Then
LBL_INFO.Text = $"{ .File.Extension.StringToUpper}{d}{ .Height}p" LBL_INFO.Text = $"{ .File.Extension.StringToUpper}{d}{ .Height}p"
Else Else
@@ -180,10 +181,10 @@ Namespace DownloadObjects.STDownloader
With MyContainer With MyContainer
If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then If Not .SiteKey = YouTubeSiteKey And .ContentType = Plugin.UserMediaTypes.Picture Then
ICON_WHAT.Image = My.Resources.ImagePic_32 ICON_WHAT.Image = My.Resources.ImagePic_32
ElseIf Not .IsMusic Then ElseIf .IsMusic Or .MediaType = Plugin.UserMediaTypes.Audio Or .MediaType = Plugin.UserMediaTypes.AudioPre Then
ICON_WHAT.Image = My.Resources.VideoCamera_32
Else
ICON_WHAT.Image = My.Resources.AudioMusic_32 ICON_WHAT.Image = My.Resources.AudioMusic_32
Else
ICON_WHAT.Image = My.Resources.VideoCamera_32
End If End If
End With End With
End Sub, EDP.None) End Sub, EDP.None)
@@ -229,7 +230,7 @@ Namespace DownloadObjects.STDownloader
.ColumnStyles.Clear() .ColumnStyles.Clear()
.ColumnCount = 0 .ColumnCount = 0
If ContainerHasElements Or MyContainer.MediaState = Plugin.UserMediaStates.Downloaded Then If ContainerHasElements Or MyContainer.MediaState = Plugin.UserMediaStates.Downloaded Then
If Not MyContainer.SiteKey = YouTubeSiteKey Then UpdateMediaIcon() UpdateMediaIcon()
If ContainerHasElements Then If ContainerHasElements Then
BTT_OPEN_FOLDER.Visible = False BTT_OPEN_FOLDER.Visible = False
BTT_OPEN_FILE.Visible = False BTT_OPEN_FILE.Visible = False
@@ -476,12 +477,28 @@ Namespace DownloadObjects.STDownloader
RaiseEvent Removal(Me, MyContainer) RaiseEvent Removal(Me, MyContainer)
End Sub End Sub
Private Sub BTT_DELETE_FILE_Click(sender As Object, e As EventArgs) Handles BTT_DELETE_FILE.Click Private Sub BTT_DELETE_FILE_Click(sender As Object, e As EventArgs) Handles BTT_DELETE_FILE.Click
If MsgBoxE({$"Are you sure you want to delete the following {FileOption.ToString.ToLower}:{vbCr}" & Dim opt$
If(FileOption = SFO.File, MyContainer.File.ToString, MyContainer.File.PathWithSeparator), Dim opt2$ = String.Empty
$"Deleting a {FileOption.ToString.ToLower}"}, vbExclamation,,, {"Process", "Cancel"}) = 0 Then If FileOption = SFO.File Then
opt = "file"
Else
opt = "item"
opt2 = "THE ITEM MAY CONTAIN MULTIPLE FILES" & vbCr
End If
Dim b As New List(Of MsgBoxButton) From {New MsgBoxButton("Process")}
If Not opt2.IsEmptyString Then _
b.Add(New MsgBoxButton("Show files", "Show files to delete") With {
.IsDialogResultButton = False,
.CallBack = Function(r, m, bb) MsgBoxE(New MMessage($"The following files will be deleted:{vbCr}{vbCr}{MyContainer.Files.ListToString(vbCr)}",
"Files to delete",, vbExclamation) With {.Editable = True})})
b.Add(New MsgBoxButton("Cancel"))
If MsgBoxE({$"Are you sure you want to delete the following {opt}:{vbCr}{opt2}" &
If(FileOption = SFO.File, MyContainer.File.ToString, MyContainer.ToString(True)),
$"Deleting {opt}"}, vbExclamation,,, b) = 0 Then
MyContainer.Delete(True) MyContainer.Delete(True)
RaiseEvent Removal(Me, MyContainer) RaiseEvent Removal(Me, MyContainer)
End If End If
b.Clear()
End Sub End Sub
#End Region #End Region
#Region "ISupportInitialize Support" #Region "ISupportInitialize Support"

View File

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

View File

@@ -19,17 +19,18 @@ Namespace API.YouTube.Objects
Dim __title$ = $" - {Title}" Dim __title$ = $" - {Title}"
If Not s.IsEmptyString Then s = $" [{s}]" If Not s.IsEmptyString Then s = $" [{s}]"
If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t = $"{PlaylistTitle} - " If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t = $"{PlaylistTitle} - "
Dim c% = {Count, ElementsNumber}.Max
If IsMusic Then If IsMusic Then
If Count <= 1 Then t &= "Single" Else t &= "Album" If c <= 1 Then t &= "Single" Else t &= "Album"
Else Else
t &= "Playlist" t &= "Playlist"
End If End If
If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t &= $" - {PlaylistTitle}" If Not PlaylistTitle.IsEmptyString And Not ForMediaItem Then t &= $" - {PlaylistTitle}"
If PlaylistTitle = Title Then __title = String.Empty If PlaylistTitle = Title Then __title = String.Empty
If ForMediaItem Then If ForMediaItem Then
Return $"{t} ({Count}){__title}" Return $"{t} ({c}){__title}"
Else Else
Return $"{t} ({Count}){__title} ({AConvert(Of String)(Duration, TimeToStringProvider)}){s}" Return $"{t} ({c}){__title} ({AConvert(Of String)(Duration, TimeToStringProvider)}){s}"
End If End If
End Function End Function
Public Overrides Function Parse(ByVal Container As EContainer, ByVal Path As SFile, ByVal IsMusic As Boolean, Public Overrides Function Parse(ByVal Container As EContainer, ByVal Path As SFile, ByVal IsMusic As Boolean,

View File

@@ -27,6 +27,7 @@ Namespace API.YouTube.Objects
Else Else
_File.Extension = mp3 _File.Extension = mp3
End If End If
_File = CleanFileName(_File)
End If End If
End Sub End Sub
Public Overrides Function ToString(ByVal ForMediaItem As Boolean) As String Public Overrides Function ToString(ByVal ForMediaItem As Boolean) As String
@@ -46,12 +47,17 @@ Namespace API.YouTube.Objects
_ObjectType = Base.YouTubeMediaType.Single _ObjectType = Base.YouTubeMediaType.Single
Me.IsMusic = IsMusic Me.IsMusic = IsMusic
If MyBase.Parse(Container, Path, IsMusic, Token, Progress) Then If MyBase.Parse(Container, Path, IsMusic, Token, Progress) Then
Dim f As SFile = MyYouTubeSettings.OutputPath With MyYouTubeSettings
Dim f As SFile = .OutputPath
If f.IsEmptyString Then f = "YouTubeDownloads\OutputFile.mp3" If f.IsEmptyString Then f = "YouTubeDownloads\OutputFile.mp3"
Dim ext$ = MyYouTubeSettings.DefaultAudioCodec.Value.StringToLower Dim ext$ = .DefaultAudioCodecMusic.Value.StringToLower.IfNullOrEmpty(.DefaultAudioCodec.Value.StringToLower)
If ext.IsEmptyString Then ext = "mp3" If ext.IsEmptyString Then ext = "mp3"
f.Extension = ext f.Extension = ext
'If f.Name.IsEmptyString Then f.Name = File.Name
File = f File = f
If _File.Extension.IsEmptyString Then _File.Extension = ext
_File = CleanFileName(_File)
End With
Return True Return True
Else Else
Return False Return False

View File

@@ -123,6 +123,15 @@ Namespace API.YouTube.Objects
<XMLEC> Public Property UserTitle As String Implements IYouTubeMediaContainer.UserTitle <XMLEC> Public Property UserTitle As String Implements IYouTubeMediaContainer.UserTitle
#End Region #End Region
#Region "Playlist support" #Region "Playlist support"
Private _ElementsNumber As Integer = 0
<XMLEC> Protected Property ElementsNumber As Integer
Get
Return If(HasElements, Count, _ElementsNumber)
End Get
Set(ByVal _ElementsNumber As Integer)
Me._ElementsNumber = _ElementsNumber
End Set
End Property
Friend ReadOnly Property Elements As List(Of IYouTubeMediaContainer) Implements IYouTubeMediaContainer.Elements Friend ReadOnly Property Elements As List(Of IYouTubeMediaContainer) Implements IYouTubeMediaContainer.Elements
Friend ReadOnly Property HasElements As Boolean Implements IYouTubeMediaContainer.HasElements Friend ReadOnly Property HasElements As Boolean Implements IYouTubeMediaContainer.HasElements
Get Get
@@ -265,6 +274,18 @@ Namespace API.YouTube.Objects
PostProcessing_OutputAudioFormats.RemoveAll(Function(s) s = -1) PostProcessing_OutputAudioFormats.RemoveAll(Function(s) s = -1)
End If End If
End Sub End Sub
<XMLEC("OutputAudioBitrate")> Protected _OutputAudioBitrate As Integer = -1
Friend Property OutputAudioBitrate As Integer
Get
Return _OutputAudioBitrate
End Get
Set(ByVal NewBitrate As Integer)
If Not [Protected] Then
_OutputAudioBitrate = NewBitrate
If HasElements Then Elements.ForEach(Sub(elem) DirectCast(elem, YouTubeMediaContainerBase).OutputAudioBitrate = NewBitrate)
End If
End Set
End Property
#End Region #End Region
#Region "Subtitles" #Region "Subtitles"
Protected ReadOnly _Subtitles As List(Of Subtitles) Protected ReadOnly _Subtitles As List(Of Subtitles)
@@ -376,10 +397,13 @@ Namespace API.YouTube.Objects
End Set End Set
End Property End Property
Protected _Size As Integer = 0 Protected _Size As Integer = 0
<XMLEC("SizeRecalculated")> Protected _SizeRecalculated As Boolean = False
<XMLEC> Public Overridable Property Size As Integer Implements IDownloadableMedia.Size <XMLEC> Public Overridable Property Size As Integer Implements IDownloadableMedia.Size
Get Get
If HasElements Then If HasElements Then
Return Elements.Sum(Function(e) If(e.Checked, e.Size, 0)) Return Elements.Sum(Function(e) If(e.Checked, e.Size, 0))
ElseIf _SizeRecalculated Then
Return _Size
Else Else
If Checked Then If Checked Then
If IsMusic And SelectedAudioIndex.ValueBetween(0, MediaObjects.Count - 1) Then If IsMusic And SelectedAudioIndex.ValueBetween(0, MediaObjects.Count - 1) Then
@@ -559,7 +583,25 @@ Namespace API.YouTube.Objects
If ObjectType = YouTubeMediaType.Single AndAlso Not GetPlayListTitle.IsEmptyString Then _SpecialPath.StringAppend(GetPlayListTitle(), "\") If ObjectType = YouTubeMediaType.Single AndAlso Not GetPlayListTitle.IsEmptyString Then _SpecialPath.StringAppend(GetPlayListTitle(), "\")
If Elements.Count > 0 Then Elements.ForEach(Sub(e) e.SpecialFolder = Path) If Elements.Count > 0 Then Elements.ForEach(Sub(e) e.SpecialFolder = Path)
End Sub End Sub
<XMLEC> Protected Friend ReadOnly Property Files As List(Of SFile) Implements IYouTubeMediaContainer.Files Private ReadOnly _Files As List(Of SFile)
<XMLEC> Protected Friend Property Files As List(Of SFile) Implements IYouTubeMediaContainer.Files
Get
If HasElements Then
Return GetFilesFiles()
Else
Return _Files
End If
End Get
Set(ByVal f As List(Of SFile))
_Files.ListAddList(f, LAP.NotContainsOnly)
End Set
End Property
Protected Overloads Sub AddFile(ByVal f As SFile)
_Files.ListAddValue(f, LAP.NotContainsOnly)
End Sub
Protected Overloads Sub AddFile(ByVal f As IEnumerable(Of SFile))
_Files.ListAddList(f, LAP.NotContainsOnly)
End Sub
<XMLEC> Protected _File As SFile <XMLEC> Protected _File As SFile
<XMLEC> Protected Friend Property FileSetManually As Boolean = False <XMLEC> Protected Friend Property FileSetManually As Boolean = False
Public Property FileIgnorePlaylist As Boolean = False Public Property FileIgnorePlaylist As Boolean = False
@@ -628,6 +670,26 @@ Namespace API.YouTube.Objects
If HasElements And Not IsMusic Then urls.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetFiles()), LAP.NotContainsOnly) If HasElements And Not IsMusic Then urls.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetFiles()), LAP.NotContainsOnly)
Return urls Return urls
End Function End Function
Private Function GetFilesFiles() As IEnumerable(Of SFile)
Dim f As New List(Of SFile)
If File.Exists Then f.Add(File)
If _Files.Count > 0 Then f.AddRange(_Files)
If ThumbnailFile.Exists Then f.Add(ThumbnailFile)
If HasElements Then f.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetFilesFiles()), LAP.NotContainsOnly)
Return f
End Function
Private _M3U8_PlaylistFiles As IEnumerable(Of SFile) = Nothing
Friend Property M3U8_PlaylistFiles As IEnumerable(Of SFile)
Get
Return _M3U8_PlaylistFiles
End Get
Set(ByVal f As IEnumerable(Of SFile))
If Not [Protected] Then
_M3U8_PlaylistFiles = f
If HasElements Then Elements.ForEach(Sub(e As YouTubeMediaContainerBase) e.M3U8_PlaylistFiles = f)
End If
End Set
End Property
#End Region #End Region
#Region "Command" #Region "Command"
<XMLEC> Public Property UseCookies As Boolean = MyYouTubeSettings.DefaultUseCookies Implements IYouTubeMediaContainer.UseCookies <XMLEC> Public Property UseCookies As Boolean = MyYouTubeSettings.DefaultUseCookies Implements IYouTubeMediaContainer.UseCookies
@@ -635,6 +697,7 @@ Namespace API.YouTube.Objects
Private Const aac As String = "aac" Private Const aac As String = "aac"
Private Const ac3 As String = "ac3" Private Const ac3 As String = "ac3"
Protected PostProcessing_AudioAC3 As Boolean = False Protected PostProcessing_AudioAC3 As Boolean = False
Protected PostProcessing_AudioMP3 As Boolean = False
Public Overridable ReadOnly Property Command(ByVal WithCookies As Boolean) As String Implements IYouTubeMediaContainer.Command Public Overridable ReadOnly Property Command(ByVal WithCookies As Boolean) As String Implements IYouTubeMediaContainer.Command
Get Get
If Not File.IsEmptyString Then If Not File.IsEmptyString Then
@@ -646,7 +709,7 @@ Namespace API.YouTube.Objects
Bitrate = 0 Bitrate = 0
_MediaType = UMTypes.Undefined _MediaType = UMTypes.Undefined
If SelectedVideoIndex >= 0 Then If SelectedVideoIndex >= 0 Then
'URGENT: 2023.3.4 -> 2023.7.6 '2023.3.4 -> 2023.7.6
'cmd.StringAppend($"bv*[format_id={SelectedVideo.ID}]") 'cmd.StringAppend($"bv*[format_id={SelectedVideo.ID}]")
cmd.StringAppend(SelectedVideo.ID) cmd.StringAppend(SelectedVideo.ID)
_Size = SelectedVideo.Size _Size = SelectedVideo.Size
@@ -663,13 +726,17 @@ Namespace API.YouTube.Objects
End If End If
If SelectedAudioIndex >= 0 Then If SelectedAudioIndex >= 0 Then
Dim atCodec$ Dim atCodec$
'URGENT: 2023.3.4 -> 2023.7.6 '2023.3.4 -> 2023.7.6
'cmd.StringAppend($"ba*[format_id={SelectedAudio.ID}]", "+") 'cmd.StringAppend($"ba*[format_id={SelectedAudio.ID}]", "+")
cmd.StringAppend(SelectedAudio.ID, "+") cmd.StringAppend(SelectedAudio.ID, "+")
If OutputAudioCodec.StringToLower = ac3 Then If OutputAudioCodec.StringToLower = ac3 Then
PostProcessing_AudioAC3 = True PostProcessing_AudioAC3 = True
formats.StringAppend($"--audio-format {aac}", " ") formats.StringAppend($"--audio-format {aac}", " ")
atCodec = aac atCodec = aac
ElseIf SelectedVideoIndex >= 0 And OutputAudioCodec.StringToLower = mp3 Then
PostProcessing_AudioMP3 = True
formats.StringAppend($"--audio-format {aac}", " ")
atCodec = aac
Else Else
formats.StringAppend($"--audio-format {OutputAudioCodec.StringToLower}", " ") formats.StringAppend($"--audio-format {OutputAudioCodec.StringToLower}", " ")
atCodec = OutputAudioCodec.StringToLower atCodec = OutputAudioCodec.StringToLower
@@ -702,9 +769,10 @@ Namespace API.YouTube.Objects
subs = $"--write-subs --write-auto-subs --sub-format {OutputSubtitlesFormat.StringToLower} --sub-langs ""{subs}"" --convert-subs {OutputSubtitlesFormat.StringToLower}" subs = $"--write-subs --write-auto-subs --sub-format {OutputSubtitlesFormat.StringToLower} --sub-langs ""{subs}"" --convert-subs {OutputSubtitlesFormat.StringToLower}"
End If End If
If Not cmd.IsEmptyString Then If Not cmd.IsEmptyString Then
'URGENT: 2023.3.4 -> 2023.7.6 '2023.3.4 -> 2023.7.6
'cmd = $"yt-dlp -f ""{cmd}""" 'cmd = $"yt-dlp -f ""{cmd}"""
cmd = $"yt-dlp -f {cmd}" 'cmd = $"yt-dlp -f {cmd}"
cmd = $"{YTDLP_NAME} -f {cmd}"
If Not MyYouTubeSettings.ReplaceModificationDate Then cmd &= " --no-mtime" If Not MyYouTubeSettings.ReplaceModificationDate Then cmd &= " --no-mtime"
cmd.StringAppend(formats, " ") cmd.StringAppend(formats, " ")
cmd.StringAppend(subs, " ") cmd.StringAppend(subs, " ")
@@ -726,7 +794,7 @@ Namespace API.YouTube.Objects
_SubtitlesDelegated = New List(Of Subtitles) _SubtitlesDelegated = New List(Of Subtitles)
SubtitlesSelectedIndexes = New List(Of Integer) SubtitlesSelectedIndexes = New List(Of Integer)
MediaObjects = New List(Of MediaObject) MediaObjects = New List(Of MediaObject)
Files = New List(Of SFile) _Files = New List(Of SFile)
PostProcessing_OutputSubtitlesFormats = New List(Of String) PostProcessing_OutputSubtitlesFormats = New List(Of String)
PostProcessing_OutputSubtitlesFormats.ListAddList(MyYouTubeSettings.DefaultSubtitlesFormatAddit) PostProcessing_OutputSubtitlesFormats.ListAddList(MyYouTubeSettings.DefaultSubtitlesFormatAddit)
@@ -755,9 +823,19 @@ Namespace API.YouTube.Objects
If RemoveFiles Then If RemoveFiles Then
Dim fErr As New ErrorsDescriber(EDP.None) Dim fErr As New ErrorsDescriber(EDP.None)
Dim dMode As SFODelete = SFODelete.DeleteToRecycleBin Dim dMode As SFODelete = SFODelete.DeleteToRecycleBin
Dim paths As New List(Of SFile)
Dim l As New ListAddParams(LAP.NotContainsOnly) With {.Comparer = New FComparer(Of SFile)(Function(x, y) x.PathNoSeparator = y.PathNoSeparator)}
Dim isArr As Boolean = ObjectType <> YouTubeMediaType.Single And ObjectType <> YouTubeMediaType.Undefined
If isArr And AbsolutePath Then paths.ListAddValue(File, l)
File.Delete(SFO.File, dMode, fErr) File.Delete(SFO.File, dMode, fErr)
If isArr Then paths.ListAddValue(ThumbnailFile, l)
ThumbnailFile.Delete(SFO.File, dMode, fErr) ThumbnailFile.Delete(SFO.File, dMode, fErr)
If Files.Count > 0 Then Files.ForEach(Sub(f) f.Delete(SFO.File, dMode, fErr)) If Files.Count > 0 Then
If isArr Then paths.ListAddList(Files, l)
Files.ForEach(Sub(f) f.Delete(SFO.File, dMode, fErr))
End If
If paths.Count > 0 Then paths.ForEach(Sub(p) If SFile.GetFiles(p,, IO.SearchOption.AllDirectories, EDP.ReturnValue).Count = 0 Then _
p.Delete(SFO.Path, dMode, EDP.SendToLog))
End If End If
If HasElements Then Elements.ForEach(Sub(e) e.Delete(RemoveFiles)) If HasElements Then Elements.ForEach(Sub(e) e.Delete(RemoveFiles))
End Sub End Sub
@@ -788,6 +866,23 @@ Namespace API.YouTube.Objects
Return Nothing Return Nothing
End Try End Try
End Function End Function
Private Function GetPlaylistRow(ByVal Element As YouTubeMediaContainerBase, Optional ByVal __file As SFile = Nothing) As String
Const m3u8DataRow$ = "#EXTINF:{0},{1}" & vbCrLf & "{2}"
With Element
Dim f As SFile = __file.IfNullOrEmpty(.File)
Dim fName$ = .Title.IfNullOrEmpty(f.Name)
If MyYouTubeSettings.MusicPlaylistCreate_M3U8_AppendNumber And .PlaylistIndex > 0 Then fName = $"{ .PlaylistIndex}. {fName}"
If Not .UserTitle.IsEmptyString Then
fName = $"{ .UserTitle} - {fName}"
If MyYouTubeSettings.MusicPlaylistCreate_M3U8_AppendArtist Then fName = $"{ .UserTitle} - {fName}"
End If
If MyYouTubeSettings.MusicPlaylistCreate_M3U8_AppendExt Then fName &= $".{f.Extension}"
Return String.Format(m3u8DataRow,
CInt(.Duration.TotalSeconds),
fName,
$"file:///{SymbolsConverter.ASCII.EncodeSymbolsOnly(f)}")
End With
End Function
Private ReadOnly DownloadProgressPattern As RParams = RParams.DMS("\[download\]\s*([\d\.,]+)", 1, EDP.ReturnValue) Private ReadOnly DownloadProgressPattern As RParams = RParams.DMS("\[download\]\s*([\d\.,]+)", 1, EDP.ReturnValue)
Public Property Progress As MyProgress Implements IYouTubeMediaContainer.Progress Public Property Progress As MyProgress Implements IYouTubeMediaContainer.Progress
Private Property IDownloadableMedia_Progress As Object Implements IDownloadableMedia.Progress Private Property IDownloadableMedia_Progress As Object Implements IDownloadableMedia.Progress
@@ -822,6 +917,32 @@ Namespace API.YouTube.Objects
DownloadCommand(UseCookies, Token) DownloadCommand(UseCookies, Token)
Else Else
DownloadCommandArray(UseCookies, Token) DownloadCommandArray(UseCookies, Token)
If HasElements AndAlso Elements(0).ObjectType = YouTubeMediaType.Single AndAlso Elements(0).IsMusic Then
Dim t As TextSaver = Nothing
Try
Dim f As SFile
If MyYouTubeSettings.MusicPlaylistCreate_M3U8 Then
t = New TextSaver
t.AppendLine("#EXTM3U")
Elements.ForEach(Sub(e) t.AppendLine(GetPlaylistRow(e)))
f = $"{Elements(0).File.PathWithSeparator}Playlist.m3u8"
t.SaveAs(f, EDP.SendToLog)
If f.Exists Then AddFile(f)
t.Dispose()
End If
If MyYouTubeSettings.MusicPlaylistCreate_M3U Then
t = New TextSaver
Elements.ForEach(Sub(e) t.AppendLine(e.File))
f = $"{Elements(0).File.PathWithSeparator}Playlist.m3u"
t.SaveAs(f, EDP.SendToLog)
If f.Exists Then AddFile(f)
t.Dispose()
End If
Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[YouTubeMediaContainerBase.Download.CreatePlaylist]")
End Try
t.DisposeIfReady
End If
End If End If
RaiseEvent DataDownloaded(Me, Nothing) RaiseEvent DataDownloaded(Me, Nothing)
End Sub End Sub
@@ -891,7 +1012,7 @@ Namespace API.YouTube.Objects
ff.Name = "album" ff.Name = "album"
ff.Extension = "url" ff.Extension = "url"
CreateUrlFile(url, ff) CreateUrlFile(url, ff)
If ff.Exists Then Files.Add(ff) If ff.Exists Then AddFile(ff)
End If End If
If MyYouTubeSettings.CreateThumbnails_Music Then If MyYouTubeSettings.CreateThumbnails_Music Then
Using resp As New Responser Using resp As New Responser
@@ -917,7 +1038,7 @@ Namespace API.YouTube.Objects
url = LinkFormatterSecure(u) url = LinkFormatterSecure(u)
f.Name = "cover" f.Name = "cover"
f.Extension = "jpg" f.Extension = "jpg"
If resp.DownloadFile(url, f, EDP.ReturnValue) And f.Exists Then CoverDownloaded = True If resp.DownloadFile(url, f, EDP.ReturnValue) And f.Exists Then CoverDownloaded = True : AddFile(f)
End If End If
End If End If
End Using End Using
@@ -926,19 +1047,61 @@ Namespace API.YouTube.Objects
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"DownloadPlaylistCover({PlsId}, {f})") ErrorsDescriber.Execute(EDP.SendToLog, ex, $"DownloadPlaylistCover({PlsId}, {f})")
End Try End Try
End Sub End Sub
Private Structure TempFileConversion
Friend File As SFile
Friend Requested As Boolean
Friend ToReplace As Boolean
Friend ReadOnly Property Exists As Boolean
Get
Return File.Exists
End Get
End Property
Friend Sub Delete()
If Not Requested Then File.Delete()
End Sub
Private Sub New(ByVal f As SFile)
File = f
Requested = False
ToReplace = False
End Sub
Friend Sub New(ByVal f As SFile, ByVal Source As YouTubeMediaContainerBase)
Me.New(f)
Requested = Source.PostProcessing_OutputAudioFormats.Count > 0 AndAlso
Source.PostProcessing_OutputAudioFormats.Exists(Function(af) af.StringToLower = f.Extension)
End Sub
Public Shared Widening Operator CType(ByVal f As SFile) As TempFileConversion
Return New TempFileConversion(f)
End Operator
Public Shared Widening Operator CType(ByVal f As TempFileConversion) As SFile
Return f.File
End Operator
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
If Not IsNothing(Obj) Then
If TypeOf Obj Is TempFileConversion Then
Return DirectCast(Obj, TempFileConversion).File = File
ElseIf TypeOf Obj Is SFile Then
Return DirectCast(Obj, SFile) = File
ElseIf TypeOf Obj Is String Then
Return New TempFileConversion(CStr(Obj)).File = File
End If
End If
Return False
End Function
End Structure
Protected Sub DownloadCommand(ByVal UseCookies As Boolean, ByVal Token As CancellationToken) Protected Sub DownloadCommand(ByVal UseCookies As Boolean, ByVal Token As CancellationToken)
Dim dCommand$ = String.Empty Dim dCommand$ = String.Empty
Try Try
ThrowAny(Token) ThrowAny(Token)
If MediaState = UMStates.Downloaded Or Not Checked Then Exit Sub If MediaState = UMStates.Downloaded Or Not Checked Then Exit Sub
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) Dim h As DataReceivedEventHandler = Sub(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
If Not e.Data.IsEmptyString Then If Not e.Data.IsEmptyString Then
Dim v# = AConvert(Of Double)(RegexReplace(e.Data, DownloadProgressPattern), NumberProvider, -1) Dim v# = AConvert(Of Double)(RegexReplace(e.Data, DownloadProgressPattern), NumberProvider, -1)
If v >= 0 Then Progress.Value = v : Progress.Perform(0) If v >= 0 Then Progress.Value = v : Progress.Perform(0)
If Token.IsCancellationRequested Then batch.Kill()
End If End If
End Sub End Sub
RaiseEvent FileDownloadStarted(Me, Nothing)
Using batch As New BatchExecutor(True) With {.Encoding = 65001}
With batch With batch
Dim prExists As Boolean = Not Progress Is Nothing Dim prExists As Boolean = Not Progress Is Nothing
If prExists Then If prExists Then
@@ -951,7 +1114,7 @@ Namespace API.YouTube.Objects
.Information = $"Download {MediaType}" .Information = $"Download {MediaType}"
End With End With
End If End If
.MainProcessName = "yt-dlp" .MainProcessName = MyYouTubeSettings.YTDLP.Name '"yt-dlp"
.FileExchanger = MyCache.NewInstance(Of BatchFileExchanger)(CachePath, EDP.ReturnValue) .FileExchanger = MyCache.NewInstance(Of BatchFileExchanger)(CachePath, EDP.ReturnValue)
.FileExchanger.DeleteCacheOnDispose = True .FileExchanger.DeleteCacheOnDispose = True
.AddCommand("chcp 65001") .AddCommand("chcp 65001")
@@ -971,18 +1134,20 @@ Namespace API.YouTube.Objects
If Not File.Exists Then _File.Name = File.File If Not File.Exists Then _File.Name = File.File
If File.Exists Then If File.Exists Then
M3U8_Append()
If DownloadObjects.STDownloader.MyDownloaderSettings.CreateUrlFiles Then If DownloadObjects.STDownloader.MyDownloaderSettings.CreateUrlFiles Then
Dim fileUrl As SFile = File Dim fileUrl As SFile = File
fileUrl.Extension = "url" fileUrl.Extension = "url"
CreateUrlFile(URL, fileUrl) CreateUrlFile(URL, fileUrl)
If fileUrl.Exists Then Files.Add(fileUrl) If fileUrl.Exists Then AddFile(fileUrl)
End If End If
If MyYouTubeSettings.CreateDescriptionFiles And Not Description.IsEmptyString Then If MyYouTubeSettings.CreateDescriptionFiles And Not Description.IsEmptyString Then
Dim fileDesr As SFile = File Dim fileDesr As SFile = File
fileDesr.Extension = "txt" fileDesr.Extension = "txt"
TextSaver.SaveTextToFile(Description, fileDesr,,, EDP.None) TextSaver.SaveTextToFile(Description, fileDesr,,, EDP.None)
If fileDesr.Exists Then Files.Add(fileDesr) If fileDesr.Exists Then AddFile(fileDesr)
End If End If
If PlaylistCount > 0 And Not CoverDownloaded And Not PlaylistID.IsEmptyString Then DownloadPlaylistCover(PlaylistID, File, UseCookies) If PlaylistCount > 0 And Not CoverDownloaded And Not PlaylistID.IsEmptyString Then DownloadPlaylistCover(PlaylistID, File, UseCookies)
@@ -1006,65 +1171,147 @@ Namespace API.YouTube.Objects
Dim format$ Dim format$
Dim fPattern$ = $"{File.PathWithSeparator}{File.Name}." & "{0}" Dim fPattern$ = $"{File.PathWithSeparator}{File.Name}." & "{0}"
Dim fPatternFiles$ = $"{File.Name}*." & "{0}" Dim fPatternFiles$ = $"{File.Name}*." & "{0}"
Dim fAacAudio As New SFile(String.Format(fPattern, aac)) Dim fAacAudio As New TempFileConversion(New SFile(String.Format(fPattern, aac)), Me)
Dim fAc3Audio As New SFile(String.Format(fPattern, ac3)) Dim mp3ThumbEmbedded As Boolean = False
Dim aacRequested As Boolean = PostProcessing_OutputAudioFormats.Count > 0 AndAlso
PostProcessing_OutputAudioFormats.Exists(Function(af) af.StringToLower = aac)
Dim ac3Requested As Boolean = PostProcessing_OutputAudioFormats.Count > 0 AndAlso
PostProcessing_OutputAudioFormats.Exists(Function(af) af.StringToLower = ac3)
Dim tempFilesList As New List(Of TempFileConversion)
Dim ttFile As TempFileConversion
Dim __updateBitrate As Boolean = OutputAudioBitrate > 0 AndAlso (SelectedAudioIndex = -1 OrElse SelectedAudio.Bitrate <> OutputAudioBitrate)
If __updateBitrate Then Bitrate = OutputAudioBitrate
Dim updateBitrate As Action(Of SFile) =
Sub(ByVal sourceFile As SFile)
If __updateBitrate AndAlso sourceFile.Exists Then
Dim destFile As SFile = sourceFile
destFile.Name &= "_new00"
.Execute($"ffmpeg -i ""{sourceFile}"" -crf {MyYouTubeSettings.DefaultAudioBitrate_crf.Value} -b:a {OutputAudioBitrate}k ""{destFile}""")
If destFile.Exists AndAlso sourceFile.Delete Then SFile.Rename(destFile, sourceFile)
End If
End Sub
Dim __getAAC_tried As Boolean = False
Dim AACExists As Func(Of Boolean) = Function() As Boolean
If Not __getAAC_tried Then
__getAAC_tried = True
.Execute($"ffmpeg -i ""{File}"" -vn -acodec {aac} ""{fAacAudio.File}""")
tempFilesList.Add(fAacAudio)
updateBitrate.Invoke(fAacAudio.File)
End If
Return fAacAudio.Exists
End Function
Dim tryToConvert As Action(Of String, SFile) =
Sub(ByVal codec As String, ByVal dFile As SFile)
ThrowAny(Token) ThrowAny(Token)
If PostProcessing_OutputSubtitlesFormats.Count > 0 Then .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 SubtitlesSelectedIndexes.Count > 0 And Not OutputSubtitlesFormat.IsEmptyString Then
files = SFile.GetFiles(File, String.Format(fPatternFiles, OutputSubtitlesFormat.StringToLower),, EDP.ReturnValue) files = SFile.GetFiles(File, String.Format(fPatternFiles, OutputSubtitlesFormat.StringToLower),, EDP.ReturnValue)
If files.ListExists Then If files.ListExists Then
AddFile(files)
If PostProcessing_OutputSubtitlesFormats.Count > 0 Then
For Each f In files For Each f In files
For Each format In PostProcessing_OutputSubtitlesFormats For Each format In PostProcessing_OutputSubtitlesFormats
format = format.StringToLower format = format.StringToLower
commandFile = $"{f.PathWithSeparator}{f.Name}.{format}" commandFile = $"{f.PathWithSeparator}{f.Name}.{format}"
Me.Files.Add(commandFile) AddFile(commandFile)
ThrowAny(Token) ThrowAny(Token)
.Execute($"ffmpeg -i ""{f}"" ""{commandFile}""") .Execute($"ffmpeg -i ""{f}"" ""{commandFile}""")
Next Next
Next Next
End If End If
End If End If
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}""")
End If End If
'Audio
ThrowAny(Token)
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 If PostProcessing_OutputAudioFormats.Count > 0 Then
For Each format In PostProcessing_OutputAudioFormats For Each format In PostProcessing_OutputAudioFormats
format = format.StringToLower format = format.StringToLower
f = String.Format(fPattern, format) f = String.Format(fPattern, format)
Me.Files.Add(f) AddFile(f)
If Not format = ac3 Or Not f.Exists Then ThrowAny(Token) : .Execute($"ffmpeg -i ""{fAacAudio}"" -f {format} ""{f}""") If Not f.Exists Then
tryToConvert.Invoke(format, f)
updateBitrate(f)
If format = mp3 And Not mp3ThumbEmbedded And MyYouTubeSettings.DefaultAudioEmbedThumbnail_ExtractedFiles Then _
embedThumbTo.Invoke(f) : mp3ThumbEmbedded = True
If Not M3U8_PlaylistFiles.ListExists AndAlso f.Exists Then M3U8_Append(f)
End If
Next Next
End If End If
End If End If
'Update video
ThrowAny(Token) ThrowAny(Token)
If PostProcessing_AudioAC3 Then If SelectedVideoIndex >= 0 AndAlso tempFilesList.Count > 0 AndAlso tempFilesList.Exists(Function(tf) tf.ToReplace) Then
f = File f = File
If SelectedVideoIndex >= 0 Then
f.Name &= "tmp00" f.Name &= "tmp00"
Else Dim tfr As SFile = tempFilesList.FirstOrDefault(Function(tf) tf.ToReplace).File
f.Extension = ac3 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 End If
If Not f.Exists Then ThrowAny(Token) : .Execute($"ffmpeg -i ""{File}"" -i ""{fAc3Audio}"" -c:v copy -c copy -map 0:v:0 -map 1:a:0 ""{f}""")
If f.Exists Then If f.Exists Then
File.Delete() File.Delete()
If SelectedVideoIndex >= 0 Then SFile.Rename(f, File,, EDP.LogMessageValue) If SelectedVideoIndex >= 0 Then SFile.Rename(f, File,, EDP.LogMessageValue)
End If End If
If fAacAudio.Exists And Not aacRequested Then fAacAudio.Delete()
If fAc3Audio.Exists And Not ac3Requested And SelectedVideoIndex >= 0 Then fAc3Audio.Delete()
End If End If
If SelectedVideoIndex >= 0 AndAlso OutputVideoFPS > 0 AndAlso SelectedVideo.Bitrate > OutputVideoFPS Then 'Delete unrequsted files
If tempFilesList.Count > 0 Then tempFilesList.ForEach(Sub(tfr) If Not tfr.Requested Then tfr.File.Delete(,, EDP.None)) : tempFilesList.Clear()
'Update video FPS
If SelectedVideoIndex >= 0 AndAlso OutputVideoFPS > 0 AndAlso SelectedVideo.Bitrate <> OutputVideoFPS Then
f = File f = File
f.Name &= "tmp00" f.Name &= "tmp00"
.Execute($"ffmpeg -i ""{File}"" -filter:v fps={OutputVideoFPS.ToString.Replace(",", ".")} -c:a copy ""{f}""") .Execute($"ffmpeg -i ""{File}"" -filter:v fps={OutputVideoFPS.ToString.Replace(",", ".")} -c:a copy ""{f}""")
@@ -1076,6 +1323,12 @@ Namespace API.YouTube.Objects
End If End If
End If End If
End With End With
Dim newSize# = 0
If File.Exists Then newSize += File.Size
If Files.Count > 0 Then newSize += (From eFile As SFile In Files Where eFile.Exists Select eFile.Size).Sum
If ThumbnailFile.Exists Then newSize += ThumbnailFile.Size
If newSize > 0 Then newSize /= 1024 : Size = newSize : _SizeRecalculated = True
End Using End Using
_MediaState = UMStates.Downloaded _MediaState = UMStates.Downloaded
Catch oex As OperationCanceledException When Token.IsCancellationRequested Catch oex As OperationCanceledException When Token.IsCancellationRequested
@@ -1092,6 +1345,33 @@ Namespace API.YouTube.Objects
End If End If
End Try End Try
End Sub End Sub
Private Sub M3U8_Append(Optional ByVal __file As SFile = Nothing)
If M3U8_PlaylistFiles.ListExists Then
For Each m3u8_file As SFile In M3U8_PlaylistFiles
If Not m3u8_file.IsEmptyString Then
Dim m3u8Row$ = String.Empty
If Not m3u8_file.Extension.IsEmptyString Then
If m3u8_file.Extension.ToLower = "m3u8" Then
m3u8Row = GetPlaylistRow(Me, __file)
ElseIf m3u8_file.Extension.ToLower = "m3u" Then
m3u8Row = __file.IfNullOrEmpty(File).ToString
End If
End If
If Not m3u8Row.IsEmptyString Then
Dim m3u8Text$
If m3u8_file.Exists Then
m3u8Text = m3u8_file.GetText
m3u8_file.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.SendToLog)
Else
m3u8Text = "#EXTM3U"
End If
m3u8Text.StringAppendLine(m3u8Row, vbCrLf)
TextSaver.SaveTextToFile(m3u8Text, m3u8_file,,, EDP.SendToLog)
End If
End If
Next
End If
End Sub
#End Region #End Region
#Region "Load" #Region "Load"
Private Sub ApplyElementCheckedValue(ByVal e As EContainer) Private Sub ApplyElementCheckedValue(ByVal e As EContainer)
@@ -1221,6 +1501,7 @@ Namespace API.YouTube.Objects
End Sub End Sub
#End Region #End Region
#Region "Parse" #Region "Parse"
Friend Const DRC As String = "drc"
Public Overridable Function Parse(ByVal Container As EContainer, ByVal Path As SFile, ByVal IsMusic As Boolean, Public Overridable Function Parse(ByVal Container As EContainer, ByVal Path As SFile, ByVal IsMusic As Boolean,
Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As IMyProgress = Nothing) As Boolean Implements IYouTubeMediaContainer.Parse Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As IMyProgress = Nothing) As Boolean Implements IYouTubeMediaContainer.Parse
Try Try
@@ -1283,6 +1564,7 @@ Namespace API.YouTube.Objects
_File.Name = $"{ID}.{ext}" _File.Name = $"{ID}.{ext}"
End If End If
If Not MyYouTubeSettings.OutputPath.IsEmptyString Then _File.Path = MyYouTubeSettings.OutputPath.Value.Path If Not MyYouTubeSettings.OutputPath.IsEmptyString Then _File.Path = MyYouTubeSettings.OutputPath.Value.Path
_File = CleanFileName(_File)
File = _File File = _File
If .Contains("duration") Then If .Contains("duration") Then
@@ -1372,7 +1654,8 @@ Namespace API.YouTube.Objects
obj = New MediaObject With { obj = New MediaObject With {
.ID = ee.Value("format_id"), .ID = ee.Value("format_id"),
.URL = ee.Value("url"), .URL = ee.Value("url"),
.Extension = ee.Value("ext") .Extension = ee.Value("ext"),
.ID_DRC = Not .ID.IsEmptyString AndAlso .ID.StringToLower.Contains(DRC)
} }
obj.Width = AConvert(Of Integer)(ee.Value("width"), NumberProvider, -1) obj.Width = AConvert(Of Integer)(ee.Value("width"), NumberProvider, -1)
obj.Height = AConvert(Of Integer)(ee.Value("height"), NumberProvider, -1) obj.Height = AConvert(Of Integer)(ee.Value("height"), NumberProvider, -1)
@@ -1427,6 +1710,14 @@ Namespace API.YouTube.Objects
If MediaObjects.Count > 0 AndAlso MediaObjects.LongCount(CountAVC) > 0 Then MediaObjects.RemoveAll(RemoveAVC) If MediaObjects.Count > 0 AndAlso MediaObjects.LongCount(CountAVC) > 0 Then MediaObjects.RemoveAll(RemoveAVC)
Next Next
End If End If
If t = UMTypes.Audio And MediaObjects.Count > 0 Then
Dim __audioComparerCount As Func(Of MediaObject, MediaObject, Boolean) =
Function(mo, mo2) (mo2.Type = t And mo2.Extension = mo.Extension And mo2.Bitrate = mo.Bitrate) AndAlso
mo2.Size.RoundDown = mo.Size.RoundDown AndAlso ACheck(Of Integer)(mo2.ID)
Dim RemoveDRC As Predicate(Of MediaObject) = Function(mo) mo.Type = t AndAlso Not ACheck(Of Integer)(mo.ID) AndAlso
MediaObjects.LongCount(Function(mo2) __audioComparerCount.Invoke(mo, mo2)) > 0
MediaObjects.RemoveAll(RemoveDRC)
End If
End Sub End Sub
Dim protocolCleaner As Action = Dim protocolCleaner As Action =
Sub() Sub()
@@ -1608,7 +1899,7 @@ Namespace API.YouTube.Objects
_SubtitlesDelegated.Clear() _SubtitlesDelegated.Clear()
SubtitlesSelectedIndexes.Clear() SubtitlesSelectedIndexes.Clear()
MediaObjects.Clear() MediaObjects.Clear()
Files.Clear() _Files.Clear()
PostProcessing_OutputAudioFormats.Clear() PostProcessing_OutputAudioFormats.Clear()
PostProcessing_OutputSubtitlesFormats.Clear() PostProcessing_OutputSubtitlesFormats.Clear()
End If End If

View File

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

View File

@@ -6,6 +6,7 @@
' '
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Runtime.CompilerServices
Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Base Namespace API.Base
@@ -49,7 +50,7 @@ Namespace API.Base
End Sub End Sub
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider, Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
Dim v% = AConvert(Of Integer)(Value, -1) Dim v% = AConvert(Of Integer)(Value, -1, EDP.ReturnValue)
If v > 0 Then If v > 0 Then
Return Value Return Value
ElseIf Not ACheck(Of Integer)(Value) Then ElseIf Not ACheck(Of Integer)(Value) Then
@@ -72,5 +73,12 @@ Namespace API.Base
$"Current query: [{CurrentQuery}]{vbCr}New query: [{NewQuery}]", $"Current query: [{CurrentQuery}]{vbCr}New query: [{NewQuery}]",
"Changing a query"}, vbExclamation,,, {"Process", "Cancel"}) = 0 "Changing a query"}, vbExclamation,,, {"Process", "Cancel"}) = 0
End Function End Function
<Extension> Friend Function GetCookieValue(ByVal Cookies As IEnumerable(Of System.Net.Cookie), ByVal CookieName As String) As String
If Cookies.ListExists Then Return If(Cookies.FirstOrDefault(Function(c) c.Name.ToLower = CookieName.ToLower)?.Value, String.Empty) Else Return String.Empty
End Function
<Extension> Friend Function GetCookieValue(ByVal Cookies As IEnumerable(Of System.Net.Cookie), ByVal CookieName As String,
ByVal PropName As String, ByVal PropNameComp As String) As String
Return If(PropName = PropNameComp, Cookies.GetCookieValue(CookieName), String.Empty)
End Function
End Module End Module
End Namespace End Namespace

View File

@@ -11,7 +11,8 @@ Namespace API.Base
Friend Const Header_Authorization As String = "authorization" Friend Const Header_Authorization As String = "authorization"
Friend Const Header_CSRFToken As String = "x-csrf-token" Friend Const Header_CSRFToken As String = "x-csrf-token"
Friend Const Header_FB_FRIENDLY_NAME As String = "x-fb-friendly-name" Friend Const CAT_UserDefs As String = "New user defaults"
Friend Const CAT_Timers As String = "Timers"
Friend Const ConcurrentDownloadsCaption As String = "Concurrent downloads" Friend Const ConcurrentDownloadsCaption As String = "Concurrent downloads"
Friend Const ConcurrentDownloadsToolTip As String = "The number of concurrent downloads." Friend Const ConcurrentDownloadsToolTip As String = "The number of concurrent downloads."

View File

@@ -59,7 +59,6 @@ Namespace API.Base
ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer ReadOnly Property DownloadedTotal(Optional ByVal Total As Boolean = True) As Integer
ReadOnly Property DownloadedInformation As String ReadOnly Property DownloadedInformation As String
Property HasError As Boolean Property HasError As Boolean
ReadOnly Property FitToAddParams As Boolean
ReadOnly Property Key As String ReadOnly Property Key As String
Property DownloadImages As Boolean Property DownloadImages As Boolean
Property DownloadVideos As Boolean Property DownloadVideos As Boolean
@@ -78,7 +77,7 @@ Namespace API.Base
''' </summary> ''' </summary>
Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer 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 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 Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
Sub OpenFolder() Sub OpenFolder()
Property DownloadTopCount As Integer? Property DownloadTopCount As Integer?

View File

@@ -42,6 +42,13 @@ Namespace API.Base
Friend NotInheritable Class M3U8Base Friend NotInheritable Class M3U8Base
Friend Const TempCacheFolderName As String = "tmpCache" Friend Const TempCacheFolderName As String = "tmpCache"
Friend Const TempFilePrefix As String = "ConPart_" Friend Const TempFilePrefix As String = "ConPart_"
Friend Const TempFileDefaultExtension As String = "ts"
''' <summary><c>SFileNumbers.NumberProviderDefault</c></summary>
Friend Shared ReadOnly Property NumberProviderDefault As ANumbers
Get
Return SFileNumbers.NumberProviderDefault
End Get
End Property
Private Sub New() Private Sub New()
End Sub End Sub
Friend Shared Function CreateUrl(ByVal Appender As String, ByVal File As String) As String Friend Shared Function CreateUrl(ByVal Appender As String, ByVal File As String) As String
@@ -63,8 +70,7 @@ Namespace API.Base
Friend Overloads Shared Function Download(ByVal URLs As List(Of M3U8URL), ByVal DestinationFile As SFile, Optional ByVal Responser As Responser = Nothing, Friend Overloads Shared Function Download(ByVal URLs As List(Of M3U8URL), ByVal DestinationFile As SFile, Optional ByVal Responser As Responser = Nothing,
Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As MyProgress = Nothing, Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As MyProgress = Nothing,
Optional ByVal UsePreProgress As Boolean = True, Optional ByVal ExistingCache As CacheKeeper = Nothing, Optional ByVal UsePreProgress As Boolean = True, Optional ByVal ExistingCache As CacheKeeper = Nothing,
Optional ByVal OnlyDownload As Boolean = False) As SFile Optional ByVal OnlyDownload As Boolean = False, Optional ByVal SkipBroken As Boolean = False) As SFile
Const defaultExtension$ = "ts"
Dim Cache As CacheKeeper = Nothing Dim Cache As CacheKeeper = Nothing
Using tmpPr As New PreProgress(Progress) Using tmpPr As New PreProgress(Progress)
Try Try
@@ -89,13 +95,13 @@ Namespace API.Base
End If End If
End If End If
Dim p As SFileNumbers = SFileNumbers.Default(ConcatFile.Name) Dim p As SFileNumbers = SFileNumbers.Default(ConcatFile.Name)
Dim pNum As ANumbers = SFileNumbers.NumberProviderDefault Dim pNum As ANumbers = NumberProviderDefault
p.NumberProvider = pNum p.NumberProvider = pNum
DirectCast(p.NumberProvider, ANumbers).GroupSize = {URLs.Count.ToString.Length, 3}.Max DirectCast(p.NumberProvider, ANumbers).GroupSize = {URLs.Count.ToString.Length, 3}.Max
ConcatFile = SFile.IndexReindex(ConcatFile,,, p, EDP.ReturnValue) ConcatFile = SFile.IndexReindex(ConcatFile,,, p, EDP.ReturnValue)
Dim i% Dim i%
Dim dFile As SFile = cache2.RootDirectory Dim dFile As SFile = cache2.RootDirectory
dFile.Extension = defaultExtension dFile.Extension = TempFileDefaultExtension
Using w As New DownloadObjects.WebClient2(Responser) Using w As New DownloadObjects.WebClient2(Responser)
For i = 0 To URLs.Count - 1 For i = 0 To URLs.Count - 1
If progressExists Then If progressExists Then
@@ -107,9 +113,13 @@ Namespace API.Base
End If End If
Token.ThrowIfCancellationRequested() Token.ThrowIfCancellationRequested()
dFile.Name = $"{TempFilePrefix}{i.NumToString(pNum)}" dFile.Name = $"{TempFilePrefix}{i.NumToString(pNum)}"
dFile.Extension = URLs(i).Extension.IfNullOrEmpty(defaultExtension) dFile.Extension = URLs(i).Extension.IfNullOrEmpty(TempFileDefaultExtension)
Try
w.DownloadFile(URLs(i).URL, dFile) w.DownloadFile(URLs(i).URL, dFile)
cache2.AddFile(dFile, True) cache2.AddFile(dFile, True)
Catch ex As Exception
If Not SkipBroken Then Throw ex
End Try
Next Next
End Using End Using
If Not OnlyDownload Then _ If Not OnlyDownload Then _

View File

@@ -17,6 +17,7 @@ Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Base Namespace API.Base
Friend MustInherit Class SiteSettingsBase : Implements ISiteSettings, IResponserContainer Friend MustInherit Class SiteSettingsBase : Implements ISiteSettings, IResponserContainer
#Region "Declarations" #Region "Declarations"
<PXML> Protected ReadOnly Property SettingsVersion As PropertyValue
Friend ReadOnly Property Site As String Implements ISiteSettings.Site Friend ReadOnly Property Site As String Implements ISiteSettings.Site
Protected _Icon As Icon = Nothing Protected _Icon As Icon = Nothing
Friend Overridable ReadOnly Property Icon As Icon Implements ISiteSettings.Icon Friend Overridable ReadOnly Property Icon As Icon Implements ISiteSettings.Icon
@@ -33,6 +34,16 @@ Namespace API.Base
Friend Property AccountName As String Implements ISiteSettings.AccountName Friend Property AccountName As String Implements ISiteSettings.AccountName
Friend Property Temporary As Boolean = False Implements ISiteSettings.Temporary Friend Property Temporary As Boolean = False Implements ISiteSettings.Temporary
Friend Property DefaultInstance As ISiteSettings = Nothing Implements ISiteSettings.DefaultInstance Friend 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 _AllowUserAgentUpdate As Boolean = True
Protected _SubscriptionsAllowed As Boolean = False Protected _SubscriptionsAllowed As Boolean = False
Friend ReadOnly Property SubscriptionsAllowed As Boolean Implements ISiteSettings.SubscriptionsAllowed Friend ReadOnly Property SubscriptionsAllowed As Boolean Implements ISiteSettings.SubscriptionsAllowed
@@ -54,7 +65,14 @@ Namespace API.Base
End Set End Set
End Property End Property
#End Region #End Region
#Region "EnvironmentPrograms"
Private Property CMDEncoding As String Implements ISiteSettings.CMDEncoding
Private Property EnvironmentPrograms As IEnumerable(Of String) Implements ISiteSettings.EnvironmentPrograms
Private Sub EnvironmentProgramsUpdated() Implements ISiteSettings.EnvironmentProgramsUpdated
End Sub
#End Region
#Region "Responser and cookies support" #Region "Responser and cookies support"
Friend Const ResponserFilePrefix As String = "Responser_"
Private _CookiesNetscapeFile As SFile = Nothing Private _CookiesNetscapeFile As SFile = Nothing
Friend ReadOnly Property CookiesNetscapeFile As SFile Friend ReadOnly Property CookiesNetscapeFile As SFile
Get Get
@@ -85,7 +103,7 @@ Namespace API.Base
End Property End Property
Protected Sub UpdateResponserFile() Protected Sub UpdateResponserFile()
Dim acc$ = If(AccountName.IsEmptyString OrElse AccountName = Hosts.SettingsHost.NameAccountNameDefault, String.Empty, $"_{AccountName}") Dim acc$ = If(AccountName.IsEmptyString OrElse AccountName = Hosts.SettingsHost.NameAccountNameDefault, String.Empty, $"_{AccountName}")
Responser.File = $"{SettingsFolderName}\Responser_{Site}{acc}.xml" Responser.File = $"{SettingsFolderName}\{ResponserFilePrefix}{Site}{acc}.xml"
_CookiesNetscapeFile = Responser.File _CookiesNetscapeFile = Responser.File
_CookiesNetscapeFile.Name &= "_Cookies_Netscape" _CookiesNetscapeFile.Name &= "_Cookies_Netscape"
_CookiesNetscapeFile.Extension = "txt" _CookiesNetscapeFile.Extension = "txt"
@@ -100,6 +118,7 @@ Namespace API.Base
_Icon = __Icon _Icon = __Icon
_Image = __Image _Image = __Image
Responser = New Responser With {.DeclaredError = EDP.ThrowException} Responser = New Responser With {.DeclaredError = EDP.ThrowException}
SettingsVersion = New PropertyValue(0)
UpdateResponserFile() UpdateResponserFile()
End Sub End Sub
Friend Sub New(ByVal SiteName As String, ByVal CookiesDomain As String, ByVal AccName As String, ByVal Temp As Boolean, Friend Sub New(ByVal SiteName As String, ByVal CookiesDomain As String, ByVal AccName As String, ByVal Temp As Boolean,
@@ -129,7 +148,6 @@ Namespace API.Base
Friend Overridable Sub BeginInit() Implements ISiteSettings.BeginInit Friend Overridable Sub BeginInit() Implements ISiteSettings.BeginInit
End Sub End Sub
Friend Overridable Sub EndInit() Implements ISiteSettings.EndInit 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) If CheckNetscapeCookiesOnEndInit Then Update_SaveCookiesNetscape(, True)
End Sub End Sub
#End Region #End Region

View 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

View 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

View File

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

View File

@@ -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

View 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

View 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>

View 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

View File

@@ -312,7 +312,7 @@ Namespace API.Base
End Set End Set
End Property End Property
Protected Sub UserSiteNameUpdate(ByVal NewName As String) Protected Sub UserSiteNameUpdate(ByVal NewName As String)
If Not NewName.IsEmptyString And (UserSiteName.IsEmptyString Or Settings.UserSiteNameUpdateEveryTime) Then UserSiteName = NewName If Not NewName.IsEmptyString And (UserSiteName.IsEmptyString Or Settings.UpdateUserSiteNameEveryTime) Then UserSiteName = NewName
End Sub End Sub
Friend ReadOnly Property UserModel As UsageModel Implements IUserData.UserModel Friend ReadOnly Property UserModel As UsageModel Implements IUserData.UserModel
Get Get
@@ -829,48 +829,19 @@ BlockNullPicture:
Return ListImagesLoader.ApplyLVIColor(Me, New ListViewItem(ToString(), GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}, True) Return ListImagesLoader.ApplyLVIColor(Me, New ListViewItem(ToString(), GetLVIGroup(Destination)) With {.Name = LVIKey, .Tag = LVIKey}, True)
End If End If
End Function End Function
Friend Overridable ReadOnly Property FitToAddParams As Boolean Implements IUserData.FitToAddParams
Get
With Settings
If IsSubscription And Not .MainFrameUsersShowSubscriptions Then Return False
If Not IsSubscription And Not .MainFrameUsersShowDefaults Then Return False
If LastUpdated.HasValue And Not .ViewDateMode.Value = ShowingDates.Off Then
Dim f As Date = If(.ViewDateFrom.HasValue, .ViewDateFrom.Value.Date, Date.MinValue.Date)
Dim t As Date = If(.ViewDateTo.HasValue, .ViewDateTo.Value.Date, Date.MaxValue.Date)
Select Case DirectCast(.ViewDateMode.Value, ShowingDates)
Case ShowingDates.In : If Not LastUpdated.Value.ValueBetween(f, t) Then Return False
Case ShowingDates.Not : If LastUpdated.Value.ValueBetween(f, t) Then Return False
End Select
End If
If Not .Labels.ExcludedIgnore AndAlso .Labels.Excluded.ValuesList.ListContains(Labels) Then Return False
If .SelectedSites.Count = 0 OrElse .SelectedSites.Contains(Site) Then
Select Case .ShowingMode.Value
Case ShowingModes.Regular : Return Not Temporary And Not Favorite
Case ShowingModes.Temporary : Return Temporary
Case ShowingModes.Favorite : Return Favorite
Case ShowingModes.Deleted : Return Not UserExists
Case ShowingModes.Suspended : Return UserSuspended
Case ShowingModes.Labels : Return Settings.Labels.Current.ValuesList.ListContains(Labels)
Case ShowingModes.NoLabels : Return Labels.Count = 0
Case Else : Return True
End Select
Else
Return False
End If
End With
End Get
End Property
Friend Function GetLVIGroup(ByVal Destination As ListView) As ListViewGroup Implements IUserData.GetLVIGroup Friend Function GetLVIGroup(ByVal Destination As ListView) As ListViewGroup Implements IUserData.GetLVIGroup
Try Try
If Settings.ShowingMode.Value = ShowingModes.Labels And Not Settings.ShowGroupsInsteadLabels Then With Settings
If Labels.Count > 0 And Settings.Labels.Current.Count > 0 Then If Not .ShowAllUsers.Value AndAlso (.AdvancedFilter.Labels.Count > 0 Or .AdvancedFilter.LabelsNo) AndAlso Not .ShowGroupsInsteadLabels Then
If Labels.Count > 0 And .AdvancedFilter.Labels.Count > 0 Then
For i% = 0 To Labels.Count - 1 For i% = 0 To Labels.Count - 1
If Settings.Labels.Current.Contains(Labels(i)) Then Return Destination.Groups.Item(Labels(i)) If .AdvancedFilter.Labels.Contains(Labels(i)) Then Return Destination.Groups.Item(Labels(i))
Next Next
End If End If
ElseIf Settings.ShowGroups Then ElseIf Settings.GroupUsers Then
Return Destination.Groups.Item(GetLviGroupName(HOST, Temporary, Favorite, IsCollection)) Return Destination.Groups.Item(GetLviGroupName(HOST, Temporary, Favorite, IsCollection))
End If End If
End With
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName) Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
Catch ex As Exception Catch ex As Exception
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName) Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
@@ -979,7 +950,10 @@ BlockNullPicture:
LogError(ex, "user information loading error") LogError(ex, "user information loading error")
End Try End Try
End Sub 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 Try
UpdateDataFiles() UpdateDataFiles()
MyFileSettings.Exists(SFO.Path) MyFileSettings.Exists(SFO.Path)
@@ -1030,7 +1004,7 @@ BlockNullPicture:
x.Save(MyFileSettings) x.Save(MyFileSettings)
End Using End Using
If Not IsSavedPosts Then Settings.UpdateUsersList(User) If Not IsSavedPosts And Not DisableUserInfoUpdate Then Settings.UpdateUsersList(User, True)
Catch ex As Exception Catch ex As Exception
LogError(ex, "user information saving error") LogError(ex, "user information saving error")
End Try End Try
@@ -1194,7 +1168,7 @@ BlockNullPicture:
If Not Responser Is Nothing Then Responser.Dispose() If Not Responser Is Nothing Then Responser.Dispose()
Responser = New Responser Responser = New Responser
If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser) If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser)
If Not Responser Is Nothing And _ResponserAutoUpdateCookies Then If Not Responser Is Nothing And (_ResponserAutoUpdateCookies Or _ResponserAddResponseReceivedHandler) Then
If _ResponserAutoUpdateCookies Then If _ResponserAutoUpdateCookies Then
Responser.CookiesUpdateMode = CookieUpdateModes.ReplaceByNameAll Responser.CookiesUpdateMode = CookieUpdateModes.ReplaceByNameAll
Responser.CookiesExtractMode = Responser.CookiesExtractModes.Any Responser.CookiesExtractMode = Responser.CookiesExtractModes.Any
@@ -1269,8 +1243,10 @@ BlockNullPicture:
Dim mca& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0) Dim mca& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0)
If DownloadedTotal(False) > 0 Or _EnvirChanged Or Not mcb = mca Or _ForceSaveUserData Then If DownloadedTotal(False) > 0 Or _EnvirChanged Or Not mcb = mca Or _ForceSaveUserData Then
If Not __isChannelsSupport Then If Not __isChannelsSupport Then
If DownloadedTotal(False) > 0 Then
LastUpdated = Now LastUpdated = Now
RunScript() RunScript()
End If
DownloadedPictures(True) = SFile.GetFiles(MyFile.CutPath, "*.jpg|*.jpeg|*.png|*.gif|*.webm",, EDP.ReturnValue).Count DownloadedPictures(True) = SFile.GetFiles(MyFile.CutPath, "*.jpg|*.jpeg|*.png|*.gif|*.webm",, EDP.ReturnValue).Count
DownloadedVideos(True) = SFile.GetFiles(MyFile.CutPath, "*.mp4|*.mkv|*.mov", SearchOption.AllDirectories, EDP.ReturnValue).Count DownloadedVideos(True) = SFile.GetFiles(MyFile.CutPath, "*.mp4|*.mkv|*.mov", SearchOption.AllDirectories, EDP.ReturnValue).Count
If Labels.Contains(LabelsKeeper.NoParsedUser) Then Labels.Remove(LabelsKeeper.NoParsedUser) If Labels.Contains(LabelsKeeper.NoParsedUser) Then Labels.Remove(LabelsKeeper.NoParsedUser)
@@ -1366,6 +1342,7 @@ BlockNullPicture:
ResetHost() ResetHost()
URL = Data.URL URL = Data.URL
AccountName = Data.AccountName AccountName = Data.AccountName
TokenQueue = Token
If HOST Is Nothing Then Throw New ExitException($"Host '{AccountName}' not found") If HOST Is Nothing Then Throw New ExitException($"Host '{AccountName}' not found")
Data.DownloadState = UserMediaStates.Tried Data.DownloadState = UserMediaStates.Tried
Progress = Data.Progress Progress = Data.Progress
@@ -1409,12 +1386,14 @@ BlockNullPicture:
If _ContentNew.Count > 0 Then If _ContentNew.Count > 0 Then
If _ContentNew.Any(Function(mm) mm.State = UStates.Downloaded) Then If _ContentNew.Any(Function(mm) mm.State = UStates.Downloaded) Then
Data.DownloadState = UserMediaStates.Downloaded Data.DownloadState = UserMediaStates.Downloaded
Dim thumbAlong As Boolean = False
If TypeOf Data Is DownloadableMediaHost Then thumbAlong = DirectCast(Data, DownloadableMediaHost).ThumbAlong
If _ContentNew(0).Type = UTypes.Picture Or _ContentNew(0).Type = UTypes.GIF Then If _ContentNew(0).Type = UTypes.Picture Or _ContentNew(0).Type = UTypes.GIF Then
DirectCast(Data, IDownloadableMedia).ThumbnailFile = _ContentNew(0).File DirectCast(Data, IDownloadableMedia).ThumbnailFile = _ContentNew(0).File
ElseIf Settings.STDownloader_TakeSnapshot And Settings.FfmpegFile.Exists And Not Settings.STDownloader_RemoveDownloadedAutomatically Then ElseIf Settings.STDownloader_TakeSnapshot And Settings.FfmpegFile.Exists And Not Settings.STDownloader_RemoveDownloadedAutomatically Then
Dim f As SFile = _ContentNew(0).File Dim f As SFile = _ContentNew(0).File
Dim ff As SFile Dim ff As SFile
If Settings.STDownloader_SnapshotsKeepWithFiles Then If Settings.STDownloader_SnapshotsKeepWithFiles Or thumbAlong Then
ff = f ff = f
Else Else
ff = Settings.CacheSnapshots(Settings.STDownloader_SnapShotsCachePermamnent).NewFile ff = Settings.CacheSnapshots(Settings.STDownloader_SnapShotsCachePermamnent).NewFile
@@ -1424,6 +1403,9 @@ BlockNullPicture:
f = Web.FFMPEG.TakeSnapshot(f, ff, Settings.FfmpegFile, TimeSpan.FromSeconds(1),,, EDP.SendToLog + EDP.ReturnValue) f = Web.FFMPEG.TakeSnapshot(f, ff, Settings.FfmpegFile, TimeSpan.FromSeconds(1),,, EDP.SendToLog + EDP.ReturnValue)
If f.Exists Then DirectCast(Data, IDownloadableMedia).ThumbnailFile = f If f.Exists Then DirectCast(Data, IDownloadableMedia).ThumbnailFile = f
End If End If
Dim filesSize# = (From mm As UserMedia In _ContentNew Where mm.State = UStates.Downloaded AndAlso mm.File.Exists Select mm.File.Size).Sum
If filesSize > 0 Then filesSize /= 1024
Data.Size = filesSize
Else Else
Data.DownloadState = UserMediaStates.Missing Data.DownloadState = UserMediaStates.Missing
End If End If
@@ -1796,6 +1778,7 @@ BlockNullPicture:
Protected Overridable Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean Protected Overridable Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean
Return True Return True
End Function End Function
''' <returns><c>MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator</c></returns>
Protected Overridable Function DownloadContentDefault_GetRootDir() As String Protected Overridable Function DownloadContentDefault_GetRootDir() As String
Return MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator Return MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator
End Function End Function
@@ -1914,7 +1897,9 @@ BlockNullPicture:
If m.Contains(IUserData.EraseMode.History) Then If m.Contains(IUserData.EraseMode.History) Then
If MyFilePosts.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True If MyFilePosts.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True
If MyFileData.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True If MyFileData.Delete(SFO.File, SFODelete.DeleteToRecycleBin, e) Then result = True
LastUpdated = Nothing
EraseData_AdditionalDataFiles() EraseData_AdditionalDataFiles()
UpdateUserInformation()
End If End If
If m.Contains(IUserData.EraseMode.Data) Then If m.Contains(IUserData.EraseMode.Data) Then
Dim files As List(Of SFile) = SFile.GetFiles(DownloadContentDefault_GetRootDir.CSFileP,, SearchOption.AllDirectories, e) Dim files As List(Of SFile) = SFile.GetFiles(DownloadContentDefault_GetRootDir.CSFileP,, SearchOption.AllDirectories, e)
@@ -1936,7 +1921,7 @@ BlockNullPicture:
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"EraseData({CInt(Mode)}): {ToStringForLog()}", False) Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"EraseData({CInt(Mode)}): {ToStringForLog()}", False)
End Try End Try
End Function End Function
Protected Overridable Sub EraseData_AdditionalDataFiles() Protected Overridable Sub EraseData_AdditionalDataFiles() Implements IPluginContentProvider.ResetHistoryData
End Sub End Sub
Friend Overridable Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer Implements IUserData.Delete Friend Overridable Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer Implements IUserData.Delete
Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path) Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path)
@@ -1952,7 +1937,18 @@ BlockNullPicture:
Return 0 Return 0
End If End If
End Function 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 UserBefore As UserInfo = User
Dim Removed As Boolean = True Dim Removed As Boolean = True
Dim _TurnBack As Boolean = False Dim _TurnBack As Boolean = False
@@ -1968,6 +1964,7 @@ BlockNullPicture:
User.SpecialCollectionPath = String.Empty User.SpecialCollectionPath = String.Empty
User.UserModel = UsageModel.Default User.UserModel = UsageModel.Default
User.CollectionModel = UsageModel.Default User.CollectionModel = UsageModel.Default
If NewUser.HasValue Then User.SpecialPath = NewUser.Value.UserNew.SpecialPath
Else Else
Settings.Users.Remove(Me) Settings.Users.Remove(Me)
Removed = True Removed = True

View File

@@ -11,7 +11,7 @@ Namespace API.Base.YTDLP
Friend Sub New(ByVal _Token As Threading.CancellationToken) Friend Sub New(ByVal _Token As Threading.CancellationToken)
MyBase.New(_Token) MyBase.New(_Token)
Commands.Clear() Commands.Clear()
MainProcessName = "yt-dlp" MainProcessName = Settings.YtdlpFile.File.Name '"yt-dlp"
ChangeDirectory(Settings.YtdlpFile.File) ChangeDirectory(Settings.YtdlpFile.File)
End Sub End Sub
End Class End Class

View File

@@ -11,9 +11,8 @@ Imports PersonalUtilities.Functions.XML.Base
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Facebook Namespace API.Facebook
Friend Module Declarations Friend Module Declarations
Friend ReadOnly Regex_UserToken_dtsg As RParams = RParams.DMS("DTSGInitialData.:.?{\s*.token.:\s*""([^""]+)", 1, EDP.ReturnValue)
Friend ReadOnly Regex_UserToken_lsd As RParams = RParams.DMS("LSD.:.?{\s*.token.:\s*""([^""]+)", 1, EDP.ReturnValue)
Friend ReadOnly Regex_UserID As RParams = RParams.DMS("userid.:.(\d+)", 1, RegexOptions.IgnoreCase, EDP.ReturnValue) Friend ReadOnly Regex_UserID As RParams = RParams.DMS("userid.:.(\d+)", 1, RegexOptions.IgnoreCase, EDP.ReturnValue)
Friend ReadOnly Regex_AppID As RParams = RParams.DMS("APP_ID.:.(\d+)", 1, RegexOptions.IgnoreCase, EDP.ReturnValue)
Friend ReadOnly Regex_Photos_by As RParams = RParams.DMS("photos_by"",""id"":""([^""]+)", 1, EDP.ReturnValue) Friend ReadOnly Regex_Photos_by As RParams = RParams.DMS("photos_by"",""id"":""([^""]+)", 1, EDP.ReturnValue)
Friend ReadOnly Regex_FileName As RParams = RParams.DM("([^/\?]+\..{3,4})(?=(\?|\Z))", 0, EDP.ReturnValue) Friend ReadOnly Regex_FileName As RParams = RParams.DM("([^/\?]+\..{3,4})(?=(\?|\Z))", 0, EDP.ReturnValue)

View File

@@ -11,6 +11,7 @@ Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Facebook Namespace API.Facebook
<Manifest("AndyProgram_Facebook"), SavedPosts, SeparatedTasks(1), SpecialForm(False)> <Manifest("AndyProgram_Facebook"), SavedPosts, SeparatedTasks(1), SpecialForm(False)>
Friend Class SiteSettings : Inherits ThreadsNet.SiteSettings Friend Class SiteSettings : Inherits ThreadsNet.SiteSettings
@@ -18,7 +19,7 @@ Namespace API.Facebook
#Region "Auth" #Region "Auth"
<PropertyOption(AllowNull:=False, ControlText:="Accept", ControlToolTip:="Header 'Accept'", IsAuth:=True), ControlNumber(21), PXML, PClonable> <PropertyOption(AllowNull:=False, ControlText:="Accept", ControlToolTip:="Header 'Accept'", IsAuth:=True), ControlNumber(21), PXML, PClonable>
Friend ReadOnly Property Header_Accept As PropertyValue Friend ReadOnly Property Header_Accept As PropertyValue
<PropertyOption(ControlText:="x-ig-app-id", AllowNull:=True, IsAuth:=True)> <PropertyOption(ControlText:="x-ig-app-id", AllowNull:=True, IsAuth:=True), HiddenControl>
Friend Overrides ReadOnly Property HH_IG_APP_ID As PropertyValue Friend Overrides ReadOnly Property HH_IG_APP_ID As PropertyValue
Get Get
Return __HH_IG_APP_ID Return __HH_IG_APP_ID
@@ -29,15 +30,13 @@ Namespace API.Facebook
Return __HH_CSRF_TOKEN Return __HH_CSRF_TOKEN
End Get End Get
End Property End Property
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, LeftOffset:=120), ControlNumber(51), PXML, PClonable>
Friend ReadOnly Property HH_PLATFORM_VER As PropertyValue
#End Region #End Region
#Region "Defaults" #Region "Defaults"
<PropertyOption(ControlText:="Download photos", IsAuth:=False), PXML, PClonable> <PropertyOption(ControlText:="Download photos", IsAuth:=False, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property ParsePhotoBlock As PropertyValue 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 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 Friend ReadOnly Property ParseStoriesBlock As PropertyValue
#End Region #End Region
#End Region #End Region
@@ -48,10 +47,9 @@ Namespace API.Facebook
With Responser.Headers With Responser.Headers
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.facebook.com")) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.facebook.com"))
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.facebook.com")) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.facebook.com"))
.Remove(DeclaredNames.Header_FB_FRIENDLY_NAME) .Remove(Instagram.UserData.GQL_HEADER_FB_FRINDLY_NAME)
End With End With
Header_Accept = New PropertyValue(String.Empty, GetType(String)) Header_Accept = New PropertyValue(String.Empty, GetType(String))
HH_PLATFORM_VER = New PropertyValue(String.Empty, GetType(String))
ParsePhotoBlock = New PropertyValue(True) ParsePhotoBlock = New PropertyValue(True)
ParseVideoBlock = New PropertyValue(True) ParseVideoBlock = New PropertyValue(True)
ParseStoriesBlock = New PropertyValue(True) ParseStoriesBlock = New PropertyValue(True)
@@ -77,7 +75,7 @@ Namespace API.Facebook
#End Region #End Region
#Region "BaseAuthExists, GetUserUrl, GetUserPostUrl, IsMyUser, IsMyImageVideo" #Region "BaseAuthExists, GetUserUrl, GetUserPostUrl, IsMyUser, IsMyImageVideo"
Friend Overrides Function BaseAuthExists() As Boolean Friend Overrides Function BaseAuthExists() As Boolean
Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value) Return Responser.CookiesExists And CBool(DownloadData_Impl.Value) 'And ACheck(HH_IG_APP_ID.Value)
End Function End Function
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return DirectCast(User, UserData).GetProfileUrl Return DirectCast(User, UserData).GetProfileUrl

View File

@@ -124,12 +124,20 @@ Namespace API.Facebook
.SendToLogOnlyMessage = True, .ReplaceMainMessage = True}) .SendToLogOnlyMessage = True, .ReplaceMainMessage = True})
End Sub End Sub
End Class End Class
Private Token_dtsg As String = String.Empty
Private Token_lsd As String = String.Empty
Private Token_Photosby As String = String.Empty Private Token_Photosby As String = String.Empty
Private Limit As Integer = -1 Private Limit As Integer = -1
Private Sub WaitTimer()
If CInt(MySettings.RequestsWaitTimer_Any.Value) > 0 Then Thread.Sleep(CInt(MySettings.RequestsWaitTimer_Any.Value))
End Sub
Private Sub DisableDownload()
MySettings.DownloadData_Impl.Value = False
MyMainLOG = $"{Site} downloading is disabled until you update your credentials"
End Sub
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
If CBool(MySettings.DownloadData_Impl.Value) Then
Try Try
If Responser.Headers.Value(IG.Header_IG_APP_ID).IsEmptyString Then Responser.Headers.Remove(IG.Header_IG_APP_ID)
ResetBaseTokens()
GetUserTokens(Token) GetUserTokens(Token)
LoadSavePostsKV(True) LoadSavePostsKV(True)
Limit = If(DownloadTopCount, -1) Limit = If(DownloadTopCount, -1)
@@ -144,6 +152,7 @@ Namespace API.Facebook
Finally Finally
MySettings.UpdateResponserData(Responser) MySettings.UpdateResponserData(Responser)
End Try End Try
End If
End Sub End Sub
Private Const Header_fb_fr_name_Photo As String = "ProfileCometAppCollectionPhotosRendererPaginationQuery" Private Const Header_fb_fr_name_Photo As String = "ProfileCometAppCollectionPhotosRendererPaginationQuery"
Private Const Header_fb_fr_name_Video As String = "PagesCometChannelTabAllVideosCardImplPaginationQuery" Private Const Header_fb_fr_name_Video As String = "PagesCometChannelTabAllVideosCardImplPaginationQuery"
@@ -167,13 +176,13 @@ Namespace API.Facebook
ValidateBaseTokens() ValidateBaseTokens()
If Token_Photosby.IsEmptyString Then Throw New TokensException("Unable to obtain token 'Token_Photosby'", False) If Token_Photosby.IsEmptyString Then Throw New TokensException("Unable to obtain token 'Token_Photosby'", False)
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Photo, Header_fb_fr_name_Photo, URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Photo, Header_fb_fr_name_Photo, Token_dtsg_Var,
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, Cursor, Token_Photosby) & "}")) SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, Cursor, Token_Photosby) & "}"))
ResponserApplyDefs(Header_fb_fr_name_Photo) ResponserApplyDefs(Header_fb_fr_name_Photo)
ThrowAny(Token) ThrowAny(Token)
WaitTimer()
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r) Using j As EContainer = JsonDocument.Parse(r)
@@ -233,13 +242,13 @@ Namespace API.Facebook
If VideoPageID.IsEmptyString Then Throw New TokensException("Unable to obtain 'VideoPageID'", False) If VideoPageID.IsEmptyString Then Throw New TokensException("Unable to obtain 'VideoPageID'", False)
ValidateBaseTokens() ValidateBaseTokens()
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Video, Header_fb_fr_name_Video, URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Video, Header_fb_fr_name_Video, Token_dtsg_Var,
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}"""), VideoPageID) & "}")) SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}"""), VideoPageID) & "}"))
ResponserApplyDefs(Header_fb_fr_name_Video) ResponserApplyDefs(Header_fb_fr_name_Video)
ThrowAny(Token) ThrowAny(Token)
WaitTimer()
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r) Using j As EContainer = JsonDocument.Parse(r)
@@ -288,13 +297,13 @@ Namespace API.Facebook
ValidateBaseTokens() ValidateBaseTokens()
If StoryBucket.IsEmptyString Then Throw New TokensException("Unable to obtain 'StoryBucket'", False) If StoryBucket.IsEmptyString Then Throw New TokensException("Unable to obtain 'StoryBucket'", False)
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Stories, Header_fb_fr_name_Stories, URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_Stories, Header_fb_fr_name_Stories, Token_dtsg_Var,
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, StoryBucket) & "}")) SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, StoryBucket) & "}"))
ResponserApplyDefs(Header_fb_fr_name_Stories) ResponserApplyDefs(Header_fb_fr_name_Stories)
ThrowAny(Token) ThrowAny(Token)
WaitTimer()
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then r = RegexReplace(r, RParams.DM("[^\r\n]+", 0, EDP.ReturnValue)) If Not r.IsEmptyString Then r = RegexReplace(r, RParams.DM("[^\r\n]+", 0, EDP.ReturnValue))
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
@@ -357,13 +366,13 @@ Namespace API.Facebook
Dim pid As PostKV Dim pid As PostKV
ValidateBaseTokens() ValidateBaseTokens()
URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_SavedPosts, Header_fb_fr_name_SavedPosts, URL = String.Format(Graphql_UrlPattern, Token_lsd, DocID_SavedPosts, Header_fb_fr_name_SavedPosts, Token_dtsg_Var,
SymbolsConverter.ASCII.EncodeSymbolsOnly(Token_dtsg),
SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}""")) & "}")) SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(VarPattern, If(Cursor.IsEmptyString, "null", $"""{Cursor}""")) & "}"))
ResponserApplyDefs(Header_fb_fr_name_SavedPosts) ResponserApplyDefs(Header_fb_fr_name_SavedPosts)
ThrowAny(Token) ThrowAny(Token)
WaitTimer()
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r) Using j As EContainer = JsonDocument.Parse(r)
@@ -421,6 +430,7 @@ Namespace API.Facebook
If Round > 0 Then ThrowAny(Token) If Round > 0 Then ThrowAny(Token)
Dim script$, newUrl$ Dim script$, newUrl$
Dim jNode As EContainer, jNode2 As EContainer Dim jNode As EContainer, jNode2 As EContainer
WaitTimer()
Dim r$ = resp.GetResponse(PostUrl) Dim r$ = resp.GetResponse(PostUrl)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
@@ -488,16 +498,20 @@ Namespace API.Facebook
#End Region #End Region
#Region "ValidateBaseTokens, GetVideoPageID, GetUserTokens" #Region "ValidateBaseTokens, GetVideoPageID, GetUserTokens"
''' <exception cref="ArgumentNullException"></exception> ''' <exception cref="ArgumentNullException"></exception>
Private Sub ValidateBaseTokens() Protected Overrides Function ValidateBaseTokens() As Boolean
Dim tokens$ = String.Empty Dim tokens$ = String.Empty
If Token_dtsg.IsEmptyString Then tokens.StringAppend("Token_dtsg") If Not ValidateBaseTokens(tokens) Then
If Token_lsd.IsEmptyString Then tokens.StringAppend("Token_lsd") DisableDownload()
If Not tokens.IsEmptyString Then Throw New TokensException($"Unable to obtain token(s) ({tokens}){vbCr}Your credentials may have expired.", True) Throw New TokensException($"Unable to obtain token(s) ({tokens}). Your credentials may have expired.", True)
End Sub Else
Return True
End If
End Function
Private Sub GetVideoPageID(ByVal Token As CancellationToken) Private Sub GetVideoPageID(ByVal Token As CancellationToken)
Dim URL$ = $"{GetProfileUrl()}\videos" Dim URL$ = $"{GetProfileUrl()}\videos"
Dim resp As Responser = HtmlResponserCreate() Dim resp As Responser = HtmlResponserCreate()
Try Try
WaitTimer()
Dim r$ = resp.GetResponse(URL) Dim r$ = resp.GetResponse(URL)
If Not r.IsEmptyString Then VideoPageID = RegexReplace(r, Regex_VideoPageID) If Not r.IsEmptyString Then VideoPageID = RegexReplace(r, Regex_VideoPageID)
Catch ex As Exception Catch ex As Exception
@@ -510,14 +524,20 @@ Namespace API.Facebook
Dim URL$ = If(IsSavedPosts, "https://www.facebook.com/saved", GetProfileUrl()) Dim URL$ = If(IsSavedPosts, "https://www.facebook.com/saved", GetProfileUrl())
Dim resp As Responser = HtmlResponserCreate() Dim resp As Responser = HtmlResponserCreate()
Try Try
Token_dtsg = String.Empty ResetBaseTokens()
Token_lsd = String.Empty
Token_Photosby = String.Empty Token_Photosby = String.Empty
WaitTimer()
Dim r$ = resp.GetResponse(URL) Dim r$ = resp.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
If Responser.CookiesExists Then Responser.Cookies.Update(resp.Cookies) If Responser.CookiesExists Then Responser.Cookies.Update(resp.Cookies)
Token_dtsg = RegexReplace(r, Regex_UserToken_dtsg) ParseTokens(r, 0)
Token_lsd = RegexReplace(r, Regex_UserToken_lsd) Dim app_id$ = RegexReplace(r, Regex_AppID)
If Not app_id.IsEmptyString Then
If Not AEquals(Of String)(MySettings.HH_IG_APP_ID.Value, app_id) Then
MySettings.HH_IG_APP_ID.Value = app_id
Responser.Headers.Add(IG.Header_IG_APP_ID, app_id)
End If
End If
Token_Photosby = RegexReplace(r, Regex_Photos_by) Token_Photosby = RegexReplace(r, Regex_Photos_by)
If StoryBucket.IsEmptyString Then StoryBucket = RegexReplace(r, Regex_StoryBucket) If StoryBucket.IsEmptyString Then StoryBucket = RegexReplace(r, Regex_StoryBucket)
If ID.IsEmptyString Then If ID.IsEmptyString Then
@@ -535,8 +555,7 @@ Namespace API.Facebook
#Region "Responser options" #Region "Responser options"
Private Sub ResponserApplyDefs(ByVal __fb_friendly_name As String) Private Sub ResponserApplyDefs(ByVal __fb_friendly_name As String)
With Responser With Responser
.Headers.Add(ThreadsNet.UserData.Header_FB_LSD, Token_lsd) UpdateHeadersGQL(__fb_friendly_name)
.Headers.Add(DeclaredNames.Header_FB_FRIENDLY_NAME, __fb_friendly_name)
.Method = "POST" .Method = "POST"
.Accept = "*/*" .Accept = "*/*"
.Referer = GetProfileUrl() .Referer = GetProfileUrl()
@@ -556,14 +575,14 @@ Namespace API.Facebook
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "none")) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "none"))
.Add("Sec-Fetch-User", "?1") .Add("Sec-Fetch-User", "?1")
.Add("Upgrade-Insecure-Requests", 1) .Add("Upgrade-Insecure-Requests", 1)
Dim h$ = Responser.Headers.Value(IG.Header_Browser) Dim cloneHeader As Action(Of String) = Sub(ByVal hName As String)
If Not h.IsEmptyString Then .Add(IG.Header_Browser, h) Dim hValue$ = Responser.Headers.Value(hName)
h = Responser.Headers.Value(IG.Header_BrowserExt) If Not hValue.IsEmptyString Then .Add(hName, hValue)
If Not h.IsEmptyString Then .Add(IG.Header_BrowserExt, h) End Sub
h = .Value(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform)) cloneHeader.Invoke(IG.Header_Browser)
If Not h.IsEmptyString Then .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform, h)) cloneHeader.Invoke(IG.Header_BrowserExt)
If ACheck(MySettings.HH_PLATFORM_VER.Value) Then _ cloneHeader.Invoke(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform).Name)
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatformVersion, MySettings.HH_PLATFORM_VER.Value)) cloneHeader.Invoke(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatformVersion).Name)
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaMobile, "?0")) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaMobile, "?0"))
.Add("Sec-Ch-Ua-Model", "") .Add("Sec-Ch-Ua-Model", "")
End With End With
@@ -655,6 +674,7 @@ Namespace API.Facebook
Else Else
URL = String.Format(VideoHtmlUrlPattern, m.Post.ID) URL = String.Format(VideoHtmlUrlPattern, m.Post.ID)
End If End If
WaitTimer()
r = resp.GetResponse(URL) r = resp.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
re.Pattern = String.Format(pattern, nameHD) re.Pattern = String.Format(pattern, nameHD)

View File

@@ -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_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 ReadOnly ObtainMedia_SizeFuncPic_RegexS As RParams = RParams.DMS("_s(\d+)x(\d+)", 1, EDP.ReturnValue)
Friend Const PageTokenRegexPatternDefault As String = "\[\],{""token"":""(.*?)""},\d+\]" 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_wwwClaimName$ = "x-ig-set-www-claim"
Const r_tokenName$ = SiteSettings.Header_CSRF_TOKEN_COOKIE Const r_tokenName$ = SiteSettings.Header_CSRF_TOKEN_COOKIE
If Not Source Is Nothing Then If Not Source Is Nothing Then
@@ -35,17 +37,17 @@ Namespace API.Instagram
Dim token$ = String.Empty Dim token$ = String.Empty
With Source With Source
If isInternal Then If isInternal Then
If .HeadersExists Then wwwClaim = .Headers.Value(wwwClaimName) If UpdateWwwClaim And .HeadersExists Then wwwClaim = .Headers.Value(wwwClaimName)
If .CookiesExists Then token = If(.Cookies.FirstOrDefault(Function(c) c.Name = tokenName)?.Value, String.Empty) If .CookiesExists Then token = If(.Cookies.FirstOrDefault(Function(c) c.Name = tokenName)?.Value, String.Empty)
Else Else
If .HeadersExists Then If .HeadersExists Then
wwwClaim = .Headers.Value(wwwClaimName) If UpdateWwwClaim Then wwwClaim = .Headers.Value(wwwClaimName)
token = .Headers.Value(tokenName) token = .Headers.Value(tokenName)
End If End If
End If End If
End With End With
If Not wwwClaim.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_IG_WWW_CLAIM, wwwClaim) If UpdateWwwClaim And Not wwwClaim.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_IG_WWW_CLAIM, wwwClaim)
If Not token.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_CSRF_TOKEN, token) If Not token.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_CSRF_TOKEN, token)
If Not isInternal Then If Not isInternal Then
Destination.Cookies.Update(Source.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll, False, EDP.SendToLog) Destination.Cookies.Update(Source.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll, False, EDP.SendToLog)

View File

@@ -14,12 +14,13 @@ Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies Imports PersonalUtilities.Tools.Web.Cookies
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Instagram Namespace API.Instagram
<Manifest(InstagramSiteKey), SeparatedTasks(1), SavedPosts, SpecialForm(False)> <Manifest(InstagramSiteKey), SeparatedTasks(1), SavedPosts, SpecialForm(False)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations" #Region "Declarations"
#Region "Providers" #Region "Providers"
Private Class TimersChecker : Inherits FieldsCheckerProviderBase Friend Class TimersChecker : Inherits FieldsCheckerProviderBase
Private ReadOnly LVProvider As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral} Private ReadOnly LVProvider As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
Private ReadOnly _LowestValue As Integer Private ReadOnly _LowestValue As Integer
Friend Sub New(ByVal LowestValue As Integer) Friend Sub New(ByVal LowestValue As Integer)
@@ -32,7 +33,7 @@ Namespace API.Instagram
If Not ACheck(Of Integer)(Value) Then If Not ACheck(Of Integer)(Value) Then
TypeError = True TypeError = True
ElseIf CInt(Value) < _LowestValue Then ElseIf CInt(Value) < _LowestValue Then
ErrorMessage = $"The value of [{Name}] field must be greater than or equal to {_LowestValue.NumToString(LVProvider)}" ErrorMessage = $"The value of '{Name}' field must be greater than or equal to {_LowestValue.NumToString(LVProvider)}"
HasError = True HasError = True
Else Else
Return Value Return Value
@@ -47,13 +48,16 @@ Namespace API.Instagram
If v > 0 Or v = -1 Then If v > 0 Or v = -1 Then
Return Value Return Value
Else Else
ErrorMessage = $"The value of [{Name}] field must be greater than 0 or equal to -1" ErrorMessage = $"The value of '{Name}' field must be greater than 0 or equal to -1"
HasError = True HasError = True
Return Nothing Return Nothing
End If End If
End Function End Function
End Class End Class
#End Region #End Region
#Region "Categories"
Private Const CAT_DOWN As String = "Download data"
#End Region
#Region "Authorization properties" #Region "Authorization properties"
Friend Const Header_IG_APP_ID As String = "x-ig-app-id" Friend Const Header_IG_APP_ID As String = "x-ig-app-id"
Friend Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim" Friend Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim"
@@ -62,24 +66,38 @@ Namespace API.Instagram
Friend Const Header_ASBD_ID As String = "X-Asbd-Id" Friend Const Header_ASBD_ID As String = "X-Asbd-Id"
Friend Const Header_Browser As String = "Sec-Ch-Ua" Friend Const Header_Browser As String = "Sec-Ch-Ua"
Friend Const Header_BrowserExt As String = "Sec-Ch-Ua-Full-Version-List" Friend Const Header_BrowserExt As String = "Sec-Ch-Ua-Full-Version-List"
Friend Const Header_Platform As String = "Sec-Ch-Ua-Platform-Version" Friend Const Header_Platform_Verion As String = "Sec-Ch-Ua-Platform-Version"
<PropertyOption(ControlText:="x-csrftoken", ControlToolTip:="Can be automatically extracted from cookies", IsAuth:=True, AllowNull:=True), ControlNumber(2), PClonable(Clone:=False)> <PropertyOption(ControlText:="x-csrftoken", ControlToolTip:="Can be automatically extracted from cookies", IsAuth:=True, AllowNull:=True), ControlNumber(2), PClonable(Clone:=False)>
Friend ReadOnly Property HH_CSRF_TOKEN As PropertyValue Friend ReadOnly Property HH_CSRF_TOKEN As PropertyValue
<CookieValueExtractor(NameOf(HH_CSRF_TOKEN))>
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
Return c.GetCookieValue(Header_CSRF_TOKEN_COOKIE, PropName, NameOf(HH_CSRF_TOKEN))
End Function
<PropertyOption(ControlText:="x-ig-app-id", IsAuth:=True, AllowNull:=False), ControlNumber(3), PClonable(Clone:=False)> <PropertyOption(ControlText:="x-ig-app-id", IsAuth:=True, AllowNull:=False), ControlNumber(3), PClonable(Clone:=False)>
Friend Property HH_IG_APP_ID As PropertyValue Friend ReadOnly Property HH_IG_APP_ID As PropertyValue
<PropertyOption(ControlText:="x-asbd-id", IsAuth:=True, AllowNull:=True), ControlNumber(4), PClonable(Clone:=False)> <PropertyOption(ControlText:="x-asbd-id", IsAuth:=True, AllowNull:=True), ControlNumber(4), PClonable(Clone:=False)>
Friend Property HH_ASBD_ID As PropertyValue Friend ReadOnly Property HH_ASBD_ID As PropertyValue
'PropertyOption(ControlText:="x-ig-www-claim", IsAuth:=True, AllowNull:=True) 'PropertyOption(ControlText:="x-ig-www-claim", IsAuth:=True, AllowNull:=True)
<ControlNumber(5), PClonable(Clone:=False)> <ControlNumber(5), PClonable(Clone:=False)>
Friend Property HH_IG_WWW_CLAIM As PropertyValue Friend ReadOnly Property HH_IG_WWW_CLAIM As PropertyValue
<PropertyOption(ControlText:="sec-ch-ua", IsAuth:=True, AllowNull:=True), ControlNumber(6), PClonable> Private ReadOnly Property HH_IG_WWW_CLAIM_IS_ZERO As Boolean
Private Property HH_BROWSER As PropertyValue Get
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", IsAuth:=True, AllowNull:=True), ControlNumber(7), PClonable> Dim v$ = AConvert(Of String)(HH_IG_WWW_CLAIM.Value, String.Empty)
Private Property HH_BROWSER_EXT As PropertyValue Return Not v.IsEmptyString AndAlso v = "0"
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, AllowNull:=True), ControlNumber(8), PClonable> End Get
Private Property HH_PLATFORM As PropertyValue End Property
<PropertyOption(ControlText:="UserAgent", IsAuth:=True, AllowNull:=True), ControlNumber(9), PClonable> <PropertyOption(ControlText:="sec-ch-ua", IsAuth:=True, AllowNull:=True,
Private Property HH_USER_AGENT As PropertyValue InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua), ControlNumber(6), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_BROWSER As PropertyValue
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", IsAuth:=True, AllowNull:=True,
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list), ControlNumber(7), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_BROWSER_EXT As PropertyValue
<PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:="sec-ch-ua-platform-version", IsAuth:=True, AllowNull:=True, LeftOffset:=135,
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_platform_version), ControlNumber(8), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_PLATFORM As PropertyValue
<PropertyOption(ControlText:="UserAgent", IsAuth:=True, AllowNull:=True,
InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), ControlNumber(9), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_USER_AGENT As PropertyValue
Friend Overrides Function BaseAuthExists() As Boolean Friend Overrides Function BaseAuthExists() As Boolean
Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value) And ACheck(HH_CSRF_TOKEN.Value) Return Responser.CookiesExists And ACheck(HH_IG_APP_ID.Value) And ACheck(HH_CSRF_TOKEN.Value)
End Function End Function
@@ -95,7 +113,7 @@ Namespace API.Instagram
Case NameOf(HH_CSRF_TOKEN) : f = Header_CSRF_TOKEN Case NameOf(HH_CSRF_TOKEN) : f = Header_CSRF_TOKEN
Case NameOf(HH_BROWSER) : f = Header_Browser Case NameOf(HH_BROWSER) : f = Header_Browser
Case NameOf(HH_BROWSER_EXT) : f = Header_BrowserExt Case NameOf(HH_BROWSER_EXT) : f = Header_BrowserExt
Case NameOf(HH_PLATFORM) : f = Header_Platform Case NameOf(HH_PLATFORM) : f = Header_Platform_Verion
Case NameOf(HH_USER_AGENT) : isUserAgent = True Case NameOf(HH_USER_AGENT) : isUserAgent = True
End Select End Select
If Not f.IsEmptyString Then If Not f.IsEmptyString Then
@@ -106,29 +124,76 @@ Namespace API.Instagram
End If End If
End If End If
End Sub End Sub
#Region "HH_IG_WWW_CLAIM"
<PropertyOption(ControlText:="ig-www-claim update interval", IsAuth:=True, LeftOffset:=150), PXML, ControlNumber(10), PClonable, HiddenControl>
Private ReadOnly Property HH_IG_WWW_CLAIM_UPDATE_INTERVAL As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: always 0", ControlToolTip:="Keep token value always = 0", IsAuth:=True),
PXML, ControlNumber(11), PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_ALWAYS_ZERO As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: reset each session", ControlToolTip:="Set 'x-ig-www-claim' to '0' before each session", IsAuth:=True),
PXML, ControlNumber(12), PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_RESET_EACH_SESSION As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: reset each target", ControlToolTip:="Set 'x-ig-www-claim' to '0' before each target", IsAuth:=True),
PXML, ControlNumber(13), PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_RESET_EACH_TARGET As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: use in requests", IsAuth:=True), PXML, ControlNumber(14), PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_USE As PropertyValue
<PropertyOption(ControlText:="ig-www-claim: use default algorithm to update", IsAuth:=True), PXML, ControlNumber(15), PClonable, HiddenControl>
Friend ReadOnly Property HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO As PropertyValue
<Provider(NameOf(HH_IG_WWW_CLAIM_UPDATE_INTERVAL), FieldsChecker:=True)>
Private ReadOnly Property TokenUpdateIntervalProvider As IFormatProvider
#End Region
<PropertyOption(ControlText:="Use GraphQL to download", IsAuth:=True), PXML, ControlNumber(16), PClonable>
Friend ReadOnly Property USE_GQL As PropertyValue
#End Region #End Region
#Region "Download properties" #Region "Download properties"
<PropertyOption(ControlText:="Request timer", AllowNull:=False), PXML("RequestsWaitTimer"), ControlNumber(20), PClonable> <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 Friend ReadOnly Property RequestsWaitTimer As PropertyValue
<Provider(NameOf(RequestsWaitTimer), FieldsChecker:=True)> <Provider(NameOf(RequestsWaitTimer), FieldsChecker:=True)>
Private ReadOnly Property RequestsWaitTimerProvider As IFormatProvider Private ReadOnly Property RequestsWaitTimerProvider As IFormatProvider
<PropertyOption(ControlText:="Request timer counter", AllowNull:=False, LeftOffset:=120), PXML("RequestsWaitTimerTaskCount"), ControlNumber(21), PClonable> <PropertyOption(ControlText:="Request timer counter",
ControlToolTip:="How many requests will be sent to Instagram before the program waits 'Request timer'." &
vbCr & "The default value is 1." & vbCr & "The minimum value is 1." & TimersUrgentTip,
AllowNull:=False, LeftOffset:=120, Category:=DN.CAT_Timers), PXML, ControlNumber(21), PClonable>
Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue
<Provider(NameOf(RequestsWaitTimerTaskCount), FieldsChecker:=True)> <Provider(NameOf(RequestsWaitTimerTaskCount), FieldsChecker:=True)>
Private ReadOnly Property RequestsWaitTimerTaskCountProvider As IFormatProvider Private ReadOnly Property RequestsWaitTimerTaskCountProvider As IFormatProvider
<PropertyOption(ControlText:="Posts limit timer", AllowNull:=False), PXML("SleepTimerOnPostsLimit"), ControlNumber(22), PClonable> <PropertyOption(ControlText:="Posts limit timer",
ControlToolTip:="The time value (in milliseconds) the program will wait before processing the next request after 195 requests." &
vbCr & "The default value is 60'000." & vbCr & "The minimum value is 10'000." & TimersUrgentTip,
AllowNull:=False, Category:=DN.CAT_Timers), PXML, ControlNumber(22), PClonable>
Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue
<Provider(NameOf(SleepTimerOnPostsLimit), FieldsChecker:=True)> <Provider(NameOf(SleepTimerOnPostsLimit), FieldsChecker:=True)>
Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider
<PropertyOption(ControlText:="Get timeline", ControlToolTip:="Default value for new users"), PXML, ControlNumber(23), PClonable> <PropertyOption(ControlText:="Get timeline", ControlToolTip:="Default value for new users", Category:=DN.CAT_UserDefs), PXML, ControlNumber(23), PClonable>
Friend ReadOnly Property GetTimeline As PropertyValue 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 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 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 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 Friend ReadOnly Property GetTagged As PropertyValue
<PropertyOption(ControlText:="Tagged notify limit", <PropertyOption(ControlText:="Tagged notify limit",
ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr & ControlToolTip:="If the number of tagged posts exceeds this number you will be notified." & vbCr &
@@ -138,20 +203,40 @@ Namespace API.Instagram
Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider
#End Region #End Region
#Region "Download ready" #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 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 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 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 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 Friend ReadOnly Property DownloadTagged As PropertyValue
<PXML> Private ReadOnly Property DownloadTagged_Def As PropertyValue
#End Region #End Region
#Region "429 bypass" #Region "429 bypass"
<PXML("InstagramDownloadingErrorDate")> <PXML("InstagramDownloadingErrorDate")>
Private ReadOnly Property DownloadingErrorDate As PropertyValue Private ReadOnly Property DownloadingErrorDate As PropertyValue
<Provider(NameOf(DownloadingErrorDate))>
Private ReadOnly Property DownloadingErrorDateProvider As IFormatProvider =
New CustomProvider(Function(ByVal v As Object, ByVal d As Type) As Object
If d Is GetType(Date) Then
Return AConvert(Of Date)(v, AModes.Var, Nothing)
ElseIf d Is GetType(String) Then
If Not IsNothing(v) AndAlso TypeOf v Is Date AndAlso CDate(v) = Date.MinValue Then
Return String.Empty
Else
Return AConvert(Of String)(v, AModes.XML, String.Empty)
End If
Else
Return Nothing
End If
End Function)
Friend Property LastApplyingValue As Integer? = Nothing Friend Property LastApplyingValue As Integer? = Nothing
Friend ReadOnly Property ReadyForDownload As Boolean Friend ReadOnly Property ReadyForDownload As Boolean
Get Get
@@ -165,11 +250,64 @@ Namespace API.Instagram
End With End With
End Get End Get
End Property End Property
Private Const LastDownloadDateResetInterval As Integer = 60
<PXML> Private ReadOnly Property LastDownloadDate As PropertyValue <PXML> Private ReadOnly Property LastDownloadDate As PropertyValue
<PXML> Private ReadOnly Property LastRequestsCount As PropertyValue <PXML> Private ReadOnly Property LastRequestsCount As PropertyValue
Private ReadOnly MyLastRequests As Dictionary(Of Date, Integer)
Private ReadOnly Property MyLastRequestsDate As Date
Get
Try
Return If(MyLastRequests.Count > 0, MyLastRequests.Keys.Max, Now.AddDays(-1))
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[SiteSettings.Instagram.MyLastRequestsDate]", Now.AddDays(-1))
End Try
End Get
End Property
Private Property MyLastRequestsCount As Integer
Get
Try
Return If(MyLastRequests.Count > 0, MyLastRequests.Values.Sum, 0)
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[SiteSettings.Instagram.MyLastRequestsCount]", 0)
End Try
End Get
Set(ByVal NewValue As Integer)
If Not MyLastRequests.ContainsKey(ActiveSessionDate) Then
MyLastRequests.Add(ActiveSessionDate, NewValue)
Else
MyLastRequests(ActiveSessionDate) += NewValue
End If
End Set
End Property
Private Sub RefreshMyLastRequests(Optional ByVal DateToReplace As Date? = Nothing)
Try
With MyLastRequests
If .Count > 0 Then
Dim d As Date
For i% = .Count - 1 To 0 Step -1
d = .Keys(i)
If (Not DateToReplace.HasValue OrElse ActiveJobs < 1 OrElse d <> ActiveSessionDate) And
d.AddMinutes(LastDownloadDateResetInterval) < Now Then .Remove(d)
Next
End If
If .Count > 0 Then
If DateToReplace.HasValue Then
If .Keys.Contains(ActiveSessionDate) Then
Dim v% = .Item(ActiveSessionDate)
.Remove(ActiveSessionDate)
.Add(DateToReplace.Value, v)
End If
End If
LastDownloadDate.Value = .Keys.Max
LastRequestsCount.Value = .Values.Sum
End If
End With
Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[SiteSettings.Instagram.RefreshMyLastRequests]")
End Try
End Sub
<PropertyOption(IsInformationLabel:=True), ControlNumber(100)> <PropertyOption(IsInformationLabel:=True), ControlNumber(100)>
Private Property LastRequestsCountLabel As PropertyValue Private ReadOnly Property LastRequestsCountLabel As PropertyValue
Private ReadOnly LastRequestsCountLabelStr As Func(Of Integer, String) = Function(r) $"Number of spent requests: {r.NumToGroupIntegral}"
Private TooManyRequestsReadyForCatch As Boolean = True Private TooManyRequestsReadyForCatch As Boolean = True
Friend Function GetWaitDate() As Date Friend Function GetWaitDate() As Date
With DownloadingErrorDate With DownloadingErrorDate
@@ -224,13 +362,19 @@ Namespace API.Instagram
asbd = .Value(Header_ASBD_ID) asbd = .Value(Header_ASBD_ID)
browser = .Value(Header_Browser) browser = .Value(Header_Browser)
browserExt = .Value(Header_BrowserExt) browserExt = .Value(Header_BrowserExt)
platform = .Value(Header_Platform) platform = .Value(Header_Platform_Verion)
End If End If
'.Add(Header_IG_WWW_CLAIM, 0)
.Add("Origin", "https://www.instagram.com")
.Add("authority", "www.instagram.com")
.Add("Dnt", 1) .Add("Dnt", 1)
'.Add("Dpr", 1)
.Remove("Dpr")
.Add("Sec-Ch-Ua-Mobile", "?0") .Add("Sec-Ch-Ua-Mobile", "?0")
.Add("Sec-Ch-Ua-Model", """""")
.Add("Sec-Ch-Ua-Platform", """Windows""") .Add("Sec-Ch-Ua-Platform", """Windows""")
.Add("Sec-Fetch-Dest", "empty") .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest, "empty"))
.Add("Sec-Fetch-Mode", "cors") .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "cors"))
.Add("Sec-Fetch-Site", "same-origin") .Add("Sec-Fetch-Site", "same-origin")
.Add("X-Requested-With", "XMLHttpRequest") .Add("X-Requested-With", "XMLHttpRequest")
End With End With
@@ -248,12 +392,31 @@ Namespace API.Instagram
HH_PLATFORM = New PropertyValue(platform, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_PLATFORM), v)) HH_PLATFORM = New PropertyValue(platform, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_PLATFORM), v))
HH_USER_AGENT = New PropertyValue(useragent, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_USER_AGENT), v)) HH_USER_AGENT = New PropertyValue(useragent, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_USER_AGENT), v))
DownloadTimeline = New PropertyValue(True) HH_IG_WWW_CLAIM_UPDATE_INTERVAL = New PropertyValue(120)
DownloadReels = New PropertyValue(False) HH_IG_WWW_CLAIM_ALWAYS_ZERO = New PropertyValue(False)
DownloadStories = New PropertyValue(True) HH_IG_WWW_CLAIM_RESET_EACH_SESSION = New PropertyValue(True)
DownloadStoriesUser = New PropertyValue(True) HH_IG_WWW_CLAIM_RESET_EACH_TARGET = New PropertyValue(True)
DownloadTagged = New PropertyValue(False) 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) RequestsWaitTimer = New PropertyValue(1000)
RequestsWaitTimerProvider = New TimersChecker(100) RequestsWaitTimerProvider = New TimersChecker(100)
RequestsWaitTimerTaskCount = New PropertyValue(1) RequestsWaitTimerTaskCount = New PropertyValue(1)
@@ -269,17 +432,22 @@ Namespace API.Instagram
TaggedNotifyLimit = New PropertyValue(200) TaggedNotifyLimit = New PropertyValue(200)
TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker
DownloadingErrorDate = New PropertyValue(Nothing, GetType(Date)) DownloadingErrorDate = New PropertyValue(Now.AddYears(-10), GetType(Date))
LastDownloadDate = New PropertyValue(Now.AddDays(-1)) LastDownloadDate = New PropertyValue(Now.AddDays(-1))
LastRequestsCount = New PropertyValue(0) LastRequestsCount = New PropertyValue(0)
LastRequestsCountLabel = New PropertyValue(LastRequestsCountLabelStr.Invoke(LastRequestsCount.Value)) LastRequestsCountLabel = New PropertyValue(String.Empty, GetType(String))
LastRequestsCount.OnChangeFunction = Sub(vv) LastRequestsCountLabel.Value = LastRequestsCountLabelStr.Invoke(vv) MyLastRequests = New Dictionary(Of Date, Integer)
_AllowUserAgentUpdate = False _AllowUserAgentUpdate = False
UrlPatternUser = "https://www.instagram.com/{0}/" UrlPatternUser = "https://www.instagram.com/{0}/"
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "instagram.com/"), 1)
ImageVideoContains = "instagram.com" ImageVideoContains = "instagram.com"
End Sub End Sub
Friend Overrides Sub EndInit()
Try : MyLastRequests.Add(LastDownloadDate.Value, LastRequestsCount.Value) : Catch : End Try
If Not CBool(HH_IG_WWW_CLAIM_USE.Value) Then Responser.Headers.Remove(Header_IG_WWW_CLAIM)
MyBase.EndInit()
End Sub
#End Region #End Region
#Region "PropertiesDataChecker" #Region "PropertiesDataChecker"
<PropertiesDataChecker({NameOf(TaggedNotifyLimit)})> <PropertiesDataChecker({NameOf(TaggedNotifyLimit)})>
@@ -307,16 +475,97 @@ Namespace API.Instagram
End Function End Function
#End Region #End Region
#Region "Downloading" #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
Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
If MyBase.Available(What, Silent) 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 Property SkipUntilNextSession As Boolean = False
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
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 End Function
Private ActiveJobs As Integer = 0 Private ActiveJobs As Integer = 0
Private ActiveSessionDate As Date
Private ActiveSessionRequestsExists As Boolean = False
Private _NextWNM As UserData.WNM = UserData.WNM.Notify Private _NextWNM As UserData.WNM = UserData.WNM.Notify
Private _NextTagged As Boolean = True Private _NextTagged As Boolean = True
Friend Overrides Sub DownloadStarted(ByVal What As Download) Friend Overrides Sub DownloadStarted(ByVal What As Download)
If ActiveJobs = 0 Then ActiveSessionRequestsExists = False
ActiveJobs += 1 ActiveJobs += 1
If CDate(LastDownloadDate.Value).AddMinutes(120) < Now Or Not ACheck(HH_IG_WWW_CLAIM.Value) Then HH_IG_WWW_CLAIM.Value = "0" If What = Download.Main Then ____DownloadStarted = True
If ActiveJobs = 1 Then ActiveSessionDate = Now
If Not HH_IG_WWW_CLAIM_IS_ZERO AndAlso
(
(CBool(HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value) AndAlso MyLastRequestsDate.AddMinutes(HH_IG_WWW_CLAIM_UPDATE_INTERVAL.Value) < Now) Or
Not ACheck(HH_IG_WWW_CLAIM.Value) Or
(
Not (
CBool(HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value) And
(CBool(HH_IG_WWW_CLAIM_RESET_EACH_SESSION.Value) Or CBool(HH_IG_WWW_CLAIM_ALWAYS_ZERO.Value))
)
)
) Then HH_IG_WWW_CLAIM.Value = "0"
End Sub End Sub
Friend Overrides Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download) Friend Overrides Sub BeforeStartDownload(ByVal User As Object, ByVal What As Download)
With DirectCast(User, UserData) With DirectCast(User, UserData)
@@ -324,10 +573,9 @@ Namespace API.Instagram
.WaitNotificationMode = _NextWNM .WaitNotificationMode = _NextWNM
.TaggedCheckSession = _NextTagged .TaggedCheckSession = _NextTagged
End If End If
If CDate(LastDownloadDate.Value).AddMinutes(60) > Now Then If MyLastRequestsDate.AddMinutes(LastDownloadDateResetInterval) > Now Then
.RequestsCount = LastRequestsCount.Value .RequestsCount = MyLastRequestsCount
Else Else
LastRequestsCount.Value = 0
.RequestsCount = 0 .RequestsCount = 0
End If End If
End With End With
@@ -337,8 +585,8 @@ Namespace API.Instagram
_NextWNM = .WaitNotificationMode _NextWNM = .WaitNotificationMode
If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify
_NextTagged = .TaggedCheckSession _NextTagged = .TaggedCheckSession
LastDownloadDate.Value = Now MyLastRequestsCount = .RequestsCountSession
LastRequestsCount.Value = .RequestsCount If .RequestsCountSession > 0 Then ActiveSessionRequestsExists = True
_FieldsChangerSuspended = True _FieldsChangerSuspended = True
HH_IG_WWW_CLAIM.Value = Responser.Headers.Value(Header_IG_WWW_CLAIM) HH_IG_WWW_CLAIM.Value = Responser.Headers.Value(Header_IG_WWW_CLAIM)
HH_CSRF_TOKEN.Value = Responser.Headers.Value(Header_CSRF_TOKEN) HH_CSRF_TOKEN.Value = Responser.Headers.Value(Header_CSRF_TOKEN)
@@ -348,9 +596,16 @@ Namespace API.Instagram
Friend Overrides Sub DownloadDone(ByVal What As Download) Friend Overrides Sub DownloadDone(ByVal What As Download)
_NextWNM = UserData.WNM.Notify _NextWNM = UserData.WNM.Notify
_NextTagged = True _NextTagged = True
LastDownloadDate.Value = Now If ActiveSessionRequestsExists Then RefreshMyLastRequests(Now)
ActiveJobs -= 1 ActiveJobs -= 1
SkipUntilNextSession = False SkipUntilNextSession = False
If What = Download.Main Then ____DownloadStarted = False
If ActiveJobs = 0 Then
____AvailableRequested = False
____AvailableChecked = False
____AvailableSilent = True
____AvailableResult = False
End If
End Sub End Sub
#End Region #End Region
#Region "Settings" #Region "Settings"
@@ -362,7 +617,17 @@ Namespace API.Instagram
Private ____HH_PLATFORM As String = String.Empty Private ____HH_PLATFORM As String = String.Empty
Private ____HH_USER_AGENT As String = String.Empty Private ____HH_USER_AGENT As String = String.Empty
Private ____Cookies As CookieKeeper = Nothing 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() 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_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_IG_APP_ID = AConvert(Of String)(HH_IG_APP_ID.Value, String.Empty)
____HH_ASBD_ID = AConvert(Of String)(HH_ASBD_ID.Value, String.Empty) ____HH_ASBD_ID = AConvert(Of String)(HH_ASBD_ID.Value, String.Empty)
@@ -371,6 +636,11 @@ Namespace API.Instagram
____HH_PLATFORM = AConvert(Of String)(HH_PLATFORM.Value, String.Empty) ____HH_PLATFORM = AConvert(Of String)(HH_PLATFORM.Value, String.Empty)
____HH_USER_AGENT = AConvert(Of String)(HH_USER_AGENT.Value, String.Empty) ____HH_USER_AGENT = AConvert(Of String)(HH_USER_AGENT.Value, String.Empty)
____Cookies = Responser.Cookies.Copy ____Cookies = Responser.Cookies.Copy
__DownloadTimeline = DownloadTimeline.Value
__DownloadReels = DownloadReels.Value
__DownloadStories = DownloadStories.Value
__DownloadStoriesUser = DownloadStoriesUser.Value
__DownloadTagged = DownloadTagged.Value
MyBase.BeginEdit() MyBase.BeginEdit()
End Sub End Sub
Friend Overrides Sub Update() Friend Overrides Sub Update()
@@ -383,13 +653,34 @@ Namespace API.Instagram
New With {.ValueOld = ____HH_PLATFORM, .ValueNew = AConvert(Of String)(HH_PLATFORM.Value, String.Empty).ToString}, New With {.ValueOld = ____HH_PLATFORM, .ValueNew = AConvert(Of String)(HH_PLATFORM.Value, String.Empty).ToString},
New With {.ValueOld = ____HH_USER_AGENT, .ValueNew = AConvert(Of String)(HH_USER_AGENT.Value, String.Empty).ToString} 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 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 If Responser.CookiesExists Then
Dim csrf$ = If(Responser.Cookies.FirstOrDefault(Function(c) c.Name.StringToLower = 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 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
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() MyBase.Update()
End Sub End Sub
Friend Overrides Sub EndEdit() Friend Overrides Sub EndEdit()
@@ -415,6 +706,12 @@ Namespace API.Instagram
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "Can't open user's post", String.Empty) Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "Can't open user's post", String.Empty)
End Try End Try
End Function End Function
#End Region
#Region "IDisposable Support"
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not disposedValue And disposing And Not MyLastRequests Is Nothing Then MyLastRequests.Clear()
MyBase.Dispose(disposing)
End Sub
#End Region #End Region
End Class End Class
End Namespace End Namespace

View 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

View File

@@ -69,7 +69,6 @@ Namespace API.Instagram
Return New EContainer("Post", ID, {New EAttribute(Name_Section, CInt(Section)), New EAttribute(Name_Code, Code)}) Return New EContainer("Post", ID, {New EAttribute(Name_Section, CInt(Section)), New EAttribute(Name_Code, Code)})
End Function End Function
End Structure End Structure
Friend Const Header_FB_LSD As String = "x-fb-lsd"
Private ReadOnly Property MySiteSettings As SiteSettings Private ReadOnly Property MySiteSettings As SiteSettings
Get Get
Return DirectCast(HOST.Source, SiteSettings) Return DirectCast(HOST.Source, SiteSettings)
@@ -139,14 +138,19 @@ Namespace API.Instagram
Friend Sub New() Friend Sub New()
PostsKVIDs = New List(Of PostKV) PostsKVIDs = New List(Of PostKV)
PostsToReparse = New List(Of PostKV) PostsToReparse = New List(Of PostKV)
_ResponserAutoUpdateCookies = True
End Sub End Sub
#End Region #End Region
#Region "Download data" #Region "Download data"
Private WwwClaimUpdate As Boolean = True
Private WwwClaimUpdate_R As Boolean = True
Private WwwClaimDefaultAlgo As Boolean = True
Private WwwClaimUse As Boolean = True
Private E560Thrown As Boolean = False Private E560Thrown As Boolean = False
Friend Err5xx As Integer = -1 Friend Err5xx As Integer = -1
Private Class ExitException : Inherits Exception Private Class ExitException : Inherits Exception
Friend Property Is560 As Boolean = False Friend Property Is560 As Boolean = False
Friend Property IsTokens As Boolean = False
Friend Property TokensData As String = String.Empty
Friend Shared Sub Throw560(ByRef Source As UserData) Friend Shared Sub Throw560(ByRef Source As UserData)
If Not Source.E560Thrown Then If Not Source.E560Thrown Then
MyMainLOG = $"{Source.ToStringForLog}: ({IIf(Source.Err5xx > 0, Source.Err5xx, 560)}) Download skipped until next session" MyMainLOG = $"{Source.ToStringForLog}: ({IIf(Source.Err5xx > 0, Source.Err5xx, 560)}) Download skipped until next session"
@@ -154,6 +158,10 @@ Namespace API.Instagram
End If End If
Throw New ExitException With {.Is560 = True} Throw New ExitException With {.Is560 = True}
End Sub End Sub
Friend Shared Sub ThrowTokens(ByRef Source As UserData, ByVal Data As String)
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 End Class
Private ReadOnly Property MyFilePostsKV As SFile Private ReadOnly Property MyFilePostsKV As SFile
Get Get
@@ -235,8 +243,75 @@ Namespace API.Instagram
Private _DownloadingInProgress As Boolean = False Private _DownloadingInProgress As Boolean = False
Private _Limit As Integer = -1 Private _Limit As Integer = -1
Private _TotalPostsParsed As Integer = 0 Private _TotalPostsParsed As Integer = 0
Private _LastWwwClaim As String = String.Empty
Private _ResponserGQLMode As Boolean = False
Private _UseGQL As Boolean = False
Private Sub ChangeResponserMode(ByVal GQL As Boolean, Optional ByVal Force As Boolean = False)
If Not _ResponserGQLMode = GQL Or Force Then
_ResponserGQLMode = GQL
ChangeResponserMode_StoreWwwClaim()
Responser.Headers.Clear()
Responser.Headers.AddRange(MySiteSettings.Responser.Headers)
If GQL Then
WwwClaimUpdate = False
With Responser
.Method = "POST"
.ContentType = "application/x-www-form-urlencoded"
.Referer = MySiteSettings.GetUserUrl(Me)
.CookiesExtractMode = Responser.CookiesExtractModes.Any
With .Headers
.Remove(SiteSettings.Header_IG_WWW_CLAIM)
.Add("origin", "https://www.instagram.com")
.Add("authority", "www.instagram.com")
End With
End With
Else
WwwClaimUpdate = WwwClaimUpdate_R
With Responser
.Method = "GET"
.ContentType = Nothing
.Referer = Nothing
.CookiesExtractMode = MySiteSettings.Responser.CookiesExtractMode
With .Headers
.Remove("origin")
.Remove("authority")
.Remove(GQL_HEADER_FB_FRINDLY_NAME)
.Remove(GQL_HEADER_FB_LSD)
Dim hv$ = MySiteSettings.Responser.Headers.Value(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest)).IfNullOrEmpty("empty")
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest, hv))
hv = MySiteSettings.Responser.Headers.Value(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode)).IfNullOrEmpty("cors")
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, hv))
If Not _UseGQL And WwwClaimUse Then .Add(SiteSettings.Header_IG_WWW_CLAIM, _LastWwwClaim)
End With
End With
End If
End If
End Sub
Private Sub ChangeResponserMode_StoreWwwClaim()
If Not _UseGQL Then
With Responser.Headers
If .Contains(SiteSettings.Header_IG_WWW_CLAIM) AndAlso Not .Value(SiteSettings.Header_IG_WWW_CLAIM).IsEmptyString Then _LastWwwClaim = .Value(SiteSettings.Header_IG_WWW_CLAIM)
End With
End If
End Sub
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
ResetBaseTokens()
UserNameRequested = False UserNameRequested = False
RequestsCountSession = 0
_LastWwwClaim = String.Empty
_ResponserGQLMode = False
_UseGQL = MySiteSettings.USE_GQL.Value
WwwClaimUse = MySiteSettings.HH_IG_WWW_CLAIM_USE.Value
WwwClaimDefaultAlgo = MySiteSettings.HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO.Value
With MySiteSettings : WwwClaimUpdate = (Not CBool(.HH_IG_WWW_CLAIM_ALWAYS_ZERO.Value) And CBool(.HH_IG_WWW_CLAIM_USE.Value)) Or
WwwClaimDefaultAlgo : End With
WwwClaimUpdate_R = WwwClaimUpdate
Dim upClaimRequest As Action = Sub() If WwwClaimUpdate And Not WwwClaimDefaultAlgo And CBool(MySiteSettings.HH_IG_WWW_CLAIM_RESET_EACH_TARGET.Value) Then _
Responser.Headers.Add(SiteSettings.Header_IG_WWW_CLAIM, 0)
DefaultParser_ElemNode = Nothing
ChangeResponserMode(_UseGQL)
Dim s As Sections = Sections.Timeline Dim s As Sections = Sections.Timeline
Dim errorFound As Boolean = False Dim errorFound As Boolean = False
Try Try
@@ -251,6 +326,7 @@ Namespace API.Instagram
Dim dt As Func(Of Boolean) = Function() (CBool(MySiteSettings.DownloadTimeline.Value) And GetTimeline) Or IsSavedPosts Dim dt As Func(Of Boolean) = Function() (CBool(MySiteSettings.DownloadTimeline.Value) And GetTimeline) Or IsSavedPosts
If dt.Invoke And Not LastCursor.IsEmptyString Then If dt.Invoke And Not LastCursor.IsEmptyString Then
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline) s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
upClaimRequest.Invoke
DownloadData(LastCursor, s, Token) DownloadData(LastCursor, s, Token)
ProgressPre.Done() ProgressPre.Done()
ThrowAny(Token) ThrowAny(Token)
@@ -258,27 +334,51 @@ Namespace API.Instagram
End If End If
If dt.Invoke And Not HasError Then If dt.Invoke And Not HasError Then
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline) s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
upClaimRequest.Invoke
ChangeResponserMode(_UseGQL)
DownloadData(String.Empty, s, Token) DownloadData(String.Empty, s, Token)
ProgressPre.Done() ProgressPre.Done()
ThrowAny(Token) ThrowAny(Token)
If Not HasError Then FirstLoadingDone = True If Not HasError Then FirstLoadingDone = True
End If End If
DefaultParser_ElemNode = Nothing
If FirstLoadingDone Then LastCursor = String.Empty If FirstLoadingDone Then LastCursor = String.Empty
If Not IsSavedPosts AndAlso MySiteSettings.BaseAuthExists() Then If Not IsSavedPosts AndAlso MySiteSettings.BaseAuthExists() Then
DefaultParser_ElemNode = Nothing
ChangeResponserMode(_UseGQL)
If CBool(MySiteSettings.DownloadReels.Value) And GetReels Then If CBool(MySiteSettings.DownloadReels.Value) And GetReels Then
s = Sections.Reels s = Sections.Reels
DefaultParser_ElemNode = {"node", "media"} DefaultParser_ElemNode = {"node", "media"}
upClaimRequest.Invoke
ChangeResponserMode(True)
DownloadData(String.Empty, s, Token) DownloadData(String.Empty, s, Token)
DefaultParser_ElemNode = Nothing GetReelsGQL_SetEnvir = False
DownloadReels_SetEnvir = False
ProgressPre.Done() ProgressPre.Done()
End If End If
If CBool(MySiteSettings.DownloadStories.Value) And GetStories Then s = Sections.Stories : DownloadData(String.Empty, s, Token) : ProgressPre.Done() DefaultParser_ElemNode = Nothing
If CBool(MySiteSettings.DownloadStoriesUser.Value) And GetStoriesUser Then s = Sections.UserStories : DownloadData(String.Empty, s, Token) : ProgressPre.Done() ChangeResponserMode(_UseGQL)
If CBool(MySiteSettings.DownloadTagged.Value) And GetTaggedData Then If CBool(MySiteSettings.DownloadStories.Value) And GetStories Then
s = Sections.Tagged s = Sections.Stories
upClaimRequest.Invoke
DownloadData(String.Empty, s, Token) DownloadData(String.Empty, s, Token)
ProgressPre.Done() 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) If PostsToReparse.Count > 0 Then DownloadPosts(Token, True)
End If End If
End If End If
@@ -289,7 +389,7 @@ Namespace API.Instagram
Throw ex Throw ex
Finally Finally
DefaultParser_ElemNode = Nothing DefaultParser_ElemNode = Nothing
DownloadReels_SetEnvir = False GetReelsGQL_SetEnvir = False
E560Thrown = False E560Thrown = False
UpdateResponser() UpdateResponser()
ValidateExtension() ValidateExtension()
@@ -315,13 +415,13 @@ Namespace API.Instagram
If _DownloadingInProgress AndAlso Not Responser Is Nothing AndAlso Not Responser.Disposed Then If _DownloadingInProgress AndAlso Not Responser Is Nothing AndAlso Not Responser.Disposed Then
_DownloadingInProgress = False _DownloadingInProgress = False
Responser_ResponseReceived_RemoveHandler() Responser_ResponseReceived_RemoveHandler()
Declarations.UpdateResponser(Responser, MySiteSettings.Responser) Declarations.UpdateResponser(Responser, MySiteSettings.Responser, WwwClaimUpdate)
End If End If
Catch Catch
End Try End Try
End Sub End Sub
Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse) Protected Overrides Sub Responser_ResponseReceived(ByVal Sender As Object, ByVal e As EventArguments.WebDataResponse)
Declarations.UpdateResponser(e, Responser) Declarations.UpdateResponser(e, Responser, WwwClaimUpdate)
End Sub End Sub
Protected Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum Protected Enum Sections : Timeline : Reels : Tagged : Stories : UserStories : SavedPosts : End Enum
Protected Const StoriesFolder As String = "Stories" Protected Const StoriesFolder As String = "Stories"
@@ -329,6 +429,12 @@ Namespace API.Instagram
#Region "429 bypass" #Region "429 bypass"
Private Const MaxPostsCount As Integer = 200 Private Const MaxPostsCount As Integer = 200
Friend Property RequestsCount As Integer = 0 Friend Property RequestsCount As Integer = 0
Friend Property RequestsCountSession As Integer = 0
Private Sub UpdateRequestNumber()
If CInt(MySiteSettings.RequestsWaitTimer_Any.Value) > 0 Then Thread.Sleep(CInt(MySiteSettings.RequestsWaitTimer_Any.Value))
RequestsCount += 1
RequestsCountSession += 1
End Sub
Friend Enum WNM As Integer Friend Enum WNM As Integer
Notify = 0 Notify = 0
SkipCurrent = 1 SkipCurrent = 1
@@ -468,46 +574,74 @@ Namespace API.Instagram
Dim HasNextPage As Boolean = False Dim HasNextPage As Boolean = False
Dim EndCursor$ = String.Empty Dim EndCursor$ = String.Empty
Dim PostID$ = String.Empty, PostDate$ = String.Empty, SpecFolder$ = String.Empty Dim PostID$ = String.Empty, PostDate$ = String.Empty, SpecFolder$ = String.Empty
Dim TokensErrData$ = String.Empty
Dim PostIDKV As PostKV Dim PostIDKV As PostKV
Dim ENode() As Object = Nothing Dim ENode() As Object = Nothing
Dim processGetResponse As Boolean = True
NextRequest(True) NextRequest(True)
'Check environment 'Check environment
If Not IsSavedPosts Then If Not IsSavedPosts Then
If ID.IsEmptyString Then GetUserId() If ID.IsEmptyString Then GetUserData()
If ID.IsEmptyString Then Throw New Plugin.ExitException("can't get user ID") If ID.IsEmptyString Then Throw New Plugin.ExitException("can't get user ID")
If _UseGQL And Cursor.IsEmptyString And Not Section = Sections.SavedPosts Then
If Not ValidateBaseTokens() Then GetPageTokens()
If Not ValidateBaseTokens(TokensErrData) Then ValidateBaseTokens_Error(TokensErrData)
End If
End If End If
'Create query 'Create query
Select Case Section Select Case Section
Case Sections.Timeline Case Sections.Timeline
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" & URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count=50" &
If(Cursor.IsEmptyString, String.Empty, $"&max_id={Cursor}") If(Cursor.IsEmptyString, String.Empty, $"&max_id={Cursor}")
ENode = Nothing ENode = Nothing
End If
Case Sections.Reels Case Sections.Reels
r = DownloadReels(Cursor, Token) ChangeResponserMode(True)
r = GetReelsGQL(Cursor)
ENode = {"data", "xdt_api__v1__clips__user__connection_v2"} ENode = {"data", "xdt_api__v1__clips__user__connection_v2"}
processGetResponse = False
Case Sections.SavedPosts Case Sections.SavedPosts
SavedPostsDownload(String.Empty, Token) ChangeResponserMode(False)
Exit Sub EndCursor = SavedPostsDownload(String.Empty, Token)
HasNextPage = Not EndCursor.IsEmptyString
MySiteSettings.TooManyRequests(False)
ThrowAny(Token)
GoTo NextPageBlock
Case Sections.Tagged Case Sections.Tagged
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 & """}" Dim vars$ = "{""id"":" & ID & ",""first"":50,""after"":""" & Cursor & """}"
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars) vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars)
URL = $"https://www.instagram.com/graphql/query/?doc_id=17946422347485809&variables={vars}" URL = $"https://www.instagram.com/graphql/query/?doc_id=17946422347485809&variables={vars}"
ENode = {"data", "user", "edge_user_to_photos_of_you"} ENode = {"data", "user", "edge_user_to_photos_of_you"}
SpecFolder = TaggedFolder End If
Case Sections.Stories Case Sections.Stories
If Not StoriesRequested Then If Not StoriesRequested Then
StoriesList = GetStoriesList() StoriesList = If(_UseGQL, GetHighlightsGQL_List(), GetStoriesList())
StoriesRequested = True StoriesRequested = True
MySiteSettings.TooManyRequests(False) MySiteSettings.TooManyRequests(False)
RequestsCount += 1
ThrowAny(Token) ThrowAny(Token)
Continue Do
End If End If
If StoriesList.ListExists Then If StoriesList.ListExists Then
If _UseGQL Then
GetHighlightsGQL(StoriesList, Token)
Else
GetStoriesData(StoriesList, False, Token) GetStoriesData(StoriesList, False, Token)
End If
MySiteSettings.TooManyRequests(False) MySiteSettings.TooManyRequests(False)
RequestsCount += 1
End If End If
If StoriesList.ListExists Then If StoriesList.ListExists Then
Continue Do Continue Do
@@ -515,16 +649,17 @@ Namespace API.Instagram
Throw New ExitException Throw New ExitException
End If End If
Case Sections.UserStories Case Sections.UserStories
GetStoriesData(Nothing, True, Token) If _UseGQL Then GetUserStoriesGQL(Token) Else GetStoriesData(Nothing, True, Token)
MySiteSettings.TooManyRequests(False) MySiteSettings.TooManyRequests(False)
RequestsCount += 1
Throw New ExitException Throw New ExitException
End Select End Select
'Get response 'Get response
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) MySiteSettings.TooManyRequests(False)
RequestsCount += 1
ThrowAny(Token) ThrowAny(Token)
'Parsing 'Parsing
@@ -608,9 +743,11 @@ Namespace API.Instagram
Else Else
Throw New ExitException Throw New ExitException
End If End If
NextPageBlock:
dValue = 0 dValue = 0
If HasNextPage And Not EndCursor.IsEmptyString Then DownloadData(EndCursor, Section, Token) If HasNextPage And Not EndCursor.IsEmptyString Then DownloadData(EndCursor, Section, Token)
Catch jsonNull As JsonDocumentException When jsonNull.State = WebDocumentEventArgs.States.Error And Section = Sections.Reels Catch jsonNull As JsonDocumentException When jsonNull.State = WebDocumentEventArgs.States.Error And
(Section = Sections.Reels Or Section = Sections.SavedPosts)
Throw jsonNull Throw jsonNull
Catch eex As ExitException Catch eex As ExitException
Throw eex Throw eex
@@ -618,10 +755,14 @@ Namespace API.Instagram
dValue = ProcessException(ex, Token, $"data downloading error [{URL}]",, Section, False) dValue = ProcessException(ex, Token, $"data downloading error [{URL}]",, Section, False)
End Try End Try
Loop Loop
Catch jsonNull2 As JsonDocumentException When jsonNull2.State = WebDocumentEventArgs.States.Error And Section = Sections.Reels Catch jsonNull2 As JsonDocumentException When jsonNull2.State = WebDocumentEventArgs.States.Error And
(Section = Sections.Reels Or Section = Sections.SavedPosts)
If Section = Sections.SavedPosts Then DisableSection(Section)
Catch eex2 As ExitException Catch eex2 As ExitException
If eex2.Is560 Then If eex2.Is560 Then
Throw New Plugin.ExitException With {.Silent = True} Throw New Plugin.ExitException With {.Silent = True}
ElseIf eex2.IsTokens And _UseGQL Then
Throw New Plugin.ExitException With {.Silent = True}
Else Else
If Not Section = Sections.Reels And (Section = Sections.Timeline Or Section = Sections.Tagged) And Not Cursor.IsEmptyString Then Throw eex2 If Not Section = Sections.Reels And (Section = Sections.Timeline Or Section = Sections.Tagged) And Not Cursor.IsEmptyString Then Throw eex2
End If End If
@@ -638,6 +779,7 @@ Namespace API.Instagram
Dim before% Dim before%
Dim specFolder$ = IIf(IsTagged, "Tagged", String.Empty) Dim specFolder$ = IIf(IsTagged, "Tagged", String.Empty)
If PostsToReparse.Count > 0 Then ProgressPre.ChangeMax(PostsToReparse.Count) If PostsToReparse.Count > 0 Then ProgressPre.ChangeMax(PostsToReparse.Count)
ChangeResponserMode(False)
Try Try
Do While dValue = 1 Do While dValue = 1
ThrowAny(Token) ThrowAny(Token)
@@ -657,9 +799,9 @@ Namespace API.Instagram
ThrowAny(Token) ThrowAny(Token)
NextRequest(((i + 1) Mod 5) = 0) NextRequest(((i + 1) Mod 5) = 0)
ThrowAny(Token) ThrowAny(Token)
UpdateRequestNumber()
r = Responser.GetResponse(URL,, e) r = Responser.GetResponse(URL,, e)
MySiteSettings.TooManyRequests(False) MySiteSettings.TooManyRequests(False)
RequestsCount += 1
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
j = JsonDocument.Parse(r) j = JsonDocument.Parse(r)
If Not j Is Nothing Then If Not j Is Nothing Then
@@ -692,40 +834,48 @@ Namespace API.Instagram
ProcessException(DoEx, Token, $"downloading posts error [{URL}]",, Sections.Tagged) ProcessException(DoEx, Token, $"downloading posts error [{URL}]",, Sections.Tagged)
End Try End Try
End Sub End Sub
Private Sub SavedPostsDownload(ByVal Cursor As String, ByVal Token As CancellationToken) ''' <summary>Cursor</summary>
Private Function SavedPostsDownload(ByVal Cursor As String, ByVal Token As CancellationToken) As String
Dim URL$ = $"https://www.instagram.com/api/v1/feed/saved/posts/?max_id={Cursor}" Dim URL$ = $"https://www.instagram.com/api/v1/feed/saved/posts/?max_id={Cursor}"
Dim HasNextPage As Boolean = False Dim HasNextPage As Boolean = False
Dim NextCursor$ = String.Empty Dim NextCursor$ = String.Empty
ThrowAny(Token) Dim processNext As Boolean = False
UpdateRequestNumber()
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
Dim nodes As IEnumerable(Of EContainer) = Nothing Dim nodes As IEnumerable(Of EContainer) = Nothing
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using e As EContainer = JsonDocument.Parse(r) Using e As EContainer = JsonDocument.Parse(r)
If If(e?.Count, 0) > 0 Then If e.ListExists Then
With e With e
HasNextPage = .Value("more_available").FromXML(Of Boolean)(False) HasNextPage = .Value("more_available").FromXML(Of Boolean)(False)
NextCursor = .Value("next_max_id") NextCursor = .Value("next_max_id")
If .Contains("items") Then nodes = (From ee As EContainer In .Item("items") Where ee.Count > 0 Select ee(0)) If .Contains("items") Then nodes = (From ee As EContainer In .Item("items") Where ee.Count > 0 Select ee(0))
End With End With
If nodes.ListExists AndAlso DefaultParser(nodes, Sections.SavedPosts, Token) AndAlso If nodes.ListExists AndAlso DefaultParser(nodes, Sections.SavedPosts, Token) AndAlso
HasNextPage AndAlso Not NextCursor.IsEmptyString Then SavedPostsDownload(NextCursor, Token) HasNextPage AndAlso Not NextCursor.IsEmptyString Then processNext = True
End If End If
End Using End Using
End If End If
End Sub Return If(processNext, NextCursor, String.Empty)
End Function
Protected DefaultParser_ElemNode() As Object = Nothing Protected DefaultParser_ElemNode() As Object = Nothing
Protected DefaultParser_IgnorePass As Boolean = False Protected DefaultParser_IgnorePass As Boolean = False
Private ReadOnly DefaultParser_PostUrlCreator_Default As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/" Private ReadOnly DefaultParser_PostUrlCreator_Default As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/"
Protected DefaultParser_PostUrlCreator As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/" Protected DefaultParser_PostUrlCreator As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/"
Protected 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, Protected Function DefaultParser(ByVal Items As IEnumerable(Of EContainer), ByVal Section As Sections, ByVal Token As CancellationToken,
Optional ByVal SpecFolder As String = Nothing, Optional ByVal State As UStates = UStates.Unknown, Optional ByVal SpecFolder As String = Nothing, Optional ByVal State As UStates = UStates.Unknown,
Optional ByVal Attempts As Integer = 0) As Boolean Optional ByVal Attempts As Integer = 0) As Boolean
ThrowAny(Token) ThrowAny(Token)
If Items.Count > 0 Then If Items.ListExists Then
Dim PostIDKV As PostKV Dim PostIDKV As PostKV
Dim Pinned As Boolean Dim Pinned As Boolean
Dim PostDate$, PostOriginUrl$ 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 If SpecFolder.IsEmptyString Then
Select Case Section Select Case Section
Case Sections.Tagged : SpecFolder = TaggedFolder Case Sections.Tagged : SpecFolder = TaggedFolder
@@ -734,14 +884,22 @@ Namespace API.Instagram
End Select End Select
End If End If
ProgressPre.ChangeMax(Items.Count) ProgressPre.ChangeMax(Items.Count)
For Each nn In Items For i = 0 To Items.Count - 1
nn = Items(i)
ProgressPre.Perform() ProgressPre.Perform()
With If(Not DefaultParser_ElemNode Is Nothing, nn.ItemF(DefaultParser_ElemNode), nn) With If(Not DefaultParser_ElemNode Is Nothing, nn.ItemF(DefaultParser_ElemNode), nn)
If .ListExists Then
PostIDKV = New PostKV(.Value("code"), .Value("id"), Section) PostIDKV = New PostKV(.Value("code"), .Value("id"), Section)
PostOriginUrl = DefaultParser_PostUrlCreator(PostIDKV) PostOriginUrl = DefaultParser_PostUrlCreator(PostIDKV)
Pinned = .Contains("timeline_pinned_user_ids") 'Pinned = .Contains("timeline_pinned_user_ids")
If Not DefaultParser_IgnorePass AndAlso PostKvExists(PostIDKV) Then If usePinFunc Then
If Not Pinned Then Return False Pinned = DefaultParser_Pinned.Invoke(Items, i)
Else
Pinned = If(.Item("timeline_pinned_user_ids")?.Count, 0) > 0
End If
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 Else
_TempPostsList.Add(PostIDKV.ID) _TempPostsList.Add(PostIDKV.ID)
PostsKVIDs.ListAddValue(PostIDKV, LNC) PostsKVIDs.ListAddValue(PostIDKV, LNC)
@@ -757,6 +915,9 @@ Namespace API.Instagram
If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1 If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1
If _Limit > 0 And _TotalPostsParsed >= _Limit Then Return False If _Limit > 0 And _TotalPostsParsed >= _Limit Then Return False
End If End If
Else
Return False
End If
End With End With
Next Next
Return True Return True
@@ -765,106 +926,6 @@ Namespace API.Instagram
End If End If
End Function End Function
#End Region #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" #Region "Code ID converters"
Protected Function CodeToID(ByVal Code As String) As String Protected Function CodeToID(ByVal Code As String) As String
Const CodeSymbols$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" 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, Protected Sub ObtainMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal SpecialFolder As String = Nothing,
Optional ByVal DateObj As String = Nothing, Optional ByVal InitialType As Integer = -1, Optional ByVal DateObj As String = Nothing, Optional ByVal InitialType As Integer = -1,
Optional ByVal PostOriginUrl As String = Nothing, Optional ByVal PostOriginUrl As String = Nothing,
Optional ByVal State As UStates = UStates.Unknown, Optional ByVal Attempts As Integer = 0) Optional ByVal State As UStates = UStates.Unknown, Optional ByVal Attempts As Integer = 0,
Optional ByVal TryExtractImage As Boolean = False)
Try Try
Dim maxSize As Func(Of EContainer, Integer) = Function(ByVal _ss As EContainer) As Integer Dim maxSize As Func(Of EContainer, Integer) = Function(ByVal _ss As EContainer) As Integer
Dim w% = AConvert(Of Integer)(_ss.Value("width"), 0) Dim w% = AConvert(Of Integer)(_ss.Value("width"), 0)
@@ -953,7 +1015,10 @@ Namespace API.Instagram
'2 - one video '2 - one video
'1 - one picture '1 - one picture
t = n.Value("media_type").FromXML(Of Integer)(-1) t = n.Value("media_type").FromXML(Of Integer)(-1)
If t = -1 And InitialType = 8 And ObtainMedia_AllowAbstract Then If TryExtractImage Then
t = 1
abstractDecision = True
ElseIf t = -1 And InitialType = 8 And ObtainMedia_AllowAbstract Then
If n.Contains(vid) Then If n.Contains(vid) Then
t = 2 t = 2
abstractDecision = True abstractDecision = True
@@ -964,7 +1029,7 @@ Namespace API.Instagram
End If End If
If t >= 0 Then If t >= 0 Then
Select Case t Select Case t
Case 1 Case 1 'one picture
If n.Contains(img) Then If n.Contains(img) Then
If Not abstractDecision Then t = n.Value("media_type").FromXML(Of Integer)(-1) If Not abstractDecision Then t = n.Value("media_type").FromXML(Of Integer)(-1)
DateObj = mDate(n) DateObj = mDate(n)
@@ -983,7 +1048,7 @@ Namespace API.Instagram
End With End With
End If End If
End If End If
Case 2 Case 2 'one video
If n.Contains(vid) Then If n.Contains(vid) Then
DateObj = mDate(n) DateObj = mDate(n)
With n.ItemF({vid}).XmlIfNothing With n.ItemF({vid}).XmlIfNothing
@@ -999,7 +1064,8 @@ Namespace API.Instagram
End If End If
End With End With
End If End If
Case 8 If Not TryExtractImage Then ObtainMedia(n, PostID, SpecialFolder, DateObj, InitialType, PostOriginUrl, State, Attempts, True)
Case 8 'gallery
DateObj = mDate(n) DateObj = mDate(n)
With n("carousel_media").XmlIfNothing With n("carousel_media").XmlIfNothing
If .Count > 0 Then If .Count > 0 Then
@@ -1016,11 +1082,12 @@ Namespace API.Instagram
End Sub End Sub
#End Region #End Region
#Region "GetUserId, GetUserName" #Region "GetUserId, GetUserName"
Private Sub GetUserId() Private Sub GetUserData()
Dim __idFound As Boolean = False Dim __idFound As Boolean = False
Try Try
RequestsCount += 1 ChangeResponserMode(False)
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}",, EDP.ThrowException) UpdateRequestNumber()
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}")
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r) Using j As EContainer = JsonDocument.Parse(r)
If Not j Is Nothing AndAlso j.Contains({"data", "user"}) Then If Not j Is Nothing AndAlso j.Contains({"data", "user"}) Then
@@ -1034,7 +1101,7 @@ Namespace API.Instagram
Dim eUrl$ = .Value("external_url") Dim eUrl$ = .Value("external_url")
If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl) If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl)
UserDescriptionUpdate(descr) UserDescriptionUpdate(descr)
Dim f As New SFile With {.Path = MyFile.CutPath.Path, .Name = "ProfilePicture", .Extension = "jpg"} Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"}
If Not f.Exists Then If Not f.Exists Then
Dim profilePicture$ = .Value("profile_pic_url_hd") Dim profilePicture$ = .Value("profile_pic_url_hd")
If profilePicture.IsEmptyString OrElse Not GetWebFile(profilePicture, f, EDP.ReturnValue) Then If profilePicture.IsEmptyString OrElse Not GetWebFile(profilePicture, f, EDP.ReturnValue) Then
@@ -1054,13 +1121,15 @@ Namespace API.Instagram
LogError(ex, "get Instagram user ID") LogError(ex, "get Instagram user ID")
End If End If
End If End If
Finally
ChangeResponserMode(_UseGQL)
End Try End Try
End Sub End Sub
Private Function GetUserNameById() As Boolean Private Function GetUserNameById() As Boolean
UserNameRequested = True UserNameRequested = True
Try Try
If Not ID.IsEmptyString Then If Not ID.IsEmptyString Then
RequestsCount += 1 UpdateRequestNumber()
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/{ID}/info/",, EDP.ReturnValue) Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/{ID}/info/",, EDP.ReturnValue)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue) Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
@@ -1093,9 +1162,9 @@ Namespace API.Instagram
Private Sub GetStoriesData(ByRef StoriesList As List(Of String), ByVal GetUserStory As Boolean, ByVal Token As CancellationToken) Private Sub GetStoriesData(ByRef StoriesList As List(Of String), ByVal GetUserStory As Boolean, ByVal Token As CancellationToken)
Const ReqUrl$ = "https://i.instagram.com/api/v1/feed/reels_media/?{0}" Const ReqUrl$ = "https://i.instagram.com/api/v1/feed/reels_media/?{0}"
Dim tmpList As IEnumerable(Of String) = Nothing Dim tmpList As IEnumerable(Of String) = Nothing
Dim qStr$, r$, sFolder$, storyID$, pid$ Dim qStr$, r$
Dim i% = -1 Dim i% = -1
Dim jj As EContainer, s As EContainer Dim jj As EContainer
ThrowAny(Token) ThrowAny(Token)
If StoriesList.ListExists Or GetUserStory Then If StoriesList.ListExists Or GetUserStory Then
If Not GetUserStory Then tmpList = StoriesList.Take(5) If Not GetUserStory Then tmpList = StoriesList.Take(5)
@@ -1105,28 +1174,39 @@ Namespace API.Instagram
Else Else
qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString("&")) qStr = String.Format(ReqUrl, tmpList.Select(Function(q) $"reel_ids=highlight:{q}").ListToString("&"))
End If End If
UpdateRequestNumber()
r = Responser.GetResponse(qStr,, EDP.ThrowException) r = Responser.GetResponse(qStr,, EDP.ThrowException)
ThrowAny(Token) ThrowAny(Token)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing
If j.Contains("reels") Then If j.Contains("reels") Then
ProgressPre.ChangeMax(j("reels").Count) ProgressPre.ChangeMax(j("reels").Count)
For Each jj In j("reels") For Each jj In j("reels") : GetStoriesData_ParseSingleHighlight(jj, i, GetUserStory, Token) : Next
End If
End Using
End If
If Not GetUserStory Then StoriesList.RemoveRange(0, tmpList.Count)
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() ProgressPre.Perform()
i += 1 Index += 1
sFolder = jj.Value("title").StringRemoveWinForbiddenSymbols Dim pid$
storyID = jj.Value("id").Replace("highlight:", String.Empty) Dim sFolder$ = .Value("title").StringRemoveWinForbiddenSymbols
Dim storyID$ = .Value("id").Replace("highlight:", String.Empty)
If GetUserStory Then If GetUserStory Then
sFolder = $"{StoriesFolder} (user)" sFolder = $"{StoriesFolder} (user)"
Else Else
If sFolder.IsEmptyString Then sFolder = $"Story_{storyID}" If sFolder.IsEmptyString Then sFolder = $"Story_{storyID.IfNullOrEmpty(Index)}"
If sFolder.IsEmptyString Then sFolder = $"Story_{i}"
sFolder = $"{StoriesFolder}\{sFolder}" sFolder = $"{StoriesFolder}\{sFolder}"
End If End If
If Not storyID.IsEmptyString Then storyID &= ":" If Not storyID.IsEmptyString Then storyID &= ":"
With jj("items").XmlIfNothing With .Item("items")
If .Count > 0 Then If .ListExists Then
For Each s In .Self For Each s As EContainer In .Self
pid = storyID & s.Value("id") pid = storyID & s.Value("id")
If Not _TempPostsList.Contains(pid) Then If Not _TempPostsList.Contains(pid) Then
ThrowAny(Token) ThrowAny(Token)
@@ -1136,16 +1216,12 @@ Namespace API.Instagram
Next Next
End If End If
End With End With
Next End With
End If
End Using
End If
If Not GetUserStory Then StoriesList.RemoveRange(0, tmpList.Count)
End If
End If End If
End Sub End Sub
Private Function GetStoriesList() As List(Of String) Private Function GetStoriesList() As List(Of String)
Try Try
UpdateRequestNumber()
Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/highlights/{ID}/highlights_tray/",, EDP.ThrowException) Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/highlights/{ID}/highlights_tray/",, EDP.ThrowException)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Dim ee As New ErrorsDescriber(EDP.ReturnValue) With {.DeclaredMessage = New MMessage($"{ToStringForLog()}:")} Dim ee As New ErrorsDescriber(EDP.ReturnValue) With {.DeclaredMessage = New MMessage($"{ToStringForLog()}:")}
@@ -1169,6 +1245,7 @@ Namespace API.Instagram
Protected Overrides Sub EraseData_AdditionalDataFiles() Protected Overrides Sub EraseData_AdditionalDataFiles()
Dim f As SFile = MyFilePostsKV Dim f As SFile = MyFilePostsKV
If f.Exists Then f.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.ReturnValue) If f.Exists Then f.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.ReturnValue)
FirstLoadingDone = False
End Sub End Sub
#End Region #End Region
#Region "Exceptions" #Region "Exceptions"
@@ -1204,6 +1281,9 @@ Namespace API.Instagram
ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500 ElseIf Responser.StatusCode = 560 Or Responser.StatusCode = HttpStatusCode.InternalServerError Then '560, 500
MySiteSettings.SkipUntilNextSession = True MySiteSettings.SkipUntilNextSession = True
Err5xx = Responser.StatusCode Err5xx = Responser.StatusCode
ElseIf Responser.StatusCode = -1 And Responser.Status = -1 Then
MySiteSettings.SkipUntilNextSession = True
Err5xx = Responser.StatusCode
Else Else
MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}/{CInt(Responser.Status)}]: {ToString()} [{s}]" MyMainLOG = $"Something is wrong. Your credentials may have expired [{CInt(Responser.StatusCode)}/{CInt(Responser.Status)}]: {ToString()} [{s}]"
DisableSection(s) DisableSection(s)
@@ -1215,16 +1295,26 @@ Namespace API.Instagram
Private Sub DisableSection(ByVal Section As Object) Private Sub DisableSection(ByVal Section As Object)
If Not IsNothing(Section) AndAlso TypeOf Section Is Sections Then If Not IsNothing(Section) AndAlso TypeOf Section Is Sections Then
Dim s As Sections = DirectCast(Section, Sections) Dim s As Sections = DirectCast(Section, Sections)
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 Select Case s
Case Sections.Timeline : MySiteSettings.DownloadTimeline.Value = False
Case Sections.Reels : MySiteSettings.DownloadReels.Value = False Case Sections.Reels : MySiteSettings.DownloadReels.Value = False
Case Sections.Tagged : MySiteSettings.DownloadTagged.Value = False Case Sections.Tagged : MySiteSettings.DownloadTagged.Value = False
Case Sections.Stories, Sections.UserStories Case Sections.Timeline, Sections.SavedPosts : MySiteSettings.DownloadTimeline.Value = False
MySiteSettings.DownloadTimeline.Value = False Case Sections.Stories : MySiteSettings.DownloadStories.Value = False
MySiteSettings.DownloadStories.Value = False Case Sections.UserStories : MySiteSettings.DownloadStoriesUser.Value = False
MySiteSettings.DownloadStoriesUser.Value = False
End Select End Select
MyMainLOG = $"[{s}] downloading is disabled until you update your credentials".ToUpper Next
MyMainLOG = $"[{ss.ListToStringE(, New ANumbers.EnumToStringProvider(GetType(Sections)))}] downloading is disabled until you update your credentials".ToUpper
End If
End If End If
End Sub End Sub
#End Region #End Region

View File

@@ -6,7 +6,7 @@
' '
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Threading Imports System.Net
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.Web Imports PersonalUtilities.Tools.Web
@@ -18,8 +18,10 @@ Namespace API.JustForFans
Friend NotInheritable Class M3U8 : Implements IDisposable Friend NotInheritable Class M3U8 : Implements IDisposable
#Region "Declarations" #Region "Declarations"
Friend Const AllVid As UTypes = UTypes.m3u8 + UTypes.VideoPre Friend Const AllVid As UTypes = UTypes.m3u8 + UTypes.VideoPre
Private ReadOnly DataVideo As List(Of String) Private Structure M3U8URL_Indexed
Private ReadOnly DataAudio As List(Of String) Friend Index As Integer
Friend File As SFile
End Structure
Private Media As UserMedia Private Media As UserMedia
Private DestinationFile As SFile Private DestinationFile As SFile
Private ReadOnly Thrower As Plugin.IThrower Private ReadOnly Thrower As Plugin.IThrower
@@ -32,31 +34,37 @@ Namespace API.JustForFans
Private UrlAudio As String Private UrlAudio As String
Private FileVideo As SFile Private FileVideo As SFile
Private FileAudio As SFile Private FileAudio As SFile
Private FileVideo_M3U8 As SFile
Private FileAudio_M3U8 As SFile
Private ReadOnly FileVideo_IndexedParts As List(Of M3U8URL_Indexed)
Private ReadOnly FileAudio_IndexedParts As List(Of M3U8URL_Indexed)
Private RootPlaylistUrl As String Private RootPlaylistUrl As String
Private ReadOnly Cache As CacheKeeper Private ReadOnly Cache As CacheKeeper
Private ReadOnly Progress As MyProgress Private ReadOnly Progress As MyProgress
Private ReadOnly ProgressPre As PreProgress Private ReadOnly ProgressPre As PreProgress
Private ReadOnly ProgressExists As Boolean Private ReadOnly ProgressExists As Boolean
Private ReadOnly UsePreProgress As Boolean Private ReadOnly UsePreProgress As Boolean
Private Property Token As CancellationToken Private ReadOnly REGEX_FILE_EXT As RParams = RParams.DMS("[^\s""]+\.(\w+)([\?&]{1}.+|)", 1, EDP.ReturnValue)
Private ReadOnly REGEX_FILE_EXT_M4S As RParams = RParams.DM("[^\s""]+\.m4s([\?&]{1}.+|)", 0, EDP.ReturnValue)
Private ReadOnly MyFileNumberProvider As ANumbers
#End Region #End Region
#Region "Initializer" #Region "Initializer"
Private Sub New(ByVal m As UserMedia, ByVal Destination As SFile, ByVal Resp As Responser, ByVal _Thrower As Plugin.IThrower, Private Sub New(ByVal m As UserMedia, ByVal Destination As SFile, ByVal Resp As Responser, ByVal _Thrower As Plugin.IThrower,
ByVal _Progress As MyProgress, ByVal _UsePreProgress As Boolean, ByVal _Token As CancellationToken) ByVal _Progress As MyProgress, ByVal _UsePreProgress As Boolean)
Media = m Media = m
DataVideo = New List(Of String)
DataAudio = New List(Of String)
DestinationFile = Destination DestinationFile = Destination
Thrower = _Thrower Thrower = _Thrower
'Responser = Resp 'Responser = Resp
Responser = New Responser Responser = New Responser
ResponserInternal = True ResponserInternal = True
FileVideo_IndexedParts = New List(Of M3U8URL_Indexed)
FileAudio_IndexedParts = New List(Of M3U8URL_Indexed)
Progress = _Progress Progress = _Progress
ProgressExists = Not Progress Is Nothing ProgressExists = Not Progress Is Nothing
If ProgressExists Then ProgressPre = New PreProgress(Progress) If ProgressExists Then ProgressPre = New PreProgress(Progress)
UsePreProgress = _UsePreProgress UsePreProgress = _UsePreProgress
Token = _Token MyFileNumberProvider = M3U8Base.NumberProviderDefault
Cache = New CacheKeeper($"{DestinationFile.PathWithSeparator}_{M3U8Base.TempCacheFolderName}\") Cache = New CacheKeeper($"{DestinationFile.PathWithSeparator}_{M3U8Base.TempCacheFolderName}\") With {.DisposeSuspended = True}
With Cache With Cache
.CacheDeleteError = CacheDeletionError(Cache) .CacheDeleteError = CacheDeletionError(Cache)
.DisposeSuspended = True .DisposeSuspended = True
@@ -91,30 +99,138 @@ Namespace API.JustForFans
UrlVideo = RegexReplace(r, RParams.DMS(R_VIDEO_REGEX_PATTERN, 6, EDP.ReturnValue)) UrlVideo = RegexReplace(r, RParams.DMS(R_VIDEO_REGEX_PATTERN, 6, EDP.ReturnValue))
UrlAudio = RegexReplace(r, REGEX_AUDIO_URL) UrlAudio = RegexReplace(r, REGEX_AUDIO_URL)
If UrlVideo.IsEmptyString Then Throw New ArgumentException("Unable to identify m3u8 video track", "M3U8 video track") If UrlVideo.IsEmptyString Then Throw New ArgumentException("Unable to identify m3u8 video track", "M3U8 video track")
Thrower.ThrowAny() Thrower.ThrowAny()
GetFiles(UrlVideo, FileVideo, False) GetFileParts(UrlVideo, FileVideo_M3U8, FileVideo_IndexedParts, False)
Thrower.ThrowAny() Thrower.ThrowAny()
If Not UrlAudio.IsEmptyString Then GetFiles(UrlAudio, FileAudio, True) If Not UrlAudio.IsEmptyString Then GetFileParts(UrlAudio, FileAudio_M3U8, FileAudio_IndexedParts, True)
If FileVideo_IndexedParts.Count > 0 Then _
FileVideo = GetTempFile(FileVideo_M3U8, FileVideo_IndexedParts, False, FileAudio_IndexedParts, FileAudio_IndexedParts.Count = 0)
If FileAudio_IndexedParts.Count > 0 Then _
FileAudio = GetTempFile(FileAudio_M3U8, FileAudio_IndexedParts, True, FileVideo_IndexedParts, False)
Thrower.ThrowAny() Thrower.ThrowAny()
MergeFiles() MergeFiles()
End If End If
End If End If
End Sub End Sub
Private Sub GetFiles(ByVal URL As String, ByRef File As SFile, ByVal IsAudio As Boolean) Private Function GetTempFile(ByVal M3U8File As SFile, ByVal IndexedList As List(Of M3U8URL_Indexed), ByVal IsAudio As Boolean,
ByVal IndexedListOther As List(Of M3U8URL_Indexed), ByVal IgnoreAudio As Boolean) As SFile
Const mapStr$ = "#EXT-X-MAP:URI"
Const extinfStr$ = "#EXTINF:"
Const m4s$ = "m4s"
Dim M3U8FileLines$() = M3U8File.GetLines
If M3U8FileLines.ListExists AndAlso IndexedList.Count > 0 AndAlso (IndexedListOther.Count > 0 Or (Not IsAudio And IgnoreAudio)) Then
Dim outputFile As SFile = $"{Cache.RootDirectory.PathWithSeparator}{IIf(IsAudio, "AUDIO.aac", "VIDEO.mp4")}"
Dim M3U8FileNew As SFile = M3U8File
M3U8FileNew.Path = IndexedList(0).File.Path
Dim v$
Dim i%, fIndx%, fIndx2%
Dim extIsm4s As Boolean
Dim LookingIndex% = -1
Dim ignoreOtherList As Boolean = IndexedListOther.Count = 0 And (Not IsAudio And IgnoreAudio)
Dim fileFinder As Predicate(Of M3U8URL_Indexed) = Function(input) input.Index = LookingIndex
Using m3u8Text As New TextSaver
For i = 0 To M3U8FileLines.Length - 1
v = M3U8FileLines(i)
If Not v.IsEmptyString Then
If v.StartsWith(mapStr) Then
LookingIndex += 1
fIndx = IndexedList.FindIndex(fileFinder)
If fIndx >= 0 Then
extIsm4s = Not IndexedList(fIndx).File.Extension.IsEmptyString AndAlso IndexedList(fIndx).File.Extension = m4s
v = v.Replace(RegexReplace(v, If(extIsm4s, REGEX_FILE_EXT_M4S, REGEX_FILE_EXT)), IndexedList(fIndx).File.File)
m3u8Text.AppendLine(v)
Else
Throw New Exception($"The map file is missing ({IIf(IsAudio, "audio", "video")})")
End If
ElseIf v.StartsWith(extinfStr) Then
LookingIndex += 1
If (i + 1) <= M3U8FileLines.Length - 1 Then
fIndx = IndexedList.FindIndex(fileFinder)
fIndx2 = If(ignoreOtherList, -1, IndexedListOther.FindIndex(fileFinder))
If fIndx >= 0 And (fIndx2 >= 0 Or ignoreOtherList) Then
If ignoreOtherList OrElse IndexedListOther(fIndx2).Index = IndexedList(fIndx).Index Then
m3u8Text.AppendLine(v)
m3u8Text.AppendLine(IndexedList(fIndx).File.File)
End If
End If
i += 1
Else
Throw New Exception($"Unexpected end of m3u8 file ({IIf(IsAudio, "audio", "video")})")
End If
Else
m3u8Text.AppendLine(v)
End If
End If
Next
m3u8Text.SaveAs(M3U8FileNew)
End Using
If M3U8FileNew.Exists Then
Using b As New BatchExecutor
AddHandler b.ErrorDataReceived, AddressOf Batch_OutputDataReceived
Thrower.ThrowAny()
ProgressChangeMax(IndexedList.Count)
b.ChangeDirectory(M3U8FileNew)
b.Execute($"""{Settings.FfmpegFile}"" -i {M3U8FileNew.File} -vcodec copy -strict -2 ""{outputFile}""")
End Using
If Not outputFile.Exists Then outputFile = Nothing
End If
Return outputFile
Else
Return Nothing
End If
End Function
Private Sub GetFileParts(ByVal URL As String, ByRef M3U8File As SFile, ByRef IndexedList As List(Of M3U8URL_Indexed), ByVal IsAudio As Boolean)
Try Try
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Dim data As List(Of RegexMatchStruct) = RegexFields(Of RegexMatchStruct)(r, {REGEX_PLS_FILES}, {1, 2}, EDP.ReturnValue) Dim data As List(Of RegexMatchStruct) = RegexFields(Of RegexMatchStruct)(r, {REGEX_PLS_FILES}, {1, 2}, EDP.ReturnValue)
If data.ListExists Then If data.ListExists Then
File = $"{Cache.RootDirectory.PathWithSeparator}{IIf(IsAudio, "AUDIO.aac", "VIDEO.mp4")}" Dim appender$ = URL.Replace(URL.Split("/").LastOrDefault, String.Empty)
Using b As New TokenBatch(Token) With {.Encoding = Settings.CMDEncoding, .MainProcessName = "ffmpeg"} Dim createM3U8URL As Func(Of String, M3U8URL) =
AddHandler b.ErrorDataReceived, AddressOf Batch_OutputDataReceived Function(input) New M3U8URL(M3U8Base.CreateUrl(appender, input), RegexReplace(input, REGEX_FILE_EXT))
ProgressChangeMax(data.Count) With (From d As RegexMatchStruct In data
b.ChangeDirectory(Cache.RootDirectory) Where Not d.Arr(0).IfNullOrEmpty(d.Arr(1)).IsEmptyString
b.Execute($"""{Settings.FfmpegFile}"" -i {URL} -vcodec copy -strict -2 ""{File}""") Select createM3U8URL.Invoke(d.Arr(0).IfNullOrEmpty(d.Arr(1)).StringTrim))
Token.ThrowIfCancellationRequested() If .ListExists Then
If Not File.Exists Then File = Nothing ProgressChangeMax(.Count)
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 End Using
Else
Throw New Exception("Can't create cache directory")
End If
End If
End With
End If End If
End If End If
Catch oex As OperationCanceledException Catch oex As OperationCanceledException
@@ -123,7 +239,7 @@ Namespace API.JustForFans
Throw dex Throw dex
Catch ex As Exception Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex, ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex,
$"API.JustForFans.M3U8.GetFiles({IIf(IsAudio, "audio", "video")}):{vbCr}URL: {URL}{vbCr}File: {File}") $"API.JustForFans.M3U8.GetFileParts({IIf(IsAudio, "audio", "video")}):{vbCr}URL: {URL}{vbCr}Post: {Media.URL_BASE}")
End Try End Try
End Sub End Sub
Private Async Sub Batch_OutputDataReceived(ByVal Sender As Object, ByVal e As DataReceivedEventArgs) Private Async Sub Batch_OutputDataReceived(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
@@ -135,8 +251,10 @@ Namespace API.JustForFans
Dim f As SFile = SFile.IndexReindex(DestinationFile,,, p, EDP.ReturnValue).IfNullOrEmpty(DestinationFile) Dim f As SFile = SFile.IndexReindex(DestinationFile,,, p, EDP.ReturnValue).IfNullOrEmpty(DestinationFile)
If Not FileVideo.IsEmptyString And Not FileAudio.IsEmptyString Then If Not FileVideo.IsEmptyString And Not FileAudio.IsEmptyString Then
DestinationFile = FFMPEG.MergeFiles({FileVideo, FileAudio}, Settings.FfmpegFile, f, Settings.CMDEncoding, p, EDP.ThrowException) DestinationFile = FFMPEG.MergeFiles({FileVideo, FileAudio}, Settings.FfmpegFile, f, Settings.CMDEncoding, p, EDP.ThrowException)
Else ElseIf FileVideo.Exists Then
If Not SFile.Move(FileVideo, f) Then DestinationFile = FileVideo If Not SFile.Move(FileVideo, f) Then DestinationFile = FileVideo
Else
Throw New Exception($"Unable to download file ({Media.URL_BASE})")
End If End If
Catch ex As Exception Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex, $"[M3U8.MergeFiles]") ErrorsDescriber.Execute(EDP.SendToLog + EDP.ThrowException, ex, $"[M3U8.MergeFiles]")
@@ -165,8 +283,8 @@ Namespace API.JustForFans
#End Region #End Region
#Region "Static Download" #Region "Static Download"
Friend Shared Function Download(ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Resp As Responser, ByVal Thrower As Plugin.IThrower, Friend Shared Function Download(ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Resp As Responser, ByVal Thrower As Plugin.IThrower,
ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean, ByVal _Token As CancellationToken) As SFile ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean) As SFile
Using m As New M3U8(Media, DestinationFile, Resp, Thrower, Progress, UsePreProgress, _Token) Using m As New M3U8(Media, DestinationFile, Resp, Thrower, Progress, UsePreProgress)
m.Download() m.Download()
If m.DestinationFile.Exists Then Return m.DestinationFile Else Return Nothing If m.DestinationFile.Exists Then Return m.DestinationFile Else Return Nothing
End Using End Using
@@ -177,8 +295,8 @@ Namespace API.JustForFans
Private Overloads Sub Dispose(ByVal disposing As Boolean) Private Overloads Sub Dispose(ByVal disposing As Boolean)
If Not disposedValue Then If Not disposedValue Then
If disposing Then If disposing Then
DataVideo.Clear() FileVideo_IndexedParts.Clear()
DataAudio.Clear() FileAudio_IndexedParts.Clear()
ProgressPre.DisposeIfReady ProgressPre.DisposeIfReady
Cache.Dispose() Cache.Dispose()
If ResponserInternal Then Responser.DisposeIfReady If ResponserInternal Then Responser.DisposeIfReady

View File

@@ -20,9 +20,14 @@ Namespace API.JustForFans
Friend ReadOnly Property UserID As PropertyValue Friend ReadOnly Property UserID As PropertyValue
<PropertyOption, PXML, PClonable(Clone:=False)> <PropertyOption, PXML, PClonable(Clone:=False)>
Friend ReadOnly Property UserHash4 As PropertyValue Friend ReadOnly Property UserHash4 As PropertyValue
<CookieValueExtractor(NameOf(UserHash4))>
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
Return c.GetCookieValue(UserHash4_CookieName, PropName, NameOf(UserHash4))
End Function
<PropertyOption(ControlText:="Accept", ControlToolTip:="Header 'Accept'"), PClonable> <PropertyOption(ControlText:="Accept", ControlToolTip:="Header 'Accept'"), PClonable>
Friend ReadOnly Property HeaderAccept As PropertyValue Friend ReadOnly Property HeaderAccept As PropertyValue
<PropertyOption, PClonable> Friend ReadOnly Property UserAgent As PropertyValue <PropertyOption(InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), PClonable, PXML(OnlyForChecked:=True)>
Friend ReadOnly Property UserAgent As PropertyValue
Private Sub UpdateHeader(ByVal HeaderName As String, ByVal HeaderValue As String) Private Sub UpdateHeader(ByVal HeaderName As String, ByVal HeaderValue As String)
Select Case HeaderName Select Case HeaderName
Case NameOf(HeaderAccept) : If HeaderValue.IsEmptyString Then Responser.Accept = Nothing Else Responser.Accept = HeaderValue Case NameOf(HeaderAccept) : If HeaderValue.IsEmptyString Then Responser.Accept = Nothing Else Responser.Accept = HeaderValue
@@ -60,7 +65,7 @@ Namespace API.JustForFans
Private Sub UpdateUserHash4() Private Sub UpdateUserHash4()
If Responser.CookiesExists Then If Responser.CookiesExists Then
Dim hv_current$ = UserHash4.Value Dim hv_current$ = UserHash4.Value
Dim hv_cookie$ = If(Responser.Cookies.FirstOrDefault(Function(cc) cc.Name.ToLower = UserHash4_CookieName)?.Value, String.Empty) Dim hv_cookie$ = GetValueFromCookies(NameOf(UserHash4), Responser.Cookies)
If Not hv_cookie.IsEmptyString And Not hv_cookie = hv_current And Responser.Cookies.Changed Then UserHash4.Value = hv_cookie If Not hv_cookie.IsEmptyString And Not hv_cookie = hv_current And Responser.Cookies.Changed Then UserHash4.Value = hv_cookie
End If End If
End Sub End Sub

View File

@@ -336,7 +336,7 @@ Namespace API.JustForFans
DownloadContentDefault(Token) DownloadContentDefault(Token)
End Sub End Sub
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
Return M3U8.Download(Media, DestinationFile, ResponserNoHandlers, Me, Progress, Not IsSingleObjectDownload, Token) Return M3U8.Download(Media, DestinationFile, ResponserNoHandlers, Me, Progress, Not IsSingleObjectDownload)
End Function End Function
#End Region #End Region
#Region "DownloadSingleObject" #Region "DownloadSingleObject"

View File

@@ -14,6 +14,7 @@ Namespace API.Mastodon
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelProfile As Boolean <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 DownloadModelSearch As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelForceApply As Boolean <PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelForceApply As Boolean
<PSetting(Address:=SettingAddress.None)> Friend Overrides Property DownloadModelLikes As Boolean
Friend Sub New(ByVal s As SiteSettings) Friend Sub New(ByVal s As SiteSettings)
MyBase.New(s) MyBase.New(s)
End Sub End Sub

View File

@@ -63,15 +63,15 @@ Namespace API.Mastodon
End Sub End Sub
#End Region #End Region
#Region "Other properties" #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 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 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 Friend ReadOnly Property GifsPrefix As PropertyValue
<Provider(NameOf(GifsSpecialFolder), Interaction:=True), Provider(NameOf(GifsPrefix), Interaction:=True)> <Provider(NameOf(GifsSpecialFolder), Interaction:=True), Provider(NameOf(GifsPrefix), Interaction:=True)>
Private ReadOnly Property GifStringChecker As IFormatProvider 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 Friend ReadOnly Property UseMD5Comparison As PropertyValue
<PropertyOption(IsAuth:=False, ControlText:="User related to my domain", <PropertyOption(IsAuth:=False, ControlText:="User related to my domain",
ControlToolTip:="Open user profiles and user posts through my domain."), PXML, PClonable> ControlToolTip:="Open user profiles and user posts through my domain."), PXML, PClonable>

View File

@@ -13,30 +13,39 @@ Imports PersonalUtilities.Forms
Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies Imports PersonalUtilities.Tools.Web.Cookies
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.OnlyFans Namespace API.OnlyFans
<Manifest("AndyProgram_OnlyFans"), SavedPosts, SpecialForm(False), SeparatedTasks(1)> <Manifest("AndyProgram_OnlyFans"), SavedPosts, SpecialForm(False), SeparatedTasks(1)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations" #Region "Declarations"
#Region "Categories"
Private Const CAT_OFS As String = "OF-Scraper support"
#End Region
#Region "Options" #Region "Options"
<PropertyOption(ControlText:="Download highlights", ControlToolTip:="Download profile highlights if they exists"), PXML, PClonable> <PropertyOption(ControlText:="Download timeline", ControlToolTip:="Download user timeline", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend Property DownloadHighlights As PropertyValue Friend ReadOnly Property DownloadTimeline As PropertyValue
<PropertyOption(ControlText:="Download chat", ControlToolTip:="Download unlocked chat media"), PXML, PClonable> <PropertyOption(ControlText:="Download stories", ControlToolTip:="Download profile stories if they exists", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend Property DownloadChatMedia As PropertyValue 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 #End Region
#Region "Headers" #Region "Headers"
Private Const HeaderBrowser As String = "sec-ch-ua" Private Const HeaderBrowser As String = "sec-ch-ua"
Private Const HeaderUserID As String = "User-Id" Private Const HeaderUserID As String = "User-Id"
Friend Const HeaderXBC As String = "X-Bc" Friend Const HeaderXBC As String = "X-Bc"
Friend Const HeaderAppToken As String = "App-Token" Friend Const HeaderAppToken As String = "App-Token"
<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 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 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 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 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 Friend ReadOnly Property UserAgent As PropertyValue
Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String) Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String)
Dim hName$ = String.Empty Dim hName$ = String.Empty
@@ -54,6 +63,16 @@ Namespace API.OnlyFans
Responser.UserAgent = Value Responser.UserAgent = Value
End If End If
End Sub End Sub
<CookieValueExtractor(NameOf(HH_USER_ID)), CookieValueExtractor(NameOf(HH_X_BC))>
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
If c.ListExists Then
Select Case PropName
Case NameOf(HH_USER_ID) : Return c.GetCookieValue("auth_id")
Case NameOf(HH_X_BC) : Return c.GetCookieValue("fp")
End Select
End If
Return String.Empty
End Function
#End Region #End Region
#Region "Rules" #Region "Rules"
<PXML("LastDateUpdated")> Private ReadOnly Property LastDateUpdated_XML As PropertyValue <PXML("LastDateUpdated")> Private ReadOnly Property LastDateUpdated_XML As PropertyValue
@@ -67,20 +86,21 @@ Namespace API.OnlyFans
End Property End Property
<PropertyOption(ControlText:="Use old authorization rules", <PropertyOption(ControlText:="Use old authorization rules",
ControlToolTip:="Use old dynamic rules (from 'DATAHOARDERS') or new ones (from 'DIGITALCRIMINALS')." & vbCr & 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> "Change this value only if you know what you are doing.", IsAuth:=True), PXML, PClonable>
Friend ReadOnly Property UseOldAuthRules As PropertyValue Friend ReadOnly Property UseOldAuthRules As PropertyValue
<PropertyOption(ControlText:="Dynamic rules update", ControlToolTip:="'Dynamic rules' update interval (minutes). Default: 1440", LeftOffset:=110), PXML, PClonable> <PropertyOption(ControlText:="Dynamic rules update", ControlToolTip:="'Dynamic rules' update interval (minutes). Default: 1440",
LeftOffset:=110, IsAuth:=True), PXML, PClonable>
Friend ReadOnly Property DynamicRulesUpdateInterval As PropertyValue Friend ReadOnly Property DynamicRulesUpdateInterval As PropertyValue
<Provider(NameOf(DynamicRulesUpdateInterval), FieldsChecker:=True)> <Provider(NameOf(DynamicRulesUpdateInterval), FieldsChecker:=True)>
Private ReadOnly Property DynamicRulesUpdateIntervalProvider As IFormatProvider Private ReadOnly Property DynamicRulesUpdateIntervalProvider As IFormatProvider
<PropertyOption(ControlText:="Dynamic rules", <PropertyOption(ControlText:="Dynamic rules",
ControlToolTip:="Overwrite 'Dynamic rules' with this URL" & vbCr & ControlToolTip:="Overwrite 'Dynamic rules' with this URL" & vbCr &
"Change this value only if you know what you are doing."), PXML, PClonable> "Change this value only if you know what you are doing.", IsAuth:=True), PXML, PClonable>
Friend ReadOnly Property DynamicRules As PropertyValue Friend ReadOnly Property DynamicRules As PropertyValue
#End Region #End Region
#Region "OFScraper" #Region "OFScraper"
<PClonable, PXML("OFScraperPath")> Private ReadOnly Property OFScraperPath_XML As PropertyValue <PClonable, PXML("OFScraperPath")> Private ReadOnly Property OFScraperPath_XML As PropertyValue
<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 Friend ReadOnly Property OFScraperPath As PropertyValue
Get Get
If Not DefaultInstance Is Nothing Then If Not DefaultInstance Is Nothing Then
@@ -91,7 +111,7 @@ Namespace API.OnlyFans
End Get End Get
End Property End Property
<PClonable, PXML("OFScraperMP4decrypt")> Private ReadOnly Property OFScraperMP4decrypt_XML As PropertyValue <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 Friend ReadOnly Property OFScraperMP4decrypt As PropertyValue
Get Get
If Not DefaultInstance Is Nothing Then If Not DefaultInstance Is Nothing Then
@@ -103,7 +123,7 @@ Namespace API.OnlyFans
End Property End Property
Friend Const KeyModeDefault_Default As String = "cdrm" Friend Const KeyModeDefault_Default As String = "cdrm"
<PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue <PClonable, PXML("KeyModeDefault")> Private ReadOnly Property KeyModeDefault_XML As PropertyValue
<PropertyOption(ControlText:="key-mode-default")> <PropertyOption(ControlText:="key-mode-default", Category:=CAT_OFS)>
Friend ReadOnly Property KeyModeDefault As PropertyValue Friend ReadOnly Property KeyModeDefault As PropertyValue
Get Get
If Not DefaultInstance Is Nothing Then If Not DefaultInstance Is Nothing Then
@@ -119,6 +139,8 @@ Namespace API.OnlyFans
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) 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) MyBase.New("OnlyFans", ".onlyfans.com", AccName, Temp, My.Resources.SiteResources.OnlyFansIcon_32, My.Resources.SiteResources.OnlyFansPic_32)
_AllowUserAgentUpdate = False
With Responser With Responser
.Accept = "application/json, text/plain, */*" .Accept = "application/json, text/plain, */*"
.AutomaticDecompression = Net.DecompressionMethods.GZip .AutomaticDecompression = Net.DecompressionMethods.GZip
@@ -144,6 +166,8 @@ Namespace API.OnlyFans
UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v)) UserAgent = New PropertyValue(IIf(.UserAgentExists, .UserAgent, String.Empty), GetType(String), Sub(v) UpdateHeader(NameOf(UserAgent), v))
End With End With
DownloadTimeline = New PropertyValue(True)
DownloadStories = New PropertyValue(True)
DownloadHighlights = New PropertyValue(True) DownloadHighlights = New PropertyValue(True)
DownloadChatMedia = New PropertyValue(True) DownloadChatMedia = New PropertyValue(True)

View File

@@ -22,6 +22,8 @@ Imports UStates = SCrawler.API.Base.UserMedia.States
Namespace API.OnlyFans Namespace API.OnlyFans
Friend Class UserData : Inherits UserDataBase Friend Class UserData : Inherits UserDataBase
#Region "XML names" #Region "XML names"
Private Const Name_MediaDownloadTimeline As String = "MediaDownloadTimeline"
Private Const Name_MediaDownloadStories As String = "MediaDownloadStories"
Private Const Name_MediaDownloadHighlights As String = "DownloadHighlights" Private Const Name_MediaDownloadHighlights As String = "DownloadHighlights"
Private Const Name_MediaDownloadChatMedia As String = "DownloadChatMedia" Private Const Name_MediaDownloadChatMedia As String = "DownloadChatMedia"
#End Region #End Region
@@ -30,6 +32,8 @@ Namespace API.OnlyFans
Private Const HeaderSign As String = "Sign" Private Const HeaderSign As String = "Sign"
Private Const HeaderTime As String = "Time" Private Const HeaderTime As String = "Time"
Private ReadOnly HighlightsList As List(Of String) Private ReadOnly HighlightsList As List(Of String)
Friend Property MediaDownloadTimeline As Boolean = True
Friend Property MediaDownloadStories As Boolean = True
Friend Property MediaDownloadHighlights As Boolean = True Friend Property MediaDownloadHighlights As Boolean = True
Friend Property MediaDownloadChatMedia As Boolean = True Friend Property MediaDownloadChatMedia As Boolean = True
Private ReadOnly Property MySettings As SiteSettings Private ReadOnly Property MySettings As SiteSettings
@@ -42,9 +46,13 @@ Namespace API.OnlyFans
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
With Container With Container
If Loading Then If Loading Then
MediaDownloadTimeline = .Value(Name_MediaDownloadTimeline).FromXML(Of Boolean)(True)
MediaDownloadStories = .Value(Name_MediaDownloadStories).FromXML(Of Boolean)(True)
MediaDownloadHighlights = .Value(Name_MediaDownloadHighlights).FromXML(Of Boolean)(True) MediaDownloadHighlights = .Value(Name_MediaDownloadHighlights).FromXML(Of Boolean)(True)
MediaDownloadChatMedia = .Value(Name_MediaDownloadChatMedia).FromXML(Of Boolean)(True) MediaDownloadChatMedia = .Value(Name_MediaDownloadChatMedia).FromXML(Of Boolean)(True)
Else Else
.Add(Name_MediaDownloadTimeline, MediaDownloadTimeline.BoolToInteger)
.Add(Name_MediaDownloadStories, MediaDownloadStories.BoolToInteger)
.Add(Name_MediaDownloadHighlights, MediaDownloadHighlights.BoolToInteger) .Add(Name_MediaDownloadHighlights, MediaDownloadHighlights.BoolToInteger)
.Add(Name_MediaDownloadChatMedia, MediaDownloadChatMedia.BoolToInteger) .Add(Name_MediaDownloadChatMedia, MediaDownloadChatMedia.BoolToInteger)
End If End If
@@ -58,6 +66,8 @@ Namespace API.OnlyFans
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object) Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
With DirectCast(Obj, UserExchangeOptions) With DirectCast(Obj, UserExchangeOptions)
MediaDownloadTimeline = .DownloadTimeline
MediaDownloadStories = .DownloadStories
MediaDownloadHighlights = .DownloadHighlights MediaDownloadHighlights = .DownloadHighlights
MediaDownloadChatMedia = .DownloadChatMedia MediaDownloadChatMedia = .DownloadChatMedia
End With End With
@@ -90,8 +100,15 @@ Namespace API.OnlyFans
Responser.Cookies.Clear() Responser.Cookies.Clear()
AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived AddHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
UpdateCookieHeader() UpdateCookieHeader()
DownloadTimeline(IIf(IsSavedPosts, 0, String.Empty), Token)
If Not IsSavedPosts Then 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 MediaDownloadHighlights And FunctionErr = FunctionErrDef Then DownloadHighlights(Token)
If MediaDownloadChatMedia And FunctionErr = FunctionErrDef Then DownloadChatMedia(0, Token) If MediaDownloadChatMedia And FunctionErr = FunctionErrDef Then DownloadChatMedia(0, Token)
End If End If
@@ -114,6 +131,7 @@ Namespace API.OnlyFans
Return ErrValue <> 2 Return ErrValue <> 2
End Function End Function
Friend Const A_HIGHLIGHT As String = "HL" Friend Const A_HIGHLIGHT As String = "HL"
Friend Const A_STORIES As String = "ST"
Friend Const A_MESSAGE As String = "MSG" Friend Const A_MESSAGE As String = "MSG"
Private Const BaseUrlPattern As String = "https://onlyfans.com{0}" Private Const BaseUrlPattern As String = "https://onlyfans.com{0}"
#Region "Download timeline" #Region "Download timeline"
@@ -133,9 +151,6 @@ Namespace API.OnlyFans
If IsSavedPosts Then If IsSavedPosts Then
path = $"/api2/v2/posts/bookmarks/all/?format=infinite&limit=10&offset={Cursor}" path = $"/api2/v2/posts/bookmarks/all/?format=infinite&limit=10&offset={Cursor}"
Else Else
If ID.IsEmptyString Then GetUserID()
If ID.IsEmptyString Then Throw New ArgumentNullException("ID", "Unable to get user ID")
path = $"/api2/v2/users/{ID}/posts/medias?limit=50&order=publish_date_desc&skip_users=all&format=infinite&counters=1" path = $"/api2/v2/users/{ID}/posts/medias?limit=50&order=publish_date_desc&skip_users=all&format=infinite&counters=1"
If Not Cursor.IsEmptyString Then path &= $"&counters=0&beforePublishTime={Cursor}" Else path &= "&counters=1" If Not Cursor.IsEmptyString Then path &= $"&counters=0&beforePublishTime={Cursor}" Else path &= "&counters=1"
End If End If
@@ -250,7 +265,7 @@ Namespace API.OnlyFans
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r) Using j As EContainer = JsonDocument.Parse(r)
If j.ListExists Then If j.ListExists Then
specFolder = j.Value("title").StringRemoveWinForbiddenSymbols.IfNullOrEmpty(HLID) specFolder = "Highlights\" & j.Value("title").StringRemoveWinForbiddenSymbols.IfNullOrEmpty(HLID)
specFolder &= "*" specFolder &= "*"
With j("stories") With j("stories")
If .ListExists Then If .ListExists Then
@@ -280,6 +295,57 @@ Namespace API.OnlyFans
Loop While Not _complete Loop While Not _complete
End Sub End Sub
#End Region #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" #Region "Download chat media"
Private Sub DownloadChatMedia(ByVal Cursor As Integer, ByVal Token As CancellationToken) Private Sub DownloadChatMedia(ByVal Cursor As Integer, ByVal Token As CancellationToken)
Dim url$ = String.Empty Dim url$ = String.Empty
@@ -329,7 +395,8 @@ Namespace API.OnlyFans
#End Region #End Region
Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing, Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing,
Optional ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False, Optional ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False,
Optional ByVal SpecFolder As String = Nothing, 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 postUrl$, postUrlBase$, ext$
Dim t As UTypes Dim t As UTypes
Dim mList As New List(Of UserMedia) Dim mList As New List(Of UserMedia)
@@ -348,7 +415,7 @@ Namespace API.OnlyFans
Case "video" Case "video"
t = UTypes.Video t = UTypes.Video
ext = "mp4" ext = "mp4"
If postUrl.IsEmptyString And Not IsHL Then If postUrl.IsEmptyString And Not IsHL And TryUseOFS Then
t = UTypes.VideoPre t = UTypes.VideoPre
_AbsMediaIndex += 1 _AbsMediaIndex += 1
If Not PostUserID.IsEmptyString And IsSingleObjectDownload Then _ If Not PostUserID.IsEmptyString And IsSingleObjectDownload Then _

View File

@@ -9,17 +9,25 @@
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Namespace API.OnlyFans Namespace API.OnlyFans
Friend Class UserExchangeOptions Friend Class UserExchangeOptions
<PSetting(NameOf(SiteSettings.DownloadTimeline), NameOf(MySettings))>
Friend Property DownloadTimeline As Boolean
<PSetting(NameOf(SiteSettings.DownloadStories), NameOf(MySettings))>
Friend Property DownloadStories As Boolean
<PSetting(NameOf(SiteSettings.DownloadHighlights), NameOf(MySettings))> <PSetting(NameOf(SiteSettings.DownloadHighlights), NameOf(MySettings))>
Friend Property DownloadHighlights As Boolean Friend Property DownloadHighlights As Boolean
<PSetting(NameOf(SiteSettings.DownloadChatMedia), NameOf(MySettings))> <PSetting(NameOf(SiteSettings.DownloadChatMedia), NameOf(MySettings))>
Friend Property DownloadChatMedia As Boolean Friend Property DownloadChatMedia As Boolean
Private ReadOnly MySettings As SiteSettings Private ReadOnly MySettings As SiteSettings
Friend Sub New(ByVal u As UserData) Friend Sub New(ByVal u As UserData)
DownloadTimeline = u.MediaDownloadTimeline
DownloadStories = u.MediaDownloadStories
DownloadHighlights = u.MediaDownloadHighlights DownloadHighlights = u.MediaDownloadHighlights
DownloadChatMedia = u.MediaDownloadChatMedia DownloadChatMedia = u.MediaDownloadChatMedia
MySettings = u.HOST.Source MySettings = u.HOST.Source
End Sub End Sub
Friend Sub New(ByVal s As SiteSettings) Friend Sub New(ByVal s As SiteSettings)
DownloadTimeline = s.DownloadTimeline.Value
DownloadStories = s.DownloadStories.Value
DownloadHighlights = s.DownloadHighlights.Value DownloadHighlights = s.DownloadHighlights.Value
DownloadChatMedia = s.DownloadChatMedia.Value DownloadChatMedia = s.DownloadChatMedia.Value
MySettings = s MySettings = s

View File

@@ -287,7 +287,7 @@ Namespace API.Pinterest
End Function End Function
End Class End Class
Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean, ByVal Token As CancellationToken) As List(Of String) Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean, ByVal Token As CancellationToken) As List(Of String)
Dim command$ = $"gallery-dl --verbose --simulate " Dim command$ = $"""{Settings.GalleryDLFile.File}"" --verbose --simulate "
Try Try
If Not URL.IsEmptyString Then If Not URL.IsEmptyString Then
If MySettings.CookiesNetscapeFile.Exists Then command &= $"--cookies ""{MySettings.CookiesNetscapeFile}"" " If MySettings.CookiesNetscapeFile.Exists Then command &= $"--cookies ""{MySettings.CookiesNetscapeFile}"" "

View File

@@ -29,7 +29,6 @@ Namespace API.PornHub
Private Const Name_DownloadFavorite As String = "DownloadFavorite" Private Const Name_DownloadFavorite As String = "DownloadFavorite"
Private Const Name_DownloadGifs As String = "DownloadGifs" Private Const Name_DownloadGifs As String = "DownloadGifs"
Private Const Name_DownloadPhotoOnlyFromModelHub As String = "DownloadPhotoOnlyFromModelHub" Private Const Name_DownloadPhotoOnlyFromModelHub As String = "DownloadPhotoOnlyFromModelHub"
<Obsolete> Private Const Name_IsUser As String = "IsUser"
#End Region #End Region
#Region "Structures" #Region "Structures"
Private Structure FlashVar : Implements IRegExCreator Private Structure FlashVar : Implements IRegExCreator
@@ -254,14 +253,7 @@ Namespace API.PornHub
DownloadFavorite = .Value(Name_DownloadFavorite).FromXML(Of Boolean)(False) DownloadFavorite = .Value(Name_DownloadFavorite).FromXML(Of Boolean)(False)
DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False) DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False)
DownloadPhotoOnlyFromModelHub = .Value(Name_DownloadPhotoOnlyFromModelHub).FromXML(Of Boolean)(True) DownloadPhotoOnlyFromModelHub = .Value(Name_DownloadPhotoOnlyFromModelHub).FromXML(Of Boolean)(True)
If .Contains(Name_SiteMode) Then
SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User) SiteMode = .Value(Name_SiteMode).FromXML(Of Integer)(SiteModes.User)
Else
'TODELETE: PornHub 'IsUser' 20231113
#Disable Warning BC40008
SiteMode = IIf(.Value(Name_IsUser).FromXML(Of Boolean)(True), SiteModes.User, SiteModes.Search)
#Enable Warning
End If
UpdateUserOptions() UpdateUserOptions()
Else Else
If UpdateUserOptions() Then .Value(Name_LabelsName) = LabelsString If UpdateUserOptions() Then .Value(Name_LabelsName) = LabelsString
@@ -404,7 +396,8 @@ Namespace API.PornHub
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Dim l As List(Of UserVideo) = RegexFields(Of UserVideo)(r, {RegexUserVideos}, {6, 7, 3, 10}) Dim l As List(Of UserVideo) = RegexFields(Of UserVideo)(r, {RegexUserVideos}, {6, 7, 3, 10})
If l.ListExists And Not SiteMode = SiteModes.Playlists Then l = l.ListTake(3, l.Count).ToList 'If l.ListExists And Not SiteMode = SiteModes.Playlists Then l = l.ListTake(3, l.Count).ToList
If l.ListExists And Not SiteMode = SiteModes.Playlists Then l = l.ListTake(1, l.Count).ToList
If l.ListExists Then If l.ListExists Then
If IsUser Then If IsUser Then
If Type = VideoTypes.Favorite Then If Type = VideoTypes.Favorite Then

View File

@@ -8,6 +8,7 @@
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Net Imports System.Net
Imports System.Threading Imports System.Threading
Imports SCrawler.API.Base
Imports SCrawler.API.Reddit.M3U8_Declarations Imports SCrawler.API.Reddit.M3U8_Declarations
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.Web Imports PersonalUtilities.Tools.Web
@@ -61,15 +62,18 @@ Namespace API.Reddit
Private ReadOnly ProgressExists As Boolean Private ReadOnly ProgressExists As Boolean
Private ReadOnly Property ProgressPre As PreProgress Private ReadOnly Property ProgressPre As PreProgress
Private ReadOnly UsePreProgress As Boolean Private ReadOnly UsePreProgress As Boolean
Private ReadOnly Media As UserMedia
#End Region #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 PlayListURL = URL
Me.Media = Media
BaseURL = RegexReplace(URL, BaseUrlPattern) BaseURL = RegexReplace(URL, BaseUrlPattern)
Video = New List(Of String) Video = New List(Of String)
Audio = New List(Of String) Audio = New List(Of String)
Me.OutFile = OutFile Me.OutFile = OutFile
Me.OutFile.Name = "PlayListFile" Me.OutFile.Name = "PlayListFile"
Me.OutFile.Extension = "mp4" Me.OutFile.Extension = "mp4"
If Media.Post.Date.HasValue Then Me.OutFile.Name = Media.Post.Date.Value.ToString("yyyyMMdd_HHmmss")
Me.Progress = Progress Me.Progress = Progress
ProgressExists = Not Me.Progress Is Nothing ProgressExists = Not Me.Progress Is Nothing
ProgressPre = New PreProgress(Progress) ProgressPre = New PreProgress(Progress)
@@ -202,9 +206,9 @@ Namespace API.Reddit
End Function End Function
#End Region #End Region
#Region "Statics" #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 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 Function
#End Region #End Region
#Region "IDisposable Support" #Region "IDisposable Support"

View File

@@ -10,6 +10,7 @@ Imports SCrawler.API.Base
Imports SCrawler.Plugin Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Clients.Base
Imports PersonalUtilities.Tools.Web.Documents.JSON Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
@@ -34,6 +35,8 @@ Namespace API.Reddit
"You can find different tokens in the responses. Make sure that bearer token belongs to Reddit and not RedGifs." & vbCr & "You can find different tokens in the responses. Make sure that bearer token belongs to Reddit and not RedGifs." & vbCr &
"There is not need to add a token if you are not using cookies to download the timeline.", IsAuth:=True)> "There is not need to add a token if you are not using cookies to download the timeline.", IsAuth:=True)>
Friend ReadOnly Property BearerToken As PropertyValue Friend ReadOnly Property BearerToken As PropertyValue
<PropertyOption(ControlText:="Use 'cUrl' to get a token", IsAuth:=True), PXML, PClonable, HiddenControl>
Private ReadOnly Property BearerTokenUseCurl As PropertyValue
#Region "TokenUpdateInterval" #Region "TokenUpdateInterval"
<PropertyOption(ControlText:="Token refresh interval", ControlToolTip:="Interval (in minutes) to refresh the token", <PropertyOption(ControlText:="Token refresh interval", ControlToolTip:="Interval (in minutes) to refresh the token",
AllowNull:=False, LeftOffset:=120, IsAuth:=True), PXML, PClonable> AllowNull:=False, LeftOffset:=120, IsAuth:=True), PXML, PClonable>
@@ -55,11 +58,14 @@ Namespace API.Reddit
Return {AuthUserName.Value, AuthPassword.Value, ApiClientID.Value, ApiClientSecret.Value}.All(Function(v$) Not v.IsEmptyString) Return {AuthUserName.Value, AuthPassword.Value, ApiClientID.Value, ApiClientSecret.Value}.All(Function(v$) Not v.IsEmptyString)
End Get End Get
End Property End Property
#End Region #End Region
#Region "Other" #Region "Other"
<PropertyOption(ControlText:="Use M3U8", ControlToolTip:="Use M3U8 or mp4 for Reddit videos", IsAuth:=False), PXML, PClonable> <PropertyOption(ControlText:="Use M3U8", ControlToolTip:="Use M3U8 or mp4 for Reddit videos", IsAuth:=False), PXML, PClonable>
Friend ReadOnly Property UseM3U8 As PropertyValue Friend ReadOnly Property UseM3U8 As PropertyValue
<PropertyOption(ControlText:="Check image", ControlToolTip:="Check the image if it exists before downloading (it makes downloading very slow)", IsAuth:=False), PXML, PClonable>
Friend ReadOnly Property CheckImage As PropertyValue
<PropertyOption(ControlText:="Check image: get original", ControlToolTip:="Get the original image if it exists", IsAuth:=False), PXML, PClonable>
Friend ReadOnly Property CheckImageReturnOrig As PropertyValue
#End Region #End Region
#End Region #End Region
#Region "Initializer" #Region "Initializer"
@@ -78,6 +84,7 @@ Namespace API.Reddit
ApiClientID = New PropertyValue(String.Empty, GetType(String)) ApiClientID = New PropertyValue(String.Empty, GetType(String))
ApiClientSecret = New PropertyValue(String.Empty, GetType(String)) ApiClientSecret = New PropertyValue(String.Empty, GetType(String))
BearerToken = New PropertyValue(token, GetType(String), Sub(v) Responser.Headers.Add(DeclaredNames.Header_Authorization, v)) BearerToken = New PropertyValue(token, GetType(String), Sub(v) Responser.Headers.Add(DeclaredNames.Header_Authorization, v))
BearerTokenUseCurl = New PropertyValue(True)
TokenUpdateInterval = New PropertyValue(60 * 12) TokenUpdateInterval = New PropertyValue(60 * 12)
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider
BearerTokenDateUpdate = New PropertyValue(Now.AddYears(-1)) BearerTokenDateUpdate = New PropertyValue(Now.AddYears(-1))
@@ -87,6 +94,8 @@ Namespace API.Reddit
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String)) SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
UseM3U8 = New PropertyValue(True) UseM3U8 = New PropertyValue(True)
CheckImage = New PropertyValue(False)
CheckImageReturnOrig = New PropertyValue(True)
UrlPatternUser = "https://www.reddit.com/{0}/{1}/" UrlPatternUser = "https://www.reddit.com/{0}/{1}/"
ImageVideoContains = "reddit.com" ImageVideoContains = "reddit.com"
@@ -263,14 +272,36 @@ Namespace API.Reddit
result = False result = False
Dim r$ = String.Empty Dim r$ = String.Empty
Dim c% = 0 Dim c% = 0
Dim _found As Boolean Dim useCurl As Boolean = Settings.CurlFile.Exists And CBool(BearerTokenUseCurl.Value)
Dim curlUsed As Boolean = useCurl
Do Do
c += 1 c += 1
Using resp As New Responser With { Using resp As New Responser With {
.Method = "POST", .Method = "POST",
.ProcessExceptionDecision = Function(status, obj, ee) If(status.StatusCode = 429, EDP.ReturnValue, ee) .ProcessExceptionDecision = Function(ByVal status As IResponserStatus, ByVal nullArg As Object, ByVal currErr As ErrorsDescriber) As ErrorsDescriber
If status.StatusCode = 429 Then
useCurl = False
Return EDP.ReturnValue
ElseIf status.StatusCode = Net.HttpStatusCode.Forbidden And Not useCurl And Settings.CurlFile.Exists Then
useCurl = True
Return EDP.ReturnValue
Else
Return currErr
End If
End Function
} }
With resp With resp
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 With .PayLoadValues
.Add("grant_type", "password") .Add("grant_type", "password")
.Add("username", UserName) .Add("username", UserName)
@@ -279,13 +310,13 @@ Namespace API.Reddit
.CredentialsUserName = ClientID .CredentialsUserName = ClientID
.CredentialsPassword = ClientSecret .CredentialsPassword = ClientSecret
.PreAuthenticate = True .PreAuthenticate = True
End If
End With End With
r = resp.GetResponse("https://www.reddit.com/api/v1/access_token",, EDP.ThrowException) r = resp.GetResponse("https://www.reddit.com/api/v1/access_token",, EDP.ThrowException)
End Using End Using
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r) Using j As EContainer = JsonDocument.Parse(r)
If j.ListExists Then If j.ListExists Then
_found = True
Dim newToken$ = j.Value("access_token") Dim newToken$ = j.Value("access_token")
If Not newToken.IsEmptyString Then If Not newToken.IsEmptyString Then
BearerToken.Value = $"Bearer {newToken}" BearerToken.Value = $"Bearer {newToken}"
@@ -296,7 +327,7 @@ Namespace API.Reddit
End If End If
End Using End Using
End If End If
Loop While c < 5 And Not _found Loop While c < 5 And Not result
End If End If
Return result Return result
Catch ex As Exception Catch ex As Exception

View File

@@ -225,6 +225,7 @@ Namespace API.Reddit
#End Region #End Region
#Region "Download Overrides" #Region "Download Overrides"
Friend Overrides Sub DownloadData(ByVal Token As CancellationToken) Friend Overrides Sub DownloadData(ByVal Token As CancellationToken)
Err429Count = 0
_CrossPosts.Clear() _CrossPosts.Clear()
If CreatedByChannel And Settings.FromChannelDownloadTopUse And Settings.FromChannelDownloadTop > 0 Then _ If CreatedByChannel And Settings.FromChannelDownloadTopUse And Settings.FromChannelDownloadTop > 0 Then _
DownloadTopCount = Settings.FromChannelDownloadTop.Value DownloadTopCount = Settings.FromChannelDownloadTop.Value
@@ -287,6 +288,7 @@ Namespace API.Reddit
End Sub End Sub
#End Region #End Region
#Region "Download Functions (User, Channel)" #Region "Download Functions (User, Channel)"
Private Err429Count As Integer = 0
Private _TotalPostsDownloaded As Integer = 0 Private _TotalPostsDownloaded As Integer = 0
Private ReadOnly _CrossPosts As List(Of String) Private ReadOnly _CrossPosts As List(Of String)
Private Const SiteGfycatKey As String = "gfycat" Private Const SiteGfycatKey As String = "gfycat"
@@ -375,6 +377,7 @@ Namespace API.Reddit
Loop While Not _completed Loop While Not _completed
End Sub End Sub
Private Sub DownloadDataChannel(ByVal POST As String, ByVal Token As CancellationToken) Private Sub DownloadDataChannel(ByVal POST As String, ByVal Token As CancellationToken)
Const savedPostsSleepTimer% = 2000
Dim eObj% = 0 Dim eObj% = 0
Dim round% = 0 Dim round% = 0
Dim URL$ = String.Empty Dim URL$ = String.Empty
@@ -392,12 +395,14 @@ Namespace API.Reddit
If IsSavedPosts Then If IsSavedPosts Then
URL = $"https://www.reddit.com/user/{TrueName}/saved.json?after={POST}" URL = $"https://www.reddit.com/user/{TrueName}/saved.json?after={POST}"
If Not POST.IsEmptyString Then Thread.Sleep(savedPostsSleepTimer)
Else Else
URL = $"https://reddit.com/r/{TrueName}/{View}.json?allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic" URL = $"https://reddit.com/r/{TrueName}/{View}.json?allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic"
End If End If
ThrowAny(Token) ThrowAny(Token)
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If IsSavedPosts Then Err429Count = 0
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
Using w As EContainer = JsonDocument.Parse(r).XmlIfNothing Using w As EContainer = JsonDocument.Parse(r).XmlIfNothing
If w.Count > 0 Then If w.Count > 0 Then
@@ -458,8 +463,12 @@ Namespace API.Reddit
End If End If
_completed = True _completed = True
Catch ex As Exception 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 If round = 2 Then eObj = HttpStatusCode.InternalServerError
ElseIf errValue = 429 And round = 0 Then
Thread.Sleep(savedPostsSleepTimer)
round += 1
Else Else
_completed = True _completed = True
End If End If
@@ -681,6 +690,9 @@ Namespace API.Reddit
End Function End Function
Private Function TryImage(ByVal URL As String) As Boolean Private Function TryImage(ByVal URL As String) As Boolean
Try Try
If Not CBool(MySiteSettings.CheckImage.Value) Then
Return MySiteSettings.CheckImageReturnOrig.Value
Else
Dim img As Image = GetImage(SFile.GetBytesFromNet(URL, EDP.ThrowException), EDP.ThrowException) Dim img As Image = GetImage(SFile.GetBytesFromNet(URL, EDP.ThrowException), EDP.ThrowException)
If Not img Is Nothing Then If Not img Is Nothing Then
img.Dispose() img.Dispose()
@@ -688,6 +700,7 @@ Namespace API.Reddit
Else Else
Return False Return False
End If End If
End If
Catch Catch
Return False Return False
End Try End Try
@@ -971,7 +984,7 @@ Namespace API.Reddit
Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID, .UserID = _UserID}} 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 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 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 Return m
End Function End Function
Private Function TryFile(ByVal URL As String) As Boolean Private Function TryFile(ByVal URL As String) As Boolean
@@ -1023,7 +1036,7 @@ Namespace API.Reddit
Return URL.Contains(SiteRedGifsKey) Return URL.Contains(SiteRedGifsKey)
End Function End Function
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
Return M3U8.Download(URL, DestinationFile, Token, Progress, Not IsSingleObjectDownload) Return M3U8.Download(URL, Media, DestinationFile, Token, Progress, Not IsSingleObjectDownload)
End Function End Function
Protected Overrides Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile Protected Overrides Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile
If Not IsChannel Or Not SaveToCache Then If Not IsChannel Or Not SaveToCache Then
@@ -1053,8 +1066,11 @@ Namespace API.Reddit
ElseIf .StatusCode = HttpStatusCode.InternalServerError Then '500 ElseIf .StatusCode = HttpStatusCode.InternalServerError Then '500
If Not IsNothing(EObj) AndAlso IsNumeric(EObj) AndAlso CInt(EObj) = HttpStatusCode.InternalServerError Then Return 1 If Not IsNothing(EObj) AndAlso IsNumeric(EObj) AndAlso CInt(EObj) = HttpStatusCode.InternalServerError Then Return 1
Return HttpStatusCode.InternalServerError Return HttpStatusCode.InternalServerError
ElseIf .StatusCode = 429 And IsSavedPosts And Err429Count = 0 Then
Err429Count += 1
Return 429
ElseIf .StatusCode = 429 AndAlso 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 Not MySiteSettings.CredentialsExists Then '429
MyMainLOG = $"{ToStringForLog()}: [{CInt(Responser.StatusCode)}] You should use OAuth authorization or disable " & 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") IIf(IsSavedPosts, "token usage for downloading saved posts", "the use of token and cookies for downloading timelines")

View File

@@ -18,9 +18,9 @@ Namespace API.RedGifs
<Manifest(RedGifsSiteKey)> <Manifest(RedGifsSiteKey)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations" #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 Friend ReadOnly Property Token As PropertyValue
<PropertyOption, ControlNumber(2), PClonable> <PropertyOption, ControlNumber(2), PClonable, HiddenControl>
Private ReadOnly Property UserAgent As PropertyValue Private ReadOnly Property UserAgent As PropertyValue
<PXML> Friend ReadOnly Property TokenLastDateUpdated As PropertyValue <PXML> Friend ReadOnly Property TokenLastDateUpdated As PropertyValue
Private Const TokenName As String = "authorization" Private Const TokenName As String = "authorization"
@@ -107,7 +107,9 @@ Namespace API.RedGifs
Friend Overrides Sub Update() Friend Overrides Sub Update()
If _SiteEditorFormOpened Then If _SiteEditorFormOpened Then
Dim NewToken$ = AConvert(Of String)(Token.Value, AModes.Var, String.Empty) 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 End If
MyBase.Update() MyBase.Update()
End Sub End Sub

View File

@@ -13,6 +13,7 @@ Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies Imports PersonalUtilities.Tools.Web.Cookies
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports IG = SCrawler.API.Instagram.SiteSettings Imports IG = SCrawler.API.Instagram.SiteSettings
Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.ThreadsNet Namespace API.ThreadsNet
<Manifest("AndyProgram_ThreadsNet"), SeparatedTasks(1)> <Manifest("AndyProgram_ThreadsNet"), SeparatedTasks(1)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
@@ -25,6 +26,10 @@ Namespace API.ThreadsNet
Return __HH_CSRF_TOKEN Return __HH_CSRF_TOKEN
End Get End Get
End Property End Property
<CookieValueExtractor(NameOf(HH_CSRF_TOKEN))>
Private Function GetValueFromCookies(ByVal PropName As String, ByVal c As CookieKeeper) As String
Return c.GetCookieValue(IG.Header_CSRF_TOKEN_COOKIE, PropName, NameOf(HH_CSRF_TOKEN))
End Function
<PClonable> Protected ReadOnly __HH_IG_APP_ID As PropertyValue <PClonable> Protected ReadOnly __HH_IG_APP_ID As PropertyValue
<PropertyOption(ControlText:="x-ig-app-id", AllowNull:=False, IsAuth:=True), ControlNumber(10)> <PropertyOption(ControlText:="x-ig-app-id", AllowNull:=False, IsAuth:=True), ControlNumber(10)>
Friend Overridable ReadOnly Property HH_IG_APP_ID As PropertyValue Friend Overridable ReadOnly Property HH_IG_APP_ID As PropertyValue
@@ -34,13 +39,17 @@ Namespace API.ThreadsNet
End Property End Property
<PropertyOption(ControlText:="x-asbd-id", AllowNull:=True, IsAuth:=True), ControlNumber(20), PClonable> <PropertyOption(ControlText:="x-asbd-id", AllowNull:=True, IsAuth:=True), ControlNumber(20), PClonable>
Friend ReadOnly Property HH_ASBD_ID As PropertyValue Friend ReadOnly Property HH_ASBD_ID As PropertyValue
<PropertyOption(ControlText:="sec-ch-ua", AllowNull:=True, IsAuth:=True), ControlNumber(30), PClonable> <PropertyOption(ControlText:="sec-ch-ua", AllowNull:=True, IsAuth:=True,
Private ReadOnly Property HH_BROWSER As PropertyValue InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua), ControlNumber(30), PClonable, PXML(OnlyForChecked:=True)>
<PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:="sec-ch-ua-full-version-list", AllowNull:=True, IsAuth:=True), ControlNumber(40), PClonable> Friend ReadOnly Property HH_BROWSER As PropertyValue
Private ReadOnly Property HH_BROWSER_EXT As PropertyValue <PropertyOption(ControlText:="sec-ch-ua-full", ControlToolTip:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list, AllowNull:=True, IsAuth:=True,
<PropertyOption(ControlText:="sec-ch-ua-platform", ControlToolTip:="sec-ch-ua-platform", AllowNull:=True, IsAuth:=True, LeftOffset:=120), ControlNumber(50), PClonable> InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_full_version_list), ControlNumber(40), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_PLATFORM As PropertyValue Friend ReadOnly Property HH_BROWSER_EXT As PropertyValue
<PropertyOption(ControlText:="UserAgent", IsAuth:=True), ControlNumber(60), PClonable> <PropertyOption(ControlText:="sec-ch-ua-platform-ver", ControlToolTip:=SettingsCLS.HEADER_DEF_sec_ch_ua_platform_version, AllowNull:=True, IsAuth:=True, LeftOffset:=135,
InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua_platform_version), ControlNumber(50), PClonable, PXML(OnlyForChecked:=True)>
Friend ReadOnly Property HH_PLATFORM_VER As PropertyValue
<PropertyOption(ControlText:="UserAgent", IsAuth:=True,
InheritanceName:=SettingsCLS.HEADER_DEF_UserAgent), ControlNumber(60), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_USER_AGENT As PropertyValue Private ReadOnly Property HH_USER_AGENT As PropertyValue
Private Sub ChangeResponserFields(ByVal PropName As String, ByVal Value As Object) Private Sub ChangeResponserFields(ByVal PropName As String, ByVal Value As Object)
If Not PropName.IsEmptyString Then If Not PropName.IsEmptyString Then
@@ -52,7 +61,7 @@ Namespace API.ThreadsNet
Case NameOf(HH_CSRF_TOKEN) : f = IG.Header_CSRF_TOKEN Case NameOf(HH_CSRF_TOKEN) : f = IG.Header_CSRF_TOKEN
Case NameOf(HH_BROWSER) : f = IG.Header_Browser Case NameOf(HH_BROWSER) : f = IG.Header_Browser
Case NameOf(HH_BROWSER_EXT) : f = IG.Header_BrowserExt Case NameOf(HH_BROWSER_EXT) : f = IG.Header_BrowserExt
Case NameOf(HH_PLATFORM) : f = IG.Header_Platform Case NameOf(HH_PLATFORM_VER) : f = IG.Header_Platform_Verion
Case NameOf(HH_USER_AGENT) : isUserAgent = True Case NameOf(HH_USER_AGENT) : isUserAgent = True
End Select End Select
If Not f.IsEmptyString Then If Not f.IsEmptyString Then
@@ -64,6 +73,19 @@ Namespace API.ThreadsNet
End If End If
End Sub End Sub
#End Region #End Region
#Region "Other properties"
<PropertyOption(ControlText:="Request timer (any)",
ControlToolTip:="The timer (in milliseconds) that SCrawler should wait before executing the next request." &
vbCr & "The default value is 1'000." & vbCr & "The minimum value is 0." & IG.TimersUrgentTip, AllowNull:=False, 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 #End Region
#Region "Initializer" #Region "Initializer"
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
@@ -86,8 +108,6 @@ Namespace API.ThreadsNet
With Responser With Responser
.Accept = "*/*" .Accept = "*/*"
'URGENT: remove after debug
.DeclaredError = EDP.SendToLog + EDP.ThrowException
If .UserAgentExists Then useragent = .UserAgent If .UserAgentExists Then useragent = .UserAgent
With .Headers With .Headers
If .Count > 0 Then If .Count > 0 Then
@@ -96,7 +116,7 @@ Namespace API.ThreadsNet
asbd = .Value(IG.Header_ASBD_ID) asbd = .Value(IG.Header_ASBD_ID)
browser = .Value(IG.Header_Browser) browser = .Value(IG.Header_Browser)
browserExt = .Value(IG.Header_BrowserExt) browserExt = .Value(IG.Header_BrowserExt)
platform = .Value(IG.Header_Platform) platform = .Value(IG.Header_Platform_Verion)
End If End If
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.threads.net")) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.threads.net"))
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.threads.net")) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.threads.net"))
@@ -108,7 +128,10 @@ Namespace API.ThreadsNet
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "cors")) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "cors"))
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "same-origin")) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "same-origin"))
.Add("Sec-Fetch-User", "?1") .Add("Sec-Fetch-User", "?1")
.Add(DeclaredNames.Header_FB_FRIENDLY_NAME, "BarcelonaProfileThreadsTabRefetchableQuery") .Add("dnt", 1)
.Add("drp", 1)
.Add(Instagram.UserData.GQL_HEADER_FB_FRINDLY_NAME, "BarcelonaProfileThreadsTabRefetchableQuery")
.Remove("dht")
End With End With
.CookiesExtractMode = Responser.CookiesExtractModes.Any .CookiesExtractMode = Responser.CookiesExtractModes.Any
.CookiesUpdateMode = CookieKeeper.UpdateModes.ReplaceByNameAll .CookiesUpdateMode = CookieKeeper.UpdateModes.ReplaceByNameAll
@@ -122,9 +145,13 @@ Namespace API.ThreadsNet
HH_ASBD_ID = New PropertyValue(asbd, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_ASBD_ID), v)) HH_ASBD_ID = New PropertyValue(asbd, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_ASBD_ID), v))
HH_BROWSER = New PropertyValue(browser, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_BROWSER), v)) HH_BROWSER = New PropertyValue(browser, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_BROWSER), v))
HH_BROWSER_EXT = New PropertyValue(browserExt, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_BROWSER_EXT), v)) HH_BROWSER_EXT = New PropertyValue(browserExt, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_BROWSER_EXT), v))
HH_PLATFORM = New PropertyValue(platform, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_PLATFORM), v)) HH_PLATFORM_VER = New PropertyValue(platform, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_PLATFORM_VER), v))
HH_USER_AGENT = New PropertyValue(useragent, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_USER_AGENT), v)) HH_USER_AGENT = New PropertyValue(useragent, GetType(String), Sub(v) ChangeResponserFields(NameOf(HH_USER_AGENT), v))
RequestsWaitTimer_Any = New PropertyValue(1000)
RequestsWaitTimer_AnyProvider = New IG.TimersChecker(0)
DownloadData_Impl = New PropertyValue(True)
UrlPatternUser = "https://www.threads.net/@{0}" UrlPatternUser = "https://www.threads.net/@{0}"
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "threads.net/@"), 1) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "threads.net/@"), 1)
ImageVideoContains = "threads.net" ImageVideoContains = "threads.net"
@@ -151,7 +178,7 @@ Namespace API.ThreadsNet
#End Region #End Region
#Region "BaseAuthExists, GetUserUrl, GetUserPostUrl" #Region "BaseAuthExists, GetUserUrl, GetUserPostUrl"
Friend Overrides Function BaseAuthExists() As Boolean Friend Overrides Function BaseAuthExists() As Boolean
Return Responser.CookiesExists And {HH_CSRF_TOKEN, HH_IG_APP_ID}.All(Function(v) ACheck(Of String)(v.Value)) Return Responser.CookiesExists And {HH_CSRF_TOKEN, HH_IG_APP_ID}.All(Function(v) ACheck(Of String)(v.Value)) And CBool(DownloadData_Impl.Value)
End Function End Function
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
Return String.Format(UrlPatternUser, DirectCast(User, UserData).NameTrue) Return String.Format(UrlPatternUser, DirectCast(User, UserData).NameTrue)
@@ -167,13 +194,23 @@ Namespace API.ThreadsNet
End Function End Function
#End Region #End Region
#Region "Update" #Region "Update"
Private __Cookies As CookieKeeper = Nothing
Friend Overrides Sub BeginEdit()
__Cookies = Responser.Cookies.Copy
MyBase.BeginEdit()
End Sub
Friend Overrides Sub Update() Friend Overrides Sub Update()
If _SiteEditorFormOpened And Responser.CookiesExists Then 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 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 End If
MyBase.Update() MyBase.Update()
End Sub End Sub
Friend Overrides Sub EndEdit()
__Cookies.DisposeIfReady
MyBase.EndEdit()
End Sub
#End Region #End Region
End Class End Class
End Namespace End Namespace

View File

@@ -17,6 +17,10 @@ Imports PersonalUtilities.Tools.Web.Clients.EventArguments
Imports IGS = SCrawler.API.Instagram.SiteSettings Imports IGS = SCrawler.API.Instagram.SiteSettings
Namespace API.ThreadsNet Namespace API.ThreadsNet
Friend Class UserData : Inherits Instagram.UserData Friend Class UserData : Inherits Instagram.UserData
#Region "XML names"
Private Const Name_MaxLastDownDate As String = "MaxLastDownDate"
Private Const Name_FirstLoadingDone As String = "FirstLoadingDone"
#End Region
#Region "Declarations" #Region "Declarations"
Private ReadOnly Property MySettings As SiteSettings Private ReadOnly Property MySettings As SiteSettings
Get Get
@@ -24,16 +28,25 @@ Namespace API.ThreadsNet
End Get End Get
End Property End Property
Private ReadOnly DefaultParser_ElemNode_Default() As Object = {"node", "thread_items", 0, "post"} Private ReadOnly DefaultParser_ElemNode_Default() As Object = {"node", "thread_items", 0, "post"}
Private OPT_LSD As String = String.Empty
Private OPT_FB_DTSG As String = String.Empty
Private ReadOnly Property Valid As Boolean Private ReadOnly Property Valid As Boolean
Get Get
Return Not OPT_LSD.IsEmptyString And Not OPT_FB_DTSG.IsEmptyString And Not ID.IsEmptyString Return ValidateBaseTokens() And Not ID.IsEmptyString
End Get End Get
End Property End Property
Private Property MaxLastDownDate As Date? = Nothing
Private Property FirstLoadingDone As Boolean = False
#End Region #End Region
#Region "Loader" #Region "Loader"
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
With Container
If Loading Then
MaxLastDownDate = AConvert(Of Date)(.Value(Name_MaxLastDownDate), DateTimeDefaultProvider, Nothing)
FirstLoadingDone = .Value(Name_FirstLoadingDone).FromXML(Of Boolean)(False)
Else
.Add(Name_MaxLastDownDate, AConvert(Of String)(MaxLastDownDate, DateTimeDefaultProvider, String.Empty))
.Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger)
End If
End With
End Sub End Sub
#End Region #End Region
#Region "Exchange" #Region "Exchange"
@@ -51,17 +64,45 @@ Namespace API.ThreadsNet
DefaultParser_PostUrlCreator = Function(post) $"https://www.threads.net/@{NameTrue}/post/{post.Code}" DefaultParser_PostUrlCreator = Function(post) $"https://www.threads.net/@{NameTrue}/post/{post.Code}"
_ResponserAutoUpdateCookies = True _ResponserAutoUpdateCookies = True
_ResponserAddResponseReceivedHandler = True _ResponserAddResponseReceivedHandler = True
DefaultParser_Pinned = AddressOf IsPinnedPost
End Sub End Sub
#End Region #End Region
#Region "Download functions" #Region "Download functions"
Private Sub WaitTimer()
If CInt(MySettings.RequestsWaitTimer_Any.Value) > 0 Then Thread.Sleep(CInt(MySettings.RequestsWaitTimer_Any.Value))
End Sub
Private Sub DisableDownload()
MySettings.DownloadData_Impl.Value = False
MyMainLOG = $"{Site} downloading is disabled until you update your credentials"
End Sub
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
If CBool(MySettings.DownloadData_Impl.Value) Then
Dim errorFound As Boolean = False Dim errorFound As Boolean = False
Try Try
Responser.Method = "POST" Responser.Method = "POST"
LoadSavePostsKV(True) LoadSavePostsKV(True)
OPT_LSD = String.Empty ResetBaseTokens()
OPT_FB_DTSG = String.Empty Dim setMaxPostDate As Action(Of List(Of UserMedia)) =
Sub(ByVal l As List(Of UserMedia))
With (From c As UserMedia In l Where c.Post.Date.HasValue Select c.Post.Date.Value)
If .ListExists Then MaxLastDownDate = .Max : _ForceSaveUserInfo = True
End With
End Sub
If FirstLoadingDone Then
If Not MaxLastDownDate.HasValue And _ContentList.Count > 0 Then setMaxPostDate.Invoke(_ContentList)
Else
If _ContentList.Count > 0 Then
FirstLoadingDone = True
If Not MaxLastDownDate.HasValue Then setMaxPostDate.Invoke(_ContentList)
End If
End If
If FirstLoadingDone Then
DefaultParser_SkipPost = Nothing
Else
DefaultParser_SkipPost = AddressOf SkipPost
End If
DownloadData(String.Empty, Token) DownloadData(String.Empty, Token)
If _TempMediaList.Count > 0 Then FirstLoadingDone = True : setMaxPostDate.Invoke(_TempMediaList)
Catch ex As Exception Catch ex As Exception
errorFound = True errorFound = True
Throw ex Throw ex
@@ -71,7 +112,23 @@ Namespace API.ThreadsNet
MySettings.UpdateResponserData(Responser) MySettings.UpdateResponserData(Responser)
If Not errorFound Then LoadSavePostsKV(False) If Not errorFound Then LoadSavePostsKV(False)
End Try End Try
End If
End Sub End Sub
Private Function IsPinnedPost(ByVal Items As IEnumerable(Of EContainer), ByVal Index As Integer) As Boolean
Try
If MaxLastDownDate.HasValue Then
Dim d As Date? = AConvert(Of Date)(Items(Index).ItemF(DefaultParser_ElemNode_Default).Value("taken_at"), UnixDate32Provider, Nothing)
If d.HasValue Then Return d.Value < MaxLastDownDate.Value
End If
Return Not FirstLoadingDone
Catch ex As Exception
LogError(ex, "IsPinnedPost")
Return Not FirstLoadingDone
End Try
End Function
Private Function SkipPost(ByVal Items As IEnumerable(Of EContainer), ByVal Index As Integer, ByVal Post As PostKV) As Boolean
Return PostKvExists(Post)
End Function
Protected Overrides Sub UpdateResponser() Protected Overrides Sub UpdateResponser()
If Not Responser Is Nothing AndAlso Not Responser.Disposed Then If Not Responser Is Nothing AndAlso Not Responser.Disposed Then
RemoveHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived RemoveHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
@@ -95,11 +152,11 @@ Namespace API.ThreadsNet
UpdateCredentials() UpdateCredentials()
If idIsNull And Not ID.IsEmptyString Then _ForceSaveUserInfo = True If idIsNull And Not ID.IsEmptyString Then _ForceSaveUserInfo = True
End If End If
If Not Valid Then Throw New Plugin.ExitException("Some credentials are missing") If Not Valid Then DisableDownload() : Throw New Plugin.ExitException("Some credentials are missing")
Responser.Method = "POST" Responser.Method = "POST"
Responser.Referer = $"https://www.threads.net/@{NameTrue}" Responser.Referer = $"https://www.threads.net/@{NameTrue}"
Responser.Headers.Add(Header_FB_LSD, OPT_LSD) Responser.Headers.Add(GQL_HEADER_FB_LSD, Token_lsd)
Dim nextCursor$ = String.Empty Dim nextCursor$ = String.Empty
Dim dataFound As Boolean = False Dim dataFound As Boolean = False
@@ -112,7 +169,7 @@ Namespace API.ThreadsNet
End If End If
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & vars & "}") vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & vars & "}")
URL = String.Format(urlPattern, OPT_LSD, vars, SymbolsConverter.ASCII.EncodeSymbolsOnly(OPT_FB_DTSG)) URL = String.Format(urlPattern, Token_lsd, vars, Token_dtsg_Var)
Using j As EContainer = GetDocument(URL, Token) Using j As EContainer = GetDocument(URL, Token)
If j.ListExists Then If j.ListExists Then
@@ -135,8 +192,9 @@ Namespace API.ThreadsNet
Private Function GetDocument(ByVal URL As String, ByVal Token As CancellationToken, Optional ByVal Round As Integer = 0) As EContainer Private Function GetDocument(ByVal URL As String, ByVal Token As CancellationToken, Optional ByVal Round As Integer = 0) As EContainer
Try Try
ThrowAny(Token) ThrowAny(Token)
If Round > 0 AndAlso Not UpdateCredentials() Then Throw New Exception("Failed to update credentials") If Round > 0 AndAlso Not UpdateCredentials() Then DisableDownload() : Throw New Exception("Failed to update credentials")
ThrowAny(Token) ThrowAny(Token)
WaitTimer()
Dim r$ = Responser.GetResponse(URL) Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then Return JsonDocument.Parse(r) Else Throw New Exception("Failed to get a response") If Not r.IsEmptyString Then Return JsonDocument.Parse(r) Else Throw New Exception("Failed to get a response")
Catch ex As Exception Catch ex As Exception
@@ -149,47 +207,42 @@ Namespace API.ThreadsNet
End Function End Function
Private Function UpdateCredentials(Optional ByVal e As ErrorsDescriber = Nothing) As Boolean Private Function UpdateCredentials(Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
Dim URL$ = $"https://www.threads.net/@{NameTrue}" Dim URL$ = $"https://www.threads.net/@{NameTrue}"
OPT_LSD = String.Empty ResetBaseTokens()
OPT_FB_DTSG = String.Empty Dim headers As New HttpHeaderCollection
headers.AddRange(Responser.Headers)
Try Try
Responser.Method = "GET" With Responser
Responser.Referer = URL .Method = "GET"
Responser.Headers.Remove(Header_FB_LSD) .Referer = URL
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException) With .Headers
Dim rr As RParams .Clear()
Dim tt$, ttVal$ .Add("dnt", 1)
If Not r.IsEmptyString Then .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.threads.net"))
rr = RParams.DM(Instagram.PageTokenRegexPatternDefault, 0, RegexReturn.List, EDP.ReturnValue) .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.threads.net"))
Dim tokens As List(Of String) = RegexReplace(r, rr) .Add("Sec-Ch-Ua-Model", "")
If tokens.ListExists Then .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaMobile, "?0"))
With rr .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecChUaPlatform, """Windows"""))
.Match = Nothing .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchDest, "document"))
.MatchSub = 1 .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchMode, "navigate"))
.WhatGet = RegexReturn.Value .Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.SecFetchSite, "none"))
.Add("Upgrade-Insecure-Requests", 1)
.Add("Sec-Fetch-User", "?1")
.Add(IGS.Header_Browser, MySettings.HH_BROWSER.Value)
.Add(IGS.Header_BrowserExt, MySettings.HH_BROWSER_EXT.Value)
End With End With
For Each tt In tokens End With
If Not OPT_FB_DTSG.IsEmptyString And Not OPT_LSD.IsEmptyString Then WaitTimer()
Exit For Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException)
Else If Not r.IsEmptyString Then
ttVal = RegexReplace(tt, rr) ParseTokens(r, 0)
If Not ttVal.IsEmptyString Then If ID.IsEmptyString Then ID = RegexReplace(r, RParams.DMS("""props"":\{""user_id"":""(\d+)""", 1, EDP.ReturnValue))
If ttVal.Contains(":") Then
If OPT_FB_DTSG.IsEmptyString Then OPT_FB_DTSG = ttVal
Else
If OPT_LSD.IsEmptyString Then OPT_LSD = ttVal
End If
End If
End If
Next
End If
If ID.IsEmptyString Then ID = RegexReplace(r, RParams.DMS("""props"":\{""user_id"":""(\d+)""\},", 1, EDP.ReturnValue))
End If End If
Return Valid Return Valid
Catch ex As Exception Catch ex As Exception
Dim notFound$ = String.Empty Dim notFound$ = String.Empty
If OPT_FB_DTSG.IsEmptyString Then notFound.StringAppend(Header_FB_LSD) ValidateBaseTokens(notFound)
If OPT_LSD.IsEmptyString Then notFound.StringAppend("lsd")
If ID.IsEmptyString Then notFound.StringAppend("User ID") If ID.IsEmptyString Then notFound.StringAppend("User ID")
DisableDownload()
Dim eex As New ErrorsDescriberException($"{ToStringForLog()}: failed to update some{IIf(notFound.IsEmptyString, String.Empty, $" ({notFound})")} credentials",,, ex) With { Dim eex As New ErrorsDescriberException($"{ToStringForLog()}: failed to update some{IIf(notFound.IsEmptyString, String.Empty, $" ({notFound})")} credentials",,, ex) With {
.ReplaceMainMessage = True, .ReplaceMainMessage = True,
.SendToLogOnlyMessage = Responser.StatusCode = Net.HttpStatusCode.InternalServerError And Responser.Status = Net.WebExceptionStatus.ProtocolError .SendToLogOnlyMessage = Responser.StatusCode = Net.HttpStatusCode.InternalServerError And Responser.Status = Net.WebExceptionStatus.ProtocolError
@@ -197,6 +250,12 @@ Namespace API.ThreadsNet
'LogError(ex, $"failed to update some{IIf(notFound.IsEmptyString, String.Empty, $" ({notFound})")} credentials", e) 'LogError(ex, $"failed to update some{IIf(notFound.IsEmptyString, String.Empty, $" ({notFound})")} credentials", e)
LogError(eex, String.Empty, e) LogError(eex, String.Empty, e)
Return False Return False
Finally
If headers.ListExists Then
Responser.Headers.Clear()
Responser.Headers.AddRange(headers)
headers.Dispose()
End If
End Try End Try
End Function End Function
#End Region #End Region
@@ -224,7 +283,7 @@ Namespace API.ThreadsNet
If m.State = UserMedia.States.Missing And Not m.Post.ID.IsEmptyString Then If m.State = UserMedia.States.Missing And Not m.Post.ID.IsEmptyString Then
ThrowAny(Token) ThrowAny(Token)
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(varsPattern, m.Post.ID.Split("_").FirstOrDefault, ID) & "}") vars = SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & String.Format(varsPattern, m.Post.ID.Split("_").FirstOrDefault, ID) & "}")
URL = String.Format(urlPattern, OPT_LSD, vars, SymbolsConverter.ASCII.EncodeSymbolsOnly(OPT_FB_DTSG)) URL = String.Format(urlPattern, Token_lsd, vars, Token_dtsg_Var)
j = GetDocument(URL, Token) j = GetDocument(URL, Token)
If j.ListExists Then If j.ListExists Then

View File

@@ -17,11 +17,13 @@ Namespace API.TikTok
Friend ReadOnly Property RemoveTagsFromTitle As PropertyValue Friend ReadOnly Property RemoveTagsFromTitle As PropertyValue
<PropertyOption(ControlText:="Use native title", ControlToolTip:="Use a user-created video title for the filename instead of the video ID."), PXML, PClonable> <PropertyOption(ControlText:="Use native title", ControlToolTip:="Use a user-created video title for the filename instead of the video ID."), PXML, PClonable>
Friend ReadOnly Property TitleUseNative As PropertyValue Friend ReadOnly Property TitleUseNative As PropertyValue
<PropertyOption(ControlText:="Use native title in standalone downloader", <PropertyOption(ControlText:="Use native title (standalone downloader)",
ControlToolTip:="Use a user-created video title for the filename instead of the video ID."), PXML, PClonable> ControlToolTip:="Use a user-created video title for the filename instead of the video ID."), PXML, PClonable>
Friend ReadOnly Property TitleUseNativeSTD As PropertyValue Friend ReadOnly Property TitleUseNativeSTD As PropertyValue
<PropertyOption(ControlText:="Add video ID to video title"), PXML, PClonable> <PropertyOption(ControlText:="Add video ID to video title"), PXML, PClonable>
Friend ReadOnly Property TitleAddVideoID As PropertyValue Friend ReadOnly Property TitleAddVideoID As PropertyValue
<PropertyOption(ControlText:="Add video ID to video title (standalone downloader)"), PXML, PClonable>
Friend ReadOnly Property TitleAddVideoIDSTD As PropertyValue
<PropertyOption(ControlText:="Use regex to clean video title"), PXML, PClonable> <PropertyOption(ControlText:="Use regex to clean video title"), PXML, PClonable>
Friend ReadOnly Property TitleUseRegexForTitle As PropertyValue Friend ReadOnly Property TitleUseRegexForTitle As PropertyValue
<PropertyOption(ControlText:="Title regex", ControlToolTip:="Regex to clean video title"), PXML, PClonable> <PropertyOption(ControlText:="Title regex", ControlToolTip:="Regex to clean video title"), PXML, PClonable>
@@ -29,15 +31,20 @@ Namespace API.TikTok
<PropertyOption(ControlText:="Use video date as file date", <PropertyOption(ControlText:="Use video date as file date",
ControlToolTip:="Set the file date to the date the video was added (website) (if available)."), PXML, PClonable> ControlToolTip:="Set the file date to the date the video was added (website) (if available)."), PXML, PClonable>
Friend ReadOnly Property UseParsedVideoDate As PropertyValue Friend ReadOnly Property UseParsedVideoDate As PropertyValue
<PropertyOption(ControlText:="Use video date as file date (standalone downloader)",
ControlToolTip:="Set the file date to the date the video was added (website) (if available)."), PXML, PClonable>
Friend ReadOnly Property UseParsedVideoDateSTD As PropertyValue
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
MyBase.New("TikTok", "www.tiktok.com", AccName, Temp, My.Resources.SiteResources.TikTokIcon_32, My.Resources.SiteResources.TikTokPic_192) MyBase.New("TikTok", "www.tiktok.com", AccName, Temp, My.Resources.SiteResources.TikTokIcon_32, My.Resources.SiteResources.TikTokPic_192)
RemoveTagsFromTitle = New PropertyValue(False) RemoveTagsFromTitle = New PropertyValue(False)
TitleUseNative = New PropertyValue(True) TitleUseNative = New PropertyValue(True)
TitleUseNativeSTD = New PropertyValue(False) TitleUseNativeSTD = New PropertyValue(True)
TitleAddVideoID = New PropertyValue(True) TitleAddVideoID = New PropertyValue(True)
TitleAddVideoIDSTD = New PropertyValue(True)
TitleUseRegexForTitle = New PropertyValue(False) TitleUseRegexForTitle = New PropertyValue(False)
TitleUseRegexForTitle_Value = New PropertyValue(String.Empty, GetType(String)) TitleUseRegexForTitle_Value = New PropertyValue(String.Empty, GetType(String))
UseParsedVideoDate = New PropertyValue(True) UseParsedVideoDate = New PropertyValue(True)
UseParsedVideoDateSTD = New PropertyValue(False)
UseNetscapeCookies = True UseNetscapeCookies = True
UrlPatternUser = "https://www.tiktok.com/@{0}/" UrlPatternUser = "https://www.tiktok.com/@{0}/"
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "tiktok.com/@"), 1) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "tiktok.com/@"), 1)

View File

@@ -151,6 +151,23 @@ Namespace API.TikTok
End Try End Try
Return Title Return Title
End Function End Function
Private Function GetNewFileName(ByVal Title As String, ByVal Native As Boolean, ByVal RemoveTags As Boolean, ByVal AddVideoID As Boolean,
ByVal PostID As String, ByVal TitleRegex As RParams) As String
If Not Title.IsEmptyString Then Title = Left(Title, 150).StringTrim
If Title.IsEmptyString Or Not Native Then
Title = PostID
Else
If RemoveTags Then Title = RegexReplace(Title, RegexTagsReplacer)
Title = Title.StringTrim
If Title.IsEmptyString Then
Title = PostID
ElseIf AddVideoID Then
Title &= $" ({PostID})"
End If
Title = ChangeTitleRegex(Title, TitleRegex)
End If
Return Title
End Function
Friend Overrides Sub DownloadData(ByVal Token As CancellationToken) Friend Overrides Sub DownloadData(ByVal Token As CancellationToken)
MyBase.DownloadData(Token) MyBase.DownloadData(Token)
UserCache.DisposeIfReady(False) UserCache.DisposeIfReady(False)
@@ -228,20 +245,8 @@ Namespace API.TikTok
Else Else
Exit Sub Exit Sub
End If End If
title = j.Value("title").StringRemoveWinForbiddenSymbols title = GetNewFileName(j.Value("title").StringRemoveWinForbiddenSymbols,
If Not title.IsEmptyString Then title = Left(title, 150) TitleUseNative, RemoveTagsFromTitle, TitleAddVideoID, postID, titleRegex)
If title.IsEmptyString Or Not TitleUseNative Then
title = postID
Else
If RemoveTagsFromTitle Then title = RegexReplace(title, RegexTagsReplacer)
title = title.StringTrim
If title.IsEmptyString Then
title = postID
ElseIf TitleAddVideoID Then
title &= $" ({postID})"
End If
title = ChangeTitleRegex(title, titleRegex)
End If
postDate = AConvert(Of Date)(j.Value("timestamp"), UnixDate32Provider, Nothing) postDate = AConvert(Of Date)(j.Value("timestamp"), UnixDate32Provider, Nothing)
If Not postDate.HasValue Then postDate = AConvert(Of Date)(j.Value("upload_date"), SimpleDateConverter, Nothing) If Not postDate.HasValue Then postDate = AConvert(Of Date)(j.Value("upload_date"), SimpleDateConverter, Nothing)
Select Case CheckDatesLimit(postDate, SimpleDateConverter) Select Case CheckDatesLimit(postDate, SimpleDateConverter)
@@ -296,7 +301,7 @@ Namespace API.TikTok
End If End If
If DateBefore.HasValue Then command &= $"--datebefore {DateBefore.Value.AddDays(1).ToStringDate(SimpleDateConverter)} " If DateBefore.HasValue Then command &= $"--datebefore {DateBefore.Value.AddDays(1).ToStringDate(SimpleDateConverter)} "
If DateAfter.HasValue Then command &= $"--dateafter {DateAfter.Value.AddDays(-1).ToStringDate(SimpleDateConverter)} " If DateAfter.HasValue Then command &= $"--dateafter {DateAfter.Value.AddDays(-1).ToStringDate(SimpleDateConverter)} "
If Not CBool(MySettings.UseParsedVideoDate.Value) Then command &= "--no-mtime " If Not CBool(If(IsSingleObjectDownload, MySettings.UseParsedVideoDateSTD, MySettings.UseParsedVideoDate).Value) Then command &= "--no-mtime "
If MySettings.CookiesNetscapeFile.Exists Then command &= $"--no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}"" " If MySettings.CookiesNetscapeFile.Exists Then command &= $"--no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}"" "
command &= $"{URL} " command &= $"{URL} "
If SupportOutput Then If SupportOutput Then
@@ -347,17 +352,18 @@ Namespace API.TikTok
Dim m As New UserMedia(Data.URL, UserMedia.Types.Video) Dim m As New UserMedia(Data.URL, UserMedia.Types.Video)
If Not f.IsEmptyString Then f = TitleHtmlConverter(f) If Not f.IsEmptyString Then f = TitleHtmlConverter(f)
If Not f.IsEmptyString Then If Not f.IsEmptyString Then
If CBool(MySettings.RemoveTagsFromTitle.Value) Then f = RegexReplace(f, RegexTagsReplacer) f = GetNewFileName(f, MySettings.TitleUseNativeSTD.Value, MySettings.RemoveTagsFromTitle.Value, MySettings.TitleAddVideoIDSTD.Value,
f = f.StringTrim m.File.Name, GetTitleRegex)
If Not f.IsEmptyString Then If Not f.IsEmptyString Then m.File.Name = f.StringTrim
If CBool(MySettings.TitleAddVideoID.Value) Then f &= $" ({m.File.Name})"
f = ChangeTitleRegex(f, GetTitleRegex)
m.File.Name = f
End If
End If End If
_TempMediaList.Add(m) _TempMediaList.Add(m)
End Sub End Sub
#End Region #End Region
#Region "EraseData"
Protected Overrides Sub EraseData_AdditionalDataFiles()
LastDownloadDate = Nothing
End Sub
#End Region
#Region "Exception" #Region "Exception"
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
Optional ByVal EObj As Object = Nothing) As Integer Optional ByVal EObj As Object = Nothing) As Integer

View File

@@ -15,6 +15,7 @@ Namespace API.Twitter
Friend Const TwitterSiteKey As String = "AndyProgram_Twitter" Friend Const TwitterSiteKey As String = "AndyProgram_Twitter"
Friend ReadOnly DateProvider As ADateTime = GetDateProvider() Friend ReadOnly DateProvider As ADateTime = GetDateProvider()
Friend ReadOnly VideoSizeRegEx As RParams = RParams.DMS("\d+x(\d+)", 1, EDP.ReturnValue) Friend ReadOnly VideoSizeRegEx As RParams = RParams.DMS("\d+x(\d+)", 1, EDP.ReturnValue)
Friend ReadOnly StatusRegEx As RParams = RParams.DM(".*?(twitter|x)\.com/\S+/status/\d+", 0, EDP.ReturnValue)
Private Function GetDateProvider() As ADateTime Private Function GetDateProvider() As ADateTime
Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone
n.FullDateTimePattern = "ddd MMM dd HH:mm:ss +ffff yyyy" n.FullDateTimePattern = "ddd MMM dd HH:mm:ss +ffff yyyy"

View File

@@ -28,16 +28,20 @@ Namespace API.Twitter
Friend Overridable Property MediaModelAllowNonUserTweets As Boolean = False Friend Overridable Property MediaModelAllowNonUserTweets As Boolean = False
<PSetting(Address:=SettingAddress.User, <PSetting(Address:=SettingAddress.User,
Caption:="Download model 'Media'", Caption:="Download model 'Media'",
ToolTip:="Download the data using the 'https://twitter.com/UserName/media' command.", LeftOffset:=DefaultOffset)> ToolTip:="Download the data using the 'https://x.com/UserName/media' command.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelMedia As Boolean = False Friend Overridable Property DownloadModelMedia As Boolean = False
<PSetting(Address:=SettingAddress.User, <PSetting(Address:=SettingAddress.User,
Caption:="Download model 'Profile'", Caption:="Download model 'Profile'",
ToolTip:="Download the data using the 'https://twitter.com/UserName' command.", LeftOffset:=DefaultOffset)> ToolTip:="Download the data using the 'https://x.com/UserName' command.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelProfile As Boolean = False Friend Overridable Property DownloadModelProfile As Boolean = False
<PSetting(Address:=SettingAddress.User, <PSetting(Address:=SettingAddress.User,
Caption:="Download model 'Search'", Caption:="Download model 'Search'",
ToolTip:="Download the data using the 'https://twitter.com/search?q=from:UserName+include:nativeretweets' command.", LeftOffset:=DefaultOffset)> ToolTip:="Download the data using the 'https://x.com/search?q=from:UserName+include:nativeretweets' command.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelSearch As Boolean = False Friend Overridable Property DownloadModelSearch As Boolean = False
<PSetting(Address:=SettingAddress.User,
Caption:="Download model 'Likes'",
ToolTip:="Download the data using the 'https://x.com/UserName/likes' command.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelLikes As Boolean = False
<PSetting(Address:=SettingAddress.User, <PSetting(Address:=SettingAddress.User,
Caption:="Force apply", Caption:="Force apply",
ToolTip:="Force overrides the default parameters for the first download." & vbCr & "Applies to first download only.", LeftOffset:=DefaultOffset)> ToolTip:="Force overrides the default parameters for the first download." & vbCr & "Applies to first download only.", LeftOffset:=DefaultOffset)>
@@ -73,6 +77,7 @@ Namespace API.Twitter
DownloadModelMedia = dm.Contains(DModels.Media) DownloadModelMedia = dm.Contains(DModels.Media)
DownloadModelProfile = dm.Contains(DModels.Profile) DownloadModelProfile = dm.Contains(DModels.Profile)
DownloadModelSearch = dm.Contains(DModels.Search) DownloadModelSearch = dm.Contains(DModels.Search)
DownloadModelLikes = dm.Contains(DModels.Likes)
End If End If
End If End If
MySettings = u.HOST.Source MySettings = u.HOST.Source

View File

@@ -16,32 +16,37 @@ Namespace API.Twitter
<Manifest(TwitterSiteKey), SavedPosts, SeparatedTasks, SpecialForm(False)> <Manifest(TwitterSiteKey), SavedPosts, SeparatedTasks, SpecialForm(False)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations" #Region "Declarations"
#Region "Categories"
Private Const CAT_DOWN As String = "Downloading"
#End Region
#Region "Other properties" #Region "Other properties"
<PropertyOption(ControlText:="Use the appropriate model", <PropertyOption(ControlText:="Use the appropriate model",
ControlToolTip:="Use the appropriate model for new users." & vbCr & ControlToolTip:="Use the appropriate model for new users." & vbCr &
"If disabled, all download models will be used for the first download. " & "If disabled, all download models will be used for the first download. " &
"Next, the appropriate download model will be automatically selected." & vbCr & "Next, the appropriate download model will be automatically selected." & vbCr &
"Otherwise the appropriate download model will be selected right from the start."), PXML, PClonable> "Otherwise the appropriate download model will be selected right from the start.", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property UseAppropriateModel As PropertyValue Friend ReadOnly Property UseAppropriateModel As PropertyValue
#Region "End points" #Region "End points"
<PropertyOption(ControlText:="New endpoint: search", ControlToolTip:="Use new endpoint argument (-o search-endpoint=graphql) for the search model."), PXML, PClonable> <PropertyOption(ControlText:="New endpoint: search", ControlToolTip:="Use new endpoint argument (-o search-endpoint=graphql) for the search model.",
Category:=CAT_DOWN), PXML, PClonable>
Friend Property UseNewEndPointSearch As PropertyValue Friend Property UseNewEndPointSearch As PropertyValue
<PropertyOption(ControlText:="New endpoint: profiles", ControlToolTip:="Use new endpoint argument (-o search-endpoint=graphql) for the profile models."), PXML, PClonable> <PropertyOption(ControlText:="New endpoint: profiles", ControlToolTip:="Use new endpoint argument (-o search-endpoint=graphql) for the profile models.",
Category:=CAT_DOWN), PXML, PClonable>
Friend Property UseNewEndPointProfiles As PropertyValue Friend Property UseNewEndPointProfiles As PropertyValue
#End Region #End Region
#Region "Limits" #Region "Limits"
<PropertyOption(ControlText:="Abort on limit", ControlToolTip:="Abort twitter downloading when limit is reached"), PXML, PClonable> <PropertyOption(ControlText:="Abort on limit", ControlToolTip:="Abort twitter downloading when limit is reached", Category:=CAT_DOWN), PXML, PClonable>
Friend Property AbortOnLimit As PropertyValue Friend Property AbortOnLimit As PropertyValue
<PropertyOption(ControlText:="Download already parsed", ControlToolTip:="Download already parsed content on abort"), PXML, PClonable> <PropertyOption(ControlText:="Download already parsed", ControlToolTip:="Download already parsed content on abort", Category:=CAT_DOWN), PXML, PClonable>
Friend Property DownloadAlreadyParsed As PropertyValue Friend Property DownloadAlreadyParsed As PropertyValue
#End Region #End Region
<PropertyOption(ControlText:="Media Model: allow non-user tweets", ControlToolTip:="Allow downloading non-user tweets in the media-model."), PXML, PClonable> <PropertyOption(ControlText:="Media Model: allow non-user tweets", ControlToolTip:="Allow downloading non-user tweets in the media-model.", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property MediaModelAllowNonUserTweets As PropertyValue Friend ReadOnly Property MediaModelAllowNonUserTweets As PropertyValue
<PropertyOption(ControlText:=DN.GifsDownloadCaption), PXML, PClonable> <PropertyOption(ControlText:=DN.GifsDownloadCaption, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property GifsDownload As PropertyValue Friend ReadOnly Property GifsDownload As PropertyValue
<PropertyOption(ControlText:=DN.GifsSpecialFolderCaption, ControlToolTip:=DN.GifsSpecialFolderToolTip), PXML, PClonable> <PropertyOption(ControlText:=DN.GifsSpecialFolderCaption, ControlToolTip:=DN.GifsSpecialFolderToolTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property GifsSpecialFolder As PropertyValue Friend ReadOnly Property GifsSpecialFolder As PropertyValue
<PropertyOption(ControlText:=DN.GifsPrefixCaption, ControlToolTip:=DN.GifsPrefixToolTip), PXML, PClonable> <PropertyOption(ControlText:=DN.GifsPrefixCaption, ControlToolTip:=DN.GifsPrefixToolTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property GifsPrefix As PropertyValue Friend ReadOnly Property GifsPrefix As PropertyValue
<Provider(NameOf(GifsSpecialFolder), Interaction:=True), Provider(NameOf(GifsPrefix), Interaction:=True)> <Provider(NameOf(GifsSpecialFolder), Interaction:=True), Provider(NameOf(GifsPrefix), Interaction:=True)>
Private ReadOnly Property GifStringChecker As IFormatProvider Private ReadOnly Property GifStringChecker As IFormatProvider
@@ -63,17 +68,17 @@ Namespace API.Twitter
Throw New NotImplementedException("[GetFormat] is not available in the context of [GifStringProvider]") Throw New NotImplementedException("[GetFormat] is not available in the context of [GifStringProvider]")
End Function End Function
End Class End Class
<PropertyOption(ControlText:=DN.UseMD5ComparisonCaption, ControlToolTip:=DN.UseMD5ComparisonToolTip), PXML, PClonable> <PropertyOption(ControlText:=DN.UseMD5ComparisonCaption, ControlToolTip:=DN.UseMD5ComparisonToolTip, Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property UseMD5Comparison As PropertyValue Friend ReadOnly Property UseMD5Comparison As PropertyValue
<PropertyOption(ControlText:=DN.ConcurrentDownloadsCaption, <PropertyOption(ControlText:=DN.ConcurrentDownloadsCaption,
ControlToolTip:=DN.ConcurrentDownloadsToolTip, AllowNull:=False, LeftOffset:=120), PXML, TaskCounter, PClonable> ControlToolTip:=DN.ConcurrentDownloadsToolTip, AllowNull:=False, LeftOffset:=120, Category:=CAT_DOWN), PXML, TaskCounter, PClonable>
Friend ReadOnly Property ConcurrentDownloads As PropertyValue Friend ReadOnly Property ConcurrentDownloads As PropertyValue
<Provider(NameOf(ConcurrentDownloads), FieldsChecker:=True)> <Provider(NameOf(ConcurrentDownloads), FieldsChecker:=True)>
Private ReadOnly Property MyConcurrentDownloadsProvider As IFormatProvider Private ReadOnly Property MyConcurrentDownloadsProvider As IFormatProvider
#End Region #End Region
#End Region #End Region
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean) Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
MyBase.New(TwitterSite, "twitter.com", AccName, Temp, My.Resources.SiteResources.TwitterIcon_32, My.Resources.SiteResources.TwitterIcon_32.ToBitmap) MyBase.New(TwitterSite, "x.com", AccName, Temp, My.Resources.SiteResources.TwitterIcon_32, My.Resources.SiteResources.TwitterIcon_32.ToBitmap)
LimitSkippedUsers = New List(Of UserDataBase) LimitSkippedUsers = New List(Of UserDataBase)
@@ -97,7 +102,7 @@ Namespace API.Twitter
MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "/(twitter|x).com/"), 2) UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "/(twitter|x).com/"), 2)
UrlPatternUser = "https://twitter.com/{0}" UrlPatternUser = "https://x.com/{0}"
ImageVideoContains = "twitter" ImageVideoContains = "twitter"
CheckNetscapeCookiesOnEndInit = True CheckNetscapeCookiesOnEndInit = True
UseNetscapeCookies = True UseNetscapeCookies = True
@@ -106,7 +111,7 @@ Namespace API.Twitter
Return New UserData Return New UserData
End Function End Function
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
Return $"https://twitter.com/{User.Name}/status/{Media.Post.ID}" Return $"https://x.com/{User.Name}/status/{Media.Post.ID}"
End Function End Function
Friend Overrides Function BaseAuthExists() As Boolean Friend Overrides Function BaseAuthExists() As Boolean
Return Responser.CookiesExists Return Responser.CookiesExists

View File

@@ -33,6 +33,7 @@ Namespace API.Twitter
Media = 1 Media = 1
Profile = 2 Profile = 2
Search = 5 Search = 5
Likes = 10
End Enum End Enum
Private FirstDownloadComplete As Boolean = False Private FirstDownloadComplete As Boolean = False
Friend Property DownloadModelForceApply As Boolean = False Friend Property DownloadModelForceApply As Boolean = False
@@ -41,6 +42,7 @@ Namespace API.Twitter
Friend Property GifsDownload As Boolean = True Friend Property GifsDownload As Boolean = True
Friend Property GifsSpecialFolder As String = String.Empty Friend Property GifsSpecialFolder As String = String.Empty
Friend Property GifsPrefix As String = String.Empty Friend Property GifsPrefix As String = String.Empty
Private ReadOnly LikesPosts As List(Of String)
Private ReadOnly _DataNames As List(Of String) Private ReadOnly _DataNames As List(Of String)
Private ReadOnly Property MySettings As SiteSettings Private ReadOnly Property MySettings As SiteSettings
Get Get
@@ -74,6 +76,7 @@ Namespace API.Twitter
If .DownloadModelMedia Then DownloadModel += DownloadModels.Media If .DownloadModelMedia Then DownloadModel += DownloadModels.Media
If .DownloadModelProfile Then DownloadModel += DownloadModels.Profile If .DownloadModelProfile Then DownloadModel += DownloadModels.Profile
If .DownloadModelSearch Then DownloadModel += DownloadModels.Search If .DownloadModelSearch Then DownloadModel += DownloadModels.Search
If .DownloadModelLikes Then DownloadModel += DownloadModels.Likes
End With End With
End If End If
End Sub End Sub
@@ -81,6 +84,7 @@ Namespace API.Twitter
#Region "Initializer, loader" #Region "Initializer, loader"
Friend Sub New() Friend Sub New()
_DataNames = New List(Of String) _DataNames = New List(Of String)
LikesPosts = New List(Of String)
End Sub End Sub
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
With Container With Container
@@ -142,6 +146,7 @@ Namespace API.Twitter
} }
End Function End Function
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Try
If MySettings.LIMIT_ABORT Then If MySettings.LIMIT_ABORT Then
Throw New TwitterLimitException(Me) Throw New TwitterLimitException(Me)
Else Else
@@ -149,14 +154,24 @@ Namespace API.Twitter
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly) If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
DownloadData_SavedPosts(Token) DownloadData_SavedPosts(Token)
Else Else
LikesPosts.Clear()
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly) If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
DownloadData_Timeline(Token) DownloadData_Timeline(Token)
If LikesPosts.Count > 0 Then
_ReparseLikes = True
ReparseMissing(Token)
_ReparseLikes = False
End If End If
End If End If
End If
Finally
_ReparseLikes = False
End Try
End Sub End Sub
Private Sub DownloadData_Timeline(ByVal Token As CancellationToken) Private Sub DownloadData_Timeline(ByVal Token As CancellationToken)
Dim URL$ = String.Empty Dim URL$ = String.Empty
Dim tCache As CacheKeeper = Nothing Dim tCache As CacheKeeper = Nothing
Dim likesDetected As Boolean = False
Try Try
Const entry$ = "entry" Const entry$ = "entry"
Dim PostID$ = String.Empty Dim PostID$ = String.Empty
@@ -199,6 +214,7 @@ Namespace API.Twitter
If Not _TempPostsList.Contains(PostID) Then If Not _TempPostsList.Contains(PostID) Then
_TempPostsList.Add(PostID) _TempPostsList.Add(PostID)
ElseIf dirIndx = 3 Then
ElseIf isPins Then ElseIf isPins Then
Return False Return False
Else Else
@@ -211,9 +227,22 @@ Namespace API.Twitter
If tmpUserId.IsEmptyString Then tmpUserId = nn.ItemF({"extended_entities", "media", 0, sourceIdPredicate}).XmlIfNothingValue. If tmpUserId.IsEmptyString Then tmpUserId = nn.ItemF({"extended_entities", "media", 0, sourceIdPredicate}).XmlIfNothingValue.
IfNullOrEmpty(nn.Value("user_id")).IfNullOrEmpty(nn.Value("user_id_str")).IfNullOrEmpty("/") IfNullOrEmpty(nn.Value("user_id")).IfNullOrEmpty(nn.Value("user_id_str")).IfNullOrEmpty("/")
If Not ParseUserMediaOnly OrElse If (Not ParseUserMediaOnly Or dirIndx = 3) OrElse
(dirIndx = 0 AndAlso MediaModelAllowNonUserTweets) OrElse (dirIndx = 0 AndAlso MediaModelAllowNonUserTweets) OrElse
(Not ID.IsEmptyString AndAlso tmpUserId = ID) Then ObtainMedia(nn, PostID, PostDate) (Not ID.IsEmptyString AndAlso tmpUserId = ID) Then
If dirIndx = 3 Then
Dim lUrl$ = nn.ItemF({"content", "itemContent", "tweet_results", "result", "legacy", "entities", "media", 0}, "expanded_url").XmlIfNothingValue
If Not lUrl.IsEmptyString Then
lUrl = RegexReplace(lUrl, StatusRegEx)
If Not lUrl.IsEmptyString Then
If Not _TempPostsList.Contains(lUrl) Then _TempPostsList.Add(lUrl) Else Return False
LikesPosts.ListAddValue(lUrl, LNC)
End If
End If
Else
ObtainMedia(nn, PostID, PostDate)
End If
End If
End If End If
Return True Return True
End Function End Function
@@ -225,6 +254,8 @@ Namespace API.Twitter
For Each dir As SFile In dirs For Each dir As SFile In dirs
dirIndx += 1 dirIndx += 1
If dirIndx = 3 Then likesDetected = True
ExistsDetected = False ExistsDetected = False
If Not dir.IsEmptyString Then If Not dir.IsEmptyString Then
@@ -287,17 +318,22 @@ Namespace API.Twitter
End If End If
End If End If
Else Else
For pIndx = 0 To IIf(dirIndx < 2, 1, 0) For pIndx = 0 To IIf(dirIndx < 2 Or dirIndx = 3, 1, 0)
optionalNode = Nothing optionalNode = Nothing
Select Case dirIndx Select Case dirIndx
Case 0, 1 Case 0, 1, 3
rootNode = j({"data", "user", "result", "timeline_v2", "timeline", "instructions"}) rootNode = j({"data", "user", "result", "timeline_v2", "timeline", "instructions"})
If rootNode.ListExists Then If rootNode.ListExists Then
If dirIndx = 3 Then
p = entriesNode
isPins = False
Else
p = If(pIndx = 0, pinNode, timelineNode) p = If(pIndx = 0, pinNode, timelineNode)
isPins = pIndx = 0 isPins = pIndx = 0
End If
optionalNode = rootNode optionalNode = rootNode
rootNode = rootNode.Find(p, False) rootNode = rootNode.Find(p, dirIndx = 3)
If rootNode.ListExists Then rootNode = rootNode.Find(entriesNode, False) If dirIndx <> 3 And rootNode.ListExists Then rootNode = rootNode.Find(entriesNode, dirIndx = 3)
End If End If
Case Else Case Else
isPins = False isPins = False
@@ -369,12 +405,12 @@ Namespace API.Twitter
ProcessException(ex, Token, $"data downloading error [{URL}]") ProcessException(ex, Token, $"data downloading error [{URL}]")
Finally Finally
If Not tCache Is Nothing Then tCache.Dispose() If Not tCache Is Nothing Then tCache.Dispose()
If _TempPostsList.Count > 0 Then _TempPostsList.Sort() If _TempPostsList.Count > 0 And Not likesDetected Then _TempPostsList.Sort()
End Try End Try
End Sub End Sub
Private Sub DownloadData_SavedPosts(ByVal Token As CancellationToken) Private Sub DownloadData_SavedPosts(ByVal Token As CancellationToken)
Try Try
Dim f As SFile = GetDataFromGalleryDL("https://twitter.com/i/bookmarks", Settings.Cache, True, Token) Dim f As SFile = GetDataFromGalleryDL("https://x.com/i/bookmarks", Settings.Cache, True, Token)
Dim files As List(Of SFile) = SFile.GetFiles(f, "*.txt") Dim files As List(Of SFile) = SFile.GetFiles(f, "*.txt")
If files.ListExists Then If files.ListExists Then
ResetFileNameProvider(Math.Max(files.Count.ToString.Length, 3)) ResetFileNameProvider(Math.Max(files.Count.ToString.Length, 3))
@@ -417,21 +453,24 @@ Namespace API.Twitter
#End Region #End Region
#Region "Obtain media" #Region "Obtain media"
Private Sub ObtainMedia(ByVal e As EContainer, ByVal PostID As String, ByVal PostDate As String, Optional ByVal State As UStates = UStates.Unknown, Private Sub ObtainMedia(ByVal e As EContainer, ByVal PostID As String, ByVal PostDate As String, Optional ByVal State As UStates = UStates.Unknown,
Optional ByVal Attempts As Integer = 0) Optional ByVal Attempts As Integer = 0, Optional ByVal SpecialFolder As String = Nothing)
Dim s As EContainer = e({"extended_entities", "media"}) Dim s As EContainer = e({"extended_entities", "media"})
If If(s?.Count, 0) = 0 Then s = e({"retweeted_status", "extended_entities", "media"}) If If(s?.Count, 0) = 0 Then s = e({"retweeted_status", "extended_entities", "media"})
If If(s?.Count, 0) = 0 Then s = e({"retweeted_status_result", "result", "legacy", "extended_entities", "media"}) If If(s?.Count, 0) = 0 Then s = e({"retweeted_status_result", "result", "legacy", "extended_entities", "media"})
If If(s?.Count, 0) > 0 Then If If(s?.Count, 0) > 0 Then
Dim mUrl$ Dim mUrl$
Dim media As UserMedia
For Each m As EContainer In s For Each m As EContainer In s
If Not CheckVideoNode(m, PostID, PostDate, State) Then If Not CheckVideoNode(m, PostID, PostDate, State, SpecialFolder) Then
mUrl = m.Value("media_url").IfNullOrEmpty(m.Value("media_url_https")) mUrl = m.Value("media_url").IfNullOrEmpty(m.Value("media_url_https"))
If Not mUrl.IsEmptyString Then If Not mUrl.IsEmptyString Then
Dim dName$ = UrlFile(mUrl) Dim dName$ = UrlFile(mUrl)
If Not dName.IsEmptyString AndAlso Not _DataNames.Contains(dName) Then If Not dName.IsEmptyString AndAlso Not _DataNames.Contains(dName) Then
_DataNames.Add(dName) _DataNames.Add(dName)
_TempMediaList.ListAddValue(MediaFromData(mUrl, PostID, PostDate, GetPictureOption(m), State, UTypes.Picture, Attempts), LNC) media = MediaFromData(mUrl, PostID, PostDate, GetPictureOption(m), State, UTypes.Picture, Attempts)
If Not SpecialFolder.IsEmptyString Then media.SpecialFolder = SpecialFolder
_TempMediaList.ListAddValue(media, LNC)
End If End If
End If End If
End If End If
@@ -439,15 +478,17 @@ Namespace API.Twitter
End If End If
End Sub End Sub
Private Function CheckVideoNode(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String, Private Function CheckVideoNode(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal State As UStates = UStates.Unknown) As Boolean Optional ByVal State As UStates = UStates.Unknown, Optional ByVal SpecialFolder As String = Nothing) As Boolean
Try Try
If CheckForGif(w, PostID, PostDate, State) Then Return True If CheckForGif(w, PostID, PostDate, State, SpecialFolder) Then Return True
Dim URL$ = GetVideoNodeURL(w) Dim URL$ = GetVideoNodeURL(w)
If Not URL.IsEmptyString Then If Not URL.IsEmptyString Then
Dim f$ = UrlFile(URL) Dim f$ = UrlFile(URL)
If Not f.IsEmptyString AndAlso Not _DataNames.Contains(f) Then If Not f.IsEmptyString AndAlso Not _DataNames.Contains(f) Then
_DataNames.Add(f) _DataNames.Add(f)
_TempMediaList.ListAddValue(MediaFromData(URL, PostID, PostDate,, State, UTypes.Video), LNC) Dim m As UserMedia = MediaFromData(URL, PostID, PostDate,, State, UTypes.Video)
If Not SpecialFolder.IsEmptyString Then m.SpecialFolder = SpecialFolder
_TempMediaList.ListAddValue(m, LNC)
End If End If
Return True Return True
End If End If
@@ -458,7 +499,7 @@ Namespace API.Twitter
End Try End Try
End Function End Function
Private Function CheckForGif(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String, Private Function CheckForGif(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal State As UStates = UStates.Unknown) As Boolean Optional ByVal State As UStates = UStates.Unknown, Optional ByVal SpecialFolder As String = Nothing) As Boolean
Try Try
Dim gifUrl As Predicate(Of EContainer) = Function(e) Not e.Value("content_type").IsEmptyString AndAlso Dim gifUrl As Predicate(Of EContainer) = Function(e) Not e.Value("content_type").IsEmptyString AndAlso
e.Value("content_type").Contains("mp4") AndAlso e.Value("content_type").Contains("mp4") AndAlso
@@ -477,9 +518,13 @@ Namespace API.Twitter
If Not ff.IsEmptyString Then If Not ff.IsEmptyString Then
If GifsDownload And Not _DataNames.Contains(ff) Then If GifsDownload And Not _DataNames.Contains(ff) Then
m = MediaFromData(url, PostID, PostDate,, State, UTypes.Video) m = MediaFromData(url, PostID, PostDate,, State, UTypes.Video)
If Not SpecialFolder.IsEmptyString Then m.SpecialFolder = SpecialFolder
f = m.File f = m.File
If Not f.IsEmptyString And Not GifsPrefix.IsEmptyString Then f.Name = $"{GifsPrefix}{f.Name}" : m.File = f If Not f.IsEmptyString And Not GifsPrefix.IsEmptyString Then f.Name = $"{GifsPrefix}{f.Name}" : m.File = f
If Not GifsSpecialFolder.IsEmptyString Then m.SpecialFolder = $"{GifsSpecialFolder}*" If Not GifsSpecialFolder.IsEmptyString Then
If Not m.SpecialFolder.IsEmptyString Then m.SpecialFolder &= "\"
m.SpecialFolder &= $"{GifsSpecialFolder}*"
End If
_TempMediaList.ListAddValue(m, LNC) _TempMediaList.ListAddValue(m, LNC)
End If End If
Return True Return True
@@ -621,11 +666,12 @@ Namespace API.Twitter
.AutoClear = True, .AutoClear = True,
.AutoReset = True, .AutoReset = True,
.CommandPermanent = $"chcp {BatchExecutor.UnicodeEncoding}", .CommandPermanent = $"chcp {BatchExecutor.UnicodeEncoding}",
.FileExchanger = confCache .FileExchanger = confCache,
.DebugMode = True
} }
tgdl.FileExchanger.DeleteCacheOnDispose = False tgdl.FileExchanger.DeleteCacheOnDispose = False
tgdl.FileExchanger.DeleteRootOnDispose = False tgdl.FileExchanger.DeleteRootOnDispose = False
For i As Byte = 0 To 2 For i As Byte = 0 To 3
dir = rootDir.NewPath dir = rootDir.NewPath
dir.Exists(SFO.Path, True, EDP.ThrowException) dir.Exists(SFO.Path, True, EDP.ThrowException)
outList.Add(dir) outList.Add(dir)
@@ -633,9 +679,10 @@ Namespace API.Twitter
command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages " command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages "
command &= GdlGetIdFilterString() command &= GdlGetIdFilterString()
Select Case i Select Case i
Case 0 : command &= $"https://twitter.com/{Name}/media" : process = dm.Contains(DownloadModels.Media) Case 0 : command &= $"https://x.com/{Name}/media" : process = dm.Contains(DownloadModels.Media)
Case 1 : command &= $"https://twitter.com/{Name}" : process = dm.Contains(DownloadModels.Profile) Case 1 : command &= $"https://x.com/{Name}" : process = dm.Contains(DownloadModels.Profile)
Case 2 : command &= $"-o search-endpoint=graphql https://twitter.com/search?q=from:{Name}+include:nativeretweets" : process = dm.Contains(DownloadModels.Search) Case 2 : command &= $"-o search-endpoint=graphql https://x.com/search?q=from:{Name}+include:nativeretweets" : process = dm.Contains(DownloadModels.Search)
Case 3 : command &= $"https://x.com/{Name}/likes" : process = dm.Contains(DownloadModels.Likes)
Case Else : process = False Case Else : process = False
End Select End Select
'#If DEBUG Then '#If DEBUG Then
@@ -687,13 +734,14 @@ Namespace API.Twitter
End Function End Function
#End Region #End Region
#Region "ReparseMissing" #Region "ReparseMissing"
Private _ReparseLikes As Boolean = False
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken) Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
Const SinglePostPattern$ = "https://twitter.com/{0}/status/{1}" Const SinglePostPattern$ = "https://x.com/{0}/status/{1}"
Dim rList As New List(Of Integer) Dim rList As New List(Of Integer)
Dim URL$ = String.Empty Dim URL$ = String.Empty
Dim cache As CacheKeeper = Nothing Dim cache As CacheKeeper = Nothing
Try Try
If ContentMissingExists Then If ContentMissingExists Or (_ReparseLikes And LikesPosts.Count > 0) Then
Dim m As UserMedia Dim m As UserMedia
Dim PostDate$ Dim PostDate$
Dim nodes As List(Of String()) = GetContainerSubnodes() Dim nodes As List(Of String()) = GetContainerSubnodes()
@@ -702,22 +750,29 @@ Namespace API.Twitter
Dim f As SFile Dim f As SFile
Dim i%, ii% Dim i%, ii%
Dim files As List(Of SFile) Dim files As List(Of SFile)
Dim lim%
Dim specFolder$ = IIf(_ReparseLikes, "Likes", String.Empty)
ResetFileNameProvider() ResetFileNameProvider()
If IsSingleObjectDownload Then If IsSingleObjectDownload Then
cache = Settings.Cache cache = Settings.Cache
ElseIf _ReparseLikes Then
cache = CreateCache()
Else Else
cache = New CacheKeeper(DownloadContentDefault_GetRootDir.CSFilePS) cache = New CacheKeeper(DownloadContentDefault_GetRootDir.CSFilePS)
cache.CacheDeleteError = CacheDeletionError(cache) cache.CacheDeleteError = CacheDeletionError(cache)
End If End If
ProgressPre.ChangeMax(_ContentList.Count) If _ReparseLikes Then lim = LikesPosts.Count Else lim = _ContentList.Count
For i = 0 To _ContentList.Count - 1 ProgressPre.ChangeMax(lim)
For i = 0 To lim - 1
ProgressPre.Perform() ProgressPre.Perform()
If _ContentList(i).State = UStates.Missing Then If _ReparseLikes OrElse _ContentList(i).State = UStates.Missing Then
m = _ContentList(i) m = If(_ReparseLikes, Nothing, _ContentList(i))
If Not m.Post.ID.IsEmptyString Or (IsSingleObjectDownload And Not m.URL_BASE.IsEmptyString) Then If Not m.Post.ID.IsEmptyString Or (IsSingleObjectDownload And Not m.URL_BASE.IsEmptyString) Or _ReparseLikes Then
ThrowAny(Token) ThrowAny(Token)
If IsSingleObjectDownload Then If IsSingleObjectDownload Then
URL = m.URL_BASE URL = m.URL_BASE
ElseIf _ReparseLikes Then
URL = LikesPosts(i)
Else Else
URL = String.Format(SinglePostPattern, Name, m.Post.ID) URL = String.Format(SinglePostPattern, Name, m.Post.ID)
End If End If
@@ -737,7 +792,7 @@ Namespace API.Twitter
If .ListExists Then If .ListExists Then
PostDate = String.Empty PostDate = String.Empty
If .Contains("created_at") Then PostDate = .Value("created_at") Else PostDate = String.Empty If .Contains("created_at") Then PostDate = .Value("created_at") Else PostDate = String.Empty
ObtainMedia(.Self, m.Post.ID, PostDate, UStates.Missing, m.Attempts) ObtainMedia(.Self, m.Post.ID, PostDate, UStates.Missing, m.Attempts, specFolder)
rList.ListAddValue(i, LNC) rList.ListAddValue(i, LNC)
End If End If
End With End With
@@ -759,7 +814,7 @@ Namespace API.Twitter
ProcessException(ex, Token, $"ReparseMissing error [{URL}]") ProcessException(ex, Token, $"ReparseMissing error [{URL}]")
Finally Finally
If Not cache Is Nothing And Not IsSingleObjectDownload Then cache.Dispose() If Not cache Is Nothing And Not IsSingleObjectDownload Then cache.Dispose()
If rList.Count > 0 Then If rList.Count > 0 And Not _ReparseLikes Then
For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next
rList.Clear() rList.Clear()
End If End If
@@ -856,7 +911,7 @@ Namespace API.Twitter
#End Region #End Region
#Region "IDisposable support" #Region "IDisposable support"
Protected Overrides Sub Dispose(ByVal disposing As Boolean) Protected Overrides Sub Dispose(ByVal disposing As Boolean)
If Not disposedValue And disposing Then _DataNames.Clear() If Not disposedValue And disposing Then _DataNames.Clear() : LikesPosts.Clear()
MyBase.Dispose(disposing) MyBase.Dispose(disposing)
End Sub End Sub
#End Region #End Region

View File

@@ -276,11 +276,6 @@ Namespace API
Set(ByVal NewDate As Date?) Set(ByVal NewDate As Date?)
End Set End Set
End Property End Property
Friend Overrides ReadOnly Property FitToAddParams As Boolean
Get
Return Count > 0 AndAlso Collections.Exists(Function(c) c.FitToAddParams)
End Get
End Property
Friend Overrides Property ScriptUse As Boolean Friend Overrides Property ScriptUse As Boolean
Get Get
Return Count > 0 AndAlso Collections.All(Function(c) c.ScriptUse) Return Count > 0 AndAlso Collections.All(Function(c) c.ScriptUse)
@@ -563,7 +558,7 @@ Namespace API
End Sub End Sub
#End Region #End Region
#Region "Move, Merge" #Region "Move, Merge"
Friend Overrides Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile) As Boolean Friend Overrides Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile, Optional ByVal NewUser As SplitCollectionUserInfo? = Nothing) As Boolean
Throw New NotImplementedException("Move files is not available in the collection context") Throw New NotImplementedException("Move files is not available in the collection context")
End Function End Function
Friend Overloads Sub MergeData(ByVal Merging As Boolean) Friend Overloads Sub MergeData(ByVal Merging As Boolean)
@@ -606,7 +601,19 @@ Namespace API
"Operation canceled", MsgBoxStyle.Critical) "Operation canceled", MsgBoxStyle.Critical)
Return False Return False
Else Else
_Item.MoveFiles(String.Empty, Nothing) Dim uObj As SplitCollectionUserInfo? = DirectCast(_Item, UserDataBase).SplitCollectionGetNewUserInfo
If uObj.Value.SameDrive Then
uObj = Nothing
Else
Using f As New SplitCollectionUserInfoChangePathsForm({uObj})
f.ShowDialog()
Select Case f.DialogResult
Case DialogResult.OK : If f.Users(0).Changed Then uObj = f.Users(0) Else uObj = Nothing
Case DialogResult.Abort : Return False
End Select
End Using
End If
_Item.MoveFiles(String.Empty, Nothing, uObj)
MainFrameObj.ImageHandler(_Item) MainFrameObj.ImageHandler(_Item)
AddRemoveBttDeleteHandler(_Item, False) AddRemoveBttDeleteHandler(_Item, False)
RaiseEvent OnUserRemoved(_Item) RaiseEvent OnUserRemoved(_Item)
@@ -623,7 +630,7 @@ Namespace API
End If End If
Dim m As New MMessage($"Collection [{CollectionName} (number of profiles: {Count})] may contain data" & vbCr & Dim m As New MMessage($"Collection [{CollectionName} (number of profiles: {Count})] may contain data" & vbCr &
"Are you sure you want to delete the collection and all of its files?", MsgTitle, "Are you sure you want to delete the collection and all of its files?", MsgTitle,
{New MsgBoxButton("Delete") With {.ToolTip = "Delete the collection and all files", .KeyCode = Keys.Enter}, {New MsgBoxButton("Delete", "Delete the collection and all files") With {.KeyCode = Keys.Enter},
New MsgBoxButton("Split") With { New MsgBoxButton("Split") With {
.ToolTip = "Users will be removed from the collection and will be displayed in the program as separate users." & vbCr & .ToolTip = "Users will be removed from the collection and will be displayed in the program as separate users." & vbCr &
"All user data will remain.", "All user data will remain.",
@@ -658,8 +665,27 @@ Namespace API
MsgBoxE({$"Collection [{CollectionName}] data merged{vbCr}Unable to split merged collection{vbCr}Operation canceled", MsgTitle}, vbExclamation) MsgBoxE({$"Collection [{CollectionName}] data merged{vbCr}Unable to split merged collection{vbCr}Operation canceled", MsgTitle}, vbExclamation)
Return 0 Return 0
Else Else
Collections.ForEach(Sub(ByVal c As IUserData) Dim uu As New List(Of SplitCollectionUserInfo)(Collections.Select(Function(uuu As UserDataBase) uuu.SplitCollectionGetNewUserInfo))
If c.MoveFiles(String.Empty, Nothing) Then If uu.All(Function(uuu) uuu.SameDrive) Then
uu.Clear()
Else
Using colPaths As New SplitCollectionUserInfoChangePathsForm(uu)
colPaths.ShowDialog()
Select Case colPaths.DialogResult
Case DialogResult.OK
If colPaths.Users.Any(Function(uuu) uuu.Changed) Then
uu = New List(Of SplitCollectionUserInfo)(colPaths.Users)
Else
uu.Clear()
End If
Case DialogResult.Abort : Return 0
End Select
End Using
End If
Collections.ListForEach(Sub(ByVal c As IUserData, ByVal indx As Integer)
Dim uObj As SplitCollectionUserInfo? = Nothing
If uu.Count > 0 AndAlso indx.ValueBetween(0, uu.Count - 1) AndAlso uu(indx).Changed Then uObj = uu(indx)
If c.MoveFiles(String.Empty, Nothing, uObj) Then
UserListLoader.UpdateUser(Settings.GetUser(c), True) UserListLoader.UpdateUser(Settings.GetUser(c), True)
MainFrameObj.ImageHandler(c) MainFrameObj.ImageHandler(c)
End If End If

View File

@@ -184,6 +184,7 @@ Namespace API.Xhamster
#End Region #End Region
#Region "Download functions" #Region "Download functions"
Friend Function GetNonUserUrl(ByVal Page As Integer) As String Friend Function GetNonUserUrl(ByVal Page As Integer) As String
Const newest$ = "/newest"
If SiteMode = SiteModes.User And Not IsCreator Then If SiteMode = SiteModes.User And Not IsCreator Then
Return String.Empty Return String.Empty
Else Else
@@ -200,6 +201,7 @@ Namespace API.Xhamster
url &= $"/{TrueName}" url &= $"/{TrueName}"
Dim args$ = Arguments Dim args$ = Arguments
If (args.IsEmptyString OrElse Not args.Contains(newest)) And Not SiteMode = SiteModes.Search Then url &= newest
If Page > 1 Then If Page > 1 Then
If args.IsEmptyString Then If args.IsEmptyString Then
If SiteMode = SiteModes.Search Then If SiteMode = SiteModes.Search Then
@@ -262,30 +264,39 @@ Namespace API.Xhamster
Dim m As UserMedia Dim m As UserMedia
Dim checkLimit As Func(Of Boolean) = Function() limit > 0 And SearchPostsCount >= limit And IsVideo Dim checkLimit As Func(Of Boolean) = Function() limit > 0 And SearchPostsCount >= limit And IsVideo
If IsSavedPosts Then
If IsVideo Then
containerNodes.Add({"favoriteVideoListComponent", "models"})
containerNodes.Add({"favoriteVideoListComponent", "videoThumbProps"})
Else
containerNodes.Add({"favoritesGalleriesAndPhotosCollection"})
End If
ElseIf Not SiteMode = SiteModes.Search Then
If IsVideo Then
containerNodes.Add({"trendingVideoListComponent", "models"})
containerNodes.Add({"pagesCategoryComponent", "trendingVideoListProps", "models"})
containerNodes.Add({"trendingVideoSectionComponent", "videoModels"})
containerNodes.Add({"trendingVideoSectionComponent", "videoListProps", "videoThumbProps"})
containerNodes.Add({"userVideoCollection"})
containerNodes.Add({"videoListComponent", "models"})
containerNodes.Add({"videoListComponent", "videoThumbProps"})
Else
containerNodes.Add({"userGalleriesCollection"})
End If
End If
If IsSavedPosts Then If IsSavedPosts Then
URL = $"https://xhamster.com/my/favorites/{IIf(IsVideo, "videos", "photos-and-galleries")}{IIf(Page = 1, String.Empty, $"/{Page}")}" URL = $"https://xhamster.com/my/favorites/{IIf(IsVideo, "videos", "photos-and-galleries")}{IIf(Page = 1, String.Empty, $"/{Page}")}"
containerNodes.Add(If(IsVideo, {"favoriteVideoListComponent", "models"}, {"favoritesGalleriesAndPhotosCollection"})) containerNodes.Add(If(IsVideo, {"favoriteVideoListComponent", "models"}, {"favoritesGalleriesAndPhotosCollection"}))
ElseIf IsChannel Then ElseIf IsChannel Then
URL = $"https://xhamster.com/channels/{TrueName}/newest{IIf(Page = 1, String.Empty, $"/{Page}")}" URL = $"https://xhamster.com/channels/{TrueName}/newest{IIf(Page = 1, String.Empty, $"/{Page}")}"
containerNodes.Add({"trendingVideoListComponent", "models"})
containerNodes.Add({"pagesCategoryComponent", "trendingVideoListProps", "models"})
ElseIf SiteMode = SiteModes.Search Then ElseIf SiteMode = SiteModes.Search Then
URL = GetNonUserUrl(Page) URL = GetNonUserUrl(Page)
containerNodes.Add({"searchResult", "models"}) containerNodes.Add({"searchResult", "models"})
ElseIf IsCreator Or SiteMode = SiteModes.Tags Or SiteMode = SiteModes.Categories Or SiteMode = SiteModes.Pornstars Then ElseIf IsCreator Or SiteMode = SiteModes.Tags Or SiteMode = SiteModes.Categories Or SiteMode = SiteModes.Pornstars Then
URL = GetNonUserUrl(Page) URL = GetNonUserUrl(Page)
If SiteMode = SiteModes.Pornstars Then
containerNodes.Add({"trendingVideoListComponent", "models"})
containerNodes.Add({"pagesCategoryComponent", "trendingVideoListProps", "models"})
Else
containerNodes.Add({"pagesCategoryComponent", "trendingVideoListProps", "models"})
containerNodes.Add({"trendingVideoListComponent", "models"})
End If
containerNodes.Add({"trendingVideoSectionComponent", "videoModels"})
Else Else
URL = $"https://xhamster.com/users/{TrueName}/{IIf(IsVideo, "videos", "photos")}{IIf(Page = 1, String.Empty, $"/{Page}")}" URL = $"https://xhamster.com/users/{TrueName}/{IIf(IsVideo, "videos", "photos")}{IIf(Page = 1, String.Empty, $"/{Page}")}"
containerNodes.Add({If(IsVideo, "userVideoCollection", "userGalleriesCollection")})
containerNodes.Add(If(IsVideo, {"videoListComponent", "models"}, {"userGalleriesCollection"}))
End If End If
ThrowAny(Token) ThrowAny(Token)

View File

@@ -230,7 +230,7 @@ Namespace API.YouTube
End If End If
If list.Count > 0 Then If list.Count > 0 Then
With list(0) With list(0)
If Settings.UserSiteNameUpdateEveryTime Or UserSiteName.IsEmptyString Then UserSiteName = .UserTitle If Settings.UpdateUserSiteNameEveryTime Or UserSiteName.IsEmptyString Then UserSiteName = .UserTitle
If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName
End With End With
_TempMediaList.AddRange(list.Select(Function(c) New UserMedia(c) With {.URL = If(IsSubscription, c.ThumbnailUrlMedia, .URL)})) _TempMediaList.AddRange(list.Select(Function(c) New UserMedia(c) With {.URL = If(IsSubscription, c.ThumbnailUrlMedia, .URL)}))
@@ -467,6 +467,13 @@ Namespace API.YouTube
_TempMediaList.Add(New UserMedia(Data)) _TempMediaList.Add(New UserMedia(Data))
End Sub End Sub
#End Region #End Region
#Region "EraseData"
Protected Overrides Sub EraseData_AdditionalDataFiles()
LastDownloadDateVideos = Nothing
LastDownloadDateShorts = Nothing
LastDownloadDatePlaylist = Nothing
End Sub
#End Region
#Region "DownloadingException" #Region "DownloadingException"
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
Optional ByVal EObj As Object = Nothing) As Integer Optional ByVal EObj As Object = Nothing) As Integer

View File

@@ -18,11 +18,10 @@ Namespace DownloadObjects
Friend Event PauseChanged(ByVal Value As PauseModes) Friend Event PauseChanged(ByVal Value As PauseModes)
Friend Enum Modes As Integer Friend Enum Modes As Integer
None = 0 None = 0
[Default] = 1
All = 2
Specified = 3 Specified = 3
Groups = 4 Groups = 4
End Enum End Enum
Friend Const NoPauseMode As Integer = -100
Friend Enum PauseModes As Integer Friend Enum PauseModes As Integer
Disabled = -2 Disabled = -2
Enabled = -1 Enabled = -1
@@ -295,6 +294,9 @@ Namespace DownloadObjects
Return $"{Name} ({GetWorkingState()}): last download date: {GetLastDateString()}; next run: {GetNextDateString()}" Return $"{Name} ({GetWorkingState()}): last download date: {GetLastDateString()}; next run: {GetNextDateString()}"
End If End If
End Function End Function
Friend Overrides Function ToStringViewFilters() As String
Return $"Scheduler plan '{Name}'{IIf(IsManual, " (manual)", String.Empty)}"
End Function
#End Region #End Region
#End Region #End Region
#Region "Initializer" #Region "Initializer"
@@ -314,7 +316,9 @@ Namespace DownloadObjects
Friend Sub New(ByVal x As EContainer) Friend Sub New(ByVal x As EContainer)
Me.New Me.New
Initialization = True Initialization = True
Mode = x.Value(Name_Mode).FromXML(Of Integer)(Modes.None) Dim m% = x.Value(Name_Mode).FromXML(Of Integer)(Modes.None)
If m = 1 Or m = 2 Then m = Modes.Specified
Mode = m
Import(x) Import(x)
If Name.IsEmptyString Then Name = "Default" If Name.IsEmptyString Then Name = "Default"
Groups.ListAddList(x.Value(Name_Groups).StringToList(Of String)("|"), LAP.NotContainsOnly) Groups.ListAddList(x.Value(Name_Groups).StringToList(Of String)("|"), LAP.NotContainsOnly)
@@ -555,38 +559,6 @@ Namespace DownloadObjects
End Try End Try
End Sub End Sub
Select Case Mode Select Case Mode
Case Modes.All
Dim CheckLabels As Predicate(Of IUserData) = Function(ByVal u As IUserData) As Boolean
If LabelsExcluded.Count = 0 Then
Return True
ElseIf u.Labels.Count = 0 Then
Return True
Else
Return Not u.Labels.ListContains(LabelsExcluded)
End If
End Function
Dim CheckSites As Predicate(Of IUserData) = Function(u) SitesExcluded.Count = 0 OrElse Not SitesExcluded.Contains(u.Site)
Dim ExistsPredicate As Predicate(Of IUserData)
If Subscriptions Then
If SubscriptionsOnly Then
ExistsPredicate = UserExistsSubscriptionsPredicate
Else
ExistsPredicate = UserExistsPredicate
End If
Else
ExistsPredicate = UserExistsNonSubscriptionsPredicate
End If
users.ListAddList(Settings.GetUsers(Function(u) ExistsPredicate(u) And CheckLabels.Invoke(u) And CheckSites.Invoke(u)))
If UsersCount <> 0 And users.Count > 0 Then
users = users.ListTake(If(UsersCount > 0, -1, -2), Math.Abs(UsersCount))
If UsersCount < 0 Then users = users.ListReverse
End If
Case Modes.Default
Using g As New GroupParameters
g.LabelsExcluded.ListAddList(LabelsExcluded)
g.SitesExcluded.ListAddList(SitesExcluded)
users.ListAddList(DownloadGroup.GetUsers(g))
End Using
Case Modes.Specified : users.ListAddList(DownloadGroup.GetUsers(Me)) Case Modes.Specified : users.ListAddList(DownloadGroup.GetUsers(Me))
Case Modes.Groups Case Modes.Groups
If Groups.Count > 0 And Settings.Groups.Count > 0 Then If Groups.Count > 0 And Settings.Groups.Count > 0 Then
@@ -601,7 +573,19 @@ Namespace DownloadObjects
With Downloader With Downloader
.AutoDownloaderWorking = True .AutoDownloaderWorking = True
If .Downloaded.Count > 0 Then .Downloaded.RemoveAll(Function(u) Keys.Contains(u.Key)) : .InvokeDownloadsChangeEvent() If .Downloaded.Count > 0 Then .Downloaded.RemoveAll(Function(u) Keys.Contains(u.Key)) : .InvokeDownloadsChangeEvent()
Do : Try : doRound += 1 : .AddRange(users, True) : Exit Do : Catch iex As IndexOutOfRangeException : Thread.Sleep(200) : End Try : Loop While doRound < doLim Do
Try
doRound += 1
.AddRange(users, True)
Exit Do
Catch iex As Exception
If doRound = doLim Then
Throw iex
Else
Thread.Sleep(200)
End If
End Try
Loop While doRound <= doLim
While .Working Or .Count > 0 : notify.Invoke() : Thread.Sleep(200) : End While While .Working Or .Count > 0 : notify.Invoke() : Thread.Sleep(200) : End While
.AutoDownloaderWorking = False .AutoDownloaderWorking = False
notify.Invoke notify.Invoke
@@ -615,7 +599,7 @@ Namespace DownloadObjects
End With End With
End If End If
Catch ex As Exception Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[AutoDownloader.Download]") ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[AutoDownloader.Download({Name})]")
Finally Finally
Keys.Clear() Keys.Clear()
LastDownloadDate = Now LastDownloadDate = Now

View File

@@ -25,16 +25,15 @@ Namespace DownloadObjects
Me.components = New System.ComponentModel.Container() Me.components = New System.ComponentModel.Container()
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
Dim TP_MODE As System.Windows.Forms.TableLayoutPanel Dim TP_MODE As System.Windows.Forms.TableLayoutPanel
Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(AutoDownloaderEditorForm)) Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(AutoDownloaderEditorForm))
Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton() Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
Dim TP_NOTIFY As System.Windows.Forms.TableLayoutPanel Dim TP_NOTIFY As System.Windows.Forms.TableLayoutPanel
Dim ActionButton7 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 ActionButton8 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 TT_MAIN As System.Windows.Forms.ToolTip Dim TT_MAIN As System.Windows.Forms.ToolTip
Me.DEF_GROUP = New SCrawler.DownloadObjects.Groups.GroupDefaults() Me.DEF_GROUP = New SCrawler.DownloadObjects.Groups.GroupDefaults()
Me.OPT_ALL = New System.Windows.Forms.RadioButton()
Me.OPT_DEFAULT = New System.Windows.Forms.RadioButton()
Me.OPT_SPEC = New System.Windows.Forms.RadioButton() Me.OPT_SPEC = New System.Windows.Forms.RadioButton()
Me.OPT_DISABLED = New System.Windows.Forms.RadioButton() Me.OPT_DISABLED = New System.Windows.Forms.RadioButton()
Me.OPT_GROUP = New System.Windows.Forms.RadioButton() Me.OPT_GROUP = New System.Windows.Forms.RadioButton()
@@ -67,13 +66,13 @@ Namespace DownloadObjects
'CONTAINER_MAIN.ContentPanel 'CONTAINER_MAIN.ContentPanel
' '
CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DEF_GROUP) CONTAINER_MAIN.ContentPanel.Controls.Add(Me.DEF_GROUP)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 388) CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(476, 519)
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
CONTAINER_MAIN.LeftToolStripPanelVisible = False CONTAINER_MAIN.LeftToolStripPanelVisible = False
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0) CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
CONTAINER_MAIN.Name = "CONTAINER_MAIN" CONTAINER_MAIN.Name = "CONTAINER_MAIN"
CONTAINER_MAIN.RightToolStripPanelVisible = False CONTAINER_MAIN.RightToolStripPanelVisible = False
CONTAINER_MAIN.Size = New System.Drawing.Size(476, 413) CONTAINER_MAIN.Size = New System.Drawing.Size(476, 519)
CONTAINER_MAIN.TabIndex = 0 CONTAINER_MAIN.TabIndex = 0
CONTAINER_MAIN.TopToolStripPanelVisible = False CONTAINER_MAIN.TopToolStripPanelVisible = False
' '
@@ -83,23 +82,27 @@ Namespace DownloadObjects
Me.DEF_GROUP.ColumnCount = 1 Me.DEF_GROUP.ColumnCount = 1
Me.DEF_GROUP.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) Me.DEF_GROUP.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.DEF_GROUP.Controls.Add(TP_MODE, 0, 0) Me.DEF_GROUP.Controls.Add(TP_MODE, 0, 0)
Me.DEF_GROUP.Controls.Add(Me.TXT_GROUPS, 0, 8) Me.DEF_GROUP.Controls.Add(Me.TXT_GROUPS, 0, 12)
Me.DEF_GROUP.Controls.Add(TP_NOTIFY, 0, 9) Me.DEF_GROUP.Controls.Add(TP_NOTIFY, 0, 13)
Me.DEF_GROUP.Controls.Add(Me.TXT_TIMER, 0, 11) Me.DEF_GROUP.Controls.Add(Me.TXT_TIMER, 0, 15)
Me.DEF_GROUP.Controls.Add(Me.NUM_DELAY, 0, 12) Me.DEF_GROUP.Controls.Add(Me.NUM_DELAY, 0, 16)
Me.DEF_GROUP.Controls.Add(Me.LBL_LAST_TIME_UP, 0, 13) Me.DEF_GROUP.Controls.Add(Me.LBL_LAST_TIME_UP, 0, 17)
Me.DEF_GROUP.Controls.Add(Me.CH_MANUAL, 0, 10) Me.DEF_GROUP.Controls.Add(Me.CH_MANUAL, 0, 14)
Me.DEF_GROUP.Dock = System.Windows.Forms.DockStyle.Fill Me.DEF_GROUP.Dock = System.Windows.Forms.DockStyle.Fill
Me.DEF_GROUP.Location = New System.Drawing.Point(0, 0) Me.DEF_GROUP.Location = New System.Drawing.Point(0, 0)
Me.DEF_GROUP.Name = "DEF_GROUP" Me.DEF_GROUP.Name = "DEF_GROUP"
Me.DEF_GROUP.RowCount = 15 Me.DEF_GROUP.RowCount = 19
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
@@ -108,23 +111,21 @@ Namespace DownloadObjects
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!)) Me.DEF_GROUP.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
Me.DEF_GROUP.Size = New System.Drawing.Size(476, 388) Me.DEF_GROUP.Size = New System.Drawing.Size(476, 519)
Me.DEF_GROUP.TabIndex = 0 Me.DEF_GROUP.TabIndex = 0
' '
'TP_MODE 'TP_MODE
' '
TP_MODE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single] TP_MODE.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
TP_MODE.ColumnCount = 5 TP_MODE.ColumnCount = 3
TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 33.33333!))
TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 20.0!)) TP_MODE.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
TP_MODE.Controls.Add(Me.OPT_ALL, 1, 0) TP_MODE.Controls.Add(Me.OPT_SPEC, 1, 0)
TP_MODE.Controls.Add(Me.OPT_DEFAULT, 2, 0)
TP_MODE.Controls.Add(Me.OPT_SPEC, 3, 0)
TP_MODE.Controls.Add(Me.OPT_DISABLED, 0, 0) TP_MODE.Controls.Add(Me.OPT_DISABLED, 0, 0)
TP_MODE.Controls.Add(Me.OPT_GROUP, 4, 0) TP_MODE.Controls.Add(Me.OPT_GROUP, 2, 0)
TP_MODE.Dock = System.Windows.Forms.DockStyle.Fill TP_MODE.Dock = System.Windows.Forms.DockStyle.Fill
TP_MODE.Location = New System.Drawing.Point(1, 1) TP_MODE.Location = New System.Drawing.Point(1, 1)
TP_MODE.Margin = New System.Windows.Forms.Padding(0) TP_MODE.Margin = New System.Windows.Forms.Padding(0)
@@ -134,39 +135,13 @@ Namespace DownloadObjects
TP_MODE.Size = New System.Drawing.Size(474, 25) TP_MODE.Size = New System.Drawing.Size(474, 25)
TP_MODE.TabIndex = 0 TP_MODE.TabIndex = 0
' '
'OPT_ALL
'
Me.OPT_ALL.AutoSize = True
Me.OPT_ALL.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_ALL.Location = New System.Drawing.Point(98, 4)
Me.OPT_ALL.Name = "OPT_ALL"
Me.OPT_ALL.Size = New System.Drawing.Size(87, 17)
Me.OPT_ALL.TabIndex = 1
Me.OPT_ALL.TabStop = True
Me.OPT_ALL.Text = "ALL"
TT_MAIN.SetToolTip(Me.OPT_ALL, "Download all users")
Me.OPT_ALL.UseVisualStyleBackColor = True
'
'OPT_DEFAULT
'
Me.OPT_DEFAULT.AutoSize = True
Me.OPT_DEFAULT.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_DEFAULT.Location = New System.Drawing.Point(192, 4)
Me.OPT_DEFAULT.Name = "OPT_DEFAULT"
Me.OPT_DEFAULT.Size = New System.Drawing.Size(87, 17)
Me.OPT_DEFAULT.TabIndex = 2
Me.OPT_DEFAULT.TabStop = True
Me.OPT_DEFAULT.Text = "Default"
TT_MAIN.SetToolTip(Me.OPT_DEFAULT, "All users marked ""Ready for download""")
Me.OPT_DEFAULT.UseVisualStyleBackColor = True
'
'OPT_SPEC 'OPT_SPEC
' '
Me.OPT_SPEC.AutoSize = True Me.OPT_SPEC.AutoSize = True
Me.OPT_SPEC.Dock = System.Windows.Forms.DockStyle.Fill Me.OPT_SPEC.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_SPEC.Location = New System.Drawing.Point(286, 4) Me.OPT_SPEC.Location = New System.Drawing.Point(161, 4)
Me.OPT_SPEC.Name = "OPT_SPEC" Me.OPT_SPEC.Name = "OPT_SPEC"
Me.OPT_SPEC.Size = New System.Drawing.Size(87, 17) Me.OPT_SPEC.Size = New System.Drawing.Size(150, 17)
Me.OPT_SPEC.TabIndex = 3 Me.OPT_SPEC.TabIndex = 3
Me.OPT_SPEC.TabStop = True Me.OPT_SPEC.TabStop = True
Me.OPT_SPEC.Text = "Specified" Me.OPT_SPEC.Text = "Specified"
@@ -179,7 +154,7 @@ Namespace DownloadObjects
Me.OPT_DISABLED.Dock = System.Windows.Forms.DockStyle.Fill Me.OPT_DISABLED.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_DISABLED.Location = New System.Drawing.Point(4, 4) Me.OPT_DISABLED.Location = New System.Drawing.Point(4, 4)
Me.OPT_DISABLED.Name = "OPT_DISABLED" Me.OPT_DISABLED.Name = "OPT_DISABLED"
Me.OPT_DISABLED.Size = New System.Drawing.Size(87, 17) Me.OPT_DISABLED.Size = New System.Drawing.Size(150, 17)
Me.OPT_DISABLED.TabIndex = 0 Me.OPT_DISABLED.TabIndex = 0
Me.OPT_DISABLED.TabStop = True Me.OPT_DISABLED.TabStop = True
Me.OPT_DISABLED.Text = "Disabled" Me.OPT_DISABLED.Text = "Disabled"
@@ -190,9 +165,9 @@ Namespace DownloadObjects
' '
Me.OPT_GROUP.AutoSize = True Me.OPT_GROUP.AutoSize = True
Me.OPT_GROUP.Dock = System.Windows.Forms.DockStyle.Fill Me.OPT_GROUP.Dock = System.Windows.Forms.DockStyle.Fill
Me.OPT_GROUP.Location = New System.Drawing.Point(380, 4) Me.OPT_GROUP.Location = New System.Drawing.Point(318, 4)
Me.OPT_GROUP.Name = "OPT_GROUP" Me.OPT_GROUP.Name = "OPT_GROUP"
Me.OPT_GROUP.Size = New System.Drawing.Size(90, 17) Me.OPT_GROUP.Size = New System.Drawing.Size(152, 17)
Me.OPT_GROUP.TabIndex = 4 Me.OPT_GROUP.TabIndex = 4
Me.OPT_GROUP.TabStop = True Me.OPT_GROUP.TabStop = True
Me.OPT_GROUP.Text = "Groups" Me.OPT_GROUP.Text = "Groups"
@@ -201,16 +176,21 @@ Namespace DownloadObjects
' '
'TXT_GROUPS 'TXT_GROUPS
' '
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image) ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
ActionButton5.Name = "Edit" ActionButton1.Name = "Edit"
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image) ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
ActionButton6.Name = "Clear" ActionButton2.Name = "Info"
Me.TXT_GROUPS.Buttons.Add(ActionButton5) ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Info
Me.TXT_GROUPS.Buttons.Add(ActionButton6) ActionButton2.ToolTipText = "Open group"
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
ActionButton3.Name = "Clear"
Me.TXT_GROUPS.Buttons.Add(ActionButton1)
Me.TXT_GROUPS.Buttons.Add(ActionButton2)
Me.TXT_GROUPS.Buttons.Add(ActionButton3)
Me.TXT_GROUPS.CaptionText = "Groups" Me.TXT_GROUPS.CaptionText = "Groups"
Me.TXT_GROUPS.CaptionWidth = 50.0R Me.TXT_GROUPS.CaptionWidth = 50.0R
Me.TXT_GROUPS.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_GROUPS.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_GROUPS.Location = New System.Drawing.Point(4, 224) Me.TXT_GROUPS.Location = New System.Drawing.Point(4, 331)
Me.TXT_GROUPS.Name = "TXT_GROUPS" Me.TXT_GROUPS.Name = "TXT_GROUPS"
Me.TXT_GROUPS.Size = New System.Drawing.Size(468, 22) Me.TXT_GROUPS.Size = New System.Drawing.Size(468, 22)
Me.TXT_GROUPS.TabIndex = 1 Me.TXT_GROUPS.TabIndex = 1
@@ -228,7 +208,7 @@ Namespace DownloadObjects
TP_NOTIFY.Controls.Add(Me.CH_SHOW_PIC_USER, 3, 0) TP_NOTIFY.Controls.Add(Me.CH_SHOW_PIC_USER, 3, 0)
TP_NOTIFY.Controls.Add(Me.CH_NOTIFY_SIMPLE, 1, 0) TP_NOTIFY.Controls.Add(Me.CH_NOTIFY_SIMPLE, 1, 0)
TP_NOTIFY.Dock = System.Windows.Forms.DockStyle.Fill TP_NOTIFY.Dock = System.Windows.Forms.DockStyle.Fill
TP_NOTIFY.Location = New System.Drawing.Point(1, 250) TP_NOTIFY.Location = New System.Drawing.Point(1, 357)
TP_NOTIFY.Margin = New System.Windows.Forms.Padding(0) TP_NOTIFY.Margin = New System.Windows.Forms.Padding(0)
TP_NOTIFY.Name = "TP_NOTIFY" TP_NOTIFY.Name = "TP_NOTIFY"
TP_NOTIFY.RowCount = 1 TP_NOTIFY.RowCount = 1
@@ -286,24 +266,24 @@ Namespace DownloadObjects
' '
'TXT_TIMER 'TXT_TIMER
' '
ActionButton7.BackgroundImage = CType(resources.GetObject("ActionButton7.BackgroundImage"), System.Drawing.Image) ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
ActionButton7.Name = "Refresh" ActionButton4.Name = "Refresh"
Me.TXT_TIMER.Buttons.Add(ActionButton7) Me.TXT_TIMER.Buttons.Add(ActionButton4)
Me.TXT_TIMER.CaptionText = "Timer" Me.TXT_TIMER.CaptionText = "Timer"
Me.TXT_TIMER.CaptionToolTipEnabled = True Me.TXT_TIMER.CaptionToolTipEnabled = True
Me.TXT_TIMER.CaptionToolTipText = "Timer (in minutes)" Me.TXT_TIMER.CaptionToolTipText = "Timer (in minutes)"
Me.TXT_TIMER.CaptionWidth = 50.0R Me.TXT_TIMER.CaptionWidth = 50.0R
Me.TXT_TIMER.Dock = System.Windows.Forms.DockStyle.Fill Me.TXT_TIMER.Dock = System.Windows.Forms.DockStyle.Fill
Me.TXT_TIMER.Location = New System.Drawing.Point(4, 308) Me.TXT_TIMER.Location = New System.Drawing.Point(4, 415)
Me.TXT_TIMER.Name = "TXT_TIMER" Me.TXT_TIMER.Name = "TXT_TIMER"
Me.TXT_TIMER.Size = New System.Drawing.Size(468, 22) Me.TXT_TIMER.Size = New System.Drawing.Size(468, 22)
Me.TXT_TIMER.TabIndex = 4 Me.TXT_TIMER.TabIndex = 4
' '
'NUM_DELAY 'NUM_DELAY
' '
ActionButton8.BackgroundImage = CType(resources.GetObject("ActionButton8.BackgroundImage"), System.Drawing.Image) ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
ActionButton8.Name = "Refresh" ActionButton5.Name = "Refresh"
Me.NUM_DELAY.Buttons.Add(ActionButton8) Me.NUM_DELAY.Buttons.Add(ActionButton5)
Me.NUM_DELAY.CaptionText = "Delay" Me.NUM_DELAY.CaptionText = "Delay"
Me.NUM_DELAY.CaptionToolTipEnabled = True Me.NUM_DELAY.CaptionToolTipEnabled = True
Me.NUM_DELAY.CaptionToolTipText = "Startup delay" Me.NUM_DELAY.CaptionToolTipText = "Startup delay"
@@ -311,7 +291,7 @@ Namespace DownloadObjects
Me.NUM_DELAY.ClearTextByButtonClear = False Me.NUM_DELAY.ClearTextByButtonClear = False
Me.NUM_DELAY.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown Me.NUM_DELAY.ControlMode = PersonalUtilities.Forms.Controls.TextBoxExtended.ControlModes.NumericUpDown
Me.NUM_DELAY.Dock = System.Windows.Forms.DockStyle.Fill Me.NUM_DELAY.Dock = System.Windows.Forms.DockStyle.Fill
Me.NUM_DELAY.Location = New System.Drawing.Point(4, 337) Me.NUM_DELAY.Location = New System.Drawing.Point(4, 444)
Me.NUM_DELAY.Name = "NUM_DELAY" Me.NUM_DELAY.Name = "NUM_DELAY"
Me.NUM_DELAY.NumberMaximum = New Decimal(New Integer() {1440, 0, 0, 0}) Me.NUM_DELAY.NumberMaximum = New Decimal(New Integer() {1440, 0, 0, 0})
Me.NUM_DELAY.NumberUpDownAlign = System.Windows.Forms.LeftRightAlignment.Left Me.NUM_DELAY.NumberUpDownAlign = System.Windows.Forms.LeftRightAlignment.Left
@@ -324,7 +304,7 @@ Namespace DownloadObjects
Me.LBL_LAST_TIME_UP.AutoSize = True Me.LBL_LAST_TIME_UP.AutoSize = True
Me.LBL_LAST_TIME_UP.Dock = System.Windows.Forms.DockStyle.Fill Me.LBL_LAST_TIME_UP.Dock = System.Windows.Forms.DockStyle.Fill
Me.LBL_LAST_TIME_UP.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, CType(204, Byte)) Me.LBL_LAST_TIME_UP.Font = New System.Drawing.Font("Microsoft Sans Serif", 8.25!, System.Drawing.FontStyle.Italic, System.Drawing.GraphicsUnit.Point, CType(204, Byte))
Me.LBL_LAST_TIME_UP.Location = New System.Drawing.Point(4, 363) Me.LBL_LAST_TIME_UP.Location = New System.Drawing.Point(4, 470)
Me.LBL_LAST_TIME_UP.Name = "LBL_LAST_TIME_UP" Me.LBL_LAST_TIME_UP.Name = "LBL_LAST_TIME_UP"
Me.LBL_LAST_TIME_UP.Size = New System.Drawing.Size(468, 25) Me.LBL_LAST_TIME_UP.Size = New System.Drawing.Size(468, 25)
Me.LBL_LAST_TIME_UP.TabIndex = 6 Me.LBL_LAST_TIME_UP.TabIndex = 6
@@ -335,7 +315,7 @@ Namespace DownloadObjects
' '
Me.CH_MANUAL.AutoSize = True Me.CH_MANUAL.AutoSize = True
Me.CH_MANUAL.Dock = System.Windows.Forms.DockStyle.Fill Me.CH_MANUAL.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_MANUAL.Location = New System.Drawing.Point(4, 282) Me.CH_MANUAL.Location = New System.Drawing.Point(4, 389)
Me.CH_MANUAL.Name = "CH_MANUAL" Me.CH_MANUAL.Name = "CH_MANUAL"
Me.CH_MANUAL.Size = New System.Drawing.Size(468, 19) Me.CH_MANUAL.Size = New System.Drawing.Size(468, 19)
Me.CH_MANUAL.TabIndex = 3 Me.CH_MANUAL.TabIndex = 3
@@ -348,15 +328,15 @@ Namespace DownloadObjects
' '
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(476, 413) Me.ClientSize = New System.Drawing.Size(476, 519)
Me.Controls.Add(CONTAINER_MAIN) Me.Controls.Add(CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = Global.SCrawler.My.Resources.Resources.ArrowDownIcon_Blue_24 Me.Icon = Global.SCrawler.My.Resources.Resources.ArrowDownIcon_Blue_24
Me.KeyPreview = True Me.KeyPreview = True
Me.MaximizeBox = False Me.MaximizeBox = False
Me.MaximumSize = New System.Drawing.Size(492, 452) Me.MaximumSize = New System.Drawing.Size(492, 558)
Me.MinimizeBox = False Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(492, 452) Me.MinimumSize = New System.Drawing.Size(492, 558)
Me.Name = "AutoDownloaderEditorForm" Me.Name = "AutoDownloaderEditorForm"
Me.ShowInTaskbar = False Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
@@ -378,8 +358,6 @@ Namespace DownloadObjects
End Sub End Sub
Private WithEvents DEF_GROUP As DownloadObjects.Groups.GroupDefaults Private WithEvents DEF_GROUP As DownloadObjects.Groups.GroupDefaults
Private WithEvents TXT_GROUPS As PersonalUtilities.Forms.Controls.TextBoxExtended Private WithEvents TXT_GROUPS As PersonalUtilities.Forms.Controls.TextBoxExtended
Private WithEvents OPT_ALL As RadioButton
Private WithEvents OPT_DEFAULT As RadioButton
Private WithEvents OPT_SPEC As RadioButton Private WithEvents OPT_SPEC As RadioButton
Private WithEvents OPT_DISABLED As RadioButton Private WithEvents OPT_DISABLED As RadioButton
Private WithEvents CH_NOTIFY As CheckBox Private WithEvents CH_NOTIFY As CheckBox

View File

@@ -123,8 +123,14 @@
<metadata name="TP_MODE.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_MODE.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </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>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<data name="ActionButton5.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAACH
DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp DwAAjA8AAP1SAACBQAAAfXkAAOmLAAA85QAAGcxzPIV3AAAKOWlDQ1BQaG90b3Nob3AgSUNDIHByb2Zp
@@ -182,7 +188,19 @@
AAAASUVORK5CYII= AAAASUVORK5CYII=
</value> </value>
</data> </data>
<data name="ActionButton6.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> <data name="ActionButton2.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>
iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1
MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAFQSURBVFhH7ZfNDcIwFIMZoXcm
YBtGYRHECIgTR1ZhBsS9YoJgQ1Poi5sfqhIOWPqkqvV7dWlI0oVzriry5Dd5HSS0PFwasAEn0AJn4Dle
o6fpykaVHYDNwB7YG6ZgzWiQrABosAbqaXNh7bprN1AyAAp3b42msuva9ooGYIFpELA931D2FI+VxzAI
gTIdAEb+7KpBz+p4RclQyifoXwdKwgAwcMAl3/mEAOz9GJgokQGyR/sHr8CzlwFwgU+vCuagUQE4gSjz
HGxUAM5iyiyxUp4IJ5QEAYomHCvlidCiJAigjKNYKU8M6B/g9wJUH4TV/4ZFE5GV8kSQE1HRVGylPBHC
qbh0MbJSnhH0YtQFyFqOiZXyCOLLMQVDckNCrJRHEN+QeMGY3JJZKY8hb0vmxQLTYAplm1IvFNbblnuh
Qb0Pk3exGZjv06wW8uT3cIs7jQnSONrSxH0AAAAASUVORK5CYII=
</value>
</data>
<data name="ActionButton3.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value> <value>
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -193,53 +211,41 @@
<metadata name="TP_NOTIFY.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <metadata name="TP_NOTIFY.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</metadata> </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==
</value>
</data>
<data name="ActionButton8.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>
<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>
<data name="CH_NOTIFY_SIMPLE.ToolTip" xml:space="preserve"> <data name="CH_NOTIFY_SIMPLE.ToolTip" xml:space="preserve">
<value>Show a simple notification instead of a user notification. <value>Show a simple notification instead of a user notification.
This means that if any user data has been downloaded with the plan, a simple notification will be shown with the number of users downloaded. This means that if any user data has been downloaded with the plan, a simple notification will be shown with the number of users downloaded.
The 'Image' and 'User icon' parameters will be ignored.</value> The 'Image' and 'User icon' parameters will be ignored.</value>
</data> </data>
<data name="ActionButton4.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="ActionButton5.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>
</root> </root>

View File

@@ -6,6 +6,7 @@
' '
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports SCrawler.DownloadObjects.Groups
Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms
Imports PersonalUtilities.Forms.Controls.Base Imports PersonalUtilities.Forms.Controls.Base
Imports DModes = SCrawler.DownloadObjects.AutoDownloader.Modes Imports DModes = SCrawler.DownloadObjects.AutoDownloader.Modes
@@ -46,11 +47,14 @@ Namespace DownloadObjects
With Plan With Plan
Select Case .Mode Select Case .Mode
Case DModes.None : OPT_DISABLED.Checked = True Case DModes.None : OPT_DISABLED.Checked = True
Case DModes.All : OPT_ALL.Checked = True
Case DModes.Default : OPT_DEFAULT.Checked = True
Case DModes.Specified : OPT_SPEC.Checked = True Case DModes.Specified : OPT_SPEC.Checked = True
Case DModes.Groups : OPT_GROUP.Checked = True Case DModes.Groups : OPT_GROUP.Checked = True
End Select End Select
TXT_GROUPS.CaptionWidth = GroupDefaults.CaptionWidthDefault
TXT_TIMER.CaptionWidth = GroupDefaults.CaptionWidthDefault
NUM_DELAY.CaptionWidth = GroupDefaults.CaptionWidthDefault
DEF_GROUP.Set(Plan) DEF_GROUP.Set(Plan)
If MyGroups.Count > 0 Then TXT_GROUPS.Text = MyGroups.ListToString If MyGroups.Count > 0 Then TXT_GROUPS.Text = MyGroups.ListToString
If Settings.Groups.Count = 0 Then TXT_GROUPS.Clear() : TXT_GROUPS.Enabled = False If Settings.Groups.Count = 0 Then TXT_GROUPS.Clear() : TXT_GROUPS.Enabled = False
@@ -67,7 +71,7 @@ Namespace DownloadObjects
.MyFieldsChecker = New FieldsChecker .MyFieldsChecker = New FieldsChecker
With .MyFieldsCheckerE With .MyFieldsCheckerE
.AddControl(Of String)(DEF_GROUP.TXT_NAME, DEF_GROUP.TXT_NAME.CaptionText,, .AddControl(Of String)(DEF_GROUP.TXT_NAME, DEF_GROUP.TXT_NAME.CaptionText,,
New Groups.GroupEditorForm.NameChecker(Plan.Name, Settings.Automation, "Plan")) New GroupEditorForm.NameChecker(Plan.Name, Settings.Automation, "Plan"))
.AddControl(Of Integer)(TXT_TIMER, TXT_TIMER.CaptionText,, New AutomationTimerChecker) .AddControl(Of Integer)(TXT_TIMER, TXT_TIMER.CaptionText,, New AutomationTimerChecker)
.EndLoaderOperations() .EndLoaderOperations()
End With End With
@@ -78,13 +82,36 @@ Namespace DownloadObjects
Private Sub AutoDownloaderEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed Private Sub AutoDownloaderEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
MyGroups.Clear() MyGroups.Clear()
End Sub End Sub
Private Sub AutoDownloaderEditorForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
Try
If e = ShowUsersButtonKey AndAlso Not OPT_DISABLED.Checked Then
Dim users As New List(Of API.Base.IUserData)
If OPT_GROUP.Checked Then
If MyGroups.Count > 0 Then
Dim i%
For Each groupName$ In MyGroups
i = Settings.Groups.IndexOf(groupName)
If i >= 0 Then users.ListAddList(DownloadGroup.GetUsers(Settings.Groups(i)), LAP.NotContainsOnly, LAP.IgnoreICopier)
Next
End If
Else
Using g As New GroupParameters
DEF_GROUP.Get(g)
users.ListAddList(DownloadGroup.GetUsers(g))
End Using
End If
GroupUsersViewer.Show(users, $"S {DEF_GROUP.TXT_NAME.Text}")
users.Clear()
End If
Catch ex As Exception
ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Show plan users")
End Try
End Sub
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
If MyDefs.MyFieldsChecker.AllParamsOK Then If MyDefs.MyFieldsChecker.AllParamsOK Then
With Plan With Plan
Select Case True Select Case True
Case OPT_DISABLED.Checked : .Mode = DModes.None Case OPT_DISABLED.Checked : .Mode = DModes.None
Case OPT_ALL.Checked : .Mode = DModes.All
Case OPT_DEFAULT.Checked : .Mode = DModes.Default
Case OPT_SPEC.Checked : .Mode = DModes.Specified Case OPT_SPEC.Checked : .Mode = DModes.Specified
Case OPT_GROUP.Checked : .Mode = DModes.Groups Case OPT_GROUP.Checked : .Mode = DModes.Groups
End Select End Select
@@ -107,17 +134,32 @@ Namespace DownloadObjects
Private Sub TXT_GROUPS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_GROUPS.ActionOnButtonClick Private Sub TXT_GROUPS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_GROUPS.ActionOnButtonClick
Select Case Sender.DefaultButton Select Case Sender.DefaultButton
Case ActionButton.DefaultButtons.Edit Case ActionButton.DefaultButtons.Edit
Using f As New LabelsForm(MyGroups, Settings.Groups.Select(Function(g) g.Name)) With {.Text = "Groups", .Icon = My.Resources.GroupByIcon_16} Using f As New LabelsForm(MyGroups, (From g As DownloadGroup In Settings.Groups Where Not g.IsViewFilter Select g.Name)) With {
.Text = "Groups (F3 to edit)",
.Icon = My.Resources.GroupByIcon_16,
.IsGroups = True
}
f.ShowDialog() f.ShowDialog()
If f.DialogResult = DialogResult.OK Then MyGroups.ListAddList(f.LabelsList, LAP.ClearBeforeAdd) : TXT_GROUPS.Text = MyGroups.ListToString If f.DialogResult = DialogResult.OK Then MyGroups.ListAddList(f.LabelsList, LAP.ClearBeforeAdd) : TXT_GROUPS.Text = MyGroups.ListToString
End Using End Using
Case ActionButton.DefaultButtons.Clear : MyGroups.Clear() Case ActionButton.DefaultButtons.Clear : MyGroups.Clear()
Case ActionButton.DefaultButtons.Info
Try
If MyGroups.Count > 0 Then
Dim i% = Settings.Groups.IndexOf(MyGroups(0))
If i >= 0 Then
Using gf As New GroupEditorForm(Settings.Groups(i)) : gf.ShowDialog() : End Using
End If
End If
Catch ex As Exception
ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Show group")
End Try
End Select End Select
End Sub End Sub
Private Sub ChangeEnabled() Handles OPT_DISABLED.CheckedChanged, OPT_ALL.CheckedChanged, OPT_DEFAULT.CheckedChanged, Private Sub ChangeEnabled() Handles OPT_DISABLED.CheckedChanged,
OPT_SPEC.CheckedChanged, OPT_GROUP.CheckedChanged, OPT_SPEC.CheckedChanged, OPT_GROUP.CheckedChanged,
CH_NOTIFY.CheckedChanged, CH_NOTIFY_SIMPLE.CheckedChanged CH_NOTIFY.CheckedChanged, CH_NOTIFY_SIMPLE.CheckedChanged
DEF_GROUP.Enabled(OPT_ALL.Checked Or OPT_DEFAULT.Checked Or OPT_SPEC.Checked, OPT_ALL.Checked) = OPT_SPEC.Checked DEF_GROUP.Enabled = OPT_SPEC.Checked
TXT_GROUPS.Enabled = OPT_GROUP.Checked TXT_GROUPS.Enabled = OPT_GROUP.Checked
TXT_TIMER.Enabled = Not OPT_DISABLED.Checked TXT_TIMER.Enabled = Not OPT_DISABLED.Checked
NUM_DELAY.Enabled = Not OPT_DISABLED.Checked NUM_DELAY.Enabled = Not OPT_DISABLED.Checked

View File

@@ -8,6 +8,8 @@
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms
Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Forms.Controls.Base
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
Imports ECI = PersonalUtilities.Forms.Toolbars.EditToolbar.ControlItem Imports ECI = PersonalUtilities.Forms.Toolbars.EditToolbar.ControlItem
Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
@@ -131,7 +133,12 @@ Namespace DownloadObjects
End With End With
End Sub End Sub
Private Sub SchedulerEditorForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown Private Sub SchedulerEditorForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
If e.KeyCode = Keys.Escape Then Close() If e.KeyCode = Keys.Escape Then
Close()
ElseIf e = ShowUsersButtonKey Then
ShowPlanUsers()
e.Handled = True
End If
End Sub End Sub
Private Sub SchedulerEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed Private Sub SchedulerEditorForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
PauseArr.Dispose() PauseArr.Dispose()
@@ -230,6 +237,25 @@ Namespace DownloadObjects
Private Function GetSchedulerFiles() As List(Of SFile) Private Function GetSchedulerFiles() As List(Of SFile)
Return SFile.GetFiles(SettingsFolderName.CSFileP, $"{Scheduler.FileNameDefault}*.xml",, EDP.ReturnValue) Return SFile.GetFiles(SettingsFolderName.CSFileP, $"{Scheduler.FileNameDefault}*.xml",, EDP.ReturnValue)
End Function End Function
Private Class SchedulerList : Inherits SimpleListForm(Of String)
Friend Sub New(ByVal Source As IEnumerable(Of String), Optional ByRef DesignXML As EContainer = Nothing)
MyBase.New(Source, DesignXML)
End Sub
Protected Overrides Sub MyForm_Load(sender As Object, e As EventArgs)
MyBase.MyForm_Load(sender, e)
CMB_DATA.Button(ADB.Add).ToolTipText = "Create a new scheduler"
CMB_DATA.Button(ADB.SaveAs).ToolTipText = "Clone an existing scheduler and save it as a new one"
CMB_DATA.Button(ADB.Delete).ToolTipText = "Delete the selected scheduler"
CMB_DATA.Buttons.UpdateButtonsPositions()
End Sub
Protected Overrides Sub CMB_DATA_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs)
If e.DefaultButton = ADB.SaveAs Then
AddNewItem(e, e.Key, e.KeyEventArgs)
Else
MyBase.CMB_DATA_ActionOnButtonClick(Sender, e)
End If
End Sub
End Class
Private Sub BTT_SETTINGS_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS.Click Private Sub BTT_SETTINGS_Click(sender As Object, e As EventArgs) Handles BTT_SETTINGS.Click
Const msgTitle$ = "Change scheduler" Const msgTitle$ = "Change scheduler"
Try Try
@@ -239,9 +265,9 @@ Namespace DownloadObjects
If .ListExists Then .ForEach(Sub(ff) l.Add(ff, ff.Name.Replace(Scheduler.FileNameDefault, String.Empty).StringTrimStart("_").IfNullOrEmpty(defName))) If .ListExists Then .ForEach(Sub(ff) l.Add(ff, ff.Name.Replace(Scheduler.FileNameDefault, String.Empty).StringTrimStart("_").IfNullOrEmpty(defName)))
End With End With
If l.Count > 0 Then If l.Count > 0 Then
Using chooser As New SimpleListForm(Of String)(l.Values.Cast(Of String), Settings.Design) With { Using chooser As New SchedulerList(l.Values.Cast(Of String), Settings.Design) With {
.DesignXMLNodeName = "SchedulerChooserForm", .DesignXMLNodeName = "SchedulerChooserForm",
.Icon = PersonalUtilities.Tools.ImageRenderer.GetIcon(My.Resources.ScriptPic_32, EDP.ReturnValue), .Icon = ImageRenderer.GetIcon(My.Resources.ScriptPic_32, EDP.ReturnValue),
.FormText = "Schedulers", .FormText = "Schedulers",
.Mode = SimpleListFormModes.SelectedItems, .Mode = SimpleListFormModes.SelectedItems,
.MultiSelect = False .MultiSelect = False
@@ -251,17 +277,56 @@ Namespace DownloadObjects
Dim f As SFile Dim f As SFile
Dim selectedName$ Dim selectedName$
Dim addedObj$ = String.Empty Dim addedObj$ = String.Empty
Dim addedObjIsClone As Boolean = False
Dim createSchedulerPath As Func(Of String, SFile) = Function(n) $"{SettingsFolderName}\{Scheduler.FileNameDefault}_{n.StringRemoveWinForbiddenSymbols}.xml"
.ClearButtons() .ClearButtons()
.Buttons = {ADB.Add, ADB.Delete} .Buttons = {ADB.Add, ADB.SaveAs, ADB.Delete}
AddHandler .AddClick, Sub(ByVal obj As Object, ByVal args As SimpleListFormEventArgs) AddHandler .AddClick, Sub(ByVal obj As Object, ByVal args As SimpleListFormEventArgs)
If addedObj.IsEmptyString Then If addedObj.IsEmptyString Then
addedObj = InputBoxE("Enter a new scheduler name:", msgTitle) addedObj = InputBoxE("Enter a new scheduler name:", msgTitle)
args.Result = Not addedObj.IsEmptyString args.Result = Not addedObj.IsEmptyString
If args.Result Then args.Item = addedObj If args.Result Then
If l.Values.Count > 0 AndAlso l.Values.ListIndexOf(Function(n) n.StringToLower = addedObj.StringToLower) >= 0 Then
args.Result = False
MsgBoxE({$"A scheduler named '{addedObj}' already exists", msgTitle}, vbCritical)
Else
args.Item = addedObj
addedObjIsClone = Not args.ButtonEventArgs Is Nothing AndAlso
TypeOf args.ButtonEventArgs Is ActionButtonEventArgs AndAlso
DirectCast(args.ButtonEventArgs, ActionButtonEventArgs).DefaultButton = ADB.SaveAs
If addedObjIsClone Then
Dim cloneF As SFile = createSchedulerPath.Invoke(addedObj)
If Not cloneF.Exists And Settings.Automation.File.Exists Then
Using x As New XmlFile(Settings.Automation.File, Protector.Modes.All, False) With {.AllowSameNames = True, .XmlReadOnly = True}
x.LoadData()
x.Save(cloneF, EDP.SendToLog)
End Using
End If
End If
End If
End If
Else Else
MsgBoxE({"You can only create one scheduler at a time", "Create a new scheduler"}, vbCritical) MsgBoxE({"You can only create one scheduler at a time", "Create a new scheduler"}, vbCritical)
End If End If
End Sub End Sub
AddHandler .DeleteClick, Sub(ByVal obj As Object, ByVal args As SimpleListFormEventArgs)
Dim n$ = AConvert(Of String)(args.Item, String.Empty)
If Not n.IsEmptyString Then
If MsgBoxE({$"Are you sure you want to delete the '{n}' scheduler?", msgTitle}, vbExclamation,,,
{"Process", "Cancel"}) = 0 Then
Dim delF As SFile = createSchedulerPath.Invoke(n)
If delF.Exists AndAlso delF.Delete Then
args.Result = True
If l.ContainsKey(delF) Then
l.Remove(delF)
Else
Dim delIndx% = l.ListIndexOf(Function(dd) dd.Value = n)
If delIndx >= 0 Then l.Remove(l.Keys(delIndx))
End If
End If
End If
End If
End Sub
If Settings.Automation.File.Name = Scheduler.FileNameDefault Then If Settings.Automation.File.Name = Scheduler.FileNameDefault Then
.DataSelectedIndexes.Add(0) .DataSelectedIndexes.Add(0)
Else Else
@@ -274,7 +339,7 @@ Namespace DownloadObjects
If selectedName = defName Then If selectedName = defName Then
f = Settings.Automation.FileDefault f = Settings.Automation.FileDefault
Else Else
f = $"{SettingsFolderName}\{Scheduler.FileNameDefault}_{selectedName.StringRemoveWinForbiddenSymbols}.xml" f = createSchedulerPath.Invoke(selectedName)
End If End If
If Not Settings.Automation.File = f AndAlso Settings.Automation.Reset(f, False) Then If Not Settings.Automation.File = f AndAlso Settings.Automation.Reset(f, False) Then
Settings.Automation.File = f Settings.Automation.File = f
@@ -356,6 +421,34 @@ Namespace DownloadObjects
Refill() Refill()
End If End If
End Sub End Sub
#End Region
#Region "ShowPlanUsers"
Private Sub ShowPlanUsers()
Try
If _LatestSelected.ValueBetween(0, Settings.Automation.Count - 1) Then
With Settings.Automation(_LatestSelected)
Dim users As New List(Of API.Base.IUserData)
If Not .Mode = AutoDownloader.Modes.None Then
If .Mode = AutoDownloader.Modes.Groups Then
If .Groups.Count > 0 Then
Dim i%
For Each groupName$ In .Groups
i = Settings.Groups.IndexOf(groupName)
If i >= 0 Then users.ListAddList(Groups.DownloadGroup.GetUsers(Settings.Groups(i)), LAP.NotContainsOnly, LAP.IgnoreICopier)
Next
End If
Else
users.ListAddList(Groups.DownloadGroup.GetUsers(.Self))
End If
End If
Groups.GroupUsersViewer.Show(users, $"S { .Name}")
users.Clear()
End With
End If
Catch ex As Exception
ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Show plan users")
End Try
End Sub
#End Region #End Region
End Class End Class
End Namespace End Namespace

View File

@@ -28,7 +28,7 @@ Namespace DownloadObjects
End Enum End Enum
Friend Property ViewMode As ViewModes Friend Property ViewMode As ViewModes
Get Get
Return IIf(MENU_VIEW_ALL.Checked, ViewModes.All, ViewModes.Session) Return IIf(ControlInvokeFast(ToolbarTOP, MENU_VIEW_ALL, Function() MENU_VIEW_ALL.Checked, True, EDP.ReturnValue), ViewModes.All, ViewModes.Session)
End Get End Get
Set(ByVal SMode As ViewModes) Set(ByVal SMode As ViewModes)
Settings.InfoViewMode.Value = CInt(SMode) Settings.InfoViewMode.Value = CInt(SMode)
@@ -57,7 +57,7 @@ Namespace DownloadObjects
InitializeComponent() InitializeComponent()
_UsersListSession = New List(Of IUserData) _UsersListSession = New List(Of IUserData)
_UsersListAll = New List(Of IUserData) _UsersListAll = New List(Of IUserData)
If Settings.InfoViewMode.Value = CInt(ViewModes.All) Then If Settings.InfoViewMode.Value = ViewModes.All Then
MENU_VIEW_SESSION.Checked = False MENU_VIEW_SESSION.Checked = False
MENU_VIEW_ALL.Checked = True MENU_VIEW_ALL.Checked = True
Else Else
@@ -66,8 +66,6 @@ Namespace DownloadObjects
End If End If
OPT_DEFAULT.Checked = Settings.InfoViewDefault OPT_DEFAULT.Checked = Settings.InfoViewDefault
OPT_SUBSCRIPTIONS.Checked = Not Settings.InfoViewDefault OPT_SUBSCRIPTIONS.Checked = Not Settings.InfoViewDefault
Settings.InfoViewMode.Value = ViewMode
RefillList()
End Sub End Sub
#End Region #End Region
#Region "Form handlers" #Region "Form handlers"
@@ -78,11 +76,11 @@ Namespace DownloadObjects
MyView.Import() MyView.Import()
MyView.SetFormSize() MyView.SetFormSize()
End If End If
BTT_CLEAR.Visible = ViewMode = ViewModes.Session ControlInvokeFast(ToolbarTOP, BTT_CLEAR, Sub() BTT_CLEAR.Visible = ViewMode = ViewModes.Session, EDP.None)
RefillList()
Catch Catch
Finally Finally
Opened = True Opened = True
RefillList()
End Try End Try
End Sub End Sub
Private Sub DownloadedInfoForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing Private Sub DownloadedInfoForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
@@ -116,9 +114,9 @@ Namespace DownloadObjects
End Function End Function
End Class End Class
Private Sub RefillList() Handles BTT_REFRESH.Click Private Sub RefillList() Handles BTT_REFRESH.Click
If Opened Then
Try Try
Dim lClear As Action = Sub() LIST_DOWN.Items.Clear() ControlInvokeFast(LIST_DOWN, Sub() LIST_DOWN.Items.Clear())
If LIST_DOWN.InvokeRequired Then LIST_DOWN.Invoke(lClear) Else lClear.Invoke
If ViewMode = ViewModes.Session Then If ViewMode = ViewModes.Session Then
With Downloader.Downloaded With Downloader.Downloaded
If .Count > 0 Then If .Count > 0 Then
@@ -137,21 +135,19 @@ Namespace DownloadObjects
If _UsersListAll.Count > 0 Then _UsersListAll.Sort(New UsersDateOrder) If _UsersListAll.Count > 0 Then _UsersListAll.Sort(New UsersDateOrder)
End If End If
Dim isDefault As Boolean = OPT_DEFAULT.Checked Dim isDefault As Boolean = OPT_DEFAULT.Checked
If Current.Count > 0 Then Current.RemoveAll(Function(u) u.IsSubscription = isDefault) If Current.Count > 0 Then Current.RemoveAll(Function(u) u Is Nothing OrElse u.IsSubscription = isDefault)
If Current.Count > 0 Then If Current.Count > 0 Then
ControlInvokeFast(LIST_DOWN,
Sub()
For Each user As IUserData In Current For Each user As IUserData In Current
If LIST_DOWN.InvokeRequired Then
LIST_DOWN.Invoke(Sub() LIST_DOWN.Items.Add(user.DownloadedInformation))
Else
LIST_DOWN.Items.Add(user.DownloadedInformation) LIST_DOWN.Items.Add(user.DownloadedInformation)
End If
Next Next
If _LatestSelected.ValueBetween(0, LIST_DOWN.Items.Count - 1) Then If _LatestSelected.ValueBetween(0, LIST_DOWN.Items.Count - 1) Then
Dim aSel As Action = Sub() LIST_DOWN.SelectedIndex = _LatestSelected LIST_DOWN.SelectedIndex = _LatestSelected
If LIST_DOWN.InvokeRequired Then LIST_DOWN.Invoke(aSel) Else aSel.Invoke
Else Else
_LatestSelected = -1 _LatestSelected = -1
End If End If
End Sub)
Else Else
_LatestSelected = -1 _LatestSelected = -1
End If End If
@@ -161,6 +157,7 @@ Namespace DownloadObjects
Finally Finally
UpdateNavigationButtons(Nothing) UpdateNavigationButtons(Nothing)
End Try End Try
End If
End Sub End Sub
#End Region #End Region
#Region "Toolbar controls" #Region "Toolbar controls"
@@ -251,7 +248,7 @@ Namespace DownloadObjects
#End Region #End Region
#Region "Downloader handlers" #Region "Downloader handlers"
Friend Sub Downloader_DownloadCountChange() Friend Sub Downloader_DownloadCountChange()
If ViewMode = ViewModes.Session Then RefillList() If Opened AndAlso ViewMode = ViewModes.Session Then RefillList()
End Sub End Sub
#End Region #End Region
End Class End Class

View File

@@ -34,6 +34,7 @@ Namespace DownloadObjects
Dim MENU_LOAD_SEP_6 As System.Windows.Forms.ToolStripSeparator Dim MENU_LOAD_SEP_6 As System.Windows.Forms.ToolStripSeparator
Dim MENU_LOAD_SEP_7 As System.Windows.Forms.ToolStripSeparator Dim MENU_LOAD_SEP_7 As System.Windows.Forms.ToolStripSeparator
Dim MENU_LOAD_SEP_0 As System.Windows.Forms.ToolStripSeparator Dim MENU_LOAD_SEP_0 As System.Windows.Forms.ToolStripSeparator
Dim MENU_LOAD_SEP_8 As System.Windows.Forms.ToolStripSeparator
Me.OPT_DEFAULT = New System.Windows.Forms.ToolStripMenuItem() Me.OPT_DEFAULT = New System.Windows.Forms.ToolStripMenuItem()
Me.OPT_SUBSCRIPTIONS = New System.Windows.Forms.ToolStripMenuItem() Me.OPT_SUBSCRIPTIONS = New System.Windows.Forms.ToolStripMenuItem()
Me.ToolbarTOP = New System.Windows.Forms.ToolStrip() Me.ToolbarTOP = New System.Windows.Forms.ToolStrip()
@@ -67,6 +68,8 @@ Namespace DownloadObjects
Me.BTT_DOWN_SELECTED = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_DOWN_SELECTED = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton() Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton()
Me.TP_DATA = New System.Windows.Forms.TableLayoutPanel() Me.TP_DATA = New System.Windows.Forms.TableLayoutPanel()
Me.BTT_VIEW_SAVE = New System.Windows.Forms.ToolStripMenuItem()
Me.BTT_VIEW_LOAD = New System.Windows.Forms.ToolStripMenuItem()
SEP_1 = New System.Windows.Forms.ToolStripSeparator() SEP_1 = New System.Windows.Forms.ToolStripSeparator()
SEP_2 = New System.Windows.Forms.ToolStripSeparator() SEP_2 = New System.Windows.Forms.ToolStripSeparator()
MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton() MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton()
@@ -78,6 +81,7 @@ Namespace DownloadObjects
MENU_LOAD_SEP_6 = New System.Windows.Forms.ToolStripSeparator() MENU_LOAD_SEP_6 = New System.Windows.Forms.ToolStripSeparator()
MENU_LOAD_SEP_7 = New System.Windows.Forms.ToolStripSeparator() MENU_LOAD_SEP_7 = New System.Windows.Forms.ToolStripSeparator()
MENU_LOAD_SEP_0 = New System.Windows.Forms.ToolStripSeparator() MENU_LOAD_SEP_0 = New System.Windows.Forms.ToolStripSeparator()
MENU_LOAD_SEP_8 = New System.Windows.Forms.ToolStripSeparator()
Me.ToolbarTOP.SuspendLayout() Me.ToolbarTOP.SuspendLayout()
Me.SuspendLayout() Me.SuspendLayout()
' '
@@ -166,7 +170,7 @@ Namespace DownloadObjects
' '
Me.MENU_LOAD_SESSION.AutoToolTip = False Me.MENU_LOAD_SESSION.AutoToolTip = False
Me.MENU_LOAD_SESSION.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image Me.MENU_LOAD_SESSION.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image
Me.MENU_LOAD_SESSION.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_LOAD_SESSION_CURRENT, Me.BTT_LOAD_SESSION_LAST, Me.BTT_LOAD_SESSION_CHOOSE, MENU_LOAD_SEP_0, Me.BTT_COPY_TO, Me.BTT_MOVE_TO, MENU_LOAD_SEP_1, Me.BTT_LOAD_FAV, Me.BTT_LOAD_SPEC, MENU_LOAD_SEP_2, Me.BTT_FEED_ADD_FAV, Me.BTT_FEED_ADD_FAV_REMOVE, Me.BTT_FEED_REMOVE_FAV, MENU_LOAD_SEP_3, Me.BTT_FEED_ADD_SPEC, Me.BTT_FEED_ADD_SPEC_REMOVE, Me.BTT_FEED_REMOVE_SPEC, MENU_LOAD_SEP_4, Me.BTT_FEED_CLEAR_FAV, Me.BTT_FEED_CLEAR_SPEC, Me.BTT_FEED_DELETE_SPEC, Me.BTT_FEED_DELETE_DAILY_LIST, Me.BTT_FEED_DELETE_DAILY_DATE, MENU_LOAD_SEP_5, Me.BTT_MERGE_SESSIONS, Me.BTT_CLEAR_DAILY, MENU_LOAD_SEP_6, Me.BTT_MERGE_FEEDS, MENU_LOAD_SEP_7, Me.BTT_CHECK_ALL, Me.BTT_CHECK_NONE}) Me.MENU_LOAD_SESSION.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_LOAD_SESSION_CURRENT, Me.BTT_LOAD_SESSION_LAST, Me.BTT_LOAD_SESSION_CHOOSE, MENU_LOAD_SEP_0, Me.BTT_COPY_TO, Me.BTT_MOVE_TO, MENU_LOAD_SEP_1, Me.BTT_LOAD_FAV, Me.BTT_LOAD_SPEC, MENU_LOAD_SEP_2, Me.BTT_FEED_ADD_FAV, Me.BTT_FEED_ADD_FAV_REMOVE, Me.BTT_FEED_REMOVE_FAV, MENU_LOAD_SEP_3, Me.BTT_FEED_ADD_SPEC, Me.BTT_FEED_ADD_SPEC_REMOVE, Me.BTT_FEED_REMOVE_SPEC, MENU_LOAD_SEP_4, Me.BTT_FEED_CLEAR_FAV, Me.BTT_FEED_CLEAR_SPEC, Me.BTT_FEED_DELETE_SPEC, Me.BTT_FEED_DELETE_DAILY_LIST, Me.BTT_FEED_DELETE_DAILY_DATE, MENU_LOAD_SEP_5, Me.BTT_MERGE_SESSIONS, Me.BTT_CLEAR_DAILY, MENU_LOAD_SEP_6, Me.BTT_MERGE_FEEDS, MENU_LOAD_SEP_7, Me.BTT_CHECK_ALL, Me.BTT_CHECK_NONE, MENU_LOAD_SEP_8, Me.BTT_VIEW_SAVE, Me.BTT_VIEW_LOAD})
Me.MENU_LOAD_SESSION.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24 Me.MENU_LOAD_SESSION.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24
Me.MENU_LOAD_SESSION.ImageTransparentColor = System.Drawing.Color.Magenta Me.MENU_LOAD_SESSION.ImageTransparentColor = System.Drawing.Color.Magenta
Me.MENU_LOAD_SESSION.Name = "MENU_LOAD_SESSION" Me.MENU_LOAD_SESSION.Name = "MENU_LOAD_SESSION"
@@ -405,6 +409,25 @@ Namespace DownloadObjects
Me.TP_DATA.Size = New System.Drawing.Size(484, 436) Me.TP_DATA.Size = New System.Drawing.Size(484, 436)
Me.TP_DATA.TabIndex = 1 Me.TP_DATA.TabIndex = 1
' '
'MENU_LOAD_SEP_8
'
MENU_LOAD_SEP_8.Name = "MENU_LOAD_SEP_8"
MENU_LOAD_SEP_8.Size = New System.Drawing.Size(349, 6)
'
'BTT_VIEW_SAVE
'
Me.BTT_VIEW_SAVE.Name = "BTT_VIEW_SAVE"
Me.BTT_VIEW_SAVE.Size = New System.Drawing.Size(352, 22)
Me.BTT_VIEW_SAVE.Text = "Save current view"
'
'BTT_VIEW_LOAD
'
Me.BTT_VIEW_LOAD.AutoToolTip = True
Me.BTT_VIEW_LOAD.Name = "BTT_VIEW_LOAD"
Me.BTT_VIEW_LOAD.Size = New System.Drawing.Size(352, 22)
Me.BTT_VIEW_LOAD.Text = "Load view (from saved)"
Me.BTT_VIEW_LOAD.ToolTipText = "Load one of your previously saved views"
'
'DownloadFeedForm 'DownloadFeedForm
' '
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!) Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
@@ -458,5 +481,7 @@ Namespace DownloadObjects
Private WithEvents BTT_CHECK_NONE As ToolStripMenuItem Private WithEvents BTT_CHECK_NONE As ToolStripMenuItem
Private WithEvents BTT_COPY_TO As ToolStripMenuItem Private WithEvents BTT_COPY_TO As ToolStripMenuItem
Private WithEvents BTT_MOVE_TO As ToolStripMenuItem Private WithEvents BTT_MOVE_TO As ToolStripMenuItem
Private WithEvents BTT_VIEW_SAVE As ToolStripMenuItem
Private WithEvents BTT_VIEW_LOAD As ToolStripMenuItem
End Class End Class
End Namespace End Namespace

View File

@@ -162,4 +162,7 @@
<metadata name="ToolbarTOP.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <metadata name="ToolbarTOP.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<value>17, 17</value> <value>17, 17</value>
</metadata> </metadata>
<metadata name="MENU_LOAD_SEP_8.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root> </root>

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