Compare commits

...

7 Commits

Author SHA1 Message Date
Andy
3a0837a1b0 2023.12.7.0 2023-12-07 11:03:34 +03:00
Andy
0657f3d195 2023.12.6.1
Update updater
YT: add new version check at start
2023-12-06 18:28:36 +03:00
Andy
c92314d8e8 2023.12.6.0
Add updater
2023-12-06 10:55:50 +03:00
Andy
d99243ce46 2023.12.5.0
YT
VideoListForm: add a check of adding a URL if it has already been downloaded ('ValidateContainerURL')
YouTubeMediaContainerBase: add 'GetUrls' and 'GetFiles' functions; make 'Files' protected friend; update 'CreateUrlFile' function

SCrawler
Add downloaded saved posts to the feed
API.ProfileSaved: add token verification for multi-acc
API.SiteSettingsBase: update 'UpdateResponserFile' and 'CLONE_PROPERTIES.filterUC' functions
API.UserDataBase: add a null host check before request a new key; update 'OpenFolder' function (for saved posts)
API.YouTube: add the ability to download YouTube user community feeds
DownloadProgress: add 'KeyClickEventArgs' to download saved posts excluding from feed; add 'FeedFilesChanged' event; update 'Start' function
DownloadSavedPostsForm: add 'FeedFilesChanged' event and handler; update 'Start' function
Feed.FeedMedia: make the class compatible to work with saved posts
StandaloneDownloader.VideoDownloaderForm: add a check of adding a URL if it has already been downloaded ('ValidateContainerURL')
TDownloader: add the 'IsSavedPosts' field to the 'UserMediaD' structure; update 'UserMediaD.New(EContainer)' function (for saved posts); update 'UserMediaD.ToEContainer' function; add 'SessionSavedPosts' property
MainFrame: add 'Alt+A' hotkey to show scheduler; add 'Alt+P' hotkey to show progress form
Hosts.DownloadableMediaHost: add URL file to files list
2023-12-05 12:05:20 +03:00
Andy
3dae40b696 Update Plugins.md 2023-12-04 07:30:21 +03:00
Andy
ee0c773c37 Update README.md 2023-11-26 03:18:04 +03:00
Andy
ebe5f0ca01 2023.11.25.0
SettingsHost, SiteSettingsBase: change comparer to 'MembersDistinctComparerExtended'
API.RedGifs: fix initial UserAgent value
AutoDownloader: add 'IsManual' to 'Copy' function
2023-11-25 03:00:14 +03:00
61 changed files with 1986 additions and 72 deletions

View File

@@ -1,3 +1,26 @@
# 2023.12.7.0
*2023-12-07*
- Added
- Saved posts: add downloaded saved posts to the feed
- **YouTube (SCrawler): the ability to download YouTube user community feeds**
- Main window: add `Alt+A` hotkey to show scheduler
- Main window: add `Alt+P` hotkey to show progress form
- YouTube: check of adding a URL if it has already been downloaded
- YouTube: ability to check for a new version at start
- **Updater**
- Fixed
- Standalone downloader: URL files are not deleted along with the file
- Minor bugs
# 2023.11.25.0
*2023-11-25*
- Fixed
- Reddit: missing refresh token button in the settings form
# 2023.11.24.0 # 2023.11.24.0
*2023-11-24* *2023-11-24*

6
FAQ.md
View File

@@ -113,4 +113,8 @@ A: I can only [suggest](#q-you-lost-me-your-program-is-too-complicated) you find
#### Q: **Can you add a step-by-step guide or video on how to use the program?** #### Q: **Can you add a step-by-step guide or video on how to use the program?**
A: **NO! NEVER!** The guide fully covers all the functionality of SCrawler! If you don't respect my work, I don't waste my time. If you want, you can create a video tutorial and send it to me. Then I add it. All options and what each option does described on the wiki. The wiki also contains a description of all settings and how-to configure them. For complex settings, there is a steep-by-steep guide. Read the [main](README.md) information and [GUIDE](https://github.com/AAndyProgram/SCrawler/wiki/) and you won't have any problems. I have developed a program with an intuitive interface. There is a Settings button, download buttons, a context menu that drops down when a user is clicked, and other controls. Anyone can use it. A: **NO! NEVER!** The guide fully covers all the functionality of SCrawler! If you don't respect my work, I don't waste my time. If you want, you can create a video tutorial and send it to me. Then I add it. All options and what each option does described on the wiki. The wiki also contains a description of all settings and how-to configure them. For complex settings, there is a steep-by-steep guide. Read the [main](README.md) information and [GUIDE](https://github.com/AAndyProgram/SCrawler/wiki/) and you won't have any problems. I have developed a program with an intuitive interface. There is a Settings button, download buttons, a context menu that drops down when a user is clicked, and other controls. Anyone can use it.
**The following video shows how to add credentials:**
[![How to configure](https://img.youtube.com/vi/XDn7zG4I700/0.jpg)](https://www.youtube.com/watch?v=XDn7zG4I700)

View File

@@ -1,3 +1,7 @@
You can create a plugin for any site you want. **To create a plugin, read [this guide](https://github.com/AAndyProgram/SCrawler/wiki/Plugins).**
If you've created a plugin, you can create a [new issue](https://github.com/AAndyProgram/SCrawler/issues/new?assignees=&labels=New+Plugin&projects=&template=plugin_add.md&title=%5BNEW+PLUGIN%5D) and I'll add your plugin to the list below.
List of available plugins: List of available plugins:
Tools: Tools:

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 20 KiB

View File

@@ -30,7 +30,7 @@ A program to download photo and video from [any site](#supported-sites) (e.g. Yo
# What can program do: # What can program do:
- Download pictures and videos from users' profiles and subreddits: - Download pictures and videos from users' profiles and subreddits:
- YouTube videos, shorts, users, artists, playlists, music, tracks; - YouTube videos, shorts, community feeds, users, artists, playlists, music, tracks;
- Reddit images, galleries of images, videos, saved posts; - Reddit images, galleries of images, videos, saved posts;
- Redgifs videos (https://www.redgifs.com/); - Redgifs videos (https://www.redgifs.com/);
- Twitter images and videos, saved (bookmarked) posts; - Twitter images and videos, saved (bookmarked) posts;
@@ -145,6 +145,10 @@ First, the program downloads the full profile. After the program downloads only
**Full guide you can find [here](https://github.com/AAndyProgram/SCrawler/wiki)** **Full guide you can find [here](https://github.com/AAndyProgram/SCrawler/wiki)**
**Video on how to configure**
[![How to configure](https://img.youtube.com/vi/XDn7zG4I700/0.jpg)](https://www.youtube.com/watch?v=XDn7zG4I700)
# Installation # Installation
**Just download the [latest release](https://github.com/AAndyProgram/SCrawler/releases/latest), unzip the program archive to any folder and enjoy.** :blush: **Just download the [latest release](https://github.com/AAndyProgram/SCrawler/releases/latest), unzip the program archive to any folder and enjoy.** :blush:
@@ -167,6 +171,8 @@ The program has an intuitive interface.
**[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)** **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**
[![How to configure](https://img.youtube.com/vi/XDn7zG4I700/0.jpg)](https://www.youtube.com/watch?v=XDn7zG4I700)
Just add a user profile and **click the ```Download``` button**. Just add a user profile and **click the ```Download``` button**.
```mermaid ```mermaid

View File

@@ -0,0 +1,3 @@
[*.vb]
# Modifier preferences
file_header_template = Copyright (C) Andy https://github.com/AAndyProgram\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see <https://www.gnu.org/licenses/>

View File

@@ -0,0 +1,33 @@
' Copyright (C) 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 [Shared]
Public Module Functions
Public Const NewReleaseFolderName As String = "__NewRelease"
Public Function GetCurrentMaxVer(Optional ByVal Path As SFile = Nothing) As Version
Try
If Path.IsEmptyString Then Path = Application.StartupPath.CSFileP
If Path.Exists(SFO.Path, False) Then
Dim versions As New List(Of Version)
Dim v As FileVersionInfo
With SFile.GetFiles(Path, "*.exe",, EDP.ReturnValue).ListIfNothing.Where(Function(f) f.Name = "SCrawler" Or f.Name = "YouTubeDownloader")
If .ListExists Then
For Each f As SFile In .Self
v = FileVersionInfo.GetVersionInfo(f)
versions.Add(New Version(v.ProductVersion))
Next
End If
End With
If versions.Count > 0 Then Return versions.LastOrDefault
End If
Catch
End Try
Return Nothing
End Function
End Module
End Namespace

View File

@@ -0,0 +1,13 @@
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MySubMain>false</MySubMain>
<SingleInstance>false</SingleInstance>
<ShutdownMode>0</ShutdownMode>
<EnableVisualStyles>true</EnableVisualStyles>
<AuthenticationMode>0</AuthenticationMode>
<ApplicationType>1</ApplicationType>
<SaveMySettingsOnExit>true</SaveMySettingsOnExit>
</MyApplicationData>

View File

@@ -0,0 +1,37 @@
Imports System.Resources
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
' General Information about an assembly is controlled through the following
' set of attributes. Change these attribute values to modify the information
' associated with an assembly.
' Review the values of the assembly attributes
<Assembly: AssemblyTitle("SCrawler.Shared")>
<Assembly: AssemblyDescription("SCrawler shared functions")>
<Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler.Shared")>
<Assembly: AssemblyCopyright("Copyright © 2024")>
<Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)>
'The following GUID is for the ID of the typelib if this project is exposed to COM
<Assembly: Guid("75a22c7c-6f90-49cb-852b-078ea0b8f2b6")>
' Version information for an assembly consists of the following four values:
'
' Major Version
' Minor Version
' Build Number
' Revision
'
' You can specify all the values or you can default the Build and Revision Numbers
' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2023.12.7.0")>
<Assembly: AssemblyFileVersion("2023.12.7.0")>
<Assembly: NeutralResourcesLanguage("en")>

View File

@@ -0,0 +1,63 @@
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On
Imports System
Namespace My.Resources
'This class was auto-generated by the StronglyTypedResourceBuilder
'class via a tool like ResGen or Visual Studio.
'To add or remove a member, edit your .ResX file then rerun ResGen
'with the /str option, or rebuild your VS project.
'''<summary>
''' A strongly-typed resource class, for looking up localized strings, etc.
'''</summary>
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0"), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _
Friend Module Resources
Private resourceMan As Global.System.Resources.ResourceManager
Private resourceCulture As Global.System.Globalization.CultureInfo
'''<summary>
''' Returns the cached ResourceManager instance used by this class.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
Get
If Object.ReferenceEquals(resourceMan, Nothing) Then
Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("SCrawler.Resources", GetType(Resources).Assembly)
resourceMan = temp
End If
Return resourceMan
End Get
End Property
'''<summary>
''' Overrides the current thread's CurrentUICulture property for all
''' resource lookups using this strongly typed resource class.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend Property Culture() As Global.System.Globalization.CultureInfo
Get
Return resourceCulture
End Get
Set
resourceCulture = value
End Set
End Property
End Module
End Namespace

View File

@@ -0,0 +1,117 @@
<?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.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: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" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
</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" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
</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=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
</root>

View File

@@ -0,0 +1,73 @@
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On
Namespace My
<Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0"), _
Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Partial Friend NotInheritable Class MySettings
Inherits Global.System.Configuration.ApplicationSettingsBase
Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings)
#Region "My.Settings Auto-Save Functionality"
#If _MyType = "WindowsForms" Then
Private Shared addedHandler As Boolean
Private Shared addedHandlerLockObject As New Object
<Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs)
If My.Application.SaveMySettingsOnExit Then
My.Settings.Save()
End If
End Sub
#End If
#End Region
Public Shared ReadOnly Property [Default]() As MySettings
Get
#If _MyType = "WindowsForms" Then
If Not addedHandler Then
SyncLock addedHandlerLockObject
If Not addedHandler Then
AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
addedHandler = True
End If
End SyncLock
End If
#End If
Return defaultInstance
End Get
End Property
End Class
End Namespace
Namespace My
<Global.Microsoft.VisualBasic.HideModuleNameAttribute(), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute()> _
Friend Module MySettingsProperty
<Global.System.ComponentModel.Design.HelpKeywordAttribute("My.Settings")> _
Friend ReadOnly Property Settings() As Global.SCrawler.My.MySettings
Get
Return Global.SCrawler.My.MySettings.Default
End Get
End Property
End Module
End Namespace

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" UseMySettingsClassName="true">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{DC634700-24C7-42DD-BF8F-87E6CC54E625}</ProjectGuid>
<OutputType>Library</OutputType>
<RootNamespace>SCrawler</RootNamespace>
<AssemblyName>SCrawler.Shared</AssemblyName>
<FileAlignment>512</FileAlignment>
<MyType>Windows</MyType>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\</OutputPath>
<DocumentationFile>
</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DocumentationFile>
</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
</PropertyGroup>
<PropertyGroup>
<OptionExplicit>On</OptionExplicit>
</PropertyGroup>
<PropertyGroup>
<OptionCompare>Binary</OptionCompare>
</PropertyGroup>
<PropertyGroup>
<OptionStrict>Off</OptionStrict>
</PropertyGroup>
<PropertyGroup>
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\x64\Debug\</OutputPath>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<DefineTrace>true</DefineTrace>
<OutputPath>bin\x64\Release\</OutputPath>
<Optimize>true</Optimize>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\x86\Debug\</OutputPath>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<DefineTrace>true</DefineTrace>
<OutputPath>bin\x86\Release\</OutputPath>
<Optimize>true</Optimize>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Windows.Forms" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="PersonalUtilities.Functions" />
<Import Include="System" />
<Import Include="System.Collections" />
<Import Include="System.Collections.Generic" />
<Import Include="System.Data" />
<Import Include="System.Diagnostics" />
<Import Include="System.Linq" />
<Import Include="System.Windows.Forms" />
<Import Include="System.Xml.Linq" />
<Import Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Compile Include="Functions.vb" />
<Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Application.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Application.myapp</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="My Project\Resources.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="My Project\Settings.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="My Project\Resources.resx">
<Generator>VbMyResourcesResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.vb</LastGenOutput>
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include=".editorconfig" />
<None Include="My Project\Application.myapp">
<Generator>MyApplicationCodeGenerator</Generator>
<LastGenOutput>Application.Designer.vb</LastGenOutput>
</None>
<None Include="My Project\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<CustomToolNamespace>My</CustomToolNamespace>
<LastGenOutput>Settings.Designer.vb</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\MyUtilities\PersonalUtilities\PersonalUtilities.vbproj">
<Project>{8405896b-2685-4916-bc93-1fb514c323a9}</Project>
<Name>PersonalUtilities</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project>

View File

@@ -0,0 +1,3 @@
[*.vb]
# Modifier preferences
file_header_template = Copyright (C) Andy https://github.com/AAndyProgram\nThis program is free software: you can redistribute it and/or modify\nit under the terms of the GNU General Public License as published by\nthe Free Software Foundation, either version 3 of the License, or\n(at your option) any later version.\n\nThis program is distributed in the hope that it will be useful,\nbut WITHOUT ANY WARRANTY; without even the implied warranty of\nMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\nGNU General Public License for more details.\n\nYou should have received a copy of the GNU General Public License\nalong with this program. If not, see <https://www.gnu.org/licenses/>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<startup>
<supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.6.1" />
</startup>
</configuration>

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

229
SCrawler.Updater/MainMod.vb Normal file
View File

@@ -0,0 +1,229 @@
' Copyright (C) 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.Net
Imports System.IO.Compression
Imports PersonalUtilities.Functions
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports SCrawler.Shared
Public Module MainMod
Private MyProcessID As Integer = -1
Private MyWorkingPath As SFile = Nothing
Private ReadOnly ProcessNames As String() = {"SCrawler", "YouTubeDownloader", "Updater"}
Private Silent As Boolean = False
Private Function GetConsoleResponse(ByVal Request As String) As String
Console.Write(Request)
Return Console.ReadLine
End Function
Private Function DownloadFile(ByVal URL As String, ByVal Destination As SFile) As Boolean
Try
Dim lastPerc% = -1
Dim currentCursor% = Console.CursorTop
Using w As New RWebClient With {.AsyncMode = True}
AddHandler w.DownloadProgressChanged,
New DownloadProgressChangedEventHandler(Sub(ByVal Sender As Object, ByVal e As DownloadProgressChangedEventArgs)
If lastPerc < e.ProgressPercentage Then
lastPerc = e.ProgressPercentage
Console.SetCursorPosition(0, currentCursor)
Console.Write("{0}% completed", e.ProgressPercentage)
End If
End Sub)
Return w.DownloadFile(URL, Destination, EDP.ReturnValue)
End Using
Catch ex As Exception
Return False
End Try
End Function
Public Sub Main()
Try
MyWorkingPath = AppDomain.CurrentDomain.BaseDirectory.CSFileP
MyProcessID = Process.GetCurrentProcess.Id
Console.Title = "SCrawler updater"
With Environment.GetCommandLineArgs
If .ListExists(2) Then Silent = .Self()(1).FromXML(Of Boolean)(False)
End With
Dim currentDir As SFile = MyWorkingPath.CutPath
Dim extractionDir As SFile = $"{currentDir.CSFilePS}{NewReleaseFolderName}\"
If extractionDir.Exists(SFO.Path, False) Then extractionDir.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.None)
Dim currVer As Version = GetCurrentMaxVer(currentDir)
If currVer Is Nothing Then
Console.WriteLine("The current version of the program cannot be determined")
Else
Console.WriteLine($"The current version is {currVer} (x{IIf(Environment.Is64BitProcess, 64, 86)})")
Dim release As GitRelease = GetGitRelease()
If Not release.URL.IsEmptyString And Not release.Version Is Nothing Then
If release.Version > currVer Then
Console.WriteLine($"The new version is {release.Version} ({release.Name})")
If Not Silent AndAlso GetConsoleResponse("Do you want to update the program? (y/n): ").IfNullOrEmpty("n") = "n" Then Exit Sub
If ActiveProcessesExist() Then
Console.WriteLine("One of the SCrawler programs is still running. Waiting for all SCrawler programs to close.")
While ActiveProcessesExist() : Threading.Thread.Sleep(100) : End While
Console.WriteLine("All SCrawler programs are closed.")
End If
If extractionDir.Exists(SFO.Path, True) Then
Dim destFile As SFile = $"{extractionDir.CSFilePS}{New SFile(release.URL).File}"
Console.WriteLine("Downloading new version...")
If DownloadFile(release.URL, destFile) Then
Console.WriteLine("")
Console.WriteLine("New version downloaded!")
Console.WriteLine("Extracting files...")
ZipFile.ExtractToDirectory(destFile, extractionDir)
Console.WriteLine("Files extracted!")
destFile.Delete(SFO.File, SFODelete.DeletePermanently, EDP.None)
If Not MoveFiles(extractionDir, currentDir) Then GetConsoleResponse("Unable to update the program. Press Enter to exit") : Exit Sub
Else
extractionDir.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.None)
Console.WriteLine("Unable to download new version")
End If
Else
Console.WriteLine("Unable to create temp directory")
End If
Else
Console.WriteLine("The program is up to date")
End If
Else
Console.WriteLine("Unable to get information about new version")
End If
End If
Catch ex As Exception
Console.WriteLine("An error occurred during update")
Console.WriteLine(ex.Message)
Finally
GetConsoleResponse("Press Enter to exit")
End Try
End Sub
Private Function MoveFiles(ByVal Source As SFile, ByVal Destination As SFile) As Boolean
Console.WriteLine("Updating files")
Try
Dim oldFiles As List(Of SFile) = SFile.GetFiles(Destination,,, EDP.ReturnValue)
Dim oldFolders As List(Of SFile) = SFile.GetDirectories(Destination,,, EDP.ReturnValue)
Dim newFiles As List(Of SFile) = SFile.GetFiles(Source,,, EDP.ReturnValue)
Dim newFolders As List(Of SFile) = SFile.GetDirectories(Source,,, EDP.ReturnValue)
Dim obj As SFile = Nothing
Dim wSegment As String = MyWorkingPath.Segments.Last
Dim filesPredicate As Predicate(Of SFile) = Function(ByVal f As SFile) As Boolean
If obj = f Or obj.Name = f.Name Then
f.Delete(SFO.File, SFODelete.DeleteToRecycleBin, EDP.None)
Return True
Else
Return False
End If
End Function
Dim foldersPredicate As Predicate(Of SFile) = Function(ByVal f As SFile) As Boolean
Dim ls$ = f.Segments.Last
If ls = obj.Segments.Last And Not ls = NewReleaseFolderName And Not ls = wSegment Then
f.Delete(SFO.Path, SFODelete.DeleteToRecycleBin, EDP.None)
Return True
Else
Return False
End If
End Function
Dim getDestFile As Func(Of SFile, Boolean, SFile) = Function(ByVal f As SFile, ByVal isFolder As Boolean) As SFile
Dim ff As SFile = f
If isFolder Then
ff = $"{Destination.PathWithSeparator}{f.Segments.Last}\"
Else
ff.Path = Destination.Path
End If
Console.WriteLine(ff)
Return ff
End Function
If newFiles.ListExists Then
If oldFiles.ListExists Then
For Each obj In newFiles : oldFiles.RemoveAll(filesPredicate) : Next
End If
newFiles.ForEach(Sub(ff) SFile.Move(ff, getDestFile(ff, False), SFO.File, True, SFODelete.DeleteToRecycleBin, EDP.None))
End If
If newFolders.ListExists Then
If oldFolders.ListExists Then
For Each obj In newFolders : oldFolders.RemoveAll(foldersPredicate) : Next
End If
newFolders.ForEach(Sub(ff) If Not ff.Segments.Last = wSegment Then _
SFile.Move(ff, getDestFile(ff, True), SFO.Path, True, SFODelete.DeleteToRecycleBin, EDP.None))
End If
Console.WriteLine("Files updated")
Return True
Catch
Return False
End Try
End Function
Private Function ActiveProcessesExist() As Boolean
Try
Return Process.GetProcesses.Any(Function(p) ProcessNames.Contains(p.ProcessName) And Not p.Id = MyProcessID)
Catch
Return True
End Try
End Function
Private Structure GitRelease
Friend URL As String
Friend Name As String
Friend Version As Version
End Structure
Private Function GetGitRelease() As GitRelease
Try
Dim nameEnd$ = $"_x{IIf(Environment.Is64BitProcess, 64, 86)}.zip"
Dim name$, relName$, relTag$
Using resp As New Responser With {.Accept = "application/vnd.github.v3+json"}
Dim r$ = resp.GetResponse("https://api.github.com/repos/AAndyProgram/SCrawler/releases",, EDP.ReturnValue)
If Not r.IsEmptyString Then
Dim getver As Func(Of String, Version) = Function(ByVal input As String) As Version
Try
If Not input.IsEmptyString Then
If input.ToLower.StartsWith("scrawler") Then
Return New Version(input.Split("_")(1))
Else
Return New Version(input)
End If
End If
Catch
End Try
Return Nothing
End Function
Using j As EContainer = JsonDocument.Parse(r, EDP.ReturnValue)
If j.ListExists Then
With j.FirstOrDefault(Function(e) Not e.Value("draft").FromXML(Of Boolean) And Not e.Value("prerelease").FromXML(Of Boolean))
If .ListExists Then
relName = .Value("name")
relTag = .Value("tag_name")
With .Item("assets")
If .ListExists Then
For Each asset As EContainer In .Self
name = asset.Value("name")
If Not name.IsEmptyString AndAlso name.EndsWith(nameEnd) Then _
Return New GitRelease With {
.Name = name,
.URL = asset.Value("browser_download_url"),
.Version = getver(name).IfNullOrEmpty(getver(relName).IfNullOrEmpty(getver(relTag)))}
Next
End If
End With
End If
End With
End If
End Using
End If
End Using
Catch
End Try
Return Nothing
End Function
End Module

View File

@@ -0,0 +1,13 @@
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<MyApplicationData xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<MySubMain>false</MySubMain>
<SingleInstance>false</SingleInstance>
<ShutdownMode>0</ShutdownMode>
<EnableVisualStyles>true</EnableVisualStyles>
<AuthenticationMode>0</AuthenticationMode>
<ApplicationType>2</ApplicationType>
<SaveMySettingsOnExit>true</SaveMySettingsOnExit>
</MyApplicationData>

View File

@@ -0,0 +1,37 @@
Imports System.Resources
Imports System
Imports System.Reflection
Imports System.Runtime.InteropServices
' General Information about an assembly is controlled through the following
' set of attributes. Change these attribute values to modify the information
' associated with an assembly.
' Review the values of the assembly attributes
<Assembly: AssemblyTitle("SCrawler updater")>
<Assembly: AssemblyDescription("SCrawler updater")>
<Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler.Updater")>
<Assembly: AssemblyCopyright("Copyright © 2024")>
<Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)>
'The following GUID is for the ID of the typelib if this project is exposed to COM
<Assembly: Guid("df008b29-ad5e-4271-acfe-650396d15c40")>
' Version information for an assembly consists of the following four values:
'
' Major Version
' Minor Version
' Build Number
' Revision
'
' You can specify all the values or you can default the Build and Revision Numbers
' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2023.12.7.0")>
<Assembly: AssemblyFileVersion("2023.12.7.0")>
<Assembly: NeutralResourcesLanguage("en")>

View File

@@ -0,0 +1,73 @@
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On
Imports System
Namespace My.Resources
'This class was auto-generated by the StronglyTypedResourceBuilder
'class via a tool like ResGen or Visual Studio.
'To add or remove a member, edit your .ResX file then rerun ResGen
'with the /str option, or rebuild your VS project.
'''<summary>
''' A strongly-typed resource class, for looking up localized strings, etc.
'''</summary>
<Global.System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0"), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.Microsoft.VisualBasic.HideModuleNameAttribute()> _
Friend Module Resources
Private resourceMan As Global.System.Resources.ResourceManager
Private resourceCulture As Global.System.Globalization.CultureInfo
'''<summary>
''' Returns the cached ResourceManager instance used by this class.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager
Get
If Object.ReferenceEquals(resourceMan, Nothing) Then
Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("SCrawler.Resources", GetType(Resources).Assembly)
resourceMan = temp
End If
Return resourceMan
End Get
End Property
'''<summary>
''' Overrides the current thread's CurrentUICulture property for all
''' resource lookups using this strongly typed resource class.
'''</summary>
<Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Friend Property Culture() As Global.System.Globalization.CultureInfo
Get
Return resourceCulture
End Get
Set
resourceCulture = value
End Set
End Property
'''<summary>
''' Looks up a localized resource of type System.Drawing.Icon similar to (Icon).
'''</summary>
Friend ReadOnly Property RainbowIcon_48() As System.Drawing.Icon
Get
Dim obj As Object = ResourceManager.GetObject("RainbowIcon_48", resourceCulture)
Return CType(obj,System.Drawing.Icon)
End Get
End Property
End Module
End Namespace

View File

@@ -0,0 +1,124 @@
<?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>
<assembly alias="System.Windows.Forms" name="System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
<data name="RainbowIcon_48" type="System.Resources.ResXFileRef, System.Windows.Forms">
<value>..\Content\Icons\RainbowIcon_48.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a</value>
</data>
</root>

View File

@@ -0,0 +1,73 @@
'------------------------------------------------------------------------------
' <auto-generated>
' This code was generated by a tool.
' Runtime Version:4.0.30319.42000
'
' Changes to this file may cause incorrect behavior and will be lost if
' the code is regenerated.
' </auto-generated>
'------------------------------------------------------------------------------
Option Strict On
Option Explicit On
Namespace My
<Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute(), _
Global.System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.10.0.0"), _
Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Partial Friend NotInheritable Class MySettings
Inherits Global.System.Configuration.ApplicationSettingsBase
Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings)
#Region "My.Settings Auto-Save Functionality"
#If _MyType = "WindowsForms" Then
Private Shared addedHandler As Boolean
Private Shared addedHandlerLockObject As New Object
<Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), Global.System.ComponentModel.EditorBrowsableAttribute(Global.System.ComponentModel.EditorBrowsableState.Advanced)> _
Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs)
If My.Application.SaveMySettingsOnExit Then
My.Settings.Save()
End If
End Sub
#End If
#End Region
Public Shared ReadOnly Property [Default]() As MySettings
Get
#If _MyType = "WindowsForms" Then
If Not addedHandler Then
SyncLock addedHandlerLockObject
If Not addedHandler Then
AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings
addedHandler = True
End If
End SyncLock
End If
#End If
Return defaultInstance
End Get
End Property
End Class
End Namespace
Namespace My
<Global.Microsoft.VisualBasic.HideModuleNameAttribute(), _
Global.System.Diagnostics.DebuggerNonUserCodeAttribute(), _
Global.System.Runtime.CompilerServices.CompilerGeneratedAttribute()> _
Friend Module MySettingsProperty
<Global.System.ComponentModel.Design.HelpKeywordAttribute("My.Settings")> _
Friend ReadOnly Property Settings() As Global.SCrawler.My.MySettings
Get
Return Global.SCrawler.My.MySettings.Default
End Get
End Property
End Module
End Namespace

View File

@@ -0,0 +1,7 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" UseMySettingsClassName="true">
<Profiles>
<Profile Name="(Default)" />
</Profiles>
<Settings />
</SettingsFile>

View File

@@ -0,0 +1,79 @@
<?xml version="1.0" encoding="utf-8"?>
<assembly manifestVersion="1.0" xmlns="urn:schemas-microsoft-com:asm.v1">
<assemblyIdentity version="1.0.0.0" name="MyApplication.app"/>
<trustInfo xmlns="urn:schemas-microsoft-com:asm.v2">
<security>
<requestedPrivileges xmlns="urn:schemas-microsoft-com:asm.v3">
<!-- UAC Manifest Options
If you want to change the Windows User Account Control level replace the
requestedExecutionLevel node with one of the following.
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
<requestedExecutionLevel level="requireAdministrator" uiAccess="false" />
<requestedExecutionLevel level="highestAvailable" uiAccess="false" />
Specifying requestedExecutionLevel element will disable file and registry virtualization.
Remove this element if your application requires this virtualization for backwards
compatibility.
-->
<requestedExecutionLevel level="asInvoker" uiAccess="false" />
</requestedPrivileges>
</security>
</trustInfo>
<compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
<application>
<!-- A list of the Windows versions that this application has been tested on
and is designed to work with. Uncomment the appropriate elements
and Windows will automatically select the most compatible environment. -->
<!-- Windows Vista -->
<!--<supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}" />-->
<!-- Windows 7 -->
<!--<supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}" />-->
<!-- Windows 8 -->
<!--<supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}" />-->
<!-- Windows 8.1 -->
<!--<supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}" />-->
<!-- Windows 10 -->
<!--<supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}" />-->
</application>
</compatibility>
<!-- Indicates that the application is DPI-aware and will not be automatically scaled by Windows at higher
DPIs. Windows Presentation Foundation (WPF) applications are automatically DPI-aware and do not need
to opt in. Windows Forms applications targeting .NET Framework 4.6 that opt into this setting, should
also set the 'EnableWindowsFormsHighDpiAutoResizing' setting to 'true' in their app.config.
Makes the application long-path aware. See https://docs.microsoft.com/windows/win32/fileio/maximum-file-path-limitation -->
<!--
<application xmlns="urn:schemas-microsoft-com:asm.v3">
<windowsSettings>
<dpiAware xmlns="http://schemas.microsoft.com/SMI/2005/WindowsSettings">true</dpiAware>
<longPathAware xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">true</longPathAware>
</windowsSettings>
</application>
-->
<!-- Enable themes for Windows common controls and dialogs (Windows XP and later) -->
<!--
<dependency>
<dependentAssembly>
<assemblyIdentity
type="win32"
name="Microsoft.Windows.Common-Controls"
version="6.0.0.0"
processorArchitecture="*"
publicKeyToken="6595b64144ccf1df"
language="*"
/>
</dependentAssembly>
</dependency>
-->
</assembly>

View File

@@ -0,0 +1,173 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}</ProjectGuid>
<OutputType>Exe</OutputType>
<StartupObject>Sub Main</StartupObject>
<RootNamespace>SCrawler</RootNamespace>
<AssemblyName>Updater</AssemblyName>
<FileAlignment>512</FileAlignment>
<MyType>Console</MyType>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<AutoGenerateBindingRedirects>true</AutoGenerateBindingRedirects>
<Deterministic>true</Deterministic>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\Debug\Updater\</OutputPath>
<DocumentationFile>
</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>pdbonly</DebugType>
<DefineDebug>false</DefineDebug>
<DefineTrace>true</DefineTrace>
<Optimize>true</Optimize>
<OutputPath>bin\Release\Updater\</OutputPath>
<DocumentationFile>
</DocumentationFile>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<Prefer32Bit>false</Prefer32Bit>
</PropertyGroup>
<PropertyGroup>
<OptionExplicit>On</OptionExplicit>
</PropertyGroup>
<PropertyGroup>
<OptionCompare>Binary</OptionCompare>
</PropertyGroup>
<PropertyGroup>
<OptionStrict>Off</OptionStrict>
</PropertyGroup>
<PropertyGroup>
<OptionInfer>On</OptionInfer>
</PropertyGroup>
<PropertyGroup>
<ApplicationIcon>Content\Icons\RainbowIcon_48.ico</ApplicationIcon>
</PropertyGroup>
<PropertyGroup>
<ApplicationManifest>My Project\app.manifest</ApplicationManifest>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x64'">
<DebugSymbols>true</DebugSymbols>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\x64\Debug\Updater\</OutputPath>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x64'">
<DefineTrace>true</DefineTrace>
<OutputPath>bin\x64\Release\Updater\</OutputPath>
<Optimize>true</Optimize>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x64</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Debug|x86'">
<DebugSymbols>true</DebugSymbols>
<DefineDebug>true</DefineDebug>
<DefineTrace>true</DefineTrace>
<OutputPath>bin\x86\Debug\Updater\</OutputPath>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DebugType>full</DebugType>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'Release|x86'">
<DefineTrace>true</DefineTrace>
<OutputPath>bin\x86\Release\Updater\</OutputPath>
<Optimize>true</Optimize>
<NoWarn>42016,41999,42017,42018,42019,42032,42036,42020,42021,42022</NoWarn>
<DebugType>pdbonly</DebugType>
<PlatformTarget>x86</PlatformTarget>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Data" />
<Reference Include="System.Deployment" />
<Reference Include="System.Drawing" />
<Reference Include="System.IO.Compression" />
<Reference Include="System.IO.Compression.FileSystem" />
<Reference Include="System.Xml" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Net.Http" />
</ItemGroup>
<ItemGroup>
<Import Include="Microsoft.VisualBasic" />
<Import Include="System" />
<Import Include="System.Collections" />
<Import Include="System.Collections.Generic" />
<Import Include="System.Data" />
<Import Include="System.Diagnostics" />
<Import Include="System.Linq" />
<Import Include="System.Xml.Linq" />
<Import Include="System.Threading.Tasks" />
</ItemGroup>
<ItemGroup>
<Compile Include="MainMod.vb" />
<Compile Include="My Project\AssemblyInfo.vb" />
<Compile Include="My Project\Application.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Application.myapp</DependentUpon>
<DesignTime>True</DesignTime>
</Compile>
<Compile Include="My Project\Resources.Designer.vb">
<AutoGen>True</AutoGen>
<DesignTime>True</DesignTime>
<DependentUpon>Resources.resx</DependentUpon>
</Compile>
<Compile Include="My Project\Settings.Designer.vb">
<AutoGen>True</AutoGen>
<DependentUpon>Settings.settings</DependentUpon>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
</Compile>
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="My Project\Resources.resx">
<Generator>VbMyResourcesResXFileCodeGenerator</Generator>
<LastGenOutput>Resources.Designer.vb</LastGenOutput>
<CustomToolNamespace>My.Resources</CustomToolNamespace>
<SubType>Designer</SubType>
</EmbeddedResource>
</ItemGroup>
<ItemGroup>
<None Include=".editorconfig" />
<None Include="My Project\app.manifest" />
<None Include="My Project\Application.myapp">
<Generator>MyApplicationCodeGenerator</Generator>
<LastGenOutput>Application.Designer.vb</LastGenOutput>
</None>
<None Include="My Project\Settings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<CustomToolNamespace>My</CustomToolNamespace>
<LastGenOutput>Settings.Designer.vb</LastGenOutput>
</None>
<None Include="App.config" />
</ItemGroup>
<ItemGroup>
<Content Include="Content\Icons\RainbowIcon_48.ico" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\MyUtilities\PersonalUtilities\PersonalUtilities.vbproj">
<Project>{8405896b-2685-4916-bc93-1fb514c323a9}</Project>
<Name>PersonalUtilities</Name>
</ProjectReference>
<ProjectReference Include="..\SCrawler.Shared\SCrawler.Shared.vbproj">
<Project>{dc634700-24c7-42dd-bf8f-87e6cc54e625}</Project>
<Name>SCrawler.Shared</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.VisualBasic.targets" />
</Project>

View File

@@ -132,6 +132,8 @@ Namespace API.YouTube.Base
OpenFolderInOtherProgram.Value = command OpenFolderInOtherProgram.Value = command
End Set End Set
End Property End Property
<Browsable(True), GridVisible(False), XMLVN({"Environment"}, True), Category("Environment"), DisplayName("Check new version at start")>
Friend ReadOnly Property CheckUpdatesAtStart As XMLValue(Of Boolean)
#End Region #End Region
#Region "Info" #Region "Info"
<Browsable(True), GridVisible, XMLVN({"Info"}), Category("Info"), DisplayName("Create URL files"), <Browsable(True), GridVisible, XMLVN({"Info"}), Category("Info"), DisplayName("Create URL files"),

View File

@@ -108,5 +108,16 @@ Namespace API.YouTube
Throw New NotImplementedException("'GetFormat' is not available in the 'DurationXmlConverter' context") Throw New NotImplementedException("'GetFormat' is not available in the 'DurationXmlConverter' context")
End Function End Function
End Class End Class
Friend Sub CheckVersion(ByVal Force As Boolean)
If Not MyYouTubeSettings Is Nothing Then
With MyYouTubeSettings
If .CheckUpdatesAtStart Or Force Then
ShowProgramInfo(.ProgramText.Value.IfNullOrEmpty("YouTube Downloader"),
SCrawler.Shared.GetCurrentMaxVer(Application.StartupPath.CSFileP).IfNullOrEmpty(My.Application.Info.Version),
True, Force, .Self, True,, False, .ProgramDescription)
End If
End With
End If
End Sub
End Module End Module
End Namespace End Namespace

View File

@@ -59,6 +59,8 @@ Namespace DownloadObjects.STDownloader
MyDownloaderSettings = MyYouTubeSettings MyDownloaderSettings = MyYouTubeSettings
End If End If
CheckVersion(False)
With MyView : .Import() : .SetFormSize() : End With With MyView : .Import() : .SetFormSize() : End With
BTT_DELETE.Enabled = False BTT_DELETE.Enabled = False
If Not AppMode Then If Not AppMode Then
@@ -326,7 +328,7 @@ Namespace DownloadObjects.STDownloader
If Not f Is Nothing Then If Not f Is Nothing Then
If TypeOf f Is IDesignXMLContainer Then DirectCast(f, IDesignXMLContainer).DesignXML = DesignXML If TypeOf f Is IDesignXMLContainer Then DirectCast(f, IDesignXMLContainer).DesignXML = DesignXML
f.ShowDialog() f.ShowDialog()
If f.DialogResult = DialogResult.OK Then ControlCreateAndAdd(c, disableDown) If f.DialogResult = DialogResult.OK AndAlso ValidateContainerURL(c) Then ControlCreateAndAdd(c, disableDown)
f.Dispose() f.Dispose()
End If End If
End If End If
@@ -340,6 +342,61 @@ Namespace DownloadObjects.STDownloader
If Not pForm Is Nothing Then pForm.Dispose() If Not pForm Is Nothing Then pForm.Dispose()
End Try End Try
End Sub End Sub
Protected Function ValidateContainerURL(ByVal c As IYouTubeMediaContainer) As Boolean
Try
If Not c Is Nothing AndAlso Not c.IsMusic Then
Dim urls As List(Of String) = Nothing
Dim files As List(Of SFile) = Nothing
Dim msg As New MMessage("The media file to be added is already downloaded. Do you want to download it again?", "Download media file", {"Process", "Cancel"}, vbExclamation)
If TP_CONTROLS.Controls.Count > 0 Then
With TP_CONTROLS.Controls.Cast(Of MediaItem)
urls.ListAddList(.SelectMany(Function(ByVal m As MediaItem) As IEnumerable(Of String)
If Not m.MyContainer Is Nothing Then
Return DirectCast(m.MyContainer, YouTubeMediaContainerBase).GetUrls()
Else
Return New String() {}
End If
End Function), LAP.NotContainsOnly, EDP.ReturnValue)
files.ListAddList(.SelectMany(Function(ByVal m As MediaItem) As IEnumerable(Of SFile)
If Not m.MyContainer Is Nothing Then
Return DirectCast(m.MyContainer, YouTubeMediaContainerBase).GetFiles()
Else
Return New SFile() {}
End If
End Function), LAP.NotContainsOnly, EDP.ReturnValue)
End With
End If
If urls.ListExists Then
Dim cUrls As New List(Of String)
cUrls.ListAddList({c.URL, c.URL_BASE}, LAP.NotContainsOnly)
If urls.ListContains(cUrls) Then Return msg.Show = 0
End If
If files.ListExists And Not c.File.IsEmptyString Then Return Not files.Contains(c.File) OrElse msg.Show = 0
If c.ObjectType = YouTubeMediaType.Single AndAlso c.File.Exists Then
Dim callBack As MsgBoxButtonCallBack = Sub(r, m, b)
Dim __sfo As SFO = IIf(r.Button.CallBackObject = 0, SFO.File, SFO.Path)
If __sfo = SFO.File Then
c.File.Open(__sfo)
Else
GlobalOpenPath(c.File)
End If
End Sub
Return MsgBoxE(New MMessage("The following file already exists at the destination." & vbCr &
"Do you want to download it again?" & vbCr & vbCr &
$"File: {c.File}", "Download media file",
{"Process",
New MsgBoxButton("Open file") With {.IsDialogResultButton = False, .CallBackObject = 0, .CallBack = callBack},
New MsgBoxButton("Open folder") With {.IsDialogResultButton = False, .CallBackObject = 1, .CallBack = callBack},
"Cancel"}, vbExclamation) With {.ButtonsPerRow = 4}) = 0
End If
urls.ListClearDispose
files.ListClearDispose
End If
Return True
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendToLog + EDP.ReturnValue, ex, "[VideoListForm.ValidateContainerURL]", True)
End Try
End Function
Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click Private Sub BTT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_DOWN.Click
With TP_CONTROLS With TP_CONTROLS
If .Controls.Count > 0 Then If .Controls.Count > 0 Then
@@ -387,8 +444,7 @@ Namespace DownloadObjects.STDownloader
Try : Process.Start("https://github.com/AAndyProgram/SCrawler/blob/main/HowToSupport.md") : Catch : End Try Try : Process.Start("https://github.com/AAndyProgram/SCrawler/blob/main/HowToSupport.md") : Catch : End Try
End Sub End Sub
Private Sub BTT_INFO_Click(sender As Object, e As EventArgs) Handles BTT_INFO.Click Private Sub BTT_INFO_Click(sender As Object, e As EventArgs) Handles BTT_INFO.Click
ShowProgramInfo(MyYouTubeSettings.ProgramText.Value.IfNullOrEmpty("YouTube Downloader"), CheckVersion(True)
My.Application.Info.Version, False, True, MyYouTubeSettings, True,, False, MyYouTubeSettings.ProgramDescription)
End Sub End Sub
Protected Overloads Sub RemoveControls(Optional ByVal Predicate As Predicate(Of MediaItem) = Nothing, Optional ByVal RemoveFiles As Boolean = False) Protected Overloads Sub RemoveControls(Optional ByVal Predicate As Predicate(Of MediaItem) = Nothing, Optional ByVal RemoveFiles As Boolean = False)
ControlInvokeFast(TP_CONTROLS, Sub() ControlInvokeFast(TP_CONTROLS, Sub()

View File

@@ -6,6 +6,7 @@
' '
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Threading
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.Web Imports PersonalUtilities.Tools.Web
Imports PersonalUtilities.Functions.Messaging Imports PersonalUtilities.Functions.Messaging
@@ -38,19 +39,54 @@ Public Module MainModShared
End If End If
End Try End Try
End Sub End Sub
Public Sub CheckNewReleaseFolder()
Try
Const updaterFolderName$ = "Updater\"
Dim f As SFile = SCrawler.Shared.NewReleaseFolderName.CSFileP
If f.Exists(SFO.Path, False) Then
Dim updater As SFile = updaterFolderName
Dim updaterNR As SFile = f.PathWithSeparator & updaterFolderName
If updaterNR.Exists(SFO.Path, False) Then
If updater.Exists(SFO.Path, False) Then updater.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.ReturnValue)
SFile.Move(updaterNR, updater, SFO.Path, True, SFODelete.DeletePermanently, EDP.ReturnValue)
End If
f.Delete(SFO.Path, SFODelete.DeletePermanently, EDP.None)
End If
Catch ex As Exception
End Try
End Sub
Public Sub ShowProgramInfo(ByVal ProgramText As String, ByVal CurrentVersion As Version, ByVal CheckForUpdate As Boolean, ByVal Force As Boolean, Public Sub ShowProgramInfo(ByVal ProgramText As String, ByVal CurrentVersion As Version, ByVal CheckForUpdate As Boolean, ByVal Force As Boolean,
ByVal EnvirData As IDownloaderSettings, ByVal IsYouTube As Boolean, ByVal EnvirData As IDownloaderSettings, ByVal IsYouTube As Boolean,
Optional ByRef NewVersionDestination As String = Nothing, Optional ByVal ShowNewVersionNotification As Boolean = True, Optional ByRef NewVersionDestination As String = Nothing, Optional ByRef ShowNewVersionNotification As Boolean = True,
Optional ByVal AdditText As String = Nothing) Optional ByVal AdditText As String = Nothing)
Try Try
Dim GoToSite As New MsgBoxButton("Go to site") With {.CallBack = Sub(r, n, b) Process.Start("https://github.com/AAndyProgram/SCrawler/releases/latest")} Dim GoToSite As New MsgBoxButton("Go to site") With {.CallBack = Sub(r, n, b) Process.Start("https://github.com/AAndyProgram/SCrawler/releases/latest")}
If CheckForUpdate AndAlso GitHub.NewVersionExists(CurrentVersion, "AAndyProgram", "SCrawler", NewVersionDestination) Then If CheckForUpdate AndAlso GitHub.NewVersionExists(CurrentVersion, "AAndyProgram", "SCrawler", NewVersionDestination) Then
If ShowNewVersionNotification Or Force Then If ShowNewVersionNotification Or Force Then
If MsgBoxE(New MMessage($"{ProgramText}: new version detected" & vbCr & Dim b As New List(Of MsgBoxButton)
$"Current version: {CurrentVersion}" & vbCr & Dim updaterFile As SFile = Nothing
$"New version: {NewVersionDestination}", Dim updateBtt As New MsgBoxButton("Update", "Update the program using the updater") With {
"New version", .CallBack = Sub(r, n, btt)
{"OK", GoToSite, "Disable notifications"})) = 2 Then ShowNewVersionNotification = False Dim th As New Thread(Sub() Process.Start(New ProcessStartInfo(updaterFile, 1))) With {.IsBackground = True}
th.SetApartmentState(ApartmentState.MTA)
th.Start()
End Sub}
With SFile.GetFiles("Updater\", "*.exe",, EDP.ReturnValue).ListIfNothing
If .ListExists Then
With .FirstOrDefault(Function(f) f.Name = "Updater")
If Not .IsEmptyString Then updaterFile = .Self
End With
End If
End With
b.AddRange({"OK", GoToSite})
If Not updaterFile.IsEmptyString Then b.Add(updateBtt)
b.Add(New MsgBoxButton("Disable notifications") With {.CallBackObject = 10})
If AConvert(Of Integer)(
MsgBoxE(New MMessage($"{ProgramText}: new version detected" & vbCr &
$"Current version: {CurrentVersion}" & vbCr &
$"New version: {NewVersionDestination}",
"New version", b) With {.ButtonsPerRow = 4}).Button.CallBackObject, -1) = 10 Then _
ShowNewVersionNotification = False
End If End If
Else Else
If Force Then If Force Then

View File

@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below: ' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> ' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2023.11.24.0")> <Assembly: AssemblyVersion("2023.12.7.0")>
<Assembly: AssemblyFileVersion("2023.11.24.0")> <Assembly: AssemblyFileVersion("2023.12.7.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -496,6 +496,12 @@ Namespace API.YouTube.Objects
_IUserMedia_URL_BASE = u _IUserMedia_URL_BASE = u
End Set End Set
End Property End Property
Friend Function GetUrls() As IEnumerable(Of String)
Dim urls As New List(Of String)
urls.ListAddList({URL, IUserMedia_URL_BASE}, LAP.NotContainsOnly)
If HasElements And Not IsMusic Then urls.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetUrls()), LAP.NotContainsOnly)
Return urls
End Function
Protected Overridable Sub GenerateFileName() Protected Overridable Sub GenerateFileName()
End Sub End Sub
Protected Function GetPlayListTitle() As String Protected Function GetPlayListTitle() As String
@@ -531,7 +537,7 @@ Namespace API.YouTube.Objects
If ObjectType = YouTubeMediaType.Single AndAlso Not GetPlayListTitle.IsEmptyString Then _SpecialPath.StringAppend(GetPlayListTitle(), "\") If ObjectType = YouTubeMediaType.Single AndAlso Not GetPlayListTitle.IsEmptyString Then _SpecialPath.StringAppend(GetPlayListTitle(), "\")
If Elements.Count > 0 Then Elements.ForEach(Sub(e) e.SpecialFolder = Path) If Elements.Count > 0 Then Elements.ForEach(Sub(e) e.SpecialFolder = Path)
End Sub End Sub
<XMLEC> Friend ReadOnly Property Files As List(Of SFile) Implements IYouTubeMediaContainer.Files <XMLEC> Protected Friend ReadOnly Property Files As List(Of SFile) Implements IYouTubeMediaContainer.Files
<XMLEC> Protected _File As SFile <XMLEC> Protected _File As SFile
<XMLEC> Protected Friend Property FileSetManually As Boolean = False <XMLEC> Protected Friend Property FileSetManually As Boolean = False
Public Property FileIgnorePlaylist As Boolean = False Public Property FileIgnorePlaylist As Boolean = False
@@ -591,6 +597,11 @@ Namespace API.YouTube.Objects
File = f File = f
End Set End Set
End Property End Property
Friend Function GetFiles() As IEnumerable(Of SFile)
Dim urls As New List(Of String)({File})
If HasElements And Not IsMusic Then urls.ListAddList(Elements.SelectMany(Function(elem As YouTubeMediaContainerBase) elem.GetFiles()), LAP.NotContainsOnly)
Return urls
End Function
#End Region #End Region
#Region "Command" #Region "Command"
<XMLEC> Public Property UseCookies As Boolean = MyYouTubeSettings.DefaultUseCookies Implements IYouTubeMediaContainer.UseCookies <XMLEC> Public Property UseCookies As Boolean = MyYouTubeSettings.DefaultUseCookies Implements IYouTubeMediaContainer.UseCookies
@@ -725,7 +736,7 @@ Namespace API.YouTube.Objects
End Sub End Sub
#End Region #End Region
#Region "Download" #Region "Download"
Protected Shared Sub CreateUrlFile(ByVal URL As String, ByVal File As SFile) Protected Shared Function CreateUrlFile(ByVal URL As String, ByVal File As SFile) As SFile
Try Try
File.Extension = "url" File.Extension = "url"
Using t As New TextSaver(File) Using t As New TextSaver(File)
@@ -735,9 +746,11 @@ Namespace API.YouTube.Objects
t.AppendLine() t.AppendLine()
t.Save(EDP.None) t.Save(EDP.None)
End Using End Using
Return File
Catch ex As Exception Catch ex As Exception
Return Nothing
End Try End Try
End Sub End Function
Private ReadOnly DownloadProgressPattern As RParams = RParams.DMS("\[download\]\s*([\d\.,]+)", 1, EDP.ReturnValue) Private ReadOnly DownloadProgressPattern As RParams = RParams.DMS("\[download\]\s*([\d\.,]+)", 1, EDP.ReturnValue)
Public Property Progress As MyProgress Implements IYouTubeMediaContainer.Progress Public Property Progress As MyProgress Implements IYouTubeMediaContainer.Progress
Private Property IDownloadableMedia_Progress As Object Implements IDownloadableMedia.Progress Private Property IDownloadableMedia_Progress As Object Implements IDownloadableMedia.Progress

View File

@@ -273,6 +273,10 @@
<Project>{d4650f6b-5a54-44b6-999b-6c675b7116b1}</Project> <Project>{d4650f6b-5a54-44b6-999b-6c675b7116b1}</Project>
<Name>SCrawler.PluginProvider</Name> <Name>SCrawler.PluginProvider</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\SCrawler.Shared\SCrawler.Shared.vbproj">
<Project>{dc634700-24c7-42dd-bf8f-87e6cc54e625}</Project>
<Name>SCrawler.Shared</Name>
</ProjectReference>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<None Include="Content\Pictures\YouTubeMusicPic_96.png" /> <None Include="Content\Pictures\YouTubeMusicPic_96.png" />

View File

@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below: ' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> ' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2023.11.24.0")> <Assembly: AssemblyVersion("2023.12.7.0")>
<Assembly: AssemblyFileVersion("2023.11.24.0")> <Assembly: AssemblyFileVersion("2023.12.7.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -4,6 +4,9 @@ Microsoft Visual Studio Solution File, Format Version 12.00
VisualStudioVersion = 16.0.31515.178 VisualStudioVersion = 16.0.31515.178
MinimumVisualStudioVersion = 10.0.40219.1 MinimumVisualStudioVersion = 10.0.40219.1
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler", "SCrawler\SCrawler.vbproj", "{4A016FAD-9F07-4957-8BB2-AE86C88BA342}" Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler", "SCrawler\SCrawler.vbproj", "{4A016FAD-9F07-4957-8BB2-AE86C88BA342}"
ProjectSection(ProjectDependencies) = postProject
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA} = {71263EEE-E25F-44DD-B0A9-F09047C0BEEA}
EndProjectSection
EndProject EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "PersonalUtilities", "..\..\MyUtilities\PersonalUtilities\PersonalUtilities.vbproj", "{8405896B-2685-4916-BC93-1FB514C323A9}" Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "PersonalUtilities", "..\..\MyUtilities\PersonalUtilities\PersonalUtilities.vbproj", "{8405896B-2685-4916-BC93-1FB514C323A9}"
EndProject EndProject
@@ -22,6 +25,10 @@ Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.YouTube", "SCrawle
EndProject EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.YouTubeDownloader", "SCrawler.YouTubeDownloader\SCrawler.YouTubeDownloader.vbproj", "{3F2F2C29-4ADB-48B5-A66E-EE0F51D0DCEF}" Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.YouTubeDownloader", "SCrawler.YouTubeDownloader\SCrawler.YouTubeDownloader.vbproj", "{3F2F2C29-4ADB-48B5-A66E-EE0F51D0DCEF}"
EndProject EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.Updater", "SCrawler.Updater\SCrawler.Updater.vbproj", "{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.Shared", "SCrawler.Shared\SCrawler.Shared.vbproj", "{DC634700-24C7-42DD-BF8F-87E6CC54E625}"
EndProject
Global Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU Debug|Any CPU = Debug|Any CPU
@@ -104,6 +111,30 @@ Global
{3F2F2C29-4ADB-48B5-A66E-EE0F51D0DCEF}.Release|x64.Build.0 = Release|x64 {3F2F2C29-4ADB-48B5-A66E-EE0F51D0DCEF}.Release|x64.Build.0 = Release|x64
{3F2F2C29-4ADB-48B5-A66E-EE0F51D0DCEF}.Release|x86.ActiveCfg = Release|x86 {3F2F2C29-4ADB-48B5-A66E-EE0F51D0DCEF}.Release|x86.ActiveCfg = Release|x86
{3F2F2C29-4ADB-48B5-A66E-EE0F51D0DCEF}.Release|x86.Build.0 = Release|x86 {3F2F2C29-4ADB-48B5-A66E-EE0F51D0DCEF}.Release|x86.Build.0 = Release|x86
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Debug|x64.ActiveCfg = Debug|x64
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Debug|x64.Build.0 = Debug|x64
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Debug|x86.ActiveCfg = Debug|x86
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Debug|x86.Build.0 = Debug|x86
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Release|Any CPU.Build.0 = Release|Any CPU
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Release|x64.ActiveCfg = Release|x64
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Release|x64.Build.0 = Release|x64
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Release|x86.ActiveCfg = Release|x86
{71263EEE-E25F-44DD-B0A9-F09047C0BEEA}.Release|x86.Build.0 = Release|x86
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Debug|x64.ActiveCfg = Debug|x64
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Debug|x64.Build.0 = Debug|x64
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Debug|x86.ActiveCfg = Debug|x86
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Debug|x86.Build.0 = Debug|x86
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|Any CPU.Build.0 = Release|Any CPU
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|x64.ActiveCfg = Release|x64
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|x64.Build.0 = Release|x64
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|x86.ActiveCfg = Release|x86
{DC634700-24C7-42DD-BF8F-87E6CC54E625}.Release|x86.Build.0 = Release|x86
EndGlobalSection EndGlobalSection
GlobalSection(SolutionProperties) = preSolution GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE HideSolutionNode = FALSE

View File

@@ -10,12 +10,21 @@ Imports System.Threading
Imports SCrawler.Plugin.Hosts Imports SCrawler.Plugin.Hosts
Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Forms.Toolbars
Imports PDownload = SCrawler.Plugin.ISiteSettings.Download Imports PDownload = SCrawler.Plugin.ISiteSettings.Download
Imports UserMediaD = SCrawler.DownloadObjects.TDownloader.UserMediaD
Namespace API.Base Namespace API.Base
Friend NotInheritable Class ProfileSaved Friend NotInheritable Class ProfileSaved
Private ReadOnly Property HOST As SettingsHostCollection Private ReadOnly Property HOST As SettingsHostCollection
Private ReadOnly Property Progress As MyProgress Private ReadOnly Property Progress As MyProgress
Private _Unavailable As Integer, _NotReady As Integer, _ErrorCount As Integer Private _Unavailable As Integer, _NotReady As Integer, _ErrorCount As Integer
Private _TotalImages As Integer, _TotalVideos As Integer Private _TotalImages As Integer, _TotalVideos As Integer
Friend Property Session As Integer
Friend Property IncludeInTheFeed As Boolean = False
Private _FeedDataExists As Boolean = False
Friend ReadOnly Property FeedDataExists As Boolean
Get
Return _FeedDataExists
End Get
End Property
Friend Sub New(ByRef h As SettingsHostCollection, ByRef Bar As MyProgress) Friend Sub New(ByRef h As SettingsHostCollection, ByRef Bar As MyProgress)
HOST = h HOST = h
Progress = Bar Progress = Bar
@@ -23,6 +32,7 @@ Namespace API.Base
Friend Overloads Sub Download(ByVal Token As CancellationToken, ByVal Multiple As Boolean) Friend Overloads Sub Download(ByVal Token As CancellationToken, ByVal Multiple As Boolean)
Dim n% = 0 Dim n% = 0
Dim c% = HOST.Sum(Function(h) IIf(h.DownloadSavedPosts, 1, 0)) Dim c% = HOST.Sum(Function(h) IIf(h.DownloadSavedPosts, 1, 0))
_FeedDataExists = False
_Unavailable = 0 _Unavailable = 0
_NotReady = 0 _NotReady = 0
_ErrorCount = 0 _ErrorCount = 0
@@ -30,7 +40,7 @@ Namespace API.Base
_TotalVideos = 0 _TotalVideos = 0
If c > 0 Then If c > 0 Then
For i% = 0 To HOST.Count - 1 For i% = 0 To HOST.Count - 1
If HOST(i).DownloadSavedPosts Then n += 1 : Download(HOST(i), n, c, Token, Multiple) If Not Token.IsCancellationRequested And HOST(i).DownloadSavedPosts Then n += 1 : Download(HOST(i), n, c, Token, Multiple)
Next Next
If c > 1 Then If c > 1 Then
Dim s% = {_Unavailable, _NotReady, _ErrorCount}.Sum Dim s% = {_Unavailable, _NotReady, _ErrorCount}.Sum
@@ -55,13 +65,19 @@ Namespace API.Base
.LoadUserInformation() .LoadUserInformation()
.Progress = Progress .Progress = Progress
If Not .FileExists Then .UpdateUserInformation() If Not .FileExists Then .UpdateUserInformation()
.IncludeInTheFeed = IncludeInTheFeed
Host.BeforeStartDownload(.Self, PDownload.SavedPosts)
.DownloadData(Token)
_TotalImages += .DownloadedPictures(False)
_TotalVideos += .DownloadedVideos(False)
If IncludeInTheFeed And .LatestData.Count > 0 Then
_FeedDataExists = True
Downloader.Files.AddRange(.LatestData.Select(Function(m) New UserMediaD(m, .Self, Session) With {.IsSavedPosts = True}))
End If
Progress.InformationTemporary = $"{Host.Name}{aStr} Images: { .DownloadedPictures(False)}; Videos: { .DownloadedVideos(False)}"
Host.AfterDownload(.Self, PDownload.SavedPosts)
End With End With
Host.BeforeStartDownload(user, PDownload.SavedPosts)
user.DownloadData(Token)
_TotalImages += user.DownloadedPictures(False)
_TotalVideos += user.DownloadedVideos(False)
Progress.InformationTemporary = $"{Host.Name}{aStr} Images: {user.DownloadedPictures(False)}; Videos: {user.DownloadedVideos(False)}"
Host.AfterDownload(user, PDownload.SavedPosts)
End If End If
End Using End Using
Else Else
@@ -72,6 +88,9 @@ Namespace API.Base
_NotReady += 1 _NotReady += 1
Progress.InformationTemporary = $"Host [{Host.Name}{aStr}] is not ready" Progress.InformationTemporary = $"Host [{Host.Name}{aStr}] is not ready"
End If End If
Catch oex As OperationCanceledException When Token.IsCancellationRequested
_ErrorCount += 1
Progress.InformationTemporary = $"{Host.Name}{aStr} downloading canceled"
Catch ex As Exception Catch ex As Exception
_ErrorCount += 1 _ErrorCount += 1
Progress.InformationTemporary = $"{Host.Name}{aStr} downloading error" Progress.InformationTemporary = $"{Host.Name}{aStr} downloading error"

View File

@@ -84,7 +84,7 @@ Namespace API.Base
Set : End Set Set : End Set
End Property End Property
Protected Sub UpdateResponserFile() Protected Sub UpdateResponserFile()
Dim acc$ = If(Not AccountName.IsEmptyString, $"_{AccountName}", String.Empty) Dim acc$ = If(AccountName.IsEmptyString OrElse AccountName = Hosts.SettingsHost.NameAccountNameDefault, String.Empty, $"_{AccountName}")
Responser.File = $"{SettingsFolderName}\Responser_{Site}{acc}.xml" Responser.File = $"{SettingsFolderName}\Responser_{Site}{acc}.xml"
_CookiesNetscapeFile = Responser.File _CookiesNetscapeFile = Responser.File
_CookiesNetscapeFile.Name &= "_Cookies_Netscape" _CookiesNetscapeFile.Name &= "_Cookies_Netscape"
@@ -280,12 +280,12 @@ Namespace API.Base
#End Region #End Region
Protected Sub CLONE_PROPERTIES(ByVal Source As ISiteSettings, ByVal Destination As ISiteSettings, ByVal IsUpdate As Boolean, Protected Sub CLONE_PROPERTIES(ByVal Source As ISiteSettings, ByVal Destination As ISiteSettings, ByVal IsUpdate As Boolean,
Optional ByVal Full As Boolean = True) Optional ByVal Full As Boolean = True)
Dim comparer As New MembersDistinctComparer Dim comparer As New MembersDistinctComparerExtended
'0 = update '0 = update
'1 = clone '1 = clone
'2 = any '2 = any
Dim filterUC As Func(Of MemberInfo, Byte, Boolean) = Function(ByVal m As MemberInfo, ByVal __mode As Byte) As Boolean Dim filterUC As Func(Of MemberInfo, Byte, Boolean) = Function(ByVal m As MemberInfo, ByVal __mode As Byte) As Boolean
If m.GetCustomAttribute(Of DoNotUse) Is Nothing Then If If(m.GetCustomAttribute(Of DoNotUse)?.Value, False) Then
Return False Return False
Else Else
With m.GetCustomAttribute(Of PClonableAttribute) With m.GetCustomAttribute(Of PClonableAttribute)

View File

@@ -202,7 +202,7 @@ Namespace API.Base
End Get End Get
Set(ByVal h As SettingsHost) Set(ByVal h As SettingsHost)
_HOST = h _HOST = h
_HostKey = h.Key If Not h Is Nothing Then _HostKey = h.Key
End Set End Set
End Property End Property
Private Sub ResetHost() Private Sub ResetHost()
@@ -1079,6 +1079,7 @@ BlockNullPicture:
End Try End Try
End Sub End Sub
Friend Overridable Sub OpenFolder() Implements IUserData.OpenFolder Friend Overridable Sub OpenFolder() Implements IUserData.OpenFolder
If MyFile.IsEmptyString And IsSavedPosts Then UpdateDataFiles()
GlobalOpenPath(MyFile.CutPath) GlobalOpenPath(MyFile.CutPath)
End Sub End Sub
#End Region #End Region

View File

@@ -43,7 +43,7 @@ Namespace API.RedGifs
t = .Headers.Value(TokenName) t = .Headers.Value(TokenName)
End With End With
Token = New PropertyValue(t, GetType(String), Sub(v) UpdateResponse(NameOf(Token), 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)) UserAgent = New PropertyValue(If(Responser.UserAgentExists, Responser.UserAgent, String.Empty), GetType(String), Sub(v) UpdateResponse(NameOf(UserAgent), v))
TokenLastDateUpdated = New PropertyValue(Now.AddYears(-1), GetType(Date)) TokenLastDateUpdated = New PropertyValue(Now.AddYears(-1), GetType(Date))
TokenUpdateInterval = New PropertyValue(60 * 12, GetType(Integer)) TokenUpdateInterval = New PropertyValue(60 * 12, GetType(Integer))
TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider TokenUpdateIntervalProvider = New TokenRefreshIntervalProvider

View File

@@ -20,6 +20,10 @@ Namespace API.YouTube
Friend ReadOnly Property DownloadShorts As PropertyValue Friend ReadOnly Property DownloadShorts As PropertyValue
<PXML, PropertyOption(ControlText:="Download user playlists"), PClonable> <PXML, PropertyOption(ControlText:="Download user playlists"), PClonable>
Friend ReadOnly Property DownloadPlaylists As PropertyValue Friend ReadOnly Property DownloadPlaylists As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: images"), PClonable>
Friend ReadOnly Property DownloadCommunityImages As PropertyValue
<PXML, PropertyOption(ControlText:="Download user community: videos"), PClonable>
Friend ReadOnly Property DownloadCommunityVideos As PropertyValue
<PXML, PropertyOption(ControlText:="Use cookies", ControlToolTip:="Default value for new users." & vbCr & "Use cookies when downloading data."), PClonable> <PXML, PropertyOption(ControlText:="Use cookies", ControlToolTip:="Default value for new users." & vbCr & "Use cookies when downloading data."), PClonable>
Friend ReadOnly Property UseCookies As PropertyValue Friend ReadOnly Property UseCookies As PropertyValue
#End Region #End Region
@@ -30,6 +34,8 @@ Namespace API.YouTube
DownloadVideos = New PropertyValue(True) DownloadVideos = New PropertyValue(True)
DownloadShorts = New PropertyValue(False) DownloadShorts = New PropertyValue(False)
DownloadPlaylists = New PropertyValue(False) DownloadPlaylists = New PropertyValue(False)
DownloadCommunityImages = New PropertyValue(False)
DownloadCommunityVideos = New PropertyValue(False)
UseCookies = New PropertyValue(False) UseCookies = New PropertyValue(False)
_SubscriptionsAllowed = True _SubscriptionsAllowed = True
UseNetscapeCookies = True UseNetscapeCookies = True

View File

@@ -11,16 +11,23 @@ Imports SCrawler.API.Base
Imports SCrawler.API.YouTube.Base Imports SCrawler.API.YouTube.Base
Imports SCrawler.API.YouTube.Objects Imports SCrawler.API.YouTube.Objects
Imports PersonalUtilities.Functions.XML 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
Namespace API.YouTube Namespace API.YouTube
Friend Class UserData : Inherits UserDataBase Friend Class UserData : Inherits UserDataBase
#Region "XML names" #Region "XML names"
Private Const Name_DownloadYTVideos As String = "YTDownloadVideos" Private Const Name_DownloadYTVideos As String = "YTDownloadVideos"
Private Const Name_DownloadYTShorts As String = "YTDownloadShorts" Private Const Name_DownloadYTShorts As String = "YTDownloadShorts"
Private Const Name_DownloadYTPlaylists As String = "YTDownloadPlaylists" Private Const Name_DownloadYTPlaylists As String = "YTDownloadPlaylists"
Private Const Name_DownloadYTCommunityImages As String = "YTDownloadCommunityImages"
Private Const Name_DownloadYTCommunityVideos As String = "YTDownloadCommunityVideos"
Private Const Name_YTUseCookies As String = "YTUseCookies" Private Const Name_YTUseCookies As String = "YTUseCookies"
Private Const Name_IsMusic As String = "YTIsMusic" Private Const Name_IsMusic As String = "YTIsMusic"
Private Const Name_IsChannelUser As String = "YTIsChannelUser" Private Const Name_IsChannelUser As String = "YTIsChannelUser"
Private Const Name_YTMediaType As String = "YTMediaType" Private Const Name_YTMediaType As String = "YTMediaType"
Private Const Name_ChannelID As String = "ChannelID"
Private Const Name_LastDownloadDateVideos As String = "YTLastDownloadDateVideos" Private Const Name_LastDownloadDateVideos As String = "YTLastDownloadDateVideos"
Private Const Name_LastDownloadDateShorts As String = "YTLastDownloadDateShorts" Private Const Name_LastDownloadDateShorts As String = "YTLastDownloadDateShorts"
Private Const Name_LastDownloadDatePlaylist As String = "YTLastDownloadDatePlaylist" Private Const Name_LastDownloadDatePlaylist As String = "YTLastDownloadDatePlaylist"
@@ -29,6 +36,9 @@ Namespace API.YouTube
Friend Property DownloadYTVideos As Boolean = True Friend Property DownloadYTVideos As Boolean = True
Friend Property DownloadYTShorts As Boolean = False Friend Property DownloadYTShorts As Boolean = False
Friend Property DownloadYTPlaylists As Boolean = False Friend Property DownloadYTPlaylists As Boolean = False
Friend Property DownloadYTCommunityImages As Boolean = False
Friend Property DownloadYTCommunityVideos As Boolean = False
Friend Property ChannelID As String = String.Empty
Friend Property YTUseCookies As Boolean = False Friend Property YTUseCookies As Boolean = False
Friend Property IsMusic As Boolean = False Friend Property IsMusic As Boolean = False
Friend Property IsChannelUser As Boolean = False Friend Property IsChannelUser As Boolean = False
@@ -70,6 +80,9 @@ Namespace API.YouTube
DownloadYTVideos = .Value(Name_DownloadYTVideos).FromXML(Of Boolean)(True) DownloadYTVideos = .Value(Name_DownloadYTVideos).FromXML(Of Boolean)(True)
DownloadYTShorts = .Value(Name_DownloadYTShorts).FromXML(Of Boolean)(False) DownloadYTShorts = .Value(Name_DownloadYTShorts).FromXML(Of Boolean)(False)
DownloadYTPlaylists = .Value(Name_DownloadYTPlaylists).FromXML(Of Boolean)(False) DownloadYTPlaylists = .Value(Name_DownloadYTPlaylists).FromXML(Of Boolean)(False)
DownloadYTCommunityImages = .Value(Name_DownloadYTCommunityImages).FromXML(Of Boolean)(False)
DownloadYTCommunityVideos = .Value(Name_DownloadYTCommunityVideos).FromXML(Of Boolean)(False)
ChannelID = .Value(Name_ChannelID)
IsMusic = .Value(Name_IsMusic).FromXML(Of Boolean)(False) IsMusic = .Value(Name_IsMusic).FromXML(Of Boolean)(False)
IsChannelUser = .Value(Name_IsChannelUser).FromXML(Of Boolean)(False) IsChannelUser = .Value(Name_IsChannelUser).FromXML(Of Boolean)(False)
YTMediaType = .Value(Name_YTMediaType).FromXML(Of Integer)(YouTubeMediaType.Undefined) YTMediaType = .Value(Name_YTMediaType).FromXML(Of Integer)(YouTubeMediaType.Undefined)
@@ -83,6 +96,9 @@ Namespace API.YouTube
.Add(Name_DownloadYTVideos, DownloadYTVideos.BoolToInteger) .Add(Name_DownloadYTVideos, DownloadYTVideos.BoolToInteger)
.Add(Name_DownloadYTShorts, DownloadYTShorts.BoolToInteger) .Add(Name_DownloadYTShorts, DownloadYTShorts.BoolToInteger)
.Add(Name_DownloadYTPlaylists, DownloadYTPlaylists.BoolToInteger) .Add(Name_DownloadYTPlaylists, DownloadYTPlaylists.BoolToInteger)
.Add(Name_DownloadYTCommunityImages, DownloadYTCommunityImages.BoolToInteger)
.Add(Name_DownloadYTCommunityVideos, DownloadYTCommunityVideos.BoolToInteger)
.Add(Name_ChannelID, ChannelID)
.Add(Name_IsMusic, IsMusic.BoolToInteger) .Add(Name_IsMusic, IsMusic.BoolToInteger)
.Add(Name_IsChannelUser, IsChannelUser.BoolToInteger) .Add(Name_IsChannelUser, IsChannelUser.BoolToInteger)
.Add(Name_YTMediaType, CInt(YTMediaType)) .Add(Name_YTMediaType, CInt(YTMediaType))
@@ -103,7 +119,10 @@ Namespace API.YouTube
DownloadYTVideos = .DownloadVideos DownloadYTVideos = .DownloadVideos
DownloadYTShorts = .DownloadShorts DownloadYTShorts = .DownloadShorts
DownloadYTPlaylists = .DownloadPlaylists DownloadYTPlaylists = .DownloadPlaylists
DownloadYTCommunityImages = .DownloadCommunityImages
DownloadYTCommunityVideos = .DownloadCommunityVideos
YTUseCookies = .UseCookies YTUseCookies = .UseCookies
ChannelID = .ChannelID
End With End With
End If End If
End Sub End Sub
@@ -184,6 +203,7 @@ Namespace API.YouTube
applySpecFolder.Invoke("Playlists", True) applySpecFolder.Invoke("Playlists", True)
If fillList.Invoke(LastDownloadDatePlaylist) Then LastDownloadDatePlaylist = If(maxDate, Now) If fillList.Invoke(LastDownloadDatePlaylist) Then LastDownloadDatePlaylist = If(maxDate, Now)
End If End If
If Not IsMusic And (DownloadYTCommunityImages Or DownloadYTCommunityVideos) Then DownloadCommunity(String.Empty, Token)
Else Else
Throw New InvalidOperationException($"Media type {YTMediaType} not implemented") Throw New InvalidOperationException($"Media type {YTMediaType} not implemented")
End If End If
@@ -203,10 +223,201 @@ Namespace API.YouTube
pr.Dispose() pr.Dispose()
End Try End Try
End Sub End Sub
Private Sub DownloadCommunity(ByVal Cursor As String, ByVal Token As CancellationToken, Optional ByVal Round As Integer = 0)
Dim URL$ = String.Empty
Try
Const postIdTemp$ = "Community_{0}"
Const specFolder$ = "Community"
Dim nextToken$ = String.Empty
Dim postId$ = String.Empty, videoId$ = String.Empty
Dim tmpPID$
Dim imgCount%, imgNum%
Dim postUrl As Func(Of String) = Function() $"https://www.youtube.com/post/{postId}"
Dim image As EContainer, thumb As EContainer
Dim sl As New List(Of Sizes)
Dim m As UserMedia
Dim v As IYouTubeMediaContainer
If ChannelID.IsEmptyString Then GetChannelID()
If ChannelID.IsEmptyString Then Throw New ArgumentNullException("ChannelID", "Channel ID cannot be null")
URL = $"https://yt.lemnoslife.com/channels?part=community&id={ChannelID}"
If Not Cursor.IsEmptyString Then URL &= $"&pageToken={Cursor}"
ProgressPre.ChangeMax(1)
Using resp As New Responser
Dim r$ = resp.GetResponse(URL,, EDP.ReturnValue)
ProgressPre.Perform()
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r)
If j.ListExists Then
With j.ItemF({"items", 0})
If .ListExists Then
nextToken = .Value("nextPageToken")
With .Item("community")
If .ListExists Then
ProgressPre.ChangeMax(.Count)
For Each jj As EContainer In .Self
With jj
postId = .Value("id")
videoId = .Value("videoId")
tmpPID = String.Format(postIdTemp, postId)
If Not _TempPostsList.Contains(tmpPID) Then _TempPostsList.Add(tmpPID) Else Exit Sub
If Not videoId.IsEmptyString Then
If DownloadYTCommunityVideos Then
v = Nothing
Try : v = YouTubeFunctions.Parse($"https://www.youtube.com/watch?v={videoId}", YTUseCookies, Token) : Catch : End Try
If Not v Is Nothing Then
With DirectCast(v, YouTubeMediaContainerBase)
.SpecialPath = specFolder & "\Videos"
.SpecialPathDisabled = False
End With
_TempMediaList.ListAddValue(New UserMedia(v) With {.Post = postId}, LNC)
End If
End If
ElseIf DownloadYTCommunityImages Then
With .Item("images")
If .ListExists Then
imgCount = .Count
imgNum = 0
For Each image In .Self
imgNum += 1
sl.Clear()
With image("thumbnails")
If .ListExists Then
For Each thumb In .Self : sl.Add(New Sizes(thumb.Value("width"), thumb.Value("url"))) : Next
If sl.Count > 0 Then sl.RemoveAll(Function(s) s.HasError Or s.Data.IsEmptyString)
If sl.Count > 0 Then
sl.Sort()
m = New UserMedia(sl(0).Data, UTypes.Picture) With {
.URL_BASE = postUrl.Invoke,
.Post = postId,
.SpecialFolder = specFolder,
.File = $"{postId}{IIf(imgCount > 1, $"_{imgNum}", String.Empty)}.jpg"
}
_TempMediaList.Add(m)
End If
End If
End With
Next
End If
End With
End If
ProgressPre.Perform()
End With
Next
End If
End With
End If
End With
End If
End Using
ElseIf resp.HasError Then
If resp.Status = Net.WebExceptionStatus.ConnectFailure And Round < 2 Then
Thread.Sleep(1000)
DownloadCommunity(Cursor, Token, Round + 1)
Else
Throw resp.ErrorException
End If
End If
End Using
If Not nextToken.IsEmptyString Then DownloadCommunity(nextToken, Token)
Catch ex As Exception
ProcessException(ex, Token, "community data downloading error")
End Try
End Sub
Private Sub GetChannelID()
Try
Dim r$ = GetWebString(GetUserUrl,, EDP.ThrowException)
If Not r.IsEmptyString Then
Dim newUrl$ = RegexReplace(r, RParams.DMS("meta property=.og:url..content=.([^""]+)", 1, EDP.ReturnValue))
If Not newUrl.IsEmptyString Then
Dim newID$ = String.Empty
YouTubeFunctions.Info_GetUrlType(newUrl,,,, newID)
If Not newID.IsEmptyString And Not ChannelID = newID Then ChannelID = newID : _ForceSaveUserInfo = True
End If
End If
Catch ex As Exception
ProcessException(ex, Nothing, "error getting channel ID")
End Try
End Sub
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
SeparateVideoFolder = False SeparateVideoFolder = False
DownloadContentDefault(Token) DownloadContentDefault(Token)
End Sub End Sub
Private Class YTPreProgressContainer : Inherits PersonalUtilities.Forms.Toolbars.MyProgress
Private ReadOnly MyPreProgress As PreProgress
Friend Sub New(ByVal PR As PreProgress)
MyBase.New(PR.Progress.MyControls)
MyPreProgress = PR
End Sub
Private _MaxChanged As Boolean = False
Public Overrides Property Maximum As Double
Get
Return MyPreProgress.Progress.Maximum0
End Get
Set(ByVal max As Double)
MyPreProgress.Progress.Maximum0 += max
_MaxChanged = True
End Set
End Property
Private _LastValue As Double = -1
Private _FirstAdded As Boolean = False
Public Overrides Property Value As Double
Get
Return MyPreProgress.Progress.Value0
End Get
Set(ByVal v As Double)
If _MaxChanged Then
If Not _FirstAdded Then
_FirstAdded = True
ElseIf v > 0 Then
Dim newValue#
If _LastValue = -1 Then
newValue = v
ElseIf _LastValue > v Then
newValue = v
Else
newValue = v - _LastValue
End If
_LastValue = v
MyPreProgress.Progress.Value0 += newValue
End If
End If
End Set
End Property
Public Overrides Sub Perform(Optional ByVal Value As Double = 1)
MyPreProgress.Perform(Value)
End Sub
Public Overrides Sub Reset()
MyPreProgress.Reset()
End Sub
Public Overrides Sub Done()
MyPreProgress.Done()
End Sub
Public Overrides Property Information As String
Get
Return String.Empty
End Get
Set : End Set
End Property
Public Overrides WriteOnly Property InformationTemporary(Optional ByVal AddPercentage As Boolean = False) As String
Set : End Set
End Property
Public Overrides Function GetLabelText() As String
Return String.Empty
End Function
Public Overrides Property Visible(Optional ByVal ProgressBar As Boolean = True, Optional ByVal Label As Boolean = True) As Boolean
Get
Return True
End Get
Set : End Set
End Property
End Class
Protected Overrides Function DownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile, Protected Overrides Function DownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile,
ByVal Token As CancellationToken) As SFile ByVal Token As CancellationToken) As SFile
If Not Media.Object Is Nothing AndAlso TypeOf Media.Object Is IYouTubeMediaContainer Then If Not Media.Object Is Nothing AndAlso TypeOf Media.Object Is IYouTubeMediaContainer Then
@@ -215,13 +426,17 @@ Namespace API.YouTube
f.Path = DestinationFile.Path f.Path = DestinationFile.Path
If Not IsSingleObjectDownload And Not .FileIsPlaylistObject Then .FileIgnorePlaylist = True If Not IsSingleObjectDownload And Not .FileIsPlaylistObject Then .FileIgnorePlaylist = True
.File = f .File = f
If IsSingleObjectDownload Then .Progress = Progress If IsSingleObjectDownload Then .Progress = Progress Else .Progress = New YTPreProgressContainer(ProgressPre)
.Download(YTUseCookies, Token) .Download(YTUseCookies, Token)
If Not .Progress Is Nothing AndAlso TypeOf .Progress Is YTPreProgressContainer Then .Progress.Dispose()
If .File.Exists Then Return .File If .File.Exists Then Return .File
End With End With
End If End If
Return Nothing Return Nothing
End Function End Function
Protected Overrides Function ValidateDownloadFile(ByVal URL As String, ByVal Media As UserMedia, ByRef Interrupt As Boolean) As Boolean
Return Not Media.Type = UTypes.Picture
End Function
Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken) Protected Overrides Sub DownloadSingleObject_GetPosts(ByVal Data As IYouTubeMediaContainer, ByVal Token As CancellationToken)
_TempMediaList.Add(New UserMedia(Data)) _TempMediaList.Add(New UserMedia(Data))
End Sub End Sub

View File

@@ -15,18 +15,29 @@ Namespace API.YouTube
Friend Property DownloadShorts As Boolean Friend Property DownloadShorts As Boolean
<PSetting(Caption:="Download playlists")> <PSetting(Caption:="Download playlists")>
Friend Property DownloadPlaylists As Boolean Friend Property DownloadPlaylists As Boolean
<PSetting(Caption:="Download community images")>
Friend Property DownloadCommunityImages As Boolean
<PSetting(Caption:="Download community videos")>
Friend Property DownloadCommunityVideos As Boolean
<PSetting(Caption:="Use cookies", ToolTip:="Use cookies when downloading data.")> <PSetting(Caption:="Use cookies", ToolTip:="Use cookies when downloading data.")>
Friend Property UseCookies As Boolean Friend Property UseCookies As Boolean
<PSetting(Caption:="Channel ID", Address:=SettingAddress.User)>
Friend Property ChannelID As String
Friend Sub New(ByVal u As UserData) Friend Sub New(ByVal u As UserData)
DownloadVideos = u.DownloadYTVideos DownloadVideos = u.DownloadYTVideos
DownloadShorts = u.DownloadYTShorts DownloadShorts = u.DownloadYTShorts
DownloadPlaylists = u.DownloadYTPlaylists DownloadPlaylists = u.DownloadYTPlaylists
DownloadCommunityImages = u.DownloadYTCommunityImages
DownloadCommunityVideos = u.DownloadYTCommunityVideos
UseCookies = u.YTUseCookies UseCookies = u.YTUseCookies
ChannelID = u.ChannelID
End Sub End Sub
Friend Sub New(ByVal s As SiteSettings) Friend Sub New(ByVal s As SiteSettings)
DownloadVideos = s.DownloadVideos.Value DownloadVideos = s.DownloadVideos.Value
DownloadShorts = s.DownloadShorts.Value DownloadShorts = s.DownloadShorts.Value
DownloadPlaylists = s.DownloadPlaylists.Value DownloadPlaylists = s.DownloadPlaylists.Value
DownloadCommunityImages = s.DownloadCommunityImages.Value
DownloadCommunityVideos = s.DownloadCommunityVideos.Value
UseCookies = s.UseCookies.Value UseCookies = s.UseCookies.Value
End Sub End Sub
End Class End Class

View File

@@ -339,6 +339,7 @@ Namespace DownloadObjects
.Name = String.Empty .Name = String.Empty
._Mode = _Mode ._Mode = _Mode
.Groups.ListAddList(Groups, LAP.ClearBeforeAdd) .Groups.ListAddList(Groups, LAP.ClearBeforeAdd)
.IsManual = IsManual
.Timer = Timer .Timer = Timer
.StartupDelay = StartupDelay .StartupDelay = StartupDelay
.ShowNotifications = ShowNotifications .ShowNotifications = ShowNotifications

View File

@@ -7,6 +7,7 @@
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Forms.Controls.KeyClick
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Imports TDJob = SCrawler.DownloadObjects.TDownloader.Job Imports TDJob = SCrawler.DownloadObjects.TDownloader.Job
Namespace DownloadObjects Namespace DownloadObjects
@@ -14,6 +15,7 @@ Namespace DownloadObjects
#Region "Events" #Region "Events"
Friend Event DownloadDone As NotificationEventHandler Friend Event DownloadDone As NotificationEventHandler
Friend Event ProgressChanged(ByVal Main As Boolean, ByVal IsMaxValue As Boolean, ByVal IsDone As Boolean) Friend Event ProgressChanged(ByVal Main As Boolean, ByVal IsMaxValue As Boolean, ByVal IsDone As Boolean)
Friend Event FeedFilesChanged As TDownloader.FeedFilesChangedEventHandler
#End Region #End Region
#Region "Declarations" #Region "Declarations"
#Region "Controls" #Region "Controls"
@@ -26,14 +28,18 @@ Namespace DownloadObjects
Private ReadOnly PR_PRE As ProgressBar Private ReadOnly PR_PRE As ProgressBar
Private ReadOnly LBL_INFO As Label Private ReadOnly LBL_INFO As Label
Private ReadOnly Icon As PictureBox Private ReadOnly Icon As PictureBox
Private ReadOnly TT_MAIN As ToolTip
#End Region #End Region
Private ReadOnly Property Instance As API.Base.ProfileSaved Private ReadOnly Property Instance As API.Base.ProfileSaved
Friend ReadOnly Property Job As TDJob Friend ReadOnly Property Job As TDJob
Private ReadOnly InternalArgs As KeyClickEventArgs
#End Region #End Region
#Region "Initializer" #Region "Initializer"
Friend Sub New(ByVal _Job As TDJob) Friend Sub New(ByVal _Job As TDJob)
Job = _Job Job = _Job
InternalArgs = New KeyClickEventArgs
TT_MAIN = New ToolTip
TP_MAIN = New TableLayoutPanel With {.Margin = New Padding(0), .Dock = DockStyle.Fill} TP_MAIN = New TableLayoutPanel With {.Margin = New Padding(0), .Dock = DockStyle.Fill}
TP_MAIN.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100)) TP_MAIN.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100))
TP_MAIN.ColumnCount = 1 TP_MAIN.ColumnCount = 1
@@ -86,6 +92,7 @@ Namespace DownloadObjects
LBL_INFO.Padding = New Padding(3, 0, 3, 0) LBL_INFO.Padding = New Padding(3, 0, 3, 0)
LBL_INFO.TextAlign = ContentAlignment.TopCenter LBL_INFO.TextAlign = ContentAlignment.TopCenter
CreateButton(BTT_START, My.Resources.StartPic_Green_16) CreateButton(BTT_START, My.Resources.StartPic_Green_16)
TT_MAIN.SetToolTip(BTT_START, "Ctrl+Click: download, exclude from feed.")
CreateButton(BTT_OPEN, PersonalUtilities.My.Resources.FolderOpenPic_Black_16) CreateButton(BTT_OPEN, PersonalUtilities.My.Resources.FolderOpenPic_Black_16)
With TP_CONTROLS With TP_CONTROLS
With .ColumnStyles With .ColumnStyles
@@ -148,7 +155,8 @@ Namespace DownloadObjects
End Function End Function
#Region "Buttons" #Region "Buttons"
Private Sub BTT_START_Click(sender As Object, e As EventArgs) Handles BTT_START.Click Private Sub BTT_START_Click(sender As Object, e As EventArgs) Handles BTT_START.Click
Start() InternalArgs.Reset()
Start(, Downloader.SessionSavedPosts, Not InternalArgs.Control)
End Sub End Sub
Private Sub BTT_STOP_Click(sender As Object, e As EventArgs) Handles BTT_STOP.Click Private Sub BTT_STOP_Click(sender As Object, e As EventArgs) Handles BTT_STOP.Click
[Stop]() [Stop]()
@@ -159,8 +167,13 @@ Namespace DownloadObjects
#End Region #End Region
#Region "Start, Stop" #Region "Start, Stop"
Private _IsMultiple As Boolean = False Private _IsMultiple As Boolean = False
Friend Sub Start(Optional ByVal Multiple As Boolean = False) Private _Session As Integer = 0
Private _IncludeInTheFeed As Boolean = True
Friend Sub Start(Optional ByVal Multiple As Boolean = False, Optional ByVal Session As Integer = -1,
Optional ByVal IncludeInTheFeed As Boolean = True)
_IsMultiple = Multiple _IsMultiple = Multiple
_Session = Session
_IncludeInTheFeed = IncludeInTheFeed
Job.StartThread(AddressOf DownloadData) Job.StartThread(AddressOf DownloadData)
End Sub End Sub
Friend Sub [Stop]() Friend Sub [Stop]()
@@ -175,7 +188,10 @@ Namespace DownloadObjects
btte.Invoke(BTT_STOP, True) btte.Invoke(BTT_STOP, True)
Job.Progress.InformationTemporary = $"{Job.HostCollection.Name} downloading started" Job.Progress.InformationTemporary = $"{Job.HostCollection.Name} downloading started"
Job.Start() Job.Start()
Instance.Session = _Session
Instance.IncludeInTheFeed = _IncludeInTheFeed
Instance.Download(Job.Token, _IsMultiple) Instance.Download(Job.Token, _IsMultiple)
If _IncludeInTheFeed And Instance.FeedDataExists Then RaiseEvent FeedFilesChanged(True)
RaiseEvent DownloadDone(SettingsCLS.NotificationObjects.SavedPosts, $"Downloading saved {Job.HostCollection.Name} posts is completed") RaiseEvent DownloadDone(SettingsCLS.NotificationObjects.SavedPosts, $"Downloading saved {Job.HostCollection.Name} posts is completed")
Catch ex As Exception Catch ex As Exception
Job.Progress.InformationTemporary = $"{Job.HostCollection.Name} downloading error" Job.Progress.InformationTemporary = $"{Job.HostCollection.Name} downloading error"
@@ -220,6 +236,7 @@ Namespace DownloadObjects
If Not Icon Is Nothing Then Icon.Dispose() If Not Icon Is Nothing Then Icon.Dispose()
PR_MAIN.DisposeIfReady() PR_MAIN.DisposeIfReady()
LBL_INFO.DisposeIfReady() LBL_INFO.DisposeIfReady()
If Not TT_MAIN Is Nothing Then TT_MAIN.Dispose()
If Not TP_CONTROLS Is Nothing Then If Not TP_CONTROLS Is Nothing Then
TP_CONTROLS.Controls.Clear() TP_CONTROLS.Controls.Clear()
TP_CONTROLS.Dispose() TP_CONTROLS.Dispose()

View File

@@ -24,7 +24,6 @@ Partial Friend Class DownloadSavedPostsForm : Inherits System.Windows.Forms.Form
Me.components = New System.ComponentModel.Container() Me.components = New System.ComponentModel.Container()
Dim TP_BUTTONS As System.Windows.Forms.TableLayoutPanel Dim TP_BUTTONS As System.Windows.Forms.TableLayoutPanel
Dim TT_MAIN As System.Windows.Forms.ToolTip Dim TT_MAIN As System.Windows.Forms.ToolTip
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(DownloadSavedPostsForm))
Me.BTT_DOWN_ALL = New System.Windows.Forms.Button() Me.BTT_DOWN_ALL = New System.Windows.Forms.Button()
Me.BTT_STOP_ALL = New System.Windows.Forms.Button() Me.BTT_STOP_ALL = New System.Windows.Forms.Button()
Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel() Me.TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
@@ -59,6 +58,7 @@ Partial Friend Class DownloadSavedPostsForm : Inherits System.Windows.Forms.Form
Me.BTT_DOWN_ALL.Size = New System.Drawing.Size(234, 31) Me.BTT_DOWN_ALL.Size = New System.Drawing.Size(234, 31)
Me.BTT_DOWN_ALL.TabIndex = 0 Me.BTT_DOWN_ALL.TabIndex = 0
Me.BTT_DOWN_ALL.Text = "Download ALL" Me.BTT_DOWN_ALL.Text = "Download ALL"
TT_MAIN.SetToolTip(Me.BTT_DOWN_ALL, "Ctrl+Click: download, exclude from feed.")
Me.BTT_DOWN_ALL.UseVisualStyleBackColor = True Me.BTT_DOWN_ALL.UseVisualStyleBackColor = True
' '
'BTT_STOP_ALL 'BTT_STOP_ALL
@@ -92,7 +92,7 @@ Partial Friend Class DownloadSavedPostsForm : Inherits System.Windows.Forms.Form
Me.ClientSize = New System.Drawing.Size(484, 41) Me.ClientSize = New System.Drawing.Size(484, 41)
Me.Controls.Add(Me.TP_MAIN) Me.Controls.Add(Me.TP_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = Global.SCrawler.My.Resources.BookmarkIcon_32 Me.Icon = Global.SCrawler.My.Resources.Resources.BookmarkIcon_32
Me.MaximizeBox = False Me.MaximizeBox = False
Me.MaximumSize = New System.Drawing.Size(500, 80) Me.MaximumSize = New System.Drawing.Size(500, 80)
Me.MinimumSize = New System.Drawing.Size(500, 80) Me.MinimumSize = New System.Drawing.Size(500, 80)

View File

@@ -117,15 +117,13 @@
<resheader name="writer"> <resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader> </resheader>
<assembly alias="mscorlib" name="mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" /> <metadata name="TP_BUTTONS.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<data name="TP_BUTTONS.GenerateMember" type="System.Boolean, mscorlib">
<value>False</value> <value>False</value>
</data> </metadata>
<data name="TT_MAIN.GenerateMember" type="System.Boolean, mscorlib"> <metadata name="TT_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value> <value>False</value>
</data> </metadata>
<assembly alias="System.Drawing" name="System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" /> <metadata name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a">
<data name="TT_MAIN.TrayLocation" type="System.Drawing.Point, System.Drawing">
<value>17, 17</value> <value>17, 17</value>
</data> </metadata>
</root> </root>

View File

@@ -10,8 +10,10 @@ Imports System.ComponentModel
Imports SCrawler.DownloadObjects Imports SCrawler.DownloadObjects
Imports SCrawler.Plugin.Hosts Imports SCrawler.Plugin.Hosts
Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms
Imports PersonalUtilities.Forms.Controls.KeyClick
Friend Class DownloadSavedPostsForm Friend Class DownloadSavedPostsForm
Friend Event DownloadDone As NotificationEventHandler Friend Event DownloadDone As NotificationEventHandler
Friend Event FeedFilesChanged As TDownloader.FeedFilesChangedEventHandler
Private MyView As FormView Private MyView As FormView
Private ReadOnly JobsList As List(Of DownloadProgress) Private ReadOnly JobsList As List(Of DownloadProgress)
Friend ReadOnly Property Working As Boolean Friend ReadOnly Property Working As Boolean
@@ -40,6 +42,7 @@ Friend Class DownloadSavedPostsForm
If JobsList.Count > 0 Then If JobsList.Count > 0 Then
For Each j As DownloadProgress In JobsList For Each j As DownloadProgress In JobsList
AddHandler j.DownloadDone, AddressOf Jobs_DownloadDone AddHandler j.DownloadDone, AddressOf Jobs_DownloadDone
AddHandler j.FeedFilesChanged, AddressOf Jobs_FeedFilesChanged
TP_MAIN.RowStyles.Add(New RowStyle(SizeType.Absolute, 60)) TP_MAIN.RowStyles.Add(New RowStyle(SizeType.Absolute, 60))
TP_MAIN.RowCount += 1 TP_MAIN.RowCount += 1
TP_MAIN.Controls.Add(j.Get, 0, TP_MAIN.RowStyles.Count - 1) TP_MAIN.Controls.Add(j.Get, 0, TP_MAIN.RowStyles.Count - 1)
@@ -60,7 +63,16 @@ Friend Class DownloadSavedPostsForm
MyView.Dispose(Settings.Design) MyView.Dispose(Settings.Design)
End Sub End Sub
Private Sub [Start]() Handles BTT_DOWN_ALL.Click Private Sub [Start]() Handles BTT_DOWN_ALL.Click
If JobsList.Count > 0 Then JobsList.ForEach(Sub(j) j.Start(True)) If JobsList.Count > 0 Then
Dim ses% = Downloader.SessionSavedPosts
Dim args As New KeyClickEventArgs
args.Reset()
JobsList.ForEach(Sub(ByVal j As DownloadProgress)
ses += 1
j.Start(True, ses, Not args.Control)
End Sub)
Downloader.SessionSavedPosts = ses
End If
End Sub End Sub
Friend Sub [Stop]() Handles BTT_STOP_ALL.Click Friend Sub [Stop]() Handles BTT_STOP_ALL.Click
If JobsList.Count > 0 Then JobsList.ForEach(Sub(j) j.Stop()) If JobsList.Count > 0 Then JobsList.ForEach(Sub(j) j.Stop())
@@ -68,4 +80,7 @@ Friend Class DownloadSavedPostsForm
Private Sub Jobs_DownloadDone(ByVal Obj As SettingsCLS.NotificationObjects, ByVal Message As String) Private Sub Jobs_DownloadDone(ByVal Obj As SettingsCLS.NotificationObjects, ByVal Message As String)
RaiseEvent DownloadDone(SettingsCLS.NotificationObjects.SavedPosts, Message) RaiseEvent DownloadDone(SettingsCLS.NotificationObjects.SavedPosts, Message)
End Sub End Sub
Private Sub Jobs_FeedFilesChanged(ByVal Added As Boolean)
RaiseEvent FeedFilesChanged(Added)
End Sub
End Class End Class

View File

@@ -300,6 +300,11 @@ Namespace DownloadObjects
Throw New ArgumentNullException With {.HelpLink = 1} Throw New ArgumentNullException With {.HelpLink = 1}
End If End If
If Media.IsSavedPosts Then
BTT_CONTEXT_OPEN_USER_URL.Visible = False
BTT_CONTEXT_FIND_USER.Visible = False
End If
If Settings.Feeds.FavoriteExists AndAlso Settings.Feeds.Favorite.Contains(Media) Then BTT_FEED_ADD_FAV.ControlChangeColor(True, False) If Settings.Feeds.FavoriteExists AndAlso Settings.Feeds.Favorite.Contains(Media) Then BTT_FEED_ADD_FAV.ControlChangeColor(True, False)
If Settings.FeedShowSpecialFeedsMediaItem Then If Settings.FeedShowSpecialFeedsMediaItem Then
With Settings.Feeds With Settings.Feeds
@@ -392,14 +397,31 @@ Namespace DownloadObjects
End Sub End Sub
Private Sub BTT_CONTEXT_OPEN_USER_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_USER.Click Private Sub BTT_CONTEXT_OPEN_USER_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_USER.Click
If Not UserKey.IsEmptyString Then If Not UserKey.IsEmptyString Then
Dim u As IUserData = Settings.GetUser(UserKey) Dim u As IUserData = Nothing
If Not u Is Nothing Then u.OpenFolder() If Not Media.IsSavedPosts Then
u = Settings.GetUser(UserKey)
Else
If Not Media.UserInfo.Plugin.IsEmptyString Then
Dim host As Plugin.Hosts.SettingsHost = Settings(Media.UserInfo.Plugin, Media.UserInfo.AccountName)
If Not host Is Nothing Then
u = host.GetInstance(Plugin.ISiteSettings.Download.SavedPosts, Media.UserInfo, False, False)
With DirectCast(u, UserDataBase)
.IsSavedPosts = True
.HostStatic = True
End With
End If
End If
End If
If Not u Is Nothing Then
u.OpenFolder()
If Media.IsSavedPosts Then u.Dispose()
End If
End If End If
End Sub End Sub
#End Region #End Region
#Region "Open URL" #Region "Open URL"
Private Sub BTT_CONTEXT_OPEN_USER_URL_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_USER_URL.Click Private Sub BTT_CONTEXT_OPEN_USER_URL_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_OPEN_USER_URL.Click
If Not UserKey.IsEmptyString Then If Not UserKey.IsEmptyString And Not Media.IsSavedPosts Then
Dim u As IUserData = Settings.GetUser(UserKey) Dim u As IUserData = Settings.GetUser(UserKey)
If Not u Is Nothing Then u.OpenSite() If Not u Is Nothing Then u.OpenSite()
End If End If
@@ -411,8 +433,26 @@ Namespace DownloadObjects
url = Post.URL_BASE url = Post.URL_BASE
Else Else
If Not UserKey.IsEmptyString And Not Post.Post.ID.IsEmptyString Then If Not UserKey.IsEmptyString And Not Post.Post.ID.IsEmptyString Then
Dim u As IUserData = Settings.GetUser(UserKey) Dim u As IUserData
If Not u Is Nothing Then url = UserDataBase.GetPostUrl(u, Post) If Media.IsSavedPosts Then
If Not Media.UserInfo.Plugin.IsEmptyString Then
Dim host As Plugin.Hosts.SettingsHostCollection = Settings(Media.UserInfo.Plugin)
If Not host Is Nothing Then
u = host.Default.GetInstance(Plugin.ISiteSettings.Download.SavedPosts, Media.UserInfo, False, False)
If Not u Is Nothing AndAlso Not u.HOST Is Nothing Then
With DirectCast(u, UserDataBase)
.IsSavedPosts = True
.HostStatic = True
End With
Try : url = u.HOST.Source.GetUserPostUrl(u, Post) : Catch : End Try
u.Dispose()
End If
End If
End If
Else
u = Settings.GetUser(UserKey)
If Not u Is Nothing Then url = UserDataBase.GetPostUrl(u, Post)
End If
End If End If
End If End If
If Not url.IsEmptyString Then If Not url.IsEmptyString Then

View File

@@ -6,7 +6,7 @@
' '
' This program is distributed in the hope that it will be useful, ' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports LibVLCSharp Imports LibVLCSharp.Shared
Imports System.Threading Imports System.Threading
Imports System.ComponentModel Imports System.ComponentModel
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
@@ -15,7 +15,7 @@ Imports VLCState = LibVLCSharp.Shared.VLCState
Namespace DownloadObjects Namespace DownloadObjects
<ToolboxItem(False), DesignTimeVisible(False)> <ToolboxItem(False), DesignTimeVisible(False)>
Public Class FeedVideo Public Class FeedVideo
Private WithEvents MediaPlayer As [Shared].MediaPlayer Private WithEvents MediaPlayer As MediaPlayer
Private ReadOnly TimeChange As Action = Sub() Private ReadOnly TimeChange As Action = Sub()
If _Disposed Then Exit Sub If _Disposed Then Exit Sub
Dim v# = DivideWithZeroChecking(MediaPlayer.Time, MediaPlayer.Length) * 10 Dim v# = DivideWithZeroChecking(MediaPlayer.Time, MediaPlayer.Length) * 10
@@ -50,7 +50,7 @@ Namespace DownloadObjects
'#If DEBUG Then '#If DEBUG Then
' debugLogs = True ' debugLogs = True
'#End If '#End If
MediaPlayer = New [Shared].MediaPlayer(New [Shared].Media(New [Shared].LibVLC(enableDebugLogs:=debugLogs), New Uri(File.ToString))) MediaPlayer = New MediaPlayer(New Media(New LibVLC(enableDebugLogs:=debugLogs), New Uri(File.ToString)))
MyVideo.MediaPlayer = MediaPlayer MyVideo.MediaPlayer = MediaPlayer
TR_VOLUME.Value = MediaPlayer.Volume / 10 TR_VOLUME.Value = MediaPlayer.Volume / 10
If Settings.UseM3U8 Then If Settings.UseM3U8 Then
@@ -111,7 +111,7 @@ Namespace DownloadObjects
End Sub, "Stop") End Sub, "Stop")
UpdateButtons() UpdateButtons()
End Sub End Sub
Private Sub MediaPlayer_TimeChanged(sender As Object, e As [Shared].MediaPlayerTimeChangedEventArgs) Handles MediaPlayer.TimeChanged Private Sub MediaPlayer_TimeChanged(sender As Object, e As MediaPlayerTimeChangedEventArgs) Handles MediaPlayer.TimeChanged
If _Disposed Then Exit Sub If _Disposed Then Exit Sub
If TR_POSITION.InvokeRequired Then TR_POSITION.Invoke(TimeChange) Else TimeChange.Invoke If TR_POSITION.InvokeRequired Then TR_POSITION.Invoke(TimeChange) Else TimeChange.Invoke
If LBL_TIME.InvokeRequired Then LBL_TIME.Invoke(TimeChangeLabel) Else TimeChangeLabel.Invoke If LBL_TIME.InvokeRequired Then LBL_TIME.Invoke(TimeChangeLabel) Else TimeChangeLabel.Invoke

View File

@@ -145,7 +145,7 @@ Namespace DownloadObjects.STDownloader
For Each url In urls For Each url In urls
If Not TryYouTube.Invoke Then If Not TryYouTube.Invoke Then
media = FindSource(url, output) media = FindSource(url, output)
If Not media Is Nothing Then media.AccountName = acc : ControlCreateAndAdd(media, disableDown) If Not media Is Nothing AndAlso ValidateContainerURL(media) Then media.AccountName = acc : ControlCreateAndAdd(media, disableDown)
End If End If
Next Next
urls.Clear() urls.Clear()
@@ -175,7 +175,7 @@ Namespace DownloadObjects.STDownloader
End If End If
If media Is Nothing Then If media Is Nothing Then
MsgBoxE({$"The URL you entered is not recognized by existing plugins.{vbCr}{url}", "Download video"}, vbCritical) MsgBoxE({$"The URL you entered is not recognized by existing plugins.{vbCr}{url}", "Download video"}, vbCritical)
Else ElseIf ValidateContainerURL(media) Then
media.AccountName = acc media.AccountName = acc
output.Exists(SFO.Path, True) output.Exists(SFO.Path, True)
ControlCreateAndAdd(media, disableDown) ControlCreateAndAdd(media, disableDown)

View File

@@ -34,12 +34,14 @@ Namespace DownloadObjects
Private Const Name_Date As String = "Date" Private Const Name_Date As String = "Date"
Private Const Name_Session As String = "Session" Private Const Name_Session As String = "Session"
Private Const Name_File As String = "File" Private Const Name_File As String = "File"
Private Const Name_IsSavedPosts As String = "IsSavedPosts"
#End Region #End Region
Friend ReadOnly User As IUserData Friend ReadOnly User As IUserData
Friend ReadOnly Data As UserMedia Friend ReadOnly Data As UserMedia
Friend ReadOnly UserInfo As UserInfo Friend ReadOnly UserInfo As UserInfo
Friend ReadOnly [Date] As Date Friend ReadOnly [Date] As Date
Friend ReadOnly Session As Integer Friend ReadOnly Session As Integer
Friend IsSavedPosts As Boolean
Friend Sub New(ByVal Data As UserMedia, ByVal User As IUserData, ByVal Session As Integer) Friend Sub New(ByVal Data As UserMedia, ByVal User As IUserData, ByVal Session As Integer)
Me.Data = Data Me.Data = Data
Me.User = User Me.User = User
@@ -54,10 +56,22 @@ Namespace DownloadObjects
Private Sub New(ByVal e As EContainer) Private Sub New(ByVal e As EContainer)
If Not e Is Nothing Then If Not e Is Nothing Then
If e.Contains(Name_User) Then If e.Contains(Name_User) Then
IsSavedPosts = e.Value(Name_IsSavedPosts).FromXML(Of Boolean)(False)
Dim u As UserInfo = e(Name_User) Dim u As UserInfo = e(Name_User)
If Not u.Name.IsEmptyString And Not u.Site.IsEmptyString Then If Not u.Name.IsEmptyString And Not u.Site.IsEmptyString Then
User = Settings.GetUser(u) If Not IsSavedPosts Then
If Not User Is Nothing Then UserInfo = DirectCast(User, UserDataBase).User User = Settings.GetUser(u)
If Not User Is Nothing Then UserInfo = DirectCast(User, UserDataBase).User Else UserInfo = u
ElseIf Not u.Plugin.IsEmptyString Then
UserInfo = u
User = Settings(u.Plugin).Default.GetInstance(Download.SavedPosts, u, False, False)
If Not User Is Nothing Then
With DirectCast(User, UserDataBase)
.HostStatic = True
.IsSavedPosts = True
End With
End If
End If
End If End If
End If End If
Data = New UserMedia(e(Name_Media), User) Data = New UserMedia(e(Name_Media), User)
@@ -90,8 +104,9 @@ Namespace DownloadObjects
Data.ToEContainer, Data.ToEContainer,
New EContainer(Name_Date, AConvert(Of String)([Date], DateTimeDefaultProvider, String.Empty)), New EContainer(Name_Date, AConvert(Of String)([Date], DateTimeDefaultProvider, String.Empty)),
New EContainer(Name_Session, Session), New EContainer(Name_Session, Session),
New EContainer(Name_File, Data.File)}, New EContainer(Name_File, Data.File),
If(Not User Is Nothing, DirectCast(User, UserDataBase).User.ToEContainer, Nothing), LAP.IgnoreICopier) New EContainer(Name_IsSavedPosts, IsSavedPosts.BoolToInteger)},
If(IsSavedPosts, UserInfo.ToEContainer, If(Not User Is Nothing, DirectCast(User, UserDataBase).User.ToEContainer, Nothing)), LAP.IgnoreICopier)
End Function End Function
End Structure End Structure
Friend ReadOnly Property Files As List(Of UserMediaD) Friend ReadOnly Property Files As List(Of UserMediaD)
@@ -415,6 +430,28 @@ Namespace DownloadObjects
#Region "Thread" #Region "Thread"
Private CheckerThread As Thread Private CheckerThread As Thread
Private MissingPostsDetected As Boolean = False Private MissingPostsDetected As Boolean = False
Private _SessionSavedPosts As Integer = -1
Friend Property SessionSavedPosts As Integer
Get
If Not Working Then
Session += 1
Return Session
ElseIf _SessionSavedPosts >= 0 Then
_SessionSavedPosts += 1
Return _SessionSavedPosts
Else
_SessionSavedPosts = Session + 1
Return _SessionSavedPosts
End If
End Get
Set(ByVal NewSessionValue As Integer)
If Not Working Then
Session = NewSessionValue
Else
_SessionSavedPosts = NewSessionValue
End If
End Set
End Property
Private Session As Integer = 0 Private Session As Integer = 0
Private Sub [Start]() Private Sub [Start]()
If Not AutoDownloaderWorking AndAlso MyProgressForm.ReadyToOpen AndAlso Pool.LongCount(Function(p) p.Count > 0) > 1 Then MyProgressForm.Show() : MainFrameObj.Focus() If Not AutoDownloaderWorking AndAlso MyProgressForm.ReadyToOpen AndAlso Pool.LongCount(Function(p) p.Count > 0) > 1 Then MyProgressForm.Show() : MainFrameObj.Focus()
@@ -462,6 +499,7 @@ Namespace DownloadObjects
RaiseEvent Downloading(False) RaiseEvent Downloading(False)
FilesUpdatePendingUsers() FilesUpdatePendingUsers()
If FilesChanged Then FilesSave() : RaiseEvent FeedFilesChanged(True) If FilesChanged Then FilesSave() : RaiseEvent FeedFilesChanged(True)
If _SessionSavedPosts <> -1 Then Session = _SessionSavedPosts : _SessionSavedPosts = -1
End Try End Try
End Sub End Sub
Private Sub StartDownloading(ByRef _Job As Job) Private Sub StartDownloading(ByRef _Job As Job)

View File

@@ -239,7 +239,20 @@ CloseResume:
Case Keys.F6 : BTT_DOWN_ALL_FULL_KeyClick(Nothing, New MyKeyEventArgs(e)) Case Keys.F6 : BTT_DOWN_ALL_FULL_KeyClick(Nothing, New MyKeyEventArgs(e))
Case Else : b = NumGroup(e) Case Else : b = NumGroup(e)
End Select End Select
If Not b And e.Control And e.KeyCode = Keys.F Then MySearch.FormShow() : b = True
If Not b Then
b = True
If e.Control And e.KeyCode = Keys.F Then
MySearch.FormShow()
ElseIf e.Alt And e.KeyCode = Keys.A Then
BTT_DOWN_AUTOMATION.PerformClick()
ElseIf e.Alt And e.KeyCode = Keys.P Then
BTT_PR_INFO.PerformClick()
Else
b = False
End If
End If
If b Then e.Handled = True If b Then e.Handled = True
End Sub End Sub
Private Function NumGroup(ByVal e As KeyEventArgs) As Boolean Private Function NumGroup(ByVal e As KeyEventArgs) As Boolean
@@ -472,7 +485,11 @@ CloseResume:
End Sub End Sub
#End Region #End Region
Private Sub ShowFeed() Handles BTT_FEED.Click, BTT_TRAY_FEED_SHOW.Click Private Sub ShowFeed() Handles BTT_FEED.Click, BTT_TRAY_FEED_SHOW.Click
If MyFeed Is Nothing Then MyFeed = New DownloadFeedForm : AddHandler Downloader.FeedFilesChanged, AddressOf MyFeed.Downloader_FilesChanged If MyFeed Is Nothing Then
MyFeed = New DownloadFeedForm
AddHandler Downloader.FeedFilesChanged, AddressOf MyFeed.Downloader_FilesChanged
If Not MySavedPosts Is Nothing Then AddHandler MySavedPosts.FeedFilesChanged, AddressOf MyFeed.Downloader_FilesChanged
End If
If MyFeed.Visible Then MyFeed.BringToFront() Else MyFeed.Show() If MyFeed.Visible Then MyFeed.BringToFront() Else MyFeed.Show()
End Sub End Sub
Private Sub BTT_CHANNELS_Click(sender As Object, e As EventArgs) Handles BTT_CHANNELS.Click, BTT_TRAY_CHANNELS.Click Private Sub BTT_CHANNELS_Click(sender As Object, e As EventArgs) Handles BTT_CHANNELS.Click, BTT_TRAY_CHANNELS.Click
@@ -487,6 +504,7 @@ CloseResume:
If MySavedPosts Is Nothing Then If MySavedPosts Is Nothing Then
MySavedPosts = New DownloadSavedPostsForm MySavedPosts = New DownloadSavedPostsForm
AddHandler MySavedPosts.DownloadDone, AddressOf MainFrameObj.ShowNotification AddHandler MySavedPosts.DownloadDone, AddressOf MainFrameObj.ShowNotification
If Not MyFeed Is Nothing Then AddHandler MySavedPosts.FeedFilesChanged, AddressOf MyFeed.Downloader_FilesChanged
End If End If
With MySavedPosts With MySavedPosts
If .Visible Then .BringToFront() Else .Show() If .Visible Then .BringToFront() Else .Show()
@@ -910,7 +928,7 @@ CloseResume:
#Region "2 - user parameters" #Region "2 - user parameters"
Private Sub BTT_CONTEXT_FAV_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_FAV.Click Private Sub BTT_CONTEXT_FAV_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_FAV.Click
Dim users As List(Of IUserData) = GetSelectedUserArray() Dim users As List(Of IUserData) = GetSelectedUserArray()
If AskForMassReplace(users, "Favorite") Then users.ForEach(Sub(u) If AskForMassReplace(users, "Favorite") Then users.ForEach(Sub(ByVal u As IUserData)
u.Favorite = Not u.Favorite u.Favorite = Not u.Favorite
u.UpdateUserInformation() u.UpdateUserInformation()
UserListUpdate(u, False) UserListUpdate(u, False)
@@ -918,7 +936,7 @@ CloseResume:
End Sub End Sub
Private Sub BTT_CONTEXT_TEMP_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_TEMP.Click Private Sub BTT_CONTEXT_TEMP_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_TEMP.Click
Dim users As List(Of IUserData) = GetSelectedUserArray() Dim users As List(Of IUserData) = GetSelectedUserArray()
If AskForMassReplace(users, "Temporary") Then users.ForEach(Sub(u) If AskForMassReplace(users, "Temporary") Then users.ForEach(Sub(ByVal u As IUserData)
u.Temporary = Not u.Temporary u.Temporary = Not u.Temporary
u.UpdateUserInformation() u.UpdateUserInformation()
UserListUpdate(u, False) UserListUpdate(u, False)
@@ -928,7 +946,7 @@ CloseResume:
Dim users As List(Of IUserData) = GetSelectedUserArray() Dim users As List(Of IUserData) = GetSelectedUserArray()
If AskForMassReplace(users, "Ready for download") Then If AskForMassReplace(users, "Ready for download") Then
Dim r As Boolean = MsgBoxE({"What state do you want to set for selected users", "Select ready state"}, vbQuestion,,, {"Not Ready", "Ready"}).Index Dim r As Boolean = MsgBoxE({"What state do you want to set for selected users", "Select ready state"}, vbQuestion,,, {"Not Ready", "Ready"}).Index
users.ForEach(Sub(u) users.ForEach(Sub(ByVal u As IUserData)
u.ReadyForDownload = r u.ReadyForDownload = r
u.UpdateUserInformation() u.UpdateUserInformation()
End Sub) End Sub)
@@ -1549,7 +1567,7 @@ CloseResume:
Friend Function GetUserListProvider(ByVal WithCollections As Boolean) As IFormatProvider Friend Function GetUserListProvider(ByVal WithCollections As Boolean) As IFormatProvider
If WithCollections Then If WithCollections Then
If OperationsUserListProviderCollections Is Nothing Then _ If OperationsUserListProviderCollections Is Nothing Then _
OperationsUserListProviderCollections = New CustomProvider(Function(v, d, p, n, ee) OperationsUserListProviderCollections = New CustomProvider(Function(ByVal v As Object) As Object
Dim OutStr$ Dim OutStr$
With DirectCast(v, IUserData) With DirectCast(v, IUserData)
If .IsCollection Then If .IsCollection Then
@@ -1563,7 +1581,7 @@ CloseResume:
Return OperationsUserListProviderCollections Return OperationsUserListProviderCollections
Else Else
If OperationsUserListProvider Is Nothing Then _ If OperationsUserListProvider Is Nothing Then _
OperationsUserListProvider = New CustomProvider(Function(v, d, p, n, ee) $"[{DirectCast(v, IUserData).Site}] {DirectCast(v, IUserData).Name}") OperationsUserListProvider = New CustomProvider(Function(v) $"[{DirectCast(v, IUserData).Site}] {DirectCast(v, IUserData).Name}")
Return OperationsUserListProvider Return OperationsUserListProvider
End If End If
End Function End Function

View File

@@ -151,7 +151,9 @@ Friend Module MainMod
Friend Sub CheckVersion(ByVal Force As Boolean) Friend Sub CheckVersion(ByVal Force As Boolean)
With Settings With Settings
If .CheckUpdatesAtStart Or Force Then If .CheckUpdatesAtStart Or Force Then
ShowProgramInfo(.ProgramText.Value.IfNullOrEmpty("SCrawler"), My.Application.Info.Version, True, Force, .Self, False, ShowProgramInfo(.ProgramText.Value.IfNullOrEmpty("SCrawler"),
SCrawler.Shared.GetCurrentMaxVer(Application.StartupPath.CSFileP).IfNullOrEmpty(My.Application.Info.Version),
True, Force, .Self, False,
.LatestVersion.Value, .ShowNewVersionNotification.Value, .ProgramDescription) .LatestVersion.Value, .ShowNewVersionNotification.Value, .ProgramDescription)
End If End If
End With End With

View File

@@ -32,6 +32,6 @@ Imports System.Runtime.InteropServices
' by using the '*' as shown below: ' by using the '*' as shown below:
' <Assembly: AssemblyVersion("1.0.*")> ' <Assembly: AssemblyVersion("1.0.*")>
<Assembly: AssemblyVersion("2023.11.24.0")> <Assembly: AssemblyVersion("2023.12.7.0")>
<Assembly: AssemblyFileVersion("2023.11.24.0")> <Assembly: AssemblyFileVersion("2023.12.7.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -8,7 +8,7 @@
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Forms.Toolbars
Friend Class PreProgress : Implements IDisposable Friend Class PreProgress : Implements IDisposable
Private ReadOnly Progress As MyProgressExt = Nothing Friend ReadOnly Progress As MyProgressExt = Nothing
Private ReadOnly ProgressExists As Boolean = False Private ReadOnly ProgressExists As Boolean = False
Private ReadOnly Property Ready As Boolean Private ReadOnly Property Ready As Boolean
Get Get

View File

@@ -120,7 +120,10 @@ Namespace Plugin.Hosts
Instance.DownloadSingleObject(If(ExternalSource, Me), Token) Instance.DownloadSingleObject(If(ExternalSource, Me), Token)
ExchangeData(ExternalSource, Me) ExchangeData(ExternalSource, Me)
Dim __url$ = DirectCast(Me, IDownloadableMedia).URL_BASE.IfNullOrEmpty(URL) Dim __url$ = DirectCast(Me, IDownloadableMedia).URL_BASE.IfNullOrEmpty(URL)
If File.Exists And Not __url.IsEmptyString And MyDownloaderSettings.CreateUrlFiles Then CreateUrlFile(__url, File) If File.Exists And Not __url.IsEmptyString And MyDownloaderSettings.CreateUrlFiles Then
Dim urlFile As SFile = CreateUrlFile(__url, File)
If urlFile.Exists Then Files.Add(urlFile)
End If
If Not ExternalSource Is Nothing Then If Not ExternalSource Is Nothing Then
With ExternalSource : _HasError = .HasError : _Exists = .Exists : End With With ExternalSource : _HasError = .HasError : _Exists = .Exists : End With
End If End If

View File

@@ -260,7 +260,7 @@ Namespace Plugin.Hosts
Source.BeginInit() Source.BeginInit()
Dim Members As IEnumerable(Of MemberInfo) = GetObjectMembers(Plugin,,, True, New MembersDistinctComparer) 'Plugin.GetType.GetTypeInfo.DeclaredMembers Dim Members As IEnumerable(Of MemberInfo) = GetObjectMembers(Plugin,,, True, New MembersDistinctComparerExtended) 'Plugin.GetType.GetTypeInfo.DeclaredMembers
_ResponserIsContainer = TypeOf Plugin Is IResponserContainer _ResponserIsContainer = TypeOf Plugin Is IResponserContainer
If Members.ListExists Then If Members.ListExists Then

View File

@@ -636,6 +636,10 @@
<Project>{d4650f6b-5a54-44b6-999b-6c675b7116b1}</Project> <Project>{d4650f6b-5a54-44b6-999b-6c675b7116b1}</Project>
<Name>SCrawler.PluginProvider</Name> <Name>SCrawler.PluginProvider</Name>
</ProjectReference> </ProjectReference>
<ProjectReference Include="..\SCrawler.Shared\SCrawler.Shared.vbproj">
<Project>{dc634700-24c7-42dd-bf8f-87e6cc54e625}</Project>
<Name>SCrawler.Shared</Name>
</ProjectReference>
<ProjectReference Include="..\SCrawler.YouTube\SCrawler.YouTube.vbproj"> <ProjectReference Include="..\SCrawler.YouTube\SCrawler.YouTube.vbproj">
<Project>{7c764707-7fd1-469c-a365-94605c193607}</Project> <Project>{7c764707-7fd1-469c-a365-94605c193607}</Project>
<Name>SCrawler.YouTube</Name> <Name>SCrawler.YouTube</Name>

View File

@@ -136,6 +136,8 @@ Friend Class SettingsCLS : Implements IDownloaderSettings, IDisposable
Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt" Private ReadOnly BlackListFile As SFile = $"{SettingsFolderName}\BlackList.txt"
Private ReadOnly UsersSettingsFile As SFile = $"{SettingsFolderName}\Users.xml" Private ReadOnly UsersSettingsFile As SFile = $"{SettingsFolderName}\Users.xml"
Friend Sub New() Friend Sub New()
CheckNewReleaseFolder()
Cache = CacheKeeper.Default Cache = CacheKeeper.Default
Cache.DisposeSuspended = True Cache.DisposeSuspended = True