Compare commits

...

5 Commits

Author SHA1 Message Date
Andy
bdc7321331 2022.11.16.0
Add sites: PornHub, XHamster
Add saved xvideos posts downloading
PluginProvider: added TaskGroup attribute; added IUserMedia inteface; changed PluginUserMedia to IUserMedia in interface declarations; changed 'User' String to IPluginContentProvider in ISiteSettings sinterface
Added update the 'LOG' button at the end of the ProfileSaved download function
API.Base: added 'IUserMedia' compatibility for 'UserMedia'; moved 'GetImage' from 'UserPost' to 'ChannelsViewForm'; update constants in UserDataBase; updated UserDataBase to new UserInfo environment.
API.Instagram.UserData: fixed date issue
API.Reddit.SiteSettings: update user patterns
API.Twitter.Declarations: moved provider here from MainFrame
UserDataBind: updated to new UserInfo environment
ActiveDownloadingProgress: updated form rendering
AutoDownloader: added SpecialDelay
TDownloader: added 'Suspended' option; updated for TaskGroups
CollectionEditorForm: fixed order bug
LabelsForm: remove old stuff
UserEditorForm: added collection editing
MainFrame: improve label selection
Add import users
Added the ability to create a virtual collection and add a virtual user to a real collection
SettingsCLS: improve users loading
2022-11-16 13:41:45 +03:00
Andy
7d169acebc 2022.10.23.0
PluginProvider: added 'DoNotUse' attribute.
Channels: copying a channel to the 'ChannelsDeleted' folder before deleting.
Twitter: updated status codes.
AutoDownloader: removed base parameters initialization; updated 'ToEContainer' function; 'StartupDelay' default value = 1; added IIndexable; fixed NextExecutionDate; added task delay based on index and tasks count; updated user selections algorithms.
MainFrame: coloring the button 'Download All' depending on the pause; updated user selections algorithms.
DownloadGroups: added LabelsExcluded, Sites and SitesExcluded; updated initialization; updated ToEContainer function; updated user selections algorithms
IGroup, GroupParameters: added LabelsExcluded, Sites and SitesExcluded; added Import and Export functions
Removed TrayIcon notifications. All notifications are now ToastNotifications.
SettingsHost: 'DoNotUse' attribute
Settings: added 'GetUsers' predicate function
2022-10-23 16:39:01 +03:00
Andy
f5c156b8e5 2022.10.18.0
Moved UserMedia xml initialization to the structure itself
Added download with feed skip
Added silent mode (temporary disabling notifications)
Added additional Instagram protection
Excluding users whose profiles do not exist from downloading with groups and AutoDownloader
Feed: delete file bugs; reorder data after file deletion; video playback bugs
SiteSettingsForm: enable 'OK' button when editing cookies
Fixed collection users ban
Settings: disabling ffmpeg missing notification; advanced notification management
Added 'ToolStripKeyMenuItem' control
Plugins: deprecated XVIDEOS and LPSG plugin libraries; moved them to SCrawler.
Updated license
PluginProvider: added 'BeginEdit' and 'EndEdit' function to ISiteSettings; changed GetSpecialData (ISiteSettings) return type to IEnumerable
PluginsEnvironment: removed 'IsMyClass' attribute
MainFrame: grouped all download buttons into one menu; reorganized code; removed 'F2' hotkey
AutoDownloader: added advanced pause options; added buttons to tray icon and AutoDownloader form
MissingPosts: finished; activated functions that were disabled; added download functions to UserData classes
UserDataBase: ability to use responser; ability to download m3u8; extended 'DownloadingException' with optional argument 'EObj'; user index in collection (button tag) changed to user instance; extended information with user labels; updated 'ProcessException' function
Replaced download buttons with 'KeyClick' control
Replaced FDatePickerForm with my library's form
Collections: Deleting multiple collections - disabled confirmation; ban each user in collection
2022-10-18 12:05:31 +03:00
Andy
d91ee72eaa Update LICENSE 2022-10-18 12:02:43 +03:00
Andy
4e9de23b60 Deprecated
Moved to SCrawler
2022-10-18 11:54:50 +03:00
256 changed files with 10272 additions and 12208 deletions

2
.gitignore vendored
View File

@@ -34,7 +34,9 @@ bld/
[Ll]og/ [Ll]og/
[Ll]ogs/ [Ll]ogs/
ffmpeg/ ffmpeg/
cURL/
Info/ Info/
Hidden/
# Visual Studio 2015/2017 cache/options directory # Visual Studio 2015/2017 cache/options directory
.vs/ .vs/

View File

@@ -8,7 +8,6 @@ I welcome requests! Follow these steps to contribute:
1. If you have a code change suggestion, you can post a replacement code block. I also accept pull requests. 1. If you have a code change suggestion, you can post a replacement code block. I also accept pull requests.
# How to build from source # How to build from source
1. Delete the "PersonalUtilities" project from the solution. 1. Delete the "PersonalUtilities" project from the solution.
1. Delete the "PersonalUtilities.Notifications" project from the solution. 1. Delete the "PersonalUtilities.Notifications" project from the solution.
1. The following libraries must be added to project references with the '**Copy to output folder**' option: 1. The following libraries must be added to project references with the '**Copy to output folder**' option:
@@ -21,7 +20,6 @@ I welcome requests! Follow these steps to contribute:
**Always use the correct libraries. You must download libraries from the same release date as the code commit date.** **Always use the correct libraries. You must download libraries from the same release date as the code commit date.**
# How to request a new site # How to request a new site
1. Check [issues](https://github.com/AAndyProgram/SCrawler/issues) (open and [closed](https://github.com/AAndyProgram/SCrawler/issues?q=is%3Aissue+is%3Aclosed)) and [discussions](https://github.com/AAndyProgram/SCrawler/discussions) to find your issue. Perhaps I have already answered your request. 1. Check [issues](https://github.com/AAndyProgram/SCrawler/issues) (open and [closed](https://github.com/AAndyProgram/SCrawler/issues?q=is%3Aissue+is%3Aclosed)) and [discussions](https://github.com/AAndyProgram/SCrawler/discussions) to find your issue. Perhaps I have already answered your request.
1. If you don't find anything, create a new issue with your request. I usually reply as soon as possible (within the next few hours). 1. If you don't find anything, create a new issue with your request. I usually reply as soon as possible (within the next few hours).
@@ -38,16 +36,5 @@ I welcome requests! Follow these steps to contribute:
If I'm interested in a site you want to add, it may be added in future releases. If I'm interested in a site you want to add, it may be added in future releases.
# Sites I will never develop # Sites I will never develop
- Facebook - Facebook
- Tumblr
# Sites requested by users
- TikTok
- API for receiving data without authorization was not found. Therefore, I don't have time to start developing this site parsing algorithm. If anyone knows of requests that may collect data without OAuth authentication, please let me know.
# Contact me
[![matrix](https://img.shields.io/badge/Matrix-%40andyprogram%3Amatrix.org-informational)](https://matrix.to/#/@andyprogram:matrix.org)
[![discord](https://img.shields.io/badge/discord-AndyProgram%233804-yellowgreen)](https://discordapp.com/users/1012768226679206009) AndyProgram#3804

View File

@@ -1,3 +1,91 @@
# 2022.11.16.0
*2022-11-16*
**ATTENTION! This version makes changes to the base SCrawler user configuration file. Since you started using this version, you still can downgrade. BUT! Once you add a virtual collection or a virtual user to a collection, you won't be able to downgrade without losing data.**
- Added
- **PornHub**
- **XHamster**
- An ability to download saved XVIDEOS posts
- Download indicator. While downloading, the rainbow tray icon changed to a blue arrow.
- Collections: the ability to edit a collection using a form
- Collections: the ability to create a **`virtual collection`** and add a **`virtual user`** to a real collection
- Collections: an easier way to added users to a collection
- Collections: an easier way to create collections
- Added icons for channels form context menu buttons
- More convenient change of user labels from the context menu of the user list
- Notifications: complete transition from default notifications to ToastNotifications
- Notifications: when you click on the notification that some of the channels are downloaded, the channels form opens
- Notifications: when you click on the notification that all users are downloaded, the main window form opens
- Notifications: when you click on the notification that the saved posts are downloaded, the saved posts form opens
- Import users
- Minor improvements
- Plugins
- Added
- `TaskGroup` attribute
- `IUserMedia` interface
- Changed
- `GetUserUrl` and `GetUserPostUrl` functions: `String UserName` and `String UserID` changed to ` IPluginContentProvider User`
- Fixed
- Collections editor: new added collections are still not added to the top of the collections list
- Users search form doesn't remember last size
- Minor bugs
# 2022.10.23.0
*2022-10-23*
- Added
- RedGifs token Auto-Renewal
- Download groups: ability to select sites
- Download groups: ability to exclude labels and sites
- AutoDownloader: ability to exclude labels and sites in ```All```, ```Default``` and ```Specified``` modes
- The ```Download All``` button turns blue when pause is enabled
- Updated Twitter status codes
- Minor improvements
- Fixed
- Updated Twitter status codes
- AutoDownloader: incorrect next run date in scheduler task information
- AutoDownloader: minor bugs
- (Issue #69) **RedGifs data is not downloading**. Requires token.
- Minor bugs
# 2022.10.18.0
*2022-10-18*
- Added
- **TikTok** ([limited](https://github.com/AAndyProgram/SCrawler/wiki/Settings#tiktok-limits))
- **Search form** (```Ctrl+F```)
- Feed improvements
- Ability to save the download session for viewing later
- Ability to download user, excluding from the feed (use the ```Ctrl``` key with a button click of with a hot key press)
- Ability to disable the notification about the absence of the ffmpeg.exe file
- Extended user information with labels
- Advanced AutoDownloader pause options
- Added pause buttons to tray icon and AutoDownloader form
- Additional Instagram protection
- Advanced notification management
- Silent mode (temporarily disable notification)
- Excluding users whose profiles do not exist from downloading with groups and AutoDownloader
- Minor improvements
- Updated
- Grouped all download buttons into one menu
- **Finished missing posts**. You can now download missing posts if they exist.
- PluginProvider: added ```BeginEdit``` and ```EndEdit``` methods
- PluginProvider: ```GetSpecialData``` return type changed from ```IEnumerable(Of PluginUserMedia)``` to ```IEnumerable```
- XVIDEOS and LPSG plugins are moved from libraries to SCrawler
- Fixed
- (Issue #69) **RedGifs data is not downloading**. Requires cookies and token.
- Some minor bugs when deleting a collection
- Feed: start video playing may cause the program to freeze (strange behavior of the vlc library)
- Feed: videos hosted on Reddit not showing up in feed
- Feed: minor bugs
- Collection users were not banned when deleted with the ban option
- When trying to delete multiple collections, each collection asked for confirmation to delete
- Minor bugs
# 2022.9.24.0 # 2022.9.24.0
*2022-09-24* *2022-09-24*

22
FAQ.md
View File

@@ -18,7 +18,7 @@ A: https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies
#### Q: **I can't copy cookies.** #### Q: **I can't copy cookies.**
A: Use the mouse. Don't use ```Ctrl``` + ```A```! A: Use the mouse. Don't use ```Ctrl+A```!
---- ----
@@ -44,6 +44,8 @@ A: How to request a new site you can read [here](CONTRIBUTING.md#how-to-request-
A: Check your credentials. Both of these sites require cookies. Check your [Twitter tokens](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-twitter-tokens) and [Instagram settings](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram-settings). If all settings are set, but nothing works, go to [create a new issue](https://github.com/AAndyProgram/SCrawler/issues). Don't forget to attach the LOG. A: Check your credentials. Both of these sites require cookies. Check your [Twitter tokens](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-twitter-tokens) and [Instagram settings](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram-settings). If all settings are set, but nothing works, go to [create a new issue](https://github.com/AAndyProgram/SCrawler/issues). Don't forget to attach the LOG.
**[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**
---- ----
#### Q: **I have set credentials but still nothing is downloading** #### Q: **I have set credentials but still nothing is downloading**
@@ -94,6 +96,24 @@ A: Just add that user back to the program. In the dialog box that opens, click o
---- ----
#### Q: **Why don't you answer how it works**
A: Because **I don't want to**. I don't want to waste my time explaining things that are already covered in the **[GUIDE](https://github.com/AAndyProgram/SCrawler/wiki)**! If you didn't bother to read the guide, why would I waste my time?! ALL FUNCTIONALITY IS DESCRIBED IN THE GUIDE. Before publishing a new release, I update the guide. If you don't respect my work, I don't waste my time.
----
#### Q: **You lost me. Your program is too complicated.**
A: **I'm fine with that**. If the program is difficult for you or you can't configure it, I can only suggest you find another (easier) program. I really don't mind! The program is free. I am develop SCrawler for myself and publish on GitHub because people found my program useful. If someone can't use it or doesn't like it, I'm fine.
----
#### Q: **I can't configure something**
A: I can only [suggest](#q-you-lost-me-your-program-is-too-complicated) you find another (easier) program.
----
#### 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**! I will not do it. 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**! I will not do it. 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.

View File

@@ -1,7 +1,7 @@
Your support is very valuable to me. Any support is greatly appreciated. Your support encourages me to make new features, update the program, add new sites, etc. Your support is very valuable to me. Any support is greatly appreciated. Your support encourages me to make new features, update the program, add new sites, etc.
You can support the program by: You can support the program by:
- **Bitcoin**: bitcoin:BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET - **Bitcoin**: BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET
- :heavy_dollar_sign: make a donation on this site: https://ko-fi.com/andyprogram - :heavy_dollar_sign: make a donation on this site: https://ko-fi.com/andyprogram
- :repeat: make a post about my program on your profile (Reddit, Twitter, Instagram and any other social networks) - :repeat: make a post about my program on your profile (Reddit, Twitter, Instagram and any other social networks)
- :speech_balloon: tell your friends about the program - :speech_balloon: tell your friends about the program

View File

@@ -1,6 +1,4 @@
List of available plugins: List of available plugins:
- LPSG
- XVIDEOS
Tools: Tools:
- [image2post](https://github.com/unknown81311/SCrawler-image2post) by @unknown81311: **get reddit post URL from file.** - [image2post](https://github.com/unknown81311/SCrawler-image2post) by @unknown81311: **get reddit post URL from file.**

Binary file not shown.

Before

Width:  |  Height:  |  Size: 574 KiB

After

Width:  |  Height:  |  Size: 483 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.4 KiB

After

Width:  |  Height:  |  Size: 9.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 370 KiB

After

Width:  |  Height:  |  Size: 363 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 381 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.7 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 16 KiB

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 15 KiB

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 13 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.6 KiB

View File

@@ -68,7 +68,7 @@ https://github.com/RipMeApp/ripme
| **Free options** | The program is completely free | The program is completely free, but site limits are not declared | | **Free options** | The program is completely free | The program is completely free, but site limits are not declared |
| Operating Systems | Windows 10+ | Windows, MacOS, Linux | | Operating Systems | Windows 10+ | Windows, MacOS, Linux |
| Select want content type to download | Yes | Yes | | Select want content type to download | Yes | Yes |
| Suported sites | 6 internal and any site using plugins | 86+ sites (declared) | | Suported sites | 9 internal and any site using plugins | 86+ sites (declared) |
| Other sites support | **Yes** | No | | Other sites support | **Yes** | No |
| Still supported | **Yes** | **No (last release date May 4, 2021)** | | Still supported | **Yes** | **No (last release date May 4, 2021)** |

View File

@@ -6,7 +6,7 @@
[![GUIDE](https://img.shields.io/badge/GUIDE-green)](https://github.com/AAndyProgram/SCrawler/wiki) [![GUIDE](https://img.shields.io/badge/GUIDE-green)](https://github.com/AAndyProgram/SCrawler/wiki)
[![How to support](https://img.shields.io/badge/HowToSupport-green)](HowToSupport.md) [![How to support](https://img.shields.io/badge/HowToSupport-green)](HowToSupport.md)
A program to download photo and video from [any site](#supported-sites) (e.g. Reddit, Twitter, Instagram). A program to download photo and video from [any site](#supported-sites) (e.g. Reddit, Twitter, Instagram, TikTok, RedGifs, PornHub, XHamster, XVIDEOS, LPSG).
**If you like SCrawler, please like the program on [this site]( https://alternativeto.net/software/scrawler/about/)** **If you like SCrawler, please like the program on [this site]( https://alternativeto.net/software/scrawler/about/)**
@@ -14,23 +14,23 @@ Do you like this program? Consider adding to my coffee fund by making a donation
[![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/andyprogram) [![ko-fi](https://www.ko-fi.com/img/githubbutton_sm.svg)](https://ko-fi.com/andyprogram)
**Bitcoin**: bitcoin:BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET **Bitcoin**: BC1Q0NH839FT5TA44DD7L7RLR97XDQAG9V8D6N7XET
![Main window](ProgramScreenshots/MainWindow.png) ![Main window](ProgramScreenshots/MainWindow.png)
![Channels window](ProgramScreenshots/Channels.png) ![Channels window](ProgramScreenshots/Channels.png)
# 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:
- Reddit images; - Reddit images, galleries of images, videos (downloading Reddit hosted video is going through ffmpeg (**ffmpeg only works with the x64 program**)), saved posts;
- Reddit galleries of images;
- Reddit videos (downloading Reddit hosted video is going through ffmpeg (**ffmpeg only works with the x64 program**));
- Redgifs videos (https://www.redgifs.com/); - Redgifs videos (https://www.redgifs.com/);
- Twitter images and videos; - Twitter images and videos, saved (bookmarked) posts;
- Instagram images and videos; - Instagram images and videos, tagged posts, stories, saved posts;
- Instagram tagged posts; - TikTok videos ([limited](https://github.com/AAndyProgram/SCrawler/wiki/Settings#tiktok-limits));
- Instagram stories;
- Imgur images, galleries and videos; - Imgur images, galleries and videos;
- Gfycat videos; - Gfycat videos;
- PornHub images, videos, save (liked) posts;
- XHamster images, videos, saved posts;
- XVIDEOS videos;
- [Other](#supported-sites) supported sites - [Other](#supported-sites) supported sites
- Parse [channel and view data](https://github.com/AAndyProgram/SCrawler/wiki/Channels) - Parse [channel and view data](https://github.com/AAndyProgram/SCrawler/wiki/Channels)
- Download [saved Reddit, Twitter and Instagram posts](https://github.com/AAndyProgram/SCrawler/wiki/Home#saved-posts) - Download [saved Reddit, Twitter and Instagram posts](https://github.com/AAndyProgram/SCrawler/wiki/Home#saved-posts)
@@ -55,14 +55,19 @@ Do you like this program? Consider adding to my coffee fund by making a donation
- **Reddit** - **Reddit**
- **Twitter** - **Twitter**
- **Instagram** - **Instagram**
- **TikTok** ([limited](https://github.com/AAndyProgram/SCrawler/wiki/Settings#tiktok-limits))
- RedGifs - RedGifs
- Imgur - Imgur
- Gfycat - Gfycat
- LPSG - LPSG
- XVIDEOS - **PornHub**
- **XHamster**
- **XVIDEOS**
- [Other sites](Plugins.md) - [Other sites](Plugins.md)
# How does it works: **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**
# How it works
First, the program downloads the full profile. After the program downloads only new posts. The program remembers downloaded posts. First, the program downloads the full profile. After the program downloads only new posts. The program remembers downloaded posts.
@@ -74,8 +79,6 @@ The program parses all user posts, obtain MD5 images hash and compares them wit
The program parses all user posts and compares file names with existing ones to remove duplicates. Then the media will be downloaded. The program parses all user posts and compares file names with existing ones to remove duplicates. Then the media will be downloaded.
You can read about Instagram restrictions [here](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram-limits)
## How to request a new site ## How to request a new site
Read [here](CONTRIBUTING.md#how-to-request-a-new-site) about Read [here](CONTRIBUTING.md#how-to-request-a-new-site) about
@@ -83,12 +86,35 @@ Read [here](CONTRIBUTING.md#how-to-request-a-new-site) about
# Requirements # Requirements
- Windows 10, 11 with NET Framework 4.6.1 or higher (v4.6.1 must be installed). You can check version compatibility with this [tool](Tools/NET.FrameworkVersion.ps1). - Windows 10, 11 with NET Framework 4.6.1 or higher (v4.6.1 must be installed). You can check version compatibility with this [tool](Tools/NET.FrameworkVersion.ps1).
- Authorization [cookies](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies) and [tokens](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-twitter-tokens) for Twitter (if you want to download data from Twitter) - **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**
- Authorization [cookies](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies) and [Hash](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram) for Instagram (if you want to download data from Instagram), [Hash 2](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-instagram-hash-2) for saved Instagram posts, Instagram [stories authorization headers](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-instagram-stories-authorization-headers) for Stories and Tagged data
- ffmpeg library for downloading videos hosted on Reddit (you can download it from the [official repo](https://github.com/GyanD/codexffmpeg/releases/tag/2021-01-12-git-ca21cb1e36) or [from my first release](https://github.com/AAndyProgram/SCrawler/releases/download/1.0.0.0/ffmpeg.zip)). **ffmpeg only works with the x64 version of the program.**
# Guide # Guide
- [Main window](https://github.com/AAndyProgram/SCrawler/wiki)
- [Users](https://github.com/AAndyProgram/SCrawler/wiki/Users)
- [Add/Edit/Delete users](https://github.com/AAndyProgram/SCrawler/wiki/Users)
- [Collections](https://github.com/AAndyProgram/SCrawler/wiki#collections)
- [User operations](https://github.com/AAndyProgram/SCrawler/wiki#context-menu)
- [User labels](https://github.com/AAndyProgram/SCrawler/wiki/Users#labels)
- **[DOWNLOAD](https://github.com/AAndyProgram/SCrawler/wiki#download)**
- [Automation](https://github.com/AAndyProgram/SCrawler/wiki/Settings#automation)
- [Download groups](https://github.com/AAndyProgram/SCrawler/wiki/Settings#download-groups)
- [Downloading information](https://github.com/AAndyProgram/SCrawler/wiki#info)
- [Reddit channels](https://github.com/AAndyProgram/SCrawler/wiki/Channels)
- [Saved posts](https://github.com/AAndyProgram/SCrawler/wiki#saved-posts)
- [View modes, filters](https://github.com/AAndyProgram/SCrawler/wiki#view)
- **[SETTINGS](https://github.com/AAndyProgram/SCrawler/wiki/Settings)**
- **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**
- [Reddit](https://github.com/AAndyProgram/SCrawler/wiki/Settings#reddit)
- [Twitter](https://github.com/AAndyProgram/SCrawler/wiki/Settings#twitter)
- [Instagram](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram)
- [TikTok](https://github.com/AAndyProgram/SCrawler/wiki/Settings#tiktok)
- [RedGifs](https://github.com/AAndyProgram/SCrawler/wiki/Settings#redgifs)
- [PornHub](https://github.com/AAndyProgram/SCrawler/wiki/Settings#pornhub)
- [XHamster](https://github.com/AAndyProgram/SCrawler/wiki/Settings#xhamster)
- [XVIDEOS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#xvideos)
- [LPSG](https://github.com/AAndyProgram/SCrawler/wiki/Settings#lpsg)
**Full guide you can find [here](https://github.com/AAndyProgram/SCrawler/wiki)** **Full guide you can find [here](https://github.com/AAndyProgram/SCrawler/wiki)**
# Installation # Installation
@@ -117,22 +143,9 @@ Read more about how to support the program [here](HowToSupport.md).
The program has an intuitive interface. The program has an intuitive interface.
You need to set up authorization for Twitter and Instagram: **[SITES REQUIREMENTS](https://github.com/AAndyProgram/SCrawler/wiki/Settings#sites-requirements)**
- Authorization [cookies](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies) and [tokens](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-twitter-tokens) for **Twitter** (if you want to download data from Twitter)
- Authorization [cookies](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-set-up-cookies), [Hash](https://github.com/AAndyProgram/SCrawler/wiki/Settings#instagram) and [authorization headers](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-instagram-authorization-headers) for **Instagram** (if you want to download data from Instagram), [Hash 2](https://github.com/AAndyProgram/SCrawler/wiki/Settings#how-to-find-instagram-hash-2) for **saved Instagram posts**
Just add a user profile and **click the ```Start downloading``` button**. Just add a user profile and **click the ```Download``` button**.
You can add users by patterns:
- https://www.instagram.com/SomeUserName
- https://twitter.com/SomeUserName
- https://reddit.com/user/SomeUserName
- https://reddit.com/r/SomeSubredditName
- https://www.redgifs.com/users/SomeUserName
- u/SomeUserName
- r/SomeSubredditName
- SomeUserName (in this case, you need to select the user's site)
- SomeSubredditName
Read more about adding users and subreddits [here](https://github.com/AAndyProgram/SCrawler/wiki/Users) Read more about adding users and subreddits [here](https://github.com/AAndyProgram/SCrawler/wiki/Users)
@@ -144,10 +157,4 @@ Create a shortcut for the program. Open shortcut properties. In the ```Shortcut`
Example: ```D:\Programs\SCrawler\SCrawler.exe v``` Example: ```D:\Programs\SCrawler\SCrawler.exe v```
![Separate video downloader](ProgramScreenshots/SeparateVideoDownloader.png) ![Separate video downloader](ProgramScreenshots/SeparateVideoDownloader.png)
# Contact me
[![matrix](https://img.shields.io/badge/Matrix-%40andyprogram%3Amatrix.org-informational)](https://matrix.to/#/@andyprogram:matrix.org)
[![discord](https://img.shields.io/badge/discord-AndyProgram%233804-yellowgreen)](https://discordapp.com/users/1012768226679206009) AndyProgram#3804

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("2022.9.17.0")> <Assembly: AssemblyVersion("2022.10.12.0")>
<Assembly: AssemblyFileVersion("2022.9.17.0")> <Assembly: AssemblyFileVersion("2022.10.12.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -54,6 +54,10 @@ Public Class SiteSettings : Implements ISiteSettings
End Sub End Sub
#End Region #End Region
#Region "Update" #Region "Update"
Public Sub BeginEdit() Implements ISiteSettings.BeginEdit
End Sub
Public Sub EndEdit() Implements ISiteSettings.EndEdit
End Sub
Public Sub BeginUpdate() Implements ISiteSettings.BeginUpdate Public Sub BeginUpdate() Implements ISiteSettings.BeginUpdate
End Sub End Sub
Public Sub EndUpdate() Implements ISiteSettings.EndUpdate Public Sub EndUpdate() Implements ISiteSettings.EndUpdate
@@ -92,7 +96,7 @@ Public Class SiteSettings : Implements ISiteSettings
Return Nothing Return Nothing
End Function End Function
Public Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available Public Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available
Return True Return If(Responser.Cookies?.Count, 0) > 0
End Function End Function
Public Function ReadyToDownload(ByVal What As ISiteSettings.Download) As Boolean Implements ISiteSettings.ReadyToDownload Public Function ReadyToDownload(ByVal What As ISiteSettings.Download) As Boolean Implements ISiteSettings.ReadyToDownload
Return True Return True

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("2022.9.24.0")> <Assembly: AssemblyVersion("2022.10.12.0")>
<Assembly: AssemblyFileVersion("2022.9.24.0")> <Assembly: AssemblyFileVersion("2022.10.12.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -97,6 +97,10 @@ Public Class SiteSettings : Implements ISiteSettings
Public Sub DownloadDone(ByVal What As ISiteSettings.Download) Implements ISiteSettings.DownloadDone Public Sub DownloadDone(ByVal What As ISiteSettings.Download) Implements ISiteSettings.DownloadDone
End Sub End Sub
#End Region #End Region
Public Sub BeginEdit() Implements ISiteSettings.BeginEdit
End Sub
Public Sub EndEdit() Implements ISiteSettings.EndEdit
End Sub
Public Sub BeginUpdate() Implements ISiteSettings.BeginUpdate Public Sub BeginUpdate() Implements ISiteSettings.BeginUpdate
End Sub End Sub
Public Sub EndUpdate() Implements ISiteSettings.EndUpdate Public Sub EndUpdate() Implements ISiteSettings.EndUpdate

View File

@@ -1,3 +1,3 @@
[*.vb] [*.vb]
# Modifier preferences # Modifier preferences
file_header_template = Copyright (C) 2022 Andy\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/> file_header_template = Copyright (C) 2023 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

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -53,6 +53,9 @@ Namespace Plugin.Attributes
ElementName = XMLElementName ElementName = XMLElementName
End Sub End Sub
End Class End Class
''' <summary>Attribute to disable some properties for host use</summary>
<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class DoNotUse : Inherits Attribute
End Class
''' <summary>Special property updater</summary> ''' <summary>Special property updater</summary>
<AttributeUsage(AttributeTargets.Method, AllowMultiple:=True, Inherited:=False)> Public NotInheritable Class PropertyUpdater : Inherits Attribute <AttributeUsage(AttributeTargets.Method, AllowMultiple:=True, Inherited:=False)> Public NotInheritable Class PropertyUpdater : Inherits Attribute
Public ReadOnly Name As String Public ReadOnly Name As String
@@ -129,13 +132,26 @@ Namespace Plugin.Attributes
''' Predefined task counter.<br/> ''' Predefined task counter.<br/>
''' <see cref="TaskCounter"/> will take precedence if it is defined. ''' <see cref="TaskCounter"/> will take precedence if it is defined.
''' </param> ''' </param>
Public Sub New(Optional ByVal JobsCount As Integer = -1) Public Sub New(Optional ByVal TasksCount As Integer = -1)
TasksCount = JobsCount Me.TasksCount = TasksCount
End Sub End Sub
End Class End Class
''' <summary>A property attribute that specifies how many users should be downloaded at the same time in one thread</summary> ''' <summary>A property attribute that specifies how many users should be downloaded at the same time in one thread</summary>
<AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class TaskCounter : Inherits Attribute <AttributeUsage(AttributeTargets.Property, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class TaskCounter : Inherits Attribute
End Class End Class
''' <remarks>
''' This attribute cannot be combined with <see cref="SeparatedTasks"/>.
''' If set to <see cref="SeparatedTasks"/>, this attribute will be ignored
''' </remarks>
''' <inheritdoc cref="SeparatedTasks"/>
<AttributeUsage(AttributeTargets.Class, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class TaskGroup : Inherits Attribute
Public ReadOnly Name As String
''' <summary>Initialize a new TaskGroup attribute.</summary>
''' <param name="Name">Group name</param>
Public Sub New(ByVal Name As String)
Me.Name = Name
End Sub
End Class
''' <summary>This attribute indicates that the plugin has a SavedPosts environment</summary> ''' <summary>This attribute indicates that the plugin has a SavedPosts environment</summary>
<AttributeUsage(AttributeTargets.Class, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class SavedPosts : Inherits Attribute <AttributeUsage(AttributeTargets.Class, AllowMultiple:=False, Inherited:=False)> Public NotInheritable Class SavedPosts : Inherits Attribute
End Class End Class

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -17,9 +17,9 @@ Namespace Plugin
Property ID As String Property ID As String
Property ParseUserMediaOnly As Boolean Property ParseUserMediaOnly As Boolean
Property UserDescription As String Property UserDescription As String
Property ExistingContentList As List(Of PluginUserMedia) Property ExistingContentList As List(Of IUserMedia)
Property TempPostsList As List(Of String) Property TempPostsList As List(Of String)
Property TempMediaList As List(Of PluginUserMedia) Property TempMediaList As List(Of IUserMedia)
Property UserExists As Boolean Property UserExists As Boolean
Property UserSuspended As Boolean Property UserSuspended As Boolean
Property IsSavedPosts As Boolean Property IsSavedPosts As Boolean

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -18,12 +18,12 @@ Namespace Plugin
ReadOnly Property Image As Image ReadOnly Property Image As Image
ReadOnly Property Site As String ReadOnly Property Site As String
Property Logger As ILogProvider Property Logger As ILogProvider
Function GetUserUrl(ByVal UserName As String, ByVal Channel As Boolean) As String Function GetUserUrl(ByVal User As IPluginContentProvider, ByVal Channel As Boolean) As String
Function IsMyUser(ByVal UserURL As String) As ExchangeOptions Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions Function IsMyImageVideo(ByVal URL As String) As ExchangeOptions
Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable(Of PluginUserMedia) Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
Function GetInstance(ByVal What As Download) As IPluginContentProvider Function GetInstance(ByVal What As Download) As IPluginContentProvider
Function GetUserPostUrl(ByVal UserID As String, ByVal PostID As String) As String Function GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String
#Region "XML Support" #Region "XML Support"
Sub Load(ByVal XMLValues As IEnumerable(Of KeyValuePair(Of String, String))) Sub Load(ByVal XMLValues As IEnumerable(Of KeyValuePair(Of String, String)))
#End Region #End Region
@@ -32,6 +32,8 @@ Namespace Plugin
Sub EndInit() Sub EndInit()
Sub BeginUpdate() Sub BeginUpdate()
Sub EndUpdate() Sub EndUpdate()
Sub BeginEdit()
Sub EndEdit()
#End Region #End Region
#Region "Site availability" #Region "Site availability"
Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean

View File

@@ -13,7 +13,7 @@ Imports System.Runtime.InteropServices
<Assembly: AssemblyDescription("Plugin provider for SCrawler")> <Assembly: AssemblyDescription("Plugin provider for SCrawler")>
<Assembly: AssemblyCompany("AndyProgram")> <Assembly: AssemblyCompany("AndyProgram")>
<Assembly: AssemblyProduct("SCrawler.PluginProvider")> <Assembly: AssemblyProduct("SCrawler.PluginProvider")>
<Assembly: AssemblyCopyright("Copyright © 2022")> <Assembly: AssemblyCopyright("Copyright © 2023")>
<Assembly: AssemblyTrademark("AndyProgram")> <Assembly: AssemblyTrademark("AndyProgram")>
<Assembly: ComVisible(False)> <Assembly: ComVisible(False)>
@@ -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("2022.9.17.0")> <Assembly: AssemblyVersion("2022.11.16.0")>
<Assembly: AssemblyFileVersion("2022.9.17.0")> <Assembly: AssemblyFileVersion("2022.11.16.0")>
<Assembly: NeutralResourcesLanguage("en")> <Assembly: NeutralResourcesLanguage("en")>

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -7,25 +7,44 @@
' 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
Namespace Plugin Namespace Plugin
Public Structure PluginUserMedia Public Enum UserMediaTypes As Integer
Enum Types As Integer Undefined = 0
Undefined = 0 [Picture] = 1
[Picture] = 1 [Video] = 2
[Video] = 2 [Text] = 3
[Text] = 3 VideoPre = 10
VideoPre = 10 GIF = 50
GIF = 50 m3u8 = 100
m3u8 = 100 End Enum
End Enum Public Enum UserMediaStates As Integer
Enum States As Integer : Unknown = 0 : Tried = 1 : Downloaded = 2 : Skipped = 3 : Missing = 4 : End Enum Unknown = 0
Public ContentType As Integer Tried = 1
Public URL As String Downloaded = 2
Public MD5 As String Skipped = 3
Public File As String Missing = 4
Public DownloadState As Integer End Enum
Public PostID As String Public Structure PluginUserMedia : Implements IUserMedia
Public PostDate As Date? Public Property ContentType As Integer Implements IUserMedia.ContentType
Public SpecialFolder As String Public Property URL As String Implements IUserMedia.URL
Public Attempts As Integer Public Property URL_BASE As String Implements IUserMedia.URL_BASE
Public Property MD5 As String Implements IUserMedia.MD5
Public Property File As String Implements IUserMedia.File
Public Property DownloadState As Integer Implements IUserMedia.DownloadState
Public Property PostID As String Implements IUserMedia.PostID
Public Property PostDate As Date? Implements IUserMedia.PostDate
Public Property SpecialFolder As String Implements IUserMedia.SpecialFolder
Public Property Attempts As Integer Implements IUserMedia.Attempts
End Structure End Structure
Public Interface IUserMedia
Property ContentType As Integer
Property URL As String
Property URL_BASE As String
Property MD5 As String
Property File As String
Property DownloadState As Integer
Property PostID As String
Property PostDate As Date?
Property SpecialFolder As String
Property Attempts As Integer
End Interface
End Namespace End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -17,10 +17,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
EndProject EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.PluginProvider", "SCrawler.PluginProvider\SCrawler.PluginProvider.vbproj", "{D4650F6B-5A54-44B6-999B-6C675B7116B1}" Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.PluginProvider", "SCrawler.PluginProvider\SCrawler.PluginProvider.vbproj", "{D4650F6B-5A54-44B6-999B-6C675B7116B1}"
EndProject EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.Plugin.LPSG", "SCrawler.Plugin.LPSG\SCrawler.Plugin.LPSG.vbproj", "{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "SCrawler.Plugin.XVIDEOS", "SCrawler.Plugin.XVIDEOS\SCrawler.Plugin.XVIDEOS.vbproj", "{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}"
EndProject
Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "PersonalUtilities.Notifications", "..\..\MyUtilities\PersonalUtilities.Notifications\PersonalUtilities.Notifications.vbproj", "{FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}" Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "PersonalUtilities.Notifications", "..\..\MyUtilities\PersonalUtilities.Notifications\PersonalUtilities.Notifications.vbproj", "{FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}"
EndProject EndProject
Global Global
@@ -69,30 +65,6 @@ Global
{D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x64.Build.0 = Release|x64 {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x64.Build.0 = Release|x64
{D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x86.ActiveCfg = Release|x86 {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x86.ActiveCfg = Release|x86
{D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x86.Build.0 = Release|x86 {D4650F6B-5A54-44B6-999B-6C675B7116B1}.Release|x86.Build.0 = Release|x86
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Debug|x64.ActiveCfg = Debug|x64
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Debug|x64.Build.0 = Debug|x64
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Debug|x86.ActiveCfg = Debug|x86
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Debug|x86.Build.0 = Debug|x86
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Release|Any CPU.Build.0 = Release|Any CPU
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Release|x64.ActiveCfg = Release|x64
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Release|x64.Build.0 = Release|x64
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Release|x86.ActiveCfg = Release|x86
{22A130B2-DDF4-4FB5-BA38-E5DB4CF1B8A2}.Release|x86.Build.0 = Release|x86
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Debug|x64.ActiveCfg = Debug|x64
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Debug|x64.Build.0 = Debug|x64
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Debug|x86.ActiveCfg = Debug|x86
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Debug|x86.Build.0 = Debug|x86
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|Any CPU.Build.0 = Release|Any CPU
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|x64.ActiveCfg = Release|x64
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|x64.Build.0 = Release|x64
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|x86.ActiveCfg = Release|x86
{CCCF47F4-C97C-4193-AC4B-C56DF2F9AA8A}.Release|x86.Build.0 = Release|x86
{FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|Any CPU.Build.0 = Debug|Any CPU {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|x64.ActiveCfg = Debug|x64 {FC532253-1AB3-4DEF-A28A-DFDD9A481EB2}.Debug|x64.ActiveCfg = Debug|x64

View File

@@ -123,4 +123,4 @@ insert_final_newline=false
[*.vb] [*.vb]
# Modifier preferences # Modifier preferences
visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion
file_header_template = Copyright (C) 2022 Andy\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/> file_header_template = Copyright (C) 2023 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

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -0,0 +1,64 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web
Imports PersonalUtilities.Tools.Web.Clients
Namespace API.Base
Namespace M3U8Declarations
Friend Module M3U8Defaults
Friend ReadOnly TsFilesRegEx As RParams = RParams.DM(".+?\.ts[^\r\n]*", 0, RegexReturn.List)
End Module
End Namespace
Friend NotInheritable Class M3U8Base
Private Sub New()
End Sub
Friend Shared Function CreateUrl(ByVal Appender As String, ByVal File As String) As String
File = File.StringTrimStart("/")
If File.StartsWith("http") Then
Return File
Else
If File.StartsWith("hls/") And Appender.Contains("hls/") Then _
Appender = LinkFormatterSecure(Appender.Replace("https://", String.Empty).Split("/").First)
Return $"{Appender.StringTrimEnd("/")}/{File}"
End If
End Function
Friend Shared Function Download(ByVal URLs As List(Of String), ByVal DestinationFile As SFile, Optional ByVal Responser As Response = Nothing) As SFile
Dim CachePath As SFile = Nothing
Try
If URLs.ListExists Then
Dim ConcatFile As SFile = DestinationFile
If ConcatFile.Name.IsEmptyString Then ConcatFile.Name = "PlayListFile"
ConcatFile.Extension = "mp4"
CachePath = $"{DestinationFile.PathWithSeparator}_Cache\{SFile.GetDirectories($"{DestinationFile.PathWithSeparator}_Cache\",,, EDP.ReturnValue).ListIfNothing.Count + 1}\"
If CachePath.Exists(SFO.Path) Then
Dim p As New SFileNumbers(ConcatFile.Name,,, New ANumbers With {.Format = ANumbers.Formats.General})
ConcatFile = SFile.Indexed_IndexFile(ConcatFile,, p, EDP.ReturnValue)
Dim i%
Dim eFiles As New List(Of SFile)
Dim dFile As SFile = CachePath
dFile.Extension = "ts"
Using w As New DownloadObjects.WebClient2(Responser)
For i = 0 To URLs.Count - 1
dFile.Name = $"ConPart_{i}"
w.DownloadFile(URLs(i), dFile)
eFiles.Add(dFile)
Next
End Using
DestinationFile = FFMPEG.ConcatenateFiles(eFiles, Settings.FfmpegFile, ConcatFile, p, EDP.ThrowException)
eFiles.Clear()
Return DestinationFile
End If
End If
Return Nothing
Finally
CachePath.Delete(SFO.Path, SFODelete.None, EDP.None)
End Try
End Function
End Class
End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -7,8 +7,8 @@
' 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 System.Threading
Imports PersonalUtilities.Forms.Toolbars
Imports SCrawler.Plugin.Hosts Imports SCrawler.Plugin.Hosts
Imports PersonalUtilities.Forms.Toolbars
Imports PDownload = SCrawler.Plugin.ISiteSettings.Download Imports PDownload = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Base Namespace API.Base
Friend NotInheritable Class ProfileSaved Friend NotInheritable Class ProfileSaved
@@ -25,7 +25,7 @@ Namespace API.Base
HOST.DownloadStarted(PDownload.SavedPosts) HOST.DownloadStarted(PDownload.SavedPosts)
Dim u As New UserInfo With {.Plugin = HOST.Key, .Site = HOST.Name, .SpecialPath = HOST.SavedPostsPath} Dim u As New UserInfo With {.Plugin = HOST.Key, .Site = HOST.Name, .SpecialPath = HOST.SavedPostsPath}
Using user As IUserData = HOST.GetInstance(PDownload.SavedPosts, Nothing, False, False) Using user As IUserData = HOST.GetInstance(PDownload.SavedPosts, Nothing, False, False)
If Not user Is Nothing AndAlso (Not user.Name.IsEmptyString Or Not HOST.IsMyClass) Then If Not user Is Nothing AndAlso Not user.Name.IsEmptyString Then
u.Name = user.Name u.Name = user.Name
With DirectCast(user, UserDataBase) With DirectCast(user, UserDataBase)
With .User : u.IsChannel = .IsChannel : u.UpdateUserFile() : End With With .User : u.IsChannel = .IsChannel : u.UpdateUserFile() : End With
@@ -45,13 +45,14 @@ Namespace API.Base
Progress.InformationTemporary = $"Host [{HOST.Name}] is unavailable" Progress.InformationTemporary = $"Host [{HOST.Name}] is unavailable"
End If End If
Else Else
Progress.InformationTemporary = $"Host [{HOST.Name}] is nor ready" Progress.InformationTemporary = $"Host [{HOST.Name}] is not ready"
End If End If
Catch ex As Exception Catch ex As Exception
Progress.InformationTemporary = $"{HOST.Name} downloading error" Progress.InformationTemporary = $"{HOST.Name} downloading error"
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.Base.ProfileSaved.Download({HOST.Key})]") ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.Base.ProfileSaved.Download({HOST.Key})]")
Finally Finally
HOST.DownloadDone(PDownload.SavedPosts) HOST.DownloadDone(PDownload.SavedPosts)
MainFrameObj.UpdateLogButton()
End Try End Try
End Sub End Sub
End Class End Class

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -7,7 +7,8 @@
' 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.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.WEB Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies
Imports SCrawler.Plugin Imports SCrawler.Plugin
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Base Namespace API.Base
@@ -40,6 +41,7 @@ Namespace API.Base
.CookiesEncryptKey = SettingsCLS.CookieEncryptKey .CookiesEncryptKey = SettingsCLS.CookieEncryptKey
.SaveSettings() .SaveSettings()
End If End If
If .CookiesDomain.IsEmptyString Then .CookiesDomain = CookiesDomain
End With End With
End Sub End Sub
#Region "XML" #Region "XML"
@@ -56,6 +58,10 @@ Namespace API.Base
End Sub End Sub
Friend Overridable Sub EndUpdate() Implements ISiteSettings.EndUpdate Friend Overridable Sub EndUpdate() Implements ISiteSettings.EndUpdate
End Sub End Sub
Friend Overridable Sub BeginEdit() Implements ISiteSettings.BeginEdit
End Sub
Friend Overridable Sub EndEdit() Implements ISiteSettings.EndEdit
End Sub
#End Region #End Region
#Region "Before and After Download" #Region "Before and After Download"
Friend Overridable Sub DownloadStarted(ByVal What As Download) Implements ISiteSettings.DownloadStarted Friend Overridable Sub DownloadStarted(ByVal What As Download) Implements ISiteSettings.DownloadStarted
@@ -70,15 +76,18 @@ Namespace API.Base
#Region "User info" #Region "User info"
Protected UrlPatternUser As String = String.Empty Protected UrlPatternUser As String = String.Empty
Protected UrlPatternChannel As String = String.Empty Protected UrlPatternChannel As String = String.Empty
Friend Overridable Function GetUserUrl(ByVal UserName As String, ByVal Channel As Boolean) As String Implements ISiteSettings.GetUserUrl Friend Overridable Function GetUserUrl(ByVal User As IPluginContentProvider, ByVal Channel As Boolean) As String Implements ISiteSettings.GetUserUrl
If Channel Then If Channel Then
If Not UrlPatternChannel.IsEmptyString Then Return String.Format(UrlPatternChannel, UserName) If Not UrlPatternChannel.IsEmptyString Then Return String.Format(UrlPatternChannel, User.Name)
Else Else
If Not UrlPatternUser.IsEmptyString Then Return String.Format(UrlPatternUser, UserName) If Not UrlPatternUser.IsEmptyString Then Return String.Format(UrlPatternUser, User.Name)
End If End If
Return String.Empty Return String.Empty
End Function End Function
Friend Overridable Function GetUserPostUrl(ByVal UserID As String, ByVal PostID As String) As String Implements ISiteSettings.GetUserPostUrl Private Function ISiteSettings_GetUserPostUrl(ByVal User As IPluginContentProvider, ByVal Media As IUserMedia) As String Implements ISiteSettings.GetUserPostUrl
Return GetUserPostUrl(User, Media)
End Function
Friend Overridable Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
Return String.Empty Return String.Empty
End Function End Function
Protected UserRegex As RParams = Nothing Protected UserRegex As RParams = Nothing
@@ -90,7 +99,7 @@ Namespace API.Base
End If End If
Return Nothing Return Nothing
Catch ex As Exception Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[API.Base.SiteSettingsBase.IsMyUser]") Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"[API.Base.SiteSettingsBase.IsMyUser({UserURL})]", New ExchangeOptions)
End Try End Try
End Function End Function
Protected ImageVideoContains As String = String.Empty Protected ImageVideoContains As String = String.Empty
@@ -101,17 +110,25 @@ Namespace API.Base
Return Nothing Return Nothing
End If End If
End Function End Function
Friend Overridable Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable(Of PluginUserMedia) Implements ISiteSettings.GetSpecialData Friend Overridable Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable Implements ISiteSettings.GetSpecialData
Return Nothing Return Nothing
End Function End Function
Friend Overridable Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia) Friend Shared Function GetSpecialDataFile(ByVal Path As String, ByVal AskForPath As Boolean, ByRef SpecFolderObj As String) As SFile
Return Nothing Dim f As SFile = Path.CSFileP
If f.Name.IsEmptyString Then f.Name = "OutputFile"
#Disable Warning BC40000
If Path.CSFileP.IsEmptyString Or AskForPath Then f = SFile.SaveAs(f, "File destination",,,, EDP.ReturnValue) : SpecFolderObj = f.Path
#Enable Warning
Return f
End Function End Function
#End Region #End Region
#Region "Ready, Available" #Region "Ready, Available"
Friend Overridable Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available Friend Overridable Function BaseAuthExists() As Boolean
Return True Return True
End Function End Function
Friend Overridable Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Implements ISiteSettings.Available
Return BaseAuthExists()
End Function
Friend Overridable Function ReadyToDownload(ByVal What As Download) As Boolean Implements ISiteSettings.ReadyToDownload Friend Overridable Function ReadyToDownload(ByVal What As Download) As Boolean Implements ISiteSettings.ReadyToDownload
Return True Return True
End Function End Function

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,9 +6,25 @@
' '
' 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 SCrawler.Plugin
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.XML.Base
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Base Namespace API.Base
Friend Module Structures Friend Module Structures
Friend Structure UserMedia : Implements IEquatable(Of UserMedia) Friend Structure UserMedia : Implements IUserMedia, IEquatable(Of UserMedia), IEContainerProvider
#Region "XML Names"
Friend Const Name_MediaNode As String = "MediaData"
Private Const Name_MediaType As String = "Type"
Private Const Name_MediaState As String = "State"
Private Const Name_MediaAttempts As String = "Attempts"
Private Const Name_MediaURL As String = "URL"
Private Const Name_MediaHash As String = "Hash"
Private Const Name_MediaFile As String = "File"
Private Const Name_MediaPostID As String = "ID"
Private Const Name_MediaPostDate As String = "Date"
Private Const Name_SpecialFolder As String = "SpecialFolder"
#End Region
Friend Enum Types As Integer Friend Enum Types As Integer
Undefined = 0 Undefined = 0
[Picture] = 1 [Picture] = 1
@@ -33,27 +49,137 @@ Namespace API.Base
''' SomeFolder\SomeFolder2 ''' SomeFolder\SomeFolder2
''' </summary> ''' </summary>
Friend SpecialFolder As String Friend SpecialFolder As String
Friend Sub New(ByVal _URL As String) Friend [Object] As Object
URL = _URL #Region "Interface Support"
URL_BASE = _URL Private Property IUserMedia_Type As Integer Implements IUserMedia.ContentType
Get
Return Type
End Get
Set(ByVal Type As Integer)
Me.Type = Type
End Set
End Property
Private Property IUserMedia_URL_BASE As String Implements IUserMedia.URL_BASE
Get
Return URL_BASE
End Get
Set(ByVal URL_BASE As String)
Me.URL_BASE = URL_BASE
End Set
End Property
Private Property IUserMedia_URL As String Implements IUserMedia.URL
Get
Return URL
End Get
Set(ByVal URL As String)
Me.URL = URL
End Set
End Property
Private Property IUserMedia_MD5 As String Implements IUserMedia.MD5
Get
Return MD5
End Get
Set(ByVal MD5 As String)
Me.MD5 = MD5
End Set
End Property
Private Property IUserMedia_File As String Implements IUserMedia.File
Get
Return File
End Get
Set(ByVal File As String)
Me.File = File
End Set
End Property
Private Property IUserMedia_State As Integer Implements IUserMedia.DownloadState
Get
Return State
End Get
Set(ByVal State As Integer)
Me.State = State
End Set
End Property
Private Property IUserMedia_PostID As String Implements IUserMedia.PostID
Get
Return Post.ID
End Get
Set(ByVal PostID As String)
Post.ID = PostID
End Set
End Property
Private Property IUserMedia_PostDate As Date? Implements IUserMedia.PostDate
Get
Return Post.Date
End Get
Set(ByVal PostDate As Date?)
Post.Date = PostDate
End Set
End Property
Private Property IUserMedia_SpecialFolder As String Implements IUserMedia.SpecialFolder
Get
Return SpecialFolder
End Get
Set(ByVal SpecialFolder As String)
Me.SpecialFolder = SpecialFolder
End Set
End Property
Private Property IUserMedia_Attempts As Integer Implements IUserMedia.Attempts
Get
Return Attempts
End Get
Set(ByVal Attempts As Integer)
Me.Attempts = Attempts
End Set
End Property
#End Region
Friend Sub New(ByVal URL As String)
Me.URL = URL
URL_BASE = URL
File = URL File = URL
Type = Types.Undefined Type = Types.Undefined
End Sub End Sub
Friend Sub New(ByVal _URL As String, ByVal _Type As Types) Friend Sub New(ByVal URL As String, ByVal Type As Types)
Me.New(_URL) Me.New(URL)
[Type] = _Type Me.Type = Type
End Sub End Sub
Friend Sub New(ByVal m As Plugin.PluginUserMedia) Friend Sub New(ByVal m As Plugin.IUserMedia)
If Not IsNothing(m) Then [Type] = m.ContentType
[Type] = m.ContentType URL = m.URL
URL = m.URL URL_BASE = m.URL_BASE
MD5 = m.MD5 MD5 = m.MD5
File = m.File File = m.File
Post = New UserPost With {.ID = m.PostID, .[Date] = m.PostDate} Post = New UserPost With {.ID = m.PostID, .[Date] = m.PostDate}
State = m.DownloadState State = m.DownloadState
SpecialFolder = m.SpecialFolder SpecialFolder = m.SpecialFolder
Attempts = m.Attempts Attempts = m.Attempts
End Sub
Friend Sub New(ByVal e As EContainer, ByVal UserInstance As IUserData)
Type = e.Attribute(Name_MediaType).Value.FromXML(Of Integer)(CInt(Types.Undefined))
State = e.Attribute(Name_MediaState).Value.FromXML(Of Integer)(CInt(States.Downloaded))
Attempts = e.Attribute(Name_MediaAttempts).Value.FromXML(Of Integer)(0)
URL = e.Attribute(Name_MediaURL).Value
URL_BASE = e.Value
MD5 = e.Attribute(Name_MediaHash).Value
File = e.Attribute(Name_MediaFile).Value
Dim vp As Boolean? = Nothing
Dim upath$ = String.Empty
If Not UserInstance Is Nothing Then
With DirectCast(UserInstance, UserDataBase)
vp = .SeparateVideoFolder
upath = .MyFile.CutPath.PathWithSeparator
End With
End If End If
SpecialFolder = e.Attribute(Name_SpecialFolder).Value
If Not SpecialFolder.IsEmptyString Then upath &= $"{SpecialFolder}\"
If vp.HasValue AndAlso vp.Value Then upath &= $"Video\"
If Not upath.IsEmptyString Then File = $"{upath.CSFilePS}{File.File}"
Post = New UserPost With {
.ID = e.Attribute(Name_MediaPostID).Value,
.[Date] = AConvert(Of Date)(e.Attribute(Name_MediaPostDate).Value, ParsersDataDateProvider, Nothing)
}
End Sub End Sub
Public Shared Widening Operator CType(ByVal _URL As String) As UserMedia Public Shared Widening Operator CType(ByVal _URL As String) As UserMedia
Return New UserMedia(_URL) Return New UserMedia(_URL)
@@ -61,44 +187,69 @@ Namespace API.Base
Public Shared Widening Operator CType(ByVal m As UserMedia) As String Public Shared Widening Operator CType(ByVal m As UserMedia) As String
Return m.URL Return m.URL
End Operator End Operator
Public Overrides Function GetHashCode() As Integer
If Not File.IsEmptyString Then
Return File.GetHashCode
ElseIf Not URL_BASE.IsEmptyString Then
Return URL_BASE.GetHashCode
ElseIf Not URL.IsEmptyString Then
Return URL.GetHashCode
Else
Return 0
End If
End Function
Public Overrides Function ToString() As String Public Overrides Function ToString() As String
Return URL Return URL
End Function End Function
Friend Function PluginUserMedia() As Plugin.PluginUserMedia
Return New Plugin.PluginUserMedia With {
.ContentType = Type,
.DownloadState = State,
.File = File,
.MD5 = MD5,
.URL = URL,
.SpecialFolder = SpecialFolder,
.PostID = Post.ID,
.PostDate = Post.Date,
.Attempts = Attempts
}
End Function
Friend Overloads Function Equals(ByVal Other As UserMedia) As Boolean Implements IEquatable(Of UserMedia).Equals Friend Overloads Function Equals(ByVal Other As UserMedia) As Boolean Implements IEquatable(Of UserMedia).Equals
Return URL = Other.URL Return URL = Other.URL
End Function End Function
Public Overrides Function Equals(ByVal Obj As Object) As Boolean Public Overrides Function Equals(ByVal Obj As Object) As Boolean
Return Equals(CType(Obj, UserMedia)) Return Equals(CType(Obj, UserMedia))
End Function End Function
Friend Function ToEContainer(Optional ByVal e As ErrorsDescriber = Nothing) As EContainer Implements IEContainerProvider.ToEContainer
Return New EContainer(Name_MediaNode, URL_BASE, {New EAttribute(Name_MediaType, CInt(Type)),
New EAttribute(Name_MediaState, CInt(State)),
New EAttribute(Name_MediaAttempts, Attempts),
New EAttribute(Name_MediaURL, URL),
New EAttribute(Name_MediaHash, MD5),
New EAttribute(Name_MediaFile, File.File),
New EAttribute(Name_SpecialFolder, SpecialFolder),
New EAttribute(Name_MediaPostID, Post.ID),
New EAttribute(Name_MediaPostDate, AConvert(Of String)(Post.Date, ParsersDataDateProvider, String.Empty))
}
)
End Function
End Structure End Structure
Friend Structure UserPost : Implements IEquatable(Of UserPost), IComparable(Of UserPost) Friend Structure UserPost : Implements IEquatable(Of UserPost), IComparable(Of UserPost)
''' <summary>Post ID</summary> ''' <summary>Post ID</summary>
Friend ID As String Friend ID As String
Friend [Date] As Date? Friend [Date] As Date?
#Region "Channel compatible fields"
Friend UserID As String Friend UserID As String
Friend CachedFile As SFile Friend CachedFile As SFile
#Region "Initializers"
Public Sub New(ByVal ID As String)
Me.ID = ID
End Sub
Public Sub New(ByVal [Date] As Date?)
Me.Date = [Date]
End Sub
Public Sub New(ByVal ID As String, ByVal [Date] As Date?)
Me.ID = ID
Me.Date = [Date]
End Sub
Public Shared Widening Operator CType(ByVal ID As String) As UserPost
Return New UserPost(ID)
End Operator
Public Shared Widening Operator CType(ByVal Post As UserPost) As String
Return Post.ID
End Operator
#End Region #End Region
Friend Function GetImage(ByVal s As Size, ByVal e As ErrorsDescriber, ByVal NullArg As Image) As Image #Region "ToString"
If Not CachedFile.IsEmptyString Then Public Overrides Function ToString() As String
Return If(PersonalUtilities.Tools.ImageRenderer.GetImage(SFile.GetBytes(CachedFile), s, e), NullArg.Clone) Return ID
Else
Return NullArg.Clone
End If
End Function End Function
#End Region
#Region "IEquatable, IComparable Support" #Region "IEquatable, IComparable Support"
Friend Overloads Function Equals(ByVal Other As UserPost) As Boolean Implements IEquatable(Of UserPost).Equals Friend Overloads Function Equals(ByVal Other As UserPost) As Boolean Implements IEquatable(Of UserPost).Equals
Return ID = Other.ID Return ID = Other.ID
@@ -116,7 +267,7 @@ Namespace API.Base
End Function End Function
#End Region #End Region
End Structure End Structure
Friend Structure Sizes : Implements IComparable(Of Sizes) Friend Structure Sizes : Implements IRegExCreator, IComparable(Of Sizes)
Friend Value As Integer Friend Value As Integer
Friend Data As String Friend Data As String
Friend ReadOnly HasError As Boolean Friend ReadOnly HasError As Boolean
@@ -128,6 +279,16 @@ Namespace API.Base
HasError = True HasError = True
End Try End Try
End Sub End Sub
Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
If ParamsArray.ListExists(2) Then
Return New Sizes With {
.Value = AConvert(Of Integer)(ParamsArray(0), 0),
.Data = ParamsArray(1)
}
Else
Return New Sizes
End If
End Function
Friend Function CompareTo(ByVal Other As Sizes) As Integer Implements IComparable(Of Sizes).CompareTo Friend Function CompareTo(ByVal Other As Sizes) As Integer Implements IComparable(Of Sizes).CompareTo
Return Value.CompareTo(Other.Value) * -1 Return Value.CompareTo(Other.Value) * -1
End Function End Function

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -10,7 +10,7 @@ Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Forms.Toolbars
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
Imports PersonalUtilities.Tools.WEB Imports PersonalUtilities.Tools.Web.Clients
Imports System.IO Imports System.IO
Imports System.Net Imports System.Net
Imports System.Threading Imports System.Threading
@@ -51,14 +51,12 @@ Namespace API.Base
#Region "Collection buttons" #Region "Collection buttons"
Private _CollectionButtonsExists As Boolean = False Private _CollectionButtonsExists As Boolean = False
Private _CollectionButtonsColorsSet As Boolean = False Private _CollectionButtonsColorsSet As Boolean = False
Friend InternalCollectionIndex As Integer = -1 Friend WithEvents BTT_CONTEXT_DOWN As ToolStripKeyMenuItem
Friend WithEvents BTT_CONTEXT_DOWN As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_EDIT As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_EDIT As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_DELETE As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_DELETE As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_OPEN_PATH As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_OPEN_PATH As ToolStripMenuItem
Friend WithEvents BTT_CONTEXT_OPEN_SITE As ToolStripMenuItem Friend WithEvents BTT_CONTEXT_OPEN_SITE As ToolStripMenuItem
Friend Sub CreateButtons(ByVal CollectionIndex As Integer) Friend Sub CreateButtons()
InternalCollectionIndex = CollectionIndex
Dim tn$ = $"[{Site}] - {Name}" Dim tn$ = $"[{Site}] - {Name}"
Dim _tn$ = $"{Site}{Name}" Dim _tn$ = $"{Site}{Name}"
Dim tnn As Func(Of String, String) = Function(Input) $"{Input}{_tn}" Dim tnn As Func(Of String, String) = Function(Input) $"{Input}{_tn}"
@@ -70,11 +68,11 @@ Namespace API.Base
i = .Image i = .Image
End If End If
End With End With
BTT_CONTEXT_DOWN = New ToolStripMenuItem(tn, i) With {.Name = tnn("DOWN"), .Tag = CollectionIndex} BTT_CONTEXT_DOWN = New ToolStripKeyMenuItem(tn, i) With {.Name = tnn("DOWN"), .Tag = Me}
BTT_CONTEXT_EDIT = New ToolStripMenuItem(tn, i) With {.Name = tnn("EDIT"), .Tag = CollectionIndex} BTT_CONTEXT_EDIT = New ToolStripMenuItem(tn, i) With {.Name = tnn("EDIT"), .Tag = Me}
BTT_CONTEXT_DELETE = New ToolStripMenuItem(tn, i) With {.Name = tnn("DELETE"), .Tag = CollectionIndex} BTT_CONTEXT_DELETE = New ToolStripMenuItem(tn, i) With {.Name = tnn("DELETE"), .Tag = Me}
BTT_CONTEXT_OPEN_PATH = New ToolStripMenuItem(tn, i) With {.Name = tnn("PATH"), .Tag = CollectionIndex} BTT_CONTEXT_OPEN_PATH = New ToolStripMenuItem(tn, i) With {.Name = tnn("PATH"), .Tag = Me}
BTT_CONTEXT_OPEN_SITE = New ToolStripMenuItem(tn, i) With {.Name = tnn("SITE"), .Tag = CollectionIndex} BTT_CONTEXT_OPEN_SITE = New ToolStripMenuItem(tn, i) With {.Name = tnn("SITE"), .Tag = Me}
UpdateButtonsColor() UpdateButtonsColor()
_CollectionButtonsExists = True _CollectionButtonsExists = True
If _UserInformationLoaded Then _CollectionButtonsColorsSet = True If _UserInformationLoaded Then _CollectionButtonsColorsSet = True
@@ -96,9 +94,16 @@ Namespace API.Base
End Sub End Sub
#End Region #End Region
#Region "XML Declarations" #Region "XML Declarations"
Private Const Name_Site As String = "Site" Private Const Name_Site As String = UserInfo.Name_Site
Private Const Name_IsChannel As String = "IsChannel" Private Const Name_Plugin As String = UserInfo.Name_Plugin
Private Const Name_UserName As String = "UserName" Private Const Name_IsChannel As String = UserInfo.Name_IsChannel
Friend Const Name_UserName As String = "UserName"
Private Const Name_Model_User As String = UserInfo.Name_Model_User
Private Const Name_Model_Collection As String = UserInfo.Name_Model_Collection
Private Const Name_Merged As String = UserInfo.Name_Merged
Private Const Name_SpecialPath As String = UserInfo.Name_SpecialPath
Private Const Name_SpecialCollectionPath As String = UserInfo.Name_SpecialCollectionPath
Private Const Name_UserExists As String = "UserExists" Private Const Name_UserExists As String = "UserExists"
Private Const Name_UserSuspended As String = "UserSuspended" Private Const Name_UserSuspended As String = "UserSuspended"
Private Const Name_FriendlyName As String = "FriendlyName" Private Const Name_FriendlyName As String = "FriendlyName"
@@ -110,8 +115,8 @@ Namespace API.Base
Private Const Name_CreatedByChannel As String = "CreatedByChannel" Private Const Name_CreatedByChannel As String = "CreatedByChannel"
Private Const Name_SeparateVideoFolder As String = "SeparateVideoFolder" Private Const Name_SeparateVideoFolder As String = "SeparateVideoFolder"
Private Const Name_CollectionName As String = "Collection" Private Const Name_CollectionName As String = UserInfo.Name_Collection
Private Const Name_LabelsName As String = "Labels" Friend Const Name_LabelsName As String = "Labels"
Private Const Name_ReadyForDownload As String = "ReadyForDownload" Private Const Name_ReadyForDownload As String = "ReadyForDownload"
Private Const Name_DownloadImages As String = "DownloadImages" Private Const Name_DownloadImages As String = "DownloadImages"
@@ -124,17 +129,7 @@ Namespace API.Base
Private Const Name_ScriptUse As String = "ScriptUse" Private Const Name_ScriptUse As String = "ScriptUse"
Private Const Name_ScriptData As String = "ScriptData" Private Const Name_ScriptData As String = "ScriptData"
Private Const Name_DataMerging As String = "DataMerging" <Obsolete("Use 'Name_Merged'", False)> Friend Const Name_DataMerging As String = "DataMerging"
#Region "Downloaded data"
Private Const Name_MediaType As String = "Type"
Private Const Name_MediaState As String = "State"
Private Const Name_MediaAttempts As String = "Attempts"
Private Const Name_MediaURL As String = "URL"
Private Const Name_MediaHash As String = "Hash"
Private Const Name_MediaFile As String = "File"
Private Const Name_MediaPostID As String = "ID"
Private Const Name_MediaPostDate As String = "Date"
#End Region
#End Region #End Region
#Region "Declarations" #Region "Declarations"
#Region "Host, Site, Progress, Self" #Region "Host, Site, Progress, Self"
@@ -168,6 +163,21 @@ Namespace API.Base
End Property End Property
Friend Overridable Property ID As String = String.Empty Implements IContentProvider.ID, IPluginContentProvider.ID Friend Overridable Property ID As String = String.Empty Implements IContentProvider.ID, IPluginContentProvider.ID
Friend Overridable Property FriendlyName As String = String.Empty Implements IContentProvider.FriendlyName Friend Overridable Property FriendlyName As String = String.Empty Implements IContentProvider.FriendlyName
Friend ReadOnly Property UserModel As UsageModel Implements IUserData.UserModel
Get
Return User.UserModel
End Get
End Property
Friend Overridable ReadOnly Property CollectionModel As UsageModel Implements IUserData.CollectionModel
Get
Return User.CollectionModel
End Get
End Property
Friend Overridable ReadOnly Property IsVirtual As Boolean Implements IUserData.IsVirtual
Get
Return UserModel = UsageModel.Virtual
End Get
End Property
#End Region #End Region
#Region "Description" #Region "Description"
Friend Property UserDescription As String = String.Empty Implements IContentProvider.Description, IPluginContentProvider.UserDescription Friend Property UserDescription As String = String.Empty Implements IContentProvider.Description, IPluginContentProvider.UserDescription
@@ -243,7 +253,7 @@ Namespace API.Base
Protected Function GetNullPicture(ByVal MaxHeigh As XML.Base.XMLValue(Of Integer)) As Bitmap Protected Function GetNullPicture(ByVal MaxHeigh As XML.Base.XMLValue(Of Integer)) As Bitmap
Return New Bitmap(CInt(DivideWithZeroChecking(MaxHeigh.Value, 100) * 75), MaxHeigh.Value) Return New Bitmap(CInt(DivideWithZeroChecking(MaxHeigh.Value, 100) * 75), MaxHeigh.Value)
End Function End Function
Protected Function GetPicture(Of T)(Optional ByVal ReturnNullImageOnNothing As Boolean = True, Optional ByVal GetToast As Boolean = False) As T Friend Function GetPicture(Of T)(Optional ByVal ReturnNullImageOnNothing As Boolean = True, Optional ByVal GetToast As Boolean = False) As T
Dim rsfile As Boolean = GetType(T) Is GetType(SFile) Dim rsfile As Boolean = GetType(T) Is GetType(SFile)
Dim f As SFile = Nothing Dim f As SFile = Nothing
Dim p As UserImage = Nothing Dim p As UserImage = Nothing
@@ -320,7 +330,7 @@ BlockNullPicture:
Friend Property SeparateVideoFolder As Boolean? Friend Property SeparateVideoFolder As Boolean?
Protected ReadOnly Property SeparateVideoFolderF As Boolean Protected ReadOnly Property SeparateVideoFolderF As Boolean
Get Get
Return (SeparateVideoFolder.HasValue AndAlso SeparateVideoFolder.Value) OrElse Settings.SeparateVideoFolder.Value Return If(SeparateVideoFolder, Settings.SeparateVideoFolder.Value)
End Get End Get
End Property End Property
#End Region #End Region
@@ -347,7 +357,7 @@ BlockNullPicture:
Friend Overridable Sub ChangeCollectionName(ByVal NewName As String, ByVal UpdateSettings As Boolean) Friend Overridable Sub ChangeCollectionName(ByVal NewName As String, ByVal UpdateSettings As Boolean)
Dim u As UserInfo = User Dim u As UserInfo = User
u.CollectionName = NewName u.CollectionName = NewName
u.IncludedInCollection = Not NewName.IsEmptyString u.UpdateUserFile()
User = u User = u
If UpdateSettings Then Settings.UpdateUsersList(User) If UpdateSettings Then Settings.UpdateUsersList(User)
End Sub End Sub
@@ -466,7 +476,13 @@ BlockNullPicture:
End Get End Get
End Property End Property
Friend Overridable Function GetUserInformation() As String Friend Overridable Function GetUserInformation() As String
Dim OutStr$ = $"User: {Name}" Dim OutStr$ = $"User: {Name} (site: {Site}"
If IncludedInCollection Then
OutStr &= $"; collection: {CollectionName}"
If CollectionModel = UsageModel.Default And UserModel = UsageModel.Virtual Then OutStr &= "; virtual"
End If
OutStr &= ")"
OutStr.StringAppendLine($"Labels: {Labels.ListToString}")
OutStr.StringAppendLine($"Path: {MyFile.CutPath.Path}") OutStr.StringAppendLine($"Path: {MyFile.CutPath.Path}")
OutStr.StringAppendLine($"Total downloaded ({DownloadedTotal(True).NumToString(ANumbers.Formats.Number, 3)}):") OutStr.StringAppendLine($"Total downloaded ({DownloadedTotal(True).NumToString(ANumbers.Formats.Number, 3)}):")
OutStr.StringAppendLine($"Pictures: {DownloadedPictures(True).NumToString(ANumbers.Formats.Number, 3)}") OutStr.StringAppendLine($"Pictures: {DownloadedPictures(True).NumToString(ANumbers.Formats.Number, 3)}")
@@ -503,9 +519,9 @@ BlockNullPicture:
Private Property IPluginContentProvider_Thrower As IThrower Implements IPluginContentProvider.Thrower Private Property IPluginContentProvider_Thrower As IThrower Implements IPluginContentProvider.Thrower
Private Property IPluginContentProvider_LogProvider As ILogProvider Implements IPluginContentProvider.LogProvider Private Property IPluginContentProvider_LogProvider As ILogProvider Implements IPluginContentProvider.LogProvider
Friend Property ExternalPlugin As IPluginContentProvider Friend Property ExternalPlugin As IPluginContentProvider
Private Property IPluginContentProvider_ExistingContentList As List(Of PluginUserMedia) Implements IPluginContentProvider.ExistingContentList Private Property IPluginContentProvider_ExistingContentList As List(Of IUserMedia) Implements IPluginContentProvider.ExistingContentList
Private Property IPluginContentProvider_TempPostsList As List(Of String) Implements IPluginContentProvider.TempPostsList Private Property IPluginContentProvider_TempPostsList As List(Of String) Implements IPluginContentProvider.TempPostsList
Private Property IPluginContentProvider_TempMediaList As List(Of PluginUserMedia) Implements IPluginContentProvider.TempMediaList Private Property IPluginContentProvider_TempMediaList As List(Of IUserMedia) Implements IPluginContentProvider.TempMediaList
Private Property IPluginContentProvider_SeparateVideoFolder As Boolean Implements IPluginContentProvider.SeparateVideoFolder Private Property IPluginContentProvider_SeparateVideoFolder As Boolean Implements IPluginContentProvider.SeparateVideoFolder
Private Property IPluginContentProvider_DataPath As String Implements IPluginContentProvider.DataPath Private Property IPluginContentProvider_DataPath As String Implements IPluginContentProvider.DataPath
Private Sub IPluginContentProvider_XmlFieldsSet(ByVal Fields As List(Of KeyValuePair(Of String, String))) Implements IPluginContentProvider.XmlFieldsSet Private Sub IPluginContentProvider_XmlFieldsSet(ByVal Fields As List(Of KeyValuePair(Of String, String))) Implements IPluginContentProvider.XmlFieldsSet
@@ -629,7 +645,7 @@ BlockNullPicture:
With DirectCast(u, UserDataBase) With DirectCast(u, UserDataBase)
If Not .User.Plugin.IsEmptyString Then If Not .User.Plugin.IsEmptyString Then
uName = .User.Name uName = .User.Name
Return Settings(.User.Plugin).GetUserPostUrl(.ID, PostData.Post.ID) Return Settings(.User.Plugin).GetUserPostUrl(.Self, PostData)
End If End If
End With End With
End If End If
@@ -666,7 +682,13 @@ BlockNullPicture:
LastUpdated = AConvert(Of Date)(x.Value(Name_LastUpdated), ADateTime.Formats.BaseDateTime, Nothing) LastUpdated = AConvert(Of Date)(x.Value(Name_LastUpdated), ADateTime.Formats.BaseDateTime, Nothing)
ScriptUse = x.Value(Name_ScriptUse).FromXML(Of Boolean)(False) ScriptUse = x.Value(Name_ScriptUse).FromXML(Of Boolean)(False)
ScriptData = x.Value(Name_ScriptData) ScriptData = x.Value(Name_ScriptData)
DataMerging = x.Value(Name_DataMerging).FromXML(Of Boolean)(False) #Disable Warning BC40000
If x.Contains(Name_DataMerging) Then
DataMerging = x.Value(Name_DataMerging).FromXML(Of Boolean)(False)
Else
DataMerging = x.Value(Name_Merged).FromXML(Of Boolean)(False)
End If
#Enable Warning
ChangeCollectionName(x.Value(Name_CollectionName), False) ChangeCollectionName(x.Value(Name_CollectionName), False)
Labels.ListAddList(x.Value(Name_LabelsName).StringToList(Of String, List(Of String))("|", EDP.ReturnValue), LAP.NotContainsOnly, LAP.ClearBeforeAdd) Labels.ListAddList(x.Value(Name_LabelsName).StringToList(Of String, List(Of String))("|", EDP.ReturnValue), LAP.NotContainsOnly, LAP.ClearBeforeAdd)
LoadUserInformation_OptionalFields(x, True) LoadUserInformation_OptionalFields(x, True)
@@ -684,7 +706,13 @@ BlockNullPicture:
MyFile.Exists(SFO.Path) MyFile.Exists(SFO.Path)
Using x As New XmlFile With {.Name = "User"} Using x As New XmlFile With {.Name = "User"}
x.Add(Name_Site, Site) x.Add(Name_Site, Site)
x.Add(Name_Plugin, HOST.Key)
x.Add(Name_UserName, User.Name) x.Add(Name_UserName, User.Name)
x.Add(Name_IsChannel, IsChannel.BoolToInteger)
x.Add(Name_Model_User, CInt(UserModel))
x.Add(Name_Model_Collection, CInt(CollectionModel))
x.Add(Name_SpecialPath, User.SpecialPath)
x.Add(Name_SpecialCollectionPath, User.SpecialCollectionPath)
x.Add(Name_UserExists, UserExists.BoolToInteger) x.Add(Name_UserExists, UserExists.BoolToInteger)
x.Add(Name_UserSuspended, UserSuspended.BoolToInteger) x.Add(Name_UserSuspended, UserSuspended.BoolToInteger)
x.Add(Name_UserID, ID) x.Add(Name_UserID, ID)
@@ -709,7 +737,7 @@ BlockNullPicture:
x.Add(Name_ScriptData, ScriptData) x.Add(Name_ScriptData, ScriptData)
x.Add(Name_CollectionName, CollectionName) x.Add(Name_CollectionName, CollectionName)
x.Add(Name_LabelsName, Labels.ListToString("|", EDP.ReturnValue)) x.Add(Name_LabelsName, Labels.ListToString("|", EDP.ReturnValue))
x.Add(Name_DataMerging, DataMerging.BoolToInteger) x.Add(Name_Merged, DataMerging.BoolToInteger)
LoadUserInformation_OptionalFields(x, False) LoadUserInformation_OptionalFields(x, False)
@@ -731,25 +759,7 @@ BlockNullPicture:
Using x As New XmlFile(MyFileData, Protector.Modes.All, False) With {.XmlReadOnly = True, .AllowSameNames = True} Using x As New XmlFile(MyFileData, Protector.Modes.All, False) With {.XmlReadOnly = True, .AllowSameNames = True}
x.LoadData() x.LoadData()
If x.Count > 0 Then If x.Count > 0 Then
For Each v As EContainer In x : _ContentList.Add(New UserMedia(v, Me)) : Next
Dim fs$ = MyFile.CutPath.PathWithSeparator
Dim gfn As Func(Of String, String) = Function(Input) If(Input.IsEmptyString, String.Empty,
If(Input.Contains("\"), Input.CSFile.File, Input))
For Each v As EContainer In x
_ContentList.Add(New UserMedia With {
.Type = v.Attribute(Name_MediaType).Value.FromXML(Of Integer)(CInt(UTypes.Undefined)),
.State = v.Attribute(Name_MediaState).Value.FromXML(Of Integer)(CInt(UStates.Downloaded)),
.Attempts = v.Attribute(Name_MediaAttempts).Value.FromXML(Of Integer)(0),
.URL = v.Attribute(Name_MediaURL).Value,
.URL_BASE = v.Value,
.MD5 = v.Attribute(Name_MediaHash).Value,
.File = fs & gfn.Invoke(v.Attribute(Name_MediaFile).Value),
.Post = New UserPost With {
.ID = v.Attribute(Name_MediaPostID).Value,
.[Date] = AConvert(Of Date)(v.Attribute(Name_MediaPostDate).Value, ParsersDataDateProvider, Nothing)
}
})
Next
End If End If
_DataLoaded = True _DataLoaded = True
End Using End Using
@@ -763,21 +773,7 @@ BlockNullPicture:
If MyFileData.IsEmptyString Then Exit Sub If MyFileData.IsEmptyString Then Exit Sub
MyFileData.Exists(SFO.Path) MyFileData.Exists(SFO.Path)
Using x As New XmlFile With {.AllowSameNames = True, .Name = "Data"} Using x As New XmlFile With {.AllowSameNames = True, .Name = "Data"}
If _ContentList.Count > 0 Then If _ContentList.Count > 0 Then x.AddRange(_ContentList)
For Each i As UserMedia In _ContentList
x.Add(New EContainer("MediaData", i.URL_BASE, {New EAttribute(Name_MediaType, CInt(i.Type)),
New EAttribute(Name_MediaState, CInt(i.State)),
New EAttribute(Name_MediaAttempts, i.Attempts),
New EAttribute(Name_MediaURL, i.URL),
New EAttribute(Name_MediaHash, i.MD5),
New EAttribute(Name_MediaFile, i.File.File),
New EAttribute(Name_MediaPostID, i.Post.ID),
New EAttribute(Name_MediaPostDate, AConvert(Of String)(i.Post.Date, ParsersDataDateProvider, String.Empty))
}
)
)
Next
End If
x.Save(MyFileData) x.Save(MyFileData)
End Using End Using
Catch ex As Exception Catch ex As Exception
@@ -789,7 +785,7 @@ BlockNullPicture:
#Region "Open site, folder" #Region "Open site, folder"
Friend Overridable Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing) Implements IContentProvider.OpenSite Friend Overridable Sub OpenSite(Optional ByVal e As ErrorsDescriber = Nothing) Implements IContentProvider.OpenSite
Try Try
Dim URL$ = HOST.Source.GetUserUrl(Name, IsChannel) Dim URL$ = HOST.Source.GetUserUrl(Me, IsChannel)
If Not URL.IsEmptyString Then Process.Start(URL) If Not URL.IsEmptyString Then Process.Start(URL)
Catch ex As Exception Catch ex As Exception
If Not e.Exists Then e = New ErrorsDescriber(EDP.ShowAllMsg) If Not e.Exists Then e = New ErrorsDescriber(EDP.ShowAllMsg)
@@ -803,6 +799,7 @@ BlockNullPicture:
#Region "Download limits" #Region "Download limits"
Protected Enum DateResult : [Continue] : [Skip] : [Exit] : End Enum Protected Enum DateResult : [Continue] : [Skip] : [Exit] : End Enum
Friend Overridable Property DownloadTopCount As Integer? = Nothing Implements IUserData.DownloadTopCount, IPluginContentProvider.PostsNumberLimit Friend Overridable Property DownloadTopCount As Integer? = Nothing Implements IUserData.DownloadTopCount, IPluginContentProvider.PostsNumberLimit
Friend Overridable Property IncludeInTheFeed As Boolean = True
Private _DownloadDateFrom As Date? = Nothing Private _DownloadDateFrom As Date? = Nothing
Private _DownloadDateFromF As Date Private _DownloadDateFromF As Date
Friend Overridable Property DownloadDateFrom As Date? Implements IUserData.DownloadDateFrom, IPluginContentProvider.DownloadDateFrom Friend Overridable Property DownloadDateFrom As Date? Implements IUserData.DownloadDateFrom, IPluginContentProvider.DownloadDateFrom
@@ -825,10 +822,10 @@ BlockNullPicture:
If _DownloadDateTo.HasValue Then _DownloadDateToF = _DownloadDateTo.Value Else _DownloadDateToF = Date.MaxValue.Date If _DownloadDateTo.HasValue Then _DownloadDateToF = _DownloadDateTo.Value Else _DownloadDateToF = Date.MaxValue.Date
End Set End Set
End Property End Property
Protected Function CheckDatesLimit(ByVal DateString As String, ByVal DateProvider As IFormatProvider) As DateResult Protected Function CheckDatesLimit(ByVal DateObj As Object, ByVal DateProvider As IFormatProvider) As DateResult
Try Try
If (DownloadDateFrom.HasValue Or DownloadDateTo.HasValue) And Not DateString.IsEmptyString Then If (DownloadDateFrom.HasValue Or DownloadDateTo.HasValue) AndAlso ACheck(DateObj) Then
Dim td As Date? = AConvert(Of Date)(DateString, DateProvider, Nothing) Dim td As Date? = AConvert(Of Date)(DateObj, DateProvider, Nothing)
If td.HasValue Then If td.HasValue Then
If td.Value.ValueBetween(_DownloadDateFromF, _DownloadDateToF) Then If td.Value.ValueBetween(_DownloadDateFromF, _DownloadDateToF) Then
Return DateResult.Continue Return DateResult.Continue
@@ -841,12 +838,13 @@ BlockNullPicture:
End If End If
Return DateResult.Continue Return DateResult.Continue
Catch ex As Exception Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[UserDataBase.CheckDatesLimit({DateString})]", DateResult.Continue) Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[UserDataBase.CheckDatesLimit({If(TypeOf DateObj Is String, CStr(DateObj), "?")})]", DateResult.Continue)
End Try End Try
End Function End Function
#End Region #End Region
#Region "Download functions and options" #Region "Download functions and options"
Protected Responser As Response Protected Responser As Response
Protected UseResponserClient As Boolean = False
Friend Overridable Sub DownloadData(ByVal Token As CancellationToken) Implements IContentProvider.DownloadData Friend Overridable Sub DownloadData(ByVal Token As CancellationToken) Implements IContentProvider.DownloadData
Dim Canceled As Boolean = False Dim Canceled As Boolean = False
_ExternalCompatibilityToken = Token _ExternalCompatibilityToken = Token
@@ -856,6 +854,9 @@ BlockNullPicture:
If Not Responser Is Nothing Then Responser.Dispose() If Not Responser Is Nothing Then Responser.Dispose()
Responser = New Response Responser = New Response
If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser) If Not HOST.Responser Is Nothing Then Responser.Copy(HOST.Responser)
'TODO: UserDataBase remove [Responser.DecodersError]
Responser.DecodersError = New ErrorsDescriber(EDP.SendInLog + EDP.ReturnValue) With {
.DeclaredMessage = New MMessage($"SymbolsConverter error: [{ToStringForLog()}]", ToStringForLog())}
Dim UpPic As Boolean = Settings.ViewModeIsPicture AndAlso GetPicture(Of Image)(False) Is Nothing Dim UpPic As Boolean = Settings.ViewModeIsPicture AndAlso GetPicture(Of Image)(False) Is Nothing
Dim sEnvir() As Boolean = {UserExists, UserSuspended} Dim sEnvir() As Boolean = {UserExists, UserSuspended}
@@ -867,6 +868,7 @@ BlockNullPicture:
DownloadedVideos(False) = 0 DownloadedVideos(False) = 0
_TempMediaList.Clear() _TempMediaList.Clear()
_TempPostsList.Clear() _TempPostsList.Clear()
LatestData.Clear()
Dim __SaveData As Boolean = Not CreatedByChannel Or Not Settings.FromChannelDownloadTopUse Dim __SaveData As Boolean = Not CreatedByChannel Or Not Settings.FromChannelDownloadTopUse
LoadContentInformation() LoadContentInformation()
@@ -878,11 +880,10 @@ BlockNullPicture:
ThrowAny(Token) ThrowAny(Token)
DownloadDataF(Token) DownloadDataF(Token)
ThrowAny(Token) ThrowAny(Token)
If Settings.ReparseMissingInTheRoutine Then ReparseMissing(Token) : ThrowAny(Token)
Else Else
'PENDING: UserDataBase ReparseMissing (DownloadDataF) ReparseMissing(Token)
'ReparseMissing(Token)
End If End If
'_TempMediaList.ListAddList(ContentMissing, LNC)
If _TempMediaList.Count > 0 Then If _TempMediaList.Count > 0 Then
If Not DownloadImages Then _TempMediaList.RemoveAll(Function(m) m.Type = UTypes.GIF Or m.Type = UTypes.Picture) If Not DownloadImages Then _TempMediaList.RemoveAll(Function(m) m.Type = UTypes.GIF Or m.Type = UTypes.Picture)
@@ -893,12 +894,13 @@ BlockNullPicture:
ReparseVideo(Token) ReparseVideo(Token)
ThrowAny(Token) ThrowAny(Token)
If _TempPostsList.Count > 0 And __SaveData Then TextSaver.SaveTextToFile(_TempPostsList.ListToString(Environment.NewLine), MyFilePosts, True,, EDP.None) If _TempPostsList.Count > 0 And Not DownloadMissingOnly And __SaveData Then _
TextSaver.SaveTextToFile(_TempPostsList.ListToString(Environment.NewLine), MyFilePosts, True,, EDP.None)
_ContentNew.ListAddList(_TempMediaList, LAP.ClearBeforeAdd) _ContentNew.ListAddList(_TempMediaList, LAP.ClearBeforeAdd)
DownloadContent(Token) DownloadContent(Token)
ThrowIfDisposed() ThrowIfDisposed()
LatestData.ListAddList(_ContentNew.Where(_downContent), LNC) If IncludeInTheFeed Then LatestData.ListAddList(_ContentNew.Where(_downContent), LNC)
Dim mcb& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0) Dim mcb& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0)
_ContentList.ListAddList(_ContentNew.Where(Function(c) _downContent(c) Or MissingFinder(c)), LNC) _ContentList.ListAddList(_ContentNew.Where(Function(c) _downContent(c) Or MissingFinder(c)), LNC)
Dim mca& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0) Dim mca& = If(ContentMissingExists, _ContentList.LongCount(Function(c) MissingFinder(c)), 0)
@@ -923,7 +925,7 @@ BlockNullPicture:
ThrowIfDisposed() ThrowIfDisposed()
If UpPic Or EnvirChanged.Invoke Then OnUserUpdated() If UpPic Or EnvirChanged.Invoke Then OnUserUpdated()
Catch oex As OperationCanceledException When Token.IsCancellationRequested Catch oex As OperationCanceledException When Token.IsCancellationRequested
MyMainLOG = $"{Site} - {Name}: downloading canceled" MyMainLOG = $"{ToStringForLog()}: downloading canceled"
Canceled = True Canceled = True
Catch dex As ObjectDisposedException When Disposed Catch dex As ObjectDisposedException When Disposed
Canceled = True Canceled = True
@@ -954,9 +956,24 @@ BlockNullPicture:
Protected MustOverride Sub DownloadDataF(ByVal Token As CancellationToken) Protected MustOverride Sub DownloadDataF(ByVal Token As CancellationToken)
Protected Overridable Sub ReparseVideo(ByVal Token As CancellationToken) Protected Overridable Sub ReparseVideo(ByVal Token As CancellationToken)
End Sub End Sub
''' <summary>
''' Missing posts must be collected from [<see cref="_ContentList"/>].<br/>
''' Reparsed post must be added to [<see cref="_TempMediaList"/>].<br/>
''' At the end of the function, reparsed posts must be removed from [<see cref="_ContentList"/>].
''' </summary>
Protected Overridable Sub ReparseMissing(ByVal Token As CancellationToken) Protected Overridable Sub ReparseMissing(ByVal Token As CancellationToken)
End Sub End Sub
Protected MustOverride Sub DownloadContent(ByVal Token As CancellationToken) Protected MustOverride Sub DownloadContent(ByVal Token As CancellationToken)
Private NotInheritable Class OptionalWebClient : Inherits DownloadObjects.WebClient2
Friend Sub New(ByRef Source As UserDataBase)
UseResponserClient = Source.UseResponserClient
If UseResponserClient Then
RC = Source.Responser
Else
WC = New WebClient
End If
End Sub
End Class
Protected Sub DownloadContentDefault(ByVal Token As CancellationToken) Protected Sub DownloadContentDefault(ByVal Token As CancellationToken)
Try Try
Dim i% Dim i%
@@ -972,8 +989,9 @@ BlockNullPicture:
Dim __isVideo As Boolean Dim __isVideo As Boolean
Dim f As SFile Dim f As SFile
Dim v As UserMedia Dim v As UserMedia
Using w As New WebClient
If vsf Then SFileShares.SFileExists($"{MyDir}\Video\", SFO.Path) Using w As New OptionalWebClient(Me)
If vsf Then CSFileP($"{MyDir}\Video\").Exists(SFO.Path)
Progress.Maximum += _ContentNew.Count Progress.Maximum += _ContentNew.Count
For i = 0 To _ContentNew.Count - 1 For i = 0 To _ContentNew.Count - 1
ThrowAny(Token) ThrowAny(Token)
@@ -989,7 +1007,7 @@ BlockNullPicture:
If v.URL_BASE.IsEmptyString Then v.URL_BASE = v.URL If v.URL_BASE.IsEmptyString Then v.URL_BASE = v.URL
If Not v.File.IsEmptyString And Not v.URL_BASE.IsEmptyString Then If Not v.File.IsEmptyString And Not v.URL.IsEmptyString Then
Try Try
__isVideo = v.Type = UTypes.Video Or f.Extension = "mp4" __isVideo = v.Type = UTypes.Video Or f.Extension = "mp4"
@@ -1011,7 +1029,13 @@ BlockNullPicture:
f.Path = $"{f.PathWithSeparator}Video" f.Path = $"{f.PathWithSeparator}Video"
If Not v.SpecialFolder.IsEmptyString Then f.Exists(SFO.Path) If Not v.SpecialFolder.IsEmptyString Then f.Exists(SFO.Path)
End If End If
w.DownloadFile(v.URL_BASE, f.ToString)
If v.Type = UTypes.m3u8 And UseInternalM3U8Function Then
f = DownloadM3U8(v.URL, v, f)
If f.IsEmptyString Then Throw New Exception("M3U8 download failed")
Else
w.DownloadFile(v.URL, f.ToString)
End If
If __isVideo Then If __isVideo Then
v.Type = UTypes.Video v.Type = UTypes.Video
@@ -1027,7 +1051,7 @@ BlockNullPicture:
Catch wex As Exception Catch wex As Exception
v.Attempts += 1 v.Attempts += 1
v.State = UStates.Missing v.State = UStates.Missing
If MissingErrorsAdd Then ErrorDownloading(f, v.URL_BASE) If MissingErrorsAdd Then ErrorDownloading(f, v.URL)
End Try End Try
Else Else
v.State = UStates.Skipped v.State = UStates.Skipped
@@ -1052,15 +1076,31 @@ BlockNullPicture:
HasError = True HasError = True
End Try End Try
End Sub End Sub
Protected UseInternalM3U8Function As Boolean = False
Protected Overridable Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile) As SFile
Return Nothing
End Function
Protected Const EXCEPTION_OPERATION_CANCELED As Integer = -1
''' <param name="RDE">Request DownloadingException</param> ''' <param name="RDE">Request DownloadingException</param>
Protected Sub ProcessException(ByVal ex As Exception, ByVal Token As CancellationToken, ByVal Message As String, Optional ByVal RDE As Boolean = True) ''' <returns>0 - exit</returns>
Protected Function ProcessException(ByVal ex As Exception, ByVal Token As CancellationToken, ByVal Message As String,
Optional ByVal RDE As Boolean = True, Optional ByVal EObj As Object = Nothing,
Optional ByVal ThrowEx As Boolean = True) As Integer
If Not ((TypeOf ex Is OperationCanceledException And Token.IsCancellationRequested) Or If Not ((TypeOf ex Is OperationCanceledException And Token.IsCancellationRequested) Or
(TypeOf ex Is ObjectDisposedException And Disposed)) Then (TypeOf ex Is ObjectDisposedException And Disposed)) Then
If RDE AndAlso DownloadingException(ex, Message, True) = 0 Then LogError(ex, Message) : HasError = True If RDE Then
Dim v% = DownloadingException(ex, Message, True, EObj)
If v = 0 Then LogError(ex, Message) : HasError = True
Return v
End If
Else
'URGENT: UserDataBase.ProcessException [Throw ex]
If ThrowEx Then Throw ex Else Return EXCEPTION_OPERATION_CANCELED
End If End If
End Sub Return 0
End Function
''' <summary>0 - Execute LogError and set HasError</summary> ''' <summary>0 - Execute LogError and set HasError</summary>
Protected MustOverride Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False) As Integer Protected MustOverride Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False, Optional ByVal EObj As Object = Nothing) As Integer
Protected Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile Protected Function ChangeFileNameByProvider(ByVal f As SFile, ByVal m As UserMedia) As SFile
Dim ff As SFile = Nothing Dim ff As SFile = Nothing
Try Try
@@ -1102,7 +1142,7 @@ BlockNullPicture:
End Sub End Sub
#End Region #End Region
#Region "Delete, Move, Merge, Copy" #Region "Delete, Move, Merge, Copy"
Friend Overridable Function Delete() As Integer Implements IUserData.Delete Friend Overridable Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer Implements IUserData.Delete
Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path) Dim f As SFile = SFile.GetPath(MyFile.CutPath.Path)
If f.Exists(SFO.Path, False) AndAlso (User.Merged OrElse f.Delete(SFO.Path, Settings.DeleteMode)) Then If f.Exists(SFO.Path, False) AndAlso (User.Merged OrElse f.Delete(SFO.Path, Settings.DeleteMode)) Then
If Not IncludedInCollection Then MainFrameObj.ImageHandler(Me, False) If Not IncludedInCollection Then MainFrameObj.ImageHandler(Me, False)
@@ -1116,44 +1156,51 @@ BlockNullPicture:
Return 0 Return 0
End If End If
End Function End Function
Friend Overridable Function MoveFiles(ByVal __CollectionName As String) As Boolean Implements IUserData.MoveFiles Friend Overridable Function MoveFiles(ByVal __CollectionName As String, ByVal __SpecialCollectionPath As SFile) As Boolean Implements IUserData.MoveFiles
Dim UserBefore As UserInfo = User Dim UserBefore As UserInfo = User
Dim Removed As Boolean = True Dim Removed As Boolean = True
Dim _TurnBack As Boolean = False Dim _TurnBack As Boolean = False
Try Try
Dim f As SFile Dim f As SFile
Dim v As Boolean = IsVirtual
If IncludedInCollection Then If IncludedInCollection Then
Settings.Users.Add(Me) Settings.Users.Add(Me)
Removed = False Removed = False
User.CollectionName = String.Empty User.CollectionName = String.Empty
User.IncludedInCollection = False User.SpecialCollectionPath = String.Empty
User.UserModel = UsageModel.Default
User.CollectionModel = UsageModel.Default
Else Else
Settings.Users.Remove(Me) Settings.Users.Remove(Me)
Removed = True Removed = True
User.CollectionName = __CollectionName User.CollectionName = __CollectionName
User.IncludedInCollection = True User.SpecialCollectionPath = __SpecialCollectionPath
User.SpecialPath = Nothing If Not IsVirtual Then User.SpecialPath = Nothing
End If End If
_TurnBack = True _TurnBack = True
User.UpdateUserFile() User.UpdateUserFile()
f = User.File.CutPath(, EDP.ThrowException)
If f.Exists(SFO.Path, False) Then If Not v Then
If If(SFile.GetFiles(f,, SearchOption.AllDirectories), New List(Of SFile)).Count > 0 AndAlso f = User.File.CutPath(, EDP.ThrowException)
MsgBoxE({$"Destination directory [{f.Path}] already exists and contains files!" & vbCr & If f.Exists(SFO.Path, False) Then
"By continuing, this directory and all files will be deleted", If If(SFile.GetFiles(f,, SearchOption.AllDirectories), New List(Of SFile)).Count > 0 AndAlso
"Destination directory is not empty!"}, MsgBoxStyle.Exclamation,,, {"Delete", "Cancel"}) = 1 Then MsgBoxE({$"Destination directory [{f.Path}] already exists and contains files!" & vbCr &
MsgBoxE("Operation canceled", MsgBoxStyle.Exclamation) "By continuing, this directory and all files will be deleted",
User = UserBefore "Destination directory is not empty!"}, MsgBoxStyle.Exclamation,,, {"Delete", "Cancel"}) = 1 Then
If Removed Then Settings.Users.Add(Me) Else Settings.Users.Remove(Me) MsgBoxE("Operation canceled", MsgBoxStyle.Exclamation)
_TurnBack = False User = UserBefore
Return False If Removed Then Settings.Users.Add(Me) Else Settings.Users.Remove(Me)
_TurnBack = False
Return False
End If
f.Delete(SFO.Path, Settings.DeleteMode, EDP.ThrowException)
End If End If
f.Delete(SFO.Path, Settings.DeleteMode, EDP.ThrowException) f.CutPath.Exists(SFO.Path)
Directory.Move(UserBefore.File.CutPath(, EDP.ThrowException).Path, f.Path)
If Not ScriptData.IsEmptyString AndAlso ScriptData.Contains(UserBefore.File.PathNoSeparator) Then _
ScriptData = ScriptData.Replace(UserBefore.File.PathNoSeparator, MyFile.PathNoSeparator)
End If End If
f.CutPath.Exists(SFO.Path)
Directory.Move(UserBefore.File.CutPath(, EDP.ThrowException).Path, f.Path)
If Not ScriptData.IsEmptyString AndAlso ScriptData.Contains(UserBefore.File.PathNoSeparator) Then _
ScriptData = ScriptData.Replace(UserBefore.File.PathNoSeparator, MyFile.PathNoSeparator)
Settings.UsersList.Remove(UserBefore) Settings.UsersList.Remove(UserBefore)
Settings.UpdateUsersList(User) Settings.UpdateUsersList(User)
UpdateUserInformation() UpdateUserInformation()
@@ -1270,7 +1317,7 @@ BlockNullPicture:
#End Region #End Region
#Region "Errors functions" #Region "Errors functions"
Protected Sub LogError(ByVal ex As Exception, ByVal Message As String) Protected Sub LogError(ByVal ex As Exception, ByVal Message As String)
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"{IIf(IncludedInCollection, $"{CollectionName}-", String.Empty)}{Site} - {Name}: {Message}") ErrorsDescriber.Execute(EDP.SendInLog, ex, $"{ToStringForLog()}: {Message}")
End Sub End Sub
Protected Sub ErrorDownloading(ByVal f As SFile, ByVal URL As String) Protected Sub ErrorDownloading(ByVal f As SFile, ByVal URL As String)
If Not f.Exists Then MyMainLOG = $"Error downloading from [{URL}] to [{f}]" If Not f.Exists Then MyMainLOG = $"Error downloading from [{URL}] to [{f}]"
@@ -1285,11 +1332,14 @@ BlockNullPicture:
End Sub End Sub
''' <exception cref="OperationCanceledException"></exception> ''' <exception cref="OperationCanceledException"></exception>
''' <exception cref="ObjectDisposedException"></exception> ''' <exception cref="ObjectDisposedException"></exception>
Friend Overloads Sub ThrowAny(ByVal Token As CancellationToken) Friend Overridable Overloads Sub ThrowAny(ByVal Token As CancellationToken)
Token.ThrowIfCancellationRequested() Token.ThrowIfCancellationRequested()
ThrowIfDisposed() ThrowIfDisposed()
End Sub End Sub
#End Region #End Region
Protected Function ToStringForLog() As String
Return $"{IIf(IncludedInCollection, $"[{CollectionName}] - ", String.Empty)}[{Site}] - {Name}"
End Function
Public Overrides Function ToString() As String Public Overrides Function ToString() As String
If IsCollection Then If IsCollection Then
Return CollectionName Return CollectionName
@@ -1308,8 +1358,8 @@ BlockNullPicture:
Return hcStr.GetHashCode Return hcStr.GetHashCode
End Function End Function
#Region "Buttons actions" #Region "Buttons actions"
Private Sub BTT_CONTEXT_DOWN_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_DOWN.Click Private Sub BTT_CONTEXT_DOWN_KeyClick(sender As Object, e As MyKeyEventArgs) Handles BTT_CONTEXT_DOWN.KeyClick
Downloader.Add(Me) Downloader.Add(Me, e.IncludeInTheFeed)
End Sub End Sub
Private Sub BTT_CONTEXT_EDIT_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_EDIT.Click Private Sub BTT_CONTEXT_EDIT_Click(sender As Object, e As EventArgs) Handles BTT_CONTEXT_EDIT.Click
Using f As New Editors.UserCreatorForm(Me) Using f As New Editors.UserCreatorForm(Me)
@@ -1386,6 +1436,7 @@ BlockNullPicture:
End Sub End Sub
#End Region #End Region
End Class End Class
#Region "Base interfaces"
Friend Interface IContentProvider Friend Interface IContentProvider
ReadOnly Property Site As String ReadOnly Property Site As String
Property Name As String Property Name As String
@@ -1408,6 +1459,9 @@ BlockNullPicture:
ReadOnly Property IsCollection As Boolean ReadOnly Property IsCollection As Boolean
Property CollectionName As String Property CollectionName As String
ReadOnly Property IncludedInCollection As Boolean ReadOnly Property IncludedInCollection As Boolean
ReadOnly Property UserModel As UsageModel
ReadOnly Property CollectionModel As UsageModel
ReadOnly Property IsVirtual As Boolean
ReadOnly Property Labels As List(Of String) ReadOnly Property Labels As List(Of String)
#End Region #End Region
ReadOnly Property IsChannel As Boolean ReadOnly Property IsChannel As Boolean
@@ -1439,8 +1493,8 @@ BlockNullPicture:
''' 2 - Collection removed<br/> ''' 2 - Collection removed<br/>
''' 3 - Collection split ''' 3 - Collection split
''' </summary> ''' </summary>
Function Delete() As Integer Function Delete(Optional ByVal Multiple As Boolean = False, Optional ByVal CollectionValue As Integer = -1) As Integer
Function MoveFiles(ByVal CollectionName As String) As Boolean Function MoveFiles(ByVal CollectionName As String, ByVal SpecialCollectionPath As SFile) As Boolean
Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
Sub OpenFolder() Sub OpenFolder()
ReadOnly Property Self As IUserData ReadOnly Property Self As IUserData
@@ -1463,4 +1517,5 @@ BlockNullPicture:
Property SkipExistsUsers As Boolean Property SkipExistsUsers As Boolean
Property SaveToCache As Boolean Property SaveToCache As Boolean
End Interface End Interface
#End Region
End Namespace End Namespace

View File

@@ -0,0 +1,86 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Forms
Imports ADB = PersonalUtilities.Forms.Controls.Base.ActionButton.DefaultButtons
Namespace API.BaseObjects
Friend Interface IDomainContainer
ReadOnly Property Icon As Icon
ReadOnly Property Site As String
ReadOnly Property Domains As List(Of String)
ReadOnly Property DomainsTemp As List(Of String)
ReadOnly Property DomainsDefault As String
ReadOnly Property DomainsSettingProp As Plugin.PropertyValue
Property DomainsChanged As Boolean
Property Initialized As Boolean
Property DomainsUpdateInProgress As Boolean
Property DomainsUpdatedBySite As Boolean
Sub UpdateDomains()
End Interface
Friend NotInheritable Class DomainContainer
Private Sub New()
End Sub
Friend Shared Sub EndInit(ByVal s As IDomainContainer)
If ACheck(s.DomainsSettingProp.Value) Then s.Domains.ListAddList(CStr(s.DomainsSettingProp.Value).Split("|"), LAP.NotContainsOnly)
End Sub
Friend Overloads Shared Sub UpdateDomains(ByVal s As IDomainContainer)
UpdateDomains(s, Nothing, True)
End Sub
Friend Overloads Shared Sub UpdateDomains(ByVal s As IDomainContainer, ByVal NewDomains As IEnumerable(Of String), ByVal Internal As Boolean)
With s
If Not .Initialized Or (.DomainsUpdatedBySite And Not Internal) Then Exit Sub
If Not .DomainsUpdateInProgress Then
.DomainsUpdateInProgress = True
.Domains.ListAddList(.DomainsDefault.Split("|"), LAP.NotContainsOnly)
.Domains.ListAddList(NewDomains, LAP.NotContainsOnly)
.DomainsSettingProp.Value = .Domains.ListToString("|")
If Not Internal Then .DomainsUpdatedBySite = True
.DomainsUpdateInProgress = False
End If
End With
End Sub
Friend Shared Sub Update(ByVal s As IDomainContainer)
With s
If .DomainsChanged Then
.Domains.Clear()
.Domains.ListAddList(.DomainsTemp, LAP.NotContainsOnly)
.UpdateDomains()
End If
End With
End Sub
Friend Shared Sub EndEdit(ByVal s As IDomainContainer)
s.DomainsTemp.ListAddList(s.Domains, LAP.ClearBeforeAdd, LAP.NotContainsOnly)
s.DomainsChanged = False
End Sub
Friend Shared Sub OpenSettingsForm(ByVal s As IDomainContainer)
Dim __add As EventHandler(Of SimpleListFormEventArgs) = Sub(sender, e) e.ValueNew = InputBoxE($"Enter a new domain using the pattern [{s.Site}.com]:", "New domain").IfNullOrEmptyE(Nothing)
Dim __delete As EventHandler(Of SimpleListFormEventArgs) = Sub(sender, e)
Dim n$ = AConvert(Of String)(e.ValueCurrent, AModes.Var, String.Empty)
e.Result = MsgBoxE({$"Are you sure you want to delete the [{n}] domain?",
"Removing domains"}, vbYesNo) = vbYes
End Sub
Using f As New SimpleListForm(Of String)(If(s.DomainsChanged, s.DomainsTemp, s.Domains), Settings.Design) With {
.Buttons = {ADB.Add, ADB.Delete},
.Mode = SimpleListFormModes.Remaining,
.FormText = s.Site,
.Icon = s.Icon,
.LocationOnly = True,
.Size = New Size(400, 330),
.DesignXMLNode = s.Site
}
AddHandler f.AddClick, __add
AddHandler f.DeleteClick, __delete
f.ShowDialog()
If f.DialogResult = DialogResult.OK Then
s.DomainsChanged = True
s.DomainsTemp.ListAddList(f.DataResult, LAP.ClearBeforeAdd, LAP.NotContainsOnly)
End If
End Using
End Sub
End Class
End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,9 +6,9 @@
' '
' 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.Functions.RegularExpressions
Imports System.Net Imports System.Net
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Gfycat Namespace API.Gfycat
Friend NotInheritable Class Envir Friend NotInheritable Class Envir
Private Sub New() Private Sub New()

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,12 +6,12 @@
' '
' 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.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.WebDocuments.JSON
Imports System.Net Imports System.Net
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.API.Imgur.Declarations Imports SCrawler.API.Imgur.Declarations
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Documents.JSON
Namespace API.Imgur Namespace API.Imgur
Namespace Declarations Namespace Declarations
Friend Module Imgur_Declarations Friend Module Imgur_Declarations
@@ -67,7 +67,7 @@ Namespace API.Imgur
Return DownloadingException(ex, $"[API.Imgur.Envir.GetImage({URL})]", String.Empty, e) Return DownloadingException(ex, $"[API.Imgur.Envir.GetImage({URL})]", String.Empty, e)
End Try End Try
End Function End Function
Friend Shared Function GetVideoInfo(ByVal URL As String) As IEnumerable(Of UserMedia) Friend Shared Function GetVideoInfo(ByVal URL As String, Optional ByVal e As ErrorsDescriber = Nothing) As IEnumerable(Of UserMedia)
Try Try
If Not URL.IsEmptyString AndAlso URL.ToLower.Contains("imgur") AndAlso Not Settings.ImgurClientID.IsEmptyString Then If Not URL.IsEmptyString AndAlso URL.ToLower.Contains("imgur") AndAlso Not Settings.ImgurClientID.IsEmptyString Then
Dim img$ = GetImage(URL, EDP.ReturnValue) Dim img$ = GetImage(URL, EDP.ReturnValue)
@@ -79,7 +79,8 @@ Namespace API.Imgur
End If End If
Return Nothing Return Nothing
Catch ex As Exception Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.ShowMainMsg + EDP.SendInLog + EDP.ReturnValue, ex, "Imgur standalone downloader: fetch media error") If Not e.Exists Then e = EDP.LogMessageValue
Return ErrorsDescriber.Execute(e, ex, "Imgur standalone downloader: fetch media error")
End Try End Try
End Function End Function
Private Shared Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Private Shared Function DownloadingException(ByVal ex As Exception, ByVal Message As String,

View File

@@ -0,0 +1,135 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Namespace API.Instagram
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
Partial Friend Class AdditionalSettingsForm : Inherits System.Windows.Forms.Form
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
Me.CH_DOWN_TIME = New System.Windows.Forms.CheckBox()
Me.CH_DOWN_TAG = New System.Windows.Forms.CheckBox()
Me.CH_DOWN_SAVED = New System.Windows.Forms.CheckBox()
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
CONTAINER_MAIN.ContentPanel.SuspendLayout()
CONTAINER_MAIN.SuspendLayout()
TP_MAIN.SuspendLayout()
Me.SuspendLayout()
'
'CONTAINER_MAIN
'
'
'CONTAINER_MAIN.ContentPanel
'
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(234, 78)
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
CONTAINER_MAIN.LeftToolStripPanelVisible = False
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
CONTAINER_MAIN.RightToolStripPanelVisible = False
CONTAINER_MAIN.Size = New System.Drawing.Size(234, 103)
CONTAINER_MAIN.TabIndex = 0
CONTAINER_MAIN.TopToolStripPanelVisible = False
'
'TP_MAIN
'
TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
TP_MAIN.ColumnCount = 1
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Absolute, 20.0!))
TP_MAIN.Controls.Add(Me.CH_DOWN_TIME, 0, 0)
TP_MAIN.Controls.Add(Me.CH_DOWN_TAG, 0, 1)
TP_MAIN.Controls.Add(Me.CH_DOWN_SAVED, 0, 2)
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
TP_MAIN.Location = New System.Drawing.Point(0, 0)
TP_MAIN.Name = "TP_MAIN"
TP_MAIN.RowCount = 4
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.Size = New System.Drawing.Size(234, 78)
TP_MAIN.TabIndex = 0
'
'CH_DOWN_TIME
'
Me.CH_DOWN_TIME.AutoSize = True
Me.CH_DOWN_TIME.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_DOWN_TIME.Location = New System.Drawing.Point(4, 4)
Me.CH_DOWN_TIME.Name = "CH_DOWN_TIME"
Me.CH_DOWN_TIME.Size = New System.Drawing.Size(226, 19)
Me.CH_DOWN_TIME.TabIndex = 0
Me.CH_DOWN_TIME.Text = "Download Timeline"
Me.CH_DOWN_TIME.UseVisualStyleBackColor = True
'
'CH_DOWN_TAG
'
Me.CH_DOWN_TAG.AutoSize = True
Me.CH_DOWN_TAG.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_DOWN_TAG.Location = New System.Drawing.Point(4, 30)
Me.CH_DOWN_TAG.Name = "CH_DOWN_TAG"
Me.CH_DOWN_TAG.Size = New System.Drawing.Size(226, 19)
Me.CH_DOWN_TAG.TabIndex = 1
Me.CH_DOWN_TAG.Text = "Download Stories and Tagged data"
Me.CH_DOWN_TAG.UseVisualStyleBackColor = True
'
'CH_DOWN_SAVED
'
Me.CH_DOWN_SAVED.AutoSize = True
Me.CH_DOWN_SAVED.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_DOWN_SAVED.Location = New System.Drawing.Point(4, 56)
Me.CH_DOWN_SAVED.Name = "CH_DOWN_SAVED"
Me.CH_DOWN_SAVED.Size = New System.Drawing.Size(226, 19)
Me.CH_DOWN_SAVED.TabIndex = 2
Me.CH_DOWN_SAVED.Text = "Download saved posts"
Me.CH_DOWN_SAVED.UseVisualStyleBackColor = True
'
'AdditionalSettingsForm
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(234, 103)
Me.Controls.Add(CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = Global.SCrawler.My.Resources.SiteResources.InstagramIcon_32
Me.KeyPreview = True
Me.MaximizeBox = False
Me.MaximumSize = New System.Drawing.Size(250, 142)
Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(250, 142)
Me.Name = "AdditionalSettingsForm"
Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
Me.Text = "Additional settings"
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
CONTAINER_MAIN.ResumeLayout(False)
CONTAINER_MAIN.PerformLayout()
TP_MAIN.ResumeLayout(False)
TP_MAIN.PerformLayout()
Me.ResumeLayout(False)
End Sub
Private WithEvents CH_DOWN_TIME As CheckBox
Private WithEvents CH_DOWN_TAG As CheckBox
Private WithEvents CH_DOWN_SAVED As CheckBox
End Class
End Namespace

View File

@@ -0,0 +1,41 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Forms
Namespace API.Instagram
Friend Class AdditionalSettingsForm
Private WithEvents MyDefs As DefaultFormOptions
Friend Property MyParameters As SettingsExchangeOptions
Friend Sub New(ByVal Parameters As SettingsExchangeOptions)
InitializeComponent()
MyParameters = Parameters
MyDefs = New DefaultFormOptions(Me, Settings.Design)
End Sub
Private Sub MyForm_Load(sender As Object, e As EventArgs) Handles Me.Load
With MyDefs
.MyViewInitialize(True)
.AddOkCancelToolbar()
With MyParameters
CH_DOWN_TIME.Checked = .DownloadTimeline
CH_DOWN_TAG.Checked = .DownloadStoriesTagged
CH_DOWN_SAVED.Checked = .DownloadSaved
End With
.EndLoaderOperations()
End With
End Sub
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
MyParameters = New SettingsExchangeOptions With {
.DownloadTimeline = CH_DOWN_TIME.Checked,
.DownloadStoriesTagged = CH_DOWN_TAG.Checked,
.DownloadSaved = CH_DOWN_SAVED.Checked,
.Changed = True
}
MyDefs.CloseForm()
End Sub
End Class
End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -10,6 +10,7 @@ Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Instagram Namespace API.Instagram
Friend Module Declarations Friend Module Declarations
Friend Const InstagramSite As String = "Instagram" Friend Const InstagramSite As String = "Instagram"
Friend Const InstagramSiteKey As String = "AndyProgram_Instagram"
Friend ReadOnly FilesPattern As RParams = RParams.DMS(".+?([^/\?]+?\.[\w\d]{3,4})(?=(\?|\Z))", 1, EDP.ReturnValue) Friend ReadOnly FilesPattern As RParams = RParams.DMS(".+?([^/\?]+?\.[\w\d]{3,4})(?=(\?|\Z))", 1, EDP.ReturnValue)
Friend ReadOnly Property DateProvider As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicode(v)) Friend ReadOnly Property DateProvider As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicode(v))
End Module End Module

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -95,13 +95,13 @@ Namespace API.Instagram
Me.ClientSize = New System.Drawing.Size(260, 78) Me.ClientSize = New System.Drawing.Size(260, 78)
Me.Controls.Add(CONTAINER_MAIN) Me.Controls.Add(CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = Global.SCrawler.My.Resources.SiteResources.InstagramIcon_32
Me.KeyPreview = True Me.KeyPreview = True
Me.MaximizeBox = False Me.MaximizeBox = False
Me.MaximumSize = New System.Drawing.Size(276, 117) Me.MaximumSize = New System.Drawing.Size(276, 117)
Me.MinimizeBox = False Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(276, 117) Me.MinimumSize = New System.Drawing.Size(276, 117)
Me.Name = "OptionsForm" Me.Name = "OptionsForm"
Me.ShowIcon = False
Me.ShowInTaskbar = False Me.ShowInTaskbar = False
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
Me.Text = "Options" Me.Text = "Options"

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -0,0 +1,23 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Namespace API.Instagram
Friend Structure SettingsExchangeOptions
Friend DownloadTimeline As Boolean
Friend DownloadStoriesTagged As Boolean
Friend DownloadSaved As Boolean
Friend Changed As Boolean
Friend Sub New(ByVal Source As SiteSettings)
With Source
DownloadTimeline = .DownloadTimeline
DownloadStoriesTagged = .DownloadStoriesTagged
DownloadSaved = .DownloadSaved
End With
End Sub
End Structure
End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -10,24 +10,23 @@ Imports SCrawler.API.Base
Imports SCrawler.Plugin Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Forms Imports PersonalUtilities.Forms
Imports PersonalUtilities.Tools.WEB
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.XML.Base Imports PersonalUtilities.Functions.XML.Base
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Instagram Namespace API.Instagram
<Manifest("AndyProgram_Instagram"), UseClassAsIs, SeparatedTasks(1), SavedPosts, SpecialForm(False)> <Manifest(InstagramSiteKey), SeparatedTasks(1), SavedPosts, SpecialForm(False), SpecialForm(True)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations" #Region "Declarations"
#Region "Images" #Region "Images"
Friend Overrides ReadOnly Property Icon As Icon Friend Overrides ReadOnly Property Icon As Icon
Get Get
Return My.Resources.InstagramIcon Return My.Resources.SiteResources.InstagramIcon_32
End Get End Get
End Property End Property
Friend Overrides ReadOnly Property Image As Image Friend Overrides ReadOnly Property Image As Image
Get Get
Return My.Resources.InstagramPic76 Return My.Resources.SiteResources.InstagramPic_76
End Get End Get
End Property End Property
#End Region #End Region
@@ -78,23 +77,23 @@ Namespace API.Instagram
End Class End Class
#End Region #End Region
#Region "Authorization properties" #Region "Authorization properties"
<PropertyOption(ControlText:="Hash", ControlToolTip:="Instagram session hash", IsAuth:=True), PXML("InstaHash"), ControlNumber(0)> <PropertyOption(ControlText:="Hash", ControlToolTip:="Instagram session hash", IsAuth:=True, AllowNull:=False), PXML("InstaHash"), ControlNumber(0)>
Friend ReadOnly Property Hash As PropertyValue Friend ReadOnly Property Hash As PropertyValue
<PropertyOption(ControlText:="Hash 2", ControlToolTip:="Instagram session hash for saved posts", IsAuth:=True), PXML("InstaHashSavedPosts"), ControlNumber(1)> Private Const HashSavedPosts_Text As String = "Hash 2"
<PropertyOption(ControlText:=HashSavedPosts_Text, ControlToolTip:="Instagram session hash for saved posts", IsAuth:=True), PXML("InstaHashSavedPosts"), ControlNumber(1)>
Friend ReadOnly Property HashSavedPosts As PropertyValue Friend ReadOnly Property HashSavedPosts As PropertyValue
<PropertyOption(ControlText:="x-csrftoken", ControlToolTip:="Instagram token for tagged data", IsAuth:=True), ControlNumber(2)> <PropertyOption(ControlText:="x-csrftoken", ControlToolTip:="Instagram token for tagged data", IsAuth:=True, AllowNull:=False), ControlNumber(2)>
Friend ReadOnly Property CSRF_TOKEN As PropertyValue Friend ReadOnly Property CSRF_TOKEN As PropertyValue
<PropertyOption(ControlText:="x-ig-app-id", IsAuth:=True), ControlNumber(3)> <PropertyOption(ControlText:="x-ig-app-id", IsAuth:=True, AllowNull:=False), ControlNumber(3)>
Friend Property IG_APP_ID As PropertyValue Friend Property IG_APP_ID As PropertyValue
<PropertyOption(ControlText:="x-ig-www-claim", IsAuth:=True), ControlNumber(4)> <PropertyOption(ControlText:="x-ig-www-claim", IsAuth:=True, AllowNull:=False), ControlNumber(4)>
Friend Property IG_WWW_CLAIM As PropertyValue Friend Property IG_WWW_CLAIM As PropertyValue
<PropertyOption(ControlText:="Saved posts user", IsAuth:=True), PXML("SavedPostsUserName"), ControlNumber(5)> Private Const SavedPostsUserName_Text As String = "Saved posts user"
<PropertyOption(ControlText:=SavedPostsUserName_Text, ControlToolTip:="Personal profile username", IsAuth:=True), PXML("SavedPostsUserName"), ControlNumber(5)>
Friend ReadOnly Property SavedPostsUserName As PropertyValue Friend ReadOnly Property SavedPostsUserName As PropertyValue
Friend ReadOnly Property BaseAuthExists As Boolean Friend Overrides Function BaseAuthExists() As Boolean
Get Return If(Responser.Cookies?.Count, 0) > 0 And ACheck(IG_APP_ID.Value) And ACheck(IG_WWW_CLAIM.Value) And ACheck(CSRF_TOKEN.Value)
Return Responser.Cookies.Count > 0 And ACheck(IG_APP_ID.Value) And ACheck(IG_WWW_CLAIM.Value) And ACheck(CSRF_TOKEN.Value) End Function
End Get
End Property
Private Const Header_IG_APP_ID As String = "x-ig-app-id" Private Const Header_IG_APP_ID As String = "x-ig-app-id"
Private Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim" Private Const Header_IG_WWW_CLAIM As String = "x-ig-www-claim"
Private Const Header_CSRF_TOKEN As String = "x-csrftoken" Private Const Header_CSRF_TOKEN As String = "x-csrftoken"
@@ -107,8 +106,8 @@ Namespace API.Instagram
Case NameOf(CSRF_TOKEN) : f = Header_CSRF_TOKEN Case NameOf(CSRF_TOKEN) : f = Header_CSRF_TOKEN
End Select End Select
If Not f.IsEmptyString Then If Not f.IsEmptyString Then
If Responser.Headers.Count > 0 AndAlso Responser.Headers.ContainsKey(f) Then Responser.Headers.Remove(f) Responser.HeadersRemove(f)
If Not CStr(Value).IsEmptyString Then Responser.Headers.Add(f, CStr(Value)) If Not CStr(Value).IsEmptyString Then Responser.HeadersAdd(f, CStr(Value))
Responser.SaveSettings() Responser.SaveSettings()
End If End If
End If End If
@@ -138,11 +137,17 @@ Namespace API.Instagram
<Provider(NameOf(TaggedNotifyLimit), FieldsChecker:=True)> <Provider(NameOf(TaggedNotifyLimit), FieldsChecker:=True)>
Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider Private ReadOnly Property TaggedNotifyLimitProvider As IFormatProvider
#End Region #End Region
#Region "Download ready"
Friend ReadOnly Property DownloadTimeline As XMLValue(Of Boolean)
Friend ReadOnly Property DownloadStoriesTagged As XMLValue(Of Boolean)
Friend ReadOnly Property DownloadSaved As XMLValue(Of Boolean)
#End Region
#Region "429 bypass" #Region "429 bypass"
Private ReadOnly Property DownloadingErrorDate As XMLValue(Of Date) Private ReadOnly Property DownloadingErrorDate As XMLValue(Of Date)
Friend Property LastApplyingValue As Integer? = Nothing Friend Property LastApplyingValue As Integer? = Nothing
Friend ReadOnly Property ReadyForDownload As Boolean Friend ReadOnly Property ReadyForDownload As Boolean
Get Get
If SkipUntilNextSession Then Return False
With DownloadingErrorDate With DownloadingErrorDate
If .ValueF.Exists Then If .ValueF.Exists Then
Return .ValueF.Value.AddMinutes(If(LastApplyingValue, 10)) < Now Return .ValueF.Value.AddMinutes(If(LastApplyingValue, 10)) < Now
@@ -186,7 +191,6 @@ Namespace API.Instagram
End With End With
End Sub End Sub
#End Region #End Region
Private Initialized As Boolean = False
#End Region #End Region
#Region "Initializer" #Region "Initializer"
Friend Sub New(ByRef _XML As XmlFile, ByVal GlobalPath As SFile) Friend Sub New(ByRef _XML As XmlFile, ByVal GlobalPath As SFile)
@@ -198,11 +202,13 @@ Namespace API.Instagram
With Responser With Responser
If .Headers.Count > 0 Then If .Headers.Count > 0 Then
With .Headers token = .HeadersValue(Header_CSRF_TOKEN)
If .ContainsKey(Header_CSRF_TOKEN) Then token = .Item(Header_CSRF_TOKEN) app_id = .HeadersValue(Header_IG_APP_ID)
If .ContainsKey(Header_IG_APP_ID) Then app_id = .Item(Header_IG_APP_ID) www_claim = .HeadersValue(Header_IG_WWW_CLAIM)
If .ContainsKey(Header_IG_WWW_CLAIM) Then www_claim = .Item(Header_IG_WWW_CLAIM) End If
End With If Not .Cookies Is Nothing Then
.Cookies.ChangedAllowInternalDrop = False
.Cookies.Changed = False
End If End If
End With End With
@@ -216,6 +222,10 @@ Namespace API.Instagram
IG_APP_ID = New PropertyValue(app_id, GetType(String), Sub(v) ChangeResponserFields(NameOf(IG_APP_ID), v)) IG_APP_ID = New PropertyValue(app_id, GetType(String), Sub(v) ChangeResponserFields(NameOf(IG_APP_ID), v))
IG_WWW_CLAIM = New PropertyValue(www_claim, GetType(String), Sub(v) ChangeResponserFields(NameOf(IG_WWW_CLAIM), v)) IG_WWW_CLAIM = New PropertyValue(www_claim, GetType(String), Sub(v) ChangeResponserFields(NameOf(IG_WWW_CLAIM), v))
DownloadTimeline = New XMLValue(Of Boolean)("DownloadTimeline", True, _XML, n)
DownloadStoriesTagged = New XMLValue(Of Boolean)("DownloadStoriesTagged", True, _XML, n)
DownloadSaved = New XMLValue(Of Boolean)("DownloadSaved", True, _XML, n)
RequestsWaitTimer = New PropertyValue(1000) RequestsWaitTimer = New PropertyValue(1000)
RequestsWaitTimerProvider = New TimersChecker(100) RequestsWaitTimerProvider = New TimersChecker(100)
RequestsWaitTimerTaskCount = New PropertyValue(1) RequestsWaitTimerTaskCount = New PropertyValue(1)
@@ -239,11 +249,62 @@ Namespace API.Instagram
UserRegex = RParams.DMS("[htps:/]{7,8}.*?instagram.com/([^/]+)", 1) UserRegex = RParams.DMS("[htps:/]{7,8}.*?instagram.com/([^/]+)", 1)
ImageVideoContains = "instagram.com" ImageVideoContains = "instagram.com"
End Sub End Sub
Friend Overrides Sub BeginInit() Private Structure LatestValues
Friend Hash As String
Friend Hash2 As String
Friend Token As String
Friend AppID As String
Friend WwwClaim As String
Friend Exists As Boolean
Friend Sub New(ByVal Source As SiteSettings)
Exists = True
With Source
Hash = AConvert(Of String)(.Hash.Value, String.Empty)
Hash2 = AConvert(Of String)(.HashSavedPosts.Value, String.Empty)
With .Responser
Token = .HeadersValue(Header_CSRF_TOKEN)
AppID = .HeadersValue(Header_IG_APP_ID)
WwwClaim = .HeadersValue(Header_IG_WWW_CLAIM)
End With
End With
End Sub
End Structure
Private LV As LatestValues = Nothing
Private ASO As SettingsExchangeOptions = Nothing
Friend Overrides Sub BeginEdit()
LV = New LatestValues(Me)
ASO = Nothing
MyBase.BeginEdit()
End Sub End Sub
Friend Overrides Sub EndInit() Friend Overrides Sub EndEdit()
Initialized = True LV = Nothing
MyBase.EndInit() ASO = Nothing
MyBase.EndEdit()
End Sub
Friend Overrides Sub Update()
If LV.Exists Then
Dim __lv As New LatestValues(Me)
If If(Responser.Cookies?.Count, 0) > 0 Then
Dim _cookiesChanged As Boolean = If(Responser.Cookies?.Changed, False)
If Not DownloadTimeline AndAlso (_cookiesChanged Or
(Not LV.Hash = __lv.Hash And Not __lv.Hash.IsEmptyString)) Then DownloadTimeline.Value = True
If Not DownloadSaved AndAlso (_cookiesChanged Or (Not LV.Hash2 = __lv.Hash2 And Not __lv.Hash2.IsEmptyString)) Then DownloadSaved.Value = True
If Not DownloadStoriesTagged AndAlso (
_cookiesChanged Or (
(Not LV.Hash = __lv.Hash Or Not LV.Token = __lv.Token Or Not LV.AppID = __lv.AppID Or Not LV.WwwClaim = __lv.WwwClaim) And
(Not __lv.Hash.IsEmptyString And Not __lv.Token.IsEmptyString And Not __lv.AppID.IsEmptyString And Not __lv.WwwClaim.IsEmptyString)
)) Then DownloadStoriesTagged.Value = True
End If
End If
If ASO.Changed Then
DownloadTimeline.Value = ASO.DownloadTimeline
DownloadStoriesTagged.Value = ASO.DownloadStoriesTagged
DownloadSaved.Value = ASO.DownloadSaved
End If
LV = Nothing
ASO = Nothing
If Not Responser.Cookies Is Nothing Then Responser.Cookies.Changed = False
MyBase.Update()
End Sub End Sub
#End Region #End Region
#Region "PropertiesDataChecker" #Region "PropertiesDataChecker"
@@ -290,6 +351,37 @@ Namespace API.Instagram
End If End If
Return False Return False
End Function End Function
<PropertiesDataChecker({NameOf(HashSavedPosts), NameOf(SavedPostsUserName)})>
Private Function CheckSavedOptions(ByVal p As IEnumerable(Of PropertyData)) As Boolean
If p.ListExists Then
Const MsgTitle$ = "Saved posts credentials"
Dim __hash$ = String.Empty
Dim __name$ = String.Empty
Dim _OptionlErrorText$ = $"For download saved posts, you must to set both [{HashSavedPosts_Text}] and [{SavedPostsUserName_Text}]."
For i% = 0 To p.Count - 1
Select Case p(i).Name
Case NameOf(HashSavedPosts) : __hash = p(i).Value
Case NameOf(SavedPostsUserName) : __name = p(i).Value
End Select
Next
If __hash = __name Then
If __hash.IsEmptyString Then
Return True
Else
MsgBoxE({$"[{HashSavedPosts_Text}] and [{SavedPostsUserName_Text}] for saved posts cannot be equal!", MsgTitle}, vbCritical)
End If
Else
If __hash.IsEmptyString Then
MsgBoxE({$"[{HashSavedPosts_Text}] not set.{vbCr}{_OptionlErrorText}", MsgTitle}, vbCritical)
ElseIf __name.IsEmptyString Then
MsgBoxE({$"[{SavedPostsUserName_Text}] not set.{vbCr}{_OptionlErrorText}", MsgTitle}, vbCritical)
Else
Return True
End If
End If
End If
Return False
End Function
#End Region #End Region
#Region "Plugin functions" #Region "Plugin functions"
Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider
@@ -303,11 +395,12 @@ Namespace API.Instagram
Return Nothing Return Nothing
End Function End Function
#Region "Downloading" #Region "Downloading"
Friend Property SkipUntilNextSession As Boolean = False
Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean Friend Overrides Function ReadyToDownload(ByVal What As Download) As Boolean
If ActiveJobs < 2 AndAlso ReadyForDownload AndAlso BaseAuthExists Then If ActiveJobs < 2 AndAlso Not SkipUntilNextSession AndAlso ReadyForDownload AndAlso BaseAuthExists() Then
Select Case What Select Case What
Case Download.Main : Return ACheck(Hash.Value) Case Download.Main : Return ACheck(Hash.Value) And DownloadTimeline
Case Download.SavedPosts : Return ACheck(HashSavedPosts.Value) Case Download.SavedPosts : Return ACheck(HashSavedPosts.Value) And DownloadSaved
End Select End Select
End If End If
Return False Return False
@@ -346,10 +439,11 @@ Namespace API.Instagram
_NextTagged = True _NextTagged = True
LastDownloadDate.Value = Now LastDownloadDate.Value = Now
ActiveJobs -= 1 ActiveJobs -= 1
SkipUntilNextSession = False
End Sub End Sub
#End Region #End Region
Friend Overrides Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia) Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
Return UserData.GetVideoInfo(URL, Responser, Me) Return UserData.GetVideoInfo(URL, Responser)
End Function End Function
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
If Options Is Nothing OrElse Not TypeOf Options Is EditorExchangeOptions Then Options = New EditorExchangeOptions(Me) If Options Is Nothing OrElse Not TypeOf Options Is EditorExchangeOptions Then Options = New EditorExchangeOptions(Me)
@@ -357,6 +451,12 @@ Namespace API.Instagram
Using f As New OptionsForm(Options) : f.ShowDialog() : End Using Using f As New OptionsForm(Options) : f.ShowDialog() : End Using
End If End If
End Sub End Sub
Friend Overrides Sub OpenSettingsForm()
Using f As New AdditionalSettingsForm(If(ASO.Changed, ASO, New SettingsExchangeOptions(Me)))
f.ShowDialog()
If f.DialogResult = DialogResult.OK Then ASO = f.MyParameters
End Using
End Sub
#End Region #End Region
End Class End Class
End Namespace End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,15 +6,14 @@
' '
' 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.Net
Imports System.Threading
Imports PersonalUtilities.Functions.XML Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.Messaging Imports PersonalUtilities.Functions.Messaging
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.WEB Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.WebDocuments.JSON Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports System.Net
Imports System.Threading
Imports System.Reflection
Imports UTypes = SCrawler.API.Base.UserMedia.Types Imports UTypes = SCrawler.API.Base.UserMedia.Types
Namespace API.Instagram Namespace API.Instagram
Friend Class UserData : Inherits UserDataBase Friend Class UserData : Inherits UserDataBase
@@ -70,41 +69,58 @@ Namespace API.Instagram
End Sub End Sub
#End Region #End Region
#Region "Download data" #Region "Download data"
Private E560Thrown As Boolean = False
Private Class ExitException : Inherits Exception Private Class ExitException : Inherits Exception
Friend Shared Sub Throw560(ByRef Source As UserData)
If Not Source.E560Thrown Then
MyMainLOG = $"{Source.ToStringForLog}: (560) Download skipped until next session"
Source.E560Thrown = True
End If
Throw New ExitException
End Sub
Friend Sub New()
End Sub
Friend Sub New(ByRef CompleteArg As Boolean) Friend Sub New(ByRef CompleteArg As Boolean)
CompleteArg = True CompleteArg = True
End Sub End Sub
End Class End Class
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Dim s As Sections = Sections.Timeline
Try Try
ThrowAny(Token)
_InstaHash = String.Empty _InstaHash = String.Empty
HasError = False HasError = False
If Not LastCursor.IsEmptyString Then Dim fc As Boolean = IIf(IsSavedPosts, MySiteSettings.DownloadSaved.Value, MySiteSettings.DownloadTimeline.Value)
DownloadData(LastCursor, Sections.Timeline, Token) If fc And Not LastCursor.IsEmptyString Then
s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
DownloadData(LastCursor, s, Token)
ThrowAny(Token) ThrowAny(Token)
If Not HasError Then FirstLoadingDone = True If Not HasError Then FirstLoadingDone = True
End If End If
If Not HasError Then If fc And Not HasError Then
DownloadData(String.Empty, Sections.Timeline, Token) s = IIf(IsSavedPosts, Sections.SavedPosts, Sections.Timeline)
DownloadData(String.Empty, s, Token)
ThrowAny(Token) ThrowAny(Token)
If Not HasError Then FirstLoadingDone = True If Not HasError Then FirstLoadingDone = True
End If End If
If FirstLoadingDone Then LastCursor = String.Empty If FirstLoadingDone Then LastCursor = String.Empty
If IsSavedPosts Then If IsSavedPosts Then
DownloadPosts(Token) If MySiteSettings.DownloadSaved Then s = Sections.SavedPosts : DownloadPosts(Token)
ElseIf MySiteSettings.BaseAuthExists Then ElseIf MySiteSettings.BaseAuthExists() Then
DownloadedTags = 0 DownloadedTags = 0
If GetStories Then DownloadData(String.Empty, Sections.Stories, Token) If MySiteSettings.DownloadStoriesTagged And GetStories Then s = Sections.Stories : DownloadData(String.Empty, s, Token)
If GetTaggedData Then DownloadData(String.Empty, Sections.Tagged, Token) If MySiteSettings.DownloadStoriesTagged And GetTaggedData Then s = Sections.Tagged : DownloadData(String.Empty, s, Token)
End If End If
If WaitNotificationMode = WNM.SkipTemp Or WaitNotificationMode = WNM.SkipCurrent Then WaitNotificationMode = WNM.Notify If WaitNotificationMode = WNM.SkipTemp Or WaitNotificationMode = WNM.SkipCurrent Then WaitNotificationMode = WNM.Notify
Catch eex As ExitException Catch eex As ExitException
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, "[API.Instagram.UserData.DownloadDataF", False) ProcessException(ex, Token, "[API.Instagram.UserData.DownloadDataF]", False, s)
Finally
E560Thrown = False
End Try End Try
End Sub End Sub
Private _InstaHash As String = String.Empty Private _InstaHash As String = String.Empty
Private Enum Sections : Timeline : Tagged : Stories : End Enum Private Enum Sections : Timeline : Tagged : Stories : SavedPosts : End Enum
Private Const StoriesFolder As String = "Stories" Private Const StoriesFolder As String = "Stories"
Private Const TaggedFolder As String = "Tagged" Private Const TaggedFolder As String = "Tagged"
#Region "429 bypass" #Region "429 bypass"
@@ -262,7 +278,7 @@ Namespace API.Instagram
'Create query 'Create query
Select Case Section Select Case Section
Case Sections.Timeline Case Sections.Timeline, Sections.SavedPosts
Dim vars$ = "{""id"":" & ID & ",""first"":50,""after"":""" & Cursor & """}" Dim vars$ = "{""id"":" & ID & ",""first"":50,""after"":""" & Cursor & """}"
vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars) vars = SymbolsConverter.ASCII.EncodeSymbolsOnly(vars)
URL = $"https://www.instagram.com/graphql/query/?query_hash={_InstaHash}&variables={vars}" URL = $"https://www.instagram.com/graphql/query/?query_hash={_InstaHash}&variables={vars}"
@@ -303,7 +319,7 @@ Namespace API.Instagram
n = j.ItemF(ENode).XmlIfNothing n = j.ItemF(ENode).XmlIfNothing
If n.Count > 0 Then If n.Count > 0 Then
Select Case Section Select Case Section
Case Sections.Timeline Case Sections.Timeline, Sections.SavedPosts
If n.Contains("page_info") Then If n.Contains("page_info") Then
With n("page_info") With n("page_info")
HasNextPage = .Value("has_next_page").FromXML(Of Boolean)(False) HasNextPage = .Value("has_next_page").FromXML(Of Boolean)(False)
@@ -317,9 +333,7 @@ Namespace API.Instagram
node = nn(0).XmlIfNothing node = nn(0).XmlIfNothing
If IsSavedPosts Then If IsSavedPosts Then
PostID = node.Value("shortcode") PostID = node.Value("shortcode")
If Not PostID.IsEmptyString Then If Not PostID.IsEmptyString AndAlso _TempPostsList.Contains(PostID) Then Throw New ExitException(_DownloadComplete)
If _TempPostsList.Contains(PostID) Then Throw New ExitException(_DownloadComplete) 'Else _SavedPostsIDs.Add(PostID)
End If
End If End If
PostID = node.Value("id") PostID = node.Value("id")
Pinned = CBool(If(node("pinned_for_users")?.Count, 0)) Pinned = CBool(If(node("pinned_for_users")?.Count, 0))
@@ -373,7 +387,7 @@ Namespace API.Instagram
Catch dex As ObjectDisposedException When Disposed Catch dex As ObjectDisposedException When Disposed
Exit Do Exit Do
Catch ex As Exception Catch ex As Exception
If DownloadingException(ex, $"data downloading error [{URL}]", Section, False) = 1 Then Continue Do Else Exit Do If DownloadingException(ex, $"data downloading error [{URL}]", False, Section) = 1 Then Continue Do Else Exit Do
End Try End Try
Loop Loop
Catch eex2 As ExitException Catch eex2 As ExitException
@@ -381,7 +395,7 @@ Namespace API.Instagram
Catch oex2 As OperationCanceledException When Token.IsCancellationRequested Or oex2.HelpLink = InstAborted Catch oex2 As OperationCanceledException When Token.IsCancellationRequested Or oex2.HelpLink = InstAborted
If oex2.HelpLink = InstAborted Then HasError = True If oex2.HelpLink = InstAborted Then HasError = True
Catch DoEx As Exception Catch DoEx As Exception
ProcessException(DoEx, Token, $"data downloading error [{URL}]") ProcessException(DoEx, Token, $"data downloading error [{URL}]",, Section)
End Try End Try
End Sub End Sub
Private Sub DownloadPosts(ByVal Token As CancellationToken) Private Sub DownloadPosts(ByVal Token As CancellationToken)
@@ -432,23 +446,26 @@ Namespace API.Instagram
Next Next
End If End If
_DownloadComplete = True _DownloadComplete = True
Catch eex As ExitException
Throw eex
Catch oex As OperationCanceledException When Token.IsCancellationRequested Catch oex As OperationCanceledException When Token.IsCancellationRequested
Exit Do Exit Do
Catch dex As ObjectDisposedException When Disposed Catch dex As ObjectDisposedException When Disposed
Exit Do Exit Do
Catch ex As Exception Catch ex As Exception
If DownloadingException(ex, $"downloading saved posts error [{URL}]") = 1 Then Continue Do Else Exit Do If DownloadingException(ex, $"downloading saved posts error [{URL}]", False, Sections.SavedPosts) = 1 Then Continue Do Else Exit Do
End Try End Try
Loop Loop
Catch eex2 As ExitException
Catch oex2 As OperationCanceledException When Token.IsCancellationRequested Or oex2.HelpLink = InstAborted Catch oex2 As OperationCanceledException When Token.IsCancellationRequested Or oex2.HelpLink = InstAborted
If oex2.HelpLink = InstAborted Then HasError = True If oex2.HelpLink = InstAborted Then HasError = True
Catch DoEx As Exception Catch DoEx As Exception
ProcessException(DoEx, Token, $"downloading saved posts error [{URL}]") ProcessException(DoEx, Token, $"downloading saved posts error [{URL}]",, Sections.SavedPosts)
End Try End Try
End Sub End Sub
#End Region #End Region
#Region "Code ID converters" #Region "Code ID converters"
Friend Shared Function CodeToID(ByVal Code As String) As String Private Shared Function CodeToID(ByVal Code As String) As String
Const CodeSymbols$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_" Const CodeSymbols$ = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_"
Try Try
If Not Code.IsEmptyString Then If Not Code.IsEmptyString Then
@@ -493,6 +510,7 @@ Namespace API.Instagram
Dim vid As Predicate(Of EContainer) = Function(_vid) Not _vid.Name.IsEmptyString AndAlso _vid.Name.StartsWith("video_versions") AndAlso _vid.Count > 0 Dim vid As Predicate(Of EContainer) = Function(_vid) Not _vid.Name.IsEmptyString AndAlso _vid.Name.StartsWith("video_versions") AndAlso _vid.Count > 0
Dim ss As Func(Of EContainer, Sizes) = Function(_ss) New Sizes(_ss.Value("width"), _ss.Value("url")) Dim ss As Func(Of EContainer, Sizes) = Function(_ss) New Sizes(_ss.Value("width"), _ss.Value("url"))
Dim mDate As Func(Of EContainer, String) = Function(ByVal elem As EContainer) As String Dim mDate As Func(Of EContainer, String) = Function(ByVal elem As EContainer) As String
If Not DateObj.IsEmptyString Then Return DateObj
If elem.Contains("taken_at") Then If elem.Contains("taken_at") Then
Return elem.Value("taken_at") Return elem.Value("taken_at")
ElseIf elem.Contains("imported_taken_at") Then ElseIf elem.Contains("imported_taken_at") Then
@@ -501,7 +519,7 @@ Namespace API.Instagram
Dim ev$ = elem.Value("device_timestamp") Dim ev$ = elem.Value("device_timestamp")
If Not ev.IsEmptyString Then If Not ev.IsEmptyString Then
If ev.Length > 10 Then If ev.Length > 10 Then
Return elem.Value("device_timestamp").Substring(0, 10) Return ev.Substring(0, 10)
Else Else
Return ev Return ev
End If End If
@@ -658,7 +676,7 @@ Namespace API.Instagram
End If End If
Return Nothing Return Nothing
Catch ex As Exception Catch ex As Exception
DownloadingException(ex, "API.Instagram.GetStoriesList", Sections.Stories, False) DownloadingException(ex, "API.Instagram.GetStoriesList", False, Sections.Stories)
Return Nothing Return Nothing
End Try End Try
End Function End Function
@@ -669,19 +687,24 @@ Namespace API.Instagram
End Sub End Sub
#End Region #End Region
#Region "Exceptions" #Region "Exceptions"
''' <exception cref="ExitException"></exception>
''' <inheritdoc cref="UserDataBase.ThrowAny(CancellationToken)"/>
Friend Overrides Sub ThrowAny(ByVal Token As CancellationToken)
If MySiteSettings.SkipUntilNextSession Then ExitException.Throw560(Me)
MyBase.ThrowAny(Token)
End Sub
''' <summary> ''' <summary>
''' <inheritdoc cref="UserDataBase.DownloadingException(Exception, String)"/><br/> ''' <inheritdoc cref="UserDataBase.DownloadingException(Exception, String, Boolean, Object)"/><br/>
''' 1 - continue ''' 1 - continue
''' </summary> ''' </summary>
Protected Overloads Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False) As Integer Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
Return DownloadingException(ex, Message, Sections.Timeline, FromPE) Optional ByVal s As Object = Nothing) As Integer
End Function
Private Overloads Function DownloadingException(ByVal ex As Exception, ByVal Message As String, ByVal s As Sections, ByVal FromPE As Boolean) As Integer
If Responser.StatusCode = HttpStatusCode.NotFound Then If Responser.StatusCode = HttpStatusCode.NotFound Then
UserExists = False UserExists = False
ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then
HasError = True HasError = True
MyMainLOG = $"Instagram credentials have expired [{CInt(Responser.StatusCode)}]: {ToString()} [{s}]" MyMainLOG = $"Instagram credentials have expired [{CInt(Responser.StatusCode)}]: {ToStringForLog()} [{s}]"
DisableSection(s)
ElseIf Responser.StatusCode = HttpStatusCode.Forbidden And s = Sections.Tagged Then ElseIf Responser.StatusCode = HttpStatusCode.Forbidden And s = Sections.Tagged Then
Return 3 Return 3
ElseIf Responser.StatusCode = 429 Then ElseIf Responser.StatusCode = 429 Then
@@ -693,13 +716,27 @@ Namespace API.Instagram
Caught429 = True Caught429 = True
MyMainLOG = $"Number of requests before error 429: {RequestsCount}" MyMainLOG = $"Number of requests before error 429: {RequestsCount}"
Return 1 Return 1
ElseIf Responser.StatusCode = 560 Then
MySiteSettings.SkipUntilNextSession = True
Else Else
MyMainLOG = $"Instagram hash requested [{CInt(Responser.StatusCode)}]: {ToString()} [{s}]" MyMainLOG = $"Instagram hash requested [{CInt(Responser.StatusCode)}]: {ToString()} [{s}]"
DisableSection(s)
If Not FromPE Then LogError(ex, Message) : HasError = True If Not FromPE Then LogError(ex, Message) : HasError = True
Return 0 Return 0
End If End If
Return 2 Return 2
End Function End Function
Private Sub DisableSection(ByVal Section As Object)
If Not IsNothing(Section) AndAlso TypeOf Section Is Sections Then
Dim s As Sections = DirectCast(Section, Sections)
Select Case s
Case Sections.Timeline : MySiteSettings.DownloadTimeline.Value = False
Case Sections.SavedPosts : MySiteSettings.DownloadSaved.Value = False
Case Else : MySiteSettings.DownloadStoriesTagged.Value = False
End Select
MyMainLOG = $"[{s}] downloading is disabled until you update your credentials".ToUpper
End If
End Sub
#End Region #End Region
#Region "Create media" #Region "Create media"
Private Shared Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String, Private Shared Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
@@ -713,14 +750,14 @@ Namespace API.Instagram
End Function End Function
#End Region #End Region
#Region "Standalone downloader" #Region "Standalone downloader"
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal r As Response, ByVal _Settings As SiteSettings) As IEnumerable(Of UserMedia) Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal r As Response) As IEnumerable(Of UserMedia)
Try Try
If Not URL.IsEmptyString AndAlso URL.Contains("instagram.com") Then If Not URL.IsEmptyString AndAlso URL.Contains("instagram.com") Then
Dim PID$ = RegexReplace(URL, RParams.DMS(".*?instagram.com/p/([_\w\d]+)", 1)) Dim PID$ = RegexReplace(URL, RParams.DMS(".*?instagram.com/p/([_\w\d]+)", 1))
If Not PID.IsEmptyString AndAlso Not ACheck(Of Long)(PID) Then PID = CodeToID(PID) If Not PID.IsEmptyString AndAlso Not ACheck(Of Long)(PID) Then PID = CodeToID(PID)
If Not PID.IsEmptyString Then If Not PID.IsEmptyString Then
Using t As New UserData Using t As New UserData
t.SetEnvironment(Settings(_Settings.GetType.GetCustomAttribute(Of Plugin.Attributes.Manifest)().GUID), Nothing, False, False) t.SetEnvironment(Settings(InstagramSiteKey), Nothing, False, False)
t.Responser = New Response t.Responser = New Response
t.Responser.Copy(r) t.Responser.Copy(r)
t._SavedPostsIDs.Add(PID) t._SavedPostsIDs.Add(PID)
@@ -731,7 +768,7 @@ Namespace API.Instagram
End If End If
Return Nothing Return Nothing
Catch ex As Exception Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.ShowMainMsg + EDP.SendInLog, ex, "Instagram standalone downloader: fetch media error") Return ErrorsDescriber.Execute(EDP.ShowMainMsg + EDP.SendInLog, ex, $"Instagram standalone downloader: fetch media error ({URL})")
End Try End Try
End Function End Function
#End Region #End Region

View File

@@ -0,0 +1,37 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.API.Base
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.LPSG
Friend Module Declarations
Friend ReadOnly Property PhotoRegEx As RParams = RParams.DM("(https://www.lpsg.com/attachments)(.+?)(?="")", 0, RegexReturn.List)
Friend ReadOnly Property PhotoRegExExt As New RParams("img.data.src=""(/proxy[^""]+?)""", Nothing, 1, RegexReturn.List) With {
.Converter = Function(Input) $"https://www.lpsg.com/{SymbolsConverter.HTML.Decode(Input)}"}
Friend ReadOnly Property NextPageRegex As RParams = RParams.DMS("<link rel=""next"" href=""(.+?/page-(\d+))""", 2)
Private Const FileUrlRegexDefault As String = "([^/]+?)(jpg|jpeg|gif|png|webm)"
Private ReadOnly InputFReplacer As New ErrorsDescriber(EDP.ReturnValue)
Private ReadOnly InputForbidRemover As Func(Of String, String) = Function(Input) If(Input.IsEmptyString, Input, Input.StringRemoveWinForbiddenSymbols(, InputFReplacer))
Friend ReadOnly Property FileRegEx As New RParams(FileUrlRegexDefault, Nothing, 0) With {
.Converter = Function(ByVal Input As String) As String
Input = InputForbidRemover.Invoke(Input)
If Not Input.IsEmptyString Then
Dim lv$ = Input.Split("-").LastOrDefault
If Not lv.IsEmptyString Then
Input = Input.Replace($"-{lv}", String.Empty)
Input &= $".{lv}"
End If
End If
Return Input
End Function}
Friend ReadOnly Property FileRegExExt As New RParams(FileUrlRegexDefault, 0, Nothing, InputForbidRemover)
Friend ReadOnly Property FileRegExExt2 As New RParams("([^/]+?)(?=(\Z|&))", 0, Nothing, InputForbidRemover)
Friend ReadOnly Property FileExistsRegEx As RParams = RParams.DMS(FileUrlRegexDefault, 2)
Friend ReadOnly Property TempListAddParams As New ListAddParams(LAP.NotContainsOnly) With {.Comparer = New FComparer(Of UserMedia)(Function(x, y) x.URL = y.URL)}
End Module
End Namespace

View File

@@ -0,0 +1,37 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.LPSG
<Manifest("AndyProgram_LPSG")>
Friend Class SiteSettings : Inherits Base.SiteSettingsBase
Friend Overrides ReadOnly Property Icon As Icon
Get
Return My.Resources.SiteResources.LPSGIcon_48
End Get
End Property
Friend Overrides ReadOnly Property Image As Image
Get
Return My.Resources.SiteResources.LPSGPic_32
End Get
End Property
Friend Sub New()
MyBase.New("LPSG", "www.lpsg.com")
UrlPatternUser = "https://www.lpsg.com/threads/{0}/"
UserRegex = RParams.DMS(".+?lpsg.com/threads/([^/]+)", 1)
End Sub
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
Return New UserData
End Function
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
Return If(Responser.Cookies?.Count, 0) > 0
End Function
End Class
End Namespace

View File

@@ -0,0 +1,105 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports System.Threading
Imports SCrawler.API.Base
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Imports UTypes = SCrawler.API.Base.UserMedia.Types
Imports Converters = PersonalUtilities.Functions.SymbolsConverter.Converters
Namespace API.LPSG
Friend Class UserData : Inherits UserDataBase
Private Const Name_LatestPage As String = "LatestPage"
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
If Loading Then
LatestPage = Container.Value(Name_LatestPage)
Else
Container.Add(Name_LatestPage, LatestPage)
End If
End Sub
Private Property LatestPage As String = String.Empty
Private Enum Mode : Internal : External : End Enum
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Dim URL$ = String.Empty
Try
Responser.DeclaredError = EDP.ThrowException
Dim NextPage$
Dim r$
Dim _LPage As Func(Of String) = Function() If(LatestPage.IsEmptyString, String.Empty, $"page-{LatestPage}")
Do
URL = $"https://www.lpsg.com/threads/{Name}/{_LPage.Invoke}"
r = Responser.GetResponse(URL)
UserExists = True
UserSuspended = False
ThrowAny(Token)
If Not r.IsEmptyString Then
NextPage = RegexReplace(r, NextPageRegex)
UpdateMediaList(RegexReplace(r, PhotoRegEx), Mode.Internal)
UpdateMediaList(RegexReplace(r, PhotoRegExExt), Mode.External)
If NextPage = LatestPage Or NextPage.IsEmptyString Then Exit Do Else LatestPage = NextPage
Else
Exit Do
End If
Loop
If _TempMediaList.ListExists And _ContentList.ListExists Then _
_TempMediaList.RemoveAll(Function(m) _ContentList.Exists(Function(mm) mm.URL = m.URL))
Catch ex As Exception
ProcessException(ex, Token, $"data downloading error [{URL}]")
End Try
End Sub
Private Sub UpdateMediaList(ByVal l As List(Of String), ByVal m As Mode)
If l.ListExists Then
Dim f As SFile
Dim u$
Dim exists As Boolean
Dim r As RParams
Dim ude As New ErrorsDescriber(EDP.ReturnValue)
For Each url$ In l
If Not url.IsEmptyString Then u = SymbolsConverter.Decode(url, {Converters.HTML, Converters.ASCII}, ude) Else u = String.Empty
If Not u.IsEmptyString Then
exists = Not IsEmptyString(RegexReplace(u, FileExistsRegEx))
If m = Mode.Internal Then
r = FileRegEx
Else
r = FileRegExExt
If Not exists Then
r = FileRegExExt2
exists = Not IsEmptyString(RegexReplace(u, FileRegExExt2))
End If
End If
If exists Then
f = CStr(RegexReplace(u, r))
f.Path = MyFile.CutPath.PathNoSeparator
f.Separator = "\"
If f.Extension.IsEmptyString Then f.Extension = "jpg"
_TempMediaList.ListAddValue(New UserMedia With {.Type = UTypes.Picture, .URL = url, .File = f}, TempListAddParams)
End If
End If
Next
End If
End Sub
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
With Responser : .Mode = Response.Modes.WebClient : .ResetStatus() : End With
UseResponserClient = True
DownloadContentDefault(Token)
End Sub
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
Optional ByVal EObj As Object = Nothing) As Integer
If Responser.StatusCode = Net.HttpStatusCode.ServiceUnavailable Then
MyMainLOG = $"{ToStringForLog()}: LPSG not available"
Return 1
Else
Return 0
End If
End Function
End Class
End Namespace

View File

@@ -0,0 +1,44 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.PornHub
Friend Module Declarations
#Region "Converters"
Private ReadOnly UnicodeHexConverter As Func(Of String, String) = Function(Input) SymbolsConverter.UnicodeHex.Decode(Input, EDP.ReturnValue)
Friend ReadOnly HtmlConverter As Func(Of String, String) = Function(Input) SymbolsConverter.HTML.Decode(Input, EDP.ReturnValue)
#End Region
#Region "Declarations video"
Friend ReadOnly RegexVideo_FlashVarsBlock As RParams = RParams.DM("(?<=flashvars_\['[nN]ext[vV]ideo'\];[\r\n]*?)(.+?)(?=;flashvars_\d+?)", 0, EDP.ReturnValue)
Friend ReadOnly RegexVideo_FlashVars_Vars As RParams = RParams.DM("var ([\w\d]{10,})=("".+?)(?=(;|\Z))", 0, RegexReturn.List)
Friend ReadOnly RegexVideo_FlashVars_Compiler As RParams = RParams.DM("(?<=\*/)([\w\d\S]{10,})", 0, RegexReturn.List)
Friend ReadOnly RegexVideo_Video_All As RParams = RParams.DM("div class=""thumbnail-info-wrapper clearfix.+?[\r\n\s]*?\<span class=""title.+?[\r\n\s]*?\<a href=""([^""]+?)""[\s]+?title=""([^""]*?)""",
0, RegexReturn.List, EDP.ReturnValue, UnicodeHexConverter)
Friend ReadOnly RegexVideo_Video_Wrong As RParams = RParams.DM("div class=""thumbnail-info-wrapper clearfix.+?[\r\n\s]*?\<span class=""title.+?[\r\n\s]*?\<a href=""([^""]+?)""[\s]+?title=""([^""]*?)""[\w\W\s\r\n]+?(?=\<div class=""videoUploaderBlock)",
0, RegexReturn.List, EDP.ReturnValue, UnicodeHexConverter)
Private ReadOnly RegexVideo_Video_Wrong_Option As RParams = RParams.DM("div class=""thumbnail-info-wrapper clearfix.+?[\r\n\s]*?\<span class=""title.+?[\r\n\s]*?\<a href=""([^""]+?)""[\s]+?title=""([^""]*?)""[\w\W\s\r\n]+?", 0, RegexReturn.ListByMatch)
Friend ReadOnly RegexVideo_Video_Wrong_Fields As RField() = {New RField(New RFieldOption(1, RegexVideo_Video_Wrong_Option)), New RField(New RFieldOption(2, RegexVideo_Video_Wrong_Option))}
Friend ReadOnly RegexVideo_Video_VideoKey As RParams = RParams.DMS("viewkey=([\w\d]+)", 1, EDP.ReturnValue)
#End Region
#Region "Declarations M3U8"
Friend ReadOnly Regex_M3U8_FirstFileRegEx As RParams = RParams.DM(".+?m3u8.*", 0)
Friend ReadOnly Regex_M3U8_FileUrl As RParams = RParams.DMS("((https://([^/]+)/.+?)([^/]+?m3u8))(.*)", 2, EDP.ReturnValue)
#End Region
#Region "Declarations GIF"
Friend ReadOnly Regex_Gif_Array As RParams = RParams.DM("\<li id=""(gif\d+)"" class=""gifLi.gifVideoBlock""\>", 0, RegexReturn.List, EDP.ReturnValue)
Friend ReadOnly Regex_Gif_UrlName As RParams = RParams.DMS("""name"":.*?""([^""]*)""[^\}]+?""contentUrl"":.*?""([^""]+)""", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
#End Region
#Region "Declarations photo"
Friend ReadOnly Regex_Photo_ModelHub_PhotoBlocks As RParams = RParams.DM("var PHOTOS_ARRAY_(\d+) = \{[\r\n\s]*?(urls:.*?\[[^]]*\])", 0, RegexReturn.List, EDP.ReturnValue)
Friend ReadOnly Regex_Photo_PornHub_PhotoBlocks As RParams = RParams.DM("photoAlbumListContainer[\r\n\s\S]+?title=""([^""]+)""[\r\n\s\S]+?a href=""(/album/\d+)""", 0, RegexReturn.List)
Friend ReadOnly Regex_Photo_PornHub_AlbumPhotoArr As RParams = RParams.DMS("\<a href=""(/photo/\d+)""", 1, RegexReturn.List, EDP.ReturnValue,
CType(Function(Input$) If(Input.IsEmptyString, String.Empty, $"https://www.pornhub.com{Input.Trim}"), Func(Of String, String)))
Friend ReadOnly Regex_Photo_PornHub_SinglePhoto As RParams = RParams.DMS("(?<!thumbImage.+?)<img src=""(https://[^""]+\d+[^""]+)""", 1, EDP.ReturnValue)
#End Region
End Module
End Namespace

View File

@@ -0,0 +1,42 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.API.Base
Imports SCrawler.API.Base.M3U8Declarations
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Namespace API.PornHub
Friend NotInheritable Class M3U8
Private Sub New()
End Sub
Private Shared Function GetUrlsList(ByVal URL As String, ByVal Responser As Response) As List(Of String)
Dim appender$ = RegexReplace(URL, Regex_M3U8_FileUrl)
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
Dim file$ = RegexReplace(r, Regex_M3U8_FirstFileRegEx)
If Not file.IsEmptyString Then
Dim NewUrl$ = M3U8Base.CreateUrl(appender, file)
If Not NewUrl.IsEmptyString Then
r = Responser.GetResponse(NewUrl)
If Not r.IsEmptyString Then
Dim l As List(Of String) = RegexReplace(r, TsFilesRegEx)
If l.ListExists Then
For i% = 0 To l.Count - 1 : l(i) = M3U8Base.CreateUrl(appender, l(i)) : Next
Return l
End If
End If
End If
End If
End If
Return Nothing
End Function
Friend Shared Function Download(ByVal URL As String, ByVal Responser As Response, ByVal Destination As SFile) As SFile
Return M3U8Base.Download(GetUrlsList(URL, Responser), Destination, Responser)
End Function
End Class
End Namespace

View File

@@ -0,0 +1,118 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Namespace API.PornHub
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
Partial Friend Class OptionsForm : Inherits System.Windows.Forms.Form
<System.Diagnostics.DebuggerNonUserCode()>
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
Try
If disposing AndAlso components IsNot Nothing Then
components.Dispose()
End If
Finally
MyBase.Dispose(disposing)
End Try
End Sub
Private components As System.ComponentModel.IContainer
<System.Diagnostics.DebuggerStepThrough()>
Private Sub InitializeComponent()
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
Dim TP_MAIN As System.Windows.Forms.TableLayoutPanel
Me.CH_DOWN_GIFS = New System.Windows.Forms.CheckBox()
Me.CH_DOWN_PHOTO_MODELHUB = New System.Windows.Forms.CheckBox()
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
TP_MAIN = New System.Windows.Forms.TableLayoutPanel()
CONTAINER_MAIN.ContentPanel.SuspendLayout()
CONTAINER_MAIN.SuspendLayout()
TP_MAIN.SuspendLayout()
Me.SuspendLayout()
'
'CONTAINER_MAIN
'
'
'CONTAINER_MAIN.ContentPanel
'
CONTAINER_MAIN.ContentPanel.Controls.Add(TP_MAIN)
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(278, 52)
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
CONTAINER_MAIN.LeftToolStripPanelVisible = False
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
CONTAINER_MAIN.RightToolStripPanelVisible = False
CONTAINER_MAIN.Size = New System.Drawing.Size(278, 77)
CONTAINER_MAIN.TabIndex = 0
CONTAINER_MAIN.TopToolStripPanelVisible = False
'
'TP_MAIN
'
TP_MAIN.CellBorderStyle = System.Windows.Forms.TableLayoutPanelCellBorderStyle.[Single]
TP_MAIN.ColumnCount = 1
TP_MAIN.ColumnStyles.Add(New System.Windows.Forms.ColumnStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.Controls.Add(Me.CH_DOWN_GIFS, 0, 0)
TP_MAIN.Controls.Add(Me.CH_DOWN_PHOTO_MODELHUB, 0, 1)
TP_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
TP_MAIN.Location = New System.Drawing.Point(0, 0)
TP_MAIN.Name = "TP_MAIN"
TP_MAIN.RowCount = 3
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Absolute, 25.0!))
TP_MAIN.RowStyles.Add(New System.Windows.Forms.RowStyle(System.Windows.Forms.SizeType.Percent, 100.0!))
TP_MAIN.Size = New System.Drawing.Size(278, 52)
TP_MAIN.TabIndex = 0
'
'CH_DOWN_GIFS
'
Me.CH_DOWN_GIFS.AutoSize = True
Me.CH_DOWN_GIFS.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_DOWN_GIFS.Location = New System.Drawing.Point(4, 4)
Me.CH_DOWN_GIFS.Name = "CH_DOWN_GIFS"
Me.CH_DOWN_GIFS.Size = New System.Drawing.Size(270, 19)
Me.CH_DOWN_GIFS.TabIndex = 0
Me.CH_DOWN_GIFS.Text = "Download gifs"
Me.CH_DOWN_GIFS.UseVisualStyleBackColor = True
'
'CH_DOWN_PHOTO_MODELHUB
'
Me.CH_DOWN_PHOTO_MODELHUB.AutoSize = True
Me.CH_DOWN_PHOTO_MODELHUB.Dock = System.Windows.Forms.DockStyle.Fill
Me.CH_DOWN_PHOTO_MODELHUB.Location = New System.Drawing.Point(4, 30)
Me.CH_DOWN_PHOTO_MODELHUB.Name = "CH_DOWN_PHOTO_MODELHUB"
Me.CH_DOWN_PHOTO_MODELHUB.Size = New System.Drawing.Size(270, 19)
Me.CH_DOWN_PHOTO_MODELHUB.TabIndex = 1
Me.CH_DOWN_PHOTO_MODELHUB.Text = "Download photo only from ModelHub"
Me.CH_DOWN_PHOTO_MODELHUB.UseVisualStyleBackColor = True
'
'OptionsForm
'
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
Me.ClientSize = New System.Drawing.Size(278, 77)
Me.Controls.Add(CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = Global.SCrawler.My.Resources.SiteResources.InstagramIcon_32
Me.KeyPreview = True
Me.MaximizeBox = False
Me.MaximumSize = New System.Drawing.Size(294, 116)
Me.MinimizeBox = False
Me.MinimumSize = New System.Drawing.Size(294, 116)
Me.Name = "OptionsForm"
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
Me.Text = "Options"
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
CONTAINER_MAIN.ResumeLayout(False)
CONTAINER_MAIN.PerformLayout()
TP_MAIN.ResumeLayout(False)
TP_MAIN.PerformLayout()
Me.ResumeLayout(False)
End Sub
Private WithEvents CH_DOWN_GIFS As CheckBox
Private WithEvents CH_DOWN_PHOTO_MODELHUB As CheckBox
End Class
End Namespace

View File

@@ -0,0 +1,126 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<!--
Microsoft ResX Schema
Version 2.0
The primary goals of this format is to allow a simple XML format
that is mostly human readable. The generation and parsing of the
various data types are done through the TypeConverter classes
associated with the data types.
Example:
... ado.net/XML headers & schema ...
<resheader name="resmimetype">text/microsoft-resx</resheader>
<resheader name="version">2.0</resheader>
<resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>
<resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>
<data name="Name1"><value>this is my long string</value><comment>this is a comment</comment></data>
<data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data>
<data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64">
<value>[base64 mime encoded serialized .NET Framework object]</value>
</data>
<data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64">
<value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>
<comment>This is a comment</comment>
</data>
There are any number of "resheader" rows that contain simple
name/value pairs.
Each data row contains a name, and value. The row also contains a
type or mimetype. Type corresponds to a .NET class that support
text/value conversion through the TypeConverter architecture.
Classes that don't support this are serialized and stored with the
mimetype set.
The mimetype is used for serialized objects, and tells the
ResXResourceReader how to depersist the object. This is currently not
extensible. For a given mimetype the value must be set accordingly:
Note - application/x-microsoft.net.object.binary.base64 is the format
that the ResXResourceWriter will generate, however the reader can
read any of the formats listed below.
mimetype: application/x-microsoft.net.object.binary.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Binary.BinaryFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.soap.base64
value : The object must be serialized with
: System.Runtime.Serialization.Formatters.Soap.SoapFormatter
: and then encoded with base64 encoding.
mimetype: application/x-microsoft.net.object.bytearray.base64
value : The object must be serialized into a byte array
: using a System.ComponentModel.TypeConverter
: and then encoded with base64 encoding.
-->
<xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata">
<xsd:import namespace="http://www.w3.org/XML/1998/namespace" />
<xsd:element name="root" msdata:IsDataSet="true">
<xsd:complexType>
<xsd:choice maxOccurs="unbounded">
<xsd:element name="metadata">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" />
</xsd:sequence>
<xsd:attribute name="name" use="required" type="xsd:string" />
<xsd:attribute name="type" type="xsd:string" />
<xsd:attribute name="mimetype" type="xsd:string" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="assembly">
<xsd:complexType>
<xsd:attribute name="alias" type="xsd:string" />
<xsd:attribute name="name" type="xsd:string" />
</xsd:complexType>
</xsd:element>
<xsd:element name="data">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
<xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" msdata:Ordinal="1" />
<xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" />
<xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" />
<xsd:attribute ref="xml:space" />
</xsd:complexType>
</xsd:element>
<xsd:element name="resheader">
<xsd:complexType>
<xsd:sequence>
<xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" />
</xsd:sequence>
<xsd:attribute name="name" type="xsd:string" use="required" />
</xsd:complexType>
</xsd:element>
</xsd:choice>
</xsd:complexType>
</xsd:element>
</xsd:schema>
<resheader name="resmimetype">
<value>text/microsoft-resx</value>
</resheader>
<resheader name="version">
<value>2.0</value>
</resheader>
<resheader name="reader">
<value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<resheader name="writer">
<value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>
</resheader>
<metadata name="CONTAINER_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
<metadata name="TP_MAIN.GenerateMember" type="System.Boolean, mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
<value>False</value>
</metadata>
</root>

View File

@@ -0,0 +1,34 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Forms
Namespace API.PornHub
Friend Class OptionsForm
Private WithEvents MyDefs As DefaultFormOptions
Private ReadOnly MyExchangeOptions As UserExchangeOptions
Friend Sub New(ByRef ExchangeOptions As UserExchangeOptions)
InitializeComponent()
MyExchangeOptions = ExchangeOptions
MyDefs = New DefaultFormOptions(Me, Settings.Design)
End Sub
Private Sub MyForm_Load(sender As Object, e As EventArgs) Handles Me.Load
With MyDefs
.MyViewInitialize(True)
.AddOkCancelToolbar()
CH_DOWN_GIFS.Checked = MyExchangeOptions.DownloadGifs
CH_DOWN_PHOTO_MODELHUB.Checked = MyExchangeOptions.DownloadPhotoOnlyFromModelHub
.EndLoaderOperations()
End With
End Sub
Private Sub MyDefs_ButtonOkClick(ByVal Sender As Object, ByVal e As KeyHandleEventArgs) Handles MyDefs.ButtonOkClick
MyExchangeOptions.DownloadGifs = CH_DOWN_GIFS.Checked
MyExchangeOptions.DownloadPhotoOnlyFromModelHub = CH_DOWN_PHOTO_MODELHUB.Checked
MyDefs.CloseForm()
End Sub
End Class
End Namespace

View File

@@ -0,0 +1,123 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports SCrawler.API.Base
Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Namespace API.PornHub
<Manifest("AndyProgram_PornHub"), SavedPosts, SpecialForm(False), SeparatedTasks(1)>
Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations"
Friend Overrides ReadOnly Property Icon As Icon
Get
Return My.Resources.SiteResources.PornHubIcon_16
End Get
End Property
Friend Overrides ReadOnly Property Image As Image
Get
Return My.Resources.SiteResources.PornHubPic_16
End Get
End Property
Private ReadOnly Property CurlPathExists As Boolean
<PropertyOption(ControlText:="Download GIF", ControlToolTip:="Default for new users", ThreeStates:=True), PXML>
Friend ReadOnly Property DownloadGifs As PropertyValue
<PropertyOption(ControlText:="Download GIFs as mp4", ControlToolTip:="Download gifs in 'mp4' format instead of native 'webm'"), PXML>
Friend ReadOnly Property DownloadGifsAsMp4 As PropertyValue
<PropertyOption(ControlText:="Photo ModelHub only",
ControlToolTip:="Download photo only from ModelHub. Prornstar photos hosted on PornHub itself will not be downloaded." & vbCr &
"Attention! Downloading photos hosted on PornHub is a very heavy job."), PXML>
Friend ReadOnly Property DownloadPhotoOnlyFromModelHub As PropertyValue
<PropertyOption(ControlText:="Saved posts user", ControlToolTip:="Personal profile username"), PXML>
Friend ReadOnly Property SavedPostsUserName As PropertyValue
#End Region
#Region "Initializer"
Friend Sub New()
MyBase.New("PornHub", "pornhub.com")
Responser.CurlPath = $"cURL\curl.exe"
CurlPathExists = Responser.CurlPath.Exists
Responser.DeclaredError = EDP.ThrowException
DownloadGifsAsMp4 = New PropertyValue(True)
DownloadGifs = New PropertyValue(CInt(CheckState.Indeterminate), GetType(Integer))
DownloadPhotoOnlyFromModelHub = New PropertyValue(True)
SavedPostsUserName = New PropertyValue(String.Empty, GetType(String))
UrlPatternUser = "https://www.pornhub.com/{0}/{1}"
UserRegex = RParams.DMS("pornhub.com/([^/]+)/([^/]+).*?", 0, RegexReturn.ListByMatch)
ImageVideoContains = "pornhub"
End Sub
#End Region
#Region "GetInstance, GetSpecialData"
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
If What = ISiteSettings.Download.SavedPosts Then
Return New UserData With {
.IsSavedPosts = True,
.VideoPageModel = UserData.VideoPageModels.Favorite,
.PersonType = UserData.PersonTypeUser,
.User = New UserInfo With {.Name = $"{UserData.PersonTypeUser}_{CStr(AConvert(Of String)(SavedPostsUserName.Value, String.Empty))}"}
}
Else
Return New UserData
End If
End Function
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
If Available(ISiteSettings.Download.Main, True) Then
Using resp As Response = Responser.Copy
Dim spf$ = String.Empty
Dim f As SFile = GetSpecialDataFile(Path, AskForPath, spf)
Dim m As UserMedia = UserData.GetVideoInfo(URL, resp, f)
If m.State = UserMedia.States.Downloaded Then
m.SpecialFolder = f
Return {m}
End If
End Using
End If
Return Nothing
End Function
#End Region
#Region "Downloading"
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean
Return Settings.UseM3U8 And CurlPathExists And (Not What = ISiteSettings.Download.SavedPosts OrElse ACheck(SavedPostsUserName.Value))
End Function
#End Region
#Region "IsMyUser"
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
Try
If Not UserURL.IsEmptyString Then
Dim alist As List(Of String) = RegexReplace(UserURL.ToLower, UserRegex)
If alist.ListExists(3) Then Return New ExchangeOptions(Site, $"{alist(1)}_{alist(2)}")
End If
Return Nothing
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"[API.PornHub.SiteSettings.IsMyUser({UserURL})]", New ExchangeOptions)
End Try
End Function
#End Region
#Region "GetUserUrl, GetUserPostUrl"
Friend Overrides Function GetUserUrl(ByVal User As IPluginContentProvider, ByVal Channel As Boolean) As String
With DirectCast(User, UserData) : Return String.Format(UrlPatternUser, .PersonType, .NameTrue) : End With
End Function
Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
'TODELETE: remove comment
Return Media.URL_BASE '$"https://www.pornhub.com/view_video.php?viewkey={Media.Post.ID}"
End Function
#End Region
#Region "User options"
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
Dim e As UserExchangeOptions = Nothing
If Not Options Is Nothing AndAlso TypeOf Options Is UserExchangeOptions Then e = Options
If e Is Nothing Then e = New UserExchangeOptions(Me)
If OpenForm Then
Using f As New OptionsForm(e) : f.ShowDialog() : End Using
End If
End Sub
#End Region
End Class
End Namespace

View File

@@ -0,0 +1,656 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports System.Threading
Imports SCrawler.API.Base
Imports 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.PornHub
Friend Class UserData : Inherits UserDataBase
Private Const UrlPattern As String = "https://www.pornhub.com/{0}"
#Region "Declarations"
#Region "XML names"
Private Const Name_PersonType As String = "PersonType"
Private Const Name_NameTrue As String = "NameTrue"
Private Const Name_VideoPageModel As String = "VideoPageModel"
Private Const Name_PhotoPageModel As String = "PhotoPageModel"
Private Const Name_DownloadGifs As String = "DownloadGifs"
Private Const Name_DownloadPhotoOnlyFromModelHub As String = "DownloadPhotoOnlyFromModelHub"
#End Region
#Region "Structures"
Private Structure FlashVar : Implements IRegExCreator
Friend Name As String
Friend Value As String
Public Shared Widening Operator CType(ByVal Name As String) As FlashVar
Return New FlashVar With {.Name = Name}
End Operator
Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
If ParamsArray.ListExists(2) Then
Name = ParamsArray(0)
Value = ParamsArray(1)
If Not Value.IsEmptyString Then Value = Value.Replace(""" + """, String.Empty).Replace("""", String.Empty).StringTrim
End If
Return Me
End Function
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
Return CType(Obj, FlashVar).Name = Name
End Function
End Structure
Private Structure UserVideo : Implements IRegExCreator
Friend URL As String
Friend ID As String
Friend Title As String
Friend Function ToUserMedia() As UserMedia
Return New UserMedia(URL, UTypes.VideoPre) With {
.File = If(Title.IsEmptyString, .File, New SFile($"{Title}.mp4")),
.Post = ID
}
End Function
Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
If ParamsArray.ListExists Then
URL = ParamsArray(0)
ID = RegexReplace(URL, RegexVideo_Video_VideoKey)
URL = String.Format(UrlPattern, URL.TrimStart("/"))
Title = HtmlConverter(ParamsArray(1)).StringRemoveWinForbiddenSymbols.StringTrim
End If
Return Me
End Function
Public Overrides Function Equals(ByVal Obj As Object) As Boolean
Return DirectCast(Obj, UserVideo).URL = URL
End Function
End Structure
Private Structure PhotoBlock : Implements IRegExCreator
Friend AlbumID As String
Friend Data As String
Private Function CreateFromArray(ByVal ParamsArray() As String) As Object Implements IRegExCreator.CreateFromArray
If ParamsArray.ListExists(2) Then
AlbumID = ParamsArray(0)
Data = ParamsArray(1).StringTrim
End If
Return Me
End Function
End Structure
#End Region
#Region "Enums"
Friend Enum VideoPageModels As Integer
[Default] = 0
ConcatPage = 1
Favorite = 2
Undefined = -1
End Enum
Private Enum PhotoPageModels As Integer
Undefined = 0
PornHubPage = 1
ModelHubPage = 2
End Enum
#End Region
#Region "Constants"
Private Const PersonTypeModel As String = "model"
Friend Const PersonTypeUser As String = "users"
#End Region
#Region "Person"
Friend Property PersonType As String
Friend Property NameTrue As String
Private _FriendlyName As String = String.Empty
Friend Overrides Property FriendlyName As String
Get
If _FriendlyName.IsEmptyString Then Return NameTrue Else Return _FriendlyName
End Get
Set(ByVal n As String)
_FriendlyName = n
End Set
End Property
#End Region
#Region "Advanced fields"
Friend Property VideoPageModel As VideoPageModels = VideoPageModels.Undefined
Private Property PhotoPageModel As PhotoPageModels = PhotoPageModels.Undefined
Friend Property DownloadGifs As Boolean
Friend Property DownloadPhotoOnlyFromModelHub As Boolean = True
#End Region
#Region "ExchangeOptions"
Friend Overrides Function ExchangeOptionsGet() As Object
Return New UserExchangeOptions(Me)
End Function
Friend Overrides Sub ExchangeOptionsSet(ByVal Obj As Object)
If Not Obj Is Nothing AndAlso TypeOf Obj Is UserExchangeOptions Then
With DirectCast(Obj, UserExchangeOptions)
DownloadGifs = .DownloadGifs
DownloadPhotoOnlyFromModelHub = .DownloadPhotoOnlyFromModelHub
End With
End If
End Sub
#End Region
Private ReadOnly Property MySettings As SiteSettings
Get
Return DirectCast(HOST.Source, SiteSettings)
End Get
End Property
#End Region
#Region "Initializer, loader"
Friend Sub New()
UseInternalM3U8Function = True
End Sub
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
With Container
Dim SetNames As Action = Sub()
If Not Name.IsEmptyString And NameTrue.IsEmptyString Then
Dim n$() = Name.Split("_")
If n.ListExists(2) Then
NameTrue = Name.Replace($"{n(0)}_", String.Empty)
PersonType = n(0)
If (PersonType = PersonTypeModel Or PersonType = PersonTypeUser) And
VideoPageModel = VideoPageModels.Undefined Then VideoPageModel = VideoPageModels.Default
End If
End If
End Sub
If Loading Then
PersonType = .Value(Name_PersonType)
NameTrue = .Value(Name_NameTrue)
VideoPageModel = .Value(Name_VideoPageModel).FromXML(Of Integer)(VideoPageModels.Undefined)
PhotoPageModel = .Value(Name_PhotoPageModel).FromXML(Of Integer)(PhotoPageModels.Undefined)
DownloadGifs = .Value(Name_DownloadGifs).FromXML(Of Integer)(False)
DownloadPhotoOnlyFromModelHub = .Value(Name_DownloadPhotoOnlyFromModelHub).FromXML(Of Boolean)(True)
SetNames.Invoke()
Else
SetNames.Invoke()
.Add(Name_PersonType, PersonType)
.Add(Name_NameTrue, NameTrue)
.Add(Name_VideoPageModel, CInt(VideoPageModel))
.Add(Name_PhotoPageModel, CInt(PhotoPageModel))
.Add(Name_DownloadGifs, DownloadGifs.BoolToInteger)
.Add(Name_DownloadPhotoOnlyFromModelHub, DownloadPhotoOnlyFromModelHub.BoolToInteger)
End If
End With
End Sub
#End Region
#Region "Downloading"
#Region "Download override"
Private Const DataDownloaded As Integer = -10
Private Const DataDownloaded_NotFound As Integer = -20
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
Try
Responser.ResetStatus()
If PersonType = PersonTypeUser Then Responser.Mode = Response.Modes.Curl
If IsSavedPosts Then VideoPageModel = VideoPageModels.Favorite
Dim page% = 1
Dim __continue As Boolean = True
Dim __videoDone As Boolean = False
Dim d%
If DownloadVideos Then
If PersonType = PersonTypeUser Then Responser.Mode = Response.Modes.Curl : Responser.Method = "POST"
If VideoPageModel = VideoPageModels.Undefined Then
__continue = False
d = DownloadUserVideos(page, Token)
Select Case d
Case DataDownloaded : __continue = True : page += 1
Case 1 : VideoPageModel = VideoPageModels.ConcatPage
Case EXCEPTION_OPERATION_CANCELED : ThrowAny(Token)
Case DataDownloaded_NotFound : __videoDone = True
End Select
If Not __continue And Not __videoDone Then
d = DownloadUserVideos(page, Token)
Select Case d
Case DataDownloaded : __continue = True : page += 1
Case 1 : VideoPageModel = VideoPageModels.Undefined
Case EXCEPTION_OPERATION_CANCELED : ThrowAny(Token)
Case DataDownloaded_NotFound : __videoDone = True
End Select
End If
End If
If __continue And Not __videoDone Then
Do While DownloadUserVideos(page, Token) = DataDownloaded And page < 100 : page += 1 : Loop
End If
End If
Responser.Method = "GET"
If DownloadGifs And Not IsSavedPosts Then DownloadUserGifs(Token)
If DownloadImages Then DownloadUserPhotos(Token)
Finally
Responser.Mode = Response.Modes.Default
Responser.Method = "GET"
End Try
End Sub
#End Region
#Region "Download video"
Private ReadOnly Property VideoPageType As String
Get
Select Case VideoPageModel
Case VideoPageModels.Default : Return "/videos/upload"
Case VideoPageModels.Favorite : Return "/videos/favorites/"
Case Else : Return String.Empty
End Select
End Get
End Property
Private ReadOnly Property VideoPageAppender As String
Get
Return If(PersonType = PersonTypeUser, "ajax?o=newest&page=", String.Empty)
End Get
End Property
Private Overloads Function DownloadUserVideos(ByVal Page As Integer, ByVal Token As CancellationToken) As Integer
Const VideoUrlPattern$ = "https://www.pornhub.com/{0}/{1}{2}{3}"
Const HtmlPageNotFoundVideo$ = "<span>Error Page Not Found</span>"
Dim URL$ = String.Empty
Try
Dim p$
If PersonType = PersonTypeUser Then
p = Page
Else
p = IIf(Page = 1, String.Empty, $"?page={Page}")
End If
URL = $"{String.Format(VideoUrlPattern, PersonType, NameTrue, VideoPageType, VideoPageAppender)}{p}"
ThrowAny(Token)
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
If PersonType = PersonTypeUser And r.Contains(HtmlPageNotFoundVideo) Then Return DataDownloaded_NotFound
Dim l As List(Of UserVideo) = RegexFields(Of UserVideo)(r, {RegexVideo_Video_All}, {1, 2})
Dim lw As List(Of UserVideo) = Nothing
If Not PersonType = PersonTypeUser Then RegexFields(Of UserVideo)(r, {RegexVideo_Video_Wrong}, RegexVideo_Video_Wrong_Fields)
If l.ListExists Then
If lw.ListExists Then l.ListWithRemove(lw)
If l.Count > 0 Then
Dim lBefore% = l.Count
l.RemoveAll(Function(ByVal uv As UserVideo) As Boolean
If Not _TempPostsList.Contains(uv.ID) Then
_TempPostsList.Add(uv.ID)
Return False
Else
Return True
End If
End Function)
If l.Count > 0 Then _TempMediaList.ListAddList(l.Select(Function(uv) uv.ToUserMedia))
If l.Count = lBefore And l.Count > 0 Then Return DataDownloaded
End If
End If
End If
Return DataDownloaded_NotFound
Catch regex_ex As RegexFieldsTextBecameNullException
If PersonType = PersonTypeUser Or IsSavedPosts Then
Return DataDownloaded_NotFound
Else
Return ProcessException(regex_ex, Token, $"videos downloading error [{URL}]")
End If
Catch ex As Exception
Return ProcessException(ex, Token, $"videos downloading error [{URL}]")
End Try
End Function
#End Region
#Region "Download GIF"
Private Sub DownloadUserGifs(ByVal Token As CancellationToken)
Dim URL$ = $"https://www.pornhub.com/{PersonType}/{NameTrue}/gifs"
Try
ThrowAny(Token)
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
Dim n$
Dim m As UserMedia = Nothing
Dim l As List(Of RegexMatchStruct) = RegexFields(Of RegexMatchStruct)(r, {Regex_Gif_Array}, {1})
Dim l2 As List(Of String) = Nothing
Dim l3 As List(Of String) = Nothing
If l.ListExists Then l2 = l.Select(Function(ll) $"gif/{ll.Arr(0).Replace("gif", String.Empty)}").ToList
If l2.ListExists Then
For Each gif$ In l2
If Not _TempPostsList.Contains(gif) Then
_TempPostsList.Add(gif)
URL = $"https://www.pornhub.com/{gif}"
m = New UserMedia(URL, UTypes.Video) With {.Post = gif, .SpecialFolder = "GIFs\"}
ThrowAny(Token)
Try
r = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
If l3.ListExists Then l3.Clear() : l3 = Nothing
l3 = RegexReplace(r, Regex_Gif_UrlName)
If l3.ListExists(3) Then
m.URL = l3(2)
m.File = m.URL
n = HtmlConverter(l3(1)).StringRemoveWinForbiddenSymbols.StringTrim
If MySettings.DownloadGifsAsMp4.Value Then m.File.Extension = "mp4"
If Not n.IsEmptyString Then m.File.Name = n
End If
End If
Catch gif_down_ex As Exception
m.State = UserMedia.States.Missing
End Try
_TempMediaList.ListAddValue(m)
End If
Next
End If
If l.ListExists Then l.Clear()
If l2.ListExists Then l2.Clear()
If l3.ListExists Then l3.Clear()
End If
Catch ex As Exception
ProcessException(ex, Token, $"gifs downloading error [{URL}]")
End Try
End Sub
#End Region
#Region "Download photo"
Private Const PhotoUrlPattern_ModelHub As String = "https://www.modelhub.com/{0}/photos"
Private Const PhotoUrlPattern_PornHub As String = "https://www.pornhub.com/{0}/{1}/photos"
Private Sub DownloadUserPhotos(ByVal Token As CancellationToken)
Try
If IsSavedPosts Then
DownloadUserPhotos_SavedPosts(Token)
ElseIf PersonType = PersonTypeModel Then
If PhotoPageModel = PhotoPageModels.Undefined Then
If DownloadUserPhotos_ModelHub(Token) Then PhotoPageModel = PhotoPageModels.ModelHubPage
ThrowAny(Token)
If PhotoPageModel = PhotoPageModels.Undefined AndAlso DownloadPhotoOnlyFromModelHub AndAlso
DownloadUserPhotos_PornHub(Token) Then PhotoPageModel = PhotoPageModels.PornHubPage
Else
Select Case PhotoPageModel
Case PhotoPageModels.ModelHubPage : DownloadUserPhotos_ModelHub(Token)
Case PhotoPageModels.PornHubPage : If DownloadPhotoOnlyFromModelHub Then DownloadUserPhotos_PornHub(Token)
End Select
End If
ElseIf Not DownloadPhotoOnlyFromModelHub Then
DownloadUserPhotos_PornHub(Token)
End If
ThrowAny(Token)
Catch ex As Exception
ProcessException(ex, Token, $"photos downloading error")
End Try
End Sub
Private Function DownloadUserPhotos_ModelHub(ByVal Token As CancellationToken) As Boolean
Dim URL$ = String.Empty
Try
Dim jErr As New ErrorsDescriber(EDP.SendInLog + EDP.ReturnValue)
Dim albumName$
If PersonType = PersonTypeModel Then
URL = String.Format(PhotoUrlPattern_ModelHub, NameTrue)
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_ModelHub_PhotoBlocks}, {1, 2})
If l.ListExists Then l.RemoveAll(Function(ll) ll.Data.IsEmptyString)
If l.ListExists Then
Dim albumRegex As RParams = RParams.DMS("", 1, EDP.ReturnValue)
For Each block As PhotoBlock In l
If Not _TempPostsList.Contains(block.AlbumID) Then _TempPostsList.Add(block.AlbumID) Else Continue For
albumRegex.Pattern = "<li id=""" & block.AlbumID & """ class=""modelBox"">[\r\n\s]*?<div class=""modelPhoto"">[\r\n\s]*?\<[^\>]*?alt=""([^""]*)"""
albumName = StringTrim(RegexReplace(r, albumRegex))
If albumName.IsEmptyString Then albumName = block.AlbumID
Using j As EContainer = JsonDocument.Parse("{" & block.Data & "}", jErr)
If Not j Is Nothing Then
If If(j("urls")?.Count, 0) > 0 Then
_TempMediaList.ListAddList(j("urls").Select(Function(jj) _
New UserMedia(jj.ItemF({0}).XmlIfNothingValue, UTypes.Picture) With {
.SpecialFolder = $"Albums\{albumName}\"}), LNC)
End If
End If
End Using
Next
l.Clear()
End If
End If
End If
Return True
Catch ex As Exception
ThrowAny(Token)
Return False
End Try
End Function
Private Overloads Function DownloadUserPhotos_PornHub(ByVal Token As CancellationToken) As Boolean
Try
Dim albumName$
Dim page%
Dim r$ = Responser.GetResponse(String.Format(PhotoUrlPattern_PornHub, PersonType, NameTrue))
If Not r.IsEmptyString Then
Dim l As List(Of PhotoBlock) = RegexFields(Of PhotoBlock)(r, {Regex_Photo_PornHub_PhotoBlocks}, {2, 1})
If l.ListExists Then l.RemoveAll(Function(ll) ll.AlbumID.IsEmptyString)
If l.ListExists Then
For Each block As PhotoBlock In l
If Not _TempPostsList.Contains(block.AlbumID) Then _TempPostsList.Add(block.AlbumID) Else Continue For
albumName = block.Data
If albumName.IsEmptyString Then
albumName = block.AlbumID.Split("/").LastOrDefault.StringTrim
Else
albumName = HtmlConverter(albumName).StringRemoveWinForbiddenSymbols.StringTrim
End If
page = 1
Do While DownloadUserPhotos_PornHub(page, block.AlbumID, albumName, Token) : page += 1 : Loop
Next
l.Clear()
End If
End If
Return True
Catch ex As Exception
ThrowAny(Token)
Return False
End Try
End Function
Private Overloads Function DownloadUserPhotos_PornHub(ByVal Page As Integer, ByVal AlbumID As String, ByVal AlbumName As String,
ByVal Token As CancellationToken) As Boolean
Try
Dim r$ = Responser.GetResponse($"https://www.pornhub.com{AlbumID}{IIf(Page = 1, String.Empty, $"?page={Page}")}")
If Not r.IsEmptyString Then
Dim l As List(Of String) = RegexReplace(r, Regex_Photo_PornHub_AlbumPhotoArr)
If l.ListExists Then l.RemoveAll(Function(_url) _url.IsEmptyString)
If l.ListExists Then
For Each url$ In l
ThrowAny(Token)
Try
r = Responser.GetResponse(url)
If Not r.IsEmptyString Then
url = RegexReplace(r, Regex_Photo_PornHub_SinglePhoto)
If Not url.IsEmptyString Then _
_TempMediaList.ListAddValue(New UserMedia(url, UTypes.Picture) With {.SpecialFolder = $"Albums\{AlbumName}\"}, LNC)
End If
Catch
End Try
Next
l.Clear()
Return True
End If
End If
Return False
Catch ex As Exception
ThrowAny(Token)
Return False
End Try
End Function
Private Function DownloadUserPhotos_SavedPosts(ByVal Token As CancellationToken) As Boolean
Const HtmlPageNotFoundPhoto$ = "Page Not Found"
Dim URL$ = $"https://www.pornhub.com/{PersonType}/{NameTrue}/photos/favorites"
Try
Dim r$ = Responser.GetResponse(URL)
If Not r.IsEmptyString Then
If r.Contains(HtmlPageNotFoundPhoto) Then Return False
Dim urls As List(Of String) = RegexReplace(r, Regex_Photo_PornHub_AlbumPhotoArr)
If urls.ListExists Then
Dim NewUrl$
Dim m As UserMedia
Dim l2 As List(Of UserMedia) = urls.Select(Function(__url) New UserMedia(__url, UTypes.Picture) With {
.Post = __url.Split("/").LastOrDefault}).ToList
urls.Clear()
If l2.ListExists Then l2.RemoveAll(Function(media) media.URL.IsEmptyString)
If l2.ListExists Then
Dim lBefore% = l2.Count
If _TempPostsList.Count > 0 Then l2.RemoveAll(Function(media) _TempPostsList.Contains(media.Post.ID))
If l2.Count > 0 Then
For i% = 0 To l2.Count - 1
m = l2(i)
ThrowAny(Token)
Try
r = Responser.GetResponse(m.URL)
If Not r.IsEmptyString Then
NewUrl = RegexReplace(r, Regex_Photo_PornHub_SinglePhoto)
If Not NewUrl.IsEmptyString Then
m.URL = NewUrl
m.File = NewUrl
_TempPostsList.ListAddValue(m.Post.ID, LNC)
Else
Throw New Exception
End If
End If
Catch
m.State = UserMedia.States.Missing
End Try
_TempMediaList.ListAddValue(m, LNC)
Next
End If
Return l2.Count = lBefore
End If
End If
End If
Return False
Catch ex As Exception
Return ProcessException(ex, Token, $"photos downloading error [{URL}]")
End Try
End Function
#End Region
#End Region
#Region "ReparseVideo"
Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken)
Const ERR_NEW_URL$ = "ERR_NEW_URL"
Dim URL$ = String.Empty
Try
If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(tm) tm.Type = UTypes.VideoPre) Then
Dim m As UserMedia
Dim r$, NewUrl$
For i% = _TempMediaList.Count - 1 To 0 Step -1
If _TempMediaList(i).Type = UTypes.VideoPre Then
m = _TempMediaList(i)
ThrowAny(Token)
Try
URL = m.URL
r = Responser.Curl(URL)
If Not r.IsEmptyString Then
NewUrl = CreateVideoURL(r)
If NewUrl.IsEmptyString Then
Throw New Exception With {.HelpLink = ERR_NEW_URL}
Else
m.URL = NewUrl
m.Type = UTypes.m3u8
_TempMediaList(i) = m
End If
Else
_TempMediaList.RemoveAt(i)
End If
Catch mid_ex As Exception
If mid_ex.HelpLink = ERR_NEW_URL OrElse DownloadingException(mid_ex, "") = 1 Then
m.State = UserMedia.States.Missing
_TempMediaList(i) = m
Else
_TempMediaList.RemoveAt(i)
End If
End Try
End If
Next
End If
Catch ex As Exception
ProcessException(ex, Token, "video reparsing error", False)
End Try
End Sub
#End Region
#Region "ReparseMissing"
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
Dim rList As New List(Of Integer)
Try
If ContentMissingExists Then
Dim m As UserMedia
Dim r$
Dim eCurl As New ErrorsDescriber(EDP.ReturnValue)
For i% = 0 To _ContentList.Count - 1
m = _ContentList(i)
If m.State = UserMedia.States.Missing AndAlso Not m.URL_BASE.IsEmptyString Then
ThrowAny(Token)
r = Responser.Curl(m.URL_BASE, eCurl)
If Not r.IsEmptyString Then
Dim NewUrl$ = CreateVideoURL(r)
If Not NewUrl.IsEmptyString Then
m.URL = NewUrl
_TempMediaList.ListAddValue(m, LNC)
rList.Add(i)
End If
End If
End If
Next
End If
Catch ex As Exception
ProcessException(ex, Token, "missing data downloading error")
Finally
If rList.Count > 0 Then
For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next
rList.Clear()
End If
End Try
End Sub
#End Region
#Region "Download content"
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
DownloadContentDefault(Token)
End Sub
Protected Overrides Function DownloadM3U8(ByVal URL As String, ByVal Media As UserMedia, ByVal DestinationFile As SFile) As SFile
Return M3U8.Download(URL, Responser, DestinationFile)
End Function
#End Region
#Region "CreateVideoURL"
Private Shared Function CreateVideoURL(ByVal r As String) As String
Try
Dim OutStr$ = String.Empty
If Not r.IsEmptyString Then
Dim _VarBlock$ = RegexReplace(r, RegexVideo_FlashVarsBlock)
If Not _VarBlock.IsEmptyString Then
Dim vars As List(Of FlashVar) = RegexFields(Of FlashVar)(_VarBlock, {RegexVideo_FlashVars_Vars}, {1, 2})
Dim compiler As List(Of String) = RegexReplace(_VarBlock, RegexVideo_FlashVars_Compiler)
If vars.ListExists And compiler.ListExists Then
Dim v$
Dim i%
For Each var$ In compiler
i = vars.IndexOf(var)
If i >= 0 Then
v = vars(i).Value
If Not v.IsEmptyString Then OutStr &= v
End If
Next
End If
End If
End If
Return OutStr
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.PornHub.UserData.CreateVideoURL]", String.Empty)
End Try
End Function
#End Region
#Region "Standalone downloader"
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal Responser As Response, ByVal Destination As SFile) As UserMedia
Try
Dim r$ = Responser.Curl(URL)
If Not r.IsEmptyString Then
Dim NewUrl$ = CreateVideoURL(r)
If Not NewUrl.IsEmptyString Then
Dim f As SFile = M3U8.Download(NewUrl, Responser, Destination)
If Not f.IsEmptyString Then Return New UserMedia With {.State = UserMedia.States.Downloaded}
End If
End If
Return Nothing
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, $"PornHub standalone download error: [{URL}]", New UserMedia)
End Try
End Function
#End Region
#Region "Exceptions"
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String,
Optional ByVal FromPE As Boolean = False, Optional ByVal EObj As Object = Nothing) As Integer
If Responser.Status = Net.WebExceptionStatus.ConnectionClosed Then
Return 1
ElseIf Responser.StatusCode = Net.HttpStatusCode.ServiceUnavailable Then
Return 2
Else
Return 0
End If
End Function
#End Region
End Class
End Namespace

View File

@@ -0,0 +1,23 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Namespace API.PornHub
Friend Class UserExchangeOptions
Friend Property DownloadGifs As Boolean
Friend Property DownloadPhotoOnlyFromModelHub As Boolean
Friend Sub New(ByVal u As UserData)
DownloadGifs = u.DownloadGifs
DownloadPhotoOnlyFromModelHub = u.DownloadPhotoOnlyFromModelHub
End Sub
Friend Sub New(ByVal s As SiteSettings)
Dim v As CheckState = CInt(s.DownloadGifs.Value)
DownloadGifs = Not v = CheckState.Unchecked
DownloadPhotoOnlyFromModelHub = s.DownloadPhotoOnlyFromModelHub.Value
End Sub
End Class
End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -236,8 +236,19 @@ Namespace API.Reddit
Return If(Name.IsEmptyString, ID, Name) Return If(Name.IsEmptyString, ID, Name)
End Function End Function
Friend Sub Delete() Friend Sub Delete()
File.Delete(, SFODelete.DeleteToRecycleBin) Dim f As SFile = ChannelsCollection.ChannelsDeletedPath
FilePosts.Delete(, SFODelete.DeleteToRecycleBin) With File
f.Name = .Name
f.Extension = .Extension
.Copy(f,, True, SFODelete.DeleteToRecycleBin)
.Delete(, SFODelete.DeleteToRecycleBin)
End With
With FilePosts
f.Name = .Name
f.Extension = .Extension
.Copy(f,, True, SFODelete.DeleteToRecycleBin)
.Delete(, SFODelete.DeleteToRecycleBin)
End With
End Sub End Sub
Friend Sub DownloadData(ByVal Token As CancellationToken, Optional ByVal SkipExists As Boolean = True, Friend Sub DownloadData(ByVal Token As CancellationToken, Optional ByVal SkipExists As Boolean = True,
Optional ByVal p As MyProgress = Nothing) Optional ByVal p As MyProgress = Nothing)

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,10 +6,10 @@
' '
' 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 SCrawler.API.Base
Imports PersonalUtilities.Tools Imports PersonalUtilities.Tools
Imports PersonalUtilities.Forms.Toolbars Imports PersonalUtilities.Forms.Toolbars
Imports SCrawler.API.Base
Imports System.Threading
Namespace API.Reddit Namespace API.Reddit
Friend Class ChannelsCollection : Implements ICollection(Of Channel), IMyEnumerator(Of Channel), IChannelLimits, IDisposable Friend Class ChannelsCollection : Implements ICollection(Of Channel), IMyEnumerator(Of Channel), IChannelLimits, IDisposable
Friend Shared ReadOnly Property ChannelsPath As SFile Friend Shared ReadOnly Property ChannelsPath As SFile
@@ -17,6 +17,11 @@ Namespace API.Reddit
Return $"{SettingsFolderName}\Channels\" Return $"{SettingsFolderName}\Channels\"
End Get End Get
End Property End Property
Friend Shared ReadOnly Property ChannelsDeletedPath As SFile
Get
Return $"{SettingsFolderName}\ChannelsDeleted\"
End Get
End Property
Friend Shared ReadOnly Property ChannelsPathCache As SFile Friend Shared ReadOnly Property ChannelsPathCache As SFile
Get Get
Return $"{Settings.GlobalPath.Value.PathWithSeparator}_CachedData\" Return $"{Settings.GlobalPath.Value.PathWithSeparator}_CachedData\"

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -8,7 +8,7 @@
' but WITHOUT ANY WARRANTY ' but WITHOUT ANY WARRANTY
Imports System.Net Imports System.Net
Imports SCrawler.API.Reddit.M3U8_Declarations Imports SCrawler.API.Reddit.M3U8_Declarations
Imports PersonalUtilities.Tools.WEB Imports PersonalUtilities.Tools.Web
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.Reddit Namespace API.Reddit
Namespace M3U8_Declarations Namespace M3U8_Declarations
@@ -130,7 +130,7 @@ Namespace API.Reddit
End If End If
If CachePath.Exists(SFO.Path) Then If CachePath.Exists(SFO.Path) Then
Dim p As New SFileNumbers(ConcatFile.Name,,, New ANumbers With {.Format = ANumbers.Formats.General}) Dim p As New SFileNumbers(ConcatFile.Name,,, New ANumbers With {.Format = ANumbers.Formats.General})
ConcatFile = SFile.Indexed_IndexFile(ConcatFile,, p, EDP.ThrowException) 'EDP.ReturnValue ConcatFile = SFile.Indexed_IndexFile(ConcatFile,, p, EDP.ThrowException)
Dim i% Dim i%
Dim eFiles As New List(Of SFile) Dim eFiles As New List(Of SFile)
Dim dFile As SFile = CachePath Dim dFile As SFile = CachePath

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -27,7 +27,6 @@ Namespace API.Reddit
Dim TP_VIEW_MODE As System.Windows.Forms.TableLayoutPanel Dim TP_VIEW_MODE As System.Windows.Forms.TableLayoutPanel
Dim LBL_VIEW_MODE As System.Windows.Forms.Label Dim LBL_VIEW_MODE As System.Windows.Forms.Label
Dim LBL_PERIOD As System.Windows.Forms.Label Dim LBL_PERIOD As System.Windows.Forms.Label
Dim resources As System.ComponentModel.ComponentResourceManager = New System.ComponentModel.ComponentResourceManager(GetType(RedditViewSettingsForm))
Me.OPT_VIEW_MODE_NEW = New System.Windows.Forms.RadioButton() Me.OPT_VIEW_MODE_NEW = New System.Windows.Forms.RadioButton()
Me.OPT_VIEW_MODE_HOT = New System.Windows.Forms.RadioButton() Me.OPT_VIEW_MODE_HOT = New System.Windows.Forms.RadioButton()
Me.OPT_VIEW_MODE_TOP = New System.Windows.Forms.RadioButton() Me.OPT_VIEW_MODE_TOP = New System.Windows.Forms.RadioButton()
@@ -266,7 +265,7 @@ Namespace API.Reddit
Me.ClientSize = New System.Drawing.Size(477, 112) Me.ClientSize = New System.Drawing.Size(477, 112)
Me.Controls.Add(CONTAINER_MAIN) Me.Controls.Add(CONTAINER_MAIN)
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
Me.Icon = CType(resources.GetObject("$this.Icon"), System.Drawing.Icon) Me.Icon = Global.SCrawler.My.Resources.SiteResources.RedditIcon_128
Me.KeyPreview = True Me.KeyPreview = True
Me.MaximizeBox = False Me.MaximizeBox = False
Me.MaximumSize = New System.Drawing.Size(493, 151) Me.MaximumSize = New System.Drawing.Size(493, 151)

File diff suppressed because it is too large Load Diff

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -68,16 +68,7 @@ Namespace API.Reddit
End With End With
MyDefs.CloseForm() MyDefs.CloseForm()
End Sub End Sub
Private Sub OPT_VIEW_MODE_NEW_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_VIEW_MODE_NEW.CheckedChanged Private Sub ChangePeriodEnabled() Handles OPT_VIEW_MODE_NEW.CheckedChanged, OPT_VIEW_MODE_HOT.CheckedChanged, OPT_VIEW_MODE_TOP.CheckedChanged
ChangePeriodEnabled()
End Sub
Private Sub OPT_VIEW_MODE_HOT_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_VIEW_MODE_HOT.CheckedChanged
ChangePeriodEnabled()
End Sub
Private Sub OPT_VIEW_MODE_TOP_CheckedChanged(sender As Object, e As EventArgs) Handles OPT_VIEW_MODE_TOP.CheckedChanged
ChangePeriodEnabled()
End Sub
Private Sub ChangePeriodEnabled()
TP_PERIOD.Enabled = OPT_VIEW_MODE_TOP.Checked TP_PERIOD.Enabled = OPT_VIEW_MODE_TOP.Checked
End Sub End Sub
End Class End Class

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -9,24 +9,23 @@
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.Plugin Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports PersonalUtilities.Tools.WEB
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports DownDetector = SCrawler.API.Base.DownDetector Imports DownDetector = SCrawler.API.Base.DownDetector
Imports Download = SCrawler.Plugin.ISiteSettings.Download Imports Download = SCrawler.Plugin.ISiteSettings.Download
Namespace API.Reddit Namespace API.Reddit
<Manifest(RedditSiteKey), UseClassAsIs, SavedPosts, SpecialForm(False)> <Manifest(RedditSiteKey), SavedPosts, SpecialForm(False)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
Friend Overrides ReadOnly Property Icon As Icon Friend Overrides ReadOnly Property Icon As Icon
Get Get
Return My.Resources.RedditIcon Return My.Resources.SiteResources.RedditIcon_128
End Get End Get
End Property End Property
Friend Overrides ReadOnly Property Image As Image Friend Overrides ReadOnly Property Image As Image
Get Get
Return My.Resources.RedditPic512 Return My.Resources.SiteResources.RedditPic_512
End Get End Get
End Property End Property
<PropertyOption(ControlText:="Saved posts user"), PXML("SavedPostsUserName")> <PropertyOption(ControlText:="Saved posts user", ControlToolTip:="Personal profile username"), PXML>
Friend ReadOnly Property SavedPostsUserName As PropertyValue Friend ReadOnly Property SavedPostsUserName As PropertyValue
<PropertyOption(ControlText:="Use M3U8", ControlToolTip:="Use M3U8 or mp4 for Reddit videos"), PXML> <PropertyOption(ControlText:="Use M3U8", ControlToolTip:="Use M3U8 or mp4 for Reddit videos"), PXML>
Friend ReadOnly Property UseM3U8 As PropertyValue Friend ReadOnly Property UseM3U8 As PropertyValue
@@ -40,7 +39,8 @@ Namespace API.Reddit
UseM3U8 = New PropertyValue(True) UseM3U8 = New PropertyValue(True)
UrlPatternUser = "https://www.reddit.com/user/{0}/" UrlPatternUser = "https://www.reddit.com/user/{0}/"
UrlPatternChannel = "https://www.reddit.com/r/{0}/" UrlPatternChannel = "https://www.reddit.com/r/{0}/"
ImageVideoContains = "redgifs" ImageVideoContains = "reddit.com"
UserRegex = RParams.DM("[htps:/]{7,8}.*?reddit.com/([user]{1,4})/([^/]+)", 0, RegexReturn.ListByMatch, EDP.ReturnValue)
End Sub End Sub
Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider Friend Overrides Function GetInstance(ByVal What As Download) As IPluginContentProvider
Select Case What Select Case What
@@ -56,19 +56,9 @@ Namespace API.Reddit
End Select End Select
Return Nothing Return Nothing
End Function End Function
Private ReadOnly RedditRegEx1 As RParams = RParams.DMS("[htps:/]{7,8}.*?reddit.com/user/([^/]+)", 1)
Private ReadOnly RedditRegEx2 As RParams = RParams.DMS(".?u/([^/]+)", 1)
Private ReadOnly RedditChannelRegEx1 As RParams = RParams.DMS("[htps:/]{7,8}.*?reddit.com/r/([^/]+)", 1)
Private ReadOnly RedditChannelRegEx2 As RParams = RParams.DMS(".?r/([^/]+)", 1)
Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions Friend Overrides Function IsMyUser(ByVal UserURL As String) As ExchangeOptions
Dim s$ Dim l As List(Of String) = RegexReplace(UserURL, UserRegex)
Dim c% = 0 If l.ListExists(3) Then Return New ExchangeOptions(Site, l(2), l(1) = "r") Else Return Nothing
For Each r As RParams In {RedditRegEx1, RedditRegEx2, RedditChannelRegEx1, RedditChannelRegEx2}
s = RegexReplace(UserURL, r)
If Not s.IsEmptyString Then Return New ExchangeOptions(Site, s, c > 1)
c += 1
Next
Return Nothing
End Function End Function
Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean Friend Overrides Function Available(ByVal What As Download, ByVal Silent As Boolean) As Boolean
Try Try
@@ -80,20 +70,32 @@ Namespace API.Reddit
If Silent Then If Silent Then
Return False Return False
Else Else
Return MsgBoxE({"Over the past hour, Reddit has received an average of " & If MsgBoxE({"Over the past hour, Reddit has received an average of " &
avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr & avg.NumToString(New ANumbers With {.FormatOptions = ANumbers.Options.GroupIntegral}) & " outage reports:" & vbCr &
dl.ListToString(vbCr) & vbCr & vbCr & dl.ListToString(vbCr) & vbCr & vbCr &
"Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes "Do you want to continue parsing Reddit data?", "There are outage reports on Reddit"}, vbYesNo) = vbYes Then
UpdateRedGifsToken()
Return True
Else
Return False
End If
End If End If
End If End If
End If End If
UpdateRedGifsToken()
Return True Return True
Catch ex As Exception Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[API.Reddit.SiteSettings.Available]", True) Return ErrorsDescriber.Execute(EDP.SendInLog + EDP.ReturnValue, ex, "[API.Reddit.SiteSettings.Available]", True)
End Try End Try
End Function End Function
Friend Overrides Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia) Private Sub UpdateRedGifsToken()
Return UserData.GetVideoInfo(URL, Responser) DirectCast(Settings(RedGifs.RedGifsSiteKey).Source, RedGifs.SiteSettings).UpdateTokenIfRequired()
End Sub
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
Dim spf$ = String.Empty
Dim f As SFile = GetSpecialDataFile(Path, AskForPath, spf)
f = $"{f.PathWithSeparator}OptionalPath\"
Return UserData.GetVideoInfo(URL, Responser, f, spf)
End Function End Function
Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean) Friend Overrides Sub UserOptions(ByRef Options As Object, ByVal OpenForm As Boolean)
If Options Is Nothing OrElse Not TypeOf Options Is RedditViewExchange Then Options = New RedditViewExchange If Options Is Nothing OrElse Not TypeOf Options Is RedditViewExchange Then Options = New RedditViewExchange
@@ -101,8 +103,8 @@ Namespace API.Reddit
Using f As New RedditViewSettingsForm(Options) : f.ShowDialog() : End Using Using f As New RedditViewSettingsForm(Options) : f.ShowDialog() : End Using
End If End If
End Sub End Sub
Friend Overrides Function GetUserPostUrl(ByVal UserID As String, ByVal PostID As String) As String Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
Return $"https://www.reddit.com/comments/{PostID.Split("_").LastOrDefault}/" Return $"https://www.reddit.com/comments/{Media.Post.ID.Split("_").LastOrDefault}/"
End Function End Function
End Class End Class
End Namespace End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,15 +6,16 @@
' '
' 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.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.ImageRenderer
Imports PersonalUtilities.Tools.WEB
Imports PersonalUtilities.Tools.WebDocuments.JSON
Imports System.Net Imports System.Net
Imports System.Threading Imports System.Threading
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports SCrawler.API.Reddit.RedditViewExchange Imports SCrawler.API.Reddit.RedditViewExchange
Imports SCrawler.Plugin.Hosts
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.ImageRenderer
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports UStates = SCrawler.API.Base.UserMedia.States Imports UStates = SCrawler.API.Base.UserMedia.States
Imports UTypes = SCrawler.API.Base.UserMedia.Types Imports UTypes = SCrawler.API.Base.UserMedia.Types
Imports CView = SCrawler.API.Reddit.IRedditView.View Imports CView = SCrawler.API.Reddit.IRedditView.View
@@ -150,9 +151,9 @@ Namespace API.Reddit
End Sub End Sub
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
_TotalPostsDownloaded = 0 _TotalPostsDownloaded = 0
'PENDING: Reddit ReparseMissing (DownloadDataF)
'If Not IsSavedPosts AndAlso (Not IsChannel OrElse ChannelInfo Is Nothing) Then ReparseMissing(Token)
If IsSavedPosts Then If IsSavedPosts Then
'TODO: Reddit saved posts: remove Unicode converter?
Responser.DecodersError = EDP.ReturnValue
DownloadDataChannel(String.Empty, Token) DownloadDataChannel(String.Empty, Token)
ElseIf IsChannel Then ElseIf IsChannel Then
If ChannelInfo Is Nothing Then If ChannelInfo Is Nothing Then
@@ -177,6 +178,8 @@ Namespace API.Reddit
#Region "Download Functions (User, Channel)" #Region "Download Functions (User, Channel)"
Private _TotalPostsDownloaded As Integer = 0 Private _TotalPostsDownloaded As Integer = 0
Private ReadOnly _CrossPosts As List(Of String) Private ReadOnly _CrossPosts As List(Of String)
Private Const SiteGfycatKey As String = "gfycat"
Private Const SiteRedGifsKey As String = "redgifs"
Private Sub DownloadDataUser(ByVal POST As String, ByVal Token As CancellationToken) Private Sub DownloadDataUser(ByVal POST As String, ByVal Token As CancellationToken)
Const CPRI$ = "crosspostRootId" Const CPRI$ = "crosspostRootId"
Const CPPI$ = "crosspostParentId" Const CPPI$ = "crosspostParentId"
@@ -239,7 +242,7 @@ Namespace API.Reddit
_ItemsBefore = _TempMediaList.Count _ItemsBefore = _TempMediaList.Count
added = True added = True
s = nn.ItemF({"source", "url"}) s = nn.ItemF({"source", "url"})
If s.XmlIfNothingValue("/").StringContains({"redgifs.com", "gfycat.com"}) Then If s.XmlIfNothingValue("/").StringContains({$"{SiteRedGifsKey}.com", $"{SiteGfycatKey}.com"}) Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre, s.Value, _PostID(), PostDate,, IsChannel), LNC) _TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre, s.Value, _PostID(), PostDate,, IsChannel), LNC)
ElseIf Not CreateImgurMedia(s.XmlIfNothingValue, _PostID(), PostDate,, IsChannel) Then ElseIf Not CreateImgurMedia(s.XmlIfNothingValue, _PostID(), PostDate,, IsChannel) Then
s = nn.ItemF({"media"}).XmlIfNothing s = nn.ItemF({"media"}).XmlIfNothing
@@ -271,7 +274,7 @@ Namespace API.Reddit
If Not s.IsEmptyString AndAlso TryFile(s.Value) Then If Not s.IsEmptyString AndAlso TryFile(s.Value) Then
With s.Value.ToLower With s.Value.ToLower
Select Case True Select Case True
Case .Contains("redgifs"), .Contains("gfycat") : tmpType = UTypes.VideoPre Case .Contains(SiteRedGifsKey), .Contains(SiteGfycatKey) : tmpType = UTypes.VideoPre
Case .Contains("m3u8") : If Settings.UseM3U8 Then tmpType = UTypes.m3u8 Case .Contains("m3u8") : If Settings.UseM3U8 Then tmpType = UTypes.m3u8
Case .Contains(".gif") And TryFile(s.Value) : tmpType = UTypes.GIF Case .Contains(".gif") And TryFile(s.Value) : tmpType = UTypes.GIF
Case TryFile(s.Value) : tmpType = UTypes.Picture Case TryFile(s.Value) : tmpType = UTypes.Picture
@@ -329,7 +332,7 @@ Namespace API.Reddit
If ChannelPostsNames.Contains(PostID) Then If ChannelPostsNames.Contains(PostID) Then
If ViewMode = CView.New Then ExistsDetected = True Else NewPostDetected = True 'bypass If ViewMode = CView.New Then ExistsDetected = True Else NewPostDetected = True 'bypass
Continue For 'Exit Sub Continue For
End If End If
If DownloadLimitCount.HasValue AndAlso _TotalPostsDownloaded >= DownloadLimitCount.Value Then Exit Sub If DownloadLimitCount.HasValue AndAlso _TotalPostsDownloaded >= DownloadLimitCount.Value Then Exit Sub
If Not DownloadLimitPost.IsEmptyString AndAlso DownloadLimitPost = PostID Then Exit Sub If Not DownloadLimitPost.IsEmptyString AndAlso DownloadLimitPost = PostID Then Exit Sub
@@ -377,8 +380,6 @@ Namespace API.Reddit
ElseIf Not s.Value({"media", "reddit_video"}, "fallback_url").IsEmptyString Then ElseIf Not s.Value({"media", "reddit_video"}, "fallback_url").IsEmptyString Then
tmpUrl = s.Value({"media", "reddit_video"}, "fallback_url") tmpUrl = s.Value({"media", "reddit_video"}, "fallback_url")
If SaveToCache Then If SaveToCache Then
'TODELETE: Reddit thumbnail -> GetVideoRedditPreview
'tmpUrl = s.Value("thumbnail")
tmpUrl = GetVideoRedditPreview(s) tmpUrl = GetVideoRedditPreview(s)
If Not tmpUrl.IsEmptyString Then If Not tmpUrl.IsEmptyString Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, tmpUrl, PostID, PostDate, _UserID, IsChannel, False), LNC) _TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, tmpUrl, PostID, PostDate, _UserID, IsChannel, False), LNC)
@@ -388,7 +389,6 @@ Namespace API.Reddit
_TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, s.Value({"media", "reddit_video"}, "hls_url"), _TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, s.Value({"media", "reddit_video"}, "hls_url"),
PostID, PostDate, _UserID, IsChannel), LNC) PostID, PostDate, _UserID, IsChannel), LNC)
Else Else
'_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre + UTypes.m3u8, tmpUrl, PostID, PostDate, _UserID, IsChannel), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, tmpUrl, PostID, PostDate, _UserID, IsChannel), LNC) _TempMediaList.ListAddValue(MediaFromData(UTypes.Video, tmpUrl, PostID, PostDate, _UserID, IsChannel), LNC)
_TotalPostsDownloaded += 1 _TotalPostsDownloaded += 1
End If End If
@@ -418,19 +418,6 @@ Namespace API.Reddit
End Sub End Sub
#End Region #End Region
#Region "Download Base Functions" #Region "Download Base Functions"
Private Function ImgurPicture(ByVal Source As EContainer, ByVal Value As String) As String
Try
Dim e As EContainer = Source({"source", "url"}).XmlIfNothing
If Not e.IsEmptyString AndAlso e.Value.ToLower.Contains("imgur") Then
Return e.Value
Else
Return Value
End If
Catch ex As Exception
LogError(ex, "[ImgurPicture]")
Return Value
End Try
End Function
Private Function CreateImgurMedia(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String, Private Function CreateImgurMedia(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
Optional ByVal _UserID As String = "", Optional ByVal IsChannel As Boolean = False) As Boolean Optional ByVal _UserID As String = "", Optional ByVal IsChannel As Boolean = False) As Boolean
If Not _URL.IsEmptyString AndAlso _URL.Contains("imgur") Then If Not _URL.IsEmptyString AndAlso _URL.Contains("imgur") Then
@@ -449,8 +436,33 @@ Namespace API.Reddit
ElseIf _URL.Contains(".gif") Then ElseIf _URL.Contains(".gif") Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.GIF, _URL, PostID, PostDate, _UserID, IsChannel), LNC) _TempMediaList.ListAddValue(MediaFromData(UTypes.GIF, _URL, PostID, PostDate, _UserID, IsChannel), LNC)
Else Else
If Not TryFile(_URL) Then _URL &= ".jpg" Dim obj As IEnumerable(Of UserMedia) = Imgur.Envir.GetVideoInfo(_URL, EDP.ReturnValue)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, _URL, PostID, PostDate, _UserID, IsChannel), LNC) If Not obj.ListExists Then
If Not TryFile(_URL) Then _URL &= ".jpg"
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, _URL, PostID, PostDate, _UserID, IsChannel), LNC)
Else
Dim ut As UTypes
Dim m As UserMedia
For Each data As UserMedia In obj
With data
If Not .URL.IsEmptyString Then
If Not .File.IsEmptyString Then
Select Case .File.Extension
Case "jpg", "png", "jpeg" : ut = UTypes.Picture
Case "gifv" : ut = IIf(SaveToCache, UTypes.Picture, UTypes.Video)
Case "mp4" : ut = UTypes.Video
Case "gif" : ut = UTypes.GIF
Case Else : ut = UTypes.Picture : .File.Extension = "jpg"
End Select
m = MediaFromData(ut, _URL, PostID, PostDate, _UserID, IsChannel)
m.URL = .URL
m.File = .File.File
_TempMediaList.ListAddValue(m, LNC)
End If
End If
End With
Next
End If
End If End If
Return True Return True
Else Else
@@ -512,20 +524,37 @@ Namespace API.Reddit
End Try End Try
End Function End Function
Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken) Protected Overrides Sub ReparseVideo(ByVal Token As CancellationToken)
Dim RedGifsResponser As Response = Nothing
Try Try
ThrowAny(Token) ThrowAny(Token)
Const v2 As UTypes = UTypes.VideoPre + UTypes.m3u8 Const v2 As UTypes = UTypes.VideoPre + UTypes.m3u8
If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(p) p.Type = UTypes.VideoPre Or p.Type = v2) Then If _TempMediaList.Count > 0 AndAlso _TempMediaList.Exists(Function(p) p.Type = UTypes.VideoPre Or p.Type = v2) Then
Dim r$, v$ Dim r$, v$
Dim e As New ErrorsDescriber(EDP.ReturnValue) Dim e As New ErrorsDescriber(EDP.ReturnValue)
Dim m As UserMedia Dim m As UserMedia, m2 As UserMedia
Dim RedGifsHost As SettingsHost = Settings(RedGifs.RedGifsSiteKey)
RedGifsResponser = RedGifsHost.Responser.Copy
For i% = _TempMediaList.Count - 1 To 0 Step -1 For i% = _TempMediaList.Count - 1 To 0 Step -1
ThrowAny(Token) ThrowAny(Token)
If _TempMediaList(i).Type = UTypes.VideoPre Or _TempMediaList(i).Type = v2 Then If _TempMediaList(i).Type = UTypes.VideoPre Or _TempMediaList(i).Type = v2 Then
m = _TempMediaList(i) m = _TempMediaList(i)
If _TempMediaList(i).Type = UTypes.VideoPre Then If _TempMediaList(i).Type = UTypes.VideoPre Then
If m.URL.Contains("gfycat.com") Then If m.URL.Contains($"{SiteGfycatKey}.com") Then
r = Gfycat.Envir.GetVideo(m.URL) r = Gfycat.Envir.GetVideo(m.URL)
ElseIf m.URL.Contains(SiteRedGifsKey) Then
m2 = RedGifs.UserData.GetDataFromUrlId(m.URL, False, RedGifsResponser, RedGifsHost)
If m2.State = UStates.Missing Then
m.State = UStates.Missing
_ContentList.Add(m)
_TempMediaList.RemoveAt(i)
ElseIf m2.State = RedGifs.UserData.DataGone Then
_TempMediaList.RemoveAt(i)
Else
m2.URL_BASE = m.URL
m2.Post = m.Post
_TempMediaList(i) = m2
End If
Continue For
Else Else
r = Responser.GetResponse(m.URL,, e) r = Responser.GetResponse(m.URL,, e)
End If End If
@@ -546,60 +575,34 @@ Namespace API.Reddit
End If End If
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, "video reparsing error", False) ProcessException(ex, Token, "video reparsing error", False)
Finally
If Not RedGifsResponser Is Nothing Then RedGifsResponser.Dispose()
End Try End Try
End Sub End Sub
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken) Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
Dim rList As New List(Of Integer) Dim rList As New List(Of Integer)
Dim RedGifsResponser As Response = Nothing
Try Try
If _ContentList.Exists(MissingFinder) Then If Not ChannelInfo Is Nothing Or SaveToCache Then Exit Sub
Dim m As UserMedia If ContentMissingExists Then
Dim j As EContainer, ss As EContainer Dim RedGifsHost As SettingsHost = Settings(RedGifs.RedGifsSiteKey)
Dim r$, tmpUrl$, PostDate$, _UserID$ RedGifsResponser = RedGifsHost.Responser.Copy
Dim err As New ErrorsDescriber(EDP.ReturnValue) Dim m As UserMedia, m2 As UserMedia
Dim node As Object() = {"data", "children", 0, "data"}
Dim eCount As Predicate(Of EContainer) = Function(e) e.Count > 0
Dim cItems As Predicate(Of EContainer) = Function(e) If(e.ItemF(node)?.Count, 0) > 0
For i% = 0 To _ContentList.Count - 1 For i% = 0 To _ContentList.Count - 1
m = _ContentList(i) m = _ContentList(i)
If m.State = UStates.Missing AndAlso Not m.Post.ID.IsEmptyString Then If m.State = UStates.Missing AndAlso Not m.Post.ID.IsEmptyString Then
ThrowAny(Token) ThrowAny(Token)
r = Responser.GetResponse($"https://www.reddit.com/comments/{m.Post.ID.Split("_").LastOrDefault}/.json",, err) If Not m.URL.IsEmptyString AndAlso m.URL.Contains(SiteRedGifsKey) Then
If Not r.IsEmptyString Then m2 = RedGifs.UserData.GetDataFromUrlId(m.URL, False, RedGifsResponser, RedGifsHost)
j = JsonDocument.Parse(r, err) If m2.State = RedGifs.UserData.DataGone Then
If Not j Is Nothing Then rList.Add(i)
If j.Contains(cItems) Then ElseIf Not m2.Type = UTypes.Undefined And Not m2.State = UStates.Missing Then
With j.ItemF({cItems}).ItemF(node) m.Type = m2.Type
If .Contains("created") Then PostDate = .Item("created").Value Else PostDate = String.Empty m.File = m2.File
_UserID = .Value("author") m.URL_BASE = m.URL
tmpUrl = .Value("url") m.URL = m2.URL
If Not tmpUrl.IsEmptyString AndAlso tmpUrl.StringContains({"redgifs.com", "gfycat.com"}) Then rList.Add(i)
_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre, tmpUrl, m.Post.ID, PostDate, _UserID, IsChannel), LNC) _TempMediaList.ListAddValue(m, LNC)
_TotalPostsDownloaded += 1
ElseIf Not .Value({"media", "reddit_video"}, "fallback_url").IsEmptyString Then
tmpUrl = .Value({"media", "reddit_video"}, "fallback_url")
If UseM3U8 AndAlso Not .Value({"media", "reddit_video"}, "hls_url").IsEmptyString Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, .Value({"media", "reddit_video"}, "hls_url"),
m.Post.ID, PostDate, _UserID, IsChannel), LNC)
Else
'_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre + UTypes.m3u8, tmpUrl, PostID, PostDate, _UserID, IsChannel), LNC)
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, tmpUrl, m.Post.ID, PostDate, _UserID, IsChannel), LNC)
_TotalPostsDownloaded += 1
End If
ElseIf CreateImgurMedia(tmpUrl, m.Post.ID, PostDate, _UserID, IsChannel) Then
_TotalPostsDownloaded += 1
ElseIf If(.Item("media_metadata")?.Count, 0) > 0 Then
DownloadGallery(.Self, m.Post.ID, PostDate, _UserID, SaveToCache)
_TotalPostsDownloaded += 1
ElseIf .Contains("preview") Then
ss = .ItemF({"preview", "images", eCount, "source", "url"}).XmlIfNothing
If Not ss.Value.IsEmptyString Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Picture, ss.Value, m.Post.ID, PostDate, _UserID, IsChannel), LNC)
_TotalPostsDownloaded += 1
End If
End If
End With
End If
j.Dispose()
End If End If
End If End If
End If End If
@@ -608,26 +611,77 @@ Namespace API.Reddit
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, "missing data downloading error") ProcessException(ex, Token, "missing data downloading error")
Finally Finally
If Not RedGifsResponser Is Nothing Then RedGifsResponser.Dispose()
If rList.Count > 0 Then If rList.Count > 0 Then
For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next
rList.Clear() rList.Clear()
End If End If
End Try End Try
End Sub End Sub
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal resp As Response) As IEnumerable(Of UserMedia) Private Sub ParsePost(ByVal URL As String)
Try Try
If Not URL.IsEmptyString AndAlso URL.Contains("redgifs") Then If Not URL.IsEmptyString Then
Dim __id$ = RegexReplace(URL, RParams.DMS("comments/([^/]+)", 1, EDP.ReturnValue))
If Not __id.IsEmptyString Then
URL = $"https://www.reddit.com/comments/{__id.Split("_").LastOrDefault}/.json"
Dim r$ = Responser.GetResponse(URL,, EDP.ReturnValue)
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r).XmlIfNothing
With j.ItemF({0, "data", "children", 0, "data"})
If .ListExists Then
If .Contains({"media"}, "reddit_video") Then
With .Item({"media"}, "reddit_video")
If UseM3U8 AndAlso .Item("hls_url").XmlIfNothingValue("/").ToLower.Contains("m3u8") Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.m3u8, .Value("hls_url"), __id, String.Empty), LNC)
ElseIf Not UseM3U8 AndAlso .Item("fallback_url").XmlIfNothingValue("/").ToLower.Contains("mp4") Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.Video, .Value("fallback_url"), __id, String.Empty), LNC)
End If
End With
ElseIf Not .Value("url").IsEmptyString Then
If .Value("url").StringContains({$"{SiteRedGifsKey}.com", $"{SiteGfycatKey}.com"}) Then
_TempMediaList.ListAddValue(MediaFromData(UTypes.VideoPre, .Value("url"), __id, String.Empty), LNC)
Else
CreateImgurMedia(.Value("url"), __id, String.Empty)
End If
End If
End If
End With
End Using
End If
End If
End If
Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"API.Reddit.ParsePost({URL})")
End Try
End Sub
Private Class AbsProgress : Inherits PersonalUtilities.Forms.Toolbars.MyProgress
Public Overrides Sub Perform(Optional ByVal Value As Double = 1)
End Sub
End Class
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal resp As Response, ByVal f As SFile, ByVal SpecialFolder As String) As IEnumerable(Of UserMedia)
Try
If Not URL.IsEmptyString Then
Using r As New UserData Using r As New UserData
r._TempMediaList.Add(MediaFromData(UTypes.VideoPre, URL, String.Empty, String.Empty,, False))
r.Responser = New Response r.Responser = New Response
r.Responser.Copy(resp) r.Responser.Copy(resp)
r.ReparseVideo(Nothing) r.ParsePost(URL)
If r._TempMediaList.ListExists Then Return {r._TempMediaList(0)} If r._TempMediaList.Count > 0 Then
r.ReparseVideo(Nothing)
If r._TempMediaList.Count > 0 Then
r._ContentNew.AddRange(r._TempMediaList)
r.Progress = New AbsProgress
r.User.File.Path = f.Path
r.SeparateVideoFolder = False
r.DownloadContent(Nothing)
If r._ContentNew.Exists(Function(c) c.State = UStates.Downloaded) Then _
Return {New UserMedia With {.State = UStates.Downloaded, .SpecialFolder = SpecialFolder}}
End If
End If
End Using End Using
End If End If
Return Nothing Return Nothing
Catch ex As Exception Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.ShowMainMsg + EDP.SendInLog, ex, "Video searching error") Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.Reddit.UserData.GetVideoInfo({URL})]")
End Try End Try
End Function End Function
#End Region #End Region
@@ -659,6 +713,7 @@ Namespace API.Reddit
End Function End Function
#End Region #End Region
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
Dim RedGifsResponser As Response = Nothing
Try Try
Const _RFN$ = "RedditVideo" Const _RFN$ = "RedditVideo"
Const RFN$ = _RFN & "{0}" Const RFN$ = _RFN & "{0}"
@@ -668,6 +723,7 @@ Namespace API.Reddit
If _ContentNew.Count > 0 Then If _ContentNew.Count > 0 Then
_ContentNew.RemoveAll(Function(c) c.URL.IsEmptyString) _ContentNew.RemoveAll(Function(c) c.URL.IsEmptyString)
If _ContentNew.Count > 0 Then If _ContentNew.Count > 0 Then
RedGifsResponser = Settings(RedGifs.RedGifsSiteKey).Responser.Copy
MyFile.Exists(SFO.Path) MyFile.Exists(SFO.Path)
Dim MissingErrorsAdd As Boolean = Settings.AddMissingErrorsToLog Dim MissingErrorsAdd As Boolean = Settings.AddMissingErrorsToLog
Dim IsImgurStuff As Boolean Dim IsImgurStuff As Boolean
@@ -689,6 +745,7 @@ Namespace API.Reddit
Dim vsf As Boolean = SeparateVideoFolderF Dim vsf As Boolean = SeparateVideoFolderF
Dim UseMD5 As Boolean = Not IsChannel Or (Not cached And Settings.ChannelsRegularCheckMD5) Dim UseMD5 As Boolean = Not IsChannel Or (Not cached And Settings.ChannelsRegularCheckMD5)
Dim bDP As New ErrorsDescriber(EDP.None) Dim bDP As New ErrorsDescriber(EDP.None)
Dim RGRERROR As New ErrorsDescriber(EDP.ThrowException)
Dim ImgurUrls As New List(Of String) Dim ImgurUrls As New List(Of String)
Dim TryBytes As Func(Of String, Imaging.ImageFormat, String) = Dim TryBytes As Func(Of String, Imaging.ImageFormat, String) =
Function(ByVal __URL As String, ByVal ImgFormat As Imaging.ImageFormat) As String Function(ByVal __URL As String, ByVal ImgFormat As Imaging.ImageFormat) As String
@@ -738,7 +795,7 @@ Namespace API.Reddit
End Function End Function
Dim m$ Dim m$
Using w As New WebClient Using w As New WebClient
If vsf Then SFileShares.SFileExists($"{MyDir}\Video\", SFO.Path) If vsf Then CSFileP($"{MyDir}\Video\").Exists(SFO.Path)
Progress.Maximum += _ContentNew.Count Progress.Maximum += _ContentNew.Count
For i = 0 To _ContentNew.Count - 1 For i = 0 To _ContentNew.Count - 1
ThrowAny(Token) ThrowAny(Token)
@@ -761,7 +818,7 @@ Namespace API.Reddit
If (Not m.IsEmptyString AndAlso Not HashList.Contains(m)) Or Not (v.Type = UTypes.Picture Or If (Not m.IsEmptyString AndAlso Not HashList.Contains(m)) Or Not (v.Type = UTypes.Picture Or
v.Type = UTypes.GIF) Or Not UseMD5 Or ImgurUrls.Count > 0 Then v.Type = UTypes.GIF) Or Not UseMD5 Or ImgurUrls.Count > 0 Then
isImgurStuff = ImgurUrls.Count > 0 IsImgurStuff = ImgurUrls.Count > 0
Do Do
If Not cached And Not m.IsEmptyString Then HashList.Add(m) If Not cached And Not m.IsEmptyString Then HashList.Add(m)
v.MD5 = m v.MD5 = m
@@ -788,12 +845,14 @@ Namespace API.Reddit
f = M3U8.Download(v.URL, f) f = M3U8.Download(v.URL, f)
ElseIf ImgurUrls.Count > 0 Then ElseIf ImgurUrls.Count > 0 Then
w.DownloadFile(ImgurUrls(0), f.ToString) w.DownloadFile(ImgurUrls(0), f.ToString)
ElseIf v.URL.Contains(SiteRedGifsKey) Then
RedGifsResponser.DownloadFile(v.URL, f, RGRERROR)
Else Else
w.DownloadFile(v.URL, f.ToString) w.DownloadFile(v.URL, f.ToString)
End If End If
If Not v.Type = UTypes.m3u8 Or Not f.IsEmptyString Then If Not v.Type = UTypes.m3u8 Or Not f.IsEmptyString Then
Select Case v.Type Select Case v.Type
Case UTypes.Picture : DownloadedPictures(False) += 1 Case UTypes.Picture, UTypes.GIF : DownloadedPictures(False) += 1
Case UTypes.Video, UTypes.m3u8 : DownloadedVideos(False) += 1 Case UTypes.Video, UTypes.m3u8 : DownloadedVideos(False) += 1
End Select End Select
If Not IsChannel Or Not SaveToCache Then If Not IsChannel Or Not SaveToCache Then
@@ -838,20 +897,22 @@ Namespace API.Reddit
HasError = True HasError = True
End Try End Try
End Sub End Sub
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False) As Integer Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
If Responser.StatusCode = HttpStatusCode.NotFound Then Optional ByVal EObj As Object = Nothing) As Integer
UserExists = False With Responser
ElseIf Responser.StatusCode = HttpStatusCode.Forbidden Then If .StatusCode = HttpStatusCode.NotFound Then
UserSuspended = True UserExists = False
ElseIf Responser.StatusCode = HttpStatusCode.BadGateway Or ElseIf .StatusCode = HttpStatusCode.Forbidden Then
Responser.StatusCode = HttpStatusCode.ServiceUnavailable Then UserSuspended = True
MyMainLOG = $"[{CInt(Responser.StatusCode)}] Reddit is currently unavailable ({ToString()})" ElseIf .StatusCode = HttpStatusCode.BadGateway Or .StatusCode = HttpStatusCode.ServiceUnavailable Then
ElseIf Responser.StatusCode = HttpStatusCode.GatewayTimeout Then MyMainLOG = $"[{CInt(Responser.StatusCode)}] Reddit is currently unavailable ({ToString()})"
Return 1 ElseIf .StatusCode = HttpStatusCode.GatewayTimeout Then
Else Return 1
If Not FromPE Then LogError(ex, Message) : HasError = True Else
Return 0 If Not FromPE Then LogError(ex, Message) : HasError = True
End If Return 0
End If
End With
Return 1 Return 1
End Function End Function
Protected Overrides Sub Dispose(ByVal disposing As Boolean) Protected Overrides Sub Dispose(ByVal disposing As Boolean)

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,9 +6,14 @@
' '
' 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.Functions.RegularExpressions
Namespace API.RedGifs Namespace API.RedGifs
Friend Module Declarations Friend Module Declarations
Friend Const RedGifsSiteKey As String = "AndyProgram_RedGifs"
Friend Const RedGifsSite As String = "RedGifs" Friend Const RedGifsSite As String = "RedGifs"
Friend ReadOnly DateProvider As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicode(v, n, e)) Friend ReadOnly DateProvider As New CustomProvider(Function(v, d, p, n, e) ADateTime.ParseUnicode(v, n, e))
Friend ReadOnly WatchIDRegex As RParams = RParams.DMS(".+?watch/([^\?&""/]+)", 1, EDP.ReturnValue)
Friend ReadOnly ThumbsIDRegex As RParams = RParams.DMS("([^/\?&""]+?)(-\w+?|)\.(mp4|jpg)", 1, EDP.ReturnValue,
CType(Function(Input$) Input.StringToLower.StringTrim, Func(Of String, String)))
End Module End Module
End Namespace End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,41 +6,155 @@
' '
' 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 SCrawler.API.Base
Imports SCrawler.Plugin Imports SCrawler.Plugin
Imports SCrawler.Plugin.Attributes Imports SCrawler.Plugin.Attributes
Imports SCrawler.API.Base Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Cookies
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports UTypes = SCrawler.API.Base.UserMedia.Types
Imports UStates = SCrawler.API.Base.UserMedia.States
Namespace API.RedGifs Namespace API.RedGifs
<Manifest("AndyProgram_RedGifs"), UseClassAsIs> <Manifest(RedGifsSiteKey)>
Friend Class SiteSettings : Inherits SiteSettingsBase Friend Class SiteSettings : Inherits SiteSettingsBase
#Region "Declarations"
Friend Overrides ReadOnly Property Icon As Icon Friend Overrides ReadOnly Property Icon As Icon
Get Get
Return My.Resources.RedGifsIcon Return My.Resources.SiteResources.RedGifsIcon_32
End Get End Get
End Property End Property
Friend Overrides ReadOnly Property Image As Image Friend Overrides ReadOnly Property Image As Image
Get Get
Return My.Resources.RedGifsPic32 Return My.Resources.SiteResources.RedGifsPic_32
End Get End Get
End Property End Property
<PropertyOption(AllowNull:=False, ControlText:="Token", ControlToolTip:="Bearer token")>
Friend Property Token As PropertyValue
<PXML> Friend Property TokenLastDateUpdated As PropertyValue
<DoNotUse> Friend ReadOnly Property NoCredentialsResponser As Response
Private Const TokenName As String = "authorization"
#End Region
#Region "Initializer"
Friend Sub New() Friend Sub New()
MyBase.New(RedGifsSite, "redgifs.com") MyBase.New(RedGifsSite, "redgifs.com")
Dim t$ = String.Empty
With Responser
Dim b As Boolean = Not .Mode = Response.Modes.WebClient
.Mode = Response.Modes.WebClient
t = .HeadersValue(TokenName)
If b Then .SaveSettings()
End With
NoCredentialsResponser = New Response($"{SettingsFolderName}\Responser_{RedGifsSite}_NC.xml") With {
.CookiesEncryptKey = SettingsCLS.CookieEncryptKey,
.CookiesDomain = "redgifs.com"
}
With NoCredentialsResponser
If .File.Exists Then
.LoadSettings()
Else
.Cookies = New CookieKeeper(.CookiesDomain) With {.EncryptKey = SettingsCLS.CookieEncryptKey}
.SaveSettings()
End If
End With
Token = New PropertyValue(t, GetType(String), Sub(v) UpdateResponse(v))
TokenLastDateUpdated = New PropertyValue(Now.AddYears(-1), GetType(Date))
UrlPatternUser = "https://www.redgifs.com/users/{0}/" UrlPatternUser = "https://www.redgifs.com/users/{0}/"
UserRegex = RParams.DMS("[htps:/]{7,8}.*?redgifs.com/users/([^/]+)", 1) UserRegex = RParams.DMS("[htps:/]{7,8}.*?redgifs.com/users/([^/]+)", 1)
ImageVideoContains = "redgifs" ImageVideoContains = "redgifs"
End Sub End Sub
#End Region
#Region "Response updater"
Private Sub UpdateResponse(ByVal Value As String)
Responser.HeadersAdd(TokenName, Value)
Responser.SaveSettings()
End Sub
#End Region
#Region "Token updaters"
Friend Function UpdateTokenIfRequired() As Boolean
Dim d As Date? = AConvert(Of Date)(TokenLastDateUpdated.Value, AModes.Var, Nothing)
If Not d.HasValue OrElse d.Value < Now.AddDays(-1) Then
Return UpdateToken()
Else
Return True
End If
End Function
<PropertyUpdater(NameOf(Token))>
Friend Function UpdateToken() As Boolean
Try
Dim r$
Dim NewToken$ = String.Empty
Using resp As New Response : r = resp.GetResponse("https://api.redgifs.com/v2/auth/temporary",, EDP.ThrowException) : End Using
If Not r.IsEmptyString Then
Dim j As EContainer = JsonDocument.Parse(r)
If Not j Is Nothing Then
NewToken = j.Value("token")
j.Dispose()
End If
End If
If Not NewToken.IsEmptyString Then
Token.Value = $"Bearer {NewToken}"
TokenLastDateUpdated.Value = Now
Return True
Else
Return False
End If
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.RedGifs.SiteSettings.UpdateToken]", False)
End Try
End Function
#End Region
#Region "Update settings"
Private _LastTokenValue As String = String.Empty
Friend Overrides Sub BeginEdit()
_LastTokenValue = AConvert(Of String)(Token.Value, AModes.Var, String.Empty)
MyBase.BeginEdit()
End Sub
Friend Overrides Sub Update()
Dim NewToken$ = AConvert(Of String)(Token.Value, AModes.Var, String.Empty)
If Not _LastTokenValue = NewToken Then TokenLastDateUpdated.Value = Now
MyBase.Update()
End Sub
Friend Overrides Sub EndEdit()
_LastTokenValue = String.Empty
MyBase.EndEdit()
End Sub
#End Region
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
Return New UserData Return New UserData
End Function End Function
Friend Overrides Function GetSpecialDataF(ByVal URL As String) As IEnumerable(Of UserMedia) Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
Return Reddit.UserData.GetVideoInfo(URL, Nothing) If BaseAuthExists() Then
Using resp As Response = Responser.Copy
Dim m As UserMedia = UserData.GetDataFromUrlId(URL, False, resp, Settings(RedGifsSiteKey))
If Not m.State = UStates.Missing And Not m.State = UserData.DataGone And (m.Type = UTypes.Picture Or m.Type = UTypes.Video) Then
Try
Dim spf$ = String.Empty
Dim f As SFile = GetSpecialDataFile(Path, AskForPath, spf)
If f.IsEmptyString Then
f = m.File.File
Else
f.Name = m.File.Name
f.Extension = m.File.Extension
End If
resp.DownloadFile(m.URL, f, EDP.ThrowException)
m.State = UStates.Downloaded
m.SpecialFolder = spf
Return {m}
Catch ex As Exception
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"Redgifs standalone download error: [{URL}]")
End Try
End If
End Using
End If
Return Nothing
End Function End Function
Friend Overrides Function GetUserPostUrl(ByVal UserID As String, ByVal PostID As String) As String Friend Overrides Function GetUserPostUrl(ByVal User As UserDataBase, ByVal Media As UserMedia) As String
Return $"https://www.redgifs.com/watch/{PostID}" Return $"https://www.redgifs.com/watch/{Media.Post.ID}"
End Function End Function
Friend Overrides Function Available(ByVal What As ISiteSettings.Download, ByVal Silent As Boolean) As Boolean Friend Overrides Function BaseAuthExists() As Boolean
'PENDING: RedGifs SiteSettings Available FALSE Return UpdateTokenIfRequired() AndAlso ACheck(Token.Value)
Return False
End Function End Function
End Class End Class
End Namespace End Namespace

View File

@@ -1,4 +1,4 @@
' Copyright (C) 2022 Andy ' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify ' 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 ' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or ' the Free Software Foundation, either version 3 of the License, or
@@ -6,29 +6,49 @@
' '
' 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.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.WebDocuments.JSON
Imports System.Net Imports System.Net
Imports System.Threading Imports System.Threading
Imports SCrawler.API.Base Imports SCrawler.API.Base
Imports PersonalUtilities.Functions.XML
Imports PersonalUtilities.Functions.RegularExpressions
Imports PersonalUtilities.Tools.Web.Clients
Imports PersonalUtilities.Tools.Web.Documents.JSON
Imports UTypes = SCrawler.API.Base.UserMedia.Types Imports UTypes = SCrawler.API.Base.UserMedia.Types
Imports UStates = SCrawler.API.Base.UserMedia.States Imports UStates = SCrawler.API.Base.UserMedia.States
Namespace API.RedGifs Namespace API.RedGifs
Friend Class UserData : Inherits UserDataBase Friend Class UserData : Inherits UserDataBase
Friend Sub New() Friend Const DataGone As HttpStatusCode = HttpStatusCode.Gone
End Sub Private Const PostDataUrl As String = "https://api.redgifs.com/v2/gifs/{0}?views=yes&users=yes"
#Region "Base declarations"
Private ReadOnly Property MySettings As SiteSettings
Get
Return DirectCast(HOST.Source, SiteSettings)
End Get
End Property
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean) Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
End Sub End Sub
#End Region
#Region "Initializer"
Friend Sub New()
UseResponserClient = True
End Sub
#End Region
#Region "Download functions"
Private NoCredentialsResponser As Response
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken) Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
ReparseMissing(Token) Try
DownloadData(1, Token) NoCredentialsResponser = MySettings.NoCredentialsResponser.Copy
DownloadData(1, Token)
Finally
NoCredentialsResponser.Dispose()
End Try
End Sub End Sub
Private Overloads Sub DownloadData(ByVal Page As Integer, ByVal Token As CancellationToken) Private Overloads Sub DownloadData(ByVal Page As Integer, ByVal Token As CancellationToken)
Dim URL$ = String.Empty Dim URL$ = String.Empty
Try Try
URL = $"https://api.redgifs.com/v2/users/{Name}/search?order=recent&page={Page}" Dim _page As Func(Of String) = Function() If(Page = 1, String.Empty, $"&page={Page}")
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException) URL = $"https://api.redgifs.com/v2/users/{Name}/search?order=recent{_page.Invoke}"
Dim r$ = NoCredentialsResponser.GetResponse(URL,, EDP.ThrowException)
Dim postDate$, postID$ Dim postDate$, postID$
Dim pTotal% = 0 Dim pTotal% = 0
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
@@ -42,7 +62,7 @@ Namespace API.RedGifs
Case DateResult.Exit : Exit Sub Case DateResult.Exit : Exit Sub
End Select End Select
postID = g.Value("id") postID = g.Value("id")
If Not _TempPostsList.Contains(postID) Then _TempPostsList.Add(postID) Else Exit For If Not _TempPostsList.Contains(postID) Then _TempPostsList.Add(postID) Else Exit Sub
ObtainMedia(g, postID, postDate) ObtainMedia(g, postID, postDate)
Next Next
End If End If
@@ -50,28 +70,40 @@ Namespace API.RedGifs
End If End If
If pTotal > 0 And Page < pTotal Then DownloadData(Page + 1, Token) If pTotal > 0 And Page < pTotal Then DownloadData(Page + 1, Token)
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, $"data downloading error [{URL}]") ProcessException(ex, Token, $"data downloading error [{URL}]",, True)
End Try End Try
End Sub End Sub
#End Region
#Region "Media obtain, extract"
Private Sub ObtainMedia(ByVal j As EContainer, ByVal PostID As String, Private Sub ObtainMedia(ByVal j As EContainer, ByVal PostID As String,
Optional ByVal PostDateStr As String = Nothing, Optional ByVal PostDateDate As Date? = Nothing, Optional ByVal PostDateStr As String = Nothing, Optional ByVal PostDateDate As Date? = Nothing,
Optional ByVal State As UStates = UStates.Unknown) Optional ByVal State As UStates = UStates.Unknown)
With j("urls") Dim tMedia As UserMedia = ExtractMedia(j)
If .ListExists Then If Not tMedia.Type = UTypes.Undefined Then _
Dim u$ = If(.Item("hd"), .Item("sd")).XmlIfNothingValue _TempMediaList.ListAddValue(MediaFromData(tMedia.Type, tMedia.URL, PostID, PostDateStr, PostDateDate, State))
If Not u.IsEmptyString Then
Dim ut As UTypes = UTypes.Undefined
'Type 1: video
'Type 2: image
Select Case j.Value("type").FromXML(Of Integer)(0)
Case 1 : ut = UTypes.Video
Case 2 : ut = UTypes.Picture
End Select
If Not ut = UTypes.Undefined Then _TempMediaList.ListAddValue(MediaFromData(ut, u, PostID, PostDateStr, PostDateDate, State))
End If
End If
End With
End Sub End Sub
Private Shared Function ExtractMedia(ByVal j As EContainer) As UserMedia
If Not j Is Nothing Then
With j("urls")
If .ListExists Then
Dim u$ = If(.Item("hd"), .Item("sd")).XmlIfNothingValue
If Not u.IsEmptyString Then
Dim ut As UTypes = UTypes.Undefined
'Type 1: video
'Type 2: image
Select Case j.Value("type").FromXML(Of Integer)(0)
Case 1 : ut = UTypes.Video
Case 2 : ut = UTypes.Picture
End Select
Return New UserMedia(u, ut)
End If
End If
End With
End If
Return Nothing
End Function
#End Region
#Region "ReparseMissing"
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken) Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
Dim rList As New List(Of Integer) Dim rList As New List(Of Integer)
Try Try
@@ -84,7 +116,7 @@ Namespace API.RedGifs
ThrowAny(Token) ThrowAny(Token)
u = _ContentList(i) u = _ContentList(i)
If Not u.Post.ID.IsEmptyString Then If Not u.Post.ID.IsEmptyString Then
url = $"https://api.redgifs.com/v2/gifs/{u.Post.ID}?views=yes&users=yes" url = String.Format(PostDataUrl, u.Post.ID.ToLower)
Try Try
r = Responser.GetResponse(url,, EDP.ThrowException) r = Responser.GetResponse(url,, EDP.ThrowException)
If Not r.IsEmptyString Then If Not r.IsEmptyString Then
@@ -108,21 +140,91 @@ Namespace API.RedGifs
End If End If
Catch dex As ObjectDisposedException When Disposed Catch dex As ObjectDisposedException When Disposed
Catch ex As Exception Catch ex As Exception
ProcessException(ex, Token, $"missing data downloading error") ProcessException(ex, Token, $"missing data downloading error",, False)
Finally Finally
If Not Disposed And rList.Count > 0 Then If Not Disposed And rList.Count > 0 Then
For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(rList(i)) : Next
End If End If
End Try End Try
End Sub End Sub
#End Region
#Region "Downloader"
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken) Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
DownloadContentDefault(Token) DownloadContentDefault(Token)
End Sub End Sub
#End Region
#Region "Get post data statics"
''' <summary>
''' https://thumbs4.redgifs.com/abcde-large.jpg?expires -> abcde<br/>
''' https://thumbs4.redgifs.com/abcde.mp4?expires -> abcde<br/>
''' https://www.redgifs.com/watch/abcde?rel=a -> abcde
''' </summary>
Friend Shared Function GetVideoIdFromUrl(ByVal URL As String) As String
If Not URL.IsEmptyString Then
Return RegexReplace(URL, If(URL.Contains("/watch/"), WatchIDRegex, ThumbsIDRegex))
Else
Return String.Empty
End If
End Function
Friend Shared Function GetDataFromUrlId(ByVal Obj As String, ByVal ObjIsID As Boolean, ByVal Responser As Response,
ByVal Host As Plugin.Hosts.SettingsHost) As UserMedia
Dim URL$ = String.Empty
Try
If Obj.IsEmptyString Then Return Nothing
If Not ObjIsID Then
Obj = GetVideoIdFromUrl(Obj)
If Not Obj.IsEmptyString Then Return GetDataFromUrlId(Obj, True, Responser, Host)
Else
If Host Is Nothing Then Host = Settings(RedGifsSiteKey)
If Host.Source.Available(Plugin.ISiteSettings.Download.Main, True) Then
If Responser Is Nothing Then Responser = Host.Responser.Copy
URL = String.Format(PostDataUrl, Obj.ToLower)
Dim r$ = Responser.GetResponse(URL,, EDP.ThrowException)
If Not r.IsEmptyString Then
Using j As EContainer = JsonDocument.Parse(r)
If Not j Is Nothing Then
Dim tm As UserMedia = ExtractMedia(j("gif"))
tm.Post.ID = Obj
tm.File = CStr(RegexReplace(tm.URL, FilesPattern))
If tm.File.IsEmptyString Then
tm.File.Name = Obj
Select Case tm.Type
Case UTypes.Picture : tm.File.Extension = "jpg"
Case UTypes.Video : tm.File.Extension = "mp4"
End Select
End If
Return tm
End If
End Using
End If
Else
Return New UserMedia With {.State = UStates.Missing}
End If
End If
Return Nothing
Catch ex As Exception
If Not Responser Is Nothing AndAlso (Responser.Client.StatusCode = DataGone Or Responser.Client.StatusCode = HttpStatusCode.NotFound) Then
Return New UserMedia With {.State = DataGone}
Else
Dim m As New UserMedia With {.State = UStates.Missing}
Dim _errText$ = "API.RedGifs.UserData.GetDataFromUrlId({0})"
If Responser.Client.StatusCode = HttpStatusCode.Unauthorized Then
_errText = $"RedGifs credentials have expired [{CInt(Responser.Client.StatusCode)}]: {_errText}"
MyMainLOG = String.Format(_errText, URL)
Return m
Else
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, String.Format(_errText, URL), m)
End If
End If
End Try
End Function
#End Region
#Region "Create media"
Private Shared Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String, Private Shared Function MediaFromData(ByVal t As UTypes, ByVal _URL As String, ByVal PostID As String,
ByVal PostDateStr As String, ByVal PostDateDate As Date?, ByVal State As UStates) As UserMedia ByVal PostDateStr As String, ByVal PostDateDate As Date?, ByVal State As UStates) As UserMedia
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern)) _URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID}} Dim m As New UserMedia(_URL, t) With {.Post = New UserPost With {.ID = PostID}}
If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern)) : m.URL_BASE = m.URL If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern))
If Not PostDateStr.IsEmptyString Then If Not PostDateStr.IsEmptyString Then
m.Post.Date = AConvert(Of Date)(PostDateStr, DateProvider, Nothing) m.Post.Date = AConvert(Of Date)(PostDateStr, DateProvider, Nothing)
ElseIf PostDateDate.HasValue Then ElseIf PostDateDate.HasValue Then
@@ -133,14 +235,32 @@ Namespace API.RedGifs
m.State = State m.State = State
Return m Return m
End Function End Function
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False) As Integer #End Region
If Responser.StatusCode = HttpStatusCode.NotFound Then #Region "Exception"
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
Optional ByVal EObj As Object = Nothing) As Integer
Dim IsNoCredentialsResponser As Boolean = AConvert(Of Boolean)(EObj, False)
Dim s As WebExceptionStatus = -1
Dim sc As HttpStatusCode = -1
If IsNoCredentialsResponser Then
If Not NoCredentialsResponser Is Nothing Then
s = NoCredentialsResponser.Status
sc = NoCredentialsResponser.StatusCode
End If
Else
s = Responser.Client.Status
sc = Responser.Client.StatusCode
End If
If sc = HttpStatusCode.NotFound Or s = DataGone Then
UserExists = False UserExists = False
ElseIf sc = HttpStatusCode.Unauthorized Then
MyMainLOG = $"RedGifs credentials have expired [{CInt(sc)}]: {ToStringForLog()}"
Else Else
If Not FromPE Then LogError(ex, Message) : HasError = True If Not FromPE Then LogError(ex, Message) : HasError = True
Return 0 Return 0
End If End If
Return 1 Return 1
End Function End Function
#End Region
End Class End Class
End Namespace End Namespace

View File

@@ -0,0 +1,65 @@
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
' This program is free software: you can redistribute it and/or modify
' it under the terms of the GNU General Public License as published by
' the Free Software Foundation, either version 3 of the License, or
' (at your option) any later version.
'
' This program is distributed in the hope that it will be useful,
' but WITHOUT ANY WARRANTY
Imports PersonalUtilities.Functions.RegularExpressions
Namespace API.TikTok
Friend Module Declarations
Friend ReadOnly RegexEnvir As New RegexParseEnvir
Friend ReadOnly CheckDateProvider As New CustomProvider(Function(v, d, p, n, e)
With DirectCast(v, Date?)
If .HasValue Then Return .Value Else Return Nothing
End With
End Function)
Friend Class RegexParseEnvir
Private ReadOnly UrlIdRegex As RParams = RParams.DMS("http[s]?://[w\.]{0,4}tiktok.com/[^/]+?/video/(\d+)", 1, EDP.ReturnValue)
Private ReadOnly RegexItemsArrPre As RParams = RParams.DMS("ItemList"":\{""user-post"":\{""list"":\[([^\[]+)\]", 1)
Private ReadOnly RegexItemsArr As RParams = RParams.DM("\d+", 0, RegexReturn.List)
Private ReadOnly VideoPattern As New RParams(String.Empty, Nothing, 1, EDP.ReturnValue)
Private ReadOnly DatePattern As New RParams(String.Empty, Nothing, 1, EDP.ReturnValue)
Private ReadOnly UserIdFromVideo As RParams = RParams.DMS("/\?a=(\d+)", 1, EDP.ReturnValue)
Friend Function GetIDList(ByVal r As String) As List(Of String)
Try
If Not r.IsEmptyString Then
Dim l As List(Of String) = Nothing
Dim IdArr$ = RegexReplace(r, RegexItemsArrPre)
If Not IdArr.IsEmptyString Then l = RegexReplace(IdArr, RegexItemsArr)
If l.ListExists Then l.RemoveAll(Function(id) id.IsEmptyString)
Return l
End If
Return Nothing
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.TikTok.RegexParseEnvir.GetIDList]")
End Try
End Function
Friend Function GetVideoData(ByVal r As String, ByVal ID As String, ByRef URL As String, ByRef [Date] As Date?) As Boolean
Try
[Date] = Nothing
URL = String.Empty
If Not r.IsEmptyString Then
VideoPattern.Pattern = "video"":\{""id"":""" & ID & """[^\}]+?""downloadAddr"":""([^""]+)"""
DatePattern.Pattern = """:{""id"":""" & ID & """,""desc"":.+?""createTime"":""(\d+)"
Dim u$ = RegexReplace(r, VideoPattern)
If Not u.IsEmptyString Then URL = SymbolsConverter.Unicode.Decode(u, EDP.ReturnValue)
Dim d$ = RegexReplace(r, DatePattern)
If Not d.IsEmptyString Then [Date] = ADateTime.ParseUnicode(d)
Return Not URL.IsEmptyString
End If
Return False
Catch ex As Exception
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, "[API.TikTok.RegexParseEnvir.GetVideoData]", False)
End Try
End Function
Friend Function ExtractPostID(ByVal URL As String) As String
If Not URL.IsEmptyString Then Return RegexReplace(URL, UrlIdRegex) Else Return String.Empty
End Function
Friend Function ExtractUserID(ByVal VideoUrl As String) As String
If Not VideoUrl.IsEmptyString Then Return RegexReplace(VideoUrl, UserIdFromVideo) Else Return String.Empty
End Function
End Class
End Module
End Namespace

Some files were not shown because too many files have changed in this diff Show More