From 00a06d3e9ab0562362642d5ea66873b6028b7ab0 Mon Sep 17 00:00:00 2001 From: Andy <88590076+AAndyProgram@users.noreply.github.com> Date: Mon, 2 Sep 2024 18:22:11 +0300 Subject: [PATCH] 2024.9.2.0 Instagram: add options to enable/disable image extraction from video OnlyFans: update to the changed API YouTube: videos are parsed from the 'featured', not from the 'videos' page Feed: add prompt before moving entire feed/session MainFrame: add 'Alt+U' and 'Ctrl+U' to open the user search form UserImage: user image creation update --- Changelog.md | 16 ++ FAQ.md | 31 +- ProgramScreenshots/SettingsSiteInstagram.png | Bin 41303 -> 47346 bytes SCrawler/API/Base/UserDataBase.vb | 272 +++++++++--------- .../API/Instagram/EditorExchangeOptions.vb | 32 +++ SCrawler/API/Instagram/SiteSettings.vb | 34 ++- SCrawler/API/Instagram/UserData.GQL.vb | 4 +- SCrawler/API/Instagram/UserData.vb | 77 ++++- SCrawler/API/OnlyFans/Declarations.vb | 5 + SCrawler/API/OnlyFans/UserData.vb | 21 +- SCrawler/API/Twitter/UserData.vb | 1 + SCrawler/API/YouTube/UserData.vb | 2 +- SCrawler/Download/Feed/DownloadFeedForm.vb | 13 +- SCrawler/MainFrame.vb | 2 +- SCrawler/My Project/AssemblyInfo.vb | 4 +- SCrawler/UserImage.vb | 21 +- Tools/ArchiveSCrawlerUsersDataFiles.bat | 6 +- 17 files changed, 371 insertions(+), 170 deletions(-) diff --git a/Changelog.md b/Changelog.md index 0e7bc93..862f78d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -1,3 +1,19 @@ +# 2024.9.2.0 + +*2024-09-02* + +- Added + - Instagram: options to enable/disable image extraction from video + - Feed: **prompt before moving entire feed/session** + - Main window: hotkeys `Alt+U` and `Ctrl+U` to open the user search form + - Minor improvements +- Updated + - gallery-dl up to version **1.27.3** +- Fixed + - **OnlyFans**: data is not downloading + - YouTube (SCrawler): incorrect parsing of video page + - Minor bugs + # 2024.8.10.0 *2024-08-10* diff --git a/FAQ.md b/FAQ.md index d26eb50..b2f1b34 100644 --- a/FAQ.md +++ b/FAQ.md @@ -28,22 +28,24 @@ I strongly recommend you to **regularly** create backup copies of the settings f **Is something doesn't download, always check the [SITE'S REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements) before asking questions!** +*How to use: find your problem in the list and read the answer.* + ## General questions - **PROFILES** - - I added a profile but **nothing downloaded**: check your cookies and [site requirements](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements). If there are any optional fields that you don't fill in, do so. - - User downloading failed: check your credentials and **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**. If all settings are set and nothing works, [report it](#how-to-report-a-problem). Don't forget to attach the LOG. + - I added a profile but **nothing downloaded** :arrow_forward: check your cookies and [site requirements](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements). If there are any optional fields that you don't fill in, do so. Still nothing works - [report it](#how-to-report-a-problem)! + - User downloading failed :arrow_forward: check your credentials and **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**. If all settings are set and nothing works, [report it](#how-to-report-a-problem). Don't forget to attach the LOG. - [How to redownload user](https://github.com/AAndyProgram/SCrawler/wiki#redownload-user) - - How to **add profile** to download: copy the **[profile URL](https://github.com/AAndyProgram/SCrawler/wiki#add-user)** and press `Insert` or `Ctrl+Insert`. **ALWAYS PASTE THE USER PROFILE URL**. After that select this user and press `F5` or click the `Download selected` button. + - How to **add profile** to download :arrow_forward: copy the **[profile URL](https://github.com/AAndyProgram/SCrawler/wiki#add-user)** and press `Insert` or `Ctrl+Insert`. **ALWAYS PASTE THE USER PROFILE URL**. After that select this user and press `F5` or click the `Download selected` button. - How to download **[saved posts](https://github.com/AAndyProgram/SCrawler/wiki#saved-posts)** - **[HOW TO ADD COOKIES](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies)** - [How to report a problem](#how-to-report-a-problem) -- I want you to **add the site** to SCrawler: **I'm not currently accepting requests to add new sites**, but you can [create a plugin](https://github.com/AAndyProgram/SCrawler/wiki/Plugins) (for your site) for SCrawler. -- What language is SCrawler written in: vb.net -- I don't know vb.net and I can't write a plugin. You can write a plugin in `C#` -- I have a suggestion, will it be added: maybe if it interested me. -- How to name files using a pattern (e.g. `Site_PostID_Name.jpg`): **there is no such functionality and there are no such plans**. +- I want you to **add the site** to SCrawler :arrow_forward: **I'm not currently accepting requests to add new sites**, but you can [create a plugin](https://github.com/AAndyProgram/SCrawler/wiki/Plugins) (for your site) for SCrawler. +- What language is SCrawler written in :arrow_forward: vb.net +- I don't know vb.net and I can't write a plugin :arrow_forward: you can write a plugin in `C#` +- I have a suggestion, will it be added :arrow_forward: maybe if it interested me. +- How to name files using a pattern (e.g. `Site_PostID_Name.jpg`) :arrow_forward: **there is no such functionality and there are no such plans**. - **DON'T CHANGE THE DEFAULT SITE SETTINGS UNLESS YOU KNOW EXACTLY WHAT YOU'RE DOING!** SCrawler already has all the default settings to work. You only need to add credentials (where [required](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)). -- My computer shut down while SCrawler was running and now **SCrawler won't start or some users are missing**: restore user settings from [backup](#backup). +- My computer shut down while SCrawler was running and now **SCrawler won't start or some users are missing** :arrow_forward: restore user settings from [backup](#backup). - Installation, update and configuration - How to install: https://github.com/AAndyProgram/SCrawler#installation - How to update: https://github.com/AAndyProgram/SCrawler#updating @@ -52,12 +54,15 @@ I strongly recommend you to **regularly** create backup copies of the settings f - [How to build from source](https://github.com/AAndyProgram/SCrawler/blob/main/CONTRIBUTING.md#how-to-build-from-source) - [Video how to configure](#video-how-to-configure) - **Antivirus** - - **Antivirus detects SCrawler as a virus**: SCrawler doesn't contain any viruses at all. All code is posted on GitHub. You can review it. I have nothing to hide. SCrawler just downloads pictures and videos. That's all. If you trust SCrawler, you should just add it to the antivirus exceptions, as I did. Sometimes antiviruses identify SCawler as a virus. This is usually related to the number of files being edited (users' settings files) and the number of files being downloaded. In this case, the antivirus can also remove these files, which will damage users' settings. **If you don't trust SCrawler, just delete it.** - - **Antivirus detects gallery-dl as a virus**: it's a trustworthy program that is trusted by thousands of people around the world. Antiviruses identify some builds as containing viruses, but this is not true. **If you don't trust gallery-dl, you can simply delete it**. **But if you delete it, you won't be able to download [Twitter & Pinterest](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl).** You should decide for yourself. + - **Antivirus detects SCrawler as a virus** :arrow_forward: SCrawler doesn't contain any viruses at all. All code is posted on GitHub. You can review it. I have nothing to hide. SCrawler just downloads pictures and videos. That's all. If you trust SCrawler, you should just add it to the antivirus exceptions, as I did. Sometimes antiviruses identify SCawler as a virus. This is usually related to the number of files being edited (users' settings files) and the number of files being downloaded. In this case, the antivirus can also remove these files, which will damage users' settings. **If you don't trust SCrawler, just delete it.** + - **Antivirus detects gallery-dl as a virus** :arrow_forward: it's a trustworthy program that is trusted by thousands of people around the world. Antiviruses identify some builds as containing viruses, but this is not true. **If you don't trust gallery-dl, you can simply delete it**. **But if you delete it, you won't be able to download [Twitter & Pinterest](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl).** You should decide for yourself. ## Sites questions -- Reddit: don't use credentials at all or configure [OAuth](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-get-reddit-credentials) -- **META** (Instagram, Threads, Facebook): you need **cookies** and fill in **all fields** + +*How to use: find the site you need in the list and read the answer.* + +- Reddit: don't use credentials at all or configure [OAuth](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-get-reddit-credentials). **Reddit profiles can be downloaded without any credentials at all. Subreddits require OAuth! If nothing downloads, use OAuth!** +- **META** (**Instagram**, Threads, Facebook): you need **cookies** and fill in **all fields** - **Instagram saved posts**: I don't consider questions like "I have 10k saved posts and only 1000 were downloaded". Download posts, remove them from saved posts, delete the `Saved posts` **settings folder**, repeat. - TikTok: works via yt-dlp. If something doesn't download, we need to wait until yt-dlp fixes it. TikTok doesn't require cookies to download. - Porn sites: **COOKIES**! diff --git a/ProgramScreenshots/SettingsSiteInstagram.png b/ProgramScreenshots/SettingsSiteInstagram.png index 241c61c9546480b6fe8075b5a91885f13044fe68..d3eca2dfe3c1732ff7f8235237c167026558660f 100644 GIT binary patch literal 47346 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 BVF&;K literal 41303 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 0 Then Return _ContentList.LastOrDefault(Function(c) c.Type = UTypes.Picture And Not c.File.IsEmptyString And Not c.File.Extension = "gif").File @@ -679,6 +674,7 @@ BlockNullPicture: Protected MyFileSettings As SFile Protected MyFileData As SFile Protected MyFilePosts As SFile + Private MyMD5File As SFile Friend Overridable Property FileExists As Boolean = False Implements IUserData.FileExists Friend Overridable Property DataMerging As Boolean Get @@ -856,6 +852,7 @@ BlockNullPicture: LatestData = New List(Of UserMedia) _TempMediaList = New List(Of UserMedia) _TempPostsList = New List(Of String) + _MD5List = New List(Of String) Labels = New List(Of String) UserUpdatedEventHandlers = New List(Of IUserData.UserUpdatedEventHandler) UserDownloadStateChangedEventHandlers = New List(Of UserDownloadStateChangedEventHandler) @@ -1037,6 +1034,8 @@ BlockNullPicture: If _ContentList.Count > 0 Then x.AddRange(_ContentList) x.Save(MyFileData) End Using + If Not MyMD5File.IsEmptyString And _MD5List.Count > 0 Then _ + TextSaver.SaveTextToFile(_MD5List.ListToString(Environment.NewLine), MyMD5File, True,, EDP.None) Catch ex As Exception LogError(ex, "history saving error") End Try @@ -1131,6 +1130,7 @@ BlockNullPicture: TokenPersonal = Nothing ProgressPre.Reset() UpdateDataFiles() + _MD5Loaded = False _DownloadInProgress = True _DescriptionChecked = False _DescriptionEveryTime = Settings.UpdateUserDescriptionEveryTime @@ -1212,7 +1212,7 @@ BlockNullPicture: ProgressPre.Done() ThrowAny(Token) - If UseMD5Comparison And Not IsSubscription Then ValidateMD5(Token) : ProgressPre.Done() : ThrowAny(Token) + If RemoveExistingDuplicates And Not IsSubscription Then ValidateMD5(Token) : ProgressPre.Done() : ThrowAny(Token) If _TempPostsList.Count > 0 And Not DownloadMissingOnly And Not __isChannelsSupport Then If _TempPostsList.Count > 1000 Then _TempPostsList.ListAddList(_TempPostsList.ListTake(-2, 1000, EDP.ReturnValue).ListReverse, LAP.ClearBeforeAdd) @@ -1315,6 +1315,11 @@ BlockNullPicture: MyFilePosts = MyFileSettings MyFilePosts.Name &= "_Posts" MyFilePosts.Extension = "txt" + If Not IsSavedPosts Then + MyMD5File = MyFileSettings + MyMD5File.Name &= "_MD5" + MyMD5File.Extension = "txt" + End If Else Throw New ArgumentNullException("User.File", "User file not detected") End If @@ -1438,81 +1443,94 @@ BlockNullPicture: End Sub #End Region #Region "MD5 support" - Protected Const VALIDATE_MD5_ERROR As String = "VALIDATE_MD5_ERROR" + Private Const VALIDATE_MD5_ERROR As String = "VALIDATE_MD5_ERROR" Friend Property UseMD5Comparison As Boolean = False Protected Property StartMD5Checked As Boolean = False Friend Property RemoveExistingDuplicates As Boolean = False - Protected Overridable Sub ValidateMD5(ByVal Token As CancellationToken) + Private ReadOnly ErrMD5 As New ErrorsDescriber(EDP.ReturnValue) + Private _MD5Loaded As Boolean = False + Private Sub LoadMD5() + Try + If Not _MD5Loaded Then + _MD5Loaded = True + _MD5List.Clear() + If _ContentList.Count > 0 Then _MD5List.ListAddList(_ContentList.Select(Function(c) c.MD5), LAP.NotContainsOnly, EDP.ReturnValue) + If MyMD5File.Exists Then _MD5List.ListAddList(MyMD5File.GetLines, LAP.NotContainsOnly, EDP.ThrowException) + End If + Catch ex As Exception + ErrorsDescriber.Execute(EDP.SendToLog, ex, "LoadMD5") + End Try + End Sub + Private Function ValidateMD5_GetMD5(ByVal __data As UserMedia, ByVal IsUrl As Boolean) As String + Try + Dim ImgFormat As Imaging.ImageFormat = Nothing + Dim hash$ = String.Empty + Dim __isGif As Boolean = False + If __data.Type = UTypes.GIF Then + ImgFormat = Imaging.ImageFormat.Gif + __isGif = True + ElseIf Not __data.File.IsEmptyString Then + ImgFormat = GetImageFormat(__data.File) + End If + If ImgFormat Is Nothing Then ImgFormat = Imaging.ImageFormat.Jpeg + If IsUrl And Not __isGif Then + hash = ByteArrayToString(GetMD5(SFile.GetBytesFromNet(__data.URL.IfNullOrEmpty(__data.URL_BASE), ErrMD5), ImgFormat, ErrMD5)) + ElseIf IsUrl And __isGif Then + hash = ByteArrayToString(GetMD5FromBytes(SFile.GetBytesFromNet(__data.URL.IfNullOrEmpty(__data.URL_BASE), ErrMD5), ErrMD5)) + Else + hash = ByteArrayToString(GetMD5(SFile.GetBytes(__data.File, ErrMD5), ImgFormat, ErrMD5)) + End If + If hash.IsEmptyString And Not __isGif Then + If ImgFormat Is Imaging.ImageFormat.Jpeg Then ImgFormat = Imaging.ImageFormat.Png Else ImgFormat = Imaging.ImageFormat.Jpeg + If IsUrl Then + hash = ByteArrayToString(GetMD5(SFile.GetBytesFromNet(__data.URL.IfNullOrEmpty(__data.URL_BASE), ErrMD5), ImgFormat, ErrMD5)) + Else + hash = ByteArrayToString(GetMD5(SFile.GetBytes(__data.File, ErrMD5), ImgFormat, ErrMD5)) + End If + End If + Return hash + Catch + Return String.Empty + End Try + End Function + Private Sub ValidateMD5(ByVal Token As CancellationToken) Try Dim missingMD5 As Predicate(Of UserMedia) = Function(d) (d.Type = UTypes.GIF Or d.Type = UTypes.Picture) And d.MD5.IsEmptyString - If UseMD5Comparison And _TempMediaList.Exists(missingMD5) Then + If RemoveExistingDuplicates Then + RemoveExistingDuplicates = False + _ForceSaveUserInfo = True + LoadMD5() Dim i% Dim itemsCount% = 0 Dim limit% = If(DownloadTopCount, 0) Dim data As UserMedia = Nothing - Dim hashList As New Dictionary(Of String, SFile) Dim f As SFile - Dim ErrMD5 As New ErrorsDescriber(EDP.ReturnValue) - Dim __getMD5 As Func(Of UserMedia, Boolean, String) = - Function(ByVal __data As UserMedia, ByVal IsUrl As Boolean) As String - Try - Dim ImgFormat As Imaging.ImageFormat = Nothing - Dim hash$ = String.Empty - Dim __isGif As Boolean = False - If __data.Type = UTypes.GIF Then - ImgFormat = Imaging.ImageFormat.Gif - __isGif = True - ElseIf Not __data.File.IsEmptyString Then - ImgFormat = GetImageFormat(__data.File) - End If - If ImgFormat Is Nothing Then ImgFormat = Imaging.ImageFormat.Jpeg - If IsUrl And Not __isGif Then - hash = ByteArrayToString(GetMD5(SFile.GetBytesFromNet(__data.URL.IfNullOrEmpty(__data.URL_BASE), ErrMD5), ImgFormat, ErrMD5)) - ElseIf IsUrl And __isGif Then - hash = ByteArrayToString(GetMD5FromBytes(SFile.GetBytesFromNet(__data.URL.IfNullOrEmpty(__data.URL_BASE), ErrMD5), ErrMD5)) - Else - hash = ByteArrayToString(GetMD5(SFile.GetBytes(__data.File, ErrMD5), ImgFormat, ErrMD5)) - End If - If hash.IsEmptyString And Not __isGif Then - If ImgFormat Is Imaging.ImageFormat.Jpeg Then ImgFormat = Imaging.ImageFormat.Png Else ImgFormat = Imaging.ImageFormat.Jpeg - If IsUrl Then - hash = ByteArrayToString(GetMD5(SFile.GetBytesFromNet(__data.URL.IfNullOrEmpty(__data.URL_BASE), ErrMD5), ImgFormat, ErrMD5)) - Else - hash = ByteArrayToString(GetMD5(SFile.GetBytes(__data.File, ErrMD5), ImgFormat, ErrMD5)) - End If - End If - Return hash - Catch - Return String.Empty - End Try - End Function + If Not StartMD5Checked Then StartMD5Checked = True - If _ContentList.Exists(missingMD5) Then - Dim existingFiles As List(Of SFile) = SFile.GetFiles(MyFileSettings.CutPath, "*.jpg|*.jpeg|*.png|*.gif",, EDP.ReturnValue).ListIfNothing - Dim eIndx% - Dim eFinder As Predicate(Of SFile) = Function(ff) ff.File = data.File.File - If RemoveExistingDuplicates Then - RemoveExistingDuplicates = False - _ForceSaveUserInfo = True - If existingFiles.Count > 0 Then - Dim h$ - ProgressPre.ChangeMax(existingFiles.Count) - For i = existingFiles.Count - 1 To 0 Step -1 - ProgressPre.Perform() - h = __getMD5(New UserMedia With {.File = existingFiles(i)}, False) - If Not h.IsEmptyString Then - If hashList.ContainsKey(h) Then - MyMainLOG = $"{ToStringForLog()}: Removed image [{existingFiles(i).File}] (duplicate of [{hashList(h).File}])" - existingFiles(i).Delete(SFO.File, SFODelete.DeleteToRecycleBin, ErrMD5) - existingFiles.RemoveAt(i) - Else - hashList.Add(h, existingFiles(i)) - End If - End If - Next + Dim existingFiles As List(Of SFile) = SFile.GetFiles(MyFileSettings.CutPath, "*.jpg|*.jpeg|*.png|*.gif",, EDP.ReturnValue).ListIfNothing + Dim eIndx% + Dim eFinder As Predicate(Of SFile) = Function(ff) ff.File = data.File.File + + If existingFiles.Count > 0 Then + Dim h$ + ProgressPre.ChangeMax(existingFiles.Count) + For i = existingFiles.Count - 1 To 0 Step -1 + ProgressPre.Perform() + h = ValidateMD5_GetMD5(New UserMedia With {.File = existingFiles(i)}, False) + If Not h.IsEmptyString Then + If _MD5List.Contains(h) Then + MyMainLOG = $"{ToStringForLog()}: Removed image [{existingFiles(i).File}] (duplicate)" + existingFiles(i).Delete(SFO.File, SFODelete.DeleteToRecycleBin, ErrMD5) + existingFiles.RemoveAt(i) + Else + _MD5List.Add(h) + End If End If - End If + Next + End If + + If _ContentList.Count > 0 AndAlso _ContentList.Exists(missingMD5) Then ProgressPre.ChangeMax(_ContentList.Count) For i = 0 To _ContentList.Count - 1 data = _ContentList(i) @@ -1522,61 +1540,34 @@ BlockNullPicture: ThrowAny(Token) eIndx = existingFiles.FindIndex(eFinder) If eIndx >= 0 Then - data.MD5 = __getMD5(New UserMedia With {.File = existingFiles(eIndx)}, False) + data.MD5 = ValidateMD5_GetMD5(New UserMedia With {.File = existingFiles(eIndx)}, False) If Not data.MD5.IsEmptyString Then _ContentList(i) = data : _ForceSaveUserData = True End If End If existingFiles.RemoveAll(eFinder) End If Next - If existingFiles.Count > 0 Then - ProgressPre.ChangeMax(existingFiles.Count) - For i = 0 To existingFiles.Count - 1 - f = existingFiles(i) - ProgressPre.Perform() - data = New UserMedia(f.File) With { - .State = UStates.Downloaded, - .Type = IIf(f.Extension = "gif", UTypes.GIF, UTypes.Picture), - .File = f - } - ThrowAny(Token) - data.MD5 = __getMD5(data, False) - If Not data.MD5.IsEmptyString Then _ContentList.Add(data) : _ForceSaveUserData = True - Next - existingFiles.Clear() - End If End If - End If - If _ContentList.Count > 0 Then - With _ContentList.Select(Function(d) d.MD5) - If .ListExists Then .ToList.ForEach(Sub(md5value) _ - If Not md5value.IsEmptyString AndAlso Not hashList.ContainsKey(md5value) Then hashList.Add(md5value, New SFile)) - End With - End If - - ProgressPre.ChangeMax(_TempMediaList.Count) - For i = _TempMediaList.Count - 1 To 0 Step -1 - ProgressPre.Perform() - If limit > 0 And itemsCount >= limit Then - _TempMediaList.RemoveAt(i) - Else - data = _TempMediaList(i) - If missingMD5(data) Then + If existingFiles.Count > 0 Then + ProgressPre.ChangeMax(existingFiles.Count) + For i = 0 To existingFiles.Count - 1 + f = existingFiles(i) + ProgressPre.Perform() + data = New UserMedia(f.File) With { + .State = UStates.Downloaded, + .Type = IIf(f.Extension = "gif", UTypes.GIF, UTypes.Picture), + .File = f + } ThrowAny(Token) - data.MD5 = __getMD5(data, True) - If Not data.MD5.IsEmptyString Then - If hashList.ContainsKey(data.MD5) Then - _TempMediaList.RemoveAt(i) - Else - hashList.Add(data.MD5, New SFile) - _TempMediaList(i) = data - itemsCount += 1 - End If - End If - End If + data.MD5 = ValidateMD5_GetMD5(data, False) + If Not data.MD5.IsEmptyString Then _ContentList.Add(data) : _ForceSaveUserData = True + Next + existingFiles.Clear() End If - Next + End If + + If _ContentList.Count > 0 Then _MD5List.ListAddList(_ContentList.Select(Function(d) d.MD5), LAP.NotContainsOnly, EDP.ReturnValue) End If Catch iex As ArgumentOutOfRangeException When Disposed Catch ex As Exception @@ -1614,6 +1605,7 @@ BlockNullPicture: Source.Progress.Done() End Sub End Class + Protected Const VideoFolderName As String = "Video" Protected Sub DownloadContentDefault(ByVal Token As CancellationToken) Try Dim i% @@ -1622,6 +1614,7 @@ BlockNullPicture: If _ContentNew.Count > 0 Then _ContentNew.RemoveAll(Function(c) c.URL.IsEmptyString) If _ContentNew.Count > 0 Then + If UseMD5Comparison Then LoadMD5() MyFile.Exists(SFO.Path) Dim MissingErrorsAdd As Boolean = Settings.AddMissingErrorsToLog Dim MyDir$ = DownloadContentDefault_GetRootDir() @@ -1630,6 +1623,7 @@ BlockNullPicture: Dim __interrupt As Boolean Dim f As SFile Dim v As UserMedia + Dim __fileDeleted As Boolean Dim fileNumProvider As SFileNumbers = SFileNumbers.Default Dim __deleteFile As Action(Of SFile, String) = Sub(ByVal FileToDelete As SFile, ByVal FileUrl As String) Try @@ -1641,9 +1635,21 @@ BlockNullPicture: ErrorsDescriber.Execute(EDP.SendToLog, file_del_ex) End Try End Sub + Dim updateDownCount As Action = Sub() + Dim __n% = IIf(__fileDeleted, -1, 1) + If __isVideo Then + v.Type = UTypes.Video + DownloadedVideos(False) += __n + ElseIf v.Type = UTypes.GIF Then + DownloadedPictures(False) += __n + Else + v.Type = UTypes.Picture + DownloadedPictures(False) += __n + End If + End Sub Using w As New OptionalWebClient(Me) - If vsf Then CSFileP($"{MyDir}\Video\").Exists(SFO.Path) + If vsf Then CSFileP($"{MyDir}\{VideoFolderName}\").Exists(SFO.Path) Progress.Maximum += _ContentNew.Count If IsSingleObjectDownload Then If _ContentNew.Count = 1 And _ContentNew(0).Type = UTypes.Video Then @@ -1671,6 +1677,8 @@ BlockNullPicture: If v.URL_BASE.IsEmptyString Then v.URL_BASE = v.URL + __fileDeleted = False + If Not f.IsEmptyString And Not v.URL.IsEmptyString Then Try __isVideo = v.Type = UTypes.Video Or f.Extension = "mp4" Or v.Type = UTypes.m3u8 @@ -1691,7 +1699,7 @@ BlockNullPicture: End If If __isVideo And vsf Then If v.SpecialFolder.IsEmptyString OrElse Not v.SpecialFolder.EndsWith("*") Then - f.Path = $"{f.PathWithSeparator}Video" + f.Path = $"{f.PathWithSeparator}{VideoFolderName}" If Not v.SpecialFolder.IsEmptyString Then f.Exists(SFO.Path) End If End If @@ -1715,19 +1723,26 @@ BlockNullPicture: End If End If - If __isVideo Then - v.Type = UTypes.Video - DownloadedVideos(False) += 1 - ElseIf v.Type = UTypes.GIF Then - DownloadedPictures(False) += 1 - Else - v.Type = UTypes.Picture - DownloadedPictures(False) += 1 - End If + updateDownCount() v.File = ChangeFileNameByProvider(f, v) v.State = UStates.Downloaded DownloadContentDefault_PostProcessing(v, f, Token) + If UseMD5Comparison And (v.Type = UTypes.GIF Or v.Type = UTypes.Picture) Then + If v.File.Exists Then + v.MD5 = ValidateMD5_GetMD5(v, False) + If Not v.MD5.IsEmptyString Then + If _MD5List.Contains(v.MD5) Then + __fileDeleted = v.File.Delete(SFO.File, SFODelete.DeletePermanently, EDP.ReturnValue) + If __fileDeleted Then dCount -= 1 : updateDownCount() + Else + _MD5List.Add(v.MD5) + End If + End If + Else + dCount -= 1 + End If + End If dCount += 1 Catch woex As OperationCanceledException When Token.IsCancellationRequested __deleteFile.Invoke(f, v.URL_BASE) @@ -1745,7 +1760,7 @@ BlockNullPicture: Else v.State = UStates.Skipped End If - _ContentNew(i) = v + If Not __fileDeleted Then _ContentNew(i) = v If DownloadTopCount.HasValue AndAlso dCount >= DownloadTopCount.Value Then Progress.Perform(_ContentNew.Count - dTotal) Exit Sub @@ -2240,6 +2255,7 @@ BlockNullPicture: LatestData.Clear() _TempMediaList.Clear() _TempPostsList.Clear() + _MD5List.Clear() TokenPersonal = Nothing If Not ProgressPre Is Nothing Then ProgressPre.Reset() : ProgressPre.Dispose() If Not Responser Is Nothing Then Responser.Dispose() diff --git a/SCrawler/API/Instagram/EditorExchangeOptions.vb b/SCrawler/API/Instagram/EditorExchangeOptions.vb index 6171a0e..7de2501 100644 --- a/SCrawler/API/Instagram/EditorExchangeOptions.vb +++ b/SCrawler/API/Instagram/EditorExchangeOptions.vb @@ -9,6 +9,7 @@ Imports SCrawler.Plugin.Attributes Namespace API.Instagram Friend Class EditorExchangeOptions +#Region "Download" Friend Property GetTimeline As Boolean @@ -19,6 +20,21 @@ Namespace API.Instagram Friend Property GetStoriesUser As Boolean Friend Property GetTagged As Boolean +#End Region +#Region "Extract image" + + Friend Property GetTimeline_VideoPic As Boolean + + Friend Property GetReels_VideoPic As Boolean + + Friend Property GetStories_VideoPic As Boolean + + Friend Property GetStoriesUser_VideoPic As Boolean + + Friend Property GetTagged_VideoPic As Boolean +#End Region + + Friend Property PutImageVideoFolder As Boolean Friend Sub New(ByVal u As UserData) With u GetTimeline = .GetTimeline @@ -26,6 +42,14 @@ Namespace API.Instagram GetStories = .GetStories GetStoriesUser = .GetStoriesUser GetTagged = .GetTaggedData + + GetTimeline_VideoPic = .GetTimeline_VideoPic + GetReels_VideoPic = .GetReels_VideoPic + GetStories_VideoPic = .GetStories_VideoPic + GetStoriesUser_VideoPic = .GetStoriesUser_VideoPic + GetTagged_VideoPic = .GetTaggedData_VideoPic + + PutImageVideoFolder = .PutImageVideoFolder End With End Sub Friend Sub New(ByVal s As SiteSettings) @@ -35,6 +59,14 @@ Namespace API.Instagram GetStories = CBool(.GetStories.Value) GetStoriesUser = CBool(.GetStoriesUser.Value) GetTagged = CBool(.GetTagged.Value) + + GetTimeline_VideoPic = CBool(.GetTimeline_VideoPic.Value) + GetReels_VideoPic = CBool(.GetReels_VideoPic.Value) + GetStories_VideoPic = CBool(.GetStories_VideoPic.Value) + GetStoriesUser_VideoPic = CBool(.GetStoriesUser_VideoPic.Value) + GetTagged_VideoPic = CBool(.GetTagged_VideoPic.Value) + + PutImageVideoFolder = CBool(.PutImageVideoFolder.Value) End With End Sub End Class diff --git a/SCrawler/API/Instagram/SiteSettings.vb b/SCrawler/API/Instagram/SiteSettings.vb index 039594a..cebff7e 100644 --- a/SCrawler/API/Instagram/SiteSettings.vb +++ b/SCrawler/API/Instagram/SiteSettings.vb @@ -57,6 +57,7 @@ Namespace API.Instagram #End Region #Region "Categories" Private Const CAT_DOWN As String = "Download data" + Private Const CAT_UserDefs_VIDEO As String = DN.CAT_UserDefs & ": extract image from video" #End Region #Region "Authorization properties" Friend Const Header_IG_APP_ID As String = "x-ig-app-id" @@ -187,14 +188,28 @@ Namespace API.Instagram Private ReadOnly Property SleepTimerOnPostsLimitProvider As IFormatProvider Friend ReadOnly Property GetTimeline As PropertyValue + + Friend ReadOnly Property GetTimeline_VideoPic As PropertyValue Friend ReadOnly Property GetReels As PropertyValue + + Friend ReadOnly Property GetReels_VideoPic As PropertyValue Friend ReadOnly Property GetStories As PropertyValue + + Friend ReadOnly Property GetStories_VideoPic As PropertyValue Friend ReadOnly Property GetStoriesUser As PropertyValue - + + Friend ReadOnly Property GetStoriesUser_VideoPic As PropertyValue + Friend ReadOnly Property GetTagged As PropertyValue + + Friend ReadOnly Property GetTagged_VideoPic As PropertyValue + + Friend ReadOnly Property GetSavedPosts_VideoPic As PropertyValue + + Friend ReadOnly Property PutImageVideoFolder As PropertyValue @@ -203,19 +218,19 @@ Namespace API.Instagram Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider #End Region #Region "Download ready" - + 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 @@ -425,10 +440,17 @@ Namespace API.Instagram SleepTimerOnPostsLimitProvider = New TimersChecker(10000) GetTimeline = New PropertyValue(True) + GetTimeline_VideoPic = New PropertyValue(True) GetReels = New PropertyValue(False) + GetReels_VideoPic = New PropertyValue(True) GetStories = New PropertyValue(False) + GetStories_VideoPic = New PropertyValue(True) GetStoriesUser = New PropertyValue(False) + GetStoriesUser_VideoPic = New PropertyValue(True) GetTagged = New PropertyValue(False) + GetTagged_VideoPic = New PropertyValue(True) + GetSavedPosts_VideoPic = New PropertyValue(True) + PutImageVideoFolder = New PropertyValue(False) TaggedNotifyLimit = New PropertyValue(200) TaggedNotifyLimitProvider = New TaggedNotifyLimitChecker diff --git a/SCrawler/API/Instagram/UserData.GQL.vb b/SCrawler/API/Instagram/UserData.GQL.vb index a436501..ddccb8b 100644 --- a/SCrawler/API/Instagram/UserData.GQL.vb +++ b/SCrawler/API/Instagram/UserData.GQL.vb @@ -194,7 +194,7 @@ Namespace API.Instagram With j({"data", "xdt_api__v1__feed__reels_media__connection", "edges"}) If .ListExists Then ProgressPre.ChangeMax(.Count) - For Each n As EContainer In .Self : GetStoriesData_ParseSingleHighlight(n("node"), i, False, Token) : Next + For Each n As EContainer In .Self : GetStoriesData_ParseSingleHighlight(n("node"), i, False, Token, Sections.Stories) : Next End If End With End If @@ -217,7 +217,7 @@ Namespace API.Instagram Using j As EContainer = JsonDocument.Parse(r) If j.ListExists Then Dim i% = -1 - GetStoriesData_ParseSingleHighlight(j.ItemF({"data", "xdt_api__v1__feed__reels_media", "reels_media", 0}), i, True, Token) + GetStoriesData_ParseSingleHighlight(j.ItemF({"data", "xdt_api__v1__feed__reels_media", "reels_media", 0}), i, True, Token, Sections.UserStories) End If End Using End If diff --git a/SCrawler/API/Instagram/UserData.vb b/SCrawler/API/Instagram/UserData.vb index b00fa96..12573d1 100644 --- a/SCrawler/API/Instagram/UserData.vb +++ b/SCrawler/API/Instagram/UserData.vb @@ -26,10 +26,16 @@ Namespace API.Instagram Private Const Name_LastCursor As String = "LastCursor" Private Const Name_FirstLoadingDone As String = "FirstLoadingDone" Private Const Name_GetTimeline As String = "GetTimeline" + Private Const Name_GetTimeline_VideoPic As String = "GetTimeline_VideoPic" Private Const Name_GetReels As String = "GetReels" + Private Const Name_GetReels_VideoPic As String = "GetReels_VideoPic" Private Const Name_GetStories As String = "GetStories" + Private Const Name_GetStories_VideoPic As String = "GetStories_VideoPic" Private Const Name_GetStoriesUser As String = "GetStoriesUser" + Private Const Name_GetStoriesUser_VideoPic As String = "GetStoriesUser_VideoPic" Private Const Name_GetTagged As String = "GetTaggedData" + Private Const Name_GetTagged_VideoPic As String = "GetTaggedData_VideoPic" + Private Const Name_PutImageVideoFolder As String = "PutImageVideoFolder" Private Const Name_TaggedChecked As String = "TaggedChecked" Private Const Name_NameTrue As String = "NameTrue" #End Region @@ -79,10 +85,32 @@ Namespace API.Instagram Private LastCursor As String = String.Empty Private FirstLoadingDone As Boolean = False Friend Property GetTimeline As Boolean = True + Friend Property GetTimeline_VideoPic As Boolean = True Friend Property GetReels As Boolean = False + Friend Property GetReels_VideoPic As Boolean = True Friend Property GetStories As Boolean + Friend Property GetStories_VideoPic As Boolean = True Friend Property GetStoriesUser As Boolean + Friend Property GetStoriesUser_VideoPic As Boolean = True Friend Property GetTaggedData As Boolean + Friend Property GetTaggedData_VideoPic As Boolean = True + Friend Property PutImageVideoFolder As Boolean = False + Private Function ExtractImageFrom(ByVal Section As Sections) As Boolean + Select Case Section + Case Sections.Timeline : Return GetTimeline_VideoPic + Case Sections.Reels : Return GetReels_VideoPic + Case Sections.Tagged : Return GetTaggedData_VideoPic + Case Sections.Stories : Return GetStories_VideoPic + Case Sections.UserStories : Return GetStoriesUser_VideoPic + Case Sections.SavedPosts + Try + If Not HOST Is Nothing AndAlso HOST.Key = InstagramSiteKey Then Return MySiteSettings.GetSavedPosts_VideoPic.Value + Catch + End Try + Return True + Case Else : Return True + End Select + End Function Protected _NameTrue As String = String.Empty Friend ReadOnly Property NameTrue As String Get @@ -98,20 +126,32 @@ Namespace API.Instagram LastCursor = .Value(Name_LastCursor) FirstLoadingDone = .Value(Name_FirstLoadingDone).FromXML(Of Boolean)(False) GetTimeline = .Value(Name_GetTimeline).FromXML(Of Boolean)(CBool(MySiteSettings.GetTimeline.Value)) - GetReels = .Value(Name_GetReels).FromXML(Of Boolean)(MySiteSettings.GetReels.Value) + GetTimeline_VideoPic = .Value(Name_GetTimeline_VideoPic).FromXML(Of Boolean)(CBool(MySiteSettings.GetTimeline_VideoPic.Value)) + GetReels = .Value(Name_GetReels).FromXML(Of Boolean)(CBool(MySiteSettings.GetReels.Value)) + GetReels_VideoPic = .Value(Name_GetReels_VideoPic).FromXML(Of Boolean)(CBool(MySiteSettings.GetReels_VideoPic.Value)) GetStories = .Value(Name_GetStories).FromXML(Of Boolean)(CBool(MySiteSettings.GetStories.Value)) - GetStoriesUser = .Value(Name_GetStoriesUser).FromXML(Of Boolean)(MySiteSettings.GetStoriesUser.Value) + GetStories_VideoPic = .Value(Name_GetStories_VideoPic).FromXML(Of Boolean)(CBool(MySiteSettings.GetStories_VideoPic.Value)) + GetStoriesUser = .Value(Name_GetStoriesUser).FromXML(Of Boolean)(CBool(MySiteSettings.GetStoriesUser.Value)) + GetStoriesUser_VideoPic = .Value(Name_GetStoriesUser_VideoPic).FromXML(Of Boolean)(CBool(MySiteSettings.GetStoriesUser_VideoPic.Value)) + PutImageVideoFolder = .Value(Name_PutImageVideoFolder).FromXML(Of Boolean)(CBool(MySiteSettings.PutImageVideoFolder.Value)) GetTaggedData = .Value(Name_GetTagged).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged.Value)) + GetTaggedData_VideoPic = .Value(Name_GetTagged_VideoPic).FromXML(Of Boolean)(CBool(MySiteSettings.GetTagged_VideoPic.Value)) TaggedChecked = .Value(Name_TaggedChecked).FromXML(Of Boolean)(False) _NameTrue = .Value(Name_NameTrue) Else .Add(Name_LastCursor, LastCursor) .Add(Name_FirstLoadingDone, FirstLoadingDone.BoolToInteger) .Add(Name_GetTimeline, GetTimeline.BoolToInteger) + .Add(Name_GetTimeline_VideoPic, GetTimeline_VideoPic.BoolToInteger) .Add(Name_GetReels, GetReels.BoolToInteger) + .Add(Name_GetReels_VideoPic, GetReels_VideoPic.BoolToInteger) .Add(Name_GetStories, GetStories.BoolToInteger) + .Add(Name_GetStories_VideoPic, GetStories_VideoPic.BoolToInteger) .Add(Name_GetStoriesUser, GetStoriesUser.BoolToInteger) + .Add(Name_GetStoriesUser_VideoPic, GetStoriesUser_VideoPic.BoolToInteger) .Add(Name_GetTagged, GetTaggedData.BoolToInteger) + .Add(Name_GetTagged_VideoPic, GetTaggedData_VideoPic.BoolToInteger) + .Add(Name_PutImageVideoFolder, PutImageVideoFolder.BoolToInteger) .Add(Name_TaggedChecked, TaggedChecked.BoolToInteger) .Add(Name_NameTrue, _NameTrue) End If @@ -130,6 +170,14 @@ Namespace API.Instagram GetStories = .GetStories GetStoriesUser = .GetStoriesUser GetTaggedData = .GetTagged + + GetTimeline_VideoPic = .GetTimeline_VideoPic + GetReels_VideoPic = .GetReels_VideoPic + GetStories_VideoPic = .GetStories_VideoPic + GetStoriesUser_VideoPic = .GetStoriesUser_VideoPic + GetTaggedData_VideoPic = .GetTagged_VideoPic + + PutImageVideoFolder = .PutImageVideoFolder End With End If End Sub @@ -809,7 +857,7 @@ NextPageBlock: With j("items") For Each jj In .Self before = _TempMediaList.Count - ObtainMedia(jj, PostsToReparse(i).ID, specFolder) + ObtainMedia(jj, PostsToReparse(i).ID, specFolder,,,,,,, IIf(IsTagged, Sections.Tagged, Sections.Timeline)) If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1 If _Limit > 0 And _TotalPostsParsed >= _Limit Then Throw New ExitException Next @@ -911,7 +959,7 @@ NextPageBlock: End Select End If before = _TempMediaList.Count - ObtainMedia(.Self, PostIDKV.ID, SpecFolder, PostDate,, PostOriginUrl, State, Attempts) + ObtainMedia(.Self, PostIDKV.ID, SpecFolder, PostDate,, PostOriginUrl, State, Attempts,, Section) If Not before = _TempMediaList.Count Then _TotalPostsParsed += 1 If _Limit > 0 And _TotalPostsParsed >= _Limit Then Return False End If @@ -950,6 +998,7 @@ NextPageBlock: Protected ObtainMedia_SizeFuncVid As Func(Of EContainer, Sizes) = Nothing Protected ObtainMedia_SizeFuncPic As Func(Of EContainer, Sizes) = Nothing Protected ObtainMedia_AllowAbstract As Boolean = False + Private Const ObtainMedia_NoSection As Integer = -10 Protected Sub ObtainMedia_SetReelsFunc() ObtainMedia_SizeFuncPic = Function(ByVal ss As EContainer) As Sizes If ss.Value("url").IsEmptyString Then @@ -971,7 +1020,8 @@ NextPageBlock: Optional ByVal DateObj As String = Nothing, Optional ByVal InitialType As Integer = -1, Optional ByVal PostOriginUrl As String = Nothing, Optional ByVal State As UStates = UStates.Unknown, Optional ByVal Attempts As Integer = 0, - Optional ByVal TryExtractImage As Boolean = False) + Optional ByVal TryExtractImage As Boolean = False, + Optional ByVal Section As Sections = ObtainMedia_NoSection) Try Dim maxSize As Func(Of EContainer, Integer) = Function(ByVal _ss As EContainer) As Integer Dim w% = AConvert(Of Integer)(_ss.Value("width"), 0) @@ -1018,6 +1068,12 @@ NextPageBlock: If TryExtractImage Then t = 1 abstractDecision = True + If Not SpecialFolder.IsEmptyString AndAlso PutImageVideoFolder Then + Dim endsAbs As Boolean = SpecialFolder.EndsWith("*") + If endsAbs Then SpecialFolder = SpecialFolder.TrimEnd("*") + If Not SpecialFolder.IsEmptyString Then SpecialFolder = $"{SpecialFolder.TrimEnd("\")}\{VideoFolderName}{IIf(Not endsAbs, "*", String.Empty)}" + If endsAbs Then SpecialFolder &= "*" + End If ElseIf t = -1 And InitialType = 8 And ObtainMedia_AllowAbstract Then If n.Contains(vid) Then t = 2 @@ -1064,7 +1120,8 @@ NextPageBlock: End If End With End If - If Not TryExtractImage Then ObtainMedia(n, PostID, SpecialFolder, DateObj, InitialType, PostOriginUrl, State, Attempts, True) + If Not TryExtractImage And Not Section = ObtainMedia_NoSection And ExtractImageFrom(Section) Then _ + ObtainMedia(n, PostID, SpecialFolder, DateObj, InitialType, PostOriginUrl, State, Attempts, True, Section) Case 8 'gallery DateObj = mDate(n) With n("carousel_media").XmlIfNothing @@ -1165,6 +1222,7 @@ NextPageBlock: Dim qStr$, r$ Dim i% = -1 Dim jj As EContainer + Dim section As Sections = IIf(GetUserStory, Sections.UserStories, Sections.Stories) ThrowAny(Token) If StoriesList.ListExists Or GetUserStory Then If Not GetUserStory Then tmpList = StoriesList.Take(5) @@ -1181,7 +1239,7 @@ NextPageBlock: Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing If j.Contains("reels") Then ProgressPre.ChangeMax(j("reels").Count) - For Each jj In j("reels") : GetStoriesData_ParseSingleHighlight(jj, i, GetUserStory, Token) : Next + For Each jj In j("reels") : GetStoriesData_ParseSingleHighlight(jj, i, GetUserStory, Token, section) : Next End If End Using End If @@ -1189,7 +1247,8 @@ NextPageBlock: End If End If End Sub - Private Sub GetStoriesData_ParseSingleHighlight(ByVal Node As EContainer, ByRef Index As Integer, ByVal GetUserStory As Boolean, ByVal Token As CancellationToken) + Private Sub GetStoriesData_ParseSingleHighlight(ByVal Node As EContainer, ByRef Index As Integer, ByVal GetUserStory As Boolean, + ByVal Token As CancellationToken, Optional ByVal Section As Sections = Sections.Stories) If Not Node Is Nothing Then With Node ProgressPre.Perform() @@ -1210,7 +1269,7 @@ NextPageBlock: pid = storyID & s.Value("id") If Not _TempPostsList.Contains(pid) Then ThrowAny(Token) - ObtainMedia(s, pid, sFolder) + ObtainMedia(s, pid, sFolder,,,,,,, Section) _TempPostsList.Add(pid) End If Next diff --git a/SCrawler/API/OnlyFans/Declarations.vb b/SCrawler/API/OnlyFans/Declarations.vb index a7938f3..b697206 100644 --- a/SCrawler/API/OnlyFans/Declarations.vb +++ b/SCrawler/API/OnlyFans/Declarations.vb @@ -11,6 +11,11 @@ Namespace API.OnlyFans Friend Module Declarations Friend ReadOnly DateProvider As New ADateTime("O") Friend ReadOnly RegExPostID As RParams = RParams.DM("(?<=onlyfans\.com/)(\d+)", 0, EDP.ReturnValue) + Friend ReadOnly FilesSources As New List(Of Object()) From { + {{"source", "source"}}, + {{"files", "source", "url"}}, + {{"files", "full", "url"}} + } Friend Property Rules As DynamicRulesEnv End Module End Namespace \ No newline at end of file diff --git a/SCrawler/API/OnlyFans/UserData.vb b/SCrawler/API/OnlyFans/UserData.vb index 777e623..050641d 100644 --- a/SCrawler/API/OnlyFans/UserData.vb +++ b/SCrawler/API/OnlyFans/UserData.vb @@ -394,6 +394,14 @@ Namespace API.OnlyFans Loop While Not _complete End Sub #End Region + Private Function GetMediaURL(ByVal m As EContainer) As String + Dim v$ + For Each node As Object() In FilesSources + v = If(m.ItemF(node)?.Value, String.Empty) + If Not v.IsEmptyString Then Return v + Next + Return String.Empty + End Function Private Function TryCreateMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal PostDate As String = Nothing, Optional ByRef Result As Boolean = False, Optional ByVal IsHL As Boolean = False, Optional ByVal SpecFolder As String = Nothing, Optional ByVal PostUserID As String = Nothing, @@ -405,11 +413,14 @@ Namespace API.OnlyFans With n("media") If .ListExists Then For Each m In .Self - If IsHL Then - postUrl = m.Value({"files", "source"}, "url") - Else - postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full")) - End If + postUrl = GetMediaURL(m) + 'If IsHL Then + ' 'postUrl = m.Value({"files", "source"}, "url") + ' postUrl = GetMediaURL(m) + 'Else + ' 'postUrl = m.Value({"source"}, "source").IfNullOrEmpty(m.Value("full")) + ' postUrl = GetMediaURL(m) + 'End If postUrlBase = String.Empty Select Case m.Value("type") Case "photo" : t = UTypes.Picture : ext = "jpg" diff --git a/SCrawler/API/Twitter/UserData.vb b/SCrawler/API/Twitter/UserData.vb index b492385..a8987b5 100644 --- a/SCrawler/API/Twitter/UserData.vb +++ b/SCrawler/API/Twitter/UserData.vb @@ -90,6 +90,7 @@ Namespace API.Twitter GifsPrefix = .GifsPrefix UseMD5Comparison = .UseMD5Comparison RemoveExistingDuplicates = .RemoveExistingDuplicates + If RemoveExistingDuplicates Then StartMD5Checked = False DownloadModel = DownloadModels.Undefined DownloadModelForceApply = .DownloadModelForceApply MediaModelAllowNonUserTweets = .MediaModelAllowNonUserTweets diff --git a/SCrawler/API/YouTube/UserData.vb b/SCrawler/API/YouTube/UserData.vb index fab709d..39b1d68 100644 --- a/SCrawler/API/YouTube/UserData.vb +++ b/SCrawler/API/YouTube/UserData.vb @@ -203,7 +203,7 @@ Namespace API.YouTube If IsMusic Or DownloadYTVideos Then maxDate = Nothing LastDownloadDateVideos = nDate(LastDownloadDateVideos) - url = $"https://{IIf(IsMusic, "music", "www")}.youtube.com/{IIf(IsMusic Or IsChannelUser, $"{YouTubeFunctions.UserChannelOption}/", "@")}{ID}" + url = $"https://{IIf(IsMusic, "music", "www")}.youtube.com/{IIf(IsMusic Or IsChannelUser, $"{YouTubeFunctions.UserChannelOption}/", "@")}{ID}/videos" container = YouTubeFunctions.Parse(url, YTUseCookies, Token, pr, __getMinDate(LastDownloadDateVideos), __maxDate,, True) applySpecFolder.Invoke(IIf(IsMusic, String.Empty, "Videos"), False) If fillList.Invoke(LastDownloadDateVideos, False) Then LastDownloadDateVideos = If(maxDate, Now) diff --git a/SCrawler/Download/Feed/DownloadFeedForm.vb b/SCrawler/Download/Feed/DownloadFeedForm.vb index d1df807..334a65e 100644 --- a/SCrawler/Download/Feed/DownloadFeedForm.vb +++ b/SCrawler/Download/Feed/DownloadFeedForm.vb @@ -550,7 +550,7 @@ Namespace DownloadObjects End Sub Private Function MoveCopyFiles(ByVal IsInternal As Boolean, ByVal Sender As Object, ByVal MCTOptions As FeedMoveCopyTo, ByVal FeedMediaData As FeedMedia, Optional ByVal GetChecked As Boolean = True) As Boolean - Const MsgTitle$ = "Copy/Move checked files" + Dim MsgTitle$ = "Copy/Move checked files" Try Dim isCopy As Boolean = Not Sender Is Nothing AndAlso (Sender Is BTT_COPY_TO OrElse Sender Is BTT_COPY_SPEC_TO) Dim moveOptions As FeedMoveCopyTo = Nothing @@ -591,7 +591,18 @@ Namespace DownloadObjects data = {FeedMediaData.Media} data_files = {FeedMediaData.File} End If + + MsgTitle = $"{IIf(isCopy, "Copy", "Move")} {IIf(Not FeedMediaData Is Nothing Or GetChecked, "checked", "ALL")} files" + If data.ListExists Then + + If (FeedMediaData Is Nothing And Not GetChecked And Not isCopy) AndAlso + MsgBoxE({$"YOU ARE TRYING TO MOVE ALL FEED/SESSION DATA.{vbCr}EVERY FILE WILL BE MOVED, NOT JUST THE SELECTED ONES.", MsgTitle}, + vbExclamation,,, {"Process", "Cancel"}) = 1 Then + ShowOperationCanceledMsg(MsgTitle) + Return False + End If + If MCTOptions.Destination.IsEmptyString Then Using f As New FeedCopyToForm(data_files, isCopy) f.ShowDialog() diff --git a/SCrawler/MainFrame.vb b/SCrawler/MainFrame.vb index 69c8bd2..a01c52e 100644 --- a/SCrawler/MainFrame.vb +++ b/SCrawler/MainFrame.vb @@ -247,7 +247,7 @@ CloseResume: BTT_DOWN_AUTOMATION.PerformClick() ElseIf e.Alt And e.KeyCode = Keys.P Then BTT_PR_INFO.PerformClick() - ElseIf e.Alt And e.KeyCode = Keys.F Then + ElseIf (e.Alt And (e.KeyCode = Keys.F Or e.KeyCode = Keys.U)) Or (e.Control And e.KeyCode = Keys.U) Then MySearch.FormShow() Else b = False diff --git a/SCrawler/My Project/AssemblyInfo.vb b/SCrawler/My Project/AssemblyInfo.vb index 5016e2e..f20c41e 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/UserImage.vb b/SCrawler/UserImage.vb index 776bb4e..b115e4e 100644 --- a/SCrawler/UserImage.vb +++ b/SCrawler/UserImage.vb @@ -13,6 +13,25 @@ Friend Class UserImage : Inherits ImageRenderer Friend Const ImagePostfix_Small As String = "_Small" Private _LargeAddress As SFile Private _SmallAddress As SFile + Private _ForceSaveOrig As Boolean = False + Friend Shared Function NewUserPicture(ByVal ImageOrig As SFile, ByVal Destination As SFile, + Optional ByVal Save As Boolean = True, Optional ByVal GetInstance As Boolean = False) As UserImage + Dim uImg As New UserImage(ImageOrig, Destination) + With uImg + ._ForceSaveOrig = ImageOrig.Extension.IsEmptyString OrElse ImageOrig.Extension.ToLower = "gif" OrElse Not {"jpg", "jpeg", "png"}.Contains(ImageOrig.Extension.ToLower) + If Not ._ForceSaveOrig Then + If .Address.Exists AndAlso Not .Address.Delete(SFO.File,, EDP.ReturnValue) Then ._ForceSaveOrig = True + If Not ._ForceSaveOrig AndAlso Not ImageOrig.Copy(.Address) Then ._ForceSaveOrig = True + End If + If Not ._ForceSaveOrig Then + ._SmallAddress.Extension = .Address.Extension + ._LargeAddress.Extension = .Address.Extension + End If + If Save Then .Save() + End With + If Not GetInstance Then uImg.Dispose() : uImg = Nothing + Return uImg + End Function Friend Sub New(ByVal _ImgOriginal As SFile, ByVal Destination As SFile, Optional ByVal GenerateLargeSmallPictures As Boolean = True) MyBase.New(_ImgOriginal) Dim f As SFile = Destination @@ -71,7 +90,7 @@ Friend Class UserImage : Inherits ImageRenderer End With End Function Public Overrides Sub Save() - MyBase.Save() + If _ForceSaveOrig Then MyBase.Save() Small.Save(_SmallAddress) Large.Save(_LargeAddress) End Sub diff --git a/Tools/ArchiveSCrawlerUsersDataFiles.bat b/Tools/ArchiveSCrawlerUsersDataFiles.bat index 6e7d187..2b7f23d 100644 --- a/Tools/ArchiveSCrawlerUsersDataFiles.bat +++ b/Tools/ArchiveSCrawlerUsersDataFiles.bat @@ -4,4 +4,8 @@ REM Replace 'd:\Downloads\SocialNetworks\' with the path to your SCrawler data f REM THIS SCRIPT IS NOT SUITABLE FOR 7ZIP OR OTHER ARCHIVING PROGRAMS. REM But I believe 7Zip also has CLI commands -"C:\Program Files\WinRAR\WinRAR.exe" a -r -ep1 -o+ -ag_YYYYMMDD_HHMMSS -m5 -tl -n*.txt -n*.xml "d:\Downloads\SocialNetworks\SCrawlerBackup.rar" "d:\Downloads\SocialNetworks\" \ No newline at end of file +REM This line archives SCrawler settings files. +"C:\Program Files\WinRAR\WinRAR.exe" a -r -ep1 -o+ -ag_YYYYMMDD_HHMMSS -m5 -tl "D:\MyPrograms\SCrawler\Backup\Settings.rar" "D:\MyPrograms\SCrawler\Settings\" + +REM This line archives SCrawler users' settings files. +"C:\Program Files\WinRAR\WinRAR.exe" a -r -ep1 -o+ -ag_YYYYMMDD_HHMMSS -m5 -tl -n*.txt -n*.xml "D:\MyPrograms\SCrawler\Backup\SCrawlerBackup.rar" "D:\MyPrograms\SCrawler\Data\" \ No newline at end of file