From d0d8e5470e28cfb925e6ada144f5683882e16e3c Mon Sep 17 00:00:00 2001 From: Andy <88590076+AAndyProgram@users.noreply.github.com> Date: Sat, 14 Feb 2026 14:24:03 +0300 Subject: [PATCH] 2026.2.14.0 TokenBatch: add 'MyWorkingDirectory' property API.Instagram: update 'ID' extraction; reduce the number of downloaded stories (GQL) to 5 API.Twitter: get a new username based on the user ID API.XHamster: videos aren't downloading Feed: fix a bug when removing from favorites --- Changelog.md | 21 +++- SCrawler/API/Base/TokenBatch.vb | 5 + SCrawler/API/Instagram/Declarations.vb | 1 + .../API/Instagram/EditorExchangeOptions.vb | 4 + SCrawler/API/Instagram/SiteSettings.vb | 15 +++ SCrawler/API/Instagram/UserData.GQL.vb | 92 +++++++++++------- SCrawler/API/Instagram/UserData.vb | 53 +++++++--- SCrawler/API/Twitter/UserData.vb | 96 ++++++++++++++++--- SCrawler/API/Xhamster/UserData.vb | 7 +- SCrawler/Download/Feed/DownloadFeedForm.vb | 88 +++++++++-------- SCrawler/Download/Feed/FeedMedia.vb | 15 +-- SCrawler/My Project/AssemblyInfo.vb | 4 +- 12 files changed, 287 insertions(+), 114 deletions(-) diff --git a/Changelog.md b/Changelog.md index 9e5e950..db0a628 100644 --- a/Changelog.md +++ b/Changelog.md @@ -2,13 +2,30 @@ - [ffmpeg](https://github.com/AAndyProgram/SCrawler/wiki/Settings#ffmpeg) - x64 version - [release](https://github.com/GyanD/codexffmpeg/releases/tag/5.1.2); [zip](https://github.com/GyanD/codexffmpeg/releases/download/5.1.2/ffmpeg-5.1.2-full_build.zip); **version `5.1.2-full_build-www.gyan.dev`** - x86 version - [release](https://github.com/yt-dlp/FFmpeg-Builds/releases/tag/autobuild-2022-11-30-12-57); [zip](https://github.com/yt-dlp/FFmpeg-Builds/releases/download/autobuild-2022-11-30-12-57/ffmpeg-N-109274-gd7a5f068c2-win32-gpl.zip); **version `N-109457-geeb280f351-20221226`** -- [Gallery-dl](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl) - **1.31.4** -- [YT-DLP](https://github.com/AAndyProgram/SCrawler/wiki/Settings#yt-dlp) - **2025.12.08** +- [Gallery-dl](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl) - **1.31.6** +- [YT-DLP](https://github.com/AAndyProgram/SCrawler/wiki/Settings#yt-dlp) - **2026.02.04.233607** - [Deno](https://github.com/AAndyProgram/SCrawler/wiki/Settings#deno) - latest *(`2.0.0` or higher)* - [OF-Scraper](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper) - **3.12.9** ([release](https://github.com/datawhores/OF-Scraper/releases/tag/3.12.9)) # 2026 +## 2026.2.14.0 + +*2026-02-14* + +- Added + - Sites: + - Twitter: get a new username based on the user ID + - Minor improvements +- Updated + - gallery-dl up to version **1.31.6** + - yt-dlp up to version **2026.02.04.233607** +- Fixed + - Sites: + - **Instagram: some profiles aren't downloading** + - xHamster: videos aren't downloading + - Minor bugs + ## 2026.1.24.0 *2026-01-24* diff --git a/SCrawler/API/Base/TokenBatch.vb b/SCrawler/API/Base/TokenBatch.vb index 9cf0332..16e8812 100644 --- a/SCrawler/API/Base/TokenBatch.vb +++ b/SCrawler/API/Base/TokenBatch.vb @@ -13,12 +13,17 @@ Namespace API.Base Friend Property TempPostsList As List(Of String) Protected ReadOnly Token As CancellationToken Friend Property DebugMode As Boolean = False + Friend Overridable Property MyWorkingDirectory As SFile = Nothing Friend Sub New(ByVal _Token As CancellationToken, Optional ByVal _MainProcessName As String = Nothing, Optional ByVal WorkingDir As SFile = Nothing) MyBase.New(True) Token = _Token MainProcessName = _MainProcessName If Not WorkingDir.IsEmptyString Then ChangeDirectory(WorkingDir) End Sub + Public Overrides Sub ChangeDirectory(ByVal Directory As SFile) + MyBase.ChangeDirectory(Directory) + If Not Directory.IsEmptyString Then MyWorkingDirectory = Directory + End Sub Protected Overrides Function Internal_Execute(ByVal Commands As IEnumerable(Of String), ByVal e As ErrorsDescriber) As Boolean If Not Encoding.HasValue Then Encoding = UnicodeEncoding Return MyBase.Internal_Execute(Commands, e) diff --git a/SCrawler/API/Instagram/Declarations.vb b/SCrawler/API/Instagram/Declarations.vb index 00c5fbf..ec24710 100644 --- a/SCrawler/API/Instagram/Declarations.vb +++ b/SCrawler/API/Instagram/Declarations.vb @@ -20,6 +20,7 @@ Namespace API.Instagram Friend Const PageTokenRegexPatternDefault As String = "\[\],{""token"":""(.*?)""},\d+\]" Friend ReadOnly Regex_UserToken_dtsg As RParams = RParams.DMS("DTSGInitialData["":,.\[\]]*?{\s*.token.:\s*""([^""]+)", 1, EDP.ReturnValue) Friend ReadOnly Regex_UserToken_lsd As RParams = RParams.DMS("LSD["":,.\[\]]*?{\s*.token.:\s*""([^""]+)", 1, EDP.ReturnValue) + Friend ReadOnly Regex_ProfileID As RParams = RParams.DMS("profilePage_(\d+)", 1, EDP.ReturnValue) Friend Sub UpdateResponser(ByVal Source As IResponse, ByRef Destination As Responser, ByVal UpdateWwwClaim As Boolean) Const r_wwwClaimName$ = "x-ig-set-www-claim" Const r_tokenName$ = SiteSettings.Header_CSRF_TOKEN_COOKIE diff --git a/SCrawler/API/Instagram/EditorExchangeOptions.vb b/SCrawler/API/Instagram/EditorExchangeOptions.vb index eaa4523..e2fa543 100644 --- a/SCrawler/API/Instagram/EditorExchangeOptions.vb +++ b/SCrawler/API/Instagram/EditorExchangeOptions.vb @@ -36,6 +36,8 @@ Namespace API.Instagram Friend Property PutImageVideoFolder As Boolean Friend Overrides Property UserName As String + + Friend Property IsVerifiedProfile As Boolean = False Friend Property ForceUpdateUserName As Boolean = False @@ -57,6 +59,8 @@ Namespace API.Instagram PutImageVideoFolder = .PutImageVideoFolder + IsVerifiedProfile = .IsVerifiedProfile + ForceUpdateUserName = .ForceUpdateUserName ForceUpdateUserInfo = .ForceUpdateUserInfo End With diff --git a/SCrawler/API/Instagram/SiteSettings.vb b/SCrawler/API/Instagram/SiteSettings.vb index c2e3af7..e3a32e8 100644 --- a/SCrawler/API/Instagram/SiteSettings.vb +++ b/SCrawler/API/Instagram/SiteSettings.vb @@ -148,6 +148,8 @@ Namespace API.Instagram #End Region Friend ReadOnly Property USE_GQL As PropertyValue + + Friend ReadOnly Property USE_GQL_UserData As PropertyValue #End Region #Region "Download data" @@ -165,6 +167,14 @@ Namespace API.Instagram Friend ReadOnly Property DownloadTagged As PropertyValue Private ReadOnly Property DownloadTagged_Def As PropertyValue + + Friend ReadOnly Property PostNumberVerified As PropertyValue + + Private ReadOnly Property PostNumberVerifiedProvider As IFormatProvider + + Friend ReadOnly Property PostNumberVerifiedNot As PropertyValue + + Private ReadOnly Property PostNumberVerifiedNotProvider As IFormatProvider #End Region #Region "Timers" Friend Const TimersUrgentTip As String = vbCr & "It is highly recommended not to change the default value." @@ -485,6 +495,7 @@ Namespace API.Instagram HH_IG_WWW_CLAIM_USE_DEFAULT_ALGO = New PropertyValue(True) TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider USE_GQL = New PropertyValue(False) + USE_GQL_UserData = New PropertyValue(True) DownloadTimeline = New PropertyValue(True) DownloadTimeline_Def = New PropertyValue(DownloadTimeline.Value, GetType(Boolean)) @@ -496,6 +507,10 @@ Namespace API.Instagram DownloadStoriesUser_Def = New PropertyValue(DownloadStoriesUser.Value, GetType(Boolean)) DownloadTagged = New PropertyValue(False) DownloadTagged_Def = New PropertyValue(DownloadTagged.Value, GetType(Boolean)) + PostNumberVerified = New PropertyValue(50) + PostNumberVerifiedProvider = New TimersChecker(12) + PostNumberVerifiedNot = New PropertyValue(12) + PostNumberVerifiedNotProvider = New TimersChecker(12) RequestsWaitTimer_Any = New PropertyValue(1000) RequestsWaitTimer_AnyProvider = New TimersChecker(0) diff --git a/SCrawler/API/Instagram/UserData.GQL.vb b/SCrawler/API/Instagram/UserData.GQL.vb index ddccb8b..22cde45 100644 --- a/SCrawler/API/Instagram/UserData.GQL.vb +++ b/SCrawler/API/Instagram/UserData.GQL.vb @@ -6,12 +6,13 @@ ' ' This program is distributed in the hope that it will be useful, ' but WITHOUT ANY WARRANTY +Imports System.Security.Cryptography Imports System.Threading -Imports SCrawler.API.Base -Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.RegularExpressions +Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Tools.Web.Clients Imports PersonalUtilities.Tools.Web.Documents.JSON +Imports SCrawler.API.Base Namespace API.Instagram Partial Friend Class UserData #Region "Tokens" @@ -43,9 +44,9 @@ Namespace API.Instagram Private Const GQL_UserStories_DocId As String = "25231722019806941" Private Const GQL_UserStories_FbFriendlyName As String = "PolarisStoriesV3ReelPageStandaloneQuery" - Private Const GQL_Timeline_DocId As String = "7268577773270422" + Private Const GQL_Timeline_DocId As String = "7268577773270422" '"34579740524958711" '"7268577773270422" Private Const GQL_Timeline_FbFriendlyName As String = "PolarisProfilePostsQuery" - Private Const GQL_Timeline_DocId_Second As String = "7286316061475375" + Private Const GQL_Timeline_DocId_Second As String = "7286316061475375" '"33944389991841132" '"7286316061475375" Private Const GQL_Timeline_FbFriendlyName_Second As String = "PolarisProfilePostsTabContentQuery_connection" Private Const GQL_Reels_DocId As String = "7191572580905225" @@ -64,33 +65,42 @@ Namespace API.Instagram Responser.Headers.Add(GQL_HEADER_FB_FRINDLY_NAME, HeaderValue) Responser.Headers.Add(GQL_HEADER_FB_LSD, Token_lsd) End Sub - - Private Sub GetUserDataGQL(ByVal Token As CancellationToken) + ' + Private Function GetUserDataGQL(ByVal Token As CancellationToken) As String Dim vars$ = String.Format(GQL_URL_PATTERN_VARS, GQL_UserData_DocId, Token_lsd, Token_dtsg_Var, GQL_UserData_FbFriendlyName, SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""id"":""{ID}"",""relay_header"":false,""render_surface"":""PROFILE""" & "}")) UpdateRequestNumber() ChangeResponserMode(True) UpdateHeadersGQL(GQL_UserData_FbFriendlyName) Dim r$ = Responser.GetResponse(GQL_URL, vars) - If Not r.IsEmptyString Then - Using j As EContainer = JsonDocument.Parse(r) - If j.ListExists Then - With j({"data", "user"}) - If .ListExists Then - UserSiteName = .Value("full_name").IfNullOrEmpty(UserSiteName) - Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"} - Dim pic$ = .Value({"hd_profile_pic_url_info"}, "url").IfNullOrEmpty(.Value("profile_pic_url")) - If Not pic.IsEmptyString Then GetWebFile(pic, f, EDP.ReturnValue) - UserDescriptionUpdate(.Value("biography")) - End If - End With - End If - End Using - End If - End Sub + Return r + 'If Not r.IsEmptyString Then + ' Using j As EContainer = JsonDocument.Parse(r) + ' If j.ListExists Then + ' With j({"data", "user"}) + ' If .ListExists Then + ' UserSiteName = .Value("full_name").IfNullOrEmpty(UserSiteName) + ' IsVerifiedProfile = .Value("is_verified").FromXML(Of Boolean)(False) + ' IsVerifiedProfile_Checked = True + ' Dim descr$ = .Value("biography") + ' If If(.Item("bio_links")?.Count, 0) > 0 Then descr.StringAppend(.Item("bio_links").Select(Function(bl) bl.Value("url")).ListToString(vbNewLine), vbNewLine) + ' Dim eUrl$ = .Value("external_url") + ' If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl) + ' UserDescriptionUpdate(descr) + + ' Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"} + ' Dim pic$ = .Value({"hd_profile_pic_url_info"}, "url").IfNullOrEmpty(.Value("profile_pic_url")) + ' If Not pic.IsEmptyString Then GetWebFile(pic, f, EDP.ReturnValue) + ' End If + ' End With + ' End If + ' End Using + 'End If + End Function Private Function GetTimelineGQL(ByVal Cursor As String, ByVal Token As CancellationToken) As String Const none_cursor$ = "none" - Dim nextCursor$ = String.Empty, hasNextPage$ = String.Empty + Dim nextCursor$ = String.Empty + Dim hasNextPage As Boolean = False Dim vars$ ThrowAny(Token) @@ -98,14 +108,18 @@ Namespace API.Instagram ChangeResponserMode(True) If Cursor.IsEmptyString Then - vars = "{""data"":{""count"":50,""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""username"":""" & + vars = "{""data"":{""count"":" & PostNumberPerRequest & ",""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""username"":""" & NameTrue & """,""__relay_internal__pv__PolarisShareMenurelayprovider"":false}" + 'vars = "{""data"":{""count"":" & PostNumberPerRequest & ",""include_reel_media_seen_timestamp"":true,""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""username"":""" & + ' NameTrue & """,""__relay_internal__pv__PolarisShareMenurelayprovider"":false}" vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Timeline_DocId, Token_lsd, Token_dtsg_Var, GQL_Timeline_FbFriendlyName, SymbolsConverter.ASCII.EncodeSymbolsOnly(vars)) UpdateHeadersGQL(GQL_Timeline_FbFriendlyName) Else - vars = "{""after"":""" & Cursor & """,""before"":null,""data"":{""count"":50,""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""first"":50,""last"":null,""username"":""" & + vars = "{""after"":""" & Cursor & """,""before"":null,""data"":{""count"":" & PostNumberPerRequest & ",""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""first"":" & PostNumberPerRequest & ",""last"":null,""username"":""" & NameTrue & """,""__relay_internal__pv__PolarisShareMenurelayprovider"":false}" + 'vars = "{""after"":""" & Cursor & """,""before"":null,""data"":{""count"":" & PostNumberPerRequest & ",""include_reel_media_seen_timestamp"":true,""include_relationship_info"":true,""latest_besties_reel_media"":true,""latest_reel_media"":true},""first"":" & PostNumberPerRequest & ",""last"":null,""username"":""" & + ' NameTrue & """}" vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Timeline_DocId_Second, Token_lsd, Token_dtsg_Var, GQL_Timeline_FbFriendlyName_Second, SymbolsConverter.ASCII.EncodeSymbolsOnly(vars)) UpdateHeadersGQL(GQL_Timeline_FbFriendlyName_Second) @@ -140,7 +154,8 @@ Namespace API.Instagram End Function Private Function GetHighlightsGQL_List() As List(Of String) - Dim nextCursor$ = String.Empty, hasNextPage$ = String.Empty + Dim nextCursor$ = String.Empty + Dim hasNextPage As Boolean = False Dim i% = -1 Dim hList As New List(Of String) Dim tmpList As New List(Of String) @@ -178,7 +193,9 @@ Namespace API.Instagram Dim tmpList As New List(Of String) Dim i% = -1 If StoriesList.ListExists Then - tmpList.AddRange(StoriesList.Take(10)) + 'TODO: 5 Instagram stories + 'tmpList.AddRange(StoriesList.Take(10)) + tmpList.AddRange(StoriesList.Take(5)) StoriesList.RemoveRange(0, tmpList.Count) Dim vars$ = String.Format(GQL_URL_PATTERN_VARS, GQL_Highlights_DocId_Second, Token_lsd, Token_dtsg_Var, GQL_Highlights_FbFriendlyName_Second, @@ -238,11 +255,9 @@ Namespace API.Instagram Private Function GetReelsGQL(ByVal Cursor As String) As String GetReelsGQL_SetEnvir = True - Dim errData$ = String.Empty - If Cursor.IsEmptyString And Not ValidateBaseTokens() Then GetPageTokens() - If Cursor.IsEmptyString And Not ValidateBaseTokens(errData) Then ValidateBaseTokens_Error(errData) + UpdateTokens(Cursor.IsEmptyString) - Dim vars$ = """data"":{""include_feed_video"":true,""page_size"":50,""target_user_id"":""" & ID & """}" + Dim vars$ = """data"":{""include_feed_video"":true,""page_size"":" & PostNumberPerRequest & ",""target_user_id"":""" & ID & """}" If Not Cursor.IsEmptyString Then vars = $"""after"":""{Cursor}"",""before"":null,{vars},""first"":4,""last"":null" vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Reels_DocId, Token_lsd, Token_dtsg_Var, GQL_Reels_FbFriendlyName, SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & vars & "}")) @@ -258,10 +273,10 @@ Namespace API.Instagram Dim vars$ If Cursor.IsEmptyString Then vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Tagged_DocId, Token_lsd, Token_dtsg_Var, GQL_Tagged_FbFriendlyName, - SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""count"":50,""user_id"":""{ID}""" & "}")) + SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""count"":{PostNumberPerRequest},""user_id"":""{ID}""" & "}")) Else vars = String.Format(GQL_URL_PATTERN_VARS, GQL_Tagged_DocId, Token_lsd, Token_dtsg_Var, GQL_Tagged_FbFriendlyName, - SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""after"":""{Cursor}"",""before"":null,""count"":50,""first"":50,""last"":null,""user_id"":""{ID}""" & "}")) + SymbolsConverter.ASCII.EncodeSymbolsOnly("{" & $"""after"":""{Cursor}"",""before"":null,""count"":{PostNumberPerRequest},""first"":{PostNumberPerRequest},""last"":null,""user_id"":""{ID}""" & "}")) End If UpdateRequestNumber() ChangeResponserMode(True) @@ -270,6 +285,13 @@ Namespace API.Instagram End Function #End Region #Region "ValidateBaseTokens" + Private Sub UpdateTokens(ByVal process As Boolean) + If process Then + Dim TokensErrData$ = String.Empty + If Not ValidateBaseTokens() Then GetPageTokens() + If Not ValidateBaseTokens(TokensErrData) Then ValidateBaseTokens_Error(TokensErrData) + End If + End Sub Protected Overridable Overloads Function ValidateBaseTokens() As Boolean Return ValidateBaseTokens(Nothing) End Function @@ -307,6 +329,10 @@ Namespace API.Instagram Try If Not r.IsEmptyString Then ResetBaseTokens() + If ID.IsEmptyString Then + Dim __id$ = RegexReplace(r, Regex_ProfileID) + If CLng(AConvert(Of Long)(__id, 0, EDP.ReturnValue)) <> 0 Then ID = __id + End If Select Case Attempt Case 0 Dim rr As RParams = RParams.DM(PageTokenRegexPatternDefault, 0, RegexReturn.List, EDP.ReturnValue) diff --git a/SCrawler/API/Instagram/UserData.vb b/SCrawler/API/Instagram/UserData.vb index 4fb28d4..291eaa5 100644 --- a/SCrawler/API/Instagram/UserData.vb +++ b/SCrawler/API/Instagram/UserData.vb @@ -39,6 +39,8 @@ Namespace API.Instagram Private Const Name_TaggedChecked As String = "TaggedChecked" Private Const Name_ForceUpdateUserName As String = "ForceUpdateUserName" Private Const Name_ForceUpdateUserInfo As String = "ForceUpdateUserInfo" + Private Const Name_IsVerifiedProfile As String = "IsVerifiedProfile" + Private Const Name_IsVerifiedProfile_Checked As String = "IsVerifiedProfile_Checked" #End Region #Region "Declarations" Friend Structure PostKV : Implements IEContainerProvider @@ -115,6 +117,13 @@ Namespace API.Instagram Private UserNameRequested As Boolean = False Friend Property ForceUpdateUserName As Boolean = False Friend Property ForceUpdateUserInfo As Boolean = False + Friend Property IsVerifiedProfile As Boolean = False + Friend Property IsVerifiedProfile_Checked As Boolean = False + Private ReadOnly Property PostNumberPerRequest As Integer + Get + With MySiteSettings : Return If(IsVerifiedProfile, .PostNumberVerified, .PostNumberVerifiedNot).Value : End With + End Get + End Property #End Region #Region "Loader" Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) @@ -136,6 +145,8 @@ Namespace API.Instagram TaggedChecked = .Value(Name_TaggedChecked).FromXML(Of Boolean)(False) ForceUpdateUserName = .Value(Name_ForceUpdateUserName).FromXML(Of Boolean)(False) ForceUpdateUserInfo = .Value(Name_ForceUpdateUserInfo).FromXML(Of Boolean)(False) + IsVerifiedProfile = .Value(Name_IsVerifiedProfile).FromXML(Of Boolean)(False) + IsVerifiedProfile_Checked = .Value(Name_IsVerifiedProfile_Checked).FromXML(Of Boolean)(False) Else .Add(Name_LastCursor, LastCursor) .Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger) @@ -153,6 +164,8 @@ Namespace API.Instagram .Add(Name_TaggedChecked, TaggedChecked.BoolToInteger) .Add(Name_ForceUpdateUserName, ForceUpdateUserName.BoolToInteger) .Add(Name_ForceUpdateUserInfo, ForceUpdateUserInfo.BoolToInteger) + .Add(Name_IsVerifiedProfile, IsVerifiedProfile.BoolToInteger) + .Add(Name_IsVerifiedProfile_Checked, IsVerifiedProfile_Checked.BoolToInteger) End If End With End Sub @@ -179,6 +192,9 @@ Namespace API.Instagram PutImageVideoFolder = .PutImageVideoFolder + IsVerifiedProfile = .IsVerifiedProfile + If IsVerifiedProfile Then IsVerifiedProfile_Checked = True + ForceUpdateUserName = .ForceUpdateUserName ForceUpdateUserInfo = .ForceUpdateUserInfo End With @@ -663,6 +679,7 @@ Namespace API.Instagram Dim StoriesList As List(Of String) = Nothing Dim StoriesRequested As Boolean = False Dim dValue% = 1 + Dim __idIsEmpty As Boolean = ID.IsEmptyString LastCursor = Cursor Try Do While dValue = 1 @@ -676,7 +693,6 @@ Namespace API.Instagram Dim HasNextPage As Boolean = False Dim EndCursor$ = String.Empty Dim PostID$ = String.Empty, PostDate$ = String.Empty, SpecFolder$ = String.Empty - Dim TokensErrData$ = String.Empty Dim PostIDKV As PostKV Dim ENode() As Object = Nothing Dim processGetResponse As Boolean = True @@ -684,14 +700,11 @@ Namespace API.Instagram 'Check environment If Not IsSavedPosts Then - If ID.IsEmptyString Then GetUserData() + If _UseGQL And Cursor.IsEmptyString And Not Section = Sections.SavedPosts Then UpdateTokens(True) + If ID.IsEmptyString Or __idIsEmpty Or Not IsVerifiedProfile_Checked Then GetUserData(Token) If ID.IsEmptyString Then UserExists = False : _ForceSaveUserInfoOnException = True : Throw New Plugin.ExitException("can't get user ID") - If _UseGQL And Cursor.IsEmptyString And Not Section = Sections.SavedPosts Then - If Not ValidateBaseTokens() Then GetPageTokens() - If Not ValidateBaseTokens(TokensErrData) Then ValidateBaseTokens_Error(TokensErrData) - End If If ForceUpdateUserName Then GetUserNameById() - If ForceUpdateUserInfo Then GetUserData() + If ForceUpdateUserInfo Then GetUserData(Token) End If 'Create query @@ -703,7 +716,7 @@ Namespace API.Instagram MySiteSettings.TooManyRequests(False) GoTo NextPageBlock Else - URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count=50" & + URL = $"https://www.instagram.com/api/v1/feed/user/{NameTrue}/username/?count={PostNumberPerRequest}" & If(Cursor.IsEmptyString, String.Empty, $"&max_id={Cursor}") ENode = Nothing End If @@ -726,7 +739,7 @@ Namespace API.Instagram ENode = {"data", "xdt_api__v1__usertags__user_id__feed_connection"} processGetResponse = False Else - Dim vars$ = "{""id"":" & ID & ",""first"":50,""after"":""" & Cursor & """}" + Dim vars$ = "{""id"":" & ID & $",""first"":{PostNumberPerRequest},""after"":""" & Cursor & """}" vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars) URL = $"https://www.instagram.com/graphql/query/?doc_id=17946422347485809&variables={vars}" ENode = {"data", "user", "edge_user_to_photos_of_you"} @@ -1241,25 +1254,37 @@ NextPageBlock: End Sub #End Region #Region "GetUserId, GetUserName" - Private Sub GetUserData() + Private Sub GetUserData(ByVal Token As CancellationToken) Dim __idFound As Boolean = False If ForceUpdateUserInfo Then ForceUpdateUserInfo = False : _ForceSaveUserInfo = True Try - ChangeResponserMode(False) - UpdateRequestNumber() - Dim r$ = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}") + Dim r$ + Dim ____dataGql As Boolean = _UseGQL Or CBool(MySiteSettings.USE_GQL_UserData.Value) + If ____dataGql Then + UpdateTokens(True) + r = GetUserDataGQL(Token) + If Not _UseGQL Then ChangeResponserMode(_UseGQL) + Else + ChangeResponserMode(False) + UpdateRequestNumber() + r = Responser.GetResponse($"https://i.instagram.com/api/v1/users/web_profile_info/?username={NameTrue}") + End If + If Not r.IsEmptyString Then Using j As EContainer = JsonDocument.Parse(r) If Not j Is Nothing AndAlso j.Contains({"data", "user"}) Then With j({"data", "user"}) - ID = .Value("id") + If Not ____dataGql Or ID.IsEmptyString Then ID = .Value("id") __idFound = True UserSiteNameUpdate(.Value("full_name")) + IsVerifiedProfile = .Value("is_verified").FromXML(Of Boolean)(False) + IsVerifiedProfile_Checked = True Dim descr$ = .Value("biography") If If(.Item("bio_links")?.Count, 0) > 0 Then descr.StringAppend(.Item("bio_links").Select(Function(bl) bl.Value("url")).ListToString(vbNewLine), vbNewLine) Dim eUrl$ = .Value("external_url") If Not eUrl.IsEmptyString AndAlso (descr.IsEmptyString OrElse Not descr.Contains(eUrl)) Then descr.StringAppendLine(eUrl) UserDescriptionUpdate(descr) + Dim f As New SFile With {.Path = DownloadContentDefault_GetRootDir(), .Name = "ProfilePicture", .Extension = "jpg"} f = SFile.IndexReindex(f) If Not f.Exists Then diff --git a/SCrawler/API/Twitter/UserData.vb b/SCrawler/API/Twitter/UserData.vb index 6a7f946..c7ddcf4 100644 --- a/SCrawler/API/Twitter/UserData.vb +++ b/SCrawler/API/Twitter/UserData.vb @@ -297,10 +297,17 @@ Namespace API.Twitter Private Const DEBUG_PROFILE As Boolean = False Private Const DEBUG_LEAVE_CACHE As Boolean = False Private JsonNullErr As Boolean = False + Private ____UserExists As Boolean = True + Private NotUserExistsAttempts As Integer = 0 + Friend Overrides Sub DownloadData(ByVal Token As CancellationToken) + ____UserExists = UserExists + MyBase.DownloadData(Token) + End Sub Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Try GDL_REQUESTS_COUNT = 0 JsonNullErr = False + NotUserExistsAttempts = 0 If MySettings.LIMIT_ABORT Then Throw New TwitterLimitException(Me) Else @@ -317,6 +324,14 @@ Namespace API.Twitter End If LikesPosts.Clear() If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly) + If Not ____UserExists Then + For i% = 0 To 1 + NotUserExistsAttempts += 1 + DownloadData_Timeline(Token) + If UserExists Then ____UserExists = True : Exit For + Next + End If + If Not UserExists Then Exit Sub DownloadData_Timeline(Token) If _TempMediaList.Count = 0 And LikesPosts.Count = 0 And JsonNullErr Then Throw New Plugin.ExitException("No deserialized data found") LoadSavePostsKV(False) @@ -366,6 +381,7 @@ Namespace API.Twitter Dim j As EContainer, rootNode As EContainer, optionalNode As EContainer, workingNode As EContainer, tmpNode As EContainer, nn As EContainer = Nothing Dim multiMode As Boolean = IsMultiMode Dim currentModel As DownloadModels = DownloadModels.Undefined + Dim onlyUpdateUser As Boolean = Not ____UserExists Dim __parseContainer As Func(Of EContainer, Boolean) = Function(ByVal ee As EContainer) As Boolean @@ -478,6 +494,7 @@ Namespace API.Twitter End If 'parse files For i = 0 To timelineFiles.Count - 1 + If userInfoParsed And onlyUpdateUser Then Exit Sub j = JsonDocument.Parse(timelineFiles(i).GetText, jsonArgs) If jsonArgs.State = WebDocumentEventArgs.States.Error Then jsonArgs.Reset(Token) @@ -530,6 +547,14 @@ Namespace API.Twitter Dim tScreenName$ = .Value({"core"}, "screen_name") With .Item({"legacy"}) If .ListExists Then + If onlyUpdateUser Then + If Not NameTrue = tScreenName Or 1 = 1 Then + Dim uStr$ = $"username changed from '{NameTrue}' to '{tScreenName}'" + LogError(Nothing, uStr) + UserDescriptionUpdate(uStr, True, True, True) + End If + NameTrue = tScreenName + End If If .Value("screen_name").IfNullOrEmpty(tScreenName).StringToLower = NameTrue.ToLower Then UserSiteNameUpdate(.Value("name")) UserDescriptionUpdate(.Value("description")) @@ -846,6 +871,23 @@ nextpIndx: Private Class TwitterGDL : Inherits GDL.GDLBatch Private ReadOnly KillOnLimit As Boolean Friend LimitReached As Boolean = False + Private _GetOnlyUserInfo As Boolean = False + Friend Overrides Property MyWorkingDirectory As SFile + Get + Return If(MyBase.MyWorkingDirectory.IsEmptyString, If(FileExchanger?.RootDirectory, MyBase.MyWorkingDirectory), MyBase.MyWorkingDirectory) + End Get + Set(ByVal dir As SFile) + MyBase.MyWorkingDirectory = dir + End Set + End Property + Friend Property GetOnlyUserInfo As Boolean + Get + Return _GetOnlyUserInfo And Not MyWorkingDirectory.IsEmptyString + End Get + Set(ByVal __GetOnlyUserInfo As Boolean) + _GetOnlyUserInfo = __GetOnlyUserInfo + End Set + End Property Friend Sub New(ByVal Dir As SFile, ByVal _Token As CancellationToken, ByVal _KillOnLimit As Boolean) MyBase.New(_Token,, Dir) KillOnLimit = _KillOnLimit @@ -855,10 +897,14 @@ nextpIndx: End Function Private Function IdExists(ByVal Value As String) As Boolean Try - Value = Value.StringTrim - If Not Value.IsEmptyString AndAlso (Value.StartsWith("*") Or Value.StartsWith(".\gallery-dl\")) Then - Dim id$ = Value.Split("\").Last.Split(".").First.Split("_").First - If Not id.IsEmptyString Then Return TempPostsList.Contains(id) + If GetOnlyUserInfo Then + Return CheckForData() + Else + Value = Value.StringTrim + If Not Value.IsEmptyString AndAlso (Value.StartsWith("*") Or Value.StartsWith(".\gallery-dl\")) Then + Dim id$ = Value.Split("\").Last.Split(".").First.Split("_").First + If Not id.IsEmptyString Then Return TempPostsList.Contains(id) + End If End If Catch ex As Exception End Try @@ -867,8 +913,14 @@ nextpIndx: Protected Overrides Async Sub ErrorDataReceiver(ByVal Sender As Object, ByVal e As DataReceivedEventArgs) Await Task.Run(Sub() CheckForLimit(e.Data)) End Sub + Private Function CheckForData() + If GetOnlyUserInfo Then + If SFile.GetFiles(MyWorkingDirectory, "*.txt",, EDP.ReturnValue).Count > 2 Then Return True + End If + Return False + End Function Private Sub CheckForLimit(ByVal Value As String) - If Token.IsCancellationRequested Or (KillOnLimit AndAlso Not ProcessKilled AndAlso + If CheckForData() Or Token.IsCancellationRequested Or (KillOnLimit AndAlso Not ProcessKilled AndAlso Not Value.IsEmptyString AndAlso (Value.ToLower.Contains("for rate limit reset") OrElse Not CStr(RegexReplace(Value, GdlLimitRegEx)).IsEmptyString)) Then LimitReached = True @@ -1017,11 +1069,12 @@ nextpIndx: .AutoClear = True, .AutoReset = True, .CommandPermanent = $"chcp {BatchExecutor.UnicodeEncoding}", - .FileExchanger = confCache + .FileExchanger = confCache, + .GetOnlyUserInfo = NotUserExistsAttempts > 0 } tgdl.FileExchanger.DeleteCacheOnDispose = False tgdl.FileExchanger.DeleteRootOnDispose = False - For i As Byte = 0 To IIf(IsCommunity, 0, 3) + For i As Byte = 0 To IIf(IsCommunity Or NotUserExistsAttempts > 0, 0, 3) dir = rootDir.NewPath dir.Exists(SFO.Path, True, EDP.ThrowException) outList.Add(dir) @@ -1032,13 +1085,28 @@ nextpIndx: Else command &= GdlGetIdFilterString() End If - Select Case i - Case 0 : command &= $"{urlPrePattern}{NameTrue}/media" : currentModel = DownloadModels.Media : process = dm.Contains(currentModel) Or IsCommunity - Case 1 : command &= $"{urlPrePattern}{NameTrue}" : currentModel = DownloadModels.Profile : process = dm.Contains(currentModel) - Case 2 : command &= $"-o search-endpoint=graphql https://x.com/search?q=from:{NameTrue}+include:nativeretweets" : currentModel = DownloadModels.Search : process = dm.Contains(currentModel) And Not IsCommunity - Case 3 : command &= $"{urlPrePattern}{NameTrue}/likes" : currentModel = DownloadModels.Likes : process = dm.Contains(currentModel) - Case Else : process = False - End Select + If NotUserExistsAttempts > 0 Then + Select Case NotUserExistsAttempts + Case 1 : command &= $"{urlPrePattern}{NameTrue}/media" : currentModel = DownloadModels.Media : process = True + Case 2 + If ID.IsEmptyString Then + process = False + Else + command &= $"https://twitter.com/intent/user?user_id={ID}" + currentModel = DownloadModels.Media + process = True + End If + Case Else : process = False + End Select + Else + Select Case i + Case 0 : command &= $"{urlPrePattern}{NameTrue}/media" : currentModel = DownloadModels.Media : process = dm.Contains(currentModel) Or IsCommunity + Case 1 : command &= $"{urlPrePattern}{NameTrue}" : currentModel = DownloadModels.Profile : process = dm.Contains(currentModel) + Case 2 : command &= $"-o search-endpoint=graphql https://x.com/search?q=from:{NameTrue}+include:nativeretweets" : currentModel = DownloadModels.Search : process = dm.Contains(currentModel) And Not IsCommunity + Case 3 : command &= $"{urlPrePattern}{NameTrue}/likes" : currentModel = DownloadModels.Likes : process = dm.Contains(currentModel) + Case Else : process = False + End Select + End If '#If DEBUG Then 'Debug.WriteLine(command) '#End If diff --git a/SCrawler/API/Xhamster/UserData.vb b/SCrawler/API/Xhamster/UserData.vb index 995dc08..ba5be9c 100644 --- a/SCrawler/API/Xhamster/UserData.vb +++ b/SCrawler/API/Xhamster/UserData.vb @@ -744,12 +744,13 @@ Namespace API.Xhamster If IsSingleObjectDownload Then cc = Settings.Cache Else - If MyCache Is Nothing Then MyCache = CreateCache() : MyCache.Validate() + If MyCache Is Nothing Then MyCache = CreateCache() cc = MyCache End If + cc.Validate() Dim path As SFile = cc.NewPath Dim c$ = If(MySettings.CookiesNetscapeFile.Exists, $" --no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}""", String.Empty) - Dim cmd$ = $"{Settings.YtdlpFile} --write-info-json --skip-download{c} {URL} -o ""{path.PathWithSeparator}file""" + Dim cmd$ = $"""{Settings.YtdlpFile}"" --write-info-json --skip-download{c} {URL} -o ""{path.PathWithSeparator}file""" path.Exists() Using ytdlp As New YTDLP.YTDLPBatch(TokenPersonal,, path) : ytdlp.Encoding = Settings.CMDEncoding : ytdlp.Execute(cmd) : End Using Return SFile.GetFiles(path, "*.json",, EDP.ReturnValue).FirstOrDefault @@ -794,7 +795,7 @@ Namespace API.Xhamster Private Function YTDLPDownload(ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile DestinationFile.Extension = "mp4" Dim c$ = If(MySettings.CookiesNetscapeFile.Exists, $" --no-cookies-from-browser --cookies ""{MySettings.CookiesNetscapeFile}""", String.Empty) - Dim cmd$ = $"{Settings.YtdlpFile} --format {DirectCast(Media.Object, XMMediaInfo).FormatID}{c} {Media.URL_BASE} -o ""{DestinationFile}""" + Dim cmd$ = $"""{Settings.YtdlpFile}"" --format {DirectCast(Media.Object, XMMediaInfo).FormatID}{c} {Media.URL_BASE} -o ""{DestinationFile}""" Using ytdlp As New YTDLP.YTDLPBatch(TokenPersonal,, DestinationFile) : ytdlp.Encoding = Settings.CMDEncoding : ytdlp.Execute(cmd) : End Using Return DestinationFile End Function diff --git a/SCrawler/Download/Feed/DownloadFeedForm.vb b/SCrawler/Download/Feed/DownloadFeedForm.vb index 7e5421c..fd2595d 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.vb +++ b/SCrawler/Download/Feed/DownloadFeedForm.vb @@ -99,42 +99,50 @@ Namespace DownloadObjects End Sub Private Sub FeedRemoveCheckedMedia(ByVal MediaList As IEnumerable(Of UserMediaD), Optional ByVal OverriddenNames As List(Of String) = Nothing, Optional ByVal RemoveChecked As Boolean = True, Optional ByVal ExcludingNames As IEnumerable(Of String) = Nothing, - Optional ByVal RemoveFromDataListOnly As Boolean = False) + Optional ByVal IsAddAndRemove As Boolean = False) Try - If FeedMode = FeedModes.Special Then - If LoadedFeedNames.Count > 0 Then - Dim dataRemoved As Boolean = False - If OverriddenNames.ListExists And Not LoadedFeedNames.ListContains(OverriddenNames) Then Exit Sub - If Not RemoveFromDataListOnly Then - Dim eNames As IEnumerable(Of String) = If(ExcludingNames, New String() {}) - With If(OverriddenNames, LoadedFeedNames) - .ForEach(Sub(ByVal feedName As String) - If Not eNames.Contains(feedName) Then - Dim indx% = Settings.Feeds.IndexOf(feedName) - If indx >= 0 Then - If Settings.Feeds(indx).Remove(MediaList) > 0 Then dataRemoved = True - End If - End If - End Sub) - End With - End If - If RemoveFromDataListOnly Then + If FeedMode = FeedModes.Saved Then Exit Sub + + Dim dataRemoved As Boolean = False + If FeedMode = FeedModes.Special And OverriddenNames.ListExists And Not LoadedFeedNames.ListContains(OverriddenNames) Then Exit Sub + Dim eNames As IEnumerable(Of String) = If(ExcludingNames, New String() {}) + With If(OverriddenNames, LoadedFeedNames) + If FeedMode = FeedModes.Special And .ListExists Then + .ForEach(Sub(ByVal feedName As String) + If Not eNames.Contains(feedName) Then + Dim indx% = Settings.Feeds.IndexOf(feedName) + If indx >= 0 Then + If Settings.Feeds(indx).Remove(MediaList) > 0 Then dataRemoved = True + End If + End If + End Sub) + ElseIf FeedMode = FeedModes.Current And Not OverriddenNames.ListExists And IsAddAndRemove Then + dataRemoved = Downloader.Files.ListDisposeRemove(MediaList) > 0 + 'Downloader.FilesSave() + Else + Exit Sub + End If + End With + + If dataRemoved Then DataList.ListDisposeRemove(MediaList) + + Select Case FeedMode + Case FeedModes.Special + If RemoveChecked And IsAddAndRemove Then + If RemoveCheckedMedia(False) Then RefillAfterDelete() + Else RefillSpecialFeedsData() - ElseIf dataRemoved Then - DataList.ListDisposeRemove(MediaList) + End If + Case FeedModes.Current + If dataRemoved Then If RemoveChecked Then - If RemoveCheckedMedia(False) Then RefillAfterDelete() + RemoveCheckedMedia(False) + RefillAfterDelete() Else - RefillSpecialFeedsData() + RefillList() End If End If - End If - ElseIf FeedMode = FeedModes.Current Then - If OverriddenNames Is Nothing AndAlso Downloader.Files.ListDisposeRemove(MediaList) > 0 AndAlso RemoveCheckedMedia(False) Then - DataList.ListDisposeRemove(MediaList) - RefillAfterDelete() - End If - End If + End Select Catch ex As Exception ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadFeedForm.FeedRemoveCheckedMedia]") End Try @@ -333,7 +341,7 @@ Namespace DownloadObjects Dim c As IEnumerable(Of UserMediaD) = GetCheckedMedia() If c.ListExists Then f.Add(c) - FeedRemoveCheckedMedia(c,,, {f.Name}) + FeedRemoveCheckedMedia(c,,, {f.Name}, True) End If End If End Sub @@ -352,7 +360,7 @@ Namespace DownloadObjects Dim m As IEnumerable(Of UserMediaD) = GetCheckedMedia() If m.ListExists Then f.Remove(m) - FeedRemoveCheckedMedia(m, {f.Name}.ToList) + FeedRemoveCheckedMedia(m, {f.Name}.ToList,,, False) End If End If End Sub @@ -1000,14 +1008,14 @@ Namespace DownloadObjects Dim m As IEnumerable(Of UserMediaD) = GetCheckedMedia() If m.ListExists Then Settings.Feeds.Favorite.Add(m) - If sender Is BTT_FEED_ADD_FAV_REMOVE Then FeedRemoveCheckedMedia(m,,, {FeedSpecial.FavoriteName}) + If sender Is BTT_FEED_ADD_FAV_REMOVE Then FeedRemoveCheckedMedia(m,,, {FeedSpecial.FavoriteName}, True) End If End Sub Private Sub BTT_FEED_REMOVE_FAV_Click(sender As Object, e As EventArgs) Handles BTT_FEED_REMOVE_FAV.Click Dim m As IEnumerable(Of UserMediaD) = GetCheckedMedia() If m.ListExists Then Settings.Feeds.Favorite.Remove(m) - If FeedMode = FeedModes.Special Then FeedRemoveCheckedMedia(m, {FeedSpecial.FavoriteName}.ToList) + If FeedMode = FeedModes.Special Then FeedRemoveCheckedMedia(m, {FeedSpecial.FavoriteName}.ToList,,, False) End If End Sub Private Sub BTT_FEED_ADD_SPEC_Click(sender As Object, e As EventArgs) Handles BTT_FEED_ADD_SPEC.Click, BTT_FEED_ADD_SPEC_REMOVE.Click @@ -1020,7 +1028,7 @@ Namespace DownloadObjects f.Add(c) End Sub) End With - If sender Is BTT_FEED_ADD_SPEC_REMOVE Then FeedRemoveCheckedMedia(c,,, names) + If sender Is BTT_FEED_ADD_SPEC_REMOVE Then FeedRemoveCheckedMedia(c,,, names, True) names.Clear() Else MsgBoxE({"You haven't selected media to add to your feed(s)", "Add to feed(s)"}, vbExclamation) @@ -1036,7 +1044,7 @@ Namespace DownloadObjects f.Remove(c) End Sub) End With - If FeedMode = FeedModes.Special Then FeedRemoveCheckedMedia(c, names) + If FeedMode = FeedModes.Special Then FeedRemoveCheckedMedia(c, names,,, False) Else MsgBoxE({"You haven't selected media to remove from your feed(s)", "Remove from feed(s)"}, vbExclamation) End If @@ -1408,8 +1416,10 @@ Namespace DownloadObjects ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Download subscription media") End Try End Sub - Private Sub FeedMedia_FeedAddWithRemove(ByVal Sender As FeedMedia, ByVal Feeds As IEnumerable(Of String), ByVal Media As UserMediaD, ByVal RemoveOperation As Boolean) - FeedRemoveCheckedMedia({Media},, False, Feeds, RemoveOperation) + Private Sub FeedMedia_FeedRemoveCheckedMedia(ByVal Sender As FeedMedia, ByVal Media As UserMediaD, ByVal Names As IEnumerable(Of String), + ByVal IsOverriddenNames As Boolean, ByVal IsAddAndRemove As Boolean) + FeedRemoveCheckedMedia({Media}, CObj(If(IsOverriddenNames, Names.ListIfNothing, Nothing)), False, + CObj(If(IsOverriddenNames, Nothing, Names)), IsAddAndRemove) End Sub #End Region #Region "Delete / Remove" @@ -1616,7 +1626,7 @@ Namespace DownloadObjects AddHandler .MediaDownload, AddressOf FeedMedia_Download AddHandler .MediaMove, AddressOf FeedMedia_MediaMove AddHandler .MediaCopy, AddressOf FeedMedia_MediaCopy - AddHandler .FeedAddWithRemove, AddressOf FeedMedia_FeedAddWithRemove + AddHandler .FeedRemoveCheckedMedia, AddressOf FeedMedia_FeedRemoveCheckedMedia End With If de.Data.Type = UTypes.Text OrElse de.Data.PostTextFile.IsEmptyString Then Exit For Next diff --git a/SCrawler/Download/Feed/FeedMedia.vb b/SCrawler/Download/Feed/FeedMedia.vb index 102db68..94f9974 100644 --- a/SCrawler/Download/Feed/FeedMedia.vb +++ b/SCrawler/Download/Feed/FeedMedia.vb @@ -20,7 +20,8 @@ Namespace DownloadObjects Friend Event MediaDeleted(ByVal Sender As Object) Friend Event MediaDeletedText(ByVal Sender As Object) Friend Event MediaDownload As EventHandler - Friend Event FeedAddWithRemove(ByVal Sender As FeedMedia, ByVal Feeds As IEnumerable(Of String), ByVal Media As UserMediaD, ByVal RemoveOperation As Boolean) + Friend Event FeedRemoveCheckedMedia(ByVal Sender As FeedMedia, ByVal Media As UserMediaD, ByVal Names As IEnumerable(Of String), + ByVal IsOverriddenNames As Boolean, ByVal IsAddAndRemove As Boolean) Friend Event MediaMove As MediaMoveCopyEventHandler Friend Event MediaCopy As MediaMoveCopyEventHandler #End Region @@ -435,11 +436,11 @@ Namespace DownloadObjects End Function Private Sub Feed_SPEC_ADD_REMOVE(ByVal Source As ToolStripMenuItem, ByVal e As EventArgs) Dim f As FeedSpecial = Feed_SPEC_ADD_Impl(Source) - If Not f Is Nothing Then RaiseEvent FeedAddWithRemove(Me, {f.Name}, Media, False) + If Not f Is Nothing Then RaiseEvent FeedRemoveCheckedMedia(Me, Media, {f.Name}, False, True) End Sub Private Sub Feed_SPEC_REMOVE(ByVal Source As ToolStripMenuItem, ByVal e As EventArgs) Dim f As FeedSpecial = Source.Tag - If Not f Is Nothing AndAlso Not f.Disposed Then f.Remove(Media) : RaiseEvent FeedAddWithRemove(Me, {f.Name}, Media, True) + If Not f Is Nothing AndAlso Not f.Disposed Then f.Remove(Media) : RaiseEvent FeedRemoveCheckedMedia(Me, Media, {f.Name}, True, False) End Sub #End Region #Region "Dispose" @@ -598,14 +599,14 @@ Namespace DownloadObjects With Settings.Feeds.Favorite If Not .Contains(Media) Then .Add(Media) BTT_FEED_ADD_FAV.ControlChangeColor(True, False) - If sender Is BTT_FEED_ADD_FAV_REMOVE Then RaiseEvent FeedAddWithRemove(Me, {FeedSpecial.FavoriteName}, Media, False) + If sender Is BTT_FEED_ADD_FAV_REMOVE Then RaiseEvent FeedRemoveCheckedMedia(Me, Media, {FeedSpecial.FavoriteName}, False, True) End With End Sub Private Sub BTT_FEED_ADD_SPEC_Click(sender As Object, e As EventArgs) Handles BTT_FEED_ADD_SPEC.Click, BTT_FEED_ADD_SPEC_REMOVE.Click With FeedSpecialCollection.ChooseFeeds(True) If .ListExists Then .ForEach(Sub(f) f.Add(Media)) - If sender Is BTT_FEED_ADD_SPEC_REMOVE Then RaiseEvent FeedAddWithRemove(Me, .Select(Function(f) f.Name), Media, False) + If sender Is BTT_FEED_ADD_SPEC_REMOVE Then RaiseEvent FeedRemoveCheckedMedia(Me, Media, .Select(Function(f) f.Name), False, True) End If End With End Sub @@ -613,14 +614,14 @@ Namespace DownloadObjects With Settings.Feeds.Favorite If .Contains(Media) Then .Remove(Media) BTT_FEED_ADD_FAV.ControlChangeColor(True) - RaiseEvent FeedAddWithRemove(Me, {FeedSpecial.FavoriteName}, Media, True) + RaiseEvent FeedRemoveCheckedMedia(Me, Media, {FeedSpecial.FavoriteName}, True, False) End With End Sub Private Sub BTT_FEED_REMOVE_SPEC_Click(sender As Object, e As EventArgs) Handles BTT_FEED_REMOVE_SPEC.Click With FeedSpecialCollection.ChooseFeeds(False) If .ListExists Then .ForEach(Sub(f) f.Remove(Media)) - RaiseEvent FeedAddWithRemove(Me, .Select(Function(f) f.Name), Media, True) + RaiseEvent FeedRemoveCheckedMedia(Me, Media, .Select(Function(f) f.Name), True, False) End If End With End Sub diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb index 5d9aeb3..a27b913 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: ' - - + +