main e63e7843ba45 cached
136 files
971.0 KB
234.3k tokens
1048 symbols
1 requests
Download .txt
Showing preview only (1,024K chars total). Download the full file or copy to clipboard to get everything.
Repository: the-database/MangaJaNaiConverterGui
Branch: main
Commit: e63e7843ba45
Files: 136
Total size: 971.0 KB

Directory structure:
gitextract_o6l0qlog/

├── .editorconfig
├── .github/
│   └── workflows/
│       └── deploy.yml
├── .gitignore
├── LICENSE
├── MangaJaNaiConverterGui/
│   ├── App.axaml
│   ├── App.axaml.cs
│   ├── Drivers/
│   │   └── NewtonsoftJsonSuspensionDriver.cs
│   ├── MangaJaNaiConverterGui.csproj
│   ├── Program.cs
│   ├── Services/
│   │   ├── Downloader.cs
│   │   ├── ETACalculator.cs
│   │   ├── IPythonService.cs
│   │   ├── ISuspensionDriverService.cs
│   │   ├── IUpdateManagerService.cs
│   │   ├── PythonService.cs
│   │   ├── SuspensionDriverService.cs
│   │   └── UpdateManagerService.cs
│   ├── ViewLocator.cs
│   ├── ViewModels/
│   │   ├── MainWindowViewModel.cs
│   │   └── ViewModelBase.cs
│   ├── Views/
│   │   ├── MainWindow.axaml
│   │   └── MainWindow.axaml.cs
│   ├── app.manifest
│   ├── appstate2.json
│   └── backend/
│       ├── ImageMagick/
│       │   ├── Custom Gray Gamma 1.0.icc
│       │   ├── Custom RGB Gamma 1.0.icc
│       │   └── Dot Gain 20%.icc
│       ├── resources/
│       │   └── default_cli_configuration.json
│       └── src/
│           ├── .pre-commit-config.yaml
│           ├── README.md
│           ├── __init__.py
│           ├── accelerator_detection.py
│           ├── api/
│           │   ├── __init__.py
│           │   ├── api.py
│           │   ├── group.py
│           │   ├── input.py
│           │   ├── iter.py
│           │   ├── lazy.py
│           │   ├── node_check.py
│           │   ├── node_context.py
│           │   ├── node_data.py
│           │   ├── output.py
│           │   ├── settings.py
│           │   └── types.py
│           ├── device_list.py
│           ├── gpu.py
│           ├── navi.py
│           ├── nodes/
│           │   ├── __init__.py
│           │   ├── condition.py
│           │   ├── group.py
│           │   ├── groups.py
│           │   ├── impl/
│           │   │   ├── __init__.py
│           │   │   ├── blend.py
│           │   │   ├── color/
│           │   │   │   ├── __init__.py
│           │   │   │   ├── color.py
│           │   │   │   ├── convert.py
│           │   │   │   ├── convert_data.py
│           │   │   │   └── convert_model.py
│           │   │   ├── image_formats.py
│           │   │   ├── image_op.py
│           │   │   ├── image_utils.py
│           │   │   ├── onnx/
│           │   │   │   ├── __init__.py
│           │   │   │   ├── auto_split.py
│           │   │   │   ├── load.py
│           │   │   │   ├── model.py
│           │   │   │   ├── np_tensor_utils.py
│           │   │   │   ├── onnx_to_ncnn.py
│           │   │   │   ├── session.py
│           │   │   │   ├── tensorproto_utils.py
│           │   │   │   ├── update_model_dims.py
│           │   │   │   └── utils.py
│           │   │   ├── pil_utils.py
│           │   │   ├── pytorch/
│           │   │   │   ├── __init__.py
│           │   │   │   ├── auto_split.py
│           │   │   │   ├── convert_to_onnx_impl.py
│           │   │   │   ├── pix_transform/
│           │   │   │   │   ├── LICENSE
│           │   │   │   │   ├── auto_split.py
│           │   │   │   │   ├── pix_transform.py
│           │   │   │   │   └── pix_transform_net.py
│           │   │   │   ├── rife/
│           │   │   │   │   ├── IFNet_HDv3_v4_14_align.py
│           │   │   │   │   └── warplayer.py
│           │   │   │   └── utils.py
│           │   │   ├── resize.py
│           │   │   └── upscale/
│           │   │       ├── __init__.py
│           │   │       ├── auto_split.py
│           │   │       ├── auto_split_tiles.py
│           │   │       ├── basic_upscale.py
│           │   │       ├── convenient_upscale.py
│           │   │       ├── custom_scale.py
│           │   │       ├── exact_split.py
│           │   │       ├── grayscale.py
│           │   │       ├── passthrough.py
│           │   │       ├── tile_blending.py
│           │   │       └── tiler.py
│           │   ├── node_cache.py
│           │   ├── properties/
│           │   │   ├── __init__.py
│           │   │   ├── inputs/
│           │   │   │   ├── __init__.py
│           │   │   │   ├── __system_inputs.py
│           │   │   │   ├── file_inputs.py
│           │   │   │   ├── generic_inputs.py
│           │   │   │   ├── image_dropdown_inputs.py
│           │   │   │   ├── label.py
│           │   │   │   ├── ncnn_inputs.py
│           │   │   │   ├── numeric_inputs.py
│           │   │   │   ├── numpy_inputs.py
│           │   │   │   ├── onnx_inputs.py
│           │   │   │   └── pytorch_inputs.py
│           │   │   └── outputs/
│           │   │       ├── __init__.py
│           │   │       ├── file_outputs.py
│           │   │       ├── generic_outputs.py
│           │   │       ├── ncnn_outputs.py
│           │   │       ├── numpy_outputs.py
│           │   │       ├── onnx_outputs.py
│           │   │       └── pytorch_outputs.py
│           │   └── utils/
│           │       ├── __init__.py
│           │       ├── format.py
│           │       ├── seed.py
│           │       └── utils.py
│           ├── packages/
│           │   └── chaiNNer_pytorch/
│           │       ├── __init__.py
│           │       ├── pytorch/
│           │       │   ├── __init__.py
│           │       │   ├── io/
│           │       │   │   └── load_model.py
│           │       │   └── processing/
│           │       │       └── upscale_image.py
│           │       └── settings.py
│           ├── progress_controller.py
│           ├── pyproject.toml
│           ├── pyrightconfig.json
│           ├── run_upscale.py
│           ├── spandrel_custom/
│           │   ├── __init__.py
│           │   └── architectures/
│           │       └── FDAT/
│           │           ├── __arch/
│           │           │   ├── LICENSE
│           │           │   └── fdat.py
│           │           └── __init__.py
│           ├── system.py
│           └── test_accelerators.py
├── MangaJaNaiConverterGui.sln
├── README.md
└── pack.bat

================================================
FILE CONTENTS
================================================

================================================
FILE: .editorconfig
================================================
# Remove the line below if you want to inherit .editorconfig settings from higher directories
root = true

# C# files
[*.cs]

#### Core EditorConfig Options ####

# Indentation and spacing
indent_size = 4
indent_style = space
tab_width = 4

# New line preferences
end_of_line = crlf
insert_final_newline = false

#### .NET Coding Conventions ####

# Organize usings
dotnet_separate_import_directive_groups = false
dotnet_sort_system_directives_first = false
file_header_template = unset

# this. and Me. preferences
dotnet_style_qualification_for_event = false
dotnet_style_qualification_for_field = false
dotnet_style_qualification_for_method = false
dotnet_style_qualification_for_property = false

# Language keywords vs BCL types preferences
dotnet_style_predefined_type_for_locals_parameters_members = true
dotnet_style_predefined_type_for_member_access = true

# Parentheses preferences
dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_binary_operators = always_for_clarity
dotnet_style_parentheses_in_other_operators = never_if_unnecessary
dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity

# Modifier preferences
dotnet_style_require_accessibility_modifiers = for_non_interface_members

# Expression-level preferences
dotnet_style_coalesce_expression = true
dotnet_style_collection_initializer = true
dotnet_style_explicit_tuple_names = true
dotnet_style_namespace_match_folder = true
dotnet_style_null_propagation = true
dotnet_style_object_initializer = true
dotnet_style_operator_placement_when_wrapping = beginning_of_line
dotnet_style_prefer_auto_properties = true
dotnet_style_prefer_collection_expression = when_types_loosely_match
dotnet_style_prefer_compound_assignment = true
dotnet_style_prefer_conditional_expression_over_assignment = true
dotnet_style_prefer_conditional_expression_over_return = true
dotnet_style_prefer_foreach_explicit_cast_in_source = when_strongly_typed
dotnet_style_prefer_inferred_anonymous_type_member_names = true
dotnet_style_prefer_inferred_tuple_names = true
dotnet_style_prefer_is_null_check_over_reference_equality_method = true
dotnet_style_prefer_simplified_boolean_expressions = true
dotnet_style_prefer_simplified_interpolation = true

# Field preferences
dotnet_style_readonly_field = true

# Parameter preferences
dotnet_code_quality_unused_parameters = all

# Suppression preferences
dotnet_remove_unnecessary_suppression_exclusions = none

# New line preferences
dotnet_style_allow_multiple_blank_lines_experimental = true
dotnet_style_allow_statement_immediately_after_block_experimental = true

#### C# Coding Conventions ####

# var preferences
csharp_style_var_elsewhere = false
csharp_style_var_for_built_in_types = false
csharp_style_var_when_type_is_apparent = false

# Expression-bodied members
csharp_style_expression_bodied_accessors = true
csharp_style_expression_bodied_constructors = false
csharp_style_expression_bodied_indexers = true
csharp_style_expression_bodied_lambdas = true
csharp_style_expression_bodied_local_functions = false
csharp_style_expression_bodied_methods = false
csharp_style_expression_bodied_operators = false
csharp_style_expression_bodied_properties = true

# Pattern matching preferences
csharp_style_pattern_matching_over_as_with_null_check = true
csharp_style_pattern_matching_over_is_with_cast_check = true
csharp_style_prefer_extended_property_pattern = true
csharp_style_prefer_not_pattern = true
csharp_style_prefer_pattern_matching = true
csharp_style_prefer_switch_expression = true

# Null-checking preferences
csharp_style_conditional_delegate_call = true

# Modifier preferences
csharp_prefer_static_local_function = true
csharp_preferred_modifier_order = public,private,protected,internal,file,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,required,volatile,async
csharp_style_prefer_readonly_struct = true
csharp_style_prefer_readonly_struct_member = true

# Code-block preferences
csharp_prefer_braces = true
csharp_prefer_simple_using_statement = true
csharp_style_namespace_declarations = block_scoped
csharp_style_prefer_method_group_conversion = true
csharp_style_prefer_primary_constructors = true
csharp_style_prefer_top_level_statements = true

# Expression-level preferences
csharp_prefer_simple_default_expression = true
csharp_style_deconstructed_variable_declaration = true
csharp_style_implicit_object_creation_when_type_is_apparent = true
csharp_style_inlined_variable_declaration = true
csharp_style_prefer_index_operator = true
csharp_style_prefer_local_over_anonymous_function = true
csharp_style_prefer_null_check_over_type_check = true
csharp_style_prefer_range_operator = true
csharp_style_prefer_tuple_swap = true
csharp_style_prefer_utf8_string_literals = true
csharp_style_throw_expression = true
csharp_style_unused_value_assignment_preference = discard_variable
csharp_style_unused_value_expression_statement_preference = discard_variable

# 'using' directive preferences
csharp_using_directive_placement = outside_namespace

# New line preferences
csharp_style_allow_blank_line_after_colon_in_constructor_initializer_experimental = true
csharp_style_allow_blank_line_after_token_in_arrow_expression_clause_experimental = true
csharp_style_allow_blank_line_after_token_in_conditional_expression_experimental = true
csharp_style_allow_blank_lines_between_consecutive_braces_experimental = true
csharp_style_allow_embedded_statements_on_same_line_experimental = true

#### C# Formatting Rules ####

# New line preferences
csharp_new_line_before_catch = true
csharp_new_line_before_else = true
csharp_new_line_before_finally = true
csharp_new_line_before_members_in_anonymous_types = true
csharp_new_line_before_members_in_object_initializers = true
csharp_new_line_before_open_brace = all
csharp_new_line_between_query_expression_clauses = true

# Indentation preferences
csharp_indent_block_contents = true
csharp_indent_braces = false
csharp_indent_case_contents = true
csharp_indent_case_contents_when_block = true
csharp_indent_labels = one_less_than_current
csharp_indent_switch_labels = true

# Space preferences
csharp_space_after_cast = false
csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true
csharp_space_after_dot = false
csharp_space_after_keywords_in_control_flow_statements = true
csharp_space_after_semicolon_in_for_statement = true
csharp_space_around_binary_operators = before_and_after
csharp_space_around_declaration_statements = false
csharp_space_before_colon_in_inheritance_clause = true
csharp_space_before_comma = false
csharp_space_before_dot = false
csharp_space_before_open_square_brackets = false
csharp_space_before_semicolon_in_for_statement = false
csharp_space_between_empty_square_brackets = false
csharp_space_between_method_call_empty_parameter_list_parentheses = false
csharp_space_between_method_call_name_and_opening_parenthesis = false
csharp_space_between_method_call_parameter_list_parentheses = false
csharp_space_between_method_declaration_empty_parameter_list_parentheses = false
csharp_space_between_method_declaration_name_and_open_parenthesis = false
csharp_space_between_method_declaration_parameter_list_parentheses = false
csharp_space_between_parentheses = false
csharp_space_between_square_brackets = false

# Wrapping preferences
csharp_preserve_single_line_blocks = true
csharp_preserve_single_line_statements = true

#### Naming styles ####

# Naming rules

dotnet_naming_rule.interface_should_be_begins_with_i.severity = suggestion
dotnet_naming_rule.interface_should_be_begins_with_i.symbols = interface
dotnet_naming_rule.interface_should_be_begins_with_i.style = begins_with_i

dotnet_naming_rule.types_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.types_should_be_pascal_case.symbols = types
dotnet_naming_rule.types_should_be_pascal_case.style = pascal_case

dotnet_naming_rule.non_field_members_should_be_pascal_case.severity = suggestion
dotnet_naming_rule.non_field_members_should_be_pascal_case.symbols = non_field_members
dotnet_naming_rule.non_field_members_should_be_pascal_case.style = pascal_case

# Symbol specifications

dotnet_naming_symbols.interface.applicable_kinds = interface
dotnet_naming_symbols.interface.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.interface.required_modifiers = 

dotnet_naming_symbols.types.applicable_kinds = class, struct, interface, enum
dotnet_naming_symbols.types.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.types.required_modifiers = 

dotnet_naming_symbols.non_field_members.applicable_kinds = property, event, method
dotnet_naming_symbols.non_field_members.applicable_accessibilities = public, internal, private, protected, protected_internal, private_protected
dotnet_naming_symbols.non_field_members.required_modifiers = 

# Naming styles

dotnet_naming_style.pascal_case.required_prefix = 
dotnet_naming_style.pascal_case.required_suffix = 
dotnet_naming_style.pascal_case.word_separator = 
dotnet_naming_style.pascal_case.capitalization = pascal_case

dotnet_naming_style.begins_with_i.required_prefix = I
dotnet_naming_style.begins_with_i.required_suffix = 
dotnet_naming_style.begins_with_i.word_separator = 
dotnet_naming_style.begins_with_i.capitalization = pascal_case


================================================
FILE: .github/workflows/deploy.yml
================================================
name: Deploy to GitHub Releases

on:
  workflow_dispatch:
    inputs:
      version:
        description: 'Version number for the release'
        required: true
        default: ''

jobs:
  deploy-to-github-releases:
    runs-on: windows-latest
    steps:
      - name: Checkout Repository
        uses: actions/checkout@v4
      - name: Install .NET
        uses: actions/setup-dotnet@v4
        with:
          dotnet-version: 10.0.x
      - name: Publish Application
        run: dotnet publish MangaJaNaiConverterGui/MangaJaNaiConverterGui.csproj -c Release -o publish -r win-x64
      - name: Create Velopack Release
        run: |
          dotnet tool install -g vpk --prerelease
          vpk download github --repoUrl https://github.com/the-database/MangaJaNaiConverterGui
          vpk pack -u MangaJaNaiConverterGui -v ${{ github.event.inputs.version }} -p publish  -i ./MangaJaNaiConverterGui/assets/logo.ico -e MangaJaNaiConverterGui.exe
          vpk upload github --repoUrl https://github.com/the-database/MangaJaNaiConverterGui --releaseName "${{ github.event.inputs.version }}" --tag ${{ github.event.inputs.version }} --token ${{ secrets.GITHUB_TOKEN }}


================================================
FILE: .gitignore
================================================
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.
##
## Get latest from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore

# User-specific files
*.rsuser
*.suo
*.user
*.userosscache
*.sln.docstates

# User-specific files (MonoDevelop/Xamarin Studio)
*.userprefs

# Mono auto generated files
mono_crash.*

# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
x64/
x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Ll]ogs/

# Visual Studio 2015/2017 cache/options directory
.vs/
# Uncomment if you have tasks that create the project's static files in wwwroot
#wwwroot/

# Visual Studio 2017 auto generated files
Generated\ Files/

# MSTest test Results
[Tt]est[Rr]esult*/
[Bb]uild[Ll]og.*

# NUnit
*.VisualState.xml
TestResult.xml
nunit-*.xml

# Build Results of an ATL Project
[Dd]ebugPS/
[Rr]eleasePS/
dlldata.c

# Benchmark Results
BenchmarkDotNet.Artifacts/

# .NET Core
project.lock.json
project.fragment.lock.json
artifacts/

# ASP.NET Scaffolding
ScaffoldingReadMe.txt

# StyleCop
StyleCopReport.xml

# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc

# Chutzpah Test files
_Chutzpah*

# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

# Visual Studio profiler
*.psess
*.vsp
*.vspx
*.sap

# Visual Studio Trace Files
*.e2e

# TFS 2012 Local Workspace
$tf/

# Guidance Automation Toolkit
*.gpState

# ReSharper is a .NET coding add-in
_ReSharper*/
*.[Rr]e[Ss]harper
*.DotSettings.user

# TeamCity is a build add-in
_TeamCity*

# DotCover is a Code Coverage Tool
*.dotCover

# AxoCover is a Code Coverage Tool
.axoCover/*
!.axoCover/settings.json

# Coverlet is a free, cross platform Code Coverage Tool
coverage*.json
coverage*.xml
coverage*.info

# Visual Studio code coverage results
*.coverage
*.coveragexml

# NCrunch
_NCrunch_*
.*crunch*.local.xml
nCrunchTemp_*

# MightyMoose
*.mm.*
AutoTest.Net/

# Web workbench (sass)
.sass-cache/

# Installshield output folder
[Ee]xpress/

# DocProject is a documentation generator add-in
DocProject/buildhelp/
DocProject/Help/*.HxT
DocProject/Help/*.HxC
DocProject/Help/*.hhc
DocProject/Help/*.hhk
DocProject/Help/*.hhp
DocProject/Help/Html2
DocProject/Help/html

# Click-Once directory
publish/

# Publish Web Output
*.[Pp]ublish.xml
*.azurePubxml
# Note: Comment the next line if you want to checkin your web deploy settings,
# but database connection strings (with potential passwords) will be unencrypted
*.pubxml
*.publishproj

# Microsoft Azure Web App publish settings. Comment the next line if you want to
# checkin your Azure Web App publish settings, but sensitive information contained
# in these scripts will be unencrypted
PublishScripts/

# NuGet Packages
*.nupkg
# NuGet Symbol Packages
*.snupkg
# The packages folder can be ignored because of Package Restore

# except build/, which is used as an MSBuild target.
!**/[Pp]ackages/build/
# Uncomment if necessary however generally it will be regenerated when needed
#!**/[Pp]ackages/repositories.config
# NuGet v3's project.json files produces more ignorable files
*.nuget.props
*.nuget.targets

# Microsoft Azure Build Output
csx/
*.build.csdef

# Microsoft Azure Emulator
ecf/
rcf/

# Windows Store app package directories and files
AppPackages/
BundleArtifacts/
Package.StoreAssociation.xml
_pkginfo.txt
*.appx
*.appxbundle
*.appxupload

# Visual Studio cache files
# files ending in .cache can be ignored
*.[Cc]ache
# but keep track of directories ending in .cache
!?*.[Cc]ache/

# Others
ClientBin/
~$*
*~
*.dbmdl
*.dbproj.schemaview
*.jfm
*.pfx
*.publishsettings
orleans.codegen.cs

# Including strong name files can present a security risk
# (https://github.com/github/gitignore/pull/2483#issue-259490424)
#*.snk

# Since there are multiple workflows, uncomment next line to ignore bower_components
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
#bower_components/

# RIA/Silverlight projects
Generated_Code/

# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
_UpgradeReport_Files/
Backup*/
UpgradeLog*.XML
UpgradeLog*.htm
ServiceFabricBackup/
*.rptproj.bak

# SQL Server files
*.mdf
*.ldf
*.ndf

# Business Intelligence projects
*.rdl.data
*.bim.layout
*.bim_*.settings
*.rptproj.rsuser
*- [Bb]ackup.rdl
*- [Bb]ackup ([0-9]).rdl
*- [Bb]ackup ([0-9][0-9]).rdl

# Microsoft Fakes
FakesAssemblies/

# GhostDoc plugin setting file
*.GhostDoc.xml

# Node.js Tools for Visual Studio
.ntvs_analysis.dat
node_modules/

# Visual Studio 6 build log
*.plg

# Visual Studio 6 workspace options file
*.opt

# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
*.vbw

# Visual Studio 6 auto-generated project file (contains which files were open etc.)
*.vbp

# Visual Studio 6 workspace and project file (working project files containing files to include in project)
*.dsw
*.dsp

# Visual Studio 6 technical files
*.ncb
*.aps

# Visual Studio LightSwitch build output
**/*.HTMLClient/GeneratedArtifacts
**/*.DesktopClient/GeneratedArtifacts
**/*.DesktopClient/ModelManifest.xml
**/*.Server/GeneratedArtifacts
**/*.Server/ModelManifest.xml
_Pvt_Extensions

# Paket dependency manager
.paket/paket.exe
paket-files/

# FAKE - F# Make
.fake/

# CodeRush personal settings
.cr/personal

# Python Tools for Visual Studio (PTVS)
__pycache__/
*.pyc

# Cake - Uncomment if you are using it
# tools/**
# !tools/packages.config

# Tabs Studio
*.tss

# Telerik's JustMock configuration file
*.jmconfig

# BizTalk build output
*.btp.cs
*.btm.cs
*.odx.cs
*.xsd.cs

# OpenCover UI analysis results
OpenCover/

# Azure Stream Analytics local run output
ASALocalRun/

# MSBuild Binary and Structured Log
*.binlog

# NVidia Nsight GPU debugger configuration file
*.nvuser

# MFractors (Xamarin productivity tool) working folder
.mfractor/

# Local History for Visual Studio
.localhistory/

# Visual Studio History (VSHistory) files
.vshistory/

# BeatPulse healthcheck temp database
healthchecksdb

# Backup folder for Package Reference Convert tool in Visual Studio 2017
MigrationBackup/

# Ionide (cross platform F# VS Code tools) working folder
.ionide/

# Fody - auto-generated XML schema
FodyWeavers.xsd

# VS Code files for those working on multiple tools
.vscode/*
!.vscode/settings.json
!.vscode/tasks.json
!.vscode/launch.json
!.vscode/extensions.json
*.code-workspace

# Local History for Visual Studio Code
.history/

# Windows Installer files from build outputs
*.cab
*.msi
*.msix
*.msm
*.msp

# JetBrains Rider
*.sln.iml

MangaJaNaiConverterGui/chaiNNer/python/


================================================
FILE: LICENSE
================================================
                    GNU GENERAL PUBLIC LICENSE
                       Version 3, 29 June 2007

 Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
 Everyone is permitted to copy and distribute verbatim copies
 of this license document, but changing it is not allowed.

                            Preamble

  The GNU General Public License is a free, copyleft license for
software and other kinds of works.

  The licenses for most software and other practical works are designed
to take away your freedom to share and change the works.  By contrast,
the GNU General Public License is intended to guarantee your freedom to
share and change all versions of a program--to make sure it remains free
software for all its users.  We, the Free Software Foundation, use the
GNU General Public License for most of our software; it applies also to
any other work released this way by its authors.  You can apply it to
your programs, too.

  When we speak of free software, we are referring to freedom, not
price.  Our General Public Licenses are designed to make sure that you
have the freedom to distribute copies of free software (and charge for
them if you wish), that you receive source code or can get it if you
want it, that you can change the software or use pieces of it in new
free programs, and that you know you can do these things.

  To protect your rights, we need to prevent others from denying you
these rights or asking you to surrender the rights.  Therefore, you have
certain responsibilities if you distribute copies of the software, or if
you modify it: responsibilities to respect the freedom of others.

  For example, if you distribute copies of such a program, whether
gratis or for a fee, you must pass on to the recipients the same
freedoms that you received.  You must make sure that they, too, receive
or can get the source code.  And you must show them these terms so they
know their rights.

  Developers that use the GNU GPL protect your rights with two steps:
(1) assert copyright on the software, and (2) offer you this License
giving you legal permission to copy, distribute and/or modify it.

  For the developers' and authors' protection, the GPL clearly explains
that there is no warranty for this free software.  For both users' and
authors' sake, the GPL requires that modified versions be marked as
changed, so that their problems will not be attributed erroneously to
authors of previous versions.

  Some devices are designed to deny users access to install or run
modified versions of the software inside them, although the manufacturer
can do so.  This is fundamentally incompatible with the aim of
protecting users' freedom to change the software.  The systematic
pattern of such abuse occurs in the area of products for individuals to
use, which is precisely where it is most unacceptable.  Therefore, we
have designed this version of the GPL to prohibit the practice for those
products.  If such problems arise substantially in other domains, we
stand ready to extend this provision to those domains in future versions
of the GPL, as needed to protect the freedom of users.

  Finally, every program is threatened constantly by software patents.
States should not allow patents to restrict development and use of
software on general-purpose computers, but in those that do, we wish to
avoid the special danger that patents applied to a free program could
make it effectively proprietary.  To prevent this, the GPL assures that
patents cannot be used to render the program non-free.

  The precise terms and conditions for copying, distribution and
modification follow.

                       TERMS AND CONDITIONS

  0. Definitions.

  "This License" refers to version 3 of the GNU General Public License.

  "Copyright" also means copyright-like laws that apply to other kinds of
works, such as semiconductor masks.

  "The Program" refers to any copyrightable work licensed under this
License.  Each licensee is addressed as "you".  "Licensees" and
"recipients" may be individuals or organizations.

  To "modify" a work means to copy from or adapt all or part of the work
in a fashion requiring copyright permission, other than the making of an
exact copy.  The resulting work is called a "modified version" of the
earlier work or a work "based on" the earlier work.

  A "covered work" means either the unmodified Program or a work based
on the Program.

  To "propagate" a work means to do anything with it that, without
permission, would make you directly or secondarily liable for
infringement under applicable copyright law, except executing it on a
computer or modifying a private copy.  Propagation includes copying,
distribution (with or without modification), making available to the
public, and in some countries other activities as well.

  To "convey" a work means any kind of propagation that enables other
parties to make or receive copies.  Mere interaction with a user through
a computer network, with no transfer of a copy, is not conveying.

  An interactive user interface displays "Appropriate Legal Notices"
to the extent that it includes a convenient and prominently visible
feature that (1) displays an appropriate copyright notice, and (2)
tells the user that there is no warranty for the work (except to the
extent that warranties are provided), that licensees may convey the
work under this License, and how to view a copy of this License.  If
the interface presents a list of user commands or options, such as a
menu, a prominent item in the list meets this criterion.

  1. Source Code.

  The "source code" for a work means the preferred form of the work
for making modifications to it.  "Object code" means any non-source
form of a work.

  A "Standard Interface" means an interface that either is an official
standard defined by a recognized standards body, or, in the case of
interfaces specified for a particular programming language, one that
is widely used among developers working in that language.

  The "System Libraries" of an executable work include anything, other
than the work as a whole, that (a) is included in the normal form of
packaging a Major Component, but which is not part of that Major
Component, and (b) serves only to enable use of the work with that
Major Component, or to implement a Standard Interface for which an
implementation is available to the public in source code form.  A
"Major Component", in this context, means a major essential component
(kernel, window system, and so on) of the specific operating system
(if any) on which the executable work runs, or a compiler used to
produce the work, or an object code interpreter used to run it.

  The "Corresponding Source" for a work in object code form means all
the source code needed to generate, install, and (for an executable
work) run the object code and to modify the work, including scripts to
control those activities.  However, it does not include the work's
System Libraries, or general-purpose tools or generally available free
programs which are used unmodified in performing those activities but
which are not part of the work.  For example, Corresponding Source
includes interface definition files associated with source files for
the work, and the source code for shared libraries and dynamically
linked subprograms that the work is specifically designed to require,
such as by intimate data communication or control flow between those
subprograms and other parts of the work.

  The Corresponding Source need not include anything that users
can regenerate automatically from other parts of the Corresponding
Source.

  The Corresponding Source for a work in source code form is that
same work.

  2. Basic Permissions.

  All rights granted under this License are granted for the term of
copyright on the Program, and are irrevocable provided the stated
conditions are met.  This License explicitly affirms your unlimited
permission to run the unmodified Program.  The output from running a
covered work is covered by this License only if the output, given its
content, constitutes a covered work.  This License acknowledges your
rights of fair use or other equivalent, as provided by copyright law.

  You may make, run and propagate covered works that you do not
convey, without conditions so long as your license otherwise remains
in force.  You may convey covered works to others for the sole purpose
of having them make modifications exclusively for you, or provide you
with facilities for running those works, provided that you comply with
the terms of this License in conveying all material for which you do
not control copyright.  Those thus making or running the covered works
for you must do so exclusively on your behalf, under your direction
and control, on terms that prohibit them from making any copies of
your copyrighted material outside their relationship with you.

  Conveying under any other circumstances is permitted solely under
the conditions stated below.  Sublicensing is not allowed; section 10
makes it unnecessary.

  3. Protecting Users' Legal Rights From Anti-Circumvention Law.

  No covered work shall be deemed part of an effective technological
measure under any applicable law fulfilling obligations under article
11 of the WIPO copyright treaty adopted on 20 December 1996, or
similar laws prohibiting or restricting circumvention of such
measures.

  When you convey a covered work, you waive any legal power to forbid
circumvention of technological measures to the extent such circumvention
is effected by exercising rights under this License with respect to
the covered work, and you disclaim any intention to limit operation or
modification of the work as a means of enforcing, against the work's
users, your or third parties' legal rights to forbid circumvention of
technological measures.

  4. Conveying Verbatim Copies.

  You may convey verbatim copies of the Program's source code as you
receive it, in any medium, provided that you conspicuously and
appropriately publish on each copy an appropriate copyright notice;
keep intact all notices stating that this License and any
non-permissive terms added in accord with section 7 apply to the code;
keep intact all notices of the absence of any warranty; and give all
recipients a copy of this License along with the Program.

  You may charge any price or no price for each copy that you convey,
and you may offer support or warranty protection for a fee.

  5. Conveying Modified Source Versions.

  You may convey a work based on the Program, or the modifications to
produce it from the Program, in the form of source code under the
terms of section 4, provided that you also meet all of these conditions:

    a) The work must carry prominent notices stating that you modified
    it, and giving a relevant date.

    b) The work must carry prominent notices stating that it is
    released under this License and any conditions added under section
    7.  This requirement modifies the requirement in section 4 to
    "keep intact all notices".

    c) You must license the entire work, as a whole, under this
    License to anyone who comes into possession of a copy.  This
    License will therefore apply, along with any applicable section 7
    additional terms, to the whole of the work, and all its parts,
    regardless of how they are packaged.  This License gives no
    permission to license the work in any other way, but it does not
    invalidate such permission if you have separately received it.

    d) If the work has interactive user interfaces, each must display
    Appropriate Legal Notices; however, if the Program has interactive
    interfaces that do not display Appropriate Legal Notices, your
    work need not make them do so.

  A compilation of a covered work with other separate and independent
works, which are not by their nature extensions of the covered work,
and which are not combined with it such as to form a larger program,
in or on a volume of a storage or distribution medium, is called an
"aggregate" if the compilation and its resulting copyright are not
used to limit the access or legal rights of the compilation's users
beyond what the individual works permit.  Inclusion of a covered work
in an aggregate does not cause this License to apply to the other
parts of the aggregate.

  6. Conveying Non-Source Forms.

  You may convey a covered work in object code form under the terms
of sections 4 and 5, provided that you also convey the
machine-readable Corresponding Source under the terms of this License,
in one of these ways:

    a) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by the
    Corresponding Source fixed on a durable physical medium
    customarily used for software interchange.

    b) Convey the object code in, or embodied in, a physical product
    (including a physical distribution medium), accompanied by a
    written offer, valid for at least three years and valid for as
    long as you offer spare parts or customer support for that product
    model, to give anyone who possesses the object code either (1) a
    copy of the Corresponding Source for all the software in the
    product that is covered by this License, on a durable physical
    medium customarily used for software interchange, for a price no
    more than your reasonable cost of physically performing this
    conveying of source, or (2) access to copy the
    Corresponding Source from a network server at no charge.

    c) Convey individual copies of the object code with a copy of the
    written offer to provide the Corresponding Source.  This
    alternative is allowed only occasionally and noncommercially, and
    only if you received the object code with such an offer, in accord
    with subsection 6b.

    d) Convey the object code by offering access from a designated
    place (gratis or for a charge), and offer equivalent access to the
    Corresponding Source in the same way through the same place at no
    further charge.  You need not require recipients to copy the
    Corresponding Source along with the object code.  If the place to
    copy the object code is a network server, the Corresponding Source
    may be on a different server (operated by you or a third party)
    that supports equivalent copying facilities, provided you maintain
    clear directions next to the object code saying where to find the
    Corresponding Source.  Regardless of what server hosts the
    Corresponding Source, you remain obligated to ensure that it is
    available for as long as needed to satisfy these requirements.

    e) Convey the object code using peer-to-peer transmission, provided
    you inform other peers where the object code and Corresponding
    Source of the work are being offered to the general public at no
    charge under subsection 6d.

  A separable portion of the object code, whose source code is excluded
from the Corresponding Source as a System Library, need not be
included in conveying the object code work.

  A "User Product" is either (1) a "consumer product", which means any
tangible personal property which is normally used for personal, family,
or household purposes, or (2) anything designed or sold for incorporation
into a dwelling.  In determining whether a product is a consumer product,
doubtful cases shall be resolved in favor of coverage.  For a particular
product received by a particular user, "normally used" refers to a
typical or common use of that class of product, regardless of the status
of the particular user or of the way in which the particular user
actually uses, or expects or is expected to use, the product.  A product
is a consumer product regardless of whether the product has substantial
commercial, industrial or non-consumer uses, unless such uses represent
the only significant mode of use of the product.

  "Installation Information" for a User Product means any methods,
procedures, authorization keys, or other information required to install
and execute modified versions of a covered work in that User Product from
a modified version of its Corresponding Source.  The information must
suffice to ensure that the continued functioning of the modified object
code is in no case prevented or interfered with solely because
modification has been made.

  If you convey an object code work under this section in, or with, or
specifically for use in, a User Product, and the conveying occurs as
part of a transaction in which the right of possession and use of the
User Product is transferred to the recipient in perpetuity or for a
fixed term (regardless of how the transaction is characterized), the
Corresponding Source conveyed under this section must be accompanied
by the Installation Information.  But this requirement does not apply
if neither you nor any third party retains the ability to install
modified object code on the User Product (for example, the work has
been installed in ROM).

  The requirement to provide Installation Information does not include a
requirement to continue to provide support service, warranty, or updates
for a work that has been modified or installed by the recipient, or for
the User Product in which it has been modified or installed.  Access to a
network may be denied when the modification itself materially and
adversely affects the operation of the network or violates the rules and
protocols for communication across the network.

  Corresponding Source conveyed, and Installation Information provided,
in accord with this section must be in a format that is publicly
documented (and with an implementation available to the public in
source code form), and must require no special password or key for
unpacking, reading or copying.

  7. Additional Terms.

  "Additional permissions" are terms that supplement the terms of this
License by making exceptions from one or more of its conditions.
Additional permissions that are applicable to the entire Program shall
be treated as though they were included in this License, to the extent
that they are valid under applicable law.  If additional permissions
apply only to part of the Program, that part may be used separately
under those permissions, but the entire Program remains governed by
this License without regard to the additional permissions.

  When you convey a copy of a covered work, you may at your option
remove any additional permissions from that copy, or from any part of
it.  (Additional permissions may be written to require their own
removal in certain cases when you modify the work.)  You may place
additional permissions on material, added by you to a covered work,
for which you have or can give appropriate copyright permission.

  Notwithstanding any other provision of this License, for material you
add to a covered work, you may (if authorized by the copyright holders of
that material) supplement the terms of this License with terms:

    a) Disclaiming warranty or limiting liability differently from the
    terms of sections 15 and 16 of this License; or

    b) Requiring preservation of specified reasonable legal notices or
    author attributions in that material or in the Appropriate Legal
    Notices displayed by works containing it; or

    c) Prohibiting misrepresentation of the origin of that material, or
    requiring that modified versions of such material be marked in
    reasonable ways as different from the original version; or

    d) Limiting the use for publicity purposes of names of licensors or
    authors of the material; or

    e) Declining to grant rights under trademark law for use of some
    trade names, trademarks, or service marks; or

    f) Requiring indemnification of licensors and authors of that
    material by anyone who conveys the material (or modified versions of
    it) with contractual assumptions of liability to the recipient, for
    any liability that these contractual assumptions directly impose on
    those licensors and authors.

  All other non-permissive additional terms are considered "further
restrictions" within the meaning of section 10.  If the Program as you
received it, or any part of it, contains a notice stating that it is
governed by this License along with a term that is a further
restriction, you may remove that term.  If a license document contains
a further restriction but permits relicensing or conveying under this
License, you may add to a covered work material governed by the terms
of that license document, provided that the further restriction does
not survive such relicensing or conveying.

  If you add terms to a covered work in accord with this section, you
must place, in the relevant source files, a statement of the
additional terms that apply to those files, or a notice indicating
where to find the applicable terms.

  Additional terms, permissive or non-permissive, may be stated in the
form of a separately written license, or stated as exceptions;
the above requirements apply either way.

  8. Termination.

  You may not propagate or modify a covered work except as expressly
provided under this License.  Any attempt otherwise to propagate or
modify it is void, and will automatically terminate your rights under
this License (including any patent licenses granted under the third
paragraph of section 11).

  However, if you cease all violation of this License, then your
license from a particular copyright holder is reinstated (a)
provisionally, unless and until the copyright holder explicitly and
finally terminates your license, and (b) permanently, if the copyright
holder fails to notify you of the violation by some reasonable means
prior to 60 days after the cessation.

  Moreover, your license from a particular copyright holder is
reinstated permanently if the copyright holder notifies you of the
violation by some reasonable means, this is the first time you have
received notice of violation of this License (for any work) from that
copyright holder, and you cure the violation prior to 30 days after
your receipt of the notice.

  Termination of your rights under this section does not terminate the
licenses of parties who have received copies or rights from you under
this License.  If your rights have been terminated and not permanently
reinstated, you do not qualify to receive new licenses for the same
material under section 10.

  9. Acceptance Not Required for Having Copies.

  You are not required to accept this License in order to receive or
run a copy of the Program.  Ancillary propagation of a covered work
occurring solely as a consequence of using peer-to-peer transmission
to receive a copy likewise does not require acceptance.  However,
nothing other than this License grants you permission to propagate or
modify any covered work.  These actions infringe copyright if you do
not accept this License.  Therefore, by modifying or propagating a
covered work, you indicate your acceptance of this License to do so.

  10. Automatic Licensing of Downstream Recipients.

  Each time you convey a covered work, the recipient automatically
receives a license from the original licensors, to run, modify and
propagate that work, subject to this License.  You are not responsible
for enforcing compliance by third parties with this License.

  An "entity transaction" is a transaction transferring control of an
organization, or substantially all assets of one, or subdividing an
organization, or merging organizations.  If propagation of a covered
work results from an entity transaction, each party to that
transaction who receives a copy of the work also receives whatever
licenses to the work the party's predecessor in interest had or could
give under the previous paragraph, plus a right to possession of the
Corresponding Source of the work from the predecessor in interest, if
the predecessor has it or can get it with reasonable efforts.

  You may not impose any further restrictions on the exercise of the
rights granted or affirmed under this License.  For example, you may
not impose a license fee, royalty, or other charge for exercise of
rights granted under this License, and you may not initiate litigation
(including a cross-claim or counterclaim in a lawsuit) alleging that
any patent claim is infringed by making, using, selling, offering for
sale, or importing the Program or any portion of it.

  11. Patents.

  A "contributor" is a copyright holder who authorizes use under this
License of the Program or a work on which the Program is based.  The
work thus licensed is called the contributor's "contributor version".

  A contributor's "essential patent claims" are all patent claims
owned or controlled by the contributor, whether already acquired or
hereafter acquired, that would be infringed by some manner, permitted
by this License, of making, using, or selling its contributor version,
but do not include claims that would be infringed only as a
consequence of further modification of the contributor version.  For
purposes of this definition, "control" includes the right to grant
patent sublicenses in a manner consistent with the requirements of
this License.

  Each contributor grants you a non-exclusive, worldwide, royalty-free
patent license under the contributor's essential patent claims, to
make, use, sell, offer for sale, import and otherwise run, modify and
propagate the contents of its contributor version.

  In the following three paragraphs, a "patent license" is any express
agreement or commitment, however denominated, not to enforce a patent
(such as an express permission to practice a patent or covenant not to
sue for patent infringement).  To "grant" such a patent license to a
party means to make such an agreement or commitment not to enforce a
patent against the party.

  If you convey a covered work, knowingly relying on a patent license,
and the Corresponding Source of the work is not available for anyone
to copy, free of charge and under the terms of this License, through a
publicly available network server or other readily accessible means,
then you must either (1) cause the Corresponding Source to be so
available, or (2) arrange to deprive yourself of the benefit of the
patent license for this particular work, or (3) arrange, in a manner
consistent with the requirements of this License, to extend the patent
license to downstream recipients.  "Knowingly relying" means you have
actual knowledge that, but for the patent license, your conveying the
covered work in a country, or your recipient's use of the covered work
in a country, would infringe one or more identifiable patents in that
country that you have reason to believe are valid.

  If, pursuant to or in connection with a single transaction or
arrangement, you convey, or propagate by procuring conveyance of, a
covered work, and grant a patent license to some of the parties
receiving the covered work authorizing them to use, propagate, modify
or convey a specific copy of the covered work, then the patent license
you grant is automatically extended to all recipients of the covered
work and works based on it.

  A patent license is "discriminatory" if it does not include within
the scope of its coverage, prohibits the exercise of, or is
conditioned on the non-exercise of one or more of the rights that are
specifically granted under this License.  You may not convey a covered
work if you are a party to an arrangement with a third party that is
in the business of distributing software, under which you make payment
to the third party based on the extent of your activity of conveying
the work, and under which the third party grants, to any of the
parties who would receive the covered work from you, a discriminatory
patent license (a) in connection with copies of the covered work
conveyed by you (or copies made from those copies), or (b) primarily
for and in connection with specific products or compilations that
contain the covered work, unless you entered into that arrangement,
or that patent license was granted, prior to 28 March 2007.

  Nothing in this License shall be construed as excluding or limiting
any implied license or other defenses to infringement that may
otherwise be available to you under applicable patent law.

  12. No Surrender of Others' Freedom.

  If conditions are imposed on you (whether by court order, agreement or
otherwise) that contradict the conditions of this License, they do not
excuse you from the conditions of this License.  If you cannot convey a
covered work so as to satisfy simultaneously your obligations under this
License and any other pertinent obligations, then as a consequence you may
not convey it at all.  For example, if you agree to terms that obligate you
to collect a royalty for further conveying from those to whom you convey
the Program, the only way you could satisfy both those terms and this
License would be to refrain entirely from conveying the Program.

  13. Use with the GNU Affero General Public License.

  Notwithstanding any other provision of this License, you have
permission to link or combine any covered work with a work licensed
under version 3 of the GNU Affero General Public License into a single
combined work, and to convey the resulting work.  The terms of this
License will continue to apply to the part which is the covered work,
but the special requirements of the GNU Affero General Public License,
section 13, concerning interaction through a network will apply to the
combination as such.

  14. Revised Versions of this License.

  The Free Software Foundation may publish revised and/or new versions of
the GNU General Public License from time to time.  Such new versions will
be similar in spirit to the present version, but may differ in detail to
address new problems or concerns.

  Each version is given a distinguishing version number.  If the
Program specifies that a certain numbered version of the GNU General
Public License "or any later version" applies to it, you have the
option of following the terms and conditions either of that numbered
version or of any later version published by the Free Software
Foundation.  If the Program does not specify a version number of the
GNU General Public License, you may choose any version ever published
by the Free Software Foundation.

  If the Program specifies that a proxy can decide which future
versions of the GNU General Public License can be used, that proxy's
public statement of acceptance of a version permanently authorizes you
to choose that version for the Program.

  Later license versions may give you additional or different
permissions.  However, no additional obligations are imposed on any
author or copyright holder as a result of your choosing to follow a
later version.

  15. Disclaimer of Warranty.

  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
ALL NECESSARY SERVICING, REPAIR OR CORRECTION.

  16. Limitation of Liability.

  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
SUCH DAMAGES.

  17. Interpretation of Sections 15 and 16.

  If the disclaimer of warranty and limitation of liability provided
above cannot be given local legal effect according to their terms,
reviewing courts shall apply local law that most closely approximates
an absolute waiver of all civil liability in connection with the
Program, unless a warranty or assumption of liability accompanies a
copy of the Program in return for a fee.

                     END OF TERMS AND CONDITIONS

            How to Apply These Terms to Your New Programs

  If you develop a new program, and you want it to be of the greatest
possible use to the public, the best way to achieve this is to make it
free software which everyone can redistribute and change under these terms.

  To do so, attach the following notices to the program.  It is safest
to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.

    <one line to give the program's name and a brief idea of what it does.>
    Copyright (C) <year>  <name of author>

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <https://www.gnu.org/licenses/>.

Also add information on how to contact you by electronic and paper mail.

  If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:

    <program>  Copyright (C) <year>  <name of author>
    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
    This is free software, and you are welcome to redistribute it
    under certain conditions; type `show c' for details.

The hypothetical commands `show w' and `show c' should show the appropriate
parts of the General Public License.  Of course, your program's commands
might be different; for a GUI interface, you would use an "about box".

  You should also get your employer (if you work as a programmer) or school,
if any, to sign a "copyright disclaimer" for the program, if necessary.
For more information on this, and how to apply and follow the GNU GPL, see
<https://www.gnu.org/licenses/>.

  The GNU General Public License does not permit incorporating your program
into proprietary programs.  If your program is a subroutine library, you
may consider it more useful to permit linking proprietary applications with
the library.  If this is what you want to do, use the GNU Lesser General
Public License instead of this License.  But first, please read
<https://www.gnu.org/licenses/why-not-lgpl.html>.


================================================
FILE: MangaJaNaiConverterGui/App.axaml
================================================
<Application xmlns="https://github.com/avaloniaui"
             xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
             x:Class="MangaJaNaiConverterGui.App"
             xmlns:local="using:MangaJaNaiConverterGui"
             RequestedThemeVariant="Default"
             xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
             xmlns:sty="using:FluentAvalonia.Styling">
             <!-- "Default" ThemeVariant follows system theme variant. "Dark" or "Light" are other available options. -->

    <Application.DataTemplates>
        <local:ViewLocator/>
    </Application.DataTemplates>
  
    <Application.Styles>
      <materialIcons:MaterialIconStyles />
      <sty:FluentAvaloniaTheme />
    </Application.Styles>
</Application>

================================================
FILE: MangaJaNaiConverterGui/App.axaml.cs
================================================
using Autofac;
using Avalonia;
using Avalonia.Markup.Xaml;
using MangaJaNaiConverterGui.Services;
using MangaJaNaiConverterGui.ViewModels;
using MangaJaNaiConverterGui.Views;
using ReactiveUI;
using Splat;
using Splat.Autofac;
using System.IO;
using ReactiveUI;
using ReactiveUI.Avalonia;
using Splat;
using Splat.Autofac;

namespace MangaJaNaiConverterGui
{
    public partial class App : Application
    {
        public override void Initialize()
        {
            AvaloniaXamlLoader.Load(this);
        }

        public override void OnFrameworkInitializationCompleted()
        {
            // Create a new Autofac container builder.
            var builder = new ContainerBuilder();
            builder.RegisterType<MainWindowViewModel>().AsSelf();
            builder.RegisterType<PythonService>().As<IPythonService>().SingleInstance();
            builder.RegisterType<UpdateManagerService>().As<IUpdateManagerService>().SingleInstance();
            builder.RegisterType<SuspensionDriverService>().As<ISuspensionDriverService>().SingleInstance();
            // etc.
            // Register the Adapter to Splat.
            // Creates and sets the Autofac resolver as the Locator.
            var autofacResolver = builder.UseAutofacDependencyResolver();

            // Register the resolver in Autofac so it can be later resolved.
            builder.RegisterInstance(autofacResolver);

            // Initialize ReactiveUI components.
            autofacResolver.InitializeReactiveUI();

            var container = builder.Build();

            autofacResolver.SetLifetimeScope(container);

            //var vm = container.Resolve<MainWindowViewModel>();
            var umService = container.Resolve<IUpdateManagerService>();

            if (umService.IsInstalled)
            {
                if (!Directory.Exists(Program.InstalledAppStateFolder))
                {
                    Directory.CreateDirectory(Program.InstalledAppStateFolder);
                }

                if (!File.Exists(Program.InstalledAppStatePath))
                {
                    File.Copy(Program.InstalledAppStateFilename, Program.InstalledAppStatePath);
                }
            }

            var suspension = new AutoSuspendHelper(ApplicationLifetime);
            RxApp.SuspensionHost.CreateNewAppState = () => new MainWindowViewModel();
            RxApp.SuspensionHost.SetupDefaultSuspendResume(container.Resolve<ISuspensionDriverService>().SuspensionDriver);
            suspension.OnFrameworkInitializationCompleted();

            // Load the saved view model state.
            var state = RxApp.SuspensionHost.GetAppState<MainWindowViewModel>();

            foreach (var wf in state.Workflows)
            {
                wf.Vm = state;

                foreach (var chain in wf.Chains)
                {
                    chain.Vm = state;
                }
            }

            state.CurrentWorkflow?.Validate();

            new MainWindow { DataContext = state }.Show();
            base.OnFrameworkInitializationCompleted();
        }
    }
}

================================================
FILE: MangaJaNaiConverterGui/Drivers/NewtonsoftJsonSuspensionDriver.cs
================================================
using Newtonsoft.Json;
using ReactiveUI;
using System;
using System.IO;
using System.Reactive;
using System.Reactive.Linq;

namespace MangaJaNaiConverterGui.Drivers
{
    public class NewtonsoftJsonSuspensionDriver : ISuspensionDriver
    {
        private readonly string _file;
        public static readonly JsonSerializerSettings Settings = new()
        {
            TypeNameHandling = TypeNameHandling.All,
            Formatting = Formatting.Indented,
        };

        public NewtonsoftJsonSuspensionDriver(string file) => _file = file;

        public IObservable<Unit> InvalidateState()
        {
            if (File.Exists(_file))
                File.Delete(_file);
            return Observable.Return(Unit.Default);
        }

        public IObservable<object> LoadState()
        {
            var lines = File.ReadAllText(_file);
            var state = JsonConvert.DeserializeObject<object>(lines, Settings)!;
            return Observable.Return(state);
        }

        public IObservable<Unit> SaveState(object state)
        {
            var lines = JsonConvert.SerializeObject(state, Settings);
            File.WriteAllText(_file, lines);
            return Observable.Return(Unit.Default);
        }
    }
}

================================================
FILE: MangaJaNaiConverterGui/MangaJaNaiConverterGui.csproj
================================================
<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <!--<PublishTrimmed>true</PublishTrimmed>
    <PublishAot>true</PublishAot>
    <IsTrimmable>true</IsTrimmable>
    <EnableTrimAnalyzer>true</EnableTrimAnalyzer>
    <EnableAotAnalyzer>true</EnableAotAnalyzer>
    <EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
    <TrimmerSingleWarn>false</TrimmerSingleWarn>-->
    <OutputType>WinExe</OutputType>
    <TargetFramework>net10.0</TargetFramework>
    <Nullable>enable</Nullable>
    <BuiltInComInteropSupport>true</BuiltInComInteropSupport>
    <ApplicationManifest>app.manifest</ApplicationManifest>
    <AvaloniaUseCompiledBindingsByDefault>true</AvaloniaUseCompiledBindingsByDefault>
    <Version>1.0.0</Version>
    <PackageIcon>logo.png</PackageIcon>
    <ApplicationIcon>Assets\logo.ico</ApplicationIcon>
    <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
  </PropertyGroup>

  <ItemGroup>
    <Folder Include="Models\" />
    <AvaloniaResource Include="Assets\**" />
  </ItemGroup>

  <ItemGroup>
    <None Remove="appstate - Copy.json" />
    <None Remove="Assets\logo.ico" />
  </ItemGroup>


  <ItemGroup>
    <PackageReference Include="Avalonia" Version="11.3.9" />
    <PackageReference Include="Avalonia.Desktop" Version="11.3.9" />
    <PackageReference Include="Avalonia.Themes.Fluent" Version="11.3.9" />
    <PackageReference Include="Avalonia.Fonts.Inter" Version="11.3.9" />
    <!--Condition below is needed to remove Avalonia.Diagnostics package from build output in Release configuration.-->
    <PackageReference Condition="'$(Configuration)' == 'Debug'" Include="Avalonia.Diagnostics" Version="11.3.9" />
    <PackageReference Include="FluentAvaloniaUI" Version="2.4.1" />
    <PackageReference Include="HyperText.Avalonia" Version="2.0.0" />
    <PackageReference Include="Material.Icons.Avalonia" Version="2.4.1" />
    <PackageReference Include="Newtonsoft.Json" Version="13.0.4" />
    <PackageReference Include="ReactiveUI.Avalonia" Version="11.3.8" />
    <PackageReference Include="SevenZipExtractor" Version="1.0.19" />
    <PackageReference Include="SharpZipLib" Version="1.4.2" />
    <PackageReference Include="Splat.Autofac" Version="17.1.1" />
    <PackageReference Include="Velopack" Version="0.0.1298" />
  </ItemGroup>


  <ItemGroup>
    <None Update="appstate2.json">
      <CopyToOutputDirectory>Always</CopyToOutputDirectory>
    </None>
    <Content Include="backend\**">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
  </ItemGroup>
</Project>


================================================
FILE: MangaJaNaiConverterGui/Program.cs
================================================
using Avalonia;
using ReactiveUI.Avalonia;
using System;
using System.IO;
using Velopack;

namespace MangaJaNaiConverterGui
{
    internal class Program
    {
        public static bool WasFirstRun { get; private set; }

        public static readonly string InstalledAppStateFolder = Path.Combine(
            Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
            "MangaJaNaiConverterGui"
        );

        public static readonly string InstalledAppStateFilename = "appstate2.json";
        public static readonly string InstalledAppStatePath = Path.Combine(InstalledAppStateFolder, InstalledAppStateFilename);

        // Initialization code. Don't use any Avalonia, third-party APIs or any
        // SynchronizationContext-reliant code before AppMain is called: things aren't initialized
        // yet and stuff might break.
        [STAThread]
        public static void Main(string[] args)
        {
            VelopackApp.Build()
                .OnBeforeUninstallFastCallback((v) =>
                {
                    // On uninstall, remove Python and models from app data
                    var pythonDir = Path.Combine(InstalledAppStateFolder, "python");
                    var modelsDir = Path.Combine(InstalledAppStateFolder, "models");
                    if (Directory.Exists(pythonDir))
                    {
                        Directory.Delete(pythonDir, true);
                    }
                    if (Directory.Exists(modelsDir))
                    {
                        Directory.Delete(modelsDir, true);
                    }
                })
                .OnFirstRun(_ =>
                {
                    WasFirstRun = true;
                })
                .Run();
            BuildAvaloniaApp().StartWithClassicDesktopLifetime(args);
        }

        // Avalonia configuration, don't remove; also used by visual designer.
        public static AppBuilder BuildAvaloniaApp()
            => AppBuilder.Configure<App>()
                .UsePlatformDetect()
                .WithInterFont()
                .LogToTrace()
                .UseReactiveUI();
    }
}

================================================
FILE: MangaJaNaiConverterGui/Services/Downloader.cs
================================================
using System;
using System.IO;
using System.Net.Http;
using System.Threading.Tasks;


namespace MangaJaNaiConverterGui.Services
{
    public class Downloader
    {
        public delegate void ProgressChanged(double percentage);

        public static async Task DownloadFileAsync(string url, string destinationFilePath, ProgressChanged progressChanged)
        {
            using HttpClient client = new();
            using HttpResponseMessage response = await client.GetAsync(url, HttpCompletionOption.ResponseHeadersRead);

            response.EnsureSuccessStatusCode();

            long totalBytes = response.Content.Headers.ContentLength ?? -1L;
            using Stream contentStream = await response.Content.ReadAsStreamAsync(), fileStream = new FileStream(destinationFilePath, FileMode.Create, FileAccess.Write, FileShare.None, 8192, true);

            var totalRead = 0L;
            var buffer = new byte[8192];
            int read;

            while ((read = await contentStream.ReadAsync(buffer)) > 0)
            {
                await fileStream.WriteAsync(buffer.AsMemory(0, read));
                totalRead += read;

                if (totalBytes != -1)
                {
                    double percentage = Math.Round((double)totalRead / totalBytes * 100, 0);
                    progressChanged?.Invoke(percentage);
                }
            }
        }
    }
}


================================================
FILE: MangaJaNaiConverterGui/Services/ETACalculator.cs
================================================
using System;
using System.Collections.Generic;
using System.Diagnostics;
using ProgressItem = System.Collections.Generic.KeyValuePair<long, float>;


namespace MangaJaNaiConverterGui.Services
{
    public interface IETACalculator
    {
        /// <summary> Clears all collected data.
        /// </summary>
        void Reset();

        /// <summary> Updates the current progress.
        /// </summary>
        /// <param name="progress">The current level of completion.
        /// Must be between 0.0 and 1.0 (inclusively).</param>
        void Update(float progress);

        /// <summary> Returns True when there is enough data to calculate the ETA.
        /// Returns False if the ETA is still calculating.
        /// </summary>
        bool ETAIsAvailable { get; }

        /// <summary> Calculates the Estimated Time of Arrival (Completion)
        /// </summary>
        DateTime ETA { get; }

        /// <summary> Calculates the Estimated Time Remaining.
        /// </summary>
        TimeSpan ETR { get; }
    }

    /// <summary> Calculates the "Estimated Time of Arrival"
    /// (or more accurately, "Estimated Time of Completion"),
    /// based on a "rolling average" of progress over time.
    /// </summary>
    public class ETACalculator : IETACalculator
    {
        /// <summary>
        /// </summary>
        /// <param name="minimumData">
        /// The minimum number of data points required before ETA can be calculated.
        /// </param>
        /// <param name="maximumDuration">
        /// Determines how many seconds of data will be used to calculate the ETA.
        /// </param>
        public ETACalculator(int minimumData, double maximumDuration)
        {
            this.minimumData = minimumData;
            maximumTicks = (long)(maximumDuration * Stopwatch.Frequency);
            queue = new Queue<ProgressItem>(minimumData * 2);
            timer = Stopwatch.StartNew();
        }

        private int minimumData;
        private long maximumTicks;
        private readonly Stopwatch timer;
        private readonly Queue<ProgressItem> queue;

        private ProgressItem current;
        private ProgressItem oldest;

        public void Reset()
        {
            queue.Clear();

            timer.Reset();
            timer.Start();
        }

        private void ClearExpired()
        {
            var expired = timer.ElapsedTicks - maximumTicks;
            while (queue.Count > minimumData && queue.Peek().Key < expired)
            {
                oldest = queue.Dequeue();
            }
        }

        /// <summary> Adds the current progress to the calculation of ETA.
        /// </summary>
        /// <param name="progress">The current level of completion.
        /// Must be between 0.0 and 1.0 (inclusively).</param>
        public void Update(float progress)
        {
            // If progress hasn't changed, ignore:
            if (current.Value == progress)
            {
                return;
            }

            // Clear space for this item:
            ClearExpired();

            // Queue this item:
            long currentTicks = timer.ElapsedTicks;
            current = new ProgressItem(currentTicks, progress);
            queue.Enqueue(current);

            // See if its the first item:
            if (queue.Count == 1)
            {
                oldest = current;
            }
        }

        /// <summary> Calculates the Estimated Time Remaining
        /// </summary>
        public TimeSpan ETR
        {
            get
            {
                // Create local copies of the oldest & current,
                // so that another thread can update them without locking:
                var oldest = this.oldest;
                var current = this.current;

                // Make sure we have enough items:
                if (queue.Count < minimumData || oldest.Value == current.Value)
                {
                    return TimeSpan.MaxValue;
                }

                // Calculate the estimated finished time:
                double finishedInTicks = (1.0d - current.Value) * (current.Key - oldest.Key) / (current.Value - oldest.Value);

                return TimeSpan.FromSeconds(finishedInTicks / Stopwatch.Frequency);
            }
        }

        /// <summary> Calculates the Estimated Time of Arrival (Completion)
        /// </summary>
        public DateTime ETA
        {
            get
            {
                return DateTime.Now.Add(ETR);
            }
        }

        /// <summary> Returns True when there is enough data to calculate the ETA.
        /// Returns False if the ETA is still calculating.
        /// </summary>
        public bool ETAIsAvailable
        {
            get
            {
                // Make sure we have enough items:
                return queue.Count >= minimumData && oldest.Value != current.Value;
            }
        }

    }

}


================================================
FILE: MangaJaNaiConverterGui/Services/IPythonService.cs
================================================
using Avalonia.Collections;
using System;
using System.Threading.Tasks;

namespace MangaJaNaiConverterGui.Services
{
    public interface IPythonService
    {
        bool IsPythonInstalled();

        Task<bool> IsPythonUpdated();
        Task<bool> IsBackendUpdated();
        bool AreModelsInstalled();
        string BackendUrl { get; }
        string BackendDirectory { get; }
        string LogsDirectory { get; }
        string PythonDirectory { get; }
        string ModelsDirectory { get; }
        string PythonPath { get; }
        string AppStateFolder { get; }
        string AppStatePath { get; }
        string AppStateFilename { get; }
        string InstallUpdatePythonDependenciesCommand { get; }
        string PythonBackendVersionPath { get; }
        Version BackendVersion { get; }
        void ExtractTgz(string gzArchiveName, string destFolder);
        void ExtractZip(string archivePath, string outFolder, ProgressChanged progressChanged);
        void Extract7z(string archivePath, string outFolder);
        void AddPythonPth(string destFolder);
        AvaloniaList<string> AllModels { get; }
    }
}


================================================
FILE: MangaJaNaiConverterGui/Services/ISuspensionDriverService.cs
================================================
using ReactiveUI;

namespace MangaJaNaiConverterGui.Services
{
    public interface ISuspensionDriverService
    {
        ISuspensionDriver SuspensionDriver { get; }
    }
}


================================================
FILE: MangaJaNaiConverterGui/Services/IUpdateManagerService.cs
================================================
using System;
using System.Threading.Tasks;
using Velopack;

namespace MangaJaNaiConverterGui.Services
{
    public interface IUpdateManagerService
    {
        bool IsInstalled { get; }
        bool IsPortable { get; }
        string AppVersion { get; }
        bool IsUpdatePendingRestart { get; }
        void ApplyUpdatesAndRestart(UpdateInfo update);
        Task<UpdateInfo?> CheckForUpdatesAsync();
        Task DownloadUpdatesAsync(UpdateInfo update, Action<int>? progress = null);
    }
}


================================================
FILE: MangaJaNaiConverterGui/Services/PythonService.cs
================================================
using Avalonia.Collections;
using ICSharpCode.SharpZipLib.Core;
using ICSharpCode.SharpZipLib.GZip;
using ICSharpCode.SharpZipLib.Tar;
using ICSharpCode.SharpZipLib.Zip;
using SevenZipExtractor;
using Splat;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MangaJaNaiConverterGui.Services
{
    public delegate void ProgressChanged(double percentage);

    // https://github.com/chaiNNer-org/chaiNNer/blob/main/src/main/python/integratedPython.ts
    public class PythonService : IPythonService
    {
        private readonly IUpdateManagerService _updateManagerService;

        public static readonly Dictionary<string, PythonDownload> PYTHON_DOWNLOADS = new()
        {
            {
                "win32",
                new PythonDownload
                {
                    Url = "https://github.com/astral-sh/python-build-standalone/releases/download/20251120/cpython-3.13.9+20251120-x86_64-pc-windows-msvc-install_only.tar.gz",
                    Path = "python/python.exe",
                    Version = "3.13.9",
                    Filename = "Python.tar.gz"
                }
            },
        };

        public Version BackendVersion => new Version(1, 5, 0);

        public string BackendUrl => $"https://github.com/the-database/MangaJaNaiConverterGui-backend/releases/download/{BackendVersion}/mangajanaiconvertergui-backend-{BackendVersion}.7z";

        public PythonService(IUpdateManagerService? updateManagerService = null)
        {
            _updateManagerService = updateManagerService ?? Locator.Current.GetService<IUpdateManagerService>()!;
        }

        public string BackendDirectory => (_updateManagerService?.IsInstalled ?? false) ? Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"MangaJaNaiConverterGui") : Path.GetFullPath(@".\backend");

        public string LogsDirectory => Path.Combine(BackendDirectory, "logs");
        public string ModelsDirectory => Path.Combine(BackendDirectory, "models");
        public string PythonDirectory => Path.Combine(BackendDirectory, "python");
        public string PythonBackendVersionPath => Path.Combine(PythonDirectory, "Version.txt");
        public string PythonPath => Path.GetFullPath(Path.Join(PythonDirectory, PYTHON_DOWNLOADS["win32"].Path));

        public string AppStateFolder => ((_updateManagerService?.IsInstalled ?? false) && !(_updateManagerService?.IsPortable ?? false)) ? Path.Join(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), @"MangaJaNaiConverterGui") : Path.GetFullPath(@".");

        public string AppStateFilename => "appstate2.json";
        public string AppStatePath => Path.Join(AppStateFolder, AppStateFilename);

        public bool IsPythonInstalled() => File.Exists(PythonPath);

        public async Task<bool> IsPythonUpdated()
        {
            var relPythonPath = @".\python\python\python.exe";

            var cmd = $@"{relPythonPath} -V";

            // Create a new process to run the CMD command
            using (var process = new Process())
            {
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.Arguments = @$"/C {cmd}";
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
                process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
                process.StartInfo.WorkingDirectory = BackendDirectory;

                Version? result = null;

                // Create a StreamWriter to write the output to a log file
                try
                {
                    process.ErrorDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            // ignore
                        }
                    };

                    process.OutputDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            result = new Version(e.Data.Replace("Python ", ""));
                        }
                    };

                    process.Start();
                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine(); // Start asynchronous reading of the output
                    await process.WaitForExitAsync();
                }
                catch (IOException) { }

                if (result == null || result.CompareTo(new Version(PYTHON_DOWNLOADS["win32"].Version)) < 0)
                {
                    return false;
                }
            }

            return true;
        }

        public async Task<bool> IsBackendUpdated()
        {
            if (File.Exists(PythonBackendVersionPath))
            {
                var currentVersion = new Version(await File.ReadAllTextAsync(PythonBackendVersionPath));

                return currentVersion.CompareTo(BackendVersion) >= 0;
            }

            return false;
        }

        public bool AreModelsInstalled() => Directory.Exists(ModelsDirectory) && Directory.GetFiles(ModelsDirectory).Length > 0 && Directory.GetFiles(ModelsDirectory).Any(x => x.Contains("2x_IllustrationJaNai_V3denoise_FDAT_M_unshuffle_30k_fp16"));

        public class PythonDownload
        {
            public string Url { get; set; }
            public string Version { get; set; }
            public string Path { get; set; }
            public string Filename { get; set; }
        }

        public void ExtractTgz(string gzArchiveName, string destFolder)
        {
            Stream inStream = File.OpenRead(gzArchiveName);
            Stream gzipStream = new GZipInputStream(inStream);

            TarArchive tarArchive = TarArchive.CreateInputTarArchive(gzipStream, Encoding.UTF8);
            tarArchive.ExtractContents(destFolder);
            tarArchive.Close();

            gzipStream.Close();
            inStream.Close();
        }

        public void ExtractZip(string archivePath, string outFolder, ProgressChanged progressChanged)
        {

            using (var fsInput = File.OpenRead(archivePath))
            using (var zf = new ZipFile(fsInput))
            {

                for (var i = 0; i < zf.Count; i++)
                {
                    ZipEntry zipEntry = zf[i];

                    if (!zipEntry.IsFile)
                    {
                        // Ignore directories
                        continue;
                    }
                    String entryFileName = zipEntry.Name;
                    // to remove the folder from the entry:
                    //entryFileName = Path.GetFileName(entryFileName);
                    // Optionally match entrynames against a selection list here
                    // to skip as desired.
                    // The unpacked length is available in the zipEntry.Size property.

                    // Manipulate the output filename here as desired.
                    var fullZipToPath = Path.Combine(outFolder, entryFileName);
                    var directoryName = Path.GetDirectoryName(fullZipToPath);
                    if (directoryName.Length > 0)
                    {
                        Directory.CreateDirectory(directoryName);
                    }

                    // 4K is optimum
                    var buffer = new byte[4096];

                    // Unzip file in buffered chunks. This is just as fast as unpacking
                    // to a buffer the full size of the file, but does not waste memory.
                    // The "using" will close the stream even if an exception occurs.
                    using (var zipStream = zf.GetInputStream(zipEntry))
                    using (Stream fsOutput = File.Create(fullZipToPath))
                    {
                        StreamUtils.Copy(zipStream, fsOutput, buffer);
                    }

                    var percentage = Math.Round((double)i / zf.Count * 100, 0);
                    progressChanged?.Invoke(percentage);
                }
            }
        }

        public void Extract7z(string archiveName, string outFolder)
        {
            using ArchiveFile archiveFile = new(archiveName);
            archiveFile.Extract(outFolder);
        }

        public void AddPythonPth(string destFolder)
        {
            string[] lines = { "python313.zip", "DLLs", "Lib", ".", "Lib/site-packages" };
            var filename = "python313._pth";

            using var outputFile = new StreamWriter(Path.Combine(destFolder, filename));

            foreach (string line in lines)
                outputFile.WriteLine(line);
        }

        public string InstallUpdatePythonDependenciesCommand
        {
            get
            {
                var relPythonPath = @".\python\python\python.exe";

                return $@"{relPythonPath} -m pip install -U pip wheel --no-warn-script-location && {relPythonPath} -m pip install torch==2.9.1 torchvision --index-url https://download.pytorch.org/whl/cu128 --no-warn-script-location && {relPythonPath} -m pip install ""{Path.GetFullPath(@".\backend\src")}"" --no-warn-script-location";
            }
        }

        private AvaloniaList<string>? _allModels;

        public AvaloniaList<string> AllModels
        {
            get
            {
                if (_allModels == null)
                {

                    try
                    {
                        var models = new AvaloniaList<string>(Directory.GetFiles(ModelsDirectory).Where(filename =>
                            Path.GetExtension(filename).Equals(".pth", StringComparison.CurrentCultureIgnoreCase) ||
                            Path.GetExtension(filename).Equals(".pt", StringComparison.CurrentCultureIgnoreCase) ||
                            Path.GetExtension(filename).Equals(".ckpt", StringComparison.CurrentCultureIgnoreCase) ||
                            Path.GetExtension(filename).Equals(".safetensors", StringComparison.CurrentCultureIgnoreCase)
                        )
                        .Select(filename => Path.GetFileName(filename))
                        .Order().ToList());

                        models.Add("No Model");

                        Debug.WriteLine($"GetAllModels: {models.Count}");

                        _allModels = models;
                    }
                    catch (DirectoryNotFoundException)
                    {
                        Debug.WriteLine($"GetAllModels: DirectoryNotFoundException");
                        return [];
                    }
                }

                return _allModels;
            }
        }
    }
}


================================================
FILE: MangaJaNaiConverterGui/Services/SuspensionDriverService.cs
================================================
using MangaJaNaiConverterGui.Drivers;
using ReactiveUI;

namespace MangaJaNaiConverterGui.Services
{
    public class SuspensionDriverService(IPythonService pythonService) : ISuspensionDriverService
    {
        private readonly ISuspensionDriver _driver = new NewtonsoftJsonSuspensionDriver(pythonService.AppStatePath);
        public ISuspensionDriver SuspensionDriver => _driver;
    }
}


================================================
FILE: MangaJaNaiConverterGui/Services/UpdateManagerService.cs
================================================
using System;
using System.Threading.Tasks;
using Velopack;
using Velopack.Sources;

namespace MangaJaNaiConverterGui.Services
{
    public class UpdateManagerService : IUpdateManagerService
    {
        private readonly UpdateManager _um;

        public UpdateManagerService()
        {
            _um = new UpdateManager(new GithubSource("https://github.com/the-database/MangaJaNaiConverterGui", null, false));
        }

        public string AppVersion { get => _um?.CurrentVersion?.ToString() ?? ""; }

        public bool IsInstalled { get => _um.IsInstalled; }
        public bool IsPortable { get => _um.IsPortable; }

        public bool IsUpdatePendingRestart { get => _um.IsUpdatePendingRestart; }

        public void ApplyUpdatesAndRestart(UpdateInfo update)
        {
            _um.ApplyUpdatesAndRestart(update);
        }

        public async Task<UpdateInfo?> CheckForUpdatesAsync()
        {
            return await _um.CheckForUpdatesAsync();
        }

        public Task DownloadUpdatesAsync(UpdateInfo update, Action<int>? progress = null)
        {
            return _um.DownloadUpdatesAsync(update, progress);
        }
    }
}


================================================
FILE: MangaJaNaiConverterGui/ViewLocator.cs
================================================
using Avalonia.Controls;
using Avalonia.Controls.Templates;
using MangaJaNaiConverterGui.ViewModels;
using System;

namespace MangaJaNaiConverterGui
{
    public class ViewLocator : IDataTemplate
    {
        public Control Build(object data)
        {
            var name = data.GetType().FullName!.Replace("ViewModel", "View");
            var type = Type.GetType(name);

            if (type != null)
            {
                return (Control)Activator.CreateInstance(type)!;
            }

            return new TextBlock { Text = "Not Found: " + name };
        }

        public bool Match(object data)
        {
            return data is ViewModelBase;
        }
    }
}

================================================
FILE: MangaJaNaiConverterGui/ViewModels/MainWindowViewModel.cs
================================================
using Avalonia.Collections;
using Avalonia.Threading;
using MangaJaNaiConverterGui.Drivers;
using MangaJaNaiConverterGui.Services;
using Newtonsoft.Json;
using ReactiveUI;
using Splat;
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Reactive.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Velopack;
using File = System.IO.File;
using Path = System.IO.Path;

namespace MangaJaNaiConverterGui.ViewModels
{
    [DataContract]
    public class MainWindowViewModel : ViewModelBase
    {
        public static readonly List<string> IMAGE_EXTENSIONS = [".png", ".jpg", ".jpeg", ".webp", ".bmp", ".avif"];
        public static readonly List<string> ARCHIVE_EXTENSIONS = [".zip", ".cbz", ".rar", ".cbr"];

        private readonly DispatcherTimer _timer = new();
        private static readonly HttpClient client = new();

        private UpdateInfo? _update = null;

        private readonly IPythonService _pythonService;
        private readonly IUpdateManagerService _updateManagerService;
        private readonly ISuspensionDriverService _suspensionDriverService;

        public MainWindowViewModel(IPythonService? pythonService = null, IUpdateManagerService? updateManagerService = null, ISuspensionDriverService? suspensionDriverService = null)
        {
            _pythonService = pythonService ?? Locator.Current.GetService<IPythonService>()!;
            _updateManagerService = updateManagerService ?? Locator.Current.GetService<IUpdateManagerService>()!;
            _suspensionDriverService = suspensionDriverService ?? Locator.Current.GetService<ISuspensionDriverService>()!;

            var g1 = this.WhenAnyValue
            (
                x => x.SelectedWorkflowIndex
            ).Subscribe(x =>
            {
                CurrentWorkflow?.Validate();
            });

            _timer.Interval = TimeSpan.FromSeconds(1);
            _timer.Tick += _timer_Tick;

            ShowDialog = new Interaction<MainWindowViewModel, MainWindowViewModel?>();

            CheckAndDoBackup();
            CheckForUpdates();
        }

        private string[] _commonResolutions = [
"0x0",
"0x1250",
"0x1251",
"0x1350",
"0x1351",
"0x1450",
"0x1451",
"0x1550",
"0x1551",
"0x1760",
"0x1761",
"0x1984",
"0x1985",];

        private static readonly string DEFAULT_WORKFLOW = """
{
  "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleWorkflow, MangaJaNaiConverterGui",
  "WorkflowName": "Upscale Manga (Default)",
  "WorkflowIndex": 0,
  "SelectedTabIndex": 0,
  "InputFilePath": "",
  "InputFolderPath": "",
  "OutputFilename": "%filename%-mangajanai",
  "OutputFolderPath": "",
  "OverwriteExistingFiles": false,
  "UpscaleImages": true,
  "UpscaleArchives": true,
  "ResizeHeightAfterUpscale": 2160,
  "ResizeWidthAfterUpscale": 3840,
  "WebpSelected": true,
  "AvifSelected": false,
  "PngSelected": false,
  "JpegSelected": false,
  "UseLosslessCompression": false,
  "LossyCompressionQuality": 80,
  "ShowLossySettings": true,
  "ModeScaleSelected": true,
  "UpscaleScaleFactor": 4,
  "ModeWidthSelected": false,
  "ModeHeightSelected": false,
  "ModeFitToDisplaySelected": false,
  "DisplayDevice": "Kobo Elipsa 2E (2023)",
  "DisplayDeviceWidth": 1404,
  "DisplayDeviceHeight": 1872,
  "DisplayPortraitSelected": true,
  "ShowAdvancedSettings": false,
  "GrayscaleDetectionThreshold": 12,
  "Chains": {
    "$type": "Avalonia.Collections.AvaloniaList`1[[MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui]], Avalonia.Base",
    "$values": [
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "1",
        "MinResolution": "0x0",
        "MaxResolution": "0x0",
        "IsGrayscale": false,
        "IsColor": true,
        "MinScaleFactor": 0,
        "MaxScaleFactor": 2,
        "ModelFilePath": "2x_IllustrationJaNai_V3denoise_FDAT_M_unshuffle_30k_fp16.safetensors",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": false,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "2",
        "MinResolution": "0x0",
        "MaxResolution": "0x0",
        "IsGrayscale": false,
        "IsColor": true,
        "MinScaleFactor": 2,
        "MaxScaleFactor": 0,
        "ModelFilePath": "4x_IllustrationJaNai_V3denoise_FDAT_M_47k_fp16.safetensors",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": false,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "3",
        "MinResolution": "0x0",
        "MaxResolution": "0x1250",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 0,
        "MaxScaleFactor": 2,
        "ModelFilePath": "2x_MangaJaNai_1200p_V1_ESRGAN_70k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "4",
        "MinResolution": "0x0",
        "MaxResolution": "0x1250",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 2,
        "MaxScaleFactor": 0,
        "ModelFilePath": "4x_MangaJaNai_1200p_V1_ESRGAN_70k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "5",
        "MinResolution": "0x1251",
        "MaxResolution": "0x1350",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 0,
        "MaxScaleFactor": 2,
        "ModelFilePath": "2x_MangaJaNai_1300p_V1_ESRGAN_75k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "6",
        "MinResolution": "0x1251",
        "MaxResolution": "0x1350",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 2,
        "MaxScaleFactor": 0,
        "ModelFilePath": "4x_MangaJaNai_1300p_V1_ESRGAN_75k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "7",
        "MinResolution": "0x1351",
        "MaxResolution": "0x1450",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 0,
        "MaxScaleFactor": 2,
        "ModelFilePath": "2x_MangaJaNai_1400p_V1_ESRGAN_70k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "8",
        "MinResolution": "0x1351",
        "MaxResolution": "0x1450",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 2,
        "MaxScaleFactor": 0,
        "ModelFilePath": "4x_MangaJaNai_1400p_V1_ESRGAN_105k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "9",
        "MinResolution": "0x1451",
        "MaxResolution": "0x1550",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 0,
        "MaxScaleFactor": 2,
        "ModelFilePath": "2x_MangaJaNai_1500p_V1_ESRGAN_90k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "10",
        "MinResolution": "0x1451",
        "MaxResolution": "0x1550",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 2,
        "MaxScaleFactor": 0,
        "ModelFilePath": "4x_MangaJaNai_1500p_V1_ESRGAN_105k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "11",
        "MinResolution": "0x1551",
        "MaxResolution": "0x1760",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 0,
        "MaxScaleFactor": 2,
        "ModelFilePath": "2x_MangaJaNai_1600p_V1_ESRGAN_90k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "12",
        "MinResolution": "0x1551",
        "MaxResolution": "0x1760",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 2,
        "MaxScaleFactor": 0,
        "ModelFilePath": "4x_MangaJaNai_1600p_V1_ESRGAN_70k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "13",
        "MinResolution": "0x1761",
        "MaxResolution": "0x1984",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 0,
        "MaxScaleFactor": 2,
        "ModelFilePath": "2x_MangaJaNai_1920p_V1_ESRGAN_70k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "14",
        "MinResolution": "0x1761",
        "MaxResolution": "0x1984",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 2,
        "MaxScaleFactor": 0,
        "ModelFilePath": "4x_MangaJaNai_1920p_V1_ESRGAN_105k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "15",
        "MinResolution": "0x1985",
        "MaxResolution": "0x0",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 0,
        "MaxScaleFactor": 2,
        "ModelFilePath": "2x_MangaJaNai_2048p_V1_ESRGAN_95k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      },
      {
        "$type": "MangaJaNaiConverterGui.ViewModels.UpscaleChain, MangaJaNaiConverterGui",
        "ChainNumber": "16",
        "MinResolution": "0x1985",
        "MaxResolution": "0x0",
        "IsGrayscale": true,
        "IsColor": false,
        "MinScaleFactor": 2,
        "MaxScaleFactor": 0,
        "ModelFilePath": "4x_MangaJaNai_2048p_V1_ESRGAN_70k.pth",
        "ModelTileSize": "Auto (Estimate)",
        "AutoAdjustLevels": true,
        "ResizeHeightBeforeUpscale": 0,
        "ResizeWidthBeforeUpscale": 0,
        "ResizeFactorBeforeUpscale": 100.0
      }
    ]
  }
}
""";

        public string[] CommonResolutions
        {
            get => _commonResolutions;
            set => this.RaiseAndSetIfChanged(ref _commonResolutions, value);
        }

        public Interaction<MainWindowViewModel, MainWindowViewModel?> ShowDialog { get; }

        private void _timer_Tick(object? sender, EventArgs e)
        {
            ElapsedTime = ElapsedTime.Add(TimeSpan.FromSeconds(1));
        }

        private CancellationTokenSource? _cancellationTokenSource;
        private Process? _runningProcess = null;
        private readonly IETACalculator _archiveEtaCalculator = new ETACalculator(2, 3.0);
        private readonly IETACalculator _totalEtaCalculator = new ETACalculator(2, 3.0);

        public TimeSpan ArchiveEtr => _archiveEtaCalculator.ETAIsAvailable ? _archiveEtaCalculator.ETR : TimeSpan.FromSeconds(0);
        public string ArchiveEta => _archiveEtaCalculator.ETAIsAvailable ? _archiveEtaCalculator.ETA.ToString("t") : "please wait";

        public TimeSpan TotalEtr => _totalEtaCalculator.ETAIsAvailable ? _totalEtaCalculator.ETR : ArchiveEtr + (ElapsedTime + ArchiveEtr) * (ProgressTotalFiles - (ProgressCurrentFile + 1));

        public string TotalEta => _totalEtaCalculator.ETAIsAvailable ? _totalEtaCalculator.ETA.ToString("t") : _archiveEtaCalculator.ETAIsAvailable ? DateTime.Now.Add(TotalEtr).ToString("t") : "please wait";

        public bool IsInstalled => _updateManagerService.IsInstalled;
        [DataMember]
        public string ModelsDirectory => _pythonService.ModelsDirectory;

        private bool _showCheckUpdateButton = true;
        public bool ShowCheckUpdateButton
        {
            get => _showCheckUpdateButton;
            set => this.RaiseAndSetIfChanged(ref _showCheckUpdateButton, value);
        }

        private bool _showDownloadButton = false;
        public bool ShowDownloadButton
        {
            get => _showDownloadButton;
            set
            {
                this.RaiseAndSetIfChanged(ref _showDownloadButton, value);
                this.RaisePropertyChanged(nameof(ShowCheckUpdateButton));
            }
        }

        private bool _showApplyButton = false;
        public bool ShowApplyButton
        {
            get => _showApplyButton;
            set
            {
                this.RaiseAndSetIfChanged(ref _showApplyButton, value);
                this.RaisePropertyChanged(nameof(ShowCheckUpdateButton));
            }
        }

        public string AppVersion => _updateManagerService.AppVersion;

        private string _updateStatusText = string.Empty;
        public string UpdateStatusText
        {
            get => _updateStatusText;
            set => this.RaiseAndSetIfChanged(ref _updateStatusText, value);
        }

        private string[] _tileSizes = [
            "Auto (Estimate)",
            "Maximum",
            "No Tiling",
            "128",
            "192",
            "256",
            "384",
            "512",
            "768",
            "1024",
            "2048",
            "4096"];

        public string[] TileSizes
        {
            get => _tileSizes;
            set => this.RaiseAndSetIfChanged(ref _tileSizes, value);
        }

        private string[] _deviceList = [];

        public string[] DeviceList
        {
            get => _deviceList;
            set
            {
                this.RaiseAndSetIfChanged(ref _deviceList, value);
                this.RaisePropertyChanged(nameof(SelectedDeviceIndex));
            }
        }

        private string _pythonPipList = string.Empty;
        public string PythonPipList
        {
            get => _pythonPipList;
            set => this.RaiseAndSetIfChanged(ref _pythonPipList, value);
        }

        private AvaloniaDictionary<string, ReaderDevice> _displayDeviceMap = [];
        [DataMember]
        public AvaloniaDictionary<string, ReaderDevice> DisplayDeviceMap
        {
            get => _displayDeviceMap;
            set => this.RaiseAndSetIfChanged(ref _displayDeviceMap, value);
        }

        private bool _autoUpdate;
        [DataMember]
        public bool AutoUpdateEnabled
        {
            get => _autoUpdate;
            set => this.RaiseAndSetIfChanged(ref _autoUpdate, value);
        }

        private int _selectedDeviceIndex;
        [DataMember]
        public int SelectedDeviceIndex
        {
            get => _selectedDeviceIndex;
            set => this.RaiseAndSetIfChanged(ref _selectedDeviceIndex, value);
        }

        private bool _useCpu;
        [DataMember]
        public bool UseCpu
        {
            get => _useCpu;
            set => this.RaiseAndSetIfChanged(ref _useCpu, value);
        }

        private bool _useFp16;
        [DataMember]
        public bool UseFp16
        {
            get => _useFp16;
            set => this.RaiseAndSetIfChanged(ref _useFp16, value);
        }


        private bool _upscaling = false;
        [IgnoreDataMember]
        public bool Upscaling
        {
            get => _upscaling;
            set
            {
                this.RaiseAndSetIfChanged(ref _upscaling, value);
                this.RaisePropertyChanged(nameof(UpscaleEnabled));
                this.RaisePropertyChanged(nameof(LeftStatus));
            }
        }

        private string _validationText = string.Empty;
        public string ValidationText
        {
            get => _validationText;
            set
            {
                this.RaiseAndSetIfChanged(ref _validationText, value);
                this.RaisePropertyChanged(nameof(LeftStatus));
            }
        }

        private string _backendSetupMainStatus = string.Empty;
        public string BackendSetupMainStatus
        {
            get => this._backendSetupMainStatus;
            set
            {
                this.RaiseAndSetIfChanged(ref _backendSetupMainStatus, value);
            }
        }

        public string BackendSetupSubStatusText => string.Join("\n", BackendSetupSubStatusQueue);

        private static readonly int BACKEND_SETUP_SUB_STATUS_QUEUE_CAPACITY = 50;

        private ConcurrentQueue<string> _backendSetupSubStatusQueue = new();
        public ConcurrentQueue<string> BackendSetupSubStatusQueue
        {
            get => this._backendSetupSubStatusQueue;
            set
            {
                this.RaiseAndSetIfChanged(ref _backendSetupSubStatusQueue, value);
                this.RaisePropertyChanged(nameof(BackendSetupSubStatusText));
            }
        }

        public string ConsoleText => string.Join("\n", ConsoleQueue);

        private static readonly int CONSOLE_QUEUE_CAPACITY = 1000;

        private ConcurrentQueue<string> _consoleQueue = new();
        public ConcurrentQueue<string> ConsoleQueue
        {
            get => this._consoleQueue;
            set
            {
                this.RaiseAndSetIfChanged(ref _consoleQueue, value);
                this.RaisePropertyChanged(nameof(ConsoleText));
            }
        }

        private bool _showConsole = false;
        public bool ShowConsole
        {
            get => _showConsole;
            set => this.RaiseAndSetIfChanged(ref _showConsole, value);
        }

        private bool _showAppSettings = false;
        public bool RequestShowAppSettings
        {
            get => _showAppSettings;
            set
            {
                this.RaiseAndSetIfChanged(ref _showAppSettings, value);
                this.RaisePropertyChanged(nameof(ShowAppSettings));
                this.RaisePropertyChanged(nameof(ShowMainForm));
            }
        }

        public string PythonPath => _pythonService.PythonPath;

        private bool _isExtractingBackend = true;
        public bool IsExtractingBackend
        {
            get => _isExtractingBackend;
            set
            {
                this.RaiseAndSetIfChanged(ref _isExtractingBackend, value);
                this.RaisePropertyChanged(nameof(RequestShowAppSettings));
                this.RaisePropertyChanged(nameof(ShowMainForm));
            }
        }

        public bool ShowAppSettings => RequestShowAppSettings && !IsExtractingBackend;

        public bool ShowMainForm => !RequestShowAppSettings && !IsExtractingBackend;

        private bool _showEstimates = false;
        public bool ShowEstimates
        {
            get => _showEstimates;
            set => this.RaiseAndSetIfChanged(ref _showEstimates, value);
        }

        private string _inputStatusText = string.Empty;
        public string InputStatusText
        {
            get => _inputStatusText;
            set
            {
                this.RaiseAndSetIfChanged(ref _inputStatusText, value);
                this.RaisePropertyChanged(nameof(LeftStatus));
            }
        }

        public string LeftStatus => !CurrentWorkflow.Valid ? ValidationText.Replace("\n", " ") : $"{InputStatusText} selected for upscaling.";

        private int _progressCurrentFile = 0;
        public int ProgressCurrentFile
        {
            get => _progressCurrentFile;
            set => this.RaiseAndSetIfChanged(ref _progressCurrentFile, value);
        }

        private int _progressTotalFiles = 0;
        public int ProgressTotalFiles
        {
            get => _progressTotalFiles;
            set => this.RaiseAndSetIfChanged(ref _progressTotalFiles, value);
        }

        private int _progressCurrentFileInCurrentArchive = 0;
        public int ProgressCurrentFileInArchive
        {
            get => _progressCurrentFileInCurrentArchive;
            set => this.RaiseAndSetIfChanged(ref _progressCurrentFileInCurrentArchive, value);
        }

        private int _progressTotalFilesInCurrentArchive = 0;
        public int ProgressTotalFilesInCurrentArchive
        {
            get => _progressTotalFilesInCurrentArchive;
            set => this.RaiseAndSetIfChanged(ref _progressTotalFilesInCurrentArchive, value);
        }

        private bool _showArchiveProgressBar = false;
        public bool ShowArchiveProgressBar
        {
            get => _showArchiveProgressBar;
            set => this.RaiseAndSetIfChanged(ref _showArchiveProgressBar, value);
        }

        public bool UpscaleEnabled => CurrentWorkflow.Valid && !Upscaling;

        private TimeSpan _elapsedTime = TimeSpan.FromSeconds(0);
        public TimeSpan ElapsedTime
        {
            get => _elapsedTime;
            set
            {
                this.RaiseAndSetIfChanged(ref _elapsedTime, value);
            }
        }

        private AvaloniaList<UpscaleWorkflow>? _workflows;
        [DataMember]
        public AvaloniaList<UpscaleWorkflow>? Workflows
        {
            get => _workflows;
            set => this.RaiseAndSetIfChanged(ref _workflows, value);
        }

        public AvaloniaList<UpscaleWorkflow> CustomWorkflows => new(Workflows.Skip(1).ToList());

        private int _selectedWorkflowIndex = 0;
        [DataMember]
        public int SelectedWorkflowIndex
        {
            get => _selectedWorkflowIndex;
            set
            {
                this.RaiseAndSetIfChanged(ref _selectedWorkflowIndex, value);
                this.RaisePropertyChanged(nameof(CurrentWorkflow));
                this.RaisePropertyChanged(nameof(CurrentWorkflow.ActiveWorkflow));
            }
        }

        public UpscaleWorkflow? CurrentWorkflow
        {
            get => Workflows?[SelectedWorkflowIndex];
            set
            {
                if (Workflows != null)
                {
                    Workflows[SelectedWorkflowIndex] = value;
                    this.RaisePropertyChanged(nameof(CurrentWorkflow));
                    this.RaisePropertyChanged(nameof(CustomWorkflows));
                }
            }
        }

        public void HandleWorkflowSelected(int workflowIndex)
        {
            SelectedWorkflowIndex = workflowIndex;
            RequestShowAppSettings = false;
        }

        public void HandleAppSettingsSelected()
        {
            RequestShowAppSettings = true;
        }




        public async Task RunUpscale()
        {
            _cancellationTokenSource = new CancellationTokenSource();
            var ct = _cancellationTokenSource.Token;

            var task = Task.Run(async () =>
            {
                await _suspensionDriverService.SuspensionDriver.SaveState(this);
                ElapsedTime = TimeSpan.FromSeconds(0);
                ShowEstimates = true;
                _archiveEtaCalculator.Reset();
                _totalEtaCalculator.Reset();
                ct.ThrowIfCancellationRequested();
                ConsoleQueueClear();
                Upscaling = true;
                ProgressCurrentFile = 0;
                ProgressCurrentFileInArchive = 0;
                ShowArchiveProgressBar = false;

                var cmd = $@".\python\python\python.exe ""{Path.GetFullPath(@".\backend\src\run_upscale.py")}"" --settings ""{_pythonService.AppStatePath}""";
                ConsoleQueueEnqueue($"Upscaling with command: {cmd}");
                await RunCommand($@" /C {cmd}");

                CurrentWorkflow.Valid = true;
            }, ct);

            try
            {
                _timer.Start();
                await task;
                _timer.Stop();
                CurrentWorkflow.Validate();
            }
            catch (OperationCanceledException e)
            {
                _timer.Stop();
                Console.WriteLine($"{nameof(OperationCanceledException)} thrown with message: {e.Message}");
                Upscaling = false;
            }
            finally
            {
                _timer.Stop();
                _cancellationTokenSource.Dispose();
                Upscaling = false;
            }
        }

        public void CancelUpscale()
        {
            try
            {
                _cancellationTokenSource?.Cancel();
                if (_runningProcess != null && !_runningProcess.HasExited)
                {
                    // Kill the process
                    _runningProcess.Kill(true);
                    _runningProcess = null; // Clear the reference to the terminated process
                }
                CurrentWorkflow.Validate();
            }
            catch { }
        }


        public void CheckInputs()
        {
            if (CurrentWorkflow.Valid && !Upscaling)
            {
                var overwriteText = CurrentWorkflow.OverwriteExistingFiles ? "overwritten" : "skipped";

                // input file
                if (CurrentWorkflow.SelectedTabIndex == 0)
                {
                    StringBuilder status = new();
                    var skipFiles = 0;



                    if (IMAGE_EXTENSIONS.Any(x => CurrentWorkflow.InputFilePath.ToLower().EndsWith(x)))
                    {
                        var outputFilePath = Path.Join(
                                                    Path.GetFullPath(CurrentWorkflow.OutputFolderPath),
                                                    CurrentWorkflow.OutputFilename.Replace("%filename%", Path.GetFileNameWithoutExtension(CurrentWorkflow.InputFilePath))) + $".{CurrentWorkflow.ImageFormat}";
                        if (File.Exists(outputFilePath))
                        {
                            status.Append($" (1 image already exists and will be {overwriteText})");
                            if (!CurrentWorkflow.OverwriteExistingFiles)
                            {
                                skipFiles++;
                            }
                        }
                    }
                    else if (ARCHIVE_EXTENSIONS.Any(x => CurrentWorkflow.InputFilePath.ToLower().EndsWith(x)))
                    {
                        var outputFilePath = Path.Join(Path.GetFullPath(CurrentWorkflow.OutputFolderPath),
                            CurrentWorkflow.OutputFilename.Replace("%filename%", Path.GetFileNameWithoutExtension(CurrentWorkflow.InputFilePath))) + ".cbz";

                        if (File.Exists(outputFilePath))
                        {
                            status.Append($" (1 archive already exists and will be {overwriteText})");
                            if (!CurrentWorkflow.OverwriteExistingFiles)
                            {
                                skipFiles++;
                            }
                        }
                    }
                    else
                    {
                        // TODO ???
                    }

                    var s = skipFiles > 0 ? "s" : "";
                    if (IMAGE_EXTENSIONS.Any(x => CurrentWorkflow.InputFilePath.ToLower().EndsWith(x)))
                    {
                        status.Insert(0, $"{1 - skipFiles} image{s}");
                    }
                    else if (ARCHIVE_EXTENSIONS.Any(x => CurrentWorkflow.InputFilePath.ToLower().EndsWith(x)))
                    {
                        status.Insert(0, $"{1 - skipFiles} archive{s}");
                    }
                    else
                    {
                        status.Insert(0, "0 files");
                    }

                    InputStatusText = status.ToString();
                    ProgressCurrentFile = 0;
                    ProgressTotalFiles = 1 - skipFiles;
                    ProgressCurrentFileInArchive = 0;
                    ProgressTotalFilesInCurrentArchive = 0;
                    ShowArchiveProgressBar = false;
                }
                else  // input folder
                {
                    List<string> statuses = new();
                    var existImageCount = 0;
                    var existArchiveCount = 0;
                    var totalFileCount = 0;

                    if (CurrentWorkflow.UpscaleImages)
                    {
                        var images = Directory.EnumerateFiles(CurrentWorkflow.InputFolderPath, "*.*", SearchOption.AllDirectories)
                            .Where(file => IMAGE_EXTENSIONS.Any(ext => file.ToLower().EndsWith(ext)));
                        var imagesCount = 0;

                        foreach (var inputImagePath in images)
                        {
                            var outputImagePath = Path.Join(
                                                        Path.GetFullPath(CurrentWorkflow.OutputFolderPath),
                                                        CurrentWorkflow.OutputFilename.Replace("%filename%", Path.GetFileNameWithoutExtension(inputImagePath))) + $"{CurrentWorkflow.ImageFormat}";
                            // if out file exists, exist count ++
                            // if overwrite image OR out file doesn't exist, count image++
                            var fileExists = File.Exists(outputImagePath);

                            if (fileExists)
                            {
                                existImageCount++;
                            }

                            if (!fileExists || CurrentWorkflow.OverwriteExistingFiles)
                            {
                                imagesCount++;
                            }
                        }

                        var imageS = imagesCount == 1 ? "" : "s";
                        var existImageS = existImageCount == 1 ? "" : "s";

                        statuses.Add($"{imagesCount} image{imageS} ({existImageCount} image{existImageS} already exist and will be {overwriteText})");
                        totalFileCount += imagesCount;
                    }
                    if (CurrentWorkflow.UpscaleArchives)
                    {
                        var archives = Directory.EnumerateFiles(CurrentWorkflow.InputFolderPath, "*.*", SearchOption.AllDirectories)
                            .Where(file => ARCHIVE_EXTENSIONS.Any(ext => file.ToLower().EndsWith(ext)));
                        var archivesCount = 0;

                        foreach (var inputArchivePath in archives)
                        {
                            var outputArchivePath = Path.Join(
                                                            Path.GetFullPath(CurrentWorkflow.OutputFolderPath),
                                                            CurrentWorkflow.OutputFilename.Replace("%filename%", Path.GetFileNameWithoutExtension(inputArchivePath))) + ".cbz";
                            var fileExists = File.Exists(outputArchivePath);

                            if (fileExists)
                            {
                                existArchiveCount++;
                            }

                            if (!fileExists || CurrentWorkflow.OverwriteExistingFiles)
                            {
                                archivesCount++;
                            }
                        }

                        var archiveS = archivesCount == 1 ? "" : "s";
                        var existArchiveS = existArchiveCount == 1 ? "" : "s";
                        statuses.Add($"{archivesCount} archive{archiveS} ({existArchiveCount} archive{existArchiveS} already exist and will be {overwriteText})");
                        totalFileCount += archivesCount;
                    }

                    if (!CurrentWorkflow.UpscaleArchives && !CurrentWorkflow.UpscaleImages)
                    {
                        InputStatusText = "0 files";
                    }
                    else
                    {
                        InputStatusText = $"{string.Join(" and ", statuses)}";
                    }

                    ProgressCurrentFile = 0;
                    ProgressTotalFiles = totalFileCount;
                    ProgressCurrentFileInArchive = 0;
                    ProgressTotalFilesInCurrentArchive = 0;
                    ShowArchiveProgressBar = false;

                }
            }
        }

        public void AddChain()
        {
            CurrentWorkflow?.Chains.Add(new UpscaleChain
            {
                Vm = this,
            });
            UpdateChainHeaders();
        }

        public void DeleteChain(UpscaleChain chain)
        {
            try
            {
                CurrentWorkflow.Chains.Remove(chain);
            }
            catch (ArgumentOutOfRangeException)
            {

            }

            UpdateChainHeaders();
        }

        public void UpdateChainHeaders()
        {
            for (var i = 0; i < CurrentWorkflow.Chains.Count; i++)
            {
                CurrentWorkflow.Chains[i].ChainNumber = (i + 1).ToString();
            }
        }



        public async Task RunCommand(string command)
        {
            // Create a new process to run the CMD command
            using (var process = new Process())
            {
                _runningProcess = process;
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.Arguments = command;
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.WorkingDirectory = _pythonService.BackendDirectory;
                process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
                process.StartInfo.StandardErrorEncoding = Encoding.UTF8;

                // Create a StreamWriter to write the output to a log file
                using (var outputFile = new StreamWriter(Path.Combine(_pythonService.LogsDirectory, "upscale.log"), append: false))
                {
                    process.ErrorDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            outputFile.WriteLine(e.Data); // Write the output to the log file
                            ConsoleQueueEnqueue(e.Data);
                        }
                    };

                    process.OutputDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            if (e.Data.StartsWith("PROGRESS="))
                            {
                                if (e.Data.Contains("_zip_image"))
                                {
                                    ShowArchiveProgressBar = true;
                                    ProgressCurrentFileInArchive++;
                                    UpdateEtas();
                                }
                                else
                                {
                                    ProgressCurrentFile++;
                                    UpdateEtas();

                                }
                            }
                            else if (e.Data.StartsWith("TOTALZIP="))
                            {
                                if (int.TryParse(e.Data.Replace("TOTALZIP=", ""), out var total))
                                {
                                    ShowArchiveProgressBar = true;
                                    ProgressCurrentFileInArchive = 0;
                                    ProgressTotalFilesInCurrentArchive = total;
                                    UpdateEtas();
                                }
                            }
                            else
                            {
                                outputFile.WriteLine(e.Data); // Write the output to the log file
                                ConsoleQueueEnqueue(e.Data);
                                Debug.WriteLine(e.Data);
                            }
                        }
                    };

                    process.Start();
                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine(); // Start asynchronous reading of the output
                    await process.WaitForExitAsync();
                }

            }
        }

        public async Task<DeviceResponse?> InitializeDeviceList()
        {
            if (!File.Exists(@".\backend\src\device_list.py"))
            {
                return null;
            }

            // Create a new process to run the CMD command
            using (var process = new Process())
            {
                _runningProcess = process;
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.Arguments = @$"/C .\python\python\python.exe {Path.GetFullPath(@".\backend\src\device_list.py")}";
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.WorkingDirectory = _pythonService.BackendDirectory;
                process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
                process.StartInfo.StandardErrorEncoding = Encoding.UTF8;

                var result = string.Empty;

                // Create a StreamWriter to write the output to a log file
                try
                {
                    using var outputFile = new StreamWriter(Path.Combine(_pythonService.LogsDirectory, "upscale.log"), append: false);
                    process.ErrorDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            //outputFile.WriteLine(e.Data); // Write the output to the log file
                            //ConsoleQueueEnqueue(e.Data);
                            Debug.WriteLine(e.Data);
                        }
                    };

                    process.OutputDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            result = e.Data;
                            Debug.WriteLine(e.Data);
                        }
                    };

                    process.Start();
                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine(); // Start asynchronous reading of the output
                    await process.WaitForExitAsync();

                    if (!string.IsNullOrEmpty(result))
                    {
                        return JsonConvert.DeserializeObject<DeviceResponse>(result);
                    }
                }
                catch (IOException) { }
            }

            return null;
        }

        public async Task<string> RunPythonPipList()
        {
            List<string> result = [];

            // Create a new process to run the CMD command
            using (var process = new Process())
            {
                _runningProcess = process;
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.Arguments = @$"/C .\python\python\python.exe -m pip list";
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.WorkingDirectory = _pythonService.BackendDirectory;
                process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
                process.StartInfo.StandardErrorEncoding = Encoding.UTF8;

                // Create a StreamWriter to write the output to a log file
                try
                {
                    process.ErrorDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            //outputFile.WriteLine(e.Data); // Write the output to the log file
                            //ConsoleQueueEnqueue(e.Data);
                            Debug.WriteLine(e.Data);
                        }
                    };

                    process.OutputDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            result.Add(e.Data);
                        }
                    };

                    process.Start();
                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine(); // Start asynchronous reading of the output
                    await process.WaitForExitAsync();
                }
                catch (IOException) { }
            }

            return string.Join("\n", result);
        }

        public async void ShowSettingsDialog()
        {
            var result = await ShowDialog.Handle(this);
        }

        private void UpdateEtas()
        {
            if (ProgressTotalFilesInCurrentArchive > 0)
            {
                _archiveEtaCalculator.Update(ProgressCurrentFileInArchive / (float)ProgressTotalFilesInCurrentArchive);
            }

            if (ProgressTotalFiles > 0)
            {
                _totalEtaCalculator.Update(ProgressCurrentFile / (float)ProgressTotalFiles);
            }

            this.RaisePropertyChanged(nameof(ArchiveEtr));
            this.RaisePropertyChanged(nameof(ArchiveEta));
            this.RaisePropertyChanged(nameof(TotalEtr));
            this.RaisePropertyChanged(nameof(TotalEta));
        }

        private void ConsoleQueueClear()
        {
            ConsoleQueue.Clear();
            this.RaisePropertyChanged(nameof(ConsoleText));
        }

        private void ConsoleQueueEnqueue(string value)
        {
            while (ConsoleQueue.Count > CONSOLE_QUEUE_CAPACITY)
            {
                ConsoleQueue.TryDequeue(out var _);
            }
            ConsoleQueue.Enqueue(value);
            this.RaisePropertyChanged(nameof(ConsoleText));
        }

        private void BackendSetupSubStatusQueueEnqueue(string value)
        {
            while (BackendSetupSubStatusQueue.Count > BACKEND_SETUP_SUB_STATUS_QUEUE_CAPACITY)
            {
                BackendSetupSubStatusQueue.TryDequeue(out var _);
            }
            BackendSetupSubStatusQueue.Enqueue(value);
            this.RaisePropertyChanged(nameof(BackendSetupSubStatusText));
        }

        public void ReadWorkflowFileToCurrentWorkflow(string fullPath)
        {
            if (!File.Exists(fullPath))
            {
                return;
            }

            var lines = File.ReadAllText(fullPath);
            var workflow = JsonConvert.DeserializeObject<UpscaleWorkflow>(lines, NewtonsoftJsonSuspensionDriver.Settings);
            if (workflow != null && CurrentWorkflow != null)
            {
                workflow.WorkflowIndex = CurrentWorkflow.WorkflowIndex;
                workflow.Vm = CurrentWorkflow.Vm;
                CurrentWorkflow = workflow;
            }
        }

        public void WriteCurrentWorkflowToFile(string fullPath)
        {
            var lines = JsonConvert.SerializeObject(CurrentWorkflow, NewtonsoftJsonSuspensionDriver.Settings);
            File.WriteAllText(fullPath, lines);
        }

        public async Task CheckAndExtractBackend()
        {
            await Task.Run(async () =>
            {
                IsExtractingBackend = true;

                if (!Directory.Exists(_pythonService.LogsDirectory))
                {
                    Directory.CreateDirectory(_pythonService.LogsDirectory);
                }

                if (!_pythonService.AreModelsInstalled())
                {
                    await DownloadModels();
                }

                if (!_pythonService.IsPythonInstalled() || !(await _pythonService.IsBackendUpdated()))
                {
                    // Download Python tgz
                    BackendSetupMainStatus = "Downloading Python Backend...";
                    var downloadUrl = _pythonService.BackendUrl;
                    var targetPath = Path.Join(_pythonService.PythonDirectory, "backend.7z");
                    if (Directory.Exists(_pythonService.PythonDirectory))
                    {
                        Directory.Delete(_pythonService.PythonDirectory, true);
                    }
                    Directory.CreateDirectory(_pythonService.PythonDirectory);
                    await Downloader.DownloadFileAsync(downloadUrl, targetPath, (progress) =>
                    {
                        BackendSetupMainStatus = $"Downloading Python Backend ({progress}%)...";
                    });

                    // Extract Python 7z
                    BackendSetupMainStatus = "Extracting Python Backend...";
                    _pythonService.Extract7z(targetPath, _pythonService.PythonDirectory);

                    Directory.Move(Path.Combine(_pythonService.PythonDirectory, "backend", "python"), Path.Combine(_pythonService.PythonDirectory, "python"));

                    using (StreamWriter sw = File.CreateText(_pythonService.PythonBackendVersionPath))
                    {
                        sw.WriteLine(_pythonService.BackendVersion);
                    }

                    Directory.Delete(Path.Combine(_pythonService.PythonDirectory, "backend"));
                    File.Delete(targetPath);
                }

                IsExtractingBackend = false;
            });

            var deviceResponse = await InitializeDeviceList();
            if (deviceResponse != null)
            {
                DeviceList = [.. deviceResponse.AllDevices.Select(d => d.Name)];
                SelectedDeviceIndex = deviceResponse.BestDevice;
            }
            else
            {
                SelectedDeviceIndex = 1; // default to first non cpu device
            }

            PythonPipList = await RunPythonPipList();
        }

        public async Task ReinstallBackend()
        {
            if (Directory.Exists(_pythonService.ModelsDirectory))
            {
                Directory.Delete(_pythonService.ModelsDirectory, true);
            }

            if (Directory.Exists(_pythonService.PythonDirectory))
            {
                Directory.Delete(_pythonService.PythonDirectory, true);
            }

            await CheckAndExtractBackend();
        }

        public async Task DownloadModels()
        {
            BackendSetupMainStatus = "Downloading MangaJaNai Models...";
            var download = "https://github.com/the-database/mangajanai/releases/download/1.0.0/MangaJaNai_V1_ModelsOnly.zip";
            var targetPath = Path.Join(_pythonService.ModelsDirectory, "mangajanai.zip");
            Directory.CreateDirectory(_pythonService.ModelsDirectory);
            await Downloader.DownloadFileAsync(download, targetPath, (progress) =>
            {
                BackendSetupMainStatus = $"Downloading MangaJaNai Models ({progress}%)...";
            });

            BackendSetupMainStatus = "Extracting MangaJaNai Models...";
            _pythonService.ExtractZip(targetPath, _pythonService.ModelsDirectory, (double progress) =>
            {
                BackendSetupMainStatus = $"Extracting MangaJaNai Models ({progress}%)...";
            });
            File.Delete(targetPath);

            BackendSetupMainStatus = "Downloading IllustrationJaNai V3denoise Models...";
            download = "https://github.com/the-database/MangaJaNai/releases/download/3.0.0/IllustrationJaNai_V3denoise.zip";
            targetPath = Path.Join(_pythonService.ModelsDirectory, "illustrationjanai.zip");
            await Downloader.DownloadFileAsync(download, targetPath, (progress) =>
            {
                BackendSetupMainStatus = $"Downloading IllustrationJaNai V3denoise Models ({progress}%)...";
            });

            BackendSetupMainStatus = "Extracting IllustrationJaNai V3denoise Models...";
            _pythonService.ExtractZip(targetPath, _pythonService.ModelsDirectory, (double progress) =>
            {
                BackendSetupMainStatus = $"Extracting IllustrationJaNai V3denoise Models ({progress}%)...";
            });
            File.Delete(targetPath);

            BackendSetupMainStatus = "Downloading IllustrationJaNai V3detail Models...";
            download = "https://github.com/the-database/MangaJaNai/releases/download/3.0.0/IllustrationJaNai_V3detail.zip";
            targetPath = Path.Join(_pythonService.ModelsDirectory, "illustrationjanai.zip");
            await Downloader.DownloadFileAsync(download, targetPath, (progress) =>
            {
                BackendSetupMainStatus = $"Downloading IllustrationJaNai V3detail Models ({progress}%)...";
            });

            BackendSetupMainStatus = "Extracting IllustrationJaNai V3detail Models...";
            _pythonService.ExtractZip(targetPath, _pythonService.ModelsDirectory, (double progress) =>
            {
                BackendSetupMainStatus = $"Extracting IllustrationJaNai V3detail Models ({progress}%)...";
            });
            File.Delete(targetPath);
        }



        public async Task<string[]> InstallUpdatePythonDependencies()
        {
            var cmd = _pythonService.InstallUpdatePythonDependenciesCommand;
            Debug.WriteLine(cmd);

            // Create a new process to run the CMD command
            using (var process = new Process())
            {
                process.StartInfo.FileName = "cmd.exe";
                process.StartInfo.Arguments = @$"/C {cmd}";
                process.StartInfo.RedirectStandardOutput = true;
                process.StartInfo.RedirectStandardError = true;
                process.StartInfo.UseShellExecute = false;
                process.StartInfo.CreateNoWindow = true;
                process.StartInfo.StandardOutputEncoding = Encoding.UTF8;
                process.StartInfo.StandardErrorEncoding = Encoding.UTF8;
                process.StartInfo.WorkingDirectory = _pythonService.BackendDirectory;

                var result = string.Empty;
                using var outputFile = new StreamWriter(Path.Combine(_pythonService.LogsDirectory, "install.log"));
                outputFile.WriteLine($"Working Directory: {process.StartInfo.WorkingDirectory}");
                outputFile.WriteLine($"Run Command: {cmd}");
                // Create a StreamWriter to write the output to a log file
                try
                {
                    //using var outputFile = new StreamWriter("error.log", append: true);
                    process.ErrorDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            //Debug.WriteLine($"STDERR = {e.Data}");
                            outputFile.WriteLine(e.Data);
                            BackendSetupSubStatusQueueEnqueue(e.Data);
                        }
                    };

                    process.OutputDataReceived += (sender, e) =>
                    {
                        if (!string.IsNullOrEmpty(e.Data))
                        {
                            result = e.Data;
                            outputFile.WriteLine(e.Data);
                            //Debug.WriteLine($"STDOUT = {e.Data}");
                            BackendSetupSubStatusQueueEnqueue(e.Data);
                        }
                    };

                    process.Start();
                    process.BeginOutputReadLine();
                    process.BeginErrorReadLine(); // Start asynchronous reading of the output
                    await process.WaitForExitAsync();
                }
                catch (IOException) { }
            }

            return [];
        }

        public void CheckAndDoBackup()
        {
            Task.Run(() =>
            {
                try
                {
                    if (!File.Exists(_pythonService.AppStatePath))
                        return;

                    var files = Directory.EnumerateFiles(_pythonService.AppStateFolder)
                        .Where(f =>
                        {
                            var name = Path.GetFileName(f);
                            return name.StartsWith("autobackup_") &&
                                   name.EndsWith(_pythonService.AppStateFilename);
                        })
                        .OrderByDescending(f => f)
                        .ToList();

                    var latestBackup = files.FirstOrDefault();
                    if (latestBackup is not null &&
                        FilesAreEqual(_pythonService.AppStatePath, latestBackup))
                    {
                        return;
                    }

                    var backupName =
                        $"autobackup_{DateTime.Now:yyyyMMdd-HHmmss}_{_pythonService.AppStateFilename}";
                    var backupPath = Path.Combine(_pythonService.AppStateFolder, backupName);

                    File.Copy(_pythonService.AppStatePath, backupPath);

                    files.Insert(0, backupPath);

                    const int maxBackups = 10;
                    if (files.Count > maxBackups)
                    {
                        foreach (var old in files.Skip(maxBackups))
                        {
                            try { File.Delete(old); }
                            catch { }
                        }
                    }
                }
                catch
                {
                }
            });
        }

        private static bool FilesAreEqual(string path1, string path2)
        {
            var info1 = new FileInfo(path1);
            var info2 = new FileInfo(path2);
            if (info1.Length != info2.Length)
                return false;

            var bytes1 = File.ReadAllBytes(path1);
            var bytes2 = File.ReadAllBytes(path2);

            return bytes1.AsSpan().SequenceEqual(bytes2);
        }

        public void ResetCurrentWorkflow()
        {
            if (CurrentWorkflow != null)
            {
                var workflow = JsonConvert.DeserializeObject<UpscaleWorkflow>(DEFAULT_WORKFLOW, NewtonsoftJsonSuspensionDriver.Settings);
                var workflowIndex = CurrentWorkflow.WorkflowIndex;
                var workflowName = $"Custom Workflow {workflowIndex}";

                if (workflow != null)
                {
                    var defaultWorkflow = new UpscaleWorkflow
                    {
                        Vm = this,
                        WorkflowIndex = workflowIndex,
                        WorkflowName = workflowName,
                        Chains = workflow.Chains
                    };

                    foreach (var chain in defaultWorkflow.Chains)
                    {
                        chain.Vm = this;
                    }

                    CurrentWorkflow = defaultWorkflow;
                }
            }
        }

        public async Task<IEnumerable<object>> PopulateDevicesAsync(string? searchText, CancellationToken cancellationToken)
        {
            try
            {
                var requestUrl = $"https://animejan.ai/mangajanai/api/search?q={Uri.EscapeDataString(searchText?.Trim() ?? "")}&p=0&s=4";
                if (string.IsNullOrWhiteSpace(searchText))
                {
                    requestUrl = $"https://animejan.ai/mangajanai/api/top";
                }
                var response = await client.GetStringAsync(requestUrl, cancellationToken);
                var devices = JsonConvert.DeserializeObject<List<ReaderDevice>>(response, NewtonsoftJsonSuspensionDriver.Settings);
                if (devices != null)
                {
                    foreach (var device in devices)
                    {
                        DisplayDeviceMap[device.ToString()] = device;

                    }
                    return devices.ToList();
                }
            }
            catch (Exception ex)
            {
                Debug.WriteLine(ex);
            }

            return [];
        }

        public async Task CheckForUpdates()
        {
            try
            {
                if (_updateManagerService.IsInstalled)
                {
                    await Task.Run(async () =>
                    {
                        _update = await _updateManagerService.CheckForUpdatesAsync().ConfigureAwait(true);
                    });

                    UpdateStatus();

                    if (AutoUpdateEnabled)
                    {
                        await DownloadUpdate();
                    }
                }
            }
            catch (Exception ex)
            {
                UpdateStatusText = $"Check for update failed: {ex.Message}";
            }
        }

        public async Task DownloadUpdate()
        {
            try
            {
                if (_update != null)
                {
                    ShowDownloadButton = false;
                    await _updateManagerService.DownloadUpdatesAsync(_update, Progress).ConfigureAwait(true);
                    UpdateStatus();
                }
            }
            catch
            {

            }
        }

        public void ApplyUpdate()
        {
            if (_update != null)
            {
                ShowApplyButton = false;
                _updateManagerService.ApplyUpdatesAndRestart(_update);
            }
        }

        private void UpdateStatus()
        {
            ShowDownloadButton = false;
            ShowApplyButton = false;
            ShowCheckUpdateButton = true;

            if (_update != null)
            {
                UpdateStatusText = $"Update is available: {_update.TargetFullRelease.Version}";
                ShowDownloadButton = true;
                ShowCheckUpdateButton = false;

                if (_updateManagerService.IsUpdatePendingRestart)
                {
                    UpdateStatusText = $"Update ready, pending restart to install version: {_update.TargetFullRelease.Version}";
                    ShowDownloadButton = false;
                    ShowApplyButton = true;
                    ShowCheckUpdateButton = false;
                }
                else
                {
                }
            }
            else
            {
                UpdateStatusText = "No updates found";
            }
        }

        private void Progress(int percent)
        {
            UpdateStatusText = $"Downloading update {_update?.TargetFullRelease.Version} ({percent}%)...";
        }


        public async void OpenModelsDirectory()
        {
            await Task.Run(() =>
            {
                Process.Start("explorer.exe", _pythonService.ModelsDirectory);
            });
        }
    }

    [DataContract]
    public class UpscaleWorkflow : ReactiveObject
    {
        public UpscaleWorkflow()
        {
            var g1 = this.WhenAnyValue
            (
                x => x.InputFilePath,
                x => x.OutputFilename,
                x => x.InputFolderPath,
                x => x.OutputFolderPath,
                x => x.SelectedTabIndex,
                x => x.DisplayDevice,
                x => x.DisplayPortraitSelected
            );

            var g2 = this.WhenAnyValue
            (
                x => x.UpscaleImages,
                x => x.UpscaleArchives,
                x => x.OverwriteExistingFiles,
                x => x.WebpSelected,
                x => x.PngSelected,
                x => x.JpegSelected,
                x => x.AvifSelected
            );

            var g3 = this.WhenAnyValue
            (
                x => x.ModeFitToDisplaySelected,
                x => x.ModeHeightSelected,
                x => x.ModeWidthSelected,
                x => x.ResizeHeightAfterUpscale,
                x => x.ResizeWidthAfterUpscale
            );

            g1.CombineLatest(g2).CombineLatest(g3).Subscribe(x =>
            {
                Validate();
            });

            this.WhenAnyValue(x => x.Vm).Subscribe(x =>
            {
                sub?.Dispose();
                sub = Vm.WhenAnyValue(
                    x => x.SelectedWorkflowIndex,
                    x => x.RequestShowAppSettings
                    ).Subscribe(x =>
                    {
                        this.RaisePropertyChanged(nameof(ActiveWorkflow));
                        Vm?.RaisePropertyChanged("Workflows");
                    });
            });

            this.WhenAnyValue(x => x.InputFilePath).Subscribe(x =>
            {
                if (string.IsNullOrWhiteSpace(OutputFolderPath) && !string.IsNullOrWhiteSpace(InputFilePath))
                {
                    try
                    {
                        OutputFolderPath = Directory.GetParent(InputFilePath)?.ToString() ?? "";
                    }
                    catch (Exception)
                    {

                    }
                }
            });

            this.WhenAnyValue(x => x.InputFolderPath).Subscribe(x =>
            {
                if (string.IsNullOrWhiteSpace(OutputFolderPath) && !string.IsNullOrWhiteSpace(InputFolderPath))
                {
                    try
                    {
                        OutputFolderPath = $"{InputFolderPath} mangajanai";
                    }
                    catch (Exception)
                    {

                    }
                }
            });
        }

        private IDisposable? sub;

        private MainWindowViewModel? _vm;
        public MainWindowViewModel? Vm
        {
            get => _vm;
            set => this.RaiseAndSetIfChanged(ref _vm, value);
        }

        private string _workflowName;
        [DataMember]
        public string WorkflowName
        {
            get => _workflowName;
            set => this.RaiseAndSetIfChanged(ref _workflowName, value);
        }

        private int _workflowIndex;
        [DataMember]
        public int WorkflowIndex
        {
            get => _workflowIndex;
            set => this.RaiseAndSetIfChanged(ref _workflowIndex, value);

        }

        public string WorkflowIcon => $"Numeric{WorkflowIndex}Circle";

        public bool ActiveWorkflow
        {
            get
            {
                Debug.WriteLine($"ActiveWorkflow {WorkflowIndex} == {Vm?.SelectedWorkflowIndex}; {Vm == null}");
                return WorkflowIndex == Vm?.SelectedWorkflowIndex && (!Vm?.ShowAppSettings ?? false);
            }

        }

        public bool IsDefaultWorkflow => WorkflowIndex == 0;

        private int _selectedTabIndex;
        [DataMember]
        public int SelectedTabIndex
        {
            get => _selectedTabIndex;
            set
            {
                if (_selectedTabIndex != value)
                {
                    this.RaiseAndSetIfChanged(ref _selectedTabIndex, value);
                    Vm?.RaisePropertyChanged(nameof(Vm.InputStatusText));  // TODO
                }
            }
        }

        private string _inputFilePath = string.Empty;
        [DataMember]
        public string InputFilePath
        {
            get => _inputFilePath;
            set
            {
                this.RaiseAndSetIfChanged(ref _inputFilePath, value);
                Vm?.RaisePropertyChanged(nameof(Vm.InputStatusText));  // TODO
            }
        }

        private string _inputFolderPath = string.Empty;
        [DataMember]
        public string InputFolderPath
        {
            get => _inputFolderPath;
            set
            {
                this.RaiseAndSetIfChanged(ref _inputFolderPath, value);
                Vm?.RaisePropertyChanged(nameof(Vm.InputStatusText)); // TODO
            }
        }

        private string _outputFilename = "%filename%-mangajanai";
        [DataMember]
        public string OutputFilename
        {
            get => _outputFilename;
            set => this.RaiseAndSetIfChanged(ref _outputFilename, value);
        }

        private string _outputFolderPath = string.Empty;
        [DataMember]
        public string OutputFolderPath
        {
            get => _outputFolderPath;
            set => this.RaiseAndSetIfChanged(ref _outputFolderPath, value);
        }

        private bool _overwriteExistingFiles = false;
        [DataMember]
        public bool OverwriteExistingFiles
        {
            get => _overwriteExistingFiles;
            set => this.RaiseAndSetIfChanged(ref _overwriteExistingFiles, value);
        }

        private bool _upscaleImages = false;
        [DataMember]
        public bool UpscaleImages
        {
            get => _upscaleImages;
            set => this.RaiseAndSetIfChanged(ref _upscaleImages, value);
        }

        private bool _upscaleArchives = true;
        [DataMember]
        public bool UpscaleArchives
        {
            get => _upscaleArchives;
            set => this.RaiseAndSetIfChanged(ref _upscaleArchives, value);
        }

        private int? _resizeHeightAfterUpscale = 2160;
        [DataMember]
        public int? ResizeHeightAfterUpscale
        {
            get => _resizeHeightAfterUpscale;
            set => this.RaiseAndSetIfChanged(ref _resizeHeightAfterUpscale, value ?? 2160);
        }

        private int? _resizeWidthAfterUpscale = 3840;
        [DataMember]
        public int? ResizeWidthAfterUpscale
        {
            get => _resizeWidthAfterUpscale;
            set => this.RaiseAndSetIfChanged(ref _resizeWidthAfterUpscale, value ?? 3840);
        }

        private bool _webpSelected = true;
        [DataMember]
        public bool WebpSelected
        {
            get => _webpSelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _webpSelected, value);
                this.RaisePropertyChanged(nameof(ShowUseLosslessCompression));
                this.RaisePropertyChanged(nameof(ShowLossyCompressionQuality));
            }
        }

        private bool _avifSelected = false;
        [DataMember]
        public bool AvifSelected
        {
            get => _avifSelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _avifSelected, value);
                this.RaisePropertyChanged(nameof(ShowLossyCompressionQuality));
                this.RaisePropertyChanged(nameof(ShowUseLosslessCompression));
            }
        }

        private bool _pngSelected = false;
        [DataMember]
        public bool PngSelected
        {
            get => _pngSelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _pngSelected, value);
            }
        }

        private bool _jpegSelected = false;
        [DataMember]
        public bool JpegSelected
        {
            get => _jpegSelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _jpegSelected, value);
                this.RaisePropertyChanged(nameof(ShowLossyCompressionQuality));
            }
        }

        public string ImageFormat => WebpSelected ? "webp" : PngSelected ? "png" : AvifSelected ? "avif" : "jpg";

        public bool ShowUseLosslessCompression => WebpSelected;

        private bool _useLosslessCompression = false;
        [DataMember]
        public bool UseLosslessCompression
        {
            get => _useLosslessCompression;
            set
            {
                this.RaiseAndSetIfChanged(ref _useLosslessCompression, value);
                this.RaisePropertyChanged(nameof(ShowLossyCompressionQuality));
            }
        }

        public bool ShowLossyCompressionQuality => JpegSelected || (WebpSelected && !UseLosslessCompression) || AvifSelected;

        private int? _lossyCompressionQuality = 80;
        [DataMember]
        public int? LossyCompressionQuality
        {
            get => _lossyCompressionQuality;
            set => this.RaiseAndSetIfChanged(ref _lossyCompressionQuality, value ?? 80);
        }

        private bool _showLossySettings = true;
        [DataMember]
        public bool ShowLossySettings
        {
            get => _showLossySettings;
            set => this.RaiseAndSetIfChanged(ref _showLossySettings, value);
        }

        private bool _modeScaleSelected = true;
        [DataMember]
        public bool ModeScaleSelected
        {
            get => _modeScaleSelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _modeScaleSelected, value);
            }
        }

        private int _upscaleScaleFactor = 4;
        [DataMember]
        public int UpscaleScaleFactor
        {
            get => _upscaleScaleFactor;
            set
            {
                this.RaiseAndSetIfChanged(ref _upscaleScaleFactor, value);
                this.RaisePropertyChanged(nameof(Is1x));
                this.RaisePropertyChanged(nameof(Is2x));
                this.RaisePropertyChanged(nameof(Is3x));
                this.RaisePropertyChanged(nameof(Is4x));
            }
        }

        public bool Is1x => UpscaleScaleFactor == 1;
        public bool Is2x => UpscaleScaleFactor == 2;
        public bool Is3x => UpscaleScaleFactor == 3;
        public bool Is4x => UpscaleScaleFactor == 4;


        public void SetUpscaleScaleFactor(int scaleFactor)
        {
            UpscaleScaleFactor = scaleFactor;
        }

        private bool _modeWidthSelected = false;
        [DataMember]
        public bool ModeWidthSelected
        {
            get => _modeWidthSelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _modeWidthSelected, value);
            }
        }

        private bool _modeHeightSelected = false;
        [DataMember]
        public bool ModeHeightSelected
        {
            get => _modeHeightSelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _modeHeightSelected, value);
            }
        }

        private bool _modeFitToDisplaySelected = false;
        [DataMember]
        public bool ModeFitToDisplaySelected
        {
            get => _modeFitToDisplaySelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _modeFitToDisplaySelected, value);
            }
        }

        private string _displayDevice;
        [DataMember]
        public string DisplayDevice
        {
            get => _displayDevice;
            set
            {
                this.RaiseAndSetIfChanged(ref _displayDevice, value);
                this.RaisePropertyChanged(nameof(DisplayDeviceWidth));
                this.RaisePropertyChanged(nameof(DisplayDeviceHeight));
            }
        }

        [DataMember]
        public int DisplayDeviceWidth
        {
            get
            {
                if (Vm != null && DisplayDevice != null)
                {
                    Vm.DisplayDeviceMap.TryGetValue(DisplayDevice, out var displayDevice);
                    if (displayDevice != null)
                    {
                        return DisplayPortraitSelected ? displayDevice.Width : displayDevice.Height;
                    }
                }

                return 0;
            }
        }

        [DataMember]
        public int DisplayDeviceHeight
        {
            get
            {
                if (Vm != null && DisplayDevice != null)
                {
                    Vm.DisplayDeviceMap.TryGetValue(DisplayDevice, out var displayDevice);
                    if (displayDevice != null)
                    {
                        return DisplayPortraitSelected ? displayDevice.Height : displayDevice.Width;
                    }
                }

                return 0;
            }
        }

        private bool _displayPortraitSelected = true;
        [DataMember]
        public bool DisplayPortraitSelected
        {
            get => _displayPortraitSelected;
            set
            {
                this.RaiseAndSetIfChanged(ref _displayPortraitSelected, value);
                this.RaisePropertyChanged(nameof(DisplayDeviceWidth));
                this.RaisePropertyChanged(nameof(DisplayDeviceHeight));
            }
        }

        private bool _showAdvancedSettings = false;
        [DataMember]
        public bool ShowAdvancedSettings
        {
            get => _showAdvancedSettings;
            set => this.RaiseAndSetIfChanged(ref _showAdvancedSettings, value);
        }

        private int _grayscaleDetectionThreshold = 12;
        [DataMember]
        public int GrayscaleDetectionThreshold
        {
            get => _grayscaleDetectionThreshold;
            set => this.RaiseAndSetIfChanged(ref _grayscaleDetectionThreshold, value);
        }

        private AvaloniaList<UpscaleChain> _chains;
        [DataMember]
        public AvaloniaList<UpscaleChain> Chains
        {
            get => _chains;
            set => this.RaiseAndSetIfChanged(ref _chains, value);
        }

        private bool _valid = false;
        [IgnoreDataMember]
        public bool Valid
        {
            get => _valid;
            set
            {
                this.RaiseAndSetIfChanged(ref _valid, value);
                if (Vm != null)
                {
                    Vm.RaisePropertyChanged(nameof(Vm.UpscaleEnabled));  // TODO
                    Vm.RaisePropertyChanged(nameof(Vm.LeftStatus));  // TODO
                }
            }
        }

        public void SetWebpSelected()
        {
            WebpSelected = true;
            PngSelected = false;
            JpegSelected = false;
            AvifSelected = false;
        }

        public void SetPngSelected()
        {
            PngSelected = true;
            WebpSelected = false;
            JpegSelected = false;
            AvifSelected = false;
        }

        public void SetJpegSelected()
        {
            JpegSelected = true;
            WebpSelected = false;
            PngSelected = false;
            AvifSelected = false;
        }

        public void SetAvifSelected()
        {
            AvifSelected = true;
            JpegSelected = false;
            WebpSelected = false;
            PngSelected = false;
        }

        public void SetModeScaleSelected()
        {
            ModeScaleSelected = true;
            ModeWidthSelected = false;
            ModeHeightSelected = false;
            ModeFitToDisplaySelected = false;
        }

        public void SetModeWidthSelected()
        {
            ModeWidthSelected = true;
            ModeScaleSelected = false;
            ModeHeightSelected = false;
            ModeFitToDisplaySelected = false;
        }

        public void SetModeHeightSelected()
        {
            ModeHeightSelected = true;
            ModeScaleSelected = false;
            ModeWidthSelected = false;
            ModeFitToDisplaySelected = false;
        }

        public void SetModeFitToDisplaySelected()
        {
            ModeFitToDisplaySelected = true;
            ModeHeightSelected = false;
            ModeWidthSelected = false;
            ModeScaleSelected = false;
        }

        public void Validate()
        {
            var valid = true;
            var validationText = new List<string>();
            if (SelectedTabIndex == 0)
            {

                if (string.IsNullOrWhiteSpace(InputFilePath))
                {
                    valid = false;
                    validationText.Add("Input File is required.");
                }
                else if (!File.Exists(InputFilePath))
                {
                    valid = false;
                    validationText.Add("Input File does not exist.");
                }

            }
            else
            {
                if (string.IsNullOrWhiteSpace(InputFolderPath))
                {
                    valid = false;
                    validationText.Add("Input Folder is required.");
                }
                else if (!Directory.Exists(InputFolderPath))
                {
                    valid = false;
                    validationText.Add("Input Folder does not exist.");
                }
            }

            if (string.IsNullOrWhiteSpace(OutputFilename))
            {
                valid = false;
                validationText.Add("Output Filename is required.");
            }

            if (string.IsNullOrWhiteSpace(OutputFolderPath))
            {
                valid = false;
                validationText.Add("Output Folder is required.");
            }

            if (ModeHeightSelected && ResizeHeightAfterUpscale == 0)
            {
                valid = false;
                validationText.Add("Output Height is invalid. Enter a height larger than 0.");
            }

            if (ModeWidthSelected && ResizeWidthAfterUpscale == 0)
            {
                valid = false;
                validationText.Add("Output Width is invalid. Enter a width larger than 0.");
            }

            if (ModeFitToDisplaySelected && (DisplayDeviceWidth == 0 || DisplayDeviceHeight == 0))
            {
                valid = false;
                validationText.Add("Tablet Device or Display is invalid. Please make a selection from the list of options.");
            }

            Valid = valid;

            if (Vm != null)
            {
                // TODO
                Vm.CheckInputs();
                if (Vm?.ProgressTotalFiles == 0)
                {
                    Valid = false;
                    validationText.Add($"{Vm?.InputStatusText} selected for upscaling. At least one file must be selected.");
                }
                Vm.ValidationText = string.Join("\n", validationText);
            }
        }
    }

    [DataContract]
    public class UpscaleChain : ReactiveObject
    {
        IPythonService _pythonService;

        public UpscaleChain(IPythonService? pythonService = null)
        {
            _pythonService = pythonService ?? Locator.Current.GetService<IPythonService>()!;

            this.WhenAnyValue(x => x.Vm).Subscribe(x =>
            {
                sub?.Dispose();
                sub = Vm.WhenAnyValue(
                    x => x.IsExtractingBackend
                    ).Subscribe(x =>
                    {
                        this.RaisePropertyChanged(nameof(AllModels));
                        this.RaisePropertyChanged(nameof(ModelFilePath));
                    });
            });

            this.RaisePropertyChanged(nameof(AllModels));
            this.RaisePropertyChanged(nameof(ModelFilePath));
        }

        private IDisposable? sub;

        private MainWindowViewModel? _vm;
        public MainWindowViewModel? Vm
        {
            get => _vm;
            set => this.RaiseAndSetIfChanged(ref _vm, value);
        }

        private string _chainNumber = string.Empty;
        [DataMember]
        public string ChainNumber
        {
            get => _chainNumber;
            set => this.RaiseAndSetIfChanged(ref _chainNumber, value);
        }

        private string _minResolution = "0x0";
        [DataMember]
        public string MinResolution
        {
            get => _minResolution;
            set => this.RaiseAndSetIfChanged(ref _minResolution, value);
        }

        private string _maxResolution = "0x0";
        [DataMember]
        public string MaxResolution
        {
            get => _maxResolution;
            set => this.RaiseAndSetIfChanged(ref _maxResolution, value);
        }

        private bool _isGrayscale = false;
        [DataMember]
        public bool IsGrayscale
        {
            get => _isGrayscale;
            set => this.RaiseAndSetIfChanged(ref _isGrayscale, value);
        }

        private bool _isColor = false;
        [DataMember]
        public bool IsColor
        {
            get => _isColor;
            set => this.RaiseAndSetIfChanged(ref _isColor, value);
        }

        private int? _minScaleFactor = 0;
        [DataMember]
        public int? MinScaleFactor
        {
            get => _minScaleFactor;
            set => this.RaiseAndSetIfChanged(ref _minScaleFactor, value ?? 0);
        }

        private int? _maxScaleFactor = 0;
        [DataMember]
        public int? MaxScaleFactor
        {
            get => _maxScaleFactor;
            set => this.RaiseAndSetIfChanged(ref _maxScaleFactor, value ?? 0);
        }

        private string _modelFilePath = string.Empty;
        [DataMember]
        public string ModelFilePath
        {
            get => _modelFilePath;
            set => this.RaiseAndSetIfChanged(ref _modelFilePath, value);
        }

        private string _modelTileSize = "Auto (Estimate)";
        [DataMember]
        public string ModelTileSize
        {
            get => _modelTileSize;
            set => this.RaiseAndSetIfChanged(ref _modelTileSize, value);
        }

        private bool _autoAdjustLevels = false;
        [DataMember]
        public bool AutoAdjustLevels
        {
            get => _autoAdjustLevels;
            set => this.RaiseAndSetIfChanged(ref _autoAdjustLevels, value);
        }

        private int? _resizeHeightBeforeUpscale = 0;
        [DataMember]
        public int? ResizeHeightBeforeUpscale
        {
            get => _resizeHeightBeforeUpscale;
            set => this.RaiseAndSetIfChanged(ref _resizeHeightBeforeUpscale, value ?? 0);
        }

        private int? _resizeWidthBeforeUpscale = 0;
        [DataMember]
        public int? ResizeWidthBeforeUpscale
        {
            get => _resizeWidthBeforeUpscale;
            set => this.RaiseAndSetIfChanged(ref _resizeWidthBeforeUpscale, value ?? 0);
        }

        private double? _resizeFactorBeforeUpscale = 100;
        [DataMember]
        public double? ResizeFactorBeforeUpscale
        {
            get => _resizeFactorBeforeUpscale;
            set => this.RaiseAndSetIfChanged(ref _resizeFactorBeforeUpscale, value ?? 100);
        }

        public AvaloniaList<string> AllModels => _pythonService.AllModels;

        private string[] _tileSizes = [
    "Auto (Estimate)",
            "Maximum",
            "No Tiling",
            "128",
            "192",
            "256",
            "384",
            "512",
            "768",
            "1024",
            "2048",
            "4096"];

        public string[] TileSizes
        {
            get => _tileSizes;
            set => this.RaiseAndSetIfChanged(ref _tileSizes, value);
        }
    }

    // TODO refactor into separate file
    public class ReaderDevice
    {
        public string Name { get; set; } = default!;
        public string Brand { get; set; } = default!;
        public string Year { get; set; } = default!;
        public int Width { get; set; } = default!;
        public int Height { get; set; } = default!;

        public override string ToString()
        {
            List<string> parts = [];

            if (!string.IsNullOrWhiteSpace(Brand))
            {
                parts.Add(Brand);
            }

            if (!string.IsNullOrWhiteSpace(Name))
            {
                parts.Add(Name);
            }

            if (!string.IsNullOrWhiteSpace(Year))
            {
                parts.Add($"({Year})");
            }

            return string.Join(" ", parts);
        }
    }

    public class DeviceResponse
    {
        [JsonProperty("all_devices")]
        public List<AcceleratorDevice> AllDevices { get; set; } = [];

        [JsonProperty("best_device")]
        public int BestDevice { get; set; }
    }

    public class AcceleratorDevice
    {
        [JsonProperty("type")]
        public string Type { get; set; } = string.Empty;

        [JsonProperty("index")]
        public int Index { get; set; }

        [JsonProperty("name")]
        public string Name { get; set; } = string.Empty;

        [JsonProperty("device_string")]
        public string DeviceString { get; set; } = string.Empty;

        [JsonProperty("supports_fp16")]
        public bool SupportsFp16 { get; set; }

        [JsonProperty("supports_bf16")]
        public bool SupportsBf16 { get; set; }

        [JsonProperty("memory_total")]
        public long? MemoryTotal { get; set; }

        [JsonProperty("memory_free")]
        public long? MemoryFree { get; set; }
    }
}

================================================
FILE: MangaJaNaiConverterGui/ViewModels/ViewModelBase.cs
================================================
using ReactiveUI;

namespace MangaJaNaiConverterGui.ViewModels
{
    //[DataContract]
    public class ViewModelBase : ReactiveObject
    {
        //private bool _autoUpdate;
        //[DataMember]
        //public bool AutoUpdateEnabled
        //{
        //    get => _autoUpdate;
        //    set => this.RaiseAndSetIfChanged(ref _autoUpdate, value);
        //}
    }
}

================================================
FILE: MangaJaNaiConverterGui/Views/MainWindow.axaml
================================================
<Window xmlns="https://github.com/avaloniaui"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:vm="using:MangaJaNaiConverterGui.ViewModels"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
        xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
        mc:Ignorable="d" 
        xmlns:materialIcons="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
        xmlns:hypertext="clr-namespace:HyperText.Avalonia.Controls;assembly=HyperText.Avalonia"
        xmlns:ui="using:FluentAvalonia.UI.Controls"
        xmlns:uip="using:FluentAvalonia.UI.Controls.Primitives"
        x:Class="MangaJaNaiConverterGui.Views.MainWindow"
        x:DataType="vm:MainWindowViewModel"
        Icon="/Assets/logo.ico"
        Title="MangaJaNaiConverterGui">

  <Window.Styles>
    <Style Selector="TabControl[TabStripPlacement=Top]">
      <!-- Override styled behaviour -->
      <Setter Property="Padding" Value="0"/>
    </Style>
    <Style Selector="TextBlock">
      <Setter Property="VerticalAlignment" Value="Center"/>
    </Style>
    <Style Selector="TextBox">
      <Setter Property="VerticalAlignment" Value="Center"/>
    </Style>
    <Style Selector="Border.border">
      
      <Setter Property="Margin" Value="0,10,0,0" />
      <Setter Property="CornerRadius" Value="5" />
      <Setter Property="BorderBrush" Value="#33888888" />
      <Setter Property="BorderThickness" Value="1" />
      <Setter Property="Padding" Value="10" />
    </Style>
    <Style Selector="Button.active">
      <Setter Property="Background" Value="{DynamicResource SystemAccentColor }" />
      <Style Selector="^:pointerover">
        <Style Selector="^ /template/ ContentPresenter#PART_ContentPresenter">
          <Setter Property="Background" Value="{DynamicResource SystemAccentColor}" />
          <Setter Property="BorderBrush" Value="{DynamicResource SystemAccentColor}" />
        </Style>
      </Style>
    </Style>
  </Window.Styles>
  
    <Design.DataContext>
        <!-- This only sets the DataContext for the previewer in an IDE,
             to set the actual DataContext for runtime, set the DataContext property in code (look at App.axaml.cs) -->
        <vm:MainWindowViewModel/>
    </Design.DataContext>

  <Grid>
    <!-- Main Content -->
    <Grid ColumnDefinitions="300,*" RowDefinitions="*" IsVisible="{Binding !IsExtractingBackend}">
      <DockPanel Grid.Column="0" Background="#33000000">

        <StackPanel DockPanel.Dock="Bottom">
          <TextBlock Margin="0">

          </TextBlock>
          <Button FontSize="13" Padding="10" Classes.active="{Binding ShowAppSettings}" Width="300" HorizontalContentAlignment="Left" Command="{Binding HandleAppSettingsSelected}">
            <StackPanel Orientation="Horizontal">
              <materialIcons:MaterialIcon Kind="Gear" VerticalAlignment="Center" />
              <TextBlock Margin="5,0,5,0" VerticalAlignment="Center">App Settings</TextBlock>
            </StackPanel>
          </Button>
        </StackPanel>

        <StackPanel>
          <TextBlock Margin="10">
            Default Workflows
          </TextBlock>

          <Button FontSize="13" Padding="10" Width="300" HorizontalContentAlignment="Left" Command="{Binding HandleWorkflowSelected}" CommandParameter="0"
                  Classes.active="{Binding Workflows[0].ActiveWorkflow}">
            <StackPanel Orientation="Horizontal">
              <materialIcons:MaterialIcon Kind="Book" VerticalAlignment="Center" />
              <TextBlock Margin="5,0,5,0" VerticalAlignment="Center" Text="{Binding Workflows[0].WorkflowName}" />
            </StackPanel>
          </Button>

          <StackPanel Orientation="Horizontal" Margin="10" ToolTip.Tip="Custom workflows provide convenient access to save and load preset settings for upscaling." ToolTip.ShowDelay="200">
            <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">
              Custom Workflows
            </TextBlock>
            <materialIcons:MaterialIcon Kind="QuestionMarkCircle" VerticalAlignment="Center" Opacity="0.5" />
          </StackPanel>

          <ItemsControl ItemsSource="{Binding CustomWorkflows}">
            <ItemsControl.ItemTemplate>
              <DataTemplate x:DataType="vm:UpscaleWorkflow">
                <StackPanel>
                  <Button FontSize="13" Padding="10" Width="300" HorizontalContentAlignment="Left"
                          ClickMode="Press"
                          Classes.active="{Binding ActiveWorkflow}"
                          Command="{Binding $parent[ItemsControl].((vm:MainWindowViewModel)DataContext).HandleWorkflowSelected}"
                          CommandParameter="{Binding WorkflowIndex}">
                    <StackPanel Orientation="Horizontal">
                      <materialIcons:MaterialIcon Kind="{Binding WorkflowIcon}" VerticalAlignment="Center" />
                      <TextBlock Margin="5,0,5,0" VerticalAlignment="Center" Text="{Binding WorkflowName}" />
                    </StackPanel>
                  </Button>
                </StackPanel>
              </DataTemplate>
            </ItemsControl.ItemTemplate>
          </ItemsControl>
        </StackPanel>

      </DockPanel>

      <DockPanel IsVisible="{Binding ShowMainForm}" Grid.Column="1">

        <StackPanel DockPanel.Dock="Bottom">
          <StackPanel Orientation="Horizontal">
            <Button Margin="20,10,0,10" FontWeight="Bold" Background="Green" IsEnabled="{Binding UpscaleEnabled}" Command="{Binding RunUpscale}">
              <StackPanel Orientation="Horizontal">
                <materialIcons:MaterialIcon Kind="PlayCircle" />
                <TextBlock Margin="5,0,0,0">Upscale</TextBlock>
              </StackPanel>

            </Button>
            <Button Margin="20,10,0,10" FontWeight="Bold" Background="Red" IsEnabled="{Binding Upscaling}" Command="{Binding CancelUpscale}">
              <StackPanel Orientation="Horizontal">
                <materialIcons:MaterialIcon Kind="StopCircle" />
                <TextBlock Margin="5,0,0,0">Cancel</TextBlock>
              </StackPanel>
            </Button>
          </StackPanel>

          <StackPanel IsVisible="{Binding ShowConsole}" >
            <DockPanel>
              <TextBlock DockPanel.Dock="Left" Margin="20,10,0,0" FontWeight="Bold" Text="Console"></TextBlock>
              <ToggleButton DockPanel.Dock="Right" Margin="0,0,20,0" IsChecked="{Binding !ShowConsole}">
                <materialIcons:MaterialIcon Kind="Close" />
              </ToggleButton>
              <Rectangle/>
            </DockPanel>

            <ScrollViewer Margin="0,10,0,0" Background="#111111" Height="450" HorizontalAlignment="Stretch" HorizontalScrollBarVisibility="Auto" Foreground="Gray"  PropertyChanged="ConsoleScrollViewer_PropertyChanged">
              <SelectableTextBlock Margin="20" Text="{Binding ConsoleText}" FontFamily="Consolas" PropertyChanged="ConsoleTextBlock_PropertyChanged" />
            </ScrollViewer>
          </StackPanel>

          <DockPanel Margin="0" Height="30" DockPanel.Dock="Bottom" HorizontalAlignment="Stretch">

            <TextBlock Margin="10,10,10,0" DockPanel.Dock="Left" FontSize="10" Text="{Binding LeftStatus}" VerticalAlignment="Center" />
            <StackPanel DockPanel.Dock="Right" Orientation="Horizontal">

              <StackPanel Orientation="Horizontal" IsVisible="{Binding ShowEstimates}">
                <TextBlock TextAlignment="Center" Width="140" FontSize="10" Margin="5,10,10,0" Text="{Binding ElapsedTime, StringFormat={}Elapsed Time: {0}}" />
                <StackPanel Orientation="Horizontal" IsVisible="{Binding ShowArchiveProgressBar}">
                  <TextBlock TextAlignment="Center" Width="220" FontSize="10" Margin="5,10,10,0" Text="{Binding ArchiveEtr, StringFormat=Remaining Time (Current Archive): {0:hh\\:mm\\:ss}}" />
                </StackPanel>
                <TextBlock TextAlignment="Center" Width="180" FontSize="10" Margin="5,10,10,0" Text="{Binding TotalEtr, StringFormat=Remaining Time (Total): {0:hh\\:mm\\:ss}}" />
                <TextBlock TextAlignment="Center" Width="180" FontSize="10" Margin="5,10,10,0" Text="{Binding TotalEta, StringFormat={}Estimated Finish Time: {0}}" />
              </StackPanel>

              <!-- progress within current archive -->
              <ProgressBar  Margin="5,0,0,0" Height="20"
                            Minimum="0"
                            Maximum="{Binding ProgressTotalFilesInCurrentArchive}"
                            Value="{Binding ProgressCurrentFileInArchive}"
                            ProgressTextFormat="{}{0:0} / {3:0} images in current archive"
                            FontSize="10"
                            ShowProgressText="True"
                            MinWidth="50"
                            Width="50"
                            MaxWidth="50"
                            IsVisible="{Binding ShowArchiveProgressBar}" />

              <!-- total progress across all files -->
              <ProgressBar  Margin="5,0,5,0" Height="20"
                            Minimum="0"
                            Maximum="{Binding ProgressTotalFiles}"
                            Value="{Binding ProgressCurrentFile}"
                            ProgressTextFormat="{}{0:0} / {3:0} total files"
                            FontSize="10"
                            MinWidth="50"
                            Width="50"
                            MaxWidth="50"
                            ShowProgressText="True"/>

              <ToggleButton IsChecked="{Binding ShowConsole}" FontSize="10" Margin="5,0,5,0">
                <StackPanel Orientation="Horizontal">
                  <materialIcons:MaterialIcon Kind="Console" VerticalAlignment="Center" />
                  <TextBlock Margin="5,0,5,0" VerticalAlignment="Center">Console</TextBlock>
                </StackPanel>
              </ToggleButton>
            </StackPanel>

            <Rectangle />
          </DockPanel>
        </StackPanel>

        <ScrollViewer HorizontalScrollBarVisibility="Auto" VerticalScrollBarVisibility="Visible">
          <!-- Main Form -->
          <StackPanel Margin="20">
            <Grid>
              <StackPanel Orientation="Horizontal" Margin="0,10,0,10" Grid.Column="0">
                <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Workflow Name</TextBlock>
                <TextBox Width="500" Margin="0,0,5,0" Text="{Binding CurrentWorkflow.WorkflowName}"/>
              </StackPanel>

              <StackPanel Grid.Column="1" HorizontalAlignment="Right" VerticalAlignment="Top">
                <StackPanel Margin="0,10,0,0" Orientation="Horizontal" HorizontalAlignment="Right">

                  <Button Margin="10,0,10,0" Click="ImportCurrentWorkflowButtonClick">
                    <StackPanel Orientation="Horizontal">
                      <materialIcons:MaterialIcon Kind="Import" VerticalAlignment="Center" />
                      <TextBlock Margin="10,0,10,0" VerticalAlignment="Center">Import Workflow</TextBlock>
                    </StackPanel>
                  </Button>
                  <Button Margin="10,0,10,0" Click="ExportCurrentWorkflowButtonClick">
                    <StackPanel Orientation="Horizontal">
                      <materialIcons:MaterialIcon Kind="Export" VerticalAlignment="Center" />
                      <TextBlock Margin="10,0,10,0" VerticalAlignment="Center">Export Workflow</TextBlock>
                    </StackPanel>
                  </Button>
                  <Button Margin="10,0,0,0" Click="ResetWorkflow">
                    <StackPanel Orientation="Horizontal">
                      <materialIcons:MaterialIcon Kind="Refresh" VerticalAlignment="Center" />
                      <TextBlock Margin="10,0,10,0" VerticalAlignment="Center">Reset Workflow</TextBlock>
                    </StackPanel>
                  </Button>
                </StackPanel>
              </StackPanel>

            </Grid>
            <DockPanel>
              <TextBlock DockPanel.Dock="Left" FontWeight="Bold" Text="Input and Output"></TextBlock>
              <CheckBox DockPanel.Dock="Right" IsChecked="{Binding CurrentWorkflow.ShowAdvancedSettings}" Content="Show Advanced Settings" />
              <Rectangle />
            </DockPanel>

            <!--Input and Output -->
            <Border Classes="border"
                    IsEnabled="{Binding !Upscaling}">
              <StackPanel>
                <TabControl SelectedIndex="{Binding CurrentWorkflow.SelectedTabIndex}" Margin="0">
                  <TabItem VerticalContentAlignment="Center" FontSize="16" Margin="0">
                    <TabItem.Header>
                      <StackPanel Orientation="Horizontal">
                        <materialIcons:MaterialIcon Kind="File" />
                        <TextBlock Margin="5,0,5,0">Single File Upscale</TextBlock>
                      </StackPanel>
                    </TabItem.Header>
                    <Border Classes="border">
                      <StackPanel>
                        <StackPanel Orientation="Horizontal" Margin="10,10,0,10">
                          <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Input File</TextBlock>
                          <TextBox x:Name="InputFileNameTextBox" Margin="0,0,5,0" Text="{Binding CurrentWorkflow.InputFilePath}" IsReadOnly="False" Width="600" DragDrop.AllowDrop="True"/>
                          <Button Content="Select File" Click="OpenInputFileButtonClick" />
                          <TextBlock Foreground="Gray" Width="500" TextWrapping="WrapWithOverflow" FontSize="12" VerticalAlignment="Center" Margin="20,0,0,0">Path of the image or archive file (such as zip or cbz) to upscale. If an archive file is selected, each image in the archive will be upscaled and saved to a new archive.</TextBlock>
                        </StackPanel>

                      </StackPanel>
                    </Border>

                  </TabItem>
                  <TabItem VerticalAlignment="Center" FontSize="16">
                    <TabItem.Header>
                      <StackPanel Orientation="Horizontal">
                        <materialIcons:MaterialIcon Kind="Folder" />
                        <TextBlock Margin="5,0,5,0">Batch Folder Upscale</TextBlock>
                      </StackPanel>
                    </TabItem.Header>
                    <StackPanel>

                      <Border Classes="border">
                        <StackPanel>
                          <StackPanel Orientation="Horizontal" Margin="10,10,0,10">
                            <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Input Folder</TextBlock>
                            <TextBox x:Name="InputFolderNameTextBox" Margin="0,0,5,0" Text="{Binding CurrentWorkflow.InputFolderPath}" IsReadOnly="False" Width="600" DragDrop.AllowDrop="True" />
                            <Button Content="Select Folder" Click="OpenInputFolderButtonClick" />
                            <TextBlock Foreground="Gray" Width="500" TextWrapping="WrapWithOverflow" FontSize="12" VerticalAlignment="Center" Margin="20,0,0,0">
                              Path of the folder to upscale. The folder and all of its subfolders will be scanned and images and/or archives will be upscaled depending on the selection below.
                            </TextBlock>
                          </StackPanel>

                          <StackPanel Orientation="Horizontal" Margin="10,0,0,10">
                            <CheckBox Margin="0,0,5,0" IsChecked="{Binding CurrentWorkflow.UpscaleArchives}">Upscale Archives</CheckBox>
                            <CheckBox IsChecked="{Binding CurrentWorkflow.UpscaleImages}">Upscale Images</CheckBox>
                            <TextBlock Foreground="Gray" FontSize="12" VerticalAlignment="Center" Margin="20,0,0,0">
                              Whether to upscale Image files (*.png, *.jpg, *.jpeg, *.webp, *.bmp) and/or Archive files (*.zip, *.cbz, *.rar, *.cbr) in the selected Input Folder.
                            </TextBlock>
                          </StackPanel>
                        </StackPanel>
                      </Border>

                    </StackPanel>
                  </TabItem>
                </TabControl>

                <Border Classes="border">
                  <StackPanel>
                    <StackPanel Orientation="Horizontal" Margin="10,10,0,10">
                      <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Output Folder</TextBlock>
                      <TextBox x:Name="OutputFolderNameTextBox" Classes="clearButton" Margin="0,0,5,0" Text="{Binding CurrentWorkflow.OutputFolderPath}" IsReadOnly="False" Width="600" DragDrop.AllowDrop="True" />
                      <Button Content="Select Folder" Click="OpenOutputFolderButtonClick" />
                      <TextBlock Foreground="Gray" Width="500" TextWrapping="WrapWithOverflow" FontSize="12" VerticalAlignment="Center" Margin="20,0,0,0">
                        Path of the folder to save the upscaled image(s) or archive(s).
                      </TextBlock>
                    </StackPanel>

                    <StackPanel Orientation="Horizontal" Margin="10,0,0,10">
                      <TextBlock Margin="0,0,5,0">Output Filename</TextBlock>
                      <TextBox Text="{Binding CurrentWorkflow.OutputFilename}" Margin="0,0,5,0" IsReadOnly="False" Width="600" DragDrop.AllowDrop="True" />
                      <SelectableTextBlock Foreground="Gray" Width="500" TextWrapping="WrapWithOverflow" FontSize="12" VerticalAlignment="Center" Margin="20,0,0,0">
                        The filename of the upscaled image(s) or archive(s), without the file extension. <Run FontFamily="Consolas">%filename%</Run> is the input filename without extension. Archives will be output with .cbz extension; images will be output with the extension of the image format selected below.
                      </SelectableTextBlock>
                    </StackPanel>

                    <StackPanel Orientation="Horizontal" Margin="10,0,0,0">
                      <CheckBox Margin="0,0,5,0" VerticalAlignment="Center" IsChecked="{Binding CurrentWorkflow.OverwriteExistingFiles}">Allow Files in Output Path to be Overwritten</CheckBox>
                      <TextBlock Width="600" TextWrapping="WrapWithOverflow" Foreground="Gray" FontSize="12" VerticalAlignment="Center" Margin="20,0,0,0">If unchecked, upscaling will be skipped for files that already exist in the output path. If checked, any files that already exist in the output path will be overwritten without warning. Use with caution.</TextBlock>
                    </StackPanel>
                  </StackPanel>
                </Border>


                <Border Classes="border">
                  <StackPanel>
                    <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10,10,0,10">
                      <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Output Image Format</TextBlock>
                      <ToggleButton IsChecked="{Binding CurrentWorkflow.WebpSelected}" Content="WebP" Command="{Binding CurrentWorkflow.SetWebpSelected}" />
                      <ToggleButton IsChecked="{Binding CurrentWorkflow.AvifSelected}" Content="AVIF" Command="{Binding CurrentWorkflow.SetAvifSelected}" />
                      <ToggleButton IsChecked="{Binding CurrentWorkflow.PngSelected}" Content="PNG" Command="{Binding CurrentWorkflow.SetPngSelected}" />
                      <ToggleButton IsChecked="{Binding CurrentWorkflow.JpegSelected}" Content="JPEG" Command="{Binding CurrentWorkflow.SetJpegSelected}" />
                      <TextBlock Foreground="Gray" Width="950" TextWrapping="WrapWithOverflow" FontSize="12" VerticalAlignment="Center" Margin="40,0,0,0" xml:space="preserve"><Bold>WebP</Bold>: Modern format recommended for good quality and efficient filesize compression, with good compatibility on modern devices. Supports lossless and lossy compression. 
<Bold>AVIF</Bold>: Modern format with better lossy compression efficiency than WebP, but not as widely supported and slower to save and load compared to WebP.
<Bold>PNG</Bold>: Lossless compressed format with excellent compatibility, but worse compression efficiency than WebP and AVIF. 
<Bold>JPEG</Bold>: Lossy compressed format with excellent compatibility, but worse compression efficiency than WebP and AVIF. 
            </TextBlock>
                    </StackPanel>

                    <StackPanel Orientation="Horizontal" Margin="10,0,0,10" IsVisible="{Binding CurrentWorkflow.ShowUseLosslessCompression}">
                      <CheckBox Margin="0,0,5,0" IsChecked="{Binding CurrentWorkflow.UseLosslessCompression}">Use Lossless Compression</CheckBox>
                      <TextBlock Foreground="Gray" FontSize="12" VerticalAlignment="Center" Margin="20,0,0,0">
                        Use lossless compression. Usually not recommended due to producing images with much larger filesize with little visual benefit.
                      </TextBlock>
                    </StackPanel>

                    <StackPanel Orientation="Horizontal" Margin="10,0,0,10" IsVisible="{Binding CurrentWorkflow.ShowLossyCompressionQuality}">
                      <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Lossy Compression Quality</TextBlock>
                      <NumericUpDown Margin="0,0,5,0" Value="{Binding CurrentWorkflow.LossyCompressionQuality}" Increment="1" Minimum="0" Maximum="100" Width="120"  />
                      <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">%</TextBlock>
                      <TextBlock Foreground="Gray" FontSize="12" VerticalAlignment="Center" Margin="20,0,0,0">
                        Quality level for compression. Note that a quality level of 100 is still lossy.
                      </TextBlock>
                    </StackPanel>
                  </StackPanel>
                </Border>

              </StackPanel>
            </Border>

            <TextBlock Margin="0,40,0,0" FontWeight="Bold" Text="Upscaling"></TextBlock>
            <Border Classes="border" IsEnabled="{Binding !Upscaling}">

              <StackPanel>

                <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10,10,0,10">
                  <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Upscale Mode</TextBlock>
                  <ToggleButton IsChecked="{Binding CurrentWorkflow.ModeScaleSelected}" Content="Scale" Command="{Binding CurrentWorkflow.SetModeScaleSelected}" />
                  <ToggleButton IsChecked="{Binding CurrentWorkflow.ModeWidthSelected}" Content="Width" Command="{Binding CurrentWorkflow.SetModeWidthSelected}" />
                  <ToggleButton IsChecked="{Binding CurrentWorkflow.ModeHeightSelected}" Content="Height" Command="{Binding CurrentWorkflow.SetModeHeightSelected}" />
                  <ToggleButton IsChecked="{Binding CurrentWorkflow.ModeFitToDisplaySelected}" Content="Fit to Display" Command="{Binding CurrentWorkflow.SetModeFitToDisplaySelected}" />
                  <TextBlock Foreground="Gray" Width="900" TextWrapping="WrapWithOverflow" FontSize="12" VerticalAlignment="Center" Margin="40,0,0,0" xml:space="preserve"><Bold>Scale</Bold>: All images will be upscaled by the specified scaling factor. For example, a factor of 2x will double the width and height of each image.
<Bold>Width</Bold>: All images will be upscaled to the specified width, while maintaining aspect ratio of the image. 
<Bold>Height</Bold>: All images will be upscaled to the specified height, while maintaining aspect ratio of the image. 
<Bold>Fit to Display</Bold>: All images will be upscaled to fit exactly within the specified display device. 
            </TextBlock>
                </StackPanel>

                <StackPanel Orientation="Horizontal" VerticalAlignment="Center" Margin="10,0,0,10" IsVisible="{Binding CurrentWorkflow.ModeScaleSelected}">
                  <TextBlock Margin="0,0,5,0" VerticalAlignment="Center">Scale Factor</TextBlock>
                  <ToggleButton IsChecked="{Binding CurrentWorkflow.Is1x}" Content="1x" Command="{Binding CurrentWorkflow.SetUpscaleScaleFactor}" CommandParameter="1" />
                  <ToggleButton IsChecked="{Binding CurrentWorkflow.Is2x}" Content="2x" Command="{Binding CurrentWorkflow.SetUpscaleScaleFactor}" CommandParameter="2" />
                  <ToggleButton IsChecked="{Binding CurrentWorkflow.Is3x}" Content="3x" Command="{Binding CurrentWorkflow.SetUpscaleScaleFactor}" CommandParameter="3" />
                  <ToggleButton IsChecked="{Binding CurrentWorkflow.Is4x}" Content="4x" Command="{Binding CurrentWorkflow.SetUpscaleScaleFactor}" CommandParameter="4" />
                  <TextBlock Foreground="Gray" Width="900" TextWrapping="WrapWithOverflow" FontSize="12" VerticalAlignment="Center" Margin="40,0,0,0">
                    All images will be upscaled by the specified scaling factor. For example, a factor of 2x will double the width and height of each image.
                  
Download .txt
gitextract_o6l0qlog/

├── .editorconfig
├── .github/
│   └── workflows/
│       └── deploy.yml
├── .gitignore
├── LICENSE
├── MangaJaNaiConverterGui/
│   ├── App.axaml
│   ├── App.axaml.cs
│   ├── Drivers/
│   │   └── NewtonsoftJsonSuspensionDriver.cs
│   ├── MangaJaNaiConverterGui.csproj
│   ├── Program.cs
│   ├── Services/
│   │   ├── Downloader.cs
│   │   ├── ETACalculator.cs
│   │   ├── IPythonService.cs
│   │   ├── ISuspensionDriverService.cs
│   │   ├── IUpdateManagerService.cs
│   │   ├── PythonService.cs
│   │   ├── SuspensionDriverService.cs
│   │   └── UpdateManagerService.cs
│   ├── ViewLocator.cs
│   ├── ViewModels/
│   │   ├── MainWindowViewModel.cs
│   │   └── ViewModelBase.cs
│   ├── Views/
│   │   ├── MainWindow.axaml
│   │   └── MainWindow.axaml.cs
│   ├── app.manifest
│   ├── appstate2.json
│   └── backend/
│       ├── ImageMagick/
│       │   ├── Custom Gray Gamma 1.0.icc
│       │   ├── Custom RGB Gamma 1.0.icc
│       │   └── Dot Gain 20%.icc
│       ├── resources/
│       │   └── default_cli_configuration.json
│       └── src/
│           ├── .pre-commit-config.yaml
│           ├── README.md
│           ├── __init__.py
│           ├── accelerator_detection.py
│           ├── api/
│           │   ├── __init__.py
│           │   ├── api.py
│           │   ├── group.py
│           │   ├── input.py
│           │   ├── iter.py
│           │   ├── lazy.py
│           │   ├── node_check.py
│           │   ├── node_context.py
│           │   ├── node_data.py
│           │   ├── output.py
│           │   ├── settings.py
│           │   └── types.py
│           ├── device_list.py
│           ├── gpu.py
│           ├── navi.py
│           ├── nodes/
│           │   ├── __init__.py
│           │   ├── condition.py
│           │   ├── group.py
│           │   ├── groups.py
│           │   ├── impl/
│           │   │   ├── __init__.py
│           │   │   ├── blend.py
│           │   │   ├── color/
│           │   │   │   ├── __init__.py
│           │   │   │   ├── color.py
│           │   │   │   ├── convert.py
│           │   │   │   ├── convert_data.py
│           │   │   │   └── convert_model.py
│           │   │   ├── image_formats.py
│           │   │   ├── image_op.py
│           │   │   ├── image_utils.py
│           │   │   ├── onnx/
│           │   │   │   ├── __init__.py
│           │   │   │   ├── auto_split.py
│           │   │   │   ├── load.py
│           │   │   │   ├── model.py
│           │   │   │   ├── np_tensor_utils.py
│           │   │   │   ├── onnx_to_ncnn.py
│           │   │   │   ├── session.py
│           │   │   │   ├── tensorproto_utils.py
│           │   │   │   ├── update_model_dims.py
│           │   │   │   └── utils.py
│           │   │   ├── pil_utils.py
│           │   │   ├── pytorch/
│           │   │   │   ├── __init__.py
│           │   │   │   ├── auto_split.py
│           │   │   │   ├── convert_to_onnx_impl.py
│           │   │   │   ├── pix_transform/
│           │   │   │   │   ├── LICENSE
│           │   │   │   │   ├── auto_split.py
│           │   │   │   │   ├── pix_transform.py
│           │   │   │   │   └── pix_transform_net.py
│           │   │   │   ├── rife/
│           │   │   │   │   ├── IFNet_HDv3_v4_14_align.py
│           │   │   │   │   └── warplayer.py
│           │   │   │   └── utils.py
│           │   │   ├── resize.py
│           │   │   └── upscale/
│           │   │       ├── __init__.py
│           │   │       ├── auto_split.py
│           │   │       ├── auto_split_tiles.py
│           │   │       ├── basic_upscale.py
│           │   │       ├── convenient_upscale.py
│           │   │       ├── custom_scale.py
│           │   │       ├── exact_split.py
│           │   │       ├── grayscale.py
│           │   │       ├── passthrough.py
│           │   │       ├── tile_blending.py
│           │   │       └── tiler.py
│           │   ├── node_cache.py
│           │   ├── properties/
│           │   │   ├── __init__.py
│           │   │   ├── inputs/
│           │   │   │   ├── __init__.py
│           │   │   │   ├── __system_inputs.py
│           │   │   │   ├── file_inputs.py
│           │   │   │   ├── generic_inputs.py
│           │   │   │   ├── image_dropdown_inputs.py
│           │   │   │   ├── label.py
│           │   │   │   ├── ncnn_inputs.py
│           │   │   │   ├── numeric_inputs.py
│           │   │   │   ├── numpy_inputs.py
│           │   │   │   ├── onnx_inputs.py
│           │   │   │   └── pytorch_inputs.py
│           │   │   └── outputs/
│           │   │       ├── __init__.py
│           │   │       ├── file_outputs.py
│           │   │       ├── generic_outputs.py
│           │   │       ├── ncnn_outputs.py
│           │   │       ├── numpy_outputs.py
│           │   │       ├── onnx_outputs.py
│           │   │       └── pytorch_outputs.py
│           │   └── utils/
│           │       ├── __init__.py
│           │       ├── format.py
│           │       ├── seed.py
│           │       └── utils.py
│           ├── packages/
│           │   └── chaiNNer_pytorch/
│           │       ├── __init__.py
│           │       ├── pytorch/
│           │       │   ├── __init__.py
│           │       │   ├── io/
│           │       │   │   └── load_model.py
│           │       │   └── processing/
│           │       │       └── upscale_image.py
│           │       └── settings.py
│           ├── progress_controller.py
│           ├── pyproject.toml
│           ├── pyrightconfig.json
│           ├── run_upscale.py
│           ├── spandrel_custom/
│           │   ├── __init__.py
│           │   └── architectures/
│           │       └── FDAT/
│           │           ├── __arch/
│           │           │   ├── LICENSE
│           │           │   └── fdat.py
│           │           └── __init__.py
│           ├── system.py
│           └── test_accelerators.py
├── MangaJaNaiConverterGui.sln
├── README.md
└── pack.bat
Download .txt
SYMBOL INDEX (1048 symbols across 98 files)

FILE: MangaJaNaiConverterGui/App.axaml.cs
  class App (line 18) | public partial class App : Application
    method Initialize (line 20) | public override void Initialize()
    method OnFrameworkInitializationCompleted (line 25) | public override void OnFrameworkInitializationCompleted()

FILE: MangaJaNaiConverterGui/Drivers/NewtonsoftJsonSuspensionDriver.cs
  class NewtonsoftJsonSuspensionDriver (line 10) | public class NewtonsoftJsonSuspensionDriver : ISuspensionDriver
    method NewtonsoftJsonSuspensionDriver (line 19) | public NewtonsoftJsonSuspensionDriver(string file) => _file = file;
    method InvalidateState (line 21) | public IObservable<Unit> InvalidateState()
    method LoadState (line 28) | public IObservable<object> LoadState()
    method SaveState (line 35) | public IObservable<Unit> SaveState(object state)

FILE: MangaJaNaiConverterGui/Program.cs
  class Program (line 9) | internal class Program
    method Main (line 24) | [STAThread]
    method BuildAvaloniaApp (line 51) | public static AppBuilder BuildAvaloniaApp()

FILE: MangaJaNaiConverterGui/Services/Downloader.cs
  class Downloader (line 9) | public class Downloader
    method DownloadFileAsync (line 13) | public static async Task DownloadFileAsync(string url, string destinat...

FILE: MangaJaNaiConverterGui/Services/ETACalculator.cs
  type IETACalculator (line 9) | public interface IETACalculator
    method Reset (line 13) | void Reset();
    method Update (line 19) | void Update(float progress);
  class ETACalculator (line 39) | public class ETACalculator : IETACalculator
    method ETACalculator (line 49) | public ETACalculator(int minimumData, double maximumDuration)
    method Reset (line 65) | public void Reset()
    method ClearExpired (line 73) | private void ClearExpired()
    method Update (line 86) | public void Update(float progress)

FILE: MangaJaNaiConverterGui/Services/IPythonService.cs
  type IPythonService (line 7) | public interface IPythonService
    method IsPythonInstalled (line 9) | bool IsPythonInstalled();
    method IsPythonUpdated (line 11) | Task<bool> IsPythonUpdated();
    method IsBackendUpdated (line 12) | Task<bool> IsBackendUpdated();
    method AreModelsInstalled (line 13) | bool AreModelsInstalled();
    method ExtractTgz (line 26) | void ExtractTgz(string gzArchiveName, string destFolder);
    method ExtractZip (line 27) | void ExtractZip(string archivePath, string outFolder, ProgressChanged ...
    method Extract7z (line 28) | void Extract7z(string archivePath, string outFolder);
    method AddPythonPth (line 29) | void AddPythonPth(string destFolder);

FILE: MangaJaNaiConverterGui/Services/ISuspensionDriverService.cs
  type ISuspensionDriverService (line 5) | public interface ISuspensionDriverService

FILE: MangaJaNaiConverterGui/Services/IUpdateManagerService.cs
  type IUpdateManagerService (line 7) | public interface IUpdateManagerService
    method ApplyUpdatesAndRestart (line 13) | void ApplyUpdatesAndRestart(UpdateInfo update);
    method CheckForUpdatesAsync (line 14) | Task<UpdateInfo?> CheckForUpdatesAsync();
    method DownloadUpdatesAsync (line 15) | Task DownloadUpdatesAsync(UpdateInfo update, Action<int>? progress = n...

FILE: MangaJaNaiConverterGui/Services/PythonService.cs
  class PythonService (line 21) | public class PythonService : IPythonService
    method PythonService (line 43) | public PythonService(IUpdateManagerService? updateManagerService = null)
    method IsPythonInstalled (line 61) | public bool IsPythonInstalled() => File.Exists(PythonPath);
    method IsPythonUpdated (line 63) | public async Task<bool> IsPythonUpdated()
    method IsBackendUpdated (line 119) | public async Task<bool> IsBackendUpdated()
    method AreModelsInstalled (line 131) | public bool AreModelsInstalled() => Directory.Exists(ModelsDirectory) ...
    class PythonDownload (line 133) | public class PythonDownload
    method ExtractTgz (line 141) | public void ExtractTgz(string gzArchiveName, string destFolder)
    method ExtractZip (line 154) | public void ExtractZip(string archivePath, string outFolder, ProgressC...
    method Extract7z (line 203) | public void Extract7z(string archiveName, string outFolder)
    method AddPythonPth (line 209) | public void AddPythonPth(string destFolder)

FILE: MangaJaNaiConverterGui/Services/SuspensionDriverService.cs
  class SuspensionDriverService (line 6) | public class SuspensionDriverService(IPythonService pythonService) : ISu...

FILE: MangaJaNaiConverterGui/Services/UpdateManagerService.cs
  class UpdateManagerService (line 8) | public class UpdateManagerService : IUpdateManagerService
    method UpdateManagerService (line 12) | public UpdateManagerService()
    method ApplyUpdatesAndRestart (line 24) | public void ApplyUpdatesAndRestart(UpdateInfo update)
    method CheckForUpdatesAsync (line 29) | public async Task<UpdateInfo?> CheckForUpdatesAsync()
    method DownloadUpdatesAsync (line 34) | public Task DownloadUpdatesAsync(UpdateInfo update, Action<int>? progr...

FILE: MangaJaNaiConverterGui/ViewLocator.cs
  class ViewLocator (line 8) | public class ViewLocator : IDataTemplate
    method Build (line 10) | public Control Build(object data)
    method Match (line 23) | public bool Match(object data)

FILE: MangaJaNaiConverterGui/ViewModels/MainWindowViewModel.cs
  class MainWindowViewModel (line 27) | [DataContract]
    method MainWindowViewModel (line 42) | public MainWindowViewModel(IPythonService? pythonService = null, IUpda...
    method _timer_Tick (line 385) | private void _timer_Tick(object? sender, EventArgs e)
    method HandleWorkflowSelected (line 729) | public void HandleWorkflowSelected(int workflowIndex)
    method HandleAppSettingsSelected (line 735) | public void HandleAppSettingsSelected()
    method RunUpscale (line 743) | public async Task RunUpscale()
    method CancelUpscale (line 790) | public void CancelUpscale()
    method CheckInputs (line 807) | public void CheckInputs()
    method AddChain (line 963) | public void AddChain()
    method DeleteChain (line 972) | public void DeleteChain(UpscaleChain chain)
    method UpdateChainHeaders (line 986) | public void UpdateChainHeaders()
    method RunCommand (line 996) | public async Task RunCommand(string command)
    method InitializeDeviceList (line 1071) | public async Task<DeviceResponse?> InitializeDeviceList()
    method RunPythonPipList (line 1133) | public async Task<string> RunPythonPipList()
    method ShowSettingsDialog (line 1183) | public async void ShowSettingsDialog()
    method UpdateEtas (line 1188) | private void UpdateEtas()
    method ConsoleQueueClear (line 1206) | private void ConsoleQueueClear()
    method ConsoleQueueEnqueue (line 1212) | private void ConsoleQueueEnqueue(string value)
    method BackendSetupSubStatusQueueEnqueue (line 1222) | private void BackendSetupSubStatusQueueEnqueue(string value)
    method ReadWorkflowFileToCurrentWorkflow (line 1232) | public void ReadWorkflowFileToCurrentWorkflow(string fullPath)
    method WriteCurrentWorkflowToFile (line 1249) | public void WriteCurrentWorkflowToFile(string fullPath)
    method CheckAndExtractBackend (line 1255) | public async Task CheckAndExtractBackend()
    method ReinstallBackend (line 1319) | public async Task ReinstallBackend()
    method DownloadModels (line 1334) | public async Task DownloadModels()
    method InstallUpdatePythonDependencies (line 1385) | public async Task<string[]> InstallUpdatePythonDependencies()
    method CheckAndDoBackup (line 1443) | public void CheckAndDoBackup()
    method FilesAreEqual (line 1493) | private static bool FilesAreEqual(string path1, string path2)
    method ResetCurrentWorkflow (line 1506) | public void ResetCurrentWorkflow()
    method PopulateDevicesAsync (line 1534) | public async Task<IEnumerable<object>> PopulateDevicesAsync(string? se...
    method CheckForUpdates (line 1563) | public async Task CheckForUpdates()
    method DownloadUpdate (line 1588) | public async Task DownloadUpdate()
    method ApplyUpdate (line 1605) | public void ApplyUpdate()
    method UpdateStatus (line 1614) | private void UpdateStatus()
    method Progress (line 1643) | private void Progress(int percent)
    method OpenModelsDirectory (line 1649) | public async void OpenModelsDirectory()
  class UpscaleWorkflow (line 1658) | [DataContract]
    method UpscaleWorkflow (line 1661) | public UpscaleWorkflow()
    method SetUpscaleScaleFactor (line 1993) | public void SetUpscaleScaleFactor(int scaleFactor)
    method SetWebpSelected (line 2133) | public void SetWebpSelected()
    method SetPngSelected (line 2141) | public void SetPngSelected()
    method SetJpegSelected (line 2149) | public void SetJpegSelected()
    method SetAvifSelected (line 2157) | public void SetAvifSelected()
    method SetModeScaleSelected (line 2165) | public void SetModeScaleSelected()
    method SetModeWidthSelected (line 2173) | public void SetModeWidthSelected()
    method SetModeHeightSelected (line 2181) | public void SetModeHeightSelected()
    method SetModeFitToDisplaySelected (line 2189) | public void SetModeFitToDisplaySelected()
    method Validate (line 2197) | public void Validate()
  class UpscaleChain (line 2276) | [DataContract]
    method UpscaleChain (line 2281) | public UpscaleChain(IPythonService? pythonService = null)
  class ReaderDevice (line 2438) | public class ReaderDevice
    method ToString (line 2446) | public override string ToString()
  class DeviceResponse (line 2469) | public class DeviceResponse
  class AcceleratorDevice (line 2478) | public class AcceleratorDevice

FILE: MangaJaNaiConverterGui/ViewModels/ViewModelBase.cs
  class ViewModelBase (line 6) | public class ViewModelBase : ReactiveObject

FILE: MangaJaNaiConverterGui/Views/MainWindow.axaml.cs
  class MainWindow (line 20) | public partial class MainWindow : AppWindow
    method MainWindow (line 25) | public MainWindow()
    method MainWindow_Opened (line 45) | private async void MainWindow_Opened(object? sender, EventArgs e)
    method MainWindow_Closing (line 53) | private async void MainWindow_Closing(object? sender, WindowClosingEve...
    method ConsoleScrollViewer_PropertyChanged (line 78) | private void ConsoleScrollViewer_PropertyChanged(object? sender, Avalo...
    method ConsoleTextBlock_PropertyChanged (line 90) | private void ConsoleTextBlock_PropertyChanged(object? sender, Avalonia...
    method MainWindow_Resized (line 107) | private void MainWindow_Resized(object? sender, WindowResizedEventArgs e)
    method SetInputFilePath (line 117) | public void SetInputFilePath(object? sender, DragEventArgs e)
    method SetInputFolderPath (line 135) | public void SetInputFolderPath(object? sender, DragEventArgs e)
    method SetOutputFolderPath (line 153) | public void SetOutputFolderPath(object? sender, DragEventArgs e)
    method OpenInputFileButtonClick (line 171) | private async void OpenInputFileButtonClick(object? sender, RoutedEven...
    method OpenInputFolderButtonClick (line 209) | private async void OpenInputFolderButtonClick(object? sender, RoutedEv...
    method OpenOutputFolderButtonClick (line 240) | private async void OpenOutputFolderButtonClick(object? sender, RoutedE...
    method ImportCurrentWorkflowButtonClick (line 271) | private async void ImportCurrentWorkflowButtonClick(object? sender, Ro...
    method ResetWorkflow (line 335) | private async void ResetWorkflow(object? sender, RoutedEventArgs e)
    method ExportCurrentWorkflowButtonClick (line 377) | private async void ExportCurrentWorkflowButtonClick(object? sender, Ro...
    method ReinstallBackendClick (line 411) | private async void ReinstallBackendClick(object? sender, RoutedEventAr...
    method ShowConfirmationDialog (line 424) | private async Task<bool> ShowConfirmationDialog(string title, string m...
    method HandleDevicesGotFocus (line 503) | public void HandleDevicesGotFocus(object? sender, GotFocusEventArgs e)
    method HandleDevicesTapped (line 514) | private void HandleDevicesTapped(object? sender, Avalonia.Input.Tapped...

FILE: MangaJaNaiConverterGui/backend/src/accelerator_detection.py
  class AcceleratorType (line 17) | class AcceleratorType(Enum):
  class AcceleratorDevice (line 27) | class AcceleratorDevice:
    method __post_init__ (line 38) | def __post_init__(self):
    method torch_device (line 46) | def torch_device(self) -> torch.device:
    method __eq__ (line 51) | def __eq__(self, other: object) -> bool:
    method __hash__ (line 57) | def __hash__(self) -> int:
  class AcceleratorDetector (line 62) | class AcceleratorDetector:
    method __init__ (line 65) | def __init__(self):
    method available_devices (line 69) | def available_devices(self) -> list[AcceleratorDevice]:
    method _detect_all_devices (line 75) | def _detect_all_devices(self) -> list[AcceleratorDevice]:
    method _detect_cuda_devices (line 102) | def _detect_cuda_devices(self) -> list[AcceleratorDevice]:
    method _detect_rocm_devices (line 133) | def _detect_rocm_devices(self) -> list[AcceleratorDevice]:
    method _detect_mps_devices (line 165) | def _detect_mps_devices(self) -> list[AcceleratorDevice]:
    method _detect_xpu_devices (line 188) | def _detect_xpu_devices(self) -> list[AcceleratorDevice]:
    method _cuda_supports_fp16 (line 221) | def _cuda_supports_fp16(self, device_props: Any, device_index: int) ->...
    method _cuda_supports_bf16 (line 246) | def _cuda_supports_bf16(self, device_props: Any) -> bool:
    method get_devices_by_type (line 256) | def get_devices_by_type(self, accelerator_type: AcceleratorType) -> li...
    method get_best_device (line 260) | def get_best_device(self, prefer_gpu: bool = True) -> AcceleratorDevice:
    method get_cpu_device (line 274) | def get_cpu_device(self) -> AcceleratorDevice:
    method get_device_by_index (line 281) | def get_device_by_index(self, device_type: AcceleratorType, index: int...
  function get_autocast_device_type (line 290) | def get_autocast_device_type(device: torch.device) -> str:
  function is_device_type_supported_for_autocast (line 306) | def is_device_type_supported_for_autocast(device: torch.device) -> bool:
  function get_accelerator_detector (line 316) | def get_accelerator_detector() -> AcceleratorDetector:
  function get_available_devices (line 324) | def get_available_devices() -> list[AcceleratorDevice]:
  function get_best_device (line 329) | def get_best_device(prefer_gpu: bool = True) -> AcceleratorDevice:

FILE: MangaJaNaiConverterGui/backend/src/api/api.py
  function _process_inputs (line 40) | def _process_inputs(base_inputs: Iterable[BaseInput | NestedGroup]):
  function _process_outputs (line 68) | def _process_outputs(base_outputs: Iterable[BaseOutput]):
  class NodeGroup (line 82) | class NodeGroup:
    method add_node (line 89) | def add_node(self, node: NodeData) -> None:
    method to_dict (line 93) | def to_dict(self):
    method register (line 101) | def register(
  class Category (line 215) | class Category:
    method add_node_group (line 225) | def add_node_group(self, name: str) -> NodeGroup:
    method to_dict (line 234) | def to_dict(self):
  class Dependency (line 247) | class Dependency:
    method to_dict (line 257) | def to_dict(self):
    method from_dict (line 268) | def from_dict(data: dict[str, Any]) -> Dependency:
  class Feature (line 280) | class Feature:
    method add_behavior (line 286) | def add_behavior(self, check: Callable[[], Awaitable[FeatureState]]) -...
    method to_dict (line 293) | def to_dict(self):
    method from_dict (line 301) | def from_dict(data: dict[str, Any]) -> Feature:
  class FeatureBehavior (line 310) | class FeatureBehavior:
  class FeatureState (line 315) | class FeatureState:
    method enabled (line 320) | def enabled(details: str | None = None) -> FeatureState:
    method disabled (line 324) | def disabled(details: str | None = None) -> FeatureState:
  class Package (line 329) | class Package:
    method add_category (line 341) | def add_category(
    method add_dependency (line 361) | def add_dependency(self, dependency: Dependency) -> None:
    method add_setting (line 364) | def add_setting(self, setting: Setting) -> None:
    method add_feature (line 367) | def add_feature(
    method to_dict (line 380) | def to_dict(self):
    method from_dict (line 393) | def from_dict(data: dict[str, Any]) -> Package:
  function _iter_py_files (line 409) | def _iter_py_files(directory: str):
  class LoadErrorInfo (line 417) | class LoadErrorInfo:
  class PackageRegistry (line 423) | class PackageRegistry:
    method __init__ (line 424) | def __init__(self) -> None:
    method get_node (line 429) | def get_node(self, schema_id: str) -> NodeData:
    method get_package (line 432) | def get_package(self, schema_id: str) -> Package:
    method add (line 435) | def add(self, package: Package) -> Package:
    method load_nodes (line 440) | def load_nodes(self, current_file: str) -> list[LoadErrorInfo]:
    method _refresh_nodes (line 466) | def _refresh_nodes(self) -> None:
  function add_package (line 484) | def add_package(

FILE: MangaJaNaiConverterGui/backend/src/api/group.py
  class GroupInfo (line 14) | class GroupInfo:
    method __init__ (line 15) | def __init__(
  class Group (line 26) | class Group(Generic[T]):
    method __init__ (line 27) | def __init__(self, info: GroupInfo, items: list[T]) -> None:
    method to_dict (line 31) | def to_dict(self):
  function group (line 45) | def group(kind: str, options: dict[str, Any] | None = None, id: int = -1):

FILE: MangaJaNaiConverterGui/backend/src/api/input.py
  class InputConversion (line 27) | class InputConversion:
    method to_dict (line 48) | def to_dict(self):
  class IOFusion (line 56) | class IOFusion:
  class LiteralErrorValue (line 60) | class LiteralErrorValue(TypedDict):
  class FormattedErrorValue (line 65) | class FormattedErrorValue(TypedDict):
  class PendingErrorValue (line 70) | class PendingErrorValue(TypedDict):
  class UnknownErrorValue (line 74) | class UnknownErrorValue(TypedDict):
  class BaseInput (line 87) | class BaseInput(Generic[T]):
    method __init__ (line 88) | def __init__(
    method enforce (line 116) | def enforce(self, value: object) -> T:
    method enforce_ (line 121) | def enforce_(self, value: object | None) -> T | None:
    method get_error_value (line 130) | def get_error_value(self, value: object) -> ErrorValue:
    method to_dict (line 151) | def to_dict(self) -> Mapping[str, Any]:
    method with_id (line 173) | def with_id(self, input_id: InputId | int):
    method with_docs (line 177) | def with_docs(self, *description: str, hint: bool = False):
    method suggest (line 182) | def suggest(self):
    method make_optional (line 186) | def make_optional(self):
    method make_lazy (line 193) | def make_lazy(self):
    method make_fused (line 197) | def make_fused(self, with_output: OutputId | int = 0):

FILE: MangaJaNaiConverterGui/backend/src/api/iter.py
  class Generator (line 12) | class Generator(Generic[I]):
    method with_fail_fast (line 18) | def with_fail_fast(self, fail_fast: bool):
    method with_metadata (line 22) | def with_metadata(self, metadata: object):
    method from_iter (line 27) | def from_iter(
    method from_list (line 33) | def from_list(l: list[L], map_fn: Callable[[L, int], I]) -> Generator[I]:
    method from_range (line 49) | def from_range(count: int, map_fn: Callable[[int], I]) -> Generator[I]:
  class Collector (line 71) | class Collector(Generic[N, R]):

FILE: MangaJaNaiConverterGui/backend/src/api/lazy.py
  class _Result (line 11) | class _Result(Generic[T]):
    method __init__ (line 14) | def __init__(self, value: T | None, error: Exception | None) -> None:
    method result (line 18) | def result(self) -> T:
    method is_ok (line 25) | def is_ok(self) -> bool:
    method ok (line 30) | def ok(value: T) -> _Result[T]:
    method err (line 34) | def err(error: Exception) -> _Result[T]:
  function _to_result (line 38) | def _to_result(fn: Callable[[], T]) -> Callable[[], _Result[T]]:
  class Lazy (line 48) | class Lazy(Generic[T]):
    method __init__ (line 49) | def __init__(self, factory: Callable[[], T]) -> None:
    method ready (line 56) | def ready(value: T) -> Lazy[T]:
    method from_coroutine (line 62) | def from_coroutine(
    method has_value (line 78) | def has_value(self) -> bool:
    method has_error (line 83) | def has_error(self) -> bool:
    method evaluation_time (line 88) | def evaluation_time(self) -> float:
    method value (line 93) | def value(self) -> T:

FILE: MangaJaNaiConverterGui/backend/src/api/node_check.py
  class CheckFailedError (line 18) | class CheckFailedError(Exception):
  class CheckLevel (line 22) | class CheckLevel(Enum):
    method parse (line 29) | def parse(s: str) -> CheckLevel:
  function _get_check_level (line 43) | def _get_check_level(name: str, default: CheckLevel) -> CheckLevel:
  class TypeTransformer (line 56) | class TypeTransformer(ast.NodeTransformer):
    method visit_BinOp (line 57) | def visit_BinOp(self, node: ast.BinOp):  # noqa
    method visit_Subscript (line 75) | def visit_Subscript(self, node: ast.Subscript):  # noqa
  function compile_type_string (line 85) | def compile_type_string(s: str, filename: str = "<string>"):
  function eval_type (line 91) | def eval_type(t: str | _Ty, __globals: dict[str, Any], /):
  function union_types (line 108) | def union_types(types: list[_Ty]) -> _Ty:
  function union_to_set (line 116) | def union_to_set(t: _Ty) -> set[_Ty]:
  function is_subset_of (line 126) | def is_subset_of(a: _Ty, b: _Ty) -> bool:
  function is_tuple (line 133) | def is_tuple(t: _Ty) -> bool:
  class FailedToParse (line 138) | class FailedToParse:
  function get_type_annotations (line 142) | def get_type_annotations(fn: Callable) -> dict[str, _Ty | FailedToParse]:
  function validate_return_type (line 158) | def validate_return_type(return_type: _Ty, node: NodeData) -> None:
  function check_schema_types (line 195) | def check_schema_types(
  function check_naming_conventions (line 284) | def check_naming_conventions(

FILE: MangaJaNaiConverterGui/backend/src/api/node_context.py
  class Aborted (line 10) | class Aborted(Exception):
  class Progress (line 14) | class Progress(ABC):
    method aborted (line 17) | def aborted(self) -> bool:
    method paused (line 24) | def paused(self) -> bool:
    method check_aborted (line 29) | def check_aborted(self) -> None:
    method suspend (line 37) | def suspend(self) -> None:
    method set_progress (line 50) | def set_progress(self, progress: float) -> None:
    method sub_progress (line 57) | def sub_progress(self, offset: float, length: float) -> "Progress":
    method noop_progress (line 70) | def noop_progress() -> "Progress":
  class _NoopProgress (line 77) | class _NoopProgress(Progress):
    method aborted (line 79) | def aborted(self) -> Literal[False]:
    method paused (line 83) | def paused(self) -> Literal[False]:
    method check_aborted (line 86) | def check_aborted(self) -> None:
    method suspend (line 89) | def suspend(self) -> None:
    method set_progress (line 92) | def set_progress(self, progress: float) -> None:
    method sub_progress (line 95) | def sub_progress(self, offset: float, length: float) -> "Progress":
  class _SubProgress (line 99) | class _SubProgress(Progress):
    method __init__ (line 100) | def __init__(self, parent: Progress, offset: float, length: float) -> ...
    method aborted (line 106) | def aborted(self) -> bool:
    method paused (line 110) | def paused(self) -> bool:
    method check_aborted (line 113) | def check_aborted(self) -> None:
    method suspend (line 116) | def suspend(self) -> None:
    method set_progress (line 119) | def set_progress(self, progress: float) -> None:
    method sub_progress (line 122) | def sub_progress(self, offset: float, length: float) -> "_SubProgress":
  class NodeContext (line 130) | class NodeContext(Progress, ABC):
    method settings (line 137) | def settings(self) -> SettingsParser:
    method storage_dir (line 144) | def storage_dir(self) -> Path:
    method add_cleanup (line 152) | def add_cleanup(

FILE: MangaJaNaiConverterGui/backend/src/api/node_data.py
  class IteratorInputInfo (line 25) | class IteratorInputInfo:
    method __init__ (line 26) | def __init__(
    method to_dict (line 39) | def to_dict(self):
  class AnyConstructor (line 50) | class AnyConstructor(Protocol, Generic[M_co]):
    method __call__ (line 51) | def __call__(self, *args: Any, **kwargs: Any) -> M_co: ...
  class IteratorOutputInfo (line 54) | class IteratorOutputInfo:
    method __init__ (line 55) | def __init__(
    method with_item_types (line 73) | def with_item_types(
    method to_dict (line 82) | def to_dict(self):
    method get_broadcast_sequence_type (line 89) | def get_broadcast_sequence_type(self, generator: Generator) -> navi.Ex...
    method get_broadcast_item_types (line 92) | def get_broadcast_item_types(
  class KeyInfo (line 102) | class KeyInfo:
    method __init__ (line 103) | def __init__(self, data: dict[str, Any]) -> None:
    method enum (line 107) | def enum(enum_input: InputId | int) -> KeyInfo:
    method number (line 111) | def number(number_input: InputId | int) -> KeyInfo:
    method type (line 115) | def type(expression: navi.ExpressionJson) -> KeyInfo:
    method to_dict (line 118) | def to_dict(self):
  class SpecialSuggestion (line 122) | class SpecialSuggestion:
    method __init__ (line 137) | def __init__(
    method _parse_query (line 149) | def _parse_query(query: str) -> tuple[str, InputId | None]:
    method to_dict (line 157) | def to_dict(self):
  class NodeData (line 174) | class NodeData:
    method single_iterable_input (line 200) | def single_iterable_input(self) -> IteratorInputInfo:
    method single_iterable_output (line 205) | def single_iterable_output(self) -> IteratorOutputInfo:

FILE: MangaJaNaiConverterGui/backend/src/api/output.py
  class BaseOutput (line 16) | class BaseOutput(Generic[T]):
    method __init__ (line 17) | def __init__(
    method to_dict (line 39) | def to_dict(self):
    method with_id (line 52) | def with_id(self, output_id: OutputId | int):
    method with_never_reason (line 56) | def with_never_reason(self, reason: str):
    method with_docs (line 60) | def with_docs(self, *description: str):
    method suggest (line 64) | def suggest(self):
    method as_passthrough_of (line 68) | def as_passthrough_of(self, input_id: InputId | int):
    method get_broadcast_data (line 72) | def get_broadcast_data(self, _value: T) -> BroadcastData | None:
    method get_broadcast_type (line 75) | def get_broadcast_type(self, _value: T) -> navi.ExpressionJson | None:
    method enforce (line 78) | def enforce(self, value: object) -> T:

FILE: MangaJaNaiConverterGui/backend/src/api/settings.py
  class ExecutionOptions (line 12) | class ExecutionOptions:
    method __init__ (line 13) | def __init__(
    method parse (line 23) | def parse(json: JsonExecutionOptions) -> ExecutionOptions:
    method get_package_settings_json (line 26) | def get_package_settings_json(self, package_id: str) -> SettingsJson:
    method get_package_settings (line 29) | def get_package_settings(self, package_id: str) -> SettingsParser:
  class SettingsParser (line 37) | class SettingsParser:
    method __init__ (line 38) | def __init__(self, raw: SettingsJson) -> None:
    method get_bool (line 41) | def get_bool(self, key: str, default: bool) -> bool:
    method get_int (line 47) | def get_int(self, key: str, default: int, parse_str: bool = False) -> ...
    method get_str (line 55) | def get_str(self, key: str, default: str) -> str:
    method get_cache_location (line 61) | def get_cache_location(self, key: str) -> str | None:
  class ToggleSetting (line 69) | class ToggleSetting:
  class DropdownOption (line 78) | class DropdownOption(TypedDict):
  class DropdownSetting (line 84) | class DropdownSetting:
  class NumberSetting (line 95) | class NumberSetting:
  class CacheSetting (line 107) | class CacheSetting:

FILE: MangaJaNaiConverterGui/backend/src/gpu.py
  class MemoryUsage (line 24) | class MemoryUsage:
  class NvDevice (line 31) | class NvDevice:
    method from_index (line 37) | def from_index(index: int) -> NvDevice:
    method architecture (line 47) | def architecture(self) -> int:
    method supports_fp16 (line 55) | def supports_fp16(self):
    method get_current_vram_usage (line 65) | def get_current_vram_usage(self) -> MemoryUsage:
  class NvInfo (line 70) | class NvInfo:
    method __init__ (line 71) | def __init__(
    method unavailable (line 78) | def unavailable():
    method __del__ (line 81) | def __del__(self) -> None:
    method devices (line 85) | def devices(self) -> Sequence[NvDevice]:
    method is_available (line 89) | def is_available(self):
    method all_support_fp16 (line 93) | def all_support_fp16(self) -> bool:
  function _try_nvml_init (line 97) | def _try_nvml_init() -> bool | None:
  function _try_nvml_shutdown (line 111) | def _try_nvml_shutdown() -> None:
  function _get_nvidia_info (line 118) | def _get_nvidia_info() -> NvInfo:

FILE: MangaJaNaiConverterGui/backend/src/navi.py
  function to_number_json (line 9) | def to_number_json(n: int | float) -> NumberJson:
  function from_number_json (line 19) | def from_number_json(n: NumberJson) -> int | float:
  class NumericLiteralTypeJson (line 49) | class NumericLiteralTypeJson(TypedDict):
  class IntervalTypeJson (line 54) | class IntervalTypeJson(TypedDict):
  class IntIntervalTypeJson (line 60) | class IntIntervalTypeJson(TypedDict):
  class StringLiteralTypeJson (line 66) | class StringLiteralTypeJson(TypedDict):
  class UnionExpressionJson (line 71) | class UnionExpressionJson(TypedDict):
  class IntersectionExpressionJson (line 76) | class IntersectionExpressionJson(TypedDict):
  class NamedExpressionJson (line 81) | class NamedExpressionJson(TypedDict):
  class FieldAccessExpressionJson (line 87) | class FieldAccessExpressionJson(TypedDict):
  class FunctionCallExpressionJson (line 93) | class FunctionCallExpressionJson(TypedDict):
  class MatchArmJson (line 99) | class MatchArmJson(TypedDict):
  class MatchExpressionJson (line 105) | class MatchExpressionJson(TypedDict):
  function literal (line 111) | def literal(value: str | (int | float)) -> ExpressionJson:
  function interval (line 123) | def interval(
  function int_interval (line 134) | def int_interval(
  function union (line 145) | def union(*items: ExpressionJson) -> ExpressionJson:
  function intersect (line 149) | def intersect(*items: ExpressionJson) -> ExpressionJson:
  function intersect_with_error (line 153) | def intersect_with_error(*items: ExpressionJson) -> ExpressionJson:
  function named (line 157) | def named(name: str, fields: dict[str, ExpressionJson] | None = None) ->...
  function field (line 161) | def field(of: ExpressionJson, field_name: str) -> ExpressionJson:
  function fn (line 165) | def fn(name: str, *args: ExpressionJson) -> ExpressionJson:
  function match (line 169) | def match(
  function Image (line 182) | def Image(  # noqa: N802
  function Color (line 210) | def Color(  # noqa: N802

FILE: MangaJaNaiConverterGui/backend/src/nodes/condition.py
  class _AndConditionJson (line 29) | class _AndConditionJson(TypedDict):
  class _OrConditionJson (line 34) | class _OrConditionJson(TypedDict):
  class _NotConditionJson (line 39) | class _NotConditionJson(TypedDict):
  class _EnumConditionJson (line 44) | class _EnumConditionJson(TypedDict):
  class _TypeConditionJson (line 50) | class _TypeConditionJson(TypedDict):
  class Condition (line 57) | class Condition:
    method __init__ (line 58) | def __init__(self, value: ConditionJson) -> None:
    method to_json (line 61) | def to_json(self):
    method __and__ (line 64) | def __and__(self, other: Condition) -> Condition:
    method __or__ (line 67) | def __or__(self, other: Condition) -> Condition:
    method __invert__ (line 70) | def __invert__(self) -> Condition:
    method enum (line 74) | def enum(enum: int, values: EnumValues) -> Condition:
    method bool (line 104) | def bool(input_id: int, value: bool) -> Condition:
    method type (line 117) | def type(
    method const (line 136) | def const(value: bool) -> Condition:

FILE: MangaJaNaiConverterGui/backend/src/nodes/group.py
  class GroupInfo (line 12) | class GroupInfo:
    method __init__ (line 13) | def __init__(
  class Group (line 24) | class Group(Generic[T]):
    method __init__ (line 25) | def __init__(self, info: GroupInfo, items: list[T]) -> None:
    method toDict (line 29) | def toDict(self):
  function group (line 43) | def group(kind: str, options: dict[str, Any] | None = None, id: int = -1):

FILE: MangaJaNaiConverterGui/backend/src/nodes/groups.py
  function if_group (line 14) | def if_group(condition: Condition):
  function if_enum_group (line 18) | def if_enum_group(enum: int, condition: EnumValues):
  function required (line 22) | def required(condition: Condition | None = None):
  function seed_group (line 52) | def seed_group(seed_input: BaseInput):
  function optional_list_group (line 67) | def optional_list_group(*inputs: BaseInput | NestedGroup):
  function linked_inputs_group (line 80) | def linked_inputs_group(*inputs: BaseInput):
  function ncnn_file_inputs_group (line 92) | def ncnn_file_inputs_group(param_input: BaseInput, bin_input: BaseInput):
  function from_to_dropdowns_group (line 99) | def from_to_dropdowns_group(from_dd: BaseInput, to_dd: BaseInput):
  function icon_set_group (line 107) | def icon_set_group(label: str):
  function menu_icon_row_group (line 115) | def menu_icon_row_group():

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/blend.py
  class BlendMode (line 10) | class BlendMode(Enum):
  function blend_mode_normalized (line 63) | def blend_mode_normalized(blend_mode: BlendMode) -> bool:
  class ImageBlender (line 70) | class ImageBlender:
    method __init__ (line 73) | def __init__(self) -> None:
    method apply_blend (line 100) | def apply_blend(
    method __normal (line 105) | def __normal(self, a: np.ndarray, _: np.ndarray) -> np.ndarray:
    method __multiply (line 108) | def __multiply(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __darken (line 111) | def __darken(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __lighten (line 114) | def __lighten(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __add (line 117) | def __add(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __color_burn (line 120) | def __color_burn(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __color_dodge (line 125) | def __color_dodge(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __reflect (line 128) | def __reflect(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __glow (line 131) | def __glow(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __overlay (line 134) | def __overlay(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __difference (line 137) | def __difference(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __negation (line 140) | def __negation(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __screen (line 143) | def __screen(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __xor (line 146) | def __xor(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __subtract (line 151) | def __subtract(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __divide (line 154) | def __divide(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __exclusion (line 157) | def __exclusion(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __soft_light (line 160) | def __soft_light(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __hard_light (line 165) | def __hard_light(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __vivid_light (line 168) | def __vivid_light(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __linear_light (line 173) | def __linear_light(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __pin_light (line 176) | def __pin_light(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
    method __linear_burn (line 181) | def __linear_burn(self, a: np.ndarray, b: np.ndarray) -> np.ndarray:
  function blend_images (line 185) | def blend_images(overlay: np.ndarray, base: np.ndarray, blend_mode: Blen...

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/color/color.py
  function _norm (line 13) | def _norm(n: FloatLike) -> float:
  class ColorJson (line 20) | class ColorJson(TypedDict):
  class Color (line 25) | class Color:
    method __init__ (line 26) | def __init__(self, value: tuple[float, ...]) -> None:
    method channels (line 31) | def channels(self) -> int:
    method gray (line 35) | def gray(gray: FloatLike) -> Color:
    method bgr (line 39) | def bgr(value: Iterable[FloatLike]) -> Color:
    method bgra (line 45) | def bgra(value: Iterable[FloatLike]) -> Color:
    method from_1x1_image (line 51) | def from_1x1_image(img: np.ndarray) -> Color:
    method from_json (line 65) | def from_json(color_json: ColorJson | str) -> Color:
    method to_1x1_image (line 83) | def to_1x1_image(self) -> np.ndarray:
    method to_image (line 86) | def to_image(self, width: int, height: int) -> np.ndarray:
    method to_json (line 93) | def to_json(self) -> ColorJson:

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert.py
  function color_space_from_id (line 20) | def color_space_from_id(id_: int) -> ColorSpace:
  function color_space_or_detector_from_id (line 27) | def color_space_or_detector_from_id(id_: int) -> ColorSpace | ColorSpace...
  class __ProcessingItem (line 38) | class __ProcessingItem(Generic[T]):  # noqa: N801
  function get_shortest_path (line 43) | def get_shortest_path(
  function convert (line 94) | def convert(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert_data.py
  function is_alpha_partner (line 43) | def is_alpha_partner(c: ColorSpace) -> bool:
  function get_alpha_partner (line 50) | def get_alpha_partner(c: ColorSpace) -> ColorSpace | None:
  function __rev3 (line 87) | def __rev3(image: np.ndarray) -> np.ndarray:
  function __rgb_to_hsv (line 93) | def __rgb_to_hsv(img: np.ndarray) -> np.ndarray:
  function __hsv_to_rgb (line 99) | def __hsv_to_rgb(img: np.ndarray) -> np.ndarray:
  function __rgb_to_hsl (line 105) | def __rgb_to_hsl(img: np.ndarray) -> np.ndarray:
  function __hsl_to_rgb (line 113) | def __hsl_to_rgb(img: np.ndarray) -> np.ndarray:
  function __hsv_to_hsl (line 120) | def __hsv_to_hsl(img: np.ndarray) -> np.ndarray:
  function __hsl_to_hsv (line 129) | def __hsl_to_hsv(img: np.ndarray) -> np.ndarray:
  function __rgb_to_cmyk (line 138) | def __rgb_to_cmyk(img: np.ndarray) -> np.ndarray:
  function __cmyk_to_rgb (line 149) | def __cmyk_to_rgb(img: np.ndarray) -> np.ndarray:
  function __rgb_to_lab (line 158) | def __rgb_to_lab(img: np.ndarray) -> np.ndarray:
  function __lab_to_rgb (line 167) | def __lab_to_rgb(img: np.ndarray) -> np.ndarray:
  function __lab_to_lch (line 175) | def __lab_to_lch(img: np.ndarray) -> np.ndarray:
  function __lch_to_lab (line 211) | def __lch_to_lab(img: np.ndarray) -> np.ndarray:
  function create_convert (line 350) | def create_convert(old_conv: Conversion):

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert_model.py
  class ColorSpace (line 11) | class ColorSpace:
    method __init__ (line 12) | def __init__(self, id_: int, name: str, channels: int) -> None:
  class ColorSpaceDetector (line 19) | class ColorSpaceDetector:
    method __init__ (line 20) | def __init__(self, id_: int, name: str, color_spaces: Iterable[ColorSp...
    method detect (line 30) | def detect(self, image: np.ndarray) -> ColorSpace:
  function assert_input_channels (line 43) | def assert_input_channels(
  function assert_output_channels (line 55) | def assert_output_channels(
  class Conversion (line 72) | class Conversion:
    method __init__ (line 73) | def __init__(
    method convert (line 87) | def convert(self, img: np.ndarray) -> np.ndarray:

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/image_formats.py
  function get_opencv_formats (line 1) | def get_opencv_formats():
  function get_pil_formats (line 33) | def get_pil_formats():
  function get_available_image_formats (line 76) | def get_available_image_formats():

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/image_op.py
  function clipped (line 15) | def clipped(op: ImageOp) -> ImageOp:
  function to_op (line 25) | def to_op(fn: Callable[Concatenate[np.ndarray, P], np.ndarray]) -> Calla...

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/image_utils.py
  class FillColor (line 28) | class FillColor(Enum):
    method get_color (line 33) | def get_color(self, channels: int):
  class FlipAxis (line 46) | class FlipAxis(Enum):
    method flip (line 52) | def flip(self, img: np.ndarray) -> np.ndarray:
  class BorderType (line 58) | class BorderType(Enum):
  class NormalMapType (line 68) | class NormalMapType(Enum):
  function convert_to_bgra (line 74) | def convert_to_bgra(img: np.ndarray, in_c: int) -> np.ndarray:
  function _get_iinfo (line 84) | def _get_iinfo(img: np.ndarray) -> np.iinfo | None:
  function normalize (line 91) | def normalize(img: np.ndarray) -> np.ndarray:
  function to_uint8 (line 108) | def to_uint8(img: np.ndarray, normalized: bool = False) -> np.ndarray:
  function to_uint16 (line 123) | def to_uint16(img: np.ndarray, normalized: bool = False) -> np.ndarray:
  class ShiftFill (line 138) | class ShiftFill(Enum):
    method to_fill_color (line 144) | def to_fill_color(self) -> FillColor:
  function shift (line 154) | def shift(
  function as_2d_grayscale (line 190) | def as_2d_grayscale(img: np.ndarray) -> np.ndarray:
  function as_3d (line 199) | def as_3d(img: np.ndarray) -> np.ndarray:
  function as_target_channels (line 206) | def as_target_channels(
  function create_border (line 249) | def create_border(
  function calculate_ssim (line 301) | def calculate_ssim(
  function cv_save_image (line 330) | def cv_save_image(path: Path | str, img: np.ndarray, params: list[int]) ...
  function cartesian_product (line 345) | def cartesian_product(arrays: list[np.ndarray]) -> np.ndarray:
  function fast_gaussian_blur (line 361) | def fast_gaussian_blur(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/auto_split.py
  function _into_batched_form (line 13) | def _into_batched_form(img: np.ndarray, change_shape: bool) -> np.ndarray:
  function _into_standard_image_form (line 33) | def _into_standard_image_form(img: np.ndarray, change_shape: bool) -> np...
  function _flip_r_b_channels (line 56) | def _flip_r_b_channels(img: np.ndarray) -> np.ndarray:
  function _pad (line 69) | def _pad(
  function onnx_auto_split (line 97) | def onnx_auto_split(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/load.py
  function _detect_size_req (line 30) | def _detect_size_req(infer: ModelShapeInference):
  function load_onnx_model (line 47) | def load_onnx_model(model_or_bytes: onnx.ModelProto | bytes) -> OnnxModel:

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/model.py
  function _ceil_div (line 11) | def _ceil_div(a: int, b: int):
  class SizeReq (line 16) | class SizeReq:
    method __init__ (line 20) | def __init__(self, minimum: int = 1, multiple_of: int = 1) -> None:
    method get_padding (line 29) | def get_padding(self, width: int, height: int) -> tuple[int, int]:
  class OnnxInfo (line 49) | class OnnxInfo:
  class OnnxGeneric (line 65) | class OnnxGeneric:
    method __init__ (line 66) | def __init__(self, model_as_bytes: bytes, info: OnnxInfo) -> None:
  class OnnxRemBg (line 72) | class OnnxRemBg:
    method __init__ (line 73) | def __init__(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/np_tensor_utils.py
  function np_denorm (line 8) | def np_denorm(x: np.ndarray, min_max: tuple[float, float] = (-1.0, 1.0))...
  function np_norm (line 19) | def np_norm(x: np.ndarray) -> np.ndarray:
  function np_bgr_to_rgb (line 25) | def np_bgr_to_rgb(img: np.ndarray) -> np.ndarray:
  function np_rgb_to_bgr (line 30) | def np_rgb_to_bgr(img: np.ndarray) -> np.ndarray:
  function np_bgra_to_rgba (line 35) | def np_bgra_to_rgba(img: np.ndarray) -> np.ndarray:
  function np_rgba_to_bgra (line 40) | def np_rgba_to_bgra(img: np.ndarray) -> np.ndarray:
  function np2nptensor (line 45) | def np2nptensor(
  function nptensor2np (line 87) | def nptensor2np(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/onnx_to_ncnn.py
  class Onnx2NcnnConverter (line 58) | class Onnx2NcnnConverter:
    method __init__ (line 59) | def __init__(self, onnx_model: ModelProto) -> None:
    method add_weight (line 72) | def add_weight(
    method clear_container (line 84) | def clear_container(
    method swap_nodes (line 90) | def swap_nodes(self, a: int, b: int) -> None:
    method fuse_rewrite_gather (line 96) | def fuse_rewrite_gather(self) -> None:
    method fuse_weight_reshape (line 117) | def fuse_weight_reshape(self, reduced_node_count: list[int]) -> None:
    method fuse_weight_transpose (line 143) | def fuse_weight_transpose(self, reduced_node_count: list[int]) -> None:
    method fuse_shufflechannel (line 179) | def fuse_shufflechannel(self, reduced_node_count: list[int]) -> None:
    method fuse_shufflechannel_split (line 283) | def fuse_shufflechannel_split(self, reduced_node_count: list[int]) -> ...
    method fuse_hardswish (line 344) | def fuse_hardswish(self, reduced_node_count: list[int]) -> None:
    method fuse_hardsigmoid (line 495) | def fuse_hardsigmoid(self, reduced_node_count: list[int]) -> None:
    method fuse_swish (line 592) | def fuse_swish(self, reduced_node_count: list[int]) -> None:
    method fuse_batchnorm1d_squeeze_unsqueeze (line 624) | def fuse_batchnorm1d_squeeze_unsqueeze(self, reduced_node_count: list[...
    method fuse_unsqueeze_prelu (line 662) | def fuse_unsqueeze_prelu(self, reduced_node_count: list[int]) -> None:
    method fuse_normalize (line 702) | def fuse_normalize(self, reduced_node_count: list[int]) -> None:
    method fuse_groupnorm (line 795) | def fuse_groupnorm(self, reduced_node_count: list[int]) -> None:
    method fuse_layernorm (line 929) | def fuse_layernorm(self, reduced_node_count: list[int]) -> None:
    method fuse_flatten (line 1110) | def fuse_flatten(self, reduced_node_count: list[int]) -> None:
    method fuse_pixelshuffle (line 1222) | def fuse_pixelshuffle(self, reduced_node_count: list[int]) -> None:
    method fuse_reorg (line 1320) | def fuse_reorg(self, reduced_node_count: list[int]) -> None:
    method fuse_expand_broadcast (line 1415) | def fuse_expand_broadcast(self, reduced_node_count: list[int]) -> None:
    method fuse_lstm_gru_rnn (line 1451) | def fuse_lstm_gru_rnn(self, reduced_node_count: list[int]) -> None:
    method fuse_multiheadattention (line 1651) | def fuse_multiheadattention(self, reduced_node_count: list[int]) -> None:
    method fuse_binaryop_with_scalar (line 2230) | def fuse_binaryop_with_scalar(self) -> None:
    method convert (line 2294) | def convert(self, is_fp16: bool = False, include_mem_data: bool = True):

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/session.py
  function create_inference_session (line 14) | def create_inference_session(
  function get_onnx_session (line 57) | def get_onnx_session(
  function get_input_shape (line 77) | def get_input_shape(session: ort.InferenceSession) -> OnnxParsedTensorSh...
  function get_output_shape (line 85) | def get_output_shape(session: ort.InferenceSession) -> OnnxParsedTensorS...

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/tensorproto_utils.py
  function get_node_attr_ai (line 15) | def get_node_attr_ai(node: NodeProto, key: str) -> np.ndarray:
  function set_node_attr_ai (line 25) | def set_node_attr_ai(node: NodeProto, key: str, value: np.ndarray) -> None:
  function get_node_attr_af (line 30) | def get_node_attr_af(node: NodeProto, key: str) -> np.ndarray:
  function get_node_attr_i (line 38) | def get_node_attr_i(node: NodeProto, key: str, default: int = 0) -> int:
  function get_node_attr_f (line 46) | def get_node_attr_f(node: NodeProto, key: str, default: float = 0) -> fl...
  function get_node_attr_s (line 54) | def get_node_attr_s(node: NodeProto, key: str, default: str = ""):
  function get_node_attr_tensor (line 62) | def get_node_attr_tensor(node: NodeProto, key: str) -> TensorProto:
  function get_node_attr_from_input_f (line 70) | def get_node_attr_from_input_f(tp: TensorProto) -> float:
  function get_node_attr_from_input_ai (line 83) | def get_node_attr_from_input_ai(tp: TensorProto) -> np.ndarray:
  function get_node_attr_from_input_af (line 103) | def get_node_attr_from_input_af(tp: TensorProto) -> np.ndarray:
  function get_tensor_proto_data_size (line 113) | def get_tensor_proto_data_size(tp: TensorProto, fpmode: int = TPT.FLOAT)...

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/update_model_dims.py
  function update_inputs_outputs_dims (line 15) | def update_inputs_outputs_dims(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/utils.py
  function _as_int (line 32) | def _as_int(value: object) -> int | None:
  function _or_else (line 38) | def _or_else(value: int | None, default: int) -> int:
  function parse_onnx_shape (line 42) | def parse_onnx_shape(shape: OnnxTensorShape) -> OnnxParsedTensorShape:
  function to_onnx_tensor_shape (line 51) | def to_onnx_tensor_shape(
  function is_tensor_input (line 63) | def is_tensor_input(input: ValueInfoProto) -> bool:
  function is_image_to_image (line 67) | def is_image_to_image(model: ModelProto) -> bool:
  class ModelShapeInference (line 79) | class ModelShapeInference:
    method __init__ (line 80) | def __init__(self, model: ModelProto) -> None:
    method infer_shape (line 105) | def infer_shape(
  function get_tensor_fp_datatype (line 165) | def get_tensor_fp_datatype(model: ModelProto) -> str:
  function get_opset (line 180) | def get_opset(model: onnx.ModelProto) -> int:
  function safely_optimize_onnx_model (line 187) | def safely_optimize_onnx_model(model_proto: ModelProto) -> ModelProto:

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pil_utils.py
  class InterpolationMethod (line 12) | class InterpolationMethod(Enum):
  class RotationInterpolationMethod (line 21) | class RotationInterpolationMethod(Enum):
    method interpolation_method (line 27) | def interpolation_method(self) -> InterpolationMethod:
  class RotateSizeChange (line 40) | class RotateSizeChange(Enum):
  function resize (line 45) | def resize(
  function rotate (line 66) | def rotate(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/auto_split.py
  function _into_standard_image_form (line 17) | def _into_standard_image_form(t: torch.Tensor) -> torch.Tensor:
  function _into_batched_form (line 31) | def _into_batched_form(t: torch.Tensor) -> torch.Tensor:
  function _rgb_to_bgr (line 42) | def _rgb_to_bgr(t: torch.Tensor) -> torch.Tensor:
  function _into_tensor (line 53) | def _into_tensor(
  function pytorch_auto_split (line 82) | def pytorch_auto_split(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/convert_to_onnx_impl.py
  function is_onnx_supported (line 10) | def is_onnx_supported(model: ModelDescriptor) -> bool:
  function convert_to_onnx_impl (line 14) | def convert_to_onnx_impl(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/auto_split.py
  class _PixTiler (line 18) | class _PixTiler(Tiler):
    method __init__ (line 19) | def __init__(self, max_tile_size: int = 2048) -> None:
    method allow_smaller_tile_size (line 22) | def allow_smaller_tile_size(self) -> bool:
    method starting_tile_size (line 25) | def starting_tile_size(self, width: int, height: int, channels: int) -...
    method split (line 29) | def split(self, tile_size: Size) -> Size:
  function _as_3d (line 37) | def _as_3d(img: np.ndarray) -> np.ndarray:
  function pix_transform_auto_split (line 43) | def pix_transform_auto_split(
  function _run_pix_transform (line 96) | def _run_pix_transform():

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/pix_transform.py
  class Params (line 15) | class Params:
  function pix_transform (line 25) | def pix_transform(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/pix_transform_net.py
  class PixTransformNet (line 6) | class PixTransformNet(nn.Module):
    method __init__ (line 7) | def __init__(
    method forward (line 60) | def forward(self, input_):  # noqa: ANN001

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/rife/IFNet_HDv3_v4_14_align.py
  function conv (line 20) | def conv(in_planes, out_planes, kernel_size=3, stride=1, padding=1, dila...
  function conv_bn (line 35) | def conv_bn(in_planes, out_planes, kernel_size=3, stride=1, padding=1, d...
  class Head (line 51) | class Head(nn.Module):
    method __init__ (line 52) | def __init__(self) -> None:
    method forward (line 60) | def forward(self, x, feat=False):  # noqa: ANN001
  class ResConv (line 73) | class ResConv(nn.Module):
    method __init__ (line 74) | def __init__(self, c, dilation=1) -> None:  # noqa: ANN001
    method forward (line 80) | def forward(self, x):  # noqa: ANN001
  class IFBlock (line 84) | class IFBlock(nn.Module):
    method __init__ (line 85) | def __init__(self, in_planes, c=64) -> None:  # noqa: ANN001
    method forward (line 105) | def forward(self, x, flow=None, scale=1):  # noqa: ANN001
  class IFNet (line 129) | class IFNet(nn.Module):
    method __init__ (line 130) | def __init__(self) -> None:
    method align_images (line 138) | def align_images(
    method forward (line 241) | def forward(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/rife/warplayer.py
  function warp (line 7) | def warp(tenInput, tenFlow, device):  # noqa: ANN001, N803

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/utils.py
  function bgr_to_rgb (line 11) | def bgr_to_rgb(image: Tensor) -> Tensor:
  function rgb_to_bgr (line 20) | def rgb_to_bgr(image: Tensor) -> Tensor:
  function bgra_to_rgba (line 25) | def bgra_to_rgba(image: Tensor) -> Tensor:
  function rgba_to_bgra (line 30) | def rgba_to_bgra(image: Tensor) -> Tensor:
  function norm (line 35) | def norm(x: Tensor):
  function np2tensor (line 41) | def np2tensor(
  function tensor2np (line 85) | def tensor2np(
  function safe_cuda_cache_empty (line 149) | def safe_cuda_cache_empty() -> None:
  function safe_accelerator_cache_empty (line 162) | def safe_accelerator_cache_empty(device: torch.device) -> None:

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/resize.py
  class ResizeFilter (line 12) | class ResizeFilter(Enum):
  function resize (line 45) | def resize(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split.py
  class Split (line 15) | class Split:
  function auto_split (line 22) | def auto_split(
  class _SplitEx (line 54) | class _SplitEx(Exception):
  function _exact_split (line 58) | def _exact_split(
  function _max_split (line 93) | def _max_split(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split_tiles.py
  function estimate_tile_size (line 13) | def estimate_tile_size(
  function parse_tile_size_input (line 46) | def parse_tile_size_input(tile_size: TileSize, estimate: Callable[[], Ti...

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/basic_upscale.py
  class UpscaleInfo (line 15) | class UpscaleInfo:
    method supports_custom_scale (line 21) | def supports_custom_scale(self) -> bool:
  class PaddingType (line 25) | class PaddingType(Enum):
    method to_border_type (line 31) | def to_border_type(self) -> BorderType:
  function _custom_scale_upscale (line 49) | def _custom_scale_upscale(
  function basic_upscale (line 83) | def basic_upscale(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/convenient_upscale.py
  function with_black_and_white_backgrounds (line 10) | def with_black_and_white_backgrounds(img: np.ndarray) -> tuple[np.ndarra...
  function denoise_and_flatten_alpha (line 23) | def denoise_and_flatten_alpha(img: np.ndarray) -> np.ndarray:
  function convenient_upscale (line 31) | def convenient_upscale(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/custom_scale.py
  function custom_scale_upscale (line 9) | def custom_scale_upscale(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/exact_split.py
  function _pad_image (line 15) | def _pad_image(img: np.ndarray, min_size: Size):
  class _Segment (line 28) | class _Segment:
    method length (line 35) | def length(self) -> int:
    method padded_length (line 39) | def padded_length(self) -> int:
  function _exact_split_into_segments (line 43) | def _exact_split_into_segments(length: int, exact: int, overlap: int) ->...
  function _exact_split_into_regions (line 88) | def _exact_split_into_regions(
  function _exact_split_without_padding (line 125) | def _exact_split_without_padding(
  function exact_split (line 200) | def exact_split(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/grayscale.py
  class SplitMode (line 12) | class SplitMode(Enum):
    method split (line 16) | def split(self, img: np.ndarray) -> list[np.ndarray]:
    method combine (line 42) | def combine(self, channels: list[np.ndarray]) -> np.ndarray:
  function grayscale_split (line 62) | def grayscale_split(

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/passthrough.py
  function passthrough_single_color (line 7) | def passthrough_single_color(img: np.ndarray, scale: int, op: ImageOp) -...

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/tile_blending.py
  function sin_blend_fn (line 12) | def sin_blend_fn(x: np.ndarray) -> np.ndarray:
  function half_sin_blend_fn (line 16) | def half_sin_blend_fn(i: np.ndarray) -> np.ndarray:
  class BlendDirection (line 22) | class BlendDirection(Enum):
  class TileOverlap (line 28) | class TileOverlap:
    method total (line 33) | def total(self) -> int:
  function _fast_mix (line 37) | def _fast_mix(a: np.ndarray, b: np.ndarray, blend: np.ndarray) -> np.nda...
  class TileBlender (line 49) | class TileBlender:
    method __init__ (line 50) | def __init__(
    method width (line 81) | def width(self) -> int:
    method height (line 85) | def height(self) -> int:
    method channels (line 89) | def channels(self) -> int:
    method _get_blend (line 92) | def _get_blend(self, blend_size: int) -> np.ndarray:
    method add_tile (line 117) | def add_tile(self, tile: np.ndarray, overlap: TileOverlap) -> None:
    method get_result (line 207) | def get_result(self) -> np.ndarray:

FILE: MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/tiler.py
  class Tiler (line 6) | class Tiler(ABC):
    method allow_smaller_tile_size (line 8) | def allow_smaller_tile_size(self) -> bool:
    method starting_tile_size (line 17) | def starting_tile_size(self, width: int, height: int, channels: int) -...
    method split (line 24) | def split(self, tile_size: Size) -> Size:
  class NoTiling (line 30) | class NoTiling(Tiler):
    method allow_smaller_tile_size (line 31) | def allow_smaller_tile_size(self) -> bool:
    method starting_tile_size (line 34) | def starting_tile_size(self, width: int, height: int, channels: int) -...
    method split (line 39) | def split(self, tile_size: Size) -> Size:
  class MaxTileSize (line 43) | class MaxTileSize(Tiler):
    method __init__ (line 44) | def __init__(self, tile_size: int = 2**31) -> None:
    method allow_smaller_tile_size (line 47) | def allow_smaller_tile_size(self) -> bool:
    method starting_tile_size (line 50) | def starting_tile_size(self, width: int, height: int, channels: int) -...
  class ExactTileSize (line 58) | class ExactTileSize(Tiler):
    method __init__ (line 59) | def __init__(self, exact_size: Size) -> None:
    method allow_smaller_tile_size (line 62) | def allow_smaller_tile_size(self) -> bool:
    method starting_tile_size (line 65) | def starting_tile_size(self, width: int, height: int, channels: int) -...
    method split (line 68) | def split(self, tile_size: Size) -> Size:

FILE: MangaJaNaiConverterGui/backend/src/nodes/node_cache.py
  class CachedNumpyArray (line 21) | class CachedNumpyArray:
    method __init__ (line 22) | def __init__(self, arr: np.ndarray) -> None:
    method value (line 29) | def value(self) -> np.ndarray:
  class NodeOutputCache (line 37) | class NodeOutputCache:
    method __init__ (line 38) | def __init__(self) -> None:
    method _args_to_key (line 46) | def _args_to_key(args: Iterable[object]) -> CacheKey:
    method _estimate_bytes (line 67) | def _estimate_bytes(output: list[object]) -> int:
    method empty (line 78) | def empty(self) -> bool:
    method oldest (line 81) | def oldest(self) -> tuple[CacheKey, float]:
    method size (line 84) | def size(self):
    method _enforce_limits (line 88) | def _enforce_limits() -> None:
    method _write_arrays_to_disk (line 106) | def _write_arrays_to_disk(output: list) -> list:
    method _read_arrays_from_disk (line 113) | def _read_arrays_from_disk(output: list) -> list:
    method _output_to_list (line 120) | def _output_to_list(output: object) -> list[object]:
    method _list_to_output (line 129) | def _list_to_output(output: list[object]):
    method get (line 134) | def get(self, args: Iterable[object]) -> object | None:
    method put (line 143) | def put(self, args: Iterable[object], output: object) -> None:
    method drop (line 150) | def drop(self, key: CacheKey) -> None:
  function cached (line 156) | def cached(run: RunFn):

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/__system_inputs.py
  class StaticValueInput (line 11) | class StaticValueInput(BaseInput):
    method __init__ (line 12) | def __init__(
    method to_dict (line 24) | def to_dict(self):
    method enforce (line 30) | def enforce(self, value: object):

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/file_inputs.py
  class FileInput (line 27) | class FileInput(BaseInput):
    method __init__ (line 30) | def __init__(
    method to_dict (line 57) | def to_dict(self):
    method enforce (line 65) | def enforce(self, value: object) -> Path:
  function ImageFileInput (line 74) | def ImageFileInput(primary_input: bool = False) -> FileInput:
  function VideoFileInput (line 85) | def VideoFileInput(primary_input: bool = False) -> FileInput:
  function PthFileInput (line 108) | def PthFileInput(primary_input: bool = False) -> FileInput:
  class DirectoryInput (line 118) | class DirectoryInput(BaseInput[Path]):
    method __init__ (line 121) | def __init__(
    method to_dict (line 142) | def to_dict(self):
    method enforce (line 148) | def enforce(self, value: object) -> Path:
  function BinFileInput (line 159) | def BinFileInput(primary_input: bool = False) -> FileInput:
  function ParamFileInput (line 169) | def ParamFileInput(primary_input: bool = False) -> FileInput:
  function OnnxFileInput (line 179) | def OnnxFileInput(primary_input: bool = False) -> FileInput:
  function _is_abs_path (line 192) | def _is_abs_path(path: str) -> bool:
  class RelativePathInput (line 196) | class RelativePathInput(TextInput):
    method __init__ (line 197) | def __init__(
    method enforce (line 220) | def enforce(self, value: object) -> str:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/generic_inputs.py
  class DropDownOption (line 38) | class DropDownOption(TypedDict):
  class DropDownGroup (line 60) | class DropDownGroup:
    method divider (line 65) | def divider(start_at: str | int | Enum):
    method to_dict (line 68) | def to_dict(self):
  class DropDownInput (line 78) | class DropDownInput(BaseInput[T]):
    method __init__ (line 81) | def __init__(
    method to_dict (line 112) | def to_dict(self):
    method make_optional (line 122) | def make_optional(self) -> Never:
    method enforce (line 125) | def enforce(self, value: object) -> T:
    method wrap_with_conditional_group (line 129) | def wrap_with_conditional_group(self):
  class _BoolEnumInput (line 150) | class _BoolEnumInput(DropDownInput[bool]):
    method __init__ (line 151) | def __init__(
    method enforce (line 175) | def enforce(self, value: object) -> bool:
  class _BoolGenericInput (line 180) | class _BoolGenericInput(BaseInput[bool]):
    method __init__ (line 181) | def __init__(self, label: str) -> None:
    method enforce (line 185) | def enforce(self, value: object) -> bool:
  function BoolInput (line 196) | def BoolInput(
  class EnumInput (line 211) | class EnumInput(DropDownInput[E]):
    method __init__ (line 232) | def __init__(
    method get_variant_type (line 305) | def get_variant_type(variant: Enum, type_name: str | None = None) -> str:
    method enforce (line 320) | def enforce(self, value: object) -> E:
  class TextInput (line 325) | class TextInput(BaseInput[str]):
    method __init__ (line 328) | def __init__(
    method enforce (line 368) | def enforce(self, value: object) -> str:
    method to_dict (line 386) | def to_dict(self):
  class ClipboardInput (line 400) | class ClipboardInput(BaseInput):
    method __init__ (line 403) | def __init__(self, label: str = "Clipboard input") -> None:
    method enforce (line 409) | def enforce(self, value: object):
    method to_dict (line 419) | def to_dict(self):
  class AnyInput (line 426) | class AnyInput(BaseInput[object]):
    method __init__ (line 427) | def __init__(self, label: str) -> None:
    method enforce_ (line 431) | def enforce_(self, value: object):
  class SeedInput (line 436) | class SeedInput(NumberInput):
    method __init__ (line 437) | def __init__(self, label: str = "Seed", *, has_handle: bool = True) ->...
    method enforce (line 459) | def enforce(self, value: object) -> Seed:  # type: ignore
    method make_optional (line 466) | def make_optional(self) -> Never:
  class ColorInput (line 470) | class ColorInput(BaseInput[Color]):
    method __init__ (line 471) | def __init__(
    method enforce (line 519) | def enforce(self, value: object) -> Color:
    method to_dict (line 535) | def to_dict(self):
    method make_optional (line 542) | def make_optional(self) -> Never:
  function BlendModeDropdown (line 546) | def BlendModeDropdown() -> DropDownInput:
  function FillColorDropdown (line 562) | def FillColorDropdown() -> DropDownInput:
  function TileSizeDropdown (line 578) | def TileSizeDropdown(
  class AudioStreamInput (line 602) | class AudioStreamInput(BaseInput):
    method __init__ (line 603) | def __init__(self, label: str = "Audio Stream") -> None:
  class OrderEnum (line 607) | class OrderEnum(Enum):
  function RowOrderDropdown (line 612) | def RowOrderDropdown() -> DropDownInput:
  class Anchor (line 620) | class Anchor(Enum):
  function AnchorInput (line 632) | def AnchorInput(label: str = "Anchor", icon: str = "BsFillImageFill") ->...

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/image_dropdown_inputs.py
  function ColorSpaceDetectorInput (line 17) | def ColorSpaceDetectorInput(label: str = "Color Space") -> DropDownInput:
  function ColorSpaceInput (line 32) | def ColorSpaceInput(label: str = "Color Space") -> DropDownInput:
  function ResizeFilterInput (line 54) | def ResizeFilterInput() -> DropDownInput:
  function RotateInterpolationInput (line 71) | def RotateInterpolationInput() -> DropDownInput:
  function BorderInput (line 81) | def BorderInput() -> DropDownInput:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/label.py
  function get_default_label_style (line 6) | def get_default_label_style(label: str) -> LabelStyle:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/ncnn_inputs.py
  class NcnnModelInput (line 6) | class NcnnModelInput(BaseInput):
    method __init__ (line 9) | def __init__(self, label: str = "Model") -> None:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numeric_inputs.py
  function _get_step (line 16) | def _get_step(precision: Precision) -> float:
  function _is_int (line 22) | def _is_int(precision: Precision) -> bool:
  function clamp_number (line 26) | def clamp_number(
  function get_number_type (line 49) | def get_number_type(
  class NumberInput (line 60) | class NumberInput(BaseInput):
    method __init__ (line 63) | def __init__(
    method to_dict (line 97) | def to_dict(self):
    method make_optional (line 112) | def make_optional(self) -> Never:
    method enforce (line 115) | def enforce(self, value: object):
  class SliderInput (line 124) | class SliderInput(NumberInput):
    method __init__ (line 127) | def __init__(
    method to_dict (line 167) | def to_dict(self):

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numpy_inputs.py
  class AudioInput (line 16) | class AudioInput(BaseInput):
    method __init__ (line 19) | def __init__(self, label: str = "Audio") -> None:
  class ImageInput (line 23) | class ImageInput(BaseInput):
    method __init__ (line 26) | def __init__(
    method enforce (line 50) | def enforce(self, value: object):
    method get_error_value (line 83) | def get_error_value(self, value: object) -> ErrorValue:
  class VideoInput (line 108) | class VideoInput(BaseInput):
    method __init__ (line 111) | def __init__(self, label: str = "Video") -> None:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/onnx_inputs.py
  class OnnxModelInput (line 9) | class OnnxModelInput(BaseInput):
    method __init__ (line 12) | def __init__(
  class OnnxGenericModelInput (line 19) | class OnnxGenericModelInput(OnnxModelInput):
    method __init__ (line 22) | def __init__(
    method enforce (line 28) | def enforce(self, value: object):
  class OnnxRemBgModelInput (line 34) | class OnnxRemBgModelInput(OnnxModelInput):
    method __init__ (line 37) | def __init__(
    method enforce (line 43) | def enforce(self, value: object):
  function OnnxFpDropdown (line 49) | def OnnxFpDropdown() -> DropDownInput:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/pytorch_inputs.py
  function _model_with_purpose (line 14) | def _model_with_purpose(purpose: set[Purpose]):
  class ModelInput (line 19) | class ModelInput(BaseInput):
    method __init__ (line 22) | def __init__(
    method enforce (line 31) | def enforce(self, value: object):
  class SrModelInput (line 40) | class SrModelInput(ModelInput):
    method __init__ (line 41) | def __init__(
    method enforce (line 55) | def enforce(self, value: object):
  class FaceModelInput (line 66) | class FaceModelInput(ModelInput):
    method __init__ (line 67) | def __init__(
    method enforce (line 79) | def enforce(self, value: object):
  class InpaintModelInput (line 90) | class InpaintModelInput(ModelInput):
    method __init__ (line 91) | def __init__(
    method enforce (line 103) | def enforce(self, value: object):
  class TorchScriptInput (line 112) | class TorchScriptInput(BaseInput):
    method __init__ (line 115) | def __init__(self, label: str = "Traced Model") -> None:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/file_outputs.py
  class DirectoryOutput (line 10) | class DirectoryOutput(BaseOutput[Path]):
    method __init__ (line 13) | def __init__(
    method get_broadcast_type (line 27) | def get_broadcast_type(self, value: Path):
    method enforce (line 30) | def enforce(self, value: object) -> Path:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/generic_outputs.py
  class NumberOutput (line 14) | class NumberOutput(BaseOutput[Union[int, float]]):
    method __init__ (line 15) | def __init__(
    method get_broadcast_type (line 26) | def get_broadcast_type(self, value: int | float):
    method enforce (line 29) | def enforce(self, value: object) -> int | float:
  class TextOutput (line 34) | class TextOutput(BaseOutput):
    method __init__ (line 35) | def __init__(
    method get_broadcast_type (line 42) | def get_broadcast_type(self, value: str):
    method enforce (line 45) | def enforce(self, value: object) -> str:
  function FileNameOutput (line 50) | def FileNameOutput(label: str = "Name", of_input: int | None = None):
  class SeedOutput (line 60) | class SeedOutput(BaseOutput):
    method __init__ (line 61) | def __init__(self, label: str = "Seed") -> None:
    method enforce (line 64) | def enforce(self, value: object) -> Seed:
  class ColorOutput (line 69) | class ColorOutput(BaseOutput):
    method __init__ (line 70) | def __init__(
    method enforce (line 86) | def enforce(self, value: object) -> Color:
  class BoolOutput (line 101) | class BoolOutput(BaseOutput):
    method __init__ (line 102) | def __init__(
  class AudioStreamOutput (line 115) | class AudioStreamOutput(BaseOutput):
    method __init__ (line 116) | def __init__(self, label: str = "Audio Stream") -> None:
  class AnyOutput (line 124) | class AnyOutput(BaseOutput):
    method __init__ (line 125) | def __init__(
    method enforce (line 134) | def enforce(self, value: object) -> object:

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/ncnn_outputs.py
  class NcnnModelOutput (line 9) | class NcnnModelOutput(BaseOutput):
    method __init__ (line 10) | def __init__(
    method get_broadcast_data (line 18) | def get_broadcast_data(self, value: NcnnModelWrapper):
    method get_broadcast_type (line 27) | def get_broadcast_type(self, value: NcnnModelWrapper):

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/numpy_outputs.py
  class NumPyOutput (line 17) | class NumPyOutput(BaseOutput[np.ndarray]):
    method __init__ (line 20) | def __init__(
    method enforce (line 35) | def enforce(self, value: object) -> np.ndarray:
  function AudioOutput (line 40) | def AudioOutput():
  class ImageOutput (line 45) | class ImageOutput(NumPyOutput):
    method __init__ (line 46) | def __init__(
    method get_broadcast_data (line 78) | def get_broadcast_data(self, value: np.ndarray) -> BroadcastData:
    method get_broadcast_type (line 86) | def get_broadcast_type(self, value: np.ndarray):
    method enforce (line 90) | def enforce(self, value: object) -> np.ndarray:
  function preview_encode (line 131) | def preview_encode(
  class LargeImageOutput (line 157) | class LargeImageOutput(ImageOutput):
    method __init__ (line 158) | def __init__(
    method get_broadcast_data (line 174) | def get_broadcast_data(self, value: np.ndarray):
  function VideoOutput (line 216) | def VideoOutput():

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/onnx_outputs.py
  class OnnxModelOutput (line 11) | class OnnxModelOutput(BaseOutput):
    method __init__ (line 14) | def __init__(
    method get_broadcast_data (line 22) | def get_broadcast_data(self, value: OnnxModel):
    method get_broadcast_type (line 34) | def get_broadcast_type(self, value: OnnxModel):

FILE: MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/pytorch_outputs.py
  class ModelOutput (line 11) | class ModelOutput(BaseOutput):
    method __init__ (line 12) | def __init__(
    method get_broadcast_data (line 20) | def get_broadcast_data(self, value: ModelDescriptor) -> dict[str, list...
    method get_broadcast_type (line 29) | def get_broadcast_type(self, value: ModelDescriptor):
  function TorchScriptOutput (line 50) | def TorchScriptOutput():

FILE: MangaJaNaiConverterGui/backend/src/nodes/utils/format.py
  function join_english (line 10) | def join_english(
  function format_image_with_channels (line 27) | def format_image_with_channels(
  function format_color_with_channels (line 49) | def format_color_with_channels(
  function format_channel_numbers (line 74) | def format_channel_numbers(input_channels: int, output_channels: int) ->...

FILE: MangaJaNaiConverterGui/backend/src/nodes/utils/seed.py
  class Seed (line 8) | class Seed:
    method from_bytes (line 15) | def from_bytes(b: bytes):
    method to_range (line 18) | def to_range(self, a: int, b: int) -> int:
    method to_u32 (line 28) | def to_u32(self) -> int:
    method cache_key_func (line 34) | def cache_key_func(self):

FILE: MangaJaNaiConverterGui/backend/src/nodes/utils/utils.py
  function round_half_up (line 23) | def round_half_up(number: float | int) -> int:
  function get_h_w_c (line 35) | def get_h_w_c(image: np.ndarray) -> tuple[int, int, int]:
  function alphanumeric_sort (line 42) | def alphanumeric_sort(value: str) -> list[str | int]:
  function split_snake_case (line 56) | def split_snake_case(s: str) -> list[str]:
  function split_pascal_case (line 63) | def split_pascal_case(s: str) -> list[str]:
  function join_pascal_case (line 70) | def join_pascal_case(words: list[str]) -> str:
  function smart_capitalize (line 77) | def smart_capitalize(word: str) -> str:
  function join_space_case (line 83) | def join_space_case(words: list[str]) -> str:
  function split_file_path (line 87) | def split_file_path(path: Path | str) -> tuple[Path, str, str]:
  function walk_error_handler (line 96) | def walk_error_handler(exception_instance: Exception) -> None:
  function list_all_files_sorted (line 102) | def list_all_files_sorted(
  class Padding (line 119) | class Padding:
    method all (line 126) | def all(value: int) -> Padding:
    method to (line 130) | def to(value: Padding | int) -> Padding:
    method horizontal (line 136) | def horizontal(self) -> int:
    method vertical (line 140) | def vertical(self) -> int:
    method empty (line 144) | def empty(self) -> bool:
    method scale (line 147) | def scale(self, factor: int) -> Padding:
    method min (line 155) | def min(self, other: Padding | int) -> Padding:
    method remove_from (line 164) | def remove_from(self, image: np.ndarray) -> np.ndarray:
  class Region (line 175) | class Region:
    method size (line 182) | def size(self) -> Size:
    method scale (line 185) | def scale(self, factor: int) -> Region:
    method intersect (line 193) | def intersect(self, other: Region) -> Region:
    method add_padding (line 200) | def add_padding(self, pad: Padding) -> Region:
    method remove_padding (line 208) | def remove_padding(self, pad: Padding) -> Region:
    method child_padding (line 211) | def child_padding(self, child: Region) -> Padding:
    method read_from (line 221) | def read_from(self, image: np.ndarray) -> np.ndarray:
    method write_into (line 232) | def write_into(self, lhs: np.ndarray, rhs: np.ndarray) -> None:

FILE: MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/__init__.py
  function get_pytorch (line 41) | def get_pytorch():

FILE: MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/io/load_model.py
  function parse_ckpt_state_dict (line 22) | def parse_ckpt_state_dict(checkpoint: dict):
  function load_model_node (line 71) | def load_model_node(

FILE: MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/processing/upscale_image.py
  function upscale (line 41) | def upscale(
  function upscale_image_node (line 317) | def upscale_image_node(

FILE: MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/settings.py
  class PyTorchSettings (line 119) | class PyTorchSettings:
    method __post_init__ (line 128) | def __post_init__(self):
    method device (line 134) | def device(self) -> torch.device:
    method accelerator_device (line 163) | def accelerator_device(self) -> 'AcceleratorDevice':
  function get_settings (line 185) | def get_settings(context: NodeContext) -> PyTorchSettings:

FILE: MangaJaNaiConverterGui/backend/src/progress_controller.py
  class Aborted (line 6) | class Aborted(Exception):
  class ProgressToken (line 10) | class ProgressToken(ABC):
    method paused (line 13) | def paused(self) -> bool:
    method aborted (line 18) | def aborted(self) -> bool:
    method suspend (line 22) | async def suspend(self) -> None:
  class ProgressController (line 29) | class ProgressController(ProgressToken):
    method __init__ (line 30) | def __init__(self) -> None:
    method paused (line 42) | def paused(self) -> bool:
    method aborted (line 46) | def aborted(self) -> bool:
    method pause (line 49) | def pause(self) -> None:
    method resume (line 52) | def resume(self) -> None:
    method abort (line 55) | def abort(self) -> None:
    method suspend (line 58) | async def suspend(self) -> None:

FILE: MangaJaNaiConverterGui/backend/src/run_upscale.py
  class _ExecutorNodeContext (line 53) | class _ExecutorNodeContext(NodeContext):
    method __init__ (line 54) | def __init__(
    method aborted (line 67) | def aborted(self) -> bool:
    method paused (line 71) | def paused(self) -> bool:
    method set_progress (line 75) | def set_progress(self, progress: float) -> None:
    method settings (line 81) | def settings(self) -> SettingsParser:
    method storage_dir (line 88) | def storage_dir(self) -> Path:
    method add_cleanup (line 91) | def add_cleanup(
  function get_tile_size (line 102) | def get_tile_size(tile_size_str: str) -> TileSize:
  function standard_resize (line 121) | def standard_resize(image: np.ndarray, new_size: tuple[int, int]) -> np....
  function dotgain20_resize (line 139) | def dotgain20_resize(image: np.ndarray, new_size: tuple[int, int]) -> np...
  function image_resize (line 160) | def image_resize(
  function get_system_codepage (line 169) | def get_system_codepage() -> Any:
  function enhance_contrast (line 173) | def enhance_contrast(image: np.ndarray) -> MatLike:
  function _read_image (line 238) | def _read_image(img_stream: bytes, filename: str) -> np.ndarray:
  function _read_image_from_path (line 242) | def _read_image_from_path(path: str) -> np.ndarray:
  function _read_vips (line 246) | def _read_vips(img_stream: bytes) -> np.ndarray:
  function cv_image_is_grayscale (line 250) | def cv_image_is_grayscale(image: np.ndarray, user_threshold: float) -> b...
  function convert_image_to_grayscale (line 286) | def convert_image_to_grayscale(image: np.ndarray) -> np.ndarray:
  function get_chain_for_image (line 296) | def get_chain_for_image(
  function should_chain_activate_for_image (line 329) | def should_chain_activate_for_image(
  function ai_upscale_image (line 364) | def ai_upscale_image(
  function postprocess_image (line 389) | def postprocess_image(image: np.ndarray) -> np.ndarray:
  function final_target_resize (line 394) | def final_target_resize(
  function save_image_zip (line 439) | def save_image_zip(
  function save_image (line 480) | def save_image(
  function preprocess_worker_archive (line 513) | def preprocess_worker_archive(
  function preprocess_worker_archive_file (line 556) | def preprocess_worker_archive_file(
  function preprocess_worker_folder (line 699) | def preprocess_worker_folder(
  function preprocess_worker_image (line 880) | def preprocess_worker_image(
  function upscale_worker (line 1000) | def upscale_worker(upscale_queue: Queue, postprocess_queue: Queue) -> None:
  function postprocess_worker_zip (line 1033) | def postprocess_worker_zip(
  function postprocess_worker_folder (line 1081) | def postprocess_worker_folder(
  function postprocess_worker_image (line 1120) | def postprocess_worker_image(
  function upscale_archive_file (line 1157) | def upscale_archive_file(
  function upscale_image_file (line 1220) | def upscale_image_file(
  function upscale_file (line 1283) | def upscale_file(
  function upscale_folder (line 1351) | def upscale_folder(
  function get_model_abs_path (line 1429) | def get_model_abs_path(chain_model_file_path: str) -> str:
  function get_gamma_icc_profile (line 1433) | def get_gamma_icc_profile() -> ImageCmsProfile:
  function get_dot20_icc_profile (line 1440) | def get_dot20_icc_profile() -> ImageCmsProfile:
  function parse_settings_from_cli (line 1447) | def parse_settings_from_cli():
  function parse_auto_settings (line 1485) | def parse_auto_settings(args):
  function parse_manual_settings (line 1492) | def parse_manual_settings(args):

FILE: MangaJaNaiConverterGui/backend/src/spandrel_custom/__init__.py
  function install (line 15) | def install(*, ignore_duplicates: bool = False) -> list[ArchSupport]:

FILE: MangaJaNaiConverterGui/backend/src/spandrel_custom/architectures/FDAT/__arch/fdat.py
  class DySample (line 30) | class DySample(nn.Module):
    method __init__ (line 36) | def __init__(
    method _init_pos (line 67) | def _init_pos(self) -> Tensor:
    method forward (line 76) | def forward(self, x: Tensor) -> Tensor:
  class LayerNorm (line 117) | class LayerNorm(nn.Module):
    method __init__ (line 118) | def __init__(self, dim: int = 64, eps: float = 1e-6) -> None:
    method forward (line 125) | def forward(self, x):
  class LDA_AQU (line 136) | class LDA_AQU(nn.Module):
    method __init__ (line 137) | def __init__(
    method init_weights (line 201) | def init_weights(self) -> None:
    method get_offset (line 211) | def get_offset(self, offset, Hout, Wout):
    method extract_feats (line 231) | def extract_feats(self, x, offset, ks=3):
    method forward (line 242) | def forward(self, x):
  class PA (line 283) | class PA(nn.Module):
    method __init__ (line 284) | def __init__(self, dim) -> None:
    method forward (line 288) | def forward(self, x):
  class UniUpsampleV3 (line 292) | class UniUpsampleV3(nn.Sequential):
    method __init__ (line 293) | def __init__(
  class FastSpatialWindowAttention (line 444) | class FastSpatialWindowAttention(nn.Module):
    method __init__ (line 445) | def __init__(self, dim, window_size=8, num_heads=4, qkv_bias=False) ->...
    method forward (line 458) | def forward(self, x, H, W):
  class FastChannelAttention (line 498) | class FastChannelAttention(nn.Module):
    method __init__ (line 499) | def __init__(self, dim, num_heads=4, qkv_bias=False) -> None:
    method forward (line 508) | def forward(self, x, H, W):  # H, W are unused but kept for API consis...
  class SimplifiedAIM (line 522) | class SimplifiedAIM(nn.Module):
    method __init__ (line 523) | def __init__(self, dim, reduction_ratio=8) -> None:
    method forward (line 534) | def forward(self, attn_feat, conv_feat, interaction_type, H, W):
  class SimplifiedFFN (line 552) | class SimplifiedFFN(nn.Module):
    method __init__ (line 553) | def __init__(self, dim, expansion_ratio=2.0, drop=0.0) -> None:
    method forward (line 564) | def forward(self, x, H, W):
  class SimplifiedDATBlock (line 575) | class SimplifiedDATBlock(nn.Module):
    method __init__ (line 576) | def __init__(self, dim, nh, ws, ffn_exp, aim_re, btype, dp, qkv_b=Fals...
    method _conv_fwd (line 592) | def _conv_fwd(self, x, H, W):
    method forward (line 598) | def forward(self, x, H, W):
  class SimplifiedResidualGroup (line 611) | class SimplifiedResidualGroup(nn.Module):
    method __init__ (line 612) | def __init__(self, dim, depth, nh, ws, ffn_exp, aim_re, pattern, dp_ra...
    method forward (line 624) | def forward(self, x: Tensor) -> Tensor:
  class FDAT (line 633) | class FDAT(nn.Module):
    method __init__ (line 636) | def __init__(
    method load_state_dict (line 698) | def load_state_dict(
    method _init_weights (line 707) | def _init_weights(self, m: nn.Module) -> None:
    method check_img_size (line 722) | def check_img_size(self, x: Tensor, h: int, w: int) -> Tensor:
    method forward (line 729) | def forward(self, x: Tensor) -> Tensor:

FILE: MangaJaNaiConverterGui/backend/src/spandrel_custom/architectures/FDAT/__init__.py
  class FDATArch (line 9) | class FDATArch(Architecture[FDAT]):
    method __init__ (line 10) | def __init__(self):
    method load (line 33) | def load(self, state_dict: StateDict) -> ImageModelDescriptor[FDAT]:

FILE: MangaJaNaiConverterGui/backend/src/test_accelerators.py
  function test_accelerator_detection (line 10) | def test_accelerator_detection():
Condensed preview — 136 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,052K chars).
[
  {
    "path": ".editorconfig",
    "chars": 9449,
    "preview": "# Remove the line below if you want to inherit .editorconfig settings from higher directories\nroot = true\n\n# C# files\n[*"
  },
  {
    "path": ".github/workflows/deploy.yml",
    "chars": 1173,
    "preview": "name: Deploy to GitHub Releases\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Version numbe"
  },
  {
    "path": ".gitignore",
    "chars": 6885,
    "preview": "## Ignore Visual Studio temporary files, build results, and\n## files generated by popular Visual Studio add-ons.\n##\n## G"
  },
  {
    "path": "LICENSE",
    "chars": 35149,
    "preview": "                    GNU GENERAL PUBLIC LICENSE\n                       Version 3, 29 June 2007\n\n Copyright (C) 2007 Free "
  },
  {
    "path": "MangaJaNaiConverterGui/App.axaml",
    "chars": 793,
    "preview": "<Application xmlns=\"https://github.com/avaloniaui\"\n             xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n "
  },
  {
    "path": "MangaJaNaiConverterGui/App.axaml.cs",
    "chars": 3090,
    "preview": "using Autofac;\nusing Avalonia;\nusing Avalonia.Markup.Xaml;\nusing MangaJaNaiConverterGui.Services;\nusing MangaJaNaiConver"
  },
  {
    "path": "MangaJaNaiConverterGui/Drivers/NewtonsoftJsonSuspensionDriver.cs",
    "chars": 1240,
    "preview": "using Newtonsoft.Json;\nusing ReactiveUI;\nusing System;\nusing System.IO;\nusing System.Reactive;\nusing System.Reactive.Li"
  },
  {
    "path": "MangaJaNaiConverterGui/MangaJaNaiConverterGui.csproj",
    "chars": 2552,
    "preview": "<Project Sdk=\"Microsoft.NET.Sdk\">\n  <PropertyGroup>\n    <!--<PublishTrimmed>true</PublishTrimmed>\n    <PublishAot>true<"
  },
  {
    "path": "MangaJaNaiConverterGui/Program.cs",
    "chars": 2149,
    "preview": "using Avalonia;\nusing ReactiveUI.Avalonia;\nusing System;\nusing System.IO;\nusing Velopack;\n\nnamespace MangaJaNaiConverte"
  },
  {
    "path": "MangaJaNaiConverterGui/Services/Downloader.cs",
    "chars": 1399,
    "preview": "using System;\nusing System.IO;\nusing System.Net.Http;\nusing System.Threading.Tasks;\n\n\nnamespace MangaJaNaiConverterGui."
  },
  {
    "path": "MangaJaNaiConverterGui/Services/ETACalculator.cs",
    "chars": 4942,
    "preview": "using System;\nusing System.Collections.Generic;\nusing System.Diagnostics;\nusing ProgressItem = System.Collections.Gener"
  },
  {
    "path": "MangaJaNaiConverterGui/Services/IPythonService.cs",
    "chars": 1131,
    "preview": "using Avalonia.Collections;\nusing System;\nusing System.Threading.Tasks;\n\nnamespace MangaJaNaiConverterGui.Services\n{\n  "
  },
  {
    "path": "MangaJaNaiConverterGui/Services/ISuspensionDriverService.cs",
    "chars": 176,
    "preview": "using ReactiveUI;\n\nnamespace MangaJaNaiConverterGui.Services\n{\n    public interface ISuspensionDriverService\n    {\n    "
  },
  {
    "path": "MangaJaNaiConverterGui/Services/IUpdateManagerService.cs",
    "chars": 500,
    "preview": "using System;\nusing System.Threading.Tasks;\nusing Velopack;\n\nnamespace MangaJaNaiConverterGui.Services\n{\n    public int"
  },
  {
    "path": "MangaJaNaiConverterGui/Services/PythonService.cs",
    "chars": 10997,
    "preview": "using Avalonia.Collections;\nusing ICSharpCode.SharpZipLib.Core;\nusing ICSharpCode.SharpZipLib.GZip;\nusing ICSharpCode.S"
  },
  {
    "path": "MangaJaNaiConverterGui/Services/SuspensionDriverService.cs",
    "chars": 393,
    "preview": "using MangaJaNaiConverterGui.Drivers;\nusing ReactiveUI;\n\nnamespace MangaJaNaiConverterGui.Services\n{\n    public class S"
  },
  {
    "path": "MangaJaNaiConverterGui/Services/UpdateManagerService.cs",
    "chars": 1162,
    "preview": "using System;\nusing System.Threading.Tasks;\nusing Velopack;\nusing Velopack.Sources;\n\nnamespace MangaJaNaiConverterGui.S"
  },
  {
    "path": "MangaJaNaiConverterGui/ViewLocator.cs",
    "chars": 685,
    "preview": "using Avalonia.Controls;\nusing Avalonia.Controls.Templates;\nusing MangaJaNaiConverterGui.ViewModels;\nusing System;\n\nname"
  },
  {
    "path": "MangaJaNaiConverterGui/ViewModels/MainWindowViewModel.cs",
    "chars": 87436,
    "preview": "using Avalonia.Collections;\nusing Avalonia.Threading;\nusing MangaJaNaiConverterGui.Drivers;\nusing MangaJaNaiConverterGu"
  },
  {
    "path": "MangaJaNaiConverterGui/ViewModels/ViewModelBase.cs",
    "chars": 377,
    "preview": "using ReactiveUI;\n\nnamespace MangaJaNaiConverterGui.ViewModels\n{\n    //[DataContract]\n    public class ViewModelBase : "
  },
  {
    "path": "MangaJaNaiConverterGui/Views/MainWindow.axaml",
    "chars": 52462,
    "preview": "<Window xmlns=\"https://github.com/avaloniaui\"\n        xmlns:x=\"http://schemas.microsoft.com/winfx/2006/xaml\"\n        xml"
  },
  {
    "path": "MangaJaNaiConverterGui/Views/MainWindow.axaml.cs",
    "chars": 19852,
    "preview": "using Avalonia;\nusing Avalonia.Controls;\nusing Avalonia.Input;\nusing Avalonia.Interactivity;\nusing Avalonia.Layout;\nusin"
  },
  {
    "path": "MangaJaNaiConverterGui/app.manifest",
    "chars": 921,
    "preview": "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n<assembly manifestVersion=\"1.0\" xmlns=\"urn:schemas-microsoft-com:asm.v1\">\n  <!--"
  },
  {
    "path": "MangaJaNaiConverterGui/appstate2.json",
    "chars": 128672,
    "preview": "{\n  \"$type\": \"MangaJaNaiConverterGui.ViewModels.MainWindowViewModel, MangaJaNaiConverterGui\",\n  \"DisplayDeviceMap\": {\n  "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/resources/default_cli_configuration.json",
    "chars": 12206,
    "preview": "{\n  \"$type\": \"MangaJaNaiConverterGui.ViewModels.MainWindowViewModel, MangaJaNaiConverterGui\",\n  \"AutoUpdateEnabled\": tru"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/.pre-commit-config.yaml",
    "chars": 200,
    "preview": "- repo: https://github.com/astral-sh/ruff-pre-commit\n  # Ruff version.\n  rev: v0.5.1\n  hooks:\n    # Run the linter.\n    "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/README.md",
    "chars": 3360,
    "preview": "# Info\nMangaJaNaiConverterGui is a convenient GUI windows tool, but in the backend it operates by running some python sc"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/accelerator_detection.py",
    "chars": 12890,
    "preview": "\"\"\"\nComprehensive accelerator detection for PyTorch backend.\nSupports all available PyTorch accelerators in PyTorch 2.7+"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/__init__.py",
    "chars": 221,
    "preview": "from .api import *\nfrom .group import *\nfrom .input import *\nfrom .iter import *\nfrom .lazy import *\nfrom .node_context "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/api.py",
    "chars": 15062,
    "preview": "from __future__ import annotations\n\nimport importlib\nimport os\nfrom collections.abc import Awaitable, Callable, Iterable"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/group.py",
    "chars": 1310,
    "preview": "from __future__ import annotations\n\nfrom typing import Any, Generic, NewType, TypeVar, Union\n\nfrom .input import BaseInp"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/input.py",
    "chars": 5618,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Mapping\nfrom dataclasses import dataclass\nfrom enum impo"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/iter.py",
    "chars": 1954,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Iterable\nfrom dataclasses import dataclass\nfro"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/lazy.py",
    "chars": 3312,
    "preview": "from __future__ import annotations\n\nimport time\nfrom asyncio import AbstractEventLoop\nfrom collections.abc import Callab"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/node_check.py",
    "chars": 9991,
    "preview": "from __future__ import annotations\n\nimport ast\nimport inspect\nimport os\nimport pathlib\nfrom collections import OrderedDi"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/node_context.py",
    "chars": 4548,
    "preview": "import time\nfrom abc import ABC, abstractmethod\nfrom collections.abc import Callable\nfrom pathlib import Path\nfrom typin"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/node_data.py",
    "chars": 6142,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Mapping\nfrom dataclasses import dataclass\nfrom"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/output.py",
    "chars": 2258,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Mapping\nfrom typing import Any, Generic, Literal, TypeVa"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/settings.py",
    "chars": 3116,
    "preview": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import TypedDict, Union\n\nfrom sanic.lo"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/api/types.py",
    "chars": 431,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Callable\nfrom typing import Any, Literal, NewType\n\nNodeI"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/device_list.py",
    "chars": 940,
    "preview": "import json\nimport os\nimport sys\n\nsys.path.append(os.path.normpath(os.path.dirname(os.path.abspath(__file__))))\nfrom acc"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/gpu.py",
    "chars": 3676,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Sequence\nfrom dataclasses import dataclass\nfro"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/navi.py",
    "chars": 5827,
    "preview": "from __future__ import annotations\n\nimport math\nfrom typing import Literal, TypedDict, Union\n\nNumberJson = Union[int, fl"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/condition.py",
    "chars": 3465,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Iterable\nfrom enum import Enum\nfrom typing import Litera"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/group.py",
    "chars": 1286,
    "preview": "from typing import Any, Generic, NewType, TypeVar, Union\n\nfrom base_types import InputId\nfrom nodes.base_input import Ba"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/groups.py",
    "chars": 3887,
    "preview": "from __future__ import annotations\n\nfrom typing import Union\n\nfrom api import BaseInput, NestedGroup, group\n\nfrom .condi"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/blend.py",
    "chars": 8874,
    "preview": "from enum import Enum\n\nimport cv2\nimport numpy as np\n\nfrom ..utils.utils import get_h_w_c\nfrom .image_utils import as_ta"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/color/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/color/color.py",
    "chars": 3083,
    "preview": "from __future__ import annotations\n\nimport json\nfrom collections.abc import Iterable\nfrom typing import Literal, TypedDi"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert.py",
    "chars": 3580,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Iterable\nfrom dataclasses import dataclass, fi"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert_data.py",
    "chars": 10240,
    "preview": "from __future__ import annotations\n\nimport math\n\nimport cv2\nimport numpy as np\n\nfrom ...utils.utils import get_h_w_c\nfro"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/color/convert_model.py",
    "chars": 2871,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Iterable\n\nimport numpy as np\n\nfrom ...utils.fo"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/image_formats.py",
    "chars": 1339,
    "preview": "def get_opencv_formats():\n    return [\n        # Bitmaps\n        \".bmp\",\n        \".dib\",\n        # JPEG\n        \".jpg\",\n"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/image_op.py",
    "chars": 1572,
    "preview": "from collections.abc import Callable\nfrom typing import Concatenate\n\nimport numpy as np\nfrom typing_extensions import Pa"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/image_utils.py",
    "chars": 13297,
    "preview": "from __future__ import annotations\n\nimport itertools\nimport math\nfrom enum import Enum\nfrom pathlib import Path\n\nimport "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/auto_split.py",
    "chars": 4251,
    "preview": "from __future__ import annotations\n\nimport gc\nfrom collections.abc import Callable\n\nimport numpy as np\nimport onnxruntim"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/load.py",
    "chars": 3428,
    "preview": "from __future__ import annotations\n\nimport onnx\nimport onnx.inliner\nimport re2\nfrom sanic.log import logger\n\nfrom .model"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/model.py",
    "chars": 2347,
    "preview": "# This class defines an interface.\n# It is important that is does not contain types that depend on ONNX.\nfrom __future__"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/np_tensor_utils.py",
    "chars": 5042,
    "preview": "from __future__ import annotations\n\nimport numpy as np\n\nfrom ..image_utils import MAX_VALUES_BY_DTYPE, as_3d\n\n\ndef np_de"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/onnx_to_ncnn.py",
    "chars": 160126,
    "preview": "# ruff: noqa: N806\nfrom __future__ import annotations\n\nimport numpy as np\nimport onnx.numpy_helper as onph\nfrom google.p"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/session.py",
    "chars": 2455,
    "preview": "from __future__ import annotations\n\nfrom typing import Any, Union\nfrom weakref import WeakKeyDictionary\n\nimport onnxrunt"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/tensorproto_utils.py",
    "chars": 3427,
    "preview": "from sys import float_info\n\nimport numpy as np\nfrom onnx import numpy_helper as onph\nfrom onnx.onnx_pb import AttributeP"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/update_model_dims.py",
    "chars": 3161,
    "preview": "# Copyright (c) ONNX Project Contributors\n# SPDX-License-Identifier: Apache-2.0\n#\n# Include in chaiNNer with the followi"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/onnx/utils.py",
    "chars": 6053,
    "preview": "from __future__ import annotations\n\nfrom typing import Any, Literal\n\nimport onnx\nfrom onnx.onnx_pb import ModelProto, Va"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pil_utils.py",
    "chars": 2368,
    "preview": "from __future__ import annotations\n\nfrom enum import Enum\n\nimport numpy as np\nfrom PIL import Image\n\nfrom ..utils.utils "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/auto_split.py",
    "chars": 5586,
    "preview": "from __future__ import annotations\n\nimport gc\n\nimport numpy as np\nimport torch\nfrom accelerator_detection import get_aut"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/convert_to_onnx_impl.py",
    "chars": 2031,
    "preview": "from io import BytesIO\n\nimport torch\nfrom spandrel import ImageModelDescriptor, ModelDescriptor\nfrom spandrel.architectu"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/LICENSE",
    "chars": 1074,
    "preview": "MIT License\n\nCopyright (c) 2019 Riccardo de Lutio\n\nPermission is hereby granted, free of charge, to any person obtaining"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/auto_split.py",
    "chars": 3328,
    "preview": "from __future__ import annotations\n\nimport gc\n\nimport numpy as np\nimport torch\n\nfrom ....utils.utils import Region, Size"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/pix_transform.py",
    "chars": 4126,
    "preview": "from __future__ import annotations\n\nfrom dataclasses import dataclass\nfrom typing import Literal\n\nimport numpy as np\nimp"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/pix_transform/pix_transform_net.py",
    "chars": 2152,
    "preview": "from __future__ import annotations\n\nfrom torch import nn\n\n\nclass PixTransformNet(nn.Module):\n    def __init__(\n        s"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/rife/IFNet_HDv3_v4_14_align.py",
    "chars": 8926,
    "preview": "# type: ignore\n# Original Rife Frame Interpolation by hzwer\n# https://github.com/megvii-research/ECCV2022-RIFE\n# https:/"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/rife/warplayer.py",
    "chars": 1233,
    "preview": "# type: ignore\nimport torch\n\nbackwarp_tenGrid = {}  # noqa: N816\n\n\ndef warp(tenInput, tenFlow, device):  # noqa: ANN001,"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/pytorch/utils.py",
    "chars": 6493,
    "preview": "from __future__ import annotations\n\nimport numpy as np\nimport torch\nfrom torch import Tensor\n\nfrom ..image_utils import "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/resize.py",
    "chars": 2888,
    "preview": "from __future__ import annotations\n\nfrom enum import Enum\n\nimport numpy as np\nfrom chainner_ext import ResizeFilter as N"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split.py",
    "chars": 8210,
    "preview": "from __future__ import annotations\n\nimport math\nfrom collections.abc import Callable\n\nimport numpy as np\nfrom sanic.log "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/auto_split_tiles.py",
    "chars": 1505,
    "preview": "from collections.abc import Callable\nfrom typing import NewType\n\nimport numpy as np\nfrom sanic.log import logger\n\nfrom ."
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/basic_upscale.py",
    "chars": 2852,
    "preview": "import math\nfrom dataclasses import dataclass\nfrom enum import Enum\n\nimport numpy as np\nfrom nodes.impl.image_op import "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/convenient_upscale.py",
    "chars": 3620,
    "preview": "from __future__ import annotations\n\nimport numpy as np\n\nfrom ...utils.utils import get_h_w_c\nfrom ..image_op import Imag"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/custom_scale.py",
    "chars": 1052,
    "preview": "import math\n\nimport numpy as np\nfrom nodes.impl.image_op import ImageOp\nfrom nodes.impl.resize import ResizeFilter, resi"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/exact_split.py",
    "chars": 6986,
    "preview": "from __future__ import annotations\n\nimport math\nfrom collections.abc import Callable\nfrom dataclasses import dataclass\n\n"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/grayscale.py",
    "chars": 2158,
    "preview": "from __future__ import annotations\n\nfrom enum import Enum\n\nimport numpy as np\n\nfrom ..color.convert import convert\nfrom "
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/passthrough.py",
    "chars": 1364,
    "preview": "import numpy as np\n\nfrom ...utils.utils import get_h_w_c\nfrom ..image_op import ImageOp\n\n\ndef passthrough_single_color(i"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/tile_blending.py",
    "chars": 6887,
    "preview": "from __future__ import annotations\n\nimport math\nfrom collections.abc import Callable\nfrom dataclasses import dataclass\nf"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/impl/upscale/tiler.py",
    "chars": 2499,
    "preview": "from abc import ABC, abstractmethod\n\nfrom ...utils.utils import Size\n\n\nclass Tiler(ABC):\n    @abstractmethod\n    def all"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/node_cache.py",
    "chars": 5201,
    "preview": "from __future__ import annotations\n\nimport functools\nimport hashlib\nimport os\nimport tempfile\nimport time\nfrom collectio"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/__init__.py",
    "chars": 345,
    "preview": "from .file_inputs import *\nfrom .generic_inputs import *\nfrom .image_dropdown_inputs import *\nfrom .numeric_inputs impor"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/__system_inputs.py",
    "chars": 971,
    "preview": "from __future__ import annotations\n\nimport math\nfrom typing import Literal\n\nfrom navi import ExpressionJson\n\nfrom api im"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/file_inputs.py",
    "chars": 6022,
    "preview": "from __future__ import annotations\n\nimport re\nfrom pathlib import Path\nfrom typing import Literal, Union\n\nimport navi\n\nf"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/generic_inputs.py",
    "chars": 20554,
    "preview": "from __future__ import annotations\n\nimport json\nimport re\nfrom dataclasses import dataclass\nfrom enum import Enum\nfrom t"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/image_dropdown_inputs.py",
    "chars": 2998,
    "preview": "import navi\n\nfrom ...impl.color.convert_data import (\n    color_spaces,\n    color_spaces_or_detectors,\n    get_alpha_par"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/label.py",
    "chars": 191,
    "preview": "from typing import Literal\n\nLabelStyle = Literal[\"default\", \"hidden\", \"inline\"]\n\n\ndef get_default_label_style(label: str"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/ncnn_inputs.py",
    "chars": 290,
    "preview": "from api import BaseInput\n\nfrom ...impl.ncnn.model import NcnnModelWrapper\n\n\nclass NcnnModelInput(BaseInput):\n    \"\"\"Inp"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numeric_inputs.py",
    "chars": 5300,
    "preview": "from __future__ import annotations\n\nimport math\nfrom typing import Literal, Never, Union\n\nimport navi\n\nfrom api import B"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/numpy_inputs.py",
    "chars": 3581,
    "preview": "# pylint: disable=relative-beyond-top-level\nfrom __future__ import annotations\n\nfrom typing import Union\n\nimport navi\nim"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/onnx_inputs.py",
    "chars": 1881,
    "preview": "import navi\n\nfrom api import BaseInput\n\nfrom ...impl.onnx.model import OnnxGeneric, OnnxModel, OnnxModels, OnnxRemBg\nfro"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/inputs/pytorch_inputs.py",
    "chars": 3546,
    "preview": "from __future__ import annotations\n\ntry:\n    import spandrel\n    from spandrel import Purpose\nexcept Exception:\n    span"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/__init__.py",
    "chars": 284,
    "preview": "from .file_outputs import *\nfrom .generic_outputs import *\nfrom .numpy_outputs import *\n\ntry:\n    from .ncnn_outputs imp"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/file_outputs.py",
    "chars": 880,
    "preview": "from __future__ import annotations\n\nfrom pathlib import Path\n\nimport navi\n\nfrom api import BaseOutput\n\n\nclass DirectoryO"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/generic_outputs.py",
    "chars": 3653,
    "preview": "from __future__ import annotations\n\nfrom typing import Union\n\nimport navi\n\nfrom api import BaseOutput\n\nfrom ...impl.colo"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/ncnn_outputs.py",
    "chars": 1062,
    "preview": "import navi\n\nfrom api import BaseOutput, OutputKind\n\nfrom ...impl.ncnn.model import NcnnModelWrapper\nfrom ...utils.forma"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/numpy_outputs.py",
    "chars": 6963,
    "preview": "from __future__ import annotations\n\nimport base64\n\nimport cv2\nimport navi\nimport numpy as np\n\nfrom api import BaseOutput"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/onnx_outputs.py",
    "chars": 1451,
    "preview": "from __future__ import annotations\n\nimport navi\nfrom nodes.utils.format import format_channel_numbers\n\nfrom api import B"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/properties/outputs/pytorch_outputs.py",
    "chars": 1680,
    "preview": "from __future__ import annotations\n\nimport navi\nfrom spandrel import ModelDescriptor, ModelTiling\n\nfrom api import BaseO"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/utils/__init__.py",
    "chars": 0,
    "preview": ""
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/utils/format.py",
    "chars": 2117,
    "preview": "from __future__ import annotations\n\nfrom collections.abc import Callable, Iterable\nfrom typing import Literal, TypeVar\n\n"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/utils/seed.py",
    "chars": 958,
    "preview": "from dataclasses import dataclass\nfrom random import Random\n\n_U32_MAX = 4294967296\n\n\n@dataclass(frozen=True)\nclass Seed:"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/nodes/utils/utils.py",
    "chars": 6886,
    "preview": "# From https://github.com/victorca25/iNNfer/blob/main/utils/utils.py\nfrom __future__ import annotations\n\nimport math\nimp"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/__init__.py",
    "chars": 4772,
    "preview": "import os\n\nfrom accelerator_detection import AcceleratorType, get_accelerator_detector\nfrom gpu import nvidia\nfrom sanic"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/__init__.py",
    "chars": 462,
    "preview": "from .. import pytorch_category\n\nio_group = pytorch_category.add_node_group(\"Input & Output\")\nprocessing_group = pytorch"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/io/load_model.py",
    "chars": 3681,
    "preview": "from __future__ import annotations\n\nimport os\nfrom pathlib import Path\n\nimport torch\nfrom nodes.properties.inputs import"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/pytorch/processing/upscale_image.py",
    "chars": 13501,
    "preview": "from __future__ import annotations\n\nimport weakref\n\nimport numpy as np\nimport psutil\nimport torch\nfrom accelerator_detec"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/packages/chaiNNer_pytorch/settings.py",
    "chars": 7402,
    "preview": "from dataclasses import dataclass\n\nimport torch\nfrom accelerator_detection import AcceleratorType, get_accelerator_detec"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/progress_controller.py",
    "chars": 1606,
    "preview": "import asyncio\nimport time\nfrom abc import ABC, abstractmethod\n\n\nclass Aborted(Exception):\n    pass\n\n\nclass ProgressToke"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/pyproject.toml",
    "chars": 3415,
    "preview": "[project]\nname = \"mangajanaiconvertergui\"\ndynamic = [\"version\"]\ndependencies = [\n  \"chainner_ext==0.3.10\",\n  \"numpy==2.2"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/pyrightconfig.json",
    "chars": 835,
    "preview": "{\n\t\"include\": [\n\t\t// \"traiNNer\"\n\t],\n\t\"exclude\": [\n\t\t\"**/__pycache__\"\n\t],\n\t\"ignore\": [],\n\t\"typeCheckingMode\": \"standard\","
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/run_upscale.py",
    "chars": 54233,
    "preview": "import argparse\nimport ctypes\nimport io\nimport json\nimport os\nimport platform\nimport sys\nimport time\nfrom collections.ab"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/spandrel_custom/__init__.py",
    "chars": 739,
    "preview": "from spandrel import (\n    MAIN_REGISTRY,\n    ArchRegistry,\n    ArchSupport,\n)\n\nfrom .architectures import FDAT\n\nCUSTOM_"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/spandrel_custom/architectures/FDAT/__arch/LICENSE",
    "chars": 1069,
    "preview": "MIT License\n\nCopyright (c) 2025 Vaibhav Bhat\n\nPermission is hereby granted, free of charge, to any person obtaining a co"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/spandrel_custom/architectures/FDAT/__arch/fdat.py",
    "chars": 26583,
    "preview": "# https://github.com/stinkybread/fdat/blob/main/fdat.py\nfrom __future__ import annotations\n\nimport math\nfrom typing impo"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/spandrel_custom/architectures/FDAT/__init__.py",
    "chars": 3850,
    "preview": "import math\n\nfrom spandrel.util import KeyCondition, get_seq_len\n\nfrom spandrel.__helpers.model_descriptor import Archit"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/system.py",
    "chars": 188,
    "preview": "import platform\nimport sys\n\nis_mac = sys.platform == \"darwin\"\nis_arm_mac = is_mac and platform.machine() == \"arm64\"\nis_w"
  },
  {
    "path": "MangaJaNaiConverterGui/backend/src/test_accelerators.py",
    "chars": 3403,
    "preview": "#!/usr/bin/env python3\n\"\"\"\nTest script for the new accelerator detection system.\n\"\"\"\n\nimport sys\nimport torch\nfrom accel"
  },
  {
    "path": "MangaJaNaiConverterGui.sln",
    "chars": 1380,
    "preview": "\nMicrosoft Visual Studio Solution File, Format Version 12.00\n# Visual Studio Version 17\nVisualStudioVersion = 17.7.3403"
  },
  {
    "path": "README.md",
    "chars": 2412,
    "preview": "# <img src=\"logo.png\" width=\"24\"></img> MangaJaNaiConverterGui\n[![Discord](https://img.shields.io/discord/11216536181735"
  },
  {
    "path": "pack.bat",
    "chars": 182,
    "preview": "vpk pack -u MangaJaNaiConverterGui -v 1.1.3 -p \".\\MangaJaNaiConverterGui\\bin\\Release\\net8.0\\publish\\win-x64\" -i ./MangaJ"
  }
]

// ... and 3 more files (download for full content)

About this extraction

This page contains the full source code of the the-database/MangaJaNaiConverterGui GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 136 files (971.0 KB), approximately 234.3k tokens, and a symbol index with 1048 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!