diff --git a/Changelog.md b/Changelog.md
index 8f0cbfd..f87378f 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -1,3 +1,30 @@
+# 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*
@@ -5,13 +32,13 @@
- Added
- YouTube (standalone app): setting to remove specific characters (`Defaults` - `Remove characters`)
- Instagram: simplify the `Connection closed` error
- - Users search: add 'FriendlyName' to search results
+ - 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)
+ - Threads: unable to obtain credentials (`ID`)
# 2024.4.26.0
diff --git a/ProgramScreenshots/SettingsSiteFacebook.png b/ProgramScreenshots/SettingsSiteFacebook.png
index f21d083..a505f92 100644
Binary files a/ProgramScreenshots/SettingsSiteFacebook.png and b/ProgramScreenshots/SettingsSiteFacebook.png differ
diff --git a/ProgramScreenshots/SettingsSiteInstagram.png b/ProgramScreenshots/SettingsSiteInstagram.png
index ad9865e..241c61c 100644
Binary files a/ProgramScreenshots/SettingsSiteInstagram.png and b/ProgramScreenshots/SettingsSiteInstagram.png differ
diff --git a/ProgramScreenshots/SettingsSiteMastodon.png b/ProgramScreenshots/SettingsSiteMastodon.png
index 5c2b069..e23a83f 100644
Binary files a/ProgramScreenshots/SettingsSiteMastodon.png and b/ProgramScreenshots/SettingsSiteMastodon.png differ
diff --git a/ProgramScreenshots/SettingsSiteOnlyFans.png b/ProgramScreenshots/SettingsSiteOnlyFans.png
index 6cb5353..f4186d7 100644
Binary files a/ProgramScreenshots/SettingsSiteOnlyFans.png and b/ProgramScreenshots/SettingsSiteOnlyFans.png differ
diff --git a/ProgramScreenshots/SettingsSiteRedGifs.png b/ProgramScreenshots/SettingsSiteRedGifs.png
index ddccbdf..1620d8a 100644
Binary files a/ProgramScreenshots/SettingsSiteRedGifs.png and b/ProgramScreenshots/SettingsSiteRedGifs.png differ
diff --git a/ProgramScreenshots/SettingsSiteThreads.png b/ProgramScreenshots/SettingsSiteThreads.png
index 4040752..773bd60 100644
Binary files a/ProgramScreenshots/SettingsSiteThreads.png and b/ProgramScreenshots/SettingsSiteThreads.png differ
diff --git a/ProgramScreenshots/SettingsSiteTwitter.png b/ProgramScreenshots/SettingsSiteTwitter.png
index dd7b574..51aafd2 100644
Binary files a/ProgramScreenshots/SettingsSiteTwitter.png and b/ProgramScreenshots/SettingsSiteTwitter.png differ
diff --git a/SCrawler.PluginProvider/Attributes/Attributes.vb b/SCrawler.PluginProvider/Attributes/Attributes.vb
index e1b9aa2..1237982 100644
--- a/SCrawler.PluginProvider/Attributes/Attributes.vb
+++ b/SCrawler.PluginProvider/Attributes/Attributes.vb
@@ -36,8 +36,22 @@ Namespace Plugin.Attributes
Public Property IsInformationLabel As Boolean = False
''' Label text alignment.
Default:
Public Property LabelTextAlign As Drawing.ContentAlignment = Drawing.ContentAlignment.TopCenter
+ Private _IsAuth As Boolean = False
''' This is an authorization property
- 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
''' Initialize a new property option attribute
diff --git a/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb b/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb
index ba65151..7d96012 100644
--- a/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb
+++ b/SCrawler.PluginProvider/Interfaces/ISiteSettings.vb
@@ -19,6 +19,7 @@ Namespace Plugin
ReadOnly Property Site As String
Property CMDEncoding As String
Property EnvironmentPrograms As IEnumerable(Of String)
+ Property UserAgentDefault As String
Sub EnvironmentProgramsUpdated()
Property AccountName As String
Property Temporary As Boolean
diff --git a/SCrawler.PluginProvider/My Project/AssemblyInfo.vb b/SCrawler.PluginProvider/My Project/AssemblyInfo.vb
index 5c197ea..b98d3e2 100644
--- a/SCrawler.PluginProvider/My Project/AssemblyInfo.vb
+++ b/SCrawler.PluginProvider/My Project/AssemblyInfo.vb
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below:
'
-
-
+
+
diff --git a/SCrawler.YouTube/Base/YouTubeSettings.vb b/SCrawler.YouTube/Base/YouTubeSettings.vb
index 12504af..a70091d 100644
--- a/SCrawler.YouTube/Base/YouTubeSettings.vb
+++ b/SCrawler.YouTube/Base/YouTubeSettings.vb
@@ -361,6 +361,12 @@ Namespace API.YouTube.Base
Throw New NotImplementedException("'GetFormat' is not available in 'FpsFormatProvider'")
End Function
End Class
+
+ Public ReadOnly Property DefaultVideoHighlightFPS_H As XMLValue(Of Integer)
+
+ Public ReadOnly Property DefaultVideoHighlightFPS_L As XMLValue(Of Integer)
#End Region
#Region "Defaults Audio"
0 AndAlso m.FPS > MyYouTubeSettings.DefaultVideoHighlightFPS_H Then _
+ BackColor = MyColor.DeleteBack : ForeColor = MyColor.DeleteFore
+ If MyYouTubeSettings.DefaultVideoHighlightFPS_L > 0 AndAlso m.FPS < MyYouTubeSettings.DefaultVideoHighlightFPS_L Then _
+ BackColor = MyColor.UpdateBack : ForeColor = MyColor.UpdateFore
End If
Dim sv% = m.Size / 1024
diff --git a/SCrawler.YouTube/My Project/AssemblyInfo.vb b/SCrawler.YouTube/My Project/AssemblyInfo.vb
index 4acca89..8dce322 100644
--- a/SCrawler.YouTube/My Project/AssemblyInfo.vb
+++ b/SCrawler.YouTube/My Project/AssemblyInfo.vb
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below:
'
-
-
+
+
diff --git a/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb b/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb
index ef02e8f..f2c11fa 100644
--- a/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb
+++ b/SCrawler.YouTubeDownloader/My Project/AssemblyInfo.vb
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below:
'
-
-
+
+
diff --git a/SCrawler/API/Base/DeclaredNames.vb b/SCrawler/API/Base/DeclaredNames.vb
index 2076ab4..34af8da 100644
--- a/SCrawler/API/Base/DeclaredNames.vb
+++ b/SCrawler/API/Base/DeclaredNames.vb
@@ -11,6 +11,9 @@ Namespace API.Base
Friend Const Header_Authorization As String = "authorization"
Friend Const Header_CSRFToken As String = "x-csrf-token"
+ Friend Const CAT_UserDefs As String = "New user defaults"
+ Friend Const CAT_Timers As String = "Timers"
+
Friend Const ConcurrentDownloadsCaption As String = "Concurrent downloads"
Friend Const ConcurrentDownloadsToolTip As String = "The number of concurrent downloads."
Friend Const SavedPostsUserNameCaption As String = "Saved posts user"
diff --git a/SCrawler/API/Base/IUserData.vb b/SCrawler/API/Base/IUserData.vb
index 14077f7..758571b 100644
--- a/SCrawler/API/Base/IUserData.vb
+++ b/SCrawler/API/Base/IUserData.vb
@@ -77,7 +77,7 @@ Namespace API.Base
'''
Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer
Function EraseData(ByVal Mode As EraseMode) As Boolean
- Function MoveFiles(ByVal CollectionName As String, ByVal SpecialCollectionPath As SFile) As Boolean
+ Function MoveFiles(ByVal CollectionName As String, ByVal SpecialCollectionPath As SFile, Optional ByVal NewUser As SplitCollectionUserInfo? = Nothing) As Boolean
Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
Sub OpenFolder()
Property DownloadTopCount As Integer?
diff --git a/SCrawler/API/Base/SiteSettingsBase.vb b/SCrawler/API/Base/SiteSettingsBase.vb
index 1e2b52e..c1448b1 100644
--- a/SCrawler/API/Base/SiteSettingsBase.vb
+++ b/SCrawler/API/Base/SiteSettingsBase.vb
@@ -34,6 +34,16 @@ Namespace API.Base
Friend Property AccountName As String Implements ISiteSettings.AccountName
Friend Property Temporary As Boolean = False Implements ISiteSettings.Temporary
Friend Property DefaultInstance As ISiteSettings = Nothing Implements ISiteSettings.DefaultInstance
+ Protected _UserAgentDefault As String = String.Empty
+ Friend Overridable Property UserAgentDefault As String Implements ISiteSettings.UserAgentDefault
+ Get
+ Return _UserAgentDefault
+ End Get
+ Set(ByVal _UserAgentDefault As String)
+ Me._UserAgentDefault = _UserAgentDefault
+ If _AllowUserAgentUpdate And Not Responser Is Nothing And Not _UserAgentDefault.IsEmptyString Then Responser.UserAgent = _UserAgentDefault
+ End Set
+ End Property
Protected _AllowUserAgentUpdate As Boolean = True
Protected _SubscriptionsAllowed As Boolean = False
Friend ReadOnly Property SubscriptionsAllowed As Boolean Implements ISiteSettings.SubscriptionsAllowed
@@ -138,7 +148,6 @@ Namespace API.Base
Friend Overridable Sub BeginInit() Implements ISiteSettings.BeginInit
End Sub
Friend Overridable Sub EndInit() Implements ISiteSettings.EndInit
- If _AllowUserAgentUpdate And Not DefaultUserAgent.IsEmptyString And Not Responser Is Nothing Then Responser.UserAgent = DefaultUserAgent
If CheckNetscapeCookiesOnEndInit Then Update_SaveCookiesNetscape(, True)
End Sub
#End Region
diff --git a/SCrawler/API/Base/SplitCollectionUserInfo.vb b/SCrawler/API/Base/SplitCollectionUserInfo.vb
new file mode 100644
index 0000000..4e9ec1b
--- /dev/null
+++ b/SCrawler/API/Base/SplitCollectionUserInfo.vb
@@ -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
\ No newline at end of file
diff --git a/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.Designer.vb b/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.Designer.vb
new file mode 100644
index 0000000..556a481
--- /dev/null
+++ b/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.Designer.vb
@@ -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
+
+ Partial Friend Class SplitCollectionUserInfoChangePathsForm : Inherits System.Windows.Forms.Form
+
+ 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
+
+ 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
\ No newline at end of file
diff --git a/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.resx b/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.resx
new file mode 100644
index 0000000..152bcac
--- /dev/null
+++ b/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.resx
@@ -0,0 +1,129 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+ False
+
+
\ No newline at end of file
diff --git a/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.vb b/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.vb
new file mode 100644
index 0000000..1bc9c2e
--- /dev/null
+++ b/SCrawler/API/Base/SplitCollectionUserInfoChangePathsForm.vb
@@ -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)
+ '''
+ ''' Cancel = use initial
+ ''' Abort = abort operation
+ ''' OK = use changes
+ '''
+ 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
\ No newline at end of file
diff --git a/SCrawler/API/Base/SplitCollectionUserInfoPathForm.Designer.vb b/SCrawler/API/Base/SplitCollectionUserInfoPathForm.Designer.vb
new file mode 100644
index 0000000..85d2148
--- /dev/null
+++ b/SCrawler/API/Base/SplitCollectionUserInfoPathForm.Designer.vb
@@ -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
+
+ Partial Friend Class SplitCollectionUserInfoPathForm : Inherits System.Windows.Forms.Form
+
+ 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
+
+ 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
\ No newline at end of file
diff --git a/SCrawler/API/Base/SplitCollectionUserInfoPathForm.resx b/SCrawler/API/Base/SplitCollectionUserInfoPathForm.resx
new file mode 100644
index 0000000..59c4986
--- /dev/null
+++ b/SCrawler/API/Base/SplitCollectionUserInfoPathForm.resx
@@ -0,0 +1,154 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ text/microsoft-resx
+
+
+ 2.0
+
+
+ System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
+
+
+ False
+
+
+ False
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
+ JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
+ QVQ4T2P4//8/QczOJyyqHpzfiE0OQwAZC8iqszAzs7CJ69o4BR768V/W2jcGXQ0KB4aFNS3dDQtnrbCb
+ ePCK48wTN1wXXXzge/jXf/clV55zC4hIIatF0cjIyMikElzc57z0wX+XHd/+2+//99/ywP//xlu//tdb
+ +eK/4Zp3/1WTOhYzARViNUAluKjTdf37/0ZTTn9TbdhwXblhwwW1/qOP1Ja9+K8w+95/6cm3/6v2Xvkv
+ qKjniGGAoIqRpW3/4e8S9uGdzFz82gwMDFxAzCxm4ZegtuLDf+VJ1/8rZM25IqLvnM/CximCYYCic1QN
+ v7x2JIwPwyrJ3XNUylddE9G2TWNmZOBDl4czmJiZMSRBmFdSyYyJgUEQmxwIYxWEYXZBCUls4sgYq6CA
+ prWNbtG8nXKeaVPR5XiVjSxEzf0yYXy4BBMLO6eQjoOXZvrkbbazrv53Xf/2v4CSbjBMXkhBl1/CMyNZ
+ qWnvGy5pNQ+YONwAfjXzAOupl/47LLr333L50/96q9/8l23YdES6cO5KuYqVW+R7Tj6SnfP0v4hryjyY
+ HhQDmFjYeHVKFp7WX/Xuv9Kq9/+Vd/z7r7rv/3+l7f//y676DEwDN/9L+BVvYkKLCTgDhNkkVUyVlr74
+ qbbz73/VOTc/qsy89kWx+9h7qbQpJwS1bbOAscGGrB6EUTggLOqf16C55ft/HlnNAFZOXgVWdi4FRgYG
+ VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
+
+
+
+
+ iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
+ wwAADsMBx2+oZAAAAR5JREFUOE+VkjFqwzAUhn2D9iShRyi+QhYbGujg3ZATZPKYdC6FQhPwlAMkg3dP
+ WQwhyWIyJIUW5NqyPb7oCVtIlhVTwYf8nv7/t2zJagel9KmqKsIACYL9RjI8UHz5zshougZr/AEvbxEP
+ aZCDBY3VslixaJvX3wzkkDiOwbZtDRGA5vdNAg+TL27qgmt5XkBG/gTdAG7Gt+3PP9oOaEGFCVEC6rp+
+ 5g9MfM/c5e4OsEZMZkQEtGL5H2DdZ5JRArDwPA+iKII0TfkC9vroC9j5vq8JTWw3WzWgLMtZGIaa0MR8
+ vlAD8PYlSaIJTTiOowY0p0Bc19XEJo6HE59FAPuMzyAINKGJ1XLFZxHALtMrnkBXOIQIIIQ8YvF/KrgB
+ cMaRN0UdBBkAAAAASUVORK5CYII=
+
+
+
\ No newline at end of file
diff --git a/SCrawler/API/Base/SplitCollectionUserInfoPathForm.vb b/SCrawler/API/Base/SplitCollectionUserInfoPathForm.vb
new file mode 100644
index 0000000..32e2272
--- /dev/null
+++ b/SCrawler/API/Base/SplitCollectionUserInfoPathForm.vb
@@ -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
\ No newline at end of file
diff --git a/SCrawler/API/Base/UserDataBase.vb b/SCrawler/API/Base/UserDataBase.vb
index ee823b4..154f7e8 100644
--- a/SCrawler/API/Base/UserDataBase.vb
+++ b/SCrawler/API/Base/UserDataBase.vb
@@ -950,7 +950,10 @@ BlockNullPicture:
LogError(ex, "user information loading error")
End Try
End Sub
- Friend Overridable Sub UpdateUserInformation() Implements IUserData.UpdateUserInformation
+ Friend Overridable Overloads Sub UpdateUserInformation() Implements IUserData.UpdateUserInformation
+ UpdateUserInformation(False)
+ End Sub
+ Friend Overridable Overloads Sub UpdateUserInformation(ByVal DisableUserInfoUpdate As Boolean)
Try
UpdateDataFiles()
MyFileSettings.Exists(SFO.Path)
@@ -1001,7 +1004,7 @@ BlockNullPicture:
x.Save(MyFileSettings)
End Using
- If Not IsSavedPosts Then Settings.UpdateUsersList(User, True)
+ If Not IsSavedPosts And Not DisableUserInfoUpdate Then Settings.UpdateUsersList(User, True)
Catch ex As Exception
LogError(ex, "user information saving error")
End Try
@@ -1934,7 +1937,18 @@ BlockNullPicture:
Return 0
End If
End Function
- Friend Overridable Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile) As Boolean Implements IUserData.MoveFiles
+ Friend Function SplitCollectionGetNewUserInfo() As SplitCollectionUserInfo
+ Dim u As New SplitCollectionUserInfo With {.UserOrig = User, .UserNew = User}
+ With u.UserNew
+ .CollectionName = String.Empty
+ .SpecialCollectionPath = Nothing
+ .UserModel = UsageModel.Default
+ .CollectionModel = UsageModel.Default
+ .UpdateUserFile()
+ End With
+ Return u
+ End Function
+ Friend Overridable Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile, Optional ByVal NewUser As SplitCollectionUserInfo? = Nothing) As Boolean Implements IUserData.MoveFiles
Dim UserBefore As UserInfo = User
Dim Removed As Boolean = True
Dim _TurnBack As Boolean = False
@@ -1950,6 +1964,7 @@ BlockNullPicture:
User.SpecialCollectionPath = String.Empty
User.UserModel = UsageModel.Default
User.CollectionModel = UsageModel.Default
+ If NewUser.HasValue Then User.SpecialPath = NewUser.Value.UserNew.SpecialPath
Else
Settings.Users.Remove(Me)
Removed = True
diff --git a/SCrawler/API/Facebook/SiteSettings.vb b/SCrawler/API/Facebook/SiteSettings.vb
index 0439de1..f246609 100644
--- a/SCrawler/API/Facebook/SiteSettings.vb
+++ b/SCrawler/API/Facebook/SiteSettings.vb
@@ -11,6 +11,7 @@ Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Functions.RegularExpressions
+Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Facebook
Friend Class SiteSettings : Inherits ThreadsNet.SiteSettings
@@ -31,11 +32,11 @@ Namespace API.Facebook
End Property
#End Region
#Region "Defaults"
-
+
Friend ReadOnly Property ParsePhotoBlock As PropertyValue
-
+
Friend ReadOnly Property ParseVideoBlock As PropertyValue
-
+
Friend ReadOnly Property ParseStoriesBlock As PropertyValue
#End Region
#End Region
diff --git a/SCrawler/API/Instagram/SiteSettings.vb b/SCrawler/API/Instagram/SiteSettings.vb
index eb02e4b..ac8fa19 100644
--- a/SCrawler/API/Instagram/SiteSettings.vb
+++ b/SCrawler/API/Instagram/SiteSettings.vb
@@ -14,6 +14,7 @@ Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies
Imports Download = SCrawler.Plugin.ISiteSettings.Download
+Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.Instagram
Friend Class SiteSettings : Inherits SiteSettingsBase
@@ -54,6 +55,9 @@ Namespace API.Instagram
End Function
End Class
#End Region
+#Region "Categories"
+ Private Const CAT_DOWN As String = "Download data"
+#End Region
#Region "Authorization properties"
Friend Const Header_IG_APP_ID As String = "x-ig-app-id"
Friend Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim"
@@ -143,10 +147,19 @@ Namespace API.Instagram
Friend ReadOnly Property USE_GQL As PropertyValue
#End Region
#Region "Download properties"
+
+ Private ReadOnly Property DownDetectorValue As PropertyValue
+
+ Private ReadOnly Property DownDetectorValueProvider As IFormatProvider
+
+ Private ReadOnly Property DownDetectorValueAddToLog As PropertyValue
Friend Const TimersUrgentTip As String = vbCr & "It is highly recommended not to change the default value."
Friend ReadOnly Property RequestsWaitTimer_Any As PropertyValue
@@ -154,33 +167,33 @@ Namespace API.Instagram
+ AllowNull:=False, Category:=DN.CAT_Timers), PXML, ControlNumber(20), PClonable>
Friend ReadOnly Property RequestsWaitTimer As PropertyValue
Private ReadOnly Property RequestsWaitTimerProvider As IFormatProvider
+ AllowNull:=False, LeftOffset:=120, Category:=DN.CAT_Timers), PXML, ControlNumber(21), PClonable>
Friend ReadOnly Property RequestsWaitTimerTaskCount As PropertyValue
Private ReadOnly Property RequestsWaitTimerTaskCountProvider As IFormatProvider
+ AllowNull:=False, Category:=DN.CAT_Timers), PXML, ControlNumber(22), PClonable>
Friend ReadOnly Property SleepTimerOnPostsLimit As PropertyValue
Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider
-
+
Friend ReadOnly Property GetTimeline As PropertyValue
-
+
Friend ReadOnly Property GetReels As PropertyValue
-
+
Friend ReadOnly Property GetStories As PropertyValue
-
+
Friend ReadOnly Property GetStoriesUser As PropertyValue
-
+
Friend ReadOnly Property GetTagged As PropertyValue
+
Friend ReadOnly Property DownloadTimeline As PropertyValue
Private ReadOnly Property DownloadTimeline_Def As PropertyValue
-
+
Friend ReadOnly Property DownloadReels As PropertyValue
Private ReadOnly Property DownloadReels_Def As PropertyValue
-
+
Friend ReadOnly Property DownloadStories As PropertyValue
Private ReadOnly Property DownloadStories_Def As PropertyValue
-
+
Friend ReadOnly Property DownloadStoriesUser As PropertyValue
Private ReadOnly Property DownloadStoriesUser_Def As PropertyValue
-
+
Friend ReadOnly Property DownloadTagged As PropertyValue
Private ReadOnly Property DownloadTagged_Def As PropertyValue
#End Region
@@ -352,8 +365,11 @@ Namespace API.Instagram
platform = .Value(Header_Platform_Verion)
End If
'.Add(Header_IG_WWW_CLAIM, 0)
+ .Add("Origin", "https://www.instagram.com")
+ .Add("authority", "www.instagram.com")
.Add("Dnt", 1)
- .Add("Dpr", 1)
+ '.Add("Dpr", 1)
+ .Remove("Dpr")
.Add("Sec-Ch-Ua-Mobile", "?0")
.Add("Sec-Ch-Ua-Model", """""")
.Add("Sec-Ch-Ua-Platform", """Windows""")
@@ -396,6 +412,9 @@ Namespace API.Instagram
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)
@@ -413,7 +432,7 @@ Namespace API.Instagram
TaggedNotifyLimit = New PropertyValue(200)
TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker
- DownloadingErrorDate = New PropertyValue(Now.AddYears(10), GetType(Date))
+ DownloadingErrorDate = New PropertyValue(Now.AddYears(-10), GetType(Date))
LastDownloadDate = New PropertyValue(Now.AddDays(-1))
LastRequestsCount = New PropertyValue(0)
LastRequestsCountLabel = New PropertyValue(String.Empty, GetType(String))
@@ -456,16 +475,85 @@ Namespace API.Instagram
End Function
#End Region
#Region "Downloading"
+ Private ____DownloadStarted As Boolean = False
+ Private ____AvailableRequested As Boolean = False
+ Private ____AvailableSilent As Boolean = True
+ Private ____AvailableChecked As Boolean = False
+ Private ____AvailableResult As Boolean = False
+ 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 Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
- Return ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso DownloadTimeline.Value
+ If ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() AndAlso CBool(DownloadTimeline.Value) Then
+ If ____DownloadStarted And ____AvailableRequested Then
+ ____AvailableResult = AvailableImpl(What, ____AvailableSilent)
+ ____AvailableChecked = True
+ ____AvailableRequested = False
+ Return ____AvailableResult
+ ElseIf ____AvailableChecked Then
+ Return ____AvailableResult
+ Else
+ Return True
+ End If
+ Else
+ Return False
+ End If
End Function
Private ActiveJobs As Integer = 0
Private ActiveSessionDate As Date
+ Private ActiveSessionRequestsExists As Boolean = False
Private _NextWNM As UserData.WNM = UserData.WNM.Notify
Private _NextTagged As Boolean = True
Friend Overrides Sub DownloadStarted(ByVal What As Download)
+ If ActiveJobs = 0 Then ActiveSessionRequestsExists = False
ActiveJobs += 1
+ If What = Download.Main Then ____DownloadStarted = True
If ActiveJobs = 1 Then ActiveSessionDate = Now
If Not HH_IG_WWW_CLAIM_IS_ZERO AndAlso
(
@@ -498,6 +586,7 @@ Namespace API.Instagram
If _NextWNM = UserData.WNM.SkipTemp Or _NextWNM = UserData.WNM.SkipCurrent Then _NextWNM = UserData.WNM.Notify
_NextTagged = .TaggedCheckSession
MyLastRequestsCount = .RequestsCountSession
+ If .RequestsCountSession > 0 Then ActiveSessionRequestsExists = True
_FieldsChangerSuspended = True
HH_IG_WWW_CLAIM.Value = Responser.Headers.Value(Header_IG_WWW_CLAIM)
HH_CSRF_TOKEN.Value = Responser.Headers.Value(Header_CSRF_TOKEN)
@@ -507,9 +596,16 @@ Namespace API.Instagram
Friend Overrides Sub DownloadDone(ByVal What As Download)
_NextWNM = UserData.WNM.Notify
_NextTagged = True
- RefreshMyLastRequests(Now)
+ If ActiveSessionRequestsExists Then RefreshMyLastRequests(Now)
ActiveJobs -= 1
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 Region
#Region "Settings"
diff --git a/SCrawler/API/Instagram/UserData.vb b/SCrawler/API/Instagram/UserData.vb
index a71b593..b00fa96 100644
--- a/SCrawler/API/Instagram/UserData.vb
+++ b/SCrawler/API/Instagram/UserData.vb
@@ -862,15 +862,20 @@ NextPageBlock:
Protected DefaultParser_IgnorePass As Boolean = False
Private ReadOnly DefaultParser_PostUrlCreator_Default As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/"
Protected DefaultParser_PostUrlCreator As Func(Of PostKV, String) = Function(post) $"https://www.instagram.com/p/{post.Code}/"
+ Protected DefaultParser_Pinned As Func(Of IEnumerable(Of EContainer), Integer, Boolean) = Nothing
+ Protected DefaultParser_SkipPost As Func(Of IEnumerable(Of EContainer), Integer, PostKV, Boolean) = Nothing
Protected Function DefaultParser(ByVal Items As IEnumerable(Of EContainer), ByVal Section As Sections, ByVal Token As CancellationToken,
Optional ByVal SpecFolder As String = Nothing, Optional ByVal State As UStates = UStates.Unknown,
Optional ByVal Attempts As Integer = 0) As Boolean
ThrowAny(Token)
- If Items.Count > 0 Then
+ If Items.ListExists Then
Dim PostIDKV As PostKV
Dim Pinned As Boolean
Dim PostDate$, PostOriginUrl$
- Dim before%
+ Dim i%, before%
+ Dim usePinFunc As Boolean = Not DefaultParser_Pinned Is Nothing
+ Dim skipPostFuncExists As Boolean = Not DefaultParser_SkipPost Is Nothing
+ Dim nn As EContainer
If SpecFolder.IsEmptyString Then
Select Case Section
Case Sections.Tagged : SpecFolder = TaggedFolder
@@ -879,14 +884,21 @@ NextPageBlock:
End Select
End If
ProgressPre.ChangeMax(Items.Count)
- For Each nn In Items
+ For i = 0 To Items.Count - 1
+ nn = Items(i)
ProgressPre.Perform()
With If(Not DefaultParser_ElemNode Is Nothing, nn.ItemF(DefaultParser_ElemNode), nn)
If .ListExists Then
PostIDKV = New PostKV(.Value("code"), .Value("id"), Section)
PostOriginUrl = DefaultParser_PostUrlCreator(PostIDKV)
- Pinned = .Contains("timeline_pinned_user_ids")
- If Not DefaultParser_IgnorePass AndAlso PostKvExists(PostIDKV) Then
+ 'Pinned = .Contains("timeline_pinned_user_ids")
+ If usePinFunc Then
+ Pinned = DefaultParser_Pinned.Invoke(Items, i)
+ Else
+ Pinned = If(.Item("timeline_pinned_user_ids")?.Count, 0) > 0
+ End If
+ If skipPostFuncExists AndAlso DefaultParser_SkipPost.Invoke(Items, i, PostIDKV) Then
+ ElseIf Not DefaultParser_IgnorePass AndAlso PostKvExists(PostIDKV) Then
If Not Section = Sections.Timeline OrElse Not Pinned Then Return False
Else
_TempPostsList.Add(PostIDKV.ID)
diff --git a/SCrawler/API/Mastodon/EditorExchangeOptions.vb b/SCrawler/API/Mastodon/EditorExchangeOptions.vb
index 6e1576f..a218601 100644
--- a/SCrawler/API/Mastodon/EditorExchangeOptions.vb
+++ b/SCrawler/API/Mastodon/EditorExchangeOptions.vb
@@ -14,6 +14,7 @@ Namespace API.Mastodon
Friend Overrides Property DownloadModelProfile As Boolean
Friend Overrides Property DownloadModelSearch As Boolean
Friend Overrides Property DownloadModelForceApply As Boolean
+ Friend Overrides Property DownloadModelLikes As Boolean
Friend Sub New(ByVal s As SiteSettings)
MyBase.New(s)
End Sub
diff --git a/SCrawler/API/Mastodon/SiteSettings.vb b/SCrawler/API/Mastodon/SiteSettings.vb
index 5b86e6e..76dbafd 100644
--- a/SCrawler/API/Mastodon/SiteSettings.vb
+++ b/SCrawler/API/Mastodon/SiteSettings.vb
@@ -63,15 +63,15 @@ Namespace API.Mastodon
End Sub
#End Region
#Region "Other properties"
-
+
Friend ReadOnly Property GifsDownload As PropertyValue
-
+
Friend ReadOnly Property GifsSpecialFolder As PropertyValue
-
+
Friend ReadOnly Property GifsPrefix As PropertyValue
Private ReadOnly Property GifStringChecker As IFormatProvider
-
+
Friend ReadOnly Property UseMD5Comparison As PropertyValue
diff --git a/SCrawler/API/OnlyFans/SiteSettings.vb b/SCrawler/API/OnlyFans/SiteSettings.vb
index 59f7991..0922b9c 100644
--- a/SCrawler/API/OnlyFans/SiteSettings.vb
+++ b/SCrawler/API/OnlyFans/SiteSettings.vb
@@ -13,18 +13,22 @@ Imports PersonalUtilities.Forms
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies
Imports PersonalUtilities.Functions.RegularExpressions
+Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.OnlyFans
Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations"
+#Region "Categories"
+ Private Const CAT_OFS As String = "OF-Scraper support"
+#End Region
#Region "Options"
-
+
Friend ReadOnly Property DownloadTimeline As PropertyValue
-
+
Friend ReadOnly Property DownloadStories As PropertyValue
-
+
Friend ReadOnly Property DownloadHighlights As PropertyValue
-
+
Friend ReadOnly Property DownloadChatMedia As PropertyValue
#End Region
#Region "Headers"
@@ -32,16 +36,16 @@ Namespace API.OnlyFans
Private Const HeaderUserID As String = "User-Id"
Friend Const HeaderXBC As String = "X-Bc"
Friend Const HeaderAppToken As String = "App-Token"
-
+
Friend ReadOnly Property HH_USER_ID As PropertyValue
-
+
Private ReadOnly Property HH_X_BC As PropertyValue
-
+
Private ReadOnly Property HH_APP_TOKEN As PropertyValue
+ InheritanceName:=SettingsCLS.HEADER_DEF_sec_ch_ua, IsAuth:=True), PClonable, PXML(OnlyForChecked:=True)>
Private ReadOnly Property HH_BROWSER As PropertyValue
-
+
Friend ReadOnly Property UserAgent As PropertyValue
Private Sub UpdateHeader(ByVal PropertyName As String, ByVal Value As String)
Dim hName$ = String.Empty
@@ -82,20 +86,21 @@ Namespace API.OnlyFans
End Property
+ "Change this value only if you know what you are doing.", IsAuth:=True), PXML, PClonable>
Friend ReadOnly Property UseOldAuthRules As PropertyValue
-
+
Friend ReadOnly Property DynamicRulesUpdateInterval As PropertyValue
Private ReadOnly Property DynamicRulesUpdateIntervalProvider As IFormatProvider
+ "Change this value only if you know what you are doing.", IsAuth:=True), PXML, PClonable>
Friend ReadOnly Property DynamicRules As PropertyValue
#End Region
#Region "OFScraper"
Private ReadOnly Property OFScraperPath_XML As PropertyValue
-
+
Friend ReadOnly Property OFScraperPath As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
@@ -106,7 +111,7 @@ Namespace API.OnlyFans
End Get
End Property
Private ReadOnly Property OFScraperMP4decrypt_XML As PropertyValue
-
+
Friend ReadOnly Property OFScraperMP4decrypt As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
@@ -118,7 +123,7 @@ Namespace API.OnlyFans
End Property
Friend Const KeyModeDefault_Default As String = "cdrm"
Private ReadOnly Property KeyModeDefault_XML As PropertyValue
-
+
Friend ReadOnly Property KeyModeDefault As PropertyValue
Get
If Not DefaultInstance Is Nothing Then
@@ -134,6 +139,8 @@ Namespace API.OnlyFans
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)
+ _AllowUserAgentUpdate = False
+
With Responser
.Accept = "application/json, text/plain, */*"
.AutomaticDecompression = Net.DecompressionMethods.GZip
diff --git a/SCrawler/API/Reddit/M3U8.vb b/SCrawler/API/Reddit/M3U8.vb
index c064beb..4fbd52e 100644
--- a/SCrawler/API/Reddit/M3U8.vb
+++ b/SCrawler/API/Reddit/M3U8.vb
@@ -8,6 +8,7 @@
' but WITHOUT ANY WARRANTY
Imports System.Net
Imports System.Threading
+Imports SCrawler.API.Base
Imports SCrawler.API.Reddit.M3U8_Declarations
Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.Web
@@ -61,15 +62,18 @@ Namespace API.Reddit
Private ReadOnly ProgressExists As Boolean
Private ReadOnly Property ProgressPre As PreProgress
Private ReadOnly UsePreProgress As Boolean
+ Private ReadOnly Media As UserMedia
#End Region
- Private Sub New(ByVal URL As String, ByVal OutFile As SFile, ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean)
+ Private Sub New(ByVal URL As String, ByVal Media As UserMedia, ByVal OutFile As SFile, ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean)
PlayListURL = URL
+ Me.Media = Media
BaseURL = RegexReplace(URL, BaseUrlPattern)
Video = New List(Of String)
Audio = New List(Of String)
Me.OutFile = OutFile
Me.OutFile.Name = "PlayListFile"
Me.OutFile.Extension = "mp4"
+ If Media.Post.Date.HasValue Then Me.OutFile.Name = Media.Post.Date.Value.ToString("yyyyMMdd_HHmmss")
Me.Progress = Progress
ProgressExists = Not Me.Progress Is Nothing
ProgressPre = New PreProgress(Progress)
@@ -202,9 +206,9 @@ Namespace API.Reddit
End Function
#End Region
#Region "Statics"
- Friend Shared Function Download(ByVal URL As String, ByVal f As SFile, ByVal Token As CancellationToken,
+ Friend Shared Function Download(ByVal URL As String, ByVal Media As UserMedia, ByVal f As SFile, ByVal Token As CancellationToken,
ByVal Progress As MyProgress, ByVal UsePreProgress As Boolean) As SFile
- Using m As New M3U8(URL, f, Progress, UsePreProgress) : Return m.Download(Token) : End Using
+ Using m As New M3U8(URL, Media, f, Progress, UsePreProgress) : Return m.Download(Token) : End Using
End Function
#End Region
#Region "IDisposable Support"
diff --git a/SCrawler/API/Reddit/SiteSettings.vb b/SCrawler/API/Reddit/SiteSettings.vb
index fff9d0c..56c6bcf 100644
--- a/SCrawler/API/Reddit/SiteSettings.vb
+++ b/SCrawler/API/Reddit/SiteSettings.vb
@@ -58,7 +58,6 @@ Namespace API.Reddit
Return {AuthUserName.Value, AuthPassword.Value, ApiClientID.Value, ApiClientSecret.Value}.All(Function(v$) Not v.IsEmptyString)
End Get
End Property
-
#End Region
#Region "Other"
diff --git a/SCrawler/API/Reddit/UserData.vb b/SCrawler/API/Reddit/UserData.vb
index a6ac46a..254d83a 100644
--- a/SCrawler/API/Reddit/UserData.vb
+++ b/SCrawler/API/Reddit/UserData.vb
@@ -225,6 +225,7 @@ Namespace API.Reddit
#End Region
#Region "Download Overrides"
Friend Overrides Sub DownloadData(ByVal Token As CancellationToken)
+ Err429Count = 0
_CrossPosts.Clear()
If CreatedByChannel And Settings.FromChannelDownloadTopUse And Settings.FromChannelDownloadTop > 0 Then _
DownloadTopCount = Settings.FromChannelDownloadTop.Value
@@ -287,6 +288,7 @@ Namespace API.Reddit
End Sub
#End Region
#Region "Download Functions (User, Channel)"
+ Private Err429Count As Integer = 0
Private _TotalPostsDownloaded As Integer = 0
Private ReadOnly _CrossPosts As List(Of String)
Private Const SiteGfycatKey As String = "gfycat"
@@ -375,6 +377,7 @@ Namespace API.Reddit
Loop While Not _completed
End Sub
Private Sub DownloadDataChannel(ByVal POST As String, ByVal Token As CancellationToken)
+ Const savedPostsSleepTimer% = 2000
Dim eObj% = 0
Dim round% = 0
Dim URL$ = String.Empty
@@ -392,12 +395,14 @@ Namespace API.Reddit
If IsSavedPosts Then
URL = $"https://www.reddit.com/user/{TrueName}/saved.json?after={POST}"
+ If Not POST.IsEmptyString Then Thread.Sleep(savedPostsSleepTimer)
Else
URL = $"https://reddit.com/r/{TrueName}/{View}.json?allow_quarantined=true&allow_over18=1&include=identity&after={POST}&dist=25&sort={View}&t={Period}&layout=classic"
End If
ThrowAny(Token)
Dim r$ = Responser.GetResponse(URL)
+ If IsSavedPosts Then Err429Count = 0
If Not r.IsEmptyString Then
Using w As EContainer = JsonDocument.Parse(r).XmlIfNothing
If w.Count > 0 Then
@@ -458,8 +463,12 @@ Namespace API.Reddit
End If
_completed = True
Catch ex As Exception
- If ProcessException(ex, Token, $"channel data downloading error [{URL}]",, eObj) = HttpStatusCode.InternalServerError Then
+ Dim errValue% = ProcessException(ex, Token, $"{IIf(IsSavedPosts, "saved posts", "channel")} data downloading error [{URL}]",, eObj)
+ If errValue = HttpStatusCode.InternalServerError Then
If round = 2 Then eObj = HttpStatusCode.InternalServerError
+ ElseIf errValue = 429 And round = 0 Then
+ Thread.Sleep(savedPostsSleepTimer)
+ round += 1
Else
_completed = True
End If
@@ -975,7 +984,7 @@ Namespace API.Reddit
Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID, .UserID = _UserID}}
If t = UTypes.Picture Or t = UTypes.GIF Then m.File = CreateFileFromUrl(m.URL) Else m.File = Nothing
If ReplacePreview And m.URL.Contains("preview") And Not t = UTypes.Picture Then m.URL = $"https://i.redd.it/{m.File.File}"
- If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, DateTrueProvider(IsChannel), Nothing) Else m.Post.Date = Nothing
+ If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, DateTrueProvider(IsChannel Or IsSavedPosts), Nothing) Else m.Post.Date = Nothing
Return m
End Function
Private Function TryFile(ByVal URL As String) As Boolean
@@ -1027,7 +1036,7 @@ Namespace API.Reddit
Return URL.Contains(SiteRedGifsKey)
End Function
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
- Return M3U8.Download(URL, DestinationFile, Token, Progress, Not IsSingleObjectDownload)
+ Return M3U8.Download(URL, Media, DestinationFile, Token, Progress, Not IsSingleObjectDownload)
End Function
Protected Overrides Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile
If Not IsChannel Or Not SaveToCache Then
@@ -1057,8 +1066,11 @@ Namespace API.Reddit
ElseIf .StatusCode = HttpStatusCode.InternalServerError Then '500
If Not IsNothing(EObj) AndAlso IsNumeric(EObj) AndAlso CInt(EObj) = HttpStatusCode.InternalServerError Then Return 1
Return HttpStatusCode.InternalServerError
+ ElseIf .StatusCode = 429 And IsSavedPosts And Err429Count = 0 Then
+ Err429Count += 1
+ Return 429
ElseIf .StatusCode = 429 AndAlso
- ((Not IsSavedPosts And CBool(MySiteSettings.UseTokenForTimelines.Value)) Or (IsSavedPosts And MySiteSettings.UseTokenForSavedPosts.Value)) AndAlso
+ ((Not IsSavedPosts And CBool(MySiteSettings.UseTokenForTimelines.Value)) Or (IsSavedPosts And CBool(MySiteSettings.UseTokenForSavedPosts.Value))) AndAlso
Not MySiteSettings.CredentialsExists Then '429
MyMainLOG = $"{ToStringForLog()}: [{CInt(Responser.StatusCode)}] You should use OAuth authorization or disable " &
IIf(IsSavedPosts, "token usage for downloading saved posts", "the use of token and cookies for downloading timelines")
diff --git a/SCrawler/API/Redgifs/SiteSettings.vb b/SCrawler/API/Redgifs/SiteSettings.vb
index 5f4fe7f..798f76b 100644
--- a/SCrawler/API/Redgifs/SiteSettings.vb
+++ b/SCrawler/API/Redgifs/SiteSettings.vb
@@ -18,9 +18,9 @@ Namespace API.RedGifs
Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations"
-
+
Friend ReadOnly Property Token As PropertyValue
-
+
Private ReadOnly Property UserAgent As PropertyValue
Friend ReadOnly Property TokenLastDateUpdated As PropertyValue
Private Const TokenName As String = "authorization"
@@ -107,7 +107,9 @@ Namespace API.RedGifs
Friend Overrides Sub Update()
If _SiteEditorFormOpened Then
Dim NewToken$ = AConvert(Of String)(Token.Value, AModes.Var, String.Empty)
- If Not _LastTokenValue = NewToken Then TokenLastDateUpdated.Value = Now
+ If Not _LastTokenValue = NewToken And Not NewToken.IsEmptyString Then TokenLastDateUpdated.Value = Now
+ If Responser.CookiesExists AndAlso MsgBoxE({"RedGifs doesn't require cookies! Do you still want to use cookies?", "RedGifs cookies"},
+ vbExclamation,,, {"Use", "Don't use"}) = 1 Then Responser.Cookies.Clear()
End If
MyBase.Update()
End Sub
diff --git a/SCrawler/API/ThreadsNet/SiteSettings.vb b/SCrawler/API/ThreadsNet/SiteSettings.vb
index acd7017..69dd25e 100644
--- a/SCrawler/API/ThreadsNet/SiteSettings.vb
+++ b/SCrawler/API/ThreadsNet/SiteSettings.vb
@@ -13,6 +13,7 @@ Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies
Imports PersonalUtilities.Functions.RegularExpressions
Imports IG = SCrawler.API.Instagram.SiteSettings
+Imports DN = SCrawler.API.Base.DeclaredNames
Namespace API.ThreadsNet
Friend Class SiteSettings : Inherits SiteSettingsBase
@@ -75,14 +76,14 @@ Namespace API.ThreadsNet
#Region "Other properties"
Friend ReadOnly Property RequestsWaitTimer_Any As PropertyValue
Private ReadOnly Property RequestsWaitTimer_AnyProvider As IFormatProvider
+ "It becomes unchecked when the site returns an error.", Category:="Download"), PXML>
Friend ReadOnly Property DownloadData_Impl As PropertyValue
#End Region
#End Region
diff --git a/SCrawler/API/ThreadsNet/UserData.vb b/SCrawler/API/ThreadsNet/UserData.vb
index 1009f97..e1a1eb4 100644
--- a/SCrawler/API/ThreadsNet/UserData.vb
+++ b/SCrawler/API/ThreadsNet/UserData.vb
@@ -17,6 +17,10 @@ Imports PersonalUtilities.Tools.Web.Clients.EventArguments
Imports IGS = SCrawler.API.Instagram.SiteSettings
Namespace API.ThreadsNet
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"
Private ReadOnly Property MySettings As SiteSettings
Get
@@ -29,9 +33,20 @@ Namespace API.ThreadsNet
Return ValidateBaseTokens() And Not ID.IsEmptyString
End Get
End Property
+ Private Property MaxLastDownDate As Date? = Nothing
+ Private Property FirstLoadingDone As Boolean = False
#End Region
#Region "Loader"
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 Region
#Region "Exchange"
@@ -49,6 +64,7 @@ Namespace API.ThreadsNet
DefaultParser_PostUrlCreator = Function(post) $"https://www.threads.net/@{NameTrue}/post/{post.Code}"
_ResponserAutoUpdateCookies = True
_ResponserAddResponseReceivedHandler = True
+ DefaultParser_Pinned = AddressOf IsPinnedPost
End Sub
#End Region
#Region "Download functions"
@@ -66,7 +82,27 @@ Namespace API.ThreadsNet
Responser.Method = "POST"
LoadSavePostsKV(True)
ResetBaseTokens()
+ 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)
+ If _TempMediaList.Count > 0 Then FirstLoadingDone = True : setMaxPostDate.Invoke(_TempMediaList)
Catch ex As Exception
errorFound = True
Throw ex
@@ -78,6 +114,21 @@ Namespace API.ThreadsNet
End Try
End If
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()
If Not Responser Is Nothing AndAlso Not Responser.Disposed Then
RemoveHandler Responser.ResponseReceived, AddressOf Responser_ResponseReceived
@@ -166,7 +217,6 @@ Namespace API.ThreadsNet
With .Headers
.Clear()
.Add("dnt", 1)
- .Add("drp", 1)
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Authority, "www.threads.net"))
.Add(HttpHeaderCollection.GetSpecialHeader(MyHeaderTypes.Origin, "https://www.threads.net"))
.Add("Sec-Ch-Ua-Model", "")
diff --git a/SCrawler/API/Twitter/Declarations.vb b/SCrawler/API/Twitter/Declarations.vb
index ac2a3fa..46d4de1 100644
--- a/SCrawler/API/Twitter/Declarations.vb
+++ b/SCrawler/API/Twitter/Declarations.vb
@@ -15,6 +15,7 @@ Namespace API.Twitter
Friend Const TwitterSiteKey As String = "AndyProgram_Twitter"
Friend ReadOnly DateProvider As ADateTime = GetDateProvider()
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
Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone
n.FullDateTimePattern = "ddd MMM dd HH:mm:ss +ffff yyyy"
diff --git a/SCrawler/API/Twitter/EditorExchangeOptions.vb b/SCrawler/API/Twitter/EditorExchangeOptions.vb
index 60f6a78..c6a9e1c 100644
--- a/SCrawler/API/Twitter/EditorExchangeOptions.vb
+++ b/SCrawler/API/Twitter/EditorExchangeOptions.vb
@@ -28,16 +28,20 @@ Namespace API.Twitter
Friend Overridable Property MediaModelAllowNonUserTweets As Boolean = False
+ ToolTip:="Download the data using the 'https://x.com/UserName/media' command.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelMedia As Boolean = False
+ ToolTip:="Download the data using the 'https://x.com/UserName' command.", LeftOffset:=DefaultOffset)>
Friend Overridable Property DownloadModelProfile As Boolean = False
+ 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 DownloadModelLikes As Boolean = False
@@ -73,6 +77,7 @@ Namespace API.Twitter
DownloadModelMedia = dm.Contains(DModels.Media)
DownloadModelProfile = dm.Contains(DModels.Profile)
DownloadModelSearch = dm.Contains(DModels.Search)
+ DownloadModelLikes = dm.Contains(DModels.Likes)
End If
End If
MySettings = u.HOST.Source
diff --git a/SCrawler/API/Twitter/SiteSettings.vb b/SCrawler/API/Twitter/SiteSettings.vb
index c559af8..28f1a92 100644
--- a/SCrawler/API/Twitter/SiteSettings.vb
+++ b/SCrawler/API/Twitter/SiteSettings.vb
@@ -16,32 +16,37 @@ Namespace API.Twitter
Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations"
+#Region "Categories"
+ Private Const CAT_DOWN As String = "Downloading"
+#End Region
#Region "Other properties"
+ "Otherwise the appropriate download model will be selected right from the start.", Category:=DN.CAT_UserDefs), PXML, PClonable>
Friend ReadOnly Property UseAppropriateModel As PropertyValue
#Region "End points"
-
+
Friend Property UseNewEndPointSearch As PropertyValue
-
+
Friend Property UseNewEndPointProfiles As PropertyValue
#End Region
#Region "Limits"
-
+
Friend Property AbortOnLimit As PropertyValue
-
+
Friend Property DownloadAlreadyParsed As PropertyValue
#End Region
-
+
Friend ReadOnly Property MediaModelAllowNonUserTweets As PropertyValue
-
+
Friend ReadOnly Property GifsDownload As PropertyValue
-
+
Friend ReadOnly Property GifsSpecialFolder As PropertyValue
-
+
Friend ReadOnly Property GifsPrefix As PropertyValue
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]")
End Function
End Class
-
+
Friend ReadOnly Property UseMD5Comparison As PropertyValue
+ ControlToolTip:=DN.ConcurrentDownloadsToolTip, AllowNull:=False, LeftOffset:=120, Category:=CAT_DOWN), PXML, TaskCounter, PClonable>
Friend ReadOnly Property ConcurrentDownloads As PropertyValue
Private ReadOnly Property MyConcurrentDownloadsProvider As IFormatProvider
#End Region
#End Region
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)
@@ -97,7 +102,7 @@ Namespace API.Twitter
MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider
UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "/(twitter|x).com/"), 2)
- UrlPatternUser = "https://twitter.com/{0}"
+ UrlPatternUser = "https://x.com/{0}"
ImageVideoContains = "twitter"
CheckNetscapeCookiesOnEndInit = True
UseNetscapeCookies = True
@@ -106,7 +111,7 @@ Namespace API.Twitter
Return New UserData
End Function
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
Friend Overrides Function BaseAuthExists() As Boolean
Return Responser.CookiesExists
diff --git a/SCrawler/API/Twitter/UserData.vb b/SCrawler/API/Twitter/UserData.vb
index a439655..a732213 100644
--- a/SCrawler/API/Twitter/UserData.vb
+++ b/SCrawler/API/Twitter/UserData.vb
@@ -33,6 +33,7 @@ Namespace API.Twitter
Media = 1
Profile = 2
Search = 5
+ Likes = 10
End Enum
Private FirstDownloadComplete As Boolean = False
Friend Property DownloadModelForceApply As Boolean = False
@@ -41,6 +42,7 @@ Namespace API.Twitter
Friend Property GifsDownload As Boolean = True
Friend Property GifsSpecialFolder 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 Property MySettings As SiteSettings
Get
@@ -74,6 +76,7 @@ Namespace API.Twitter
If .DownloadModelMedia Then DownloadModel += DownloadModels.Media
If .DownloadModelProfile Then DownloadModel += DownloadModels.Profile
If .DownloadModelSearch Then DownloadModel += DownloadModels.Search
+ If .DownloadModelLikes Then DownloadModel += DownloadModels.Likes
End With
End If
End Sub
@@ -81,6 +84,7 @@ Namespace API.Twitter
#Region "Initializer, loader"
Friend Sub New()
_DataNames = New List(Of String)
+ LikesPosts = New List(Of String)
End Sub
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
With Container
@@ -142,21 +146,32 @@ Namespace API.Twitter
}
End Function
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
- If MySettings.LIMIT_ABORT Then
- Throw New TwitterLimitException(Me)
- Else
- If IsSavedPosts Then
- If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
- DownloadData_SavedPosts(Token)
+ Try
+ If MySettings.LIMIT_ABORT Then
+ Throw New TwitterLimitException(Me)
Else
- If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
- DownloadData_Timeline(Token)
+ If IsSavedPosts Then
+ If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
+ DownloadData_SavedPosts(Token)
+ Else
+ LikesPosts.Clear()
+ If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
+ DownloadData_Timeline(Token)
+ If LikesPosts.Count > 0 Then
+ _ReparseLikes = True
+ ReparseMissing(Token)
+ _ReparseLikes = False
+ End If
+ End If
End If
- End If
+ Finally
+ _ReparseLikes = False
+ End Try
End Sub
Private Sub DownloadData_Timeline(ByVal Token As CancellationToken)
Dim URL$ = String.Empty
Dim tCache As CacheKeeper = Nothing
+ Dim likesDetected As Boolean = False
Try
Const entry$ = "entry"
Dim PostID$ = String.Empty
@@ -199,6 +214,7 @@ Namespace API.Twitter
If Not _TempPostsList.Contains(PostID) Then
_TempPostsList.Add(PostID)
+ ElseIf dirIndx = 3 Then
ElseIf isPins Then
Return False
Else
@@ -211,9 +227,22 @@ Namespace API.Twitter
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("/")
- If Not ParseUserMediaOnly OrElse
+ If (Not ParseUserMediaOnly Or dirIndx = 3) 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
Return True
End Function
@@ -225,6 +254,8 @@ Namespace API.Twitter
For Each dir As SFile In dirs
dirIndx += 1
+ If dirIndx = 3 Then likesDetected = True
+
ExistsDetected = False
If Not dir.IsEmptyString Then
@@ -287,17 +318,22 @@ Namespace API.Twitter
End If
End If
Else
- For pIndx = 0 To IIf(dirIndx < 2, 1, 0)
+ For pIndx = 0 To IIf(dirIndx < 2 Or dirIndx = 3, 1, 0)
optionalNode = Nothing
Select Case dirIndx
- Case 0, 1
+ Case 0, 1, 3
rootNode = j({"data", "user", "result", "timeline_v2", "timeline", "instructions"})
If rootNode.ListExists Then
- p = If(pIndx = 0, pinNode, timelineNode)
- isPins = pIndx = 0
+ If dirIndx = 3 Then
+ p = entriesNode
+ isPins = False
+ Else
+ p = If(pIndx = 0, pinNode, timelineNode)
+ isPins = pIndx = 0
+ End If
optionalNode = rootNode
- rootNode = rootNode.Find(p, False)
- If rootNode.ListExists Then rootNode = rootNode.Find(entriesNode, False)
+ rootNode = rootNode.Find(p, dirIndx = 3)
+ If dirIndx <> 3 And rootNode.ListExists Then rootNode = rootNode.Find(entriesNode, dirIndx = 3)
End If
Case Else
isPins = False
@@ -369,12 +405,12 @@ Namespace API.Twitter
ProcessException(ex, Token, $"data downloading error [{URL}]")
Finally
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 Sub
Private Sub DownloadData_SavedPosts(ByVal Token As CancellationToken)
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")
If files.ListExists Then
ResetFileNameProvider(Math.Max(files.Count.ToString.Length, 3))
@@ -417,21 +453,24 @@ Namespace API.Twitter
#End Region
#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,
- 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"})
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
Dim mUrl$
+ Dim media As UserMedia
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"))
If Not mUrl.IsEmptyString Then
Dim dName$ = UrlFile(mUrl)
If Not dName.IsEmptyString AndAlso Not _DataNames.Contains(dName) Then
_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
@@ -439,15 +478,17 @@ Namespace API.Twitter
End If
End Sub
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
- If CheckForGif(w, PostID, PostDate, State) Then Return True
+ If CheckForGif(w, PostID, PostDate, State, SpecialFolder) Then Return True
Dim URL$ = GetVideoNodeURL(w)
If Not URL.IsEmptyString Then
Dim f$ = UrlFile(URL)
If Not f.IsEmptyString AndAlso Not _DataNames.Contains(f) Then
_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
Return True
End If
@@ -458,7 +499,7 @@ Namespace API.Twitter
End Try
End Function
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
Dim gifUrl As Predicate(Of EContainer) = Function(e) Not e.Value("content_type").IsEmptyString AndAlso
e.Value("content_type").Contains("mp4") AndAlso
@@ -477,9 +518,13 @@ Namespace API.Twitter
If Not ff.IsEmptyString Then
If GifsDownload And Not _DataNames.Contains(ff) Then
m = MediaFromData(url, PostID, PostDate,, State, UTypes.Video)
+ If Not SpecialFolder.IsEmptyString Then m.SpecialFolder = SpecialFolder
f = m.File
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)
End If
Return True
@@ -621,11 +666,12 @@ Namespace API.Twitter
.AutoClear = True,
.AutoReset = True,
.CommandPermanent = $"chcp {BatchExecutor.UnicodeEncoding}",
- .FileExchanger = confCache
+ .FileExchanger = confCache,
+ .DebugMode = True
}
tgdl.FileExchanger.DeleteCacheOnDispose = False
tgdl.FileExchanger.DeleteRootOnDispose = False
- For i As Byte = 0 To 2
+ For i As Byte = 0 To 3
dir = rootDir.NewPath
dir.Exists(SFO.Path, True, EDP.ThrowException)
outList.Add(dir)
@@ -633,9 +679,10 @@ Namespace API.Twitter
command = $"""{Settings.GalleryDLFile}"" --verbose --no-download --no-skip --config ""{conf}"" --write-pages "
command &= GdlGetIdFilterString()
Select Case i
- Case 0 : command &= $"https://twitter.com/{Name}/media" : process = dm.Contains(DownloadModels.Media)
- Case 1 : command &= $"https://twitter.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 0 : command &= $"https://x.com/{Name}/media" : process = dm.Contains(DownloadModels.Media)
+ Case 1 : command &= $"https://x.com/{Name}" : process = dm.Contains(DownloadModels.Profile)
+ 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
End Select
'#If DEBUG Then
@@ -687,13 +734,14 @@ Namespace API.Twitter
End Function
#End Region
#Region "ReparseMissing"
+ Private _ReparseLikes As Boolean = False
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 URL$ = String.Empty
Dim cache As CacheKeeper = Nothing
Try
- If ContentMissingExists Then
+ If ContentMissingExists Or (_ReparseLikes And LikesPosts.Count > 0) Then
Dim m As UserMedia
Dim PostDate$
Dim nodes As List(Of String()) = GetContainerSubnodes()
@@ -702,22 +750,29 @@ Namespace API.Twitter
Dim f As SFile
Dim i%, ii%
Dim files As List(Of SFile)
+ Dim lim%
+ Dim specFolder$ = IIf(_ReparseLikes, "Likes", String.Empty)
ResetFileNameProvider()
If IsSingleObjectDownload Then
cache = Settings.Cache
+ ElseIf _ReparseLikes Then
+ cache = CreateCache()
Else
cache = New CacheKeeper(DownloadContentDefault_GetRootDir.CSFilePS)
cache.CacheDeleteError = CacheDeletionError(cache)
End If
- ProgressPre.ChangeMax(_ContentList.Count)
- For i = 0 To _ContentList.Count - 1
+ If _ReparseLikes Then lim = LikesPosts.Count Else lim = _ContentList.Count
+ ProgressPre.ChangeMax(lim)
+ For i = 0 To lim - 1
ProgressPre.Perform()
- If _ContentList(i).State = UStates.Missing Then
- m = _ContentList(i)
- If Not m.Post.ID.IsEmptyString Or (IsSingleObjectDownload And Not m.URL_BASE.IsEmptyString) Then
+ If _ReparseLikes OrElse _ContentList(i).State = UStates.Missing Then
+ m = If(_ReparseLikes, Nothing, _ContentList(i))
+ If Not m.Post.ID.IsEmptyString Or (IsSingleObjectDownload And Not m.URL_BASE.IsEmptyString) Or _ReparseLikes Then
ThrowAny(Token)
If IsSingleObjectDownload Then
URL = m.URL_BASE
+ ElseIf _ReparseLikes Then
+ URL = LikesPosts(i)
Else
URL = String.Format(SinglePostPattern, Name, m.Post.ID)
End If
@@ -737,7 +792,7 @@ Namespace API.Twitter
If .ListExists Then
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)
End If
End With
@@ -759,7 +814,7 @@ Namespace API.Twitter
ProcessException(ex, Token, $"ReparseMissing error [{URL}]")
Finally
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
rList.Clear()
End If
@@ -856,7 +911,7 @@ Namespace API.Twitter
#End Region
#Region "IDisposable support"
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)
End Sub
#End Region
diff --git a/SCrawler/API/UserDataBind.vb b/SCrawler/API/UserDataBind.vb
index 2de6cd9..d8f4ef4 100644
--- a/SCrawler/API/UserDataBind.vb
+++ b/SCrawler/API/UserDataBind.vb
@@ -558,7 +558,7 @@ Namespace API
End Sub
#End Region
#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")
End Function
Friend Overloads Sub MergeData(ByVal Merging As Boolean)
@@ -601,7 +601,19 @@ Namespace API
"Operation canceled", MsgBoxStyle.Critical)
Return False
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)
AddRemoveBttDeleteHandler(_Item, False)
RaiseEvent OnUserRemoved(_Item)
@@ -618,7 +630,7 @@ Namespace API
End If
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,
- {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 {
.ToolTip = "Users will be removed from the collection and will be displayed in the program as separate users." & vbCr &
"All user data will remain.",
@@ -653,12 +665,31 @@ Namespace API
MsgBoxE({$"Collection [{CollectionName}] data merged{vbCr}Unable to split merged collection{vbCr}Operation canceled", MsgTitle}, vbExclamation)
Return 0
Else
- Collections.ForEach(Sub(ByVal c As IUserData)
- If c.MoveFiles(String.Empty, Nothing) Then
- UserListLoader.UpdateUser(Settings.GetUser(c), True)
- MainFrameObj.ImageHandler(c)
- End If
- End Sub)
+ Dim uu As New List(Of SplitCollectionUserInfo)(Collections.Select(Function(uuu As UserDataBase) uuu.SplitCollectionGetNewUserInfo))
+ 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)
+ MainFrameObj.ImageHandler(c)
+ End If
+ End Sub)
If Collections.All(Function(c) c.CollectionName.IsEmptyString) Then
Settings.Users.Remove(Me)
Collections.Clear()
diff --git a/SCrawler/Download/Automation/AutoDownloader.vb b/SCrawler/Download/Automation/AutoDownloader.vb
index fa4cbe4..32ee52f 100644
--- a/SCrawler/Download/Automation/AutoDownloader.vb
+++ b/SCrawler/Download/Automation/AutoDownloader.vb
@@ -21,6 +21,7 @@ Namespace DownloadObjects
Specified = 3
Groups = 4
End Enum
+ Friend Const NoPauseMode As Integer = -100
Friend Enum PauseModes As Integer
Disabled = -2
Enabled = -1
diff --git a/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb b/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb
index aee5ea9..430c934 100644
--- a/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb
+++ b/SCrawler/Download/Automation/AutoDownloaderEditorForm.Designer.vb
@@ -28,9 +28,10 @@ Namespace DownloadObjects
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 ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
- Dim TP_NOTIFY As System.Windows.Forms.TableLayoutPanel
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
+ Dim TP_NOTIFY As System.Windows.Forms.TableLayoutPanel
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 TT_MAIN As System.Windows.Forms.ToolTip
Me.DEF_GROUP = New SCrawler.DownloadObjects.Groups.GroupDefaults()
Me.OPT_SPEC = New System.Windows.Forms.RadioButton()
@@ -178,9 +179,14 @@ Namespace DownloadObjects
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
ActionButton1.Name = "Edit"
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
- ActionButton2.Name = "Clear"
+ ActionButton2.Name = "Info"
+ ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Info
+ 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.CaptionWidth = 50.0R
Me.TXT_GROUPS.Dock = System.Windows.Forms.DockStyle.Fill
@@ -260,9 +266,9 @@ Namespace DownloadObjects
'
'TXT_TIMER
'
- ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
- ActionButton3.Name = "Refresh"
- Me.TXT_TIMER.Buttons.Add(ActionButton3)
+ ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
+ ActionButton4.Name = "Refresh"
+ Me.TXT_TIMER.Buttons.Add(ActionButton4)
Me.TXT_TIMER.CaptionText = "Timer"
Me.TXT_TIMER.CaptionToolTipEnabled = True
Me.TXT_TIMER.CaptionToolTipText = "Timer (in minutes)"
@@ -275,9 +281,9 @@ Namespace DownloadObjects
'
'NUM_DELAY
'
- ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
- ActionButton4.Name = "Refresh"
- Me.NUM_DELAY.Buttons.Add(ActionButton4)
+ ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
+ ActionButton5.Name = "Refresh"
+ Me.NUM_DELAY.Buttons.Add(ActionButton5)
Me.NUM_DELAY.CaptionText = "Delay"
Me.NUM_DELAY.CaptionToolTipEnabled = True
Me.NUM_DELAY.CaptionToolTipText = "Startup delay"
diff --git a/SCrawler/Download/Automation/AutoDownloaderEditorForm.resx b/SCrawler/Download/Automation/AutoDownloaderEditorForm.resx
index bdebe69..e2ab359 100644
--- a/SCrawler/Download/Automation/AutoDownloaderEditorForm.resx
+++ b/SCrawler/Download/Automation/AutoDownloaderEditorForm.resx
@@ -189,6 +189,18 @@
+
+ iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAIGNIUk0AAHolAACAgwAA+f8AAIDpAAB1
+ MAAA6mAAADqYAAAXb5JfxUYAAAAJcEhZcwAADsQAAA7EAZUrDhsAAAFQSURBVFhH7ZfNDcIwFIMZoXcm
+ YBtGYRHECIgTR1ZhBsS9YoJgQ1Poi5sfqhIOWPqkqvV7dWlI0oVzriry5Dd5HSS0PFwasAEn0AJn4Dle
+ o6fpykaVHYDNwB7YG6ZgzWiQrABosAbqaXNh7bprN1AyAAp3b42msuva9ooGYIFpELA931D2FI+VxzAI
+ gTIdAEb+7KpBz+p4RclQyifoXwdKwgAwcMAl3/mEAOz9GJgokQGyR/sHr8CzlwFwgU+vCuagUQE4gSjz
+ HGxUAM5iyiyxUp4IJ5QEAYomHCvlidCiJAigjKNYKU8M6B/g9wJUH4TV/4ZFE5GV8kSQE1HRVGylPBHC
+ qbh0MbJSnhH0YtQFyFqOiZXyCOLLMQVDckNCrJRHEN+QeMGY3JJZKY8hb0vmxQLTYAplm1IvFNbblnuh
+ Qb0Pk3exGZjv06wW8uT3cIs7jQnSONrSxH0AAAAASUVORK5CYII=
+
+
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
@@ -204,7 +216,7 @@
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.
-
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
@@ -220,7 +232,7 @@ The 'Image' and 'User icon' parameters will be ignored.
VnR1MIwhwMTCyqEQ37qEmZVDFF0OE/9nAACtFF4Ey6OP+wAAAABJRU5ErkJggg==
-
+
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAACOElE
diff --git a/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb b/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb
index eaebf45..0a8390c 100644
--- a/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb
+++ b/SCrawler/Download/Automation/AutoDownloaderEditorForm.vb
@@ -134,11 +134,26 @@ Namespace DownloadObjects
Private Sub TXT_GROUPS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As EventArgs) Handles TXT_GROUPS.ActionOnButtonClick
Select Case Sender.DefaultButton
Case ActionButton.DefaultButtons.Edit
- Using f As New LabelsForm(MyGroups, (From g As DownloadGroup In Settings.Groups Where Not g.IsViewFilter Select 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()
If f.DialogResult = DialogResult.OK Then MyGroups.ListAddList(f.LabelsList, LAP.ClearBeforeAdd) : TXT_GROUPS.Text = MyGroups.ListToString
End Using
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 Sub
Private Sub ChangeEnabled() Handles OPT_DISABLED.CheckedChanged,
diff --git a/SCrawler/Download/Automation/SchedulerEditorForm.vb b/SCrawler/Download/Automation/SchedulerEditorForm.vb
index 1bb045b..112514c 100644
--- a/SCrawler/Download/Automation/SchedulerEditorForm.vb
+++ b/SCrawler/Download/Automation/SchedulerEditorForm.vb
@@ -8,6 +8,8 @@
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Forms
Imports PersonalUtilities.Forms.Toolbars
+Imports PersonalUtilities.Forms.Controls.Base
+Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Tools
Imports ECI = PersonalUtilities.Forms.Toolbars.EditToolbar.ControlItem
Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
@@ -235,6 +237,25 @@ Namespace DownloadObjects
Private Function GetSchedulerFiles() As List(Of SFile)
Return SFile.GetFiles(SettingsFolderName.CSFileP, $"{Scheduler.FileNameDefault}*.xml",, EDP.ReturnValue)
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
Const msgTitle$ = "Change scheduler"
Try
@@ -244,7 +265,7 @@ Namespace DownloadObjects
If .ListExists Then .ForEach(Sub(ff) l.Add(ff, ff.Name.Replace(Scheduler.FileNameDefault, String.Empty).StringTrimStart("_").IfNullOrEmpty(defName)))
End With
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",
.Icon = ImageRenderer.GetIcon(My.Resources.ScriptPic_32, EDP.ReturnValue),
.FormText = "Schedulers",
@@ -256,17 +277,56 @@ Namespace DownloadObjects
Dim f As SFile
Dim selectedName$
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()
- .Buttons = {ADB.Add, ADB.Delete}
+ .Buttons = {ADB.Add, ADB.SaveAs, ADB.Delete}
AddHandler .AddClick, Sub(ByVal obj As Object, ByVal args As SimpleListFormEventArgs)
If addedObj.IsEmptyString Then
addedObj = InputBoxE("Enter a new scheduler name:", msgTitle)
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
MsgBoxE({"You can only create one scheduler at a time", "Create a new scheduler"}, vbCritical)
End If
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
.DataSelectedIndexes.Add(0)
Else
@@ -279,7 +339,7 @@ Namespace DownloadObjects
If selectedName = defName Then
f = Settings.Automation.FileDefault
Else
- f = $"{SettingsFolderName}\{Scheduler.FileNameDefault}_{selectedName.StringRemoveWinForbiddenSymbols}.xml"
+ f = createSchedulerPath.Invoke(selectedName)
End If
If Not Settings.Automation.File = f AndAlso Settings.Automation.Reset(f, False) Then
Settings.Automation.File = f
diff --git a/SCrawler/Download/Groups/DownloadGroup.vb b/SCrawler/Download/Groups/DownloadGroup.vb
index a3d711c..914f25e 100644
--- a/SCrawler/Download/Groups/DownloadGroup.vb
+++ b/SCrawler/Download/Groups/DownloadGroup.vb
@@ -20,6 +20,7 @@ Namespace DownloadObjects.Groups
Private Const Name_FilterViewMode As String = "FilterViewMode"
Private Const Name_FilterGroupUsers As String = "FilterGroupUsers"
Private Const Name_FilterShowGroupsInsteadLabels As String = "FilterShowGroupsInsteadLabels"
+ Private Const Name_FilterShowAllUsers As String = "FilterShowAllUsers"
#End Region
#Region "Declarations"
#Region "Controls"
@@ -36,6 +37,7 @@ Namespace DownloadObjects.Groups
Friend Property FilterViewMode As ViewModes = ViewModes.IconLarge
Friend Property FilterGroupUsers As Boolean = True
Friend Property FilterShowGroupsInsteadLabels As Boolean = True
+ Friend Property FilterShowAllUsers As Boolean = False
#End Region
Private File As SFile = Nothing
Friend Overrides Property Name As String
@@ -144,13 +146,15 @@ Namespace DownloadObjects.Groups
FilterViewMode = e.Value(Name_FilterViewMode).FromXML(Of Integer)(ViewModes.IconLarge)
FilterGroupUsers = e.Value(Name_FilterGroupUsers).FromXML(Of Boolean)(True)
FilterShowGroupsInsteadLabels = e.Value(Name_FilterShowGroupsInsteadLabels).FromXML(Of Boolean)(True)
+ FilterShowAllUsers = e.Value(Name_FilterShowAllUsers).FromXML(Of Boolean)(False)
End If
End Sub
Protected Overrides Function Export(ByVal e As EContainer) As EContainer
MyBase.Export(e)
e.AddRange({New EContainer(Name_FilterViewMode, CInt(FilterViewMode)),
New EContainer(Name_FilterGroupUsers, FilterGroupUsers.BoolToInteger),
- New EContainer(Name_FilterShowGroupsInsteadLabels, FilterShowGroupsInsteadLabels.BoolToInteger)})
+ New EContainer(Name_FilterShowGroupsInsteadLabels, FilterShowGroupsInsteadLabels.BoolToInteger),
+ New EContainer(Name_FilterShowAllUsers, FilterShowAllUsers.BoolToInteger)})
Return e
End Function
#End Region
@@ -166,6 +170,7 @@ Namespace DownloadObjects.Groups
FilterViewMode = .FilterViewMode
FilterGroupUsers = .FilterGroupUsers
FilterShowGroupsInsteadLabels = .FilterShowGroupsInsteadLabels
+ FilterShowAllUsers = .FilterShowAllUsers
End If
End With
End If
diff --git a/SCrawler/Download/Groups/GroupListForm.vb b/SCrawler/Download/Groups/GroupListForm.vb
index 23a5c70..bb5e32d 100644
--- a/SCrawler/Download/Groups/GroupListForm.vb
+++ b/SCrawler/Download/Groups/GroupListForm.vb
@@ -117,6 +117,8 @@ Namespace DownloadObjects.Groups
RefillList()
If Not IsViewFilter Then Settings.Groups.BeginUpdate()
+ If IsViewFilter And LIST_GROUPS.Items.Count > 0 Then .MyOkCancel.EnableOK = True : _LatestSelected = 0
+
.DelegateClosingChecker = False
.EndLoaderOperations()
diff --git a/SCrawler/Editors/GlobalSettingsForm.vb b/SCrawler/Editors/GlobalSettingsForm.vb
index 8369f70..2ca3930 100644
--- a/SCrawler/Editors/GlobalSettingsForm.vb
+++ b/SCrawler/Editors/GlobalSettingsForm.vb
@@ -19,6 +19,7 @@ Namespace Editors
Friend Property HeadersChanged As Boolean = False
Friend Property PictureChanged As Boolean = False
Friend Property EnvironmentProgramsChanged As Boolean = False
+ Friend Property UserAgentChanged As Boolean = False
Friend Sub New()
InitializeComponent()
MyDefs = New DefaultFormOptions(Me, Settings.Design)
@@ -183,6 +184,7 @@ Namespace Editors
"Do you really want to continue?",
"Increasing download tasks"},
vbExclamation,,, {"Confirm", $"Set to default ({SettingsCLS.DefaultMaxDownloadingTasks})", "Cancel"})
+
If CInt(TXT_MAX_JOBS_USERS.Value) > SettingsCLS.DefaultMaxDownloadingTasks Then
Select Case a.Invoke("users", TXT_MAX_JOBS_USERS.Value)
Case 1 : TXT_MAX_JOBS_USERS.Value = SettingsCLS.DefaultMaxDownloadingTasks
@@ -213,6 +215,25 @@ Namespace Editors
"If this case, the functionality of SCrawler will be limited, and some sites will not work at all.",
"Environment missing"}, vbExclamation,,, {"Process", "Cancel"}) = 1 Then Exit Sub
+ If Not .GlobalPath.Value.PathWithSeparator = TXT_GLOBAL_PATH.Text.CSFilePS Or Not .CollectionsPath.Value = TXT_COLLECTIONS_PATH.Text Then
+ If Not Plugin.Hosts.SettingsHostCollection.UpdateHostPath_CheckDownloader Then Exit Sub
+ If MsgBoxE({"You have changed the global path or collections folder!" & vbCr & vbCr &
+ $"Global path ({IIf(.GlobalPath.Value.PathWithSeparator = TXT_GLOBAL_PATH.Text.CSFilePS, "not changed", "CHANGED")})" & vbCr &
+ $"Current: { .GlobalPath.Value}" & vbCr &
+ $"New: {TXT_GLOBAL_PATH.Text}" & vbCr & vbCr &
+ $"Collections folder ({IIf(.CollectionsPath.Value = TXT_COLLECTIONS_PATH.Text, "not changed", "CHANGED")})" & vbCr &
+ $"Current: { .CollectionsPath.Value}" & vbCr &
+ $"New: {TXT_COLLECTIONS_PATH.Text}" & vbCr & vbCr &
+ "Are you sure you want to continue?",
+ "Global path changed"}, vbExclamation,,, {"Process", "Cancel"}) = 0 Then
+ If Not Plugin.Hosts.SettingsHostCollection.UpdateHostPath(.GlobalPath, TXT_GLOBAL_PATH.Text.CSFileP,
+ .CollectionsPath, TXT_COLLECTIONS_PATH.Text) Then _
+ MsgBoxE({"Something went wrong while updating the global paths.", "Global path changed"}, vbCritical)
+ Else
+ Exit Sub
+ End If
+ End If
+
Dim detector As Func(Of IXMLValue, Boolean) = Function(hh) hh.ChangesDetected
.BeginUpdate()
@@ -225,7 +246,7 @@ Namespace Editors
.ChannelsMaxJobsCount.Value = TXT_MAX_JOBS_CHANNELS.Value
.CheckUpdatesAtStart.Value = CH_CHECK_VER_START.Checked
.UserAgent.Value = TXT_USER_AGENT.Text
- DefaultUserAgent = TXT_USER_AGENT.Text
+ UserAgentChanged = .UserAgent.ChangesDetected
.ImgurClientID.Value = TXT_IMGUR_CLIENT_ID.Text
'Design
.ProgramText.Value = TXT_PRG_TITLE.Text
diff --git a/SCrawler/Editors/LabelsForm.vb b/SCrawler/Editors/LabelsForm.vb
index 5888b0b..b43955e 100644
--- a/SCrawler/Editors/LabelsForm.vb
+++ b/SCrawler/Editors/LabelsForm.vb
@@ -27,6 +27,7 @@ Friend Class LabelsForm
End Property
Friend Property WithDeleteButton As Boolean = False
Private ReadOnly AddNoParsed As Boolean = False
+ Friend Property IsGroups As Boolean = False
Friend Sub New(ByVal LabelsArr As IEnumerable(Of String), Optional ByVal AddNoParsed As Boolean = True)
InitializeComponent()
Me.AddNoParsed = AddNoParsed
@@ -65,7 +66,15 @@ Friend Class LabelsForm
End Try
End Sub
Private Sub LabelsForm_KeyDown(sender As Object, e As KeyEventArgs) Handles Me.KeyDown
- If e.KeyCode = Keys.Insert And _Source Is Nothing Then AddNewLabel() : e.Handled = True
+ Dim b As Boolean = True
+ If e.KeyCode = Keys.Insert And _Source Is Nothing Then
+ AddNewLabel()
+ ElseIf e.KeyCode = Keys.F3 And IsGroups Then
+ EditSelectedGroup()
+ Else
+ b = False
+ End If
+ If b Then e.Handled = True
End Sub
Private Sub LabelsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
LabelsList.Clear()
@@ -101,4 +110,20 @@ Friend Class LabelsForm
End If
End If
End Sub
+ Private Sub EditSelectedGroup()
+ Try
+ If CMB_LABELS.Count > 0 And CMB_LABELS.SelectedIndex >= 0 Then
+ Dim gName$ = CMB_LABELS.Value
+ Dim i%
+ If Not gName.IsEmptyString Then
+ i = Settings.Groups.IndexOf(gName)
+ If i >= 0 Then
+ Using f As New DownloadObjects.Groups.GroupEditorForm(Settings.Groups(i)) : f.ShowDialog() : End Using
+ End If
+ End If
+ End If
+ Catch ex As Exception
+ ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Show group")
+ End Try
+ End Sub
End Class
\ No newline at end of file
diff --git a/SCrawler/Editors/SiteEditorForm.vb b/SCrawler/Editors/SiteEditorForm.vb
index da467a3..053cd03 100644
--- a/SCrawler/Editors/SiteEditorForm.vb
+++ b/SCrawler/Editors/SiteEditorForm.vb
@@ -7,6 +7,7 @@
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.Plugin
+Imports SCrawler.Plugin.Attributes
Imports SCrawler.Plugin.Hosts
Imports PersonalUtilities.Forms
Imports PersonalUtilities.Forms.Controls
@@ -15,13 +16,13 @@ Imports PersonalUtilities.Tools.Web.Cookies
Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
Namespace Editors
Friend Class SiteEditorForm
- Private ReadOnly LBL_AUTH As Label
- Private ReadOnly LBL_OTHER As Label
Private WithEvents MyDefs As DefaultFormOptions
Private WithEvents SpecialButton As Button
Private Property Cookies As CookieKeeper
Private ReadOnly CookiesControlsInteraction As List(Of PropertyValueHost)
Private CookiesChanged As Boolean = False
+ Private Const OtherOptionsText As String = "Other Parameters"
+ Private ReadOnly LabelControls As List(Of Label)
#Region "Providers"
Private Class SavedPostsChecker : Inherits AccountsNameChecker
Friend ReadOnly PathControl As TextBoxExtended
@@ -138,6 +139,108 @@ Namespace Editors
Return Nothing
End Function
End Class
+#End Region
+#Region "CatReorder"
+ Private Class CatReorder : Implements IDisposable
+ Private ReadOnly Items As Dictionary(Of String, List(Of PropertyValueHost))
+ Private Const EmptyCat As String = "----"
+ Friend Sub New()
+ Items = New Dictionary(Of String, List(Of PropertyValueHost))
+ End Sub
+ Friend ReadOnly Property Count As Integer
+ Get
+ Return Items.Count
+ End Get
+ End Property
+ Friend Sub Add(ByVal Item As PropertyValueHost)
+ Dim category$ = Item.Category.IfNullOrEmpty(EmptyCat)
+ If Items.ContainsKey(category) Then
+ Items(category).Add(Item)
+ Else
+ Items.Add(category, New List(Of PropertyValueHost) From {Item})
+ End If
+ End Sub
+ Friend Overloads Shared Sub AddToTable(ByRef Form As SiteEditorForm, ByVal cnt As Control, ByVal _height As Integer,
+ ByRef h As Integer, ByRef c As Integer)
+ With Form.TP_SITE_PROPS
+ .RowStyles.Add(New RowStyle(SizeType.Absolute, _height))
+ .RowCount += 1
+ .Controls.Add(cnt, 0, .RowStyles.Count - 1)
+ End With
+ h += _height
+ c += 1
+ End Sub
+ Friend Overloads Sub AddToTable(ByRef Form As SiteEditorForm, ByRef h As Integer, ByRef c As Integer, ByRef offset As Integer)
+ If Items.Count > 0 Then
+ Dim iCount% = Items.Count
+ Dim otherOptionsCat As KeyValuePair(Of String, List(Of PropertyValueHost)) = Nothing
+ Dim otherOptionsCatExists As Boolean = False
+ Dim AuthCat As KeyValuePair(Of String, List(Of PropertyValueHost)) = Nothing
+ Dim AuthCatExists As Boolean = False
+ If Items.Count > 1 Then
+ Dim catIndx% = Items.ListIndexOf(Function(cc) Not cc.Key.IsEmptyString AndAlso (cc.Key = EmptyCat Or cc.Key = OtherOptionsText))
+ If catIndx >= 0 Then
+ otherOptionsCat = New KeyValuePair(Of String, List(Of PropertyValueHost))(Items.Keys(catIndx), Items(Items.Keys(catIndx)))
+ otherOptionsCatExists = True
+ Items.Remove(otherOptionsCat.Key)
+ End If
+ catIndx = Items.ListIndexOf(Function(cc) Not cc.Key.IsEmptyString AndAlso (cc.Key = PropertyOption.CategoryAuth))
+ If catIndx >= 0 Then
+ AuthCat = New KeyValuePair(Of String, List(Of PropertyValueHost))(Items.Keys(catIndx), Items(Items.Keys(catIndx)))
+ AuthCatExists = True
+ Items.Remove(AuthCat.Key)
+ End If
+ End If
+ If AuthCatExists Then AddToTable(Form, iCount, AuthCat, h, c, offset)
+ For Each obj As KeyValuePair(Of String, List(Of PropertyValueHost)) In Items
+ AddToTable(Form, iCount, obj, h, c, offset)
+ Next
+ If otherOptionsCatExists Then AddToTable(Form, iCount, otherOptionsCat, h, c, offset)
+ End If
+ End Sub
+ Private Overloads Sub AddToTable(ByRef Form As SiteEditorForm, ByVal ItemsCount As Integer,
+ ByVal obj As KeyValuePair(Of String, List(Of PropertyValueHost)),
+ ByRef h As Integer, ByRef c As Integer, ByRef offset As Integer)
+ If ItemsCount > 1 And obj.Value.Count > 0 Then
+ Dim category$ = obj.Key.IfNullOrEmpty(OtherOptionsText)
+ If category = EmptyCat Then category = OtherOptionsText
+ Form.LabelControls.Add(New Label With {.Text = category,
+ .TextAlign = ContentAlignment.MiddleCenter,
+ .Dock = DockStyle.Fill})
+ AddToTable(Form, Form.LabelControls.Last, 25, h, c)
+ End If
+ If obj.Value.Count > 0 Then
+ For Each prop As PropertyValueHost In obj.Value
+ With prop
+ If .CookieValueExtractorExists Then Form.CookiesControlsInteraction.Add(prop)
+ .CreateControl(Form.TT_MAIN)
+ AddToTable(Form, .Control, .ControlHeight, h, c)
+ If .LeftOffset > offset Then offset = .LeftOffset
+ If Not .Options.AllowNull Or Not .ProviderFieldsChecker Is Nothing Then _
+ Form.MyDefs.MyFieldsCheckerE.AddControl(.Control, .Options.ControlText, .Type,
+ .Options.AllowNull, .ProviderFieldsChecker)
+ End With
+ Next
+ End If
+ End Sub
+#Region "IDisposable Support"
+ Private disposedValue As Boolean = False
+ Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
+ If Not disposedValue Then
+ If disposing And Items.Count > 0 Then Items.Clear()
+ disposedValue = True
+ End If
+ End Sub
+ Protected Overrides Sub Finalize()
+ Dispose(False)
+ MyBase.Finalize()
+ End Sub
+ Friend Overloads Sub Dispose() Implements IDisposable.Dispose
+ Dispose(True)
+ GC.SuppressFinalize(Me)
+ End Sub
+#End Region
+ End Class
#End Region
Private ReadOnly PropertyValid As Predicate(Of PropertyValueHost) = Function(p) (Not p.IsHidden Or SiteSettingsShowHiddenControls) And Not p.Options Is Nothing
Private ReadOnly Property Host As SettingsHost
@@ -148,8 +251,7 @@ Namespace Editors
Host = h
CookiesControlsInteraction = New List(Of PropertyValueHost)
If Not Host.Responser Is Nothing Then Cookies = Host.Responser.Cookies.Copy
- LBL_AUTH = New Label With {.Text = "Authorization", .TextAlign = ContentAlignment.MiddleCenter, .Dock = DockStyle.Fill}
- LBL_OTHER = New Label With {.Text = "Other Parameters", .TextAlign = ContentAlignment.MiddleCenter, .Dock = DockStyle.Fill}
+ LabelControls = New List(Of Label)
Host.BeginEdit()
End Sub
Private Sub SiteEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load
@@ -191,13 +293,6 @@ Namespace Editors
Dim offset% = PropertyValueHost.LeftOffsetDefault
Dim h% = 0, c% = 0
- Dim AddTpControl As Action(Of Control, Integer) = Sub(ByVal cnt As Control, ByVal _height As Integer)
- TP_SITE_PROPS.RowStyles.Add(New RowStyle(SizeType.Absolute, _height))
- TP_SITE_PROPS.RowCount += 1
- TP_SITE_PROPS.Controls.Add(cnt, 0, TP_SITE_PROPS.RowStyles.Count - 1)
- h += _height
- c += 1
- End Sub
If Host.Responser Is Nothing Then
h -= 28
@@ -214,41 +309,17 @@ Namespace Editors
Dim laAdded As Boolean = False
Dim loAdded As Boolean = False
- Dim pArr() As Boolean
- If .PropList.Exists(Function(p) If(p.Options?.IsAuth, False)) Then pArr = {True, False} Else pArr = {False}
If .PropList.Exists(Function(p) p.ControlNumber >= 0) Then .PropList.Sort()
- For Each pAuth As Boolean In pArr
+ Using pc As New CatReorder
For Each prop As PropertyValueHost In .PropList
- If PropertyValid.Invoke(prop) Then
- With prop
- If .Options.IsAuth = pAuth Then
-
- If .CookieValueExtractorExists Then CookiesControlsInteraction.Add(prop)
-
- If pArr.Length = 2 Then
- Select Case pAuth
- Case True
- If Not laAdded Then AddTpControl(LBL_AUTH, 25) : laAdded = True
- Case False
- If Not loAdded Then AddTpControl(LBL_OTHER, 25) : loAdded = True
- End Select
- End If
-
- .CreateControl(TT_MAIN)
- AddTpControl(.Control, .ControlHeight)
- If .LeftOffset > offset Then offset = .LeftOffset
- If Not .Options.AllowNull Or Not .ProviderFieldsChecker Is Nothing Then _
- MyDefs.MyFieldsCheckerE.AddControl(.Control, .Options.ControlText, .Type,
- .Options.AllowNull, .ProviderFieldsChecker)
- End If
- End With
- End If
+ If PropertyValid.Invoke(prop) Then pc.Add(prop)
Next
- Next
+ If pc.Count > 0 Then pc.AddToTable(Me, h, c, offset)
+ End Using
End If
SpecialButton = .GetSettingsButtonInternal
- If Not SpecialButton Is Nothing Then AddTpControl(SpecialButton, 28)
+ If Not SpecialButton Is Nothing Then CatReorder.AddToTable(Me, SpecialButton, 28, h, c)
TP_SITE_PROPS.BaseControlsPadding = New Padding(offset, 0, 0, 0)
offset += PaddingE.GetOf({TP_SITE_PROPS}).Left
TXT_PATH.CaptionWidth = offset
@@ -290,8 +361,7 @@ Namespace Editors
If Host.PropList.Count > 0 Then Host.PropList.ForEach(Sub(p) p.DisposeControl())
If Not SpecialButton Is Nothing Then SpecialButton.Dispose()
CookiesControlsInteraction.Clear()
- LBL_AUTH.Dispose()
- LBL_OTHER.Dispose()
+ LabelControls.ListClearDispose
Host.EndEdit()
If Not Cookies Is Nothing Then Cookies.Dispose()
End Sub
@@ -317,6 +387,13 @@ Namespace Editors
Next
End If
+ If TXT_PATH.Text.IsEmptyString Then TXT_PATH.Text = .PathGenerate.CSFilePS
+ If Not .Path.PathWithSeparator = TXT_PATH.Text Then
+ If Not SettingsHostCollection.UpdateHostPath_CheckDownloader Then Exit Sub
+ If Not SettingsHostCollection.UpdateHostPath(.Self, .Path, TXT_PATH.Text.CSFileP, True) Then _
+ MsgBoxE({"Something went wrong while updating the site path.", "Site path changed"}, vbCritical)
+ End If
+
SiteDefaultsFunctions.SetPropByChecker(TP_SITE_PROPS, Host)
If TXT_PATH.IsEmptyString Then .Path = Nothing Else .Path = TXT_PATH.Text
.SavedPostsPath = TXT_PATH_SAVED_POSTS.Text
diff --git a/SCrawler/MainFrame.vb b/SCrawler/MainFrame.vb
index f9006e5..b3c3241 100644
--- a/SCrawler/MainFrame.vb
+++ b/SCrawler/MainFrame.vb
@@ -325,9 +325,10 @@ CloseResume:
TrayIcon.Visible = .CloseToTray
If f.EnvironmentProgramsChanged Then Settings.UpdateEnvironmentPrograms()
If f.FeedParametersChanged And Not MyFeed Is Nothing Then MyFeed.UpdateSettings()
- If f.HeadersChanged Then
+ If f.HeadersChanged Or (f.UserAgentChanged And Not Settings.UserAgent.IsEmptyString) Then
Settings.BeginUpdate()
- Settings.Plugins.ForEach(Sub(p) p.Settings.UpdateInheritance())
+ If f.UserAgentChanged Then Settings.UpdatePluginsUserAgent(False)
+ If f.HeadersChanged Then Settings.Plugins.ForEach(Sub(p) p.Settings.UpdateInheritance())
Settings.EndUpdate()
End If
UpdateSilentButtons()
@@ -781,6 +782,7 @@ CloseResume:
f.FilterViewMode = Settings.ViewMode
f.FilterGroupUsers = Settings.GroupUsers
f.FilterShowGroupsInsteadLabels = Settings.ShowGroupsInsteadLabels
+ f.FilterShowAllUsers = Settings.ShowAllUsers
f.Name = fName
Settings.Groups.Add(f, isFilter, True)
MsgBoxE({$"The '{fName}' {IIf(isFilter, "filter", "group")} has been saved", $"Save {IIf(isFilter, "filter", "group")}"})
@@ -825,8 +827,11 @@ CloseResume:
Settings.ViewMode.Value = .FilterViewMode
Settings.GroupUsers.Value = .FilterGroupUsers
Settings.ShowGroupsInsteadLabels.Value = .FilterShowGroupsInsteadLabels
+ Settings.ShowAllUsers.Value = .FilterShowAllUsers
End With
ApplyViewPattern(Settings.ViewMode.Value, True)
+ Else
+ Settings.ShowAllUsers.Value = False
End If
Settings.AdvancedFilter.Copy(filter)
Settings.AdvancedFilter.UpdateFile()
diff --git a/SCrawler/MainMod.vb b/SCrawler/MainMod.vb
index f95ee96..b9ec1d0 100644
--- a/SCrawler/MainMod.vb
+++ b/SCrawler/MainMod.vb
@@ -80,7 +80,6 @@ Friend Module MainMod
Friend ReadOnly SessionDateTimeProvider As New ADateTime("yyyyMMdd_HHmmss")
Friend ReadOnly FeedVideoLengthProvider As New ADateTime("hh\:mm\:ss") With {.TimeParseMode = ADateTime.TimeModes.TimeSpan}
Friend ReadOnly LogConnector As New LogHost
- Friend DefaultUserAgent As String = String.Empty
Friend SiteSettingsShowHiddenControls As Boolean = False
#Region "NonExistingUsersLog"
Friend ReadOnly NonExistingUsersLog As New TextSaver($"LOGs\NonExistingUsers.txt") With {.LogMode = True, .AutoSave = True}
diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb
index c9017bd..7403443 100644
--- a/SCrawler/My Project/AssemblyInfo.vb
+++ b/SCrawler/My Project/AssemblyInfo.vb
@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below:
'
-
-
+
+
diff --git a/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb b/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb
index 24795d4..aa998d9 100644
--- a/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb
+++ b/SCrawler/PluginsEnvironment/Hosts/PropertyValueHost.vb
@@ -54,6 +54,7 @@ Namespace Plugin.Hosts
#Region "Control"
Friend Property Control As Control
Friend Property ControlNumber As Integer = -1
+ Friend Property Category As String = String.Empty
Friend ReadOnly Property ControlHeight As Integer
Get
If Not Control Is Nothing Then
@@ -333,6 +334,8 @@ Namespace Plugin.Hosts
If DirectCast(Member, PropertyInfo).PropertyType Is GetType(PropertyValue) Then
UpdateMember()
Options = Member.GetCustomAttribute(Of PropertyOption)()
+ Category = If(Options?.Category, String.Empty)
+ If Category.IsEmptyString Then Category = If(Member.GetCustomAttribute(Of ComponentModel.CategoryAttribute)?.Category, String.Empty)
IsTaskCounter = Not Member.GetCustomAttribute(Of TaskCounter)() Is Nothing
IsHidden = If(Member.GetCustomAttribute(Of HiddenControlAttribute)?.IsHidden, False)
With Member.GetCustomAttribute(Of PXML)
diff --git a/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb
index 2517607..5a7371d 100644
--- a/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb
+++ b/SCrawler/PluginsEnvironment/Hosts/SettingsHost.vb
@@ -219,10 +219,10 @@ Namespace Plugin.Hosts
Friend ReadOnly Property DownloadImages As XMLValue(Of Boolean)
Friend ReadOnly Property DownloadVideos As XMLValue(Of Boolean)
Private ReadOnly _Path As XMLValue(Of SFile)
- Friend Property Path(Optional ByVal SetProp As Boolean = True) As SFile
+ Friend Property Path(Optional ByVal SetProp As Boolean = True, Optional ByVal GetActualValue As Boolean = False) As SFile
Get
- If _Path.IsEmptyString Then
- Dim tmpPath As SFile = SFile.GetPath($"{Settings.GlobalPath.Value.PathWithSeparator}{Source.Site}")
+ If Not GetActualValue And _Path.IsEmptyString Then
+ Dim tmpPath As SFile = PathGenerate()
If SetProp Then _Path.Value = tmpPath Else Return tmpPath
End If
Return _Path.Value
@@ -231,6 +231,9 @@ Namespace Plugin.Hosts
_Path.Value = NewPath
End Set
End Property
+ Friend Function PathGenerate() As SFile
+ Return SFile.GetPath($"{Settings.GlobalPath.Value.PathWithSeparator}{Source.Site}")
+ End Function
Friend Const SavedPostsFolderName As String = "!Saved"
Private ReadOnly _SavedPostsPath As XMLValue(Of SFile)
Friend Property SavedPostsPath(Optional ByVal GetAny As Boolean = True) As SFile
diff --git a/SCrawler/PluginsEnvironment/Hosts/SettingsHostCollection.vb b/SCrawler/PluginsEnvironment/Hosts/SettingsHostCollection.vb
index 18101ce..a485b1f 100644
--- a/SCrawler/PluginsEnvironment/Hosts/SettingsHostCollection.vb
+++ b/SCrawler/PluginsEnvironment/Hosts/SettingsHostCollection.vb
@@ -25,6 +25,7 @@ Namespace Plugin.Hosts
Private ReadOnly Hosts As List(Of SettingsHost)
Private ReadOnly HostsUnavailableIndexes As List(Of Integer)
Private ReadOnly HostsXml As List(Of XmlFile)
+ Private Const NoPauseMode As Integer = DownloadObjects.AutoDownloader.NoPauseMode
#Region "Controls"
Private WithEvents BTT_SETTINGS As ToolStripMenuItem
Private BTT_SETTINGS_SEP_1 As ToolStripSeparator
@@ -226,8 +227,7 @@ Namespace Plugin.Hosts
''' 1 - error
'''
Private Function Hosts_Deleted_MoveAcc(ByVal Obj As SettingsHost) As Integer
- Const np% = -100
- Dim p As PauseModes = np
+ Dim p As PauseModes = NoPauseMode
Dim changedUsers As New List(Of String)
Try
With Settings
@@ -294,10 +294,10 @@ Namespace Plugin.Hosts
.UpdateUsersList()
End If
Else
- p = np
+ p = NoPauseMode
End If
Else
- p = np
+ p = NoPauseMode
End If
End With
Return 0
@@ -309,11 +309,13 @@ Namespace Plugin.Hosts
End If
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, msg, 1)
Finally
- If p <> np Then Settings.Automation.Pause = p
+ If p <> NoPauseMode Then Settings.Automation.Pause = p
End Try
End Function
- Friend Shared Sub UpdateUserAccount(ByRef ChangingUser As UserInfo, ByVal HostOld As SettingsHost, ByVal HostNew As SettingsHost,
- ByVal UpdateUserInTheList As Boolean, Optional ByRef UserIndex As Integer = -1)
+ Friend Shared Function UpdateUserAccount(ByRef ChangingUser As UserInfo, ByVal HostOld As SettingsHost, ByVal HostNew As SettingsHost,
+ ByVal UpdateUserInTheList As Boolean, Optional ByRef UserIndex As Integer = -1,
+ Optional ByVal ForceCollections As Boolean = False) As Boolean
+ Dim result As Boolean = False
With Settings
UserIndex = .UsersList.IndexOf(ChangingUser)
If UserIndex = -1 Then
@@ -322,16 +324,17 @@ Namespace Plugin.Hosts
Dim processUserPath As Boolean
Dim samePath As Boolean = HostOld.Path(False) = HostNew.Path(False)
With ChangingUser
- If Not samePath AndAlso .SpecialPath.IsEmptyString AndAlso .SpecialCollectionPath.IsEmptyString Then
+ If (Not samePath Or ForceCollections) AndAlso .SpecialPath.IsEmptyString AndAlso .SpecialCollectionPath.IsEmptyString Then
processUserPath = False
If .IncludedInCollection Then
If Not .IsVirtual Then
.SpecialCollectionPath = .GetCollectionRootPath
+ result = True
Else
- processUserPath = True
+ If Not samePath Then processUserPath = True
End If
End If
- If Not .IncludedInCollection Or processUserPath Then .SpecialPath = .File.CutPath.PathWithSeparator
+ If Not .IncludedInCollection Or processUserPath Then .SpecialPath = .File.CutPath.PathWithSeparator : result = True
End If
End With
ChangingUser.AccountName = HostNew.AccountName
@@ -339,7 +342,108 @@ Namespace Plugin.Hosts
If UpdateUserInTheList Then .UsersList(UserIndex) = ChangingUser
End If
End With
- End Sub
+ Return result
+ End Function
+ Friend Shared Function UpdateHostPath_CheckDownloader() As Boolean
+ If Downloader.Working Then
+ MsgBoxE({"You cannot change global paths while the downloader is working!", "Changing paths"}, vbCritical)
+ Return False
+ Else
+ Return True
+ End If
+ End Function
+ Friend Overloads Shared Function UpdateHostPath(ByVal PathOld As SFile, ByVal PathNew As SFile,
+ ByVal ColNameOld As String, ByVal ColNameNew As String) As Boolean
+ Dim p As PauseModes = NoPauseMode
+ Try
+ If UpdateHostPath_CheckDownloader() Then Return False
+ If Not AEquals(Of String)(PathOld.PathWithSeparator, PathNew.PathWithSeparator) Or Not AEquals(Of String)(ColNameOld, ColNameNew) Then
+ p = Settings.Automation.Pause
+ Settings.Automation.Pause = PauseModes.Unlimited
+ With Settings.Plugins
+ If .Count > 0 Then
+ Dim h As SettingsHost
+ For Each plugin As PluginHost In .Self
+ If plugin.Settings.Count > 0 Then
+ For Each h In plugin.Settings
+ If Not UpdateHostPath(h, PathOld, PathNew, False, False, Not ColNameOld = ColNameNew) Then Return False
+ Next
+ End If
+ Next
+ End If
+ End With
+ End If
+ Return True
+ Catch ex As Exception
+ Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[SettingsHostCollection.UpdateHostPath]", False)
+ Finally
+ If p <> NoPauseMode Then Settings.Automation.Pause = p
+ End Try
+ End Function
+ Friend Overloads Shared Function UpdateHostPath(ByVal Host As SettingsHost, ByVal PathOld As SFile, ByVal PathNew As SFile,
+ Optional ByVal Abs As Boolean = True,
+ Optional ByVal PauseDownloader As Boolean = True,
+ Optional ByVal ForceCollections As Boolean = False) As Boolean
+ Dim p As PauseModes = NoPauseMode
+ Try
+ If UpdateHostPath_CheckDownloader() Then Return False
+ If Not PathNew.IsEmptyString And Settings.UsersList.Count > 0 Then
+ Dim hp As SFile = Host.Path(False, True)
+ Dim diffPaths As Boolean = (Abs And hp.PathWithSeparator = PathOld.PathWithSeparator) Or
+ (Not Abs And hp.PathWithSeparator.StartsWith(PathOld.PathWithSeparator))
+ If Not hp.IsEmptyString AndAlso (diffPaths Or ForceCollections) Then
+ If PauseDownloader Then
+ p = Settings.Automation.Pause
+ Settings.Automation.Pause = PauseModes.Unlimited
+ End If
+ Dim checkAccName As Func(Of UserInfo, Boolean) = Function(u) _
+ (
+ (Host.AccountName.IsEmptyString Or Host.AccountName = SettingsHost.NameAccountNameDefault) And
+ (u.AccountName.IsEmptyString Or u.AccountName = SettingsHost.NameAccountNameDefault)
+ ) Or
+ (Host.AccountName = u.AccountName)
+ Dim tUser As UserInfo, tUserNew As UserInfo
+ Dim tUserBase As UserDataBase
+ Dim i%
+ Dim newHost As SettingsHost = Nothing
+ Dim userListUpdated As Boolean = False
+ For i = 0 To Settings.UsersList.Count - 1
+ tUser = Settings.UsersList(i)
+ tUserNew = tUser
+ If tUser.Plugin = Host.Key And checkAccName.Invoke(tUser) Then
+ If newHost Is Nothing Then
+ newHost = Host.Clone
+ newHost.AccountName = Host.AccountName
+ If Abs Then
+ newHost.Path = PathNew
+ Else
+ newHost.Path = $"{PathNew.PathWithSeparator}{Host.Source.Site}".CSFileP
+ End If
+ End If
+ If UpdateUserAccount(tUserNew, Host, newHost, False,, ForceCollections) Then
+ tUserBase = Settings.GetUser(tUser)
+ If Not tUserBase Is Nothing Then tUserBase.User = tUserNew : tUserBase.UpdateUserInformation(True)
+ Settings.UsersList(i) = tUserNew
+ userListUpdated = True
+ End If
+ End If
+ Next
+ newHost.DisposeIfReady(False)
+ If userListUpdated Then Settings.UpdateUsersList()
+ If Abs Then
+ Host.Path = PathNew
+ Else
+ Host.Path = $"{PathNew.PathWithSeparator}{Host.Source.Site}".CSFileP
+ End If
+ End If
+ End If
+ Return True
+ Catch ex As Exception
+ Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[SettingsHostCollection.UpdateHostPath(HOST)]", False)
+ Finally
+ If p <> NoPauseMode Then Settings.Automation.Pause = p
+ End Try
+ End Function
#End Region
#Region "Count, Item"
Friend ReadOnly Property Count As Integer Implements IMyEnumerator(Of SettingsHost).MyEnumeratorCount
diff --git a/SCrawler/SCrawler.vbproj b/SCrawler/SCrawler.vbproj
index 4b5f446..d565a15 100644
--- a/SCrawler/SCrawler.vbproj
+++ b/SCrawler/SCrawler.vbproj
@@ -173,6 +173,19 @@
+
+
+ SplitCollectionUserInfoChangePathsForm.vb
+
+
+ Form
+
+
+ SplitCollectionUserInfoPathForm.vb
+
+
+ Form
+
@@ -518,6 +531,12 @@
InternalSettingsForm.vb
+
+ SplitCollectionUserInfoChangePathsForm.vb
+
+
+ SplitCollectionUserInfoPathForm.vb
+
My.Resources
ResXFileCodeGenerator
diff --git a/SCrawler/SettingsCLS.vb b/SCrawler/SettingsCLS.vb
index 11cc414..f67a475 100644
--- a/SCrawler/SettingsCLS.vb
+++ b/SCrawler/SettingsCLS.vb
@@ -54,6 +54,14 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
a.Invoke(CurlFile)
Plugins.ForEach(Sub(p) p.Settings.UpdateEnvironmentPrograms(EnvironmentProgramsList, CMDEncoding.Value))
End Sub
+ Friend Sub UpdatePluginsUserAgent(Optional ByVal InvokeUpdate As Boolean = True)
+ If Not UserAgent.IsEmptyString Then
+ If InvokeUpdate Then BeginUpdate()
+ Dim __userAgent$ = UserAgent
+ Plugins.ForEach(Sub(p) p.Settings.ListForEach(Sub(ps, psi) ps.Source.UserAgentDefault = __userAgent))
+ If InvokeUpdate Then EndUpdate()
+ End If
+ End Sub
Friend Class ProgramFile
Friend Const File_FFMPEG As String = "ffmpeg.exe"
Friend Const File_YTDLP As String = "yt-dlp.exe"
@@ -244,7 +252,6 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
MaxUsersJobsCount = New XMLValue(Of Integer)("MaxJobsCount", DefaultMaxDownloadingTasks, MyXML, n)
UserAgent = New XMLValue(Of String)("UserAgent",, MyXML, n)
If Not SettingsReoranized Then UserAgent.Value = New XMLValue(Of String)("UserAgent",, MyXML).Value 'URGENT: remove this line
- If Not UserAgent.IsEmptyString Then DefaultUserAgent = UserAgent
ImgurClientID = New XMLValue(Of String)("ImgurClientID", String.Empty, MyXML, {Name_Node_Sites})
'Basis: new version
@@ -462,6 +469,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Plugins.AddRange(tmpPluginList)
End If
UpdateEnvironmentPrograms()
+ UpdatePluginsUserAgent(False)
#End Region
Labels = New LabelsKeeper(MyXML)
@@ -1001,7 +1009,7 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Friend ReadOnly Property CollectionsPathF As SFile
Get
If GlobalPath.IsEmptyString Then
- Throw New ArgumentNullException("GlobalPath", "GlobalPath not set")
+ Throw New ArgumentNullException("GlobalPath", "Global path not set")
Else
Return SFile.GetPath($"{GlobalPath.Value.PathWithSeparator}{CollectionsPath.Value}")
End If