Fixed bundling into an exe not working as intended.

This commit is contained in:
2025-12-14 13:16:35 -05:00
parent 76c9eb38f6
commit 5d8441bf27
124 changed files with 279314 additions and 103 deletions

4
.env Normal file
View File

@@ -0,0 +1,4 @@
DISCORD_CLIENT_ID=1440755333055250616
ANILIST_CLIENT_SECRET=fTql48IjI7ZAE2cbjFaMl2I5HhYHYr1kVDyDNpOk
ANILIST_CLIENT_ID=32898
JWT_SECRET=900c86474497ce3c329ba9949d834dbb1dd072163a5867957be929f3adc592d2d6352c03aecbfe065a8b9e9eff0e7fe7516a50dbf206d5e8c69bdcba22477cb6

View File

@@ -0,0 +1 @@
<EFBFBD><EFBFBD>

View File

@@ -0,0 +1,2 @@
PlatformToolSet=v143:VCToolArchitecture=Native64Bit:VCToolsVersion=14.44.35207:TargetPlatformVersion=10.0.26100.0:
Release|x64|C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\build\|

148
build/anime_core.vcxproj Normal file
View File

@@ -0,0 +1,148 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="14.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<ProjectGuid>{E80660A8-73CA-0176-CBD2-DA941C5BB848}</ProjectGuid>
<Keyword>Win32Proj</Keyword>
<RootNamespace>anime_core</RootNamespace>
<IgnoreWarnCompileDuplicatedFilename>true</IgnoreWarnCompileDuplicatedFilename>
<PreferredToolArchitecture>x64</PreferredToolArchitecture>
<WindowsTargetPlatformVersion>10.0.26100.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props"/>
<PropertyGroup Label="Configuration">
<ConfigurationType>DynamicLibrary</ConfigurationType>
</PropertyGroup>
<PropertyGroup Label="Locals">
<PlatformToolset>v143</PlatformToolset>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props"/>
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.props"/>
<ImportGroup Label="ExtensionSettings"/>
<ImportGroup Label="PropertySheets">
<Import Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props"/>
</ImportGroup>
<PropertyGroup Label="UserMacros"/>
<PropertyGroup>
<ExecutablePath>$(ExecutablePath);$(MSBuildProjectDirectory)\..\bin\;$(MSBuildProjectDirectory)\..\bin\</ExecutablePath>
<IgnoreImportLibrary>true</IgnoreImportLibrary>
<IntDir>$(Configuration)\obj\$(ProjectName)\</IntDir>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>
<LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">true</LinkIncremental>
<OutDir>$(SolutionDir)$(Configuration)\</OutDir>
<TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.node</TargetExt>
<TargetExt Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">.node</TargetExt>
<TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.node</TargetExt>
<TargetExt Condition="'$(Configuration)|$(Platform)'=='Release|x64'">.node</TargetExt>
<TargetName>$(ProjectName)</TargetName>
<TargetPath>$(OutDir)\$(ProjectName).node</TargetPath>
</PropertyGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<AdditionalIncludeDirectories>C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\include\node;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\src;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\openssl\config;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\openssl\openssl\include;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\uv\include;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\zlib;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\v8\include;C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-addon-api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/Zc:__cplusplus -std:c++20 /Zm2000 %(AdditionalOptions)</AdditionalOptions>
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
<BufferSecurityCheck>true</BufferSecurityCheck>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<DisableSpecificWarnings>4351;4355;4800;4251;4275;4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ExceptionHandling>false</ExceptionHandling>
<MinimalRebuild>false</MinimalRebuild>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<OmitFramePointers>false</OmitFramePointers>
<Optimization>Disabled</Optimization>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>NODE_GYP_MODULE_NAME=anime_core;USING_UV_SHARED=1;USING_V8_SHARED=1;V8_DEPRECATION_WARNINGS=1;_GLIBCXX_USE_CXX11_ABI=1;_FILE_OFFSET_BITS=64;WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_HAS_EXCEPTIONS=0;NOMINMAX;OPENSSL_NO_PINSHARED;OPENSSL_THREADS;NAPI_DISABLE_CPP_EXCEPTIONS;BUILDING_NODE_EXTENSION;HOST_BINARY=&quot;node.exe&quot;;DEBUG;_DEBUG;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
<StringPooling>true</StringPooling>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TreatWarningAsError>false</TreatWarningAsError>
<WarningLevel>Level3</WarningLevel>
<WholeProgramOptimization>true</WholeProgramOptimization>
</ClCompile>
<Lib>
<AdditionalOptions>/LTCG:INCREMENTAL %(AdditionalOptions)</AdditionalOptions>
</Lib>
<Link>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;delayimp.lib;&quot;C:\\Users\\synta\\AppData\\Local\\node-gyp\\Cache\\25.2.1\\x64\\node.lib&quot;</AdditionalDependencies>
<AdditionalOptions>/LTCG:INCREMENTAL /ignore:4199 %(AdditionalOptions)</AdditionalOptions>
<DelayLoadDLLs>node.exe;%(DelayLoadDLLs)</DelayLoadDLLs>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OptimizeReferences>true</OptimizeReferences>
<OutputFile>$(OutDir)$(ProjectName).node</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TargetExt>.node</TargetExt>
<TargetMachine>MachineX64</TargetMachine>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\include\node;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\src;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\openssl\config;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\openssl\openssl\include;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\uv\include;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\zlib;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\v8\include;C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-addon-api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NODE_GYP_MODULE_NAME=anime_core;USING_UV_SHARED=1;USING_V8_SHARED=1;V8_DEPRECATION_WARNINGS=1;_GLIBCXX_USE_CXX11_ABI=1;_FILE_OFFSET_BITS=64;WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_HAS_EXCEPTIONS=0;NOMINMAX;OPENSSL_NO_PINSHARED;OPENSSL_THREADS;NAPI_DISABLE_CPP_EXCEPTIONS;BUILDING_NODE_EXTENSION;HOST_BINARY=&quot;node.exe&quot;;DEBUG;_DEBUG;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<AdditionalIncludeDirectories>C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\include\node;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\src;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\openssl\config;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\openssl\openssl\include;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\uv\include;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\zlib;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\v8\include;C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-addon-api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<AdditionalOptions>/Zc:__cplusplus -std:c++20 /Zm2000 %(AdditionalOptions)</AdditionalOptions>
<BufferSecurityCheck>true</BufferSecurityCheck>
<DebugInformationFormat>OldStyle</DebugInformationFormat>
<DisableSpecificWarnings>4351;4355;4800;4251;4275;4244;4267;%(DisableSpecificWarnings)</DisableSpecificWarnings>
<ExceptionHandling>false</ExceptionHandling>
<FavorSizeOrSpeed>Speed</FavorSizeOrSpeed>
<FunctionLevelLinking>true</FunctionLevelLinking>
<InlineFunctionExpansion>AnySuitable</InlineFunctionExpansion>
<IntrinsicFunctions>true</IntrinsicFunctions>
<MultiProcessorCompilation>true</MultiProcessorCompilation>
<OmitFramePointers>true</OmitFramePointers>
<Optimization>Full</Optimization>
<PrecompiledHeader>NotUsing</PrecompiledHeader>
<PreprocessorDefinitions>NODE_GYP_MODULE_NAME=anime_core;USING_UV_SHARED=1;USING_V8_SHARED=1;V8_DEPRECATION_WARNINGS=1;_GLIBCXX_USE_CXX11_ABI=1;_FILE_OFFSET_BITS=64;WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_HAS_EXCEPTIONS=0;NOMINMAX;OPENSSL_NO_PINSHARED;OPENSSL_THREADS;NAPI_DISABLE_CPP_EXCEPTIONS;BUILDING_NODE_EXTENSION;HOST_BINARY=&quot;node.exe&quot;;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
<RuntimeTypeInfo>false</RuntimeTypeInfo>
<StringPooling>true</StringPooling>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TreatWarningAsError>false</TreatWarningAsError>
<WarningLevel>Level3</WarningLevel>
<WholeProgramOptimization>true</WholeProgramOptimization>
</ClCompile>
<Lib>
<AdditionalOptions>/LTCG:INCREMENTAL %(AdditionalOptions)</AdditionalOptions>
</Lib>
<Link>
<AdditionalDependencies>kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;delayimp.lib;&quot;C:\\Users\\synta\\AppData\\Local\\node-gyp\\Cache\\25.2.1\\x64\\node.lib&quot;</AdditionalDependencies>
<AdditionalOptions>/LTCG:INCREMENTAL /ignore:4199 %(AdditionalOptions)</AdditionalOptions>
<DelayLoadDLLs>node.exe;%(DelayLoadDLLs)</DelayLoadDLLs>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<GenerateDebugInformation>true</GenerateDebugInformation>
<OptimizeReferences>true</OptimizeReferences>
<OutputFile>$(OutDir)$(ProjectName).node</OutputFile>
<SuppressStartupBanner>true</SuppressStartupBanner>
<TargetExt>.node</TargetExt>
<TargetMachine>MachineX64</TargetMachine>
</Link>
<ResourceCompile>
<AdditionalIncludeDirectories>C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\include\node;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\src;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\openssl\config;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\openssl\openssl\include;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\uv\include;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\zlib;C:\Users\synta\AppData\Local\node-gyp\Cache\25.2.1\deps\v8\include;C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-addon-api;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
<PreprocessorDefinitions>NODE_GYP_MODULE_NAME=anime_core;USING_UV_SHARED=1;USING_V8_SHARED=1;V8_DEPRECATION_WARNINGS=1;_GLIBCXX_USE_CXX11_ABI=1;_FILE_OFFSET_BITS=64;WIN32;_CRT_SECURE_NO_DEPRECATE;_CRT_NONSTDC_NO_DEPRECATE;_HAS_EXCEPTIONS=0;NOMINMAX;OPENSSL_NO_PINSHARED;OPENSSL_THREADS;NAPI_DISABLE_CPP_EXCEPTIONS;BUILDING_NODE_EXTENSION;HOST_BINARY=&quot;node.exe&quot;;%(PreprocessorDefinitions);%(PreprocessorDefinitions)</PreprocessorDefinitions>
</ResourceCompile>
</ItemDefinitionGroup>
<ItemGroup>
<None Include="..\binding.gyp"/>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\main.cpp">
<ObjectFileName>$(IntDir)\src\main.obj</ObjectFileName>
</ClCompile>
<ClCompile Include="C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-gyp\src\win_delay_load_hook.cc"/>
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets"/>
<Import Project="$(VCTargetsPath)\BuildCustomizations\masm.targets"/>
<ImportGroup Label="ExtensionTargets"/>
</Project>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="..">
<UniqueIdentifier>{739DB09A-CC57-A953-A6CF-F64FA08E4FA7}</UniqueIdentifier>
</Filter>
<Filter Include="..\src">
<UniqueIdentifier>{8CDEE807-BC53-E450-C8B8-4DEBB66742D4}</UniqueIdentifier>
</Filter>
<Filter Include="C:">
<UniqueIdentifier>{7B735499-E5DD-1C2B-6C26-70023832A1CF}</UniqueIdentifier>
</Filter>
<Filter Include="C:\Users">
<UniqueIdentifier>{E9F714C1-DA89-54E2-60CF-39FEB20BF756}</UniqueIdentifier>
</Filter>
<Filter Include="C:\Users\synta">
<UniqueIdentifier>{D7D157CA-EDF9-12DF-226F-983123736FEF}</UniqueIdentifier>
</Filter>
<Filter Include="C:\Users\synta\Documents">
<UniqueIdentifier>{87566BA0-CA33-1144-65F5-087C5F9D6C20}</UniqueIdentifier>
</Filter>
<Filter Include="C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)">
<UniqueIdentifier>{97B1B44F-2681-8BEF-5A42-2C7ED19BB9BD}</UniqueIdentifier>
</Filter>
<Filter Include="C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)">
<UniqueIdentifier>{4A3EC618-7921-9CB6-9236-10DE349C556E}</UniqueIdentifier>
</Filter>
<Filter Include="C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules">
<UniqueIdentifier>{56DF7A98-063D-FB9D-485C-089023B4C16A}</UniqueIdentifier>
</Filter>
<Filter Include="C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-gyp">
<UniqueIdentifier>{77348C0E-2034-7791-74D5-63C077DF5A3B}</UniqueIdentifier>
</Filter>
<Filter Include="C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-gyp\src">
<UniqueIdentifier>{8CDEE807-BC53-E450-C8B8-4DEBB66742D4}</UniqueIdentifier>
</Filter>
<Filter Include="..">
<UniqueIdentifier>{739DB09A-CC57-A953-A6CF-F64FA08E4FA7}</UniqueIdentifier>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="..\src\main.cpp">
<Filter>..\src</Filter>
</ClCompile>
<ClCompile Include="C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-gyp\src\win_delay_load_hook.cc">
<Filter>C:\Users\synta\Documents\WaifuBoard SRC Code (Multiple In Here)\Waifu Board (Server)\node_modules\node-gyp\src</Filter>
</ClCompile>
<None Include="..\binding.gyp">
<Filter>..</Filter>
</None>
</ItemGroup>
</Project>

19
build/binding.sln Normal file
View File

@@ -0,0 +1,19 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 2015
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "anime_core", "anime_core.vcxproj", "{E80660A8-73CA-0176-CBD2-DA941C5BB848}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Release|x64 = Release|x64
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{E80660A8-73CA-0176-CBD2-DA941C5BB848}.Debug|x64.ActiveCfg = Debug|x64
{E80660A8-73CA-0176-CBD2-DA941C5BB848}.Debug|x64.Build.0 = Debug|x64
{E80660A8-73CA-0176-CBD2-DA941C5BB848}.Release|x64.ActiveCfg = Release|x64
{E80660A8-73CA-0176-CBD2-DA941C5BB848}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

526
build/config.gypi Normal file
View File

@@ -0,0 +1,526 @@
# Do not edit. File was generated by node-gyp's "configure" step
{
"target_defaults": {
"cflags": [],
"configurations": {
"Debug": {
"v8_enable_v8_checks": 0,
"variables": {}
},
"Release": {
"v8_enable_v8_checks": 1,
"variables": {}
}
},
"default_configuration": "Release",
"defines": [],
"include_dirs": [],
"libraries": [],
"msbuild_toolset": "v143",
"msvs_windows_target_platform_version": "10.0.26100.0"
},
"variables": {
"asan": 0,
"clang": 0,
"control_flow_guard": "false",
"coverage": "false",
"dcheck_always_on": 0,
"debug_nghttp2": "false",
"debug_node": "false",
"enable_lto": "false",
"enable_pgo_generate": "false",
"enable_pgo_use": "false",
"error_on_warn": "false",
"force_dynamic_crt": 0,
"host_arch": "x64",
"icu_data_in": "..\\..\\deps\\icu-tmp\\icudt77l.dat",
"icu_endianness": "l",
"icu_gyp_path": "tools/icu/icu-generic.gyp",
"icu_path": "deps/icu-small",
"icu_small": "false",
"icu_ver_major": "77",
"libdir": "lib",
"llvm_version": "19.1.5",
"napi_build_version": "10",
"nasm_version": "3.01",
"node_builtin_shareable_builtins": [
"deps/cjs-module-lexer/lexer.js",
"deps/cjs-module-lexer/dist/lexer.js",
"deps/undici/undici.js",
"deps/amaro/dist/index.js"
],
"node_byteorder": "little",
"node_cctest_sources": [
"src/node_snapshot_stub.cc",
"test/cctest/inspector/test_network_requests_buffer.cc",
"test/cctest/inspector/test_node_protocol.cc",
"test/cctest/node_test_fixture.cc",
"test/cctest/test_aliased_buffer.cc",
"test/cctest/test_base64.cc",
"test/cctest/test_base_object_ptr.cc",
"test/cctest/test_cppgc.cc",
"test/cctest/test_crypto_clienthello.cc",
"test/cctest/test_dataqueue.cc",
"test/cctest/test_environment.cc",
"test/cctest/test_inspector_socket.cc",
"test/cctest/test_inspector_socket_server.cc",
"test/cctest/test_json_utils.cc",
"test/cctest/test_linked_binding.cc",
"test/cctest/test_lru_cache.cc",
"test/cctest/test_node_api.cc",
"test/cctest/test_node_crypto.cc",
"test/cctest/test_node_crypto_env.cc",
"test/cctest/test_node_postmortem_metadata.cc",
"test/cctest/test_node_task_runner.cc",
"test/cctest/test_path.cc",
"test/cctest/test_per_process.cc",
"test/cctest/test_platform.cc",
"test/cctest/test_quic_cid.cc",
"test/cctest/test_quic_error.cc",
"test/cctest/test_quic_preferredaddress.cc",
"test/cctest/test_quic_tokens.cc",
"test/cctest/test_report.cc",
"test/cctest/test_sockaddr.cc",
"test/cctest/test_string_bytes.cc",
"test/cctest/test_traced_value.cc",
"test/cctest/test_util.cc",
"test/cctest/node_test_fixture.h"
],
"node_debug_lib": "false",
"node_enable_d8": "false",
"node_enable_v8_vtunejit": "false",
"node_enable_v8windbg": "false",
"node_fipsinstall": "false",
"node_install_corepack": "false",
"node_install_npm": "true",
"node_library_files": [
"lib/_http_agent.js",
"lib/_http_client.js",
"lib/_http_common.js",
"lib/_http_incoming.js",
"lib/_http_outgoing.js",
"lib/_http_server.js",
"lib/_stream_duplex.js",
"lib/_stream_passthrough.js",
"lib/_stream_readable.js",
"lib/_stream_transform.js",
"lib/_stream_wrap.js",
"lib/_stream_writable.js",
"lib/_tls_common.js",
"lib/_tls_wrap.js",
"lib/assert.js",
"lib/assert/strict.js",
"lib/async_hooks.js",
"lib/buffer.js",
"lib/child_process.js",
"lib/cluster.js",
"lib/console.js",
"lib/constants.js",
"lib/crypto.js",
"lib/dgram.js",
"lib/diagnostics_channel.js",
"lib/dns.js",
"lib/dns/promises.js",
"lib/domain.js",
"lib/events.js",
"lib/fs.js",
"lib/fs/promises.js",
"lib/http.js",
"lib/http2.js",
"lib/https.js",
"lib/inspector.js",
"lib/inspector/promises.js",
"lib/internal/abort_controller.js",
"lib/internal/assert.js",
"lib/internal/assert/assertion_error.js",
"lib/internal/assert/myers_diff.js",
"lib/internal/assert/utils.js",
"lib/internal/async_context_frame.js",
"lib/internal/async_hooks.js",
"lib/internal/async_local_storage/async_context_frame.js",
"lib/internal/async_local_storage/async_hooks.js",
"lib/internal/blob.js",
"lib/internal/blocklist.js",
"lib/internal/bootstrap/node.js",
"lib/internal/bootstrap/realm.js",
"lib/internal/bootstrap/shadow_realm.js",
"lib/internal/bootstrap/switches/does_not_own_process_state.js",
"lib/internal/bootstrap/switches/does_own_process_state.js",
"lib/internal/bootstrap/switches/is_main_thread.js",
"lib/internal/bootstrap/switches/is_not_main_thread.js",
"lib/internal/bootstrap/web/exposed-wildcard.js",
"lib/internal/bootstrap/web/exposed-window-or-worker.js",
"lib/internal/buffer.js",
"lib/internal/child_process.js",
"lib/internal/child_process/serialization.js",
"lib/internal/cli_table.js",
"lib/internal/cluster/child.js",
"lib/internal/cluster/primary.js",
"lib/internal/cluster/round_robin_handle.js",
"lib/internal/cluster/shared_handle.js",
"lib/internal/cluster/utils.js",
"lib/internal/cluster/worker.js",
"lib/internal/console/constructor.js",
"lib/internal/console/global.js",
"lib/internal/constants.js",
"lib/internal/crypto/aes.js",
"lib/internal/crypto/argon2.js",
"lib/internal/crypto/certificate.js",
"lib/internal/crypto/cfrg.js",
"lib/internal/crypto/chacha20_poly1305.js",
"lib/internal/crypto/cipher.js",
"lib/internal/crypto/diffiehellman.js",
"lib/internal/crypto/ec.js",
"lib/internal/crypto/hash.js",
"lib/internal/crypto/hashnames.js",
"lib/internal/crypto/hkdf.js",
"lib/internal/crypto/kem.js",
"lib/internal/crypto/keygen.js",
"lib/internal/crypto/keys.js",
"lib/internal/crypto/mac.js",
"lib/internal/crypto/ml_dsa.js",
"lib/internal/crypto/ml_kem.js",
"lib/internal/crypto/pbkdf2.js",
"lib/internal/crypto/random.js",
"lib/internal/crypto/rsa.js",
"lib/internal/crypto/scrypt.js",
"lib/internal/crypto/sig.js",
"lib/internal/crypto/util.js",
"lib/internal/crypto/webcrypto.js",
"lib/internal/crypto/webidl.js",
"lib/internal/crypto/x509.js",
"lib/internal/data_url.js",
"lib/internal/debugger/inspect.js",
"lib/internal/debugger/inspect_client.js",
"lib/internal/debugger/inspect_repl.js",
"lib/internal/dgram.js",
"lib/internal/dns/callback_resolver.js",
"lib/internal/dns/promises.js",
"lib/internal/dns/utils.js",
"lib/internal/encoding.js",
"lib/internal/error_serdes.js",
"lib/internal/errors.js",
"lib/internal/errors/error_source.js",
"lib/internal/event_target.js",
"lib/internal/events/abort_listener.js",
"lib/internal/events/symbols.js",
"lib/internal/file.js",
"lib/internal/fixed_queue.js",
"lib/internal/freelist.js",
"lib/internal/freeze_intrinsics.js",
"lib/internal/fs/cp/cp-sync.js",
"lib/internal/fs/cp/cp.js",
"lib/internal/fs/dir.js",
"lib/internal/fs/glob.js",
"lib/internal/fs/promises.js",
"lib/internal/fs/read/context.js",
"lib/internal/fs/recursive_watch.js",
"lib/internal/fs/rimraf.js",
"lib/internal/fs/streams.js",
"lib/internal/fs/sync_write_stream.js",
"lib/internal/fs/utils.js",
"lib/internal/fs/watchers.js",
"lib/internal/heap_utils.js",
"lib/internal/histogram.js",
"lib/internal/http.js",
"lib/internal/http2/compat.js",
"lib/internal/http2/core.js",
"lib/internal/http2/util.js",
"lib/internal/inspector/network.js",
"lib/internal/inspector/network_http.js",
"lib/internal/inspector/network_http2.js",
"lib/internal/inspector/network_resources.js",
"lib/internal/inspector/network_undici.js",
"lib/internal/inspector_async_hook.js",
"lib/internal/inspector_network_tracking.js",
"lib/internal/js_stream_socket.js",
"lib/internal/legacy/processbinding.js",
"lib/internal/linkedlist.js",
"lib/internal/locks.js",
"lib/internal/main/check_syntax.js",
"lib/internal/main/embedding.js",
"lib/internal/main/eval_stdin.js",
"lib/internal/main/eval_string.js",
"lib/internal/main/inspect.js",
"lib/internal/main/mksnapshot.js",
"lib/internal/main/print_help.js",
"lib/internal/main/prof_process.js",
"lib/internal/main/repl.js",
"lib/internal/main/run_main_module.js",
"lib/internal/main/test_runner.js",
"lib/internal/main/watch_mode.js",
"lib/internal/main/worker_thread.js",
"lib/internal/mime.js",
"lib/internal/modules/cjs/loader.js",
"lib/internal/modules/customization_hooks.js",
"lib/internal/modules/esm/assert.js",
"lib/internal/modules/esm/create_dynamic_module.js",
"lib/internal/modules/esm/formats.js",
"lib/internal/modules/esm/get_format.js",
"lib/internal/modules/esm/hooks.js",
"lib/internal/modules/esm/load.js",
"lib/internal/modules/esm/loader.js",
"lib/internal/modules/esm/module_job.js",
"lib/internal/modules/esm/module_map.js",
"lib/internal/modules/esm/resolve.js",
"lib/internal/modules/esm/shared_constants.js",
"lib/internal/modules/esm/translators.js",
"lib/internal/modules/esm/utils.js",
"lib/internal/modules/esm/worker.js",
"lib/internal/modules/helpers.js",
"lib/internal/modules/package_json_reader.js",
"lib/internal/modules/run_main.js",
"lib/internal/modules/typescript.js",
"lib/internal/navigator.js",
"lib/internal/net.js",
"lib/internal/options.js",
"lib/internal/per_context/domexception.js",
"lib/internal/per_context/messageport.js",
"lib/internal/per_context/primordials.js",
"lib/internal/perf/event_loop_delay.js",
"lib/internal/perf/event_loop_utilization.js",
"lib/internal/perf/nodetiming.js",
"lib/internal/perf/observe.js",
"lib/internal/perf/performance.js",
"lib/internal/perf/performance_entry.js",
"lib/internal/perf/resource_timing.js",
"lib/internal/perf/timerify.js",
"lib/internal/perf/usertiming.js",
"lib/internal/perf/utils.js",
"lib/internal/priority_queue.js",
"lib/internal/process/execution.js",
"lib/internal/process/finalization.js",
"lib/internal/process/per_thread.js",
"lib/internal/process/permission.js",
"lib/internal/process/pre_execution.js",
"lib/internal/process/promises.js",
"lib/internal/process/report.js",
"lib/internal/process/signal.js",
"lib/internal/process/task_queues.js",
"lib/internal/process/warning.js",
"lib/internal/process/worker_thread_only.js",
"lib/internal/promise_hooks.js",
"lib/internal/querystring.js",
"lib/internal/quic/quic.js",
"lib/internal/quic/state.js",
"lib/internal/quic/stats.js",
"lib/internal/quic/symbols.js",
"lib/internal/readline/callbacks.js",
"lib/internal/readline/emitKeypressEvents.js",
"lib/internal/readline/interface.js",
"lib/internal/readline/promises.js",
"lib/internal/readline/utils.js",
"lib/internal/repl.js",
"lib/internal/repl/await.js",
"lib/internal/repl/completion.js",
"lib/internal/repl/history.js",
"lib/internal/repl/utils.js",
"lib/internal/socket_list.js",
"lib/internal/socketaddress.js",
"lib/internal/source_map/prepare_stack_trace.js",
"lib/internal/source_map/source_map.js",
"lib/internal/source_map/source_map_cache.js",
"lib/internal/source_map/source_map_cache_map.js",
"lib/internal/stream_base_commons.js",
"lib/internal/streams/add-abort-signal.js",
"lib/internal/streams/compose.js",
"lib/internal/streams/destroy.js",
"lib/internal/streams/duplex.js",
"lib/internal/streams/duplexify.js",
"lib/internal/streams/duplexpair.js",
"lib/internal/streams/end-of-stream.js",
"lib/internal/streams/fast-utf8-stream.js",
"lib/internal/streams/from.js",
"lib/internal/streams/lazy_transform.js",
"lib/internal/streams/legacy.js",
"lib/internal/streams/operators.js",
"lib/internal/streams/passthrough.js",
"lib/internal/streams/pipeline.js",
"lib/internal/streams/readable.js",
"lib/internal/streams/state.js",
"lib/internal/streams/transform.js",
"lib/internal/streams/utils.js",
"lib/internal/streams/writable.js",
"lib/internal/test/binding.js",
"lib/internal/test/transfer.js",
"lib/internal/test_runner/assert.js",
"lib/internal/test_runner/coverage.js",
"lib/internal/test_runner/harness.js",
"lib/internal/test_runner/mock/loader.js",
"lib/internal/test_runner/mock/mock.js",
"lib/internal/test_runner/mock/mock_timers.js",
"lib/internal/test_runner/reporter/dot.js",
"lib/internal/test_runner/reporter/junit.js",
"lib/internal/test_runner/reporter/lcov.js",
"lib/internal/test_runner/reporter/rerun.js",
"lib/internal/test_runner/reporter/spec.js",
"lib/internal/test_runner/reporter/tap.js",
"lib/internal/test_runner/reporter/utils.js",
"lib/internal/test_runner/reporter/v8-serializer.js",
"lib/internal/test_runner/runner.js",
"lib/internal/test_runner/snapshot.js",
"lib/internal/test_runner/test.js",
"lib/internal/test_runner/tests_stream.js",
"lib/internal/test_runner/utils.js",
"lib/internal/timers.js",
"lib/internal/tls/common.js",
"lib/internal/tls/secure-context.js",
"lib/internal/tls/wrap.js",
"lib/internal/trace_events_async_hooks.js",
"lib/internal/tty.js",
"lib/internal/url.js",
"lib/internal/util.js",
"lib/internal/util/colors.js",
"lib/internal/util/comparisons.js",
"lib/internal/util/debuglog.js",
"lib/internal/util/diff.js",
"lib/internal/util/inspect.js",
"lib/internal/util/inspector.js",
"lib/internal/util/parse_args/parse_args.js",
"lib/internal/util/parse_args/utils.js",
"lib/internal/util/trace_sigint.js",
"lib/internal/util/types.js",
"lib/internal/v8/startup_snapshot.js",
"lib/internal/v8_prof_polyfill.js",
"lib/internal/v8_prof_processor.js",
"lib/internal/validators.js",
"lib/internal/vm.js",
"lib/internal/vm/module.js",
"lib/internal/wasm_web_api.js",
"lib/internal/watch_mode/files_watcher.js",
"lib/internal/watchdog.js",
"lib/internal/webidl.js",
"lib/internal/webstorage.js",
"lib/internal/webstreams/adapters.js",
"lib/internal/webstreams/compression.js",
"lib/internal/webstreams/encoding.js",
"lib/internal/webstreams/queuingstrategies.js",
"lib/internal/webstreams/readablestream.js",
"lib/internal/webstreams/transfer.js",
"lib/internal/webstreams/transformstream.js",
"lib/internal/webstreams/util.js",
"lib/internal/webstreams/writablestream.js",
"lib/internal/worker.js",
"lib/internal/worker/clone_dom_exception.js",
"lib/internal/worker/io.js",
"lib/internal/worker/js_transferable.js",
"lib/internal/worker/messaging.js",
"lib/module.js",
"lib/net.js",
"lib/os.js",
"lib/path.js",
"lib/path/posix.js",
"lib/path/win32.js",
"lib/perf_hooks.js",
"lib/process.js",
"lib/punycode.js",
"lib/querystring.js",
"lib/quic.js",
"lib/readline.js",
"lib/readline/promises.js",
"lib/repl.js",
"lib/sea.js",
"lib/sqlite.js",
"lib/stream.js",
"lib/stream/consumers.js",
"lib/stream/promises.js",
"lib/stream/web.js",
"lib/string_decoder.js",
"lib/sys.js",
"lib/test.js",
"lib/test/reporters.js",
"lib/timers.js",
"lib/timers/promises.js",
"lib/tls.js",
"lib/trace_events.js",
"lib/tty.js",
"lib/url.js",
"lib/util.js",
"lib/util/types.js",
"lib/v8.js",
"lib/vm.js",
"lib/wasi.js",
"lib/worker_threads.js",
"lib/zlib.js"
],
"node_module_version": 141,
"node_no_browser_globals": "false",
"node_prefix": "\\usr\\local",
"node_release_urlbase": "https://nodejs.org/download/release/",
"node_shared": "false",
"node_shared_ada": "false",
"node_shared_brotli": "false",
"node_shared_cares": "false",
"node_shared_http_parser": "false",
"node_shared_libuv": "false",
"node_shared_nghttp2": "false",
"node_shared_nghttp3": "false",
"node_shared_ngtcp2": "false",
"node_shared_openssl": "false",
"node_shared_simdjson": "false",
"node_shared_simdutf": "false",
"node_shared_sqlite": "false",
"node_shared_uvwasi": "false",
"node_shared_zlib": "false",
"node_shared_zstd": "false",
"node_tag": "",
"node_target_type": "executable",
"node_use_amaro": "true",
"node_use_bundled_v8": "true",
"node_use_node_code_cache": "true",
"node_use_node_snapshot": "true",
"node_use_openssl": "true",
"node_use_sqlite": "true",
"node_use_v8_platform": "true",
"node_with_ltcg": "true",
"node_without_node_options": "false",
"node_write_snapshot_as_array_literals": "true",
"openssl_is_fips": "false",
"openssl_version": 810549327,
"ossfuzz": "false",
"shlib_suffix": "so.141",
"single_executable_application": "true",
"suppress_all_error_on_warn": "false",
"target_arch": "x64",
"ubsan": 0,
"use_ccache_win": 0,
"use_prefix_to_find_headers": "false",
"v8_enable_31bit_smis_on_64bit_arch": 0,
"v8_enable_extensible_ro_snapshot": 0,
"v8_enable_external_code_space": 0,
"v8_enable_gdbjit": 0,
"v8_enable_hugepage": 0,
"v8_enable_i18n_support": 1,
"v8_enable_inspector": 1,
"v8_enable_javascript_promise_hooks": 1,
"v8_enable_lite_mode": 0,
"v8_enable_maglev": 1,
"v8_enable_object_print": 1,
"v8_enable_pointer_compression": 0,
"v8_enable_pointer_compression_shared_cage": 0,
"v8_enable_sandbox": 0,
"v8_enable_short_builtin_calls": 1,
"v8_enable_wasm_simd256_revec": 1,
"v8_enable_webassembly": 1,
"v8_optimized_debug": 1,
"v8_promise_internal_field_count": 1,
"v8_random_seed": 0,
"v8_trace_maps": 0,
"v8_use_siphash": 1,
"want_separate_host_toolset": 0,
"nodedir": "C:\\Users\\synta\\AppData\\Local\\node-gyp\\Cache\\25.2.1",
"python": "C:\\Users\\synta\\AppData\\Local\\Python\\pythoncore-3.14-64\\python.exe",
"standalone_static_library": 1,
"msbuild_path": "C:\\Program Files\\Microsoft Visual Studio\\2022\\Community\\MSBuild\\Current\\Bin\\MSBuild.exe",
"user_agent": "npm/11.6.4 node/v25.2.1 win32 x64 workspaces/false",
"userconfig": "C:\\Users\\synta\\.npmrc",
"prefix": "C:\\Users\\synta\\AppData\\Roaming\\npm",
"npm_version": "11.6.4",
"node_gyp": "C:\\Users\\synta\\AppData\\Roaming\\npm\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js",
"local_prefix": "C:\\Users\\synta\\Documents\\WaifuBoard SRC Code (Multiple In Here)\\Waifu Board (Server)",
"init_module": "C:\\Users\\synta\\.npm-init.js",
"global_prefix": "C:\\Users\\synta\\AppData\\Roaming\\npm",
"globalconfig": "C:\\Users\\synta\\AppData\\Roaming\\npm\\etc\\npmrc",
"cache": "C:\\Users\\synta\\AppData\\Local\\npm-cache"
}
}

BIN
dist/Waifu Board 2.0.0.exe vendored Normal file

Binary file not shown.

32
dist/builder-debug.yml vendored Normal file

File diff suppressed because one or more lines are too long

22
dist/builder-effective-config.yaml vendored Normal file
View File

@@ -0,0 +1,22 @@
directories:
output: dist
buildResources: build
appId: app.waifuboard
productName: Waifu Board
files:
- filter:
- electron/**/*
- server.js
- main.js
- preload.js
- package.json
- views/**/*
- src/scripts/**/*
- public/assets/*
extraResources:
- filter:
- ./.env
win:
target: portable
icon: public/assets/waifuboards.ico
electronVersion: 39.2.5

21
dist/win-unpacked/LICENSE.electron.txt vendored Normal file
View File

@@ -0,0 +1,21 @@
Copyright (c) Electron contributors
Copyright (c) 2013-2020 GitHub Inc.
Permission is hereby granted, free of charge, to any person obtaining
a copy of this software and associated documentation files (the
"Software"), to deal in the Software without restriction, including
without limitation the rights to use, copy, modify, merge, publish,
distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to
the following conditions:
The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

270541
dist/win-unpacked/LICENSES.chromium.html vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
dist/win-unpacked/Waifu Board.exe vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/chrome_100_percent.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/chrome_200_percent.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/d3dcompiler_47.dll vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/dxcompiler.dll vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/dxil.dll vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/ffmpeg.dll vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/icudtl.dat vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/libEGL.dll vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/libGLESv2.dll vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/af.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/am.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ar.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/bg.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/bn.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ca.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/cs.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/da.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/de.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/el.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/en-GB.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/en-US.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/es-419.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/es.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/et.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/fa.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/fi.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/fil.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/fr.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/gu.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/he.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/hi.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/hr.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/hu.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/id.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/it.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ja.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/kn.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ko.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/lt.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/lv.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ml.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/mr.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ms.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/nb.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/nl.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/pl.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/pt-BR.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/pt-PT.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ro.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ru.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/sk.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/sl.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/sr.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/sv.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/sw.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ta.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/te.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/th.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/tr.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/uk.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/ur.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/vi.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/zh-CN.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/locales/zh-TW.pak vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/resources.pak vendored Normal file

Binary file not shown.

4
dist/win-unpacked/resources/.env vendored Normal file
View File

@@ -0,0 +1,4 @@
DISCORD_CLIENT_ID=1440755333055250616
ANILIST_CLIENT_SECRET=fTql48IjI7ZAE2cbjFaMl2I5HhYHYr1kVDyDNpOk
ANILIST_CLIENT_ID=32898
JWT_SECRET=900c86474497ce3c329ba9949d834dbb1dd072163a5867957be929f3adc592d2d6352c03aecbfe065a8b9e9eff0e7fe7516a50dbf206d5e8c69bdcba22477cb6

BIN
dist/win-unpacked/resources/app.asar vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/resources/elevate.exe vendored Normal file

Binary file not shown.

BIN
dist/win-unpacked/snapshot_blob.bin vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
dist/win-unpacked/vk_swiftshader.dll vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1 @@
{"file_format_version": "1.0.0", "ICD": {"library_path": ".\\vk_swiftshader.dll", "api_version": "1.0.5"}}

BIN
dist/win-unpacked/vulkan-1.dll vendored Normal file

Binary file not shown.

76
electron/api/anilist.js Normal file
View File

@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const database_1 = require("../shared/database");
async function anilist(fastify) {
fastify.get("/anilist", async (request, reply) => {
try {
const { code, state } = request.query;
if (!code)
return reply.status(400).send("No code");
if (!state)
return reply.status(400).send("No user state");
const userId = Number(state);
if (!userId || isNaN(userId)) {
return reply.status(400).send("Invalid user id");
}
const tokenRes = await fetch("https://anilist.co/api/v2/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "authorization_code",
client_id: process.env.ANILIST_CLIENT_ID,
client_secret: process.env.ANILIST_CLIENT_SECRET,
redirect_uri: "http://localhost:54322/api/anilist",
code
})
});
const tokenData = await tokenRes.json();
if (!tokenData.access_token) {
console.error("AniList token error:", tokenData);
return reply.status(500).send("Failed to get AniList token");
}
const userRes = await fetch("https://graphql.anilist.co", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `${tokenData.token_type} ${tokenData.access_token}`
},
body: JSON.stringify({
query: `query { Viewer { id } }`
})
});
const userData = await userRes.json();
const anilistUserId = userData?.data?.Viewer?.id;
if (!anilistUserId) {
console.error("AniList Viewer error:", userData);
return reply.status(500).send("Failed to fetch AniList user");
}
const expiresAt = new Date(Date.now() + tokenData.expires_in * 1000).toISOString();
await (0, database_1.run)(`
INSERT INTO UserIntegration
(user_id, platform, access_token, refresh_token, token_type, anilist_user_id, expires_at)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(user_id) DO UPDATE SET
access_token = excluded.access_token,
refresh_token = excluded.refresh_token,
token_type = excluded.token_type,
anilist_user_id = excluded.anilist_user_id,
expires_at = excluded.expires_at
`, [
userId,
"AniList",
tokenData.access_token,
tokenData.refresh_token,
tokenData.token_type,
anilistUserId,
expiresAt
], "userdata");
return reply.redirect("http://localhost:54322/?anilist=success");
}
catch (e) {
console.error("AniList error:", e);
return reply.redirect("http://localhost:54322/?anilist=error");
}
});
}
exports.default = anilist;

View File

@@ -0,0 +1,76 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const database_1 = require("../../shared/database");
async function anilist(fastify) {
fastify.get("/anilist", async (request, reply) => {
try {
const { code, state } = request.query;
if (!code)
return reply.status(400).send("No code");
if (!state)
return reply.status(400).send("No user state");
const userId = Number(state);
if (!userId || isNaN(userId)) {
return reply.status(400).send("Invalid user id");
}
const tokenRes = await fetch("https://anilist.co/api/v2/oauth/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
grant_type: "authorization_code",
client_id: process.env.ANILIST_CLIENT_ID,
client_secret: process.env.ANILIST_CLIENT_SECRET,
redirect_uri: "http://localhost:54322/api/anilist",
code
})
});
const tokenData = await tokenRes.json();
if (!tokenData.access_token) {
console.error("AniList token error:", tokenData);
return reply.status(500).send("Failed to get AniList token");
}
const userRes = await fetch("https://graphql.anilist.co", {
method: "POST",
headers: {
"Content-Type": "application/json",
Authorization: `${tokenData.token_type} ${tokenData.access_token}`
},
body: JSON.stringify({
query: `query { Viewer { id } }`
})
});
const userData = await userRes.json();
const anilistUserId = userData?.data?.Viewer?.id;
if (!anilistUserId) {
console.error("AniList Viewer error:", userData);
return reply.status(500).send("Failed to fetch AniList user");
}
const expiresAt = new Date(Date.now() + tokenData.expires_in * 1000).toISOString();
await (0, database_1.run)(`
INSERT INTO UserIntegration
(user_id, platform, access_token, refresh_token, token_type, anilist_user_id, expires_at)
VALUES (?, ?, ?, ?, ?, ?, ?)
ON CONFLICT(user_id) DO UPDATE SET
access_token = excluded.access_token,
refresh_token = excluded.refresh_token,
token_type = excluded.token_type,
anilist_user_id = excluded.anilist_user_id,
expires_at = excluded.expires_at
`, [
userId,
"AniList",
tokenData.access_token,
tokenData.refresh_token,
tokenData.token_type,
anilistUserId,
expiresAt
], "userdata");
return reply.redirect("http://localhost:54322/?anilist=success");
}
catch (e) {
console.error("AniList error:", e);
return reply.redirect("http://localhost:54322/?anilist=error");
}
});
}
exports.default = anilist;

View File

@@ -0,0 +1,478 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getUserAniList = getUserAniList;
exports.updateAniListEntry = updateAniListEntry;
exports.deleteAniListEntry = deleteAniListEntry;
exports.getSingleAniListEntry = getSingleAniListEntry;
const database_1 = require("../../shared/database");
const USER_DB = 'userdata';
// Configuración de reintentos
const RETRY_CONFIG = {
maxRetries: 3,
initialDelay: 1000,
maxDelay: 5000,
backoffMultiplier: 2
};
// Helper para hacer requests con reintentos y manejo de errores
async function fetchWithRetry(url, options, retries = RETRY_CONFIG.maxRetries) {
let lastError = null;
let delay = RETRY_CONFIG.initialDelay;
for (let attempt = 0; attempt <= retries; attempt++) {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 10000); // 10s timeout
const response = await fetch(url, {
...options,
signal: controller.signal
});
clearTimeout(timeoutId);
// Si es rate limit (429), esperamos más tiempo
if (response.status === 429) {
const retryAfter = response.headers.get('Retry-After');
const waitTime = retryAfter ? parseInt(retryAfter) * 1000 : delay;
if (attempt < retries) {
console.warn(`Rate limited. Esperando ${waitTime}ms antes de reintentar...`);
await new Promise(resolve => setTimeout(resolve, waitTime));
delay = Math.min(delay * RETRY_CONFIG.backoffMultiplier, RETRY_CONFIG.maxDelay);
continue;
}
}
// Si es un error de servidor (5xx), reintentamos
if (response.status >= 500 && attempt < retries) {
console.warn(`Error del servidor (${response.status}). Reintentando en ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
delay = Math.min(delay * RETRY_CONFIG.backoffMultiplier, RETRY_CONFIG.maxDelay);
continue;
}
return response;
}
catch (error) {
lastError = error;
if (attempt < retries && (error instanceof Error && (error.name === 'AbortError' ||
error.message.includes('fetch') ||
error.message.includes('network')))) {
console.warn(`Error de conexión (intento ${attempt + 1}/${retries + 1}). Reintentando en ${delay}ms...`);
await new Promise(resolve => setTimeout(resolve, delay));
delay = Math.min(delay * RETRY_CONFIG.backoffMultiplier, RETRY_CONFIG.maxDelay);
continue;
}
throw error;
}
}
throw lastError || new Error('Request failed after all retries');
}
async function getUserAniList(appUserId) {
try {
const sql = `
SELECT access_token, anilist_user_id
FROM UserIntegration
WHERE user_id = ? AND platform = 'AniList';
`;
const integration = await (0, database_1.queryOne)(sql, [appUserId], USER_DB);
if (!integration)
return [];
const { access_token, anilist_user_id } = integration;
if (!access_token || !anilist_user_id)
return [];
const query = `
query ($userId: Int) {
anime: MediaListCollection(userId: $userId, type: ANIME) {
lists {
entries {
media {
id
title { romaji english userPreferred }
coverImage { extraLarge }
episodes
nextAiringEpisode { episode }
}
status
progress
score
repeat
notes
private
startedAt { year month day }
completedAt { year month day }
}
}
}
manga: MediaListCollection(userId: $userId, type: MANGA) {
lists {
entries {
media {
id
type
format
title { romaji english userPreferred }
coverImage { extraLarge }
chapters
volumes
}
status
progress
score
repeat
notes
private
startedAt { year month day }
completedAt { year month day }
}
}
}
}
`;
const res = await fetchWithRetry('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Authorization': `Bearer ${access_token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query,
variables: { userId: anilist_user_id }
}),
});
if (!res.ok)
throw new Error(`AniList API error: ${res.status}`);
const json = await res.json();
if (json?.errors?.length)
throw new Error(json.errors[0].message);
const fromFuzzy = (d) => {
if (!d?.year)
return null;
const m = String(d.month || 1).padStart(2, '0');
const day = String(d.day || 1).padStart(2, '0');
return `${d.year}-${m}-${day}`;
};
const normalize = (lists, type) => {
const result = [];
for (const list of lists || []) {
for (const entry of list.entries || []) {
const media = entry.media;
const totalEpisodes = media?.episodes ||
(media?.nextAiringEpisode?.episode
? media.nextAiringEpisode.episode - 1
: 0);
const totalChapters = media?.chapters ||
(media?.volumes ? media.volumes * 10 : 0);
const resolvedType = type === 'MANGA' &&
(media?.format === 'LIGHT_NOVEL' || media?.format === 'NOVEL')
? 'NOVEL'
: type;
result.push({
user_id: appUserId,
entry_id: media.id,
source: 'anilist',
// ✅ AHORA TU FRONT RECIBE NOVEL
entry_type: resolvedType,
status: entry.status,
progress: entry.progress || 0,
score: entry.score || null,
start_date: fromFuzzy(entry.startedAt),
end_date: fromFuzzy(entry.completedAt),
repeat_count: entry.repeat || 0,
notes: entry.notes || null,
is_private: entry.private ? 1 : 0,
title: media?.title?.userPreferred
|| media?.title?.english
|| media?.title?.romaji
|| 'Unknown Title',
poster: media?.coverImage?.extraLarge
|| 'https://placehold.co/400x600?text=No+Cover',
total_episodes: resolvedType === 'ANIME' ? totalEpisodes : undefined,
total_chapters: resolvedType !== 'ANIME' ? totalChapters : undefined,
updated_at: new Date().toISOString()
});
}
}
return result;
};
return [
...normalize(json?.data?.anime?.lists, 'ANIME'),
...normalize(json?.data?.manga?.lists, 'MANGA')
];
}
catch (error) {
console.error('Error fetching AniList data:', error);
return [];
}
}
async function updateAniListEntry(token, params) {
try {
if (!token)
throw new Error('AniList token is required');
const mutation = `
mutation (
$mediaId: Int,
$status: MediaListStatus,
$progress: Int,
$score: Float,
$startedAt: FuzzyDateInput,
$completedAt: FuzzyDateInput,
$repeat: Int,
$notes: String,
$private: Boolean
) {
SaveMediaListEntry (
mediaId: $mediaId,
status: $status,
progress: $progress,
score: $score,
startedAt: $startedAt,
completedAt: $completedAt,
repeat: $repeat,
notes: $notes,
private: $private
) {
id
status
progress
score
startedAt { year month day }
completedAt { year month day }
repeat
notes
private
}
}
`;
const toFuzzyDate = (dateStr) => {
if (!dateStr)
return null;
const [year, month, day] = dateStr.split('-').map(Number);
return { year, month, day };
};
const variables = {
mediaId: Number(params.mediaId),
status: params.status ?? undefined,
progress: params.progress ?? undefined,
score: params.score ?? undefined,
startedAt: toFuzzyDate(params.start_date),
completedAt: toFuzzyDate(params.end_date),
repeat: params.repeat_count ?? undefined,
notes: params.notes ?? undefined,
private: typeof params.is_private === 'boolean'
? params.is_private
: params.is_private === 1
};
const res = await fetchWithRetry('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ query: mutation, variables }),
});
if (!res.ok) {
const errorText = await res.text();
throw new Error(`AniList update failed: ${res.status} - ${errorText}`);
}
const json = await res.json();
if (json?.errors?.length) {
throw new Error(`AniList GraphQL error: ${json.errors[0].message}`);
}
return json.data?.SaveMediaListEntry || null;
}
catch (error) {
console.error('Error updating AniList entry:', error);
throw error;
}
}
async function deleteAniListEntry(token, mediaId) {
if (!token)
throw new Error("AniList token required");
try {
// 1⃣ OBTENER VIEWER
const viewerQuery = `query { Viewer { id name } }`;
const vRes = await fetchWithRetry('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({ query: viewerQuery }),
});
const vJson = await vRes.json();
const userId = vJson?.data?.Viewer?.id;
if (!userId)
throw new Error("Invalid AniList token");
// 2⃣ DETECTAR TIPO REAL DEL MEDIA
const mediaQuery = `
query ($id: Int) {
Media(id: $id) {
id
type
}
}
`;
const mTypeRes = await fetchWithRetry('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query: mediaQuery,
variables: { id: mediaId }
}),
});
const mTypeJson = await mTypeRes.json();
const mediaType = mTypeJson?.data?.Media?.type;
if (!mediaType) {
throw new Error("Media not found in AniList");
}
// 3⃣ BUSCAR ENTRY CON TIPO REAL
const listQuery = `
query ($userId: Int, $mediaId: Int, $type: MediaType) {
MediaList(userId: $userId, mediaId: $mediaId, type: $type) {
id
}
}
`;
const qRes = await fetchWithRetry('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query: listQuery,
variables: {
userId,
mediaId,
type: mediaType
}
}),
});
const qJson = await qRes.json();
const listEntryId = qJson?.data?.MediaList?.id;
if (!listEntryId) {
throw new Error("Entry not found in user's AniList");
}
// 4⃣ BORRAR
const mutation = `
mutation ($id: Int) {
DeleteMediaListEntry(id: $id) {
deleted
}
}
`;
const delRes = await fetchWithRetry('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query: mutation,
variables: { id: listEntryId }
}),
});
const delJson = await delRes.json();
if (delJson?.errors?.length) {
throw new Error(delJson.errors[0].message);
}
return true;
}
catch (err) {
console.error("AniList DELETE failed:", err);
throw err;
}
}
async function getSingleAniListEntry(token, mediaId, type) {
try {
if (!token) {
throw new Error('AniList token is required');
}
// 1⃣ Obtener userId desde el token
const viewerRes = await fetchWithRetry('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query: `query { Viewer { id } }`
})
});
const viewerJson = await viewerRes.json();
const userId = viewerJson?.data?.Viewer?.id;
if (!userId) {
throw new Error('Failed to get AniList userId');
}
// 2⃣ Query correcta con userId
const query = `
query ($mediaId: Int, $type: MediaType, $userId: Int) {
MediaList(mediaId: $mediaId, type: $type, userId: $userId) {
id
status
progress
score
repeat
private
notes
startedAt { year month day }
completedAt { year month day }
}
}
`;
const res = await fetchWithRetry('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Authorization': `Bearer ${token}`,
'Content-Type': 'application/json',
'Accept': 'application/json',
},
body: JSON.stringify({
query,
variables: { mediaId, type, userId }
})
});
if (res.status === 404) {
return null; // ✅ No existe entry todavía → es totalmente válido
}
if (!res.ok) {
const errorText = await res.text();
throw new Error(`AniList fetch failed: ${res.status} - ${errorText}`);
}
const json = await res.json();
if (json?.errors?.length) {
if (json.errors[0].status === 404)
return null;
throw new Error(`GraphQL error: ${json.errors[0].message}`);
}
const entry = json?.data?.MediaList;
if (!entry)
return null;
return {
entry_id: mediaId,
source: 'anilist',
entry_type: type,
status: entry.status,
progress: entry.progress || 0,
score: entry.score ?? null,
start_date: entry.startedAt?.year
? `${entry.startedAt.year}-${String(entry.startedAt.month).padStart(2, '0')}-${String(entry.startedAt.day).padStart(2, '0')}`
: null,
end_date: entry.completedAt?.year
? `${entry.completedAt.year}-${String(entry.completedAt.month).padStart(2, '0')}-${String(entry.completedAt.day).padStart(2, '0')}`
: null,
repeat_count: entry.repeat || 0,
notes: entry.notes || null,
is_private: entry.private ? 1 : 0,
};
}
catch (error) {
console.error('Error fetching single AniList entry:', error);
throw error;
}
}

View File

@@ -0,0 +1,130 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAnime = getAnime;
exports.getAnimeEpisodes = getAnimeEpisodes;
exports.getTrending = getTrending;
exports.getTopAiring = getTopAiring;
exports.search = search;
exports.searchInExtension = searchInExtension;
exports.getWatchStream = getWatchStream;
const animeService = __importStar(require("./anime.service"));
const extensions_1 = require("../../shared/extensions");
async function getAnime(req, reply) {
try {
const { id } = req.params;
const source = req.query.source;
let anime;
if (source === 'anilist') {
anime = await animeService.getAnimeById(id);
}
else {
const ext = (0, extensions_1.getExtension)(source);
anime = await animeService.getAnimeInfoExtension(ext, id);
}
return anime;
}
catch (err) {
return { error: "Database error" };
}
}
async function getAnimeEpisodes(req, reply) {
try {
const { id } = req.params;
const source = req.query.source || 'anilist';
const ext = (0, extensions_1.getExtension)(source);
return await animeService.searchEpisodesInExtension(ext, source, id);
}
catch (err) {
return { error: "Database error" };
}
}
async function getTrending(req, reply) {
try {
const results = await animeService.getTrendingAnime();
return { results };
}
catch (err) {
return { results: [] };
}
}
async function getTopAiring(req, reply) {
try {
const results = await animeService.getTopAiringAnime();
return { results };
}
catch (err) {
return { results: [] };
}
}
async function search(req, reply) {
try {
const query = req.query.q;
const results = await animeService.searchAnimeLocal(query);
if (results.length > 0) {
return { results: results };
}
}
catch (err) {
return { results: [] };
}
}
async function searchInExtension(req, reply) {
try {
const extensionName = req.params.extension;
const query = req.query.q;
const ext = (0, extensions_1.getExtension)(extensionName);
if (!ext)
return { results: [] };
const results = await animeService.searchAnimeInExtension(ext, extensionName, query);
return { results };
}
catch {
return { results: [] };
}
}
async function getWatchStream(req, reply) {
try {
const { animeId, episode, server, category, ext, source } = req.query;
const extension = (0, extensions_1.getExtension)(ext);
if (!extension)
return { error: "Extension not found" };
return await animeService.getStreamData(extension, episode, animeId, source, server, category);
}
catch (err) {
const error = err;
return { error: error.message };
}
}

View File

@@ -0,0 +1,46 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const controller = __importStar(require("./anime.controller"));
async function animeRoutes(fastify) {
fastify.get('/anime/:id', controller.getAnime);
fastify.get('/anime/:id/:episodes', controller.getAnimeEpisodes);
fastify.get('/trending', controller.getTrending);
fastify.get('/top-airing', controller.getTopAiring);
fastify.get('/search', controller.search);
fastify.get('/search/:extension', controller.searchInExtension);
fastify.get('/watch/stream', controller.getWatchStream);
}
exports.default = animeRoutes;

View File

@@ -0,0 +1,375 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getAnimeById = getAnimeById;
exports.getTrendingAnime = getTrendingAnime;
exports.getTopAiringAnime = getTopAiringAnime;
exports.searchAnimeLocal = searchAnimeLocal;
exports.getAnimeInfoExtension = getAnimeInfoExtension;
exports.searchAnimeInExtension = searchAnimeInExtension;
exports.searchEpisodesInExtension = searchEpisodesInExtension;
exports.getStreamData = getStreamData;
const queries_1 = require("../../shared/queries");
const database_1 = require("../../shared/database");
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
const TTL = 60 * 60 * 6;
const ANILIST_URL = "https://graphql.anilist.co";
const MEDIA_FIELDS = `
id
idMal
title { romaji english native userPreferred }
type
format
status
description
startDate { year month day }
endDate { year month day }
season
seasonYear
episodes
duration
chapters
volumes
countryOfOrigin
isLicensed
source
hashtag
trailer { id site thumbnail }
updatedAt
coverImage { extraLarge large medium color }
bannerImage
genres
synonyms
averageScore
popularity
isLocked
trending
favourites
isAdult
siteUrl
tags { id name description category rank isGeneralSpoiler isMediaSpoiler isAdult }
relations {
edges {
relationType
node {
id
title { romaji }
type
format
status
}
}
}
studios {
edges {
isMain
node { id name isAnimationStudio }
}
}
nextAiringEpisode { airingAt timeUntilAiring episode }
externalLinks { id url site type language color icon notes }
rankings { id rank type format year season allTime context }
stats {
scoreDistribution { score amount }
statusDistribution { status amount }
}
recommendations(perPage: 7, sort: RATING_DESC) {
nodes {
mediaRecommendation {
id
title { romaji }
coverImage { medium }
format
type
}
}
}
`;
async function fetchAniList(query, variables) {
const res = await fetch(ANILIST_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ query, variables })
});
const json = await res.json();
return json?.data;
}
async function getAnimeById(id) {
const row = await (0, database_1.queryOne)("SELECT full_data FROM anime WHERE id = ?", [id]);
if (row)
return JSON.parse(row.full_data);
const query = `
query ($id: Int) {
Media(id: $id, type: ANIME) { ${MEDIA_FIELDS} }
}
`;
const data = await fetchAniList(query, { id: Number(id) });
if (!data?.Media)
return { error: "Anime not found" };
await (0, database_1.queryOne)("INSERT INTO anime (id, title, updatedAt, full_data) VALUES (?, ?, ?, ?)", [
data.Media.id,
data.Media.title?.english || data.Media.title?.romaji,
data.Media.updatedAt || 0,
JSON.stringify(data.Media)
]);
return data.Media;
}
async function getTrendingAnime() {
const rows = await (0, database_1.queryAll)("SELECT full_data, updated_at FROM trending ORDER BY rank ASC LIMIT 10");
if (rows.length) {
const expired = (Date.now() / 1000 - rows[0].updated_at) > TTL;
if (!expired) {
return rows.map((r) => JSON.parse(r.full_data));
}
}
const query = `
query {
Page(page: 1, perPage: 10) {
media(type: ANIME, sort: TRENDING_DESC) { ${MEDIA_FIELDS} }
}
}
`;
const data = await fetchAniList(query, {});
const list = data?.Page?.media || [];
const now = Math.floor(Date.now() / 1000);
await (0, database_1.queryOne)("DELETE FROM trending");
let rank = 1;
for (const anime of list) {
await (0, database_1.queryOne)("INSERT INTO trending (rank, id, full_data, updated_at) VALUES (?, ?, ?, ?)", [rank++, anime.id, JSON.stringify(anime), now]);
}
return list;
}
async function getTopAiringAnime() {
const rows = await (0, database_1.queryAll)("SELECT full_data, updated_at FROM top_airing ORDER BY rank ASC LIMIT 10");
if (rows.length) {
const expired = (Date.now() / 1000 - rows[0].updated_at) > TTL;
if (!expired) {
return rows.map((r) => JSON.parse(r.full_data));
}
}
const query = `
query {
Page(page: 1, perPage: 10) {
media(type: ANIME, status: RELEASING, sort: SCORE_DESC) { ${MEDIA_FIELDS} }
}
}
`;
const data = await fetchAniList(query, {});
const list = data?.Page?.media || [];
const now = Math.floor(Date.now() / 1000);
await (0, database_1.queryOne)("DELETE FROM top_airing");
let rank = 1;
for (const anime of list) {
await (0, database_1.queryOne)("INSERT INTO top_airing (rank, id, full_data, updated_at) VALUES (?, ?, ?, ?)", [rank++, anime.id, JSON.stringify(anime), now]);
}
return list;
}
async function searchAnimeLocal(query) {
if (!query || query.length < 2)
return [];
const sql = `SELECT full_data FROM anime WHERE full_data LIKE ? LIMIT 50`;
const rows = await (0, database_1.queryAll)(sql, [`%${query}%`]);
const localResults = rows
.map((r) => JSON.parse(r.full_data))
.filter((anime) => {
const q = query.toLowerCase();
const titles = [
anime.title?.english,
anime.title?.romaji,
anime.title?.native,
...(anime.synonyms || [])
]
.filter(Boolean)
.map(t => t.toLowerCase());
return titles.some(t => t.includes(q));
})
.slice(0, 10);
if (localResults.length >= 5) {
return localResults;
}
const gql = `
query ($search: String) {
Page(page: 1, perPage: 10) {
media(type: ANIME, search: $search) { ${MEDIA_FIELDS} }
}
}
`;
const data = await fetchAniList(gql, { search: query });
const remoteResults = data?.Page?.media || [];
for (const anime of remoteResults) {
await (0, database_1.queryOne)("INSERT OR IGNORE INTO anime (id, title, updatedAt, full_data) VALUES (?, ?, ?, ?)", [
anime.id,
anime.title?.english || anime.title?.romaji,
anime.updatedAt || 0,
JSON.stringify(anime)
]);
}
const merged = [...localResults];
for (const anime of remoteResults) {
if (!merged.find(a => a.id === anime.id)) {
merged.push(anime);
}
if (merged.length >= 10)
break;
}
return merged;
}
async function getAnimeInfoExtension(ext, id) {
if (!ext)
return { error: "not found" };
const extName = ext.constructor.name;
const cached = await (0, queries_1.getCachedExtension)(extName, id);
if (cached) {
try {
console.log(`[${extName}] Metadata cache hit for ID: ${id}`);
return JSON.parse(cached.metadata);
}
catch {
}
}
if ((ext.type === 'anime-board') && ext.getMetadata) {
try {
const match = await ext.getMetadata(id);
if (match) {
await (0, queries_1.cacheExtension)(extName, id, match.title, match);
return match;
}
}
catch (e) {
console.error(`Extension getMetadata failed:`, e);
}
}
return { error: "not found" };
}
async function searchAnimeInExtension(ext, name, query) {
if (!ext)
return [];
if (ext.type === 'anime-board' && ext.search) {
try {
console.log(`[${name}] Searching for anime: ${query}`);
const matches = await ext.search({
query: query,
media: {
romajiTitle: query,
englishTitle: query,
startDate: { year: 0, month: 0, day: 0 }
}
});
if (matches && matches.length > 0) {
return matches.map(m => ({
id: m.id,
extensionName: name,
title: { romaji: m.title, english: m.title, native: null },
coverImage: { large: m.image || '' },
averageScore: m.rating || m.score || null,
format: 'ANIME',
seasonYear: null,
isExtensionResult: true,
}));
}
}
catch (e) {
console.error(`Extension search failed for ${name}:`, e);
}
}
return [];
}
async function searchEpisodesInExtension(ext, name, query) {
if (!ext)
return [];
const cacheKey = `anime:episodes:${name}:${query}`;
const cached = await (0, queries_1.getCache)(cacheKey);
if (cached) {
const isExpired = Date.now() - cached.created_at > CACHE_TTL_MS;
if (!isExpired) {
console.log(`[${name}] Episodes cache hit for: ${query}`);
try {
return JSON.parse(cached.result);
}
catch (e) {
console.error(`[${name}] Error parsing cached episodes:`, e);
}
}
else {
console.log(`[${name}] Episodes cache expired for: ${query}`);
}
}
if (ext.type === "anime-board" && ext.search && typeof ext.findEpisodes === "function") {
try {
const title = await (0, queries_1.getExtensionTitle)(name, query);
let mediaId;
if (!title) {
const matches = await ext.search({
query,
media: {
romajiTitle: query,
englishTitle: query,
startDate: { year: 0, month: 0, day: 0 }
}
});
if (!matches || matches.length === 0)
return [];
const res = matches[0];
if (!res?.id)
return [];
mediaId = res.id;
}
else {
mediaId = query;
}
const chapterList = await ext.findEpisodes(mediaId);
if (!Array.isArray(chapterList))
return [];
const result = chapterList.map(ep => ({
id: ep.id,
number: ep.number,
url: ep.url,
title: ep.title
}));
await (0, queries_1.setCache)(cacheKey, result, CACHE_TTL_MS);
return result;
}
catch (e) {
console.error(`Extension search failed for ${name}:`, e);
}
}
return [];
}
async function getStreamData(extension, episode, id, source, server, category) {
const providerName = extension.constructor.name;
const cacheKey = `anime:stream:${providerName}:${id}:${episode}:${server || 'default'}:${category || 'sub'}`;
const cached = await (0, queries_1.getCache)(cacheKey);
if (cached) {
const isExpired = Date.now() - cached.created_at > CACHE_TTL_MS;
if (!isExpired) {
console.log(`[${providerName}] Stream data cache hit for episode ${episode}`);
try {
return JSON.parse(cached.result);
}
catch (e) {
console.error(`[${providerName}] Error parsing cached stream data:`, e);
}
}
else {
console.log(`[${providerName}] Stream data cache expired for episode ${episode}`);
}
}
if (!extension.findEpisodes || !extension.findEpisodeServer) {
throw new Error("Extension doesn't support required methods");
}
let episodes;
if (source === "anilist") {
const anime = await getAnimeById(id);
episodes = await searchEpisodesInExtension(extension, extension.constructor.name, anime.title.romaji);
}
else {
episodes = await extension.findEpisodes(id);
}
const targetEp = episodes.find(e => e.number === parseInt(episode));
if (!targetEp) {
throw new Error("Episode not found");
}
const serverName = server || "default";
const streamData = await extension.findEpisodeServer(targetEp, serverName);
await (0, queries_1.setCache)(cacheKey, streamData, CACHE_TTL_MS);
return streamData;
}

View File

@@ -0,0 +1,140 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBook = getBook;
exports.getTrending = getTrending;
exports.getPopular = getPopular;
exports.searchBooks = searchBooks;
exports.searchBooksInExtension = searchBooksInExtension;
exports.getChapters = getChapters;
exports.getChapterContent = getChapterContent;
const booksService = __importStar(require("./books.service"));
const extensions_1 = require("../../shared/extensions");
async function getBook(req, reply) {
try {
const { id } = req.params;
const source = req.query.source;
let book;
if (source === 'anilist') {
book = await booksService.getBookById(id);
}
else {
const ext = (0, extensions_1.getExtension)(source);
const result = await booksService.getBookInfoExtension(ext, id);
book = result || null;
}
return book;
}
catch (err) {
return { error: err.message };
}
}
async function getTrending(req, reply) {
try {
const results = await booksService.getTrendingBooks();
return { results };
}
catch (err) {
return { results: [] };
}
}
async function getPopular(req, reply) {
try {
const results = await booksService.getPopularBooks();
return { results };
}
catch (err) {
return { results: [] };
}
}
async function searchBooks(req, reply) {
try {
const query = req.query.q;
const dbResults = await booksService.searchBooksLocal(query);
if (dbResults.length > 0) {
return { results: dbResults };
}
console.log(`[Books] Local DB miss for "${query}", fetching live...`);
const anilistResults = await booksService.searchBooksAniList(query);
if (anilistResults.length > 0) {
return { results: anilistResults };
}
return { results: [] };
}
catch (e) {
const error = e;
console.error("Search Error:", error.message);
return { results: [] };
}
}
async function searchBooksInExtension(req, reply) {
try {
const extensionName = req.params.extension;
const query = req.query.q;
const ext = (0, extensions_1.getExtension)(extensionName);
if (!ext)
return { results: [] };
const results = await booksService.searchBooksInExtension(ext, extensionName, query);
return { results };
}
catch (e) {
const error = e;
console.error("Search Error:", error.message);
return { results: [] };
}
}
async function getChapters(req, reply) {
try {
const { id } = req.params;
const source = req.query.source || 'anilist';
const isExternal = source !== 'anilist';
return await booksService.getChaptersForBook(id, isExternal);
}
catch {
return { chapters: [] };
}
}
async function getChapterContent(req, reply) {
try {
const { bookId, chapter, provider } = req.params;
const source = req.query.source || 'anilist';
const content = await booksService.getChapterContent(bookId, chapter, provider, source);
return reply.send(content);
}
catch (err) {
console.error("getChapterContent error:", err.message);
return reply.code(500).send({ error: "Error loading chapter" });
}
}

View File

@@ -0,0 +1,46 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const controller = __importStar(require("./books.controller"));
async function booksRoutes(fastify) {
fastify.get('/book/:id', controller.getBook);
fastify.get('/books/trending', controller.getTrending);
fastify.get('/books/popular', controller.getPopular);
fastify.get('/search/books', controller.searchBooks);
fastify.get('/search/books/:extension', controller.searchBooksInExtension);
fastify.get('/book/:id/chapters', controller.getChapters);
fastify.get('/book/:bookId/:chapter/:provider', controller.getChapterContent);
}
exports.default = booksRoutes;

View File

@@ -0,0 +1,482 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getBookById = getBookById;
exports.getTrendingBooks = getTrendingBooks;
exports.getPopularBooks = getPopularBooks;
exports.searchBooksLocal = searchBooksLocal;
exports.searchBooksAniList = searchBooksAniList;
exports.getBookInfoExtension = getBookInfoExtension;
exports.searchBooksInExtension = searchBooksInExtension;
exports.getChaptersForBook = getChaptersForBook;
exports.getChapterContent = getChapterContent;
const queries_1 = require("../../shared/queries");
const database_1 = require("../../shared/database");
const extensions_1 = require("../../shared/extensions");
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
const TTL = 60 * 60 * 6;
const ANILIST_URL = "https://graphql.anilist.co";
async function fetchAniList(query, variables) {
const res = await fetch(ANILIST_URL, {
method: "POST",
headers: {
"Content-Type": "application/json",
"Accept": "application/json"
},
body: JSON.stringify({ query, variables })
});
if (!res.ok) {
throw new Error(`AniList error ${res.status}`);
}
const json = await res.json();
return json?.data;
}
const MEDIA_FIELDS = `
id
title {
romaji
english
native
userPreferred
}
type
format
status
description
startDate { year month day }
endDate { year month day }
season
seasonYear
episodes
chapters
volumes
duration
genres
synonyms
averageScore
popularity
favourites
isAdult
siteUrl
coverImage {
extraLarge
large
medium
color
}
bannerImage
updatedAt
`;
async function getBookById(id) {
const row = await (0, database_1.queryOne)("SELECT full_data FROM books WHERE id = ?", [id]);
if (row) {
return JSON.parse(row.full_data);
}
try {
console.log(`[Book] Local miss for ID ${id}, fetching live...`);
const query = `
query ($id: Int) {
Media(id: $id, type: MANGA) {
id idMal title { romaji english native userPreferred } type format status description
startDate { year month day } endDate { year month day } season seasonYear seasonInt
episodes duration chapters volumes countryOfOrigin isLicensed source hashtag
trailer { id site thumbnail } updatedAt coverImage { extraLarge large medium color }
bannerImage genres synonyms averageScore meanScore popularity isLocked trending favourites
tags { id name description category rank isGeneralSpoiler isMediaSpoiler isAdult userId }
relations { edges { relationType node { id title { romaji } } } }
characters(page: 1, perPage: 10) { nodes { id name { full } } }
studios { nodes { id name isAnimationStudio } }
isAdult nextAiringEpisode { airingAt timeUntilAiring episode }
externalLinks { url site }
rankings { id rank type format year season allTime context }
}
}`;
const response = await fetch('https://graphql.anilist.co', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Accept': 'application/json'
},
body: JSON.stringify({
query,
variables: { id: parseInt(id.toString()) }
})
});
const data = await response.json();
if (data?.data?.Media) {
const media = data.data.Media;
const insertSql = `
INSERT INTO books (id, title, updatedAt, full_data)
VALUES (?, ?, ?, ?)
ON CONFLICT(id) DO UPDATE SET
title = EXCLUDED.title,
updatedAt = EXCLUDED.updatedAt,
full_data = EXCLUDED.full_data;
`;
await (0, database_1.run)(insertSql, [
media.id,
media.title?.userPreferred || media.title?.romaji || media.title?.english || null,
media.updatedAt || Math.floor(Date.now() / 1000),
JSON.stringify(media)
]);
return media;
}
}
catch (e) {
console.error("Fetch error:", e);
}
return { error: "Book not found" };
}
async function getTrendingBooks() {
const rows = await (0, database_1.queryAll)("SELECT full_data, updated_at FROM trending_books ORDER BY rank ASC LIMIT 10");
if (rows.length) {
const expired = (Date.now() / 1000 - rows[0].updated_at) > TTL;
if (!expired) {
return rows.map((r) => JSON.parse(r.full_data));
}
}
const query = `
query {
Page(page: 1, perPage: 10) {
media(type: MANGA, sort: TRENDING_DESC) { ${MEDIA_FIELDS} }
}
}
`;
const data = await fetchAniList(query, {});
const list = data?.Page?.media || [];
const now = Math.floor(Date.now() / 1000);
await (0, database_1.queryOne)("DELETE FROM trending_books");
let rank = 1;
for (const book of list) {
await (0, database_1.queryOne)("INSERT INTO trending_books (rank, id, full_data, updated_at) VALUES (?, ?, ?, ?)", [rank++, book.id, JSON.stringify(book), now]);
}
return list;
}
async function getPopularBooks() {
const rows = await (0, database_1.queryAll)("SELECT full_data, updated_at FROM popular_books ORDER BY rank ASC LIMIT 10");
if (rows.length) {
const expired = (Date.now() / 1000 - rows[0].updated_at) > TTL;
if (!expired) {
return rows.map((r) => JSON.parse(r.full_data));
}
}
const query = `
query {
Page(page: 1, perPage: 10) {
media(type: MANGA, sort: POPULARITY_DESC) { ${MEDIA_FIELDS} }
}
}
`;
const data = await fetchAniList(query, {});
const list = data?.Page?.media || [];
const now = Math.floor(Date.now() / 1000);
await (0, database_1.queryOne)("DELETE FROM popular_books");
let rank = 1;
for (const book of list) {
await (0, database_1.queryOne)("INSERT INTO popular_books (rank, id, full_data, updated_at) VALUES (?, ?, ?, ?)", [rank++, book.id, JSON.stringify(book), now]);
}
return list;
}
async function searchBooksLocal(query) {
if (!query || query.length < 2) {
return [];
}
const sql = `SELECT full_data FROM books WHERE full_data LIKE ? LIMIT 50`;
const rows = await (0, database_1.queryAll)(sql, [`%${query}%`]);
const results = rows.map((row) => JSON.parse(row.full_data));
const clean = results.filter(book => {
const searchTerms = [
book.title.english,
book.title.romaji,
book.title.native,
...(book.synonyms || [])
].filter(Boolean).map(t => t.toLowerCase());
return searchTerms.some(term => term.includes(query.toLowerCase()));
});
return clean.slice(0, 10);
}
async function searchBooksAniList(query) {
const gql = `
query ($search: String) {
Page(page: 1, perPage: 5) {
media(search: $search, type: MANGA, isAdult: false) {
id title { romaji english native }
coverImage { extraLarge large }
bannerImage description averageScore format
seasonYear startDate { year }
}
}
}`;
const response = await fetch('https://graphql.anilist.co', {
method: 'POST',
headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' },
body: JSON.stringify({ query: gql, variables: { search: query } })
});
const liveData = await response.json();
if (liveData.data && liveData.data.Page.media.length > 0) {
return liveData.data.Page.media;
}
return [];
}
async function getBookInfoExtension(ext, id) {
if (!ext)
return [];
const extName = ext.constructor.name;
const cached = await (0, queries_1.getCachedExtension)(extName, id);
if (cached) {
try {
return JSON.parse(cached.metadata);
}
catch {
}
}
if (ext.type === 'book-board' && ext.getMetadata) {
try {
const info = await ext.getMetadata(id);
if (info) {
await (0, queries_1.cacheExtension)(extName, id, info.title, info);
return info;
}
}
catch (e) {
console.error(`Extension getInfo failed:`, e);
}
}
return [];
}
async function searchBooksInExtension(ext, name, query) {
if (!ext)
return [];
if ((ext.type === 'book-board') && ext.search) {
try {
console.log(`[${name}] Searching for book: ${query}`);
const matches = await ext.search({
query: query,
media: {
romajiTitle: query,
englishTitle: query,
startDate: { year: 0, month: 0, day: 0 }
}
});
if (matches?.length) {
return matches.map(m => ({
id: m.id,
extensionName: name,
title: { romaji: m.title, english: m.title, native: null },
coverImage: { large: m.image || '' },
averageScore: m.rating || m.score || null,
format: m.format,
seasonYear: null,
isExtensionResult: true
}));
}
}
catch (e) {
console.error(`Extension search failed for ${name}:`, e);
}
}
return [];
}
async function fetchBookMetadata(id) {
try {
const query = `query ($id: Int) {
Media(id: $id, type: MANGA) {
title { romaji english }
startDate { year month day }
}
}`;
const res = await fetch('https://graphql.anilist.co', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ query, variables: { id: parseInt(id) } })
});
const d = await res.json();
return d.data?.Media || null;
}
catch (e) {
console.error("Failed to fetch book metadata:", e);
return null;
}
}
async function searchChaptersInExtension(ext, name, searchTitle, search, origin) {
const cacheKey = `chapters:${name}:${origin}:${search ? "search" : "id"}:${searchTitle}`;
const cached = await (0, queries_1.getCache)(cacheKey);
if (cached) {
const isExpired = Date.now() - cached.created_at > CACHE_TTL_MS;
if (!isExpired) {
console.log(`[${name}] Chapters cache hit for: ${searchTitle}`);
try {
return JSON.parse(cached.result);
}
catch (e) {
console.error(`[${name}] Error parsing cached chapters:`, e);
}
}
else {
console.log(`[${name}] Chapters cache expired for: ${searchTitle}`);
}
}
try {
console.log(`[${name}] Searching chapters for: ${searchTitle}`);
let mediaId;
if (search) {
const matches = await ext.search({
query: searchTitle,
media: {
romajiTitle: searchTitle,
englishTitle: searchTitle,
startDate: { year: 0, month: 0, day: 0 }
}
});
const best = matches?.[0];
if (!best) {
return [];
}
mediaId = best.id;
}
else {
const match = await ext.getMetadata(searchTitle);
mediaId = match.id;
}
const chaps = await ext.findChapters(mediaId);
if (!chaps?.length) {
return [];
}
console.log(`[${name}] Found ${chaps.length} chapters.`);
const result = chaps.map((ch) => ({
id: ch.id,
number: parseFloat(ch.number.toString()),
title: ch.title,
date: ch.releaseDate,
provider: name,
index: ch.index
}));
await (0, queries_1.setCache)(cacheKey, result, CACHE_TTL_MS);
return result;
}
catch (e) {
const error = e;
console.error(`Failed to fetch chapters from ${name}:`, error.message);
return [];
}
}
async function getChaptersForBook(id, ext, onlyProvider) {
let bookData = null;
let searchTitle = "";
if (!ext) {
const result = await getBookById(id);
if (!result || "error" in result)
return { chapters: [] };
bookData = result;
const titles = [bookData.title.english, bookData.title.romaji].filter(Boolean);
searchTitle = titles[0];
}
const bookExtensions = (0, extensions_1.getBookExtensionsMap)();
let extension;
if (!searchTitle) {
for (const [name, ext] of bookExtensions) {
const title = await (0, queries_1.getExtensionTitle)(name, id);
if (title) {
searchTitle = title;
extension = name;
}
}
}
const allChapters = [];
let exts = "anilist";
if (ext)
exts = "ext";
for (const [name, ext] of bookExtensions) {
if (onlyProvider && name !== onlyProvider)
continue;
if (name == extension) {
const chapters = await searchChaptersInExtension(ext, name, id, false, exts);
allChapters.push(...chapters);
}
else {
const chapters = await searchChaptersInExtension(ext, name, searchTitle, true, exts);
allChapters.push(...chapters);
}
}
return {
chapters: allChapters.sort((a, b) => Number(a.number) - Number(b.number))
};
}
async function getChapterContent(bookId, chapterIndex, providerName, source) {
const extensions = (0, extensions_1.getAllExtensions)();
const ext = extensions.get(providerName);
if (!ext) {
throw new Error("Provider not found");
}
const contentCacheKey = `content:${providerName}:${source}:${bookId}:${chapterIndex}`;
const cachedContent = await (0, queries_1.getCache)(contentCacheKey);
if (cachedContent) {
const isExpired = Date.now() - cachedContent.created_at > CACHE_TTL_MS;
if (!isExpired) {
console.log(`[${providerName}] Content cache hit for Book ID ${bookId}, Index ${chapterIndex}`);
try {
return JSON.parse(cachedContent.result);
}
catch (e) {
console.error(`[${providerName}] Error parsing cached content:`, e);
}
}
else {
console.log(`[${providerName}] Content cache expired for Book ID ${bookId}, Index ${chapterIndex}`);
}
}
const isExternal = source !== 'anilist';
const chapterList = await getChaptersForBook(bookId, isExternal, providerName);
if (!chapterList?.chapters || chapterList.chapters.length === 0) {
throw new Error("Chapters not found");
}
const providerChapters = chapterList.chapters.filter(c => c.provider === providerName);
const index = parseInt(chapterIndex, 10);
if (Number.isNaN(index)) {
throw new Error("Invalid chapter index");
}
if (!providerChapters[index]) {
throw new Error("Chapter index out of range");
}
const selectedChapter = providerChapters[index];
const chapterId = selectedChapter.id;
const chapterTitle = selectedChapter.title || null;
const chapterNumber = typeof selectedChapter.number === 'number' ? selectedChapter.number : index;
try {
if (!ext.findChapterPages) {
throw new Error("Extension doesn't support findChapterPages");
}
let contentResult;
if (ext.mediaType === "manga") {
const pages = await ext.findChapterPages(chapterId);
contentResult = {
type: "manga",
chapterId,
title: chapterTitle,
number: chapterNumber,
provider: providerName,
pages
};
}
else if (ext.mediaType === "ln") {
const content = await ext.findChapterPages(chapterId);
contentResult = {
type: "ln",
chapterId,
title: chapterTitle,
number: chapterNumber,
provider: providerName,
content
};
}
else {
throw new Error("Unknown mediaType");
}
await (0, queries_1.setCache)(contentCacheKey, contentResult, CACHE_TTL_MS);
return contentResult;
}
catch (err) {
const error = err;
console.error(`[Chapter] Error loading from ${providerName}:`, error.message);
throw err;
}
}

View File

@@ -0,0 +1,71 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.getExtensions = getExtensions;
exports.getAnimeExtensions = getAnimeExtensions;
exports.getBookExtensions = getBookExtensions;
exports.getGalleryExtensions = getGalleryExtensions;
exports.getExtensionSettings = getExtensionSettings;
exports.installExtension = installExtension;
exports.uninstallExtension = uninstallExtension;
const extensions_1 = require("../../shared/extensions");
async function getExtensions(req, reply) {
return { extensions: (0, extensions_1.getExtensionsList)() };
}
async function getAnimeExtensions(req, reply) {
const animeExtensions = (0, extensions_1.getAnimeExtensionsMap)();
return { extensions: Array.from(animeExtensions.keys()) };
}
async function getBookExtensions(req, reply) {
const bookExtensions = (0, extensions_1.getBookExtensionsMap)();
return { extensions: Array.from(bookExtensions.keys()) };
}
async function getGalleryExtensions(req, reply) {
const galleryExtensions = (0, extensions_1.getGalleryExtensionsMap)();
return { extensions: Array.from(galleryExtensions.keys()) };
}
async function getExtensionSettings(req, reply) {
const { name } = req.params;
const ext = (0, extensions_1.getExtension)(name);
if (!ext) {
return { error: "Extension not found" };
}
if (!ext.getSettings) {
return { episodeServers: ["default"], supportsDub: false };
}
return ext.getSettings();
}
async function installExtension(req, reply) {
const { fileName } = req.body;
if (!fileName || !fileName.endsWith('.js')) {
return reply.code(400).send({ error: "Invalid extension fileName provided" });
}
try {
const downloadUrl = `https://git.waifuboard.app/ItsSkaiya/WaifuBoard-Extensions/raw/branch/main/${fileName}`;
await (0, extensions_1.saveExtensionFile)(fileName, downloadUrl);
req.server.log.info(`Extension installed: ${fileName}`);
return reply.code(200).send({ success: true, message: `Extension ${fileName} installed successfully.` });
}
catch (error) {
req.server.log.error(`Failed to install extension ${fileName}:`, error);
return reply.code(500).send({ success: false, error: `Failed to install extension ${fileName}.` });
}
}
async function uninstallExtension(req, reply) {
const { fileName } = req.body;
if (!fileName || !fileName.endsWith('.js')) {
return reply.code(400).send({ error: "Invalid extension fileName provided" });
}
try {
await (0, extensions_1.deleteExtensionFile)(fileName);
req.server.log.info(`Extension uninstalled: ${fileName}`);
return reply.code(200).send({ success: true, message: `Extension ${fileName} uninstalled successfully.` });
}
catch (error) {
// @ts-ignore
if (error.code === 'ENOENT') {
return reply.code(200).send({ success: true, message: `Extension ${fileName} already uninstalled (file not found).` });
}
req.server.log.error(`Failed to uninstall extension ${fileName}:`, error);
return reply.code(500).send({ success: false, error: `Failed to uninstall extension ${fileName}.` });
}
}

View File

@@ -0,0 +1,46 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const controller = __importStar(require("./extensions.controller"));
async function extensionsRoutes(fastify) {
fastify.get('/extensions', controller.getExtensions);
fastify.get('/extensions/anime', controller.getAnimeExtensions);
fastify.get('/extensions/book', controller.getBookExtensions);
fastify.get('/extensions/gallery', controller.getGalleryExtensions);
fastify.get('/extensions/:name/settings', controller.getExtensionSettings);
fastify.post('/extensions/install', controller.installExtension);
fastify.post('/extensions/uninstall', controller.uninstallExtension);
}
exports.default = extensionsRoutes;

View File

@@ -0,0 +1,172 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.search = search;
exports.searchInExtension = searchInExtension;
exports.getInfo = getInfo;
exports.getFavorites = getFavorites;
exports.getFavoriteById = getFavoriteById;
exports.addFavorite = addFavorite;
exports.removeFavorite = removeFavorite;
const galleryService = __importStar(require("./gallery.service"));
async function search(req, reply) {
try {
const query = req.query.q || '';
const page = parseInt(req.query.page) || 1;
const perPage = parseInt(req.query.perPage) || 48;
return await galleryService.searchGallery(query, page, perPage);
}
catch (err) {
const error = err;
console.error("Gallery Search Error:", error.message);
return {
results: [],
total: 0,
page: 1,
hasNextPage: false
};
}
}
async function searchInExtension(req, reply) {
try {
const provider = req.query.provider;
const query = req.query.q || '';
const page = parseInt(req.query.page) || 1;
const perPage = parseInt(req.query.perPage) || 48;
if (!provider) {
return reply.code(400).send({ error: "Missing provider" });
}
return await galleryService.searchInExtension(provider, query, page, perPage);
}
catch (err) {
console.error("Gallery SearchInExtension Error:", err.message);
return {
results: [],
total: 0,
page: 1,
hasNextPage: false
};
}
}
async function getInfo(req, reply) {
try {
const { id } = req.params;
const provider = req.query.provider;
return await galleryService.getGalleryInfo(id, provider);
}
catch (err) {
const error = err;
console.error("Gallery Info Error:", error.message);
return reply.code(404).send({ error: "Gallery item not found" });
}
}
async function getFavorites(req, reply) {
try {
if (!req.user)
return reply.code(401).send({ error: "Unauthorized" });
const favorites = await galleryService.getFavorites(req.user.id);
return { favorites };
}
catch (err) {
console.error("Get Favorites Error:", err.message);
return reply.code(500).send({ error: "Failed to retrieve favorites" });
}
}
async function getFavoriteById(req, reply) {
try {
if (!req.user)
return reply.code(401).send({ error: "Unauthorized" });
const { id } = req.params;
const favorite = await galleryService.getFavoriteById(id, req.user.id);
if (!favorite) {
return reply.code(404).send({ error: "Favorite not found" });
}
return { favorite };
}
catch (err) {
console.error("Get Favorite By ID Error:", err.message);
return reply.code(500).send({ error: "Failed to retrieve favorite" });
}
}
async function addFavorite(req, reply) {
try {
if (!req.user)
return reply.code(401).send({ error: "Unauthorized" });
const { id, title, image_url, thumbnail_url, tags, provider, headers } = req.body;
if (!id || !title || !image_url || !thumbnail_url) {
return reply.code(400).send({
error: "Missing required fields"
});
}
const result = await galleryService.addFavorite({
id,
user_id: req.user.id,
title,
image_url,
thumbnail_url,
tags: tags || '',
provider: provider || "",
headers: headers || ""
});
if (result.success) {
return reply.code(201).send(result);
}
else {
return reply.code(409).send(result);
}
}
catch (err) {
console.error("Add Favorite Error:", err.message);
return reply.code(500).send({ error: "Failed to add favorite" });
}
}
async function removeFavorite(req, reply) {
try {
if (!req.user)
return reply.code(401).send({ error: "Unauthorized" });
const { id } = req.params;
const result = await galleryService.removeFavorite(id, req.user.id);
if (result.success) {
return { success: true, message: "Favorite removed successfully" };
}
else {
return reply.code(404).send({ error: "Favorite not found" });
}
}
catch (err) {
console.error("Remove Favorite Error:", err.message);
return reply.code(500).send({ error: "Failed to remove favorite" });
}
}

View File

@@ -0,0 +1,46 @@
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
const controller = __importStar(require("./gallery.controller"));
async function galleryRoutes(fastify) {
fastify.get('/gallery/search', controller.search);
fastify.get('/gallery/fetch/:id', controller.getInfo);
fastify.get('/gallery/search/provider', controller.searchInExtension);
fastify.get('/gallery/favorites', controller.getFavorites);
fastify.get('/gallery/favorites/:id', controller.getFavoriteById);
fastify.post('/gallery/favorites', controller.addFavorite);
fastify.delete('/gallery/favorites/:id', controller.removeFavorite);
}
exports.default = galleryRoutes;

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