Skip to content

Commit be97752

Browse files
committed
2026.3.23.0
API.EditorExchangeOptionsBase: move the 'Apply' function API.EditorExchangeOptionsBase_P: remove the 'Apply' function API.UserDataBase: update the 'UserSiteNameUpdate' function; remove the '_ForceSaveUserInfoOnException' property; update the 'DownloadContentDefault_ConvertWebp' function; add the 'DownloadContentDefault_ConvertWebp_TestImg' and 'DownloadContentDefault_ConvertWebp_Impl' functions API.Bluesky: add the 'EditorExchangeOptions' class; add 'did:' handle for recognizing API.PornHub: update M3U8 download functions API.TikTok: fix where gallery-dl would hang when downloading stories API.Twitter: fix downloading a single media file (standalone downloader) fails API.Xhamster: add 'webm' to 'jpg' correct conversion FeedMedia: move the 'ImageRenderer2' class into the 'UserImage' class UserImage: add the 'ImageRenderer2' class
1 parent d0d8e54 commit be97752

21 files changed

Lines changed: 253 additions & 89 deletions

Changelog.md

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,34 @@
22
- [ffmpeg](https://github.com/AAndyProgram/SCrawler/wiki/Settings#ffmpeg)
33
- x64 version - [release](https://github.com/GyanD/codexffmpeg/releases/tag/5.1.2); [zip](https://github.com/GyanD/codexffmpeg/releases/download/5.1.2/ffmpeg-5.1.2-full_build.zip); **version `5.1.2-full_build-www.gyan.dev`**
44
- x86 version - [release](https://github.com/yt-dlp/FFmpeg-Builds/releases/tag/autobuild-2022-11-30-12-57); [zip](https://github.com/yt-dlp/FFmpeg-Builds/releases/download/autobuild-2022-11-30-12-57/ffmpeg-N-109274-gd7a5f068c2-win32-gpl.zip); **version `N-109457-geeb280f351-20221226`**
5-
- [Gallery-dl](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl) - **1.31.6**
6-
- [YT-DLP](https://github.com/AAndyProgram/SCrawler/wiki/Settings#yt-dlp) - **2026.02.04.233607**
5+
- [Gallery-dl](https://github.com/AAndyProgram/SCrawler/wiki/Settings#gallery-dl) - **1.31.9**
6+
- [YT-DLP](https://github.com/AAndyProgram/SCrawler/wiki/Settings#yt-dlp) - **2026.03.17**
77
- [Deno](https://github.com/AAndyProgram/SCrawler/wiki/Settings#deno) - latest *(`2.0.0` or higher)*
88
- [OF-Scraper](https://github.com/AAndyProgram/SCrawler/wiki/Settings#of-scraper) - **3.12.9** ([release](https://github.com/datawhores/OF-Scraper/releases/tag/3.12.9))
99

1010
# 2026
1111

12+
## 2026.3.23.0
13+
14+
*2026-03-23*
15+
16+
- Added
17+
- Sites:
18+
- Bluesky:
19+
- download models (user options), like in Twitter
20+
- `did` handle for recognizing
21+
- PornHub: `M3U8` download functionality update
22+
- xHamster: add `webm` to `jpg` correct conversion
23+
- Minor improvements
24+
- Updated
25+
- gallery-dl up to version **1.31.9**
26+
- yt-dlp up to version **2026.03.17**
27+
- Fixed
28+
- Sites:
29+
- TikTok: gallery-dl downloads stories slowly when the user has no stories
30+
- Twitter: downloading a single media file (standalone downloader) fails
31+
- Minor bugs
32+
1233
## 2026.2.14.0
1334

1435
*2026-02-14*

SCrawler/API/Base/EditorExchangeOptionsBase.vb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,9 @@ Namespace API.Base
4242
u.DownloadTextSpecialFolder = DownloadTextSpecialFolder
4343
End If
4444
End Sub
45+
Friend Overridable Sub Apply(ByRef u As UserDataBase)
46+
ApplyBase(u)
47+
If TypeOf u Is IPSite And TypeOf Me Is IPSite Then DirectCast(u, IPSite).QueryString = DirectCast(Me, IPSite).QueryString
48+
End Sub
4549
End Class
4650
End Namespace

SCrawler/API/Base/EditorExchangeOptionsBase_P.vb

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,6 @@ Namespace API.Base
3131
MyBase.New(s)
3232
DisableBase()
3333
End Sub
34-
Friend Overridable Sub Apply(ByRef u As IPSite)
35-
ApplyBase(u)
36-
u.QueryString = QueryString
37-
End Sub
3834
Protected Overridable Sub DisableBase()
3935
_ApplyBase_Name = False
4036
_ApplyBase_Text = False

SCrawler/API/Base/UserDataBase.vb

Lines changed: 30 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -360,8 +360,8 @@ Namespace API.Base
360360
Me._UserSiteName = _UserSiteName
361361
End Set
362362
End Property
363-
Protected Sub UserSiteNameUpdate(ByVal NewName As String)
364-
If Not NewName.IsEmptyString And (UserSiteName.IsEmptyString Or Settings.UpdateUserSiteNameEveryTime) Then UserSiteName = NewName
363+
Protected Sub UserSiteNameUpdate(ByVal NewName As String, Optional ByVal Force As Boolean = False)
364+
If Not NewName.IsEmptyString And (UserSiteName.IsEmptyString Or Settings.UpdateUserSiteNameEveryTime Or Force) Then UserSiteName = NewName
365365
End Sub
366366
Friend ReadOnly Property UserModel As UsageModel Implements IUserData.UserModel
367367
Get
@@ -1030,7 +1030,7 @@ BlockNullPicture:
10301030
End Try
10311031
End Sub
10321032
Private Sub UpdateUserInformation_Ex()
1033-
If _ForceSaveUserInfoOnException Then UpdateUserInformation()
1033+
If _ForceSaveUserInfo Then UpdateUserInformation()
10341034
End Sub
10351035
Friend Overridable Overloads Sub UpdateUserInformation() Implements IUserData.UpdateUserInformation
10361036
UpdateUserInformation(False)
@@ -1207,7 +1207,6 @@ BlockNullPicture:
12071207
Protected UseClientTokens As Boolean = False
12081208
Protected _ForceSaveUserData As Boolean = False
12091209
Protected _ForceSaveUserInfo As Boolean = False
1210-
Protected _ForceSaveUserInfoOnException As Boolean = False
12111210
Private _DownloadInProgress As Boolean = False
12121211
Private _EnvirUserExists As Boolean
12131212
Private _EnvirUserSuspended As Boolean
@@ -1227,7 +1226,6 @@ BlockNullPicture:
12271226
_DescriptionEveryTime = Settings.UpdateUserDescriptionEveryTime
12281227
_ForceSaveUserData = False
12291228
_ForceSaveUserInfo = False
1230-
_ForceSaveUserInfoOnException = False
12311229
_EnvirUserExists = UserExists
12321230
_EnvirUserSuspended = UserSuspended
12331231
_EnvirCreatedByChannel = CreatedByChannel
@@ -1244,8 +1242,8 @@ BlockNullPicture:
12441242
Select Case Caller
12451243
Case NameOf(UserExists) : If Not _EnvirUserExists = CBool(NewValue) Then _EnvirChanged = True : _EnvirInvokeUserUpdated = True
12461244
Case NameOf(UserSuspended) : If Not _EnvirUserSuspended = CBool(NewValue) Then _EnvirChanged = True : _EnvirInvokeUserUpdated = True
1247-
Case NameOf(NameTrue) : _EnvirChanged = True : _ForceSaveUserInfo = True : _ForceSaveUserInfoOnException = True
1248-
Case NameOf(ID) : _EnvirChanged = True : _ForceSaveUserInfo = True : _ForceSaveUserInfoOnException = True
1245+
Case NameOf(NameTrue) : _EnvirChanged = True : _ForceSaveUserInfo = True
1246+
Case NameOf(ID) : _EnvirChanged = True : _ForceSaveUserInfo = True
12491247
Case Else : _EnvirChanged = True
12501248
End Select
12511249
End If
@@ -1843,7 +1841,8 @@ BlockNullPicture:
18431841

18441842
updateDownCount(False)
18451843

1846-
v.File = DownloadContentDefault_ConvertWebp(ChangeFileNameByProvider(f, v), postProcessWebp)
1844+
v.File = ChangeFileNameByProvider(f, v)
1845+
v.File = DownloadContentDefault_ConvertWebp(v, postProcessWebp)
18471846
v.State = UStates.Downloaded
18481847
DownloadContentDefault_PostProcessing(v, f, Token)
18491848
If UseMD5Comparison And (v.Type = UTypes.GIF Or v.Type = UTypes.Picture) Then
@@ -1943,7 +1942,11 @@ stxt:
19431942
End Function
19441943
Protected Overridable Sub DownloadContentDefault_PostProcessing(ByRef m As UserMedia, ByVal File As SFile, ByVal Token As CancellationToken)
19451944
End Sub
1946-
Protected Overridable Function DownloadContentDefault_ConvertWebp(ByVal WebpFile As SFile, ByVal Process As Boolean) As SFile
1945+
Protected Overridable Function DownloadContentDefault_ConvertWebp(ByVal m As UserMedia, ByVal Process As Boolean) As SFile
1946+
Return DownloadContentDefault_ConvertWebp_Impl(m, Process)
1947+
End Function
1948+
Protected Overridable Function DownloadContentDefault_ConvertWebp_Impl(ByVal m As UserMedia, ByVal Process As Boolean) As SFile
1949+
Dim WebpFile As SFile = m.File
19471950
Dim f As SFile = WebpFile
19481951
If Process AndAlso f.Exists Then
19491952
f.Path = $"{f.PathWithSeparator}Sources"
@@ -1959,6 +1962,24 @@ stxt:
19591962
End If
19601963
Return f
19611964
End Function
1965+
Protected Function DownloadContentDefault_ConvertWebp_TestImg(ByVal m As UserMedia, ByVal Process As Boolean) As SFile
1966+
If m.Type = UTypes.Picture And Settings.DownloadNativeImageFormat Then
1967+
Using testImg As New UserImage.ImageRenderer2(m.File, EDP.ReturnValue)
1968+
If testImg.IsWebP Then
1969+
Dim f As SFile = m.File
1970+
If f.Extension.IsEmptyString OrElse Not f.Extension = UserImage.ExtWebp Then
1971+
f.Extension = UserImage.ExtWebp
1972+
f = SFile.Rename(m.File, f,, EDP.ReturnValue).IfNullOrEmpty(m.File)
1973+
End If
1974+
m.File = f
1975+
Return DownloadContentDefault_ConvertWebp_Impl(m, True).IfNullOrEmpty(f)
1976+
End If
1977+
End Using
1978+
ElseIf Process Then
1979+
Return DownloadContentDefault_ConvertWebp_Impl(m, Process)
1980+
End If
1981+
Return m.File
1982+
End Function
19621983
Protected Overridable Function DownloadContentDefault_ProcessDownloadException() As Boolean
19631984
Return True
19641985
End Function
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
' Copyright (C) Andy https://github.com/AAndyProgram
2+
' This program is free software: you can redistribute it and/or modify
3+
' it under the terms of the GNU General Public License as published by
4+
' the Free Software Foundation, either version 3 of the License, or
5+
' (at your option) any later version.
6+
'
7+
' This program is distributed in the hope that it will be useful,
8+
' but WITHOUT ANY WARRANTY
9+
Imports SCrawler.API.Base
10+
Imports SCrawler.Plugin.Attributes
11+
Namespace API.Bluesky
12+
Friend Class EditorExchangeOptions : Inherits Base.EditorExchangeOptionsBase
13+
Friend Overrides Property SiteKey As String = BlueskySiteKey
14+
<PSetting(NameOf(SiteSettings.DownloadModelMedia), NameOf(MySettings), Address:=SettingAddress.User)>
15+
Friend Overridable Property DownloadModelMedia As Boolean = False
16+
<PSetting(NameOf(SiteSettings.DownloadModelProfile), NameOf(MySettings), Address:=SettingAddress.User)>
17+
Friend Overridable Property DownloadModelProfile As Boolean = False
18+
Private ReadOnly Property MySettings As Object
19+
Friend Sub New(ByVal s As SiteSettings)
20+
DownloadModelMedia = s.DownloadModelMedia.Value
21+
DownloadModelProfile = s.DownloadModelProfile.Value
22+
MySettings = s
23+
End Sub
24+
Friend Sub New(ByVal u As UserData)
25+
DownloadModelMedia = u.DownloadModelMedia
26+
DownloadModelProfile = u.DownloadModelProfile
27+
MySettings = u.HOST.Source
28+
End Sub
29+
Friend Overrides Sub Apply(ByRef u As UserDataBase)
30+
MyBase.Apply(u)
31+
If Not DownloadModelMedia And Not DownloadModelProfile Then
32+
DownloadModelMedia = True
33+
ElseIf DownloadModelMedia Then
34+
DownloadModelProfile = False
35+
Else
36+
DownloadModelMedia = False
37+
End If
38+
With DirectCast(u, UserData)
39+
.DownloadModelMedia = DownloadModelMedia
40+
.DownloadModelProfile = DownloadModelProfile
41+
End With
42+
End Sub
43+
End Class
44+
End Namespace

SCrawler/API/Bluesky/SiteSettings.vb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,10 @@ Namespace API.Bluesky
2626
<PXML> Friend ReadOnly Property TokenUpdateTime As PropertyValue
2727
<PropertyOption(ControlText:="Token update", ControlToolTip:="Token refresh interval (in minutes)." & vbCr & "Default: 120.", IsAuth:=True), PXML, PClonable, HiddenControl>
2828
Friend ReadOnly Property TokenRefreshInterval As PropertyValue
29+
<PropertyOption(ControlText:="Download model 'Media'", ControlToolTip:="Parse the 'Media' block", Category:=DeclaredNames.CAT_UserDefs), PXML, PClonable>
30+
Friend ReadOnly Property DownloadModelMedia As PropertyValue
31+
<PropertyOption(ControlText:="Download model 'Profile'", ControlToolTip:="Parse the 'Posts' block", Category:=DeclaredNames.CAT_UserDefs), PXML, PClonable>
32+
Friend ReadOnly Property DownloadModelProfile As PropertyValue
2933
Friend Sub New(ByVal AccName As String, ByVal Temp As Boolean)
3034
MyBase.New("Bluesky", "bsky.app", AccName, Temp, My.Resources.SiteResources.BlueskyIcon_32, My.Resources.SiteResources.BlueskyPic_32)
3135

@@ -38,11 +42,14 @@ Namespace API.Bluesky
3842
TokenUpdateTime = New PropertyValue(Now.AddYears(-1))
3943
TokenRefreshInterval = New PropertyValue(120)
4044

45+
DownloadModelMedia = New PropertyValue(True)
46+
DownloadModelProfile = New PropertyValue(False)
47+
4148
_AllowUserAgentUpdate = False
4249
UrlPatternUser = "https://bsky.app/profile/{0}"
4350
ImageVideoContains = "bsky.app"
4451
UserRegex = RParams.DMS("bsky.app/profile/([^/\?]+)", 1, EDP.ReturnValue)
45-
UserOptionsType = GetType(EditorExchangeOptionsBase)
52+
UserOptionsType = GetType(EditorExchangeOptions)
4653
End Sub
4754
Protected Overrides Function UserOptionsValid(ByVal Options As Object) As Boolean
4855
Return DirectCast(Options, EditorExchangeOptionsBase).SiteKey = BlueskySiteKey
@@ -96,5 +103,10 @@ Namespace API.Bluesky
96103
_TokenUpdating = False
97104
End Try
98105
End Function
106+
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
107+
Dim e As ExchangeOptions = MyBase.IsMyUser(UserURL)
108+
If Not e.UserName.IsEmptyString AndAlso e.UserName.StartsWith("did:") Then e.UserName = e.UserName.Replace(":", "@")
109+
Return e
110+
End Function
99111
End Class
100112
End Namespace

SCrawler/API/Bluesky/UserData.vb

Lines changed: 53 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@ Imports UTypes = SCrawler.API.Base.UserMedia.Types
1616
Imports UStates = SCrawler.API.Base.UserMedia.States
1717
Namespace API.Bluesky
1818
Friend Class UserData : Inherits UserDataBase
19+
#Region "XML names"
20+
Private Const Name_DownloadModelMedia As String = "DownloadModelMedia"
21+
Private Const Name_DownloadModelProfile As String = "DownloadModelProfile"
22+
Private Const Name_ForceParseProfileInfo As String = "ForceParseProfileInfo"
23+
#End Region
1924
#Region "Declarations"
2025
Private ReadOnly Property MySettings As SiteSettings
2126
Get
@@ -28,16 +33,45 @@ Namespace API.Bluesky
2833
End Get
2934
End Property
3035
Private ReadOnly _TmpPosts2 As List(Of String)
36+
Friend Property DownloadModelMedia As Boolean = True
37+
Friend Property DownloadModelProfile As Boolean = False
38+
Private Property ForceParseProfileInfo As Boolean = False
3139
#End Region
3240
#Region "Loader"
41+
Private Sub UpdateUserOptions()
42+
If ID.IsEmptyString AndAlso Not Name.IsEmptyString AndAlso Name.StartsWith("did@") Then
43+
NameTrue = Name.Replace("@", ":")
44+
ID = NameTrue
45+
ForceParseProfileInfo = True
46+
End If
47+
End Sub
3348
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
49+
With Container
50+
If Loading Then
51+
If .Contains(Name_DownloadModelMedia) Then
52+
DownloadModelMedia = .Value(Name_DownloadModelMedia).FromXML(Of Boolean)(True)
53+
DownloadModelProfile = .Value(Name_DownloadModelProfile).FromXML(Of Boolean)(False)
54+
Else
55+
DownloadModelMedia = ParseUserMediaOnly
56+
DownloadModelProfile = Not ParseUserMediaOnly
57+
End If
58+
ForceParseProfileInfo = .Value(Name_ForceParseProfileInfo).FromXML(Of Boolean)(False)
59+
Else
60+
If ID.IsEmptyString Then
61+
UpdateUserOptions()
62+
.Value(Name_UserID) = ID
63+
End If
64+
.Add(Name_DownloadModelMedia, DownloadModelMedia.BoolToInteger)
65+
.Add(Name_DownloadModelProfile, DownloadModelProfile.BoolToInteger)
66+
.Add(Name_ForceParseProfileInfo, ForceParseProfileInfo.BoolToInteger)
67+
End If
68+
End With
3469
End Sub
3570
Friend Overrides Function ExchangeOptionsGet() As Object
36-
Return New EditorExchangeOptionsBase(Me) With {.SiteKey = BlueskySiteKey}
71+
Return New EditorExchangeOptions(Me)
3772
End Function
3873
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
39-
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptionsBase AndAlso
40-
DirectCast(Obj, EditorExchangeOptionsBase).SiteKey = BlueskySiteKey Then DirectCast(Obj, EditorExchangeOptionsBase).ApplyBase(Me)
74+
If Not Obj Is Nothing AndAlso TypeOf Obj Is EditorExchangeOptions Then DirectCast(Obj, EditorExchangeOptions).Apply(Me)
4175
End Sub
4276
#End Region
4377
#Region "Initializer"
@@ -66,6 +100,13 @@ Namespace API.Bluesky
66100
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
67101
_TmpPosts2.Clear()
68102
Try
103+
If Not DownloadModelMedia And Not DownloadModelProfile Then
104+
DownloadModelMedia = True
105+
ElseIf DownloadModelMedia Then
106+
DownloadModelProfile = False
107+
Else
108+
DownloadModelMedia = False
109+
End If
69110
If Not CBool(MySettings.CookiesEnabled.Value) Then Responser.Cookies.Clear()
70111
UpdateToken(, True)
71112
_TokenUpdateCount = 0
@@ -79,7 +120,7 @@ Namespace API.Bluesky
79120
Private Overloads Sub DownloadData(ByVal Cursor As String, ByVal Token As CancellationToken)
80121
Dim URL$ = String.Empty
81122
Try
82-
If Not IsSavedPosts And ID.IsEmptyString Then GetProfileInfo(Token)
123+
If (Not IsSavedPosts And ID.IsEmptyString) Or ForceParseProfileInfo Then GetProfileInfo(Token)
83124
If Not IsSavedPosts And ID.IsEmptyString Then Throw New ArgumentNullException("ID", "ID is null")
84125
If UpdateToken() Then
85126
Dim nextCursor$ = String.Empty
@@ -91,7 +132,9 @@ Namespace API.Bluesky
91132
n = {"bookmarks"}
92133
p = {"item"}
93134
Else
94-
URL = $"https://bsky.social/xrpc/app.bsky.feed.getAuthorFeed?actor={ID_Encoded}&filter=posts_and_author_threads&includePins=false&limit=99"
135+
'posts_and_author_threads
136+
'posts_with_media
137+
URL = $"https://bsky.social/xrpc/app.bsky.feed.getAuthorFeed?actor={ID_Encoded}&filter={IIf(DownloadModelMedia, "posts_with_media", "posts_and_author_threads")}&includePins=false&limit=99"
95138
If Not Cursor.IsEmptyString Then URL &= $"&cursor={SymbolsConverter.ASCII.EncodeSymbolsOnly(Cursor)}"
96139
n = {"feed"}
97140
p = {"post"}
@@ -106,7 +149,7 @@ Namespace API.Bluesky
106149
If .ListExists Then
107150
For Each post As EContainer In .Self
108151
With post(p)
109-
c = DefaultParser(.Self,, nextCursor)
152+
c = DefaultParser(.Self) ',, nextCursor)
110153
Select Case c
111154
Case CInt(DateResult.Skip) * -1 : Continue For
112155
Case CInt(DateResult.Exit) * -1 : Exit Sub
@@ -238,6 +281,7 @@ Namespace API.Bluesky
238281
#Region "GetProfileInfo"
239282
Private Sub GetProfileInfo(ByVal Token As CancellationToken)
240283
Try
284+
If ForceParseProfileInfo Then ForceParseProfileInfo = False : _ForceSaveUserInfo = True
241285
If UpdateToken() Then
242286
Dim r$ = Responser.GetResponse($"https://bsky.social/xrpc/app.bsky.actor.getProfile?actor={ID.IfNullOrEmpty(NameTrue)}")
243287
TokenUpdateCountReset()
@@ -344,6 +388,9 @@ Namespace API.Bluesky
344388
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, ByVal Token As CancellationToken) As SFile
345389
Return M3U8.Download(URL, DestinationFile, Token, Progress, Not IsSingleObjectDownload)
346390
End Function
391+
Protected Overrides Function DownloadContentDefault_ConvertWebp(ByVal m As UserMedia, ByVal Process As Boolean) As SFile
392+
Return DownloadContentDefault_ConvertWebp_TestImg(m, Process)
393+
End Function
347394
#End Region
348395
#Region "DownloadSingleObject"
349396
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)

0 commit comments

Comments
 (0)