├── .editorconfig
├── .github
└── FUNDING.yml
├── .gitignore
├── .vsconfig
├── Assets
├── Icon.ico
├── Icon.png
├── Legacy1_CommandLine.md
├── Legacy1_Configuration.md
├── Legacy1_Home.md
├── Legacy1_Interface.md
├── Legacy2_Usage.md
├── SmartImage icon.psd
├── Usage.md
└── Wiki_TOC_Old.md
├── Directory.Build.props
├── Examples
├── Config.png
├── Demo 1.gif
├── Main menu 2.png
├── Main menu diagram.png
├── Main menu.png
├── Main menu.psd
├── Old
│ ├── Config (2).png
│ ├── Config.png
│ ├── Context menu integration.png
│ ├── Demo 1-old.gif
│ ├── Demo 1.gif
│ ├── Demo 2.gif
│ ├── Demo 3.gif
│ ├── Example search results.png
│ ├── Main menu (2).png
│ ├── Main menu.png
│ ├── Rdx help.png
│ ├── Results 1.png
│ └── Results 2.png
├── Rdx context menu.png
├── Rdx help.png
├── Results 1.png
└── ShareX.png
├── LICENSE
├── NuGet.config
├── README.md
├── SmartImage.Lib
├── Clients
│ ├── AnilistClient.cs
│ ├── Booru
│ │ ├── BaseBooruClient.cs
│ │ ├── BaseGelbooruClient.cs
│ │ └── Rule34Booru.cs
│ ├── FlareSolverrClient.cs
│ └── HydrusClient.cs
├── Cookies
│ ├── BrowserCookiesProvider.cs
│ ├── DefaultCookiesProvider.cs
│ ├── ICookiesEngine.cs
│ └── ICookiesProvider.cs
├── Engines
│ ├── BaseSearchEngine.cs
│ ├── IEndpointEngine.cs
│ ├── Impl
│ │ ├── Search
│ │ │ ├── ArchiveMoeEngine.cs
│ │ │ ├── Ascii2DEngine.cs
│ │ │ ├── EHentaiEngine.cs
│ │ │ ├── FluffleEngine.cs
│ │ │ ├── GoogleLensEngine.cs
│ │ │ ├── Iqdb3DEngine.cs
│ │ │ ├── IqdbEngine.cs
│ │ │ ├── Other
│ │ │ │ ├── BingEngine.cs
│ │ │ │ ├── GoogleImagesEngine.cs
│ │ │ │ ├── ImgOpsEngine.cs
│ │ │ │ └── KarmaDecayEngine.cs
│ │ │ ├── RepostSleuthEngine.cs
│ │ │ ├── SauceNaoEngine.cs
│ │ │ ├── TinEyeEngine.cs
│ │ │ ├── TraceMoeEngine.cs
│ │ │ └── YandexEngine.cs
│ │ └── Upload
│ │ │ ├── BaseUploadEngine.cs
│ │ │ ├── CatboxEngine.cs
│ │ │ ├── LitterboxEngine.cs
│ │ │ ├── PomfEngine.cs
│ │ │ └── UploadEngineOptions.cs
│ ├── SearchEngineOptions.cs
│ └── WebSearchEngine.cs
├── Images
│ ├── ImageScanner.cs
│ └── Uni
│ │ ├── UniImage.cs
│ │ ├── UniImageFile.cs
│ │ ├── UniImageStream.cs
│ │ └── UniImageUri.cs
├── Resources.Designer.cs
├── Resources.resx
├── Results
│ ├── Data
│ │ ├── IHashable.cs
│ │ ├── IItemConvertable.cs
│ │ ├── IItemConverter.cs
│ │ ├── ISearchConfigReceiver.cs
│ │ ├── ISimilarity.cs
│ │ └── ISize.cs
│ ├── SearchResult.cs
│ ├── SearchResultItem.cs
│ └── UploadResult.cs
├── SearchClient.cs
├── SearchConfig.cs
├── SearchQuery.cs
├── Serialization.Designer.cs
├── Serialization.resx
├── SmartImage.Lib.csproj
├── SmartImage.Lib.csproj.DotSettings
└── Utilities
│ ├── BaseSearchEngineTypeConverter.cs
│ ├── Diagnostics
│ ├── AppSupport.cs
│ ├── HttpLoggingHandler.cs
│ └── SmartImageException.cs
│ ├── FieldValueMap.cs
│ ├── Integration
│ ├── BaseOSIntegration.cs
│ ├── LinuxOSIntegration.cs
│ └── WindowsOSIntegration.cs
│ ├── NodeHelper.cs
│ ├── SearchResultTypeConverter.cs
│ └── SearchUtil.cs
├── SmartImage.Rdx
├── Commands
│ ├── CommonCommandSettings.cs
│ ├── IntegrationCommand.cs
│ ├── IntegrationCommandSettings.cs
│ ├── SearchCommand.cs
│ ├── SearchCommandSettings.cs
│ ├── SearchServerResponse.cs
│ ├── ServerCommand.cs
│ └── ServerCommandSettings.cs
├── Icon.ico
├── Program.cs
├── Resources.Designer.cs
├── Resources.resx
├── Resources
│ ├── Cybermedium.flf
│ ├── Santa Clara.flf
│ └── larry3d.flf
├── Shell
│ ├── ColorUtil.cs
│ ├── ConsoleFormat.cs
│ ├── ConsoleUtil.cs
│ └── CustomHelpProvider.cs
├── SmartImage.Rdx.csproj
└── Utilities
│ ├── TypeRegistrar.cs
│ └── TypeResolver.cs
├── SmartImage.UI
├── App.xaml
├── App.xaml.cs
├── AssemblyInfo.cs
├── Controls
│ ├── AppComponents.cs
│ ├── ControlsHelper.cs
│ ├── Converters
│ │ ├── BoolToValConverter.cs
│ │ ├── BooleanToBrushConverter.cs
│ │ ├── ImageDimensionConverter.cs
│ │ ├── InvertableBooleanToVisibilityConverter.cs
│ │ ├── UnitStringConverter.cs
│ │ └── UrlConverter.cs
│ ├── GridViewSort.cs
│ ├── SharedImageControl.xaml
│ └── SharedImageControl.xaml.cs
├── HydrusWindow.xaml
├── HydrusWindow.xaml.cs
├── Icon.ico
├── MainWindow.Handlers.cs
├── MainWindow.State.cs
├── MainWindow.xaml
├── MainWindow.xaml.cs
├── Model
│ ├── IBitmapImageSource.cs
│ ├── INamed.cs
│ ├── LazyProperty.cs
│ ├── LogEntry.cs
│ ├── QueryModel.cs
│ ├── ResultItem.cs
│ ├── SharedInfo.cs
│ └── UniResultItem.cs
├── ResourceDict.xaml
├── Resources.Designer.cs
├── Resources.resx
├── Resources
│ ├── accept.png
│ ├── arrow_down.png
│ ├── arrow_redo.png
│ ├── arrow_refresh.png
│ ├── arrow_rotate_anticlockwise.png
│ ├── arrow_undo.png
│ ├── artwork.png
│ ├── asterisk_yellow.png
│ ├── clipboard_invoice.png
│ ├── clipboard_sign.png
│ ├── emotion_question.png
│ ├── exclamation.png
│ ├── help.png
│ ├── image.png
│ ├── image_link.png
│ ├── information.png
│ ├── link.png
│ ├── picture.png
│ ├── picture_add.png
│ ├── picture_delete.png
│ ├── picture_empty.png
│ ├── picture_error.png
│ ├── picture_go.png
│ ├── picture_insert.png
│ ├── picture_link.png
│ ├── picture_save.png
│ └── pictures.png
├── ResultWindow.xaml
├── ResultWindow.xaml.cs
└── SmartImage.UI.csproj
├── SmartImage.UI2
├── Program.cs
└── SmartImage.UI2.csproj
└── SmartImage.sln
/.editorconfig:
--------------------------------------------------------------------------------
1 | [*.{cs,vb}]
2 |
3 | # IDE0049: Simplify Names
4 | dotnet_diagnostic.IDE0049.severity = none
5 |
--------------------------------------------------------------------------------
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 | # These are supported funding model platforms
2 |
3 | github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
4 | patreon: # Replace with a single Patreon username
5 | open_collective: # Replace with a single Open Collective username
6 | ko_fi: # Replace with a single Ko-fi username
7 | tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
8 | community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
9 | liberapay: # Replace with a single Liberapay username
10 | issuehunt: # Replace with a single IssueHunt username
11 | otechie: # Replace with a single Otechie username
12 | custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
13 |
14 | custom: ['https://paypal.me/decimation001']
15 |
--------------------------------------------------------------------------------
/.vsconfig:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0",
3 | "components": [
4 | "Microsoft.VisualStudio.Component.CoreEditor",
5 | "Microsoft.VisualStudio.Workload.CoreEditor",
6 | "Microsoft.NetCore.Component.Runtime.3.1",
7 | "Microsoft.NetCore.Component.SDK",
8 | "Microsoft.VisualStudio.Component.NuGet",
9 | "Microsoft.VisualStudio.Component.Roslyn.Compiler",
10 | "Microsoft.VisualStudio.Component.Roslyn.LanguageServices",
11 | "Microsoft.NetCore.Component.DevelopmentTools",
12 | "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions",
13 | "Microsoft.VisualStudio.Component.DockerTools",
14 | "Microsoft.NetCore.Component.Web",
15 | "Microsoft.Net.ComponentGroup.DevelopmentPrerequisites",
16 | "Microsoft.VisualStudio.Component.TypeScript.4.0",
17 | "Microsoft.VisualStudio.Component.JavaScript.TypeScript",
18 | "Microsoft.VisualStudio.Component.JavaScript.Diagnostics",
19 | "Microsoft.Component.MSBuild",
20 | "Microsoft.VisualStudio.Component.TextTemplating",
21 | "Component.Microsoft.VisualStudio.RazorExtension",
22 | "Microsoft.VisualStudio.Component.IISExpress",
23 | "Microsoft.VisualStudio.Component.SQL.ADAL",
24 | "Microsoft.VisualStudio.Component.SQL.LocalDB.Runtime",
25 | "Microsoft.VisualStudio.Component.Common.Azure.Tools",
26 | "Microsoft.VisualStudio.Component.SQL.CLR",
27 | "Microsoft.VisualStudio.Component.MSODBC.SQL",
28 | "Microsoft.VisualStudio.Component.MSSQL.CMDLnUtils",
29 | "Microsoft.VisualStudio.Component.ManagedDesktop.Core",
30 | "Microsoft.VisualStudio.Component.SQL.SSDT",
31 | "Microsoft.VisualStudio.Component.SQL.DataSources",
32 | "Component.Microsoft.Web.LibraryManager",
33 | "Microsoft.VisualStudio.ComponentGroup.Web",
34 | "Microsoft.VisualStudio.Component.Web",
35 | "Microsoft.VisualStudio.Component.IntelliCode",
36 | "Component.Microsoft.VisualStudio.LiveShare",
37 | "Microsoft.VisualStudio.ComponentGroup.Web.Client",
38 | "Microsoft.Net.ComponentGroup.TargetingPacks.Common",
39 | "Component.Microsoft.VisualStudio.Web.AzureFunctions",
40 | "Microsoft.VisualStudio.ComponentGroup.AzureFunctions",
41 | "Microsoft.VisualStudio.Component.Azure.Compute.Emulator",
42 | "Microsoft.VisualStudio.Component.Azure.Storage.Emulator",
43 | "Microsoft.VisualStudio.Component.Azure.ClientLibs",
44 | "Microsoft.VisualStudio.Component.Azure.AuthoringTools",
45 | "Microsoft.VisualStudio.Component.CloudExplorer",
46 | "Microsoft.VisualStudio.ComponentGroup.Web.CloudTools",
47 | "Microsoft.VisualStudio.Component.DiagnosticTools",
48 | "Microsoft.VisualStudio.Component.EntityFramework",
49 | "Microsoft.VisualStudio.Component.AspNet45",
50 | "Microsoft.VisualStudio.Component.AppInsights.Tools",
51 | "Microsoft.VisualStudio.Component.WebDeploy",
52 | "Microsoft.VisualStudio.Workload.NetWeb",
53 | "Microsoft.VisualStudio.ComponentGroup.Azure.Prerequisites",
54 | "Microsoft.VisualStudio.Component.Azure.Waverton.BuildTools",
55 | "Microsoft.VisualStudio.Component.Azure.Waverton",
56 | "Microsoft.Component.Azure.DataLake.Tools",
57 | "Microsoft.VisualStudio.Component.Azure.Kubernetes.Tools",
58 | "Microsoft.VisualStudio.Component.Azure.ResourceManager.Tools",
59 | "Microsoft.VisualStudio.ComponentGroup.Azure.ResourceManager.Tools",
60 | "Microsoft.VisualStudio.ComponentGroup.Azure.CloudServices",
61 | "Microsoft.VisualStudio.Component.Azure.ServiceFabric.Tools",
62 | "Microsoft.VisualStudio.Workload.Azure",
63 | "Microsoft.VisualStudio.Component.VC.CoreIde",
64 | "Microsoft.VisualStudio.Component.VC.Tools.x86.x64",
65 | "Microsoft.VisualStudio.Component.Graphics.Tools",
66 | "Microsoft.VisualStudio.Component.Windows10SDK.19041",
67 | "Microsoft.VisualStudio.Component.ManagedDesktop.Prerequisites",
68 | "Microsoft.ComponentGroup.Blend",
69 | "Microsoft.VisualStudio.Component.Debugger.JustInTime",
70 | "Microsoft.VisualStudio.ComponentGroup.MSIX.Packaging",
71 | "Microsoft.VisualStudio.Workload.ManagedDesktop",
72 | "Microsoft.VisualStudio.Component.Windows10SDK.18362",
73 | "Microsoft.Component.NetFX.Native",
74 | "Microsoft.VisualStudio.ComponentGroup.UWP.NetCoreAndStandard",
75 | "Microsoft.VisualStudio.Component.Graphics",
76 | "Microsoft.VisualStudio.ComponentGroup.UWP.Xamarin",
77 | "Microsoft.VisualStudio.ComponentGroup.UWP.Support",
78 | "Microsoft.VisualStudio.Component.VC.Tools.ARM64",
79 | "Microsoft.VisualStudio.Component.UWP.VC.ARM64",
80 | "Microsoft.VisualStudio.Component.VC.Tools.ARM",
81 | "Microsoft.VisualStudio.ComponentGroup.UWP.VC",
82 | "Microsoft.VisualStudio.Workload.Universal",
83 | "Component.OpenJDK",
84 | "Microsoft.VisualStudio.Component.MonoDebugger",
85 | "Microsoft.VisualStudio.Component.Merq",
86 | "Component.Xamarin.RemotedSimulator",
87 | "Microsoft.VisualStudio.ComponentGroup.WebToolsExtensions.TemplateEngine",
88 | "Component.Xamarin",
89 | "Component.Android.SDK28",
90 | "Microsoft.VisualStudio.Workload.NetCrossPlat",
91 | "Microsoft.VisualStudio.Workload.NetCoreTools",
92 | "Microsoft.VisualStudio.ComponentGroup.Maui.All",
93 | ]
94 | }
95 |
--------------------------------------------------------------------------------
/Assets/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Assets/Icon.ico
--------------------------------------------------------------------------------
/Assets/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Assets/Icon.png
--------------------------------------------------------------------------------
/Assets/Legacy1_CommandLine.md:
--------------------------------------------------------------------------------
1 | # Configuration
2 |
3 | ## Search Engines
4 |
5 | `-se ` _(parameter)_
6 |
7 | Specifies engines with a comma-separated list of search engine names
8 |
9 | ## Priority Engines
10 |
11 | `-pe ` _(parameter)_
12 |
13 | Specifies priority engines with a comma-separated list of search engine names
14 |
15 | ## Filtering
16 |
17 | `-f` _(switch)_
18 |
19 | Enables filtering of results (omit to disable)
20 |
21 | # Input
22 |
23 | The last parameter should be the image query.
24 |
25 | # Examples
26 |
27 | `smartimage -se All -pe SauceNao 'https://litter.catbox.moe/x8jfkj.jpg'`
28 |
29 | Runs a search of `https://litter.catbox.moe/x8jfkj.jpg` with `All` search engines, and `SauceNao` as a priority engine.
30 |
31 |
32 |
33 | `smartimage 'C:\Users\\Downloads\image.jpg'`
34 |
35 | Runs a search of `C:\Users\\Downloads\image.jpg` with configuration from the config file.
36 |
--------------------------------------------------------------------------------
/Assets/Legacy1_Configuration.md:
--------------------------------------------------------------------------------
1 | # Settings
2 |
3 | **SmartImage** is highly customizable. The following table lists config options.
4 |
5 | | Setting | Description |
6 | |--|--|
7 | | Search engines | Engines to use when searching. |
8 | | Priority engines | Engines whose results are opened in the browser.|
9 | | Filtering | Hides low-quality and unsuccessful results. |
10 | | Notification | Displays a toast notification with result information once the search is completed.|
11 | | Notification image | Includes a preview image in the toast notification. |
12 | | Context menu | Integrates **SmartImage** into the context menu (right-click menu). |
13 |
14 | These settings can be configured in different ways:
15 |
16 | - The main menu
17 | - The command line
18 |
19 |
20 | # Engines
21 |
22 | ## Options
23 |
24 | Search engine names and configuration:
25 |
26 | | Real Name | Option Name |
27 | | --------------- | --------------- |
28 | | (All) | `All` |
29 | | (None) | `None` |
30 | | (Auto) | `Auto` |
31 | | (Artwork) | `Artwork` |
32 | | SauceNao | `SauceNao` |
33 | | ImgOps | `ImgOps` |
34 | | Google Images | `GoogleImages` |
35 | | TinEye | `TinEye` |
36 | | IQDB | `Iqdb` |
37 | | trace.moe | `TraceMoe` |
38 | | Karma Decay | `KarmaDecay` |
39 | | Yandex | `Yandex` |
40 | | Bing | `Bing` |
41 | | Tidder | `Tidder` |
42 | | Ascii2D | `Ascii2D` |
43 |
44 | Special options:
45 | - `All`: Use all available engines
46 | - `None`: Use no engines1
47 | - `Auto`: Use the best engine result1
48 | - `Artwork`: The engines *SauceNao*, *IQDB*, *Ascii2D*
49 |
50 | 1 This option can only be used with priority engine options. They cannot be used for the main search engine options.
51 |
52 | ## Search Engines
53 |
54 | These options are the engines used to perform searches.
55 |
56 | ## Priority Search Engines
57 |
58 | These options are the engines whose results will be opened in your browser. If `Auto` is used, the best result is opened. If `None` is used,
59 | no results will be opened in the browser.
60 |
61 |
62 | # Behavior
63 |
64 | Configuration is loaded in this order:
65 | 1. Configuration file (`SmartImage.cfg`)
66 | 2. Command line parameters (if specified)
67 |
68 | Therefore, command line parameters have precedence over the config file. For example, if the config file designates `SauceNao` as a priority engine but the command line argument `-pe Iqdb` is specified, `Iqdb` will take precedence.
69 |
70 | This can be useful when you want to run a search with certain settings but want to keep your personalized configuration intact.
--------------------------------------------------------------------------------
/Assets/Legacy1_Home.md:
--------------------------------------------------------------------------------
1 | ```
2 | ____ _ ___
3 | / ___| _ __ ___ __ _ _ __| |_|_ _|_ __ ___ __ _ __ _ ___
4 | \___ \| '_ ` _ \ / _` | '__| __|| || '_ ` _ \ / _` |/ _` |/ _ \
5 | ___) | | | | | | (_| | | | |_ | || | | | | | (_| | (_| | __/
6 | |____/|_| |_| |_|\__,_|_| \__|___|_| |_| |_|\__,_|\__, |\___|
7 | |___/
8 | ```
9 |
10 | Welcome to the **SmartImage** wiki!
11 |
12 | # Installation
13 |
14 | ## Requirements
15 |
16 | The only requirements are .NET 6 and Windows. You can check if .NET 6 is installed by running
17 | one of the following commands:
18 |
19 | `dotnet --list-runtimes`:
20 | _Output:_ `Microsoft.NETCore.App 6.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]`
21 |
22 | `dotnet --list-sdks`:
23 | _Output:_ `6.0.100 [C:\Program Files\dotnet\sdk]`
24 |
25 | If the version major number is 6 (i.e., first number in the version), then .NET 6 is installed.
26 |
27 | ***
28 |
29 | **SmartImage** must be added to the system PATH (*`%PATH%`*) environment variable, otherwise context menu integration will not work. **SmartImage** will automatically do this for you. Otherwise, you can read about how to manually do this [here](https://superuser.com/questions/949560/how-do-i-set-system-environment-variables-in-windows-10).
30 |
31 | If the IME (system language) is a non-Romance language (e.g., Japanese or Chinese), some features may not work correctly (i.e., keyboard input, context menu integration). To resolve this, set the IME to English.
32 |
33 | # Engines
34 |
35 | Supported search engines and notes:
36 |
37 | - [SauceNao](https://saucenao.com/)
38 | - Multi-service image search
39 | - **Use case:** Finding sauce, usually artwork
40 | - [IQDB](https://iqdb.org/)
41 | - Multi-service image search
42 | - Similar to *SauceNao*
43 | - **Use case:** Finding sauce, usually artwork
44 | - [trace.moe](https://trace.moe/)
45 | - Multi-database image search
46 | - **Use case:** Identifying anime from a screenshot
47 | - [Karma Decay](http://karmadecay.com/)
48 | - Reddit image search
49 | - **Disclaimer:** Very slow
50 | - [ImgOps](http://imgops.com/)
51 | - Multi-service image search
52 | - **Use case:** Performing multiple image operations
53 | - **Restrictions:** Max upload size is 5MB
54 | - [Google Images](https://images.google.com/)
55 | - General-purpose image search
56 | - [TinEye](https://tineye.com/)
57 | - General-purpose image search
58 | - Generally better than *Google Images*
59 | - [Yandex](https://yandex.com/images/)
60 | - General-purpose image search
61 | - **Disclaimer:** Russian
62 | - [Bing](https://www.bing.com/images/)
63 | - General-purpose image search
64 | - [Tidder](http://tidder.xyz/)
65 | - Reddit image search
66 | - Generally better than *Karma Decay*
67 | - [Ascii2D](https://ascii2d.net/)
68 | - Multi-service image search
69 | - Similar to *SauceNao* and *IQDB*
70 | - **Use case:** Finding sauce, usually artwork
71 |
72 | ***
73 |
74 |
75 | # Usage
76 |
77 | SmartImage can be used in multiple ways:
78 |
79 | - Open the program normally (double click) and you can use the program in a user-friendly way. You can then drag and drop your image into the command prompt and run a search.
80 |
81 |
82 |
83 |
84 |
85 | - Right-click on an image (once the context menu integration is set up) and select the SmartImage option to immediately perform a search.
86 |
87 |
88 |
89 |
90 |
91 | - Drag and drop an image over the executable to immediately perform a search (functionally the same as right-clicking on an image and using the SmartImage option).
92 |
93 |
94 |
95 |
96 |
97 | - Use the command line.
98 |
--------------------------------------------------------------------------------
/Assets/Legacy1_Interface.md:
--------------------------------------------------------------------------------
1 | # Main menu
2 |
3 |
4 |
5 |
6 |
7 |
8 | - **Run**: Runs a search
9 | - **Engines**: Configures engines
10 | - **Priority engines**: Configures priority engines
11 | - **Filter**: Toggles filtering
12 | - **Notification**: Toggles toast notification
13 | - **Notification image**: Toggles toast notification image
14 | - **Context menu**: Toggles context menu integration
15 | - **Config**: Displays current configuration
16 | - **Info**: Displays program info
17 | - **Update**: Checks and installs new versions
18 | - **Help**: Opens help
19 |
20 |
21 |
23 |
24 |
25 |
26 |
27 |
28 |
29 | # Results
30 |
31 | ## Result
32 |
33 | Once the search is complete, the UI shows extensive information about the search results.
34 |
35 |
36 |
37 |
38 |
39 |
40 | - **Result**: Most accurate and specific result URL
41 | - **Raw**: Undifferentiated result URL
42 | - **Direct**: Direct image URL
43 | - **Similarity**: Image similarity (delta)
44 | - **Description**: Image description, caption, etc.
45 | - **Site**: Result site1
46 | - **Artist**: Image artist1
47 | - **Characters**: Character(s) in the image1
48 | - **Source**: Image source1
49 | - **Resolution**: Image resolution
50 | - **Detail score**: Number of detail fields
51 | - **Other image results**: Other results
52 |
53 |
54 |
55 |
56 | 1 This metadata is usually only for anime or related image results (i.e. *SauceNao*, *IQDB*, *TraceMoe*, etc.)
57 |
58 | # Interaction
59 |
60 | ## General
61 |
62 | - Press the option character on the keyboard to open it (i.e., press **0** to open **[0]** or **A** to open **[A]**).
63 | - Press `Escape` to return to the previous interface.
64 | - Press `F1` to show filtered results.
65 | - Press `F5` to refresh the console buffer.
66 |
67 | ## Results
68 |
69 | - Press the result character to open the **Result** URL in your browser.
70 | - Hold down `Ctrl` to search for a direct image link.
71 | - Hold down `Alt` to show more info and results. Certain engines will return multiple results; those can be viewed using this option.
72 | - Hold down `Shift` to open the raw URL.
73 | - Hold down `Ctrl` and `Alt` to download the image.
--------------------------------------------------------------------------------
/Assets/Legacy2_Usage.md:
--------------------------------------------------------------------------------
1 | # Input
2 |
3 | **SmartImage** can be used in a variety of ways.
4 | - Input text field in the main menu
5 | - Copy/paste text
6 | - Use the *Browse* button to open the file picker dialog
7 | - Context menu
8 | - Use the *Config* button to open the [configuration dialog](https://github.com/Decimation/SmartImage/wiki/Interface#configuration) to toggle context menu integration
9 | - Clipboard
10 | - copying an _image_ or _URI_ outside of the program will automatically populate the input field
11 | - Command line
12 | - Use the `--i` parameter to specify input
13 |
14 | # Queries
15 |
16 | The value given as [input](#Input) is referred to hereafter as _search query_ or _query_.
17 |
18 | * Search queries may be either a _file_ or _URI_.
19 | * All queries must be a recognized image type.
20 | * If query is a _URI_, it must be a direct link (i.e., the payload returned is a binary image). For example, `https://i.imgur.com/zoBIh8t.jpg` returns
21 | an image payload with `Content-Type` as `image/jpeg`.
22 |
23 |
--------------------------------------------------------------------------------
/Assets/SmartImage icon.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Assets/SmartImage icon.psd
--------------------------------------------------------------------------------
/Assets/Usage.md:
--------------------------------------------------------------------------------
1 | # Main Menu
2 |
3 | 
4 |
5 | | | |
6 | | ---- | ------------------------ |
7 | | `1` | Input field |
8 | | `2` | Upload URL |
9 | | `3` | Input image type |
10 | | `4` | Input image size |
11 | | `5` | Input image dimensions |
12 | | `6` | Preview image URL |
13 | | `7` | Preview result URL |
14 | | `8` | Preview image info |
15 | | `9` | Preview image size |
16 | | `10` | Preview image dimensions |
17 | | `11` | Functions... |
18 | | `12` | Status... |
19 | | `13` | Search status... |
20 | | `14` | Input queue |
21 | | `15` | Preview |
22 | | `16` | Results |
23 |
24 | # Results
25 |
26 | 
27 |
28 | # Configuration
29 |
30 | 
31 |
32 | # Command-Line
33 |
34 | | Parameter | Definition | Type |
35 | | --------- | ----------------------------------------------------- | ---------- |
36 | | `-i` | Input | _`value`_ |
37 | | `-auto` | Start search immediately | _`switch`_ |
38 | | `-h` | Hide GUI | _`switch`_ |
39 | | `-s` | Switch current queue input to value specified by `-i` | _`switch`_ |
40 |
41 | # Integration
42 |
43 | **SmartImage** is compatible with **ShareX** actions.
44 |
45 | ### About
46 |
47 | **Last updated:** `2024-01-24 v4.0.4`
48 |
--------------------------------------------------------------------------------
/Assets/Wiki_TOC_Old.md:
--------------------------------------------------------------------------------
1 | ## [Home](https://github.com/Decimation/SmartImage/wiki)
2 | - [Engines](https://github.com/Decimation/SmartImage/wiki/Engines)
3 | - Miscellaneous
4 | - [Reviews](https://github.com/Decimation/SmartImage/wiki/Reviews)
5 |
6 | ***
7 |
8 | ## GUI
9 | - [Usage](https://github.com/Decimation/SmartImage/wiki/Usage)
10 | - [Main Menu](https://github.com/Decimation/SmartImage/wiki/Usage#main-menu)
11 | - [Results](https://github.com/Decimation/SmartImage/wiki/Usage#results)
12 | - [Configuration](https://github.com/Decimation/SmartImage/wiki/Usage#configuration)
13 |
14 |
15 | ***
16 |
17 | ### Legacy²
18 | - [Interface](https://github.com/Decimation/SmartImage/wiki/Interface-(Legacy²))
19 | - [Main Menu](https://github.com/Decimation/SmartImage/wiki/Interface-(Legacy²)#main-menu)
20 | - [Configuration](https://github.com/Decimation/SmartImage/wiki/Interface-(Legacy²)#configuration)
21 | - [Results](https://github.com/Decimation/SmartImage/wiki/Interface-(Legacy²)#results)
22 | - [Usage](https://github.com/Decimation/SmartImage/wiki/Usage-(Legacy²))
23 | - [Input](https://github.com/Decimation/SmartImage/wiki/Usage-(Legacy²)#Input)
24 |
25 | ***
26 |
27 | ### [Legacy](https://github.com/Decimation/SmartImage/wiki/Home-(Legacy))¹
28 |
--------------------------------------------------------------------------------
/Directory.Build.props:
--------------------------------------------------------------------------------
1 |
2 |
3 | enable
4 | 11.1.0
5 |
6 |
7 |
--------------------------------------------------------------------------------
/Examples/Config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Config.png
--------------------------------------------------------------------------------
/Examples/Demo 1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Demo 1.gif
--------------------------------------------------------------------------------
/Examples/Main menu 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Main menu 2.png
--------------------------------------------------------------------------------
/Examples/Main menu diagram.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Main menu diagram.png
--------------------------------------------------------------------------------
/Examples/Main menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Main menu.png
--------------------------------------------------------------------------------
/Examples/Main menu.psd:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Main menu.psd
--------------------------------------------------------------------------------
/Examples/Old/Config (2).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Config (2).png
--------------------------------------------------------------------------------
/Examples/Old/Config.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Config.png
--------------------------------------------------------------------------------
/Examples/Old/Context menu integration.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Context menu integration.png
--------------------------------------------------------------------------------
/Examples/Old/Demo 1-old.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Demo 1-old.gif
--------------------------------------------------------------------------------
/Examples/Old/Demo 1.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Demo 1.gif
--------------------------------------------------------------------------------
/Examples/Old/Demo 2.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Demo 2.gif
--------------------------------------------------------------------------------
/Examples/Old/Demo 3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Demo 3.gif
--------------------------------------------------------------------------------
/Examples/Old/Example search results.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Example search results.png
--------------------------------------------------------------------------------
/Examples/Old/Main menu (2).png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Main menu (2).png
--------------------------------------------------------------------------------
/Examples/Old/Main menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Main menu.png
--------------------------------------------------------------------------------
/Examples/Old/Rdx help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Rdx help.png
--------------------------------------------------------------------------------
/Examples/Old/Results 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Results 1.png
--------------------------------------------------------------------------------
/Examples/Old/Results 2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Old/Results 2.png
--------------------------------------------------------------------------------
/Examples/Rdx context menu.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Rdx context menu.png
--------------------------------------------------------------------------------
/Examples/Rdx help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Rdx help.png
--------------------------------------------------------------------------------
/Examples/Results 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/Results 1.png
--------------------------------------------------------------------------------
/Examples/ShareX.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/Examples/ShareX.png
--------------------------------------------------------------------------------
/NuGet.config:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
SmartImage
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Find the source image in one click!
24 |
25 | Releases »
26 |
27 | Wiki »
28 |
29 | Issues »
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
SmartImage is a powerful reverse image search tool for Windows. SmartImage will open the best match found returned from various image search engines (see the supported sites) right in your web browser. This behavior can be configured to the user's preferences.
43 |
44 |
48 |
49 | SmartImage has two forms: GUI (Windows) and Rdx (cross-platform).
50 |
51 |
52 |
53 |
54 |
55 | ## Supported Engines 🔎
56 |
57 | Supported search engines:
58 |
59 | **See the [Engines Index](https://docs.google.com/spreadsheets/d/1BdBqGzEQ7x6XKcDSE_w_KrfBzTaYB48CCPJyeSNtbyg/edit?usp=sharing) spreadsheet.**
60 |
61 |
73 |
74 |
75 |
76 | ## Getting Started (GUI) 1️⃣
77 |
78 | **SmartImage** is designed to be intuitive and agile. There are multiple ways of running the program, and you can use 2 different forms of input: direct image links or files.
79 |
80 | See [Usage »](https://github.com/Decimation/SmartImage/wiki/(GUI)-Usage)
81 |
82 | ### 1. Input Field
83 |
84 | #### 1a. Text 📲
85 |
86 | Simply use the text input field in the shell UI. You are able to copy/paste text and use common text manipulation functions such as undo/redo.
87 |
88 | #### 1b. Drag-and-drop ↕
89 |
90 | You are able to drag-and-drop images into the text field, in which case the file path populates the input field.
91 |
92 | ### 2. Clipboard 📋
93 |
94 | **SmartImage** automatically detects clipboard changes
95 |
96 | #### 2a. Text 🔗
97 |
98 | Copy a link or file path, and the input field will update with its value.
99 |
100 | #### 2b. Image file 🖼
101 |
102 | Copy an image file, and the input field will update with its file path.
103 |
104 | ### 3. Context menu 🖱
105 |
106 | Once the context menu integration is enabled, right-click on an image to open **SmartImage** with the selected
107 | file used as the input.
108 |
109 | ### 4. Command-line 🖥
110 |
111 | Use the documented arguments to invoke **SmartImage** from various system contexts such as scripts or
112 | applications.
113 |
114 | ## Wiki 📕
115 |
116 | **See the _[Wiki »](https://github.com/Decimation/SmartImage/wiki)_ for documentation.**
--------------------------------------------------------------------------------
/SmartImage.Lib/Clients/AnilistClient.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Nodes;
3 | using Kantan.Net;
4 |
5 | // ReSharper disable PossibleNullReferenceException
6 |
7 | // ReSharper disable UnusedMember.Global
8 |
9 | namespace SmartImage.Lib.Clients;
10 |
11 | public sealed class AnilistClient : IDisposable
12 | {
13 | private readonly GraphQLClient m_client;
14 |
15 | public AnilistClient()
16 | {
17 | m_client = new GraphQLClient("https://graphql.anilist.co");
18 | }
19 |
20 | public async Task GetTitleAsync(int anilistId)
21 | {
22 | /*
23 | * https://anilist.gitbook.io/anilist-apiv2-docs/overview/graphql
24 | * https://anilist.gitbook.io/anilist-apiv2-docs/overview/graphql/getting-started
25 | * https://graphql.org/learn/queries/
26 | */
27 |
28 | const string GRAPH_QUERY = @"query ($id: Int) { # Define which variables will be used in the query (id)
29 | Media(id: $id, type: ANIME) { # Insert our variables into the query arguments (id) (type: ANIME is hard-coded in the query)
30 | id
31 | title {
32 | romaji
33 | english
34 | native
35 | }
36 | }
37 | }";
38 |
39 | var response = await m_client.ExecuteAsync(GRAPH_QUERY, new
40 | {
41 | query = GRAPH_QUERY,
42 | id = anilistId
43 | });
44 |
45 | var value = response["data"];
46 | var title = value?["Media"]?["title"];
47 | return title?["english"]?.ToString() ?? title?["romaji"]?.ToString();
48 | }
49 |
50 | #region IDisposable
51 |
52 | public void Dispose()
53 | {
54 |
55 | m_client.Dispose();
56 | }
57 |
58 | #endregion
59 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Clients/Booru/BaseBooruClient.cs:
--------------------------------------------------------------------------------
1 |
2 | using SmartImage.Lib.Utilities;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Diagnostics.CodeAnalysis;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using SmartImage.Lib.Utilities.Diagnostics;
10 |
11 | namespace SmartImage.Lib.Clients.Booru;
12 |
13 | // TODO
14 | [Experimental(AppSupport.DIAG_SMRTIMG_EXP001)]
15 | public abstract class BaseBooruClient : IDisposable
16 | {
17 |
18 | public Url BaseUrl { get; }
19 |
20 | public abstract string Name { get; }
21 |
22 | protected BaseBooruClient(Url baseUrl)
23 | {
24 | BaseUrl = baseUrl;
25 | }
26 |
27 | public virtual void Dispose() { }
28 |
29 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Clients/Booru/BaseGelbooruClient.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics.CodeAnalysis;
2 | using System.Reflection;
3 | using Flurl.Http;
4 | using SmartImage.Lib.Utilities;
5 | using SmartImage.Lib.Utilities.Diagnostics;
6 |
7 | namespace SmartImage.Lib.Clients.Booru;
8 |
9 | // TODO
10 |
11 | [Experimental(AppSupport.DIAG_SMRTIMG_EXP001)]
12 | public abstract class BaseGelbooruClient : BaseBooruClient
13 | {
14 |
15 | public FlurlClient Client { get; }
16 |
17 | [CBN]
18 | public string Key { get; set; }
19 |
20 | [CBN]
21 | public string Id { get; set; }
22 |
23 | protected BaseGelbooruClient(Url baseUrl) : base(baseUrl)
24 | {
25 |
26 | Client = new FlurlClient()
27 | {
28 | BaseUrl = baseUrl,
29 | Settings =
30 | {
31 | JsonSerializer = { }
32 | }
33 | };
34 | }
35 |
36 | public class PostsRequest
37 | {
38 |
39 | public int Limit { get; set; }
40 |
41 | public int Pid { get; set; }
42 |
43 | public string Tags { get; set; }
44 |
45 | public long Cid { get; set; }
46 |
47 | public int Id { get; set; }
48 |
49 | public int Json { get; set; } = 1;
50 |
51 | public PostsRequest() { }
52 |
53 | }
54 |
55 | protected int PostMax { get; set; } = 100;
56 |
57 | protected virtual bool Verify(PostsRequest r)
58 | {
59 | r.Limit = Math.Clamp(r.Limit, 1, PostMax);
60 |
61 | return true;
62 | }
63 |
64 | public virtual async Task GetPostsAsync(PostsRequest r)
65 | {
66 | if (!Verify(r)) {
67 | throw new ArgumentException();
68 | }
69 |
70 | var properties = new List();
71 |
72 | foreach (PropertyInfo p in r.GetType().GetProperties()) {
73 | var o = p.GetValue(r);
74 |
75 | if (o == null || o is string s && string.IsNullOrWhiteSpace(s) || o.Equals(0)) {
76 | continue;
77 | }
78 |
79 | var sss = o.ToString();
80 | var h = p.Name.ToLower() + "=" + Url.Encode(sss, true);
81 | properties.Add(h);
82 | }
83 |
84 | var ss = string.Join('&', properties);
85 |
86 | return await Client.Request("/index.php?page=post&s=list", ss)
87 | .GetAsync();
88 | }
89 |
90 | public override void Dispose()
91 | {
92 | Client?.Dispose();
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/SmartImage.Lib/Clients/Booru/Rule34Booru.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: Rule34Booru.cs
2 | // Date: 2024/06/18 @ 14:06:04
3 |
4 | using System.Diagnostics.CodeAnalysis;
5 | using SmartImage.Lib.Utilities;
6 | using SmartImage.Lib.Utilities.Diagnostics;
7 |
8 | namespace SmartImage.Lib.Clients.Booru;
9 | // TODO
10 |
11 | [Experimental(AppSupport.DIAG_SMRTIMG_EXP001)]
12 | public class Rule34Booru : BaseGelbooruClient
13 | {
14 |
15 | public override string Name => "Rule34";
16 |
17 | public Rule34Booru() : base("https://rule34.xxx/") { }
18 |
19 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Clients/FlareSolverrClient.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: FlareSolverrClient.cs
2 | // Date: 2024/10/25 @ 12:10:45
3 |
4 | using System.Diagnostics;
5 | using System.Reflection;
6 | using CliWrap;
7 | using FlareSolverrSharp;
8 | using SmartImage.Lib.Results.Data;
9 |
10 | namespace SmartImage.Lib.Clients;
11 |
12 | public sealed class FlareSolverrClient : IDisposable, ISearchConfigReceiver
13 | {
14 |
15 | [MNNW(true, nameof(Client))]
16 | public bool HasClient => Client != null;
17 |
18 | [MNNW(true, nameof(Clearance))]
19 | public bool HasClearance => Clearance != null;
20 |
21 | [MNNW(true, nameof(Clearance), nameof(Client))]
22 | public bool IsInitialized => HasClearance && HasClient;
23 |
24 | public ClearanceHandler Clearance { get; private set; }
25 |
26 | public HttpClient Client { get; private set; }
27 |
28 | public bool Configure(string api)
29 | {
30 | Dispose();
31 |
32 | Clearance = new ClearanceHandler(api)
33 | {
34 | EnsureResponseIntegrity = false
35 | };
36 |
37 | Client = new HttpClient(Clearance);
38 |
39 | Trace.WriteLine($"{nameof(FlareSolverrClient)}: init {api}");
40 |
41 | return HasClient;
42 | }
43 |
44 | private FlareSolverrClient() { }
45 |
46 | static FlareSolverrClient() { }
47 |
48 | public static FlareSolverrClient Value { get; private set; } = new();
49 |
50 | public void Dispose()
51 | {
52 | Debug.WriteLine($"Disposing {nameof(FlareSolverrClient)}");
53 | Clearance?.Dispose();
54 | Client?.Dispose();
55 | Clearance = null;
56 | Client = null;
57 | }
58 |
59 | #region Implementation of ISearchConfigReceiver
60 |
61 | public ValueTask ApplyConfigAsync(SearchConfig cfg, CancellationToken ct = default)
62 | {
63 | var ok = false;
64 |
65 | if (cfg.FlareSolverr) {
66 | ok = Configure(cfg.FlareSolverrApiUrl);
67 | }
68 |
69 | return ValueTask.FromResult(ok);
70 | }
71 |
72 | #endregion
73 |
74 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Cookies/BrowserCookiesProvider.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: CookiesManager.cs
2 |
3 | using System.Data;
4 | using System.Diagnostics;
5 | using System.Runtime.Caching;
6 | using Kantan.Net.Web;
7 |
8 | namespace SmartImage.Lib.Cookies;
9 |
10 | public class BrowserCookiesProvider : ICookiesProvider
11 | {
12 |
13 | private const string CH_NAME = "cookies";
14 |
15 | public BaseCookieReader Reader { get; }
16 |
17 | public MemoryCache Cache { get; }
18 |
19 | public BrowserCookiesProvider(BaseCookieReader reader)
20 | {
21 | Reader = reader;
22 | Cache = new MemoryCache($"{nameof(BrowserCookiesProvider)}_Cache");
23 | }
24 |
25 | public async ValueTask OpenAsync()
26 | {
27 | // Opening is idempotent
28 | await Reader.Connection.OpenAsync();
29 | }
30 |
31 | public async ValueTask CloseAsync()
32 | {
33 | await Reader.Connection.CloseAsync();
34 | }
35 |
36 | public async ValueTask> GetOrLoadCookiesAsync(CancellationToken ct = default)
37 | {
38 | if (!IsOpen) {
39 | await OpenAsync();
40 | }
41 |
42 | /*if (IsClosedOrBroken) {
43 | throw new InvalidOperationException();
44 | }*/
45 |
46 |
47 | var itemPolicy = new CacheItemPolicy()
48 | {
49 |
50 | // AbsoluteExpiration =
51 | };
52 |
53 | var chi = (IList) Cache.Get(CH_NAME);
54 |
55 | if (chi == null) {
56 |
57 | var cookies = await Reader.ReadCookiesAsync();
58 | var addOk = Cache.Add(CH_NAME, cookies, itemPolicy);
59 |
60 | if (addOk) {
61 | chi = (IList) Cache.Get(CH_NAME);
62 | }
63 |
64 | }
65 | else {
66 | Trace.WriteLine($"Found {CH_NAME} in cache");
67 | }
68 |
69 | return chi;
70 | }
71 |
72 | public bool IsOpen => Reader.Connection.State is ConnectionState.Open;
73 |
74 | public bool IsOpenOrInUse => Reader.Connection.State is < ConnectionState.Broken and >= ConnectionState.Open;
75 |
76 | public bool IsClosedOrBroken => Reader.Connection.State is ConnectionState.Broken or ConnectionState.Closed;
77 |
78 | public void Dispose()
79 | {
80 | Debug.WriteLine($"Disposing {nameof(BrowserCookiesProvider)}");
81 | Reader.Dispose();
82 | Cache.Dispose();
83 | }
84 |
85 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Cookies/DefaultCookiesProvider.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: DefaultCookiesProvider.cs
2 | // Date: 2025/02/04 @ 12:02:26
3 |
4 | using Kantan.Net.Web;
5 |
6 | namespace SmartImage.Lib.Cookies;
7 |
8 | public class DefaultCookiesProvider : ICookiesProvider
9 | {
10 |
11 | public List Cookies { get; }
12 |
13 | public DefaultCookiesProvider()
14 | {
15 | Cookies = new List();
16 | }
17 |
18 | public ValueTask> GetOrLoadCookiesAsync(CancellationToken ct = default)
19 | {
20 | return ValueTask.FromResult>(Cookies);
21 | }
22 |
23 | public void Dispose()
24 | {
25 | Cookies.Clear();
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Cookies/ICookiesEngine.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: ICookiesReceiver.cs
2 | // Date: 2024/06/06 @ 17:06:56
3 |
4 | using Flurl.Http;
5 |
6 | namespace SmartImage.Lib.Cookies;
7 |
8 | public interface ICookiesEngine
9 | {
10 | public CookieJar Jar { get; }
11 |
12 | public ICookiesProvider Provider { get; set; }
13 |
14 | public ValueTask ApplyCookiesAsync(CancellationToken token = default);
15 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Cookies/ICookiesProvider.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: ICookieProvider.cs
2 | // Date: 2024/10/15 @ 12:10:00
3 |
4 | using Kantan.Net.Web;
5 | using SmartImage.Lib.Utilities.Integration;
6 |
7 | namespace SmartImage.Lib.Cookies;
8 |
9 | public interface ICookiesProvider : IDisposable
10 | {
11 |
12 | public ValueTask> GetOrLoadCookiesAsync(CancellationToken ct = default);
13 |
14 |
15 | public static ICookiesProvider GetProvider()
16 | {
17 | if (BaseOSIntegration.Integration.IsFirefoxInstalled) {
18 | var cookieFile = FirefoxCookieReader.FindCookieFile();
19 | if (cookieFile != null) {
20 | return new BrowserCookiesProvider(new FirefoxCookieReader(cookieFile.FullName));
21 |
22 | }
23 | }
24 |
25 | return new DefaultCookiesProvider();
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/IEndpointEngine.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: IEndpointEngine.cs
2 | // Date: 2025/03/27 @ 12:03:18
3 |
4 | using Flurl.Http;
5 |
6 | namespace SmartImage.Lib.Engines;
7 |
8 | #pragma warning disable CS0649
9 |
10 | public interface IEndpointEngine
11 | {
12 |
13 | public Url EndpointUrl { get; }
14 |
15 | // public IFlurlClient Client { get; }
16 |
17 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Search/ArchiveMoeEngine.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: ArchiveMoeEngine.cs
2 | // Date: 2024/06/06 @ 14:06:00
3 |
4 | using System.Security.Cryptography;
5 | using System.Text.RegularExpressions;
6 | using AngleSharp.Dom;
7 | using AngleSharp.Html.Dom;
8 | using Novus.Streams;
9 | using SmartImage.Lib.Results;
10 | using SmartImage.Lib.Results.Data;
11 |
12 | namespace SmartImage.Lib.Engines.Impl.Search;
13 |
14 | public class ArchiveMoeEngine : WebSearchEngine
15 | {
16 |
17 | public override SearchEngineOptions EngineOption => SearchEngineOptions.ArchiveMoe;
18 |
19 | protected string Base64Hash { get; set; }
20 |
21 | protected override string NodesSelector => "//article[contains(@class,'post')]";
22 |
23 | public ArchiveMoeEngine() : this("https://archived.moe/_/search/") { }
24 |
25 | protected ArchiveMoeEngine(string baseUrl) : base(baseUrl) { }
26 |
27 | protected override Url GetRawUrl(SearchQuery query)
28 | {
29 | Base64Hash = GetHash(query);
30 |
31 | var r = Url.Combine(BaseUrl, "image", Base64Hash);
32 | return r;
33 |
34 | // return (BaseUrl.AppendPathSegments("image").AppendPathSegment(Base64Hash));
35 | }
36 |
37 | protected override ValueTask ParseResultItem(INode n, SearchResult r)
38 | {
39 | // ReSharper disable PossibleNullReferenceException
40 |
41 | var p = ChanPost.Parse(n);
42 | return ValueTask.FromResult(p.ToItem(r));
43 |
44 | // ReSharper restore PossibleNullReferenceException
45 | }
46 |
47 | protected static string GetHash(SearchQuery q)
48 | {
49 | //var digestBase64URL = digestBase64.replace('==', '').replace(/\//g, '_').replace(/\+/g, '-');
50 | var data = MD5.HashData(q.Source.Stream);
51 | var b64 = Convert.ToBase64String(data).Replace("==", "");
52 | b64 = Regex.Replace(b64, @"\//", "_");
53 | b64 = Regex.Replace(b64, @"\+", "-");
54 |
55 | q.Source.Stream.TrySeek();
56 |
57 | return b64;
58 | }
59 |
60 | public override void Dispose()
61 | {
62 | GC.SuppressFinalize(this);
63 | }
64 |
65 | }
66 |
67 | public record ChanPost : ISearchResultItemConverter
68 | {
69 |
70 | public string Author;
71 | public string Board;
72 | public Url File;
73 | public string Filename;
74 | public int Height;
75 |
76 | public long Id;
77 | public string Size;
78 | public string Text;
79 | public string Time1;
80 | public DateTime Time2;
81 | public string Title;
82 | public string Tripcode;
83 | public int Width;
84 |
85 | public static ChanPost Parse(INode n)
86 | {
87 | var e = n as HtmlElement;
88 |
89 | var pff = e.QuerySelector(".post_file_filename");
90 | var pfm = e.QuerySelector(".post_file_metadata").TextContent.Split(", ");
91 | var pd = e.QuerySelector(".post_data");
92 | var pt = pd.QuerySelector(".post_title").TextContent;
93 | var pa = pd.QuerySelector(".post_author").TextContent;
94 | var ptc = pd.QuerySelector(".post_tripcode").TextContent;
95 | var tw = pd.QuerySelector(".time_wrap").Children[0];
96 | var time2Ok = DateTime.TryParse(tw.GetAttribute("datetime"), out var time2);
97 | var time = tw.TextContent;
98 | var text = e.QuerySelector(".text").TextContent;
99 |
100 | var wh = pfm[1].Split('x');
101 |
102 | var p = new ChanPost
103 | {
104 | Id = Int64.Parse(e.GetAttribute("id")),
105 | Board = e.GetAttribute("data-board"),
106 | Filename = pff.TextContent,
107 | File = pff.GetAttribute("href"),
108 | Width = Int32.Parse(wh[0]),
109 | Height = Int32.Parse(wh[1]),
110 | Size = pfm[0],
111 | Title = pt,
112 | Author = pa,
113 | Tripcode = ptc,
114 | Time1 = time,
115 | Time2 = time2,
116 | Text = text
117 | };
118 |
119 | return p;
120 | }
121 |
122 | public SearchResultItem ToItem(SearchResult sr)
123 | {
124 |
125 | var sri = new SearchResultItem(sr)
126 | {
127 | Url = File,
128 | Width = Width,
129 | Height = Height,
130 | Artist = Author,
131 | Description = Title,
132 | Time = Time2,
133 | Site = File.Host,
134 | Metadata = this
135 | };
136 |
137 | return sri;
138 | }
139 |
140 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Search/FluffleEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Net;
5 | using System.Text;
6 | using System.Text.Json;
7 | using System.Text.Json.Serialization;
8 | using System.Threading.Tasks;
9 | using Flurl.Http;
10 | using JetBrains.Annotations;
11 | using Novus.Streams;
12 | using SixLabors.ImageSharp;
13 | using SixLabors.ImageSharp.Formats;
14 | using SixLabors.ImageSharp.Formats.Png;
15 | using SixLabors.ImageSharp.Processing;
16 | using SmartImage.Lib.Results;
17 | using SmartImage.Lib.Results.Data;
18 | using SmartImage.Lib.Utilities.Diagnostics;
19 |
20 | namespace SmartImage.Lib.Engines.Impl.Search;
21 |
22 | public class FluffleEngine : BaseSearchEngine, IEndpointEngine, IDisposable
23 | {
24 |
25 | public const string URL_ENDPOINT = "https://api.fluffle.xyz/v1/";
26 | public const string URL_BASE = "https://fluffle.xyz/";
27 |
28 | public FluffleEngine() : base(URL_BASE)
29 | {
30 | MaxSize = 4_194_304; // MiB
31 |
32 | // Timeout = TimeSpan.FromSeconds(10);
33 | }
34 |
35 |
36 | public Url EndpointUrl => URL_ENDPOINT;
37 |
38 |
39 | public override async Task GetResultAsync(SearchQuery query, CancellationToken token = default)
40 | {
41 |
42 | var sr = await base.GetResultAsync(query, token);
43 |
44 | IFlurlResponse response = null;
45 |
46 | if (sr.Status == SearchResultStatus.IllegalInput) {
47 | // return sr;
48 | goto ret;
49 | }
50 |
51 | var hdr = $"{R1.Name}/{AppSupport.Version} (by {R1.Author} on GitHub)";
52 |
53 |
54 | response = await Client.Request(EndpointUrl, "search")
55 | .WithHeaders(new
56 | {
57 | User_Agent = hdr
58 | })
59 | .WithTimeout(Timeout)
60 | .OnError(e => { e.ExceptionHandled = true; })
61 | .PostMultipartAsync(c =>
62 | {
63 | // var tmp = query.WriteImageToFile();
64 | query.Source.Stream.TrySeek();
65 |
66 | c.AddFile("file", query.Source.Stream, "file");
67 | query.Source.Stream.TrySeek();
68 |
69 | c.AddString("includeNsfw", true.ToString());
70 | c.AddString("limit", 32.ToString());
71 |
72 | // c.AddString("platforms", null)
73 | // c.AddString("createLink", false)
74 | }, cancellationToken: token);
75 |
76 | if (response is { ResponseMessage: { IsSuccessStatusCode: false } }) {
77 | var er = await response.GetJsonAsync();
78 |
79 | sr.ErrorMessage = $"{er.Message}: {er.Code}";
80 | sr.Status = SearchResultStatus.UnknownError;
81 |
82 | // return sr;
83 | goto ret;
84 | }
85 |
86 | if (response == null) {
87 | sr.Status = SearchResultStatus.UnknownError;
88 | goto ret;
89 | }
90 |
91 | var fr = await response.GetJsonAsync();
92 |
93 | foreach (FluffleResult result in fr.Results) {
94 | var item = result.ToItem(sr);
95 | sr.Results.Add(item);
96 | }
97 |
98 | sr.Status = SearchResultStatus.Success;
99 | ret:
100 | sr.Update();
101 | response?.Dispose();
102 | return sr;
103 | }
104 |
105 | protected override Url GetRawUrl(SearchQuery query)
106 | {
107 | return base.GetRawUrl(query);
108 | }
109 |
110 | public override SearchEngineOptions EngineOption => SearchEngineOptions.Fluffle;
111 |
112 | public override void Dispose() { }
113 |
114 | }
115 |
116 | #region API Objects
117 |
118 | public class FluffleErrorCode
119 | {
120 |
121 | [JsonPropertyName("code")]
122 | public string Code { get; set; }
123 |
124 | [JsonPropertyName("message")]
125 | public string Message { get; set; }
126 |
127 | [JsonPropertyName("traceId")]
128 | public string TraceId { get; set; }
129 |
130 | }
131 |
132 | public class FluffleResultCredit
133 | {
134 |
135 | [JsonPropertyName("id")]
136 | public int Id { get; set; }
137 |
138 | [JsonPropertyName("name")]
139 | public string Name { get; set; }
140 |
141 | }
142 |
143 | public class FluffleResult : ISearchResultItemConvertable
144 | {
145 |
146 | [JsonPropertyName("id")]
147 | public int Id { get; set; }
148 |
149 | [JsonPropertyName("score")]
150 | public double Score { get; set; }
151 |
152 | [JsonPropertyName("match")]
153 | public string Match { get; set; }
154 |
155 | [JsonPropertyName("platform")]
156 | public string Platform { get; set; }
157 |
158 | [JsonPropertyName("location")]
159 | public string Location { get; set; }
160 |
161 | [JsonPropertyName("isSfw")]
162 | public bool IsSfw { get; set; }
163 |
164 | [JsonPropertyName("thumbnail")]
165 | public FluffleResultThumbnail Thumbnail { get; set; }
166 |
167 | [JsonPropertyName("credits")]
168 | public List Credits { get; set; }
169 |
170 | public SearchResultItem ToItem(SearchResult sr)
171 | {
172 |
173 | var sri = new SearchResultItem(sr)
174 | {
175 | Artist = Credits.FirstOrDefault()?.Name,
176 | Url = Location,
177 | Similarity = Math.Round(Score * 100.0d, 2),
178 | Metadata = this,
179 | Thumbnail = Thumbnail?.Location,
180 | Site = Platform
181 | };
182 | return sri;
183 | }
184 |
185 | }
186 |
187 | public class FluffleResponse
188 | {
189 |
190 | [JsonPropertyName("id")]
191 | public string Id { get; set; }
192 |
193 | [JsonPropertyName("stats")]
194 | public FluffleResultStats Stats { get; set; }
195 |
196 | [JsonPropertyName("results")]
197 | public List Results { get; set; }
198 |
199 | }
200 |
201 | public class FluffleResultStats
202 | {
203 |
204 | [JsonPropertyName("count")]
205 | public int Count { get; set; }
206 |
207 | [JsonPropertyName("elapsedMilliseconds")]
208 | public int ElapsedMilliseconds { get; set; }
209 |
210 | }
211 |
212 | public class FluffleResultThumbnail
213 | {
214 |
215 | [JsonPropertyName("width")]
216 | public int Width { get; set; }
217 |
218 | [JsonPropertyName("centerX")]
219 | public int CenterX { get; set; }
220 |
221 | [JsonPropertyName("height")]
222 | public int Height { get; set; }
223 |
224 | [JsonPropertyName("centerY")]
225 | public int CenterY { get; set; }
226 |
227 | [JsonPropertyName("location")]
228 | public string Location { get; set; }
229 |
230 | }
231 |
232 | #endregion
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Search/Iqdb3DEngine.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.Lib IqdbEngine.cs
2 | // 2023-01-13 @ 11:21 PM
3 |
4 | // ReSharper disable UnusedMember.Global
5 |
6 | #region
7 |
8 | using System.Diagnostics;
9 | using System.Net;
10 | using AngleSharp.Dom;
11 | using AngleSharp.Html.Dom;
12 | using AngleSharp.Html.Parser;
13 | using AngleSharp.XPath;
14 | using Flurl.Http;
15 | using Kantan.Net.Utilities;
16 | using Kantan.Text;
17 | using SmartImage.Lib.Results;
18 | using SmartImage.Lib.Utilities;
19 |
20 | #endregion
21 |
22 | // ReSharper disable StringLiteralTypo
23 |
24 | namespace SmartImage.Lib.Engines.Impl.Search;
25 |
26 | #nullable disable
27 |
28 | public sealed class Iqdb3DEngine : IqdbEngine
29 | {
30 |
31 | private const string URL_BASE = "https://3d.iqdb.org/";
32 | private const string URL_QUERY = "https://3d.iqdb.org/?url=";
33 |
34 | public override SearchEngineOptions EngineOption => SearchEngineOptions.Iqdb3D;
35 |
36 | public Iqdb3DEngine() : base(URL_QUERY) { }
37 |
38 | #region Overrides of IqdbEngine
39 |
40 | public override Url EndpointUrl => URL_BASE;
41 |
42 | #endregion
43 |
44 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Search/Other/BingEngine.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json;
2 | using System.Text.Json.Nodes;
3 | using AngleSharp.Dom;
4 | using AngleSharp.Html.Parser;
5 | using Flurl;
6 | using Flurl.Http;
7 | using Kantan.Net.Utilities;
8 | using Kantan.Text;
9 | using SmartImage.Lib.Results;
10 |
11 | namespace SmartImage.Lib.Engines.Impl.Search.Other;
12 |
13 | public sealed class BingEngine : BaseSearchEngine
14 | {
15 |
16 | public BingEngine() : base("https://www.bing.com/images/searchbyimage?cbir=sbi&imgurl=")
17 | { }
18 |
19 | public override SearchEngineOptions EngineOption => SearchEngineOptions.Bing;
20 |
21 | public override void Dispose() { }
22 |
23 | // Parsing does not seem feasible ATM
24 |
25 | public async Task SearchAltQueryAsync(string query)
26 | {
27 | var sr = new SearchResult(this)
28 | {
29 | RawUrl = GetAltQueryUrl(query),
30 | };
31 |
32 | var req = await sr.RawUrl.WithHeaders(new
33 | {
34 | User_Agent = HttpUtilities.UserAgent
35 | }).GetAsync();
36 |
37 | var parser = new HtmlParser();
38 | var s = await req.GetStringAsync();
39 | var doc = await parser.ParseDocumentAsync(s);
40 |
41 | var elem = doc.QuerySelectorAll(".iuscp");
42 |
43 | foreach (IElement e in elem) {
44 | var imgpt = e.FirstChild;
45 |
46 | if (imgpt is IElement { ClassName: "tit" }) {
47 | continue;
48 | }
49 |
50 | var iusc = imgpt.FirstChild;
51 | var attr = iusc.TryGetAttribute("m");
52 | var j = JsonValue.Parse(attr);
53 |
54 | var infopt = e.ChildNodes[1];
55 |
56 | sr.Results.Add(new SearchResultItem(sr)
57 | {
58 | Url = j["murl"].ToString().CleanString(),
59 | Description = infopt.TextContent
60 | });
61 | }
62 |
63 | return sr;
64 | }
65 |
66 | private const string ALT_QUERY_URL = "https://www.bing.com/images/async";
67 |
68 | private static Url GetAltQueryUrl(string query, int cnt = 35)
69 | {
70 | var url = ALT_QUERY_URL.SetQueryParams(new
71 | {
72 | q = query,
73 | first = 0,
74 | count = cnt,
75 | // qft = @""""
76 | });
77 | return url;
78 | }
79 |
80 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Search/Other/GoogleImagesEngine.cs:
--------------------------------------------------------------------------------
1 | namespace SmartImage.Lib.Engines.Impl.Search.Other;
2 |
3 | public sealed class GoogleImagesEngine : BaseSearchEngine
4 | {
5 |
6 | public GoogleImagesEngine() : base("https://lens.google.com/uploadbyurl?url=") { }
7 |
8 | public override string Name => "Google Images";
9 |
10 | public override SearchEngineOptions EngineOption => SearchEngineOptions.GoogleImages;
11 |
12 | #region Overrides of BaseSearchEngine
13 |
14 | public override void Dispose() { }
15 |
16 | #endregion
17 |
18 | // https://html-agility-pack.net/knowledge-base/2113924/how-can-i-use-html-agility-pack-to-retrieve-all-the-images-from-a-website-
19 |
20 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Search/Other/ImgOpsEngine.cs:
--------------------------------------------------------------------------------
1 | namespace SmartImage.Lib.Engines.Impl.Search.Other;
2 |
3 | public sealed class ImgOpsEngine : BaseSearchEngine
4 | {
5 |
6 | public ImgOpsEngine() : base("https://imgops.com/") { }
7 |
8 | public override SearchEngineOptions EngineOption => SearchEngineOptions.ImgOps;
9 |
10 | public override void Dispose() { }
11 |
12 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Search/Other/KarmaDecayEngine.cs:
--------------------------------------------------------------------------------
1 |
2 | //todo
3 | namespace SmartImage.Lib.Engines.Impl.Search.Other;
4 |
5 | public sealed class KarmaDecayEngine : BaseSearchEngine
6 | {
7 |
8 | public KarmaDecayEngine() : base("http://karmadecay.com/search/?q=")
9 | { }
10 |
11 | public override SearchEngineOptions EngineOption => SearchEngineOptions.KarmaDecay;
12 |
13 | public override void Dispose() { }
14 |
15 | /*protected override async Task> GetNodesAsync(IDocument doc)
16 | {
17 | var results = doc.QuerySelectorAll(NodesSelector).Cast().ToList();
18 |
19 | return await Task.FromResult(results);
20 | }*/
21 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Search/YandexEngine.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: YandexEngine.cs
2 | // Date: 2024/06/06 @ 14:06:00
3 |
4 | using System.Diagnostics;
5 | using System.Text.Json;
6 | using System.Text.Json.Nodes;
7 | using System.Text.Json.Serialization;
8 | using AngleSharp.Dom;
9 | using AngleSharp.Html.Dom;
10 | using AngleSharp.Html.Parser;
11 | using AngleSharp.XPath;
12 | using Flurl;
13 | using Flurl.Http;
14 | using Kantan.Net.Utilities;
15 | using Kantan.Text;
16 | using Microsoft.Extensions.Logging;
17 | using SmartImage.Lib.Images.Uni;
18 | using SmartImage.Lib.Results;
19 | using SmartImage.Lib.Results.Data;
20 |
21 | // ReSharper disable SuggestVarOrType_SimpleTypes
22 |
23 | #pragma warning disable 8602
24 |
25 | namespace SmartImage.Lib.Engines.Impl.Search;
26 |
27 | public sealed class YandexEngine : BaseSearchEngine
28 | {
29 |
30 | public const string URL_YANDEX = "https://yandex.com/";
31 |
32 | public override SearchEngineOptions EngineOption => SearchEngineOptions.Yandex;
33 |
34 | protected override string[] ErrorBodyMessages
35 | =>
36 | [
37 | "Please confirm that you and not a robot are sending requests",
38 | "Изображение не загрузилось, попробуйте загрузить другое."
39 |
40 | // "No matching images found"
41 | ];
42 |
43 | public YandexEngine() : base("https://yandex.com/images/search?rpt=imageview&url=")
44 | {
45 | Timeout = TimeSpan.FromSeconds(30);
46 | }
47 |
48 | private static (int? w, int? h) ParseResolution(string resText)
49 | {
50 | string[] resFull = resText.Split(Strings.Constants.MUL_SIGN);
51 |
52 | int? w = null, h = null;
53 |
54 | if (resFull.Length == 1 && resFull[0] == resText) {
55 | const string TIMES_DELIM = "×";
56 |
57 | if (resText.Contains(TIMES_DELIM)) {
58 | resFull = resText.Split(TIMES_DELIM);
59 | }
60 | }
61 |
62 | if (resFull.Length == 2) {
63 | w = Int32.Parse(resFull[0]);
64 | h = Int32.Parse(resFull[1]);
65 | }
66 |
67 | return (w, h);
68 | }
69 |
70 | #region Overrides of BaseSearchEngine
71 |
72 | protected override Url GetRawUrl(SearchQuery query)
73 | {
74 | var url = BaseUrl.Clone();
75 | url.QueryParams.AddOrReplace("url", query.Upload);
76 | url.QueryParams.AddOrReplace("cbir_page", "sites");
77 | return url;
78 | }
79 |
80 | #endregion
81 |
82 |
83 | public override async Task GetResultAsync(SearchQuery query, CancellationToken token = default)
84 | {
85 | // var sr = await base.GetResultAsync(query, token);
86 |
87 | var url = GetRawUrl(query);
88 |
89 | var sr = new SearchResult(this)
90 | {
91 | RawUrl = url
92 | };
93 |
94 | lock (sr.Results) {
95 | sr.Results.Add(sr.RawResultItem);
96 | }
97 |
98 | IDocument doc = null;
99 |
100 | IFlurlResponse res = null;
101 |
102 | Stream str = null;
103 |
104 | try {
105 | res = await Client.Request(sr.RawUrl)
106 | .WithTimeout(Timeout)
107 | .GetAsync(cancellationToken: token);
108 |
109 | str = await res.GetStreamAsync();
110 |
111 | var parser = new HtmlParser();
112 | doc = await parser.ParseDocumentAsync(str);
113 |
114 | var imagesAppNode = doc.Body.SelectSingleNode(Serialization.S_Yandex_Json);
115 | var json = imagesAppNode.TryGetAttribute("data-state");
116 |
117 | var jsonNode = JsonNode.Parse(json);
118 | var sites = jsonNode["initialState"]["cbirSites"]["sites"];
119 | var sitesObj = sites.Deserialize();
120 | var sri = sitesObj.AsParallel().Select(e => e.ToItem(sr));
121 | sr.Results.AddRange(sri);
122 |
123 |
124 | }
125 | catch (Exception e) {
126 | // Console.WriteLine(e);
127 | // throw;
128 | doc = null;
129 | Logger.LogError(e, "{Name} error", Name);
130 |
131 | sr.Status = SearchResultStatus.UnknownError;
132 | }
133 | finally { }
134 |
135 |
136 | sr.Status = SearchResultStatus.Success;
137 | ret:
138 | sr.Update();
139 | res?.Dispose();
140 | str?.Dispose();
141 | doc?.Dispose();
142 | return sr;
143 | }
144 |
145 |
146 | public override void Dispose() { }
147 |
148 | }
149 |
150 | public record YandexImage
151 | {
152 |
153 | [JsonPropertyName("url")]
154 | public string Url { get; set; }
155 |
156 | [JsonPropertyName("height")]
157 | public int Height { get; set; }
158 |
159 | [JsonPropertyName("width")]
160 | public int Width { get; set; }
161 |
162 | }
163 |
164 | public record YandexSite : ISearchResultItemConvertable
165 | {
166 |
167 | [JsonPropertyName("title")]
168 | public string Title { get; set; }
169 |
170 | [JsonPropertyName("description")]
171 | public string Description { get; set; }
172 |
173 | [JsonPropertyName("url")]
174 | public string Url { get; set; }
175 |
176 | [JsonPropertyName("domain")]
177 | public string Domain { get; set; }
178 |
179 | [JsonPropertyName("thumb")]
180 | public YandexImage Thumb { get; set; }
181 |
182 | [JsonPropertyName("originalImage")]
183 | public YandexImage OriginalImage { get; set; }
184 |
185 | public SearchResultItem ToItem(SearchResult sr)
186 | {
187 | return new SearchResultItem(sr)
188 | {
189 | Title = Title,
190 | Description = Description,
191 | Url = OriginalImage.Url,
192 | Site = Domain,
193 | Thumbnail = Thumb.Url.StartsWith("//") ? "https:" + Thumb.Url : Thumb.Url
194 | };
195 | }
196 |
197 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Upload/CatboxEngine.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel;
2 | using Flurl.Http;
3 | using Flurl.Http.Content;
4 | using Kantan.Net.Utilities;
5 | using SmartImage.Lib.Results;
6 |
7 | namespace SmartImage.Lib.Engines.Impl.Upload;
8 |
9 | public abstract class BaseCatboxEngine : BaseUploadEngine
10 | {
11 |
12 | public override UploadEngineOptions UploadOption => UploadEngineOptions.Catbox;
13 |
14 | public override async Task UploadFileAsync(string file, CancellationToken ct = default)
15 | {
16 | Verify(file);
17 |
18 | var response = await Client.Request(EndpointUrl)
19 | .WithSettings(r => { r.Timeout = Timeout; })
20 | .WithHeaders(new
21 | {
22 | User_Agent = HttpUtilities.UserAgent
23 | })
24 | .PostMultipartAsync(mp =>
25 | {
26 | mp.AddFile("fileToUpload", file)
27 | .AddString("reqtype", "fileupload")
28 | .AddString("time", "1h")
29 | .AddString("userhash", string.Empty);
30 | }, cancellationToken: ct, completionOption: HttpCompletionOption.ResponseHeadersRead);
31 |
32 | return await ProcessResultAsync(response, ct).ConfigureAwait(false);
33 | }
34 |
35 | /*public async Task UploadFileAsync(Stream file, CancellationToken ct = default)
36 | {
37 |
38 | var response = await Client.Request(EndpointUrl)
39 | .WithSettings(r => { r.Timeout = Timeout; })
40 | .WithHeaders(new
41 | {
42 | User_Agent = HttpUtilities.UserAgent
43 | })
44 | .PostMultipartAsync(mp =>
45 | {
46 | mp.AddFile("fileToUpload", file, Path.GetTempFileName())
47 | .AddString("reqtype", "fileupload")
48 | .AddString("time", "1h")
49 | .AddString("userhash", string.Empty);
50 | }, cancellationToken: ct, completionOption: HttpCompletionOption.ResponseHeadersRead);
51 |
52 | return await ProcessResultAsync(response, ct).ConfigureAwait(false);
53 | }*/
54 |
55 | protected BaseCatboxEngine(string s) : base(s) { }
56 |
57 | }
58 |
59 | public sealed class CatboxEngine : BaseCatboxEngine
60 | {
61 |
62 | public override long? MaxSize => 200_000_000L;
63 |
64 | public CatboxEngine() : base("https://catbox.moe/user/api.php") { }
65 |
66 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Upload/LitterboxEngine.cs:
--------------------------------------------------------------------------------
1 | using Flurl.Http;
2 |
3 | // ReSharper disable StringLiteralTypo
4 |
5 | // ReSharper disable UnusedMember.Global
6 |
7 | namespace SmartImage.Lib.Engines.Impl.Upload;
8 |
9 | public sealed class LitterboxEngine : BaseCatboxEngine
10 | {
11 |
12 | public override UploadEngineOptions UploadOption => UploadEngineOptions.Litterbox;
13 |
14 |
15 | public override long? MaxSize => 1_000_000_000L;
16 |
17 | public LitterboxEngine() : base("https://litterbox.catbox.moe/resources/internals/api.php") { }
18 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Upload/PomfEngine.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Flurl.Http;
8 | using Kantan.Net.Utilities;
9 | using SmartImage.Lib.Results;
10 |
11 | namespace SmartImage.Lib.Engines.Impl.Upload;
12 |
13 | public sealed class PomfEngine : BaseUploadEngine
14 | {
15 |
16 | public override UploadEngineOptions UploadOption => UploadEngineOptions.Pomf;
17 |
18 | public PomfEngine() : base("https://pomf.lain.la/upload.php") { }
19 |
20 | public override long? MaxSize => 1_000_000_000;
21 |
22 |
23 | public override async Task UploadFileAsync(string file, CancellationToken ct = default)
24 | {
25 | Verify(file);
26 |
27 | var response = await Client.Request(EndpointUrl)
28 | .WithSettings(r =>
29 | {
30 | r.Timeout = Timeout;
31 | }).OnError(r =>
32 | {
33 | r.ExceptionHandled = true;
34 | Trace.WriteLine($"{r.Exception.Message}: {file} {Name}");
35 | })
36 | .PostMultipartAsync(mp =>
37 | {
38 | mp.AddFile("files[]", file);
39 | }, cancellationToken: ct);
40 |
41 | if (response == null) {
42 | Debugger.Break();
43 | return new UploadResult()
44 | {
45 | IsValid = false
46 | };
47 | }
48 |
49 | var pr = await response.GetJsonAsync();
50 |
51 | var bur = new UploadResult()
52 | {
53 | Value = pr,
54 | Size = pr.Files[0].Size,
55 | Url = pr.Files[0].Url,
56 | IsValid = pr.Success,
57 | Response = response
58 | };
59 |
60 | return bur;
61 | }
62 |
63 | }
64 |
65 | public sealed class PomfResult
66 | {
67 |
68 | public bool Success { get; set; }
69 |
70 | public PomfFileResult[] Files { get; set; }
71 |
72 | }
73 |
74 | public sealed class PomfFileResult
75 | {
76 |
77 | public string Hash { get; set; }
78 |
79 | public string Name { get; set; }
80 |
81 | public string Url { get; set; }
82 |
83 | public long Size { get; set; }
84 |
85 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/Impl/Upload/UploadEngineOptions.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: UploadEngineOptions.cs
2 | // Date: 2024/12/06 @ 03:12:53
3 |
4 | namespace SmartImage.Lib.Engines.Impl.Upload;
5 |
6 | public enum UploadEngineOptions
7 | {
8 |
9 | None = 0,
10 |
11 | ///
12 | ///
13 | ///
14 | Catbox,
15 |
16 | ///
17 | ///
18 | ///
19 | Litterbox,
20 |
21 | ///
22 | ///
23 | ///
24 | Pomf,
25 |
26 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/SearchEngineOptions.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable UnusedMember.Global
2 |
3 | using SmartImage.Lib.Engines.Impl.Search;
4 | using SmartImage.Lib.Engines.Impl.Search.Other;
5 |
6 | namespace SmartImage.Lib.Engines;
7 |
8 | ///
9 | /// Search engine options
10 | ///
11 | [Flags]
12 | public enum SearchEngineOptions
13 | {
14 |
15 | ///
16 | /// No engines
17 | ///
18 | None = 0,
19 |
20 | ///
21 | /// Automatic (use best result)
22 | ///
23 | Auto = 1,
24 |
25 | ///
26 | ///
27 | ///
28 | SauceNao = 1 << 1,
29 |
30 | ///
31 | ///
32 | ///
33 | ImgOps = 1 << 2,
34 |
35 | ///
36 | ///
37 | ///
38 | GoogleImages = 1 << 3,
39 |
40 | ///
41 | ///
42 | ///
43 | TinEye = 1 << 4,
44 |
45 | ///
46 | ///
47 | ///
48 | Iqdb = 1 << 5,
49 |
50 | ///
51 | ///
52 | ///
53 | TraceMoe = 1 << 6,
54 |
55 | ///
56 | ///
57 | ///
58 | KarmaDecay = 1 << 7,
59 |
60 | ///
61 | ///
62 | ///
63 | Yandex = 1 << 8,
64 |
65 | ///
66 | ///
67 | ///
68 | Bing = 1 << 9,
69 |
70 | ///
71 | ///
72 | ///
73 | Ascii2D = 1 << 10,
74 |
75 | ///
76 | ///
77 | ///
78 | RepostSleuth = 1 << 11,
79 |
80 | ///
81 | ///
82 | ///
83 | EHentai = 1 << 12,
84 |
85 | ///
86 | ///
87 | ///
88 | ArchiveMoe = 1 << 13,
89 |
90 | ///
91 | ///
92 | ///
93 | Iqdb3D = 1 << 14,
94 |
95 | ///
96 | ///
97 | ///
98 | Fluffle = 1 << 15,
99 |
100 | ///
101 | ///
102 | ///
103 | GoogleLens = 1 << 16,
104 |
105 |
106 | #region
107 |
108 | ///
109 | /// All engines
110 | ///
111 | All = SauceNao | ImgOps | GoogleImages | TinEye | Iqdb | TraceMoe | KarmaDecay | Yandex | Bing |
112 | Ascii2D | RepostSleuth | EHentai | ArchiveMoe | Iqdb3D | Fluffle | GoogleLens,
113 |
114 | Artwork = SauceNao | Iqdb | Ascii2D | EHentai,
115 |
116 | Advanced = All & ~ (Bing | GoogleImages | ImgOps | KarmaDecay)
117 |
118 | #endregion
119 |
120 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Engines/WebSearchEngine.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: WebSearchEngine.cs
2 | // Date: 2024/06/06 @ 14:06:00
3 |
4 | using System.Diagnostics;
5 | using AngleSharp.Dom;
6 | using AngleSharp.Html.Parser;
7 | using AngleSharp.XPath;
8 | using Flurl.Http;
9 | using Kantan.Diagnostics;
10 | using Kantan.Net.Utilities;
11 | using Microsoft.Extensions.Logging;
12 | using SmartImage.Lib.Results;
13 | using SmartImage.Lib.Results.Data;
14 |
15 | namespace SmartImage.Lib.Engines;
16 |
17 | abstract class ParsedSearchEngine : BaseSearchEngine
18 | {
19 | protected ParsedSearchEngine([NN] Url baseUrl) : base(baseUrl) { }
20 |
21 | protected abstract ValueTask> GetRawItems();
22 |
23 | }
24 |
25 | public abstract class WebSearchEngine : BaseSearchEngine
26 | {
27 |
28 | protected abstract string NodesSelector { get; }
29 |
30 | protected WebSearchEngine([NN] Url baseUrl) : base(baseUrl) { }
31 |
32 |
33 | public override async Task GetResultAsync(SearchQuery query, CancellationToken token = default)
34 | {
35 | var res = await base.GetResultAsync(query, token);
36 |
37 | IDocument doc = null;
38 |
39 | if (res.Status == SearchResultStatus.IllegalInput) {
40 | goto ret;
41 | }
42 |
43 | try {
44 | doc = await GetDocumentAsync(res, query: query, token: token);
45 | }
46 | catch (Exception e) {
47 | Logger.LogError(e, "{Name} error", e);
48 |
49 | }
50 |
51 | if (!Validate(doc, res)) {
52 | goto ret;
53 | }
54 |
55 | var nodes = (await GetNodes(doc));
56 |
57 | foreach (INode node in nodes) {
58 | if (token.IsCancellationRequested) {
59 | break;
60 | }
61 |
62 | var sri = await ParseResultItem(node, res);
63 |
64 | if (sri is { }) {
65 | res.Results.Add(sri);
66 | }
67 | }
68 |
69 | Logger.LogInformation("{Name} :: {RawUrl} document {DocLength}", Name, res.RawUrl, doc?.TextContent?.Length);
70 |
71 | res.Status = SearchResultStatus.Success;
72 |
73 | ret:
74 | res.Update();
75 | doc?.Dispose();
76 | Logger.LogDebug("Disposing {Name} doc", Name);
77 | return res;
78 | }
79 |
80 | [ICBN]
81 | [MURV]
82 | protected virtual async Task GetDocumentAsync(SearchResult sr, SearchQuery query,
83 | CancellationToken token = default)
84 | {
85 |
86 | var parser = new HtmlParser();
87 |
88 | try {
89 |
90 | var res = await Client.Request(sr.RawUrl)
91 | .WithCookies(out var cj)
92 | .WithTimeout(Timeout)
93 | .WithHeaders(new
94 | {
95 | User_Agent = HttpUtilities.UserAgent
96 | })
97 | /*.OnError(s =>
98 | {
99 | s.ExceptionHandled = true;
100 | })*/
101 | .GetAsync(cancellationToken: token);
102 |
103 | var str = await res.GetStreamAsync();
104 |
105 | var document = await parser.ParseDocumentAsync(str, token);
106 |
107 | return document;
108 |
109 | }
110 | catch (Exception e) {
111 | // return await Task.FromException(e);
112 | Logger.LogError(e, "{Name} failed to get doc", Name);
113 | return null;
114 | }
115 | }
116 |
117 | protected abstract ValueTask ParseResultItem(INode n, SearchResult r);
118 |
119 | protected virtual ValueTask> GetNodes(IDocument d)
120 | {
121 | return ValueTask.FromResult>(d.Body.SelectNodes(NodesSelector));
122 | }
123 |
124 | #region Overrides of BaseSearchEngine
125 |
126 | protected override Url GetRawUrl(SearchQuery query)
127 | {
128 | return base.GetRawUrl(query);
129 | }
130 |
131 | #endregion
132 |
133 | protected bool Validate([CBN] IDocument doc, SearchResult sr)
134 | {
135 | if (doc is null or { Body: null }) {
136 | return false;
137 | }
138 |
139 | foreach (string s in ErrorBodyMessages) {
140 | if (doc.Body.TextContent.Contains(s)) {
141 | return false;
142 | }
143 | }
144 |
145 | return true;
146 |
147 | }
148 |
149 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Images/Uni/UniImageFile.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: UniImageFile.cs
2 | // Date: 2024/07/17 @ 02:07:16
3 |
4 | using System.IO.MemoryMappedFiles;
5 | using Microsoft;
6 |
7 | namespace SmartImage.Lib.Images.Uni;
8 |
9 | public class UniImageFile : UniImage
10 | {
11 |
12 | internal UniImageFile(object value, FileInfo fi)
13 | : base(value, UniImageType.File)
14 | {
15 | FileInfo = fi;
16 | FilePath = ValueString;
17 | }
18 |
19 | public FileInfo FileInfo { get; }
20 |
21 | public override string WriteToFile(string fn = null)
22 | {
23 | if (!HasFile) {
24 | throw new FileNotFoundException(ValueString);
25 | }
26 |
27 | return ValueString;
28 | }
29 |
30 |
31 | public override async ValueTask AllocAsync(CancellationToken ct = default)
32 | {
33 | if (!HasStream) {
34 | var fullName = FileInfo.FullName;
35 |
36 | Stream = File.OpenRead(fullName);
37 | }
38 | return HasStream;
39 | }
40 |
41 | public static bool IsFileType(object o, out FileInfo f)
42 | {
43 | f = null;
44 |
45 | if (o is string { } s && File.Exists(s)) {
46 | f = new FileInfo(s);
47 | }
48 |
49 | return f != null;
50 | }
51 |
52 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Images/Uni/UniImageStream.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: UniImageStream.cs
2 | // Date: 2024/07/17 @ 02:07:31
3 |
4 | using Novus.Streams;
5 |
6 | namespace SmartImage.Lib.Images.Uni;
7 |
8 | public class UniImageStream : UniImage
9 | {
10 |
11 |
12 | internal UniImageStream(object value, Stream str)
13 | : base(value, str, UniImageType.Stream) { }
14 |
15 |
16 | public static bool IsStreamType(object o, out Stream t2)
17 | {
18 | t2 = Stream.Null;
19 |
20 | if (o is Stream sz) {
21 | t2 = sz;
22 | }
23 |
24 | return t2 != Stream.Null;
25 | }
26 |
27 | public override ValueTask AllocAsync(CancellationToken ct = default)
28 | {
29 | return ValueTask.FromResult(HasStream);
30 | }
31 |
32 | public override string WriteToFile(string fn = null)
33 | => WriteStreamToFile(fn);
34 |
35 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Images/Uni/UniImageUri.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: UniImageUri.cs
2 | // Date: 2024/07/17 @ 02:07:26
3 |
4 | using System.Collections.Immutable;
5 | using System.Net;
6 | using Flurl.Http;
7 |
8 | namespace SmartImage.Lib.Images.Uni;
9 |
10 | public class UniImageUri : UniImage
11 | {
12 |
13 | public IFlurlResponse Response { get; private set; }
14 |
15 | [MNNW(true, nameof(Response))]
16 | public bool HasResponse => Response != null;
17 |
18 | public Url Url { get; }
19 |
20 | internal UniImageUri(object value, Url url, IFlurlResponse response = null)
21 | : base(value, Stream.Null, UniImageType.Uri)
22 | {
23 | Url = url;
24 | Response = response;
25 | }
26 |
27 | public override string WriteToFile(string fn = null)
28 | => WriteStreamToFile(fn);
29 |
30 | public static bool IsUriType(object o, out Url u)
31 | {
32 | u = o switch
33 | {
34 | Url u2 => u2,
35 | string s when Url.IsValid(s) => s,
36 | _ => null
37 | };
38 |
39 | if (u == null) {
40 | return false;
41 | }
42 |
43 | var scheme = u.Scheme;
44 |
45 | return LegalSchemes.Contains(scheme);
46 | }
47 |
48 | public override async ValueTask AllocAsync(CancellationToken ct = default)
49 | {
50 | if (!HasResponse) {
51 | Response = await GetResponseAsync(Url, ct);
52 | }
53 |
54 | if (!HasStream) {
55 | Stream = await Response.GetStreamAsync();
56 | }
57 |
58 | return HasResponse && HasStream;
59 | }
60 |
61 | public async ValueTask AllocResponseAsync(CancellationToken ct = default)
62 | {
63 | Response = await GetResponseAsync(Url, ct);
64 |
65 | return HasResponse;
66 | }
67 |
68 | public static readonly ImmutableArray RestrictedSchemes = ["file", "javascript", "cpu"];
69 |
70 | public static readonly ImmutableArray LegalSchemes = ["http", "https"];
71 |
72 | public static async ValueTask GetResponseAsync(Url value, CancellationToken ct)
73 | {
74 | // value = value.CleanString();
75 | /*if (value.Scheme == "javascript") {
76 | throw new ArgumentException($"{value}");
77 | }*/
78 |
79 | var req1 = ImageScanner.Client.Request(value);
80 | var req = await ValueTask.FromResult(req1);
81 |
82 | /*.AllowAnyHttpStatus()
83 | .WithHeaders(new
84 | {
85 | // todo
86 | User_Agent = R1.UserAgent1,
87 | });*/
88 |
89 | var res = await req.GetAsync(cancellationToken: ct);
90 |
91 | if (res.ResponseMessage.StatusCode == HttpStatusCode.NotFound) {
92 | throw new ArgumentException($"{value} returned {HttpStatusCode.NotFound}");
93 |
94 | }
95 |
96 | return res;
97 | }
98 |
99 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Results/Data/IHashable.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: IHashable.cs
2 | // Date: 2024/11/13 @ 16:11:29
3 |
4 | using System.Security.Cryptography;
5 | using SmartImage.Lib.Utilities;
6 |
7 | namespace SmartImage.Lib.Results.Data;
8 |
9 | public interface IHashable
10 | {
11 | public Lazy Hash { get; }
12 |
13 | public const ulong HASH_ERROR = UInt64.MaxValue;
14 |
15 | public bool HasHash => Hash is { IsValueCreated: true, Value: not HASH_ERROR };
16 |
17 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Results/Data/IItemConvertable.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.Lib IResultParsable.cs
2 | // 2023-07-04 @ 1:27 PM
3 |
4 |
5 | // Read S SmartImage.Lib IResultParsable.cs
6 | // 2023-07-04 @ 1:27 PM
7 |
8 | using SmartImage.Lib.Results;
9 |
10 | namespace SmartImage.Lib.Results.Data;
11 |
12 | public interface IItemConvertable
13 | {
14 |
15 | public TItem ToItem(SearchResult sr);
16 |
17 | }
18 |
19 | public interface ISearchResultItemConvertable
20 | : IItemConvertable { }
--------------------------------------------------------------------------------
/SmartImage.Lib/Results/Data/IItemConverter.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: IResultConverter.cs
2 | // Date: 2025/04/16 @ 01:04:44
3 |
4 | using AngleSharp.Dom;
5 |
6 | namespace SmartImage.Lib.Results.Data;
7 |
8 | public interface IItemConverter
9 | : IItemConvertable
10 | /*where TResult : IResultConvertable*/
11 | {
12 |
13 | // public TResult2 ToResultItem(SearchResult sr);
14 |
15 | public static abstract TResult Parse(INode n);
16 |
17 | }
18 |
19 | public interface ISearchResultItemConverter
20 | : IItemConverter { }
--------------------------------------------------------------------------------
/SmartImage.Lib/Results/Data/ISearchConfigReceiver.cs:
--------------------------------------------------------------------------------
1 | // Read Stanton SmartImage.Lib ISearchConfigReceiver.cs
2 | // 2023-01-13 @ 11:09 PM
3 |
4 | namespace SmartImage.Lib.Results.Data;
5 |
6 | public interface ISearchConfigReceiver
7 | {
8 | public ValueTask ApplyConfigAsync(SearchConfig cfg, CancellationToken ct = default);
9 |
10 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Results/Data/ISimilarity.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: ISimilarity.cs
2 | // Date: 2024/11/13 @ 16:11:26
3 |
4 | namespace SmartImage.Lib.Results.Data;
5 |
6 | #pragma warning disable CS0168
7 | public interface ISimilarity
8 | {
9 |
10 | // todo
11 | public double? Similarity { get; }
12 |
13 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Results/Data/ISize.cs:
--------------------------------------------------------------------------------
1 | // Deci SmartImage.Lib IItemSize.cs
2 | // $File.CreatedYear-$File.CreatedMonth-24 @ 15:40
3 |
4 | namespace SmartImage.Lib.Results.Data;
5 |
6 | // TODO: delete
7 |
8 | public interface ISize
9 | {
10 | public long Size { get; }
11 |
12 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Results/SearchResult.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Concurrent;
2 | using System.ComponentModel;
3 | using System.Diagnostics;
4 | using System.Net;
5 | using System.Runtime.CompilerServices;
6 | using System.Text.Json.Serialization;
7 | using System.Threading.Channels;
8 | using AngleSharp.Html.Parser;
9 | using Flurl.Http;
10 | using Kantan.Diagnostics;
11 | using Kantan.Net.Utilities;
12 | using SmartImage.Lib.Engines;
13 | using SmartImage.Lib.Images;
14 | using SmartImage.Lib.Utilities;
15 | using static SmartImage.Lib.Engines.BaseSearchEngine;
16 |
17 | namespace SmartImage.Lib.Results;
18 |
19 | #nullable disable
20 |
21 | public enum SearchResultStatus
22 | {
23 |
24 | ///
25 | /// N/A
26 | ///
27 | None = 0,
28 |
29 | ///
30 | /// Result obtained successfully
31 | ///
32 | Success,
33 |
34 | ///
35 | /// Engine is on cooldown due to too many requests
36 | ///
37 | Cooldown,
38 |
39 | ///
40 | /// Obtaining results failed due to an engine error
41 | ///
42 | UnknownError,
43 |
44 | IllegalInput,
45 |
46 | ///
47 | /// Engine is unavailable
48 | ///
49 | Unavailable
50 |
51 | }
52 |
53 | [Flags]
54 | public enum SearchResultFlags
55 | {
56 |
57 | None = 0,
58 |
59 | ///
60 | /// Engine returned no results
61 | ///
62 | NoResults = 1 << 0,
63 |
64 | ///
65 | /// Result is extraneous
66 | ///
67 | Extraneous = 1 << 1,
68 |
69 | }
70 |
71 | ///
72 | /// Root search result returned by a
73 | ///
74 | public class SearchResult : IDisposable, INotifyPropertyChanged
75 | {
76 |
77 | ///
78 | /// Engine which returned this result
79 | ///
80 | [JI]
81 | public BaseSearchEngine Engine { get; }
82 |
83 | // todo: make the engine reference weak
84 |
85 | ///
86 | /// Undifferentiated result URL
87 | ///
88 | public Url RawUrl { get; internal set; }
89 |
90 | [JI]
91 | public bool HasResults => !Flags.HasFlagFast(SearchResultFlags.NoResults);
92 |
93 | public bool IsSuccessful => Status.IsSuccessful();
94 |
95 | ///
96 | /// Results; first element should be
97 | ///
98 | [NN]
99 | public List Results { get; }
100 |
101 | [CBN]
102 | public string ErrorMessage { get; internal set; }
103 |
104 | public SearchResultStatus Status { get; internal set; }
105 |
106 | public SearchResultFlags Flags { get; internal set; }
107 |
108 | [CBN]
109 | public string Overview { get; internal set; }
110 |
111 | internal SearchResult(BaseSearchEngine bse)
112 | {
113 | Engine = bse;
114 | Results = [];
115 |
116 | // Results = [GetRawResultItem()];
117 | }
118 |
119 | public void Update()
120 | {
121 | if (Status.IsUnknown()) {
122 |
123 | }
124 | if (Status.IsError()) {
125 | return;
126 | }
127 |
128 | }
129 |
130 | public event PropertyChangedEventHandler PropertyChanged;
131 |
132 | private void OnPropertyChanged([CMN] string propertyName = null)
133 | {
134 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
135 | }
136 |
137 | private bool SetField(ref T field, T value, [CMN] string propertyName = null)
138 | {
139 | if (EqualityComparer.Default.Equals(field, value))
140 | return false;
141 |
142 | field = value;
143 | OnPropertyChanged(propertyName);
144 | return true;
145 | }
146 |
147 | [CBN]
148 | public SearchResultItem GetBestResult()
149 | {
150 | if (Results.Count == 0) {
151 | // This should never happen so long as results contains the raw item
152 | Debugger.Break();
153 | return null;
154 | }
155 |
156 | return Results.OrderByDescending(static r => r.Similarity)
157 | .FirstOrDefault(static r => Url.IsValid(r.Url));
158 | }
159 |
160 | [JI]
161 | public SearchResultItem RawResultItem
162 | {
163 | get
164 | {
165 | var rawCache = new SearchResultItem(this, true)
166 | {
167 | Url = RawUrl
168 | };
169 | return rawCache;
170 | }
171 | }
172 |
173 | public override string ToString()
174 | {
175 | return $"[{Engine.Name}] {RawUrl} | {Results.Count} | {Status} {ErrorMessage}";
176 | }
177 |
178 | public void Dispose()
179 | {
180 | Debug.WriteLine($"Disposing {Engine.Name} with {Results.Count}", LogCategories.C_VERBOSE);
181 |
182 | foreach (SearchResultItem item in Results) {
183 | item.Dispose();
184 | }
185 | }
186 |
187 | }
188 |
--------------------------------------------------------------------------------
/SmartImage.Lib/Results/UploadResult.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.Lib BaseUploadResponse.cs
2 | // 2023-05-28 @ 7:49 PM
3 |
4 | using Flurl.Http;
5 |
6 | namespace SmartImage.Lib.Results;
7 |
8 | public sealed class UploadResult : IDisposable
9 | {
10 |
11 | public Url Url { get; init; }
12 |
13 | [MN]
14 | public IFlurlResponse Response { get; init; }
15 |
16 | public long? Size { get; init; }
17 |
18 | public bool IsValid { get; init; }
19 |
20 | [CBN]
21 | public object Value { get; init; }
22 |
23 | /*public static implicit operator Url(UploadResult result)
24 | {
25 | if (!result.IsValid) {
26 | throw new Exception();
27 | }
28 |
29 | return result.Url;
30 | }*/
31 |
32 | public void Dispose()
33 | {
34 | Response?.Dispose();
35 | }
36 |
37 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/SearchQuery.cs:
--------------------------------------------------------------------------------
1 | global using MN = System.Diagnostics.CodeAnalysis.MaybeNullAttribute;
2 | global using CBN = JetBrains.Annotations.CanBeNullAttribute;
3 | global using NN = System.Diagnostics.CodeAnalysis.NotNullAttribute;
4 | global using MNNW = System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute;
5 | global using ISImage = SixLabors.ImageSharp.Image;
6 | using System.Diagnostics;
7 | using System.Diagnostics.CodeAnalysis;
8 | using System.Drawing;
9 | using System.Runtime.CompilerServices;
10 | using System.Security.Cryptography;
11 | using JetBrains.Annotations;
12 | using Microsoft;
13 | using Novus.FileTypes.Uni;
14 | using Novus.Streams;
15 | using Novus.Win32;
16 | using SixLabors.ImageSharp;
17 | using SixLabors.ImageSharp.Formats.Png;
18 | using SixLabors.ImageSharp.Processing;
19 | using SmartImage.Lib.Engines;
20 | using SmartImage.Lib.Engines.Impl.Upload;
21 | using SmartImage.Lib.Results.Data;
22 | using SmartImage.Lib.Results;
23 | using SmartImage.Lib.Utilities;
24 | using SixLabors.ImageSharp.Formats;
25 | using SmartImage.Lib.Images.Uni;
26 |
27 | [assembly: InternalsVisibleTo("SmartImage")]
28 | [assembly: InternalsVisibleTo("SmartImage.UI")]
29 | [assembly: InternalsVisibleTo("SmartImage.Rdx")]
30 | [assembly: InternalsVisibleTo("Test")]
31 | [assembly: InternalsVisibleTo("SmartImage.Lib.UnitTest")]
32 |
33 | namespace SmartImage.Lib;
34 |
35 | public sealed class SearchQuery : IDisposable, IEquatable
36 | {
37 |
38 | [MN]
39 | public Url Upload { get; private set; }
40 |
41 | [MNNW(true, nameof(Upload))]
42 | public bool IsUploaded => Url.IsValid(Upload);
43 |
44 | public UniImage Source { get; }
45 |
46 | internal SearchQuery(UniImage img, Url upload)
47 | {
48 | Source = img;
49 | Upload = upload;
50 |
51 | // Size = Uni == null ? default : Uni.Stream.Length;
52 | }
53 |
54 | internal SearchQuery(UniImage img) : this(img, null) { }
55 |
56 | static SearchQuery() { }
57 |
58 | public static readonly SearchQuery Null = new(UniImage.Null);
59 |
60 | public static async Task TryCreateAsync(object o, CancellationToken t = default)
61 | {
62 | var task = await UniImage.TryCreateAsync(o, ct: t);
63 |
64 | if (task != UniImage.Null) {
65 | return new SearchQuery(task);
66 |
67 | }
68 | else {
69 | return Null;
70 | }
71 | }
72 |
73 | public async Task UploadAsync(BaseUploadEngine engine = null, CancellationToken ct = default)
74 | {
75 | if (IsUploaded) {
76 | return Upload;
77 | }
78 |
79 |
80 | if (Source.IsUri) {
81 | Upload = Source.ValueString;
82 |
83 | // Size = BaseSearchEngine.NA_SIZE;
84 | // var fmt = await ISImage.DetectFormatAsync(Stream);
85 |
86 | Debug.WriteLine($"Skipping upload for {Source.ValueString}", nameof(UploadAsync));
87 | }
88 | else {
89 | // fu = await test(fu);
90 |
91 | string fu;
92 |
93 | if (Source.IsFile) {
94 | fu = Source.ValueString;
95 | }
96 | else {
97 | // fu = Source.WriteToFile();
98 | fu = null;
99 | if (Source.TryWriteToFile()) {
100 | fu = Source.FilePath;
101 | }
102 | Trace.WriteLine($"Wrote to file {fu}");
103 | }
104 |
105 | engine ??= BaseUploadEngine.Default;
106 |
107 | UploadResult u = await engine.UploadFileAsync(fu, ct);
108 | Url url;
109 |
110 | if (!u.IsValid) {
111 | url = null;
112 | Debug.WriteLine($"{u} is invalid!");
113 |
114 | // Debugger.Break();
115 | }
116 | else {
117 | url = u.Url;
118 |
119 | }
120 |
121 | // TODO: AUTO-RETRY
122 | /*
123 | UploadResult u = await UploadAutoAsync(engine, fu, ct);
124 | Url url = u?.Url;
125 | */
126 |
127 | /*if (!u.IsValid) {
128 | engine = BaseUploadEngine.All[Array.IndexOf(BaseUploadEngine.All, engine) + 1];
129 | Debug.WriteLine($"{u.Response.ResponseMessage} failed, retrying with {engine.Name}");
130 | u = await engine.UploadFileAsync(Uni.Value.ToString(), ct);
131 | }*/
132 |
133 | Upload = url;
134 |
135 | /*if (u.Response is { }) {
136 | Size = NetHelper.GetContentLength(u.Response) ?? Size;
137 | }*/
138 | // Size = u.Size ?? Size;
139 | u.Dispose();
140 | }
141 |
142 | return Upload;
143 | }
144 |
145 | public void Dispose()
146 | {
147 | Trace.WriteLine($"Disposing {Source}");
148 | Source?.Dispose();
149 | }
150 |
151 | public override string ToString()
152 | {
153 | return $"{Source}: {IsUploaded}";
154 | }
155 |
156 | #region Equality members
157 |
158 | public bool Equals(SearchQuery other)
159 | {
160 | if (other is null) return false;
161 | if (ReferenceEquals(this, other)) return true;
162 |
163 | return Equals(Source, other.Source) && Equals(Upload, other.Upload);
164 | }
165 |
166 | public override bool Equals(object obj)
167 | {
168 | return ReferenceEquals(this, obj) || (obj is SearchQuery other && Equals(other));
169 | }
170 |
171 | public override int GetHashCode()
172 | {
173 | // return HashCode.Combine(Uni, Upload, Size);
174 | return HashCode.Combine(Source);
175 |
176 | // return Uni.GetHashCode();
177 | }
178 |
179 | public static bool operator ==(SearchQuery left, SearchQuery right)
180 | {
181 | return Equals(left, right);
182 | }
183 |
184 | public static bool operator !=(SearchQuery left, SearchQuery right)
185 | {
186 | return !Equals(left, right);
187 | }
188 |
189 | #endregion
190 |
191 | }
192 |
--------------------------------------------------------------------------------
/SmartImage.Lib/SmartImage.Lib.csproj.DotSettings:
--------------------------------------------------------------------------------
1 |
5 | UI
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/BaseSearchEngineTypeConverter.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: BaseSearchEngineTypeConverter.cs
2 | // Date: 2024/12/11 @ 23:12:21
3 |
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using SmartImage.Lib.Engines;
7 |
8 | namespace SmartImage.Lib.Utilities;
9 |
10 | public sealed class BaseSearchEngineTypeConverter : JsonConverter
11 | {
12 |
13 | #region Overrides of JsonConverter
14 |
15 | public override BaseSearchEngine Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
16 | {
17 | throw new NotImplementedException();
18 | }
19 |
20 | public override void Write(Utf8JsonWriter writer, BaseSearchEngine value, JsonSerializerOptions options)
21 | {
22 | writer.WriteString(nameof(BaseSearchEngine.Name), value.Name);
23 | }
24 |
25 | #endregion
26 |
27 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/Diagnostics/AppSupport.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: AppSupport.cs
2 | // Date: 2024/12/04 @ 22:12:34
3 |
4 | #pragma warning disable IDE1006
5 | global using USI = JetBrains.Annotations.UsedImplicitlyAttribute;
6 | using System.Reflection;
7 | using System.Text.Json.Serialization;
8 | using Flurl.Http;
9 | using JetBrains.Annotations;
10 | using Kantan.Net.Utilities;
11 | using Microsoft.Extensions.Logging;
12 |
13 | // ReSharper disable InconsistentNaming
14 |
15 | namespace SmartImage.Lib.Utilities.Diagnostics;
16 |
17 | public static class AppSupport
18 | {
19 |
20 | internal static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
21 |
22 | internal static readonly Version Version = Assembly.GetName().Version;
23 |
24 | internal static readonly ILoggerFactory Factory =
25 | LoggerFactory.Create(builder => builder.AddDebug().SetMinimumLevel(LogLevel.Trace));
26 |
27 | public static async Task GetRepoReleasesAsync()
28 | {
29 | var r = await R1.Url_GitHubApi
30 | .WithAutoRedirect(true)
31 | .AllowAnyHttpStatus()
32 | .WithHeaders(new
33 | {
34 | User_Agent = HttpUtilities.UserAgent
35 | })
36 | .OnError(e => { e.ExceptionHandled = true; })
37 | .GetJsonAsync();
38 |
39 | if (r == null) {
40 | return [];
41 | }
42 |
43 | foreach (var x in r) {
44 | var s = x.tag_name[1..].Split('-')[0];
45 | x.IsRdx = x.name.Contains("Rdx", StringComparison.CurrentCultureIgnoreCase);
46 |
47 | if (Version.TryParse(s, out var xv)) {
48 | x.Version = xv;
49 | }
50 | }
51 |
52 | return r;
53 | }
54 |
55 | /*
56 | * HKEY_CLASSES_ROOT is an alias, a merging, of two other locations:
57 | * HKEY_CURRENT_USER\Software\Classes
58 | * HKEY_LOCAL_MACHINE\Software\Classes
59 | */
60 |
61 | public const string DIAG_SMRTIMG_EXP001 = "SMRTIMG_EXP001";
62 |
63 | }
64 |
65 | [USI(ImplicitUseTargetFlags.WithMembers)]
66 | public class GitHubReleaseAsset
67 | {
68 |
69 | public string url { get; set; }
70 |
71 | public int id { get; set; }
72 |
73 | public string node_id { get; set; }
74 |
75 | public string name { get; set; }
76 |
77 | public object label { get; set; }
78 |
79 | public GitHubUploader uploader { get; set; }
80 |
81 | public string content_type { get; set; }
82 |
83 | public string state { get; set; }
84 |
85 | public int size { get; set; }
86 |
87 | public int download_count { get; set; }
88 |
89 | public DateTime created_at { get; set; }
90 |
91 | public DateTime updated_at { get; set; }
92 |
93 | public string browser_download_url { get; set; }
94 |
95 | }
96 |
97 | [USI(ImplicitUseTargetFlags.WithMembers)]
98 | public class GitHubAuthor
99 | {
100 |
101 | public string login { get; set; }
102 |
103 | public int id { get; set; }
104 |
105 | public string node_id { get; set; }
106 |
107 | public string avatar_url { get; set; }
108 |
109 | public string gravatar_id { get; set; }
110 |
111 | public string url { get; set; }
112 |
113 | public string html_url { get; set; }
114 |
115 | public string followers_url { get; set; }
116 |
117 | public string following_url { get; set; }
118 |
119 | public string gists_url { get; set; }
120 |
121 | public string starred_url { get; set; }
122 |
123 | public string subscriptions_url { get; set; }
124 |
125 | public string organizations_url { get; set; }
126 |
127 | public string repos_url { get; set; }
128 |
129 | public string events_url { get; set; }
130 |
131 | public string received_events_url { get; set; }
132 |
133 | public string type { get; set; }
134 |
135 | public bool site_admin { get; set; }
136 |
137 | }
138 |
139 | [USI(ImplicitUseTargetFlags.WithMembers)]
140 | public class GitHubReactions
141 | {
142 |
143 | public string url { get; set; }
144 |
145 | public int total_count { get; set; }
146 |
147 | [JsonPropertyName("+1")]
148 | public int Plus1 { get; set; }
149 |
150 | [JsonPropertyName("-1")]
151 | public int Minus1 { get; set; }
152 |
153 | public int laugh { get; set; }
154 |
155 | public int hooray { get; set; }
156 |
157 | public int confused { get; set; }
158 |
159 | public int heart { get; set; }
160 |
161 | public int rocket { get; set; }
162 |
163 | public int eyes { get; set; }
164 |
165 | }
166 |
167 | [USI(ImplicitUseTargetFlags.WithMembers)]
168 | public class GitHubUploader
169 | {
170 |
171 | public string login { get; set; }
172 |
173 | public int id { get; set; }
174 |
175 | public string node_id { get; set; }
176 |
177 | public string avatar_url { get; set; }
178 |
179 | public string gravatar_id { get; set; }
180 |
181 | public string url { get; set; }
182 |
183 | public string html_url { get; set; }
184 |
185 | public string followers_url { get; set; }
186 |
187 | public string following_url { get; set; }
188 |
189 | public string gists_url { get; set; }
190 |
191 | public string starred_url { get; set; }
192 |
193 | public string subscriptions_url { get; set; }
194 |
195 | public string organizations_url { get; set; }
196 |
197 | public string repos_url { get; set; }
198 |
199 | public string events_url { get; set; }
200 |
201 | public string received_events_url { get; set; }
202 |
203 | public string type { get; set; }
204 |
205 | public bool site_admin { get; set; }
206 |
207 | }
208 |
209 | [USI(ImplicitUseTargetFlags.WithMembers)]
210 | public class GitHubRelease
211 | {
212 |
213 | [JI]
214 | [field: NonSerialized]
215 | public Version Version { get; set; }
216 |
217 | [JI]
218 | [field: NonSerialized]
219 | public bool IsRdx { get; set; }
220 |
221 |
222 | public string url { get; set; }
223 |
224 | public string assets_url { get; set; }
225 |
226 | public string upload_url { get; set; }
227 |
228 | public string html_url { get; set; }
229 |
230 | public int id { get; set; }
231 |
232 | public GitHubAuthor author { get; set; }
233 |
234 | public string node_id { get; set; }
235 |
236 | public string tag_name { get; set; }
237 |
238 | public string target_commitish { get; set; }
239 |
240 | public string name { get; set; }
241 |
242 | public bool draft { get; set; }
243 |
244 | public bool prerelease { get; set; }
245 |
246 | public DateTime created_at { get; set; }
247 |
248 | public DateTime published_at { get; set; }
249 |
250 | public List assets { get; set; }
251 |
252 | public string tarball_url { get; set; }
253 |
254 | public string zipball_url { get; set; }
255 |
256 | public string body { get; set; }
257 |
258 | public string discussion_url { get; set; }
259 |
260 | public GitHubReactions reactions { get; set; }
261 |
262 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/Diagnostics/HttpLoggingHandler.cs:
--------------------------------------------------------------------------------
1 | // Read Stanton SmartImage.Lib LoggingHandler.cs
2 | // 2023-02-14 @ 12:17 AM
3 |
4 | using JetBrains.Annotations;
5 | using Microsoft.Extensions.Logging;
6 |
7 | namespace SmartImage.Lib.Utilities.Diagnostics;
8 |
9 | internal class HttpLoggingHandler : DelegatingHandler
10 | {
11 |
12 | public HttpLoggingHandler(ILogger l)
13 | {
14 | m_logger = l;
15 | }
16 |
17 | public HttpLoggingHandler([NotNull] HttpMessageHandler innerHandler) : base(innerHandler) { }
18 |
19 | private readonly ILogger m_logger;
20 |
21 | protected override Task SendAsync(HttpRequestMessage request,
22 | CancellationToken cancellationToken)
23 | {
24 | m_logger.LogDebug("Request {Request}", request.RequestUri);
25 |
26 | return base.SendAsync(request, cancellationToken);
27 | }
28 |
29 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/Diagnostics/SmartImageException.cs:
--------------------------------------------------------------------------------
1 | namespace SmartImage.Lib.Utilities.Diagnostics;
2 |
3 | public sealed class SmartImageException : Exception
4 | {
5 |
6 | public SmartImageException() { }
7 |
8 | public SmartImageException([CBN] string message) : base(message) { }
9 |
10 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/FieldValueMap.cs:
--------------------------------------------------------------------------------
1 | using Kantan.Utilities;
2 | using Novus.Utilities;
3 |
4 | namespace SmartImage.Lib.Utilities;
5 |
6 | // todo
7 |
8 | public class FieldValueMap
9 | {
10 |
11 | public string Name { get; }
12 |
13 | public object Value { get; }
14 |
15 | public string FieldName { get; }
16 |
17 | public static FieldValueMap Find(T inst, string name)
18 | {
19 | var type = inst?.GetType() ?? typeof(T);
20 | return Find(inst, name, type);
21 | }
22 |
23 | private static FieldValueMap Find(T inst, string name, Type type)
24 | {
25 | var fld = type.GetAnyResolvedField(name);
26 |
27 | if (fld is not null) {
28 | return new FieldValueMap(name, fld.GetValue(inst), fld.Name);
29 | }
30 |
31 | return null;
32 | }
33 |
34 | public static FieldValueMap[] Find(T inst, string[] names)
35 | {
36 | var type = inst?.GetType() ?? typeof(T);
37 |
38 | var values = new FieldValueMap[names.Length];
39 | int i = 0;
40 |
41 | foreach (string name in names) {
42 | var val = Find(inst, name, type);
43 |
44 | if (val is not null) {
45 | values[i++] = val;
46 | }
47 | }
48 |
49 | Array.Resize(ref values, i);
50 |
51 | return values;
52 | }
53 |
54 | public static FieldValueMap[] Find(T inst, T2 names) where T2 : struct, Enum
55 | {
56 | var names2 = names.GetSetFlags().Select(x => x.ToString()).ToArray();
57 | return Find(inst, names2);
58 | }
59 |
60 | public FieldValueMap(string name, object value, string fieldName)
61 | {
62 | Name = name;
63 | Value = value;
64 | FieldName = fieldName;
65 | }
66 |
67 | public override string ToString()
68 | {
69 | return $"{nameof(Name)}: {Name} | {nameof(FieldName)}: {FieldName} | {nameof(Value)}: {Value}";
70 | }
71 |
72 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/Integration/BaseOSIntegration.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: BaseOSIntegration.cs
2 | // Date: 2024/12/05 @ 21:12:49
3 |
4 | using System.Diagnostics;
5 | using System.Runtime.Versioning;
6 | using Novus.OS;
7 |
8 | // ReSharper disable InconsistentNaming
9 |
10 | namespace SmartImage.Lib.Utilities.Integration;
11 |
12 | public abstract class BaseOSIntegration
13 | {
14 |
15 | #region
16 |
17 | public virtual bool IsRoot => FileSystem.IsRoot;
18 |
19 | public abstract bool IsContextMenuAdded { get; }
20 |
21 | public abstract string ProgramFilesPath { get; }
22 |
23 | public abstract string AppDataPath { get; }
24 |
25 | public virtual string PersonalPath { get; } = Environment.GetFolderPath(Environment.SpecialFolder.Personal);
26 |
27 | public abstract string LaunchArgs { get; }
28 |
29 | [CBN]
30 | public abstract string ChromePath { get; }
31 |
32 | [CBN]
33 | public abstract string FirefoxPath { get; }
34 |
35 |
36 | [MNNW(true, nameof(ChromePath))]
37 | public bool IsChromeInstalled => Path.Exists(ChromePath);
38 |
39 | [MNNW(true, nameof(FirefoxPath))]
40 | public bool IsFirefoxInstalled => Path.Exists(FirefoxPath);
41 |
42 | #endregion
43 |
44 | static BaseOSIntegration()
45 | {
46 | Executable = GetProcessMainModuleFileName();
47 | ExecutableDirectory = Path.GetDirectoryName(Executable);
48 |
49 | if (IsWindows) {
50 | Integration = new WindowsOSIntegration();
51 | }
52 | else if (IsLinux) {
53 | Integration = new LinuxOSIntegration();
54 | }
55 | else {
56 | Integration = null;
57 | throw new NotSupportedException("OS not supported");
58 | }
59 | }
60 |
61 | #region
62 |
63 | internal const string OS_WIN = "windows";
64 |
65 | internal const string OS_LINUX = "linux";
66 |
67 | public const int EC_ERROR = -1;
68 |
69 | public const int EC_OK = 0;
70 |
71 | [SupportedOSPlatformGuard(OS_LINUX)]
72 | public static readonly bool IsLinux = OperatingSystem.IsLinux();
73 |
74 | [SupportedOSPlatformGuard(OS_WIN)]
75 | public static readonly bool IsWindows = OperatingSystem.IsWindows();
76 |
77 |
78 | public static BaseOSIntegration Integration { get; }
79 |
80 | public static string ExecutableDirectory { get; }
81 |
82 | public static bool IsExecutableInPath
83 | => FileSystem.IsFolderInPath(ExecutableDirectory);
84 |
85 | public static string Executable { get; }
86 |
87 | #endregion
88 |
89 | /// true if operation succeeded; false otherwise
90 | public abstract bool? AddToPath(bool option);
91 |
92 | /// true if operation succeeded; false otherwise
93 | public abstract bool? HandleContextMenu(bool option, string args);
94 |
95 | public abstract void FlashNotify(nint fd);
96 |
97 | public static string GetProcessMainModuleFileName()
98 | {
99 | // TODO: vs Directory.GetCurrentDirectory
100 |
101 | ProcessModule module = Process.GetCurrentProcess().MainModule;
102 |
103 | // Require.NotNull(module);
104 | Trace.Assert(module != null);
105 | return module.FileName;
106 | }
107 |
108 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/Integration/LinuxOSIntegration.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: LinuxOSIntegration.cs
2 | // Date: 2024/12/04 @ 22:12:30
3 |
4 | using System.Runtime.Versioning;
5 | using Novus.OS;
6 | using SmartImage.Lib.Utilities.Diagnostics;
7 |
8 | // ReSharper disable InconsistentNaming
9 |
10 | namespace SmartImage.Lib.Utilities.Integration;
11 |
12 | [SupportedOSPlatform(OS_LINUX)]
13 | public sealed class LinuxOSIntegration : BaseOSIntegration
14 | {
15 |
16 | public override string LaunchArgs { get; } = R1.Linux_Launch_Args;
17 |
18 | public override string ProgramFilesPath => null;
19 |
20 | public override string AppDataPath => null;
21 |
22 |
23 | public override bool IsContextMenuAdded => File.Exists(DesktopFile);
24 |
25 | public override string ChromePath => null;
26 |
27 | public override string FirefoxPath => null;
28 |
29 | public static readonly string DesktopFile = Path.Combine(R1.Linux_Applications_Dir, R1.Linux_Desktop_File);
30 |
31 | public override bool? AddToPath(bool option)
32 | {
33 | return null;
34 | }
35 |
36 | public override bool? HandleContextMenu(bool option, string args)
37 | {
38 | if (!FileSystem.IsRoot) {
39 | throw new SmartImageException("Root permissions required");
40 |
41 | }
42 |
43 | args ??= R1.Linux_Launch_Args;
44 |
45 | if (option) {
46 | string dsk = $"""
47 | [Desktop Entry]
48 |
49 | Type=Application
50 | Version=1.0
51 | Name=SmartImage
52 | Terminal=true
53 | Exec={Executable} {args}
54 | """;
55 | File.WriteAllText(DesktopFile, dsk);
56 |
57 | }
58 | else {
59 | if (IsContextMenuAdded) {
60 | File.Delete(DesktopFile);
61 | }
62 | }
63 |
64 | // Console.WriteLine(Path.GetFullPath(s));
65 | // Console.ReadLine();
66 |
67 | // File.WriteAllText("~/.local/share/nautilus/scripts/smartimage.desktop", dsk);
68 |
69 | return true;
70 | }
71 |
72 | public override void FlashNotify(nint fd) { }
73 |
74 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/Integration/WindowsOSIntegration.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: WindowsOSIntegration.cs
2 | // Date: 2024/12/05 @ 21:12:18
3 |
4 | using System.Diagnostics;
5 | using System.Runtime.InteropServices;
6 | using System.Runtime.Versioning;
7 | using JetBrains.Annotations;
8 | using Microsoft.Win32;
9 | using Novus.OS;
10 | using Novus.Win32;
11 | using Novus.Win32.Structures.User32;
12 |
13 | // ReSharper disable InconsistentNaming
14 |
15 | namespace SmartImage.Lib.Utilities.Integration;
16 |
17 | [SupportedOSPlatform(OS_WIN)]
18 | public sealed class WindowsOSIntegration : BaseOSIntegration
19 | {
20 | [NN]
21 | public override string ChromePath => Path.Combine(ProgramFilesPath, @"Google\Chrome\Application\chrome.exe");
22 |
23 | [NN]
24 | public override string FirefoxPath => Path.Combine(AppDataPath, @"Mozilla");
25 |
26 | public override string LaunchArgs { get; } = R1.Reg_Launch_Args;
27 |
28 | public override string ProgramFilesPath { get; } =
29 | Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
30 |
31 | public override string AppDataPath { get; } = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData);
32 |
33 |
34 | public override bool IsContextMenuAdded
35 | {
36 | get
37 | {
38 | using var reg = Registry.CurrentUser.OpenSubKey(R1.Reg_Shell_Cmd);
39 | return reg != null;
40 |
41 | }
42 | }
43 |
44 | public override bool? AddToPath(bool option)
45 | {
46 | if (option) {
47 | var p = FileSystem.GetEnvironmentPath();
48 | FileSystem.SetEnvironmentPath(p + $";{ExecutableDirectory}");
49 |
50 | }
51 | else {
52 | FileSystem.RemoveFromPath(ExecutableDirectory);
53 | }
54 |
55 | return true;
56 | }
57 |
58 | public override bool? HandleContextMenu(bool option, string args)
59 | {
60 | /*
61 | * New context menu
62 | */
63 | bool ok = false;
64 |
65 | switch (option) {
66 | case true:
67 |
68 | args ??= R1.Reg_Launch_Args;
69 |
70 | RegistryKey regMenu = null;
71 | RegistryKey regCmd = null;
72 |
73 | string fullPath = Executable;
74 |
75 | try {
76 | regMenu = Registry.CurrentUser.CreateSubKey(R1.Reg_Shell);
77 | regMenu?.SetValue(String.Empty, R1.Name);
78 | regMenu?.SetValue("Icon", $"\"{fullPath}\"");
79 |
80 | regCmd = Registry.CurrentUser.CreateSubKey(R1.Reg_Shell_Cmd);
81 |
82 | regCmd?.SetValue(String.Empty, $"\"{fullPath}\" {args}");
83 |
84 | // regCmd?.SetValue(String.Empty, $"\"{fullPath}\" \"%1\"");
85 | // regCmd?.SetValue(String.Empty, $"\"{fullPath}\" -i \"%1\" -auto -s");
86 | ok = true;
87 | }
88 | catch (Exception ex) {
89 | Trace.WriteLine($"{ex.Message}");
90 |
91 | // return false;
92 | ok = false;
93 | }
94 | finally {
95 | regMenu?.Close();
96 | regCmd?.Close();
97 | }
98 |
99 | break;
100 |
101 | case false:
102 |
103 | try {
104 | var reg = Registry.CurrentUser.OpenSubKey(R1.Reg_Shell_Cmd);
105 |
106 | if (reg != null) {
107 | reg.Close();
108 | Registry.CurrentUser.DeleteSubKey(R1.Reg_Shell_Cmd);
109 | }
110 |
111 | reg = Registry.CurrentUser.OpenSubKey(R1.Reg_Shell);
112 |
113 | if (reg != null) {
114 | reg.Close();
115 | Registry.CurrentUser.DeleteSubKey(R1.Reg_Shell);
116 | }
117 |
118 | // return true;
119 | ok = true;
120 | }
121 | catch (Exception ex) {
122 | Trace.WriteLine($"{ex.Message}");
123 | ok = false;
124 |
125 | // return false;
126 | }
127 |
128 | break;
129 |
130 | }
131 |
132 | return ok;
133 | }
134 |
135 | public override void FlashNotify(nint hwnd)
136 | {
137 | var pwfi = new FLASHWINFO
138 | {
139 | cbSize = (uint) Marshal.SizeOf(),
140 | hwnd = hwnd,
141 | dwFlags = FlashWindowType.FLASHW_TRAY,
142 | uCount = 8,
143 | dwTimeout = 75
144 | };
145 |
146 | Native.FlashWindowEx(ref pwfi);
147 | }
148 |
149 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/NodeHelper.cs:
--------------------------------------------------------------------------------
1 | // Read Stanton SmartImage.Lib NodeHelper.cs
2 | // 2023-01-13 @ 11:37 PM
3 |
4 | using System.Diagnostics;
5 | using System.Net;
6 | using System.Net.Mime;
7 | using System.Text.Json;
8 | using System.Text.Json.Nodes;
9 | using System.Text.Json.Serialization;
10 | using AngleSharp.Dom;
11 | using Flurl.Http;
12 | using Flurl.Http.Configuration;
13 | using JetBrains.Annotations;
14 | using Novus.Win32;
15 |
16 | // ReSharper disable AnnotateNotNullParameter
17 |
18 | namespace SmartImage.Lib.Utilities;
19 |
20 | public static class NodeHelper
21 | {
22 |
23 | public static JsonNode TryGetKeyValue(this JsonObject v, string k)
24 | => v.ContainsKey(k) ? v[k] : null;
25 |
26 | [CBN]
27 | internal static INode FirstOrDefaultElement(this INodeList nodes, Func predicate)
28 | => ApplyFunctorInnerPredicate(nodes.FirstOrDefault, predicate);
29 |
30 | /*[CBN]
31 | internal static INode ElemFunctor(Func, INode> functor, Predicate elemPredicate)
32 | => ApplyFunctorInnerPredicate(functor, elemPredicate);*/
33 |
34 |
35 | [CBN]
36 | internal static INode FirstOrDefaultElementByClassName(this INodeList nodes, string className)
37 | => ApplyFunctorInnerPredicate(nodes.FirstOrDefault, e => e.ClassName == className);
38 |
39 | [CBN]
40 | [LinqTunnel]
41 | internal static T2 ApplyFunctorInnerPredicate(Func, T2> functor,
42 | Func predicate)
43 | => functor(f => f is T e && predicate(e));
44 |
45 |
46 | /*
47 | * IEnumerable bound
48 | *
49 | */
50 |
51 |
52 | // Why am I recreating LINQ
53 |
54 |
55 | [return: NN]
56 | internal static IEnumerable TryFindElementsByClassName(Func, IEnumerable> where,
57 | Func predicate)
58 | => where(predicate);
59 |
60 |
61 | public static IEnumerable QueryAllAttribute(this IParentNode doc, string sel, string attr)
62 | => doc.QuerySelectorAll(sel)
63 | .Select(e => e.GetAttribute(attr));
64 |
65 | public static INode RecurseChildren(this INode node, int childNodeIndex, int level)
66 | {
67 | /*if (level <= 0) {
68 | return n;
69 | }
70 |
71 | return RecurseChildren(n.ChildNodes[childNodeIndex], childNodeIndex, --level);*/
72 | // return level <= 0 ? n : RecurseChildren(n.ChildNodes[childNodeIndex], childNodeIndex, --level);
73 |
74 | while (true) {
75 | if (level <= 0)
76 | return node;
77 |
78 | node = node.ChildNodes[childNodeIndex];
79 | level = --level;
80 | }
81 | }
82 |
83 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/SearchResultTypeConverter.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Lib | Name: SearchResultTypeConverter.cs
2 | // Date: 2024/11/21 @ 13:11:05
3 |
4 | using System.Text.Json;
5 | using System.Text.Json.Serialization;
6 | using System.Text.Json.Serialization.Metadata;
7 | using SmartImage.Lib.Results;
8 |
9 | namespace SmartImage.Lib.Utilities;
10 |
11 |
12 | public sealed class SearchResultTypeConverter : JsonConverter
13 | {
14 |
15 | #region Overrides of JsonConverter
16 |
17 | public override SearchResult Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
18 | {
19 | throw new NotImplementedException();
20 | }
21 |
22 | public override void Write(Utf8JsonWriter writer, SearchResult value, JsonSerializerOptions options)
23 | {
24 | writer.WriteStartObject();
25 | writer.WriteString(nameof(SearchResult.Engine.Name), value.Engine.EngineOption.ToString());
26 | writer.WriteString(nameof(SearchResult.Status), $"{value.Status}");
27 | writer.WriteStartArray(nameof(SearchResult.Results));
28 |
29 | foreach (SearchResultItem result in value.Results) {
30 | writer.WriteStartObject();
31 | writer.WriteRawValue(JsonSerializer.Serialize(result, options));
32 | writer.WriteEndObject();
33 | }
34 |
35 | writer.WriteEndArray();
36 | writer.WriteEndObject();
37 | }
38 |
39 | #endregion
40 |
41 | }
--------------------------------------------------------------------------------
/SmartImage.Lib/Utilities/SearchUtil.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using SmartImage.Lib.Engines;
7 | using SmartImage.Lib.Results;
8 |
9 | namespace SmartImage.Lib.Utilities;
10 |
11 | public static class SearchUtil
12 | {
13 |
14 | /*public static bool IsSuccessful(this SearchResultStatus s)
15 | => s is SearchResultStatus.Success || (!s.IsError() && !s.IsUnknown());*/
16 |
17 | public static bool IsSuccessful(this SearchResultStatus s)
18 | => s is SearchResultStatus.Success;
19 |
20 | public static bool IsUnknown(this SearchResultStatus s)
21 | => s is SearchResultStatus.None;
22 |
23 | public static bool IsError(this SearchResultStatus s)
24 | => s is SearchResultStatus.UnknownError or SearchResultStatus.IllegalInput
25 | or SearchResultStatus.Unavailable or SearchResultStatus.Cooldown;
26 |
27 | public const SearchResultFlags ALT_STATUS =
28 | SearchResultFlags.NoResults | SearchResultFlags.Extraneous;
29 |
30 | public static bool HasFlagFast(this SearchResultFlags value, SearchResultFlags status)
31 | => (value & status) != 0;
32 |
33 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Commands/CommonCommandSettings.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Rdx | Name: CommonCommandSettings.cs
2 | // Date: 2025/02/25 @ 10:02:23
3 |
4 | using System.ComponentModel;
5 | using SmartImage.Lib;
6 | using SmartImage.Lib.Engines;
7 | using Spectre.Console.Cli;
8 |
9 | namespace SmartImage.Rdx.Commands;
10 |
11 | public class CommonCommandSettings : CommandSettings
12 | {
13 |
14 | [CommandOption("-e|--search-engines")]
15 | [DefaultValue(SearchConfig.SE_DEFAULT)]
16 | [Description("Search engines (comma-delimited)")]
17 | public SearchEngineOptions SearchEngines { get; internal set; }
18 |
19 | [CommandOption("-p|--priority-engines")]
20 | [DefaultValue(SearchConfig.PE_DEFAULT)]
21 | [Description("Engines whose results to open (comma-delimited)")]
22 | public SearchEngineOptions PriorityEngines { get; internal set; }
23 |
24 | [CommandOption("--read-cookies")]
25 | [DefaultValue(SearchConfig.READCOOKIES_DEFAULT)]
26 | [Description("Read cookies from browser")]
27 | public bool ReadCookies { get; internal set; }
28 |
29 | [CommandOption("--flaresolverr")]
30 | [DefaultValue(SearchConfig.FLARESOLVERR_DEFAULT)]
31 | [Description("Use FlareSolverr")]
32 | public bool FlareSolverr { get; internal set; }
33 |
34 | [CommandOption("--flaresolverr-api")]
35 | [DefaultValue(SearchConfig.FLARE_SOLVERR_API_URL_DEFAULT)]
36 | [Description("FlareSolverr API URL")]
37 | public string? FlareSolverrApiUrl { get; internal set; }
38 |
39 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Commands/IntegrationCommand.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Rdx | Name: IntegrationCommand.cs
2 | // Date: 2024/05/22 @ 16:05:51
3 |
4 | using System.Diagnostics;
5 | using SmartImage.Lib.Utilities.Integration;
6 | using SmartImage.Rdx.Shell;
7 | using Spectre.Console;
8 | using Spectre.Console.Cli;
9 |
10 | namespace SmartImage.Rdx.Commands;
11 |
12 | internal class IntegrationCommand : Command
13 | {
14 |
15 | public override int Execute(CommandContext context, IntegrationCommandSettings settings)
16 | {
17 | try {
18 | // AnsiConsole.WriteLine($"{AppSupport.IsContextMenuAdded}");
19 |
20 | if (settings.ContextMenu.HasValue) {
21 | var rv = BaseOSIntegration.Integration.HandleContextMenu(settings.ContextMenu.Value, settings.ContextMenuArguments);
22 | AnsiConsole.WriteLine($"Context menu change: {rv}");
23 | }
24 |
25 | AnsiConsole.WriteLine($"Context menu enabled: {BaseOSIntegration.Integration.IsContextMenuAdded}");
26 | }
27 | catch (Exception e) {
28 | AnsiConsole.WriteException(e);
29 | }
30 |
31 | return BaseOSIntegration.EC_OK;
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Commands/IntegrationCommandSettings.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Rdx | Name: IntegrationCommandSettings.cs
2 | // Date: 2024/05/22 @ 16:05:47
3 |
4 | using SmartImage.Lib.Utilities.Integration;
5 | using Spectre.Console;
6 | using Spectre.Console.Cli;
7 |
8 | namespace SmartImage.Rdx.Commands;
9 |
10 | internal class IntegrationCommandSettings : CommandSettings
11 | {
12 |
13 | [CommandOption("--ctx-menu")]
14 | public bool? ContextMenu { get; internal set; }
15 |
16 | [CommandOption("--ctx-menu-args")]
17 | public string? ContextMenuArguments { get; internal set; }
18 |
19 | public override ValidationResult Validate()
20 | {
21 | ContextMenuArguments ??= BaseOSIntegration.Integration.LaunchArgs;
22 |
23 | return base.Validate();
24 | }
25 |
26 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Commands/SearchCommandSettings.cs:
--------------------------------------------------------------------------------
1 | // Deci SmartImage.Rdx SearchCommandSettings.cs
2 | // $File.CreatedYear-$File.CreatedMonth-26 @ 0:56
3 |
4 | using System.ComponentModel;
5 | using System.Diagnostics;
6 | using System.Text;
7 | using Novus.Win32;
8 | using SmartImage.Lib.Images.Uni;
9 | using SmartImage.Rdx.Shell;
10 | using Spectre.Console.Cli;
11 | using ValidationResult = Spectre.Console.ValidationResult;
12 |
13 | namespace SmartImage.Rdx.Commands;
14 |
15 | public sealed class SearchCommandSettings : CommonCommandSettings
16 | {
17 |
18 | [CommandArgument(0, "")]
19 | [Description("Query: file or URL; see wiki")]
20 | public string? Query { get; internal set; }
21 |
22 | #region
23 |
24 | [CommandOption("--keep-open")]
25 | [DefaultValue(false)]
26 | [Description("Waits for input before terminating")]
27 | public bool KeepOpen { get; internal set; }
28 |
29 | #endregion
30 |
31 | #region
32 |
33 | [CommandOption("-f|--output-format")]
34 | [DefaultValue(OutputFileFormat.None)]
35 | [Description("Output file format")]
36 | public OutputFileFormat OutputFileFormat { get; internal set; }
37 |
38 | [CommandOption("-o|--output-file")]
39 | [Description("Output file name")]
40 | public string? OutputFile { get; internal set; }
41 |
42 | [CommandOption("-d|--output-delim")]
43 | [DefaultValue(",")]
44 | [Description("Output file delimiter")]
45 | public string? OutputFileDelimiter { get; internal set; }
46 |
47 | [CommandOption("--output-fields")]
48 | [DefaultValue(OUTPUT_FIELDS_DEFAULT)]
49 | [Description("Output fields (comma-delimited)")]
50 | public OutputFields OutputFields { get; internal set; }
51 |
52 | public const OutputFields OUTPUT_FIELDS_DEFAULT =
53 | OutputFields.Name | OutputFields.Similarity | OutputFields.Url;
54 |
55 | #endregion
56 |
57 | #region
58 |
59 | [CommandOption("-x|--command-exe")]
60 | [Description($"Command/executable to invoke upon completion")]
61 | public string? Command { get; internal set; }
62 |
63 | [CommandOption("-c|--command-args")]
64 | [Description($"Arguments to pass to command")]
65 | public string? CommandArguments { get; internal set; }
66 |
67 | #endregion
68 |
69 | // public bool? Silent { get; internal set; } //todo
70 |
71 | // public const string PROP_ARG_RESULTS = "$all_results";
72 |
73 | [CommandOption("--interactive")]
74 | [DefaultValue(false)]
75 | [Description("Interactive results")]
76 | public bool Interactive { get; internal set; }
77 |
78 | public override ValidationResult Validate()
79 | {
80 | var result = base.Validate();
81 |
82 | if (!UniImage.IsValidSourceType(Query, false)) {
83 | return ValidationResult.Error("Invalid query");
84 | }
85 |
86 | var hasOutputFile = !string.IsNullOrWhiteSpace(OutputFile);
87 | var hasOutputFileDelim = !string.IsNullOrEmpty(OutputFileDelimiter);
88 | bool isOutputFormatDelim = OutputFileFormat == OutputFileFormat.Delimited;
89 |
90 | if (!isOutputFormatDelim && hasOutputFile) {
91 | OutputFileFormat = OutputFileFormat.Delimited;
92 | isOutputFormatDelim = true;
93 | }
94 |
95 | if (isOutputFormatDelim) {
96 | if (!hasOutputFile) {
97 | return ValidationResult.Error(
98 | $"{nameof(OutputFile)} must be set if {nameof(OutputFileFormat)} == {nameof(OutputFileFormat.Delimited)}");
99 | }
100 |
101 | if (!hasOutputFileDelim) {
102 | return ValidationResult.Error(
103 | $"{nameof(OutputFileDelimiter)} must be set if {nameof(OutputFileFormat)} == {nameof(OutputFileFormat.Delimited)}");
104 | }
105 | }
106 |
107 | return result;
108 | }
109 |
110 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Commands/SearchServerResponse.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Rdx | Name: SearchServerResponse.cs
2 | // Date: 2025/02/04 @ 12:02:39
3 |
4 | using System.Text.Json.Serialization;
5 | using SmartImage.Lib.Results;
6 |
7 | namespace SmartImage.Rdx.Commands;
8 |
9 | public class SearchServerResponse
10 | {
11 |
12 | [MN]
13 | [JsonPropertyOrder(0)]
14 | public SearchResultItem Best { get; internal set; }
15 |
16 | [JsonPropertyOrder(1)]
17 | public SearchResult[] Results { get; internal set; }
18 |
19 | [MN]
20 | [JsonPropertyOrder(2)]
21 | public string Message { get; internal set; }
22 |
23 | public SearchServerResponse() { }
24 |
25 | public SearchServerResponse(SearchResult[] results, SearchResultItem best)
26 | {
27 | Best = best;
28 | Results = results;
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Commands/ServerCommandSettings.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Rdx | Name: ServerCommandSettings.cs
2 | // Date: 2024/12/03 @ 10:12:10
3 |
4 | using System.ComponentModel;
5 | using SmartImage.Lib.Utilities.Diagnostics;
6 | using SmartImage.Lib.Utilities.Integration;
7 | using Spectre.Console;
8 | using Spectre.Console.Cli;
9 |
10 | namespace SmartImage.Rdx.Commands;
11 |
12 | public sealed class ServerCommandSettings : CommonCommandSettings
13 | {
14 |
15 | [CommandOption("--port")]
16 | [DefaultValue(25565)]
17 | public int Port { get; set; }
18 |
19 | public override ValidationResult Validate()
20 | {
21 | /*if (!BaseOSIntegration.Integration.IsRoot) {
22 | throw new SmartImageException("Must be admin");
23 | }*/
24 |
25 | return base.Validate();
26 | }
27 |
28 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.Rdx/Icon.ico
--------------------------------------------------------------------------------
/SmartImage.Rdx/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Buffers;
2 | using System.Collections.Concurrent;
3 | using System.Diagnostics;
4 | using System.Reflection;
5 | using System.Runtime.InteropServices;
6 | using System.Text;
7 | using System.Text.Json;
8 | using System.Threading.Channels;
9 | using Flurl.Http;
10 | using Flurl.Http.Configuration;
11 | using Kantan.Text;
12 | using Microsoft.Extensions.Configuration;
13 | using Microsoft.Extensions.DependencyInjection;
14 | using Microsoft.Extensions.Hosting;
15 | using Novus.Streams;
16 | using Novus.Win32;
17 | using SixLabors.ImageSharp;
18 | using SixLabors.ImageSharp.Formats;
19 | using SmartImage.Lib;
20 | using SmartImage.Lib.Images;
21 | using SmartImage.Lib.Images.Uni;
22 | using Spectre.Console;
23 | using Spectre.Console.Cli;
24 | using SmartImage.Rdx.Shell;
25 | using SmartImage.Rdx.Utilities;
26 | using SmartImage.Rdx.Commands;
27 | using SmartImage.Lib.Utilities.Integration;
28 |
29 | namespace SmartImage.Rdx;
30 |
31 | public static class Program
32 | {
33 |
34 | public static async Task Main(string[] args)
35 | {
36 | /*AppDomain.CurrentDomain.UnhandledException += (sender, eventArgs) =>
37 | {
38 | Trace.WriteLine($"{sender} -> {eventArgs}");
39 | };*/
40 |
41 | Encoding.RegisterProvider(CodePagesEncodingProvider.Instance);
42 |
43 | #if DEBUG
44 |
45 | // Debugger.Launch();
46 | #endif
47 | HandleArgs(ref args);
48 |
49 | await DisplayHeaderAsync();
50 |
51 | DisplayInfoGrid();
52 |
53 | var app = new CommandApp();
54 |
55 | app.Configure(c =>
56 | {
57 | #if DEBUG
58 | c.PropagateExceptions();
59 | c.ValidateExamples();
60 | #endif
61 |
62 | var helpProvider = new CustomHelpProvider(c.Settings);
63 | c.SetHelpProvider(helpProvider);
64 |
65 | c.AddCommand("integrate")
66 | .WithDescription("Configure system integration such as context menu");
67 |
68 | c.AddCommand("server")
69 | .WithDescription("Start listen server");
70 | });
71 |
72 | int x = BaseOSIntegration.EC_OK;
73 |
74 | try {
75 | x = await app.RunAsync(args);
76 |
77 | }
78 | catch (Exception e) {
79 | AnsiConsole.WriteException(e);
80 | x = BaseOSIntegration.EC_ERROR;
81 | }
82 | finally {
83 |
84 | if (x != BaseOSIntegration.EC_OK) {
85 | AnsiConsole.Confirm("Press any key to continue");
86 | }
87 | }
88 |
89 | return x;
90 | }
91 |
92 | private static void DisplayInfoGrid()
93 | {
94 | Grid grd = ConsoleFormat.MapToGrid(ConsoleFormat.InfoMap);
95 | AnsiConsole.Write(grd);
96 | }
97 |
98 | private static async Task DisplayHeaderAsync()
99 | {
100 | var ff = ConsoleFormat.LoadFigletFontFromResource(nameof(R2.Fg_larry3d), out var ms);
101 |
102 | var fg = new FigletText(ff, R1.Name)
103 | .LeftJustified()
104 | .Color(ConsoleFormat.Clr_Misc1);
105 | await ms.DisposeAsync();
106 |
107 | AnsiConsole.Write(fg);
108 | }
109 |
110 | private static void HandleArgs(ref string[] args)
111 | {
112 | if (args.Length == 0) {
113 |
114 | // todo
115 |
116 | /*if (Clipboard.Open()) {
117 | /*var hasBmp = Clipboard.IsFormatAvailable((uint) ClipboardFormat.CF_BITMAP);
118 |
119 | if (hasBmp) {
120 | var data = (nint) Clipboard.GetData((uint) ClipboardFormat.CF_BITMAP);
121 | var sz = Native.GlobalSize(data);
122 | var buf = new byte[sz];
123 | Marshal.Copy(data, buf, 0, (int) sz);
124 | var mg = await Image.LoadAsync(new MemoryStream(buf));
125 |
126 | }#1#
127 |
128 | var hasFileName = Clipboard.IsFormatAvailable((uint) ClipboardFormat.FileNameW);
129 |
130 | }*/
131 |
132 | // var s = AnsiConsole.Ask("...");
133 |
134 | }
135 |
136 | /*if (args.Length == 0) {
137 | var prompt = new TextPrompt("Input")
138 | {
139 | Converter = s =>
140 | {
141 | /*
142 | var task = SearchQuery.TryCreateAsync(s);
143 | task.Wait();
144 | var res = task.Result;
145 | #1#
146 |
147 | if (UniImage.IsValidSourceType(s)) {
148 | // var sq = SearchQuery.TryCreateAsync(s).Result;
149 |
150 | return s;
151 | }
152 |
153 | else {
154 | return null;
155 | }
156 | }
157 | };
158 | var sz = AnsiConsole.Prompt(prompt);
159 |
160 | args = [sz];
161 | }*/
162 | if (Console.IsInputRedirected) {
163 | Trace.WriteLine("Input redirected");
164 | var pipeInput = ConsoleUtil.ParseInputStream();
165 |
166 | var newArgs = new string[args.Length + 1];
167 | newArgs[0] = pipeInput;
168 | args.CopyTo(newArgs, 1);
169 |
170 | args = newArgs;
171 |
172 | AnsiConsole.WriteLine($"Received input from stdin");
173 | }
174 | }
175 |
176 |
177 | private static IConfigurationRoot GetConfig()
178 | {
179 | /*var bldr2 = new ConfigurationBuilder();
180 | var host = Host.CreateDefaultBuilder();
181 | var bldr = host.ConfigureServices((ctx, svc) => { svc.AddSingleton(); });
182 |
183 | bldr2.SetBasePath(Directory.GetCurrentDirectory())
184 | .AddJsonFile("smartimage.json", optional: false, reloadOnChange: true);
185 | */
186 |
187 | // TODO
188 |
189 | var currentDirectory = Directory.GetCurrentDirectory();
190 | var configFileName = $"{R1.Name}.json";
191 | var configFilePath = Path.Combine(currentDirectory, configFileName);
192 |
193 | var cfg = new ConfigurationBuilder()
194 | .AddJsonFile(configFileName)
195 | .Build();
196 |
197 | return cfg;
198 | }
199 |
200 | public static readonly Assembly Assembly = Assembly.GetExecutingAssembly();
201 |
202 | public static readonly Version Version = Assembly.GetName().Version;
203 |
204 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Resources.Designer.cs:
--------------------------------------------------------------------------------
1 | //------------------------------------------------------------------------------
2 | //
3 | // This code was generated by a tool.
4 | // Runtime Version:4.0.30319.42000
5 | //
6 | // Changes to this file may cause incorrect behavior and will be lost if
7 | // the code is regenerated.
8 | //
9 | //------------------------------------------------------------------------------
10 |
11 | namespace SmartImage.Rdx {
12 | using System;
13 |
14 |
15 | ///
16 | /// A strongly-typed resource class, for looking up localized strings, etc.
17 | ///
18 | // This class was auto-generated by the StronglyTypedResourceBuilder
19 | // class via a tool like ResGen or Visual Studio.
20 | // To add or remove a member, edit your .ResX file then rerun ResGen
21 | // with the /str option, or rebuild your VS project.
22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "17.0.0.0")]
23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
25 | internal class Resources {
26 |
27 | private static global::System.Resources.ResourceManager resourceMan;
28 |
29 | private static global::System.Globalization.CultureInfo resourceCulture;
30 |
31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
32 | internal Resources() {
33 | }
34 |
35 | ///
36 | /// Returns the cached ResourceManager instance used by this class.
37 | ///
38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
39 | internal static global::System.Resources.ResourceManager ResourceManager {
40 | get {
41 | if (object.ReferenceEquals(resourceMan, null)) {
42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("SmartImage.Rdx.Resources", typeof(Resources).Assembly);
43 | resourceMan = temp;
44 | }
45 | return resourceMan;
46 | }
47 | }
48 |
49 | ///
50 | /// Overrides the current thread's CurrentUICulture property for all
51 | /// resource lookups using this strongly typed resource class.
52 | ///
53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
54 | internal static global::System.Globalization.CultureInfo Culture {
55 | get {
56 | return resourceCulture;
57 | }
58 | set {
59 | resourceCulture = value;
60 | }
61 | }
62 |
63 | ///
64 | /// Looks up a localized string similar to exit.
65 | ///
66 | internal static string Chc_Exit {
67 | get {
68 | return ResourceManager.GetString("Chc_Exit", resourceCulture);
69 | }
70 | }
71 |
72 | ///
73 | /// Looks up a localized string similar to open.
74 | ///
75 | internal static string Chc_Open {
76 | get {
77 | return ResourceManager.GetString("Chc_Open", resourceCulture);
78 | }
79 | }
80 |
81 | ///
82 | /// Looks up a localized string similar to scan.
83 | ///
84 | internal static string Chc_Scan {
85 | get {
86 | return ResourceManager.GetString("Chc_Scan", resourceCulture);
87 | }
88 | }
89 |
90 | ///
91 | /// Looks up a localized resource of type System.Byte[].
92 | ///
93 | internal static byte[] Fg_Cybermedium {
94 | get {
95 | object obj = ResourceManager.GetObject("Fg_Cybermedium", resourceCulture);
96 | return ((byte[])(obj));
97 | }
98 | }
99 |
100 | ///
101 | /// Looks up a localized resource of type System.Byte[].
102 | ///
103 | internal static byte[] Fg_larry3d {
104 | get {
105 | object obj = ResourceManager.GetObject("Fg_larry3d", resourceCulture);
106 | return ((byte[])(obj));
107 | }
108 | }
109 |
110 | ///
111 | /// Looks up a localized resource of type System.Byte[].
112 | ///
113 | internal static byte[] Fg_SantaClara {
114 | get {
115 | object obj = ResourceManager.GetObject("Fg_SantaClara", resourceCulture);
116 | return ((byte[])(obj));
117 | }
118 | }
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/SmartImage.Rdx/Resources/Cybermedium.flf:
--------------------------------------------------------------------------------
1 | flf2a$ 4 3 8 -1 20
2 | Cyberfont - medium
3 | Figlet conversion by Kent Nassen, kentn@cyberspace.org, 8-11-94
4 | From: stock@fwi.uva.nl (Lennert Stock)
5 | Date: 15 Jul 1994 00:04:25 GMT
6 |
7 | Here are some fonts. Non-figlet I'm afraid, if you wanna convert them, be
8 | my guest. I posted the isometric fonts before.
9 |
10 | ------------------------------------------------------------------------------
11 |
12 | .x%%%%%%x. .x%%%%%%x.
13 | ,%%%%%%%%%%. .%%%%%%%%%%.
14 | ,%%%' )' \) :( `( `%%%.
15 | ,%x%)________) --------- L e n n e r t S t o c k ( _ __ (%x%.
16 | (%%%~^88P~88P| |~=> .=-~ %%%)
17 | (%%::. .:,\ .' `. /,:. .::%%)
18 | `;%:`\. `-' | | `-' ./':%:'
19 | ``x`. -===.' stock@fwi.uva.nl -------- `.===- .'x''
20 | / `:`.__.; :.__.':' \
21 | .d8b. ..`. .'.. .d8b.
22 | $ $@
23 | $ $@
24 | $ $@
25 | $ $@@
26 | /@
27 | / @
28 | . @
29 | @@
30 | ..@
31 | ''@
32 | @
33 | @@
34 | @
35 | @
36 | @
37 | @@
38 | @
39 | @
40 | @
41 | @@
42 | @
43 | @
44 | @
45 | @@
46 | @
47 | @
48 | @
49 | @@
50 | . @
51 | ' @
52 | @
53 | @@
54 | @
55 | @
56 | @
57 | @@
58 | @
59 | @
60 | @
61 | @@
62 | @
63 | @
64 | @
65 | @@
66 | @
67 | @
68 | @
69 | @@
70 | @
71 | @
72 | . @
73 | ' @@
74 | @
75 | __ @
76 | @
77 | @@
78 | @
79 | @
80 | .@
81 | @@
82 | / @
83 | / @
84 | / @
85 | @@
86 | @
87 | @
88 | @
89 | @@
90 | @
91 | @
92 | @
93 | @@
94 | @
95 | @
96 | @
97 | @@
98 | @
99 | @
100 | @
101 | @@
102 | @
103 | @
104 | @
105 | @@
106 | @
107 | @
108 | @
109 | @@
110 | @
111 | @
112 | @
113 | @@
114 | @
115 | @
116 | @
117 | @@
118 | @
119 | @
120 | @
121 | @@
122 | @
123 | @
124 | @
125 | @@
126 | $@
127 | .@
128 | .@
129 | @@
130 | $@
131 | .@
132 | ,@
133 | @@
134 | @
135 | @
136 | @
137 | @@
138 | @
139 | @
140 | @
141 | @@
142 | @
143 | @
144 | @
145 | @@
146 | __.@
147 | _]@
148 | . @
149 | @@
150 | @
151 | @
152 | @
153 | @@
154 | ____ @
155 | |__| @
156 | | | @
157 | @@
158 | ___ @
159 | |__] @
160 | |__] @
161 | @@
162 | ____ @
163 | | @
164 | |___ @
165 | @@
166 | ___ @
167 | | \ @
168 | |__/ @
169 | @@
170 | ____ @
171 | |___ @
172 | |___ @
173 | @@
174 | ____ @
175 | |___ @
176 | | @
177 | @@
178 | ____ @
179 | | __ @
180 | |__] @
181 | @@
182 | _ _ @
183 | |__| @
184 | | | @
185 | @@
186 | _ @
187 | | @
188 | | @
189 | @@
190 | _ @
191 | | @
192 | _| @
193 | @@
194 | _ _ @
195 | |_/ @
196 | | \_ @
197 | @@
198 | _ @
199 | | @
200 | |___ @
201 | @@
202 | _ _ @
203 | |\/| @
204 | | | @
205 | @@
206 | _ _ @
207 | |\ | @
208 | | \| @
209 | @@
210 | ____ @
211 | | | @
212 | |__| @
213 | @@
214 | ___ @
215 | |__] @
216 | | @
217 | @@
218 | ____ @
219 | | | @
220 | |_\| @
221 | @@
222 | ____ @
223 | |__/ @
224 | | \ @
225 | @@
226 | ____ @
227 | [__ @
228 | ___] @
229 | @@
230 | ___ @
231 | | @
232 | | @
233 | @@
234 | _ _ @
235 | | | @
236 | |__| @
237 | @@
238 | _ _ @
239 | | | @
240 | \/ @
241 | @@
242 | _ _ _ @
243 | | | | @
244 | |_|_| @
245 | @@
246 | _ _ @
247 | \/ @
248 | _/\_ @
249 | @@
250 | _ _ @
251 | \_/ @
252 | | @
253 | @@
254 | ___ @
255 | / @
256 | /__ @
257 | @@
258 | @
259 | @
260 | @
261 | @@
262 | \ @
263 | \ @
264 | \ @
265 | @@
266 | @
267 | @
268 | @
269 | @@
270 | @
271 | @
272 | @
273 | @@
274 | @
275 | @
276 | ___ @
277 | @@
278 | . @
279 | ` @
280 | @
281 | @@
282 | ____ @
283 | |__| @
284 | | | @
285 | @@
286 | ___ @
287 | |__] @
288 | |__] @
289 | @@
290 | ____ @
291 | | @
292 | |___ @
293 | @@
294 | ___ @
295 | | \ @
296 | |__/ @
297 | @@
298 | ____ @
299 | |___ @
300 | |___ @
301 | @@
302 | ____ @
303 | |___ @
304 | | @
305 | @@
306 | ____ @
307 | | __ @
308 | |__] @
309 | @@
310 | _ _ @
311 | |__| @
312 | | | @
313 | @@
314 | _ @
315 | | @
316 | | @
317 | @@
318 | _ @
319 | | @
320 | _| @
321 | @@
322 | _ _ @
323 | |_/ @
324 | | \_ @
325 | @@
326 | _ @
327 | | @
328 | |___ @
329 | @@
330 | _ _ @
331 | |\/| @
332 | | | @
333 | @@
334 | _ _ @
335 | |\ | @
336 | | \| @
337 | @@
338 | ____ @
339 | | | @
340 | |__| @
341 | @@
342 | ___ @
343 | |__] @
344 | | @
345 | @@
346 | ____ @
347 | | | @
348 | |_\| @
349 | @@
350 | ____ @
351 | |__/ @
352 | | \ @
353 | @@
354 | ____ @
355 | [__ @
356 | ___] @
357 | @@
358 | ___ @
359 | | @
360 | | @
361 | @@
362 | _ _ @
363 | | | @
364 | |__| @
365 | @@
366 | _ _ @
367 | | | @
368 | \/ @
369 | @@
370 | _ _ _ @
371 | | | | @
372 | |_|_| @
373 | @@
374 | _ _ @
375 | \/ @
376 | _/\_ @
377 | @@
378 | _ _ @
379 | \_/ @
380 | | @
381 | @@
382 | ___ @
383 | / @
384 | /__ @
385 | @@
386 | @
387 | @
388 | @
389 | @@
390 | | @
391 | | @
392 | | @
393 | | @@
394 | @
395 | @
396 | @
397 | @@
398 | @
399 | @
400 | @
401 | @@
402 | @
403 | @
404 | @
405 | @@
406 | @
407 | @
408 | @
409 | @@
410 | @
411 | @
412 | @
413 | @@
414 | @
415 | @
416 | @
417 | @@
418 | @
419 | @
420 | @
421 | @@
422 | @
423 | @
424 | @
425 | @@
426 | @
427 | @
428 | @
429 | @@
430 |
--------------------------------------------------------------------------------
/SmartImage.Rdx/Shell/ColorUtil.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Rdx | Name: ColorUtil.cs
2 | // Date: 2024/04/24 @ 21:04:10
3 |
4 | using Spectre.Console;
5 |
6 | namespace SmartImage.Rdx.Shell;
7 |
8 | internal static class ColorUtil
9 | {
10 |
11 | public const double BYTE_D = 255.0;
12 |
13 | private const double LUM_DELTA = 0.05;
14 |
15 | public static double GetLuminance(this Color c)
16 | {
17 | return 0.2126 * c.R / BYTE_D + 0.7152 * c.G / BYTE_D + 0.0722 * c.B / BYTE_D;
18 | }
19 |
20 | public static double GetContrastRatio(this Color color1, Color color2)
21 | {
22 | double luminance1 = color1.GetLuminance();
23 | double luminance2 = color2.GetLuminance();
24 |
25 | if (luminance1 > luminance2)
26 | return (luminance1 + LUM_DELTA) / (luminance2 + LUM_DELTA);
27 | else
28 | return (luminance2 + LUM_DELTA) / (luminance1 + LUM_DELTA);
29 | }
30 |
31 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Shell/ConsoleUtil.cs:
--------------------------------------------------------------------------------
1 | global using STable = Spectre.Console.Table;
2 | global using DTable = System.Data.DataTable;
3 | using System.Data;
4 | using Kantan.Utilities;
5 | using Spectre.Console;
6 | using Spectre.Console.Cli;
7 | using Spectre.Console.Rendering;
8 |
9 | // $User.Name $File.ProjectName $File.FileName
10 | // $File.CreatedYear-$File.CreatedMonth-$File.CreatedDay @ $File.CreatedHour:$File.CreatedMinute
11 |
12 | namespace SmartImage.Rdx.Shell;
13 |
14 | [Flags]
15 | public enum OutputFields
16 | {
17 |
18 | None = 0,
19 |
20 | Name = 1 << 0,
21 | Url = 1 << 1,
22 | Similarity = 1 << 2,
23 | Artist = 1 << 3,
24 | Site = 1 << 4,
25 |
26 | // Default = Name | Url | Similarity
27 |
28 | }
29 |
30 | public enum OutputFileFormat
31 | {
32 |
33 | None = 0,
34 | Delimited,
35 |
36 | }
37 |
38 | internal static class ConsoleUtil
39 | {
40 |
41 | private static readonly byte[] s_utf8BomSig =
42 | [
43 | 0xEF, 0xBB, 0xBF
44 | ];
45 |
46 | public static string ParseInputStream(int bufSize = 4096, int maxSize = 10_000_000)
47 | {
48 | string path = null;
49 |
50 | using Stream stdin = Console.OpenStandardInput();
51 |
52 | var buffer = new byte[bufSize];
53 | var buffer2 = new byte[maxSize];
54 | int bytesRead;
55 | int iter = 0;
56 | int b2pos = 0;
57 |
58 | while ((bytesRead = stdin.Read(buffer, 0, buffer.Length)) > 0) {
59 | if (iter == 0) {
60 |
61 | if (buffer[0] == s_utf8BomSig[0]
62 | && buffer[1] == s_utf8BomSig[1]
63 | && buffer[2] == s_utf8BomSig[2]) {
64 |
65 | buffer = buffer[3..];
66 | bytesRead -= s_utf8BomSig.Length;
67 | }
68 | }
69 |
70 | Array.Copy(buffer, 0, buffer2, b2pos, bytesRead);
71 | b2pos += bytesRead;
72 |
73 | iter++;
74 |
75 | // prog?.Report(b2pos);
76 | }
77 |
78 | if (buffer2[(b2pos - 1)] == '\n' && buffer2[(b2pos - 2)] == '\r') {
79 | b2pos -= 2;
80 | }
81 |
82 | Array.Resize(ref buffer2, b2pos);
83 |
84 | var s = Console.InputEncoding.GetString(buffer2);
85 |
86 | if (File.Exists(s)) {
87 | path = s;
88 | }
89 | else {
90 | path = Path.GetTempFileName();
91 | File.WriteAllBytes(path, buffer2);
92 | }
93 |
94 | return path;
95 | }
96 |
97 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/Shell/CustomHelpProvider.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.Rdx | Name: CustomHelpProvider.cs
2 | // Date: 2024/04/10 @ 18:04:50
3 |
4 | using Spectre.Console;
5 | using Spectre.Console.Cli;
6 | using Spectre.Console.Cli.Help;
7 | using Spectre.Console.Rendering;
8 |
9 | namespace SmartImage.Rdx.Shell;
10 |
11 | internal class CustomHelpProvider : HelpProvider
12 | {
13 |
14 | public CustomHelpProvider(ICommandAppSettings settings)
15 | : base(settings) { }
16 |
17 | public override IEnumerable GetUsage(ICommandModel model, ICommandInfo? command)
18 | {
19 | var usage = base.GetUsage(model, command);
20 | return usage;
21 | }
22 |
23 | /*public override IEnumerable GetExamples(ICommandModel model, ICommandInfo? command)
24 | {
25 | return
26 | [
27 | new Text(
28 | "smartimage \"C:\\Users\\Deci\\Pictures\\Epic anime\\Kallen_FINAL_1-3.png\" --search-engines All --output-format \"Delimited\" --output-file \"output.csv\" --read-cookies")
29 | ];
30 |
31 | return base.GetExamples(model, command);
32 | }*/
33 |
34 | public override IEnumerable GetDescription(ICommandModel model, ICommandInfo? command)
35 | {
36 | return new[]
37 | {
38 | Text.NewLine,
39 | new Text("DESCRIPTION:", new Style(Color.Yellow, decoration: Decoration.Bold)), Text.NewLine,
40 | new Text($" Homepage: {R1.Url_Repo}", new Style(link: R1.Url_Repo)), Text.NewLine,
41 | new Text($" Wiki: {R1.Url_Wiki}", new Style(link: R1.Url_Wiki)), Text.NewLine,
42 | Text.NewLine,
43 | Text.NewLine,
44 | };
45 | }
46 |
47 | public override IEnumerable GetFooter(ICommandModel model, ICommandInfo? command)
48 | {
49 | return base.GetFooter(model, command);
50 | }
51 |
52 | /*public override IEnumerable GetHeader(ICommandModel model, ICommandInfo? command)
53 | {
54 |
55 | switch (command) {
56 | case null:
57 |
58 | break;
59 | }
60 |
61 | return [Text.Empty];
62 | }*/
63 |
64 | }
--------------------------------------------------------------------------------
/SmartImage.Rdx/SmartImage.Rdx.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 | enable
8 | JETBRAINS_ANNOTATIONS;TRACE
9 | True
10 | True
11 | Read Stanton
12 | https://github.com/Decimation/SmartImage
13 | https://github.com/Decimation/SmartImage
14 | SmartImage
15 | true
16 | Debug;Release;Test;UnitTest
17 |
18 |
19 |
20 |
21 | en-US;en
22 | 1.0.9
23 | Icon.ico
24 |
25 |
26 | DEBUG;TRACE;JETBRAINS_ANNOTATIONS
27 |
28 |
29 |
30 | DEBUG;TRACE;JETBRAINS_ANNOTATIONS;TEST
31 |
32 |
33 |
34 | TRACE;JETBRAINS_ANNOTATIONS
35 |
36 |
37 |
38 |
39 |
40 | DEBUG;TRACE;JETBRAINS_ANNOTATIONS;TEST
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 | all
73 | runtime; build; native; contentfiles; analyzers; buildtransitive
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 | ..\..\..\VSProjects\FlareSolverrSharp\src\FlareSolverrSharp\bin\Release\net9.0\FlareSolverrSharp.dll
91 |
92 |
93 | ..\..\..\VSProjects\Kantan\Kantan\bin\Release\net9.0\Kantan.dll
94 |
95 |
96 | ..\..\..\VSProjects\Kantan\Kantan.Net\bin\Release\net9.0\Kantan.Net.dll
97 |
98 |
99 | ..\..\..\VSProjects\Novus\Novus\bin\Release\net9.0\Novus.dll
100 |
101 |
106 |
107 |
108 |
109 |
110 | True
111 | True
112 | Resources.resx
113 |
114 |
115 |
116 |
117 |
118 | ResXFileCodeGenerator
119 | Resources.Designer.cs
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
--------------------------------------------------------------------------------
/SmartImage.Rdx/Utilities/TypeRegistrar.cs:
--------------------------------------------------------------------------------
1 | // Deci SmartImage.Rdx TypeRegistrar.cs
2 | // $File.CreatedYear-$File.CreatedMonth-26 @ 1:46
3 |
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Spectre.Console.Cli;
6 |
7 | namespace SmartImage.Rdx.Utilities;
8 |
9 | public sealed class TypeRegistrar : ITypeRegistrar
10 | {
11 |
12 | private readonly IServiceCollection _builder;
13 |
14 | public TypeRegistrar(IServiceCollection builder)
15 | {
16 | _builder = builder;
17 | }
18 |
19 | public ITypeResolver Build()
20 | {
21 | return new TypeResolver(_builder.BuildServiceProvider());
22 | }
23 |
24 | public void Register(Type service, Type implementation)
25 | {
26 | _builder.AddSingleton(service, implementation);
27 | }
28 |
29 | public void RegisterInstance(Type service, object implementation)
30 | {
31 | _builder.AddSingleton(service, implementation);
32 | }
33 |
34 | public void RegisterLazy(Type service, Func func)
35 | {
36 | if (func is null) {
37 | throw new ArgumentNullException(nameof(func));
38 | }
39 |
40 | _builder.AddSingleton(service, (provider) => func());
41 | }
42 |
43 | }
44 |
45 |
--------------------------------------------------------------------------------
/SmartImage.Rdx/Utilities/TypeResolver.cs:
--------------------------------------------------------------------------------
1 | // Deci SmartImage.Rdx TypeResolver.cs
2 | // $File.CreatedYear-$File.CreatedMonth-26 @ 1:46
3 |
4 | using Spectre.Console.Cli;
5 |
6 | namespace SmartImage.Rdx.Utilities;
7 |
8 | public sealed class TypeResolver : ITypeResolver, IDisposable
9 | {
10 |
11 | private readonly IServiceProvider _provider;
12 |
13 | public TypeResolver(IServiceProvider provider)
14 | {
15 | _provider = provider ?? throw new ArgumentNullException(nameof(provider));
16 | }
17 |
18 | public object? Resolve(Type? type)
19 | {
20 | if (type == null) {
21 | return null;
22 | }
23 |
24 | return _provider.GetService(type);
25 | }
26 |
27 | public void Dispose()
28 | {
29 | if (_provider is IDisposable disposable) {
30 | disposable.Dispose();
31 | }
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/SmartImage.UI/App.xaml:
--------------------------------------------------------------------------------
1 |
6 |
7 |
8 |
9 |
10 |
38 |
39 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
--------------------------------------------------------------------------------
/SmartImage.UI/App.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Diagnostics;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.IO;
6 | using System.IO.Pipes;
7 | using System.Threading;
8 | using System.Windows;
9 | using System.Windows.Interop;
10 | using JetBrains.Annotations;
11 | using Novus.Utilities;
12 | using Novus.Win32;
13 |
14 | #nullable disable
15 | namespace SmartImage.UI;
16 |
17 | ///
18 | /// Interaction logic for App.xaml
19 | ///
20 | public partial class App : Application
21 | {
22 |
23 | ///
24 | /// This identifier must be unique for each application.
25 | ///
26 | public const string SINGLE_GUID = "{910e8c27-ab31-4043-9c5d-1382707e6c93}";
27 |
28 | public const string IPC_PIPE_NAME = "SIPC";
29 |
30 | public const char ARGS_DELIM = '\0';
31 |
32 | private static Mutex _singleMutex;
33 |
34 | public NamedPipeServerStream PipeServer { get; private set; }
35 |
36 | public Thread PipeThread { get; private set; }
37 |
38 | private void Application_Startup(object sender, StartupEventArgs startupArgs)
39 | {
40 | bool multipleInstances = false, pipeServer = true;
41 |
42 | var enumerator = startupArgs.Args.GetEnumerator();
43 | using var unknown = enumerator as IDisposable;
44 |
45 | while (enumerator.MoveNext()) {
46 | var el = enumerator.Current;
47 | var els = el?.ToString();
48 |
49 | switch (els) {
50 | case "-mi":
51 | multipleInstances = true;
52 | break;
53 |
54 | case "-nms":
55 | pipeServer = false;
56 | break;
57 |
58 | default:
59 | break;
60 | }
61 | }
62 |
63 | _singleMutex = new Mutex(true, SINGLE_GUID);
64 | var isOnlyInstance = _singleMutex.WaitOne(TimeSpan.Zero, true);
65 |
66 | if (multipleInstances || isOnlyInstance) {
67 | // Show main window
68 | StartupUri = new Uri("MainWindow.xaml", UriKind.Relative);
69 |
70 | // Release SingleInstance Mutex
71 | _singleMutex.ReleaseMutex();
72 |
73 | if (pipeServer) {
74 | StartServer();
75 |
76 | }
77 | }
78 | else {
79 | // Bring the already running application into the foreground
80 | // Native.PostMessage(0xffff, AppUtil.m_registerWindowMessage, 0, 0);
81 | SendMessage(startupArgs);
82 |
83 | Shutdown();
84 |
85 | }
86 |
87 | }
88 |
89 | private static void SendMessage(StartupEventArgs e)
90 | {
91 | using var pipe = new NamedPipeClientStream(".", IPC_PIPE_NAME, PipeDirection.Out);
92 |
93 | using var stream = new StreamWriter(pipe);
94 |
95 | pipe.Connect();
96 |
97 | foreach (var s in e.Args) {
98 | stream.WriteLine(s);
99 | }
100 |
101 | stream.Write($"{ARGS_DELIM}{ProcessHelper.GetParent().Id}");
102 |
103 | }
104 |
105 | public delegate void PipeMessageCallback(string s);
106 |
107 | public event PipeMessageCallback OnPipeMessage;
108 |
109 | [DebuggerHidden]
110 | private void StartServer()
111 | {
112 | PipeServer = new NamedPipeServerStream(IPC_PIPE_NAME, PipeDirection.In);
113 |
114 | PipeThread = new Thread(Start)
115 | {
116 | IsBackground = true
117 | };
118 | PipeThread.Start();
119 | }
120 |
121 | [DebuggerHidden]
122 | private void Start()
123 | {
124 | while (true) {
125 | PipeServer.WaitForConnection();
126 | var sr = new StreamReader(PipeServer);
127 |
128 | while (!sr.EndOfStream) {
129 | var v = sr.ReadLine();
130 | OnPipeMessage?.Invoke(v);
131 | }
132 |
133 | PipeServer.Disconnect();
134 | }
135 | }
136 |
137 | }
--------------------------------------------------------------------------------
/SmartImage.UI/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Windows;
2 |
3 | [assembly: ThemeInfo(
4 | ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located
5 | //(used if a resource is not found in the page,
6 | // or application resource dictionaries)
7 | ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located
8 | //(used if a resource is not found in the page,
9 | // app, or any theme specific resource dictionaries)
10 | )]
11 |
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/AppComponents.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.UI AppControls.cs
2 | // 2023-07-23 @ 4:16 PM
3 | global using R4 = SmartImage.Lib.Serialization;
4 | global using R2 = SmartImage.UI.Resources;
5 | global using R1 = SmartImage.Lib.Resources;
6 | using System;
7 | using System.Drawing;
8 | using System.IO;
9 | using System.Reflection;
10 | using System.Runtime.CompilerServices;
11 | using System.Windows;
12 | using System.Windows.Media.Imaging;
13 | using JetBrains.Annotations;
14 | using SmartImage.Lib.Utilities;
15 |
16 | // ReSharper disable NullableWarningSuppressionIsUsed
17 |
18 | // ReSharper disable InconsistentNaming
19 |
20 | namespace SmartImage.UI.Controls;
21 | #nullable enable
22 | public static class AppComponents
23 | {
24 |
25 | public const int WIDTH = 20;
26 | public const int HEIGHT = 20;
27 |
28 | static AppComponents() { }
29 |
30 | public static BitmapImage LoadInline([CallerMemberName] string? name = null, int w = WIDTH, int h = HEIGHT,
31 | string? ext = "png")
32 | => Load(Path.ChangeExtension(name, ext)!, w, h);
33 |
34 | public static BitmapImage Load(string name, int w = WIDTH, int h = HEIGHT)
35 | {
36 | var bmp = new BitmapImage()
37 | { };
38 | bmp.BeginInit();
39 | bmp.CacheOption = BitmapCacheOption.OnLoad;
40 | bmp.UriSource = GetComponentUri(name);
41 | bmp.EndInit();
42 | bmp = bmp.ResizeBitmap(w, h);
43 |
44 | if (bmp.CanFreeze) {
45 | bmp.Freeze();
46 | }
47 |
48 | return bmp;
49 | }
50 |
51 | public static Uri GetComponentUri(string n, string resources = "Resources")
52 | {
53 | return new Uri($"pack://application:,,,/{Assembly.GetExecutingAssembly().GetName().Name};component/{resources}/{n}");
54 | }
55 |
56 | #region
57 |
58 | public static readonly BitmapImage accept = LoadInline();
59 |
60 | public static readonly BitmapImage exclamation = LoadInline();
61 |
62 | public static readonly BitmapImage help = LoadInline();
63 |
64 | public static readonly BitmapImage information = LoadInline();
65 |
66 | public static readonly BitmapImage picture = LoadInline();
67 | public static readonly BitmapImage pictures = LoadInline();
68 |
69 | public static readonly BitmapImage picture_save = LoadInline();
70 | public static readonly BitmapImage picture_error = LoadInline();
71 | public static readonly BitmapImage picture_empty = LoadInline();
72 | public static readonly BitmapImage picture_link = LoadInline();
73 |
74 | public static readonly BitmapImage artwork = LoadInline();
75 |
76 | public static readonly BitmapImage image = LoadInline();
77 |
78 | public static readonly BitmapImage image_link = LoadInline();
79 |
80 | public static readonly BitmapImage link = LoadInline();
81 |
82 | public static readonly BitmapImage arrow_refresh = LoadInline();
83 |
84 | public static readonly BitmapImage clipboard_invoice = LoadInline();
85 |
86 | public static readonly BitmapImage asterisk_yellow = LoadInline();
87 |
88 | #endregion
89 |
90 | public static Bitmap BitmapImage2Bitmap(this BitmapImage bitmapImage)
91 | {
92 | // BitmapImage bitmapImage = new BitmapImage(new Uri("../Images/test.png", UriKind.Relative));
93 |
94 | using (MemoryStream outStream = new MemoryStream()) {
95 | BitmapEncoder enc = new BmpBitmapEncoder();
96 | enc.Frames.Add(BitmapFrame.Create(bitmapImage));
97 | enc.Save(outStream);
98 | Bitmap bitmap = new Bitmap(outStream);
99 |
100 | return new Bitmap(bitmap);
101 | }
102 | }
103 |
104 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/ControlsHelper.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Numerics;
4 | using System.Windows;
5 | using System.Windows.Controls;
6 | using System.Windows.Input;
7 | using System.Windows.Media;
8 | using System.Windows.Media.Imaging;
9 | using Kantan.Utilities;
10 | using SmartImage.Lib.Engines;
11 | using SmartImage.Lib.Engines.Impl.Upload;
12 | using SmartImage.Lib.Images;
13 | using SmartImage.Lib.Images.Uni;
14 |
15 | namespace SmartImage.UI.Controls;
16 |
17 | public static class ControlsHelper
18 | {
19 |
20 | public static bool IsDoubleClick(this MouseButtonEventArgs e)
21 | {
22 | return e.ClickCount == 2;
23 | }
24 |
25 | public static BitmapImage ResizeBitmap(this BitmapImage originalBitmap, int newWidth, int newHeight)
26 | {
27 | // Calculate the scale factors for width and height
28 | double scaleX = (double) newWidth / originalBitmap.PixelWidth;
29 | double scaleY = (double) newHeight / originalBitmap.PixelHeight;
30 |
31 | // Create a new Transform to apply the scale factors
32 | Transform transform = new ScaleTransform(scaleX, scaleY);
33 |
34 | // Create a new TransformedBitmap with the original BitmapImage and the scale Transform
35 | var resizedBitmap = new TransformedBitmap(originalBitmap, transform);
36 |
37 | // Create a new BitmapImage and set it as the source of the resized image
38 | var bitmapImage = new BitmapImage();
39 | bitmapImage.BeginInit();
40 |
41 | if (originalBitmap.UriSource != null) {
42 | bitmapImage.UriSource = originalBitmap.UriSource;
43 | }
44 |
45 | else if (originalBitmap.StreamSource != null) {
46 | bitmapImage.StreamSource = originalBitmap.StreamSource;
47 | }
48 |
49 | bitmapImage.DecodePixelWidth = newWidth;
50 | bitmapImage.DecodePixelHeight = newHeight;
51 | bitmapImage.CacheOption = BitmapCacheOption.OnLoad;
52 | bitmapImage.EndInit();
53 |
54 | return bitmapImage;
55 | }
56 |
57 | public static bool IsLoaded(this RoutedEventArgs e)
58 | => e is { Source: FrameworkElement { IsLoaded: true } fx };
59 |
60 | public static void HandleEnum(this ListBox lb, T src) where T : struct, Enum
61 | {
62 | foreach (T t in lb.ItemsSource.OfType()) {
63 | if (src.HasFlag(t)) {
64 | lb.SelectedItems.Add(t);
65 | }
66 | else {
67 | lb.SelectedItems.Remove(t);
68 | }
69 | }
70 | }
71 |
72 | public static SearchEngineOptions HandleEnum(this ListBox lb, SelectionChangedEventArgs e,
73 | SearchEngineOptions orig)
74 | {
75 | // var rg = lb.ItemsSource.OfType().ToArray();
76 |
77 | var ai = e.AddedItems.OfType()
78 | .Aggregate(default(SearchEngineOptions), Or);
79 |
80 | var ri = e.RemovedItems.OfType()
81 | .Aggregate(default(SearchEngineOptions), Or);
82 |
83 | var si = lb.SelectedItems.OfType().ToArray();
84 |
85 | var siv = si.Aggregate(default(SearchEngineOptions), Or);
86 |
87 | orig &= siv;
88 | orig &= (~ri);
89 | orig |= ai;
90 |
91 | return orig;
92 |
93 | static SearchEngineOptions Or(SearchEngineOptions n, SearchEngineOptions l) => n | l;
94 | }
95 |
96 |
97 | public static string[] GetFilesFromDrop(this DragEventArgs e)
98 | {
99 | if (e.Data.GetDataPresent(DataFormats.FileDrop)) {
100 |
101 | if (e.Data.GetData(DataFormats.FileDrop, true) is string[] files
102 | && files.Any()) {
103 |
104 | return files;
105 |
106 | }
107 | }
108 |
109 | return [];
110 | }
111 |
112 | public static string FormatDescription(string name, UniImage uni, int? w, int? h)
113 | {
114 | string bytes = FormatSize(uni);
115 |
116 | string i;
117 |
118 | i = FormatDimensions(w, h);
119 |
120 | return $"{name} ⇉ [{(uni.HasImageFormat ? uni.ImageFormat : "?")}] [{bytes}] • {i}";
121 | }
122 |
123 | public static string FormatSize(UniImage uni)
124 | {
125 | string bytes;
126 |
127 | if (!uni.Stream.CanRead) {
128 | bytes = "???";
129 | }
130 | else
131 | bytes = FormatHelper.FormatBytes(uni.Stream.Length);
132 |
133 | return bytes;
134 | }
135 |
136 | public static (bool ctrl, bool alt, bool shift) GetModifiers()
137 | {
138 | var ctrl = Keyboard.Modifiers.HasFlag(ModifierKeys.Control);
139 | var alt = Keyboard.Modifiers.HasFlag(ModifierKeys.Alt);
140 | var shift = Keyboard.Modifiers.HasFlag(ModifierKeys.Shift);
141 | return (ctrl, alt, shift);
142 | }
143 |
144 | public static string FormatDimensions(int? w, int? h)
145 | {
146 | if (w.HasValue && h.HasValue) {
147 | return $"{w}\u00d7{h}";
148 | }
149 |
150 | return String.Empty;
151 | }
152 |
153 | internal const string STR_NA = "-";
154 |
155 | public static readonly string[] UploadEngineNames = Enum.GetNames();
156 |
157 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/Converters/BoolToValConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Drawing;
3 | using System.Globalization;
4 | using System.Text;
5 | using System.Windows;
6 | using System.Windows.Data;
7 | using SmartImage.Lib.Results.Data;
8 |
9 | namespace SmartImage.UI.Controls.Converters;
10 |
11 | public class BoolToValueConverter : IValueConverter
12 | {
13 |
14 | public T FalseValue { get; set; }
15 |
16 | public T TrueValue { get; set; }
17 |
18 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
19 | {
20 | if (value == null)
21 | return FalseValue;
22 |
23 | return (bool) value ? TrueValue : FalseValue;
24 | }
25 |
26 | public object ConvertBack(object? value, Type targetType, object? parameter,
27 | CultureInfo culture)
28 | {
29 | return value != null ? value.Equals(TrueValue) : false;
30 | }
31 |
32 | }
33 |
34 | [ValueConversion(typeof(bool), typeof(string))]
35 | public class BoolToStringConverter : BoolToValueConverter { }
36 |
37 | public class BoolToBrushConverter : BoolToValueConverter { }
38 |
39 | public class BoolToVisibilityConverter : BoolToValueConverter { }
40 |
41 | [ValueConversion(typeof(bool), typeof(object))]
42 | public class BoolToObjectConverter : BoolToValueConverter { }
43 |
44 | public class EnumFlagsToStringConverter : IValueConverter
45 | {
46 |
47 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
48 | {
49 | string enumString;
50 |
51 | try {
52 | var x = value.ToString().Split(", ");
53 | StringBuilder sb = new(x.Length);
54 |
55 | foreach (var v in x) {
56 | sb.Append(v[0]);
57 | }
58 |
59 | return sb.ToString();
60 | }
61 | catch {
62 | return string.Empty;
63 | }
64 | }
65 |
66 | // No need to implement converting back on a one-way binding
67 | public object ConvertBack(object? value, Type targetType, object? parameter,
68 | CultureInfo culture)
69 | {
70 | throw new NotImplementedException();
71 | }
72 |
73 | }
74 |
75 | public class EnumToStringConverter : IValueConverter
76 | {
77 |
78 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
79 | {
80 | string enumString;
81 |
82 | try {
83 | enumString = Enum.GetName(value.GetType(), value);
84 | return enumString;
85 | }
86 | catch {
87 | return string.Empty;
88 | }
89 | }
90 |
91 | // No need to implement converting back on a one-way binding
92 | public object ConvertBack(object? value, Type targetType, object? parameter,
93 | CultureInfo culture)
94 | {
95 | throw new NotImplementedException();
96 | }
97 |
98 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/Converters/BooleanToBrushConverter.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.UI BooleanToBrushConverter.cs
2 | // 2023-08-20 @ 11:38 AM
3 |
4 | using System;
5 | using System.Globalization;
6 | using System.Windows.Data;
7 | using System.Windows.Media;
8 |
9 | namespace SmartImage.UI.Controls.Converters;
10 |
11 | #pragma warning disable CS8618
12 | [ValueConversion(typeof(bool), typeof(Brush))]
13 | public class BooleanToBrushConverter : IValueConverter
14 | {
15 |
16 | public Brush TrueBrush { get; set; }
17 |
18 | public Brush FalseBrush { get; set; }
19 |
20 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
21 | {
22 | if (value is bool boolValue) {
23 | return boolValue ? TrueBrush : FalseBrush;
24 | }
25 |
26 | return FalseBrush;
27 | }
28 |
29 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
30 | {
31 | throw new NotImplementedException();
32 | }
33 |
34 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/Converters/ImageDimensionConverter.cs:
--------------------------------------------------------------------------------
1 | using Kantan.Utilities;
2 | using Novus.Win32;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Globalization;
6 | using System.Linq;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 | using System.Windows.Controls;
10 | using System.Windows.Data;
11 | using SmartImage.Lib.Results.Data;
12 | using SmartImage.UI.Model;
13 |
14 | namespace SmartImage.UI.Controls.Converters;
15 |
16 | [ValueConversion(typeof(IBitmapImageSource), typeof(string))]
17 | public class ImageDimensionConverter : IValueConverter
18 | {
19 |
20 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
21 | {
22 | var val = (IBitmapImageSource) value;
23 |
24 | if (val == null) {
25 | return null;
26 | }
27 |
28 | string dim;
29 |
30 | int? w = null, h = null;
31 |
32 | if (val is { } ri) {
33 | if (ri is { IsThumbnail: true, HasImage: true, Image: { } i }) {
34 | w = i.PixelWidth;
35 | h = i.PixelHeight;
36 | }
37 |
38 | }
39 | else {
40 | w = val.Width;
41 | h = val.Height;
42 |
43 | }
44 |
45 | // w ??= val.Width;
46 | // h ??= val.Height;
47 |
48 | dim = ControlsHelper.FormatDimensions(w, h);
49 |
50 | return dim;
51 | }
52 |
53 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
54 | {
55 | return null;
56 | }
57 |
58 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/Converters/InvertableBooleanToVisibilityConverter.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.UI InvertableBooleanToVisibilityConverter.cs
2 | // 2023-09-02 @ 3:46 AM
3 |
4 | using System;
5 | using System.Globalization;
6 | using System.Windows;
7 | using System.Windows.Data;
8 |
9 | namespace SmartImage.UI.Controls.Converters;
10 |
11 | [ValueConversion(typeof(bool), typeof(Visibility))]
12 | public class InvertableBooleanToVisibilityConverter : IValueConverter
13 | {
14 | public enum Parameters
15 | {
16 | Normal, Inverted
17 | }
18 |
19 | public object Convert(object value, Type targetType,
20 | object parameter, CultureInfo culture)
21 | {
22 | var boolValue = (bool)value;
23 | var direction = (Parameters)Enum.Parse(typeof(Parameters), (string)parameter);
24 |
25 | if (direction == Parameters.Inverted)
26 | return !boolValue ? Visibility.Visible : Visibility.Hidden;
27 |
28 | return boolValue ? Visibility.Visible : Visibility.Hidden;
29 | }
30 |
31 | public object ConvertBack(object value, Type targetType,
32 | object parameter, CultureInfo culture)
33 | {
34 | return null;
35 | }
36 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/Converters/UnitStringConverter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Globalization;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows.Data;
8 | using Kantan.Utilities;
9 | using Novus.Win32;
10 |
11 | namespace SmartImage.UI.Controls.Converters;
12 |
13 | [ValueConversion(typeof(long), typeof(string))]
14 | internal class UnitStringConverter : IValueConverter
15 | {
16 |
17 | public object Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
18 | {
19 | var val = (long) value;
20 |
21 | string bytes;
22 |
23 | if (val == Native.ERROR_SV) {
24 | bytes = "N/A";
25 | }
26 | else bytes = FormatHelper.FormatBytes(val);
27 |
28 | return bytes;
29 | }
30 |
31 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
32 | {
33 | return null;
34 | }
35 |
36 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/Converters/UrlConverter.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.UI UrlConverter.cs
2 | // 2023-09-27 @ 3:20 AM
3 |
4 | using System;
5 | using System.Globalization;
6 | using System.Windows.Data;
7 | using Flurl;
8 |
9 | namespace SmartImage.UI.Controls.Converters;
10 |
11 | [ValueConversion(typeof(Url), typeof(string))]
12 | public class UrlConverter : IValueConverter
13 | {
14 | public object? Convert(object? value, Type targetType, object? parameter, CultureInfo culture)
15 | {
16 | if (value == null)
17 | {
18 | return null;
19 | }
20 |
21 | var date = (Url)value;
22 | return date.ToString();
23 | }
24 |
25 | public object? ConvertBack(object? value, Type targetType, object? parameter, CultureInfo culture)
26 | {
27 | if (value == null)
28 | {
29 | return null;
30 | }
31 |
32 | string strValue = value as string;
33 | Url resultDateTime;
34 |
35 | return (Url)strValue;
36 | }
37 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/SharedImageControl.xaml:
--------------------------------------------------------------------------------
1 |
9 |
10 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/SmartImage.UI/Controls/SharedImageControl.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Windows;
7 | using System.Windows.Controls;
8 | using System.Windows.Data;
9 | using System.Windows.Documents;
10 | using System.Windows.Input;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using System.Windows.Navigation;
14 | using System.Windows.Shapes;
15 |
16 | namespace SmartImage.UI;
17 |
18 | ///
19 | /// Interaction logic for SharedImageControl.xaml
20 | ///
21 | public partial class SharedImageControl : UserControl
22 | {
23 | public SharedImageControl()
24 | {
25 | InitializeComponent();
26 | }
27 |
28 | public static readonly DependencyProperty ImageSourceProperty =
29 | DependencyProperty.Register(nameof(ImageSource), typeof(ImageSource), typeof(SharedImageControl),
30 | new PropertyMetadata(null));
31 |
32 | public ImageSource ImageSource
33 | {
34 | get { return (ImageSource) GetValue(ImageSourceProperty); }
35 | set { SetValue(ImageSourceProperty, value); }
36 | }
37 | }
--------------------------------------------------------------------------------
/SmartImage.UI/HydrusWindow.xaml:
--------------------------------------------------------------------------------
1 |
12 |
13 |
14 |
15 |
16 |
19 |
20 |
23 |
24 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/SmartImage.UI/HydrusWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Security.Cryptography;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using System.Windows;
8 | using System.Windows.Controls;
9 | using System.Windows.Data;
10 | using System.Windows.Documents;
11 | using System.Windows.Input;
12 | using System.Windows.Media;
13 | using System.Windows.Media.Imaging;
14 | using System.Windows.Shapes;
15 | using Kantan.Utilities;
16 | using Novus.Streams;
17 | using SmartImage.UI.Model;
18 |
19 | namespace SmartImage.UI;
20 |
21 | ///
22 | /// Interaction logic for HydrusWindow.xaml
23 | ///
24 | public partial class HydrusWindow : Window
25 | {
26 | public HydrusWindow(SharedInfo s)
27 | {
28 | DataContext = this;
29 | InitializeComponent();
30 | Shared = s;
31 | }
32 |
33 | public SharedInfo Shared { get; set; }
34 |
35 | private void Btn_1_OnClick(object sender, RoutedEventArgs e)
36 | {
37 | /*Tb_Info.Dispatcher.InvokeAsync(async () =>
38 | {
39 | Shared.Query.Uni.Stream.TrySeek();
40 | var data = SHA256.HashData(Shared.Query.Uni.Stream);
41 | var hash = HashHelper.Sha256.ToString(data);
42 | var t = await Shared.m_hydrus.GetFileAsync(hash);
43 | var d = await t.GetStreamAsync();
44 | var imageSource = new BitmapImage();
45 | imageSource.BeginInit();
46 | imageSource.StreamSource = d;
47 | imageSource.EndInit();
48 | imageSource.Freeze();
49 | Img_Preview.Source = imageSource;
50 | });*/
51 | e.Handled = true;
52 | }
53 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Icon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Icon.ico
--------------------------------------------------------------------------------
/SmartImage.UI/Model/IBitmapImageSource.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.UI IImageProvider.cs
2 | // 2023-09-13 @ 5:27 PM
3 |
4 | using System.Windows.Media;
5 | using System.Windows.Media.Imaging;
6 |
7 | namespace SmartImage.UI.Model;
8 |
9 | public interface IBitmapImageSource
10 | {
11 |
12 | public BitmapSource? Image { get; internal set; }
13 |
14 | bool HasImage { get; }
15 |
16 | bool CanLoadImage { get; }
17 |
18 | bool IsThumbnail { get; }
19 |
20 | int? Width { get; }
21 |
22 | int? Height { get; }
23 |
24 | bool LoadImage();
25 |
26 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Model/INamed.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.UI INamed.cs
2 | // 2023-09-14 @ 11:18 AM
3 |
4 | namespace SmartImage.UI.Model;
5 |
6 | public interface INamed
7 | {
8 | public string Name { get; set; }
9 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Model/LazyProperty.cs:
--------------------------------------------------------------------------------
1 | // Author: Deci | Project: SmartImage.UI | Name: LazyProperty.cs
2 | // Date: $File.CreatedYear-$File.CreatedMonth-21 @ 13:0:28
3 |
4 | using System;
5 | using System.Collections.Generic;
6 | using System.ComponentModel;
7 | using System.Runtime.CompilerServices;
8 | using System.Threading;
9 | using System.Threading.Tasks;
10 |
11 | namespace SmartImage.UI.Model;
12 |
13 | public class LazyProperty : INotifyPropertyChanged
14 | {
15 |
16 | private CancellationTokenSource m_cancelTokenSource = new();
17 |
18 | private bool m_isLoading;
19 | private bool m_errorOnLoading;
20 |
21 | private T m_defaultValue;
22 | private T m_value;
23 |
24 | private Func> m_retrievalFunc;
25 |
26 | private bool IsLoaded { get; set; }
27 |
28 | public bool IsLoading
29 | {
30 | get => m_isLoading;
31 | set
32 | {
33 | if (m_isLoading != value) {
34 | m_isLoading = value;
35 | OnPropertyChanged();
36 | }
37 | }
38 | }
39 |
40 | public bool ErrorOnLoading
41 | {
42 | get => m_errorOnLoading;
43 | set
44 | {
45 | if (m_errorOnLoading != value) {
46 | m_errorOnLoading = value;
47 | OnPropertyChanged();
48 | }
49 | }
50 | }
51 |
52 | public T Value
53 | {
54 | get
55 | {
56 | if (IsLoaded)
57 | return m_value;
58 |
59 | if (!m_isLoading) {
60 | IsLoading = true;
61 |
62 | LoadValueAsync().ContinueWith(t =>
63 | {
64 | if (!t.IsCanceled) {
65 | if (t.IsFaulted) {
66 | m_value = m_defaultValue;
67 | ErrorOnLoading = true;
68 | IsLoaded = true;
69 | IsLoading = false;
70 | OnPropertyChanged();
71 | }
72 | else {
73 | Value = t.Result;
74 | }
75 | }
76 | });
77 | }
78 |
79 | return m_defaultValue;
80 | }
81 |
82 | // if you want a ReadOnly-property just set this setter to private
83 | set
84 | {
85 | if (m_isLoading)
86 |
87 | // since we set the value now, there is no need
88 | // to retrieve the "old" value asynchronously
89 | CancelLoading();
90 |
91 | if (!EqualityComparer.Default.Equals(m_value, value)) {
92 | m_value = value;
93 | IsLoaded = true;
94 | IsLoading = false;
95 | ErrorOnLoading = false;
96 |
97 | OnPropertyChanged();
98 | }
99 | }
100 | }
101 |
102 | private async Task LoadValueAsync()
103 | {
104 | return await m_retrievalFunc(m_cancelTokenSource.Token);
105 | }
106 |
107 | public void CancelLoading()
108 | {
109 | m_cancelTokenSource.Cancel();
110 | }
111 |
112 | public LazyProperty(Func> retrievalFunc, T defaultValue)
113 | {
114 | m_retrievalFunc = retrievalFunc ?? throw new ArgumentNullException(nameof(retrievalFunc));
115 | m_defaultValue = defaultValue;
116 |
117 | m_value = default(T);
118 | }
119 |
120 | ///
121 | /// This allows you to assign the value of this lazy property directly
122 | /// to a variable of type T
123 | ///
124 | public static implicit operator T(LazyProperty p)
125 | {
126 | return p.Value;
127 | }
128 |
129 | public event PropertyChangedEventHandler PropertyChanged;
130 |
131 | protected void OnPropertyChanged([CallerMemberName] string propertyName = "")
132 | {
133 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
134 | }
135 |
136 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Model/LogEntry.cs:
--------------------------------------------------------------------------------
1 | // Read S SmartImage.UI LogEntry.cs
2 | // 2023-08-11 @ 12:28 PM
3 |
4 | using System;
5 |
6 | namespace SmartImage.UI.Model;
7 |
8 | #pragma warning disable CS8618
9 | public sealed class LogEntry
10 | {
11 | public DateTime Time { get; }
12 | public string Message { get; }
13 |
14 | public LogEntry(string message) : this(DateTime.Now, message) { }
15 |
16 | public LogEntry(DateTime time, string message)
17 | {
18 | Time = time;
19 | Message = message;
20 | }
21 |
22 | public override string ToString()
23 | {
24 | return $"{Time} {Message}";
25 | }
26 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Model/SharedInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel;
4 | using System.Linq;
5 | using System.Runtime.CompilerServices;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows.Shapes;
9 | using SmartImage.Lib.Clients;
10 |
11 | namespace SmartImage.UI.Model
12 | {
13 | public class SharedInfo : INotifyPropertyChanged
14 | {
15 | public SharedInfo()
16 | {
17 | // Client = new SearchClient(new SearchConfig());
18 |
19 | m_hydrus = new HydrusClient();
20 | }
21 |
22 | public readonly HydrusClient m_hydrus;
23 |
24 | private string _hash;
25 |
26 | public string Hash
27 | {
28 | get { return _hash; }
29 | set
30 | {
31 | if (_hash != value) {
32 | _hash = value;
33 | OnPropertyChanged();
34 | }
35 | }
36 | }
37 |
38 | public event PropertyChangedEventHandler? PropertyChanged;
39 |
40 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = null)
41 | {
42 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
43 | }
44 | }
45 | }
--------------------------------------------------------------------------------
/SmartImage.UI/Model/UniResultItem.cs:
--------------------------------------------------------------------------------
1 | // Deci SmartImage.UI UniResultItem.cs
2 | // $File.CreatedYear-$File.CreatedMonth-25 @ 4:3
3 |
4 | using System;
5 | using System.Diagnostics;
6 | using System.IO;
7 | using System.Linq;
8 | using System.Net.Cache;
9 | using System.Security.Cryptography;
10 | using System.Threading.Tasks;
11 | using System.Windows.Media;
12 | using System.Windows.Media.Imaging;
13 | using Flurl;
14 | using Kantan.Net.Utilities;
15 | using Kantan.Utilities;
16 | using Novus.FileTypes;
17 | using Novus.FileTypes.Uni;
18 | using Novus.OS;
19 | using Novus.Streams;
20 | using Novus.Win32;
21 | using SmartImage.Lib.Images;
22 | using SmartImage.Lib.Images.Uni;
23 | using SmartImage.Lib.Results.Data;
24 | using SmartImage.Lib.Utilities;
25 | using SmartImage.Lib.Utilities.Integration;
26 | using SmartImage.UI.Controls;
27 |
28 | namespace SmartImage.UI.Model;
29 |
30 | #pragma warning disable CS8618
31 | public class UniResultItem : ResultItem
32 | {
33 |
34 | #region
35 |
36 | public override bool CanLoadImage => !HasImage && Uni != null;
37 |
38 | public override long Size
39 | {
40 | get
41 | {
42 | if (Uni != null) {
43 | return Uni.Stream.Length;
44 | }
45 |
46 | return Native.ERROR_SV;
47 | }
48 | }
49 |
50 | public UniImage? Uni
51 | {
52 | get
53 | {
54 | if (UniIndex.HasValue && Result.Uni != null) {
55 | return Result.Uni[UniIndex.Value];
56 |
57 | }
58 |
59 | return null;
60 | }
61 | }
62 |
63 | public string Hash { get; }
64 |
65 | public int? UniIndex { get; }
66 |
67 | #endregion
68 |
69 | public UniResultItem(ResultItem ri, int? idx)
70 | : base(ri.Result, $"{ri.Name} ({idx})")
71 | {
72 | UniIndex = idx;
73 |
74 | if (Uni == null) {
75 | Debugger.Break();
76 | }
77 |
78 | if (Uni != null) {
79 | if (Uni.IsStream) {
80 | // todo: update GetFileName
81 | Url = ri.Url.GetFileName().Split(':')[0];
82 |
83 | if (String.IsNullOrWhiteSpace(Path.GetExtension(Url)) && Uni.HasImageFormat) {
84 |
85 | Url = Path.ChangeExtension(Url, Uni.ImageFormat?.FileExtensions.First());
86 | }
87 | }
88 | else {
89 | Url = Uni.Value.ToString();
90 |
91 | }
92 |
93 | // StatusImage = Image;
94 | }
95 | else {
96 | Image = null;
97 | }
98 |
99 | StatusImage = AppComponents.picture;
100 |
101 | // SizeFormat = ControlsHelper.FormatSize(Uni);
102 | Hash = HashHelper.Sha256.ToString(SHA256.HashData(Uni.Stream));
103 | Uni.Stream.TrySeek();
104 |
105 | }
106 |
107 | protected override void OnImageDownloadProgress(object? sender, DownloadProgressEventArgs args)
108 | {
109 | PreviewProgress = (args.Progress);
110 | PreviewText = $"Download progress...{PreviewProgress}";
111 | }
112 |
113 | protected override void OnImageDownloadFailed(object? sender, ExceptionEventArgs args)
114 | {
115 | PreviewProgress = 0;
116 | PreviewText = $"Download failed: {args.ErrorException.Message}";
117 | }
118 |
119 | protected override void OnImageDownloadCompleted(object? sender, EventArgs args)
120 | {
121 | PreviewText = $"Download complete";
122 |
123 | IsThumbnail = false;
124 | OnPropertyChanged(nameof(IsThumbnail));
125 |
126 | // Properties &= ResultItemProperties.Thumbnail;
127 |
128 | if (Image is { CanFreeze: true }) {
129 | Image.Freeze();
130 | }
131 |
132 | }
133 |
134 | public override bool LoadImage()
135 | {
136 | if (HasImage) {
137 | return true;
138 | }
139 | else if (!CanLoadImage) {
140 | return false;
141 | }
142 |
143 | var image = new BitmapImage()
144 | { };
145 | image.BeginInit();
146 | Trace.Assert(Uni != null);
147 |
148 | image.StreamSource = Uni.Stream;
149 |
150 | // Image.StreamSource = Uni.Stream;
151 | // m_image.StreamSource = Query.Uni.Stream;
152 | // Image.CacheOption = BitmapCacheOption.OnLoad;
153 | image.CacheOption = BitmapCacheOption.OnDemand;
154 |
155 | // Image.CreateOptions = BitmapCreateOptions.DelayCreation;
156 | image.UriCachePolicy = new RequestCachePolicy(RequestCacheLevel.Default);
157 | image.EndInit();
158 |
159 | image.DownloadFailed += OnImageDownloadFailed;
160 | image.DownloadProgress += OnImageDownloadProgress;
161 | image.DownloadCompleted += OnImageDownloadCompleted;
162 |
163 | Image = image;
164 | UpdateProperties();
165 | return HasImage;
166 | }
167 |
168 | public override async Task DownloadAsync(string? dir = null, bool exp = true)
169 | {
170 | string path;
171 | Trace.Assert(Uni != null);
172 |
173 | if (Uni.IsStream) {
174 | path = Url;
175 | }
176 | else /*if (uni.IsUri)*/ {
177 | var url = (Url) Uni.Value.ToString();
178 | path = url.GetFileName();
179 |
180 | }
181 |
182 | dir ??= BaseOSIntegration.Integration.PersonalPath;
183 | var path2 = Path.Combine(dir, path);
184 |
185 | var fs = File.OpenWrite(path2);
186 | Uni.Stream.TrySeek();
187 |
188 | StatusImage = AppComponents.picture_save;
189 | await Uni.Stream.CopyToAsync(fs);
190 |
191 | if (exp) {
192 | FileSystem.ExploreFile(path2);
193 | }
194 |
195 | await fs.DisposeAsync();
196 |
197 | CanDownload = false;
198 | Download = path2;
199 |
200 | // u.Dispose();
201 | UpdateProperties();
202 |
203 | return path2;
204 | }
205 |
206 | public override void Dispose()
207 | {
208 | GC.SuppressFinalize(this);
209 | base.Dispose();
210 |
211 | Uni?.Dispose();
212 | }
213 |
214 | }
--------------------------------------------------------------------------------
/SmartImage.UI/ResourceDict.xaml:
--------------------------------------------------------------------------------
1 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/accept.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/accept.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/arrow_down.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/arrow_down.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/arrow_redo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/arrow_redo.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/arrow_refresh.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/arrow_refresh.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/arrow_rotate_anticlockwise.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/arrow_rotate_anticlockwise.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/arrow_undo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/arrow_undo.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/artwork.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/artwork.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/asterisk_yellow.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/asterisk_yellow.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/clipboard_invoice.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/clipboard_invoice.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/clipboard_sign.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/clipboard_sign.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/emotion_question.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/emotion_question.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/exclamation.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/exclamation.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/help.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/help.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/image.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/image.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/image_link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/image_link.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/information.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/information.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/link.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture_add.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture_add.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture_delete.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture_delete.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture_empty.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture_empty.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture_error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture_error.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture_go.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture_go.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture_insert.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture_insert.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture_link.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture_link.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/picture_save.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/picture_save.png
--------------------------------------------------------------------------------
/SmartImage.UI/Resources/pictures.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Decimation/SmartImage/49b6e8266eb5c288bf7ff7685d7e54d0a5b17442/SmartImage.UI/Resources/pictures.png
--------------------------------------------------------------------------------
/SmartImage.UI/ResultWindow.xaml.cs:
--------------------------------------------------------------------------------
1 | global using MNNW = System.Diagnostics.CodeAnalysis.MemberNotNullWhenAttribute;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Diagnostics.CodeAnalysis;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using System.Windows;
9 | using System.Windows.Controls;
10 | using System.Windows.Data;
11 | using System.Windows.Documents;
12 | using System.Windows.Input;
13 | using System.Windows.Media;
14 | using System.Windows.Media.Imaging;
15 | using System.Windows.Shapes;
16 | using SmartImage.UI.Model;
17 |
18 | namespace SmartImage.UI;
19 |
20 | ///
21 | /// Interaction logic for SubWindow.xaml
22 | ///
23 | public partial class ResultWindow : Window
24 | {
25 | public ResultWindow(ResultItem result)
26 | {
27 | DataContext = this;
28 | Result = result;
29 |
30 | InitializeComponent();
31 | }
32 |
33 | public ResultItem Result { get; set; }
34 |
35 | [MNNW(true, nameof(UniResult))]
36 | public bool IsUni => Result is UniResultItem;
37 |
38 | public UniResultItem? UniResult => Result as UniResultItem;
39 |
40 | private void Btn_HyRun_Click(object sender, RoutedEventArgs e)
41 | {
42 |
43 | }
44 | }
--------------------------------------------------------------------------------
/SmartImage.UI2/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Reactive.Concurrency;
2 | using System.Reactive.Disposables;
3 | using ReactiveUI;
4 | using Terminal.Gui;
5 |
6 | namespace SmartImage.UI2
7 | {
8 | public static class Program
9 | {
10 |
11 | public class TerminalScheduler : LocalScheduler
12 | {
13 |
14 | public static readonly TerminalScheduler Default = new();
15 |
16 | private TerminalScheduler() { }
17 |
18 | public override IDisposable Schedule(
19 | TState state,
20 | TimeSpan dueTime,
21 | Func action
22 | )
23 | {
24 | IDisposable PostOnMainLoop()
25 | {
26 | var composite = new CompositeDisposable(2);
27 | var cancellation = new CancellationDisposable();
28 |
29 | Application.MainLoop.Invoke(
30 | () =>
31 | {
32 | if (!cancellation.Token.IsCancellationRequested) {
33 | composite.Add(action(this, state));
34 | }
35 | }
36 | );
37 | composite.Add(cancellation);
38 |
39 | return composite;
40 | }
41 |
42 | IDisposable PostOnMainLoopAsTimeout()
43 | {
44 | var composite = new CompositeDisposable(2);
45 |
46 | object timeout = Application.MainLoop.AddTimeout(
47 | dueTime, (cb) =>
48 | {
49 | composite.Add(action(this, state));
50 |
51 | return false;
52 | }
53 | );
54 | composite.Add(Disposable.Create(() => Application.MainLoop.RemoveTimeout(timeout)));
55 |
56 | return composite;
57 | }
58 |
59 | return dueTime == TimeSpan.Zero
60 | ? PostOnMainLoop()
61 | : PostOnMainLoopAsTimeout();
62 | }
63 |
64 | }
65 |
66 | public class Item1 { }
67 |
68 | public static void Main(string[] args)
69 | {
70 | Application.Init();
71 | RxApp.MainThreadScheduler = Program.TerminalScheduler.Default;
72 | RxApp.TaskpoolScheduler = TaskPoolScheduler.Default;
73 |
74 | var tx = new TextField();
75 |
76 | var tv = new TreeView();
77 |
78 | var w = new Window();
79 | w.Add(tv);
80 |
81 | Application.Run(w);
82 | Application.Top.Dispose();
83 | Application.Shutdown();
84 |
85 | }
86 |
87 | }
88 | }
--------------------------------------------------------------------------------
/SmartImage.UI2/SmartImage.UI2.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net9.0
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | ..\..\..\VSProjects\Kantan\Kantan\bin\Release\net9.0\Kantan.dll
40 |
41 |
42 | ..\..\..\VSProjects\Kantan\Kantan.Net\bin\Release\net9.0\Kantan.Net.dll
43 |
44 |
45 | ..\..\..\VSProjects\Novus\Novus\bin\Release\net9.0\Novus.dll
46 |
47 |
48 |
49 |
50 |
--------------------------------------------------------------------------------