Compare commits
6 Commits
2022.9.17.
...
2022.10.23
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d169acebc | ||
|
|
f5c156b8e5 | ||
|
|
d91ee72eaa | ||
|
|
4e9de23b60 | ||
|
|
129558c262 | ||
|
|
a3e79eb4bc |
1
.gitignore
vendored
@@ -35,6 +35,7 @@ bld/
|
|||||||
[Ll]ogs/
|
[Ll]ogs/
|
||||||
ffmpeg/
|
ffmpeg/
|
||||||
Info/
|
Info/
|
||||||
|
Hidden/
|
||||||
|
|
||||||
# Visual Studio 2015/2017 cache/options directory
|
# Visual Studio 2015/2017 cache/options directory
|
||||||
.vs/
|
.vs/
|
||||||
|
|||||||
@@ -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
|
|
||||||
|
|
||||||
[](https://matrix.to/#/@andyprogram:matrix.org)
|
|
||||||
|
|
||||||
[](https://discordapp.com/users/1012768226679206009) AndyProgram#3804
|
|
||||||
72
Changelog.md
@@ -1,3 +1,75 @@
|
|||||||
|
# 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-09-24*
|
||||||
|
|
||||||
|
- Added
|
||||||
|
- Ability to copy user data to another destination
|
||||||
|
- Ability to add 'Session' and 'Date' values to the post title in the feed
|
||||||
|
- Minor feed improvements
|
||||||
|
- The newly created collection will now appear at the top of the list (after reopening the form)
|
||||||
|
- Ability to add multiple users at a time to the collection.
|
||||||
|
- Fixed
|
||||||
|
- Autodownloader opens a compressed image instead of a full one
|
||||||
|
- Incorrect resizing of the feed grid after deleting a media file
|
||||||
|
- Incorrect behavior when deleting/removing a user from a collection.
|
||||||
|
- An incorrect function that displayed the number of spent Instagram requests.
|
||||||
|
- Bug in the XVIDEOS downloader
|
||||||
|
- Minor bugs
|
||||||
|
|
||||||
# 2022.9.17.0
|
# 2022.9.17.0
|
||||||
|
|
||||||
*2022-09-17*
|
*2022-09-17*
|
||||||
|
|||||||
22
FAQ.md
@@ -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.
|
||||||
@@ -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.**
|
||||||
|
Before Width: | Height: | Size: 574 KiB After Width: | Height: | Size: 483 KiB |
BIN
ProgramScreenshots/FeedWindowItemContext.png
Normal file
|
After Width: | Height: | Size: 9.7 KiB |
BIN
ProgramScreenshots/FeedWindowSessionContext.png
Normal file
|
After Width: | Height: | Size: 8.7 KiB |
|
Before Width: | Height: | Size: 7.4 KiB After Width: | Height: | Size: 9.2 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 370 KiB After Width: | Height: | Size: 363 KiB |
|
Before Width: | Height: | Size: 379 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 15 KiB |
BIN
ProgramScreenshots/MainWindowPause.png
Normal file
|
After Width: | Height: | Size: 16 KiB |
BIN
ProgramScreenshots/MainWindowView.png
Normal file
|
After Width: | Height: | Size: 8.5 KiB |
BIN
ProgramScreenshots/SearchUsers.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 16 KiB After Width: | Height: | Size: 18 KiB |
|
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 19 KiB |
|
Before Width: | Height: | Size: 13 KiB After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 12 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 10 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 16 KiB |
|
Before Width: | Height: | Size: 8.5 KiB After Width: | Height: | Size: 11 KiB |
BIN
ProgramScreenshots/SettingsGlobalNotifications.png
Normal file
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 7.6 KiB After Width: | Height: | Size: 12 KiB |
|
Before Width: | Height: | Size: 26 KiB After Width: | Height: | Size: 28 KiB |
BIN
ProgramScreenshots/SettingsSiteInstagramAdditional.png
Normal file
|
After Width: | Height: | Size: 5.8 KiB |
BIN
ProgramScreenshots/SettingsSiteLPSG.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
BIN
ProgramScreenshots/SettingsSiteRedGifs.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 14 KiB |
BIN
ProgramScreenshots/SettingsSiteTikTok.png
Normal file
|
After Width: | Height: | Size: 13 KiB |
|
Before Width: | Height: | Size: 15 KiB After Width: | Height: | Size: 16 KiB |
BIN
ProgramScreenshots/SettingsSiteXvideos.png
Normal file
|
After Width: | Height: | Size: 14 KiB |
BIN
ProgramScreenshots/SettingsSiteXvideosAdditional.png
Normal file
|
After Width: | Height: | Size: 6.9 KiB |
BIN
ProgramScreenshots/TrayContextMenu.png
Normal file
|
After Width: | Height: | Size: 3.6 KiB |
70
README.md
@@ -6,7 +6,9 @@
|
|||||||
[](https://github.com/AAndyProgram/SCrawler/wiki)
|
[](https://github.com/AAndyProgram/SCrawler/wiki)
|
||||||
[](HowToSupport.md)
|
[](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, XVIDEOS, LPSG).
|
||||||
|
|
||||||
|
**If you like SCrawler, please like the program on [this site]( https://alternativeto.net/software/scrawler/about/)**
|
||||||
|
|
||||||
Do you like this program? Consider adding to my coffee fund by making a donation to show your support. :blush:
|
Do you like this program? Consider adding to my coffee fund by making a donation to show your support. :blush:
|
||||||
|
|
||||||
@@ -19,14 +21,11 @@ Do you like this program? Consider adding to my coffee fund by making a donation
|
|||||||
|
|
||||||
# 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**));
|
||||||
- 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;
|
||||||
- Instagram images and videos;
|
- Instagram images and videos, tagged posts, stories;
|
||||||
- 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;
|
||||||
- [Other](#supported-sites) supported sites
|
- [Other](#supported-sites) supported sites
|
||||||
@@ -53,6 +52,7 @@ 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
|
||||||
@@ -60,7 +60,9 @@ Do you like this program? Consider adding to my coffee fund by making a donation
|
|||||||
- XVIDEOS
|
- 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.
|
||||||
|
|
||||||
@@ -72,8 +74,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
|
||||||
@@ -81,12 +81,33 @@ 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)
|
||||||
|
- [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
|
||||||
@@ -115,22 +136,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)
|
||||||
|
|
||||||
@@ -143,9 +151,3 @@ 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```
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
# Contact me
|
|
||||||
|
|
||||||
[](https://matrix.to/#/@andyprogram:matrix.org)
|
|
||||||
|
|
||||||
[](https://discordapp.com/users/1012768226679206009) AndyProgram#3804
|
|
||||||
|
|||||||
@@ -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")>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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")>
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ Public Class SettingsForm
|
|||||||
Settings.UpdateDomains()
|
Settings.UpdateDomains()
|
||||||
MyDefs.CloseForm()
|
MyDefs.CloseForm()
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub MyDefs_ButtonAddClick(ByVal Sender As Object, ByVal e As EditToolbar.EditToolbarEventArgs) Handles MyDefs.ButtonAddClick
|
Private Sub MyDefs_ButtonAddClick(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonAddClick
|
||||||
Dim nd$ = InputBoxE("Enter a new domain using the pattern [xvideos.com]:", "New domain")
|
Dim nd$ = InputBoxE("Enter a new domain using the pattern [xvideos.com]:", "New domain")
|
||||||
If Not nd.IsEmptyString Then
|
If Not nd.IsEmptyString Then
|
||||||
If Not LIST_DOMAINS.Items.Contains(nd) Then
|
If Not LIST_DOMAINS.Items.Contains(nd) Then
|
||||||
@@ -45,7 +45,7 @@ Public Class SettingsForm
|
|||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Private Sub MyDefs_ButtonDeleteClickE(ByVal Sender As Object, ByVal e As EditToolbar.EditToolbarEventArgs) Handles MyDefs.ButtonDeleteClickE
|
Private Sub MyDefs_ButtonDeleteClickE(ByVal Sender As Object, ByVal e As EditToolbarEventArgs) Handles MyDefs.ButtonDeleteClickE
|
||||||
If _LatestSelected.ValueBetween(0, LIST_DOMAINS.Items.Count - 1) Then
|
If _LatestSelected.ValueBetween(0, LIST_DOMAINS.Items.Count - 1) Then
|
||||||
Dim n$ = LIST_DOMAINS.Items(_LatestSelected)
|
Dim n$ = LIST_DOMAINS.Items(_LatestSelected)
|
||||||
If MsgBoxE({$"Are you sure you want to delete the [{n}] domain?", "Removing domains"}, MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
|
If MsgBoxE({$"Are you sure you want to delete the [{n}] domain?", "Removing domains"}, MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ Public Class UserData : Implements IPluginContentProvider
|
|||||||
|
|
||||||
Dim NextPage% = 0
|
Dim NextPage% = 0
|
||||||
Dim r$
|
Dim r$
|
||||||
Dim j As EContainer, jj As EContainer
|
Dim jj As EContainer
|
||||||
Dim e As ErrorsDescriber = EDP.ThrowException
|
Dim e As ErrorsDescriber = EDP.ThrowException
|
||||||
Dim user$ = Settings.GetUserUrl(Name, False)
|
Dim user$ = Settings.GetUserUrl(Name, False)
|
||||||
Dim p As PluginUserMedia
|
Dim p As PluginUserMedia
|
||||||
@@ -82,8 +82,7 @@ Public Class UserData : Implements IPluginContentProvider
|
|||||||
r = Responser.GetResponse($"https://www.xvideos.com/{user}/videos/new/{If(NextPage = 0, String.Empty, NextPage)}",, e)
|
r = Responser.GetResponse($"https://www.xvideos.com/{user}/videos/new/{If(NextPage = 0, String.Empty, NextPage)}",, e)
|
||||||
If Not r.IsEmptyString Then
|
If Not r.IsEmptyString Then
|
||||||
If Not EnvirSet Then UserExists = True : UserSuspended = False : EnvirSet = True
|
If Not EnvirSet Then UserExists = True : UserSuspended = False : EnvirSet = True
|
||||||
j = JsonDocument.Parse(r).XmlIfNothing
|
With JsonDocument.Parse(r).XmlIfNothing
|
||||||
With j
|
|
||||||
If .Contains("videos") Then
|
If .Contains("videos") Then
|
||||||
With .Item("videos")
|
With .Item("videos")
|
||||||
If .Count > 0 Then
|
If .Count > 0 Then
|
||||||
@@ -94,9 +93,12 @@ Public Class UserData : Implements IPluginContentProvider
|
|||||||
.URL = $"https://www.xvideos.com{jj.Value("u")}"
|
.URL = $"https://www.xvideos.com{jj.Value("u")}"
|
||||||
}
|
}
|
||||||
If Not p.PostID.IsEmptyString And Not jj.Value("u").IsEmptyString Then
|
If Not p.PostID.IsEmptyString And Not jj.Value("u").IsEmptyString Then
|
||||||
If Not TempPostsList.Contains(p.PostID) Then TempPostsList.Add(p.PostID) : TempMediaList.Add(p) Else Exit Do
|
If Not TempPostsList.Contains(p.PostID) Then TempPostsList.Add(p.PostID) : TempMediaList.Add(p) Else .Dispose() : Exit Do
|
||||||
End If
|
End If
|
||||||
Next
|
Next
|
||||||
|
Else
|
||||||
|
.Dispose()
|
||||||
|
Exit Do
|
||||||
End If
|
End If
|
||||||
End With
|
End With
|
||||||
Else
|
Else
|
||||||
|
|||||||
@@ -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/>
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -21,7 +21,7 @@ Namespace Plugin
|
|||||||
Function GetUserUrl(ByVal UserName As String, ByVal Channel As Boolean) As String
|
Function GetUserUrl(ByVal UserName As String, 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 UserID As String, ByVal PostID As String) As String
|
||||||
#Region "XML Support"
|
#Region "XML Support"
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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.10.23.0")>
|
||||||
<Assembly: AssemblyFileVersion("2022.9.17.0")>
|
<Assembly: AssemblyFileVersion("2022.10.23.0")>
|
||||||
<Assembly: NeutralResourcesLanguage("en")>
|
<Assembly: NeutralResourcesLanguage("en")>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
28
SCrawler.sln
@@ -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
|
||||||
|
|||||||
@@ -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/>
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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,7 +45,7 @@ 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"
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -56,6 +56,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
|
||||||
@@ -90,7 +94,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})]")
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
Protected ImageVideoContains As String = String.Empty
|
Protected ImageVideoContains As String = String.Empty
|
||||||
@@ -101,17 +105,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
|
||||||
|
|||||||
@@ -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,24 @@
|
|||||||
'
|
'
|
||||||
' 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.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 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 +48,54 @@ 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 Sub New(ByVal URL As String)
|
||||||
URL = _URL
|
Me.URL = URL
|
||||||
URL_BASE = _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.PluginUserMedia)
|
||||||
If Not IsNothing(m) Then
|
[Type] = m.ContentType
|
||||||
[Type] = m.ContentType
|
URL = m.URL
|
||||||
URL = m.URL
|
URL_BASE = URL
|
||||||
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,6 +103,17 @@ 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
|
||||||
@@ -83,6 +136,19 @@ Namespace API.Base
|
|||||||
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>
|
||||||
@@ -116,7 +182,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 +194,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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
@@ -125,16 +123,6 @@ Namespace API.Base
|
|||||||
Private Const Name_ScriptData As String = "ScriptData"
|
Private Const Name_ScriptData As String = "ScriptData"
|
||||||
|
|
||||||
Private Const Name_DataMerging As String = "DataMerging"
|
Private 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"
|
||||||
@@ -320,7 +308,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
|
||||||
@@ -466,7 +454,10 @@ 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}"
|
||||||
|
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)}")
|
||||||
@@ -554,7 +545,7 @@ BlockNullPicture:
|
|||||||
If LastUpdated.HasValue And Not .ViewDateMode.Value = ShowingDates.Off Then
|
If LastUpdated.HasValue And Not .ViewDateMode.Value = ShowingDates.Off Then
|
||||||
Dim f As Date = If(.ViewDateFrom.HasValue, .ViewDateFrom.Value.Date, Date.MinValue.Date)
|
Dim f As Date = If(.ViewDateFrom.HasValue, .ViewDateFrom.Value.Date, Date.MinValue.Date)
|
||||||
Dim t As Date = If(.ViewDateTo.HasValue, .ViewDateTo.Value.Date, Date.MaxValue.Date)
|
Dim t As Date = If(.ViewDateTo.HasValue, .ViewDateTo.Value.Date, Date.MaxValue.Date)
|
||||||
Select Case DirectCast(.ViewMode.Value, ShowingDates)
|
Select Case DirectCast(.ViewDateMode.Value, ShowingDates)
|
||||||
Case ShowingDates.In : If Not LastUpdated.Value.ValueBetween(f, t) Then Return False
|
Case ShowingDates.In : If Not LastUpdated.Value.ValueBetween(f, t) Then Return False
|
||||||
Case ShowingDates.Not : If LastUpdated.Value.ValueBetween(f, t) Then Return False
|
Case ShowingDates.Not : If LastUpdated.Value.ValueBetween(f, t) Then Return False
|
||||||
End Select
|
End Select
|
||||||
@@ -731,25 +722,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 +736,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
|
||||||
@@ -803,6 +762,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 +785,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 +801,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 +817,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: remove
|
||||||
|
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}
|
||||||
@@ -878,10 +842,10 @@ BlockNullPicture:
|
|||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
DownloadDataF(Token)
|
DownloadDataF(Token)
|
||||||
ThrowAny(Token)
|
ThrowAny(Token)
|
||||||
|
If Settings.ReparseMissingInTheRoutine Then ReparseMissing(Token) : ThrowAny(Token)
|
||||||
Else
|
Else
|
||||||
'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)
|
||||||
@@ -892,7 +856,8 @@ 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()
|
||||||
@@ -953,9 +918,50 @@ 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 : Implements IDisposable
|
||||||
|
Private ReadOnly WC As WebClient
|
||||||
|
Private ReadOnly RC As Response
|
||||||
|
Private ReadOnly RCERROR As New ErrorsDescriber(EDP.ThrowException)
|
||||||
|
Private ReadOnly UseResponserClient As Boolean
|
||||||
|
Friend Sub New(ByRef Source As UserDataBase)
|
||||||
|
UseResponserClient = Source.UseResponserClient
|
||||||
|
If UseResponserClient Then
|
||||||
|
RC = Source.Responser
|
||||||
|
Else
|
||||||
|
WC = New WebClient
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
Friend Sub DownloadFile(ByVal URL As String, ByVal File As String)
|
||||||
|
If UseResponserClient Then
|
||||||
|
RC.DownloadFile(URL, File, RCERROR)
|
||||||
|
Else
|
||||||
|
WC.DownloadFile(URL, File)
|
||||||
|
End If
|
||||||
|
End Sub
|
||||||
|
#Region "IDisposable Support"
|
||||||
|
Private disposedValue As Boolean = False
|
||||||
|
Protected Sub Dispose(ByVal disposing As Boolean)
|
||||||
|
If Not disposedValue And disposing And Not WC Is Nothing Then WC.Dispose()
|
||||||
|
disposedValue = True
|
||||||
|
End Sub
|
||||||
|
Protected Overrides Sub Finalize()
|
||||||
|
Dispose(False)
|
||||||
|
MyBase.Finalize()
|
||||||
|
End Sub
|
||||||
|
Friend Sub Dispose() Implements IDisposable.Dispose
|
||||||
|
Dispose(True)
|
||||||
|
GC.SuppressFinalize(Me)
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
|
End Class
|
||||||
Protected Sub DownloadContentDefault(ByVal Token As CancellationToken)
|
Protected Sub DownloadContentDefault(ByVal Token As CancellationToken)
|
||||||
Try
|
Try
|
||||||
Dim i%
|
Dim i%
|
||||||
@@ -971,8 +977,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)
|
||||||
@@ -988,7 +995,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"
|
||||||
|
|
||||||
@@ -1010,7 +1017,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
|
||||||
@@ -1026,7 +1039,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
|
||||||
@@ -1051,15 +1064,25 @@ 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
|
||||||
''' <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) 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
|
||||||
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
|
||||||
@@ -1100,14 +1123,14 @@ BlockNullPicture:
|
|||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Delete, Move, Merge"
|
#Region "Delete, Move, Merge, Copy"
|
||||||
Friend Overridable Function Delete() As Integer Implements IUserData.Delete
|
Friend Overridable Function Delete(Optional ByVal Multiple As Boolean = False) 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
|
||||||
MainFrameObj.ImageHandler(Me, False)
|
If Not IncludedInCollection Then MainFrameObj.ImageHandler(Me, False)
|
||||||
Settings.UsersList.Remove(User)
|
Settings.UsersList.Remove(User)
|
||||||
Settings.UpdateUsersList()
|
Settings.UpdateUsersList()
|
||||||
Settings.Users.Remove(Me)
|
If Not IncludedInCollection Then Settings.Users.Remove(Me)
|
||||||
Downloader.UserRemove(Me)
|
Downloader.UserRemove(Me)
|
||||||
Dispose(True)
|
Dispose(True)
|
||||||
Return 1
|
Return 1
|
||||||
@@ -1224,10 +1247,52 @@ BlockNullPicture:
|
|||||||
End If
|
End If
|
||||||
Return f
|
Return f
|
||||||
End Function
|
End Function
|
||||||
|
Private Class FilesCopyingException : Inherits ErrorsDescriberException
|
||||||
|
Friend Sub New(ByVal User As IUserData, ByVal Msg As String, ByVal Path As SFile)
|
||||||
|
SendInLogOnlyMessage = True
|
||||||
|
If User.IncludedInCollection Then _MainMessage = $"[{User.CollectionName}] - "
|
||||||
|
_MainMessage &= $"[{User.Site}] - [{User.Name}]. {Msg}: {Path.Path}."
|
||||||
|
End Sub
|
||||||
|
End Class
|
||||||
|
Friend Overridable Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean Implements IUserData.CopyFiles
|
||||||
|
Dim fSource As SFile = Nothing
|
||||||
|
Dim fDest As SFile = Nothing
|
||||||
|
Try
|
||||||
|
Dim pOffset%
|
||||||
|
If IncludedInCollection Then
|
||||||
|
If DataMerging Then pOffset = 1 Else pOffset = 2
|
||||||
|
Else
|
||||||
|
pOffset = 1
|
||||||
|
End If
|
||||||
|
fSource = User.File.CutPath(pOffset).Path.CSFileP
|
||||||
|
|
||||||
|
Dim OptPath$ = String.Empty
|
||||||
|
If IncludedInCollection Then
|
||||||
|
OptPath = $"Collections\{CollectionName}" 'Copying a collection based on the first file
|
||||||
|
Else
|
||||||
|
OptPath = $"{Site}\{Name}"
|
||||||
|
End If
|
||||||
|
fDest = $"{DestinationPath.PathWithSeparator}{OptPath}".CSFileP
|
||||||
|
If fDest.Exists(SFO.Path, False) AndAlso MsgBoxE({$"The following path already exists:{vbCr}{fDest.Path}" & vbCr &
|
||||||
|
"Do you want to copy files here?", "Copying files"}, vbExclamation + vbYesNo) = vbNo Then _
|
||||||
|
Throw New FilesCopyingException(Me, "The following path already exists", fDest)
|
||||||
|
|
||||||
|
If DestinationPath.Exists(SFO.Path, True) Then
|
||||||
|
My.Computer.FileSystem.CopyDirectory(fSource, fDest, FileIO.UIOption.OnlyErrorDialogs, FileIO.UICancelOption.ThrowException)
|
||||||
|
Else
|
||||||
|
Throw New FilesCopyingException(Me, "Cannot create the following path", fDest)
|
||||||
|
End If
|
||||||
|
Return True
|
||||||
|
Catch cex As OperationCanceledException
|
||||||
|
Return ErrorsDescriber.Execute(e, New FilesCopyingException(Me, "Copy canceled", fDest),, False)
|
||||||
|
Catch ex As Exception
|
||||||
|
Return ErrorsDescriber.Execute(e, ex,, False)
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
#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}]"
|
||||||
@@ -1242,11 +1307,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
|
||||||
@@ -1265,8 +1333,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)
|
||||||
@@ -1396,8 +1464,9 @@ 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) As Integer
|
||||||
Function MoveFiles(ByVal CollectionName As String) As Boolean
|
Function MoveFiles(ByVal CollectionName As String) 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
|
||||||
Property DownloadTopCount As Integer?
|
Property DownloadTopCount As Integer?
|
||||||
|
|||||||
@@ -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()
|
||||||
|
|||||||
@@ -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.WebDocuments.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,
|
||||||
|
|||||||
135
SCrawler/API/Instagram/AdditionalSettingsForm.Designer.vb
generated
Normal 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
|
||||||
41
SCrawler/API/Instagram/AdditionalSettingsForm.vb
Normal 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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
4
SCrawler/API/Instagram/OptionsForm.Designer.vb
generated
@@ -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"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
23
SCrawler/API/Instagram/SettingsExchangeOptions.vb
Normal 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
|
||||||
@@ -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, 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"
|
||||||
@@ -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)
|
||||||
@@ -204,6 +208,10 @@ Namespace API.Instagram
|
|||||||
If .ContainsKey(Header_IG_WWW_CLAIM) Then www_claim = .Item(Header_IG_WWW_CLAIM)
|
If .ContainsKey(Header_IG_WWW_CLAIM) Then www_claim = .Item(Header_IG_WWW_CLAIM)
|
||||||
End With
|
End With
|
||||||
End If
|
End If
|
||||||
|
If Not .Cookies Is Nothing Then
|
||||||
|
.Cookies.ChangedAllowInternalDrop = False
|
||||||
|
.Cookies.Changed = False
|
||||||
|
End If
|
||||||
End With
|
End With
|
||||||
|
|
||||||
Dim n() As String = {SettingsCLS.Name_Node_Sites, Site.ToString}
|
Dim n() As String = {SettingsCLS.Name_Node_Sites, Site.ToString}
|
||||||
@@ -216,6 +224,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)
|
||||||
@@ -233,17 +245,68 @@ Namespace API.Instagram
|
|||||||
LastDownloadDate = New XMLValue(Of Date)("LastDownloadDate", Now.AddDays(-1), _XML, n)
|
LastDownloadDate = New XMLValue(Of Date)("LastDownloadDate", Now.AddDays(-1), _XML, n)
|
||||||
LastRequestsCount = New XMLValue(Of Integer)("LastRequestsCount", 0, _XML, n)
|
LastRequestsCount = New XMLValue(Of Integer)("LastRequestsCount", 0, _XML, n)
|
||||||
LastRequestsCountLabel = New PropertyValue(LastRequestsCountLabelStr.Invoke(LastRequestsCount.Value))
|
LastRequestsCountLabel = New PropertyValue(LastRequestsCountLabelStr.Invoke(LastRequestsCount.Value))
|
||||||
AddHandler LastRequestsCount.OnValueChanged, Sub(sender, __name, __value) LastRequestsCountLabel.Value = LastRequestsCountLabelStr.Invoke(__value)
|
AddHandler LastRequestsCount.OnValueChanged, Sub(sender, __name, __value) LastRequestsCountLabel.Value = LastRequestsCountLabelStr.Invoke(DirectCast(__value, Existable(Of Integer)).Value)
|
||||||
|
|
||||||
UrlPatternUser = "https://www.instagram.com/{0}/"
|
UrlPatternUser = "https://www.instagram.com/{0}/"
|
||||||
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.Headers
|
||||||
|
If .ContainsKey(Header_CSRF_TOKEN) Then Token = .Item(Header_CSRF_TOKEN)
|
||||||
|
If .ContainsKey(Header_IG_APP_ID) Then AppID = .Item(Header_IG_APP_ID)
|
||||||
|
If .ContainsKey(Header_IG_WWW_CLAIM) Then WwwClaim = .Item(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 +353,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 +397,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 +441,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 +453,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
|
||||||
@@ -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
|
||||||
Imports PersonalUtilities.Tools.WebDocuments.JSON
|
Imports PersonalUtilities.Tools.WebDocuments.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
|
||||||
@@ -658,7 +675,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 +686,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 +715,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 +749,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 +767,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
|
||||||
|
|||||||
37
SCrawler/API/LPSG/Declarations.vb
Normal 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
|
||||||
37
SCrawler/API/LPSG/SiteSettings.vb
Normal 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
|
||||||
104
SCrawler/API/LPSG/UserData.vb
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
' 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 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.Error = 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 : .UseWebClient = True : .UseWebClientCookies = True : .ResetError() : 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
|
||||||
@@ -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)
|
||||||
|
|||||||
@@ -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\"
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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,21 +9,20 @@
|
|||||||
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"), PXML("SavedPostsUserName")>
|
||||||
@@ -40,7 +39,7 @@ 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"
|
||||||
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
|
||||||
@@ -80,10 +79,15 @@ 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
|
||||||
|
DirectCast(Settings(RedGifs.RedGifsSiteKey).Source, RedGifs.SiteSettings).UpdateTokenIfRequired()
|
||||||
|
Return True
|
||||||
|
Else
|
||||||
|
Return False
|
||||||
|
End If
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
@@ -92,8 +96,11 @@ Namespace API.Reddit
|
|||||||
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)
|
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||||
Return UserData.GetVideoInfo(URL, Responser)
|
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
|
||||||
|
|||||||
@@ -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 System.Net
|
||||||
|
Imports System.Threading
|
||||||
|
Imports SCrawler.API.Base
|
||||||
|
Imports SCrawler.API.Reddit.RedditViewExchange
|
||||||
|
Imports SCrawler.Plugin.Hosts
|
||||||
Imports PersonalUtilities.Functions.XML
|
Imports PersonalUtilities.Functions.XML
|
||||||
Imports PersonalUtilities.Functions.RegularExpressions
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
Imports PersonalUtilities.Tools.ImageRenderer
|
Imports PersonalUtilities.Tools.ImageRenderer
|
||||||
Imports PersonalUtilities.Tools.WEB
|
Imports PersonalUtilities.Tools.WEB
|
||||||
Imports PersonalUtilities.Tools.WebDocuments.JSON
|
Imports PersonalUtilities.Tools.WebDocuments.JSON
|
||||||
Imports System.Net
|
|
||||||
Imports System.Threading
|
|
||||||
Imports SCrawler.API.Base
|
|
||||||
Imports SCrawler.API.Reddit.RedditViewExchange
|
|
||||||
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,7 +151,6 @@ 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
|
||||||
'If Not IsSavedPosts AndAlso (Not IsChannel OrElse ChannelInfo Is Nothing) Then ReparseMissing(Token)
|
|
||||||
If IsSavedPosts Then
|
If IsSavedPosts Then
|
||||||
DownloadDataChannel(String.Empty, Token)
|
DownloadDataChannel(String.Empty, Token)
|
||||||
ElseIf IsChannel Then
|
ElseIf IsChannel Then
|
||||||
@@ -176,6 +176,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"
|
||||||
@@ -238,7 +240,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
|
||||||
@@ -270,7 +272,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
|
||||||
@@ -328,7 +330,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
|
||||||
@@ -376,7 +378,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
|
||||||
'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)
|
||||||
@@ -386,7 +387,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
|
||||||
@@ -416,19 +416,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
|
||||||
@@ -447,8 +434,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
|
||||||
@@ -510,20 +522,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
|
||||||
@@ -544,60 +573,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
|
||||||
@@ -606,26 +609,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
|
||||||
@@ -657,6 +711,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}"
|
||||||
@@ -666,6 +721,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
|
||||||
@@ -687,6 +743,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
|
||||||
@@ -736,7 +793,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)
|
||||||
@@ -759,7 +816,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
|
||||||
@@ -786,12 +843,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
|
||||||
@@ -836,20 +895,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)
|
||||||
|
|||||||
@@ -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,
|
||||||
|
Function(v) If(CStr(v).IsEmptyString, String.Empty, CStr(v).ToLower.Trim))
|
||||||
End Module
|
End Module
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -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,40 +6,158 @@
|
|||||||
'
|
'
|
||||||
' 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
|
||||||
|
Imports PersonalUtilities.Tools.WebDocuments.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 .UseWebClient Or Not .UseWebClientCookies Or Not .UseWebClientAdditionalHeaders
|
||||||
|
.UseWebClient = True
|
||||||
|
.UseWebClientCookies = True
|
||||||
|
.UseWebClientAdditionalHeaders = True
|
||||||
|
If .Headers.Count > 0 AndAlso .Headers.ContainsKey(TokenName) Then t = .Headers(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)
|
||||||
|
With Responser.Headers
|
||||||
|
If .Count = 0 OrElse Not .ContainsKey(TokenName) Then .Add(TokenName, Value) Else .Item(TokenName) = Value
|
||||||
|
Responser.SaveSettings()
|
||||||
|
End With
|
||||||
|
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 UserID As String, ByVal PostID As String) As String
|
||||||
Return $"https://www.redgifs.com/watch/{PostID}"
|
Return $"https://www.redgifs.com/watch/{PostID}"
|
||||||
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
|
||||||
Return False
|
Return UpdateTokenIfRequired() AndAlso ACheck(Token.Value)
|
||||||
End Function
|
End Function
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -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
|
||||||
|
Imports PersonalUtilities.Tools.WebDocuments.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
|
||||||
@@ -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,84 @@ 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.DownloadString(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
|
||||||
|
Return ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[API.RedGifs.UserData.GetDataFromUrlId({URL})]",
|
||||||
|
New UserMedia With {.State = UStates.Missing})
|
||||||
|
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 +228,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
|
||||||
65
SCrawler/API/TikTok/Declarations.vb
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
' Copyright (C) 2023 Andy https://github.com/AAndyProgram
|
||||||
|
' This program is free software: you can redistribute it and/or modify
|
||||||
|
' it under the terms of the GNU General Public License as published by
|
||||||
|
' the Free Software Foundation, either version 3 of the License, or
|
||||||
|
' (at your option) any later version.
|
||||||
|
'
|
||||||
|
' This program is distributed in the hope that it will be useful,
|
||||||
|
' but WITHOUT ANY WARRANTY
|
||||||
|
Imports 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
|
||||||
42
SCrawler/API/TikTok/SiteSettings.vb
Normal 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.Plugin
|
||||||
|
Imports SCrawler.Plugin.Attributes
|
||||||
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
|
Namespace API.TikTok
|
||||||
|
<Manifest("AndyProgram_TikTok")>
|
||||||
|
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||||
|
Friend Overrides ReadOnly Property Icon As Icon
|
||||||
|
Get
|
||||||
|
Return My.Resources.SiteResources.TikTokIcon_32
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Friend Overrides ReadOnly Property Image As Image
|
||||||
|
Get
|
||||||
|
Return My.Resources.SiteResources.TikTokPic_192
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Friend Sub New()
|
||||||
|
MyBase.New("TikTok", "www.tiktok.com")
|
||||||
|
UrlPatternUser = "https://www.tiktok.com/@{0}/"
|
||||||
|
UserRegex = RParams.DMS("[htps:/]{7,8}.*?tiktok.com/@([^/]+)", 1)
|
||||||
|
ImageVideoContains = "tiktok.com"
|
||||||
|
End Sub
|
||||||
|
Friend Overrides Function GetInstance(ByVal What As ISiteSettings.Download) As IPluginContentProvider
|
||||||
|
Return New UserData
|
||||||
|
End Function
|
||||||
|
Friend Overrides Function GetSpecialData(ByVal URL As String, ByVal Path As String, ByVal AskForPath As Boolean) As IEnumerable
|
||||||
|
Return UserData.GetVideoInfo(URL, Responser)
|
||||||
|
End Function
|
||||||
|
Friend Overrides Function BaseAuthExists() As Boolean
|
||||||
|
Return If(Responser.Cookies?.Count, 0) > 0
|
||||||
|
End Function
|
||||||
|
End Class
|
||||||
|
End Namespace
|
||||||
93
SCrawler/API/TikTok/UserData.vb
Normal file
@@ -0,0 +1,93 @@
|
|||||||
|
' 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
|
||||||
|
Namespace API.TikTok
|
||||||
|
Friend Class UserData : Inherits UserDataBase
|
||||||
|
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||||
|
End Sub
|
||||||
|
Friend Sub New()
|
||||||
|
SeparateVideoFolder = False
|
||||||
|
End Sub
|
||||||
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
|
Dim URL$ = String.Empty
|
||||||
|
Try
|
||||||
|
Dim PostIDs As List(Of String)
|
||||||
|
Dim PostDate As Date? = Nothing
|
||||||
|
Dim PostURL$ = String.Empty
|
||||||
|
Dim r$
|
||||||
|
URL = $"https://www.tiktok.com/@{Name}"
|
||||||
|
r = Responser.GetResponse(URL,, EDP.ThrowException)
|
||||||
|
PostIDs = RegexEnvir.GetIDList(r)
|
||||||
|
If PostIDs.ListExists Then
|
||||||
|
For Each __id$ In PostIDs
|
||||||
|
If Not _TempPostsList.Contains(__id) Then
|
||||||
|
_TempPostsList.Add(__id)
|
||||||
|
If RegexEnvir.GetVideoData(r, __id, PostURL, PostDate) Then
|
||||||
|
Select Case CheckDatesLimit(PostDate, CheckDateProvider)
|
||||||
|
Case DateResult.Skip : Continue For
|
||||||
|
Case DateResult.Exit : Exit Sub
|
||||||
|
End Select
|
||||||
|
If ID.IsEmptyString And Not PostURL.IsEmptyString Then ID = RegexEnvir.ExtractUserID(PostURL)
|
||||||
|
_TempMediaList.ListAddValue(MediaFromData(PostURL, __id, PostDate))
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
Exit Sub
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
Catch ex As Exception
|
||||||
|
ProcessException(ex, Token, $"data downloading error [{URL}]")
|
||||||
|
End Try
|
||||||
|
End Sub
|
||||||
|
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||||
|
DownloadContentDefault(Token)
|
||||||
|
End Sub
|
||||||
|
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal Responser As Response, Optional ByVal e As ErrorsDescriber = Nothing) As IEnumerable(Of UserMedia)
|
||||||
|
Try
|
||||||
|
If Not URL.IsEmptyString Then
|
||||||
|
Dim PostId$ = String.Empty
|
||||||
|
Dim PostDate As Date? = Nothing
|
||||||
|
Dim PostURL$ = String.Empty
|
||||||
|
Dim r$
|
||||||
|
PostId = RegexEnvir.ExtractPostID(URL)
|
||||||
|
If Not PostId.IsEmptyString Then
|
||||||
|
Using resp As Response = Responser.Copy() : r = resp.GetResponse(URL,, EDP.ThrowException) : End Using
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
If RegexEnvir.GetVideoData(r, PostId, PostURL, PostDate) Then Return {MediaFromData(PostURL, PostId, PostDate)}
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Return Nothing
|
||||||
|
Catch ex As Exception
|
||||||
|
If Not e.Exists Then e = New ErrorsDescriber(EDP.ShowMainMsg + EDP.SendInLog)
|
||||||
|
Return ErrorsDescriber.Execute(e, ex, $"TikTok standalone downloader: fetch media error ({URL})")
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
|
Private Shared Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As Date?) As UserMedia
|
||||||
|
_URL = LinkFormatterSecure(RegexReplace(_URL.Replace("\", String.Empty), LinkPattern))
|
||||||
|
Dim m As New UserMedia(_URL, UserMedia.Types.Video) With {.Post = New UserPost With {.ID = PostID}}
|
||||||
|
If Not m.URL.IsEmptyString Then m.File = $"{PostID}.mp4"
|
||||||
|
If PostDate.HasValue Then m.Post.Date = PostDate
|
||||||
|
Return m
|
||||||
|
End Function
|
||||||
|
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||||
|
Optional ByVal EObj As Object = Nothing) As Integer
|
||||||
|
If Responser.Status = Net.WebExceptionStatus.ConnectionClosed Then
|
||||||
|
UserExists = False
|
||||||
|
Return 1
|
||||||
|
Else
|
||||||
|
Return 0
|
||||||
|
End If
|
||||||
|
End Function
|
||||||
|
End Class
|
||||||
|
End Namespace
|
||||||
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
@@ -12,18 +12,18 @@ Imports SCrawler.Plugin.Attributes
|
|||||||
Imports PersonalUtilities.Tools.WEB
|
Imports PersonalUtilities.Tools.WEB
|
||||||
Imports PersonalUtilities.Functions.RegularExpressions
|
Imports PersonalUtilities.Functions.RegularExpressions
|
||||||
Namespace API.Twitter
|
Namespace API.Twitter
|
||||||
<Manifest("AndyProgram_Twitter"), SavedPosts, UseClassAsIs>
|
<Manifest("AndyProgram_Twitter"), SavedPosts>
|
||||||
Friend Class SiteSettings : Inherits SiteSettingsBase
|
Friend Class SiteSettings : Inherits SiteSettingsBase
|
||||||
Friend Const Header_Authorization As String = "authorization"
|
Friend Const Header_Authorization As String = "authorization"
|
||||||
Friend Const Header_Token As String = "x-csrf-token"
|
Friend Const Header_Token As String = "x-csrf-token"
|
||||||
Friend Overrides ReadOnly Property Icon As Icon
|
Friend Overrides ReadOnly Property Icon As Icon
|
||||||
Get
|
Get
|
||||||
Return My.Resources.TwitterIcon
|
Return My.Resources.SiteResources.TwitterIcon_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.TwitterPic400
|
Return My.Resources.SiteResources.TwitterPic_400
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
<PropertyOption(AllowNull:=False, ControlText:="Authorization",
|
<PropertyOption(AllowNull:=False, ControlText:="Authorization",
|
||||||
@@ -100,11 +100,14 @@ Namespace API.Twitter
|
|||||||
Return New UserData
|
Return New UserData
|
||||||
End If
|
End If
|
||||||
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 UserData.GetVideoInfo(URL, Responser)
|
Return UserData.GetVideoInfo(URL, Responser)
|
||||||
End Function
|
End Function
|
||||||
Friend Overrides Function GetUserPostUrl(ByVal UserID As String, ByVal PostID As String) As String
|
Friend Overrides Function GetUserPostUrl(ByVal UserID As String, ByVal PostID As String) As String
|
||||||
Return $"https://twitter.com/{UserID}/status/{PostID}"
|
Return $"https://twitter.com/{UserID}/status/{PostID}"
|
||||||
End Function
|
End Function
|
||||||
|
Friend Overrides Function BaseAuthExists() As Boolean
|
||||||
|
Return If(Responser.Cookies?.Count, 0) > 0 And ACheck(Token.Value) And ACheck(Auth.Value)
|
||||||
|
End Function
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -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,26 +6,27 @@
|
|||||||
'
|
'
|
||||||
' 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.Tools.WEB
|
|
||||||
Imports PersonalUtilities.Tools.WebDocuments.JSON
|
|
||||||
Imports PersonalUtilities.Functions.XML
|
|
||||||
Imports PersonalUtilities.Functions.RegularExpressions
|
|
||||||
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
|
||||||
|
Imports PersonalUtilities.Tools.WebDocuments.JSON
|
||||||
Imports UStates = SCrawler.API.Base.UserMedia.States
|
Imports UStates = SCrawler.API.Base.UserMedia.States
|
||||||
Namespace API.Twitter
|
Namespace API.Twitter
|
||||||
Friend Class UserData : Inherits UserDataBase
|
Friend Class UserData : Inherits UserDataBase
|
||||||
|
Private Const SinglePostUrl As String = "https://api.twitter.com/1.1/statuses/show.json?id={0}&tweet_mode=extended"
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
Private ReadOnly _DataNames As List(Of String)
|
Private ReadOnly _DataNames As List(Of String)
|
||||||
|
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
||||||
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "Initializer"
|
#Region "Initializer"
|
||||||
Friend Sub New()
|
Friend Sub New()
|
||||||
_DataNames = New List(Of String)
|
_DataNames = New List(Of String)
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
Protected Overrides Sub LoadUserInformation_OptionalFields(ByRef Container As XmlFile, ByVal Loading As Boolean)
|
|
||||||
End Sub
|
|
||||||
#Region "Download functions"
|
#Region "Download functions"
|
||||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
If IsSavedPosts Then
|
If IsSavedPosts Then
|
||||||
@@ -34,7 +35,6 @@ Namespace API.Twitter
|
|||||||
Else
|
Else
|
||||||
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
|
If _ContentList.Count > 0 Then _DataNames.ListAddList(_ContentList.Select(Function(c) c.File.File), LAP.ClearBeforeAdd, LAP.NotContainsOnly)
|
||||||
DownloadData(String.Empty, Token)
|
DownloadData(String.Empty, Token)
|
||||||
'ReparseMissing(Token)
|
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Private Overloads Sub DownloadData(ByVal POST As String, ByVal Token As CancellationToken)
|
Private Overloads Sub DownloadData(ByVal POST As String, ByVal Token As CancellationToken)
|
||||||
@@ -43,12 +43,11 @@ Namespace API.Twitter
|
|||||||
Dim NextCursor$ = String.Empty
|
Dim NextCursor$ = String.Empty
|
||||||
Dim __NextCursor As Predicate(Of EContainer) = Function(e) e.Value({"content", "operation", "cursor"}, "cursorType") = "Bottom"
|
Dim __NextCursor As Predicate(Of EContainer) = Function(e) e.Value({"content", "operation", "cursor"}, "cursorType") = "Bottom"
|
||||||
Dim PostID$ = String.Empty
|
Dim PostID$ = String.Empty
|
||||||
Dim PostDate$ ', dName$
|
Dim PostDate$
|
||||||
Dim nn As EContainer, s As EContainer ', m As EContainer
|
Dim nn As EContainer, s As EContainer
|
||||||
Dim NewPostDetected As Boolean = False
|
Dim NewPostDetected As Boolean = False
|
||||||
Dim ExistsDetected As Boolean = False
|
Dim ExistsDetected As Boolean = False
|
||||||
|
|
||||||
Dim PicNode As Predicate(Of EContainer) = Function(e) e.Count > 0 AndAlso e.Contains("media_url")
|
|
||||||
Dim UID As Func(Of EContainer, String) = Function(e) e.XmlIfNothing.Item({"user", "id"}).XmlIfNothingValue
|
Dim UID As Func(Of EContainer, String) = Function(e) e.XmlIfNothing.Item({"user", "id"}).XmlIfNothingValue
|
||||||
|
|
||||||
If IsSavedPosts Then
|
If IsSavedPosts Then
|
||||||
@@ -99,26 +98,11 @@ Namespace API.Twitter
|
|||||||
Continue For
|
Continue For
|
||||||
End If
|
End If
|
||||||
|
|
||||||
If IsSavedPosts OrElse Not ParseUserMediaOnly OrElse (Not nn.Contains("retweeted_status") OrElse
|
If IsSavedPosts OrElse Not ParseUserMediaOnly OrElse
|
||||||
(Not ID.IsEmptyString AndAlso UID(nn("retweeted_status")) = ID)) Then
|
(
|
||||||
'If Not CheckVideoNode(nn, PostID, PostDate) Then
|
Not nn.Contains("retweeted_status") OrElse
|
||||||
' s = nn.ItemF({"extended_entities", "media"})
|
(Not ID.IsEmptyString AndAlso UID(nn("retweeted_status")) = ID)
|
||||||
' If s Is Nothing OrElse s.Count = 0 Then s = nn.ItemF({"retweeted_status", "extended_entities", "media"})
|
) Then ObtainMedia(nn, PostID, PostDate)
|
||||||
' If Not s Is Nothing AndAlso s.Count > 0 Then
|
|
||||||
' For Each m In s
|
|
||||||
' If m.Count > 0 AndAlso m.Contains("media_url") Then
|
|
||||||
' dName = UrlFile(m("media_url").Value)
|
|
||||||
' If Not dName.IsEmptyString AndAlso Not _DataNames.Contains(dName) Then
|
|
||||||
' _DataNames.Add(dName)
|
|
||||||
' _TempMediaList.ListAddValue(MediaFromData(m("media_url").Value,
|
|
||||||
' PostID, PostDate, GetPictureOption(m)), LNC)
|
|
||||||
' End If
|
|
||||||
' End If
|
|
||||||
' Next
|
|
||||||
' End If
|
|
||||||
'End If
|
|
||||||
ObtainMedia(nn, PostID, PostDate)
|
|
||||||
End If
|
|
||||||
End If
|
End If
|
||||||
Next
|
Next
|
||||||
|
|
||||||
@@ -142,8 +126,10 @@ Namespace API.Twitter
|
|||||||
ProcessException(ex, Token, $"data downloading error{IIf(IsSavedPosts, " (Saved Posts)", String.Empty)} [{URL}]")
|
ProcessException(ex, Token, $"data downloading error{IIf(IsSavedPosts, " (Saved Posts)", String.Empty)} [{URL}]")
|
||||||
End Try
|
End Try
|
||||||
End Sub
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "Obtain media"
|
||||||
Private Sub ObtainMedia(ByVal e As EContainer, ByVal PostID As String, ByVal PostDate As String, Optional ByVal State As UStates = UStates.Unknown)
|
Private Sub ObtainMedia(ByVal e As EContainer, ByVal PostID As String, ByVal PostDate As String, Optional ByVal State As UStates = UStates.Unknown)
|
||||||
If Not CheckVideoNode(e, PostID, PostDate) Then
|
If Not CheckVideoNode(e, PostID, PostDate, State) Then
|
||||||
Dim s As EContainer = e.ItemF({"extended_entities", "media"})
|
Dim s As EContainer = e.ItemF({"extended_entities", "media"})
|
||||||
If s Is Nothing OrElse s.Count = 0 Then s = e.ItemF({"retweeted_status", "extended_entities", "media"})
|
If s Is Nothing OrElse s.Count = 0 Then s = e.ItemF({"retweeted_status", "extended_entities", "media"})
|
||||||
If If(s?.Count, 0) > 0 Then
|
If If(s?.Count, 0) > 0 Then
|
||||||
@@ -160,110 +146,16 @@ Namespace API.Twitter
|
|||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
|
Private Function CheckVideoNode(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String,
|
||||||
Dim rList As New List(Of Integer)
|
Optional ByVal State As UStates = UStates.Unknown) As Boolean
|
||||||
Dim URL$ = String.Empty
|
|
||||||
Try
|
Try
|
||||||
If ContentMissingExists Then
|
If CheckForGif(w, PostID, PostDate, State) Then Return True
|
||||||
Dim m As UserMedia
|
|
||||||
Dim r$, PostDate$
|
|
||||||
Dim j As EContainer
|
|
||||||
For i% = 0 To _ContentList.Count - 1
|
|
||||||
If _ContentList(i).State = UStates.Missing Then
|
|
||||||
m = _ContentList(i)
|
|
||||||
If Not m.Post.ID.IsEmptyString Then
|
|
||||||
ThrowAny(Token)
|
|
||||||
URL = $"https://api.twitter.com/1.1/statuses/show.json?id={m.Post.ID}"
|
|
||||||
r = Responser.GetResponse(URL,, EDP.ReturnValue)
|
|
||||||
If Not r.IsEmptyString Then
|
|
||||||
j = JsonDocument.Parse(r)
|
|
||||||
If Not j Is Nothing Then
|
|
||||||
PostDate = String.Empty
|
|
||||||
If j.Contains("created_at") Then PostDate = j("created_at").Value Else PostDate = String.Empty
|
|
||||||
ObtainMedia(j, m.Post.ID, PostDate, UStates.Missing)
|
|
||||||
rList.Add(i)
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
Next
|
|
||||||
End If
|
|
||||||
Catch ex As Exception
|
|
||||||
ProcessException(ex, Token, $"ReparseMissing error [{URL}]")
|
|
||||||
Finally
|
|
||||||
If rList.Count > 0 Then
|
|
||||||
For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(i) : Next
|
|
||||||
rList.Clear()
|
|
||||||
End If
|
|
||||||
End Try
|
|
||||||
End Sub
|
|
||||||
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal resp As Response) As IEnumerable(Of UserMedia)
|
|
||||||
Try
|
|
||||||
If URL.Contains("twitter") Then
|
|
||||||
Dim PostID$ = RegexReplace(URL, RParams.DM("(?<=/)\d+", 0))
|
|
||||||
If Not PostID.IsEmptyString Then
|
|
||||||
Dim r$ = DirectCast(resp.Copy(), Response).GetResponse($"https://api.twitter.com/1.1/statuses/show.json?id={PostID}",, EDP.ReturnValue)
|
|
||||||
If Not r.IsEmptyString Then
|
|
||||||
Using j As EContainer = JsonDocument.Parse(r)
|
|
||||||
If j.ListExists Then
|
|
||||||
Dim u$ = GetVideoNodeURL(j)
|
|
||||||
If Not u.IsEmptyString Then Return {MediaFromData(u, PostID, String.Empty)}
|
|
||||||
End If
|
|
||||||
End Using
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
End If
|
|
||||||
Return Nothing
|
|
||||||
Catch ex As Exception
|
|
||||||
Return ErrorsDescriber.Execute(EDP.ShowMainMsg + EDP.SendInLog, ex, "Video searching error")
|
|
||||||
End Try
|
|
||||||
End Function
|
|
||||||
#Region "Picture options"
|
|
||||||
Private Function GetPictureOption(ByVal w As EContainer) As String
|
|
||||||
Const P4K As String = "4096x4096"
|
|
||||||
Try
|
|
||||||
Dim ww As EContainer = w("sizes")
|
|
||||||
If ww.ListExists Then
|
|
||||||
Dim l As New List(Of Sizes)
|
|
||||||
Dim Orig As Sizes? = New Sizes(w.Value({"original_info"}, "height").FromXML(Of Integer)(-1), P4K)
|
|
||||||
If Orig.Value.Value = -1 Then Orig = Nothing
|
|
||||||
Dim LargeContained As Boolean = ww.Contains("large")
|
|
||||||
For Each v As EContainer In ww
|
|
||||||
If v.Count > 0 AndAlso v.Contains("h") Then l.Add(New Sizes(v.Value("h"), v.Name))
|
|
||||||
Next
|
|
||||||
If l.Count > 0 Then
|
|
||||||
l.Sort()
|
|
||||||
If Orig.HasValue AndAlso l(0).Value < Orig.Value.Value Then
|
|
||||||
Return P4K
|
|
||||||
ElseIf l(0).Data.IsEmptyString Then
|
|
||||||
Return P4K
|
|
||||||
Else
|
|
||||||
Return l(0).Data
|
|
||||||
End If
|
|
||||||
Else
|
|
||||||
Return P4K
|
|
||||||
End If
|
|
||||||
ElseIf Not w.Value({"original_info"}, "height").IsEmptyString Then
|
|
||||||
Return P4K
|
|
||||||
Else
|
|
||||||
Return String.Empty
|
|
||||||
End If
|
|
||||||
Catch ex As Exception
|
|
||||||
LogError(ex, "[API.Twitter.UserData.GetPictureOption]")
|
|
||||||
Return String.Empty
|
|
||||||
End Try
|
|
||||||
End Function
|
|
||||||
#End Region
|
|
||||||
#Region "Video options"
|
|
||||||
Private Function CheckVideoNode(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String) As Boolean
|
|
||||||
Try
|
|
||||||
If CheckForGif(w, PostID, PostDate) Then Return True
|
|
||||||
Dim URL$ = GetVideoNodeURL(w)
|
Dim URL$ = GetVideoNodeURL(w)
|
||||||
If Not URL.IsEmptyString Then
|
If Not URL.IsEmptyString Then
|
||||||
Dim f$ = UrlFile(URL)
|
Dim f$ = UrlFile(URL)
|
||||||
If Not f.IsEmptyString AndAlso Not _DataNames.Contains(f) Then
|
If Not f.IsEmptyString AndAlso Not _DataNames.Contains(f) Then
|
||||||
_DataNames.Add(f)
|
_DataNames.Add(f)
|
||||||
_TempMediaList.ListAddValue(MediaFromData(URL, PostID, PostDate), LNC)
|
_TempMediaList.ListAddValue(MediaFromData(URL, PostID, PostDate,, State), LNC)
|
||||||
End If
|
End If
|
||||||
Return True
|
Return True
|
||||||
End If
|
End If
|
||||||
@@ -273,7 +165,8 @@ Namespace API.Twitter
|
|||||||
Return False
|
Return False
|
||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
Private Function CheckForGif(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String) As Boolean
|
Private Function CheckForGif(ByVal w As EContainer, ByVal PostID As String, ByVal PostDate As String,
|
||||||
|
Optional ByVal State As UStates = UStates.Unknown) As Boolean
|
||||||
Try
|
Try
|
||||||
Dim gifUrl As Predicate(Of EContainer) = Function(e) Not e.Value("content_type").IsEmptyString AndAlso
|
Dim gifUrl As Predicate(Of EContainer) = Function(e) Not e.Value("content_type").IsEmptyString AndAlso
|
||||||
e.Value("content_type").Contains("mp4") AndAlso
|
e.Value("content_type").Contains("mp4") AndAlso
|
||||||
@@ -290,7 +183,7 @@ Namespace API.Twitter
|
|||||||
ff = UrlFile(url)
|
ff = UrlFile(url)
|
||||||
If Not ff.IsEmptyString Then
|
If Not ff.IsEmptyString Then
|
||||||
If Not _DataNames.Contains(ff) Then
|
If Not _DataNames.Contains(ff) Then
|
||||||
m = MediaFromData(url, PostID, PostDate)
|
m = MediaFromData(url, PostID, PostDate,, State)
|
||||||
f = m.File
|
f = m.File
|
||||||
If Not f.IsEmptyString Then f.Name = $"GIF_{f.Name}" : m.File = f
|
If Not f.IsEmptyString Then f.Name = $"GIF_{f.Name}" : m.File = f
|
||||||
_TempMediaList.ListAddValue(m, LNC)
|
_TempMediaList.ListAddValue(m, LNC)
|
||||||
@@ -329,6 +222,107 @@ Namespace API.Twitter
|
|||||||
End If
|
End If
|
||||||
Return String.Empty
|
Return String.Empty
|
||||||
End Function
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "ReparseMissing"
|
||||||
|
Protected Overrides Sub ReparseMissing(ByVal Token As CancellationToken)
|
||||||
|
Dim rList As New List(Of Integer)
|
||||||
|
Dim URL$ = String.Empty
|
||||||
|
Try
|
||||||
|
If ContentMissingExists Then
|
||||||
|
Dim m As UserMedia
|
||||||
|
Dim r$, PostDate$
|
||||||
|
Dim j As EContainer
|
||||||
|
For i% = 0 To _ContentList.Count - 1
|
||||||
|
If _ContentList(i).State = UStates.Missing Then
|
||||||
|
m = _ContentList(i)
|
||||||
|
If Not m.Post.ID.IsEmptyString Then
|
||||||
|
ThrowAny(Token)
|
||||||
|
URL = String.Format(SinglePostUrl, m.Post.ID)
|
||||||
|
r = Responser.GetResponse(URL,, EDP.ReturnValue)
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
j = JsonDocument.Parse(r)
|
||||||
|
If Not j Is Nothing Then
|
||||||
|
PostDate = String.Empty
|
||||||
|
If j.Contains("created_at") Then PostDate = j("created_at").Value Else PostDate = String.Empty
|
||||||
|
ObtainMedia(j, m.Post.ID, PostDate, UStates.Missing)
|
||||||
|
rList.Add(i)
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Next
|
||||||
|
End If
|
||||||
|
Catch ex As Exception
|
||||||
|
ProcessException(ex, Token, $"ReparseMissing error [{URL}]")
|
||||||
|
Finally
|
||||||
|
If rList.Count > 0 Then
|
||||||
|
For i% = rList.Count - 1 To 0 Step -1 : _ContentList.RemoveAt(i) : Next
|
||||||
|
rList.Clear()
|
||||||
|
End If
|
||||||
|
End Try
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "Get video static"
|
||||||
|
Friend Shared Function GetVideoInfo(ByVal URL As String, ByVal resp As Response) As IEnumerable(Of UserMedia)
|
||||||
|
Try
|
||||||
|
If URL.Contains("twitter") Then
|
||||||
|
Dim PostID$ = RegexReplace(URL, RParams.DM("(?<=/)\d+", 0))
|
||||||
|
If Not PostID.IsEmptyString Then
|
||||||
|
Dim r$
|
||||||
|
Using rc As Response = resp.Copy() : r = rc.GetResponse(String.Format(SinglePostUrl, PostID),, EDP.ReturnValue) : End Using
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
Using j As EContainer = JsonDocument.Parse(r)
|
||||||
|
If j.ListExists Then
|
||||||
|
Dim u$ = GetVideoNodeURL(j)
|
||||||
|
If Not u.IsEmptyString Then Return {MediaFromData(u, PostID, String.Empty)}
|
||||||
|
End If
|
||||||
|
End Using
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Return Nothing
|
||||||
|
Catch ex As Exception
|
||||||
|
Return ErrorsDescriber.Execute(EDP.ShowMainMsg + EDP.SendInLog, ex, $"Twitter standalone downloader: fetch media error ({URL})")
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "Picture options"
|
||||||
|
Private Function GetPictureOption(ByVal w As EContainer) As String
|
||||||
|
Const P4K As String = "4096x4096"
|
||||||
|
Try
|
||||||
|
Dim ww As EContainer = w("sizes")
|
||||||
|
If ww.ListExists Then
|
||||||
|
Dim l As New List(Of Sizes)
|
||||||
|
Dim Orig As Sizes? = New Sizes(w.Value({"original_info"}, "height").FromXML(Of Integer)(-1), P4K)
|
||||||
|
If Orig.Value.Value = -1 Then Orig = Nothing
|
||||||
|
Dim LargeContained As Boolean = ww.Contains("large")
|
||||||
|
For Each v As EContainer In ww
|
||||||
|
If v.Count > 0 AndAlso v.Contains("h") Then l.Add(New Sizes(v.Value("h"), v.Name))
|
||||||
|
Next
|
||||||
|
If l.Count > 0 Then
|
||||||
|
l.Sort()
|
||||||
|
If Orig.HasValue AndAlso l(0).Value < Orig.Value.Value Then
|
||||||
|
Return P4K
|
||||||
|
ElseIf l(0).Data.IsEmptyString Then
|
||||||
|
Return P4K
|
||||||
|
Else
|
||||||
|
Return l(0).Data
|
||||||
|
End If
|
||||||
|
Else
|
||||||
|
Return P4K
|
||||||
|
End If
|
||||||
|
ElseIf Not w.Value({"original_info"}, "height").IsEmptyString Then
|
||||||
|
Return P4K
|
||||||
|
Else
|
||||||
|
Return String.Empty
|
||||||
|
End If
|
||||||
|
Catch ex As Exception
|
||||||
|
LogError(ex, "[API.Twitter.UserData.GetPictureOption]")
|
||||||
|
Return String.Empty
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "UrlFile"
|
||||||
Private Function UrlFile(ByVal URL As String) As String
|
Private Function UrlFile(ByVal URL As String) As String
|
||||||
Try
|
Try
|
||||||
Dim f As SFile = CStr(RegexReplace(LinkFormatterSecure(RegexReplace(URL.Replace("\", String.Empty), LinkPattern)), FilesPattern))
|
Dim f As SFile = CStr(RegexReplace(LinkFormatterSecure(RegexReplace(URL.Replace("\", String.Empty), LinkPattern)), FilesPattern))
|
||||||
@@ -338,6 +332,7 @@ Namespace API.Twitter
|
|||||||
End Try
|
End Try
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
|
#Region "Create media"
|
||||||
Private Shared Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
|
Private Shared Function MediaFromData(ByVal _URL As String, ByVal PostID As String, ByVal PostDate As String,
|
||||||
Optional ByVal _PictureOption As String = Nothing,
|
Optional ByVal _PictureOption As String = Nothing,
|
||||||
Optional ByVal State As UStates = UStates.Unknown) As UserMedia
|
Optional ByVal State As UStates = UStates.Unknown) As UserMedia
|
||||||
@@ -345,34 +340,43 @@ Namespace API.Twitter
|
|||||||
Dim m As New UserMedia(_URL) With {.PictureOption = _PictureOption, .Post = New UserPost With {.ID = PostID}}
|
Dim m As New UserMedia(_URL) With {.PictureOption = _PictureOption, .Post = New UserPost With {.ID = PostID}}
|
||||||
If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern))
|
If Not m.URL.IsEmptyString Then m.File = CStr(RegexReplace(m.URL, FilesPattern))
|
||||||
If Not m.PictureOption.IsEmptyString And Not m.File.IsEmptyString And Not m.URL.IsEmptyString Then
|
If Not m.PictureOption.IsEmptyString And Not m.File.IsEmptyString And Not m.URL.IsEmptyString Then
|
||||||
m.URL_BASE = $"{m.URL.Replace($".{m.File.Extension}", String.Empty)}?format={m.File.Extension}&name={m.PictureOption}"
|
m.URL = $"{m.URL.Replace($".{m.File.Extension}", String.Empty)}?format={m.File.Extension}&name={m.PictureOption}"
|
||||||
End If
|
End If
|
||||||
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, Declarations.DateProvider, Nothing) Else m.Post.Date = Nothing
|
If Not PostDate.IsEmptyString Then m.Post.Date = AConvert(Of Date)(PostDate, Declarations.DateProvider, Nothing) Else m.Post.Date = Nothing
|
||||||
m.State = State
|
m.State = State
|
||||||
Return m
|
Return m
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#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
|
||||||
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"
|
||||||
UserExists = False
|
Protected Overrides Function DownloadingException(ByVal ex As Exception, ByVal Message As String, Optional ByVal FromPE As Boolean = False,
|
||||||
ElseIf Responser.StatusCode = HttpStatusCode.Unauthorized Then
|
Optional ByVal EObj As Object = Nothing) As Integer
|
||||||
UserSuspended = True
|
With Responser
|
||||||
ElseIf Responser.StatusCode = HttpStatusCode.BadRequest Then
|
If .StatusCode = HttpStatusCode.NotFound Then
|
||||||
MyMainLOG = "Twitter has invalid credentials"
|
UserExists = False
|
||||||
ElseIf Responser.StatusCode = HttpStatusCode.ServiceUnavailable Then
|
ElseIf .StatusCode = HttpStatusCode.Unauthorized Then
|
||||||
MyMainLOG = $"Twitter is currently unavailable ({ToString()})"
|
UserSuspended = True
|
||||||
Else
|
ElseIf .StatusCode = HttpStatusCode.BadRequest Then
|
||||||
If Not FromPE Then LogError(ex, Message) : HasError = True
|
MyMainLOG = "Twitter has invalid credentials"
|
||||||
Return 0
|
ElseIf .StatusCode = HttpStatusCode.ServiceUnavailable Or .StatusCode = HttpStatusCode.InternalServerError Then
|
||||||
End If
|
MyMainLOG = $"[{CInt(.StatusCode)}] Twitter is currently unavailable ({ToString()})"
|
||||||
|
Else
|
||||||
|
If Not FromPE Then LogError(ex, Message) : HasError = True
|
||||||
|
Return 0
|
||||||
|
End If
|
||||||
|
End With
|
||||||
Return 1
|
Return 1
|
||||||
End Function
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "IDisposable support"
|
||||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||||
If Not disposedValue And disposing Then _DataNames.Clear()
|
If Not disposedValue And disposing Then _DataNames.Clear()
|
||||||
MyBase.Dispose(disposing)
|
MyBase.Dispose(disposing)
|
||||||
End Sub
|
End Sub
|
||||||
|
#End Region
|
||||||
End Class
|
End Class
|
||||||
End Namespace
|
End Namespace
|
||||||
@@ -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,17 +6,20 @@
|
|||||||
'
|
'
|
||||||
' 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.Functions.XML
|
Imports PersonalUtilities.Functions.XML
|
||||||
Imports PersonalUtilities.Functions.Messaging
|
Imports PersonalUtilities.Functions.Messaging
|
||||||
Imports System.Threading
|
|
||||||
Imports SCrawler.API.Base
|
|
||||||
Namespace API
|
Namespace API
|
||||||
Friend Class UserDataBind : Inherits UserDataBase : Implements ICollection(Of IUserData), IMyEnumerator(Of IUserData)
|
Friend Class UserDataBind : Inherits UserDataBase : Implements ICollection(Of IUserData), IMyEnumerator(Of IUserData)
|
||||||
|
#Region "Events"
|
||||||
Friend Event OnCollectionSelfRemoved(ByVal Collection As IUserData)
|
Friend Event OnCollectionSelfRemoved(ByVal Collection As IUserData)
|
||||||
Friend Event OnUserRemoved(ByVal User As IUserData)
|
Friend Event OnUserRemoved(ByVal User As IUserData)
|
||||||
|
#End Region
|
||||||
#Region "Declarations"
|
#Region "Declarations"
|
||||||
Friend ReadOnly Property Collections As List(Of IUserData)
|
Friend ReadOnly Property Collections As List(Of IUserData)
|
||||||
|
#Region "Base class overrides"
|
||||||
Private _CollectionName As String = String.Empty
|
Private _CollectionName As String = String.Empty
|
||||||
Friend Overrides Property CollectionName As String
|
Friend Overrides Property CollectionName As String
|
||||||
Get
|
Get
|
||||||
@@ -30,6 +33,10 @@ Namespace API
|
|||||||
ChangeCollectionName(NewName, True)
|
ChangeCollectionName(NewName, True)
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
|
Friend Overrides Sub ChangeCollectionName(ByVal NewName As String, ByVal UpdateSettings As Boolean)
|
||||||
|
_CollectionName = NewName
|
||||||
|
If Count > 0 Then Collections.ForEach(Sub(c) c.CollectionName = NewName)
|
||||||
|
End Sub
|
||||||
Friend Overrides Property Name As String
|
Friend Overrides Property Name As String
|
||||||
Get
|
Get
|
||||||
Return CollectionName
|
Return CollectionName
|
||||||
@@ -38,6 +45,21 @@ Namespace API
|
|||||||
CollectionName = NewCollectionName
|
CollectionName = NewCollectionName
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
|
Friend Overrides Property FriendlyName As String
|
||||||
|
Get
|
||||||
|
If Count > 0 Then
|
||||||
|
Return Collections(0).FriendlyName
|
||||||
|
Else
|
||||||
|
Return String.Empty
|
||||||
|
End If
|
||||||
|
End Get
|
||||||
|
Set(ByVal NewName As String)
|
||||||
|
If Count > 0 Then Collections.ForEach(Sub(c)
|
||||||
|
c.FriendlyName = NewName
|
||||||
|
c.UpdateUserInformation()
|
||||||
|
End Sub)
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
Friend Overrides Property UserExists As Boolean
|
Friend Overrides Property UserExists As Boolean
|
||||||
Get
|
Get
|
||||||
Return Count > 0 AndAlso Collections.Exists(Function(c) c.Exists)
|
Return Count > 0 AndAlso Collections.Exists(Function(c) c.Exists)
|
||||||
@@ -52,25 +74,6 @@ Namespace API
|
|||||||
Set(ByVal s As Boolean)
|
Set(ByVal s As Boolean)
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
Friend Overrides Sub ChangeCollectionName(ByVal NewName As String, ByVal UpdateSettings As Boolean)
|
|
||||||
_CollectionName = NewName
|
|
||||||
If Count > 0 Then Collections.ForEach(Sub(c) c.CollectionName = NewName)
|
|
||||||
End Sub
|
|
||||||
Friend Overrides Property FriendlyName As String
|
|
||||||
Get
|
|
||||||
If Count > 0 Then
|
|
||||||
Return Collections(0).FriendlyName
|
|
||||||
Else
|
|
||||||
Return String.Empty
|
|
||||||
End If
|
|
||||||
End Get
|
|
||||||
Set(ByVal NewName As String)
|
|
||||||
If Count > 0 Then Collections.ForEach(Sub(c)
|
|
||||||
c.FriendlyName = NewName
|
|
||||||
c.UpdateUserInformation()
|
|
||||||
End Sub)
|
|
||||||
End Set
|
|
||||||
End Property
|
|
||||||
#Region "Images"
|
#Region "Images"
|
||||||
Friend Overrides Sub SetPicture(ByVal f As SFile)
|
Friend Overrides Sub SetPicture(ByVal f As SFile)
|
||||||
If Count > 0 Then Collections.ForEach(Sub(c) c.SetPicture(f))
|
If Count > 0 Then Collections.ForEach(Sub(c) c.SetPicture(f))
|
||||||
@@ -97,15 +100,6 @@ Namespace API
|
|||||||
Return Count > 0 AndAlso Collections.Exists(Function(c) DirectCast(c, UserDataBase).ContentMissingExists)
|
Return Count > 0 AndAlso Collections.Exists(Function(c) DirectCast(c, UserDataBase).ContentMissingExists)
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
Friend ReadOnly Property Count As Integer Implements ICollection(Of IUserData).Count, IMyEnumerator(Of IUserData).MyEnumeratorCount
|
|
||||||
Get
|
|
||||||
If Collections Is Nothing Then
|
|
||||||
Return 0
|
|
||||||
Else
|
|
||||||
Return Collections.Count
|
|
||||||
End If
|
|
||||||
End Get
|
|
||||||
End Property
|
|
||||||
Friend Overrides Property MyFile As SFile
|
Friend Overrides Property MyFile As SFile
|
||||||
Get
|
Get
|
||||||
If Count > 0 Then Return Collections(0).File Else Return Nothing
|
If Count > 0 Then Return Collections(0).File Else Return Nothing
|
||||||
@@ -190,7 +184,7 @@ Namespace API
|
|||||||
End Property
|
End Property
|
||||||
Friend Overrides Function GetUserInformation() As String
|
Friend Overrides Function GetUserInformation() As String
|
||||||
Dim OutStr$ = String.Empty
|
Dim OutStr$ = String.Empty
|
||||||
If Count > 0 Then Collections.ForEach(Sub(c) OutStr.StringAppendLine(DirectCast(c, UserDataBase).GetUserInformation(), $"{vbCrLf}{vbCrLf}"))
|
If Count > 0 Then Collections.ForEach(Sub(c) OutStr.StringAppendLine(DirectCast(c, UserDataBase).GetUserInformation(), vbNewLine.StringDup(2)))
|
||||||
Return OutStr
|
Return OutStr
|
||||||
End Function
|
End Function
|
||||||
Friend Overrides Property LastUpdated As Date?
|
Friend Overrides Property LastUpdated As Date?
|
||||||
@@ -224,6 +218,7 @@ Namespace API
|
|||||||
End Sub)
|
End Sub)
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
|
#End Region
|
||||||
#Region "Context buttons"
|
#Region "Context buttons"
|
||||||
Friend ReadOnly Property ContextDown As ToolStripMenuItem()
|
Friend ReadOnly Property ContextDown As ToolStripMenuItem()
|
||||||
Get
|
Get
|
||||||
@@ -276,7 +271,6 @@ Namespace API
|
|||||||
Friend Sub New()
|
Friend Sub New()
|
||||||
_IsCollection = True
|
_IsCollection = True
|
||||||
Collections = New List(Of IUserData)
|
Collections = New List(Of IUserData)
|
||||||
'ImageHandler(Me, True)
|
|
||||||
End Sub
|
End Sub
|
||||||
Friend Sub New(ByVal _Name As String)
|
Friend Sub New(ByVal _Name As String)
|
||||||
Me.New
|
Me.New
|
||||||
@@ -299,24 +293,48 @@ Namespace API
|
|||||||
#Region "Download"
|
#Region "Download"
|
||||||
Friend Overrides Property DownloadTopCount As Integer?
|
Friend Overrides Property DownloadTopCount As Integer?
|
||||||
Get
|
Get
|
||||||
If Count > 0 Then
|
Return If(Count > 0, Item(0).DownloadTopCount, Nothing)
|
||||||
Return Collections(0).DownloadTopCount
|
|
||||||
Else
|
|
||||||
Return Nothing
|
|
||||||
End If
|
|
||||||
End Get
|
End Get
|
||||||
Set(ByVal NewLimit As Integer?)
|
Set(ByVal NewLimit As Integer?)
|
||||||
If Count > 0 Then Collections.ForEach(Sub(c) c.DownloadTopCount = NewLimit)
|
If Count > 0 Then Collections.ForEach(Sub(c) c.DownloadTopCount = NewLimit)
|
||||||
End Set
|
End Set
|
||||||
End Property
|
End Property
|
||||||
|
Friend Overrides Property IncludeInTheFeed As Boolean
|
||||||
|
Get
|
||||||
|
Return If(Count > 0, DirectCast(Item(0), UserDataBase).IncludeInTheFeed, Nothing)
|
||||||
|
End Get
|
||||||
|
Set(ByVal Include As Boolean)
|
||||||
|
If Count > 0 Then Collections.ForEach(Sub(c) DirectCast(c, UserDataBase).IncludeInTheFeed = Include)
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
Friend Overrides Property DownloadDateFrom As Date?
|
||||||
|
Get
|
||||||
|
Return If(Count > 0, Item(0).DownloadDateFrom, Nothing)
|
||||||
|
End Get
|
||||||
|
Set(ByVal d As Date?)
|
||||||
|
If Count > 0 Then Collections.ForEach(Sub(c) c.DownloadDateFrom = d)
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
|
Friend Overrides Property DownloadDateTo As Date?
|
||||||
|
Get
|
||||||
|
Return If(Count > 0, Item(0).DownloadDateTo, Nothing)
|
||||||
|
End Get
|
||||||
|
Set(ByVal d As Date?)
|
||||||
|
If Count > 0 Then Collections.ForEach(Sub(c) c.DownloadDateTo = d)
|
||||||
|
End Set
|
||||||
|
End Property
|
||||||
Friend Overrides Sub DownloadData(ByVal Token As CancellationToken)
|
Friend Overrides Sub DownloadData(ByVal Token As CancellationToken)
|
||||||
If Count > 0 Then Downloader.AddRange(Collections)
|
If Count > 0 Then Downloader.AddRange(Collections, True)
|
||||||
|
End Sub
|
||||||
|
Friend Overloads Sub DownloadData(ByVal Token As CancellationToken, ByVal __IncludedInTheFeed As Boolean)
|
||||||
|
If Count > 0 Then Downloader.AddRange(Collections, __IncludedInTheFeed)
|
||||||
End Sub
|
End Sub
|
||||||
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadDataF(ByVal Token As CancellationToken)
|
||||||
End Sub
|
End Sub
|
||||||
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
Protected Overrides Sub DownloadContent(ByVal Token As CancellationToken)
|
||||||
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,
|
||||||
|
Optional ByVal s As Object = Nothing) As Integer
|
||||||
Return 0
|
Return 0
|
||||||
End Function
|
End Function
|
||||||
Private Sub User_OnUserUpdated(ByVal User As IUserData)
|
Private Sub User_OnUserUpdated(ByVal User As IUserData)
|
||||||
@@ -336,16 +354,35 @@ Namespace API
|
|||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
#Region "ICollection Support"
|
#Region "ICollection Support"
|
||||||
Default Friend ReadOnly Property Item(ByVal Index As Integer) As IUserData Implements IMyEnumerator(Of IUserData).MyEnumeratorObject
|
|
||||||
Get
|
|
||||||
Return Collections(Index)
|
|
||||||
End Get
|
|
||||||
End Property
|
|
||||||
Private ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of IUserData).IsReadOnly
|
Private ReadOnly Property IsReadOnly As Boolean Implements ICollection(Of IUserData).IsReadOnly
|
||||||
Get
|
Get
|
||||||
Return False
|
Return False
|
||||||
End Get
|
End Get
|
||||||
End Property
|
End Property
|
||||||
|
Private Sub CopyTo(ByVal _Array() As IUserData, ByVal _ArrayIndex As Integer) Implements ICollection(Of IUserData).CopyTo
|
||||||
|
Throw New NotImplementedException("The [CopyTo] method is not supported in a collection context")
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "Item, Count, Clear"
|
||||||
|
Default Friend ReadOnly Property Item(ByVal Index As Integer) As IUserData Implements IMyEnumerator(Of IUserData).MyEnumeratorObject
|
||||||
|
Get
|
||||||
|
Return Collections(Index)
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Friend ReadOnly Property Count As Integer Implements ICollection(Of IUserData).Count, IMyEnumerator(Of IUserData).MyEnumeratorCount
|
||||||
|
Get
|
||||||
|
If Collections Is Nothing Then
|
||||||
|
Return 0
|
||||||
|
Else
|
||||||
|
Return Collections.Count
|
||||||
|
End If
|
||||||
|
End Get
|
||||||
|
End Property
|
||||||
|
Friend Sub Clear() Implements ICollection(Of IUserData).Clear
|
||||||
|
Collections.ListClearDispose
|
||||||
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "Add"
|
||||||
''' <exception cref="InvalidOperationException"></exception>
|
''' <exception cref="InvalidOperationException"></exception>
|
||||||
Friend Overloads Sub Add(ByVal _Item As IUserData) Implements ICollection(Of IUserData).Add
|
Friend Overloads Sub Add(ByVal _Item As IUserData) Implements ICollection(Of IUserData).Add
|
||||||
With _Item
|
With _Item
|
||||||
@@ -388,7 +425,7 @@ Namespace API
|
|||||||
Try
|
Try
|
||||||
With DirectCast(User, UserDataBase)
|
With DirectCast(User, UserDataBase)
|
||||||
If IsAdd Then
|
If IsAdd Then
|
||||||
.CreateButtons(Count - 1)
|
.CreateButtons()
|
||||||
AddHandler .BTT_CONTEXT_DELETE.Click, AddressOf DeleteRemoveUserFromCollection
|
AddHandler .BTT_CONTEXT_DELETE.Click, AddressOf DeleteRemoveUserFromCollection
|
||||||
Else
|
Else
|
||||||
RemoveHandler .BTT_CONTEXT_DELETE.Click, AddressOf DeleteRemoveUserFromCollection
|
RemoveHandler .BTT_CONTEXT_DELETE.Click, AddressOf DeleteRemoveUserFromCollection
|
||||||
@@ -413,6 +450,8 @@ Namespace API
|
|||||||
For i% = 0 To _Items.Count - 1 : Add(_Items(i)) : Next
|
For i% = 0 To _Items.Count - 1 : Add(_Items(i)) : Next
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "Move, Merge"
|
||||||
Friend Overrides Function MoveFiles(ByVal __CollectionName As String) As Boolean
|
Friend Overrides Function MoveFiles(ByVal __CollectionName As String) As Boolean
|
||||||
Throw New NotImplementedException("Move files is not available in the collection context")
|
Throw New NotImplementedException("Move files is not available in the collection context")
|
||||||
End Function
|
End Function
|
||||||
@@ -440,15 +479,8 @@ Namespace API
|
|||||||
End If
|
End If
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Friend Sub Clear() Implements ICollection(Of IUserData).Clear
|
#End Region
|
||||||
Collections.ListClearDispose
|
#Region "Remove, Delete"
|
||||||
End Sub
|
|
||||||
Friend Function Contains(ByVal _Item As IUserData) As Boolean Implements ICollection(Of IUserData).Contains
|
|
||||||
Return Count > 0 AndAlso Collections.Contains(_Item)
|
|
||||||
End Function
|
|
||||||
Private Sub CopyTo(ByVal _Array() As IUserData, ByVal _ArrayIndex As Integer) Implements ICollection(Of IUserData).CopyTo
|
|
||||||
Throw New NotImplementedException("[CopyTo] method does not supported in collections context")
|
|
||||||
End Sub
|
|
||||||
Friend Function Remove(ByVal _Item As IUserData) As Boolean Implements ICollection(Of IUserData).Remove
|
Friend Function Remove(ByVal _Item As IUserData) As Boolean Implements ICollection(Of IUserData).Remove
|
||||||
If DataMerging Then
|
If DataMerging Then
|
||||||
MsgBoxE($"Collection [{CollectionName}] data is already merged" & vbCr &
|
MsgBoxE($"Collection [{CollectionName}] data is already merged" & vbCr &
|
||||||
@@ -463,90 +495,103 @@ Namespace API
|
|||||||
Return Collections.Remove(_Item)
|
Return Collections.Remove(_Item)
|
||||||
End If
|
End If
|
||||||
End Function
|
End Function
|
||||||
Friend Overrides Function Delete() As Integer
|
Friend Overrides Function Delete(Optional ByVal Multiple As Boolean = False) As Integer
|
||||||
If Count > 0 Then
|
If Count > 0 Then
|
||||||
|
Const MsgTitle$ = "Deleting a collection"
|
||||||
Dim f As SFile
|
Dim f As SFile
|
||||||
If MsgBoxE({$"Collection may contain data{vbCr}Do you really want to delete collection and all of it files?", "Collection deleting"},
|
Dim m As New MMessage($"Collection [{CollectionName} (number of profiles: {Count})] may contain data" & vbCr &
|
||||||
MsgBoxStyle.Exclamation + MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
|
"Are you sure you want to delete the collection and all of its files?", MsgTitle,
|
||||||
f = Collections(0).File.CutPath(IIf(DataMerging, 1, 2)).PathWithSeparator
|
{New MsgBoxButton("Delete") With {.ToolTip = "Delete the collection and all files"},
|
||||||
Settings.Users.Remove(Me)
|
New MsgBoxButton("Split") With {
|
||||||
Collections.ForEach(Sub(c) c.Delete())
|
.ToolTip = "Users will be removed from the collection and will be displayed in the program as separate users." & vbCr &
|
||||||
Downloader.UserRemove(Me)
|
"All user data will remain."},
|
||||||
MainFrameObj.ImageHandler(Me, False)
|
"Cancel"}, vbExclamation)
|
||||||
Collections.ListClearDispose
|
Select Case If(Multiple, 0, MsgBoxE(m).Index)
|
||||||
Dispose(False)
|
Case 0
|
||||||
f.Delete(SFO.Path, SFODelete.EmptyOnly + Settings.DeleteMode, EDP.SendInLog)
|
f = Collections(0).File.CutPath(IIf(DataMerging, 1, 2)).PathWithSeparator
|
||||||
Return 2
|
|
||||||
Else
|
|
||||||
If DataMerging Then
|
|
||||||
MsgBoxE($"Collection [{CollectionName}] data are already merged{vbCr}Cannot split merged collection{vbCr}Operation canceled", MsgBoxStyle.Exclamation)
|
|
||||||
Return 0
|
|
||||||
End If
|
|
||||||
If MsgBoxE({"Do you want to delete only the collection and split users' profiles??" & vbCr &
|
|
||||||
"Users will be removed from the collection and split by sites." & vbCr &
|
|
||||||
"All user data will remain.", "Collection deleting"},
|
|
||||||
MsgBoxStyle.Question + MsgBoxStyle.YesNo) = MsgBoxResult.Yes Then
|
|
||||||
f = Collections(0).File.CutPath(2)
|
|
||||||
Settings.Users.Remove(Me)
|
Settings.Users.Remove(Me)
|
||||||
Collections.ForEach(Sub(c)
|
Collections.ForEach(Sub(c) c.Delete())
|
||||||
c.MoveFiles(String.Empty)
|
|
||||||
MainFrameObj.ImageHandler(c)
|
|
||||||
End Sub)
|
|
||||||
Collections.Clear()
|
|
||||||
f.Delete(SFO.Path, SFODelete.Default + Settings.DeleteMode, EDP.SendInLog)
|
|
||||||
Downloader.UserRemove(Me)
|
Downloader.UserRemove(Me)
|
||||||
MainFrameObj.ImageHandler(Me, False)
|
MainFrameObj.ImageHandler(Me, False)
|
||||||
|
Collections.ListClearDispose
|
||||||
Dispose(False)
|
Dispose(False)
|
||||||
Return 3
|
f.Delete(SFO.Path, SFODelete.EmptyOnly + Settings.DeleteMode, EDP.SendInLog)
|
||||||
Else
|
Return 2
|
||||||
MsgBoxE("Operation canceled")
|
Case 1
|
||||||
End If
|
If DataMerging Then
|
||||||
End If
|
MsgBoxE({$"Collection [{CollectionName}] data merged{vbCr}Unable to split merged collection{vbCr}Operation canceled", MsgTitle}, vbExclamation)
|
||||||
|
Return 0
|
||||||
|
Else
|
||||||
|
f = Collections(0).File.CutPath(2)
|
||||||
|
Settings.Users.Remove(Me)
|
||||||
|
Collections.ForEach(Sub(c)
|
||||||
|
c.MoveFiles(String.Empty)
|
||||||
|
MainFrameObj.ImageHandler(c)
|
||||||
|
End Sub)
|
||||||
|
Collections.Clear()
|
||||||
|
f.Delete(SFO.Path, SFODelete.Default + Settings.DeleteMode, EDP.SendInLog)
|
||||||
|
Downloader.UserRemove(Me)
|
||||||
|
MainFrameObj.ImageHandler(Me, False)
|
||||||
|
Dispose(False)
|
||||||
|
Return 3
|
||||||
|
End If
|
||||||
|
Case Else : If Not Multiple Then MsgBoxE({"Operation canceled", MsgTitle})
|
||||||
|
End Select
|
||||||
End If
|
End If
|
||||||
Return 0
|
Return 0
|
||||||
End Function
|
End Function
|
||||||
Private Sub DeleteRemoveUserFromCollection(sender As Object, e As EventArgs)
|
Private Sub DeleteRemoveUserFromCollection(sender As Object, e As EventArgs)
|
||||||
With DirectCast(sender, ToolStripMenuItem)
|
Dim obj As IUserData = DirectCast(sender, ToolStripMenuItem).Tag
|
||||||
Dim i% = AConvert(Of Integer)(.Tag, -1)
|
Dim i% = Collections.IndexOf(obj)
|
||||||
If i >= 0 Then
|
If i >= 0 Then
|
||||||
Dim n$ = Collections(i).Name
|
Dim n$ = Collections(i).Name
|
||||||
Dim s$ = Collections(i).Site.ToString
|
Dim s$ = Collections(i).Site.ToString
|
||||||
Dim RemoveMeIfNull As Action = Sub()
|
Dim RemoveMeIfNull As Action = Sub()
|
||||||
If Count = 0 Then
|
If Count = 0 Then
|
||||||
Settings.Users.Remove(Me)
|
Settings.Users.Remove(Me)
|
||||||
MainFrameObj.ImageHandler(Me, False)
|
MainFrameObj.ImageHandler(Me, False)
|
||||||
RaiseEvent OnCollectionSelfRemoved(Me)
|
RaiseEvent OnCollectionSelfRemoved(Me)
|
||||||
Dispose(False)
|
Dispose(False)
|
||||||
End If
|
End If
|
||||||
End Sub
|
End Sub
|
||||||
Select Case MsgBoxE({$"Are you sure you want to remove user profile [{n}] of site [{s}] from collection [{Name}]?" & vbCr &
|
Select Case MsgBoxE({$"Are you sure you want to remove user profile [{n}] of site [{s}] from collection [{Name}]?" & vbCr &
|
||||||
"You can remove a user from the collection while keeping data (Remove) or deleting the data (Delete)" & vbCr &
|
"You can remove a user from the collection while keeping data (Remove) or deleting the data (Delete)" & vbCr &
|
||||||
"Deleting this profile will remove it from the collection and all its data will be erased." & vbCr &
|
"Deleting this profile will remove it from the collection and all its data will be erased." & vbCr &
|
||||||
"Removing this profile will remove it from the collection and all its data will remain." &
|
"Removing this profile will remove it from the collection and all its data will remain." &
|
||||||
"This user will still appear in the program, but not in the collection.",
|
"This user will still appear in the program, but not in the collection.",
|
||||||
"Deleting a user"}, vbExclamation,,,
|
"Deleting a user"}, vbExclamation,,,
|
||||||
{
|
{
|
||||||
New MsgBoxButton("Remove") With {
|
New MsgBoxButton("Remove") With {
|
||||||
.ToolTip = "Remove a user from the collection only. All its data will remain. The user will appear in the program."},
|
.ToolTip = "Remove a user from the collection only. All its data will remain. The user will appear in the program."},
|
||||||
New MsgBoxButton("Delete") With {
|
New MsgBoxButton("Delete") With {
|
||||||
.ToolTip = "Delete a user from the collection and erase their data."},
|
.ToolTip = "Delete a user from the collection and erase their data."},
|
||||||
"Cancel"
|
"Cancel"
|
||||||
}).Index
|
}).Index
|
||||||
Case 0
|
Case 0
|
||||||
Remove(Collections(i))
|
Remove(Collections(i))
|
||||||
MsgBoxE($"User [{s} - {n}] has been removed from the collection. Now it should be displayed in the program.")
|
MsgBoxE($"User [{s} - {n}] has been removed from the collection. Now it should be displayed in the program.")
|
||||||
RemoveMeIfNull.Invoke
|
RemoveMeIfNull.Invoke
|
||||||
Case 1
|
Case 1
|
||||||
Collections(i).Delete()
|
Collections(i).Delete()
|
||||||
Collections(i).Dispose()
|
Collections(i).Dispose()
|
||||||
Collections.RemoveAt(i)
|
Collections.RemoveAt(i)
|
||||||
MsgBoxE($"User profile [{n}] of site [{s}] has been deleted")
|
MsgBoxE($"User profile [{n}] of site [{s}] has been deleted")
|
||||||
RemoveMeIfNull.Invoke
|
RemoveMeIfNull.Invoke
|
||||||
Case Else : MsgBoxE("Operation canceled")
|
Case Else : MsgBoxE("Operation canceled")
|
||||||
End Select
|
End Select
|
||||||
End If
|
End If
|
||||||
End With
|
|
||||||
End Sub
|
End Sub
|
||||||
|
#End Region
|
||||||
|
#Region "Copy"
|
||||||
|
Friend Overrides Function CopyFiles(ByVal DestinationPath As SFile, Optional ByVal e As ErrorsDescriber = Nothing) As Boolean
|
||||||
|
Return Count > 0 AndAlso Collections(0).CopyFiles(DestinationPath, e)
|
||||||
|
End Function
|
||||||
|
#End Region
|
||||||
|
#Region "Contains"
|
||||||
|
Friend Function Contains(ByVal _Item As IUserData) As Boolean Implements ICollection(Of IUserData).Contains
|
||||||
|
Return Count > 0 AndAlso Collections.Contains(_Item)
|
||||||
|
End Function
|
||||||
|
#End Region
|
||||||
#Region "IEnumerable Support"
|
#Region "IEnumerable Support"
|
||||||
Private Function GetEnumerator() As IEnumerator(Of IUserData) Implements IEnumerable(Of IUserData).GetEnumerator
|
Private Function GetEnumerator() As IEnumerator(Of IUserData) Implements IEnumerable(Of IUserData).GetEnumerator
|
||||||
Return New MyEnumerator(Of IUserData)(Me)
|
Return New MyEnumerator(Of IUserData)(Me)
|
||||||
@@ -555,19 +600,18 @@ Namespace API
|
|||||||
Return GetEnumerator()
|
Return GetEnumerator()
|
||||||
End Function
|
End Function
|
||||||
#End Region
|
#End Region
|
||||||
#End Region
|
#Region "IEquatable support"
|
||||||
Friend Overrides Function Equals(ByVal Other As UserDataBase) As Boolean
|
Friend Overrides Function Equals(ByVal Other As UserDataBase) As Boolean
|
||||||
If Other.IsCollection Then
|
If Other.IsCollection Then
|
||||||
Return CollectionName = Other.CollectionName
|
Return CollectionName = Other.CollectionName
|
||||||
Else
|
Else
|
||||||
Return Count > 0 AndAlso Collections.Exists(Function(u) u.Equals(Other))
|
Return False
|
||||||
End If
|
End If
|
||||||
End Function
|
End Function
|
||||||
|
#End Region
|
||||||
#Region "IDisposable Support"
|
#Region "IDisposable Support"
|
||||||
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||||
If Not disposedValue Then
|
If Not disposedValue And disposing Then Collections.ListClearDispose
|
||||||
If disposing Then Collections.ListClearDispose
|
|
||||||
End If
|
|
||||||
MyBase.Dispose(disposing)
|
MyBase.Dispose(disposing)
|
||||||
End Sub
|
End Sub
|
||||||
#End Region
|
#End Region
|
||||||
|
|||||||
19
SCrawler/API/XVIDEOS/Declarations.vb
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
' 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.XVIDEOS
|
||||||
|
Friend Module Declarations
|
||||||
|
Friend Const XvideosSiteKey As String = "AndyProgram_XVIDEOS"
|
||||||
|
Friend ReadOnly Property M3U8Regex As RParams = RParams.DM("http.+?.m3u8.*?(?=')", 0)
|
||||||
|
Friend ReadOnly Property VideoTitleRegex As RParams = RParams.DMS("html5player.setVideoTitle\('(.+)(?='\);)", 1)
|
||||||
|
Friend ReadOnly Property VideoID As RParams = RParams.DMS(".*?www.xvideos.com/(video\d+).*", 1)
|
||||||
|
Friend ReadOnly Property M3U8Reparse As RParams = RParams.DM("NAME=""(\d+).*?""[\r\n]*?(.+)(?=(|[\r\n]+?))", 0, RegexReturn.List)
|
||||||
|
Friend ReadOnly Property M3U8Appender As RParams = RParams.DM("(.+)(?=/.+?\.m3u8.*?)", 0)
|
||||||
|
End Module
|
||||||
|
End Namespace
|
||||||
66
SCrawler/API/XVIDEOS/M3U8.vb
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
' 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.Net
|
||||||
|
Imports PersonalUtilities.Tools.WEB
|
||||||
|
Namespace API.XVIDEOS
|
||||||
|
Friend NotInheritable Class M3U8
|
||||||
|
Private Sub New()
|
||||||
|
End Sub
|
||||||
|
Private Shared Function Save(ByVal URLs As List(Of String), ByVal ffmpegFile As SFile, ByVal f As SFile) As SFile
|
||||||
|
Dim CachePath As SFile = Nothing
|
||||||
|
Try
|
||||||
|
If URLs.ListExists Then
|
||||||
|
Dim ConcatFile As SFile = f
|
||||||
|
If ConcatFile.Name.IsEmptyString Then ConcatFile.Name = "PlayListFile"
|
||||||
|
ConcatFile.Extension = "mp4"
|
||||||
|
CachePath = $"{f.PathWithSeparator}_Cache\{SFile.GetDirectories($"{f.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 WebClient
|
||||||
|
For i = 0 To URLs.Count - 1
|
||||||
|
dFile.Name = $"ConPart_{i}"
|
||||||
|
w.DownloadFile(URLs(i), dFile)
|
||||||
|
eFiles.Add(dFile)
|
||||||
|
Next
|
||||||
|
End Using
|
||||||
|
f = FFMPEG.ConcatenateFiles(eFiles, ffmpegFile, ConcatFile, p, EDP.ThrowException)
|
||||||
|
eFiles.Clear()
|
||||||
|
Return f
|
||||||
|
End If
|
||||||
|
End If
|
||||||
|
Return Nothing
|
||||||
|
Finally
|
||||||
|
CachePath.Delete(SFO.Path, SFODelete.None, EDP.None)
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
|
Friend Shared Function Download(ByVal URL As String, ByVal Appender As String, ByVal ffmpegFile As SFile, ByVal f As SFile) As SFile
|
||||||
|
Try
|
||||||
|
If Not URL.IsEmptyString Then
|
||||||
|
Using w As New WebClient
|
||||||
|
Dim r$ = w.DownloadString(URL)
|
||||||
|
If Not r.IsEmptyString Then
|
||||||
|
Dim l As List(Of String) = ListAddList(Nothing, r.StringFormatLines.StringToList(Of String)(vbNewLine).ListWithRemove(Function(v) v.Trim.StartsWith("#")),
|
||||||
|
New ListAddParams With {.Converter = Function(Input) $"{Appender}/{Input.ToString.Trim}"})
|
||||||
|
If l.ListExists Then Return Save(l, ffmpegFile, f)
|
||||||
|
End If
|
||||||
|
End Using
|
||||||
|
End If
|
||||||
|
Return Nothing
|
||||||
|
Catch ex As Exception
|
||||||
|
ErrorsDescriber.Execute(EDP.SendInLog, ex, $"[M3U8.Download({URL}, {Appender}, {ffmpegFile}, {f})]")
|
||||||
|
Throw ex
|
||||||
|
End Try
|
||||||
|
End Function
|
||||||
|
End Class
|
||||||
|
End Namespace
|
||||||
80
SCrawler/API/XVIDEOS/SettingsForm.Designer.vb
generated
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
' 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.XVIDEOS
|
||||||
|
<Global.Microsoft.VisualBasic.CompilerServices.DesignerGenerated()>
|
||||||
|
Partial Friend Class SettingsForm : Inherits System.Windows.Forms.Form
|
||||||
|
<System.Diagnostics.DebuggerNonUserCode()>
|
||||||
|
Protected Overrides Sub Dispose(ByVal disposing As Boolean)
|
||||||
|
Try
|
||||||
|
If disposing AndAlso components IsNot Nothing Then
|
||||||
|
components.Dispose()
|
||||||
|
End If
|
||||||
|
Finally
|
||||||
|
MyBase.Dispose(disposing)
|
||||||
|
End Try
|
||||||
|
End Sub
|
||||||
|
Private components As System.ComponentModel.IContainer
|
||||||
|
<System.Diagnostics.DebuggerStepThrough()>
|
||||||
|
Private Sub InitializeComponent()
|
||||||
|
Dim CONTAINER_MAIN As System.Windows.Forms.ToolStripContainer
|
||||||
|
Me.LIST_DOMAINS = New System.Windows.Forms.ListBox()
|
||||||
|
CONTAINER_MAIN = New System.Windows.Forms.ToolStripContainer()
|
||||||
|
CONTAINER_MAIN.ContentPanel.SuspendLayout()
|
||||||
|
CONTAINER_MAIN.SuspendLayout()
|
||||||
|
Me.SuspendLayout()
|
||||||
|
'
|
||||||
|
'CONTAINER_MAIN
|
||||||
|
'
|
||||||
|
'
|
||||||
|
'CONTAINER_MAIN.ContentPanel
|
||||||
|
'
|
||||||
|
CONTAINER_MAIN.ContentPanel.Controls.Add(Me.LIST_DOMAINS)
|
||||||
|
CONTAINER_MAIN.ContentPanel.Size = New System.Drawing.Size(384, 241)
|
||||||
|
CONTAINER_MAIN.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
|
CONTAINER_MAIN.LeftToolStripPanelVisible = False
|
||||||
|
CONTAINER_MAIN.Location = New System.Drawing.Point(0, 0)
|
||||||
|
CONTAINER_MAIN.Name = "CONTAINER_MAIN"
|
||||||
|
CONTAINER_MAIN.RightToolStripPanelVisible = False
|
||||||
|
CONTAINER_MAIN.Size = New System.Drawing.Size(384, 291)
|
||||||
|
CONTAINER_MAIN.TabIndex = 0
|
||||||
|
'
|
||||||
|
'LIST_DOMAINS
|
||||||
|
'
|
||||||
|
Me.LIST_DOMAINS.Dock = System.Windows.Forms.DockStyle.Fill
|
||||||
|
Me.LIST_DOMAINS.FormattingEnabled = True
|
||||||
|
Me.LIST_DOMAINS.Location = New System.Drawing.Point(0, 0)
|
||||||
|
Me.LIST_DOMAINS.Name = "LIST_DOMAINS"
|
||||||
|
Me.LIST_DOMAINS.Size = New System.Drawing.Size(384, 241)
|
||||||
|
Me.LIST_DOMAINS.TabIndex = 0
|
||||||
|
'
|
||||||
|
'SettingsForm
|
||||||
|
'
|
||||||
|
Me.AutoScaleDimensions = New System.Drawing.SizeF(6.0!, 13.0!)
|
||||||
|
Me.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font
|
||||||
|
Me.ClientSize = New System.Drawing.Size(384, 291)
|
||||||
|
Me.Controls.Add(CONTAINER_MAIN)
|
||||||
|
Me.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle
|
||||||
|
Me.Icon = Global.SCrawler.My.Resources.SiteResources.XvideosIcon_48
|
||||||
|
Me.KeyPreview = True
|
||||||
|
Me.MaximizeBox = False
|
||||||
|
Me.MaximumSize = New System.Drawing.Size(400, 330)
|
||||||
|
Me.MinimizeBox = False
|
||||||
|
Me.MinimumSize = New System.Drawing.Size(400, 330)
|
||||||
|
Me.Name = "SettingsForm"
|
||||||
|
Me.SizeGripStyle = System.Windows.Forms.SizeGripStyle.Hide
|
||||||
|
Me.Text = "Settings"
|
||||||
|
CONTAINER_MAIN.ContentPanel.ResumeLayout(False)
|
||||||
|
CONTAINER_MAIN.ResumeLayout(False)
|
||||||
|
CONTAINER_MAIN.PerformLayout()
|
||||||
|
Me.ResumeLayout(False)
|
||||||
|
|
||||||
|
End Sub
|
||||||
|
Private WithEvents LIST_DOMAINS As Windows.Forms.ListBox
|
||||||
|
End Class
|
||||||
|
End Namespace
|
||||||