mirror of
https://github.com/AAndyProgram/SCrawler.git
synced 2026-03-15 00:02:17 +00:00
2023.4.28.0
Plugins IPluginContentProvider: added DownloadSingleObject function; added tokens to GetMedia and Download functions; removed GetSpecialData function Add IDownloadableMedia interface Removed 'Channel' option from all functions and enums ISiteSettings: added GetSingleMediaInstance function ExchangeOptions: removed 'IsChannel' UserMediaTypes: added Audio and AudioPre enums IUserMedia, PluginUserMedia: changed ContentType and DownloadState from integers to their enums SCrawler Add YouTube standalone downloader Add gallery-dl & yt-dlp support Remove 'UserInfo' requirement from 'ProfilesSaved' Update 'SiteSettingsBase' to use domains and Netscape cookies UserDataBase: remove channels; remove old 'Merge' const; standardize SavedPosts file naming; move 'ValidateMD5' function from Twitter to UserDataBase to use it in other UserData classes; add 'DownloadSingleObject' environment for single posts; add validating file extension for m3u8 during download; add reindex of video file during download Rewritten DomainsContainer Create a universal settings form and PSettingsArttribute Gfycat, Imgur: turn these classes into IUserData to download a single object All plugins: update 'GetInstance' function for saved posts; update domains where implemented; remove 'OptionForm' where it exists; update options where they exist; update unix date providers; reconfigure channels where they exist LPSG: fix attachments; update converters and regex Add sites: ThisVid, Mastodon, Pinterest, YouTube, YouTube music Reddit: standardize container parsing for all data types; new channel environment; fix 'ReparseMissing' function; redirect data downloading to the base download function, saved crossposts support Twitter: fixed gif path bug; fixed downloading saved posts PornHub: hide unnecessary errors; photo galleries bug RedGifs: add 'UserAgent' option Added icons to download progress Rename some objects Completely redesigned standalone downloader form and rewritten its environment WebClient2: update to use tokens Labels: update label form (save labels to file only when OK button is clicked); change removing labels.txt from recycle bin to permanent; disable storing label 'NoParsedUser' UserCreatorForm: remove the 'Channel' checkbox and related functions; ability to extract the user's URL from the buffer and apply parameters if found Remove temporary 'EncryptCookies' module MainFrame: added simplified way to create new users (Ctrl+Insert to create a new user with default parameters from clipboard URL); removed SCrawler command line argument "-v" (remove the ability to run SCrawler as video downloader) PropertyValueHost: update for option forms compatibility SettingsHost: removed 'GetSpecialData' fork; added 'GetSingleMediaInstance' fork UserDataHost: update functions with tokens; update events; add 'DownloadSingleObject' function Settings: add the ability to get environment from 4 destinations; add the ability to set the program environment manually; add CMDEncoding; add cache; remove the old function 'RemoveUnusedPlugins'; add 'STDownloader' properties; add YT compatibility; add new notification options; add deleting user settings file when 'SettingsCLS.Dispose()' if where are no users in SCrawler UserFinder: remove old 'Merge' const; remove channel option UserInfo: remove channel option
This commit is contained in:
@@ -8,7 +8,11 @@
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Base
|
||||
Friend Module Declarations
|
||||
Friend Const UserLabelName As String = "User"
|
||||
Friend ReadOnly LNC As New ListAddParams(LAP.NotContainsOnly)
|
||||
Friend ReadOnly UnixDate32Provider As New ADateTime(ADateTime.Formats.Unix32)
|
||||
Friend ReadOnly UnixDate64Provider As New ADateTime(ADateTime.Formats.Unix64)
|
||||
Friend ReadOnly HtmlConverter As Func(Of String, String) = Function(Input) SymbolsConverter.HTML.Decode(Input, EDP.ReturnValue)
|
||||
Friend ReadOnly TitleHtmlConverter As Func(Of String, String) =
|
||||
Function(Input) SymbolsConverter.HTML.Decode(SymbolsConverter.Convert(Input, EDP.ReturnValue), EDP.ReturnValue).
|
||||
StringRemoveWinForbiddenSymbols().StringTrim()
|
||||
|
||||
@@ -60,7 +60,7 @@ Namespace API.Base
|
||||
End Using
|
||||
Return l2
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"[DownDetector.GetData({Site})]")
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[DownDetector.GetData({Site})]")
|
||||
End Try
|
||||
End Function
|
||||
End Class
|
||||
|
||||
86
SCrawler/API/Base/GDLBatch.vb
Normal file
86
SCrawler/API/Base/GDLBatch.vb
Normal file
@@ -0,0 +1,86 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Base.GDL
|
||||
Friend Module Declarations
|
||||
Private Structure GDLURL : Implements IRegExCreator
|
||||
Private _URL As String
|
||||
Friend ReadOnly Property URL As String
|
||||
Get
|
||||
Return _URL
|
||||
End Get
|
||||
End Property
|
||||
Public Shared Widening Operator CType(ByVal u As String) As GDLURL
|
||||
Return New GDLURL With {._URL = u}
|
||||
End Operator
|
||||
Public Shared Widening Operator CType(ByVal u As GDLURL) As String
|
||||
Return u.URL
|
||||
End Operator
|
||||
Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
|
||||
If ParamsArray.ListExists(2) Then
|
||||
Dim u$ = ParamsArray(0).StringTrim.StringTrimEnd("/"), u2$
|
||||
If Not u.IsEmptyString Then
|
||||
u2 = ParamsArray(1).StringTrim
|
||||
If Not u2.IsEmptyString AndAlso u2.StartsWith("GET", StringComparison.OrdinalIgnoreCase) Then
|
||||
u2 = u2.Remove(0, 3).StringTrim.StringTrimStart("/")
|
||||
If Not u2.IsEmptyString Then _URL = $"{u}/{u2}"
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Return Me
|
||||
End Function
|
||||
Public Shared Operator =(ByVal x As GDLURL, ByVal y As GDLURL) As Boolean
|
||||
Return x.URL = y.URL
|
||||
End Operator
|
||||
Public Shared Operator <>(ByVal x As GDLURL, ByVal y As GDLURL) As Boolean
|
||||
Return Not x.URL = y.URL
|
||||
End Operator
|
||||
Public Overrides Function ToString() As String
|
||||
Return URL
|
||||
End Function
|
||||
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
|
||||
Return URL = CType(Obj, String)
|
||||
End Function
|
||||
End Structure
|
||||
Private ReadOnly Property GdlUrlPattern As RParams = RParams.DM(GDLBatch.UrlLibStart.Replace("[", "\[").Replace("]", "\]") &
|
||||
"([^""]+?)""(GET [^""]+)""", 0, EDP.ReturnValue)
|
||||
Friend Function GetUrlsFromGalleryDl(ByVal Batch As BatchExecutor, ByVal Command As String) As List(Of String)
|
||||
Dim urls As New List(Of String)
|
||||
Dim u As GDLURL
|
||||
With Batch
|
||||
.Execute(Command)
|
||||
If .ErrorOutputData.Count > 0 Then
|
||||
For Each eValue$ In .ErrorOutputData
|
||||
u = RegexFields(Of GDLURL)(eValue, {GdlUrlPattern}, {1, 2}, EDP.ReturnValue).ListIfNothing.FirstOrDefault
|
||||
If Not u.URL.IsEmptyString Then urls.ListAddValue(u, LNC)
|
||||
Next
|
||||
End If
|
||||
End With
|
||||
Return urls
|
||||
End Function
|
||||
End Module
|
||||
Friend Class GDLBatch : Inherits BatchExecutor
|
||||
Friend Property TempPostsList As List(Of String)
|
||||
Friend Const UrlLibStart As String = "[urllib3.connectionpool][debug]"
|
||||
Friend Const UrlTextStart As String = UrlLibStart & " https"
|
||||
Friend Sub New()
|
||||
MyBase.New(True)
|
||||
ChangeDirectory(Settings.GalleryDLFile.File)
|
||||
End Sub
|
||||
Protected Overrides Async Sub OutputDataReceiver(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
|
||||
MyBase.OutputDataReceiver(Sender, e)
|
||||
Await Validate(e.Data)
|
||||
End Sub
|
||||
Protected Overridable Async Function Validate(ByVal Value As String) As Task
|
||||
If Await Task.Run(Of Boolean)(Function() Not Value.IsEmptyString AndAlso
|
||||
TempPostsList.Exists(Function(v) Value.Contains(v))) Then Kill(EDP.None)
|
||||
End Function
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -6,9 +6,12 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports System.Threading
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Tools.Web
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Base
|
||||
Namespace M3U8Declarations
|
||||
Friend Module M3U8Defaults
|
||||
@@ -16,6 +19,7 @@ Namespace API.Base
|
||||
End Module
|
||||
End Namespace
|
||||
Friend NotInheritable Class M3U8Base
|
||||
Friend Const TempCacheFolderName As String = "tmpCache"
|
||||
Private Sub New()
|
||||
End Sub
|
||||
Friend Shared Function CreateUrl(ByVal Appender As String, ByVal File As String) As String
|
||||
@@ -28,36 +32,40 @@ Namespace API.Base
|
||||
Return $"{Appender.StringTrimEnd("/")}/{File}"
|
||||
End If
|
||||
End Function
|
||||
Friend Shared Function Download(ByVal URLs As List(Of String), ByVal DestinationFile As SFile, Optional ByVal Responser As Responser = Nothing) As SFile
|
||||
Dim CachePath As SFile = Nothing
|
||||
Friend Shared Function Download(ByVal URLs As List(Of String), ByVal DestinationFile As SFile, Optional ByVal Responser As Responser = Nothing,
|
||||
Optional ByVal Token As CancellationToken = Nothing, Optional ByVal Progress As MyProgress = Nothing) As SFile
|
||||
Dim Cache As CacheKeeper = Nothing
|
||||
Try
|
||||
If URLs.ListExists Then
|
||||
Dim ConcatFile As SFile = DestinationFile
|
||||
If ConcatFile.Name.IsEmptyString Then ConcatFile.Name = "PlayListFile"
|
||||
ConcatFile.Extension = "mp4"
|
||||
CachePath = $"{DestinationFile.PathWithSeparator}_Cache\{SFile.GetDirectories($"{DestinationFile.PathWithSeparator}_Cache\",,, EDP.ReturnValue).ListIfNothing.Count + 1}\"
|
||||
If CachePath.Exists(SFO.Path) Then
|
||||
Dim p As New SFileNumbers(ConcatFile.Name,,, New ANumbers With {.Format = ANumbers.Formats.General})
|
||||
ConcatFile = SFile.Indexed_IndexFile(ConcatFile,, p, EDP.ReturnValue)
|
||||
Cache = New CacheKeeper($"{DestinationFile.PathWithSeparator}_{TempCacheFolderName}\")
|
||||
Dim cache2 As CacheKeeper = Cache.NewInstance
|
||||
If cache2.RootDirectory.Exists(SFO.Path) Then
|
||||
Dim progressExists As Boolean = Not Progress Is Nothing
|
||||
If progressExists Then Progress.Maximum += URLs.Count
|
||||
Dim p As SFileNumbers = SFileNumbers.Default(ConcatFile.Name)
|
||||
ConcatFile = SFile.IndexReindex(ConcatFile,,, p, EDP.ReturnValue)
|
||||
Dim i%
|
||||
Dim eFiles As New List(Of SFile)
|
||||
Dim dFile As SFile = CachePath
|
||||
Dim dFile As SFile = cache2.RootDirectory
|
||||
dFile.Extension = "ts"
|
||||
Using w As New DownloadObjects.WebClient2(Responser)
|
||||
For i = 0 To URLs.Count - 1
|
||||
If progressExists Then Progress.Perform()
|
||||
Token.ThrowIfCancellationRequested()
|
||||
dFile.Name = $"ConPart_{i}"
|
||||
w.DownloadFile(URLs(i), dFile)
|
||||
eFiles.Add(dFile)
|
||||
cache2.AddFile(dFile, True)
|
||||
Next
|
||||
End Using
|
||||
DestinationFile = FFMPEG.ConcatenateFiles(eFiles, Settings.FfmpegFile, ConcatFile, p, EDP.ThrowException)
|
||||
eFiles.Clear()
|
||||
DestinationFile = FFMPEG.ConcatenateFiles(cache2, Settings.FfmpegFile.File, ConcatFile, Settings.CMDEncoding, p, EDP.ThrowException)
|
||||
Return DestinationFile
|
||||
End If
|
||||
End If
|
||||
Return Nothing
|
||||
Finally
|
||||
CachePath.Delete(SFO.Path, SFODelete.None, EDP.None)
|
||||
Cache.DisposeIfReady
|
||||
End Try
|
||||
End Function
|
||||
End Class
|
||||
|
||||
@@ -18,20 +18,17 @@ Namespace API.Base
|
||||
HOST = h
|
||||
Progress = Bar
|
||||
End Sub
|
||||
Friend Sub Download(ByVal Token As CancellationToken)
|
||||
Friend Sub Download(ByVal Token As CancellationToken, ByVal Multiple As Boolean)
|
||||
Try
|
||||
If HOST.Source.ReadyToDownload(PDownload.SavedPosts) Then
|
||||
If HOST.Available(PDownload.SavedPosts, False) Then
|
||||
If HOST.Available(PDownload.SavedPosts, Multiple) Then
|
||||
HOST.DownloadStarted(PDownload.SavedPosts)
|
||||
Dim u As New UserInfo With {.Plugin = HOST.Key, .Site = HOST.Name, .SpecialPath = HOST.SavedPostsPath}
|
||||
Using user As IUserData = HOST.GetInstance(PDownload.SavedPosts, Nothing, False, False)
|
||||
If Not user Is Nothing AndAlso Not user.Name.IsEmptyString Then
|
||||
u.Name = user.Name
|
||||
If Not user Is Nothing Then
|
||||
With DirectCast(user, UserDataBase)
|
||||
With .User : u.IsChannel = .IsChannel : u.UpdateUserFile() : End With
|
||||
.User = u
|
||||
.LoadUserInformation()
|
||||
.IsSavedPosts = True
|
||||
.LoadUserInformation()
|
||||
.Progress = Progress
|
||||
If Not .FileExists Then .UpdateUserInformation()
|
||||
End With
|
||||
@@ -49,7 +46,7 @@ Namespace API.Base
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Progress.InformationTemporary = $"{HOST.Name} downloading error"
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.Base.ProfileSaved.Download({HOST.Key})]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[API.Base.ProfileSaved.Download({HOST.Key})]")
|
||||
Finally
|
||||
HOST.DownloadDone(PDownload.SavedPosts)
|
||||
MainFrameObj.UpdateLogButton()
|
||||
|
||||
@@ -6,10 +6,9 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Cookies
|
||||
Imports SCrawler.Plugin
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports Download = SCrawler.Plugin.ISiteSettings.Download
|
||||
Namespace API.Base
|
||||
Friend MustInherit Class SiteSettingsBase : Implements ISiteSettings, IResponserContainer
|
||||
@@ -18,6 +17,23 @@ Namespace API.Base
|
||||
Friend Overridable ReadOnly Property Image As Image Implements ISiteSettings.Image
|
||||
Private Property Logger As ILogProvider = LogConnector Implements ISiteSettings.Logger
|
||||
Friend Overridable ReadOnly Property Responser As Responser
|
||||
Friend ReadOnly Property CookiesNetscapeFile As SFile
|
||||
Protected CheckNetscapeCookiesOnEndInit As Boolean = False
|
||||
Private _UseNetscapeCookies As Boolean = False
|
||||
Protected Property UseNetscapeCookies As Boolean
|
||||
Get
|
||||
Return _UseNetscapeCookies
|
||||
End Get
|
||||
Set(ByVal use As Boolean)
|
||||
Dim b As Boolean = Not _UseNetscapeCookies = use
|
||||
_UseNetscapeCookies = use
|
||||
If Not Responser Is Nothing Then
|
||||
Responser.Cookies.ChangedAllowInternalDrop = Not _UseNetscapeCookies
|
||||
Responser.Cookies.Changed = False
|
||||
End If
|
||||
If b And _UseNetscapeCookies Then Update_SaveCookiesNetscape()
|
||||
End Set
|
||||
End Property
|
||||
Private Property IResponserContainer_Responser As Responser Implements IResponserContainer.Responser
|
||||
Get
|
||||
Return Responser
|
||||
@@ -27,20 +43,15 @@ Namespace API.Base
|
||||
Friend MustOverride Function GetInstance(ByVal What As Download) As IPluginContentProvider Implements ISiteSettings.GetInstance
|
||||
Friend Sub New(ByVal SiteName As String)
|
||||
Site = SiteName
|
||||
CookiesNetscapeFile = $"{SettingsFolderName}\Responser_{Site}_Cookies_Netscape.txt"
|
||||
End Sub
|
||||
Friend Sub New(ByVal SiteName As String, ByVal CookiesDomain As String)
|
||||
Site = SiteName
|
||||
Responser = New Responser($"{SettingsFolderName}\Responser_{Site}.xml")
|
||||
Me.New(SiteName)
|
||||
Responser = New Responser($"{SettingsFolderName}\Responser_{Site}.xml") With {.DeclaredError = EDP.ThrowException}
|
||||
With Responser
|
||||
If .File.Exists Then
|
||||
If EncryptCookies.CookiesEncrypted Then .CookiesEncryptKey = SettingsCLS.CookieEncryptKey
|
||||
.LoadSettings()
|
||||
Else
|
||||
.CookiesDomain = CookiesDomain
|
||||
.CookiesEncryptKey = SettingsCLS.CookieEncryptKey
|
||||
.SaveSettings()
|
||||
End If
|
||||
If .CookiesDomain.IsEmptyString Then .CookiesDomain = CookiesDomain
|
||||
.CookiesDomain = CookiesDomain
|
||||
.CookiesEncryptKey = SettingsCLS.CookieEncryptKey
|
||||
If .File.Exists Then .LoadSettings() Else .SaveSettings()
|
||||
End With
|
||||
End Sub
|
||||
#Region "XML"
|
||||
@@ -51,17 +62,47 @@ Namespace API.Base
|
||||
Friend Overridable Sub BeginInit() Implements ISiteSettings.BeginInit
|
||||
End Sub
|
||||
Friend Overridable Sub EndInit() Implements ISiteSettings.EndInit
|
||||
EncryptCookies.ValidateCookiesEncrypt(Responser)
|
||||
If Not DefaultUserAgent.IsEmptyString And Not Responser Is Nothing Then Responser.UserAgent = DefaultUserAgent
|
||||
If CheckNetscapeCookiesOnEndInit Then Update_SaveCookiesNetscape(, True)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Update, Edit"
|
||||
Friend Overridable Sub BeginUpdate() Implements ISiteSettings.BeginUpdate
|
||||
End Sub
|
||||
Friend Overridable Sub EndUpdate() Implements ISiteSettings.EndUpdate
|
||||
End Sub
|
||||
Protected _SiteEditorFormOpened As Boolean = False
|
||||
Friend Overridable Sub BeginEdit() Implements ISiteSettings.BeginEdit
|
||||
_SiteEditorFormOpened = True
|
||||
End Sub
|
||||
Friend Overridable Sub EndEdit() Implements ISiteSettings.EndEdit
|
||||
If _SiteEditorFormOpened Then DomainsReset()
|
||||
_SiteEditorFormOpened = False
|
||||
End Sub
|
||||
Friend Overridable Sub Update() Implements ISiteSettings.Update
|
||||
If _SiteEditorFormOpened Then
|
||||
If UseNetscapeCookies Then Update_SaveCookiesNetscape()
|
||||
DomainsApply()
|
||||
End If
|
||||
If Not Responser Is Nothing Then Responser.SaveSettings()
|
||||
End Sub
|
||||
Protected Sub Update_SaveCookiesNetscape(Optional ByVal Force As Boolean = False, Optional ByVal IsInit As Boolean = False)
|
||||
If Not Responser Is Nothing Then
|
||||
With Responser
|
||||
If .Cookies.Changed Or Force Or IsInit Then
|
||||
If IsInit And CookiesNetscapeFile.Exists Then Exit Sub
|
||||
If .CookiesExists Then .Cookies.SaveNetscapeFile(CookiesNetscapeFile) Else CookiesNetscapeFile.Delete()
|
||||
.Cookies.Changed = False
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
End Sub
|
||||
#Region "Specialized"
|
||||
Protected Overridable Sub DomainsApply()
|
||||
End Sub
|
||||
Protected Overridable Sub DomainsReset()
|
||||
End Sub
|
||||
#End Region
|
||||
#End Region
|
||||
#Region "Before and After Download"
|
||||
Friend Overridable Sub DownloadStarted(ByVal What As Download) Implements ISiteSettings.DownloadStarted
|
||||
@@ -75,20 +116,15 @@ Namespace API.Base
|
||||
#End Region
|
||||
#Region "User info"
|
||||
Protected UrlPatternUser As String = String.Empty
|
||||
Protected UrlPatternChannel As String = String.Empty
|
||||
Friend Overridable Function GetUserUrl(ByVal User As IPluginContentProvider, ByVal Channel As Boolean) As String Implements ISiteSettings.GetUserUrl
|
||||
If Channel Then
|
||||
If Not UrlPatternChannel.IsEmptyString Then Return String.Format(UrlPatternChannel, User.Name)
|
||||
Else
|
||||
If Not UrlPatternUser.IsEmptyString Then Return String.Format(UrlPatternUser, User.Name)
|
||||
End If
|
||||
Friend Overridable Function GetUserUrl(ByVal User As IPluginContentProvider) As String Implements ISiteSettings.GetUserUrl
|
||||
If Not UrlPatternUser.IsEmptyString Then Return String.Format(UrlPatternUser, User.Name)
|
||||
Return String.Empty
|
||||
End Function
|
||||
Private Function ISiteSettings_GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String Implements ISiteSettings.GetUserPostUrl
|
||||
Return GetUserPostUrl(User, Media)
|
||||
End Function
|
||||
Friend Overridable Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
Return String.Empty
|
||||
Return Media.URL_BASE.IfNullOrEmpty(Media.URL)
|
||||
End Function
|
||||
Protected UserRegex As RParams = Nothing
|
||||
Friend Overridable Function IsMyUser(ByVal UserURL As String) As ExchangeOptions Implements ISiteSettings.IsMyUser
|
||||
@@ -99,43 +135,40 @@ Namespace API.Base
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"[API.Base.SiteSettingsBase.IsMyUser({UserURL})]", New ExchangeOptions)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[API.Base.SiteSettingsBase.IsMyUser({UserURL})]", New ExchangeOptions)
|
||||
End Try
|
||||
End Function
|
||||
Protected ImageVideoContains As String = String.Empty
|
||||
Friend Overridable Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions Implements ISiteSettings.IsMyImageVideo
|
||||
If Not ImageVideoContains.IsEmptyString AndAlso URL.Contains(ImageVideoContains) Then
|
||||
Return New ExchangeOptions With {.Exists = True}
|
||||
Return New ExchangeOptions(Site, String.Empty) With {.Exists = True}
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
Friend Overridable Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable Implements ISiteSettings.GetSpecialData
|
||||
Return Nothing
|
||||
Private Function ISiteSettings_GetSingleMediaInstance(ByVal URL As String, ByVal OutputFile As String) As IDownloadableMedia Implements ISiteSettings.GetSingleMediaInstance
|
||||
Return GetSingleMediaInstance(URL, OutputFile)
|
||||
End Function
|
||||
Friend Shared Function GetSpecialDataFile(ByVal Path As String, ByVal AskForPath As Boolean, ByRef SpecFolderObj As String) As SFile
|
||||
Dim f As SFile = Path.CSFileP
|
||||
If f.Name.IsEmptyString Then f.Name = "OutputFile"
|
||||
#Disable Warning BC40000
|
||||
If Path.CSFileP.IsEmptyString Or AskForPath Then f = SFile.SaveAs(f, "File destination",,,, EDP.ReturnValue) : SpecFolderObj = f.Path
|
||||
#Enable Warning
|
||||
Return f
|
||||
Friend Overridable Function GetSingleMediaInstance(ByVal URL As String, ByVal OutputFile As SFile) As IDownloadableMedia
|
||||
Return New Hosts.DownloadableMediaHost(URL, OutputFile)
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Ready, Available"
|
||||
''' <returns>True</returns>
|
||||
Friend Overridable Function BaseAuthExists() As Boolean
|
||||
Return True
|
||||
End Function
|
||||
''' <summary>JOB: leave or remove</summary>
|
||||
''' <returns>Return BaseAuthExists()</returns>
|
||||
Friend Overridable Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available
|
||||
Return BaseAuthExists()
|
||||
End Function
|
||||
''' <summary>'DownloadData': before processing</summary>
|
||||
''' <returns>True</returns>
|
||||
Friend Overridable Function ReadyToDownload(ByVal What As Download) As Boolean Implements ISiteSettings.ReadyToDownload
|
||||
Return True
|
||||
End Function
|
||||
#End Region
|
||||
Friend Overridable Sub Update() Implements ISiteSettings.Update
|
||||
If Not Responser Is Nothing Then Responser.SaveSettings()
|
||||
End Sub
|
||||
Friend Overridable Sub Reset() Implements ISiteSettings.Reset
|
||||
End Sub
|
||||
Friend Overridable Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) Implements ISiteSettings.UserOptions
|
||||
|
||||
@@ -27,10 +27,12 @@ Namespace API.Base
|
||||
#End Region
|
||||
Friend Enum Types As Integer
|
||||
Undefined = 0
|
||||
[Picture] = 1
|
||||
[Video] = 2
|
||||
[Text] = 3
|
||||
Picture = 1
|
||||
Video = 2
|
||||
Audio = 200
|
||||
Text = 4
|
||||
VideoPre = 10
|
||||
AudioPre = 215
|
||||
GIF = 50
|
||||
m3u8 = 100
|
||||
End Enum
|
||||
@@ -51,12 +53,12 @@ Namespace API.Base
|
||||
Friend SpecialFolder As String
|
||||
Friend [Object] As Object
|
||||
#Region "Interface Support"
|
||||
Private Property IUserMedia_Type As Integer Implements IUserMedia.ContentType
|
||||
Private Property IUserMedia_Type As UserMediaTypes Implements IUserMedia.ContentType
|
||||
Get
|
||||
Return Type
|
||||
Return CInt(Type)
|
||||
End Get
|
||||
Set(ByVal Type As Integer)
|
||||
Me.Type = Type
|
||||
Set(ByVal Type As UserMediaTypes)
|
||||
Me.Type = CInt(Type)
|
||||
End Set
|
||||
End Property
|
||||
Private Property IUserMedia_URL_BASE As String Implements IUserMedia.URL_BASE
|
||||
@@ -91,12 +93,12 @@ Namespace API.Base
|
||||
Me.File = File
|
||||
End Set
|
||||
End Property
|
||||
Private Property IUserMedia_State As Integer Implements IUserMedia.DownloadState
|
||||
Private Property IUserMedia_State As UserMediaStates Implements IUserMedia.DownloadState
|
||||
Get
|
||||
Return State
|
||||
Return CInt(State)
|
||||
End Get
|
||||
Set(ByVal State As Integer)
|
||||
Me.State = State
|
||||
Set(ByVal State As UserMediaStates)
|
||||
Me.State = CInt(State)
|
||||
End Set
|
||||
End Property
|
||||
Private Property IUserMedia_PostID As String Implements IUserMedia.PostID
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
Imports System.IO
|
||||
Imports System.Net
|
||||
Imports System.Threading
|
||||
Imports System.ComponentModel
|
||||
Imports System.Runtime.CompilerServices
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Hosts
|
||||
@@ -18,6 +19,7 @@ Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.ImageRenderer
|
||||
Imports UStates = SCrawler.API.Base.UserMedia.States
|
||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||
Namespace API.Base
|
||||
@@ -98,7 +100,7 @@ Namespace API.Base
|
||||
#Region "XML Declarations"
|
||||
Private Const Name_Site As String = UserInfo.Name_Site
|
||||
Private Const Name_Plugin As String = UserInfo.Name_Plugin
|
||||
Private Const Name_IsChannel As String = UserInfo.Name_IsChannel
|
||||
Protected Const Name_IsChannel As String = "IsChannel"
|
||||
Friend Const Name_UserName As String = "UserName"
|
||||
Private Const Name_Model_User As String = UserInfo.Name_Model_User
|
||||
Private Const Name_Model_Collection As String = UserInfo.Name_Model_Collection
|
||||
@@ -108,9 +110,9 @@ Namespace API.Base
|
||||
|
||||
Private Const Name_UserExists As String = "UserExists"
|
||||
Private Const Name_UserSuspended As String = "UserSuspended"
|
||||
Private Const Name_FriendlyName As String = "FriendlyName"
|
||||
Protected Const Name_FriendlyName As String = "FriendlyName"
|
||||
Private Const Name_UserSiteName As String = "UserSiteName"
|
||||
Private Const Name_UserID As String = "UserID"
|
||||
Protected Const Name_UserID As String = "UserID"
|
||||
Private Const Name_Description As String = "Description"
|
||||
Private Const Name_ParseUserMediaOnly As String = "ParseUserMediaOnly"
|
||||
Private Const Name_Temporary As String = "Temporary"
|
||||
@@ -132,10 +134,12 @@ Namespace API.Base
|
||||
Private Const Name_ScriptUse As String = "ScriptUse"
|
||||
Private Const Name_ScriptData As String = "ScriptData"
|
||||
|
||||
<Obsolete("Use 'Name_Merged'", False)> Friend Const Name_DataMerging As String = "DataMerging"
|
||||
Protected Const Name_UseMD5Comparison As String = "UseMD5Comparison"
|
||||
Protected Const Name_RemoveExistingDuplicates As String = "RemoveExistingDuplicates"
|
||||
Protected Const Name_StartMD5Checked As String = "StartMD5Checked"
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
#Region "Host, Site, Progress, Self"
|
||||
#Region "Host, Site, Progress"
|
||||
Friend Property HOST As SettingsHost Implements IUserData.HOST
|
||||
Friend ReadOnly Property Site As String Implements IContentProvider.Site
|
||||
Get
|
||||
@@ -167,15 +171,17 @@ Namespace API.Base
|
||||
Me._UserSuspended = _UserSuspended
|
||||
End Set
|
||||
End Property
|
||||
Friend Overridable Property Name As String Implements IContentProvider.Name, IPluginContentProvider.Name
|
||||
Private Property IPluginContentProvider_Name As String Implements IPluginContentProvider.Name
|
||||
Get
|
||||
Return Name
|
||||
End Get
|
||||
Set(ByVal NewName As String)
|
||||
End Set
|
||||
End Property
|
||||
Friend Overridable ReadOnly Property Name As String Implements IContentProvider.Name
|
||||
Get
|
||||
Return User.Name
|
||||
End Get
|
||||
Set(ByVal NewName As String)
|
||||
User.Name = NewName
|
||||
User.UpdateUserFile()
|
||||
Settings.UpdateUsersList(User)
|
||||
End Set
|
||||
End Property
|
||||
Friend Overridable Property ID As String = String.Empty Implements IContentProvider.ID, IPluginContentProvider.ID
|
||||
Protected _FriendlyName As String = String.Empty
|
||||
@@ -275,11 +281,6 @@ Namespace API.Base
|
||||
End Property
|
||||
#End Region
|
||||
#Region "Channel"
|
||||
Friend Overridable ReadOnly Property IsChannel As Boolean Implements IUserData.IsChannel
|
||||
Get
|
||||
Return User.IsChannel
|
||||
End Get
|
||||
End Property
|
||||
Friend Property CreatedByChannel As Boolean = False
|
||||
#End Region
|
||||
#Region "Images"
|
||||
@@ -564,7 +565,7 @@ BlockNullPicture:
|
||||
#End Region
|
||||
#Region "Plugins Support"
|
||||
Protected Event ProgressChanged As IPluginContentProvider.ProgressChangedEventHandler Implements IPluginContentProvider.ProgressChanged
|
||||
Protected Event TotalCountChanged As IPluginContentProvider.TotalCountChangedEventHandler Implements IPluginContentProvider.TotalCountChanged
|
||||
Protected Event ProgressMaximumChanged As IPluginContentProvider.ProgressMaximumChangedEventHandler Implements IPluginContentProvider.ProgressMaximumChanged
|
||||
Private Property IPluginContentProvider_Settings As ISiteSettings Implements IPluginContentProvider.Settings
|
||||
Get
|
||||
Return HOST.Source
|
||||
@@ -585,9 +586,11 @@ BlockNullPicture:
|
||||
Private Function IPluginContentProvider_XmlFieldsGet() As List(Of KeyValuePair(Of String, String)) Implements IPluginContentProvider.XmlFieldsGet
|
||||
Return Nothing
|
||||
End Function
|
||||
Private Sub IPluginContentProvider_GetMedia() Implements IPluginContentProvider.GetMedia
|
||||
Private Sub IPluginContentProvider_GetMedia(ByVal Token As CancellationToken) Implements IPluginContentProvider.GetMedia
|
||||
End Sub
|
||||
Private Sub IPluginContentProvider_Download() Implements IPluginContentProvider.Download
|
||||
Private Sub IPluginContentProvider_Download(ByVal Token As CancellationToken) Implements IPluginContentProvider.Download
|
||||
End Sub
|
||||
Private Sub IPluginContentProvider_DownloadSingleObject(ByVal Data As IDownloadableMedia, ByVal Token As CancellationToken) Implements IPluginContentProvider.DownloadSingleObject
|
||||
End Sub
|
||||
Friend Overridable Function ExchangeOptionsGet() As Object Implements IPluginContentProvider.ExchangeOptionsGet
|
||||
Return Nothing
|
||||
@@ -598,8 +601,8 @@ BlockNullPicture:
|
||||
#End Region
|
||||
#Region "IIndexable Support"
|
||||
Friend Property Index As Integer = 0 Implements IIndexable.Index
|
||||
Private Function SetIndex(ByVal Obj As Object, ByVal _Index As Integer) As Object Implements IIndexable.SetIndex
|
||||
DirectCast(Obj, UserDataBase).Index = _Index
|
||||
Private Function SetIndex(ByVal Obj As Object, ByVal Index As Integer) As Object Implements IIndexable.SetIndex
|
||||
DirectCast(Obj, UserDataBase).Index = Index
|
||||
Return Obj
|
||||
End Function
|
||||
#End Region
|
||||
@@ -607,7 +610,7 @@ BlockNullPicture:
|
||||
Friend ReadOnly Property LVIKey As String Implements IUserData.Key
|
||||
Get
|
||||
If Not _IsCollection Then
|
||||
Return $"{IIf(IsChannel, "C", String.Empty)}{Site.ToString.ToUpper}_{Name}"
|
||||
Return $"{Site.ToString.ToUpper}_{Name}"
|
||||
Else
|
||||
Return $"CCCC_{CollectionName}"
|
||||
End If
|
||||
@@ -658,7 +661,7 @@ BlockNullPicture:
|
||||
Next
|
||||
End If
|
||||
ElseIf Settings.ShowGroups Then
|
||||
Return Destination.Groups.Item(GetLviGroupName(HOST, Temporary, Favorite, IsCollection, IsChannel))
|
||||
Return Destination.Groups.Item(GetLviGroupName(HOST, Temporary, Favorite, IsCollection))
|
||||
End If
|
||||
Return Destination.Groups.Item(LabelsKeeper.NoLabeledName)
|
||||
Catch ex As Exception
|
||||
@@ -689,7 +692,7 @@ BlockNullPicture:
|
||||
''' <exception cref="ArgumentOutOfRangeException"></exception>
|
||||
Friend Shared Function GetInstance(ByVal u As UserInfo, Optional ByVal _LoadUserInformation As Boolean = True) As IUserData
|
||||
If Not u.Plugin.IsEmptyString Then
|
||||
Return Settings(u.Plugin).GetInstance(u.DownloadOption, u, _LoadUserInformation)
|
||||
Return Settings(u.Plugin).GetInstance(ISiteSettings.Download.Main, u, _LoadUserInformation)
|
||||
Else
|
||||
Throw New ArgumentOutOfRangeException("Plugin", $"Plugin [{u.Plugin}] information does not recognized by loader")
|
||||
End If
|
||||
@@ -707,7 +710,7 @@ BlockNullPicture:
|
||||
End If
|
||||
Return String.Empty
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"GetPostUrl({uName}, {PostData.Post.ID})", String.Empty)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, $"GetPostUrl({uName}, {PostData.Post.ID})", String.Empty)
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
@@ -716,7 +719,7 @@ BlockNullPicture:
|
||||
Private _UserInformationLoaded As Boolean = False
|
||||
Friend Overridable Sub LoadUserInformation() Implements IUserData.LoadUserInformation
|
||||
Try
|
||||
UpdateDataFiles(, True)
|
||||
UpdateDataFiles()
|
||||
If MyFileSettings.Exists Then
|
||||
FileExists = True
|
||||
Using x As New XmlFile(MyFileSettings) With {.XmlReadOnly = True}
|
||||
@@ -740,14 +743,7 @@ BlockNullPicture:
|
||||
LastUpdated = AConvert(Of Date)(x.Value(Name_LastUpdated), ADateTime.Formats.BaseDateTime, Nothing)
|
||||
ScriptUse = x.Value(Name_ScriptUse).FromXML(Of Boolean)(False)
|
||||
ScriptData = x.Value(Name_ScriptData)
|
||||
'TODELETE: UserDataBase remove old 'merge' constant
|
||||
#Disable Warning BC40000
|
||||
If x.Contains(Name_DataMerging) Then
|
||||
DataMerging = x.Value(Name_DataMerging).FromXML(Of Boolean)(False)
|
||||
Else
|
||||
DataMerging = x.Value(Name_Merged).FromXML(Of Boolean)(False)
|
||||
End If
|
||||
#Enable Warning
|
||||
DataMerging = x.Value(Name_Merged).FromXML(Of Boolean)(False)
|
||||
ChangeCollectionName(x.Value(Name_CollectionName), False)
|
||||
Labels.ListAddList(x.Value(Name_LabelsName).StringToList(Of String, List(Of String))("|", EDP.ReturnValue), LAP.NotContainsOnly, LAP.ClearBeforeAdd)
|
||||
LoadUserInformation_OptionalFields(x, True)
|
||||
@@ -762,13 +758,12 @@ BlockNullPicture:
|
||||
End Sub
|
||||
Friend Overridable Sub UpdateUserInformation() Implements IUserData.UpdateUserInformation
|
||||
Try
|
||||
UpdateDataFiles(True)
|
||||
UpdateDataFiles()
|
||||
MyFileSettings.Exists(SFO.Path)
|
||||
Using x As New XmlFile With {.Name = "User"}
|
||||
x.Add(Name_Site, Site)
|
||||
x.Add(Name_Plugin, HOST.Key)
|
||||
x.Add(Name_UserName, User.Name)
|
||||
x.Add(Name_IsChannel, IsChannel.BoolToInteger)
|
||||
x.Add(Name_Model_User, CInt(UserModel))
|
||||
x.Add(Name_Model_Collection, CInt(CollectionModel))
|
||||
x.Add(Name_SpecialPath, User.SpecialPath)
|
||||
@@ -815,7 +810,7 @@ BlockNullPicture:
|
||||
#Region "User data"
|
||||
Friend Overridable Overloads Sub LoadContentInformation(Optional ByVal Force As Boolean = False)
|
||||
Try
|
||||
UpdateDataFiles(, True)
|
||||
UpdateDataFiles()
|
||||
If Not MyFileData.Exists Or (_DataLoaded And Not Force) Then Exit Sub
|
||||
Using x As New XmlFile(MyFileData, Protector.Modes.All, False) With {.XmlReadOnly = True, .AllowSameNames = True}
|
||||
x.LoadData()
|
||||
@@ -830,7 +825,7 @@ BlockNullPicture:
|
||||
End Sub
|
||||
Friend Sub UpdateContentInformation()
|
||||
Try
|
||||
UpdateDataFiles(True, True)
|
||||
UpdateDataFiles()
|
||||
If MyFileData.IsEmptyString Then Exit Sub
|
||||
MyFileData.Exists(SFO.Path)
|
||||
Using x As New XmlFile With {.AllowSameNames = True, .Name = "Data"}
|
||||
@@ -846,7 +841,7 @@ BlockNullPicture:
|
||||
#Region "Open site, folder"
|
||||
Friend Overridable Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing) Implements IContentProvider.OpenSite
|
||||
Try
|
||||
Dim URL$ = HOST.Source.GetUserUrl(Me, IsChannel)
|
||||
Dim URL$ = HOST.Source.GetUserUrl(Me)
|
||||
If Not URL.IsEmptyString Then Process.Start(URL)
|
||||
Catch ex As Exception
|
||||
If Not e.Exists Then e = New ErrorsDescriber(EDP.ShowAllMsg)
|
||||
@@ -886,7 +881,7 @@ BlockNullPicture:
|
||||
Protected Function CheckDatesLimit(ByVal DateObj As Object, ByVal DateProvider As IFormatProvider) As DateResult
|
||||
Try
|
||||
If (DownloadDateFrom.HasValue Or DownloadDateTo.HasValue) AndAlso ACheck(DateObj) Then
|
||||
Dim td As Date? = AConvert(Of Date)(DateObj, DateProvider, Nothing)
|
||||
Dim td As Date? = AConvert(DateObj, AModes.Var, GetType(Date),, True, Nothing, DateProvider)
|
||||
If td.HasValue Then
|
||||
If td.Value.ValueBetween(_DownloadDateFromF, _DownloadDateToF) Then
|
||||
Return DateResult.Continue
|
||||
@@ -899,13 +894,14 @@ BlockNullPicture:
|
||||
End If
|
||||
Return DateResult.Continue
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[UserDataBase.CheckDatesLimit({If(TypeOf DateObj Is String, CStr(DateObj), "?")})]", DateResult.Continue)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[UserDataBase.CheckDatesLimit({If(TypeOf DateObj Is String, CStr(DateObj), "?")})]", DateResult.Continue)
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Download functions and options"
|
||||
Protected Responser As Responser
|
||||
Protected UseResponserClient As Boolean = False
|
||||
Protected UseClientTokens As Boolean = False
|
||||
Protected _ForceSaveUserData As Boolean = False
|
||||
Protected _ForceSaveUserInfo As Boolean = False
|
||||
Private _DownloadInProgress As Boolean = False
|
||||
@@ -915,7 +911,7 @@ BlockNullPicture:
|
||||
Private _PictureExists As Boolean
|
||||
Private _EnvirInvokeUserUpdated As Boolean = False
|
||||
Protected Sub EnvirDownloadSet()
|
||||
UpdateDataFiles(, True)
|
||||
UpdateDataFiles()
|
||||
_DownloadInProgress = True
|
||||
_DescriptionChecked = False
|
||||
_DescriptionEveryTime = Settings.UpdateUserDescriptionEveryTime
|
||||
@@ -948,8 +944,8 @@ BlockNullPicture:
|
||||
If Not Responser Is Nothing Then Responser.Dispose()
|
||||
Responser = New Responser
|
||||
If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser)
|
||||
'TODO: UserDataBase remove [Responser.DecodersError]
|
||||
Responser.DecodersError = New ErrorsDescriber(EDP.SendInLog + EDP.ReturnValue) With {
|
||||
|
||||
Responser.DecodersError = New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue) With {
|
||||
.DeclaredMessage = New MMessage($"SymbolsConverter error: [{ToStringForLog()}]", ToStringForLog())}
|
||||
|
||||
Dim _downContent As Func(Of UserMedia, Boolean) = Function(c) c.State = UStates.Downloaded
|
||||
@@ -981,7 +977,9 @@ BlockNullPicture:
|
||||
|
||||
ReparseVideo(Token)
|
||||
ThrowAny(Token)
|
||||
If IsSavedPosts Then UpdateDataFiles(True)
|
||||
|
||||
If UseMD5Comparison Then ValidateMD5(Token) : ThrowAny(Token)
|
||||
|
||||
If _TempPostsList.Count > 0 And Not DownloadMissingOnly And __SaveData Then _
|
||||
TextSaver.SaveTextToFile(_TempPostsList.ListToString(Environment.NewLine), MyFilePosts, True,, EDP.None)
|
||||
_ContentNew.ListAddList(_TempMediaList, LAP.ClearBeforeAdd)
|
||||
@@ -1035,19 +1033,14 @@ BlockNullPicture:
|
||||
_ForceSaveUserInfo = False
|
||||
End Try
|
||||
End Sub
|
||||
Protected Sub UpdateDataFiles(Optional ByVal ForceSaved As Boolean = False, Optional ByVal ValidateContetnt As Boolean = False)
|
||||
'TODELETE: saved posts name compatibility 2023.2.5.0
|
||||
Dim __validateSaved As Func(Of Boolean) = Function() MyFileData.Exists Or MyFilePosts.Exists
|
||||
If Not User.File.IsEmptyString Then
|
||||
Protected Sub UpdateDataFiles()
|
||||
If Not User.File.IsEmptyString OrElse IsSavedPosts Then
|
||||
MyFileSettings = Nothing
|
||||
If IsSavedPosts Then
|
||||
Dim u As UserInfo = User
|
||||
u.Name = "SavedPosts"
|
||||
u.UpdateUserFile()
|
||||
Dim mfp As SFile = u.File
|
||||
mfp.Name &= "_Posts"
|
||||
mfp.Extension = "txt"
|
||||
If (ValidateContetnt AndAlso mfp.Exists) Or (Not ValidateContetnt AndAlso u.File.Exists) Or ForceSaved Then MyFileSettings = u.File
|
||||
User = New UserInfo(SettingsHost.SavedPostsFolderName, HOST)
|
||||
User.File.Path = $"{HOST.SavedPostsPath.PathWithSeparator}{SettingsFolderName}"
|
||||
MyFileSettings = User.File
|
||||
MyFileSettings.Name = MyFileSettings.Name.Replace(SettingsHost.SavedPostsFolderName, "SavedPosts")
|
||||
End If
|
||||
If MyFileSettings.IsEmptyString Then MyFileSettings = User.File
|
||||
MyFileData = MyFileSettings
|
||||
@@ -1060,6 +1053,71 @@ BlockNullPicture:
|
||||
End If
|
||||
End Sub
|
||||
Protected MustOverride Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
#Region "DownloadSingleObject"
|
||||
Protected IsSingleObjectDownload As Boolean = False
|
||||
Friend Overridable Sub DownloadSingleObject(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken) Implements IUserData.DownloadSingleObject
|
||||
Try
|
||||
Data.DownloadState = UserMediaStates.Tried
|
||||
Progress = Data.Progress
|
||||
If Not Responser Is Nothing Then Responser.Dispose()
|
||||
Responser = New Responser
|
||||
If Not HOST Is Nothing AndAlso Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser)
|
||||
SeparateVideoFolder = False
|
||||
IsSingleObjectDownload = True
|
||||
UseInternalDownloadFileFunction_UseProgress = True
|
||||
UseInternalM3U8Function_UseProgress = True
|
||||
DownloadSingleObject_GetPosts(Data, Token)
|
||||
DownloadSingleObject_CreateMedia(Data, Token)
|
||||
DownloadSingleObject_Download(Data, Token)
|
||||
DownloadSingleObject_PostProcessing(Data)
|
||||
Catch ex As Exception
|
||||
Data.DownloadState = UserMediaStates.Missing
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"{Site} single data downloader error: {Data.URL}")
|
||||
End Try
|
||||
End Sub
|
||||
Protected Overridable Sub DownloadSingleObject_CreateMedia(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
If _TempMediaList.Count > 0 Then
|
||||
For Each m As UserMedia In _TempMediaList
|
||||
m.File = DownloadSingleObject_CreateFile(Data, m.File)
|
||||
_ContentNew.Add(m)
|
||||
Next
|
||||
End If
|
||||
End Sub
|
||||
Protected Overridable Sub DownloadSingleObject_PostProcessing(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, Optional ByVal ResetTitle As Boolean = True)
|
||||
If _ContentNew.Count > 0 Then
|
||||
If _ContentNew.Any(Function(mm) mm.State = UStates.Downloaded) Then
|
||||
Data.DownloadState = UserMediaStates.Downloaded
|
||||
If _ContentNew(0).Type = UTypes.Picture Or _ContentNew(0).Type = UTypes.GIF Then
|
||||
DirectCast(Data, IDownloadableMedia).ThumbnailFile = _ContentNew(0).File
|
||||
ElseIf Settings.STDownloader_TakeSnapshot And Settings.FfmpegFile.Exists And Not Settings.STDownloader_RemoveDownloadedAutomatically Then
|
||||
Dim f As SFile = _ContentNew(0).File
|
||||
Dim ff As SFile = f
|
||||
ff.Name &= "_thumb"
|
||||
ff.Extension = "jpg"
|
||||
f = Web.FFMPEG.TakeSnapshot(f, ff, Settings.FfmpegFile, TimeSpan.FromSeconds(1),,, EDP.LogMessageValue)
|
||||
If f.Exists Then DirectCast(Data, IDownloadableMedia).ThumbnailFile = f
|
||||
End If
|
||||
Else
|
||||
Data.DownloadState = UserMediaStates.Missing
|
||||
End If
|
||||
YouTube.Objects.YouTubeMediaContainerBase.Update(_ContentNew(0), Data)
|
||||
If ResetTitle And Not _ContentNew(0).File.Name.IsEmptyString Then Data.Title = _ContentNew(0).File.Name
|
||||
Else
|
||||
Data.DownloadState = UserMediaStates.Missing
|
||||
End If
|
||||
End Sub
|
||||
Protected Function DownloadSingleObject_CreateFile(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal DFile As SFile) As SFile
|
||||
If Not Data.File.Path.IsEmptyString Then DFile.Path = Data.File.Path
|
||||
If DFile.Name.IsEmptyString Then DFile.Name = "OutputFile"
|
||||
Return DFile
|
||||
End Function
|
||||
Protected Overridable Sub DownloadSingleObject_Download(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
DownloadContent(Token)
|
||||
End Sub
|
||||
Protected Overridable Sub DownloadSingleObject_GetPosts(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "ReparseVideo, ReparseMissing"
|
||||
Protected Overridable Sub ReparseVideo(ByVal Token As CancellationToken)
|
||||
End Sub
|
||||
''' <summary>
|
||||
@@ -1069,15 +1127,170 @@ BlockNullPicture:
|
||||
''' </summary>
|
||||
Protected Overridable Sub ReparseMissing(ByVal Token As CancellationToken)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "MD5 support"
|
||||
Protected Const VALIDATE_MD5_ERROR As String = "VALIDATE_MD5_ERROR"
|
||||
Friend Property UseMD5Comparison As Boolean = False
|
||||
Protected Property StartMD5Checked As Boolean = True
|
||||
Friend Property RemoveExistingDuplicates As Boolean = False
|
||||
Protected Overridable 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
|
||||
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 Then
|
||||
hash = ByteArrayToString(GetMD5(SFile.GetBytesFromNet(__data.URL_BASE.IfNullOrEmpty(__data.URL), ErrMD5), ImgFormat, 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_BASE.IfNullOrEmpty(__data.URL), 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$
|
||||
For i = existingFiles.Count - 1 To 0 Step -1
|
||||
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
|
||||
End If
|
||||
End If
|
||||
For i = 0 To _ContentList.Count - 1
|
||||
data = _ContentList(i)
|
||||
If (data.Type = UTypes.GIF Or data.Type = UTypes.Picture) Then
|
||||
If data.MD5.IsEmptyString Then
|
||||
ThrowAny(Token)
|
||||
eIndx = existingFiles.FindIndex(eFinder)
|
||||
If eIndx >= 0 Then
|
||||
data.MD5 = __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
|
||||
For i = 0 To existingFiles.Count - 1
|
||||
f = existingFiles(i)
|
||||
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
|
||||
|
||||
For i = _TempMediaList.Count - 1 To 0 Step -1
|
||||
If limit > 0 And itemsCount >= limit Then
|
||||
_TempMediaList.RemoveAt(i)
|
||||
Else
|
||||
data = _TempMediaList(i)
|
||||
If missingMD5(data) Then
|
||||
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
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Catch iex As ArgumentOutOfRangeException When Disposed
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, "ValidateMD5",, VALIDATE_MD5_ERROR)
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "DownloadContent"
|
||||
Protected MustOverride Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
Private NotInheritable Class OptionalWebClient : Inherits DownloadObjects.WebClient2
|
||||
Private ReadOnly Source As UserDataBase
|
||||
Friend Sub New(ByRef Source As UserDataBase)
|
||||
Me.Source = Source
|
||||
UseResponserClient = Source.UseResponserClient
|
||||
If UseResponserClient Then
|
||||
RC = Source.Responser
|
||||
Client = Source.Responser
|
||||
Else
|
||||
WC = New WebClient
|
||||
Client = New RWebClient With {.UseNativeClient = Not Source.IsSingleObjectDownload}
|
||||
End If
|
||||
If Source.IsSingleObjectDownload Then DelegateEvents = True
|
||||
End Sub
|
||||
Private _LastProgressValue As Integer = 0
|
||||
Protected Overrides Sub Client_DownloadProgressChanged(ByVal Sender As Object, ByVal e As DownloadProgressChangedEventArgs)
|
||||
Dim v% = e.ProgressPercentage
|
||||
If v > _LastProgressValue Then
|
||||
If v > 100 Then v = 100
|
||||
Source.Progress.Value = v
|
||||
Source.Progress.Perform(0)
|
||||
End If
|
||||
_LastProgressValue = e.ProgressPercentage
|
||||
End Sub
|
||||
Protected Overrides Sub Client_DownloadFileCompleted(ByVal Sender As Object, ByVal e As AsyncCompletedEventArgs)
|
||||
Source.Progress.Done()
|
||||
End Sub
|
||||
End Class
|
||||
Protected Sub DownloadContentDefault(ByVal Token As CancellationToken)
|
||||
@@ -1090,37 +1303,51 @@ BlockNullPicture:
|
||||
If _ContentNew.Count > 0 Then
|
||||
MyFile.Exists(SFO.Path)
|
||||
Dim MissingErrorsAdd As Boolean = Settings.AddMissingErrorsToLog
|
||||
Dim MyDir$ = MyFile.CutPath.PathNoSeparator
|
||||
Dim MyDir$ = DownloadContentDefault_GetRootDir()
|
||||
Dim vsf As Boolean = SeparateVideoFolderF
|
||||
Dim __isVideo As Boolean
|
||||
Dim __interrupt As Boolean
|
||||
Dim f As SFile
|
||||
Dim v As UserMedia
|
||||
Dim fileNumProvider As SFileNumbers = SFileNumbers.Default
|
||||
|
||||
Using w As New OptionalWebClient(Me)
|
||||
If vsf Then CSFileP($"{MyDir}\Video\").Exists(SFO.Path)
|
||||
Progress.Maximum += _ContentNew.Count
|
||||
If IsSingleObjectDownload Then
|
||||
If _ContentNew.Count = 1 And _ContentNew(0).Type = UTypes.Video Then
|
||||
Progress.Value = 0
|
||||
Progress.Maximum = 100
|
||||
Progress.Provider = MyProgressNumberProvider.Percentage
|
||||
ElseIf _ContentNew(0).Type = UTypes.m3u8 Then
|
||||
Progress.Provider = MyProgressNumberProvider.Percentage
|
||||
Else
|
||||
w.DelegateEvents = False
|
||||
End If
|
||||
End If
|
||||
|
||||
For i = 0 To _ContentNew.Count - 1
|
||||
ThrowAny(Token)
|
||||
v = _ContentNew(i)
|
||||
v.State = UStates.Tried
|
||||
If v.File.IsEmptyString Then
|
||||
f = v.URL
|
||||
f = CreateFileFromUrl(v.URL)
|
||||
Else
|
||||
f = v.File
|
||||
End If
|
||||
f.Separator = "\"
|
||||
f.Path = MyDir
|
||||
If Not IsSingleObjectDownload Then f.Path = MyDir
|
||||
|
||||
If v.URL_BASE.IsEmptyString Then v.URL_BASE = v.URL
|
||||
|
||||
If Not v.File.IsEmptyString And Not v.URL.IsEmptyString Then
|
||||
If Not f.IsEmptyString And Not v.URL.IsEmptyString Then
|
||||
Try
|
||||
__isVideo = v.Type = UTypes.Video Or f.Extension = "mp4"
|
||||
__isVideo = v.Type = UTypes.Video Or f.Extension = "mp4" Or v.Type = UTypes.m3u8
|
||||
|
||||
If f.Extension.IsEmptyString Then
|
||||
Select Case v.Type
|
||||
Case UTypes.Picture : f.Extension = "jpg"
|
||||
Case UTypes.Video : f.Extension = "mp4"
|
||||
Case UTypes.Video, UTypes.m3u8 : f.Extension = "mp4"
|
||||
Case UTypes.GIF : f.Extension = "gif"
|
||||
End Select
|
||||
ElseIf f.Extension = "webp" And Settings.DownloadNativeImageFormat Then
|
||||
@@ -1138,16 +1365,29 @@ BlockNullPicture:
|
||||
End If
|
||||
End If
|
||||
|
||||
If __isVideo Then fileNumProvider.FileName = f.Name : f = SFile.IndexReindex(f,,, fileNumProvider)
|
||||
|
||||
__interrupt = False
|
||||
If v.Type = UTypes.m3u8 And UseInternalM3U8Function Then
|
||||
f = DownloadM3U8(v.URL, v, f)
|
||||
f = DownloadM3U8(v.URL, v, f, Token)
|
||||
If f.IsEmptyString Then Throw New Exception("M3U8 download failed")
|
||||
ElseIf UseInternalDownloadFileFunction AndAlso ValidateDownloadFile(v.URL, v, __interrupt) Then
|
||||
f = DownloadFile(v.URL, v, f, Token)
|
||||
If f.IsEmptyString Then Throw New Exception("InternalFunc download failed")
|
||||
Else
|
||||
w.DownloadFile(v.URL, f.ToString)
|
||||
If UseInternalDownloadFileFunction And __interrupt Then Throw New Exception("InternalFunc download interrupted")
|
||||
If UseClientTokens Then
|
||||
w.DownloadFile(v.URL, f, Token)
|
||||
Else
|
||||
w.DownloadFile(v.URL, f)
|
||||
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
|
||||
@@ -1155,11 +1395,20 @@ BlockNullPicture:
|
||||
|
||||
v.File = ChangeFileNameByProvider(f, v)
|
||||
v.State = UStates.Downloaded
|
||||
DownloadContentDefault_PostProcessing(v, f, Token)
|
||||
dCount += 1
|
||||
Catch wex As Exception
|
||||
v.Attempts += 1
|
||||
Catch woex As OperationCanceledException When Token.IsCancellationRequested
|
||||
If f.Exists Then f.Delete()
|
||||
v.State = UStates.Missing
|
||||
If MissingErrorsAdd Then ErrorDownloading(f, v.URL)
|
||||
v.Attempts += 1
|
||||
_ContentNew(i) = v
|
||||
Throw woex
|
||||
Catch wex As Exception
|
||||
If DownloadContentDefault_ProcessDownloadException() Then
|
||||
v.Attempts += 1
|
||||
v.State = UStates.Missing
|
||||
If MissingErrorsAdd Then ErrorDownloading(f, v.URL)
|
||||
End If
|
||||
End Try
|
||||
Else
|
||||
v.State = UStates.Skipped
|
||||
@@ -1185,9 +1434,30 @@ BlockNullPicture:
|
||||
End Try
|
||||
End Sub
|
||||
Protected UseInternalM3U8Function As Boolean = False
|
||||
Protected Overridable Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile) As SFile
|
||||
Protected UseInternalM3U8Function_UseProgress As Boolean = False
|
||||
Protected Overridable Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile,
|
||||
ByVal Token As CancellationToken) As SFile
|
||||
Return Nothing
|
||||
End Function
|
||||
Protected UseInternalDownloadFileFunction As Boolean = False
|
||||
Protected UseInternalDownloadFileFunction_UseProgress As Boolean = False
|
||||
Protected Overridable Function DownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile,
|
||||
ByVal Token As CancellationToken) As SFile
|
||||
Return Nothing
|
||||
End Function
|
||||
Protected Overridable Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean
|
||||
Return True
|
||||
End Function
|
||||
Protected Overridable Function DownloadContentDefault_GetRootDir() As String
|
||||
Return MyFile.CutPath(IIf(IsSingleObjectDownload, 0, 1)).PathNoSeparator
|
||||
End Function
|
||||
Protected Overridable Sub DownloadContentDefault_PostProcessing(ByRef m As UserMedia, ByVal File As SFile, ByVal Token As CancellationToken)
|
||||
End Sub
|
||||
Protected Overridable Function DownloadContentDefault_ProcessDownloadException() As Boolean
|
||||
Return True
|
||||
End Function
|
||||
#End Region
|
||||
#Region "ProcessException"
|
||||
Protected Const EXCEPTION_OPERATION_CANCELED As Integer = -1
|
||||
''' <param name="RDE">Request DownloadingException</param>
|
||||
''' <returns>0 - exit</returns>
|
||||
@@ -1208,14 +1478,19 @@ BlockNullPicture:
|
||||
End Function
|
||||
''' <summary>0 - Execute LogError and set HasError</summary>
|
||||
Protected MustOverride Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Optional ByVal EObj As Object = Nothing) As Integer
|
||||
Protected Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile
|
||||
#End Region
|
||||
#Region "ChangeFileNameByProvider, RunScript"
|
||||
Protected Overridable Function CreateFileFromUrl(ByVal URL As String) As SFile
|
||||
Return New SFile(URL)
|
||||
End Function
|
||||
Protected Overridable Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile
|
||||
Dim ff As SFile = Nothing
|
||||
Try
|
||||
If f.Exists Then
|
||||
If Not Settings.FileReplaceNameByDate.Value = FileNameReplaceMode.None Then
|
||||
ff = f
|
||||
ff.Name = String.Format(FileDateAppenderPattern, f.Name, CStr(AConvert(Of String)(If(m.Post.Date, Now), FileDateAppenderProvider, String.Empty)))
|
||||
ff = SFile.Indexed_IndexFile(ff,, New NumberedFile(ff))
|
||||
ff = SFile.IndexReindex(ff,,, New NumberedFile(ff))
|
||||
End If
|
||||
If Not ff.Name.IsEmptyString Then My.Computer.FileSystem.RenameFile(f, ff.File) : Return ff
|
||||
End If
|
||||
@@ -1238,7 +1513,7 @@ BlockNullPicture:
|
||||
If Not ScriptPattern.IsEmptyString Then
|
||||
If Not ScriptPattern.Contains(spa) Then ScriptPattern &= $" ""{spa}"""
|
||||
Using b As New BatchExecutor With {.RedirectStandardError = True}
|
||||
b.Execute({String.Format(ScriptPattern, MyFile.CutPath(1).PathNoSeparator)}, EDP.SendInLog + EDP.ThrowException)
|
||||
b.Execute({String.Format(ScriptPattern, MyFile.CutPath(1).PathNoSeparator)}, EDP.SendToLog + EDP.ThrowException)
|
||||
If b.HasError Or Not b.ErrorOutput.IsEmptyString Then Throw New Exception(b.ErrorOutput, b.ErrorException)
|
||||
End Using
|
||||
End If
|
||||
@@ -1248,6 +1523,7 @@ BlockNullPicture:
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#End Region
|
||||
#Region "Delete, Move, Merge, Copy"
|
||||
Friend Overridable Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer Implements IUserData.Delete
|
||||
Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path)
|
||||
@@ -1359,7 +1635,7 @@ BlockNullPicture:
|
||||
FilesMover.Invoke
|
||||
If SFile.GetFiles(UserBefore.File.CutPath,, SearchOption.AllDirectories,
|
||||
New ErrorsDescriber(False, False, False, New List(Of SFile))).Count = 0 Then
|
||||
UserBefore.File.CutPath.Delete(SFO.Path, Settings.DeleteMode, EDP.SendInLog)
|
||||
UserBefore.File.CutPath.Delete(SFO.Path, Settings.DeleteMode, EDP.SendToLog)
|
||||
End If
|
||||
If Not ScriptData.IsEmptyString AndAlso ScriptData.Contains(UserBefore.File.PathNoSeparator) Then _
|
||||
ScriptData = ScriptData.Replace(UserBefore.File.PathNoSeparator, MyFile.PathNoSeparator)
|
||||
@@ -1424,7 +1700,7 @@ BlockNullPicture:
|
||||
#End Region
|
||||
#Region "Errors functions"
|
||||
Protected Sub LogError(ByVal ex As Exception, ByVal Message As String)
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"{ToStringForLog()}: {Message}")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"{ToStringForLog()}: {Message}")
|
||||
End Sub
|
||||
Protected Sub ErrorDownloading(ByVal f As SFile, ByVal URL As String)
|
||||
If Not f.Exists Then MyMainLOG = $"Error downloading from [{URL}] to [{f}]"
|
||||
@@ -1546,7 +1822,7 @@ BlockNullPicture:
|
||||
#Region "Base interfaces"
|
||||
Friend Interface IContentProvider
|
||||
ReadOnly Property Site As String
|
||||
Property Name As String
|
||||
ReadOnly Property Name As String
|
||||
Property ID As String
|
||||
Property FriendlyName As String
|
||||
Property Description As String
|
||||
@@ -1554,6 +1830,7 @@ BlockNullPicture:
|
||||
Property Temporary As Boolean
|
||||
Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing)
|
||||
Sub DownloadData(ByVal Token As CancellationToken)
|
||||
Sub DownloadSingleObject(ByVal Data As YouTube.Objects.IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
End Interface
|
||||
Friend Interface IUserData : Inherits IContentProvider, IComparable(Of UserDataBase), IComparable, IEquatable(Of UserDataBase), IIndexable, IDisposable
|
||||
Event UserUpdated(ByVal User As IUserData)
|
||||
@@ -1571,7 +1848,6 @@ BlockNullPicture:
|
||||
ReadOnly Property IsVirtual As Boolean
|
||||
ReadOnly Property Labels As List(Of String)
|
||||
#End Region
|
||||
ReadOnly Property IsChannel As Boolean
|
||||
Property Exists As Boolean
|
||||
Property Suspended As Boolean
|
||||
Property ReadyForDownload As Boolean
|
||||
|
||||
@@ -1,86 +0,0 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
|
||||
Namespace API.BaseObjects
|
||||
Friend Interface IDomainContainer
|
||||
ReadOnly Property Icon As Icon
|
||||
ReadOnly Property Site As String
|
||||
ReadOnly Property Domains As List(Of String)
|
||||
ReadOnly Property DomainsTemp As List(Of String)
|
||||
ReadOnly Property DomainsDefault As String
|
||||
ReadOnly Property DomainsSettingProp As Plugin.PropertyValue
|
||||
Property DomainsChanged As Boolean
|
||||
Property Initialized As Boolean
|
||||
Property DomainsUpdateInProgress As Boolean
|
||||
Property DomainsUpdatedBySite As Boolean
|
||||
Sub UpdateDomains()
|
||||
End Interface
|
||||
Friend NotInheritable Class DomainContainer
|
||||
Private Sub New()
|
||||
End Sub
|
||||
Friend Shared Sub EndInit(ByVal s As IDomainContainer)
|
||||
If ACheck(s.DomainsSettingProp.Value) Then s.Domains.ListAddList(CStr(s.DomainsSettingProp.Value).Split("|"), LAP.NotContainsOnly)
|
||||
End Sub
|
||||
Friend Overloads Shared Sub UpdateDomains(ByVal s As IDomainContainer)
|
||||
UpdateDomains(s, Nothing, True)
|
||||
End Sub
|
||||
Friend Overloads Shared Sub UpdateDomains(ByVal s As IDomainContainer, ByVal NewDomains As IEnumerable(Of String), ByVal Internal As Boolean)
|
||||
With s
|
||||
If Not .Initialized Or (.DomainsUpdatedBySite And Not Internal) Then Exit Sub
|
||||
If Not .DomainsUpdateInProgress Then
|
||||
.DomainsUpdateInProgress = True
|
||||
.Domains.ListAddList(.DomainsDefault.Split("|"), LAP.NotContainsOnly)
|
||||
.Domains.ListAddList(NewDomains, LAP.NotContainsOnly)
|
||||
.DomainsSettingProp.Value = .Domains.ListToString("|")
|
||||
If Not Internal Then .DomainsUpdatedBySite = True
|
||||
.DomainsUpdateInProgress = False
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
Friend Shared Sub Update(ByVal s As IDomainContainer)
|
||||
With s
|
||||
If .DomainsChanged Then
|
||||
.Domains.Clear()
|
||||
.Domains.ListAddList(.DomainsTemp, LAP.NotContainsOnly)
|
||||
.UpdateDomains()
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
Friend Shared Sub EndEdit(ByVal s As IDomainContainer)
|
||||
s.DomainsTemp.ListAddList(s.Domains, LAP.ClearBeforeAdd, LAP.NotContainsOnly)
|
||||
s.DomainsChanged = False
|
||||
End Sub
|
||||
Friend Shared Sub OpenSettingsForm(ByVal s As IDomainContainer)
|
||||
Dim __add As EventHandler(Of SimpleListFormEventArgs) = Sub(sender, e) e.Item = InputBoxE($"Enter a new domain using the pattern [{s.Site}.com]:", "New domain").IfNullOrEmptyE(Nothing)
|
||||
Dim __delete As EventHandler(Of SimpleListFormEventArgs) = Sub(sender, e)
|
||||
Dim n$ = AConvert(Of String)(e.Item, AModes.Var, String.Empty)
|
||||
e.Result = MsgBoxE({$"Are you sure you want to delete the [{n}] domain?",
|
||||
"Removing domains"}, vbYesNo) = vbYes
|
||||
End Sub
|
||||
Using f As New SimpleListForm(Of String)(If(s.DomainsChanged, s.DomainsTemp, s.Domains), Settings.Design) With {
|
||||
.Buttons = {ADB.Add, ADB.Delete},
|
||||
.Mode = SimpleListFormModes.Remaining,
|
||||
.FormText = s.Site,
|
||||
.Icon = s.Icon,
|
||||
.LocationOnly = True,
|
||||
.Size = New Size(400, 330),
|
||||
.DesignXMLNodeName = s.Site
|
||||
}
|
||||
AddHandler f.AddClick, __add
|
||||
AddHandler f.DeleteClick, __delete
|
||||
f.ShowDialog()
|
||||
If f.DialogResult = DialogResult.OK Then
|
||||
s.DomainsChanged = True
|
||||
s.DomainsTemp.ListAddList(f.DataResult, LAP.ClearBeforeAdd, LAP.NotContainsOnly)
|
||||
End If
|
||||
End Using
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
108
SCrawler/API/BaseObjects/DomainsContainer.vb
Normal file
108
SCrawler/API/BaseObjects/DomainsContainer.vb
Normal file
@@ -0,0 +1,108 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
|
||||
Namespace API.Base
|
||||
Friend Class DomainsContainer : Implements IEnumerable(Of String), IMyEnumerator(Of String)
|
||||
Friend Event DomainsUpdated(ByVal Sender As DomainsContainer)
|
||||
Friend ReadOnly Property Domains As List(Of String)
|
||||
Friend ReadOnly Property DomainsTemp As List(Of String)
|
||||
Friend ReadOnly Property DomainsDefault As String
|
||||
Friend Property Changed As Boolean
|
||||
Private DomainsUpdateInProgress As Boolean = False
|
||||
Friend Property UpdatedBySite As Boolean
|
||||
Protected ReadOnly Property Instance As ISiteSettings
|
||||
Friend Property DestinationProp As PropertyValue
|
||||
Default Friend ReadOnly Property Item(ByVal Index As Integer) As String Implements IMyEnumerator(Of String).MyEnumeratorObject
|
||||
Get
|
||||
Return Domains(Index)
|
||||
End Get
|
||||
End Property
|
||||
Friend ReadOnly Property Count As Integer Implements IMyEnumerator(Of String).MyEnumeratorCount
|
||||
Get
|
||||
Return Domains.Count
|
||||
End Get
|
||||
End Property
|
||||
Friend Sub New(ByVal _Instance As ISiteSettings, ByVal DefaultValue As String)
|
||||
Domains = New List(Of String)
|
||||
DomainsTemp = New List(Of String)
|
||||
Instance = _Instance
|
||||
DomainsDefault = DefaultValue
|
||||
If Not DomainsDefault.IsEmptyString Then Domains.ListAddList(CStr(DomainsDefault).Split("|"), LAP.NotContainsOnly)
|
||||
End Sub
|
||||
Friend Sub PopulateInitialDomains(ByVal InitialValue As String)
|
||||
If Not InitialValue.IsEmptyString Then Domains.ListAddList(CStr(InitialValue).Split("|"), LAP.NotContainsOnly)
|
||||
End Sub
|
||||
Public Overrides Function ToString() As String
|
||||
Return Domains.ListToString("|")
|
||||
End Function
|
||||
Friend Sub Add(ByVal NewDomains As IEnumerable(Of String), ByVal UpdateBySite As Boolean)
|
||||
If Not DomainsUpdateInProgress Then
|
||||
DomainsUpdateInProgress = True
|
||||
Domains.ListAddList(NewDomains, LAP.NotContainsOnly)
|
||||
If UpdateBySite Then Me.UpdatedBySite = True
|
||||
Save()
|
||||
DomainsUpdateInProgress = False
|
||||
RaiseEvent DomainsUpdated(Me)
|
||||
End If
|
||||
End Sub
|
||||
Friend Overridable Function Apply() As Boolean
|
||||
If Changed Then
|
||||
Domains.Clear()
|
||||
Domains.ListAddList(DomainsTemp, LAP.NotContainsOnly)
|
||||
Save()
|
||||
RaiseEvent DomainsUpdated(Me)
|
||||
Return True
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
Friend Overridable Sub Save()
|
||||
If Not DestinationProp Is Nothing Then DestinationProp.Value = ToString()
|
||||
End Sub
|
||||
Friend Overridable Sub Reset()
|
||||
Changed = False
|
||||
DomainsTemp.Clear()
|
||||
End Sub
|
||||
Friend Overridable Sub OpenSettingsForm()
|
||||
Dim __add As EventHandler(Of SimpleListFormEventArgs) = Sub(sender, e) e.Item = InputBoxE($"Enter a new domain using the pattern [{Instance.Site}.com]:", "New domain").IfNullOrEmptyE(Nothing)
|
||||
Dim __delete As EventHandler(Of SimpleListFormEventArgs) = Sub(sender, e)
|
||||
Dim n$ = AConvert(Of String)(e.Item, AModes.Var, String.Empty)
|
||||
e.Result = MsgBoxE({$"Are you sure you want to delete the [{n}] domain?",
|
||||
"Removing domains"}, vbYesNo) = vbYes
|
||||
End Sub
|
||||
Using f As New SimpleListForm(Of String)(If(Changed, DomainsTemp, Domains), Settings.Design) With {
|
||||
.Buttons = {ADB.Add, ADB.Delete},
|
||||
.Mode = SimpleListFormModes.Remaining,
|
||||
.FormText = Instance.Site,
|
||||
.Icon = Instance.Icon,
|
||||
.LocationOnly = True,
|
||||
.Size = New Size(400, 330),
|
||||
.DesignXMLNodeName = $"{Instance.Site}_DomainsForm"
|
||||
}
|
||||
AddHandler f.AddClick, __add
|
||||
AddHandler f.DeleteClick, __delete
|
||||
f.ShowDialog()
|
||||
If f.DialogResult = DialogResult.OK Then
|
||||
Changed = True
|
||||
DomainsTemp.Clear()
|
||||
DomainsTemp.ListAddList(f.DataResult, LAP.NotContainsOnly)
|
||||
End If
|
||||
End Using
|
||||
End Sub
|
||||
Private Function GetEnumerator() As IEnumerator(Of String) Implements IEnumerable(Of String).GetEnumerator
|
||||
Return New MyEnumerator(Of String)(Me)
|
||||
End Function
|
||||
Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
|
||||
Return GetEnumerator()
|
||||
End Function
|
||||
End Class
|
||||
End Namespace
|
||||
89
SCrawler/API/BaseObjects/InternalSettingsForm.Designer.vb
generated
Normal file
89
SCrawler/API/BaseObjects/InternalSettingsForm.Designer.vb
generated
Normal file
@@ -0,0 +1,89 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Base
|
||||
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||
Partial Friend Class InternalSettingsForm : Inherits System.Windows.Forms.Form
|
||||
<System.Diagnostics.DebuggerNonUserCode()>
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
Try
|
||||
If disposing AndAlso components IsNot Nothing Then
|
||||
components.Dispose()
|
||||
End If
|
||||
Finally
|
||||
MyBase.Dispose(disposing)
|
||||
End Try
|
||||
End Sub
|
||||
Private components As System.ComponentModel.IContainer
|
||||
<System.Diagnostics.DebuggerStepThrough()>
|
||||
Private Sub InitializeComponent()
|
||||
Me.components = New System.ComponentModel.Container()
|
||||
Me.CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||
Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||
Me.TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
|
||||
Me.CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||
Me.CONTAINER_MAIN.SuspendLayout()
|
||||
Me.SuspendLayout()
|
||||
'
|
||||
'CONTAINER_MAIN
|
||||
'
|
||||
'
|
||||
'CONTAINER_MAIN.ContentPanel
|
||||
'
|
||||
Me.CONTAINER_MAIN.ContentPanel.Controls.Add(Me.TP_MAIN)
|
||||
Me.CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(184, 0)
|
||||
Me.CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||
Me.CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
Me.CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||
Me.CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||
Me.CONTAINER_MAIN.Size = New System.Drawing.Size(184, 25)
|
||||
Me.CONTAINER_MAIN.TabIndex = 0
|
||||
Me.CONTAINER_MAIN.TopToolStripPanelVisible = False
|
||||
'
|
||||
'TP_MAIN
|
||||
'
|
||||
Me.TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||
Me.TP_MAIN.ColumnCount = 1
|
||||
Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
Me.TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
|
||||
Me.TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
Me.TP_MAIN.Name = "TP_MAIN"
|
||||
Me.TP_MAIN.RowCount = 1
|
||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
Me.TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 160.0!))
|
||||
Me.TP_MAIN.Size = New System.Drawing.Size(184, 0)
|
||||
Me.TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'InternalSettingsForm
|
||||
'
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(184, 25)
|
||||
Me.Controls.Add(Me.CONTAINER_MAIN)
|
||||
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
||||
Me.KeyPreview = True
|
||||
Me.MaximizeBox = False
|
||||
Me.MinimizeBox = False
|
||||
Me.Name = "InternalSettingsForm"
|
||||
Me.ShowInTaskbar = False
|
||||
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
||||
Me.Text = "Settings"
|
||||
Me.CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
Me.CONTAINER_MAIN.ResumeLayout(False)
|
||||
Me.CONTAINER_MAIN.PerformLayout()
|
||||
Me.ResumeLayout(False)
|
||||
|
||||
End Sub
|
||||
|
||||
Private WithEvents TP_MAIN As TableLayoutPanel
|
||||
Private WithEvents TT_MAIN As ToolTip
|
||||
Private WithEvents CONTAINER_MAIN As ToolStripContainer
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -117,10 +117,7 @@
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
<metadata name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
|
||||
<value>17, 17</value>
|
||||
</metadata>
|
||||
</root>
|
||||
256
SCrawler/API/BaseObjects/InternalSettingsForm.vb
Normal file
256
SCrawler/API/BaseObjects/InternalSettingsForm.vb
Normal file
@@ -0,0 +1,256 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Reflection
|
||||
Imports System.ComponentModel
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Namespace API.Base
|
||||
Friend Class InternalSettingsForm
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Private ReadOnly Property MySettingsInstance As ISiteSettings
|
||||
Private ReadOnly Property MyObject As Object
|
||||
Private ReadOnly IsSettingsForm As Boolean = True
|
||||
Private ReadOnly Property MyMembers As List(Of MemberOption)
|
||||
''' <summary>Default: 200</summary>
|
||||
Friend Property MinimumWidth As Integer = 200
|
||||
Private Class MemberOption : Inherits Hosts.PropertyValueHost : Implements IDisposable
|
||||
Friend ToolTip As String
|
||||
Friend Caption As String
|
||||
Friend ThreeState As Boolean = False
|
||||
Friend AllowNull As Boolean = True
|
||||
Friend Provider As Type
|
||||
Friend Overrides Property Type As Type
|
||||
Get
|
||||
Return _Type
|
||||
End Get
|
||||
Set(ByVal t As Type)
|
||||
MyBase.Type = t
|
||||
End Set
|
||||
End Property
|
||||
Friend Overrides ReadOnly Property Name As String
|
||||
Get
|
||||
Return Member.Name
|
||||
End Get
|
||||
End Property
|
||||
Friend Overrides Property LeftOffset As Integer
|
||||
Get
|
||||
Return If(_LeftOffset, 0)
|
||||
End Get
|
||||
Set(ByVal NewOffset As Integer)
|
||||
MyBase.LeftOffset = NewOffset
|
||||
End Set
|
||||
End Property
|
||||
Private ReadOnly _MinimumWidth As Integer? = Nothing
|
||||
Friend ReadOnly Property Width As Integer
|
||||
Get
|
||||
Return LeftOffset + If(_MinimumWidth, 0) + If(TypeOf Control Is CheckBox, 0, 200) +
|
||||
PaddingE.GetOf({Control}).Horizontal(2) + MeasureText(Caption, Control.Font).Width
|
||||
End Get
|
||||
End Property
|
||||
Friend OptName As String = String.Empty
|
||||
Friend Sub New(ByRef PropertySource As Object, ByVal m As MemberInfo, ByVal ps As PSettingAttribute, ByVal po As PropertyOption)
|
||||
Source = PropertySource
|
||||
Member = m
|
||||
_Type = Member.GetMemberType
|
||||
_Value = Member.GetMemberValue(PropertySource)
|
||||
With ps
|
||||
ToolTip = .ToolTip
|
||||
Caption = .Caption
|
||||
ThreeState = .ThreeState
|
||||
AllowNull = .AllowNull
|
||||
Provider = .Provider
|
||||
_LeftOffset = .LeftOffsetGet
|
||||
ControlNumber = .Number
|
||||
_MinimumWidth = .MinimumWidth
|
||||
End With
|
||||
If Not po Is Nothing Then
|
||||
With po
|
||||
OptName = po.Name
|
||||
If ToolTip.IsEmptyString Then ToolTip = .ControlToolTip
|
||||
If Caption.IsEmptyString Then Caption = .ControlText
|
||||
End With
|
||||
End If
|
||||
End Sub
|
||||
Protected Overrides ReadOnly Property Control_IsInformationLabel As Boolean
|
||||
Get
|
||||
Return False
|
||||
End Get
|
||||
End Property
|
||||
Protected Overrides ReadOnly Property Control_ThreeStates As Boolean
|
||||
Get
|
||||
Return ThreeState
|
||||
End Get
|
||||
End Property
|
||||
Protected Overrides ReadOnly Property Control_Caption As String
|
||||
Get
|
||||
Return Caption
|
||||
End Get
|
||||
End Property
|
||||
Protected Overrides ReadOnly Property Control_ToolTip As String
|
||||
Get
|
||||
Return ToolTip
|
||||
End Get
|
||||
End Property
|
||||
Friend Overloads Sub CreateControl(ByVal f As FieldsChecker, ByVal TT As ToolTip)
|
||||
CreateControl(TT)
|
||||
If Not Provider Is Nothing Then f.AddControl(Control, Caption, Type, AllowNull, Activator.CreateInstance(Provider))
|
||||
End Sub
|
||||
#Region "IDisposable Support"
|
||||
Private disposedValue As Boolean = False
|
||||
Protected Overridable Overloads Sub Dispose(ByVal disposing As Boolean)
|
||||
If Not disposedValue Then
|
||||
If disposing Then Control.Dispose()
|
||||
Control = Nothing
|
||||
disposedValue = True
|
||||
End If
|
||||
End Sub
|
||||
Protected Overrides Sub Finalize()
|
||||
Dispose(False)
|
||||
MyBase.Finalize()
|
||||
End Sub
|
||||
Friend Overloads Sub Dispose() Implements IDisposable.Dispose
|
||||
Dispose(True)
|
||||
GC.SuppressFinalize(Me)
|
||||
End Sub
|
||||
#End Region
|
||||
End Class
|
||||
Friend Sub New(ByVal Obj As Object, ByVal s As ISiteSettings, ByVal _IsSettingsForm As Boolean)
|
||||
InitializeComponent()
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
MyMembers = New List(Of MemberOption)
|
||||
|
||||
MyObject = Obj
|
||||
MySettingsInstance = s
|
||||
IsSettingsForm = _IsSettingsForm
|
||||
If _IsSettingsForm Then
|
||||
Text = "Settings"
|
||||
Else
|
||||
Text = "Options"
|
||||
End If
|
||||
Icon = s.Icon
|
||||
End Sub
|
||||
Private Sub InternalSettingsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
Try
|
||||
With MyDefs
|
||||
.MyViewInitialize(True)
|
||||
.AddOkCancelToolbar()
|
||||
|
||||
Dim members As IEnumerable(Of MemberInfo)
|
||||
Dim member As MemberInfo
|
||||
Dim attr As PSettingAttribute
|
||||
Dim opt As PropertyOption
|
||||
Dim providersMembersSettings As IEnumerable(Of MemberInfo)
|
||||
Dim providersMembersObj As IEnumerable(Of MemberInfo)
|
||||
Dim providersPredicate As Func(Of MemberInfo, Boolean) = Function(m) m.MemberType = MemberTypes.Property AndAlso
|
||||
m.GetMemberCustomAttributes(Of Provider).ListExists
|
||||
Dim m1 As MemberInfo, m2 As MemberInfo
|
||||
Dim tmpObj As Object
|
||||
|
||||
members = GetObjectMembers(MyObject, Function(m) (m.MemberType = MemberTypes.Field Or m.MemberType = MemberTypes.Property) AndAlso
|
||||
Not m.GetCustomAttribute(Of PSettingAttribute) Is Nothing)
|
||||
providersMembersSettings = GetObjectMembers(MySettingsInstance, providersPredicate)
|
||||
providersMembersObj = GetObjectMembers(MyObject, providersPredicate)
|
||||
|
||||
If members.ListExists Then
|
||||
For Each member In members
|
||||
attr = member.GetMemberCustomAttribute(Of PSettingAttribute)
|
||||
If Not attr Is Nothing AndAlso
|
||||
(attr.Address = SettingAddress.Both OrElse
|
||||
(
|
||||
(IsSettingsForm And attr.Address = SettingAddress.Settings) Or
|
||||
(Not IsSettingsForm And attr.Address = SettingAddress.User)
|
||||
)
|
||||
) Then
|
||||
opt = Nothing
|
||||
|
||||
If Not attr.NameAssoc.IsEmptyString Then
|
||||
m1 = GetObjectMembers(MyObject, Function(m) m.Name = attr.NameAssocInstance).FirstOrDefault
|
||||
If Not m1 Is Nothing AndAlso (m1.MemberType = MemberTypes.Property Or
|
||||
m1.MemberType = MemberTypes.Field Or
|
||||
m1.MemberType = MemberTypes.Method) Then
|
||||
tmpObj = m1.GetMemberValue(MyObject)
|
||||
If Not tmpObj Is Nothing Then
|
||||
m2 = GetObjectMembers(tmpObj, Function(m) m.Name = attr.NameAssoc).FirstOrDefault
|
||||
If Not m2 Is Nothing Then opt = m2.GetMemberCustomAttribute(Of PropertyOption)
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
|
||||
MyMembers.Add(New MemberOption(MyObject, member, attr, opt))
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
|
||||
.MyFieldsCheckerE = New FieldsChecker
|
||||
|
||||
If MyMembers.Count > 0 Then
|
||||
|
||||
Dim prov As IEnumerable(Of Provider)
|
||||
Dim _prov As Provider
|
||||
Dim si% = -1
|
||||
Dim i%
|
||||
For Each provEnum In {providersMembersObj, providersMembersSettings}
|
||||
si += 1
|
||||
If provEnum.ListExists Then
|
||||
For Each member In provEnum
|
||||
prov = member.GetMemberCustomAttributes(Of Provider)
|
||||
If prov.ListExists Then
|
||||
For Each _prov In prov
|
||||
i = MyMembers.FindIndex(Function(m) If(si = 0, m.Name, m.OptName) = _prov.Name)
|
||||
If i >= 0 Then MyMembers(i).SetProvider(member.GetMemberValue(If(si = 0, MyObject, CObj(MySettingsInstance))), _prov)
|
||||
Next
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Next
|
||||
|
||||
TP_MAIN.RowStyles.Clear()
|
||||
TP_MAIN.RowCount = 0
|
||||
For i% = 0 To MyMembers.Count - 1
|
||||
With MyMembers(i)
|
||||
.CreateControl(MyDefs.MyFieldsCheckerE, TT_MAIN)
|
||||
TP_MAIN.RowStyles.Add(New RowStyle(SizeType.Absolute, .ControlHeight))
|
||||
TP_MAIN.RowCount += 1
|
||||
TP_MAIN.Controls.Add(.Control, 0, TP_MAIN.RowStyles.Count - 1)
|
||||
End With
|
||||
Next
|
||||
Else
|
||||
Throw New ArgumentOutOfRangeException("Members", "Settings instance does not contain settings members")
|
||||
End If
|
||||
|
||||
.MyFieldsChecker.EndLoaderOperations()
|
||||
|
||||
Dim s As Size = Size
|
||||
s.Height += (MyMembers.Sum(Function(m) m.ControlHeight) +
|
||||
(PaddingE.GetOf({TP_MAIN},,,,,, 0).Vertical(MyMembers.Count - 1) / 2).RoundDown + MyMembers.Count - 1)
|
||||
s.Width = MyMembers.Max(Function(m) m.Width) + PaddingE.GetOf({TP_MAIN, CONTAINER_MAIN, CONTAINER_MAIN.ContentPanel, Me}, False).Horizontal(2)
|
||||
If MinimumWidth > 0 And s.Width < MinimumWidth Then s.Width = MinimumWidth
|
||||
Size = s
|
||||
MinimumSize = s
|
||||
MaximumSize = s
|
||||
|
||||
.EndLoaderOperations()
|
||||
End With
|
||||
Catch ex As Exception
|
||||
MyDefs.InvokeLoaderError(ex)
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub InternalSettingsForm_Closing(sender As Object, e As CancelEventArgs) Handles Me.Closing
|
||||
TP_MAIN.Controls.Clear()
|
||||
MyMembers.ListClearDispose
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
|
||||
If MyDefs.MyFieldsChecker.AllParamsOK Then
|
||||
MyMembers.ForEach(Sub(m) m.UpdateValueByControl())
|
||||
MyDefs.CloseForm()
|
||||
End If
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -7,12 +7,70 @@
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Net
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports SCrawler.Plugin.Hosts
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Gfycat
|
||||
Friend NotInheritable Class Envir
|
||||
Private Sub New()
|
||||
Friend NotInheritable Class Envir : Inherits UserDataBase
|
||||
Friend Const SiteKey As String = "AndyProgram_Gfycat"
|
||||
Friend Const SiteName As String = "Gfycat"
|
||||
Friend Sub New()
|
||||
End Sub
|
||||
#Region "UserDataBase Support"
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As PersonalUtilities.Functions.XML.XmlFile, ByVal Loading As Boolean)
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
SeparateVideoFolder = False
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
Return 0
|
||||
End Function
|
||||
#End Region
|
||||
#Region "DownloadSingleObject"
|
||||
Private _IsRedGifs As Boolean = False
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
Dim urlVideo$ = GetVideo(Data.URL)
|
||||
If Not urlVideo.IsEmptyString Then
|
||||
If urlVideo.Contains("redgifs.com") Then
|
||||
_IsRedGifs = True
|
||||
DirectCast(Settings(RedGifs.RedGifsSiteKey).Source, RedGifs.SiteSettings).UpdateTokenIfRequired()
|
||||
Dim newData As IYouTubeMediaContainer = Settings(RedGifs.RedGifsSiteKey).GetSingleMediaInstance(urlVideo, Data.File)
|
||||
If Not newData Is Nothing Then
|
||||
newData.Progress = Data.Progress
|
||||
newData.Download(Data.UseCookies, Token)
|
||||
YouTubeMediaContainerBase.Update(newData, Data)
|
||||
DirectCast(Data, DownloadableMediaHost).ExchangeData(newData, Data)
|
||||
With DirectCast(Data, YouTubeMediaContainerBase)
|
||||
.Site = RedGifs.RedGifsSite
|
||||
.SiteKey = RedGifs.RedGifsSiteKey
|
||||
.SiteIcon = Settings(RedGifs.RedGifsSiteKey).Source.Image
|
||||
End With
|
||||
Else
|
||||
Throw New Exception($"Unable to get RedGifs instance{vbCr}{Data.URL}{vbCr}{urlVideo}")
|
||||
End If
|
||||
Else
|
||||
Dim m As New UserMedia(urlVideo, UserMedia.Types.Video) With {.URL_BASE = Data.URL}
|
||||
m.File.Path = Data.File.Path
|
||||
_TempMediaList.Add(m)
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadSingleObject_CreateMedia(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
If Not _IsRedGifs Then MyBase.DownloadSingleObject_CreateMedia(Data, Token)
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadSingleObject_PostProcessing(ByVal Data As IYouTubeMediaContainer, Optional ByVal ResetTitle As Boolean = True)
|
||||
If Not _IsRedGifs Then MyBase.DownloadSingleObject_PostProcessing(Data, ResetTitle)
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadSingleObject_Download(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
If Not _IsRedGifs Then MyBase.DownloadSingleObject_Download(Data, Token)
|
||||
End Sub
|
||||
#End Region
|
||||
Friend Shared Function GetVideo(ByVal URL As String) As String
|
||||
Try
|
||||
Dim r$
|
||||
@@ -33,14 +91,22 @@ Namespace API.Gfycat
|
||||
Dim e As EDP = EDP.ReturnValue
|
||||
If TypeOf ex Is WebException Then
|
||||
Dim obj As HttpWebResponse = TryCast(DirectCast(ex, WebException).Response, HttpWebResponse)
|
||||
If Not If(obj?.StatusCode, HttpStatusCode.OK) = HttpStatusCode.NotFound Then e += EDP.SendInLog
|
||||
If Not If(obj?.StatusCode, HttpStatusCode.OK) = HttpStatusCode.NotFound Then e += EDP.SendToLog
|
||||
End If
|
||||
Return ErrorsDescriber.Execute(e, ex, $"[API.Gfycat.Envir.GetVideo({URL})]", String.Empty)
|
||||
End Try
|
||||
End Function
|
||||
Friend Shared Function GetVideoInfo(ByVal URL As String) As IEnumerable(Of UserMedia)
|
||||
Dim u$ = GetVideo(URL)
|
||||
Return If(u.IsEmptyString, Nothing, {New UserMedia(u, UserMedia.Types.Video)})
|
||||
Friend Shared Function GetSingleMediaInstance(ByVal URL As String, ByVal OutputFile As SFile) As IYouTubeMediaContainer
|
||||
If Not URL.IsEmptyString AndAlso URL.Contains("gfycat") Then
|
||||
Return New DownloadableMediaHost(URL, OutputFile) With {
|
||||
.Instance = New Envir,
|
||||
.Site = SiteName,
|
||||
.SiteKey = SiteKey,
|
||||
.SiteIcon = Nothing
|
||||
}
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -7,8 +7,11 @@
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Net
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.Imgur.Declarations
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports SCrawler.Plugin.Hosts
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
@@ -18,8 +21,28 @@ Namespace API.Imgur
|
||||
Friend ReadOnly PostRegex As RParams = RParams.DMS("/([^/]+?)(|#.*?|\.[\w]{0,4})(|\?.*?)\Z", 1)
|
||||
End Module
|
||||
End Namespace
|
||||
Friend NotInheritable Class Envir
|
||||
Private Sub New()
|
||||
Friend NotInheritable Class Envir : Inherits UserDataBase
|
||||
Friend Const SiteKey As String = "AndyProgram_Imgur"
|
||||
Friend Const SiteName As String = "Imgur"
|
||||
Friend Sub New()
|
||||
End Sub
|
||||
#Region "UserDataBase Support"
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
SeparateVideoFolder = False
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
Return 0
|
||||
End Function
|
||||
#End Region
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
Dim videos As IEnumerable(Of UserMedia) = GetVideoInfo(Data.URL, EDP.SendToLog)
|
||||
If videos.ListExists Then _TempMediaList.AddRange(videos)
|
||||
End Sub
|
||||
Friend Shared Function GetGallery(ByVal URL As String, Optional ByVal e As ErrorsDescriber = Nothing) As List(Of String)
|
||||
Try
|
||||
@@ -47,7 +70,7 @@ Namespace API.Imgur
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Return DownloadingException(ex, $"[API.Imgur.Envir.GetGallery({URL})]", Nothing, e)
|
||||
Return DownloadingException_Internal(ex, $"[API.Imgur.Envir.GetGallery({URL})]", Nothing, e)
|
||||
End Try
|
||||
End Function
|
||||
Friend Shared Function GetImage(ByVal URL As String, Optional ByVal e As ErrorsDescriber = Nothing) As String
|
||||
@@ -64,7 +87,7 @@ Namespace API.Imgur
|
||||
End If
|
||||
Return String.Empty
|
||||
Catch ex As Exception
|
||||
Return DownloadingException(ex, $"[API.Imgur.Envir.GetImage({URL})]", String.Empty, e)
|
||||
Return DownloadingException_Internal(ex, $"[API.Imgur.Envir.GetImage({URL})]", String.Empty, e)
|
||||
End Try
|
||||
End Function
|
||||
Friend Shared Function GetVideoInfo(ByVal URL As String, Optional ByVal e As ErrorsDescriber = Nothing) As IEnumerable(Of UserMedia)
|
||||
@@ -81,11 +104,23 @@ Namespace API.Imgur
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
If Not e.Exists Then e = EDP.LogMessageValue
|
||||
Return ErrorsDescriber.Execute(e, ex, "Imgur standalone downloader: fetch media error")
|
||||
Return ErrorsDescriber.Execute(e, ex, $"[API.Imgur.Envir.GetVideoInfo({URL})]: fetch media error")
|
||||
End Try
|
||||
End Function
|
||||
Private Shared Function DownloadingException(ByVal ex As Exception, ByVal Message As String,
|
||||
ByVal NullArg As Object, ByVal e As ErrorsDescriber) As Object
|
||||
Friend Shared Function GetSingleMediaInstance(ByVal URL As String, ByVal OutputFile As SFile) As IYouTubeMediaContainer
|
||||
If Not URL.IsEmptyString AndAlso URL.Contains("imgur.com") Then
|
||||
Return New DownloadableMediaHost(URL, OutputFile) With {
|
||||
.Instance = New Envir,
|
||||
.Site = SiteName,
|
||||
.SiteKey = SiteKey,
|
||||
.SiteIcon = Nothing
|
||||
}
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
Private Shared Function DownloadingException_Internal(ByVal ex As Exception, ByVal Message As String,
|
||||
ByVal NullArg As Object, ByVal e As ErrorsDescriber) As Object
|
||||
If TypeOf ex Is WebException Then
|
||||
Dim obj As HttpWebResponse = TryCast(DirectCast(ex, WebException).Response, HttpWebResponse)
|
||||
If Not obj Is Nothing Then
|
||||
@@ -97,7 +132,7 @@ Namespace API.Imgur
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
If Not e.Exists Then e = New ErrorsDescriber(EDP.ReturnValue + EDP.SendInLog)
|
||||
If Not e.Exists Then e = New ErrorsDescriber(EDP.ReturnValue + EDP.SendToLog)
|
||||
Return ErrorsDescriber.Execute(e, ex, Message, NullArg)
|
||||
End Function
|
||||
End Class
|
||||
|
||||
@@ -6,16 +6,15 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Cookies
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Clients.EventArguments
|
||||
Imports PersonalUtilities.Tools.Web.Cookies
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Instagram
|
||||
Friend Module Declarations
|
||||
Friend Const InstagramSite As String = "Instagram"
|
||||
Friend Const InstagramSiteKey As String = "AndyProgram_Instagram"
|
||||
Friend ReadOnly FilesPattern As RParams = RParams.DMS(".+?([^/\?]+?\.[\w\d]{3,4})(?=(\?|\Z))", 1, EDP.ReturnValue)
|
||||
Friend ReadOnly Property DateProvider As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicode(v))
|
||||
Friend Sub UpdateResponser(ByVal Source As IResponse, ByRef Destination As Responser)
|
||||
Const r_wwwClaimName$ = "x-ig-set-www-claim"
|
||||
Const r_tokenName$ = "csrftoken"
|
||||
@@ -46,7 +45,7 @@ Namespace API.Instagram
|
||||
If Not wwwClaim.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_IG_WWW_CLAIM, wwwClaim)
|
||||
If Not token.IsEmptyString Then Destination.Headers.Add(SiteSettings.Header_CSRF_TOKEN, token)
|
||||
If Not isInternal Then
|
||||
Destination.Cookies.Update(Source.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll, False, EDP.SendInLog)
|
||||
Destination.Cookies.Update(Source.Cookies, CookieKeeper.UpdateModes.ReplaceByNameAll, False, EDP.SendToLog)
|
||||
Destination.SaveSettings()
|
||||
End If
|
||||
End If
|
||||
|
||||
@@ -6,14 +6,24 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Namespace API.Instagram
|
||||
Friend Class EditorExchangeOptions
|
||||
<PSetting(Caption:="Get timeline", ToolTip:="Download user timeline")>
|
||||
Friend Property GetTimeline As Boolean
|
||||
<PSetting(Caption:="Get stories", ToolTip:="Download user stories")>
|
||||
Friend Property GetStories As Boolean
|
||||
<PSetting(Caption:="Get tagged posts", ToolTip:="Download user tagged posts")>
|
||||
Friend Property GetTagged As Boolean
|
||||
Friend Sub New(ByVal h As ISiteSettings)
|
||||
With DirectCast(h, SiteSettings)
|
||||
Friend Sub New(ByVal u As UserData)
|
||||
With u
|
||||
GetTimeline = .GetTimeline
|
||||
GetStories = .GetStories
|
||||
GetTagged = .GetTaggedData
|
||||
End With
|
||||
End Sub
|
||||
Friend Sub New(ByVal s As SiteSettings)
|
||||
With s
|
||||
GetTimeline = CBool(.GetTimeline.Value)
|
||||
GetStories = CBool(.GetStories.Value)
|
||||
GetTagged = CBool(.GetTagged.Value)
|
||||
|
||||
135
SCrawler/API/Instagram/OptionsForm.Designer.vb
generated
135
SCrawler/API/Instagram/OptionsForm.Designer.vb
generated
@@ -1,135 +0,0 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Instagram
|
||||
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||
Partial Friend Class OptionsForm : Inherits System.Windows.Forms.Form
|
||||
<System.Diagnostics.DebuggerNonUserCode()>
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
Try
|
||||
If disposing AndAlso components IsNot Nothing Then
|
||||
components.Dispose()
|
||||
End If
|
||||
Finally
|
||||
MyBase.Dispose(disposing)
|
||||
End Try
|
||||
End Sub
|
||||
Private components As System.ComponentModel.IContainer
|
||||
<System.Diagnostics.DebuggerStepThrough()>
|
||||
Private Sub InitializeComponent()
|
||||
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
|
||||
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
|
||||
Me.CH_GET_STORIES = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_GET_TAGGED = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_GET_TIMELINE = New System.Windows.Forms.CheckBox()
|
||||
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||
CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||
CONTAINER_MAIN.SuspendLayout()
|
||||
TP_MAIN.SuspendLayout()
|
||||
Me.SuspendLayout()
|
||||
'
|
||||
'CONTAINER_MAIN
|
||||
'
|
||||
'
|
||||
'CONTAINER_MAIN.ContentPanel
|
||||
'
|
||||
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
|
||||
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(260, 79)
|
||||
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||
CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Size = New System.Drawing.Size(260, 104)
|
||||
CONTAINER_MAIN.TabIndex = 0
|
||||
CONTAINER_MAIN.TopToolStripPanelVisible = False
|
||||
'
|
||||
'TP_MAIN
|
||||
'
|
||||
TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||
TP_MAIN.ColumnCount = 1
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Controls.Add(Me.CH_GET_STORIES, 0, 1)
|
||||
TP_MAIN.Controls.Add(Me.CH_GET_TAGGED, 0, 2)
|
||||
TP_MAIN.Controls.Add(Me.CH_GET_TIMELINE, 0, 0)
|
||||
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
TP_MAIN.Name = "TP_MAIN"
|
||||
TP_MAIN.RowCount = 4
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Size = New System.Drawing.Size(260, 79)
|
||||
TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'CH_GET_STORIES
|
||||
'
|
||||
Me.CH_GET_STORIES.AutoSize = True
|
||||
Me.CH_GET_STORIES.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_GET_STORIES.Location = New System.Drawing.Point(4, 30)
|
||||
Me.CH_GET_STORIES.Name = "CH_GET_STORIES"
|
||||
Me.CH_GET_STORIES.Size = New System.Drawing.Size(252, 19)
|
||||
Me.CH_GET_STORIES.TabIndex = 1
|
||||
Me.CH_GET_STORIES.Text = "Get stories"
|
||||
Me.CH_GET_STORIES.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_GET_TAGGED
|
||||
'
|
||||
Me.CH_GET_TAGGED.AutoSize = True
|
||||
Me.CH_GET_TAGGED.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_GET_TAGGED.Location = New System.Drawing.Point(4, 56)
|
||||
Me.CH_GET_TAGGED.Name = "CH_GET_TAGGED"
|
||||
Me.CH_GET_TAGGED.Size = New System.Drawing.Size(252, 19)
|
||||
Me.CH_GET_TAGGED.TabIndex = 2
|
||||
Me.CH_GET_TAGGED.Text = "Get tagged data"
|
||||
Me.CH_GET_TAGGED.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_GET_TIMELINE
|
||||
'
|
||||
Me.CH_GET_TIMELINE.AutoSize = True
|
||||
Me.CH_GET_TIMELINE.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_GET_TIMELINE.Location = New System.Drawing.Point(4, 4)
|
||||
Me.CH_GET_TIMELINE.Name = "CH_GET_TIMELINE"
|
||||
Me.CH_GET_TIMELINE.Size = New System.Drawing.Size(252, 19)
|
||||
Me.CH_GET_TIMELINE.TabIndex = 0
|
||||
Me.CH_GET_TIMELINE.Text = "Get Timeline"
|
||||
Me.CH_GET_TIMELINE.UseVisualStyleBackColor = True
|
||||
'
|
||||
'OptionsForm
|
||||
'
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(260, 104)
|
||||
Me.Controls.Add(CONTAINER_MAIN)
|
||||
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
||||
Me.Icon = Global.SCrawler.My.Resources.SiteResources.InstagramIcon_32
|
||||
Me.KeyPreview = True
|
||||
Me.MaximizeBox = False
|
||||
Me.MaximumSize = New System.Drawing.Size(276, 143)
|
||||
Me.MinimizeBox = False
|
||||
Me.MinimumSize = New System.Drawing.Size(276, 143)
|
||||
Me.Name = "OptionsForm"
|
||||
Me.ShowInTaskbar = False
|
||||
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
||||
Me.Text = "Options"
|
||||
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
CONTAINER_MAIN.ResumeLayout(False)
|
||||
CONTAINER_MAIN.PerformLayout()
|
||||
TP_MAIN.ResumeLayout(False)
|
||||
TP_MAIN.PerformLayout()
|
||||
Me.ResumeLayout(False)
|
||||
|
||||
End Sub
|
||||
|
||||
Private WithEvents CH_GET_STORIES As CheckBox
|
||||
Private WithEvents CH_GET_TAGGED As CheckBox
|
||||
Private WithEvents CH_GET_TIMELINE As CheckBox
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -1,40 +0,0 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Forms
|
||||
Namespace API.Instagram
|
||||
Friend Class OptionsForm
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Private ReadOnly Property MyExchangeOptions As EditorExchangeOptions
|
||||
Friend Sub New(ByRef ExchangeOptions As EditorExchangeOptions)
|
||||
InitializeComponent()
|
||||
MyExchangeOptions = ExchangeOptions
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
End Sub
|
||||
Private Sub OptionsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
.MyViewInitialize(True)
|
||||
.AddOkCancelToolbar()
|
||||
With MyExchangeOptions
|
||||
CH_GET_TIMELINE.Checked = .GetTimeline
|
||||
CH_GET_STORIES.Checked = .GetStories
|
||||
CH_GET_TAGGED.Checked = .GetTagged
|
||||
End With
|
||||
.EndLoaderOperations()
|
||||
End With
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
|
||||
With MyExchangeOptions
|
||||
.GetTimeline = CH_GET_TIMELINE.Checked
|
||||
.GetStories = CH_GET_STORIES.Checked
|
||||
.GetTagged = CH_GET_TAGGED.Checked
|
||||
End With
|
||||
MyDefs.CloseForm()
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -34,49 +34,39 @@ Namespace API.Instagram
|
||||
End Property
|
||||
#End Region
|
||||
#Region "Providers"
|
||||
Private Class TimersChecker : Implements IFieldsCheckerProvider
|
||||
Private Property ErrorMessage As String Implements IFieldsCheckerProvider.ErrorMessage
|
||||
Private Property Name As String Implements IFieldsCheckerProvider.Name
|
||||
Private Property TypeError As Boolean Implements IFieldsCheckerProvider.TypeError
|
||||
Private Class TimersChecker : Inherits FieldsCheckerProviderBase
|
||||
Private ReadOnly LVProvider As New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}
|
||||
Private ReadOnly _LowestValue As Integer
|
||||
Friend Sub New(ByVal LowestValue As Integer)
|
||||
_LowestValue = LowestValue
|
||||
End Sub
|
||||
Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
|
||||
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
||||
TypeError = False
|
||||
ErrorMessage = String.Empty
|
||||
If Not ACheck(Of Integer)(Value) Then
|
||||
TypeError = True
|
||||
ElseIf CInt(Value) < _LowestValue Then
|
||||
ErrorMessage = $"The value of [{Name}] field must be greater than or equal to {_LowestValue.NumToString(LVProvider)}"
|
||||
HasError = True
|
||||
Else
|
||||
Return Value
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
|
||||
Throw New NotImplementedException("[GetFormat] is not available in the context of [TimersChecker]")
|
||||
End Function
|
||||
End Class
|
||||
Private Class TaggedNotifyLimitChecker : Implements IFieldsCheckerProvider
|
||||
Private Property ErrorMessage As String Implements IFieldsCheckerProvider.ErrorMessage
|
||||
Private Property Name As String Implements IFieldsCheckerProvider.Name
|
||||
Private Property TypeError As Boolean Implements IFieldsCheckerProvider.TypeError
|
||||
Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
|
||||
Private Class TaggedNotifyLimitChecker : Inherits FieldsCheckerProviderBase
|
||||
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
||||
Dim v% = AConvert(Of Integer)(Value, -10)
|
||||
If v > 0 Or v = -1 Then
|
||||
Return Value
|
||||
Else
|
||||
ErrorMessage = $"The value of [{Name}] field must be greater than 0 or equal to -1"
|
||||
HasError = True
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
|
||||
Throw New NotImplementedException("[GetFormat] is not available in the context of [TaggedNotifyLimitChecker]")
|
||||
End Function
|
||||
End Class
|
||||
#End Region
|
||||
#Region "Authorization properties"
|
||||
@@ -270,17 +260,11 @@ Namespace API.Instagram
|
||||
Return False
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Plugin functions"
|
||||
#Region "GetInstance"
|
||||
Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider
|
||||
Select Case What
|
||||
Case Download.Main : Return New UserData
|
||||
Case Download.SavedPosts
|
||||
Dim u As New UserData
|
||||
DirectCast(u, UserDataBase).User = New UserInfo With {.Name = Site}
|
||||
Return u
|
||||
End Select
|
||||
Return Nothing
|
||||
Return New UserData
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Downloading"
|
||||
Friend Property SkipUntilNextSession As Boolean = False
|
||||
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
|
||||
@@ -328,13 +312,11 @@ Namespace API.Instagram
|
||||
SkipUntilNextSession = False
|
||||
End Sub
|
||||
#End Region
|
||||
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||
Return UserData.GetVideoInfo(URL, Responser)
|
||||
End Function
|
||||
#Region "UserOptions, GetUserPostUrl"
|
||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||
If Options Is Nothing OrElse Not TypeOf Options Is EditorExchangeOptions Then Options = New EditorExchangeOptions(Me)
|
||||
If OpenForm Then
|
||||
Using f As New OptionsForm(Options) : f.ShowDialog() : End Using
|
||||
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
|
||||
End If
|
||||
End Sub
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
@@ -342,7 +324,7 @@ Namespace API.Instagram
|
||||
Dim code$ = DirectCast(User, UserData).GetPostCodeById(Media.Post.ID)
|
||||
If Not code.IsEmptyString Then Return $"https://instagram.com/p/{code}/" Else Return String.Empty
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "Can't open user's post", String.Empty)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "Can't open user's post", String.Empty)
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
|
||||
@@ -8,13 +8,14 @@
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Net
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.XML.Base
|
||||
Imports PersonalUtilities.Functions.Messaging
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports SCrawler.API.Base
|
||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||
Namespace API.Instagram
|
||||
Friend Class UserData : Inherits UserDataBase
|
||||
@@ -77,7 +78,7 @@ Namespace API.Instagram
|
||||
#End Region
|
||||
#Region "Exchange options"
|
||||
Friend Overrides Function ExchangeOptionsGet() As Object
|
||||
Return New EditorExchangeOptions(HOST.Source) With {.GetTimeline = GetTimeline, .GetStories = GetStories, .GetTagged = GetTaggedData}
|
||||
Return New EditorExchangeOptions(Me)
|
||||
End Function
|
||||
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
|
||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptions Then
|
||||
@@ -139,7 +140,7 @@ Namespace API.Instagram
|
||||
x = New XmlFile With {.AllowSameNames = True}
|
||||
x.AddRange(PostsKVIDs)
|
||||
x.Name = "Posts"
|
||||
x.Save(f, EDP.SendInLog)
|
||||
x.Save(f, EDP.SendToLog)
|
||||
x.Dispose()
|
||||
End If
|
||||
End If
|
||||
@@ -182,7 +183,7 @@ Namespace API.Instagram
|
||||
End If
|
||||
Return String.Empty
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"{ToStringForLog()}: Cannot find post code by ID ({PostID})", String.Empty)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, $"{ToStringForLog()}: Cannot find post code by ID ({PostID})", String.Empty)
|
||||
End Try
|
||||
End Function
|
||||
Private Function GetPostIdBySection(ByVal ID As String, ByVal Section As Sections) As String
|
||||
@@ -621,7 +622,7 @@ Namespace API.Instagram
|
||||
PostsKVIDs.ListAddValue(PostIDKV, LNC)
|
||||
PostDate = .Value("taken_at")
|
||||
If Not IsSavedPosts Then
|
||||
Select Case CheckDatesLimit(PostDate, DateProvider)
|
||||
Select Case CheckDatesLimit(PostDate, UnixDate32Provider)
|
||||
Case DateResult.Skip : Continue For
|
||||
Case DateResult.Exit : If Not Pinned Then Return False
|
||||
End Select
|
||||
@@ -637,7 +638,7 @@ Namespace API.Instagram
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Code ID converters"
|
||||
Private Shared Function CodeToID(ByVal Code As String) As String
|
||||
Private Function CodeToID(ByVal Code As String) As String
|
||||
Const CodeSymbols$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
|
||||
Try
|
||||
If Not Code.IsEmptyString Then
|
||||
@@ -652,13 +653,13 @@ Namespace API.Instagram
|
||||
Return String.Empty
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.Instagram.UserData.CodeToID({Code})", String.Empty)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[API.Instagram.UserData.CodeToID({Code})", String.Empty)
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Obtain Media"
|
||||
Private Sub ObtainMedia(ByVal n As EContainer, ByVal PostID As String, Optional ByVal SpecialFolder As String = Nothing,
|
||||
Optional ByVal DateObj As String = Nothing)
|
||||
Optional ByVal DateObj As String = Nothing)
|
||||
Try
|
||||
Dim img As Predicate(Of EContainer) = Function(_img) Not _img.Name.IsEmptyString AndAlso _img.Name.StartsWith("image_versions") AndAlso _img.Count > 0
|
||||
Dim vid As Predicate(Of EContainer) = Function(_vid) Not _vid.Name.IsEmptyString AndAlso _vid.Name.StartsWith("video_versions") AndAlso _vid.Count > 0
|
||||
@@ -737,7 +738,7 @@ Namespace API.Instagram
|
||||
l.Clear()
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "API.Instagram.ObtainMedia2")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "API.Instagram.ObtainMedia2")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
@@ -898,38 +899,25 @@ Namespace API.Instagram
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Create media"
|
||||
Private Shared Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
|
||||
Optional ByVal SpecialFolder As String = Nothing) As UserMedia
|
||||
Private Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
|
||||
Optional ByVal SpecialFolder As String = Nothing) As UserMedia
|
||||
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
|
||||
Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID}}
|
||||
If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern))
|
||||
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, DateProvider, Nothing) Else m.Post.Date = Nothing
|
||||
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, UnixDate32Provider, Nothing) Else m.Post.Date = Nothing
|
||||
m.SpecialFolder = SpecialFolder
|
||||
Return m
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Standalone downloader"
|
||||
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal r As Responser) As IEnumerable(Of UserMedia)
|
||||
Try
|
||||
If Not URL.IsEmptyString AndAlso URL.Contains("instagram.com") Then
|
||||
Dim PID$ = RegexReplace(URL, RParams.DMS(".*?instagram.com/p/([_\w\d]+)", 1))
|
||||
If Not PID.IsEmptyString AndAlso Not ACheck(Of Long)(PID) Then PID = CodeToID(PID)
|
||||
If Not PID.IsEmptyString Then
|
||||
Using t As New UserData
|
||||
t.SetEnvironment(Settings(InstagramSiteKey), Nothing, False, False)
|
||||
t.Responser = New Responser
|
||||
t.Responser.Copy(r)
|
||||
t.PostsToReparse.Add(New PostKV With {.ID = PID})
|
||||
t.DownloadPosts(Nothing)
|
||||
Return ListAddList(Nothing, t._TempMediaList)
|
||||
End Using
|
||||
End If
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.ShowMainMsg + EDP.SendInLog, ex, $"Instagram standalone downloader: fetch media error ({URL})")
|
||||
End Try
|
||||
End Function
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
Dim PID$ = RegexReplace(Data.URL, RParams.DMS(".*?instagram.com/p/([_\w\d]+)", 1))
|
||||
If Not PID.IsEmptyString AndAlso Not ACheck(Of Long)(PID) Then PID = CodeToID(PID)
|
||||
If Not PID.IsEmptyString Then
|
||||
PostsToReparse.Add(New PostKV With {.ID = PID})
|
||||
DownloadPosts(Token)
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "IDisposable Support"
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
|
||||
@@ -10,28 +10,41 @@ Imports SCrawler.API.Base
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.LPSG
|
||||
Friend Module Declarations
|
||||
Friend ReadOnly Property PhotoRegEx As RParams = RParams.DM("(https://www.lpsg.com/attachments)(.+?)(?="")", 0, RegexReturn.List)
|
||||
Friend ReadOnly Property PhotoRegEx As RParams =
|
||||
RParams.DM("(?<=(https://www.lpsg.com|)/attachments/)([^/]+?[-\.]{1}(jpg|jpeg|gif|png|webm)\.?\d*)(?=/?"")", 0, RegexReturn.List,
|
||||
CType(Function(Input$) If(Input.IsEmptyString, String.Empty, $"https://www.lpsg.com/attachments/{Input.StringTrimStart("/")}"),
|
||||
Func(Of String, String)))
|
||||
Friend ReadOnly Property PhotoRegExExt As New RParams("img.data.src=""(/proxy[^""]+?)""", Nothing, 1, RegexReturn.List) With {
|
||||
.Converter = Function(Input) $"https://www.lpsg.com/{SymbolsConverter.HTML.Decode(Input)}"}
|
||||
.Converter = Function(Input) $"https://www.lpsg.com/{SymbolsConverter.HTML.Decode(Input)}"}
|
||||
Friend ReadOnly Property NextPageRegex As RParams = RParams.DMS("<link rel=""next"" href=""(.+?/page-(\d+))""", 2)
|
||||
Private Const FileUrlRegexDefault As String = "([^/]+?)(jpg|jpeg|gif|png|webm)"
|
||||
Private ReadOnly InputFReplacer As New ErrorsDescriber(EDP.ReturnValue)
|
||||
Private ReadOnly InputForbidRemover As Func(Of String, String) = Function(Input) If(Input.IsEmptyString, Input, Input.StringRemoveWinForbiddenSymbols(, InputFReplacer))
|
||||
Friend ReadOnly Property FileRegEx As New RParams(FileUrlRegexDefault, Nothing, 0) With {
|
||||
.Converter = Function(ByVal Input As String) As String
|
||||
Input = InputForbidRemover.Invoke(Input)
|
||||
If Not Input.IsEmptyString Then
|
||||
Dim lv$ = Input.Split("-").LastOrDefault
|
||||
If Not lv.IsEmptyString Then
|
||||
Input = Input.Replace($"-{lv}", String.Empty)
|
||||
Input &= $".{lv}"
|
||||
End If
|
||||
End If
|
||||
Return Input
|
||||
End Function}
|
||||
Friend ReadOnly Property FileRegExExt As New RParams(FileUrlRegexDefault, 0, Nothing, InputForbidRemover)
|
||||
Friend ReadOnly Property FileRegExExt2 As New RParams("([^/]+?)(?=(\Z|&))", 0, Nothing, InputForbidRemover)
|
||||
Private ReadOnly FileRegEx As RParams = RParams.DMS(FileUrlRegexDefault, 0, RegexReturn.ListByMatch, InputFReplacer)
|
||||
#Disable Warning IDE0060
|
||||
Friend Function FileRegExF(ByVal Input As String, ByVal Index As Integer) As String
|
||||
#Enable Warning
|
||||
If Not Input.IsEmptyString Then
|
||||
Dim l As List(Of String) = RegexReplace(Input, FileRegEx)
|
||||
If l.ListExists(3) Then
|
||||
Dim ext$ = l(2)
|
||||
Dim f$ = l(1).StringTrim("-", ".")
|
||||
Input = $"{f}.{ext}"
|
||||
End If
|
||||
End If
|
||||
Return Input
|
||||
End Function
|
||||
Private ReadOnly FileRegExExt As RParams = RParams.DM(FileUrlRegexDefault, 0, InputForbidRemover, InputFReplacer)
|
||||
Private ReadOnly FileRegExExt2 As RParams = RParams.DM("([^/]+?)(?=(\Z|&))", 0, InputForbidRemover, InputFReplacer)
|
||||
Friend Function FileRegExExtF(ByVal Input As String, ByVal Index As Integer) As String
|
||||
If Index = 0 Then
|
||||
Return RegexReplace(Input, FileRegExExt)
|
||||
Else
|
||||
Return RegexReplace(Input, FileRegExExt2)
|
||||
End If
|
||||
End Function
|
||||
Friend ReadOnly Property FileExistsRegEx As RParams = RParams.DMS(FileUrlRegexDefault, 2)
|
||||
Friend ReadOnly Property TempListAddParams As New ListAddParams(LAP.NotContainsOnly) With {.Comparer = New FComparer(Of UserMedia)(Function(x, y) x.URL = y.URL)}
|
||||
Friend ReadOnly Property ContentTitleRegEx As RParams = RParams.DMS("meta property=.og:title..content=""([^""]+)""", 1, InputFReplacer)
|
||||
End Module
|
||||
End Namespace
|
||||
@@ -25,7 +25,7 @@ Namespace API.LPSG
|
||||
Friend Sub New()
|
||||
MyBase.New("LPSG", "www.lpsg.com")
|
||||
UrlPatternUser = "https://www.lpsg.com/threads/{0}/"
|
||||
UserRegex = RParams.DMS(".+?lpsg.com/threads/([^/]+)", 1)
|
||||
UserRegex = RParams.DMS(".+?lpsg.com/threads/[^/]+?\.(\d+)", 1, EDP.ReturnValue)
|
||||
End Sub
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
Return New UserData
|
||||
|
||||
@@ -32,6 +32,7 @@ Namespace API.LPSG
|
||||
|
||||
Dim NextPage$
|
||||
Dim r$
|
||||
Dim titleChecked As Boolean = False
|
||||
Dim _LPage As Func(Of String) = Function() If(LatestPage.IsEmptyString, String.Empty, $"page-{LatestPage}")
|
||||
|
||||
Do
|
||||
@@ -41,6 +42,14 @@ Namespace API.LPSG
|
||||
UserSuspended = False
|
||||
ThrowAny(Token)
|
||||
If Not r.IsEmptyString Then
|
||||
If UserSiteName.IsEmptyString And Not titleChecked Then
|
||||
UserSiteName = RegexReplace(r, ContentTitleRegEx)
|
||||
If Not UserSiteName.IsEmptyString Then
|
||||
_ForceSaveUserInfo = True
|
||||
If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName
|
||||
End If
|
||||
End If
|
||||
titleChecked = True
|
||||
NextPage = RegexReplace(r, NextPageRegex)
|
||||
UpdateMediaList(RegexReplace(r, PhotoRegEx), Mode.Internal)
|
||||
UpdateMediaList(RegexReplace(r, PhotoRegExExt), Mode.External)
|
||||
@@ -61,23 +70,24 @@ Namespace API.LPSG
|
||||
Dim f As SFile
|
||||
Dim u$
|
||||
Dim exists As Boolean
|
||||
Dim r As RParams
|
||||
Dim r As Func(Of String, Integer, String)
|
||||
Dim indx% = 0
|
||||
Dim ude As New ErrorsDescriber(EDP.ReturnValue)
|
||||
For Each url$ In l
|
||||
If Not url.IsEmptyString Then u = SymbolsConverter.Decode(url, {Converters.HTML, Converters.ASCII}, ude) Else u = String.Empty
|
||||
If Not u.IsEmptyString Then
|
||||
exists = Not IsEmptyString(RegexReplace(u, FileExistsRegEx))
|
||||
If m = Mode.Internal Then
|
||||
r = FileRegEx
|
||||
r = AddressOf FileRegExF
|
||||
Else
|
||||
r = FileRegExExt
|
||||
r = AddressOf FileRegExExtF
|
||||
If Not exists Then
|
||||
r = FileRegExExt2
|
||||
exists = Not IsEmptyString(RegexReplace(u, FileRegExExt2))
|
||||
indx = 1
|
||||
exists = Not IsEmptyString(FileRegExExtF(u, 1))
|
||||
End If
|
||||
End If
|
||||
If exists Then
|
||||
f = CStr(RegexReplace(u, r))
|
||||
f = r.Invoke(u, indx)
|
||||
f.Path = MyFile.CutPath.PathNoSeparator
|
||||
f.Separator = "\"
|
||||
If f.Extension.IsEmptyString Then f.Extension = "jpg"
|
||||
|
||||
48
SCrawler/API/Mastodon/Credentials.vb
Normal file
48
SCrawler/API/Mastodon/Credentials.vb
Normal file
@@ -0,0 +1,48 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.XML.Base
|
||||
Namespace API.Mastodon
|
||||
Friend Structure Credentials : Implements IEContainerProvider
|
||||
Private Const Name_Bearer As String = "Bearer"
|
||||
Private Const Name_Csrf As String = "Csrf"
|
||||
Friend Domain As String
|
||||
Friend Bearer As String
|
||||
Friend Csrf As String
|
||||
Friend ReadOnly Property Exists As Boolean
|
||||
Get
|
||||
Return Not Domain.IsEmptyString And Not Bearer.IsEmptyString And Not Csrf.IsEmptyString
|
||||
End Get
|
||||
End Property
|
||||
Private Sub New(ByVal e As EContainer)
|
||||
Domain = e.Value
|
||||
Bearer = e.Attribute(Name_Bearer)
|
||||
Csrf = e.Attribute(Name_Csrf)
|
||||
End Sub
|
||||
Public Shared Widening Operator CType(ByVal e As EContainer) As Credentials
|
||||
Return New Credentials(e)
|
||||
End Operator
|
||||
Public Shared Widening Operator CType(ByVal Domain As String) As Credentials
|
||||
Return New Credentials With {.Domain = Domain}
|
||||
End Operator
|
||||
Private Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer
|
||||
Return New EContainer("Item", Domain, {New EAttribute(Name_Bearer, Bearer), New EAttribute(Name_Csrf, Csrf)})
|
||||
End Function
|
||||
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
|
||||
If Not IsNothing(Obj) Then
|
||||
If TypeOf Obj Is Credentials Then
|
||||
Return Domain = DirectCast(Obj, Credentials).Domain
|
||||
Else
|
||||
Return Domain = CStr(Obj)
|
||||
End If
|
||||
End If
|
||||
Return False
|
||||
End Function
|
||||
End Structure
|
||||
End Namespace
|
||||
14
SCrawler/API/Mastodon/Declarations.vb
Normal file
14
SCrawler/API/Mastodon/Declarations.vb
Normal file
@@ -0,0 +1,14 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Mastodon
|
||||
Friend Module Declarations
|
||||
Friend Const MastodonSiteKey As String = "AndyProgram_Mastodon"
|
||||
Friend ReadOnly DateProvider As New ADateTime("O")
|
||||
End Module
|
||||
End Namespace
|
||||
65
SCrawler/API/Mastodon/MastodonDomains.vb
Normal file
65
SCrawler/API/Mastodon/MastodonDomains.vb
Normal file
@@ -0,0 +1,65 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.API.Base
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Namespace API.Mastodon
|
||||
Friend Class MastodonDomains : Inherits DomainsContainer
|
||||
Friend ReadOnly Property Credentials As List(Of Credentials)
|
||||
Friend ReadOnly Property CredentialsTemp As List(Of Credentials)
|
||||
Private ReadOnly CredentialsFile As SFile = $"{SettingsFolderName}\Responser_Mastodon_DomainsCredentials.xml"
|
||||
Friend Sub New(ByVal _Instance As ISiteSettings, ByVal DefaultValue As String)
|
||||
MyBase.New(_Instance, DefaultValue)
|
||||
Credentials = New List(Of Credentials)
|
||||
CredentialsTemp = New List(Of Credentials)
|
||||
If CredentialsFile.Exists Then
|
||||
Using x As New XmlFile(CredentialsFile,, False) With {.AllowSameNames = True, .XmlReadOnly = True}
|
||||
x.LoadData()
|
||||
If x.Count > 0 Then Credentials.ListAddList(x, LAP.IgnoreICopier)
|
||||
End Using
|
||||
End If
|
||||
End Sub
|
||||
Friend Overrides Function Apply() As Boolean
|
||||
If Changed Then
|
||||
Credentials.Clear()
|
||||
If CredentialsTemp.Count > 0 Then Credentials.AddRange(CredentialsTemp)
|
||||
CredentialsTemp.Clear()
|
||||
End If
|
||||
Return MyBase.Apply()
|
||||
End Function
|
||||
Friend Overrides Sub Save()
|
||||
If Credentials.Count > 0 Then
|
||||
Using x As New XmlFile With {.AllowSameNames = True}
|
||||
x.AddRange(Credentials)
|
||||
x.Name = "DomainsCredentials"
|
||||
x.Save(CredentialsFile)
|
||||
End Using
|
||||
Else
|
||||
CredentialsFile.Delete(,, EDP.None)
|
||||
End If
|
||||
MyBase.Save()
|
||||
End Sub
|
||||
Friend Overrides Sub Reset()
|
||||
CredentialsTemp.Clear()
|
||||
MyBase.Reset()
|
||||
End Sub
|
||||
Friend Overrides Sub OpenSettingsForm()
|
||||
Using f As New SettingsForm(Instance)
|
||||
f.ShowDialog()
|
||||
If f.DialogResult = DialogResult.OK Then
|
||||
Changed = True
|
||||
CredentialsTemp.Clear()
|
||||
If f.MyCredentials.Count > 0 Then CredentialsTemp.AddRange(f.MyCredentials)
|
||||
DomainsTemp.Clear()
|
||||
If f.MyDomains.Count > 0 Then DomainsTemp.ListAddList(f.MyDomains, LAP.NotContainsOnly)
|
||||
End If
|
||||
End Using
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
165
SCrawler/API/Mastodon/SettingsForm.Designer.vb
generated
Normal file
165
SCrawler/API/Mastodon/SettingsForm.Designer.vb
generated
Normal file
@@ -0,0 +1,165 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Mastodon
|
||||
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||
Partial Friend Class SettingsForm : Inherits System.Windows.Forms.Form
|
||||
<System.Diagnostics.DebuggerNonUserCode()>
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
Try
|
||||
If disposing AndAlso components IsNot Nothing Then
|
||||
components.Dispose()
|
||||
End If
|
||||
Finally
|
||||
MyBase.Dispose(disposing)
|
||||
End Try
|
||||
End Sub
|
||||
Private components As System.ComponentModel.IContainer
|
||||
<System.Diagnostics.DebuggerStepThrough()>
|
||||
Private Sub InitializeComponent()
|
||||
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
|
||||
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
|
||||
Dim ActionButton1 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(SettingsForm))
|
||||
Dim ActionButton2 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton5 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim ActionButton6 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Me.CMB_DOMAINS = New PersonalUtilities.Forms.Controls.ComboBoxExtended()
|
||||
Me.TXT_AUTH = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
Me.TXT_TOKEN = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||
CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||
CONTAINER_MAIN.SuspendLayout()
|
||||
TP_MAIN.SuspendLayout()
|
||||
CType(Me.CMB_DOMAINS, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
CType(Me.TXT_AUTH, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
CType(Me.TXT_TOKEN, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
Me.SuspendLayout()
|
||||
'
|
||||
'CONTAINER_MAIN
|
||||
'
|
||||
'
|
||||
'CONTAINER_MAIN.ContentPanel
|
||||
'
|
||||
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
|
||||
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(384, 361)
|
||||
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||
CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Size = New System.Drawing.Size(384, 361)
|
||||
CONTAINER_MAIN.TabIndex = 0
|
||||
CONTAINER_MAIN.TopToolStripPanelVisible = False
|
||||
'
|
||||
'TP_MAIN
|
||||
'
|
||||
TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||
TP_MAIN.ColumnCount = 1
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
|
||||
TP_MAIN.Controls.Add(Me.CMB_DOMAINS, 0, 0)
|
||||
TP_MAIN.Controls.Add(Me.TXT_AUTH, 0, 1)
|
||||
TP_MAIN.Controls.Add(Me.TXT_TOKEN, 0, 2)
|
||||
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
TP_MAIN.Name = "TP_MAIN"
|
||||
TP_MAIN.RowCount = 3
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||
TP_MAIN.Size = New System.Drawing.Size(384, 361)
|
||||
TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'CMB_DOMAINS
|
||||
'
|
||||
ActionButton1.BackgroundImage = CType(resources.GetObject("ActionButton1.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton1.Name = "Add"
|
||||
ActionButton1.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Add
|
||||
ActionButton2.BackgroundImage = CType(resources.GetObject("ActionButton2.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton2.Name = "Delete"
|
||||
ActionButton2.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Delete
|
||||
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton3.Name = "Clear"
|
||||
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton4.Name = "ArrowDown"
|
||||
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.ArrowDown
|
||||
ActionButton4.Visible = False
|
||||
Me.CMB_DOMAINS.Buttons.Add(ActionButton1)
|
||||
Me.CMB_DOMAINS.Buttons.Add(ActionButton2)
|
||||
Me.CMB_DOMAINS.Buttons.Add(ActionButton3)
|
||||
Me.CMB_DOMAINS.Buttons.Add(ActionButton4)
|
||||
Me.CMB_DOMAINS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CMB_DOMAINS.ListDropDownStyle = PersonalUtilities.Forms.Controls.ComboBoxExtended.ListMode.Simple
|
||||
Me.CMB_DOMAINS.Location = New System.Drawing.Point(4, 4)
|
||||
Me.CMB_DOMAINS.Name = "CMB_DOMAINS"
|
||||
Me.CMB_DOMAINS.Size = New System.Drawing.Size(378, 296)
|
||||
Me.CMB_DOMAINS.TabIndex = 0
|
||||
'
|
||||
'TXT_AUTH
|
||||
'
|
||||
ActionButton5.BackgroundImage = CType(resources.GetObject("ActionButton5.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton5.Name = "Clear"
|
||||
ActionButton5.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
Me.TXT_AUTH.Buttons.Add(ActionButton5)
|
||||
Me.TXT_AUTH.CaptionText = "Auth"
|
||||
Me.TXT_AUTH.CaptionToolTipEnabled = True
|
||||
Me.TXT_AUTH.CaptionToolTipText = "Bearer token"
|
||||
Me.TXT_AUTH.CaptionWidth = 50.0R
|
||||
Me.TXT_AUTH.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_AUTH.Location = New System.Drawing.Point(4, 306)
|
||||
Me.TXT_AUTH.Name = "TXT_AUTH"
|
||||
Me.TXT_AUTH.Size = New System.Drawing.Size(376, 22)
|
||||
Me.TXT_AUTH.TabIndex = 1
|
||||
'
|
||||
'TXT_TOKEN
|
||||
'
|
||||
ActionButton6.BackgroundImage = CType(resources.GetObject("ActionButton6.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton6.Name = "Clear"
|
||||
ActionButton6.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
Me.TXT_TOKEN.Buttons.Add(ActionButton6)
|
||||
Me.TXT_TOKEN.CaptionText = "Token"
|
||||
Me.TXT_TOKEN.CaptionToolTipEnabled = True
|
||||
Me.TXT_TOKEN.CaptionToolTipText = "csrf token"
|
||||
Me.TXT_TOKEN.CaptionWidth = 50.0R
|
||||
Me.TXT_TOKEN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_TOKEN.Location = New System.Drawing.Point(4, 335)
|
||||
Me.TXT_TOKEN.Name = "TXT_TOKEN"
|
||||
Me.TXT_TOKEN.Size = New System.Drawing.Size(376, 22)
|
||||
Me.TXT_TOKEN.TabIndex = 2
|
||||
'
|
||||
'SettingsForm
|
||||
'
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(384, 361)
|
||||
Me.Controls.Add(CONTAINER_MAIN)
|
||||
Me.Icon = Global.SCrawler.My.Resources.SiteResources.MastodonIcon_48
|
||||
Me.MinimumSize = New System.Drawing.Size(400, 400)
|
||||
Me.Name = "SettingsForm"
|
||||
Me.ShowInTaskbar = False
|
||||
Me.Text = "Mastodon domains"
|
||||
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
CONTAINER_MAIN.ResumeLayout(False)
|
||||
CONTAINER_MAIN.PerformLayout()
|
||||
TP_MAIN.ResumeLayout(False)
|
||||
CType(Me.CMB_DOMAINS, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
CType(Me.TXT_AUTH, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
CType(Me.TXT_TOKEN, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
Me.ResumeLayout(False)
|
||||
|
||||
End Sub
|
||||
Private WithEvents CMB_DOMAINS As PersonalUtilities.Forms.Controls.ComboBoxExtended
|
||||
Private WithEvents TXT_AUTH As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
Private WithEvents TXT_TOKEN As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
End Class
|
||||
End Namespace
|
||||
292
SCrawler/API/Mastodon/SettingsForm.resx
Normal file
292
SCrawler/API/Mastodon/SettingsForm.resx
Normal file
@@ -0,0 +1,292 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<root>
|
||||
<!--
|
||||
Microsoft ResX Schema
|
||||
|
||||
Version 2.0
|
||||
|
||||
The primary goals of this format is to allow a simple XML format
|
||||
that is mostly human readable. The generation and parsing of the
|
||||
various data types are done through the TypeConverter classes
|
||||
associated with the data types.
|
||||
|
||||
Example:
|
||||
|
||||
... ado.net/XML headers & schema ...
|
||||
<resheader name="resmimetype">text/microsoft-resx</resheader>
|
||||
<resheader name="version">2.0</resheader>
|
||||
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
|
||||
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
|
||||
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
|
||||
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
|
||||
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
|
||||
<value>[base64 mime encoded serialized .NET Framework object]</value>
|
||||
</data>
|
||||
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
|
||||
<comment>This is a comment</comment>
|
||||
</data>
|
||||
|
||||
There are any number of "resheader" rows that contain simple
|
||||
name/value pairs.
|
||||
|
||||
Each data row contains a name, and value. The row also contains a
|
||||
type or mimetype. Type corresponds to a .NET class that support
|
||||
text/value conversion through the TypeConverter architecture.
|
||||
Classes that don't support this are serialized and stored with the
|
||||
mimetype set.
|
||||
|
||||
The mimetype is used for serialized objects, and tells the
|
||||
ResXResourceReader how to depersist the object. This is currently not
|
||||
extensible. For a given mimetype the value must be set accordingly:
|
||||
|
||||
Note - application/x-microsoft.net.object.binary.base64 is the format
|
||||
that the ResXResourceWriter will generate, however the reader can
|
||||
read any of the formats listed below.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.binary.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.soap.base64
|
||||
value : The object must be serialized with
|
||||
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
|
||||
: and then encoded with base64 encoding.
|
||||
|
||||
mimetype: application/x-microsoft.net.object.bytearray.base64
|
||||
value : The object must be serialized into a byte array
|
||||
: using a System.ComponentModel.TypeConverter
|
||||
: and then encoded with base64 encoding.
|
||||
-->
|
||||
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
|
||||
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
|
||||
<xsd:element name="root" msdata:IsDataSet="true">
|
||||
<xsd:complexType>
|
||||
<xsd:choice maxOccurs="unbounded">
|
||||
<xsd:element name="metadata">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" use="required" type="xsd:string" />
|
||||
<xsd:attribute name="type" type="xsd:string" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="assembly">
|
||||
<xsd:complexType>
|
||||
<xsd:attribute name="alias" type="xsd:string" />
|
||||
<xsd:attribute name="name" type="xsd:string" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="data">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
|
||||
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
|
||||
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
|
||||
<xsd:attribute ref="xml:space" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
<xsd:element name="resheader">
|
||||
<xsd:complexType>
|
||||
<xsd:sequence>
|
||||
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
|
||||
</xsd:sequence>
|
||||
<xsd:attribute name="name" type="xsd:string" use="required" />
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:choice>
|
||||
</xsd:complexType>
|
||||
</xsd:element>
|
||||
</xsd:schema>
|
||||
<resheader name="resmimetype">
|
||||
<value>text/microsoft-resx</value>
|
||||
</resheader>
|
||||
<resheader name="version">
|
||||
<value>2.0</value>
|
||||
</resheader>
|
||||
<resheader name="reader">
|
||||
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<resheader name="writer">
|
||||
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
|
||||
</resheader>
|
||||
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
|
||||
<value>False</value>
|
||||
</metadata>
|
||||
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
|
||||
<data name="ActionButton1.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGOfPtRkwAAACBjSFJNAAB6
|
||||
JQAAgIMAAPn/AACA6QAAdTAAAOpgAAA6mAAAF2+SX8VGAAAACXBIWXMAAAsTAAALEwEAmpwYAAADmUlE
|
||||
QVRIS62WWWxMURjHL220JW1HausmlFrDFKUhnUGH6bRFzJ2idImlC0Vp2mlji1A8iNhCPIjIRES8EU+W
|
||||
h2oEtbSDTk3HNNM7S01VKsXjkb/vXBo3k1Ee7sMvmZzzzf//ne/+z50RAAxL1MUIG4G/YAv3HSVhF5Vw
|
||||
IYNdz3LadVj9RgdTB+HQYYPHIJuE1ocSdlEJFzG+1bPRLQLinglIeCkg+XUkKvz56hnkOfQs/rmA8S9H
|
||||
YEp7FDI64tAQtKhnsMapZ7zzNHsUFnbGY4VzIk70l6hnIH4wsDR7NBZ3apDrSqL5T8eFgUr1DLZ78lim
|
||||
Q4N8VzK29MxEpZSBa4M16hnU+c3M9CEFpdJsVHsXos63DDcHrf9nQEXD5VymwW/5USLNwl5vJhp7dTgW
|
||||
NML2pR7jbsUMS+KdMTa5Q8NQxinfBU4dRFcOyjy52OtbhwOBDTgZLKPPmTgY0ON4MBdNfSbYBupxY8Aq
|
||||
G10dqMG5/nIc7ytGQ6CQRliAamkTN/g1Ai4e95Qy3iogpX0UtBRDnhRzdxq2SXOxz5eFQ70rScCEU335
|
||||
ssGxj0YS06HSm4GN3ekwdE2C1hGH1LZR0JDOJof5jwHvnIvzTa0jlooTYfktvt+fhcOBHDQFTWRgxJGP
|
||||
ObAGsulZLMLWnjlY756K5c4JmNcRi6T2SGheCIihS2l5ozAo6NRhMolnUAcGV6IcwwqvFrX+JTjYuwKH
|
||||
SfRAYDms/mzs9y1GFe2VSnOw1j0FejqpLN4WCX4ZufiIBwLMLxQGm12rsLQzgWKYgmLPLNTQw6ynpDSS
|
||||
IBet8y+TqaVRVdFIeJrWuCcj+/0EzH43BomvIhBLI45uFiDcJ+6QwROFwa6+Amb9bGFNg6Xs9Ncd7Oy3
|
||||
Knb2eyU7/20nu9y/m136tIvEl6BC0qKoZwby3alo9JVhj7T5R7m/kJVIIityi8zyXmTiW+I10SqyIQNb
|
||||
uIgNwYuuf25kFd75KPKkI49OmUWnrfYWyXv/wBb2cijhhVf6a9lGei65XclYRDd6mj0GWz2iLBJaH0rY
|
||||
RSVc5Eywmhm7kuQXHX+bJlBStrh+zTi0PpSwi0q4yNFAOVvgiEcKJWUsxZn/NhT+znlofShhF5VwkRpv
|
||||
MUtti4KGYjj6sYCIh5QSu4oG27stjItHU+cjeQzvkcFzFQ2KnSKLoc4FukDCXeI2GbSoaFD4ziyPxNxK
|
||||
0AUyNxOP1DOwcaG/8I+/LRB+At7psBnyDBG0AAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="ActionButton2.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABgAAAAYCAYAAADgdz34AAAABGdBTUEAALGPC/xhBQAAABl0RVh0U29m
|
||||
dHdhcmUAQWRvYmUgSW1hZ2VSZWFkeXHJZTwAAAVoSURBVEhLhZVrTJNXGMdfrtNSQIoadKRz2o0CorU3
|
||||
WkDIVBRaaGNbwAteh+AARRQlitEYTTRekiX7sH3YPmyZH9wtziybigLRCWTaCW5sCBWhlrb0Ci9zSxbo
|
||||
2f+UliGX7SS/tO85z/k9T57zXhhCCPO7Wh3VIhB83JKQ0Nu4bNlHm5YseZ1hmHC69n+Y5HLFcz7/ft/S
|
||||
pY+vr1hhwL4oEBJcZ0x793If5uZ+1VNfT/qvXCHP6+p8tzMymqRxcW8hMGKqbDo9MlmWddu2AfbiRTJ6
|
||||
+TIZKC52fyAUVi2JiYkLJmGaBYIPnx4+TPrOnCH9p08TC4LNx46RWwrF/ZXR0W/PleRZZuY669atZvbS
|
||||
JcJiL9vQQEZPnSKmwkLPjcTE97GPB8KZlvh4C5X31dWRgRMniAVBtvPnyWB9ve+2XP7jmtjYpOlJTOnp
|
||||
G60lJRZaOZWPQs4ePUpGUZh3xw7SnJDQhT0KEM3c5fOv9paVkX4kMAPL8ePEig1D584RG9rVpFS2rY6J
|
||||
EQaTmKTSjbbiYsvIhQuERTGjKIrFvtHaWjK8fz9plsudexYu/BLxKsBj9ALBGzel0vt9e/b4XiBoENhQ
|
||||
zRDOxIWWOY4cIS0KRZs4Nja5QyLJtRoM1pGzZ/0tYVExi/ayNTVkBPJ76enuJA7nM4j3gVWAHjgTIYqL
|
||||
E96SStvMu3YR64EDxF5dTYYOHSJOJPNA5Kiu9rUrlZ1mrdbCnjzpr5jFGotYtqpqQi6TuVM4nKvwlYHU
|
||||
gDzU31OMSGl8fPJtsbjVsn27z15RQRzAVVlJ3BB4kcx78CAZQbUjVIxrFtd+OdrbmpHhEXG5VE4rTwHz
|
||||
wMRdFDw4jEgFj5dyRyRqsxYVEcfu3cQFPPv2ES8qHEbCYRzgsFZLvO+8Q7xKJXGDVoXCK46Ovob95YBW
|
||||
Ph/8+xwE/wSTyHi81OZVq9qsGs2Ye8sW4srPJy6JhDgTE4kzOpo4IyKIMyyMOLhcX9Py5R4lj0cPtAKs
|
||||
BBwwKfc7p174J5BEhHY9FIk6bBDaIRuiQkDFfsLDSbdU+pdBKPwe8e+BNDBD7vdNn6BYd+6stK5da7bP
|
||||
nz9TDujcoEAw1lJY+CyFz9dCHDubnDJjwltRccS5fr3TjurnlIMBYE5NJY8Nhq7SrCwREsz6xL9y4S4v
|
||||
b3Bt2uSyR0XNkDvQe9ouKu8HvaGh5FfQIxL5OgyG30qUStqmGUkm/3jKy0+48vLcs1XuiI8nL/Ly/rYl
|
||||
JfmovCcgN4JW+l8iGe8oKuoqzcyckSQob3CpVB47l+sXv9KWxYtJt0r1x9ns7HZjQYHNnJxMfoH0EXgA
|
||||
7oFm0CmTjRsNhs6Na9bQF+Tkq57xlJXVu9Rqz9Bs8kWLSG9BwcsqieQONlXnpaaWdul0z7rR+6C8CTSC
|
||||
m8Aol4+36/XGT7VaevCRIIRx6/WWoQULZq2cyveLxY0IrAT0IHm1OTmZT3Q6U2da2qT8B/Ad+BZ05OSM
|
||||
GXW6p4hdBiIZZ1FRt5vPn6vyuwiqCsj9Xyq6qXbDBkWnXm/6OS3NN1X+dUgIeZSdPXZPoxlEXC6IY9pL
|
||||
S7faNBqXC9Iplf95YBb5ZF+RpGbdunQcbO/D1avJ9YC8LT19/Iv8/BeqpKRPEDORAGNeY3HxSYtG43Eq
|
||||
FL5etfpljUzWhPlZ5VOTlGVliR+hHUbs+0mpHP9GpRqM5XAuY20zmGgRRohYKIx9rNd/3qfTOa7l5uLu
|
||||
C63BvARw6fp0eRCMyBslJe8+2bx58EFhoVMlFNJvgQ4kgggQEgykvV0ApEAd+J3z8Z8KxmuA3pr0zikA
|
||||
b4LJZ2FqYBigFdOPNf0NC679Fxi0OPr+XxiAJgwURph/AJfOQQebMR8TAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="ActionButton3.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="ActionButton4.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAAgAAAAIACAYAAAD0eNT6AAAABGdBTUEAALGPC/xhBQAAE65JREFUeF7t
|
||||
3X2sJWddB/DdLi2lQG2hdOHuvfM887J7Cxca4ELTQMDWKigIFpBAEAgi9g+CJpJo9Q8NJhgBiYZIYspL
|
||||
GlAKCkhEC4KgQlsLQkqhKi/lrYWWlxaw3dLddrerz/Q89+7dc2fbfTn3npf5fJJv2rS758z85nnOzJz5
|
||||
nZktAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAMK3O3r79wVUIz65jfGNVxI/VIX69CvGO9M//a9P+e8o3B/8v
|
||||
vKn9s+3fyX8dAJgmaWd+fl3E96Wd/E9XdvZHkfbvXNa+Rn45AGCS3bvjj/E/h3box5OrmxjPyy8PAEyS
|
||||
XXO7zqhCeH/HDnwUOdCE+J6zdux4eH47YIrEGE8uy/Ls9Bnx/LooL0oH9b9Th/I1TVG+rCqKC+q6Xsh/
|
||||
FJgmO8vy6WknfdPQTnsjckMdwlPy2wITLO3wF6si/lGas1ekuXvX0Fzuyg9S3psOCl6qDwimQB3ji9Ok
|
||||
3btmEm907kpnEa/Mbw9Mlq1pB/6cdHZ/ZcfcPZrcXoXyrVVVFfl1gUmSdsS/libqPUMTd5NSvjktwrbB
|
||||
kgDjVi1UT26K+Nnu+XrMuaud60uPWHpIfhtg3JqyfEaanHcPTdZNTRPCPy4uLj40LxIwBudt2fKAtOP/
|
||||
0zQnN+5koIg3tpca81sC49J+LZcm5a3rJulYEq6LSV40YBOFEB6V5uFV6+flRiTsSwf9r81vDYzBCSO4
|
||||
vjfq/KAuiqfm5QM2QRPjuWnubUbz71DCn6W33zpYCmDT1EX5m92Tcuy5q47xFXkxgQ3UduqnOXfn0Bzc
|
||||
xJSvz4sCbIb2pzlp8v1w/WScnKSzkjekRT1hsMTAKC0vL5/Ydud3zb1NT1FelBcL2GiDm3d0TMTJy0ea
|
||||
pjk1LzYwAu3NvtLc+uTQXBtn7tYYCJtja/vQno5JOJFpQrzWb4hhNJoQnpjm1Q3D82wCcnNRFKfnxQQ2
|
||||
Qttk1zH5JjzhFmcIcHzyzb6O5aFem5J0sP/OvKjARmg7b7sm3xRkT3vDorwawJHb1t6Ep2NOTVoOtDch
|
||||
yssMjFr6IPh8x8SbnsT4lrQamgPhCMzPzz+sifHjnXNpMnN5XnRglJaWlk5KE2z/0ISbxnzQQ0bgvlXz
|
||||
1ePSXPnG0NyZ+DRF8Zi8CsCo7Azh0V0TbkrzRc2B0G3wIJ9429CcmZLce4MgYJTyff87JtzU5uayLM/J
|
||||
qwcM7vD5+jQ3DgzNlWnKDXldgFFJZwW/2jHZpj1727uZ5VWE3mofqJXmw4eG5sdUpqqqXXm1gFGoQnhJ
|
||||
12SbgRxoYvzjtIruK04vxRjPSvPgK0PzYmqTPqtemVcNGIU6xgu7JtusJH1ovH9ubu6UvLrQC2ncPyuN
|
||||
/58Mz4fpTvnmvHrAKJQL5dO6J9ssJXxucWFhLq8yzLKtaUf5h2ncb9zz+8eUKsYP53UERmHX/PyOrsk2
|
||||
g7nJDUWYZUuPWHpIE8oPdIz92UiMn86rCoxIOmOYta8KD5uftk2Peb1hZtTzdVOHcF3HmJ+ZVCF+Ia8u
|
||||
MCppcl0+PNlmOG1zYPtYYc2BzIQ0np+ZxvWPh8b5LObqvMrAqEzRo4BHmctijCfnEsBUqkP5u2ksz8Kd
|
||||
PI8g5SfyagOj0jbIpQk2c01DR5Brmh3NfC4DTI324LWO8V0dY3pm48mAsEGm7OEgo0sRb9wZ4+NzGWDi
|
||||
lWUZ0ti9Zt1YnvUU8fdyCYBRmsFbAh9xqhDvqEN4Xi4FTKz8s93vD4/hPiSdpJyXywCMWPtrgKuGJ12P
|
||||
ck/6gPmDXAuYOHVR/lY6UN3XMXb7kDv17MAGqhaqJ6WJ1sdegDUJ726a5oG5JDB2917vL+Kl3eO1N/lQ
|
||||
LgewUdIO8E0dk69vubosy+25JDA2bYNuFeJnOsZovxLjhbkkwEZZXl4+0QfOvfl2Ogg4O5cFNl1dFE9N
|
||||
4/B7Q+Oyj7mh/VzKZQE2UtM0j6iL+LWOidizhN3OPBiHuigvSmPwrvVjsn9pQnh1LguwGQa3Fo3fHp6M
|
||||
Pcw97c1WcllgQy0tLZ2UDr7/qmMc9jJNiF/WkwNjMHhQ0GzfX/yIU8RLfRCxkdq+kzTfrugcf/3MgZ1l
|
||||
+fRcHmCztU8Yq2P8h47J2cdcpTmQjdCE8IQ0vnzjdkjKP8nlAcZoWxXin3dP0n4l1eGb9UL92FwXOG51
|
||||
Ub48ja09w2Otz2nvTJpKs21QIWDs0lnKb6TJqTEphN3NQvncXBY4VtvSju4N3WOs17l6cXHxoblGwKRo
|
||||
r8mlHeAtHZO2b9mfDohem8sCR2XX3K4z0hj65NCYklSTGONpuUzApNlVFFWaqP81NHF7mvD2tnM7lwbu
|
||||
V/vwqTR2vrV+LPU7VSjf4ff+MAU0B65NeWVd12fm0sBhpTnzosHDp7rGUV8T9lVFvDiXCJgSrmEezDea
|
||||
onhMrgsM25rmyuvSODkwNG56nvZyYvi5XCNg2mgOXM3tVVH9ci4L3KtpmlN9W7Y+VYhfiEkuEzCt8n3L
|
||||
fzA8yXuY/b7OZEVZlovt3ew6xknf8965ublTcpmAaac5cG3C2zQ09Vv7bVAaC/+7fmz0Og6QYVZpDlyT
|
||||
GD/dPlgpl4b+2Nru5NIYuGfdmOhxmhB/VBblL+QaATNKc+DBfH1nCI/OdWHGtTewSdv874fGgIT4xfYb
|
||||
wlwmYNZpDlzNbVUIz85lYUblJ2i6BDacGP/u7O3bH5zLBPSF5sDV7K+L+Nu5LMyYtJP7xbSNfzy0zfue
|
||||
A+03gak8WwdVAnpHc+CaxHiJ5sCZsnK9f/+6bd3v3JZ2/r+SawT0mebAg0kfjB93v/Pp136t3X693bWN
|
||||
e56v6nsBhmkOXE24Ph0EnJXrwpSp63qhDuXnu7dtn1P+U1VVP5PLBHAozYGDtD+LchvU6TN4Iqa+lqGs
|
||||
XO8/YVAlgMPQHLiSsC+dNb0ml4UJVxflRWm73b1+O/Y5YXcVwvNziQDun+bANYnxkvO2bHlALg0TJsZ4
|
||||
cl3ESzu3Xa8Trm+KYimXCeDIaQ48mKqIH9McOHl2zc/vaIr42a5t1vN8tCiK03OZAI6J5sCVFPFr7QNk
|
||||
cl0Ys3yp6nvrtlO/s3K9f9ugSgDHSXPgILk58PxcFsYkX+93J8s1qUK8oynKF+YSAYyO5sCVhH3pgOjV
|
||||
uSxsoqZpHpjq//bu7dLjFPHGND+Xc5kARk9z4JrE+JZUEl+1bpLFhYW5VPf/WLcd5N/ruj4zlwlg42gO
|
||||
PCQfdXOVjdeE8MRU6xuGai9uXw2MgebA1YTrFkMoc10YsaYoX5rqfOf6uvc6e9LO/xW5RACbT3Pgam5N
|
||||
B0Q/m8vCCLT3XnCQ2ZXwnWqhenIuE8D4aA5czV3OykZj19yuM1I9PzlUXwnhirIst+cyAYyf5sA1GTQH
|
||||
uu/6MdoZ4+NTHb+1rq59j+v9wKTSHHhIPtI0zam5NByhNH5enGr306Fa9j1720ttuUQAE0tz4Epi/FJM
|
||||
cl24b8ZNd25KdTk31whg8mkOXEm4pX1EbS4LHebn5x+WdnIf765fr3NVCOFRuUwA00Nz4Gr21kX58lwW
|
||||
1qjmq8el+nxjqF4S4yVLS0sn5TIBTB/NgWuiOfAQTVE+J9XltnV16nXCvqqIF+cSAUw3zYGH5INnb9/+
|
||||
4Fyavtra7uRSLe4Zqk3f88MmxvNyjQBmhiavg/liVVVFrkuvLC4uPjSt/4eG6iEhXlOWZchlApg9mgNX
|
||||
c3P6wD8nl6UXqvlqZ1rv/xmqQ+/ThPJv5ufnH5TLBDC7NAeuZm97n/tclplWhfCstL4/GVr/nsf1fqCH
|
||||
NAeu5kB7aSSVZFabA13v786tVVFckGsE0C+aAw8mnSG/f25u7pRcmpnQbt8mlB/oWt8+pwnxWk+PBNAc
|
||||
uJoqxC/MSnNgs7BQ1yFc17WePc97Z+1AD+C4aA5czU3T/qjXdED3zLQePx5ar75nf77ev3VQJQBWaQ5c
|
||||
zZ4qhJfkskyVuigvapvbOtapt2lC/FFTls/IJQKgi+bA1aw0B07FGWOM8eQ6xnd1rEe/E+OX2jGdywTA
|
||||
fdEcuCYx/u2kXzNudjTz6az/c53L3+NUMX7YI6EBjp7mwIO5pt3J5rpMlHKhfFpavu8PLW/fM+s/7QTY
|
||||
eJoDV/PduiiWc1kmwuB6f7x7aDn7ntvrGC/MJQLgeGgOHKQK8Y46hOflsoxN0zQPbEJ8Z9cy9jxf3RnC
|
||||
o3OZABgFzYGrab9efl0qyViaAxcXFubSgchnOpar77k8xnhaLhMAo6Q5cE2K+L7NfoBMHcJT0nvfvG5Z
|
||||
+h3X+wE2iebAg7m6LMvtuS4bKl/v14txSMLuKoQX5BIBsBk0B64kfCfV4om5LCN33pYtD3DA1ZVwfb1Q
|
||||
PzaXCYDNpDlwJWH3RnSe75rbdUZ6/X9b/369z0eLojg9lwmAcdAcuJqV5sCRaEJ4QnrNbw+9R9+zcr1/
|
||||
26BKAIyV5sBDcll7W95cmmPSPocgvc6dQ6/b9+xpivJluUQATBDNgQdz1TE2B6phV4p446TdhAmAIens
|
||||
9ZXpQ1tz4KA58Am5LPfrrB07Hp7+zr90v1Z/UxXxU3Vdn5nLBMAk0xy4krC7WSifm8tyWGVZnl2F+M3u
|
||||
1+hxYrxkeXn5xFwmAKaB5sDV7E9nsRfnsqyTdnIvGtxiuPPv9jV7Ul1ekUsEwLTRHHgwVSjfsbS0dFIu
|
||||
TWtr+6uB9P8ODP/Znue7ZVmek2sEwBTT2Laa8sr2enb7jHoHRl0JV8QYH5nHDQCzwJ0DV/P1tKO7vuO/
|
||||
9zpVKN/qej/AjNIcKB3Z24TyVXmIADCrNAfKmtzUxHhuHhoAzDrNgZJyVQjhUXlIANAjmgN7m/Du471d
|
||||
MgBTzp0D+5Sw777uiQBAz2gO7EPCLSnn500OAAOaA2c615RlGfKmBoBDaQ6cvTQhvmd+fv5BeRMDwGFp
|
||||
DpyJuN4PwDHQHDjVubUqigvypgSAo6M5cPrShHjtYghl3oQAcGw0B05Rivi+ubm5U/KmA4Djozlw4rM/
|
||||
X+/fOthiADA6mgMnME2IP2rK8hl5GwHAxtAcOFH5SozxrLxpAGBjaQ4cf6oYP9w0zal5kwDA5tAcOLYc
|
||||
aC/FpE1wwmBLAMAm0xy46bk91fvCXH4AGCvNgZuRIn6tKYrH5JoDwGTQHLihuTzGeFouNQBMFs2BI4/r
|
||||
/QBMB82Bo0rYXYXwglxWAJh8mgOPN+H6eqF+bC4nAEwVzYHHkiL+c1EUp+caAsB00hx4FInxLalk2waV
|
||||
A4AppznwfrOnLsqX53IBwOzQHHiYFPHGaqF6Ui4TAMwezYGHpirip+q6PjOXBwBmmubANjFesry8fGKu
|
||||
CQD0Q4+bA/dWMf56LgMA9E8PmwO/W5blOXn1AaC/+tMcWF4ZY3xkXm0AYOabA2O8ZGlp6aS8ugDAGrPY
|
||||
HLi3CeWr8voBAIczQ82BN6UDmnPzagEA92f6mwPLz1dVVeTVAQCO1LQ2B1Yh/PX8/PyD8moAAEdrupoD
|
||||
w76qiBfnRQcAjtMUNAeGW1LOz8sLAIzKBDcHXlOWZciLCQCM2gQ2B142Nzd3Sl48AGCjTEhz4H7X+wFg
|
||||
k425OfDWqqh+Pi8KALDJtqWDgDemHfKBoR30hqUJ8dqY5PcHAMalKcrnpJ3z94Z31qNO+/t+1/sBYIKk
|
||||
k/LT6hD+Mu2oR/4rgXTW/+X02r+U3woAmDTtz/GaIv5F2nH/ZHhHfpS5J+Vf01n/S9LLbhu8OgAw0dpb
|
||||
8TYL5XPTmfvb0o78v/MOvWtHvybtzXzKT1Qx/n5d1wv5pQCAaXXvAUFRLLXd+3WMFzZF+cKUl7X/rIri
|
||||
gsWFhbn8RwEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
|
||||
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6LEtW/4flgYiLD1qeX0A
|
||||
AAAASUVORK5CYII=
|
||||
</value>
|
||||
</data>
|
||||
<data name="ActionButton5.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
<data name="ActionButton6.BackgroundImage" type="System.Drawing.Bitmap, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
|
||||
<value>
|
||||
iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAABGdBTUEAALGPC/xhBQAAAAlwSFlzAAAO
|
||||
xAAADsQBlSsOGwAAAIZJREFUOE+1j10KwCAMgz2b755xl/IsvnaL2K20UfbDAmEako+ZROSTafjE12Go
|
||||
tbbB43rK5xSAQq1VYFtmeQBoqZTSreVZvgTknM8yyyjA/qodsDF9gspD2Bj6B+DH+NqzhQQAG+POMnSX
|
||||
AFuc5QFgn6ClHh5iOQVAKNixyucB8NY0vG9JOzzyhrdq5IRgAAAAAElFTkSuQmCC
|
||||
</value>
|
||||
</data>
|
||||
</root>
|
||||
154
SCrawler/API/Mastodon/SettingsForm.vb
Normal file
154
SCrawler/API/Mastodon/SettingsForm.vb
Normal file
@@ -0,0 +1,154 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Forms.Controls.Base
|
||||
Namespace API.Mastodon
|
||||
Friend Class SettingsForm
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Friend ReadOnly Property MyCredentials As List(Of Credentials)
|
||||
Friend ReadOnly Property MyDomains As List(Of String)
|
||||
Friend Sub New(ByVal s As SiteSettings)
|
||||
InitializeComponent()
|
||||
MyCredentials = New List(Of Credentials)
|
||||
If s.Domains.Credentials.Count > 0 Then MyCredentials.AddRange(s.Domains.Credentials)
|
||||
MyDomains = New List(Of String)
|
||||
MyDomains.ListAddList(s.Domains)
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
End Sub
|
||||
Private Sub MyForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
.MyView = New FormView(Me, Settings.Design, "MastodonSettingsForm")
|
||||
.MyView.Import()
|
||||
.MyView.SetFormSize()
|
||||
.AddOkCancelToolbar()
|
||||
RefillList()
|
||||
.EndLoaderOperations()
|
||||
End With
|
||||
End Sub
|
||||
Private Sub SettingsForm_Disposed(sender As Object, e As EventArgs) Handles Me.Disposed
|
||||
MyCredentials.Clear()
|
||||
MyDomains.Clear()
|
||||
End Sub
|
||||
Private Sub RefillList()
|
||||
CMB_DOMAINS.Items.Clear()
|
||||
If MyDomains.Count > 0 Then
|
||||
MyDomains.Sort()
|
||||
CMB_DOMAINS.BeginUpdate()
|
||||
CMB_DOMAINS.Items.AddRange(MyDomains.Select(Function(d) New ListItem(d)))
|
||||
CMB_DOMAINS.EndUpdate(True)
|
||||
End If
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
|
||||
ApplyCredentials()
|
||||
If MyCredentials.Count > 0 Then MyCredentials.RemoveAll(Function(c) c.Domain.IsEmptyString Or c.Bearer.IsEmptyString Or c.Csrf.IsEmptyString)
|
||||
If MyDomains.Count > 0 Then
|
||||
If MyCredentials.Count > 0 Then
|
||||
MyCredentials.RemoveAll(Function(c) Not MyDomains.Contains(c.Domain))
|
||||
Else
|
||||
MyCredentials.Clear()
|
||||
End If
|
||||
End If
|
||||
MyDefs.CloseForm()
|
||||
End Sub
|
||||
Private Sub CMB_DOMAINS_ActionOnButtonClick(ByVal Sender As ActionButton, ByVal e As ActionButtonEventArgs) Handles CMB_DOMAINS.ActionOnButtonClick
|
||||
Try
|
||||
Dim d$
|
||||
Dim i% = -1
|
||||
Select Case e.DefaultButton
|
||||
Case ActionButton.DefaultButtons.Add
|
||||
d = InputBoxE("Enter a new domain using the pattern [mastodon.social]:", "New domain")
|
||||
If Not d.IsEmptyString Then
|
||||
If MyDomains.Count > 0 Then i = MyDomains.IndexOf(d)
|
||||
If i >= 0 Then
|
||||
MsgBoxE({$"Domain '{d}' already exists", "Add domain"}, vbExclamation)
|
||||
If i <= CMB_DOMAINS.Count - 1 Then CMB_DOMAINS.SelectedIndex = i
|
||||
Else
|
||||
ApplyCredentials()
|
||||
ClearCredentials()
|
||||
MyDomains.Add(d)
|
||||
_Suspended = True
|
||||
RefillList()
|
||||
_Suspended = False
|
||||
i = MyDomains.IndexOf(d)
|
||||
If i.ValueBetween(0, CMB_DOMAINS.Count - 1) Then
|
||||
CMB_DOMAINS.SelectedIndex = i
|
||||
Else
|
||||
_LatestSelected = -1
|
||||
_CurrentCredentialsIndex = -1
|
||||
_CurrentDomain = String.Empty
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Case ActionButton.DefaultButtons.Delete
|
||||
If _LatestSelected >= 0 Then
|
||||
d = CMB_DOMAINS.Items(_LatestSelected).Value(0)
|
||||
If Not d.IsEmptyString AndAlso MsgBoxE({$"Are you sure you want to delete the [{d}] domain?",
|
||||
"Removing domains"}, vbYesNo) = vbYes Then
|
||||
i = MyDomains.IndexOf(d)
|
||||
Dim l% = _LatestSelected
|
||||
If i >= 0 Then
|
||||
ClearCredentials()
|
||||
MyDomains.RemoveAt(i)
|
||||
_Suspended = True
|
||||
RefillList()
|
||||
_Suspended = False
|
||||
If (l - 1).ValueBetween(0, CMB_DOMAINS.Count - 1) Then
|
||||
CMB_DOMAINS.SelectedIndex = l - 1
|
||||
Else
|
||||
_LatestSelected = -1
|
||||
_CurrentCredentialsIndex = -1
|
||||
_CurrentDomain = String.Empty
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End Select
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.LogMessageValue, ex, "API.Mastodon.SettingsForm.ActionButtonClick")
|
||||
End Try
|
||||
End Sub
|
||||
Private _LatestSelected As Integer = -1
|
||||
Private _CurrentCredentialsIndex As Integer = -1
|
||||
Private _CurrentDomain As String = String.Empty
|
||||
Private _Suspended As Boolean = False
|
||||
Private Sub CMB_DOMAINS_ActionSelectedItemChanged(ByVal Sender As Object, ByVal e As EventArgs, ByVal Item As ListViewItem) Handles CMB_DOMAINS.ActionSelectedItemChanged
|
||||
If Not MyDefs.Initializing And Not _Suspended Then
|
||||
Dim DropCredentials As Boolean = True
|
||||
If Not Item Is Nothing Then
|
||||
ApplyCredentials()
|
||||
_LatestSelected = Item.Index
|
||||
_CurrentDomain = Item.Text
|
||||
If MyCredentials.Count > 0 And Not _CurrentDomain.IsEmptyString Then
|
||||
_CurrentCredentialsIndex = MyCredentials.IndexOf(_CurrentDomain)
|
||||
If _CurrentCredentialsIndex >= 0 Then
|
||||
With MyCredentials(_CurrentCredentialsIndex) : TXT_AUTH.Text = .Bearer : TXT_TOKEN.Text = .Csrf : End With
|
||||
DropCredentials = False
|
||||
End If
|
||||
Else
|
||||
_CurrentCredentialsIndex = -1
|
||||
End If
|
||||
End If
|
||||
If DropCredentials Then ClearCredentials()
|
||||
End If
|
||||
End Sub
|
||||
Private Sub ClearCredentials()
|
||||
TXT_AUTH.Clear()
|
||||
TXT_TOKEN.Clear()
|
||||
End Sub
|
||||
Private Sub ApplyCredentials()
|
||||
Try
|
||||
If _LatestSelected >= 0 And Not _CurrentDomain.IsEmptyString Then
|
||||
Dim c As New Credentials With {.Domain = _CurrentDomain, .Bearer = TXT_AUTH.Text, .Csrf = TXT_TOKEN.Text}
|
||||
If _CurrentCredentialsIndex.ValueBetween(0, MyCredentials.Count - 1) Then MyCredentials(_CurrentCredentialsIndex) = c Else MyCredentials.Add(c)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
End Try
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
214
SCrawler/API/Mastodon/SiteSettings.vb
Normal file
214
SCrawler/API/Mastodon/SiteSettings.vb
Normal file
@@ -0,0 +1,214 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports TS = SCrawler.API.Twitter.SiteSettings
|
||||
Namespace API.Mastodon
|
||||
<Manifest(MastodonSiteKey), SavedPosts, SpecialForm(True), SpecialForm(False)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
Friend Overrides ReadOnly Property Icon As Icon
|
||||
Get
|
||||
Return My.Resources.SiteResources.MastodonIcon_48
|
||||
End Get
|
||||
End Property
|
||||
Friend Overrides ReadOnly Property Image As Image
|
||||
Get
|
||||
Return My.Resources.SiteResources.MastodonPic_48
|
||||
End Get
|
||||
End Property
|
||||
#Region "Domains"
|
||||
<PXML("Domains")> Private ReadOnly Property SiteDomains As PropertyValue
|
||||
Friend ReadOnly Property Domains As MastodonDomains
|
||||
<PXML> Private ReadOnly Property DomainsLastUpdateDate As PropertyValue
|
||||
#End Region
|
||||
#Region "Auth"
|
||||
<PropertyOption(IsAuth:=True, AllowNull:=False, ControlText:="My domain",
|
||||
ControlToolTip:="Your account domain without 'https://' (for example, 'mastodon.social')"), PXML>
|
||||
Friend ReadOnly Property MyDomain As PropertyValue
|
||||
<PropertyOption(AllowNull:=False, IsAuth:=True, ControlText:="Authorization",
|
||||
ControlToolTip:="Set authorization from [authorization] response header. This field must start from [Bearer] key word")>
|
||||
Friend ReadOnly Property Auth As PropertyValue
|
||||
<PropertyOption(AllowNull:=False, IsAuth:=True, ControlText:="Token", ControlToolTip:="Set token from [x-csrf-token] response header")>
|
||||
Friend ReadOnly Property Token As PropertyValue
|
||||
Private Sub ChangeResponserFields(ByVal PropName As String, ByVal Value As Object)
|
||||
If Not PropName.IsEmptyString Then
|
||||
Dim f$ = String.Empty
|
||||
Select Case PropName
|
||||
Case NameOf(Auth) : f = TS.Header_Authorization
|
||||
Case NameOf(Token) : f = TS.Header_Token
|
||||
End Select
|
||||
If Not f.IsEmptyString Then
|
||||
Responser.Headers.Remove(f)
|
||||
If Not CStr(Value).IsEmptyString Then Responser.Headers.Add(f, CStr(Value))
|
||||
Responser.SaveSettings()
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Other properties"
|
||||
<PropertyOption(IsAuth:=False, ControlText:=TS.GifsDownload_Text), PXML>
|
||||
Friend ReadOnly Property GifsDownload As PropertyValue
|
||||
<PropertyOption(IsAuth:=False, ControlText:=TS.GifsSpecialFolder_Text, ControlToolTip:=TS.GifsSpecialFolder_ToolTip), PXML>
|
||||
Friend ReadOnly Property GifsSpecialFolder As PropertyValue
|
||||
<PropertyOption(IsAuth:=False, ControlText:=TS.GifsPrefix_Text, ControlToolTip:=TS.GifsPrefix_ToolTip), PXML>
|
||||
Friend ReadOnly Property GifsPrefix As PropertyValue
|
||||
<Provider(NameOf(GifsSpecialFolder), Interaction:=True), Provider(NameOf(GifsPrefix), Interaction:=True)>
|
||||
Private ReadOnly Property GifStringChecker As IFormatProvider
|
||||
<PropertyOption(IsAuth:=False, ControlText:=TS.UseMD5Comparison_Text, ControlToolTip:=TS.UseMD5Comparison_ToolTip), PXML>
|
||||
Friend ReadOnly Property UseMD5Comparison As PropertyValue
|
||||
<PropertyOption(IsAuth:=False, ControlText:="User related to my domain",
|
||||
ControlToolTip:="Open user profiles and user posts through my domain."), PXML>
|
||||
Friend ReadOnly Property UserRelatedToMyDomain As PropertyValue
|
||||
#End Region
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
MyBase.New("Mastodon", "mastodon.social")
|
||||
|
||||
Domains = New MastodonDomains(Me, "mastodon.social")
|
||||
SiteDomains = New PropertyValue(Domains.DomainsDefault, GetType(String))
|
||||
Domains.DestinationProp = SiteDomains
|
||||
DomainsLastUpdateDate = New PropertyValue(Now.AddYears(-1))
|
||||
|
||||
Auth = New PropertyValue(Responser.Headers.Value(TS.Header_Authorization), GetType(String), Sub(v) ChangeResponserFields(NameOf(Auth), v))
|
||||
Token = New PropertyValue(Responser.Headers.Value(TS.Header_Token), GetType(String), Sub(v) ChangeResponserFields(NameOf(Token), v))
|
||||
|
||||
GifsDownload = New PropertyValue(True)
|
||||
GifsSpecialFolder = New PropertyValue(String.Empty, GetType(String))
|
||||
GifsPrefix = New PropertyValue("GIF_")
|
||||
GifStringChecker = New TS.GifStringProvider
|
||||
UseMD5Comparison = New PropertyValue(False)
|
||||
MyDomain = New PropertyValue(String.Empty, GetType(String))
|
||||
UserRelatedToMyDomain = New PropertyValue(False)
|
||||
|
||||
UserRegex = RParams.DMS("", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
|
||||
End Sub
|
||||
Friend Overrides Sub EndInit()
|
||||
Domains.PopulateInitialDomains(SiteDomains.Value)
|
||||
If CDate(DomainsLastUpdateDate.Value).AddDays(7) < Now Then UpdateServersList()
|
||||
MyBase.EndInit()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetInstance"
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
Return New UserData
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Domains Support"
|
||||
Protected Overrides Sub DomainsApply()
|
||||
Domains.Apply()
|
||||
MyBase.DomainsApply()
|
||||
End Sub
|
||||
Protected Overrides Sub DomainsReset()
|
||||
Domains.Reset()
|
||||
MyBase.DomainsReset()
|
||||
End Sub
|
||||
Friend Overrides Sub OpenSettingsForm()
|
||||
Domains.OpenSettingsForm()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Update"
|
||||
Friend Overrides Sub Update()
|
||||
If _SiteEditorFormOpened Then
|
||||
Dim tf$ = GifsSpecialFolder.Value
|
||||
If Not tf.IsEmptyString Then tf = tf.StringTrim("\") : GifsSpecialFolder.Value = tf
|
||||
End If
|
||||
MyBase.Update()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "UserOptions"
|
||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||
If Options Is Nothing OrElse (Not TypeOf Options Is Twitter.EditorExchangeOptions OrElse
|
||||
Not DirectCast(Options, Twitter.EditorExchangeOptions).SiteKey = MastodonSiteKey) Then _
|
||||
Options = New Twitter.EditorExchangeOptions(Me) With {.SiteKey = MastodonSiteKey}
|
||||
If OpenForm Then
|
||||
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download"
|
||||
Friend Overrides Function BaseAuthExists() As Boolean
|
||||
Return ACheck(Token.Value) And ACheck(Auth.Value) And ACheck(MyDomain.Value)
|
||||
End Function
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
If What = ISiteSettings.Download.SavedPosts Or What = ISiteSettings.Download.SingleObject Then
|
||||
If Not ACheck(MyDomain.Value) Then MyMainLOG = "Mastodon account domain not set" : Return False
|
||||
Else
|
||||
If CDate(DomainsLastUpdateDate.Value).AddDays(7) < Now Then UpdateServersList()
|
||||
End If
|
||||
Return MyBase.Available(What, Silent)
|
||||
End Function
|
||||
#End Region
|
||||
#Region "GetUserUrl, GetUserPostUrl"
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
If Not ACheck(MyDomain.Value) Then Return String.Empty
|
||||
With DirectCast(User, UserData)
|
||||
If UserRelatedToMyDomain.Value Then
|
||||
If MyDomain.Value = .UserDomain Then
|
||||
Return $"https://{ .UserDomain}/@{ .TrueName}"
|
||||
Else
|
||||
Return $"https://{MyDomain.Value}/@{ .TrueName}@{ .UserDomain}"
|
||||
End If
|
||||
Else
|
||||
Return $"https://{ .UserDomain}/@{ .TrueName}"
|
||||
End If
|
||||
End With
|
||||
End Function
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
If Not ACheck(MyDomain.Value) Then Return String.Empty
|
||||
Return $"{GetUserUrl(User)}/{Media.Post.ID}"
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IsMyUser, IsMyImageVideo"
|
||||
Private Const UserRegexDefault As String = "https?://{0}/@([^/@]+)@?([^/]*)"
|
||||
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
|
||||
If Domains.Count > 0 Then
|
||||
Dim l As List(Of String)
|
||||
For Each domain$ In Domains
|
||||
UserRegex.Pattern = String.Format(UserRegexDefault, domain)
|
||||
l = RegexReplace(UserURL, UserRegex)
|
||||
If l.ListExists(2) Then Return New ExchangeOptions(Site, $"{l(2).IfNullOrEmpty(domain)}@{l(1)}")
|
||||
Next
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Friend Overrides Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
|
||||
If Not URL.IsEmptyString And Domains.Count > 0 Then
|
||||
If Domains.Domains.Exists(Function(d) URL.Contains(d)) Then Return New ExchangeOptions(Site, URL) With {.Exists = True}
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
#End Region
|
||||
#Region "UpdateServersList"
|
||||
Private Sub UpdateServersList()
|
||||
Try
|
||||
Dim r$ = GetWebString("https://api.joinmastodon.org/servers?language=&category=®ion=&ownership=®istrations=",, EDP.ThrowException)
|
||||
If Not r.IsEmptyString Then
|
||||
Dim j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
|
||||
If If(j?.Count, 0) > 0 Then
|
||||
Domains.Domains.ListAddList(j.Select(Function(e) e.Value("domain")), LAP.NotContainsOnly, EDP.ReturnValue)
|
||||
Domains.Domains.Sort()
|
||||
Domains.Save()
|
||||
j.Dispose()
|
||||
End If
|
||||
End If
|
||||
DomainsLastUpdateDate.Value = Now
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.Mastodon.SiteSettings.UpdateServersList]")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
287
SCrawler/API/Mastodon/UserData.vb
Normal file
287
SCrawler/API/Mastodon/UserData.vb
Normal file
@@ -0,0 +1,287 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||
Namespace API.Mastodon
|
||||
Friend Class UserData : Inherits Twitter.UserData
|
||||
#Region "XML names"
|
||||
Private Const Name_UserDomain As String = "UserDomain"
|
||||
Private Const Name_TrueName As String = "TrueName"
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Private _UserDomain As String = String.Empty
|
||||
Friend Property UserDomain As String
|
||||
Get
|
||||
Return _UserDomain.IfNullOrEmpty(MySettings.MyDomain.Value)
|
||||
End Get
|
||||
Set(ByVal d As String)
|
||||
_UserDomain = d
|
||||
End Set
|
||||
End Property
|
||||
Friend Property TrueName As String = String.Empty
|
||||
Private ReadOnly Property MySettings As SiteSettings
|
||||
Get
|
||||
Return HOST.Source
|
||||
End Get
|
||||
End Property
|
||||
Private MyCredentials As Credentials
|
||||
Private Sub ResetCredentials()
|
||||
MyCredentials = Nothing
|
||||
With MySettings
|
||||
Dim setDef As Boolean = True
|
||||
If Not IsSavedPosts Then
|
||||
If ACheck(.MyDomain.Value) AndAlso UserDomain = .MyDomain.Value Then
|
||||
setDef = True
|
||||
ElseIf .Domains.Credentials.Count > 0 Then
|
||||
Dim i% = .Domains.Credentials.IndexOf(UserDomain)
|
||||
If i >= 0 Then
|
||||
MyCredentials = .Domains.Credentials(i)
|
||||
setDef = Not MyCredentials.Exists
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
If setDef Then MyCredentials = New Credentials With {.Domain = UserDomain, .Bearer = MySettings.Auth.Value, .Csrf = MySettings.Token.Value}
|
||||
End With
|
||||
With MyCredentials
|
||||
Responser.Headers.Add(Twitter.SiteSettings.Header_Authorization, .Bearer)
|
||||
Responser.Headers.Add(Twitter.SiteSettings.Header_Token, .Csrf)
|
||||
End With
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "LoadUserInformation"
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
MyBase.LoadUserInformation_OptionalFields(Container, Loading)
|
||||
Dim obtainNames As Action = Sub()
|
||||
If _UserDomain.IsEmptyString And Not Name.IsEmptyString Then
|
||||
Dim l$() = Name.Split("@")
|
||||
If l.ListExists(2) Then
|
||||
_UserDomain = l(0)
|
||||
TrueName = l(1)
|
||||
Else
|
||||
_UserDomain = MySettings.MyDomain.Value
|
||||
TrueName = Name
|
||||
End If
|
||||
If FriendlyName.IsEmptyString Then FriendlyName = TrueName
|
||||
End If
|
||||
End Sub
|
||||
If Loading Then
|
||||
_UserDomain = Container.Value(Name_UserDomain)
|
||||
TrueName = Container.Value(Name_TrueName)
|
||||
obtainNames.Invoke
|
||||
Else
|
||||
obtainNames.Invoke
|
||||
Container.Add(Name_UserDomain, _UserDomain)
|
||||
Container.Add(Name_TrueName, TrueName)
|
||||
Container.Value(Name_FriendlyName) = FriendlyName
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download functions"
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
ResetCredentials()
|
||||
DownloadData(String.Empty, Token)
|
||||
End Sub
|
||||
Private Overloads Sub DownloadData(ByVal POST As String, ByVal Token As CancellationToken)
|
||||
Dim URL$ = String.Empty
|
||||
Try
|
||||
Dim PostID$ = String.Empty
|
||||
Dim PostDate$
|
||||
Dim s As EContainer, ss As EContainer
|
||||
Dim NewPostDetected As Boolean = False
|
||||
Dim ExistsDetected As Boolean = False
|
||||
|
||||
If IsSavedPosts Then
|
||||
URL = $"https://{MySettings.MyDomain.Value}/api/v1/bookmarks"
|
||||
If Not POST.IsEmptyString Then URL &= $"?max_id={POST}"
|
||||
Else
|
||||
If POST.IsEmptyString And ID.IsEmptyString Then
|
||||
ObtainUserID()
|
||||
If ID.IsEmptyString Then Throw New ArgumentNullException("ID", "Unable to get user ID") With {.HelpLink = 1}
|
||||
End If
|
||||
URL = $"https://{MyCredentials.Domain}/api/v1/accounts/{ID}/statuses?"
|
||||
If ParseUserMediaOnly Then URL &= "only_media=true&"
|
||||
URL &= "limit=40"
|
||||
If Not POST.IsEmptyString Then URL &= $"&max_id={POST}"
|
||||
End If
|
||||
|
||||
ThrowAny(Token)
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If If(j?.Count, 0) > 0 Then
|
||||
For Each jj As EContainer In j
|
||||
With jj
|
||||
If Not IsSavedPosts And POST.IsEmptyString And Not .Item("account") Is Nothing Then
|
||||
With .Item("account")
|
||||
If .Value("id") = ID Then
|
||||
UserSiteNameUpdate(.Value("display_name"))
|
||||
UserDescriptionUpdate(.Value("note"))
|
||||
Dim __getImage As Action(Of String) = Sub(ByVal img As String)
|
||||
If Not img.IsEmptyString Then
|
||||
Dim __imgFile As SFile = img
|
||||
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)
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
__getImage.Invoke(.Value("header").IfNullOrEmpty(.Value("header_static")))
|
||||
__getImage.Invoke(.Value("avatar").IfNullOrEmpty(.Value("avatar_static")))
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
|
||||
PostID = .Value("id")
|
||||
PostDate = .Value("created_at")
|
||||
|
||||
If Not IsSavedPosts And Not PostDate.IsEmptyString Then
|
||||
Select Case CheckDatesLimit(PostDate, DateProvider)
|
||||
Case DateResult.Skip : Continue For
|
||||
Case DateResult.Exit : Exit Sub
|
||||
End Select
|
||||
End If
|
||||
|
||||
If Not _TempPostsList.Contains(PostID) Then
|
||||
NewPostDetected = True
|
||||
_TempPostsList.Add(PostID)
|
||||
Else
|
||||
ExistsDetected = True
|
||||
Continue For
|
||||
End If
|
||||
|
||||
If IsSavedPosts OrElse (Not ParseUserMediaOnly OrElse
|
||||
(If(.Item("reblog")?.Count, 0) = 0 OrElse .Value({"reblog", "account"}, "id") = ID)) Then
|
||||
If If(.Item("media_attachments")?.Count, 0) > 0 Then
|
||||
s = .Item("media_attachments")
|
||||
Else
|
||||
s = .Item({"reblog", "account"}, "media_attachments")
|
||||
End If
|
||||
If s.ListExists Then
|
||||
For Each ss In s : ObtainMedia(ss, PostID, PostDate) : Next
|
||||
End If
|
||||
End If
|
||||
End With
|
||||
Next
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
|
||||
If POST.IsEmptyString And ExistsDetected Then Exit Sub
|
||||
If Not PostID.IsEmptyString And NewPostDetected Then DownloadData(PostID, Token)
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"data downloading error{IIf(IsSavedPosts, " (Saved Posts)", String.Empty)} [{URL}]")
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub ObtainMedia(ByVal e As EContainer, ByVal PostID As String, ByVal PostDate As String, Optional ByVal BaseUrl As String = Nothing)
|
||||
Dim t As UTypes = UTypes.Undefined
|
||||
Select Case e.Value("type")
|
||||
Case "video" : t = UTypes.Video
|
||||
Case "image" : t = UTypes.Picture
|
||||
Case "gifv" : t = UTypes.GIF
|
||||
End Select
|
||||
If Not t = UTypes.Undefined Then
|
||||
Dim m As New UserMedia(e.Value("url"), t) With {
|
||||
.Post = New UserPost(PostID, AConvert(Of Date)(PostDate, DateProvider, Nothing, EDP.ReturnValue)),
|
||||
.URL_BASE = BaseUrl.IfNullOrEmpty(MySettings.GetUserPostUrl(Me, m))
|
||||
}
|
||||
If Not t = UTypes.GIF Or GifsDownload Then
|
||||
If t = UTypes.GIF Then
|
||||
If Not GifsSpecialFolder.IsEmptyString Then m.SpecialFolder = GifsSpecialFolder
|
||||
If Not GifsPrefix.IsEmptyString Then m.File.Name = $"{GifsPrefix}{m.File.Name}"
|
||||
End If
|
||||
If Not m.URL.IsEmptyString Then _TempMediaList.ListAddValue(m, LNC)
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
Private Sub ObtainUserID()
|
||||
Try
|
||||
If ID.IsEmptyString Then
|
||||
Dim url$ = $"https://{MyCredentials.Domain}/api/v1/accounts/lookup?acct="
|
||||
If Not UserDomain.IsEmptyString Then
|
||||
If UserDomain = MyCredentials.Domain Then
|
||||
url &= $"@{TrueName}"
|
||||
Else
|
||||
url &= $"@{TrueName}@{UserDomain}"
|
||||
End If
|
||||
Else
|
||||
url &= $"@{TrueName}"
|
||||
End If
|
||||
Dim r$ = Responser.GetResponse(url)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If Not j Is Nothing Then ID = j.Value("id")
|
||||
End Using
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"API.Mastodon.UserData.ObtainUserID({ToStringForLog()})")
|
||||
End Try
|
||||
End Sub
|
||||
Private Function GetSinglePostPattern(ByVal Domain As String) As String
|
||||
Return $"https://{Domain}/api/v1/statuses/" & "{0}"
|
||||
End Function
|
||||
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
|
||||
SinglePostUrl = GetSinglePostPattern(MyCredentials.Domain)
|
||||
MyBase.ReparseMissing(Token)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "DownloadSingleObject"
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
Dim PostID$ = RegexReplace(Data.URL, RParams.DM("(?<=/)\d+", 0, EDP.ReturnValue))
|
||||
If Not PostID.IsEmptyString Then
|
||||
ResetCredentials()
|
||||
Dim pattern$
|
||||
If Not ACheck(MySettings.MyDomain.Value) Then
|
||||
Throw New ArgumentNullException("Mastodon domain", "Mastodon domain not set")
|
||||
Else
|
||||
pattern = GetSinglePostPattern(MySettings.MyDomain.Value)
|
||||
End If
|
||||
Dim r$ = Responser.GetResponse(String.Format(pattern, PostID),, EDP.ReturnValue)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If j.ListExists AndAlso j.Contains("media_attachments") Then
|
||||
For Each jj As EContainer In j("media_attachments") : ObtainMedia(jj, PostID, String.Empty, Data.URL) : Next
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Exception"
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
If TypeOf ex Is ArgumentNullException AndAlso Not ex.HelpLink.IsEmptyString And ex.HelpLink = 1 Then
|
||||
Return 0
|
||||
Else
|
||||
If Responser.Status = Net.WebExceptionStatus.NameResolutionFailure Then
|
||||
MyMainLOG = $"User domain ({UserDomain}) not found: {ToStringForLog()}"
|
||||
Return 1
|
||||
ElseIf Responser.StatusCode = Net.HttpStatusCode.NotFound Then
|
||||
UserExists = False
|
||||
Return 1
|
||||
ElseIf Responser.StatusCode = Net.HttpStatusCode.Unauthorized Then
|
||||
MyMainLOG = $"{ToStringForLog()}: account credentials have expired"
|
||||
Return 2
|
||||
ElseIf Responser.StatusCode = Net.HttpStatusCode.Gone Then
|
||||
UserSuspended = True
|
||||
Return 1
|
||||
Else
|
||||
Return 0
|
||||
End If
|
||||
End If
|
||||
End Function
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -12,9 +12,10 @@ Imports SCrawler.Plugin.Attributes
|
||||
Namespace API.PathPlugin
|
||||
<Manifest(PluginKey)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
Private ReadOnly _Icon As Icon = Nothing
|
||||
Friend Overrides ReadOnly Property Icon As Icon
|
||||
Get
|
||||
Return PersonalUtilities.Tools.ImageRenderer.GetIcon(PersonalUtilities.My.Resources.FolderOpenPic_Orange_16, EDP.ReturnValue)
|
||||
Return _Icon
|
||||
End Get
|
||||
End Property
|
||||
Friend Overrides ReadOnly Property Image As Image
|
||||
@@ -24,6 +25,7 @@ Namespace API.PathPlugin
|
||||
End Property
|
||||
Friend Sub New()
|
||||
MyBase.New(PluginName)
|
||||
_Icon = PersonalUtilities.Tools.ImageRenderer.GetIcon(PersonalUtilities.My.Resources.FolderOpenPic_Orange_16, EDP.ReturnValue)
|
||||
End Sub
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
Return New UserData
|
||||
@@ -42,7 +44,7 @@ Namespace API.PathPlugin
|
||||
Friend Overrides Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
|
||||
Return Nothing
|
||||
End Function
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider, ByVal Channel As Boolean) As String
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
Return String.Empty
|
||||
End Function
|
||||
End Class
|
||||
|
||||
21
SCrawler/API/Pinterest/Declarations.vb
Normal file
21
SCrawler/API/Pinterest/Declarations.vb
Normal file
@@ -0,0 +1,21 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Globalization
|
||||
Namespace API.Pinterest
|
||||
Friend Module Declarations
|
||||
Friend ReadOnly DateProvider As ADateTime = GetDateProvider()
|
||||
Private Function GetDateProvider() As ADateTime
|
||||
Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone
|
||||
n.FullDateTimePattern = "ddd dd MMM yyyy HH:mm:ss"
|
||||
n.TimeSeparator = String.Empty
|
||||
'Sat, 01 Jan 2000 01:10:15 +0000
|
||||
Return New ADateTime(DirectCast(n.Clone, DateTimeFormatInfo)) With {.DateTimeStyle = DateTimeStyles.AssumeUniversal}
|
||||
End Function
|
||||
End Module
|
||||
End Namespace
|
||||
101
SCrawler/API/Pinterest/SiteSettings.vb
Normal file
101
SCrawler/API/Pinterest/SiteSettings.vb
Normal file
@@ -0,0 +1,101 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Pinterest
|
||||
<Manifest("AndyProgram_Pinterest"), SavedPosts, SeparatedTasks>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
Friend Overrides ReadOnly Property Icon As Icon
|
||||
Get
|
||||
Return My.Resources.SiteResources.PinterestIcon_32
|
||||
End Get
|
||||
End Property
|
||||
Friend Overrides ReadOnly Property Image As Image
|
||||
Get
|
||||
Return My.Resources.SiteResources.PinterestPic_48
|
||||
End Get
|
||||
End Property
|
||||
Private Class ConcurrentDownloadsValidator : Inherits FieldsCheckerProviderBase
|
||||
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
||||
Dim v% = AConvert(Of Integer)(Value, -1)
|
||||
Dim defV% = Settings.MaxUsersJobsCount
|
||||
If v.ValueBetween(1, defV) Then
|
||||
Return Value
|
||||
Else
|
||||
ErrorMessage = $"The number of concurrent downloads must be greater than 0 and equal to or less than {defV} (global limit)."
|
||||
HasError = True
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
End Class
|
||||
<Provider(NameOf(ConcurrentDownloads), FieldsChecker:=True)>
|
||||
Private ReadOnly Property ConcurrentDownloadsProvider As IFormatProvider
|
||||
<PXML, PropertyOption(ControlText:="Concurrent downloads", ControlToolTip:="The number of concurrent downloads.", LeftOffset:=120), TaskCounter>
|
||||
Friend ReadOnly Property ConcurrentDownloads As PropertyValue
|
||||
<PXML, PropertyOption(ControlText:="Saved posts user", ControlToolTip:="Personal profile username")>
|
||||
Friend ReadOnly Property SavedPostsUserName As PropertyValue
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
MyBase.New("Pinterest", "pinterest.com")
|
||||
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
|
||||
ConcurrentDownloads = New PropertyValue(1)
|
||||
ConcurrentDownloadsProvider = New ConcurrentDownloadsValidator
|
||||
CheckNetscapeCookiesOnEndInit = True
|
||||
UseNetscapeCookies = True
|
||||
UserRegex = RParams.DMS("https?://w{0,3}.?[^/]*?.?pinterest.com/([^/]+)/?(?(_)|([^/]*))", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetInstance, Available"
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
Return New UserData
|
||||
End Function
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
Return Settings.GalleryDLFile.Exists And (Not What = ISiteSettings.Download.SavedPosts OrElse
|
||||
(Responser.CookiesExists And ACheck(SavedPostsUserName.Value)))
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IsMyUser, IsMyImageVideo, GetUserUrl, GetUserPostUrl"
|
||||
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
|
||||
If Not UserURL.IsEmptyString Then
|
||||
Dim l As List(Of String) = RegexReplace(UserURL, UserRegex)
|
||||
If l.ListExists(3) Then
|
||||
Dim n$ = l(1)
|
||||
If Not l(2).IsEmptyString Then n &= $"@{l(2)}"
|
||||
Return New ExchangeOptions(Site, n) With {.Exists = True}
|
||||
End If
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Friend Overrides Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
|
||||
Return IsMyUser(URL)
|
||||
End Function
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
With DirectCast(User, UserData)
|
||||
Dim n$ = .TrueUserName
|
||||
Dim c$ = .TrueBoardName
|
||||
If Not c.IsEmptyString Then c &= "/"
|
||||
Return $"https://www.pinterest.com/{n}/{c}"
|
||||
End With
|
||||
End Function
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
If Not Media.Post.ID.IsEmptyString Then
|
||||
Return $"https://www.pinterest.com/pin/{Media.Post.ID}/"
|
||||
Else
|
||||
Return String.Empty
|
||||
End If
|
||||
End Function
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
330
SCrawler/API/Pinterest/UserData.vb
Normal file
330
SCrawler/API/Pinterest/UserData.vb
Normal file
@@ -0,0 +1,330 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.Base.GDL
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Namespace API.Pinterest
|
||||
Friend Class UserData : Inherits UserDataBase
|
||||
#Region "XML names"
|
||||
Private Const Name_IsUser As String = "IsUser"
|
||||
Private Const Name_TrueUserName As String = "TrueUserName"
|
||||
Private Const Name_TrueBoardName As String = "TrueBoardName"
|
||||
#End Region
|
||||
#Region "Structures"
|
||||
Private Structure BoardInfo
|
||||
Friend ID As String
|
||||
Friend Title As String
|
||||
Friend URL As String
|
||||
Friend Description As String
|
||||
Friend UserID As String
|
||||
Friend UserTitle As String
|
||||
End Structure
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Private ReadOnly Property MySettings As SiteSettings
|
||||
Get
|
||||
Return HOST.Source
|
||||
End Get
|
||||
End Property
|
||||
Friend Property TrueUserName As String
|
||||
Friend Property TrueBoardName As String
|
||||
Friend Property IsUser As Boolean
|
||||
#End Region
|
||||
#Region "Load"
|
||||
Private Function ReconfUserName() As Boolean
|
||||
If TrueUserName.IsEmptyString Then
|
||||
Dim n$() = Name.Split("@")
|
||||
If n.ListExists Then
|
||||
TrueUserName = n(0)
|
||||
IsUser = True
|
||||
If n.Length > 1 Then TrueBoardName = n(1) : IsUser = False
|
||||
If Not IsSavedPosts And Not IsSingleObjectDownload Then
|
||||
Dim l$ = IIf(IsUser, UserLabelName, "Board")
|
||||
Settings.Labels.Add(l)
|
||||
Labels.ListAddValue(l, LNC)
|
||||
Labels.Sort()
|
||||
End If
|
||||
Return True
|
||||
End If
|
||||
End If
|
||||
Return False
|
||||
End Function
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
With Container
|
||||
If Loading Then
|
||||
TrueUserName = .Value(Name_TrueUserName)
|
||||
TrueBoardName = .Value(Name_TrueBoardName)
|
||||
IsUser = .Value(Name_IsUser).FromXML(Of Boolean)(False)
|
||||
ReconfUserName()
|
||||
Else
|
||||
If ReconfUserName() Then .Value(Name_LabelsName) = Labels.ListToString("|", EDP.ReturnValue)
|
||||
.Add(Name_TrueUserName, TrueUserName)
|
||||
.Add(Name_TrueBoardName, TrueBoardName)
|
||||
.Add(Name_IsUser, IsUser.BoolToInteger)
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download overrides"
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
Dim URL$ = String.Empty
|
||||
Try
|
||||
If IsSavedPosts Then
|
||||
IsUser = True
|
||||
TrueUserName = MySettings.SavedPostsUserName.Value
|
||||
If TrueUserName.IsEmptyString Then Throw New ArgumentNullException("SavedPostsUserName", "Saved posts user not set")
|
||||
End If
|
||||
Dim boards As List(Of BoardInfo)
|
||||
Dim board As BoardInfo
|
||||
Dim b$ = TrueBoardName
|
||||
If Not b.IsEmptyString Then b &= "/"
|
||||
URL = $"https://www.pinterest.com/{TrueUserName}/{b}"
|
||||
If IsUser Then
|
||||
boards = GetBoards(Token)
|
||||
Else
|
||||
boards = New List(Of BoardInfo) From {New BoardInfo With {.URL = URL, .ID = ID, .Title = UserSiteName}}
|
||||
End If
|
||||
If boards.ListExists Then
|
||||
For i% = 0 To boards.Count - 1
|
||||
ThrowAny(Token)
|
||||
board = boards(i)
|
||||
DownloadBoardImages(board, Token)
|
||||
boards(i) = board
|
||||
Next
|
||||
With boards.First
|
||||
If IsUser Then
|
||||
If ID.IsEmptyString Then ID = .UserID
|
||||
UserSiteNameUpdate(.UserTitle)
|
||||
Else
|
||||
If ID.IsEmptyString Then ID = .ID
|
||||
UserSiteNameUpdate(.Title)
|
||||
UserDescriptionUpdate(.Description)
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Get boards, images"
|
||||
Private Function GetBoards(ByVal Token As CancellationToken) As List(Of BoardInfo)
|
||||
Dim URL$ = $"https://www.pinterest.com/{TrueUserName}/"
|
||||
Try
|
||||
Dim boards As New List(Of BoardInfo)
|
||||
Dim b As BoardInfo
|
||||
Dim r$
|
||||
Dim j As EContainer, jj As EContainer
|
||||
Dim rootNode$() = {"resource_response", "data"}
|
||||
Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue)
|
||||
Dim urls As List(Of String) = GetDataFromGalleryDL(URL, True)
|
||||
If urls.ListExists Then urls.RemoveAll(Function(__url) Not __url.Contains("BoardsResource/get/"))
|
||||
If urls.ListExists Then
|
||||
For Each URL In urls
|
||||
ThrowAny(Token)
|
||||
r = Responser.GetResponse(URL,, EDP.ReturnValue)
|
||||
If Not r.IsEmptyString Then
|
||||
j = JsonDocument.Parse(r, jErr)
|
||||
If Not j Is Nothing Then
|
||||
If If(j(rootNode)?.Count, 0) > 0 Then
|
||||
For Each jj In j(rootNode)
|
||||
b = New BoardInfo With {
|
||||
.URL = jj.Value("url"),
|
||||
.Title = TitleHtmlConverter(jj.Value("name")),
|
||||
.ID = jj.Value("id")
|
||||
}
|
||||
If Not b.URL.IsEmptyString Then
|
||||
b.URL = $"https://www.pinterest.com/{b.URL.StringTrimStart("/").StringTrimEnd("/")}/"
|
||||
boards.Add(b)
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
j.Dispose()
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Return boards
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"data (gallery-dl boards) downloading error [{URL}]")
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
Private Sub DownloadBoardImages(ByRef Board As BoardInfo, ByVal Token As CancellationToken)
|
||||
Dim bUrl$ = String.Empty
|
||||
Try
|
||||
Dim r$
|
||||
Dim j As EContainer, jj As EContainer
|
||||
Dim u As UserMedia
|
||||
Dim folder$ = If(IsUser, Board.Title.IfNullOrEmpty(Board.ID), String.Empty)
|
||||
Dim titleExists As Boolean = Not Board.Title.IsEmptyString
|
||||
Dim i% = -1
|
||||
Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue)
|
||||
Dim rootNode$() = {"resource_response", "data"}
|
||||
Dim images As List(Of Sizes)
|
||||
Dim imgSelector As Func(Of EContainer, Sizes) = Function(img) New Sizes(img.Value("width"), img.Value("url"))
|
||||
Dim fullData As Predicate(Of EContainer) = Function(e) e.Count > 5
|
||||
Dim l As List(Of String) = GetDataFromGalleryDL(Board.URL, False)
|
||||
If l.ListExists Then l.RemoveAll(Function(ll) Not ll.Contains("BoardFeedResource/get/"))
|
||||
If l.ListExists Then
|
||||
For Each bUrl In l
|
||||
ThrowAny(Token)
|
||||
r = Responser.GetResponse(bUrl,, EDP.ReturnValue)
|
||||
If Not r.IsEmptyString Then
|
||||
j = JsonDocument.Parse(r, jErr)
|
||||
If Not j Is Nothing Then
|
||||
If If(j(rootNode)?.Count, 0) > 0 Then
|
||||
For Each jj In j(rootNode)
|
||||
With jj
|
||||
If .Contains("images") Then
|
||||
images = .Item("images").Select(imgSelector).ToList
|
||||
If images.Count > 0 Then
|
||||
images.Sort()
|
||||
i += 1
|
||||
u = New UserMedia(images(0).Data) With {
|
||||
.Post = New UserPost(jj.Value("id"), AConvert(Of Date)(jj.Value("created_at"), DateProvider, Nothing)),
|
||||
.Type = UserMedia.Types.Picture,
|
||||
.SpecialFolder = folder
|
||||
}
|
||||
If i = 0 Then
|
||||
If Board.Title.IsEmptyString Or Board.ID.IsEmptyString Then
|
||||
Board.Title = TitleHtmlConverter(.Value({"board"}, "name"))
|
||||
Board.ID = .Value({"board"}, "id")
|
||||
End If
|
||||
Board.UserID = .Value({"board", "owner"}, "id")
|
||||
Board.UserTitle = TitleHtmlConverter(.Value({"board", "owner"}, "full_name"))
|
||||
If Not titleExists And IsUser Then
|
||||
If Not Board.Title.IsEmptyString Then
|
||||
folder = Board.Title
|
||||
ElseIf Not Board.ID.IsEmptyString Then
|
||||
folder = Board.ID
|
||||
End If
|
||||
u.SpecialFolder = folder
|
||||
End If
|
||||
End If
|
||||
|
||||
If Not u.URL.IsEmptyString Then
|
||||
If u.Post.Date.HasValue Then
|
||||
Select Case CheckDatesLimit(u.Post.Date.Value, Nothing)
|
||||
Case DateResult.Skip : _TempPostsList.ListAddValue(u.Post.ID, LNC) : Continue For
|
||||
Case DateResult.Exit : Exit Sub
|
||||
End Select
|
||||
End If
|
||||
If Not _TempPostsList.Contains(u.Post.ID) Then
|
||||
_TempPostsList.ListAddValue(u.Post.ID, LNC)
|
||||
_TempMediaList.ListAddValue(u, LNC)
|
||||
Else
|
||||
Exit For
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End With
|
||||
Next
|
||||
End If
|
||||
j.Dispose()
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"data (gallery-dl images) downloading error [{bUrl}]")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Gallery-DL Support"
|
||||
Private Class GDLBatch : Inherits GDL.GDLBatch
|
||||
Private ReadOnly Property Source As UserData
|
||||
Private ReadOnly IsBoardsRequested As Boolean
|
||||
Friend Sub New(ByRef s As UserData, ByVal IsBoardsRequested As Boolean)
|
||||
MyBase.New
|
||||
Source = s
|
||||
Me.IsBoardsRequested = IsBoardsRequested
|
||||
End Sub
|
||||
Protected Overrides Async Sub OutputDataReceiver(ByVal Sender As Object, ByVal e As DataReceivedEventArgs)
|
||||
If IsBoardsRequested Then
|
||||
Await Validate(e.Data)
|
||||
Else
|
||||
MyBase.OutputDataReceiver(Sender, e)
|
||||
Await Validate(e.Data)
|
||||
End If
|
||||
End Sub
|
||||
Protected Overrides Async Function Validate(ByVal Value As String) As Task
|
||||
If IsBoardsRequested Then
|
||||
If ErrorOutputData.Count > 0 Then
|
||||
If Await Task.Run(Of Boolean)(Function() ErrorOutputData.Exists(Function(ee) Not ee.IsEmptyString AndAlso
|
||||
ee.StartsWith(UrlTextStart))) Then Kill(EDP.None)
|
||||
End If
|
||||
Else
|
||||
If Await Task.Run(Of Boolean)(Function() Not Value.IsEmptyString AndAlso
|
||||
Source._TempPostsList.Exists(Function(v) Value.Contains(v))) Then Kill(EDP.None)
|
||||
End If
|
||||
End Function
|
||||
End Class
|
||||
Private Function GetDataFromGalleryDL(ByVal URL As String, ByVal IsBoardsRequested As Boolean) As List(Of String)
|
||||
Dim command$ = $"gallery-dl --verbose --simulate "
|
||||
Try
|
||||
If Not URL.IsEmptyString Then
|
||||
If MySettings.CookiesNetscapeFile.Exists Then command &= $"--cookies ""{MySettings.CookiesNetscapeFile}"" "
|
||||
command &= URL
|
||||
Using batch As New GDLBatch(Me, IsBoardsRequested)
|
||||
Return GetUrlsFromGalleryDl(batch, command)
|
||||
End Using
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
HasError = True
|
||||
LogError(ex, $"GetJson({command})")
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "DownloadContent"
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "DownloadSingleObject"
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
User = New UserInfo(MySettings.IsMyUser(Data.URL).UserName, HOST)
|
||||
User.File.Path = Data.File.Path
|
||||
SeparateVideoFolder = False
|
||||
ReconfUserName()
|
||||
DownloadDataF(Token)
|
||||
Data.Title = UserSiteName
|
||||
If Data.Title.IsEmptyString Then
|
||||
Data.Title = TrueUserName
|
||||
If Not TrueBoardName.IsEmptyString Then Data.Title &= $"/{TrueBoardName}"
|
||||
End If
|
||||
Dim additPath$ = TitleHtmlConverter(UserSiteName)
|
||||
If additPath.IsEmptyString Then additPath = IIf(IsUser, TrueUserName, TrueBoardName)
|
||||
If Not additPath.IsEmptyString Then
|
||||
Dim f As SFile = User.File
|
||||
f.Path = f.PathWithSeparator & additPath
|
||||
User.File = f
|
||||
f = Data.File
|
||||
f.Path = User.File.Path
|
||||
Data.File = f
|
||||
End If
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadSingleObject_PostProcessing(ByVal Data As IYouTubeMediaContainer, Optional ByVal ResetTitle As Boolean = True)
|
||||
MyBase.DownloadSingleObject_PostProcessing(Data, Data.Title.IsEmptyString Or Not Data.Title = UserSiteName)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Exception"
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
Return 0
|
||||
End Function
|
||||
End Class
|
||||
#End Region
|
||||
End Namespace
|
||||
@@ -23,6 +23,7 @@ Namespace API.PornHub
|
||||
Private ReadOnly RegexVideo_Video_Wrong_Option As RParams = RParams.DM("div class=""thumbnail-info-wrapper clearfix.+?[\r\n\s]*?\<span class=""title.+?[\r\n\s]*?\<a href=""([^""]+?)""[\s]+?title=""([^""]*?)""[\w\W\s\r\n]+?", 0, RegexReturn.ListByMatch)
|
||||
Friend ReadOnly RegexVideo_Video_Wrong_Fields As RField() = {New RField(New RFieldOption(1, RegexVideo_Video_Wrong_Option)), New RField(New RFieldOption(2, RegexVideo_Video_Wrong_Option))}
|
||||
Friend ReadOnly RegexVideo_Video_VideoKey As RParams = RParams.DMS("viewkey=([\w\d]+)", 1, EDP.ReturnValue)
|
||||
Friend ReadOnly RegexVideoPageTitle As RParams = RParams.DMS("meta (property|name)=""[^:]+?:title"" content=""([^""]+)""", 2, EDP.ReturnValue)
|
||||
#End Region
|
||||
#Region "Declarations M3U8"
|
||||
Friend ReadOnly Regex_M3U8_FirstFileRegEx As RParams = RParams.DM(".+?m3u8.*", 0)
|
||||
@@ -38,6 +39,7 @@ Namespace API.PornHub
|
||||
Friend ReadOnly Regex_Photo_PornHub_AlbumPhotoArr As RParams = RParams.DMS("\<a href=""(/photo/\d+)""", 1, RegexReturn.List, EDP.ReturnValue,
|
||||
CType(Function(Input$) If(Input.IsEmptyString, String.Empty, $"https://www.pornhub.com{Input.Trim}"), Func(Of String, String)))
|
||||
Friend ReadOnly Regex_Photo_PornHub_SinglePhoto As RParams = RParams.DMS("(?<!thumbImage.+?)<img src=""(https://[^""]+\d+[^""]+)""", 1, EDP.ReturnValue)
|
||||
Friend ReadOnly Regex_Photo_File As RParams = RParams.DM("\d+\.[\w]{3,4}", 0, EDP.ReturnValue)
|
||||
#End Region
|
||||
End Module
|
||||
End Namespace
|
||||
@@ -6,10 +6,12 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.Base.M3U8Declarations
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.PornHub
|
||||
Friend NotInheritable Class M3U8
|
||||
Private Sub New()
|
||||
@@ -35,8 +37,9 @@ Namespace API.PornHub
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Friend Shared Function Download(ByVal URL As String, ByVal Responser As Responser, ByVal Destination As SFile) As SFile
|
||||
Return M3U8Base.Download(GetUrlsList(URL, Responser), Destination, Responser)
|
||||
Friend Shared Function Download(ByVal URL As String, ByVal Responser As Responser, ByVal Destination As SFile,
|
||||
ByVal Token As CancellationToken, ByVal Progress As MyProgress) As SFile
|
||||
Return M3U8Base.Download(GetUrlsList(URL, Responser), Destination, Responser, Token, Progress)
|
||||
End Function
|
||||
End Class
|
||||
End Namespace
|
||||
118
SCrawler/API/PornHub/OptionsForm.Designer.vb
generated
118
SCrawler/API/PornHub/OptionsForm.Designer.vb
generated
@@ -1,118 +0,0 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.PornHub
|
||||
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||
Partial Friend Class OptionsForm : Inherits System.Windows.Forms.Form
|
||||
<System.Diagnostics.DebuggerNonUserCode()>
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
Try
|
||||
If disposing AndAlso components IsNot Nothing Then
|
||||
components.Dispose()
|
||||
End If
|
||||
Finally
|
||||
MyBase.Dispose(disposing)
|
||||
End Try
|
||||
End Sub
|
||||
Private components As System.ComponentModel.IContainer
|
||||
<System.Diagnostics.DebuggerStepThrough()>
|
||||
Private Sub InitializeComponent()
|
||||
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
|
||||
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
|
||||
Me.CH_DOWN_GIFS = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_DOWN_PHOTO_MODELHUB = New System.Windows.Forms.CheckBox()
|
||||
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||
CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||
CONTAINER_MAIN.SuspendLayout()
|
||||
TP_MAIN.SuspendLayout()
|
||||
Me.SuspendLayout()
|
||||
'
|
||||
'CONTAINER_MAIN
|
||||
'
|
||||
'
|
||||
'CONTAINER_MAIN.ContentPanel
|
||||
'
|
||||
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
|
||||
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(278, 52)
|
||||
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||
CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Size = New System.Drawing.Size(278, 77)
|
||||
CONTAINER_MAIN.TabIndex = 0
|
||||
CONTAINER_MAIN.TopToolStripPanelVisible = False
|
||||
'
|
||||
'TP_MAIN
|
||||
'
|
||||
TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||
TP_MAIN.ColumnCount = 1
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Controls.Add(Me.CH_DOWN_GIFS, 0, 0)
|
||||
TP_MAIN.Controls.Add(Me.CH_DOWN_PHOTO_MODELHUB, 0, 1)
|
||||
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
TP_MAIN.Name = "TP_MAIN"
|
||||
TP_MAIN.RowCount = 3
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Size = New System.Drawing.Size(278, 52)
|
||||
TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'CH_DOWN_GIFS
|
||||
'
|
||||
Me.CH_DOWN_GIFS.AutoSize = True
|
||||
Me.CH_DOWN_GIFS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_DOWN_GIFS.Location = New System.Drawing.Point(4, 4)
|
||||
Me.CH_DOWN_GIFS.Name = "CH_DOWN_GIFS"
|
||||
Me.CH_DOWN_GIFS.Size = New System.Drawing.Size(270, 19)
|
||||
Me.CH_DOWN_GIFS.TabIndex = 0
|
||||
Me.CH_DOWN_GIFS.Text = "Download gifs"
|
||||
Me.CH_DOWN_GIFS.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_DOWN_PHOTO_MODELHUB
|
||||
'
|
||||
Me.CH_DOWN_PHOTO_MODELHUB.AutoSize = True
|
||||
Me.CH_DOWN_PHOTO_MODELHUB.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_DOWN_PHOTO_MODELHUB.Location = New System.Drawing.Point(4, 30)
|
||||
Me.CH_DOWN_PHOTO_MODELHUB.Name = "CH_DOWN_PHOTO_MODELHUB"
|
||||
Me.CH_DOWN_PHOTO_MODELHUB.Size = New System.Drawing.Size(270, 19)
|
||||
Me.CH_DOWN_PHOTO_MODELHUB.TabIndex = 1
|
||||
Me.CH_DOWN_PHOTO_MODELHUB.Text = "Download photo only from ModelHub"
|
||||
Me.CH_DOWN_PHOTO_MODELHUB.UseVisualStyleBackColor = True
|
||||
'
|
||||
'OptionsForm
|
||||
'
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(278, 77)
|
||||
Me.Controls.Add(CONTAINER_MAIN)
|
||||
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
||||
Me.Icon = Global.SCrawler.My.Resources.SiteResources.InstagramIcon_32
|
||||
Me.KeyPreview = True
|
||||
Me.MaximizeBox = False
|
||||
Me.MaximumSize = New System.Drawing.Size(294, 116)
|
||||
Me.MinimizeBox = False
|
||||
Me.MinimumSize = New System.Drawing.Size(294, 116)
|
||||
Me.Name = "OptionsForm"
|
||||
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
||||
Me.Text = "Options"
|
||||
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
CONTAINER_MAIN.ResumeLayout(False)
|
||||
CONTAINER_MAIN.PerformLayout()
|
||||
TP_MAIN.ResumeLayout(False)
|
||||
TP_MAIN.PerformLayout()
|
||||
Me.ResumeLayout(False)
|
||||
|
||||
End Sub
|
||||
Private WithEvents CH_DOWN_GIFS As CheckBox
|
||||
Private WithEvents CH_DOWN_PHOTO_MODELHUB As CheckBox
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -1,34 +0,0 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Forms
|
||||
Namespace API.PornHub
|
||||
Friend Class OptionsForm
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Private ReadOnly MyExchangeOptions As UserExchangeOptions
|
||||
Friend Sub New(ByRef ExchangeOptions As UserExchangeOptions)
|
||||
InitializeComponent()
|
||||
MyExchangeOptions = ExchangeOptions
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
End Sub
|
||||
Private Sub MyForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
.MyViewInitialize(True)
|
||||
.AddOkCancelToolbar()
|
||||
CH_DOWN_GIFS.Checked = MyExchangeOptions.DownloadGifs
|
||||
CH_DOWN_PHOTO_MODELHUB.Checked = MyExchangeOptions.DownloadPhotoOnlyFromModelHub
|
||||
.EndLoaderOperations()
|
||||
End With
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
|
||||
MyExchangeOptions.DownloadGifs = CH_DOWN_GIFS.Checked
|
||||
MyExchangeOptions.DownloadPhotoOnlyFromModelHub = CH_DOWN_PHOTO_MODELHUB.Checked
|
||||
MyDefs.CloseForm()
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -25,7 +25,6 @@ Namespace API.PornHub
|
||||
Return My.Resources.SiteResources.PornHubPic_16
|
||||
End Get
|
||||
End Property
|
||||
Private ReadOnly Property CurlPathExists As Boolean
|
||||
<PropertyOption(ControlText:="Download GIF", ControlToolTip:="Default for new users", ThreeStates:=True), PXML>
|
||||
Friend ReadOnly Property DownloadGifs As PropertyValue
|
||||
<PropertyOption(ControlText:="Download GIFs as mp4", ControlToolTip:="Download gifs in 'mp4' format instead of native 'webm'"), PXML>
|
||||
@@ -40,10 +39,7 @@ Namespace API.PornHub
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
MyBase.New("PornHub", "pornhub.com")
|
||||
Responser.CurlPath = $"cURL\curl.exe"
|
||||
Responser.CurlArgumentsRight = "--ssl-no-revoke"
|
||||
CurlPathExists = Responser.CurlPath.Exists
|
||||
Responser.DeclaredError = EDP.ThrowException
|
||||
With Responser : .CurlSslNoRevoke = True : .CurlInsecure = True : End With
|
||||
|
||||
DownloadGifsAsMp4 = New PropertyValue(True)
|
||||
DownloadGifs = New PropertyValue(CInt(CheckState.Indeterminate), GetType(Integer))
|
||||
@@ -55,37 +51,16 @@ Namespace API.PornHub
|
||||
ImageVideoContains = "pornhub"
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetInstance, GetSpecialData"
|
||||
#Region "GetInstance"
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
If What = ISiteSettings.Download.SavedPosts Then
|
||||
Return New UserData With {
|
||||
.IsSavedPosts = True,
|
||||
.VideoPageModel = UserData.VideoPageModels.Favorite,
|
||||
.PersonType = UserData.PersonTypeUser,
|
||||
.User = New UserInfo With {.Name = $"{UserData.PersonTypeUser}_{CStr(AConvert(Of String)(SavedPostsUserName.Value, String.Empty))}"}
|
||||
}
|
||||
Else
|
||||
Return New UserData
|
||||
End If
|
||||
End Function
|
||||
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||
If Available(ISiteSettings.Download.Main, True) Then
|
||||
Using resp As Responser = Responser.Copy
|
||||
Dim spf$ = String.Empty
|
||||
Dim f As SFile = GetSpecialDataFile(Path, AskForPath, spf)
|
||||
Dim m As UserMedia = UserData.GetVideoInfo(URL, resp, f)
|
||||
If m.State = UserMedia.States.Downloaded Then
|
||||
m.SpecialFolder = f
|
||||
Return {m}
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
Return Nothing
|
||||
Return New UserData
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Downloading"
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
Return Settings.UseM3U8 And CurlPathExists And (Not What = ISiteSettings.Download.SavedPosts OrElse ACheck(SavedPostsUserName.Value))
|
||||
Responser.CurlPath = Settings.CurlFile
|
||||
Return Settings.UseM3U8 And Settings.CurlFile.Exists And
|
||||
(Not What = ISiteSettings.Download.SavedPosts OrElse (ACheck(SavedPostsUserName.Value) And Responser.CookiesExists))
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IsMyUser"
|
||||
@@ -97,23 +72,20 @@ Namespace API.PornHub
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"[API.PornHub.SiteSettings.IsMyUser({UserURL})]", New ExchangeOptions)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, $"[API.PornHub.SiteSettings.IsMyUser({UserURL})]", New ExchangeOptions)
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "GetUserUrl, GetUserPostUrl"
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider, ByVal Channel As Boolean) As String
|
||||
#Region "GetUserUrl"
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, .PersonType, .NameTrue) : End With
|
||||
End Function
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
Return Media.URL_BASE
|
||||
End Function
|
||||
#End Region
|
||||
#Region "User options"
|
||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||
If Options Is Nothing OrElse Not TypeOf Options Is UserExchangeOptions Then Options = New UserExchangeOptions(Me)
|
||||
If OpenForm Then
|
||||
Using f As New OptionsForm(Options) : f.ShowDialog() : End Using
|
||||
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
@@ -136,6 +137,7 @@ Namespace API.PornHub
|
||||
#Region "Initializer, loader"
|
||||
Friend Sub New()
|
||||
UseInternalM3U8Function = True
|
||||
UseClientTokens = True
|
||||
End Sub
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
With Container
|
||||
@@ -179,7 +181,11 @@ Namespace API.PornHub
|
||||
Responser.ResetStatus()
|
||||
If PersonType = PersonTypeUser Then Responser.Mode = Responser.Modes.Curl
|
||||
|
||||
If IsSavedPosts Then VideoPageModel = VideoPageModels.Favorite
|
||||
If IsSavedPosts Then
|
||||
VideoPageModel = VideoPageModels.Favorite
|
||||
PersonType = PersonTypeUser
|
||||
NameTrue = MySettings.SavedPostsUserName.Value
|
||||
End If
|
||||
|
||||
Dim page% = 1
|
||||
Dim __continue As Boolean = True
|
||||
@@ -295,7 +301,7 @@ Namespace API.PornHub
|
||||
If Not r.IsEmptyString Then
|
||||
Dim n$
|
||||
Dim m As UserMedia = Nothing
|
||||
Dim l As List(Of RegexMatchStruct) = RegexFields(Of RegexMatchStruct)(r, {Regex_Gif_Array}, {1})
|
||||
Dim l As List(Of RegexMatchStruct) = RegexFields(Of RegexMatchStruct)(r, {Regex_Gif_Array}, {1}, EDP.ReturnValue)
|
||||
Dim l2 As List(Of String) = Nothing
|
||||
Dim l3 As List(Of String) = Nothing
|
||||
If l.ListExists Then l2 = l.Select(Function(ll) $"gif/{ll.Arr(0).Replace("gif", String.Empty)}").ToList
|
||||
@@ -336,6 +342,10 @@ Namespace API.PornHub
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download photo"
|
||||
Private Function CreatePhotoFile(ByVal URL As String, ByVal File As SFile) As SFile
|
||||
Dim pFile$ = RegexReplace(URL, Regex_Photo_File)
|
||||
If Not pFile.IsEmptyString Then Return New SFile(pFile) Else Return File
|
||||
End Function
|
||||
Private Const PhotoUrlPattern_ModelHub As String = "https://www.modelhub.com/{0}/photos"
|
||||
Private Const PhotoUrlPattern_PornHub As String = "https://www.pornhub.com/{0}/{1}/photos"
|
||||
Private Sub DownloadUserPhotos(ByVal Token As CancellationToken)
|
||||
@@ -365,7 +375,8 @@ Namespace API.PornHub
|
||||
Private Function DownloadUserPhotos_ModelHub(ByVal Token As CancellationToken) As Boolean
|
||||
Dim URL$ = String.Empty
|
||||
Try
|
||||
Dim jErr As New ErrorsDescriber(EDP.SendInLog + EDP.ReturnValue)
|
||||
Dim j As EContainer
|
||||
Dim jErr As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue)
|
||||
Dim albumName$
|
||||
If PersonType = PersonTypeModel Then
|
||||
URL = String.Format(PhotoUrlPattern_ModelHub, NameTrue)
|
||||
@@ -380,15 +391,16 @@ Namespace API.PornHub
|
||||
albumRegex.Pattern = "<li id=""" & block.AlbumID & """ class=""modelBox"">[\r\n\s]*?<div class=""modelPhoto"">[\r\n\s]*?\<[^\>]*?alt=""([^""]*)"""
|
||||
albumName = StringTrim(RegexReplace(r, albumRegex))
|
||||
If albumName.IsEmptyString Then albumName = block.AlbumID
|
||||
Using j As EContainer = JsonDocument.Parse("{" & block.Data & "}", jErr)
|
||||
If Not j Is Nothing Then
|
||||
If If(j("urls")?.Count, 0) > 0 Then
|
||||
_TempMediaList.ListAddList(j("urls").Select(Function(jj) _
|
||||
New UserMedia(jj.ItemF({0}).XmlIfNothingValue, UTypes.Picture) With {
|
||||
.SpecialFolder = $"Albums\{albumName}\"}), LNC)
|
||||
End If
|
||||
j = JsonDocument.Parse("{" & block.Data & "}", jErr)
|
||||
If Not j Is Nothing Then
|
||||
If If(j("urls")?.Count, 0) > 0 Then
|
||||
_TempMediaList.ListAddList(j("urls").Select(Function(jj) _
|
||||
New UserMedia(jj.ItemF({0}).XmlIfNothingValue, UTypes.Picture) With {
|
||||
.SpecialFolder = $"Albums\{albumName}\",
|
||||
.File = CreatePhotoFile(.URL, .File)}), LNC)
|
||||
End If
|
||||
End Using
|
||||
j.Dispose()
|
||||
End If
|
||||
Next
|
||||
l.Clear()
|
||||
End If
|
||||
@@ -444,7 +456,9 @@ Namespace API.PornHub
|
||||
If Not r.IsEmptyString Then
|
||||
url = RegexReplace(r, Regex_Photo_PornHub_SinglePhoto)
|
||||
If Not url.IsEmptyString Then _
|
||||
_TempMediaList.ListAddValue(New UserMedia(url, UTypes.Picture) With {.SpecialFolder = $"Albums\{AlbumName}\"}, LNC)
|
||||
_TempMediaList.ListAddValue(New UserMedia(url, UTypes.Picture) With {
|
||||
.SpecialFolder = $"Albums\{AlbumName}\",
|
||||
.File = CreatePhotoFile(url, .File)}, LNC)
|
||||
End If
|
||||
Catch
|
||||
End Try
|
||||
@@ -468,7 +482,7 @@ Namespace API.PornHub
|
||||
If r.Contains(HtmlPageNotFoundPhoto) Then Return False
|
||||
Dim urls As List(Of String) = RegexReplace(r, Regex_Photo_PornHub_AlbumPhotoArr)
|
||||
If urls.ListExists Then
|
||||
Dim NewUrl$
|
||||
Dim NewUrl$, pFile$
|
||||
Dim m As UserMedia
|
||||
Dim l2 As List(Of UserMedia) = urls.Select(Function(__url) New UserMedia(__url, UTypes.Picture) With {
|
||||
.Post = __url.Split("/").LastOrDefault}).ToList
|
||||
@@ -487,7 +501,8 @@ Namespace API.PornHub
|
||||
NewUrl = RegexReplace(r, Regex_Photo_PornHub_SinglePhoto)
|
||||
If Not NewUrl.IsEmptyString Then
|
||||
m.URL = NewUrl
|
||||
m.File = NewUrl
|
||||
pFile = RegexReplace(NewUrl, Regex_Photo_File)
|
||||
If Not pFile.IsEmptyString Then m.File = pFile Else m.File = NewUrl
|
||||
_TempPostsList.ListAddValue(m.Post.ID, LNC)
|
||||
Else
|
||||
Throw New Exception
|
||||
@@ -511,13 +526,17 @@ Namespace API.PornHub
|
||||
#End Region
|
||||
#End Region
|
||||
#Region "ReparseVideo"
|
||||
Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken)
|
||||
Protected Overloads Overrides Sub ReparseVideo(ByVal Token As CancellationToken)
|
||||
ReparseVideo(Token, False)
|
||||
End Sub
|
||||
Protected Overloads Sub ReparseVideo(ByVal Token As CancellationToken, ByVal CreateFileName As Boolean,
|
||||
Optional ByRef Data As IYouTubeMediaContainer = Nothing)
|
||||
Const ERR_NEW_URL$ = "ERR_NEW_URL"
|
||||
Dim URL$ = String.Empty
|
||||
Try
|
||||
If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(tm) tm.Type = UTypes.VideoPre) Then
|
||||
Dim m As UserMedia
|
||||
Dim r$, NewUrl$
|
||||
Dim r$, NewUrl$, tmpName$
|
||||
For i% = _TempMediaList.Count - 1 To 0 Step -1
|
||||
If _TempMediaList(i).Type = UTypes.VideoPre Then
|
||||
m = _TempMediaList(i)
|
||||
@@ -532,6 +551,14 @@ Namespace API.PornHub
|
||||
Else
|
||||
m.URL = NewUrl
|
||||
m.Type = UTypes.m3u8
|
||||
If CreateFileName Then
|
||||
tmpName = RegexReplace(r, RegexVideoPageTitle)
|
||||
If Not tmpName.IsEmptyString Then
|
||||
If Not Data Is Nothing Then Data.Title = tmpName
|
||||
m.File.Name = TitleHtmlConverter(tmpName)
|
||||
m.File.Extension = "mp4"
|
||||
End If
|
||||
End If
|
||||
_TempMediaList(i) = m
|
||||
End If
|
||||
Else
|
||||
@@ -565,7 +592,7 @@ Namespace API.PornHub
|
||||
m = _ContentList(i)
|
||||
If m.State = UserMedia.States.Missing AndAlso Not m.URL_BASE.IsEmptyString Then
|
||||
ThrowAny(Token)
|
||||
r = Responser.Curl(m.URL_BASE, eCurl)
|
||||
r = Responser.Curl(m.URL_BASE,, eCurl)
|
||||
If Not r.IsEmptyString Then
|
||||
Dim NewUrl$ = CreateVideoURL(r)
|
||||
If Not NewUrl.IsEmptyString Then
|
||||
@@ -591,12 +618,12 @@ Namespace API.PornHub
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile) As SFile
|
||||
Return M3U8.Download(URL, Responser, DestinationFile)
|
||||
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
|
||||
Return M3U8.Download(URL, Responser, DestinationFile, Token, If(UseInternalM3U8Function_UseProgress, Progress, Nothing))
|
||||
End Function
|
||||
#End Region
|
||||
#Region "CreateVideoURL"
|
||||
Private Shared Function CreateVideoURL(ByVal r As String) As String
|
||||
Private Function CreateVideoURL(ByVal r As String) As String
|
||||
Try
|
||||
Dim OutStr$ = String.Empty
|
||||
If Not r.IsEmptyString Then
|
||||
@@ -619,26 +646,18 @@ Namespace API.PornHub
|
||||
End If
|
||||
Return OutStr
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.PornHub.UserData.CreateVideoURL]", String.Empty)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.PornHub.UserData.CreateVideoURL]", String.Empty)
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Standalone downloader"
|
||||
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal Responser As Responser, ByVal Destination As SFile) As UserMedia
|
||||
Try
|
||||
Dim r$ = Responser.Curl(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Dim NewUrl$ = CreateVideoURL(r)
|
||||
If Not NewUrl.IsEmptyString Then
|
||||
Dim f As SFile = M3U8.Download(NewUrl, Responser, Destination)
|
||||
If Not f.IsEmptyString Then Return New UserMedia With {.State = UserMedia.States.Downloaded}
|
||||
End If
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"PornHub standalone download error: [{URL}]", New UserMedia)
|
||||
End Try
|
||||
End Function
|
||||
#Region "DownloadSingleObject"
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
_TempMediaList.Add(New UserMedia(Data.URL, UTypes.VideoPre))
|
||||
ReparseVideo(Token, True, Data)
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadSingleObject_PostProcessing(ByVal Data As IYouTubeMediaContainer, Optional ByVal ResetTitle As Boolean = True)
|
||||
MyBase.DownloadSingleObject_PostProcessing(Data, False)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Exceptions"
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String,
|
||||
|
||||
@@ -6,18 +6,24 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Namespace API.PornHub
|
||||
Friend Class UserExchangeOptions
|
||||
<PSetting(Caption:="Download gifs")>
|
||||
Friend Property DownloadGifs As Boolean
|
||||
<PSetting(NameOf(SiteSettings.DownloadPhotoOnlyFromModelHub), NameOf(MySettings), Caption:="Download photo only from ModelHub")>
|
||||
Friend Property DownloadPhotoOnlyFromModelHub As Boolean
|
||||
Private ReadOnly Property MySettings As SiteSettings
|
||||
Friend Sub New(ByVal u As UserData)
|
||||
DownloadGifs = u.DownloadGifs
|
||||
DownloadPhotoOnlyFromModelHub = u.DownloadPhotoOnlyFromModelHub
|
||||
MySettings = u.HOST.Source
|
||||
End Sub
|
||||
Friend Sub New(ByVal s As SiteSettings)
|
||||
Dim v As CheckState = CInt(s.DownloadGifs.Value)
|
||||
DownloadGifs = Not v = CheckState.Unchecked
|
||||
DownloadPhotoOnlyFromModelHub = s.DownloadPhotoOnlyFromModelHub.Value
|
||||
MySettings = s
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -258,7 +258,8 @@ Namespace API.Reddit
|
||||
.Progress = p,
|
||||
.SaveToCache = True,
|
||||
.SkipExistsUsers = SkipExists,
|
||||
.ChannelInfo = Me
|
||||
.ChannelInfo = Me,
|
||||
.IsChannel = True
|
||||
}
|
||||
With d
|
||||
.SetEnvironment(HOST, CUser, False)
|
||||
@@ -306,7 +307,7 @@ Namespace API.Reddit
|
||||
Friend Function GetEnumerator() As IEnumerator(Of UserPost) Implements IEnumerable(Of UserPost).GetEnumerator
|
||||
Return New MyEnumerator(Of UserPost)(Me)
|
||||
End Function
|
||||
Friend Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
|
||||
Private Function IEnumerable_GetEnumerator() As IEnumerator Implements IEnumerable.GetEnumerator
|
||||
Return GetEnumerator()
|
||||
End Function
|
||||
#End Region
|
||||
@@ -373,7 +374,7 @@ Namespace API.Reddit
|
||||
Dim l As New List(Of String)
|
||||
If Posts.Count > 0 Or PostsLatest.Count > 0 Then l.ListAddList((From p In PostsAll Where Not p.ID.IsEmptyString Select p.ID), LNC)
|
||||
l.ListAddList(PostsNames, LNC)
|
||||
If l.Count > 0 Then TextSaver.SaveTextToFile(l.ListToString("|"), FilePosts, True,, EDP.SendInLog)
|
||||
If l.Count > 0 Then TextSaver.SaveTextToFile(l.ListToString("|"), FilePosts, True,, EDP.SendToLog)
|
||||
End If
|
||||
Using x As New XmlFile With {.AllowSameNames = True, .Name = "Channel"}
|
||||
x.Add(Name_Name, Name)
|
||||
@@ -418,7 +419,7 @@ Namespace API.Reddit
|
||||
CountOfAddedUsers.Clear()
|
||||
CountOfLoadedPostsPerSession.Clear()
|
||||
ChannelExistentUserNames.Clear()
|
||||
CachePath.Delete(SFO.Path, SFODelete.None, EDP.SendInLog)
|
||||
CachePath.Delete(SFO.Path, SFODelete.None, EDP.SendToLog)
|
||||
End If
|
||||
disposedValue = True
|
||||
End If
|
||||
|
||||
@@ -55,7 +55,7 @@ Namespace API.Reddit
|
||||
Return Nothing
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[API.Reddit.ChannelsCollection.GetUserFiles]")
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[API.Reddit.ChannelsCollection.GetUserFiles]")
|
||||
End Try
|
||||
End Function
|
||||
Friend Sub UpdateUsersStats()
|
||||
|
||||
@@ -15,10 +15,12 @@ Namespace API.Reddit
|
||||
Friend ReadOnly JsonNodesJson() As NodeParams = {New NodeParams("posts", True, True, True, True, 3)}
|
||||
Friend ReadOnly ChannelJsonNodes() As NodeParams = {New NodeParams("data", True, True, True, True, 1),
|
||||
New NodeParams("children", True, True, True)}
|
||||
Friend ReadOnly SingleJsonNodes() As NodeParams = {New NodeParams("data", True, True, True, True, 2),
|
||||
New NodeParams("children", True, True, True),
|
||||
New NodeParams("data", True, True, True, True, 1)}
|
||||
Friend ReadOnly UrlBasePattern As RParams = RParams.DM("(?<=/)([^/]+?\.[\w]{3,4})(?=(\?|\Z))", 0)
|
||||
Friend ReadOnly VideoRegEx As RParams = RParams.DM("http.{0,1}://[^" & Chr(34) & "]+?mp4", 0)
|
||||
Private ReadOnly EUR_PROVIDER As New ANumbers(ANumbers.Cultures.EUR)
|
||||
Friend ReadOnly DateProvider As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicodeJS(v, n, e))
|
||||
Friend ReadOnly DateProviderChannel As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicode(AConvert(Of Integer)(v, EUR_PROVIDER, v), n, e))
|
||||
Friend ReadOnly UnixDate32ProviderReddit As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnix32(AConvert(Of Integer)(v, EUR_PROVIDER, v), n, e))
|
||||
End Module
|
||||
End Namespace
|
||||
@@ -7,8 +7,11 @@
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Net
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Reddit.M3U8_Declarations
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Tools.Web
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Reddit
|
||||
Namespace M3U8_Declarations
|
||||
@@ -19,7 +22,7 @@ Namespace API.Reddit
|
||||
''' <summary>Audio, Video</summary>
|
||||
Friend ReadOnly PlayListRegEx_2 As RParams = RParams.DM("(?<=#EXT-X-BYTERANGE.+?[\r\n]{1,2})(.+)(?=[\r\n]{0,2})", 0, RegexReturn.List)
|
||||
Friend ReadOnly PlayListAudioRegEx As RParams = RParams.DM("(HLS_AUDIO_(\d+)[^""]+)", 0, RegexReturn.List)
|
||||
Friend ReadOnly DPED As New ErrorsDescriber(EDP.SendInLog + EDP.ReturnValue)
|
||||
Friend ReadOnly DPED As New ErrorsDescriber(EDP.SendToLog + EDP.ReturnValue)
|
||||
End Module
|
||||
End Namespace
|
||||
Friend NotInheritable Class M3U8 : Implements IDisposable
|
||||
@@ -52,9 +55,12 @@ Namespace API.Reddit
|
||||
Private OutFile As SFile
|
||||
Private VideoFile As SFile
|
||||
Private AudioFile As SFile
|
||||
Private CachePath As SFile
|
||||
Private ReadOnly Cache As CacheKeeper
|
||||
Private ReadOnly CacheFiles As CacheKeeper
|
||||
Private ReadOnly Property Progress As MyProgress
|
||||
Private ReadOnly ProgressExists As Boolean
|
||||
#End Region
|
||||
Private Sub New(ByVal URL As String, ByVal OutFile As SFile)
|
||||
Private Sub New(ByVal URL As String, ByVal OutFile As SFile, ByVal Progress As MyProgress)
|
||||
PlayListURL = URL
|
||||
BaseURL = RegexReplace(URL, BaseUrlPattern)
|
||||
Video = New List(Of String)
|
||||
@@ -62,7 +68,10 @@ Namespace API.Reddit
|
||||
Me.OutFile = OutFile
|
||||
Me.OutFile.Name = "PlayListFile"
|
||||
Me.OutFile.Extension = "mp4"
|
||||
CachePath = $"{OutFile.PathWithSeparator}_Cache\{SFile.GetDirectories($"{OutFile.PathWithSeparator}_Cache\",,, EDP.ReturnValue).ListIfNothing.Count + 1}\"
|
||||
Me.Progress = Progress
|
||||
ProgressExists = Not Me.Progress Is Nothing
|
||||
Cache = New CacheKeeper($"{OutFile.PathWithSeparator}_{Base.M3U8Base.TempCacheFolderName}\")
|
||||
CacheFiles = Cache.NewInstance
|
||||
End Sub
|
||||
#Region "Internal functions"
|
||||
#Region "GetPlaylistUrls"
|
||||
@@ -78,7 +87,7 @@ Namespace API.Reddit
|
||||
If Not r.IsEmptyString Then
|
||||
Dim l As New List(Of Resolution)
|
||||
If Type = Types.Video Then
|
||||
l = RegexFields(Of Resolution)(r, {PlayListRegEx_1}, {6, 4})
|
||||
l = RegexFields(Of Resolution)(r, {PlayListRegEx_1}, {6, 4}, EDP.ReturnValue)
|
||||
Else
|
||||
Try
|
||||
l = RegexFields(Of Resolution)(r, {PlayListAudioRegEx}, {1, 2})
|
||||
@@ -112,41 +121,44 @@ Namespace API.Reddit
|
||||
End Function
|
||||
#End Region
|
||||
#Region "ConcatData"
|
||||
Private Overloads Sub ConcatData()
|
||||
ConcatData(Video, Types.Video, VideoFile)
|
||||
ConcatData(Audio, Types.Audio, AudioFile)
|
||||
Private Overloads Sub ConcatData(ByVal Token As CancellationToken)
|
||||
ConcatData(Video, Types.Video, VideoFile, Token)
|
||||
ConcatData(Audio, Types.Audio, AudioFile, Token)
|
||||
MergeFiles()
|
||||
End Sub
|
||||
Private Overloads Sub ConcatData(ByVal Urls As List(Of String), ByVal Type As Types, ByRef TFile As SFile)
|
||||
Private Overloads Sub ConcatData(ByVal Urls As List(Of String), ByVal Type As Types, ByRef TFile As SFile, ByVal Token As CancellationToken)
|
||||
Try
|
||||
Token.ThrowIfCancellationRequested()
|
||||
If Urls.ListExists Then
|
||||
Dim ConcatFile As SFile = OutFile
|
||||
Dim tmpCache As CacheKeeper = CacheFiles.NewInstance
|
||||
Dim ConcatFile As SFile = CacheFiles
|
||||
If Type = Types.Audio Then
|
||||
ConcatFile.Name &= "_AUDIO"
|
||||
ConcatFile.Name &= "AUDIO"
|
||||
ConcatFile.Extension = "aac"
|
||||
Else
|
||||
If Audio.Count > 0 Then ConcatFile.Name &= "_VIDEO"
|
||||
If Audio.Count > 0 Then ConcatFile.Name &= "VIDEO"
|
||||
ConcatFile.Extension = "mp4"
|
||||
End If
|
||||
If CachePath.Exists(SFO.Path) Then
|
||||
Dim p As New SFileNumbers(ConcatFile.Name,,, New ANumbers With {.Format = ANumbers.Formats.General})
|
||||
ConcatFile = SFile.Indexed_IndexFile(ConcatFile,, p, EDP.ThrowException)
|
||||
If tmpCache.Validate Then
|
||||
Dim i%
|
||||
Dim eFiles As New List(Of SFile)
|
||||
Dim dFile As SFile = CachePath
|
||||
Dim dFile As SFile = tmpCache.RootDirectory
|
||||
If ProgressExists Then Progress.Maximum += Urls.Count
|
||||
dFile.Extension = New SFile(Urls(0)).Extension
|
||||
If dFile.Extension.IsEmptyString Then dFile.Extension = "ts"
|
||||
Using w As New WebClient
|
||||
For i = 0 To Urls.Count - 1
|
||||
If ProgressExists Then Progress.Perform()
|
||||
Token.ThrowIfCancellationRequested()
|
||||
dFile.Name = $"ConPart_{i}"
|
||||
w.DownloadFile(Urls(i), dFile)
|
||||
eFiles.Add(dFile)
|
||||
tmpCache.AddFile(dFile, True)
|
||||
Next
|
||||
End Using
|
||||
TFile = FFMPEG.ConcatenateFiles(eFiles, Settings.FfmpegFile, ConcatFile, p, DPED)
|
||||
eFiles.Clear()
|
||||
TFile = FFMPEG.ConcatenateFiles(tmpCache, Settings.FfmpegFile.File, ConcatFile, Settings.CMDEncoding,, DPED)
|
||||
End If
|
||||
End If
|
||||
Catch oex As OperationCanceledException When Token.IsCancellationRequested
|
||||
Throw oex
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(DPED, ex, $"[M3U8.Save({Type})]")
|
||||
End Try
|
||||
@@ -154,25 +166,27 @@ Namespace API.Reddit
|
||||
#End Region
|
||||
Private Sub MergeFiles()
|
||||
Try
|
||||
Dim p As SFileNumbers = SFileNumbers.Default(OutFile.Name)
|
||||
Dim f As SFile = SFile.IndexReindex(OutFile,,, p, EDP.ReturnValue)
|
||||
If Not VideoFile.IsEmptyString And Not AudioFile.IsEmptyString Then
|
||||
Dim p As New SFileNumbers(OutFile.Name,, RParams.DMS("PlayListFile_(\d*)", 1), New ANumbers With {.Format = ANumbers.Formats.General})
|
||||
OutFile = FFMPEG.MergeFiles({VideoFile, AudioFile}, Settings.FfmpegFile, OutFile, p, DPED)
|
||||
OutFile = FFMPEG.MergeFiles({VideoFile, AudioFile}, Settings.FfmpegFile.File, f, Settings.CMDEncoding, p, DPED)
|
||||
Else
|
||||
OutFile = VideoFile
|
||||
If f.IsEmptyString Then f = OutFile
|
||||
If Not SFile.Move(VideoFile, f) Then OutFile = VideoFile
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(DPED, ex, $"[M3U8.MergeFiles]")
|
||||
End Try
|
||||
End Sub
|
||||
Friend Function Download() As SFile
|
||||
Friend Function Download(ByVal Token As CancellationToken) As SFile
|
||||
GetPlaylistUrls()
|
||||
ConcatData()
|
||||
ConcatData(Token)
|
||||
Return OutFile
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Statics"
|
||||
Friend Shared Function Download(ByVal URL As String, ByVal f As SFile) As SFile
|
||||
Using m As New M3U8(URL, f) : Return m.Download() : End Using
|
||||
Friend Shared Function Download(ByVal URL As String, ByVal f As SFile, ByVal Token As CancellationToken, ByVal Progress As MyProgress) As SFile
|
||||
Using m As New M3U8(URL, f, Progress) : Return m.Download(Token) : End Using
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IDisposable Support"
|
||||
@@ -182,7 +196,7 @@ Namespace API.Reddit
|
||||
If disposing Then
|
||||
Video.Clear()
|
||||
Audio.Clear()
|
||||
CachePath.Delete(SFO.Path, SFODelete.None, DPED)
|
||||
Cache.Dispose()
|
||||
End If
|
||||
disposedValue = True
|
||||
End If
|
||||
|
||||
@@ -18,7 +18,7 @@ Namespace API.Reddit
|
||||
MyOptions = opt
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
End Sub
|
||||
Private Sub ChannelSettingsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
Private Sub RedditViewSettingsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
Try
|
||||
Dim n$ = String.Empty
|
||||
If TypeOf MyOptions Is Channel Then
|
||||
|
||||
@@ -38,31 +38,28 @@ Namespace API.Reddit
|
||||
End With
|
||||
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
|
||||
UseM3U8 = New PropertyValue(True)
|
||||
UrlPatternUser = "https://www.reddit.com/user/{0}/"
|
||||
UrlPatternChannel = "https://www.reddit.com/r/{0}/"
|
||||
UrlPatternUser = "https://www.reddit.com/{0}/{1}/"
|
||||
ImageVideoContains = "reddit.com"
|
||||
UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
|
||||
End Sub
|
||||
Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider
|
||||
Select Case What
|
||||
Case Download.Main : Return New UserData
|
||||
Case Download.Channel : Return New UserData With {.SaveToCache = False, .SkipExistsUsers = False, .AutoGetLimits = True}
|
||||
Case Download.SavedPosts
|
||||
Dim u As New UserData With {.IsSavedPosts = True}
|
||||
DirectCast(u, UserDataBase).User = New UserInfo With {
|
||||
.Name = CStr(AConvert(Of String)(SavedPostsUserName.Value, String.Empty)),
|
||||
.IsChannel = True
|
||||
}
|
||||
Return u
|
||||
End Select
|
||||
Return Nothing
|
||||
Return New UserData
|
||||
End Function
|
||||
Friend Const ChannelOption As String = "r"
|
||||
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
|
||||
Dim l As List(Of String) = RegexReplace(UserURL, UserRegex)
|
||||
If l.ListExists(3) Then Return New ExchangeOptions(Site, l(2), l(1) = "r") Else Return Nothing
|
||||
If l.ListExists(3) Then
|
||||
Dim n$ = l(2)
|
||||
If Not l(1).IsEmptyString AndAlso l(1) = ChannelOption Then n &= $"@{ChannelOption}"
|
||||
Return New ExchangeOptions(Site, n)
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
|
||||
Try
|
||||
Dim trueValue As Boolean = Not What = Download.SavedPosts OrElse (Responser.CookiesExists And ACheck(SavedPostsUserName.Value))
|
||||
If Not trueValue Then Return False
|
||||
Dim dl As List(Of DownDetector.Data) = DownDetector.GetData("reddit")
|
||||
If dl.ListExists Then
|
||||
dl = dl.Take(4).ToList
|
||||
@@ -76,7 +73,7 @@ Namespace API.Reddit
|
||||
dl.ListToString(vbCr) & vbCr & vbCr &
|
||||
"Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes Then
|
||||
UpdateRedGifsToken()
|
||||
Return True
|
||||
Return trueValue
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
@@ -84,28 +81,29 @@ Namespace API.Reddit
|
||||
End If
|
||||
End If
|
||||
UpdateRedGifsToken()
|
||||
Return True
|
||||
Return trueValue
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[API.Reddit.SiteSettings.Available]", True)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[API.Reddit.SiteSettings.Available]", True)
|
||||
End Try
|
||||
End Function
|
||||
Private Sub UpdateRedGifsToken()
|
||||
DirectCast(Settings(RedGifs.RedGifsSiteKey).Source, RedGifs.SiteSettings).UpdateTokenIfRequired()
|
||||
End Sub
|
||||
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||
Dim spf$ = String.Empty
|
||||
Dim f As SFile = GetSpecialDataFile(Path, AskForPath, spf)
|
||||
f = $"{f.PathWithSeparator}OptionalPath\"
|
||||
Return UserData.GetVideoInfo(URL, Responser, f, spf)
|
||||
End Function
|
||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||
If Options Is Nothing OrElse Not TypeOf Options Is RedditViewExchange Then Options = New RedditViewExchange
|
||||
If OpenForm Then
|
||||
Using f As New RedditViewSettingsForm(Options) : f.ShowDialog() : End Using
|
||||
End If
|
||||
End Sub
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, "user"), .TrueName) : End With
|
||||
End Function
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
Return $"https://www.reddit.com/comments/{Media.Post.ID.Split("_").LastOrDefault}/"
|
||||
If Not Media.Post.ID.IsEmptyString Then
|
||||
Return $"https://www.reddit.com/comments/{Media.Post.ID.Split("_").LastOrDefault}/"
|
||||
Else
|
||||
Return String.Empty
|
||||
End If
|
||||
End Function
|
||||
End Class
|
||||
End Namespace
|
||||
File diff suppressed because it is too large
Load Diff
@@ -11,7 +11,6 @@ Namespace API.RedGifs
|
||||
Friend Module Declarations
|
||||
Friend Const RedGifsSiteKey As String = "AndyProgram_RedGifs"
|
||||
Friend Const RedGifsSite As String = "RedGifs"
|
||||
Friend ReadOnly DateProvider As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicode(v, n, e))
|
||||
Friend ReadOnly WatchIDRegex As RParams = RParams.DMS(".+?watch/([^\?&""/]+)", 1, EDP.ReturnValue)
|
||||
Friend ReadOnly ThumbsIDRegex As RParams = RParams.DMS("([^/\?&""]+?)(-\w+?|)\.(mp4|jpg)", 1, EDP.ReturnValue,
|
||||
CType(Function(Input$) Input.StringToLower.StringTrim, Func(Of String, String)))
|
||||
|
||||
@@ -14,8 +14,6 @@ Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||
Imports UStates = SCrawler.API.Base.UserMedia.States
|
||||
Namespace API.RedGifs
|
||||
<Manifest(RedGifsSiteKey)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
@@ -32,18 +30,17 @@ Namespace API.RedGifs
|
||||
End Property
|
||||
<PropertyOption(ControlToolTip:="Bearer token", AllowNull:=False), ControlNumber(1)>
|
||||
Friend ReadOnly Property Token As PropertyValue
|
||||
<PropertyOption, ControlNumber(2)>
|
||||
Private ReadOnly Property UserAgent As PropertyValue
|
||||
<PXML> Friend ReadOnly Property TokenLastDateUpdated As PropertyValue
|
||||
Private Const TokenName As String = "authorization"
|
||||
#Region "TokenUpdateInterval"
|
||||
<PropertyOption(ControlText:="Token refresh interval", ControlToolTip:="Interval (in minutes) to refresh the token", AllowNull:=False, LeftOffset:=120),
|
||||
PXML, ControlNumber(0)>
|
||||
Friend ReadOnly Property TokenUpdateInterval As PropertyValue
|
||||
Private Class TokenIntervalProvider : Implements IFieldsCheckerProvider
|
||||
Private Property ErrorMessage As String Implements IFieldsCheckerProvider.ErrorMessage
|
||||
Private Property Name As String Implements IFieldsCheckerProvider.Name
|
||||
Private Property TypeError As Boolean Implements IFieldsCheckerProvider.TypeError
|
||||
Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
|
||||
Private Class TokenIntervalProvider : Inherits FieldsCheckerProviderBase
|
||||
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
||||
TypeError = False
|
||||
ErrorMessage = String.Empty
|
||||
If Not ACheck(Of Integer)(Value) Then
|
||||
@@ -52,12 +49,10 @@ Namespace API.RedGifs
|
||||
Return Value
|
||||
Else
|
||||
ErrorMessage = $"The value of [{Name}] field must be greater than or equal to 1"
|
||||
HasError = True
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
|
||||
Throw New NotImplementedException("[GetFormat] is not available in the context of [TokenIntervalProvider]")
|
||||
End Function
|
||||
End Class
|
||||
<Provider(NameOf(TokenUpdateInterval), FieldsChecker:=True)>
|
||||
Private ReadOnly Property TokenUpdateIntervalProvider As IFormatProvider
|
||||
@@ -68,12 +63,14 @@ Namespace API.RedGifs
|
||||
MyBase.New(RedGifsSite, "redgifs.com")
|
||||
Dim t$ = String.Empty
|
||||
With Responser
|
||||
Dim b As Boolean = Not .Mode = Responser.Modes.WebClient
|
||||
.Mode = Responser.Modes.WebClient
|
||||
If Not .UserAgentExists Then .UserAgent = ParserUserAgent
|
||||
.ClientWebUseCookies = False
|
||||
.ClientWebUseHeaders = True
|
||||
t = .Headers.Value(TokenName)
|
||||
If b Then .SaveSettings()
|
||||
End With
|
||||
Token = New PropertyValue(t, GetType(String), Sub(v) UpdateResponse(v))
|
||||
Token = New PropertyValue(t, GetType(String), Sub(v) UpdateResponse(NameOf(Token), v))
|
||||
UserAgent = New PropertyValue(Responser.UserAgent, GetType(String), Sub(v) UpdateResponse(NameOf(UserAgent), v))
|
||||
TokenLastDateUpdated = New PropertyValue(Now.AddYears(-1), GetType(Date))
|
||||
TokenUpdateInterval = New PropertyValue(60 * 12, GetType(Integer))
|
||||
TokenUpdateIntervalProvider = New TokenIntervalProvider
|
||||
@@ -83,8 +80,11 @@ Namespace API.RedGifs
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Response updater"
|
||||
Private Sub UpdateResponse(ByVal Value As String)
|
||||
Responser.Headers.Add(TokenName, Value)
|
||||
Private Sub UpdateResponse(ByVal Name As String, ByVal Value As String)
|
||||
Select Case Name
|
||||
Case NameOf(Token) : Responser.Headers.Add(TokenName, Value)
|
||||
Case NameOf(UserAgent) : Responser.UserAgent = Value
|
||||
End Select
|
||||
Responser.SaveSettings()
|
||||
End Sub
|
||||
#End Region
|
||||
@@ -101,16 +101,18 @@ Namespace API.RedGifs
|
||||
Friend Function UpdateToken() As Boolean
|
||||
Try
|
||||
Dim r$
|
||||
Dim NewToken$ = String.Empty
|
||||
Dim NewToken$ = String.Empty, NewAgent$ = String.Empty
|
||||
Using resp As New Responser : r = resp.GetResponse("https://api.redgifs.com/v2/auth/temporary",, EDP.ThrowException) : End Using
|
||||
If Not r.IsEmptyString Then
|
||||
Dim j As EContainer = JsonDocument.Parse(r)
|
||||
If Not j Is Nothing Then
|
||||
NewToken = j.Value("token")
|
||||
NewAgent = j.Value("agent")
|
||||
j.Dispose()
|
||||
End If
|
||||
End If
|
||||
If Not NewToken.IsEmptyString Then
|
||||
If Not NewAgent.IsEmptyString Then UserAgent.Value = NewAgent
|
||||
Token.Value = $"Bearer {NewToken}"
|
||||
TokenLastDateUpdated.Value = Now
|
||||
Return True
|
||||
@@ -118,7 +120,7 @@ Namespace API.RedGifs
|
||||
Return False
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.RedGifs.SiteSettings.UpdateToken]", False)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.RedGifs.SiteSettings.UpdateToken]", False)
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
@@ -129,8 +131,10 @@ Namespace API.RedGifs
|
||||
MyBase.BeginEdit()
|
||||
End Sub
|
||||
Friend Overrides Sub Update()
|
||||
Dim NewToken$ = AConvert(Of String)(Token.Value, AModes.Var, String.Empty)
|
||||
If Not _LastTokenValue = NewToken Then TokenLastDateUpdated.Value = Now
|
||||
If _SiteEditorFormOpened Then
|
||||
Dim NewToken$ = AConvert(Of String)(Token.Value, AModes.Var, String.Empty)
|
||||
If Not _LastTokenValue = NewToken Then TokenLastDateUpdated.Value = Now
|
||||
End If
|
||||
MyBase.Update()
|
||||
End Sub
|
||||
Friend Overrides Sub EndEdit()
|
||||
@@ -141,32 +145,6 @@ Namespace API.RedGifs
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
Return New UserData
|
||||
End Function
|
||||
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||
If BaseAuthExists() Then
|
||||
Using resp As Responser = Responser.Copy
|
||||
Dim m As UserMedia = UserData.GetDataFromUrlId(URL, False, resp, Settings(RedGifsSiteKey))
|
||||
If Not m.State = UStates.Missing And Not m.State = UserData.DataGone And (m.Type = UTypes.Picture Or m.Type = UTypes.Video) Then
|
||||
Try
|
||||
Dim spf$ = String.Empty
|
||||
Dim f As SFile = GetSpecialDataFile(Path, AskForPath, spf)
|
||||
If f.IsEmptyString Then
|
||||
f = m.File.File
|
||||
Else
|
||||
f.Name = m.File.Name
|
||||
f.Extension = m.File.Extension
|
||||
End If
|
||||
resp.DownloadFile(m.URL, f, EDP.ThrowException)
|
||||
m.State = UStates.Downloaded
|
||||
m.SpecialFolder = spf
|
||||
Return {m}
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"Redgifs standalone download error: [{URL}]")
|
||||
End Try
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
Return $"https://www.redgifs.com/watch/{Media.Post.ID}"
|
||||
End Function
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
Imports System.Net
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
@@ -42,7 +43,7 @@ Namespace API.RedGifs
|
||||
Try
|
||||
Dim _page As Func(Of String) = Function() If(Page = 1, String.Empty, $"&page={Page}")
|
||||
URL = $"https://api.redgifs.com/v2/users/{Name}/search?order=recent{_page.Invoke}"
|
||||
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException)
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
Dim postDate$, postID$
|
||||
Dim pTotal% = 0
|
||||
If Not r.IsEmptyString Then
|
||||
@@ -51,7 +52,7 @@ Namespace API.RedGifs
|
||||
pTotal = j.Value("pages").FromXML(Of Integer)(0)
|
||||
For Each g As EContainer In j("gifs")
|
||||
postDate = g.Value("createDate")
|
||||
Select Case CheckDatesLimit(postDate, DateProvider)
|
||||
Select Case CheckDatesLimit(postDate, UnixDate32Provider)
|
||||
Case DateResult.Skip : Continue For
|
||||
Case DateResult.Exit : Exit Sub
|
||||
End Select
|
||||
@@ -106,13 +107,13 @@ Namespace API.RedGifs
|
||||
Dim u As UserMedia
|
||||
Dim j As EContainer
|
||||
For i% = 0 To _ContentList.Count - 1
|
||||
If _ContentList(i).State = UserMedia.States.Missing Then
|
||||
If _ContentList(i).State = UStates.Missing Then
|
||||
ThrowAny(Token)
|
||||
u = _ContentList(i)
|
||||
If Not u.Post.ID.IsEmptyString Then
|
||||
url = String.Format(PostDataUrl, u.Post.ID.ToLower)
|
||||
Try
|
||||
r = Responser.GetResponse(url,, EDP.ThrowException)
|
||||
r = Responser.GetResponse(url)
|
||||
If Not r.IsEmptyString Then
|
||||
j = JsonDocument.Parse(r)
|
||||
If Not j Is Nothing Then
|
||||
@@ -207,20 +208,29 @@ Namespace API.RedGifs
|
||||
MyMainLOG = String.Format(_errText, URL)
|
||||
Return m
|
||||
Else
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, String.Format(_errText, URL), m)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, String.Format(_errText, URL), m)
|
||||
End If
|
||||
End If
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Single data downloader"
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
Dim m As UserMedia = GetDataFromUrlId(Data.URL, False, Responser, HOST)
|
||||
If Not m.State = UStates.Missing And Not m.State = DataGone And (m.Type = UTypes.Picture Or m.Type = UTypes.Video) Then
|
||||
m.URL_BASE = MySettings.GetUserPostUrl(Me, m)
|
||||
_TempMediaList.Add(m)
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Create media"
|
||||
Private Shared Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String,
|
||||
ByVal PostDateStr As String, ByVal PostDateDate As Date?, ByVal State As UStates) As UserMedia
|
||||
Private Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String,
|
||||
ByVal PostDateStr As String, ByVal PostDateDate As Date?, ByVal State As UStates) As UserMedia
|
||||
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
|
||||
Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID}}
|
||||
If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern))
|
||||
If Not PostDateStr.IsEmptyString Then
|
||||
m.Post.Date = AConvert(Of Date)(PostDateStr, DateProvider, Nothing)
|
||||
m.Post.Date = AConvert(Of Date)(PostDateStr, UnixDate32Provider, Nothing)
|
||||
ElseIf PostDateDate.HasValue Then
|
||||
m.Post.Date = PostDateDate
|
||||
Else
|
||||
@@ -233,8 +243,8 @@ Namespace API.RedGifs
|
||||
#Region "Exception"
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
Dim s As WebExceptionStatus = Responser.Client.Status
|
||||
Dim sc As HttpStatusCode = Responser.Client.StatusCode
|
||||
Dim s As WebExceptionStatus = Responser.Status
|
||||
Dim sc As HttpStatusCode = Responser.StatusCode
|
||||
If sc = HttpStatusCode.NotFound Or s = DataGone Then
|
||||
UserExists = False
|
||||
ElseIf sc = HttpStatusCode.Unauthorized Then
|
||||
|
||||
22
SCrawler/API/ThisVid/Declarations.vb
Normal file
22
SCrawler/API/ThisVid/Declarations.vb
Normal file
@@ -0,0 +1,22 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.ThisVid
|
||||
Friend Module Declarations
|
||||
Friend Const ThisVidSiteKey As String = "AndyProgram_ThisVid"
|
||||
Friend ReadOnly RegExNextPage As RParams = RParams.DMS("class=.pagination-next...a class=.selective..href=""([^""]+)""", 1)
|
||||
Friend ReadOnly RegExVideoList As RParams = RParams.DMS("\<a href=""([^""]+)"" title=""[^""]+"" class=""tumbpu""", 1, RegexReturn.List, EDP.ReturnValue)
|
||||
Friend ReadOnly RegExVideoListSavedPosts As RParams = RParams.DMS("\<a href=""([^""]+)"" title=""[^""]+"">[\r\n\s]*<span class=""thumb ", 1, RegexReturn.List, EDP.ReturnValue)
|
||||
Friend ReadOnly RegExAlbumsList As RParams = RParams.DM("(https://thisvid.com/albums/[^/]+/?)"" title=""([^""]*?)"" class=""tumbpu""", 0, RegexReturn.List, EDP.ReturnValue)
|
||||
Friend ReadOnly RegExAlbumsListSaved As RParams = RParams.DM("(https://thisvid.com/albums/[^/]+/?)"" title=""([^""]*?)"">[\r\n\s]*\<span class=""thumb""", 0, RegexReturn.List, EDP.ReturnValue)
|
||||
Friend ReadOnly RegExAlbumID As RParams = RParams.DMS("albumId:.'(\d+)'", 1)
|
||||
Friend ReadOnly RegExAlbumImagesList As RParams = RParams.DMS("""([^""]+?image\d+/?)""", 1, RegexReturn.List, EDP.ReturnValue)
|
||||
Friend ReadOnly RegExAlbumImageUrl As RParams = RParams.DMS("\<img src=""(https?://media.thisvid.com/contents/albums/[^""]+?)""", 1, EDP.ReturnValue)
|
||||
End Module
|
||||
End Namespace
|
||||
69
SCrawler/API/ThisVid/SiteSettings.vb
Normal file
69
SCrawler/API/ThisVid/SiteSettings.vb
Normal file
@@ -0,0 +1,69 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.ThisVid
|
||||
<Manifest(ThisVidSiteKey), SeparatedTasks(1), SpecialForm(False), SavedPosts>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
Friend Overrides ReadOnly Property Icon As Icon
|
||||
Get
|
||||
Return My.Resources.SiteResources.ThisVidIcon_16
|
||||
End Get
|
||||
End Property
|
||||
Friend Overrides ReadOnly Property Image As Image
|
||||
Get
|
||||
Return My.Resources.SiteResources.ThisVidPic_16
|
||||
End Get
|
||||
End Property
|
||||
<PXML, PropertyOption(ControlText:="Public videos", ControlToolTip:="Download public videos")>
|
||||
Friend ReadOnly Property DownloadPublic As PropertyValue
|
||||
<PXML, PropertyOption(ControlText:="Private videos", ControlToolTip:="Download private videos")>
|
||||
Friend ReadOnly Property DownloadPrivate As PropertyValue
|
||||
<PXML, PropertyOption(ControlText:="Different folders",
|
||||
ControlToolTip:="Use different folders to store video files." & vbCr &
|
||||
"If true, then public videos will be stored in the 'Public' folder, private - in the 'Private' folder." & vbCr &
|
||||
"If false, all videos will be stored in the 'Video' folder.")>
|
||||
Friend ReadOnly Property DifferentFolders As PropertyValue
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
MyBase.New("ThisVid", "thisvid.com")
|
||||
DownloadPublic = New PropertyValue(True)
|
||||
DownloadPrivate = New PropertyValue(True)
|
||||
DifferentFolders = New PropertyValue(True)
|
||||
CheckNetscapeCookiesOnEndInit = True
|
||||
UseNetscapeCookies = True
|
||||
UserRegex = RParams.DMS("thisvid.com/members/(\d+)", 1)
|
||||
UrlPatternUser = "https://thisvid.com/members/{0}/"
|
||||
ImageVideoContains = "https://thisvid.com/videos/"
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetInstance, GetSpecialData"
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
Return New UserData
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Downloading"
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
Return Settings.YtdlpFile.Exists And (What = ISiteSettings.Download.SingleObject Or Responser.CookiesExists)
|
||||
End Function
|
||||
#End Region
|
||||
#Region "UserOptions"
|
||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||
If Options Is Nothing OrElse Not TypeOf Options Is UserExchangeOptions Then Options = New UserExchangeOptions(Me)
|
||||
If OpenForm Then
|
||||
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
332
SCrawler/API/ThisVid/UserData.vb
Normal file
332
SCrawler/API/ThisVid/UserData.vb
Normal file
@@ -0,0 +1,332 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Namespace API.ThisVid
|
||||
Friend Class UserData : Inherits UserDataBase
|
||||
#Region "XML names"
|
||||
Private Const Name_DownloadPublic As String = "DownloadPublic"
|
||||
Private Const Name_DownloadPrivate As String = "DownloadPrivate"
|
||||
Private Const Name_DifferentFolders As String = "DifferentFolders"
|
||||
#End Region
|
||||
#Region "Structures"
|
||||
Private Structure Album : Implements IRegExCreator
|
||||
Friend URL As String
|
||||
Friend Title As String
|
||||
Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
|
||||
If ParamsArray.ListExists(2) Then
|
||||
URL = ParamsArray(0)
|
||||
Title = TitleHtmlConverter(ParamsArray(1))
|
||||
End If
|
||||
Return Me
|
||||
End Function
|
||||
End Structure
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Friend Property DownloadPublic As Boolean = True
|
||||
Friend Property DownloadPrivate As Boolean = True
|
||||
Friend Property DifferentFolders As Boolean = True
|
||||
#End Region
|
||||
#Region "Loaders"
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
With Container
|
||||
If Loading Then
|
||||
DownloadPublic = .Value(Name_DownloadPublic).FromXML(Of Boolean)(True)
|
||||
DownloadPrivate = .Value(Name_DownloadPrivate).FromXML(Of Boolean)(True)
|
||||
DifferentFolders = .Value(Name_DifferentFolders).FromXML(Of Boolean)(True)
|
||||
Else
|
||||
.Add(Name_DownloadPublic, DownloadPublic.BoolToInteger)
|
||||
.Add(Name_DownloadPrivate, DownloadPrivate.BoolToInteger)
|
||||
.Add(Name_DifferentFolders, DifferentFolders.BoolToInteger)
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
Friend Overrides Function ExchangeOptionsGet() As Object
|
||||
Return New UserExchangeOptions(Me)
|
||||
End Function
|
||||
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
|
||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
|
||||
With DirectCast(Obj, UserExchangeOptions)
|
||||
DownloadPublic = .DownloadPublic
|
||||
DownloadPrivate = .DownloadPrivate
|
||||
DifferentFolders = .DifferentFolders
|
||||
End With
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
UseClientTokens = True
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Validation"
|
||||
Private Function IsValid() As Boolean
|
||||
Const ProfileDataPattern$ = "{0}[\r\n\s\W]*:[\r\n\s\W]*\<strong\>[\r\n\s\W]*([^\<]*)[\r\n\s\W]*\</strong"
|
||||
Const DescriptionPattern$ = "span style=""line-height: \d*px;""\>[\r\n\s\W]*([^\<]*)[\r\n\s\W]*\<"
|
||||
Try
|
||||
If Not IsSavedPosts Then
|
||||
Dim r$ = Responser.GetResponse($"https://thisvid.com/members/{ID}/")
|
||||
If Not r.IsEmptyString Then
|
||||
Dim rr As New RParams("", Nothing, 1, EDP.ReturnValue)
|
||||
Dim __getValue As Func(Of String, Boolean, String) = Function(ByVal member As String, ByVal appendMember As Boolean) As String
|
||||
rr.Pattern = String.Format(ProfileDataPattern, member)
|
||||
Dim v$ = CStr(RegexReplace(r, rr)).StringTrim
|
||||
If Not v.IsEmptyString And appendMember Then v = $"{member}: {v}"
|
||||
Return v
|
||||
End Function
|
||||
UserSiteNameUpdate(__getValue("Name", False))
|
||||
If Not UserSiteName.IsEmptyString And FriendlyName.IsEmptyString Then FriendlyName = UserSiteName : _ForceSaveUserData = True
|
||||
Dim descr$ = String.Empty
|
||||
descr.StringAppendLine(__getValue("Birth date", True))
|
||||
descr.StringAppendLine(__getValue("Country", True))
|
||||
descr.StringAppendLine(__getValue("City", True))
|
||||
descr.StringAppendLine(__getValue("Gender", True))
|
||||
descr.StringAppendLine(__getValue("Orientation", True))
|
||||
descr.StringAppendLine(__getValue("Relationship status", True))
|
||||
descr.StringAppendLine(__getValue("Favourite category", True))
|
||||
descr.StringAppendLine(__getValue("My interests", True))
|
||||
rr.Pattern = DescriptionPattern
|
||||
descr.StringAppendLine(CStr(RegexReplace(r, rr)).StringTrim)
|
||||
UserDescriptionUpdate(descr)
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End If
|
||||
Return True
|
||||
Catch ex As Exception
|
||||
UserExists = False
|
||||
Return False
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Download functions"
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
If ID.IsEmptyString Then ID = Name
|
||||
If IsValid() Then
|
||||
If IsSavedPosts Then
|
||||
DownloadData(1, True, Token)
|
||||
DownloadData_Images(Token)
|
||||
Else
|
||||
If DownloadVideos Then
|
||||
If DownloadPublic Then DownloadData(1, True, Token)
|
||||
If DownloadPrivate Then DownloadData(1, False, Token)
|
||||
End If
|
||||
If DownloadImages Then DownloadData_Images(Token)
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
Private Overloads Sub DownloadData(ByVal Page As Integer, ByVal IsPublic As Boolean, ByVal Token As CancellationToken)
|
||||
Dim URL$ = String.Empty
|
||||
Try
|
||||
Dim p$ = IIf(Page = 1, String.Empty, $"{Page}/")
|
||||
If IsSavedPosts Then
|
||||
URL = $"https://thisvid.com/my_favourite_videos/{p}"
|
||||
Else
|
||||
URL = $"https://thisvid.com/members/{ID}/{IIf(IsPublic, "public", "private")}_videos/{p}"
|
||||
End If
|
||||
ThrowAny(Token)
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
Dim cBefore% = _TempMediaList.Count
|
||||
If Not r.IsEmptyString Then
|
||||
Dim __SpecialFolder$ = IIf(DifferentFolders, IIf(IsPublic, "Public", "Private"), String.Empty)
|
||||
Dim l As List(Of String) = RegexReplace(r, If(IsSavedPosts, RegExVideoListSavedPosts, RegExVideoList))
|
||||
If l.ListExists Then
|
||||
For Each u$ In l
|
||||
If Not u.IsEmptyString Then
|
||||
If Not _TempPostsList.Contains(u) Then
|
||||
_TempPostsList.Add(u)
|
||||
_TempMediaList.Add(New UserMedia(u) With {.Type = UserMedia.Types.VideoPre, .SpecialFolder = __SpecialFolder})
|
||||
Else
|
||||
Exit Sub
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
If Not cBefore = _TempMediaList.Count Then DownloadData(Page + 1, IsPublic, Token)
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"videos downloading error [{URL}]")
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub DownloadData_Images(ByVal Token As CancellationToken)
|
||||
Dim __baseUrl$ = If(IsSavedPosts, "https://thisvid.com/my_favourite_albums/", $"https://thisvid.com/members/{ID}/albums/")
|
||||
Dim URL$ = String.Empty
|
||||
Try
|
||||
Dim r$
|
||||
Dim i% = 0
|
||||
Dim __continue As Boolean = False
|
||||
Dim rAlbums As RParams = If(IsSavedPosts, RegExAlbumsListSaved, RegExAlbumsList)
|
||||
Do
|
||||
i += 1
|
||||
__continue = False
|
||||
URL = __baseUrl
|
||||
If i > 1 Then URL &= $"{i}/"
|
||||
r = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString() Then
|
||||
Dim albums As List(Of Album) = RegexFields(Of Album)(r, {rAlbums}, {1, 2}, EDP.ReturnValue)
|
||||
Dim images As List(Of String)
|
||||
Dim albumId$, img$, imgUrl$, imgId$
|
||||
Dim u As UserMedia
|
||||
Dim rErr As New ErrorsDescriber(EDP.ReturnValue)
|
||||
__continue = True
|
||||
If albums.ListExists Then
|
||||
If albums.Count < 20 Then __continue = False
|
||||
For Each a As Album In albums
|
||||
If Not a.URL.IsEmptyString Then
|
||||
ThrowAny(Token)
|
||||
r = Responser.GetResponse(a.URL,, rErr)
|
||||
If Not r.IsEmptyString Then
|
||||
albumId = RegexReplace(r, RegExAlbumID)
|
||||
If a.Title.IsEmptyString Then a.Title = albumId
|
||||
images = RegexReplace(r, RegExAlbumImagesList)
|
||||
If images.ListExists Then
|
||||
For Each img In images
|
||||
ThrowAny(Token)
|
||||
r = Responser.GetResponse(img,, rErr)
|
||||
If Not r.IsEmptyString Then
|
||||
imgUrl = RegexReplace(r, RegExAlbumImageUrl)
|
||||
If Not imgUrl.IsEmptyString Then
|
||||
u = New UserMedia(imgUrl) With {
|
||||
.SpecialFolder = a.Title,
|
||||
.Type = UserMedia.Types.Picture,
|
||||
.URL_BASE = img
|
||||
}
|
||||
If Not u.File.File.IsEmptyString Then
|
||||
imgId = $"{albumId}_{u.File.Name}"
|
||||
If u.File.Extension.IsEmptyString Then u.File.Extension = "jpg"
|
||||
u.Post = imgId
|
||||
If Not _TempPostsList.Contains(imgId) Then
|
||||
_TempPostsList.Add(imgId)
|
||||
_TempMediaList.Add(u)
|
||||
Else
|
||||
Exit For
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
images.Clear()
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
Else
|
||||
Exit Do
|
||||
End If
|
||||
End If
|
||||
Loop While __continue
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"images downloading error [{URL}]")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "ReparseVideo"
|
||||
Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken)
|
||||
Try
|
||||
If _TempMediaList.Count > 0 Then
|
||||
Dim u As UserMedia
|
||||
Dim dirCmd$ = String.Empty
|
||||
Dim f As SFile = Settings.YtdlpFile.File
|
||||
Dim n$
|
||||
Dim cookieFile As SFile = DirectCast(HOST.Source, SiteSettings).CookiesNetscapeFile
|
||||
Dim command$
|
||||
Dim e As EContainer
|
||||
For i% = _TempMediaList.Count - 1 To 0 Step -1
|
||||
u = _TempMediaList(i)
|
||||
If u.Type = UserMedia.Types.VideoPre Then
|
||||
ThrowAny(Token)
|
||||
command = $"""{f}"" --verbose --dump-json "
|
||||
If cookieFile.Exists Then command &= $"--no-cookies-from-browser --cookies ""{cookieFile}"" "
|
||||
command &= u.URL
|
||||
e = GetJson(command)
|
||||
If Not e Is Nothing Then
|
||||
u.URL = e.Value("url")
|
||||
u.Post = New UserPost(e.Value("id"), ADateTime.ParseUnix32(e.Value("epoch")))
|
||||
If u.Post.Date.HasValue Then
|
||||
Select Case CheckDatesLimit(u.Post.Date.Value, Nothing)
|
||||
Case DateResult.Skip : _TempPostsList.ListAddValue(u.Post.ID, LNC) : _TempMediaList.RemoveAt(i) : Continue For
|
||||
Case DateResult.Exit : Exit Sub
|
||||
End Select
|
||||
End If
|
||||
n = TitleHtmlConverter(e.Value("title"))
|
||||
If Not n.IsEmptyString Then n = n.Replace("ThisVid.com", String.Empty).StringTrim.StringTrimEnd("-").StringTrim
|
||||
If n.IsEmptyString Then n = u.Post.ID
|
||||
If n.IsEmptyString Then n = "VideoFile"
|
||||
u.File = $"{n}.mp4"
|
||||
If u.URL.IsEmptyString OrElse (Not u.Post.ID.IsEmptyString AndAlso _TempPostsList.Contains(u.Post.ID)) Then
|
||||
_TempMediaList.RemoveAt(i)
|
||||
Else
|
||||
u.Type = UserMedia.Types.Video
|
||||
_TempPostsList.Add(u.Post.ID)
|
||||
_TempMediaList(i) = u
|
||||
End If
|
||||
e.Dispose()
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, "video reparsing error")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetJson"
|
||||
Private Function GetJson(ByVal Command As String) As EContainer
|
||||
Try
|
||||
Using b As New BatchExecutor(True)
|
||||
b.Execute(Command, EDP.ReturnValue)
|
||||
If b.OutputData.Count > 0 Then
|
||||
Dim e As EContainer
|
||||
For Each d$ In b.OutputData
|
||||
If Not d.IsEmptyString AndAlso d.StartsWith("{") Then
|
||||
e = JsonDocument.Parse(d, EDP.ReturnValue)
|
||||
If Not e Is Nothing Then Return e
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End Using
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
HasError = True
|
||||
LogError(ex, $"GetJson({Command})")
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "DownloadContent"
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
Dim s As Boolean? = SeparateVideoFolder
|
||||
If DifferentFolders Then SeparateVideoFolder = False Else SeparateVideoFolder = Nothing
|
||||
DownloadContentDefault(Token)
|
||||
SeparateVideoFolder = s
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Standalone downloader"
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
_TempMediaList.Add(New UserMedia(Data.URL) With {.Type = UserMedia.Types.VideoPre})
|
||||
ReparseVideo(Token)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "DownloadingException"
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
If Responser.StatusCode = Net.HttpStatusCode.NotFound Then
|
||||
Return 1
|
||||
Else
|
||||
Return 0
|
||||
End If
|
||||
End Function
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
32
SCrawler/API/ThisVid/UserExchangeOptions.vb
Normal file
32
SCrawler/API/ThisVid/UserExchangeOptions.vb
Normal file
@@ -0,0 +1,32 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Namespace API.ThisVid
|
||||
Friend Class UserExchangeOptions
|
||||
<PSetting(Caption:="Download public videos")>
|
||||
Friend Property DownloadPublic As Boolean = True
|
||||
<PSetting(Caption:="Download private videos")>
|
||||
Friend Property DownloadPrivate As Boolean = True
|
||||
<PSetting(NameOf(SiteSettings.DifferentFolders), NameOf(MySettings), Caption:="Different video folders")>
|
||||
Friend Property DifferentFolders As Boolean = True
|
||||
Private ReadOnly Property MySettings As SiteSettings
|
||||
Friend Sub New(ByVal s As SiteSettings)
|
||||
DownloadPublic = s.DownloadPublic.Value
|
||||
DownloadPrivate = s.DownloadPrivate.Value
|
||||
DifferentFolders = s.DifferentFolders.Value
|
||||
MySettings = s
|
||||
End Sub
|
||||
Friend Sub New(ByVal u As UserData)
|
||||
DownloadPublic = u.DownloadPublic
|
||||
DownloadPrivate = u.DownloadPrivate
|
||||
DifferentFolders = u.DifferentFolders
|
||||
MySettings = u.HOST.Source
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -10,11 +10,7 @@ Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.TikTok
|
||||
Friend Module Declarations
|
||||
Friend ReadOnly RegexEnvir As New RegexParseEnvir
|
||||
Friend ReadOnly CheckDateProvider As New CustomProvider(Function(v, d, p, n, e)
|
||||
With DirectCast(v, Date?)
|
||||
If .HasValue Then Return .Value Else Return Nothing
|
||||
End With
|
||||
End Function)
|
||||
Friend ReadOnly CheckDateProvider As New CustomProvider(Function(v) IIf(CType(v, Date?).HasValue, CObj(CType(v, Date?).Value), Nothing))
|
||||
Friend Class RegexParseEnvir
|
||||
Private ReadOnly UrlIdRegex As RParams = RParams.DMS("http[s]?://[w\.]{0,4}tiktok.com/[^/]+?/video/(\d+)", 1, EDP.ReturnValue)
|
||||
Private ReadOnly RegexItemsArrPre As RParams = RParams.DMS("ItemList"":\{""user-post"":\{""list"":\[([^\[]+)\]", 1)
|
||||
@@ -33,7 +29,7 @@ Namespace API.TikTok
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.TikTok.RegexParseEnvir.GetIDList]")
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.TikTok.RegexParseEnvir.GetIDList]")
|
||||
End Try
|
||||
End Function
|
||||
Friend Function GetVideoData(ByVal r As String, ByVal ID As String, ByRef URL As String, ByRef [Date] As Date?) As Boolean
|
||||
@@ -46,12 +42,12 @@ Namespace API.TikTok
|
||||
Dim u$ = RegexReplace(r, VideoPattern)
|
||||
If Not u.IsEmptyString Then URL = SymbolsConverter.Unicode.Decode(u, EDP.ReturnValue)
|
||||
Dim d$ = RegexReplace(r, DatePattern)
|
||||
If Not d.IsEmptyString Then [Date] = ADateTime.ParseUnicode(d)
|
||||
If Not d.IsEmptyString Then [Date] = ADateTime.ParseUnix32(d)
|
||||
Return Not URL.IsEmptyString
|
||||
End If
|
||||
Return False
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.TikTok.RegexParseEnvir.GetVideoData]", False)
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[API.TikTok.RegexParseEnvir.GetVideoData]", False)
|
||||
End Try
|
||||
End Function
|
||||
Friend Function ExtractPostID(ByVal URL As String) As String
|
||||
|
||||
@@ -32,11 +32,12 @@ Namespace API.TikTok
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
Return New UserData
|
||||
End Function
|
||||
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||
Return UserData.GetVideoInfo(URL, Responser)
|
||||
End Function
|
||||
Friend Overrides Function BaseAuthExists() As Boolean
|
||||
Return Responser.CookiesExists
|
||||
End Function
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
'TODO: TikTok disabled
|
||||
Return False
|
||||
End Function
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -26,7 +26,7 @@ Namespace API.TikTok
|
||||
Dim PostURL$ = String.Empty
|
||||
Dim r$
|
||||
URL = $"https://www.tiktok.com/@{Name}"
|
||||
r = Responser.GetResponse(URL,, EDP.ThrowException)
|
||||
r = Responser.GetResponse(URL)
|
||||
PostIDs = RegexEnvir.GetIDList(r)
|
||||
If PostIDs.ListExists Then
|
||||
For Each __id$ In PostIDs
|
||||
@@ -52,28 +52,7 @@ Namespace API.TikTok
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal Responser As Responser, Optional ByVal e As ErrorsDescriber = Nothing) As IEnumerable(Of UserMedia)
|
||||
Try
|
||||
If Not URL.IsEmptyString Then
|
||||
Dim PostId$ = String.Empty
|
||||
Dim PostDate As Date? = Nothing
|
||||
Dim PostURL$ = String.Empty
|
||||
Dim r$
|
||||
PostId = RegexEnvir.ExtractPostID(URL)
|
||||
If Not PostId.IsEmptyString Then
|
||||
Using resp As Responser = Responser.Copy() : r = resp.GetResponse(URL,, EDP.ThrowException) : End Using
|
||||
If Not r.IsEmptyString Then
|
||||
If RegexEnvir.GetVideoData(r, PostId, PostURL, PostDate) Then Return {MediaFromData(PostURL, PostId, PostDate)}
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
If Not e.Exists Then e = New ErrorsDescriber(EDP.ShowMainMsg + EDP.SendInLog)
|
||||
Return ErrorsDescriber.Execute(e, ex, $"TikTok standalone downloader: fetch media error ({URL})")
|
||||
End Try
|
||||
End Function
|
||||
Private Shared Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As Date?) As UserMedia
|
||||
Private Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As Date?) As UserMedia
|
||||
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
|
||||
Dim m As New UserMedia(_URL, UserMedia.Types.Video) With {.Post = New UserPost With {.ID = PostID}}
|
||||
If Not m.URL.IsEmptyString Then m.File = $"{PostID}.mp4"
|
||||
|
||||
@@ -16,7 +16,6 @@ Namespace API.Twitter
|
||||
Friend ReadOnly DateProvider As ADateTime = GetDateProvider()
|
||||
Friend ReadOnly VideoNode As NodeParams() = {New NodeParams("video_info", True, True, True, True, 10)}
|
||||
Friend ReadOnly VideoSizeRegEx As RParams = RParams.DMS("\d+x(\d+)", 1, EDP.ReturnValue)
|
||||
Friend ReadOnly UserIdRegEx As RParams = RParams.DMS("user_id.:.(\d+)", 1, EDP.ReturnValue)
|
||||
Private Function GetDateProvider() As ADateTime
|
||||
Dim n As DateTimeFormatInfo = CultureInfo.GetCultureInfo("en-us").DateTimeFormat.Clone
|
||||
n.FullDateTimePattern = "ddd MMM dd HH:mm:ss +ffff yyyy"
|
||||
|
||||
@@ -6,20 +6,37 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Namespace API.Twitter
|
||||
Friend Class EditorExchangeOptions
|
||||
Private Const DefaultOffset As Integer = 100
|
||||
Friend Property SiteKey As String = TwitterSiteKey
|
||||
<PSetting(NameOf(SiteSettings.GifsDownload), NameOf(MySettings), LeftOffset:=DefaultOffset)>
|
||||
Friend Property GifsDownload As Boolean
|
||||
<PSetting(NameOf(SiteSettings.GifsSpecialFolder), NameOf(MySettings), LeftOffset:=DefaultOffset)>
|
||||
Friend Property GifsSpecialFolder As String
|
||||
<PSetting(NameOf(SiteSettings.GifsPrefix), NameOf(MySettings), LeftOffset:=DefaultOffset)>
|
||||
Friend Property GifsPrefix As String
|
||||
<PSetting(NameOf(SiteSettings.UseMD5Comparison), NameOf(MySettings), LeftOffset:=DefaultOffset)>
|
||||
Friend Property UseMD5Comparison As Boolean = False
|
||||
<PSetting(Caption:="Remove existing duplicates",
|
||||
ToolTip:="Existing files will be checked for duplicates and duplicates removed." & vbCr &
|
||||
"Works only on the first activation 'Use MD5 comparison'.", LeftOffset:=DefaultOffset)>
|
||||
Friend Property RemoveExistingDuplicates As Boolean = False
|
||||
Friend Sub New()
|
||||
End Sub
|
||||
Private ReadOnly Property MySettings As Object
|
||||
Friend Sub New(ByVal s As SiteSettings)
|
||||
GifsDownload = s.GifsDownload.Value
|
||||
GifsSpecialFolder = s.GifsSpecialFolder.Value
|
||||
GifsPrefix = s.GifsPrefix.Value
|
||||
UseMD5Comparison = s.UseMD5Comparison.Value
|
||||
MySettings = s
|
||||
End Sub
|
||||
Friend Sub New(ByVal s As Mastodon.SiteSettings)
|
||||
GifsDownload = s.GifsDownload.Value
|
||||
GifsSpecialFolder = s.GifsSpecialFolder.Value
|
||||
GifsPrefix = s.GifsPrefix.Value
|
||||
UseMD5Comparison = s.UseMD5Comparison.Value
|
||||
MySettings = s
|
||||
End Sub
|
||||
Friend Sub New(ByVal u As UserData)
|
||||
GifsDownload = u.GifsDownload
|
||||
@@ -27,6 +44,7 @@ Namespace API.Twitter
|
||||
GifsPrefix = u.GifsPrefix
|
||||
UseMD5Comparison = u.UseMD5Comparison
|
||||
RemoveExistingDuplicates = u.RemoveExistingDuplicates
|
||||
MySettings = u.HOST.Source
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
185
SCrawler/API/Twitter/OptionsForm.Designer.vb
generated
185
SCrawler/API/Twitter/OptionsForm.Designer.vb
generated
@@ -1,185 +0,0 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Namespace API.Twitter
|
||||
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||
Partial Friend Class OptionsForm : Inherits System.Windows.Forms.Form
|
||||
<System.Diagnostics.DebuggerNonUserCode()>
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
Try
|
||||
If disposing AndAlso components IsNot Nothing Then
|
||||
components.Dispose()
|
||||
End If
|
||||
Finally
|
||||
MyBase.Dispose(disposing)
|
||||
End Try
|
||||
End Sub
|
||||
Private components As System.ComponentModel.IContainer
|
||||
<System.Diagnostics.DebuggerStepThrough()>
|
||||
Private Sub InitializeComponent()
|
||||
Me.components = New System.ComponentModel.Container()
|
||||
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
|
||||
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
|
||||
Dim ActionButton3 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(OptionsForm))
|
||||
Dim ActionButton4 As PersonalUtilities.Forms.Controls.Base.ActionButton = New PersonalUtilities.Forms.Controls.Base.ActionButton()
|
||||
Dim TT_MAIN As System.Windows.Forms.ToolTip
|
||||
Me.CH_DOWN_GIFS = New System.Windows.Forms.CheckBox()
|
||||
Me.TXT_GIF_FOLDER = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
Me.TXT_GIF_PREFIX = New PersonalUtilities.Forms.Controls.TextBoxExtended()
|
||||
Me.CH_USE_MD5 = New System.Windows.Forms.CheckBox()
|
||||
Me.CH_REMOVE_EXISTING_DUP = New System.Windows.Forms.CheckBox()
|
||||
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
|
||||
TT_MAIN = New System.Windows.Forms.ToolTip(Me.components)
|
||||
CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||
CONTAINER_MAIN.SuspendLayout()
|
||||
TP_MAIN.SuspendLayout()
|
||||
CType(Me.TXT_GIF_FOLDER, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
CType(Me.TXT_GIF_PREFIX, System.ComponentModel.ISupportInitialize).BeginInit()
|
||||
Me.SuspendLayout()
|
||||
'
|
||||
'CONTAINER_MAIN
|
||||
'
|
||||
'
|
||||
'CONTAINER_MAIN.ContentPanel
|
||||
'
|
||||
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
|
||||
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(304, 161)
|
||||
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||
CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||
CONTAINER_MAIN.Size = New System.Drawing.Size(304, 161)
|
||||
CONTAINER_MAIN.TabIndex = 0
|
||||
CONTAINER_MAIN.TopToolStripPanelVisible = False
|
||||
'
|
||||
'TP_MAIN
|
||||
'
|
||||
TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
|
||||
TP_MAIN.ColumnCount = 1
|
||||
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Controls.Add(Me.CH_DOWN_GIFS, 0, 0)
|
||||
TP_MAIN.Controls.Add(Me.TXT_GIF_FOLDER, 0, 1)
|
||||
TP_MAIN.Controls.Add(Me.TXT_GIF_PREFIX, 0, 2)
|
||||
TP_MAIN.Controls.Add(Me.CH_USE_MD5, 0, 3)
|
||||
TP_MAIN.Controls.Add(Me.CH_REMOVE_EXISTING_DUP, 0, 4)
|
||||
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
TP_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||
TP_MAIN.Name = "TP_MAIN"
|
||||
TP_MAIN.RowCount = 6
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 28.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
|
||||
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
TP_MAIN.Size = New System.Drawing.Size(304, 161)
|
||||
TP_MAIN.TabIndex = 0
|
||||
'
|
||||
'CH_DOWN_GIFS
|
||||
'
|
||||
Me.CH_DOWN_GIFS.AutoSize = True
|
||||
Me.CH_DOWN_GIFS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_DOWN_GIFS.Location = New System.Drawing.Point(4, 4)
|
||||
Me.CH_DOWN_GIFS.Name = "CH_DOWN_GIFS"
|
||||
Me.CH_DOWN_GIFS.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0)
|
||||
Me.CH_DOWN_GIFS.Size = New System.Drawing.Size(296, 19)
|
||||
Me.CH_DOWN_GIFS.TabIndex = 0
|
||||
Me.CH_DOWN_GIFS.Text = "Download GIFs"
|
||||
Me.CH_DOWN_GIFS.UseVisualStyleBackColor = True
|
||||
'
|
||||
'TXT_GIF_FOLDER
|
||||
'
|
||||
ActionButton3.BackgroundImage = CType(resources.GetObject("ActionButton3.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton3.Name = "Clear"
|
||||
ActionButton3.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
Me.TXT_GIF_FOLDER.Buttons.Add(ActionButton3)
|
||||
Me.TXT_GIF_FOLDER.CaptionText = "GIFs special folder"
|
||||
Me.TXT_GIF_FOLDER.CaptionToolTipText = "Put the GIFs in a special folder"
|
||||
Me.TXT_GIF_FOLDER.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_GIF_FOLDER.Location = New System.Drawing.Point(4, 30)
|
||||
Me.TXT_GIF_FOLDER.Name = "TXT_GIF_FOLDER"
|
||||
Me.TXT_GIF_FOLDER.Size = New System.Drawing.Size(296, 22)
|
||||
Me.TXT_GIF_FOLDER.TabIndex = 1
|
||||
'
|
||||
'TXT_GIF_PREFIX
|
||||
'
|
||||
ActionButton4.BackgroundImage = CType(resources.GetObject("ActionButton4.BackgroundImage"), System.Drawing.Image)
|
||||
ActionButton4.Name = "Clear"
|
||||
ActionButton4.Tag = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons.Clear
|
||||
Me.TXT_GIF_PREFIX.Buttons.Add(ActionButton4)
|
||||
Me.TXT_GIF_PREFIX.CaptionText = "GIF prefix"
|
||||
Me.TXT_GIF_PREFIX.CaptionToolTipText = "This prefix will be added to the beginning of the filename"
|
||||
Me.TXT_GIF_PREFIX.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.TXT_GIF_PREFIX.Location = New System.Drawing.Point(4, 59)
|
||||
Me.TXT_GIF_PREFIX.Name = "TXT_GIF_PREFIX"
|
||||
Me.TXT_GIF_PREFIX.Size = New System.Drawing.Size(296, 22)
|
||||
Me.TXT_GIF_PREFIX.TabIndex = 2
|
||||
'
|
||||
'CH_USE_MD5
|
||||
'
|
||||
Me.CH_USE_MD5.AutoSize = True
|
||||
Me.CH_USE_MD5.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_USE_MD5.Location = New System.Drawing.Point(4, 88)
|
||||
Me.CH_USE_MD5.Name = "CH_USE_MD5"
|
||||
Me.CH_USE_MD5.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0)
|
||||
Me.CH_USE_MD5.Size = New System.Drawing.Size(296, 19)
|
||||
Me.CH_USE_MD5.TabIndex = 3
|
||||
Me.CH_USE_MD5.Text = "Use MD5 comparison"
|
||||
TT_MAIN.SetToolTip(Me.CH_USE_MD5, "Each image will be checked for existence using MD5")
|
||||
Me.CH_USE_MD5.UseVisualStyleBackColor = True
|
||||
'
|
||||
'CH_REMOVE_EXISTING_DUP
|
||||
'
|
||||
Me.CH_REMOVE_EXISTING_DUP.AutoSize = True
|
||||
Me.CH_REMOVE_EXISTING_DUP.Dock = System.Windows.Forms.DockStyle.Fill
|
||||
Me.CH_REMOVE_EXISTING_DUP.Location = New System.Drawing.Point(4, 114)
|
||||
Me.CH_REMOVE_EXISTING_DUP.Name = "CH_REMOVE_EXISTING_DUP"
|
||||
Me.CH_REMOVE_EXISTING_DUP.Padding = New System.Windows.Forms.Padding(100, 0, 0, 0)
|
||||
Me.CH_REMOVE_EXISTING_DUP.Size = New System.Drawing.Size(296, 19)
|
||||
Me.CH_REMOVE_EXISTING_DUP.TabIndex = 4
|
||||
Me.CH_REMOVE_EXISTING_DUP.Text = "Remove existing duplicates"
|
||||
TT_MAIN.SetToolTip(Me.CH_REMOVE_EXISTING_DUP, "Existing files will be checked for duplicates and duplicates removed." & Global.Microsoft.VisualBasic.ChrW(13) & Global.Microsoft.VisualBasic.ChrW(10) & "Works only" &
|
||||
" on the first activation 'Use MD5 comparison'.")
|
||||
Me.CH_REMOVE_EXISTING_DUP.UseVisualStyleBackColor = True
|
||||
'
|
||||
'OptionsForm
|
||||
'
|
||||
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||
Me.ClientSize = New System.Drawing.Size(304, 161)
|
||||
Me.Controls.Add(CONTAINER_MAIN)
|
||||
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
||||
Me.Icon = Global.SCrawler.My.Resources.SiteResources.TwitterIcon_32
|
||||
Me.MaximizeBox = False
|
||||
Me.MaximumSize = New System.Drawing.Size(320, 200)
|
||||
Me.MinimizeBox = False
|
||||
Me.MinimumSize = New System.Drawing.Size(320, 200)
|
||||
Me.Name = "OptionsForm"
|
||||
Me.ShowInTaskbar = False
|
||||
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
||||
Me.Text = "Options"
|
||||
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||
CONTAINER_MAIN.ResumeLayout(False)
|
||||
CONTAINER_MAIN.PerformLayout()
|
||||
TP_MAIN.ResumeLayout(False)
|
||||
TP_MAIN.PerformLayout()
|
||||
CType(Me.TXT_GIF_FOLDER, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
CType(Me.TXT_GIF_PREFIX, System.ComponentModel.ISupportInitialize).EndInit()
|
||||
Me.ResumeLayout(False)
|
||||
|
||||
End Sub
|
||||
Private WithEvents CH_DOWN_GIFS As CheckBox
|
||||
Private WithEvents TXT_GIF_FOLDER As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
Private WithEvents TXT_GIF_PREFIX As PersonalUtilities.Forms.Controls.TextBoxExtended
|
||||
Private WithEvents CH_USE_MD5 As CheckBox
|
||||
Private WithEvents CH_REMOVE_EXISTING_DUP As CheckBox
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -1,81 +0,0 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Forms
|
||||
Imports PersonalUtilities.Forms.Controls
|
||||
Namespace API.Twitter
|
||||
Friend Class OptionsForm
|
||||
Private WithEvents MyDefs As DefaultFormOptions
|
||||
Private ReadOnly Property MyExchangeOptions As EditorExchangeOptions
|
||||
Private ReadOnly MyGifTextProvider As SiteSettings.GifStringProvider
|
||||
Friend Sub New(ByRef ExchangeOptions As EditorExchangeOptions)
|
||||
InitializeComponent()
|
||||
MyExchangeOptions = ExchangeOptions
|
||||
MyGifTextProvider = New SiteSettings.GifStringProvider
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
End Sub
|
||||
Private Sub OptionsForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
.MyViewInitialize(True)
|
||||
.AddOkCancelToolbar()
|
||||
With MyExchangeOptions
|
||||
CH_DOWN_GIFS.Checked = .GifsDownload
|
||||
TXT_GIF_FOLDER.Text = .GifsSpecialFolder
|
||||
TXT_GIF_FOLDER.Tag = NameOf(SiteSettings.GifsSpecialFolder)
|
||||
TXT_GIF_PREFIX.Text = .GifsPrefix
|
||||
TXT_GIF_PREFIX.Tag = NameOf(SiteSettings.GifsPrefix)
|
||||
CH_USE_MD5.Checked = .UseMD5Comparison
|
||||
CH_REMOVE_EXISTING_DUP.Checked = .RemoveExistingDuplicates
|
||||
|
||||
Try
|
||||
Dim p As PropertyOption
|
||||
With Settings(TwitterSiteKey)
|
||||
p = .PropList.Find(Function(pp) pp.Name = TXT_GIF_FOLDER.Tag).Options
|
||||
If Not p Is Nothing Then
|
||||
TXT_GIF_FOLDER.CaptionText = p.ControlText
|
||||
TXT_GIF_FOLDER.CaptionToolTipText = p.ControlToolTip
|
||||
TXT_GIF_FOLDER.CaptionToolTipEnabled = Not TXT_GIF_FOLDER.CaptionToolTipText.IsEmptyString
|
||||
End If
|
||||
|
||||
p = .PropList.Find(Function(pp) pp.Name = TXT_GIF_PREFIX.Tag).Options
|
||||
If Not p Is Nothing Then
|
||||
TXT_GIF_PREFIX.CaptionText = p.ControlText
|
||||
TXT_GIF_PREFIX.CaptionToolTipText = p.ControlToolTip
|
||||
TXT_GIF_PREFIX.CaptionToolTipEnabled = Not TXT_GIF_PREFIX.CaptionToolTipText.IsEmptyString
|
||||
End If
|
||||
End With
|
||||
Catch
|
||||
End Try
|
||||
End With
|
||||
.EndLoaderOperations()
|
||||
End With
|
||||
End Sub
|
||||
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
|
||||
With MyExchangeOptions
|
||||
.GifsDownload = CH_DOWN_GIFS.Checked
|
||||
.GifsSpecialFolder = TXT_GIF_FOLDER.Text
|
||||
.GifsPrefix = TXT_GIF_PREFIX.Text
|
||||
.UseMD5Comparison = CH_USE_MD5.Checked
|
||||
.RemoveExistingDuplicates = CH_REMOVE_EXISTING_DUP.Checked
|
||||
End With
|
||||
MyDefs.CloseForm()
|
||||
End Sub
|
||||
Private Sub TXT_ActionOnTextChanged(ByVal Sender As TextBoxExtended, ByVal e As EventArgs) Handles TXT_GIF_FOLDER.ActionOnTextChanged,
|
||||
TXT_GIF_PREFIX.ActionOnTextChanged
|
||||
If Not MyDefs.Initializing Then
|
||||
With Sender
|
||||
MyGifTextProvider.PropertyName = .Tag
|
||||
Dim s% = .SelectionStart
|
||||
Dim t$ = AConvert(Of String)(.Text, String.Empty, MyGifTextProvider)
|
||||
If Not .Text = t Then .Text = t : .Select(s, 0)
|
||||
End With
|
||||
End If
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -11,12 +11,25 @@ Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Cookies
|
||||
Namespace API.Twitter
|
||||
<Manifest(TwitterSiteKey), SavedPosts, SpecialForm(False)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Token names"
|
||||
Friend Const Header_Authorization As String = "authorization"
|
||||
Friend Const Header_Token As String = "x-csrf-token"
|
||||
#End Region
|
||||
#Region "Properties constants"
|
||||
Friend Const GifsSpecialFolder_Text As String = "GIFs special folder"
|
||||
Friend Const GifsSpecialFolder_ToolTip As String = "Put the GIFs in a special folder" & vbCr &
|
||||
"This is a folder name, not an absolute path." & vbCr &
|
||||
"This folder(s) will be created relative to the user's root folder." & vbCr &
|
||||
"Examples:" & vbCr & "SomeFolderName" & vbCr & "SomeFolderName\SomeFolderName2"
|
||||
Friend Const GifsPrefix_Text As String = "GIF prefix"
|
||||
Friend Const GifsPrefix_ToolTip As String = "This prefix will be added to the beginning of the filename"
|
||||
Friend Const GifsDownload_Text As String = "Download GIFs"
|
||||
Friend Const UseMD5Comparison_Text As String = "Use MD5 comparison"
|
||||
Friend Const UseMD5Comparison_ToolTip As String = "Each image will be checked for existence using MD5"
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Friend Overrides ReadOnly Property Icon As Icon
|
||||
Get
|
||||
@@ -34,19 +47,13 @@ Namespace API.Twitter
|
||||
Private ReadOnly Property Auth As PropertyValue
|
||||
<PropertyOption(AllowNull:=False, IsAuth:=True, ControlText:="Token", ControlToolTip:="Set token from [x-csrf-token] response header")>
|
||||
Private ReadOnly Property Token As PropertyValue
|
||||
<PropertyOption(IsAuth:=True, ControlText:="Saved posts user", ControlToolTip:="Personal profile username"), PXML>
|
||||
Friend ReadOnly Property SavedPostsUserName As PropertyValue
|
||||
#End Region
|
||||
#Region "Other properties"
|
||||
<PropertyOption(IsAuth:=False, ControlText:="Download GIFs"), PXML>
|
||||
<PropertyOption(IsAuth:=False, ControlText:=GifsDownload_Text), PXML>
|
||||
Friend ReadOnly Property GifsDownload As PropertyValue
|
||||
<PropertyOption(IsAuth:=False, ControlText:="GIFs special folder",
|
||||
ControlToolTip:="Put the GIFs in a special folder" & vbCr &
|
||||
"This is a folder name, not an absolute path." & vbCr &
|
||||
"This folder(s) will be created relative to the user's root folder." & vbCr &
|
||||
"Examples:" & vbCr & "SomeFolderName" & vbCr & "SomeFolderName\SomeFolderName2"), PXML>
|
||||
<PropertyOption(IsAuth:=False, ControlText:=GifsSpecialFolder_Text, ControlToolTip:=GifsSpecialFolder_ToolTip), PXML>
|
||||
Friend ReadOnly Property GifsSpecialFolder As PropertyValue
|
||||
<PropertyOption(IsAuth:=False, ControlText:="GIF prefix", ControlToolTip:="This prefix will be added to the beginning of the filename"), PXML>
|
||||
<PropertyOption(IsAuth:=False, ControlText:=GifsPrefix_Text, ControlToolTip:=GifsPrefix_ToolTip), PXML>
|
||||
Friend ReadOnly Property GifsPrefix As PropertyValue
|
||||
<Provider(NameOf(GifsSpecialFolder), Interaction:=True), Provider(NameOf(GifsPrefix), Interaction:=True)>
|
||||
Private ReadOnly Property GifStringChecker As IFormatProvider
|
||||
@@ -60,69 +67,18 @@ Namespace API.Twitter
|
||||
v = v.StringRemoveWinForbiddenSymbols
|
||||
Else
|
||||
v = v.StringReplaceSymbols(GetWinForbiddenSymbols.ToList.ListWithRemove("\").ToArray, String.Empty, EDP.ReturnValue)
|
||||
v = v.StringTrim("\")
|
||||
End If
|
||||
End If
|
||||
Return v
|
||||
End Function
|
||||
Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
|
||||
Throw New NotImplementedException("[GetFormat] is not available in the context of [TimersChecker]")
|
||||
Throw New NotImplementedException("[GetFormat] is not available in the context of [GifStringProvider]")
|
||||
End Function
|
||||
End Class
|
||||
<PropertyOption(IsAuth:=False, ControlText:="Use MD5 comparison", ControlToolTip:="Each image will be checked for existence using MD5"), PXML>
|
||||
<PropertyOption(IsAuth:=False, ControlText:=UseMD5Comparison_Text, ControlToolTip:=UseMD5Comparison_ToolTip), PXML>
|
||||
Friend ReadOnly Property UseMD5Comparison As PropertyValue
|
||||
#End Region
|
||||
Friend Overrides ReadOnly Property Responser As Responser
|
||||
#End Region
|
||||
Friend Sub New()
|
||||
MyBase.New(TwitterSite)
|
||||
Responser = New Responser($"{SettingsFolderName}\Responser_{Site}.xml")
|
||||
|
||||
Dim a$ = String.Empty
|
||||
Dim t$ = String.Empty
|
||||
|
||||
With Responser
|
||||
If .File.Exists Then
|
||||
Dim b As Boolean = .CookiesDomain.IsEmptyString
|
||||
If EncryptCookies.CookiesEncrypted Then .CookiesEncryptKey = SettingsCLS.CookieEncryptKey
|
||||
.LoadSettings()
|
||||
a = .Headers.Value(Header_Authorization)
|
||||
t = .Headers.Value(Header_Token)
|
||||
.CookiesDomain = "twitter.com"
|
||||
If b Then .SaveSettings()
|
||||
Else
|
||||
.ContentType = "application/json"
|
||||
.Accept = "*/*"
|
||||
.CookiesDomain = "twitter.com"
|
||||
.CookiesEncryptKey = SettingsCLS.CookieEncryptKey
|
||||
.Decoders.Add(SymbolsConverter.Converters.Unicode)
|
||||
.Headers.Add("sec-ch-ua", " Not;A Brand"";v=""99"", ""Google Chrome"";v=""91"", ""Chromium"";v=""91""")
|
||||
.Headers.Add("sec-ch-ua-mobile", "?0")
|
||||
.Headers.Add("sec-fetch-dest", "empty")
|
||||
.Headers.Add("sec-fetch-mode", "cors")
|
||||
.Headers.Add("sec-fetch-site", "same-origin")
|
||||
.Headers.Add(Header_Token, String.Empty)
|
||||
.Headers.Add("x-twitter-active-user", "yes")
|
||||
.Headers.Add("x-twitter-auth-type", "OAuth2Session")
|
||||
.Headers.Add(Header_Authorization, String.Empty)
|
||||
.SaveSettings()
|
||||
End If
|
||||
End With
|
||||
|
||||
Auth = New PropertyValue(a, GetType(String), Sub(v) ChangeResponserFields(NameOf(Auth), v))
|
||||
Token = New PropertyValue(t, GetType(String), Sub(v) ChangeResponserFields(NameOf(Token), v))
|
||||
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
|
||||
|
||||
GifsDownload = New PropertyValue(True)
|
||||
GifsSpecialFolder = New PropertyValue(String.Empty, GetType(String))
|
||||
GifsPrefix = New PropertyValue("GIF_")
|
||||
GifStringChecker = New GifStringProvider
|
||||
UseMD5Comparison = New PropertyValue(False)
|
||||
|
||||
UserRegex = RParams.DMS("[htps:/]{7,8}.*?twitter.com/([^/]+)", 1)
|
||||
UrlPatternUser = "https://twitter.com/{0}"
|
||||
ImageVideoContains = "twitter"
|
||||
End Sub
|
||||
Private Sub ChangeResponserFields(ByVal PropName As String, ByVal Value As Object)
|
||||
If Not PropName.IsEmptyString Then
|
||||
Dim f$ = String.Empty
|
||||
@@ -137,15 +93,59 @@ Namespace API.Twitter
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
Friend Sub New()
|
||||
MyBase.New(TwitterSite)
|
||||
Responser = New Responser($"{SettingsFolderName}\Responser_{Site}.xml") With {.DeclaredError = EDP.ThrowException}
|
||||
|
||||
Dim a$ = String.Empty
|
||||
Dim t$ = String.Empty
|
||||
|
||||
With Responser
|
||||
If .File.Exists Then
|
||||
.CookiesDomain = "twitter.com"
|
||||
.CookiesEncryptKey = SettingsCLS.CookieEncryptKey
|
||||
.LoadSettings()
|
||||
a = .Headers.Value(Header_Authorization)
|
||||
t = .Headers.Value(Header_Token)
|
||||
Else
|
||||
.ContentType = "application/json"
|
||||
.Accept = "*/*"
|
||||
.CookiesDomain = "twitter.com"
|
||||
.CookiesEncryptKey = SettingsCLS.CookieEncryptKey
|
||||
.Decoders.Add(SymbolsConverter.Converters.Unicode)
|
||||
.Headers.Add("sec-ch-ua", """Chromium"";v=""112"", ""Google Chrome"";v=""112"", ""Not:A-Brand"";v=""99""")
|
||||
.Headers.Add("sec-ch-ua-mobile", "?0")
|
||||
.Headers.Add("sec-fetch-dest", "empty")
|
||||
.Headers.Add("sec-fetch-mode", "cors")
|
||||
.Headers.Add("sec-fetch-site", "same-origin")
|
||||
.Headers.Add(Header_Token, String.Empty)
|
||||
.Headers.Add("x-twitter-active-user", "yes")
|
||||
.Headers.Add("x-twitter-auth-type", "OAuth2Session")
|
||||
.Headers.Add(Header_Authorization, String.Empty)
|
||||
.SaveSettings()
|
||||
End If
|
||||
.Cookies.ChangedAllowInternalDrop = False
|
||||
.Cookies.Changed = False
|
||||
End With
|
||||
|
||||
Auth = New PropertyValue(a, GetType(String), Sub(v) ChangeResponserFields(NameOf(Auth), v))
|
||||
Token = New PropertyValue(t, GetType(String), Sub(v) ChangeResponserFields(NameOf(Token), v))
|
||||
|
||||
GifsDownload = New PropertyValue(True)
|
||||
GifsSpecialFolder = New PropertyValue(String.Empty, GetType(String))
|
||||
GifsPrefix = New PropertyValue("GIF_")
|
||||
GifStringChecker = New GifStringProvider
|
||||
UseMD5Comparison = New PropertyValue(False)
|
||||
|
||||
UserRegex = RParams.DMS("[htps:/]{7,8}.*?twitter.com/([^/]+)", 1)
|
||||
UrlPatternUser = "https://twitter.com/{0}"
|
||||
ImageVideoContains = "twitter"
|
||||
CheckNetscapeCookiesOnEndInit = True
|
||||
UseNetscapeCookies = True
|
||||
End Sub
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
If What = ISiteSettings.Download.SavedPosts Then
|
||||
Return New UserData With {.IsSavedPosts = True, .User = New UserInfo With {.Name = CStr(AConvert(Of String)(SavedPostsUserName.Value, String.Empty))}}
|
||||
Else
|
||||
Return New UserData
|
||||
End If
|
||||
End Function
|
||||
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||
Return UserData.GetVideoInfo(URL, Responser)
|
||||
Return New UserData
|
||||
End Function
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
Return $"https://twitter.com/{User.Name}/status/{Media.Post.ID}"
|
||||
@@ -153,11 +153,31 @@ Namespace API.Twitter
|
||||
Friend Overrides Function BaseAuthExists() As Boolean
|
||||
Return Responser.CookiesExists And ACheck(Token.Value) And ACheck(Auth.Value)
|
||||
End Function
|
||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||
If Options Is Nothing OrElse Not TypeOf Options Is EditorExchangeOptions Then Options = New EditorExchangeOptions(Me)
|
||||
If OpenForm Then
|
||||
Using f As New OptionsForm(Options) : f.ShowDialog() : End Using
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
If MyBase.Available(What, Silent) Then
|
||||
If What = ISiteSettings.Download.SavedPosts Then
|
||||
Return Settings.GalleryDLFile.Exists
|
||||
Else
|
||||
Return True
|
||||
End If
|
||||
Else
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||
If Options Is Nothing OrElse (Not TypeOf Options Is EditorExchangeOptions OrElse
|
||||
Not DirectCast(Options, EditorExchangeOptions).SiteKey = TwitterSiteKey) Then _
|
||||
Options = New EditorExchangeOptions(Me)
|
||||
If OpenForm Then
|
||||
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
|
||||
End If
|
||||
End Sub
|
||||
Friend Overrides Sub Update()
|
||||
If _SiteEditorFormOpened Then
|
||||
Dim tf$ = GifsSpecialFolder.Value
|
||||
If Not tf.IsEmptyString Then tf = tf.StringTrim("\") : GifsSpecialFolder.Value = tf
|
||||
End If
|
||||
MyBase.Update()
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -7,39 +7,32 @@
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Net
|
||||
Imports System.Drawing
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports PersonalUtilities.Tools.ImageRenderer
|
||||
Imports UStates = SCrawler.API.Base.UserMedia.States
|
||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||
Namespace API.Twitter
|
||||
Friend Class UserData : Inherits UserDataBase
|
||||
Private Const SinglePostUrl As String = "https://api.twitter.com/1.1/statuses/show.json?id={0}&tweet_mode=extended"
|
||||
Protected SinglePostUrl As String = "https://api.twitter.com/1.1/statuses/show.json?id={0}&tweet_mode=extended"
|
||||
#Region "XML names"
|
||||
Private Const Name_GifsDownload As String = "GifsDownload"
|
||||
Private Const Name_GifsSpecialFolder As String = "GifsSpecialFolder"
|
||||
Private Const Name_GifsPrefix As String = "GifsPrefix"
|
||||
Private Const Name_UseMD5Comparison As String = "UseMD5Comparison"
|
||||
Private Const Name_RemoveExistingDuplicates As String = "RemoveExistingDuplicates"
|
||||
Private Const Name_StartMD5Checked As String = "StartMD5Checked"
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Friend Property GifsDownload As Boolean
|
||||
Friend Property GifsSpecialFolder As String
|
||||
Friend Property GifsPrefix As String
|
||||
Friend Property GifsDownload As Boolean = True
|
||||
Friend Property GifsSpecialFolder As String = String.Empty
|
||||
Friend Property GifsPrefix As String = String.Empty
|
||||
Private ReadOnly _DataNames As List(Of String)
|
||||
Friend Property UseMD5Comparison As Boolean = False
|
||||
Private StartMD5Checked As Boolean = False
|
||||
Friend Property RemoveExistingDuplicates As Boolean = False
|
||||
#End Region
|
||||
#Region "Exchange options"
|
||||
Friend Overrides Function ExchangeOptionsGet() As Object
|
||||
Return New EditorExchangeOptions(Me)
|
||||
Return New EditorExchangeOptions(Me) With {.SiteKey = HOST.Key}
|
||||
End Function
|
||||
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
|
||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptions Then
|
||||
@@ -83,45 +76,35 @@ Namespace API.Twitter
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
If IsSavedPosts Then
|
||||
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.Post.ID), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
|
||||
DownloadData(String.Empty, Token)
|
||||
DownloadData_SavedPosts(Token)
|
||||
Else
|
||||
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
|
||||
DownloadData(String.Empty, Token)
|
||||
If UseMD5Comparison Then ValidateMD5(Token)
|
||||
End If
|
||||
End Sub
|
||||
Private Overloads Sub DownloadData(ByVal POST As String, ByVal Token As CancellationToken)
|
||||
Dim URL$ = String.Empty
|
||||
Try
|
||||
Dim NextCursor$ = String.Empty
|
||||
Dim __NextCursor As Predicate(Of EContainer) = Function(e) e.Value({"content", "operation", "cursor"}, "cursorType") = "Bottom"
|
||||
Dim PostID$ = String.Empty
|
||||
Dim PostDate$
|
||||
Dim nn As EContainer, s As EContainer
|
||||
Dim nn As EContainer
|
||||
Dim NewPostDetected As Boolean = False
|
||||
Dim ExistsDetected As Boolean = False
|
||||
|
||||
Dim UID As Func(Of EContainer, String) = Function(e) e.XmlIfNothing.Item({"user", "id"}).XmlIfNothingValue
|
||||
|
||||
If IsSavedPosts Then
|
||||
If Name.IsEmptyString Then Throw New ArgumentNullException With {.HelpLink = 1}
|
||||
URL = $"https://api.twitter.com/2/timeline/bookmark.json?screen_name={Name}&count=200" &
|
||||
"&tweet_mode=extended&include_entities=true&include_user_entities=true&include_ext_media_availability=true"
|
||||
If Not POST.IsEmptyString Then URL &= $"&cursor={SymbolsConverter.ASCII.EncodeSymbolsOnly(POST)}"
|
||||
Else
|
||||
URL = $"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name={Name}&count=200&exclude_replies=false&include_rts=1&tweet_mode=extended"
|
||||
If Not POST.IsEmptyString Then URL &= $"&max_id={POST}"
|
||||
End If
|
||||
URL = $"https://api.twitter.com/1.1/statuses/user_timeline.json?screen_name={Name}&count=200&exclude_replies=false&include_rts=1&tweet_mode=extended"
|
||||
If Not POST.IsEmptyString Then URL &= $"&max_id={POST}"
|
||||
|
||||
ThrowAny(Token)
|
||||
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException)
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Using w As EContainer = JsonDocument.Parse(r)
|
||||
If w.ListExists Then
|
||||
|
||||
If Not IsSavedPosts And POST.IsEmptyString And Not w.ItemF({0, "user"}) Is Nothing Then
|
||||
If POST.IsEmptyString And Not w.ItemF({0, "user"}) Is Nothing Then
|
||||
With w.ItemF({0, "user"})
|
||||
If .Value("screen_name").StringToLower = Name Then
|
||||
If .Value("screen_name").StringToLower = Name.ToLower Then
|
||||
UserSiteNameUpdate(.Value("name"))
|
||||
UserDescriptionUpdate(.Value("description"))
|
||||
Dim __getImage As Action(Of String) = Sub(ByVal img As String)
|
||||
@@ -145,15 +128,10 @@ Namespace API.Twitter
|
||||
For Each nn In If(IsSavedPosts, w({"globalObjects", "tweets"}).XmlIfNothing, w)
|
||||
ThrowAny(Token)
|
||||
If nn.Count > 0 Then
|
||||
If IsSavedPosts Then
|
||||
PostID = nn.Value
|
||||
If PostID.IsEmptyString Then PostID = nn.Value("id_str")
|
||||
Else
|
||||
PostID = nn.Value("id")
|
||||
If ID.IsEmptyString Then
|
||||
ID = UID(nn)
|
||||
If Not ID.IsEmptyString Then UpdateUserInformation()
|
||||
End If
|
||||
PostID = nn.Value("id")
|
||||
If ID.IsEmptyString Then
|
||||
ID = UID(nn)
|
||||
If Not ID.IsEmptyString Then UpdateUserInformation()
|
||||
End If
|
||||
|
||||
'Date Pattern:
|
||||
@@ -172,32 +150,58 @@ Namespace API.Twitter
|
||||
Continue For
|
||||
End If
|
||||
|
||||
If IsSavedPosts OrElse Not ParseUserMediaOnly OrElse
|
||||
(
|
||||
Not nn.Contains("retweeted_status") OrElse
|
||||
(Not ID.IsEmptyString AndAlso UID(nn("retweeted_status")) = ID)
|
||||
) Then ObtainMedia(nn, PostID, PostDate)
|
||||
If Not ParseUserMediaOnly OrElse
|
||||
(Not nn.Contains("retweeted_status") OrElse (Not ID.IsEmptyString AndAlso UID(nn("retweeted_status")) = ID)) Then _
|
||||
ObtainMedia(nn, PostID, PostDate)
|
||||
End If
|
||||
Next
|
||||
|
||||
If IsSavedPosts Then
|
||||
s = w.ItemF({"timeline", "instructions", 0, "addEntries", "entries"}).XmlIfNothing
|
||||
If s.Count > 0 Then NextCursor = If(s.ItemF({__NextCursor})?.Value({"content", "operation", "cursor"}, "value"), String.Empty)
|
||||
End If
|
||||
End If
|
||||
End Using
|
||||
|
||||
If IsSavedPosts Then
|
||||
If Not NextCursor.IsEmptyString And Not NextCursor = POST Then DownloadData(NextCursor, Token)
|
||||
Else
|
||||
If POST.IsEmptyString And ExistsDetected Then Exit Sub
|
||||
If Not PostID.IsEmptyString And NewPostDetected Then DownloadData(PostID, Token)
|
||||
If POST.IsEmptyString And ExistsDetected Then Exit Sub
|
||||
If Not PostID.IsEmptyString And NewPostDetected Then DownloadData(PostID, Token)
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
||||
End Try
|
||||
End Sub
|
||||
Private Sub DownloadData_SavedPosts(ByVal Token As CancellationToken)
|
||||
Try
|
||||
Dim urls As List(Of String) = GetBookmarksUrlsFromGalleryDL()
|
||||
If urls.ListExists Then
|
||||
Dim postIds As New List(Of String)
|
||||
Dim r$
|
||||
Dim j As EContainer, jj As EContainer
|
||||
Dim jErr As New ErrorsDescriber(EDP.ReturnValue)
|
||||
Dim rPattern As RParams = RParams.DM("(?<=tweet-)(\d+)\Z", 0, EDP.ReturnValue)
|
||||
For Each url$ In urls
|
||||
r = Responser.GetResponse(url)
|
||||
If Not r.IsEmptyString Then
|
||||
j = JsonDocument.Parse(r, jErr)
|
||||
If Not j Is Nothing Then
|
||||
jj = j.ItemF({"data", "bookmark_timeline_v2", "timeline", "instructions", 0, "entries"})
|
||||
If If(jj?.Count, 0) > 0 Then postIds.ListAddList(jj.Select(Function(jj2) CStr(RegexReplace(jj2.Value("entryId"), rPattern))), LNC)
|
||||
j.Dispose()
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
If postIds.Count > 0 Then postIds.RemoveAll(Function(pid) pid.IsEmptyString OrElse (_TempPostsList.Contains(pid) Or _DataNames.Contains(pid)))
|
||||
If postIds.Count > 0 Then
|
||||
For Each __id$ In postIds
|
||||
_TempPostsList.Add(__id)
|
||||
r = Responser.GetResponse(String.Format(SinglePostUrl, __id),, EDP.ReturnValue)
|
||||
If Not r.IsEmptyString Then
|
||||
j = JsonDocument.Parse(r, jErr)
|
||||
If Not j Is Nothing Then
|
||||
If j.Count > 0 Then ObtainMedia(j, __id, j.Value("created_at"))
|
||||
j.Dispose()
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
End If
|
||||
End If
|
||||
Catch ane As ArgumentNullException When ane.HelpLink = 1
|
||||
MyMainLOG = "Username not set for saved Twitter posts"
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"data downloading error{IIf(IsSavedPosts, " (Saved Posts)", String.Empty)} [{URL}]")
|
||||
ProcessException(ex, Token, "data downloading error (Saved Posts)")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
@@ -252,18 +256,24 @@ Namespace API.Twitter
|
||||
If .ListExists Then
|
||||
For Each n As EContainer In .Self
|
||||
If n.Value("type") = "animated_gif" Then
|
||||
With n({"video_info", "variants"}).XmlIfNothing.ItemF({gifUrl}).XmlIfNothing
|
||||
url = .Value("url")
|
||||
ff = UrlFile(url)
|
||||
If Not ff.IsEmptyString Then
|
||||
If GifsDownload And Not _DataNames.Contains(ff) Then
|
||||
m = MediaFromData(url, PostID, PostDate,, State, UTypes.Video)
|
||||
f = m.File
|
||||
If Not f.IsEmptyString And Not GifsPrefix.IsEmptyString Then f.Name = $"{GifsPrefix}{f.Name}" : m.File = f
|
||||
If Not GifsSpecialFolder.IsEmptyString Then m.SpecialFolder = $"{GifsSpecialFolder}*"
|
||||
_TempMediaList.ListAddValue(m, LNC)
|
||||
End If
|
||||
Return True
|
||||
With n({"video_info", "variants"})
|
||||
If .ListExists Then
|
||||
With .ItemF({gifUrl})
|
||||
If .ListExists Then
|
||||
url = .Value("url")
|
||||
ff = UrlFile(url)
|
||||
If Not ff.IsEmptyString Then
|
||||
If GifsDownload And Not _DataNames.Contains(ff) Then
|
||||
m = MediaFromData(url, PostID, PostDate,, State, UTypes.Video)
|
||||
f = m.File
|
||||
If Not f.IsEmptyString And Not GifsPrefix.IsEmptyString Then f.Name = $"{GifsPrefix}{f.Name}" : m.File = f
|
||||
If Not GifsSpecialFolder.IsEmptyString Then m.SpecialFolder = $"{GifsSpecialFolder}*"
|
||||
_TempMediaList.ListAddValue(m, LNC)
|
||||
End If
|
||||
Return True
|
||||
End If
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
@@ -276,7 +286,7 @@ Namespace API.Twitter
|
||||
Return False
|
||||
End Try
|
||||
End Function
|
||||
Private Shared Function GetVideoNodeURL(ByVal w As EContainer) As String
|
||||
Private Function GetVideoNodeURL(ByVal w As EContainer) As String
|
||||
Dim v As EContainer = w.GetNode(VideoNode)
|
||||
If v.ListExists Then
|
||||
Dim l As New List(Of Sizes)
|
||||
@@ -298,6 +308,18 @@ Namespace API.Twitter
|
||||
Return String.Empty
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Gallery-DL Support"
|
||||
Private Function GetBookmarksUrlsFromGalleryDL() As List(Of String)
|
||||
Dim command$ = $"gallery-dl --verbose --simulate --cookies ""{DirectCast(HOST.Source, SiteSettings).CookiesNetscapeFile}"" https://twitter.com/i/bookmarks"
|
||||
Try
|
||||
Using batch As New GDL.GDLBatch With {.TempPostsList = _TempPostsList} : Return GDL.GetUrlsFromGalleryDl(batch, command) : End Using
|
||||
Catch ex As Exception
|
||||
HasError = True
|
||||
LogError(ex, $"GetJson({command})")
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "ReparseMissing"
|
||||
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
|
||||
Dim rList As New List(Of Integer)
|
||||
@@ -337,156 +359,19 @@ Namespace API.Twitter
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "MD5 support"
|
||||
Private Const VALIDATE_MD5_ERROR As String = "VALIDATE_MD5_ERROR"
|
||||
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
|
||||
Dim i%
|
||||
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 Then
|
||||
hash = ByteArrayToString(GetMD5(SFile.GetBytesFromNet(__data.URL_BASE.IfNullOrEmpty(__data.URL), ErrMD5), ImgFormat, 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_BASE.IfNullOrEmpty(__data.URL), 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$
|
||||
For i = existingFiles.Count - 1 To 0 Step -1
|
||||
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
|
||||
End If
|
||||
End If
|
||||
For i = 0 To _ContentList.Count - 1
|
||||
data = _ContentList(i)
|
||||
If (data.Type = UTypes.GIF Or data.Type = UTypes.Picture) Then
|
||||
If data.MD5.IsEmptyString Then
|
||||
ThrowAny(Token)
|
||||
eIndx = existingFiles.FindIndex(eFinder)
|
||||
If eIndx >= 0 Then
|
||||
data.MD5 = __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
|
||||
For i = 0 To existingFiles.Count - 1
|
||||
f = existingFiles(i)
|
||||
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
|
||||
|
||||
For i = _TempMediaList.Count - 1 To 0 Step -1
|
||||
data = _TempMediaList(i)
|
||||
If missingMD5(data) Then
|
||||
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
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
#Region "DownloadSingleObject"
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
Dim PostID$ = RegexReplace(Data.URL, RParams.DM("(?<=/)\d+", 0))
|
||||
If Not PostID.IsEmptyString Then
|
||||
Dim r$ = Responser.GetResponse(String.Format(SinglePostUrl, PostID),, EDP.ReturnValue)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If j.ListExists Then ObtainMedia(j, j.Value("id"), j.Value("created_at"))
|
||||
End Using
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, "ValidateMD5",, VALIDATE_MD5_ERROR)
|
||||
End Try
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Get video static"
|
||||
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal resp As Responser) As IEnumerable(Of UserMedia)
|
||||
Try
|
||||
If URL.Contains("twitter") Then
|
||||
Dim PostID$ = RegexReplace(URL, RParams.DM("(?<=/)\d+", 0))
|
||||
If Not PostID.IsEmptyString Then
|
||||
Dim r$
|
||||
Using rc As Responser = resp.Copy() : r = rc.GetResponse(String.Format(SinglePostUrl, PostID),, EDP.ReturnValue) : End Using
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If j.ListExists Then
|
||||
Dim u$ = GetVideoNodeURL(j)
|
||||
If Not u.IsEmptyString Then Return {MediaFromData(u, PostID, String.Empty,,, UTypes.Video)}
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.ShowMainMsg + EDP.SendInLog, ex, $"Twitter standalone downloader: fetch media error ({URL})")
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Picture options"
|
||||
Private Function GetPictureOption(ByVal w As EContainer) As String
|
||||
Const P4K As String = "4096x4096"
|
||||
@@ -541,10 +426,10 @@ Namespace API.Twitter
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Create media"
|
||||
Private Shared Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
|
||||
Optional ByVal _PictureOption As String = Nothing,
|
||||
Optional ByVal State As UStates = UStates.Unknown,
|
||||
Optional ByVal Type As UTypes = UTypes.Undefined) As UserMedia
|
||||
Private Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
|
||||
Optional ByVal _PictureOption As String = Nothing,
|
||||
Optional ByVal State As UStates = UStates.Unknown,
|
||||
Optional ByVal Type As UTypes = UTypes.Undefined) As UserMedia
|
||||
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
|
||||
Dim m As New UserMedia(_URL) With {.PictureOption = _PictureOption, .Post = New UserPost With {.ID = PostID}, .Type = Type}
|
||||
If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern))
|
||||
|
||||
@@ -49,13 +49,10 @@ Namespace API
|
||||
_CollectionName = NewName
|
||||
If Count > 0 Then Collections.ForEach(Sub(c) c.CollectionName = NewName)
|
||||
End Sub
|
||||
Friend Overrides Property Name As String
|
||||
Friend Overrides ReadOnly Property Name As String
|
||||
Get
|
||||
Return CollectionName
|
||||
End Get
|
||||
Set(ByVal NewCollectionName As String)
|
||||
CollectionName = NewCollectionName
|
||||
End Set
|
||||
End Property
|
||||
Friend Overrides Property FriendlyName As String
|
||||
Get
|
||||
@@ -367,7 +364,7 @@ Namespace API
|
||||
#End Region
|
||||
#Region "Open site, folder"
|
||||
Friend Overrides Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing)
|
||||
If Not e.Exists Then e = New ErrorsDescriber(EDP.SendInLog)
|
||||
If Not e.Exists Then e = New ErrorsDescriber(EDP.SendToLog)
|
||||
If Count > 0 Then Collections.ForEach(Sub(c) c.OpenSite(e))
|
||||
End Sub
|
||||
Private ReadOnly RealUser As Predicate(Of IUserData) = Function(u) u.UserModel = UsageModel.Default And Not u.HOST.Key = PathPlugin.PluginKey
|
||||
@@ -575,7 +572,7 @@ Namespace API
|
||||
MainFrameObj.ImageHandler(Me, False)
|
||||
Collections.ListClearDispose
|
||||
Dispose(False)
|
||||
If Not f.IsEmptyString Then f.Delete(SFO.Path, SFODelete.EmptyOnly + Settings.DeleteMode, EDP.SendInLog)
|
||||
If Not f.IsEmptyString Then f.Delete(SFO.Path, SFODelete.EmptyOnly + Settings.DeleteMode, EDP.SendToLog)
|
||||
Return 2
|
||||
End If
|
||||
Case 1
|
||||
@@ -592,7 +589,7 @@ Namespace API
|
||||
If Collections.All(Function(c) c.CollectionName.IsEmptyString) Then
|
||||
Settings.Users.Remove(Me)
|
||||
Collections.Clear()
|
||||
If Not f.IsEmptyString Then f.Delete(SFO.Path, SFODelete.Default + Settings.DeleteMode, EDP.SendInLog)
|
||||
If Not f.IsEmptyString Then f.Delete(SFO.Path, SFODelete.Default + Settings.DeleteMode, EDP.SendToLog)
|
||||
Downloader.UserRemove(Me)
|
||||
MainFrameObj.ImageHandler(Me, False)
|
||||
Dispose(False)
|
||||
|
||||
@@ -16,7 +16,7 @@ Namespace API.XVIDEOS
|
||||
Friend ReadOnly Regex_VideoID As RParams = RParams.DMS(".*?www.xvideos.com/(video\d+).*", 1)
|
||||
Friend ReadOnly Regex_M3U8_Reparse As RParams = RParams.DM("NAME=""(\d+).*?""[\r\n]*?(.+)(?=(|[\r\n]+?))", 0, RegexReturn.List)
|
||||
Friend ReadOnly Regex_M3U8_Appender As RParams = RParams.DM("(.+)(?=/.+?\.m3u8.*?)", 0)
|
||||
Friend ReadOnly Regex_SavedVideosPlaylist As RParams = RParams.DM("<div id=""video.+?data-id=""(\d+).+?a href=""([^""]+)"".+?title=""([^""]*)""",
|
||||
0, RegexReturn.List, EDP.ReturnValue, TitleHtmlConverter)
|
||||
Friend ReadOnly Regex_SavedVideosPlaylist As RParams = RParams.DM("\<div id=""video.+?data-id=""(\d+).+?a href=""([^""]+)"".+?title=""([^""]*)""",
|
||||
0, RegexReturn.List, EDP.ReturnValue)
|
||||
End Module
|
||||
End Namespace
|
||||
@@ -7,11 +7,14 @@
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Net
|
||||
Imports System.Threading
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Namespace API.XVIDEOS
|
||||
Friend NotInheritable Class M3U8
|
||||
Private Sub New()
|
||||
End Sub
|
||||
Friend Shared Function Download(ByVal URL As String, ByVal Appender As String, ByVal f As SFile) As SFile
|
||||
Friend Shared Function Download(ByVal URL As String, ByVal Appender As String, ByVal f As SFile,
|
||||
ByVal Token As CancellationToken, ByVal Progress As MyProgress) As SFile
|
||||
Try
|
||||
If Not URL.IsEmptyString Then
|
||||
Using w As New WebClient
|
||||
@@ -19,13 +22,13 @@ Namespace API.XVIDEOS
|
||||
If Not r.IsEmptyString Then
|
||||
Dim l As List(Of String) = ListAddList(Nothing, r.StringFormatLines.StringToList(Of String)(vbNewLine).ListWithRemove(Function(v) v.Trim.StartsWith("#")),
|
||||
New ListAddParams With {.Converter = Function(Input) $"{Appender}/{Input.ToString.Trim}"})
|
||||
If l.ListExists Then Return Base.M3U8Base.Download(l, f)
|
||||
If l.ListExists Then Return Base.M3U8Base.Download(l, f,, Token, Progress)
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[M3U8.Download({URL}, {Appender}, {f})]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[M3U8.Download({URL}, {Appender}, {f})]")
|
||||
Throw ex
|
||||
End Try
|
||||
End Function
|
||||
|
||||
@@ -7,16 +7,15 @@
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.BaseObjects
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.XVIDEOS
|
||||
<Manifest(XvideosSiteKey), SavedPosts, SpecialForm(True), TaskGroup(SettingsCLS.TaskStackNamePornSite)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase : Implements IDomainContainer
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
Friend Overrides ReadOnly Property Icon As Icon Implements IDomainContainer.Icon
|
||||
Friend Overrides ReadOnly Property Icon As Icon
|
||||
Get
|
||||
Return My.Resources.SiteResources.XvideosIcon_48
|
||||
End Get
|
||||
@@ -26,21 +25,10 @@ Namespace API.XVIDEOS
|
||||
Return My.Resources.SiteResources.XvideosPic_32
|
||||
End Get
|
||||
End Property
|
||||
#Region "Domains"
|
||||
Private ReadOnly Property IDomainContainer_Site As String Implements IDomainContainer.Site
|
||||
Get
|
||||
Return Site
|
||||
End Get
|
||||
End Property
|
||||
<PXML("Domains")> Private ReadOnly Property SiteDomains As PropertyValue Implements IDomainContainer.DomainsSettingProp
|
||||
Friend ReadOnly Property Domains As List(Of String) Implements IDomainContainer.Domains
|
||||
Private ReadOnly Property DomainsTemp As List(Of String) Implements IDomainContainer.DomainsTemp
|
||||
Private Property DomainsChanged As Boolean = False Implements IDomainContainer.DomainsChanged
|
||||
Private ReadOnly Property DomainsDefault As String = "xvideos.com|xnxx.com" Implements IDomainContainer.DomainsDefault
|
||||
#End Region
|
||||
<PXML("Domains")> Private ReadOnly Property SiteDomains As PropertyValue
|
||||
Friend ReadOnly Property Domains As DomainsContainer
|
||||
<PropertyOption(ControlText:="Download UHD", ControlToolTip:="Download UHD (4K) content"), PXML>
|
||||
Friend Property DownloadUHD As PropertyValue
|
||||
Private Property Initialized As Boolean = False Implements IDomainContainer.Initialized
|
||||
<PropertyOption(ControlText:="Playlist of saved videos",
|
||||
ControlToolTip:="Your personal videos playlist to download as 'saved posts'. " & vbCr &
|
||||
"This playlist must be private (Visibility = 'Only me'). It also required cookies." & vbCr &
|
||||
@@ -51,46 +39,34 @@ Namespace API.XVIDEOS
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
MyBase.New("XVIDEOS", "www.xvideos.com")
|
||||
Responser.DeclaredError = EDP.ThrowException
|
||||
Domains = New List(Of String)
|
||||
DomainsTemp = New List(Of String)
|
||||
SiteDomains = New PropertyValue(DomainsDefault, GetType(String), Sub(s) UpdateDomains())
|
||||
Domains = New DomainsContainer(Me, "xvideos.com|xnxx.com")
|
||||
SiteDomains = New PropertyValue(Domains.DomainsDefault, GetType(String))
|
||||
Domains.DestinationProp = SiteDomains
|
||||
DownloadUHD = New PropertyValue(False)
|
||||
SavedVideosPlaylist = New PropertyValue(String.Empty, GetType(String))
|
||||
UrlPatternUser = "https://xvideos.com/{0}"
|
||||
End Sub
|
||||
Friend Overrides Sub EndInit()
|
||||
Initialized = True
|
||||
DomainContainer.EndInit(Me)
|
||||
DomainsTemp.ListAddList(Domains)
|
||||
Domains.PopulateInitialDomains(SiteDomains.Value)
|
||||
MyBase.EndInit()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Edit"
|
||||
Private Property DomainsUpdateInProgress As Boolean = False Implements IDomainContainer.DomainsUpdateInProgress
|
||||
Private Property DomainsUpdatedBySite As Boolean = False Implements IDomainContainer.DomainsUpdatedBySite
|
||||
Friend Sub UpdateDomains() Implements IDomainContainer.UpdateDomains
|
||||
DomainContainer.UpdateDomains(Me)
|
||||
#Region "Domains Support"
|
||||
Protected Overrides Sub DomainsApply()
|
||||
Domains.Apply()
|
||||
MyBase.DomainsApply()
|
||||
End Sub
|
||||
Friend Overrides Sub Update()
|
||||
DomainContainer.Update(Me)
|
||||
Responser.SaveSettings()
|
||||
End Sub
|
||||
Friend Overrides Sub EndEdit()
|
||||
DomainContainer.EndEdit(Me)
|
||||
MyBase.EndEdit()
|
||||
Protected Overrides Sub DomainsReset()
|
||||
Domains.Reset()
|
||||
MyBase.DomainsReset()
|
||||
End Sub
|
||||
Friend Overrides Sub OpenSettingsForm()
|
||||
DomainContainer.OpenSettingsForm(Me)
|
||||
Domains.OpenSettingsForm()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download"
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
If What = ISiteSettings.Download.SavedPosts Then
|
||||
Return New UserData With {.IsSavedPosts = True, .User = New UserInfo With {.Name = "XVIDEOS"}}
|
||||
Else
|
||||
Return New UserData
|
||||
End If
|
||||
Return New UserData
|
||||
End Function
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
If Settings.UseM3U8 Then
|
||||
@@ -110,9 +86,11 @@ Namespace API.XVIDEOS
|
||||
__user &= $"/{User.Name.Replace($"{__user}_", String.Empty)}"
|
||||
Return __user
|
||||
End Function
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider, ByVal Channel As Boolean) As String
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
Return String.Format(UrlPatternUser, GetUserUrlPart(User))
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IsMyUser, IsMyImageVideo"
|
||||
Private Const UserRegexDefault As String = "/(profiles|[\w]*?[-]{0,1}channels)/([^/]+)(\Z|.*?)"
|
||||
Private Const URD As String = ".*?{0}{1}"
|
||||
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
|
||||
@@ -132,27 +110,9 @@ Namespace API.XVIDEOS
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Get special data"
|
||||
Friend Overrides Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
|
||||
If Not URL.IsEmptyString And Domains.Count > 0 Then
|
||||
If Domains.Exists(Function(d) URL.Contains(d)) Then Return New ExchangeOptions With {.UserName = URL, .Exists = True}
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||
If Not URL.IsEmptyString And Settings.UseM3U8 Then
|
||||
Dim spf$ = String.Empty
|
||||
Dim f As SFile = GetSpecialDataFile(Path, AskForPath, spf)
|
||||
f.Name = "video"
|
||||
f.Extension = "mp4"
|
||||
Using resp As Responser = Responser.Copy
|
||||
Using user As New UserData With {.HOST = Settings(XvideosSiteKey)}
|
||||
DirectCast(user, UserDataBase).User.File = f
|
||||
Dim p As UserMedia = user.Download(URL, resp, DownloadUHD.Value, String.Empty)
|
||||
If p.State = UserMedia.States.Downloaded Then p.SpecialFolder = spf : Return {p}
|
||||
End Using
|
||||
End Using
|
||||
If Domains.Domains.Exists(Function(d) URL.Contains(d)) Then Return New ExchangeOptions(Site, URL)
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
|
||||
@@ -8,11 +8,11 @@
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports UStates = SCrawler.API.Base.UserMedia.States
|
||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||
Namespace API.XVIDEOS
|
||||
Friend Class UserData : Inherits UserDataBase
|
||||
@@ -24,8 +24,8 @@ Namespace API.XVIDEOS
|
||||
If ParamsArray.ListExists(3) Then
|
||||
ID = ParamsArray(0)
|
||||
URL = ParamsArray(1)
|
||||
If Not URL.IsEmptyString Then URL = $"https://www.xvideos.com/{URL.StringTrimStart("/")}"
|
||||
Title = ParamsArray(2)
|
||||
If Not URL.IsEmptyString Then URL = $"https://www.xvideos.com/{HtmlConverter(URL).StringTrimStart("/")}"
|
||||
Title = TitleHtmlConverter(ParamsArray(2))
|
||||
End If
|
||||
Return Me
|
||||
End Function
|
||||
@@ -43,6 +43,7 @@ Namespace API.XVIDEOS
|
||||
Friend Sub New()
|
||||
SeparateVideoFolder = False
|
||||
UseInternalM3U8Function = True
|
||||
UseClientTokens = True
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
If Not Settings.UseM3U8 Then MyMainLOG = $"{ToStringForLog()}: File [ffmpeg.exe] not found" : Exit Sub
|
||||
@@ -55,6 +56,7 @@ Namespace API.XVIDEOS
|
||||
End Sub
|
||||
Private Sub DownloadUserVideo(ByVal Token As CancellationToken)
|
||||
Dim URL$ = String.Empty
|
||||
Dim isQuickies As Boolean = False
|
||||
Try
|
||||
Dim NextPage%, d%
|
||||
Dim limit% = If(DownloadTopCount, -1)
|
||||
@@ -77,39 +79,43 @@ Namespace API.XVIDEOS
|
||||
URL = $"https://www.xvideos.com/{user}/videos/new/{If(NextPage = 0, String.Empty, NextPage)}"
|
||||
Else 'Quickies
|
||||
URL = $"https://www.xvideos.com/quickies-api/profilevideos/all/none/N/{ID}/{NextPage}"
|
||||
isQuickies = True
|
||||
End If
|
||||
If Not j Is Nothing Then j.Dispose()
|
||||
r = Responser.GetResponse(URL,, EDP.ReturnValue)
|
||||
If Not r.IsEmptyString Then
|
||||
If Not EnvirSet Then UserExists = True : UserSuspended = False : EnvirSet = True
|
||||
j = JsonDocument.Parse(r).XmlIfNothing
|
||||
With j
|
||||
If .Contains("videos") Then
|
||||
With .Item("videos")
|
||||
If .Count > 0 Then
|
||||
NextPage += 1
|
||||
For Each jj In .Self
|
||||
p = New UserMedia With {
|
||||
.Post = jj.Value("id"),
|
||||
.URL = $"https://www.xvideos.com/{jj.Value(n).StringTrimStart("/")}"
|
||||
}
|
||||
If Not p.Post.ID.IsEmptyString And Not jj.Value(n).IsEmptyString Then
|
||||
If Not _TempPostsList.Contains(p.Post.ID) Then
|
||||
_TempPostsList.Add(p.Post.ID)
|
||||
_TempMediaList.Add(p)
|
||||
d += 1
|
||||
If limit > 0 And d = limit Then Exit Do
|
||||
Else
|
||||
Exit Do
|
||||
j = JsonDocument.Parse(r)
|
||||
If Not j Is Nothing Then
|
||||
With j
|
||||
If .Contains("videos") Then
|
||||
With .Item("videos")
|
||||
If .Count > 0 Then
|
||||
NextPage += 1
|
||||
For Each jj In .Self
|
||||
p = New UserMedia With {
|
||||
.Post = jj.Value("id"),
|
||||
.URL = $"https://www.xvideos.com/{jj.Value(n).StringTrimStart("/")}"
|
||||
}
|
||||
If Not p.Post.ID.IsEmptyString And Not jj.Value(n).IsEmptyString Then
|
||||
If Not _TempPostsList.Contains(p.Post.ID) Then
|
||||
_TempPostsList.Add(p.Post.ID)
|
||||
_TempMediaList.Add(p)
|
||||
d += 1
|
||||
If limit > 0 And d = limit Then Exit Do
|
||||
Else
|
||||
Exit Do
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
Next
|
||||
Continue Do
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
End With
|
||||
Next
|
||||
Continue Do
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
.Dispose()
|
||||
End With
|
||||
End If
|
||||
End If
|
||||
If Not j Is Nothing Then j.Dispose()
|
||||
Exit Do
|
||||
Loop While NextPage < 100
|
||||
Next
|
||||
@@ -119,18 +125,12 @@ Namespace API.XVIDEOS
|
||||
If _TempMediaList.Count > 0 Then
|
||||
For i% = 0 To _TempMediaList.Count - 1
|
||||
ThrowAny(Token)
|
||||
_TempMediaList(i) = GetVideoData(_TempMediaList(i), Responser, MySettings.DownloadUHD.Value)
|
||||
_TempMediaList(i) = GetVideoData(_TempMediaList(i))
|
||||
Next
|
||||
_TempMediaList.RemoveAll(Function(m) m.URL.IsEmptyString)
|
||||
End If
|
||||
Catch oex As OperationCanceledException
|
||||
Catch dex As ObjectDisposedException
|
||||
Catch ex As Exception
|
||||
If Responser.StatusCode = Net.HttpStatusCode.NotFound Then
|
||||
UserExists = False
|
||||
Else
|
||||
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
||||
End If
|
||||
ProcessException(ex, Token, $"data downloading error [{URL}]",, isQuickies)
|
||||
Finally
|
||||
If _TempMediaList.ListExists Then _TempMediaList.RemoveAll(Function(m) m.URL.IsEmptyString)
|
||||
End Try
|
||||
@@ -152,8 +152,16 @@ Namespace API.XVIDEOS
|
||||
URL = $"{MySettings.SavedVideosPlaylist.Value}{If(NextPage = 0, String.Empty, $"/{NextPage}")}"
|
||||
r = Responser.GetResponse(URL,, EDP.ReturnValue)
|
||||
If Responser.HasError Then
|
||||
If Responser.StatusCode = Net.HttpStatusCode.NotFound And NextPage > 0 Then Exit Do
|
||||
Throw New Exception(Responser.ErrorText, Responser.ErrorException)
|
||||
If Responser.StatusCode = Net.HttpStatusCode.NotFound Then
|
||||
If NextPage = 0 Then
|
||||
MyMainLOG = $"XVIDEOS saved video playlist {URL} not found."
|
||||
Exit Sub
|
||||
Else
|
||||
Exit Do
|
||||
End If
|
||||
Else
|
||||
Throw New Exception(Responser.ErrorText, Responser.ErrorException)
|
||||
End If
|
||||
End If
|
||||
NextPage += 1
|
||||
If Not r.IsEmptyString Then
|
||||
@@ -174,7 +182,7 @@ Namespace API.XVIDEOS
|
||||
If _TempMediaList.Count > 0 Then
|
||||
For i% = 0 To _TempMediaList.Count - 1
|
||||
ThrowAny(Token)
|
||||
_TempMediaList(i) = GetVideoData(_TempMediaList(i), Responser, MySettings.DownloadUHD.Value)
|
||||
_TempMediaList(i) = GetVideoData(_TempMediaList(i))
|
||||
Next
|
||||
_TempMediaList.RemoveAll(Function(m) m.URL.IsEmptyString)
|
||||
End If
|
||||
@@ -182,19 +190,19 @@ Namespace API.XVIDEOS
|
||||
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
||||
End Try
|
||||
End Sub
|
||||
Private Function GetVideoData(ByVal Media As UserMedia, ByVal resp As Responser, ByVal DownloadUHD As Boolean) As UserMedia
|
||||
Private Function GetVideoData(ByVal Media As UserMedia) As UserMedia
|
||||
Try
|
||||
If Not Media.URL.IsEmptyString Then
|
||||
Dim r$ = resp.GetResponse(Media.URL)
|
||||
Dim r$ = Responser.GetResponse(Media.URL)
|
||||
If Not r.IsEmptyString Then
|
||||
Dim NewUrl$ = RegexReplace(r, Regex_M3U8)
|
||||
If Not NewUrl.IsEmptyString Then
|
||||
Dim appender$ = RegexReplace(NewUrl, Regex_M3U8_Appender)
|
||||
Dim t$ = If(Media.PictureOption.IsEmptyString, RegexReplace(r, Regex_VideoTitle), Media.PictureOption)
|
||||
r = resp.GetResponse(NewUrl)
|
||||
r = Responser.GetResponse(NewUrl)
|
||||
If Not r.IsEmptyString Then
|
||||
Dim ls As List(Of Sizes) = RegexFields(Of Sizes)(r, {Regex_M3U8_Reparse}, {1, 2})
|
||||
If ls.ListExists And Not DownloadUHD Then ls.RemoveAll(Function(v) Not v.Value.ValueBetween(1, 1080))
|
||||
If ls.ListExists And Not MySettings.DownloadUHD.Value Then ls.RemoveAll(Function(v) Not v.Value.ValueBetween(1, 1080))
|
||||
If ls.ListExists Then
|
||||
ls.Sort()
|
||||
NewUrl = $"{appender}/{ls(0).Data.StringTrimStart("/")}"
|
||||
@@ -228,31 +236,28 @@ Namespace API.XVIDEOS
|
||||
Return Nothing
|
||||
End Try
|
||||
End Function
|
||||
Friend Function Download(ByVal URL As String, ByVal resp As Responser, ByVal DownloadUHD As Boolean, ByVal ID As String)
|
||||
Dim m As UserMedia = GetVideoData(New UserMedia(URL, UTypes.VideoPre) With {.Post = ID}, resp, DownloadUHD)
|
||||
If Not m.URL.IsEmptyString Then
|
||||
Dim f As SFile = m.File
|
||||
f.Path = MyFile.PathNoSeparator
|
||||
m.State = UStates.Tried
|
||||
Try
|
||||
f = M3U8.Download(m.URL, m.PictureOption, f)
|
||||
m.File = f
|
||||
m.State = UStates.Downloaded
|
||||
Catch ex As Exception
|
||||
m.State = UStates.Missing
|
||||
End Try
|
||||
End If
|
||||
Return m
|
||||
End Function
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile) As SFile
|
||||
Return M3U8.Download(Media.URL, Media.PictureOption, DestinationFile)
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
Dim m As UserMedia = GetVideoData(New UserMedia(Data.URL, UTypes.VideoPre))
|
||||
If Not m.URL.IsEmptyString Then _TempMediaList.Add(m)
|
||||
End Sub
|
||||
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
|
||||
Return M3U8.Download(Media.URL, Media.PictureOption, DestinationFile, Token, If(UseInternalM3U8Function_UseProgress, Progress, Nothing))
|
||||
End Function
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
Return 0
|
||||
Dim isQuickies As Boolean = False
|
||||
If Not IsNothing(EObj) AndAlso TypeOf EObj Is Boolean Then isQuickies = CBool(EObj)
|
||||
If Responser.StatusCode = Net.HttpStatusCode.NotFound Then
|
||||
UserExists = False
|
||||
Return 1
|
||||
ElseIf isQuickies And Responser.StatusCode = Net.HttpStatusCode.InternalServerError Then
|
||||
Return 1
|
||||
Else
|
||||
Return 0
|
||||
End If
|
||||
End Function
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -13,7 +13,6 @@ Namespace API.Xhamster
|
||||
Friend Const XhamsterSiteKey As String = "AndyProgram_XHamster"
|
||||
Friend ReadOnly HtmlScript As RParams = RParams.DMS("\<script id='initials-script'\>window.initials=(\{.+?\});\</script\>", 1, EDP.ReturnValue,
|
||||
CType(Function(Input$) Input.StringTrim, Func(Of String, String)))
|
||||
Friend ReadOnly DateProvider As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicode(v))
|
||||
Friend ReadOnly FirstM3U8FileRegEx As RParams = RParams.DM("RESOLUTION=\d+x(\d+).*?[\r\n]+?([^#]*?\.m3u8.*)", 0, RegexReturn.List)
|
||||
End Module
|
||||
End Namespace
|
||||
@@ -6,10 +6,12 @@
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.Base.M3U8Declarations
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Forms.Toolbars
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Xhamster
|
||||
Friend NotInheritable Class M3U8
|
||||
Private Sub New()
|
||||
@@ -72,8 +74,9 @@ Namespace API.Xhamster
|
||||
Responser.UseGZipStream = False
|
||||
End Try
|
||||
End Function
|
||||
Friend Shared Function Download(ByVal Media As UserMedia, ByVal Responser As Responser, ByVal UHD As Boolean) As SFile
|
||||
Return M3U8Base.Download(ObtainUrls(Media.URL, Responser, UHD), Media.File, Responser)
|
||||
Friend Shared Function Download(ByVal Media As UserMedia, ByVal Responser As Responser, ByVal UHD As Boolean,
|
||||
ByVal Token As CancellationToken, ByVal Progress As MyProgress) As SFile
|
||||
Return M3U8Base.Download(ObtainUrls(Media.URL, Responser, UHD), Media.File, Responser, Token, Progress)
|
||||
End Function
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -7,16 +7,15 @@
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.BaseObjects
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Namespace API.Xhamster
|
||||
<Manifest(XhamsterSiteKey), SavedPosts, SpecialForm(True), TaskGroup(SettingsCLS.TaskStackNamePornSite)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase : Implements IDomainContainer
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
Friend Overrides ReadOnly Property Icon As Icon Implements IDomainContainer.Icon
|
||||
Friend Overrides ReadOnly Property Icon As Icon
|
||||
Get
|
||||
Return My.Resources.SiteResources.XhamsterIcon_32
|
||||
End Get
|
||||
@@ -26,94 +25,45 @@ Namespace API.Xhamster
|
||||
Return My.Resources.SiteResources.XhamsterPic_32
|
||||
End Get
|
||||
End Property
|
||||
#Region "Domains"
|
||||
Private ReadOnly Property IDomainContainer_Site As String Implements IDomainContainer.Site
|
||||
Get
|
||||
Return Site
|
||||
End Get
|
||||
End Property
|
||||
<PXML("Domains")> Private ReadOnly Property SiteDomains As PropertyValue Implements IDomainContainer.DomainsSettingProp
|
||||
Friend ReadOnly Property Domains As List(Of String) Implements IDomainContainer.Domains
|
||||
Private ReadOnly Property DomainsTemp As List(Of String) Implements IDomainContainer.DomainsTemp
|
||||
Private Property DomainsChanged As Boolean = False Implements IDomainContainer.DomainsChanged
|
||||
Friend ReadOnly Property DomainsUpdated As Boolean
|
||||
Get
|
||||
Return DomainsUpdatedBySite
|
||||
End Get
|
||||
End Property
|
||||
Private ReadOnly Property DomainsDefault As String = "xhamster.com" Implements IDomainContainer.DomainsDefault
|
||||
#End Region
|
||||
<PXML("Domains")> Private ReadOnly Property SiteDomains As PropertyValue
|
||||
Friend ReadOnly Property Domains As DomainsContainer
|
||||
<PropertyOption(ControlText:="Download UHD", ControlToolTip:="Download UHD (4K) content"), PXML>
|
||||
Friend Property DownloadUHD As PropertyValue
|
||||
Private Property Initialized As Boolean = False Implements IDomainContainer.Initialized
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
MyBase.New("XHamster", "xhamster.com")
|
||||
|
||||
Responser.DeclaredError = EDP.ThrowException
|
||||
|
||||
Domains = New List(Of String)
|
||||
DomainsTemp = New List(Of String)
|
||||
SiteDomains = New PropertyValue(DomainsDefault, GetType(String), Sub(s) UpdateDomains())
|
||||
Domains = New DomainsContainer(Me, "xhamster.com")
|
||||
SiteDomains = New PropertyValue(Domains.DomainsDefault, GetType(String))
|
||||
Domains.DestinationProp = SiteDomains
|
||||
DownloadUHD = New PropertyValue(False)
|
||||
|
||||
UrlPatternUser = "https://xhamster.com/users/{0}"
|
||||
UrlPatternChannel = "https://xhamster.com/channels/{0}"
|
||||
|
||||
UrlPatternUser = "https://xhamster.com/{0}/{1}"
|
||||
UserRegex = RParams.DMS($"/({UserOption}|{ChannelOption})/([^/]+)(\Z|.*)", 0, RegexReturn.ListByMatch)
|
||||
ImageVideoContains = "xhamster"
|
||||
End Sub
|
||||
Friend Overrides Sub EndInit()
|
||||
Initialized = True
|
||||
DomainContainer.EndInit(Me)
|
||||
DomainsTemp.ListAddList(Domains)
|
||||
Domains.PopulateInitialDomains(SiteDomains.Value)
|
||||
MyBase.EndInit()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "UpdateDomains"
|
||||
Private Property DomainsUpdateInProgress As Boolean = False Implements IDomainContainer.DomainsUpdateInProgress
|
||||
Private Property DomainsUpdatedBySite As Boolean = False Implements IDomainContainer.DomainsUpdatedBySite
|
||||
Friend Overloads Sub UpdateDomains() Implements IDomainContainer.UpdateDomains
|
||||
DomainContainer.UpdateDomains(Me)
|
||||
#Region "Domains Support"
|
||||
Protected Overrides Sub DomainsApply()
|
||||
Domains.Apply()
|
||||
MyBase.DomainsApply()
|
||||
End Sub
|
||||
Friend Overloads Sub UpdateDomains(ByVal NewDomains As IEnumerable(Of String), ByVal Internal As Boolean)
|
||||
DomainContainer.UpdateDomains(Me, NewDomains, Internal)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Edit"
|
||||
Friend Overrides Sub Update()
|
||||
DomainContainer.Update(Me)
|
||||
Responser.SaveSettings()
|
||||
MyBase.Update()
|
||||
End Sub
|
||||
Friend Overrides Sub EndEdit()
|
||||
DomainContainer.EndEdit(Me)
|
||||
MyBase.EndEdit()
|
||||
Protected Overrides Sub DomainsReset()
|
||||
Domains.Reset()
|
||||
MyBase.DomainsReset()
|
||||
End Sub
|
||||
Friend Overrides Sub OpenSettingsForm()
|
||||
DomainContainer.OpenSettingsForm(Me)
|
||||
Domains.OpenSettingsForm()
|
||||
End Sub
|
||||
#End Region
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
If What = ISiteSettings.Download.SavedPosts Then
|
||||
Return New UserData With {.IsSavedPosts = True, .User = New UserInfo With {.Name = "xhamster"}}
|
||||
Else
|
||||
Return New UserData
|
||||
End If
|
||||
End Function
|
||||
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||
If Available(ISiteSettings.Download.Main, True) Then
|
||||
Using resp As Responser = Responser.Copy
|
||||
Dim spf$ = String.Empty
|
||||
Dim f As SFile = GetSpecialDataFile(Path, AskForPath, spf)
|
||||
Dim m As UserMedia = UserData.GetVideoInfo(URL, resp, f)
|
||||
If m.State = UserMedia.States.Downloaded Then
|
||||
m.SpecialFolder = f
|
||||
Return {m}
|
||||
End If
|
||||
End Using
|
||||
End If
|
||||
Return Nothing
|
||||
Return New UserData
|
||||
End Function
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
If Settings.UseM3U8 AndAlso MyBase.Available(What, Silent) Then
|
||||
@@ -126,22 +76,26 @@ Namespace API.Xhamster
|
||||
Return False
|
||||
End If
|
||||
End Function
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
Return Media.URL_BASE
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, IIf(.IsChannel, ChannelOption, UserOption), .TrueName) : End With
|
||||
End Function
|
||||
#Region "Is my user/data"
|
||||
Private Const ChannelOption As String = "channels"
|
||||
#Region "IsMyUser, IsMyImageVideo"
|
||||
Friend Const ChannelOption As String = "channels"
|
||||
Private Const UserOption As String = "users"
|
||||
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
|
||||
If Not UserURL.IsEmptyString AndAlso Domains.Count > 0 AndAlso Domains.Exists(Function(d) UserURL.ToLower.Contains(d.ToLower)) Then
|
||||
If Not UserURL.IsEmptyString AndAlso Domains.Domains.Count > 0 AndAlso Domains.Domains.Exists(Function(d) UserURL.ToLower.Contains(d.ToLower)) Then
|
||||
Dim data As List(Of String) = RegexReplace(UserURL, UserRegex)
|
||||
If data.ListExists(3) AndAlso Not data(2).IsEmptyString Then Return New ExchangeOptions(Site, data(2), data(1) = ChannelOption)
|
||||
If data.ListExists(3) AndAlso Not data(2).IsEmptyString Then
|
||||
Dim n$ = data(2)
|
||||
If Not data(1).IsEmptyString AndAlso data(1) = ChannelOption Then n &= $"@{data(1)}"
|
||||
Return New ExchangeOptions(Site, n)
|
||||
End If
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Friend Overrides Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
|
||||
If Not URL.IsEmptyString And Domains.Count > 0 Then
|
||||
If Domains.Exists(Function(d) URL.Contains(d)) Then Return New ExchangeOptions With {.UserName = URL, .Exists = True}
|
||||
If Not URL.IsEmptyString And Domains.Domains.Count > 0 Then
|
||||
If Domains.Domains.Exists(Function(d) URL.Contains(d)) Then Return New ExchangeOptions(Site, URL)
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Functions.RegularExpressions
|
||||
Imports PersonalUtilities.Tools.Web.Clients
|
||||
@@ -15,7 +16,12 @@ Imports PersonalUtilities.Tools.Web.Documents.JSON
|
||||
Imports UTypes = SCrawler.API.Base.UserMedia.Types
|
||||
Namespace API.Xhamster
|
||||
Friend Class UserData : Inherits UserDataBase
|
||||
#Region "XML names"
|
||||
Private Const Name_TrueName As String = "TrueName"
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Friend Property IsChannel As Boolean = False
|
||||
Friend Property TrueName As String = String.Empty
|
||||
Private ReadOnly Property MySettings As SiteSettings
|
||||
Get
|
||||
Return DirectCast(HOST.Source, SiteSettings)
|
||||
@@ -26,11 +32,39 @@ Namespace API.Xhamster
|
||||
End Structure
|
||||
Private ReadOnly _TempPhotoData As List(Of UserMedia)
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
Dim setNames As Action = Sub()
|
||||
If TrueName.IsEmptyString Then
|
||||
Dim n$() = Name.Split("@")
|
||||
If n.ListExists Then
|
||||
If n.Length = 2 Then
|
||||
TrueName = n(0)
|
||||
IsChannel = True
|
||||
ElseIf IsChannel Then
|
||||
TrueName = Name
|
||||
Else
|
||||
TrueName = n(0)
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
With Container
|
||||
If Loading Then
|
||||
IsChannel = .Value(Name_IsChannel).FromXML(Of Boolean)(False)
|
||||
TrueName = .Value(Name_TrueName)
|
||||
setNames.Invoke
|
||||
Else
|
||||
setNames.Invoke
|
||||
.Add(Name_IsChannel, IsChannel.BoolToInteger)
|
||||
.Add(Name_TrueName, TrueName)
|
||||
setNames.Invoke
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
UseInternalM3U8Function = True
|
||||
UseClientTokens = True
|
||||
_TempPhotoData = New List(Of UserMedia)
|
||||
End Sub
|
||||
#End Region
|
||||
@@ -58,10 +92,10 @@ Namespace API.Xhamster
|
||||
URL = $"https://xhamster.com/my/favorites/{IIf(IsVideo, "videos", "photos-and-galleries")}{IIf(Page = 1, String.Empty, $"/{Page}")}"
|
||||
listNode = If(IsVideo, {"favoriteVideoListComponent", "models"}, {"favoritesGalleriesAndPhotosCollection"})
|
||||
ElseIf IsChannel Then
|
||||
URL = $"https://xhamster.com/channels/{Name}/newest{IIf(Page = 1, String.Empty, $"/{Page}")}"
|
||||
URL = $"https://xhamster.com/channels/{TrueName}/newest{IIf(Page = 1, String.Empty, $"/{Page}")}"
|
||||
listNode = {"trendingVideoListComponent", "models"}
|
||||
Else
|
||||
URL = $"https://xhamster.com/users/{Name}/{IIf(IsVideo, "videos", "photos")}{IIf(Page = 1, String.Empty, $"/{Page}")}"
|
||||
URL = $"https://xhamster.com/users/{TrueName}/{IIf(IsVideo, "videos", "photos")}{IIf(Page = 1, String.Empty, $"/{Page}")}"
|
||||
listNode = {If(IsVideo, "userVideoCollection", "userGalleriesCollection")}
|
||||
End If
|
||||
ThrowAny(Token)
|
||||
@@ -69,10 +103,10 @@ Namespace API.Xhamster
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
If Not r.IsEmptyString Then r = RegexReplace(r, HtmlScript)
|
||||
If Not r.IsEmptyString Then
|
||||
Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing
|
||||
If j.Count > 0 Then
|
||||
If Not MySettings.DomainsUpdated AndAlso j.Contains("trustURLs") Then _
|
||||
MySettings.UpdateDomains(j("trustURLs").Select(Function(d) d(0).XmlIfNothingValue), False)
|
||||
Using j As EContainer = JsonDocument.Parse(r)
|
||||
If j.ListExists Then
|
||||
If Not MySettings.Domains.UpdatedBySite AndAlso j.Contains("trustURLs") Then _
|
||||
MySettings.Domains.Add(j("trustURLs").Select(Function(d) d(0).XmlIfNothingValue), True)
|
||||
|
||||
MaxPage = j.Value(mPages).FromXML(Of Integer)(-1)
|
||||
|
||||
@@ -113,7 +147,8 @@ Namespace API.Xhamster
|
||||
End Using
|
||||
End If
|
||||
|
||||
If (Not _TempMediaList.Count = cBefore Or skipped) And (IsChannel Or (MaxPage > 0 And Page < MaxPage)) Then DownloadData(Page + 1, IsVideo, Token)
|
||||
If (Not _TempMediaList.Count = cBefore Or skipped) And
|
||||
(IsChannel Or (MaxPage > 0 And Page < MaxPage)) Then DownloadData(Page + 1, IsVideo, Token)
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
||||
End Try
|
||||
@@ -130,7 +165,7 @@ Namespace API.Xhamster
|
||||
m = _TempMediaList(i)
|
||||
If Not m.URL_BASE.IsEmptyString Then
|
||||
m2 = Nothing
|
||||
If GetM3U8(m2, m.URL_BASE, Responser) Then
|
||||
If GetM3U8(m2, m.URL_BASE) Then
|
||||
m2.URL_BASE = m.URL_BASE
|
||||
_TempMediaList(i) = m2
|
||||
Else
|
||||
@@ -205,7 +240,7 @@ Namespace API.Xhamster
|
||||
If m.State = UserMedia.States.Missing AndAlso Not m.URL_BASE.IsEmptyString Then
|
||||
ThrowAny(Token)
|
||||
m2 = Nothing
|
||||
If GetM3U8(m2, m.URL_BASE, Responser) Then
|
||||
If GetM3U8(m2, m.URL_BASE) Then
|
||||
m2.URL_BASE = m.URL_BASE
|
||||
_TempMediaList.ListAddValue(m2, LNC)
|
||||
rList.Add(i)
|
||||
@@ -224,8 +259,7 @@ Namespace API.Xhamster
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetM3U8"
|
||||
Private Overloads Function GetM3U8(ByRef m As UserMedia, ByVal URL As String, ByVal Responser As Responser,
|
||||
Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
|
||||
Private Overloads Function GetM3U8(ByRef m As UserMedia, ByVal URL As String) As Boolean
|
||||
Try
|
||||
If Not URL.IsEmptyString Then
|
||||
Dim r$ = Responser.GetResponse(URL)
|
||||
@@ -242,8 +276,7 @@ Namespace API.Xhamster
|
||||
End If
|
||||
Return False
|
||||
Catch ex As Exception
|
||||
If Not e.Exists Then e = EDP.ReturnValue
|
||||
Return ErrorsDescriber.Execute(e, ex, $"[{ToStringForLog()}]: API.Xhamster.GetM3U8({URL})", False)
|
||||
Return ErrorsDescriber.Execute(EDP.ReturnValue, ex, $"[{ToStringForLog()}]: API.Xhamster.GetM3U8({URL})", False)
|
||||
End Try
|
||||
End Function
|
||||
Private Overloads Function GetM3U8(ByRef m As UserMedia, ByVal j As EContainer) As Boolean
|
||||
@@ -252,44 +285,29 @@ Namespace API.Xhamster
|
||||
Return False
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Standalone downloader"
|
||||
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal Responser As Responser, ByVal Path As SFile) As UserMedia
|
||||
Try
|
||||
Using u As New UserData With {.Responser = Responser, .HOST = Settings(XhamsterSiteKey)}
|
||||
Dim m As UserMedia = Nothing
|
||||
If u.GetM3U8(m, URL, Responser, EDP.ThrowException) Then
|
||||
m.File.Path = Path.Path
|
||||
Dim f As SFile = u.DownloadM3U8(m.URL, m, m.File)
|
||||
If Not f.IsEmptyString Then
|
||||
m.File = f
|
||||
m.State = UserMedia.States.Downloaded
|
||||
Return m
|
||||
End If
|
||||
End If
|
||||
End Using
|
||||
Return Nothing
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"XHamster standalone download error: [{URL}]", New UserMedia)
|
||||
End Try
|
||||
End Function
|
||||
#Region "DownloadSingleObject"
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
_ContentList.Add(New UserMedia(Data.URL_BASE) With {.State = UserMedia.States.Missing})
|
||||
ReparseMissing(Token)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download data"
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
Protected Overloads Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile) As SFile
|
||||
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
|
||||
Media.File = DestinationFile
|
||||
Return M3U8.Download(Media, Responser, MySettings.DownloadUHD.Value)
|
||||
Return M3U8.Download(Media, Responser, MySettings.DownloadUHD.Value, Token, If(UseInternalM3U8Function_UseProgress, Progress, Nothing))
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Create media"
|
||||
Private Shared Function ExtractMedia(ByVal j As EContainer, ByVal t As UTypes, Optional ByVal UrlNode As String = "pageURL",
|
||||
Optional ByVal DetectGalery As Boolean = True, Optional ByVal PostDate As Date? = Nothing) As UserMedia
|
||||
Private Function ExtractMedia(ByVal j As EContainer, ByVal t As UTypes, Optional ByVal UrlNode As String = "pageURL",
|
||||
Optional ByVal DetectGalery As Boolean = True, Optional ByVal PostDate As Date? = Nothing) As UserMedia
|
||||
If Not j Is Nothing Then
|
||||
Dim m As New UserMedia(j.Value(UrlNode).Replace("\", String.Empty), t) With {
|
||||
.Post = New UserPost With {
|
||||
.ID = j.Value("id"),
|
||||
.Date = AConvert(Of Date)(j.Value("created"), DateProvider, Nothing)
|
||||
.Date = AConvert(Of Date)(j.Value("created"), UnixDate32Provider, Nothing)
|
||||
},
|
||||
.PictureOption = TitleHtmlConverter(j.Value("title")),
|
||||
.Object = New ExchObj
|
||||
|
||||
116
SCrawler/API/YouTube/SiteSettings.vb
Normal file
116
SCrawler/API/YouTube/SiteSettings.vb
Normal file
@@ -0,0 +1,116 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Base
|
||||
Namespace API.YouTube
|
||||
<Manifest(YouTubeSiteKey), SpecialForm(True), SpecialForm(False), SeparatedTasks(1)>
|
||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||
#Region "Declarations"
|
||||
Friend Overrides ReadOnly Property Icon As Icon
|
||||
Get
|
||||
Return My.Resources.SiteYouTube.YouTubeIcon_32
|
||||
End Get
|
||||
End Property
|
||||
Friend Overrides ReadOnly Property Image As Image
|
||||
Get
|
||||
Return My.Resources.SiteYouTube.YouTubePic_96
|
||||
End Get
|
||||
End Property
|
||||
<PXML, PropertyOption(ControlText:="Download user videos")>
|
||||
Friend ReadOnly Property DownloadVideos As PropertyValue
|
||||
<PXML, PropertyOption(ControlText:="Download user shorts")>
|
||||
Friend ReadOnly Property DownloadShorts As PropertyValue
|
||||
<PXML, PropertyOption(ControlText:="Download user playlists")>
|
||||
Friend ReadOnly Property DownloadPlaylists As PropertyValue
|
||||
<PXML, PropertyOption(ControlText:="Use cookies", ControlToolTip:="Default value for new users." & vbCr & "Use cookies when downloading data.")>
|
||||
Friend ReadOnly Property UseCookies As PropertyValue
|
||||
#End Region
|
||||
#Region "Initializer"
|
||||
Friend Sub New()
|
||||
MyBase.New(YouTubeSite, "youtube.com")
|
||||
Responser.Cookies.ChangedAllowInternalDrop = False
|
||||
DownloadVideos = New PropertyValue(True)
|
||||
DownloadShorts = New PropertyValue(False)
|
||||
DownloadPlaylists = New PropertyValue(False)
|
||||
UseCookies = New PropertyValue(False)
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "GetInstance"
|
||||
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||
Return New UserData
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Edit, Update"
|
||||
Friend Overrides Sub Update()
|
||||
If _SiteEditorFormOpened Then
|
||||
With Responser.Cookies
|
||||
If .Changed Then
|
||||
.Changed = False
|
||||
With DirectCast(MyYouTubeSettings, YTSettings_Internal)
|
||||
.Cookies.Clear()
|
||||
.Cookies.AddRange(Responser.Cookies)
|
||||
.CookiesUpdated = True
|
||||
.PerformUpdate()
|
||||
End With
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
MyBase.Update()
|
||||
End Sub
|
||||
Friend Overrides Sub EndEdit()
|
||||
If _SiteEditorFormOpened Then DirectCast(MyYouTubeSettings, YTSettings_Internal).ResetUpdate()
|
||||
MyBase.EndEdit()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Available"
|
||||
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
|
||||
Return Settings.YtdlpFile.Exists And Settings.FfmpegFile.Exists
|
||||
End Function
|
||||
#End Region
|
||||
#Region "MyUser, MyUrl, get urls"
|
||||
Friend Const ChannelUserInt As Integer = 10000
|
||||
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
|
||||
Dim isMusic As Boolean = False
|
||||
Dim id$ = String.Empty
|
||||
Dim isChannelUser As Boolean = False
|
||||
Dim t As YouTubeMediaType = YouTubeFunctions.Info_GetUrlType(UserURL, isMusic, isChannelUser, id)
|
||||
If Not t = YouTubeMediaType.Undefined And Not t = YouTubeMediaType.Single And Not id.IsEmptyString Then
|
||||
Return New ExchangeOptions(Site, $"{id}@{CInt(t) + IIf(isMusic, UserMedia.Types.Audio, 0) + IIf(isChannelUser, ChannelUserInt, 0)}")
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Friend Overrides Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
|
||||
If YouTubeFunctions.IsMyUrl(URL) Then Return New ExchangeOptions(Site, URL) Else Return Nothing
|
||||
End Function
|
||||
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
|
||||
If Not User Is Nothing AndAlso TypeOf User Is UserData Then
|
||||
Return $"https://{IIf(DirectCast(User, UserData).IsMusic, "music", "www")}.youtube.com/watch?v={Media.Post.ID}"
|
||||
Else
|
||||
Return String.Empty
|
||||
End If
|
||||
End Function
|
||||
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider) As String
|
||||
If Not User Is Nothing AndAlso TypeOf User Is UserData Then Return DirectCast(User, UserData).GetUserUrl Else Return String.Empty
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Settings form, options"
|
||||
Friend Overrides Sub OpenSettingsForm()
|
||||
MyYouTubeSettings.ShowForm(False)
|
||||
End Sub
|
||||
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
|
||||
If Options Is Nothing OrElse Not TypeOf Options Is UserExchangeOptions Then Options = New UserExchangeOptions(Me)
|
||||
If OpenForm Then
|
||||
Using f As New InternalSettingsForm(Options, Me, False) : f.ShowDialog() : End Using
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
245
SCrawler/API/YouTube/UserData.vb
Normal file
245
SCrawler/API/YouTube/UserData.vb
Normal file
@@ -0,0 +1,245 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.API.Base
|
||||
Imports SCrawler.API.YouTube.Base
|
||||
Imports SCrawler.API.YouTube.Objects
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Namespace API.YouTube
|
||||
Friend Class UserData : Inherits UserDataBase
|
||||
#Region "XML names"
|
||||
Private Const Name_DownloadYTVideos As String = "YTDownloadVideos"
|
||||
Private Const Name_DownloadYTShorts As String = "YTDownloadShorts"
|
||||
Private Const Name_DownloadYTPlaylists As String = "YTDownloadPlaylists"
|
||||
Private Const Name_YTUseCookies As String = "YTUseCookies"
|
||||
Private Const Name_IsMusic As String = "YTIsMusic"
|
||||
Private Const Name_IsChannelUser As String = "YTIsChannelUser"
|
||||
Private Const Name_YTMediaType As String = "YTMediaType"
|
||||
Private Const Name_LastDownloadDateVideos As String = "YTLastDownloadDateVideos"
|
||||
Private Const Name_LastDownloadDateShorts As String = "YTLastDownloadDateShorts"
|
||||
Private Const Name_LastDownloadDatePlaylist As String = "YTLastDownloadDatePlaylist"
|
||||
#End Region
|
||||
#Region "Declarations"
|
||||
Friend Property DownloadYTVideos As Boolean = True
|
||||
Friend Property DownloadYTShorts As Boolean = False
|
||||
Friend Property DownloadYTPlaylists As Boolean = False
|
||||
Friend Property YTUseCookies As Boolean = False
|
||||
Friend Property IsMusic As Boolean = False
|
||||
Friend Property IsChannelUser As Boolean = False
|
||||
Friend Property YTMediaType As YouTubeMediaType = YouTubeMediaType.Undefined
|
||||
Private LastDownloadDateVideos As Date? = Nothing
|
||||
Private LastDownloadDateShorts As Date? = Nothing
|
||||
Private LastDownloadDatePlaylist As Date? = Nothing
|
||||
Friend Function GetUserUrl() As String
|
||||
If YTMediaType = YouTubeMediaType.PlayList Then
|
||||
Return $"https://{IIf(IsMusic, "music", "www")}.youtube.com/playlist?list={ID}"
|
||||
Else
|
||||
Return $"https://{IIf(IsMusic, "music", "www")}.youtube.com/{IIf(IsMusic Or IsChannelUser, $"{YouTubeFunctions.UserChannelOption}/", "@")}{ID}"
|
||||
End If
|
||||
End Function
|
||||
#End Region
|
||||
#Region "Initializer, loader"
|
||||
Friend Sub New()
|
||||
UseInternalDownloadFileFunction = True
|
||||
SeparateVideoFolder = False
|
||||
End Sub
|
||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||
With Container
|
||||
Dim SetNames As Action = Sub()
|
||||
If Not Name.IsEmptyString And ID.IsEmptyString Then
|
||||
Dim n As List(Of String) = Name.Split("@").ToList
|
||||
If n.ListExists(2) Then
|
||||
Dim intValue% = n(1)
|
||||
If intValue > 0 Then
|
||||
If intValue >= SiteSettings.ChannelUserInt Then IsChannelUser = True : intValue -= SiteSettings.ChannelUserInt
|
||||
If intValue >= UserMedia.Types.Audio Then IsMusic = True : intValue -= UserMedia.Types.Audio
|
||||
YTMediaType = intValue
|
||||
n.RemoveAt(1)
|
||||
ID = n(0)
|
||||
End If
|
||||
End If
|
||||
End If
|
||||
End Sub
|
||||
If Loading Then
|
||||
DownloadYTVideos = .Value(Name_DownloadYTVideos).FromXML(Of Boolean)(True)
|
||||
DownloadYTShorts = .Value(Name_DownloadYTShorts).FromXML(Of Boolean)(False)
|
||||
DownloadYTPlaylists = .Value(Name_DownloadYTPlaylists).FromXML(Of Boolean)(False)
|
||||
IsMusic = .Value(Name_IsMusic).FromXML(Of Boolean)(False)
|
||||
IsChannelUser = .Value(Name_IsChannelUser).FromXML(Of Boolean)(False)
|
||||
YTMediaType = .Value(Name_YTMediaType).FromXML(Of Integer)(YouTubeMediaType.Undefined)
|
||||
LastDownloadDateVideos = AConvert(Of Date)(.Value(Name_LastDownloadDateVideos), DateTimeDefaultProvider, Nothing)
|
||||
LastDownloadDateShorts = AConvert(Of Date)(.Value(Name_LastDownloadDateShorts), DateTimeDefaultProvider, Nothing)
|
||||
LastDownloadDatePlaylist = AConvert(Of Date)(.Value(Name_LastDownloadDatePlaylist), DateTimeDefaultProvider, Nothing)
|
||||
SetNames.Invoke()
|
||||
Else
|
||||
SetNames.Invoke()
|
||||
If Not ID.IsEmptyString Then .Value(Name_UserID) = ID
|
||||
.Add(Name_DownloadYTVideos, DownloadYTVideos.BoolToInteger)
|
||||
.Add(Name_DownloadYTShorts, DownloadYTShorts.BoolToInteger)
|
||||
.Add(Name_DownloadYTPlaylists, DownloadYTPlaylists.BoolToInteger)
|
||||
.Add(Name_IsMusic, IsMusic.BoolToInteger)
|
||||
.Add(Name_IsChannelUser, IsChannelUser.BoolToInteger)
|
||||
.Add(Name_YTMediaType, CInt(YTMediaType))
|
||||
.Add(Name_LastDownloadDateVideos, AConvert(Of String)(LastDownloadDateVideos, DateTimeDefaultProvider, String.Empty))
|
||||
.Add(Name_LastDownloadDateShorts, AConvert(Of String)(LastDownloadDateShorts, DateTimeDefaultProvider, String.Empty))
|
||||
.Add(Name_LastDownloadDatePlaylist, AConvert(Of String)(LastDownloadDatePlaylist, DateTimeDefaultProvider, String.Empty))
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Exchange options"
|
||||
Friend Overrides Function ExchangeOptionsGet() As Object
|
||||
Return New UserExchangeOptions(Me)
|
||||
End Function
|
||||
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
|
||||
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
|
||||
With DirectCast(Obj, UserExchangeOptions)
|
||||
DownloadYTVideos = .DownloadVideos
|
||||
DownloadYTShorts = .DownloadShorts
|
||||
DownloadYTPlaylists = .DownloadPlaylists
|
||||
YTUseCookies = .UseCookies
|
||||
End With
|
||||
End If
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Download"
|
||||
'Playlist reconfiguration implemented only for channels + music
|
||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||
Try
|
||||
Dim container As IYouTubeMediaContainer = Nothing
|
||||
Dim list As New List(Of IYouTubeMediaContainer)
|
||||
Dim url$ = String.Empty
|
||||
Dim maxDate As Date? = Nothing
|
||||
Dim nDate As Func(Of Date?, Date?) = Function(ByVal dInput As Date?) As Date?
|
||||
If dInput.HasValue Then
|
||||
If dInput.Value.AddDays(3) < Now Then Return dInput.Value.AddDays(1) Else Return dInput
|
||||
Else
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
Dim fillList As Func(Of Date?, Boolean) = Function(ByVal lDate As Date?) As Boolean
|
||||
If Not container Is Nothing AndAlso container.HasElements Then
|
||||
Dim ce As IEnumerable(Of IYouTubeMediaContainer)
|
||||
ce = container.Elements
|
||||
If ce.ListExists Then ce = ce.Where(Function(e) e.ObjectType = YouTubeMediaType.Single)
|
||||
If ce.ListExists AndAlso lDate.HasValue Then _
|
||||
ce = ce.Where(Function(e) e.DateAdded <= lDate.Value AndAlso
|
||||
Not e.ID.IsEmptyString AndAlso Not _TempPostsList.Contains(e.ID))
|
||||
If ce.ListExists Then
|
||||
maxDate = ce.Max(Function(e) e.DateAdded)
|
||||
list.AddRange(ce)
|
||||
Return True
|
||||
End If
|
||||
End If
|
||||
Return False
|
||||
End Function
|
||||
Dim applySpecFolder As Action(Of String, Boolean) = Sub(ByVal fName As String, ByVal isPls As Boolean)
|
||||
If If(container?.Count, 0) > 0 Then _
|
||||
container.Elements.ForEach(Sub(ByVal el As YouTubeMediaContainerBase)
|
||||
If isPls Then
|
||||
el.SpecialPathSetForPlaylist(fName)
|
||||
Else
|
||||
el.SpecialPath = fName
|
||||
el.SpecialPathDisabled = False
|
||||
End If
|
||||
End Sub)
|
||||
End Sub
|
||||
If YTMediaType = YouTubeMediaType.PlayList Then
|
||||
maxDate = Nothing
|
||||
LastDownloadDatePlaylist = nDate(LastDownloadDatePlaylist)
|
||||
url = $"https://{IIf(IsMusic, "music", "www")}.youtube.com/playlist?list={ID}"
|
||||
container = YouTubeFunctions.Parse(url, YTUseCookies, Token,, True, False,, LastDownloadDatePlaylist)
|
||||
applySpecFolder.Invoke(String.Empty, False)
|
||||
If fillList.Invoke(LastDownloadDatePlaylist) Then LastDownloadDatePlaylist = If(maxDate, Now)
|
||||
ElseIf YTMediaType = YouTubeMediaType.Channel Then
|
||||
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}"
|
||||
container = YouTubeFunctions.Parse(url, YTUseCookies, Token,, True, False,, LastDownloadDateVideos)
|
||||
applySpecFolder.Invoke(IIf(IsMusic, String.Empty, "Videos"), False)
|
||||
If fillList.Invoke(LastDownloadDateVideos) Then LastDownloadDateVideos = If(maxDate, Now)
|
||||
End If
|
||||
If Not IsMusic And DownloadYTShorts Then
|
||||
maxDate = Nothing
|
||||
LastDownloadDateShorts = nDate(LastDownloadDateShorts)
|
||||
url = $"https://www.youtube.com/{IIf(IsChannelUser, $"{YouTubeFunctions.UserChannelOption}/", "@")}{ID}/shorts"
|
||||
container = YouTubeFunctions.Parse(url, YTUseCookies, Token,, True, False,, LastDownloadDateShorts)
|
||||
applySpecFolder.Invoke("Shorts", False)
|
||||
If fillList.Invoke(LastDownloadDateShorts) Then LastDownloadDateShorts = If(maxDate, Now)
|
||||
End If
|
||||
If Not IsMusic And DownloadYTPlaylists Then
|
||||
maxDate = Nothing
|
||||
LastDownloadDatePlaylist = nDate(LastDownloadDatePlaylist)
|
||||
url = $"https://www.youtube.com/{IIf(IsChannelUser, $"{YouTubeFunctions.UserChannelOption}/", "@")}{ID}/playlists"
|
||||
container = YouTubeFunctions.Parse(url, YTUseCookies, Token,, True, False,, LastDownloadDatePlaylist)
|
||||
applySpecFolder.Invoke("Playlists", True)
|
||||
If fillList.Invoke(LastDownloadDatePlaylist) Then LastDownloadDatePlaylist = If(maxDate, Now)
|
||||
End If
|
||||
Else
|
||||
Throw New InvalidOperationException($"Media type {YTMediaType} not implemented")
|
||||
End If
|
||||
If list.Count > 0 Then
|
||||
With list(0)
|
||||
If Settings.UserSiteNameUpdateEveryTime Or UserSiteName.IsEmptyString Then UserSiteName = .UserTitle
|
||||
If FriendlyName.IsEmptyString Then FriendlyName = UserSiteName
|
||||
End With
|
||||
_TempMediaList.AddRange(list.Select(Function(c) New UserMedia(c)))
|
||||
_TempPostsList.ListAddList(_TempMediaList.Select(Function(m) m.Post.ID), LNC)
|
||||
list.Clear()
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ProcessException(ex, Token, "data downloading error")
|
||||
End Try
|
||||
End Sub
|
||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||
SeparateVideoFolder = False
|
||||
DownloadContentDefault(Token)
|
||||
End Sub
|
||||
Protected Overrides Function DownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile,
|
||||
ByVal Token As CancellationToken) As SFile
|
||||
If Not Media.Object Is Nothing AndAlso TypeOf Media.Object Is IYouTubeMediaContainer Then
|
||||
With DirectCast(Media.Object, YouTubeMediaContainerBase)
|
||||
Dim f As SFile = .File
|
||||
f.Path = DestinationFile.Path
|
||||
If Not IsSingleObjectDownload And Not .FileIsPlaylistObject Then .FileIgnorePlaylist = True
|
||||
.File = f
|
||||
If IsSingleObjectDownload Then .Progress = Progress
|
||||
.Download(YTUseCookies, Token)
|
||||
If .File.Exists Then Return .File
|
||||
End With
|
||||
End If
|
||||
Return Nothing
|
||||
End Function
|
||||
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
|
||||
_TempMediaList.Add(New UserMedia(Data))
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "DownloadingException"
|
||||
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||
Optional ByVal EObj As Object = Nothing) As Integer
|
||||
Return 0
|
||||
End Function
|
||||
#End Region
|
||||
#Region "IDisposable Support"
|
||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||
If Not disposedValue And disposing Then
|
||||
With _ContentList.Concat(_ContentNew)
|
||||
If .Count > 0 Then
|
||||
For Each m As UserMedia In .Self
|
||||
If Not m.Object Is Nothing AndAlso TypeOf m.Object Is IYouTubeMediaContainer Then DirectCast(m.Object, IYouTubeMediaContainer).Dispose()
|
||||
Next
|
||||
End If
|
||||
End With
|
||||
End If
|
||||
MyBase.Dispose(disposing)
|
||||
End Sub
|
||||
#End Region
|
||||
End Class
|
||||
End Namespace
|
||||
33
SCrawler/API/YouTube/UserExchangeOptions.vb
Normal file
33
SCrawler/API/YouTube/UserExchangeOptions.vb
Normal file
@@ -0,0 +1,33 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports SCrawler.Plugin.Attributes
|
||||
Namespace API.YouTube
|
||||
Friend Class UserExchangeOptions
|
||||
<PSetting(Caption:="Download videos")>
|
||||
Friend Property DownloadVideos As Boolean
|
||||
<PSetting(Caption:="Download shorts")>
|
||||
Friend Property DownloadShorts As Boolean
|
||||
<PSetting(Caption:="Download playlists")>
|
||||
Friend Property DownloadPlaylists As Boolean
|
||||
<PSetting(Caption:="Use cookies", ToolTip:="Use cookies when downloading data.")>
|
||||
Friend Property UseCookies As Boolean
|
||||
Friend Sub New(ByVal u As UserData)
|
||||
DownloadVideos = u.DownloadYTVideos
|
||||
DownloadShorts = u.DownloadYTShorts
|
||||
DownloadPlaylists = u.DownloadYTPlaylists
|
||||
UseCookies = u.YTUseCookies
|
||||
End Sub
|
||||
Friend Sub New(ByVal s As SiteSettings)
|
||||
DownloadVideos = s.DownloadVideos.Value
|
||||
DownloadShorts = s.DownloadShorts.Value
|
||||
DownloadPlaylists = s.DownloadPlaylists.Value
|
||||
UseCookies = s.UseCookies.Value
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
38
SCrawler/API/YouTube/YTSettings_Internal.vb
Normal file
38
SCrawler/API/YouTube/YTSettings_Internal.vb
Normal file
@@ -0,0 +1,38 @@
|
||||
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||
' This program is free software: you can redistribute it and/or modify
|
||||
' it under the terms of the GNU General Public License as published by
|
||||
' the Free Software Foundation, either version 3 of the License, or
|
||||
' (at your option) any later version.
|
||||
'
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports PersonalUtilities.Functions.XML.Base
|
||||
Namespace API.YouTube
|
||||
Friend Class YTSettings_Internal : Inherits Base.YouTubeSettings
|
||||
Private DataUpdated As Boolean = False
|
||||
Friend CookiesUpdated As Boolean = False
|
||||
Friend Sub ResetUpdate()
|
||||
XMLValuesEndEdit(Me)
|
||||
DataUpdated = False
|
||||
CookiesUpdated = False
|
||||
End Sub
|
||||
Friend Sub PerformUpdate()
|
||||
If DataUpdated Then
|
||||
MyBase.BeginUpdate()
|
||||
MyBase.Apply()
|
||||
MyBase.EndUpdate()
|
||||
ElseIf CookiesUpdated Then
|
||||
ApplyCookies()
|
||||
End If
|
||||
End Sub
|
||||
Protected Overrides Sub Apply()
|
||||
DataUpdated = True
|
||||
End Sub
|
||||
Protected Overrides Sub BeginUpdate()
|
||||
End Sub
|
||||
Protected Overrides Sub EndUpdate()
|
||||
End Sub
|
||||
Protected Overrides Sub EndEdit()
|
||||
End Sub
|
||||
End Class
|
||||
End Namespace
|
||||
@@ -313,7 +313,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
|
||||
Dim StartIndex% = Settings.Users.Count
|
||||
Dim f As SFile
|
||||
Dim umo As Boolean = HOST.GetUserMediaOnly
|
||||
Settings.Labels.Add(CannelsLabelName)
|
||||
Settings.Labels.Add(UserData.CannelsLabelName_ChannelsForm)
|
||||
Settings.Labels.Add(LabelsKeeper.NoParsedUser)
|
||||
Dim rUsers$() = UserBanned(PendingUsers.Select(Function(u) u.ID).ToArray)
|
||||
If rUsers.ListExists Then PendingUsers.RemoveAll(Function(u) rUsers.Contains(u))
|
||||
@@ -336,7 +336,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
|
||||
End With
|
||||
Settings.Users.Add(tmpUser)
|
||||
With Settings.Users.Last
|
||||
.Labels.Add(CannelsLabelName)
|
||||
.Labels.Add(UserData.CannelsLabelName_ChannelsForm)
|
||||
.UpdateUserInformation()
|
||||
If Settings.FromChannelCopyImageToUser And Not f.IsEmptyString And Not .File.IsEmptyString Then _
|
||||
CopyFile(ListAddValue(Nothing, New ChannelsCollection.ChannelImage(cn, f)).ListAddList(Settings.Channels.GetUserFiles(.Name), c), .File)
|
||||
@@ -417,7 +417,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
|
||||
Private Async Sub BTT_DOWNLOAD_Click(sender As Object, e As EventArgs) Handles BTT_DOWNLOAD.Click
|
||||
Try
|
||||
AppendPendingUsers()
|
||||
If Not TokenSource Is Nothing OrElse Not HOST.Source.Available(Plugin.ISiteSettings.Download.Channel, False) Then Exit Sub
|
||||
If Not TokenSource Is Nothing OrElse Not HOST.Source.Available(Plugin.ISiteSettings.Download.Main, False) Then Exit Sub
|
||||
Dim InvokeToken As Action = Sub()
|
||||
If TokenSource Is Nothing Then
|
||||
CProgress.Maximum = 0
|
||||
@@ -471,7 +471,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
|
||||
Catch aex As ArgumentException When aex.HelpLink = 1
|
||||
ErrorsDescriber.Execute(EDP.ShowAllMsg, aex)
|
||||
Catch oex As OperationCanceledException When Token.IsCancellationRequested
|
||||
Dim ee As EDP = EDP.SendInLog
|
||||
Dim ee As EDP = EDP.SendToLog
|
||||
If _ShowCancelNotification Then ee += EDP.ShowMainMsg
|
||||
ErrorsDescriber.Execute(ee, oex, New MMessage("Downloading operation canceled", "Status...",, MsgBoxStyle.Exclamation))
|
||||
Catch ex As Exception
|
||||
@@ -803,7 +803,7 @@ Friend Class ChannelViewForm : Implements IChannelLimits
|
||||
Catch aex As ArgumentException When aex.HelpLink = 1
|
||||
ErrorsDescriber.Execute(EDP.LogMessageValue, aex)
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "Post searching error")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "Post searching error")
|
||||
End Try
|
||||
Return p
|
||||
End Function
|
||||
|
||||
BIN
SCrawler/Content/Icons/SiteIcons/MastodonIcon_48.ico
Normal file
BIN
SCrawler/Content/Icons/SiteIcons/MastodonIcon_48.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
BIN
SCrawler/Content/Icons/SiteIcons/PinterestIcon_32.ico
Normal file
BIN
SCrawler/Content/Icons/SiteIcons/PinterestIcon_32.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
BIN
SCrawler/Content/Icons/SiteIcons/ThisVidIcon_16.ico
Normal file
BIN
SCrawler/Content/Icons/SiteIcons/ThisVidIcon_16.ico
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.1 KiB |
BIN
SCrawler/Content/Pictures/SitePictures/MastodonPic_48.png
Normal file
BIN
SCrawler/Content/Pictures/SitePictures/MastodonPic_48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.6 KiB |
BIN
SCrawler/Content/Pictures/SitePictures/PinterestPic_48.png
Normal file
BIN
SCrawler/Content/Pictures/SitePictures/PinterestPic_48.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.7 KiB |
BIN
SCrawler/Content/Pictures/SitePictures/ThisVidPic_16.png
Normal file
BIN
SCrawler/Content/Pictures/SitePictures/ThisVidPic_16.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 684 B |
@@ -72,7 +72,7 @@ Namespace DownloadObjects
|
||||
Dim s As Size = Size
|
||||
Dim ss As Size = Screen.PrimaryScreen.WorkingArea.Size
|
||||
Dim c% = TP_MAIN.RowStyles.Count - 1
|
||||
s.Height = c * RowHeight + LowestValue + (PaddingE.GetOf({TP_MAIN}).Vertical(c) / c).RoundDown - c
|
||||
s.Height = c * RowHeight + LowestValue + (PaddingE.GetOf({TP_MAIN}).Vertical(c) / c).RoundDown
|
||||
If s.Height > ss.Height Then s.Height = ss.Height
|
||||
MinimumSize = Nothing
|
||||
Size = s
|
||||
|
||||
@@ -16,11 +16,6 @@ Imports PersonalUtilities.Tools.Notifications
|
||||
Namespace DownloadObjects
|
||||
Friend Class AutoDownloader : Inherits GroupParameters : Implements IIndexable, IEContainerProvider
|
||||
Friend Event PauseDisabled()
|
||||
Private Shared ReadOnly Property CachePath As SFile
|
||||
Get
|
||||
Return Settings.CachePath
|
||||
End Get
|
||||
End Property
|
||||
Friend Enum Modes As Integer
|
||||
None = 0
|
||||
[Default] = 1
|
||||
@@ -106,9 +101,9 @@ Namespace DownloadObjects
|
||||
uif_orig = uif
|
||||
If uif.Exists Then
|
||||
uif_compressed = uif
|
||||
uif_compressed.Path = CachePath.Path
|
||||
uif_compressed.Path = Settings.Cache.RootDirectory.Path
|
||||
uif_compressed.Name = $"360_{uif.Name}"
|
||||
Using imgR As New ImageRenderer(uif, EDP.SendInLog)
|
||||
Using imgR As New ImageRenderer(uif, EDP.SendToLog)
|
||||
Try : imgR.FitToWidth(360).Save(uif_compressed) : Catch : End Try
|
||||
End Using
|
||||
If uif_compressed.Exists Then uif = uif_compressed
|
||||
@@ -131,7 +126,7 @@ Namespace DownloadObjects
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "[AutoDownloader.NotifiedUser.ShowNotification]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[AutoDownloader.NotifiedUser.ShowNotification]")
|
||||
If Not User Is Nothing Then
|
||||
MainFrameObj.ShowNotification(SettingsCLS.NotificationObjects.AutoDownloader,
|
||||
User.ToString & vbNewLine &
|
||||
@@ -151,7 +146,7 @@ Namespace DownloadObjects
|
||||
ElseIf KeySite = _Key Then
|
||||
User.OpenSite()
|
||||
ElseIf Images.ContainsKey(_Key) Then
|
||||
Images(_Key).Open(, EDP.None)
|
||||
Images(_Key).Open()
|
||||
End If
|
||||
Else
|
||||
Return True
|
||||
@@ -453,7 +448,7 @@ Namespace DownloadObjects
|
||||
Thread.Sleep(500)
|
||||
End While
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "[AutoDownloader.Checker]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[AutoDownloader.Checker]")
|
||||
Finally
|
||||
_StopRequested = False
|
||||
End Try
|
||||
@@ -533,7 +528,7 @@ Namespace DownloadObjects
|
||||
End With
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "[AutoDownloader.Download]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[AutoDownloader.Download]")
|
||||
Finally
|
||||
Keys.Clear()
|
||||
LastDownloadDate = Now
|
||||
|
||||
@@ -20,21 +20,24 @@ Namespace DownloadObjects
|
||||
MyDefs = New DefaultFormOptions(Me, Settings.Design)
|
||||
MyGroups.ListAddList(Plan.Groups, LAP.NotContainsOnly)
|
||||
End Sub
|
||||
Private Class AutomationTimerChecker : Implements IFieldsCheckerProvider
|
||||
Private Property ErrorMessage As String = "The timer value must be greater than 0" Implements IFieldsCheckerProvider.ErrorMessage
|
||||
Private Property Name As String Implements IFieldsCheckerProvider.Name
|
||||
Private Property TypeError As Boolean Implements IFieldsCheckerProvider.TypeError
|
||||
Private Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object Implements ICustomProvider.Convert
|
||||
Private Class AutomationTimerChecker : Inherits FieldsCheckerProviderBase
|
||||
Public Overrides Property ErrorMessage As String
|
||||
Get
|
||||
Return "The timer value must be greater than 0"
|
||||
End Get
|
||||
Set(ByVal msg As String)
|
||||
MyBase.ErrorMessage = msg
|
||||
End Set
|
||||
End Property
|
||||
Public Overrides Function Convert(ByVal Value As Object, ByVal DestinationType As Type, ByVal Provider As IFormatProvider,
|
||||
Optional ByVal NothingArg As Object = Nothing, Optional ByVal e As ErrorsDescriber = Nothing) As Object
|
||||
If CInt(AConvert(Of Integer)(Value, -10)) > 0 Then
|
||||
Return Value
|
||||
Else
|
||||
HasError = True
|
||||
Return Nothing
|
||||
End If
|
||||
End Function
|
||||
Private Function GetFormat(ByVal FormatType As Type) As Object Implements IFormatProvider.GetFormat
|
||||
Throw New NotImplementedException("[GetFormat] is not available in the context of [AutoDownloaderEditorForm]")
|
||||
End Function
|
||||
End Class
|
||||
Private Sub AutoDownloaderEditorForm_Load(sender As Object, e As EventArgs) Handles Me.Load
|
||||
With MyDefs
|
||||
|
||||
@@ -138,7 +138,7 @@ Namespace DownloadObjects
|
||||
BTT_PAUSE_UNLIMITED, BTT_PAUSE_DISABLE}
|
||||
|
||||
If UpdateBase Then UpdateBaseButton(Not p = PauseModes.Disabled)
|
||||
If Not VerifyAll OrElse Settings.Automation.All(Function(ByVal plan As AutoDownloader)
|
||||
If Not VerifyAll OrElse Settings.Automation.All(Function(ByVal plan As AutoDownloader) As Boolean
|
||||
If plan.Mode = AutoDownloader.Modes.None Then
|
||||
Return True
|
||||
Else
|
||||
@@ -166,7 +166,7 @@ Namespace DownloadObjects
|
||||
cntList.Clear()
|
||||
End With
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "[MainFrame.UpdatePauseButtons]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[MainFrame.UpdatePauseButtons]")
|
||||
Finally
|
||||
Select Case Place
|
||||
Case ButtonsPlace.MainFrame : TrayButtons.UpdatePauseButtons(True, True)
|
||||
|
||||
@@ -7,9 +7,9 @@
|
||||
' This program is distributed in the hope that it will be useful,
|
||||
' but WITHOUT ANY WARRANTY
|
||||
Imports System.Threading
|
||||
Imports SCrawler.DownloadObjects.Groups
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports PersonalUtilities.Tools
|
||||
Imports PersonalUtilities.Functions.XML
|
||||
Imports SCrawler.DownloadObjects.Groups
|
||||
Imports PauseModes = SCrawler.DownloadObjects.AutoDownloader.PauseModes
|
||||
Namespace DownloadObjects
|
||||
Friend Class Scheduler : Implements IEnumerable(Of AutoDownloader), IMyEnumerator(Of AutoDownloader), IDisposable
|
||||
|
||||
@@ -75,7 +75,7 @@ Namespace DownloadObjects
|
||||
_RefillInProgress = False
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "[DownloadObjects.SchedulerEditorForm.Refill]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadObjects.SchedulerEditorForm.Refill]")
|
||||
End Try
|
||||
End Sub
|
||||
#Region "Add, Edit, Delete"
|
||||
|
||||
@@ -24,6 +24,7 @@ Namespace DownloadObjects
|
||||
Private WithEvents BTT_OPEN As Button
|
||||
Private ReadOnly PR_MAIN As ProgressBar
|
||||
Private ReadOnly LBL_INFO As Label
|
||||
Private ReadOnly Icon As PictureBox
|
||||
#End Region
|
||||
Private ReadOnly Property Instance As API.Base.ProfileSaved
|
||||
Friend ReadOnly Property Job As TDJob
|
||||
@@ -38,7 +39,21 @@ Namespace DownloadObjects
|
||||
TP_CONTROLS = New TableLayoutPanel With {.Margin = New Padding(0), .Dock = DockStyle.Fill}
|
||||
PR_MAIN = New ProgressBar With {.Dock = DockStyle.Fill}
|
||||
LBL_INFO = New Label With {.Text = String.Empty, .Dock = DockStyle.Fill}
|
||||
Icon = New PictureBox With {
|
||||
.SizeMode = PictureBoxSizeMode.Zoom,
|
||||
.Dock = DockStyle.Fill,
|
||||
.Margin = New Padding(3),
|
||||
.Padding = New Padding(3)
|
||||
}
|
||||
CreateButton(BTT_STOP, My.Resources.DeletePic_24)
|
||||
Dim img As Image = Nothing
|
||||
If Not _Job.Host Is Nothing Then
|
||||
With Job.Host.Source
|
||||
If Not .Icon Is Nothing Then img = .Icon.ToBitmap
|
||||
If img Is Nothing AndAlso Not .Image Is Nothing Then img = .Image
|
||||
End With
|
||||
End If
|
||||
If Not img Is Nothing Then Icon.Image = img : Icon.InitialImage = img
|
||||
|
||||
If Job.Type = Download.Main Then
|
||||
LBL_INFO.Margin = New Padding(3)
|
||||
@@ -48,6 +63,7 @@ Namespace DownloadObjects
|
||||
.RowCount = 1
|
||||
End With
|
||||
With TP_CONTROLS
|
||||
.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 30))
|
||||
.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 30))
|
||||
.ColumnStyles.Add(New ColumnStyle(SizeType.Absolute, 150))
|
||||
.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100))
|
||||
@@ -55,9 +71,10 @@ Namespace DownloadObjects
|
||||
.RowStyles.Add(New RowStyle(SizeType.Percent, 100))
|
||||
.RowCount = 1
|
||||
With .Controls
|
||||
.Add(BTT_STOP, 0, 0)
|
||||
.Add(PR_MAIN, 1, 0)
|
||||
.Add(LBL_INFO, 2, 0)
|
||||
If Not img Is Nothing Then .Add(Icon, 0, 0)
|
||||
.Add(BTT_STOP, 1, 0)
|
||||
.Add(PR_MAIN, 2, 0)
|
||||
.Add(LBL_INFO, 3, 0)
|
||||
End With
|
||||
End With
|
||||
TP_MAIN.Controls.Add(TP_CONTROLS, 0, 0)
|
||||
@@ -71,16 +88,18 @@ Namespace DownloadObjects
|
||||
.Add(New ColumnStyle(SizeType.Absolute, 30))
|
||||
.Add(New ColumnStyle(SizeType.Absolute, 30))
|
||||
.Add(New ColumnStyle(SizeType.Absolute, 30))
|
||||
.Add(New ColumnStyle(SizeType.Absolute, 30))
|
||||
.Add(New ColumnStyle(SizeType.Percent, 100))
|
||||
End With
|
||||
.ColumnCount = 4
|
||||
.ColumnCount = .ColumnStyles.Count
|
||||
.RowStyles.Add(New RowStyle(SizeType.Percent, 50))
|
||||
.RowCount = 1
|
||||
With .Controls
|
||||
.Add(BTT_START, 0, 0)
|
||||
.Add(BTT_STOP, 1, 0)
|
||||
.Add(BTT_OPEN, 2, 0)
|
||||
.Add(PR_MAIN, 3, 0)
|
||||
If Not img Is Nothing Then .Add(Icon, 0, 0)
|
||||
.Add(BTT_START, 1, 0)
|
||||
.Add(BTT_STOP, 2, 0)
|
||||
.Add(BTT_OPEN, 3, 0)
|
||||
.Add(PR_MAIN, 4, 0)
|
||||
End With
|
||||
End With
|
||||
With TP_MAIN
|
||||
@@ -129,11 +148,13 @@ Namespace DownloadObjects
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "Start, Stop"
|
||||
Friend Sub Start()
|
||||
Job.Start(AddressOf DownloadData)
|
||||
Private _IsMultiple As Boolean = False
|
||||
Friend Sub Start(Optional ByVal Multiple As Boolean = False)
|
||||
_IsMultiple = Multiple
|
||||
Job.StartThread(AddressOf DownloadData)
|
||||
End Sub
|
||||
Friend Sub [Stop]()
|
||||
Job.Stop()
|
||||
Job.Cancel()
|
||||
End Sub
|
||||
#End Region
|
||||
#Region "SavedPosts downloading"
|
||||
@@ -144,15 +165,16 @@ Namespace DownloadObjects
|
||||
btte.Invoke(BTT_STOP, True)
|
||||
Job.Progress.InformationTemporary = $"{Job.Host.Name} downloading started"
|
||||
Job.Start()
|
||||
Instance.Download(Job.Token)
|
||||
Instance.Download(Job.Token, _IsMultiple)
|
||||
RaiseEvent DownloadDone(SettingsCLS.NotificationObjects.SavedPosts, $"Downloading saved {Job.Host.Name} posts is completed")
|
||||
Catch ex As Exception
|
||||
Job.Progress.InformationTemporary = $"{Job.Host.Name} downloading error"
|
||||
ErrorsDescriber.Execute(EDP.LogMessageValue, ex, {$"{Job.Host.Name} saved posts downloading error", "Saved posts"})
|
||||
Finally
|
||||
_IsMultiple = False
|
||||
btte.Invoke(BTT_START, True)
|
||||
btte.Invoke(BTT_STOP, False)
|
||||
Job.Stopped()
|
||||
Job.Finish()
|
||||
If Job.Type = Download.SavedPosts Then Job.Progress.Maximum = 0 : Job.Progress.Value = 0
|
||||
End Try
|
||||
End Sub
|
||||
@@ -173,6 +195,7 @@ Namespace DownloadObjects
|
||||
If Not BTT_START Is Nothing Then BTT_START.Dispose()
|
||||
If Not BTT_STOP Is Nothing Then BTT_STOP.Dispose()
|
||||
If Not BTT_OPEN Is Nothing Then BTT_OPEN.Dispose()
|
||||
If Not Icon Is Nothing Then Icon.Dispose()
|
||||
PR_MAIN.Dispose()
|
||||
LBL_INFO.Dispose()
|
||||
TP_CONTROLS.Controls.Clear()
|
||||
|
||||
@@ -60,7 +60,7 @@ Friend Class DownloadSavedPostsForm
|
||||
MyView.Dispose(Settings.Design)
|
||||
End Sub
|
||||
Private Sub [Start]() Handles BTT_DOWN_ALL.Click
|
||||
If JobsList.Count > 0 Then JobsList.ForEach(Sub(j) j.Start())
|
||||
If JobsList.Count > 0 Then JobsList.ForEach(Sub(j) j.Start(True))
|
||||
End Sub
|
||||
Friend Sub [Stop]() Handles BTT_STOP_ALL.Click
|
||||
If JobsList.Count > 0 Then JobsList.ForEach(Sub(j) j.Stop())
|
||||
|
||||
@@ -126,7 +126,7 @@ Namespace DownloadObjects
|
||||
_LatestSelected = -1
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "[DownloadedInfoForm.RefillList]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadedInfoForm.RefillList]")
|
||||
Finally
|
||||
UpdateNavigationButtons(Nothing)
|
||||
End Try
|
||||
|
||||
@@ -99,6 +99,7 @@ Namespace DownloadObjects
|
||||
'TP_DATA
|
||||
'
|
||||
Me.TP_DATA.AutoScroll = True
|
||||
Me.TP_DATA.AutoSizeMode = System.Windows.Forms.AutoSizeMode.GrowAndShrink
|
||||
Me.TP_DATA.ColumnCount = 1
|
||||
Me.TP_DATA.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
|
||||
Me.TP_DATA.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
|
||||
|
||||
@@ -213,7 +213,7 @@ Namespace DownloadObjects
|
||||
End If
|
||||
If fList.ListExists Then fList.Clear()
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[DownloadObjects.DownloadFeedForm.SessionChooser({GetLast})]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[DownloadObjects.DownloadFeedForm.SessionChooser({GetLast})]")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
@@ -242,7 +242,7 @@ Namespace DownloadObjects
|
||||
If d > 0 Then
|
||||
.AutoScroll = False
|
||||
.AutoScroll = True
|
||||
If LatestScrollValue.HasValue Then TP_DATA.VerticalScroll.Value = LatestScrollValue.Value
|
||||
SetScrollValue(False)
|
||||
.PerformLayout()
|
||||
LatestScrollValueDisabled = False
|
||||
End If
|
||||
@@ -302,7 +302,7 @@ Namespace DownloadObjects
|
||||
If HeightChanged Then
|
||||
TP_DATA.AutoScroll = False
|
||||
TP_DATA.AutoScroll = True
|
||||
If LatestScrollValue.HasValue Then TP_DATA.VerticalScroll.Value = LatestScrollValue.Value
|
||||
SetScrollValue(False)
|
||||
TP_DATA.PerformLayout()
|
||||
LatestScrollValueDisabled = False
|
||||
End If
|
||||
@@ -331,10 +331,7 @@ Namespace DownloadObjects
|
||||
DirectCast(MyRange.Switcher, RangeSwitcher(Of UserMediaD)).PerformIndexChanged()
|
||||
If Not indxChanged Then
|
||||
LatestScrollValueDisabled = False
|
||||
If LatestScrollValue.HasValue Then
|
||||
TP_DATA.VerticalScroll.Value = LatestScrollValue.Value
|
||||
TP_DATA.PerformLayout()
|
||||
End If
|
||||
SetScrollValue(True)
|
||||
End If
|
||||
End If
|
||||
.HandlersSuspended = False
|
||||
@@ -415,7 +412,7 @@ Namespace DownloadObjects
|
||||
RefillInProgress = False
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[DownloadObjects.DownloadFeedForm.Range.IndexChanged({Sender.CurrentIndex})]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[DownloadObjects.DownloadFeedForm.Range.IndexChanged({Sender.CurrentIndex})]")
|
||||
RefillInProgress = False
|
||||
Finally
|
||||
If Not RefillInProgress AndAlso Sender.CurrentIndex >= 0 Then
|
||||
@@ -495,8 +492,7 @@ Namespace DownloadObjects
|
||||
If LatestScrollValue.HasValue Then ControlInvoke(TP_DATA, Sub()
|
||||
Dim b As Boolean = ScrollSuspended
|
||||
If Not b Then ScrollSuspended = True
|
||||
TP_DATA.VerticalScroll.Value = LatestScrollValue.Value
|
||||
TP_DATA.PerformLayout()
|
||||
SetScrollValue(True)
|
||||
If Not b Then ScrollSuspended = False
|
||||
End Sub)
|
||||
End Sub
|
||||
@@ -515,6 +511,26 @@ Namespace DownloadObjects
|
||||
End Sub)
|
||||
End If
|
||||
End Sub
|
||||
Private Sub TP_DATA_StyleChanged(sender As Object, e As EventArgs) Handles TP_DATA.StyleChanged
|
||||
ControlInvokeFast(TP_DATA, Sub()
|
||||
With TP_DATA
|
||||
.Padding = New Padding(0, 0, .VerticalScroll.Visible.BoolToInteger * 3, 0)
|
||||
.HorizontalScroll.Visible = False
|
||||
.HorizontalScroll.Enabled = False
|
||||
.PerformLayout()
|
||||
End With
|
||||
End Sub, EDP.None)
|
||||
End Sub
|
||||
Private Sub SetScrollValue(ByVal Perform As Boolean)
|
||||
With TP_DATA
|
||||
If LatestScrollValue.HasValue Then
|
||||
If LatestScrollValue.Value < 0 Then LatestScrollValue = 0
|
||||
If LatestScrollValue.Value > .VerticalScroll.Maximum Then LatestScrollValue = .VerticalScroll.Maximum
|
||||
.VerticalScroll.Value = LatestScrollValue.Value
|
||||
If Perform Then .PerformLayout()
|
||||
End If
|
||||
End With
|
||||
End Sub
|
||||
#End Region
|
||||
Private Sub ClearTable()
|
||||
ControlInvoke(TP_DATA, Sub()
|
||||
|
||||
@@ -53,7 +53,7 @@ Namespace DownloadObjects
|
||||
End If
|
||||
End Set
|
||||
End Property
|
||||
Private ReadOnly Property ObjectsPaddingHeight
|
||||
Private ReadOnly Property ObjectsPaddingHeight As Integer
|
||||
Get
|
||||
Return TP_MAIN.RowStyles(0).Height + PaddingE.GetOf({TP_MAIN}).Vertical(2)
|
||||
End Get
|
||||
@@ -205,7 +205,7 @@ Namespace DownloadObjects
|
||||
Catch tex As Threading.ThreadStateException
|
||||
HasError = True
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[DownloadObjects.FeedMedia({File})]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"[DownloadObjects.FeedMedia({File})]")
|
||||
HasError = True
|
||||
End Try
|
||||
End Sub
|
||||
@@ -235,7 +235,7 @@ Namespace DownloadObjects
|
||||
#End Region
|
||||
#Region "Context"
|
||||
Private Sub BTT_CONTEXT_OPEN_MEDIA_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_MEDIA.Click
|
||||
File.Open(, EDP.None)
|
||||
File.Open()
|
||||
End Sub
|
||||
Private Sub BTT_CONTEXT_OPEN_USER_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_USER.Click
|
||||
If Not UserKey.IsEmptyString Then
|
||||
|
||||
@@ -54,8 +54,8 @@ Namespace DownloadObjects
|
||||
MyVideo.MediaPlayer = MediaPlayer
|
||||
TR_VOLUME.Value = MediaPlayer.Volume / 10
|
||||
If Settings.UseM3U8 Then
|
||||
Dim f As SFile = $"{Settings.CachePath.PathWithSeparator}FeedSnapshots\{File.GetHashCode}.png"
|
||||
If Not f.Exists Then f = FFMPEG.TakeSnapshot(File, f, Settings.FfmpegFile, TimeSpan.FromSeconds(1))
|
||||
Dim f As SFile = $"{Settings.Cache.RootDirectory.PathWithSeparator}FeedSnapshots\{File.GetHashCode}.png"
|
||||
If Not f.Exists Then f = FFMPEG.TakeSnapshot(File, f, Settings.FfmpegFile.File, TimeSpan.FromSeconds(1))
|
||||
If f.Exists Then
|
||||
MyImage = New ImageRenderer(f, EDP.None)
|
||||
Try
|
||||
@@ -64,14 +64,14 @@ Namespace DownloadObjects
|
||||
MyVideo.BackgroundImageLayout = ImageLayout.Zoom
|
||||
End If
|
||||
Catch img_set_ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, img_set_ex, "Error setting background image for media player." & vbCr &
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, img_set_ex, "Error setting background image for media player." & vbCr &
|
||||
$"File: {File}{vbCr}Image: {f}")
|
||||
End Try
|
||||
End If
|
||||
End If
|
||||
UpdateButtons()
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"Media player initialization error({File})")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"Media player initialization error({File})")
|
||||
HasError = True
|
||||
End Try
|
||||
End Sub
|
||||
@@ -173,7 +173,7 @@ Namespace DownloadObjects
|
||||
Catch oex As OperationCanceledException
|
||||
MyMainLOG = $"Cannot perform action [{ActionName}] on file [{MediaFile}]"
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"An error occurred while performing action [{ActionName}] on file [{MediaFile}]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, $"An error occurred while performing action [{ActionName}] on file [{MediaFile}]")
|
||||
End Try
|
||||
End Function
|
||||
End Class
|
||||
|
||||
@@ -171,7 +171,7 @@ Namespace DownloadObjects.Groups
|
||||
Return Nothing
|
||||
End If
|
||||
Catch ex As Exception
|
||||
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[DownloadGroup.GetUsers]")
|
||||
Return ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadGroup.GetUsers]")
|
||||
End Try
|
||||
End Function
|
||||
#End Region
|
||||
@@ -187,7 +187,7 @@ Namespace DownloadObjects.Groups
|
||||
End If
|
||||
End If
|
||||
Catch ex As Exception
|
||||
ErrorsDescriber.Execute(EDP.SendInLog, ex, "[DownloadGroup.DownloadUsers]")
|
||||
ErrorsDescriber.Execute(EDP.SendToLog, ex, "[DownloadGroup.DownloadUsers]")
|
||||
End Try
|
||||
End Sub
|
||||
#End Region
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user