From 2ae8c3acfc592b6845ac4d369c145958471d257e Mon Sep 17 00:00:00 2001 From: Andy <88590076+AAndyProgram@users.noreply.github.com> Date: Tue, 4 Jun 2024 03:28:05 +0300 Subject: [PATCH] 2024.6.4.0 API.Twitter: add communities downloading, change post opening URL Feed: add the ability to select one of the download sessions and set it as the current session --- Changelog.md | 19 ++ .../SettingsGlobalDownloading.png | Bin 20762 -> 22383 bytes README.md | 10 +- SCrawler.YouTube/My Project/AssemblyInfo.vb | 4 +- .../My Project/AssemblyInfo.vb | 4 +- SCrawler/API/Base/UserDataBase.vb | 2 +- SCrawler/API/Twitter/SiteSettings.vb | 18 +- SCrawler/API/Twitter/UserData.vb | 164 +++++++++++++----- .../Feed/DownloadFeedForm.Designer.vb | 51 +++--- SCrawler/Download/Feed/DownloadFeedForm.resx | 6 +- SCrawler/Download/Feed/DownloadFeedForm.vb | 52 ++++-- SCrawler/Download/TDownloader.vb | 24 ++- SCrawler/My Project/AssemblyInfo.vb | 4 +- 13 files changed, 261 insertions(+), 97 deletions(-) diff --git a/Changelog.md b/Changelog.md index fe94de6..92b66d8 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,22 @@ +# 2024.6.4.0 + +*2024-06-04* + +**If you were using the [`yt-dlp-TTUser`](https://github.com/bashonly/yt-dlp-TTUser) plugin, you should remove it because this plugin was added to yt-dlp itself! Read more [here](https://github.com/AAndyProgram/SCrawler/wiki/Settings#tiktok-requirements).** + +- Added + - Added highlighting of scheduler plans (working, stopped, pending, etc.) + - YouTube (standalone app): add option to add the video upload date before/after the file name (`Settings` - `Defaults` - `Add date to file name`) + - Twitter: **`Communities` downloading** + - Feed: ability to select one of the download sessions and set it as the current session + - Minor improvements +- Updated + - yt-dlp up to version **2024.05.27** + - gallery-dl up to version **1.27.0** +- Fixed + - Twitter: deleting user directory when redownloading missing posts + - Minor bugs + # 2024.5.19.0 *2024-05-19* diff --git a/ProgramScreenshots/SettingsGlobalDownloading.png b/ProgramScreenshots/SettingsGlobalDownloading.png index 7eb466f4ae1389c5ee0ed5c81431e5d01c24c08c..a4a9843e01ee100cc6be3f04879a2c8faa0e3a6d 100644 GIT binary patch literal 22383 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 literal 20762 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 E3s0KZ{{R30 diff --git a/README.md b/README.md index b834f0f..1b7fe77 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ - +# 🏳️‍🌈 Happy LGBT Pride Month 🎉 + # 🏳️‍🌈 Social networks crawler 🏳️‍🌈 [![GitHub release (latest by date)](https://img.shields.io/github/v/release/AAndyProgram/SCrawler)](https://github.com/AAndyProgram/SCrawler/releases/latest) @@ -37,8 +37,8 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo - YouTube videos, shorts, community feeds, users, artists, playlists, music, tracks; - Reddit images, galleries of images, videos, saved posts; - Redgifs videos (https://www.redgifs.com/); - - Twitter images and videos, saved (bookmarked) posts; - - OnlyFans images and videos, saved (bookmarked) posts; + - Twitter images and videos, saved (bookmarked) posts, likes, communities; + - OnlyFans images and videos, saved (bookmarked) posts, stories; - JustForFans images and videos, saved (bookmarked) posts; - Mastodon images and videos, saved (bookmarked) posts; - Instagram images and videos, tagged posts, stories, saved posts; @@ -79,11 +79,11 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo - **Reddit** - **Twitter** - **OnlyFans** *(partial support)*[^1] -- **Mastodon** - **Instagram** - **Threads** - **Facebook** - JustForFans *(partial support)*[^1] +- Mastodon *(out of support)* - TikTok - RedGifs - Pinterest diff --git a/SCrawler.YouTube/My Project/AssemblyInfo.vb b/SCrawler.YouTube/My Project/AssemblyInfo.vb index 47e54e6..9c8577a 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 272d70b..8940716 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/UserDataBase.vb b/SCrawler/API/Base/UserDataBase.vb index 154f7e8..1bd1d2e 100644 --- a/SCrawler/API/Base/UserDataBase.vb +++ b/SCrawler/API/Base/UserDataBase.vb @@ -143,7 +143,7 @@ Namespace API.Base Protected Const Name_UserID As String = "UserID" Protected Const Name_Options As String = "Options" Protected Const Name_Description As String = "Description" - Private Const Name_ParseUserMediaOnly As String = "ParseUserMediaOnly" + Protected Const Name_ParseUserMediaOnly As String = "ParseUserMediaOnly" Private Const Name_IsSubscription As String = UserInfo.Name_IsSubscription Private Const Name_Temporary As String = "Temporary" Private Const Name_Favorite As String = "Favorite" diff --git a/SCrawler/API/Twitter/SiteSettings.vb b/SCrawler/API/Twitter/SiteSettings.vb index 28f1a92..1b06d23 100644 --- a/SCrawler/API/Twitter/SiteSettings.vb +++ b/SCrawler/API/Twitter/SiteSettings.vb @@ -101,7 +101,7 @@ Namespace API.Twitter ConcurrentDownloads = New PropertyValue(1) MyConcurrentDownloadsProvider = New ConcurrentDownloadsProvider - UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, "/(twitter|x).com/"), 2) + UserRegex = RParams.DMS(String.Format(UserRegexDefaultPattern, $"/(twitter|x).com({CommunitiesUser}|)/"), 3) UrlPatternUser = "https://x.com/{0}" ImageVideoContains = "twitter" CheckNetscapeCookiesOnEndInit = True @@ -110,8 +110,9 @@ Namespace API.Twitter Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider Return New UserData End Function + Friend Const SinglePostPattern As String = "https://x.com/i/web/status/{0}" Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String - Return $"https://x.com/{User.Name}/status/{Media.Post.ID}" + Return String.Format(SinglePostPattern, Media.Post.ID) End Function Friend Overrides Function BaseAuthExists() As Boolean Return Responser.CookiesExists @@ -151,5 +152,18 @@ Namespace API.Twitter End If MyBase.Update() End Sub + Friend Const CommunitiesUser As String = "/i/communities" + Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions + Dim e As ExchangeOptions = MyBase.IsMyUser(UserURL) + If Not e.UserName.IsEmptyString Then + If UserURL.Contains(CommunitiesUser) Then e.Options = CommunitiesUser : e.UserName &= "@c" + Return e + Else + Return Nothing + End If + End Function + Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String + Return DirectCast(User, UserData).GetUserUrl + End Function End Class End Namespace \ No newline at end of file diff --git a/SCrawler/API/Twitter/UserData.vb b/SCrawler/API/Twitter/UserData.vb index 1a6eafc..d93f7af 100644 --- a/SCrawler/API/Twitter/UserData.vb +++ b/SCrawler/API/Twitter/UserData.vb @@ -26,8 +26,24 @@ Namespace API.Twitter Private Const Name_GifsDownload As String = "GifsDownload" Private Const Name_GifsSpecialFolder As String = "GifsSpecialFolder" Private Const Name_GifsPrefix As String = "GifsPrefix" + Private Const Name_IsCommunity As String = "IsCommunity" #End Region #Region "Declarations" + Private Const Label_Community As String = "Community" + Private _NameTrue As String = String.Empty + Friend Property NameTrue As String + Get + Return _NameTrue.IfNullOrEmpty(Name) + End Get + Set(ByVal NewName As String) + _NameTrue = NewName + End Set + End Property + Friend Overrides ReadOnly Property SpecialLabels As IEnumerable(Of String) + Get + Return {Label_Community} + End Get + End Property Friend Enum DownloadModels As Integer Undefined = 0 Media = 1 @@ -42,6 +58,7 @@ Namespace API.Twitter Friend Property GifsDownload As Boolean = True Friend Property GifsSpecialFolder As String = String.Empty Friend Property GifsPrefix As String = String.Empty + Friend Property IsCommunity As Boolean = False Private ReadOnly LikesPosts As List(Of String) Private ReadOnly _DataNames As List(Of String) Private ReadOnly Property MySettings As SiteSettings @@ -57,6 +74,9 @@ Namespace API.Twitter Private Function RenameGdlFile(ByVal Input As SFile, ByVal i As Integer) As SFile Return SFile.Rename(Input, $"{Input.PathWithSeparator}{i.NumToString(FileNameProvider)}.{Input.Extension}",, EDP.ThrowException) End Function + Friend Function GetUserUrl() As String + Return $"https://x.com{IIf(IsCommunity, SiteSettings.CommunitiesUser, String.Empty)}/{NameTrue}" + End Function #End Region #Region "Exchange options" Friend Overrides Function ExchangeOptionsGet() As Object @@ -121,7 +141,20 @@ Namespace API.Twitter RemoveExistingDuplicates = .Value(Name_RemoveExistingDuplicates).FromXML(Of Boolean)(False) StartMD5Checked = .Value(Name_StartMD5Checked).FromXML(Of Boolean)(False) MediaModelAllowNonUserTweets = .Value(Name_MediaModelAllowNonUserTweets).FromXML(Of Boolean)(False) + IsCommunity = .Value(Name_IsCommunity).FromXML(Of Boolean)(False) + _NameTrue = .Value(Name_TrueName) Else + If Name.Contains("@") And Not IsCommunity Then + IsCommunity = True + _NameTrue = Name.Split("@")(0) + ID = _NameTrue + ParseUserMediaOnly = False + Labels.ListAddValue(Label_Community, LNC) + Labels.Sort() + .Add(Name_UserID, ID) + .Add(Name_LabelsName, LabelsString) + .Add(Name_ParseUserMediaOnly, ParseUserMediaOnly.BoolToInteger) + End If .Add(Name_FirstDownloadComplete, FirstDownloadComplete.BoolToInteger) .Add(Name_DownloadModelForceApply, DownloadModelForceApply.BoolToInteger) .Add(Name_DownloadModel, CInt(DownloadModel)) @@ -132,6 +165,8 @@ Namespace API.Twitter .Add(Name_RemoveExistingDuplicates, RemoveExistingDuplicates.BoolToInteger) .Add(Name_StartMD5Checked, StartMD5Checked.BoolToInteger) .Add(Name_MediaModelAllowNonUserTweets, MediaModelAllowNonUserTweets.BoolToInteger) + .Add(Name_IsCommunity, IsCommunity.BoolToInteger) + .Add(Name_TrueName, _NameTrue) End If End With End Sub @@ -188,14 +223,15 @@ Namespace API.Twitter Dim newTwitterNodes() As Object = {0, "content", "items"} Dim p As Predicate(Of EContainer) Dim pIndx% + Dim indxChanged As Boolean = False Dim isOneNode As Boolean, isPins As Boolean, ExistsDetected As Boolean, userInfoParsed As Boolean = False Dim j As EContainer, rootNode As EContainer, optionalNode As EContainer, workingNode As EContainer, tmpNode As EContainer, nn As EContainer = Nothing Dim __parseContainer As Func(Of EContainer, Boolean) = Function(ByVal ee As EContainer) As Boolean nn = Nothing - If dirIndx > 1 Then nn = ee - If Not nn.ListExists Then + If dirIndx > 1 Or IsCommunity Then nn = ee + If Not nn.ListExists Or IsCommunity Then For Each node In nodes nn = ee(node) If nn.ListExists Then Exit For @@ -269,10 +305,22 @@ Namespace API.Twitter For i = 0 To timelineFiles.Count - 1 j = JsonDocument.Parse(timelineFiles(i).GetText) If Not j Is Nothing Then - If i = 0 Then + If i = 0 And Not indxChanged Then If Not userInfoParsed Then userInfoParsed = True - Dim resValue$ = j.Value({"data", "user", "result"}, "__typename").StringTrim.StringToLower + Dim resValue$ = j.Value({"data", IIf(IsCommunity, "communityResults", "user"), "result"}, "__typename").StringTrim.StringToLower + Dim icon$ + Dim __getImage As Action(Of String) = Sub(ByVal img As String) + If Not img.IsEmptyString Then + Dim __imgFile As SFile = UrlFile(img, True) + If Not __imgFile.Name.IsEmptyString Then + If __imgFile.Extension.IsEmptyString Then __imgFile.Extension = "jpg" + __imgFile.Path = MyFile.CutPath.Path + If Not __imgFile.Exists Then GetWebFile(img, __imgFile, EDP.None) + If __imgFile.Exists Then IconBannerDownloaded = True + End If + End If + End Sub If resValue.IsEmptyString Then UserExists = False j.Dispose() @@ -281,6 +329,29 @@ Namespace API.Twitter UserSuspended = True j.Dispose() Exit Sub + ElseIf IsCommunity Then + With j({"data", "communityResults", "result", "community_media_timeline", "timeline", "instructions"}) + If .ListExists Then + With .Find(entriesNode, True) + If .ListExists Then + With .ItemF({0, "content", "items", 0, "item", "itemContent", "tweet_results", "result", "tweet", "community_results", "result"}) + If .ListExists Then + If ID = .Value("id_str") Then + UserSiteNameUpdate(.Value("name")) + UserDescriptionUpdate(.Value("description")) + + icon = .Value({"custom_banner_media", "media_info"}, "original_img_url"). + IfNullOrEmpty(.Value({"default_banner_media", "media_info"}, "original_img_url")) + If Not icon.IsEmptyString And DownloadIconBanner Then __getImage.Invoke(icon) + End If + End If + End With + End If + End With + End If + End With + i = -1 + indxChanged = True Else With j({"data", "user", "result"}) If .ListExists Then @@ -290,21 +361,11 @@ Namespace API.Twitter End If With .Item({"legacy"}) If .ListExists Then - If .Value("screen_name").StringToLower = Name.ToLower Then + If .Value("screen_name").StringToLower = NameTrue.ToLower Then UserSiteNameUpdate(.Value("name")) UserDescriptionUpdate(.Value("description")) - Dim __getImage As Action(Of String) = Sub(ByVal img As String) - If Not img.IsEmptyString Then - Dim __imgFile As SFile = UrlFile(img, True) - If Not __imgFile.Name.IsEmptyString Then - If __imgFile.Extension.IsEmptyString Then __imgFile.Extension = "jpg" - __imgFile.Path = MyFile.CutPath.Path - If Not __imgFile.Exists Then GetWebFile(img, __imgFile, EDP.None) - If __imgFile.Exists Then IconBannerDownloaded = True - End If - End If - End Sub - Dim icon$ = .Value("profile_image_url_https") + + icon = .Value("profile_image_url_https") If Not icon.IsEmptyString Then icon = icon.Replace("_normal", String.Empty) If DownloadIconBanner Then __getImage.Invoke(.Value("profile_banner_url")) @@ -316,34 +377,55 @@ Namespace API.Twitter End If End With End If + ElseIf IsCommunity Then + i = -1 + indxChanged = True End If Else For pIndx = 0 To IIf(dirIndx < 2 Or dirIndx = 3, 1, 0) optionalNode = Nothing - Select Case dirIndx - Case 0, 1, 3 - rootNode = j({"data", "user", "result", "timeline_v2", "timeline", "instructions"}) - If rootNode.ListExists Then - If dirIndx = 3 Then - p = entriesNode - isPins = False + rootNode = Nothing + If IsCommunity Then + With j({"data", "communityResults", "result", "community_media_timeline", "timeline", "instructions"}) + If .ListExists Then + If i = 0 Then + rootNode = .Find(entriesNode, True) Else - p = If(pIndx = 0, pinNode, timelineNode) - isPins = pIndx = 0 + rootNode = .Find(moduleItemsPredicate, True) End If optionalNode = rootNode - 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 - rootNode = j({"globalObjects", "tweets"}) - optionalNode = rootNode - End Select + End With + Else + Select Case dirIndx + Case 0, 1, 3 + rootNode = j({"data", "user", "result", "timeline_v2", "timeline", "instructions"}) + If rootNode.ListExists Then + 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, dirIndx = 3) + If dirIndx <> 3 And rootNode.ListExists Then rootNode = rootNode.Find(entriesNode, dirIndx = 3) + End If + Case Else + isPins = False + rootNode = j({"globalObjects", "tweets"}) + optionalNode = rootNode + End Select + End If If rootNode.ListExists Then With rootNode - isOneNode = dirIndx < 2 AndAlso .Name = entry + If IsCommunity Then + isOneNode = pIndx = 0 + Else + isOneNode = dirIndx < 2 AndAlso .Name = entry + End If ProgressPre.ChangeMax(If(isOneNode, 1, .Count)) If isOneNode Then ProgressPre.Perform() @@ -660,6 +742,7 @@ Namespace API.Twitter Dim dir As SFile Dim dm As List(Of DownloadModels) = EnumExtract(Of DownloadModels)(DownloadModel).ListIfNothing Dim process As Boolean + Dim urlPrePattern$ = $"https://x.com{IIf(IsCommunity, SiteSettings.CommunitiesUser, String.Empty)}/" Using tgdl As New TwitterGDL(Nothing, Token, MySettings.AbortOnLimit.Value) With { .TempPostsList = _TempPostsList, @@ -670,7 +753,7 @@ Namespace API.Twitter } tgdl.FileExchanger.DeleteCacheOnDispose = False tgdl.FileExchanger.DeleteRootOnDispose = False - For i As Byte = 0 To 3 + For i As Byte = 0 To IIf(IsCommunity, 0, 3) dir = rootDir.NewPath dir.Exists(SFO.Path, True, EDP.ThrowException) outList.Add(dir) @@ -678,10 +761,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://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 0 : command &= $"{urlPrePattern}{NameTrue}/media" : process = dm.Contains(DownloadModels.Media) Or IsCommunity + Case 1 : command &= $"{urlPrePattern}{NameTrue}" : process = dm.Contains(DownloadModels.Profile) + Case 2 : command &= $"-o search-endpoint=graphql https://x.com/search?q=from:{NameTrue}+include:nativeretweets" : process = dm.Contains(DownloadModels.Search) And Not IsCommunity + Case 3 : command &= $"{urlPrePattern}{NameTrue}/likes" : process = dm.Contains(DownloadModels.Likes) Case Else : process = False End Select '#If DEBUG Then @@ -735,7 +818,6 @@ Namespace API.Twitter #Region "ReparseMissing" Private _ReparseLikes As Boolean = False Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken) - Const SinglePostPattern$ = "https://x.com/{0}/status/{1}" Dim rList As New List(Of Integer) Dim URL$ = String.Empty Dim cache As CacheKeeper = Nothing @@ -766,7 +848,7 @@ Namespace API.Twitter ElseIf _ReparseLikes Then URL = LikesPosts(i) Else - URL = String.Format(SinglePostPattern, Name, m.Post.ID) + URL = String.Format(SiteSettings.SinglePostPattern, m.Post.ID) End If f = GetDataFromGalleryDL(URL, cache, False, Token) If Not f.IsEmptyString Then diff --git a/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb b/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb index c07a588..63af54e 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb +++ b/SCrawler/Download/Feed/DownloadFeedForm.Designer.vb @@ -62,14 +62,15 @@ Namespace DownloadObjects Me.BTT_MERGE_FEEDS = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CHECK_ALL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_CHECK_NONE = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_VIEW_SAVE = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_VIEW_LOAD = New System.Windows.Forms.ToolStripMenuItem() Me.SEP_0 = New System.Windows.Forms.ToolStripSeparator() Me.MENU_DOWN = New System.Windows.Forms.ToolStripDropDownButton() Me.BTT_DOWN_ALL = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_DOWN_SELECTED = New System.Windows.Forms.ToolStripMenuItem() Me.BTT_REFRESH = New System.Windows.Forms.ToolStripButton() Me.TP_DATA = New System.Windows.Forms.TableLayoutPanel() - Me.BTT_VIEW_SAVE = New System.Windows.Forms.ToolStripMenuItem() - Me.BTT_VIEW_LOAD = New System.Windows.Forms.ToolStripMenuItem() + Me.BTT_CURR_SESSION_SET = New System.Windows.Forms.ToolStripMenuItem() SEP_1 = New System.Windows.Forms.ToolStripSeparator() SEP_2 = New System.Windows.Forms.ToolStripSeparator() MENU_VIEW = New System.Windows.Forms.ToolStripDropDownButton() @@ -157,6 +158,11 @@ Namespace DownloadObjects MENU_LOAD_SEP_0.Name = "MENU_LOAD_SEP_0" MENU_LOAD_SEP_0.Size = New System.Drawing.Size(349, 6) ' + 'MENU_LOAD_SEP_8 + ' + MENU_LOAD_SEP_8.Name = "MENU_LOAD_SEP_8" + MENU_LOAD_SEP_8.Size = New System.Drawing.Size(349, 6) + ' 'ToolbarTOP ' Me.ToolbarTOP.GripStyle = System.Windows.Forms.ToolStripGripStyle.Hidden @@ -170,7 +176,7 @@ Namespace DownloadObjects ' Me.MENU_LOAD_SESSION.AutoToolTip = False Me.MENU_LOAD_SESSION.DisplayStyle = System.Windows.Forms.ToolStripItemDisplayStyle.Image - Me.MENU_LOAD_SESSION.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_LOAD_SESSION_CURRENT, Me.BTT_LOAD_SESSION_LAST, Me.BTT_LOAD_SESSION_CHOOSE, MENU_LOAD_SEP_0, Me.BTT_COPY_TO, Me.BTT_MOVE_TO, MENU_LOAD_SEP_1, Me.BTT_LOAD_FAV, Me.BTT_LOAD_SPEC, MENU_LOAD_SEP_2, Me.BTT_FEED_ADD_FAV, Me.BTT_FEED_ADD_FAV_REMOVE, Me.BTT_FEED_REMOVE_FAV, MENU_LOAD_SEP_3, Me.BTT_FEED_ADD_SPEC, Me.BTT_FEED_ADD_SPEC_REMOVE, Me.BTT_FEED_REMOVE_SPEC, MENU_LOAD_SEP_4, Me.BTT_FEED_CLEAR_FAV, Me.BTT_FEED_CLEAR_SPEC, Me.BTT_FEED_DELETE_SPEC, Me.BTT_FEED_DELETE_DAILY_LIST, Me.BTT_FEED_DELETE_DAILY_DATE, MENU_LOAD_SEP_5, Me.BTT_MERGE_SESSIONS, Me.BTT_CLEAR_DAILY, MENU_LOAD_SEP_6, Me.BTT_MERGE_FEEDS, MENU_LOAD_SEP_7, Me.BTT_CHECK_ALL, Me.BTT_CHECK_NONE, MENU_LOAD_SEP_8, Me.BTT_VIEW_SAVE, Me.BTT_VIEW_LOAD}) + Me.MENU_LOAD_SESSION.DropDownItems.AddRange(New System.Windows.Forms.ToolStripItem() {Me.BTT_LOAD_SESSION_CURRENT, Me.BTT_LOAD_SESSION_LAST, Me.BTT_LOAD_SESSION_CHOOSE, MENU_LOAD_SEP_0, Me.BTT_COPY_TO, Me.BTT_MOVE_TO, MENU_LOAD_SEP_1, Me.BTT_LOAD_FAV, Me.BTT_LOAD_SPEC, MENU_LOAD_SEP_2, Me.BTT_FEED_ADD_FAV, Me.BTT_FEED_ADD_FAV_REMOVE, Me.BTT_FEED_REMOVE_FAV, MENU_LOAD_SEP_3, Me.BTT_FEED_ADD_SPEC, Me.BTT_FEED_ADD_SPEC_REMOVE, Me.BTT_FEED_REMOVE_SPEC, MENU_LOAD_SEP_4, Me.BTT_FEED_CLEAR_FAV, Me.BTT_FEED_CLEAR_SPEC, Me.BTT_FEED_DELETE_SPEC, Me.BTT_FEED_DELETE_DAILY_LIST, Me.BTT_FEED_DELETE_DAILY_DATE, MENU_LOAD_SEP_5, Me.BTT_CURR_SESSION_SET, Me.BTT_MERGE_SESSIONS, Me.BTT_CLEAR_DAILY, MENU_LOAD_SEP_6, Me.BTT_MERGE_FEEDS, MENU_LOAD_SEP_7, Me.BTT_CHECK_ALL, Me.BTT_CHECK_NONE, MENU_LOAD_SEP_8, Me.BTT_VIEW_SAVE, Me.BTT_VIEW_LOAD}) Me.MENU_LOAD_SESSION.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24 Me.MENU_LOAD_SESSION.ImageTransparentColor = System.Drawing.Color.Magenta Me.MENU_LOAD_SESSION.Name = "MENU_LOAD_SESSION" @@ -343,6 +349,20 @@ Namespace DownloadObjects Me.BTT_CHECK_NONE.Size = New System.Drawing.Size(352, 22) Me.BTT_CHECK_NONE.Text = "Select none" ' + 'BTT_VIEW_SAVE + ' + Me.BTT_VIEW_SAVE.Name = "BTT_VIEW_SAVE" + Me.BTT_VIEW_SAVE.Size = New System.Drawing.Size(352, 22) + Me.BTT_VIEW_SAVE.Text = "Save current view" + ' + 'BTT_VIEW_LOAD + ' + Me.BTT_VIEW_LOAD.AutoToolTip = True + Me.BTT_VIEW_LOAD.Name = "BTT_VIEW_LOAD" + Me.BTT_VIEW_LOAD.Size = New System.Drawing.Size(352, 22) + Me.BTT_VIEW_LOAD.Text = "Load view (from saved)" + Me.BTT_VIEW_LOAD.ToolTipText = "Load one of your previously saved views" + ' 'SEP_0 ' Me.SEP_0.Name = "SEP_0" @@ -409,24 +429,14 @@ Namespace DownloadObjects Me.TP_DATA.Size = New System.Drawing.Size(484, 436) Me.TP_DATA.TabIndex = 1 ' - 'MENU_LOAD_SEP_8 + 'BTT_CURR_SESSION_SET ' - MENU_LOAD_SEP_8.Name = "MENU_LOAD_SEP_8" - MENU_LOAD_SEP_8.Size = New System.Drawing.Size(349, 6) - ' - 'BTT_VIEW_SAVE - ' - Me.BTT_VIEW_SAVE.Name = "BTT_VIEW_SAVE" - Me.BTT_VIEW_SAVE.Size = New System.Drawing.Size(352, 22) - Me.BTT_VIEW_SAVE.Text = "Save current view" - ' - 'BTT_VIEW_LOAD - ' - Me.BTT_VIEW_LOAD.AutoToolTip = True - Me.BTT_VIEW_LOAD.Name = "BTT_VIEW_LOAD" - Me.BTT_VIEW_LOAD.Size = New System.Drawing.Size(352, 22) - Me.BTT_VIEW_LOAD.Text = "Load view (from saved)" - Me.BTT_VIEW_LOAD.ToolTipText = "Load one of your previously saved views" + Me.BTT_CURR_SESSION_SET.AutoToolTip = True + Me.BTT_CURR_SESSION_SET.Image = Global.SCrawler.My.Resources.Resources.ArrowDownPic_Blue_24 + Me.BTT_CURR_SESSION_SET.Name = "BTT_CURR_SESSION_SET" + Me.BTT_CURR_SESSION_SET.Size = New System.Drawing.Size(352, 22) + Me.BTT_CURR_SESSION_SET.Text = "Set current session..." + Me.BTT_CURR_SESSION_SET.ToolTipText = "Select one of the download sessions and set it as the current session" ' 'DownloadFeedForm ' @@ -483,5 +493,6 @@ Namespace DownloadObjects Private WithEvents BTT_MOVE_TO As ToolStripMenuItem Private WithEvents BTT_VIEW_SAVE As ToolStripMenuItem Private WithEvents BTT_VIEW_LOAD As ToolStripMenuItem + Private WithEvents BTT_CURR_SESSION_SET As ToolStripMenuItem End Class End Namespace \ No newline at end of file diff --git a/SCrawler/Download/Feed/DownloadFeedForm.resx b/SCrawler/Download/Feed/DownloadFeedForm.resx index d63cc5b..5d446a2 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.resx +++ b/SCrawler/Download/Feed/DownloadFeedForm.resx @@ -159,10 +159,10 @@ False - - 17, 17 - False + + 17, 17 + \ No newline at end of file diff --git a/SCrawler/Download/Feed/DownloadFeedForm.vb b/SCrawler/Download/Feed/DownloadFeedForm.vb index 8b7b439..22ce038 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.vb +++ b/SCrawler/Download/Feed/DownloadFeedForm.vb @@ -439,7 +439,9 @@ Namespace DownloadObjects End Sub Private Sub SessionChooser(ByVal GetLast As Boolean, Optional ByVal GetFilesOnly As Boolean = False, Optional ByRef ResultFilesList As List(Of SFile) = Nothing, - Optional ByVal SelectedMode As FeedModes = -1) + Optional ByVal SelectedMode As FeedModes = -1, + Optional ByVal GetSessionFile As Boolean = False, + Optional ByRef SessionFile As SFile = Nothing) Try LoadedSessionName = String.Empty Downloader.ClearSessions() @@ -474,6 +476,12 @@ Namespace DownloadObjects If fList.ListExists Then If GetFilesOnly Then ResultFilesList.AddRange(fList) + ElseIf GetSessionFile Then + If fList.Count > 1 Then + MsgBoxE({"You must select one session file", "Get session file"}, vbExclamation) + Else + SessionFile = fList(0) + End If Else DataList.Clear() If SelectedMode >= 0 Then @@ -900,16 +908,31 @@ Namespace DownloadObjects End Try End Sub #End Region -#Region "Clear session" - Private Sub BTT_CLEAR_DAILY_Click(sender As Object, e As EventArgs) Handles BTT_CLEAR_DAILY.Click - If MsgBoxE({"Are you sure you want to clear this session data?", "Clear session"}, vbExclamation,,, {"Process", "Cancel"}) = 0 Then - Downloader.Files.Clear() - ClearTable() - RefillList() - End If +#Region "Sessions set, merge, clear" + Private Sub BTT_CURR_SESSION_SET_Click(sender As Object, e As EventArgs) Handles BTT_CURR_SESSION_SET.Click + Try + Dim f As SFile = Nothing + SessionChooser(False,,,, True, f) + If f.Exists Then + Using x As New XmlFile(f, Protector.Modes.All, False) With {.AllowSameNames = True, .XmlReadOnly = True} + x.LoadData() + If x.Count > 0 Then + With Downloader + .Files.Clear() + .Files.ListAddList(x, LAP.NotContainsOnly, LAP.IgnoreICopier) + .FilesLoadLastSession(f) + End With + FeedChangeMode(FeedModes.Current) + RefillList(True, False) + Else + MsgBoxE({"There is no data in the selected session", "Replace current session"}, vbCritical) + End If + End Using + End If + Catch ex As Exception + ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "Replace current session") + End Try End Sub -#End Region -#Region "Merge feeds" Private Sub BTT_MERGE_SESSIONS_Click(sender As Object, e As EventArgs) Handles BTT_MERGE_SESSIONS.Click Try Const msgTitle$ = "Merge feeds" @@ -970,6 +993,15 @@ Namespace DownloadObjects ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadFeedForm.MergeSessions]") End Try End Sub + Private Sub BTT_CLEAR_DAILY_Click(sender As Object, e As EventArgs) Handles BTT_CLEAR_DAILY.Click + If MsgBoxE({"Are you sure you want to clear this session data?", "Clear session"}, vbExclamation,,, {"Process", "Cancel"}) = 0 Then + Downloader.Files.Clear() + ClearTable() + RefillList() + End If + End Sub +#End Region +#Region "Merge feeds" Private Sub BTT_MERGE_FEEDS_Click(sender As Object, e As EventArgs) Handles BTT_MERGE_FEEDS.Click Try Const msgTitle$ = "Merge feeds" diff --git a/SCrawler/Download/TDownloader.vb b/SCrawler/Download/TDownloader.vb index f3233ed..c4a5802 100644 --- a/SCrawler/Download/TDownloader.vb +++ b/SCrawler/Download/TDownloader.vb @@ -149,24 +149,30 @@ Namespace DownloadObjects End Try End Function Private _FilesSessionChecked_Impl As Boolean = False - Friend Sub FilesLoadLastSession() + Friend Sub FilesLoadLastSession(Optional ByVal SelectedSessionFile As SFile = Nothing) Try - If Not _FilesSessionChecked And Not _FilesSessionChecked_Impl And _FilesSessionActual.IsEmptyString Then + If Not SelectedSessionFile.IsEmptyString Or (Not _FilesSessionChecked And Not _FilesSessionChecked_Impl And _FilesSessionActual.IsEmptyString) Then _FilesSessionChecked = True _FilesSessionChecked_Impl = True Dim settingValue% = Settings.FeedCurrentTryLoadLastSession - If settingValue >= 0 Then + Dim ssfExists As Boolean = Not SelectedSessionFile.IsEmptyString AndAlso SelectedSessionFile.Exists + If settingValue >= 0 Or ssfExists Then Dim startTime As Date = Process.GetCurrentProcess.StartTime - Dim files As List(Of SFile) = SFile.GetFiles(SessionsPath.CSFileP, "*.xml",, EDP.ReturnValue) - If files.ListExists Then files.RemoveAll(Settings.Feeds.FeedSpecialRemover) - If files.ListExists Then - Dim nd$ = Now.ToString("yyyyMMdd") - files.RemoveAll(Function(f) Not f.Name.StartsWith(nd)) + Dim files As List(Of SFile) + If ssfExists Then + files = New List(Of SFile) From {SelectedSessionFile} + Else + files = SFile.GetFiles(SessionsPath.CSFileP, "*.xml",, EDP.ReturnValue) + If files.ListExists Then files.RemoveAll(Settings.Feeds.FeedSpecialRemover) + If files.ListExists Then + Dim nd$ = Now.ToString("yyyyMMdd") + files.RemoveAll(Function(f) Not f.Name.StartsWith(nd)) + End If End If If files.ListExists Then files.Sort() Dim lastDate As Date = AConvert(Of Date)(files.Last.Name, SessionDateTimeProvider) - If lastDate.Date = startTime.Date Then + If ssfExists Or lastDate.Date = startTime.Date Then Dim __files As New List(Of UserMediaD) Using x As New XmlFile(files.Last, Protector.Modes.All, False) With {.AllowSameNames = True, .XmlReadOnly = True} x.LoadData() diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb index 4936e74..95ece00 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: ' - - + +