mirror of
https://github.com/Zaarrg/stremio-community-v5.git
synced 2026-01-11 20:10:31 +00:00
Added WebMods feature
- Added WebMods feature, allows loading custom js and css
This commit is contained in:
parent
c5a6a7fb64
commit
7add3f622b
8 changed files with 307 additions and 140 deletions
|
|
@ -1,6 +1,6 @@
|
|||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(stremio VERSION "5.0.19")
|
||||
project(stremio VERSION "5.0.20")
|
||||
|
||||
set(CMAKE_CXX_STANDARD 20)
|
||||
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ using json = nlohmann::json;
|
|||
#define APP_TITLE "Stremio - Freedom to Stream"
|
||||
#define APP_NAME "Stremio"
|
||||
#define APP_CLASS L"Stremio"
|
||||
#define APP_VERSION "5.0.19"
|
||||
#define APP_VERSION "5.0.20"
|
||||
|
||||
// -----------------------------------------------------------------------------
|
||||
// Globals
|
||||
|
|
|
|||
274
src/main.cpp
274
src/main.cpp
|
|
@ -1,178 +1,176 @@
|
|||
#pragma comment(linker, "/SUBSYSTEM:WINDOWS")
|
||||
#pragma comment(linker, "/ENTRY:mainCRTStartup")
|
||||
|
||||
#include "discord_rpc.h"
|
||||
#include <windows.h>
|
||||
#include <shellscalingapi.h>
|
||||
#include <VersionHelpers.h>
|
||||
#include <gdiplus.h>
|
||||
#include <iostream>
|
||||
#include <shellscalingapi.h>
|
||||
#include <sstream>
|
||||
#include <VersionHelpers.h>
|
||||
#include "discord_rpc.h"
|
||||
|
||||
#include "ui/mainwindow.h"
|
||||
#include "core/globals.h"
|
||||
#include "utils/crashlog.h"
|
||||
#include "utils/config.h"
|
||||
#include "tray/tray.h"
|
||||
#include "mpv/player.h"
|
||||
#include "node/server.h"
|
||||
#include "tray/tray.h"
|
||||
#include "ui/mainwindow.h"
|
||||
#include "ui/splash.h"
|
||||
#include "webview/webview.h"
|
||||
#include "updater/updater.h"
|
||||
#include "utils/helpers.h"
|
||||
#include "utils/config.h"
|
||||
#include "utils/crashlog.h"
|
||||
#include "utils/discord.h"
|
||||
#include "utils/helpers.h"
|
||||
#include "webview/webview.h"
|
||||
// This started as 1-week project so please don't take the code to seriously
|
||||
int main(int argc, char* argv[])
|
||||
{
|
||||
// Catch unhandled exceptions
|
||||
SetUnhandledExceptionFilter([](EXCEPTION_POINTERS* info) -> LONG
|
||||
{
|
||||
std::wstringstream ws;
|
||||
ws << L"Unhandled exception! Code=0x" << std::hex << info->ExceptionRecord->ExceptionCode;
|
||||
AppendToCrashLog(ws.str());
|
||||
Cleanup();
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
});
|
||||
atexit(Cleanup);
|
||||
int main(int argc, char *argv[]) {
|
||||
// Catch unhandled exceptions
|
||||
SetUnhandledExceptionFilter([](EXCEPTION_POINTERS *info) -> LONG {
|
||||
std::wstringstream ws;
|
||||
ws << L"Unhandled exception! Code=0x" << std::hex
|
||||
<< info->ExceptionRecord->ExceptionCode;
|
||||
AppendToCrashLog(ws.str());
|
||||
Cleanup();
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
});
|
||||
atexit(Cleanup);
|
||||
|
||||
// DPI
|
||||
if (IsWindowsVersionOrGreater(10, 0, 14393)){
|
||||
typedef BOOL(WINAPI *SetDpiCtxFn)(DPI_AWARENESS_CONTEXT);
|
||||
auto setDpiAwarenessContext = (SetDpiCtxFn)GetProcAddress(GetModuleHandleW(L"user32.dll"), "SetProcessDpiAwarenessContext");
|
||||
if(setDpiAwarenessContext){
|
||||
setDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
} else {
|
||||
// Fallback for Windows 8.1 and Windows 10 before 1607:
|
||||
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
// DPI
|
||||
if (IsWindowsVersionOrGreater(10, 0, 14393)) {
|
||||
typedef BOOL(WINAPI * SetDpiCtxFn)(DPI_AWARENESS_CONTEXT);
|
||||
auto setDpiAwarenessContext = (SetDpiCtxFn)GetProcAddress(
|
||||
GetModuleHandleW(L"user32.dll"), "SetProcessDpiAwarenessContext");
|
||||
if (setDpiAwarenessContext) {
|
||||
setDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2);
|
||||
}
|
||||
} else {
|
||||
// Fallback for Windows 8.1 and Windows 10 before 1607:
|
||||
SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE);
|
||||
}
|
||||
|
||||
// parse cmd line
|
||||
for(int i=1; i<argc; i++){
|
||||
std::string arg(argv[i]);
|
||||
if(arg.rfind("--webui-url=", 0)==0){
|
||||
g_webuiUrls.insert(g_webuiUrls.begin(), Utf8ToWstring(arg.substr(12)));
|
||||
} else if(arg.rfind("--autoupdater-endpoint=",0)==0){
|
||||
g_updateUrl = arg.substr(23);
|
||||
} else if(arg=="--streaming-server-disabled"){
|
||||
g_streamingServer=false;
|
||||
} else if(arg=="--autoupdater-force-full"){
|
||||
g_autoupdaterForceFull=true;
|
||||
}
|
||||
// parse cmd line
|
||||
for (int i = 1; i < argc; i++) {
|
||||
std::string arg(argv[i]);
|
||||
if (arg.rfind("--webui-url=", 0) == 0) {
|
||||
g_webuiUrls.insert(g_webuiUrls.begin(), Utf8ToWstring(arg.substr(12)));
|
||||
} else if (arg.rfind("--autoupdater-endpoint=", 0) == 0) {
|
||||
g_updateUrl = arg.substr(23);
|
||||
} else if (arg == "--streaming-server-disabled") {
|
||||
g_streamingServer = false;
|
||||
} else if (arg == "--autoupdater-force-full") {
|
||||
g_autoupdaterForceFull = true;
|
||||
}
|
||||
}
|
||||
|
||||
// single instance
|
||||
std::wstring launchProtocol;
|
||||
if(!CheckSingleInstance(argc, argv, launchProtocol)){
|
||||
return 0;
|
||||
}
|
||||
g_launchProtocol = launchProtocol;
|
||||
// single instance
|
||||
std::wstring launchProtocol;
|
||||
if (!CheckSingleInstance(argc, argv, launchProtocol)) {
|
||||
return 0;
|
||||
}
|
||||
g_launchProtocol = launchProtocol;
|
||||
|
||||
// check stremio-runtime duplicates
|
||||
std::vector<std::wstring> processesToCheck={L"stremio.exe", L"stremio-runtime.exe"};
|
||||
if(IsDuplicateProcessRunning(processesToCheck)){
|
||||
MessageBoxW(nullptr,
|
||||
L"An older version of Stremio or Stremio server may be running. There could be issues.",
|
||||
L"Stremio Already Running", MB_OK|MB_ICONWARNING);
|
||||
}
|
||||
// check stremio-runtime duplicates
|
||||
std::vector<std::wstring> processesToCheck = {L"stremio.exe",
|
||||
L"stremio-runtime.exe"};
|
||||
if (IsDuplicateProcessRunning(processesToCheck)) {
|
||||
MessageBoxW(nullptr,
|
||||
L"An older version of Stremio or Stremio server may be "
|
||||
L"running. There could be issues.",
|
||||
L"Stremio Already Running", MB_OK | MB_ICONWARNING);
|
||||
}
|
||||
|
||||
// init GDI+
|
||||
Gdiplus::GdiplusStartupInput gpsi;
|
||||
if(Gdiplus::GdiplusStartup(&g_gdiplusToken, &gpsi, nullptr)!=Gdiplus::Ok){
|
||||
AppendToCrashLog(L"[BOOT]: GdiplusStartup failed.");
|
||||
return 1;
|
||||
}
|
||||
// init GDI+
|
||||
Gdiplus::GdiplusStartupInput gpsi;
|
||||
if (Gdiplus::GdiplusStartup(&g_gdiplusToken, &gpsi, nullptr) != Gdiplus::Ok) {
|
||||
AppendToCrashLog(L"[BOOT]: GdiplusStartup failed.");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Load config
|
||||
LoadSettings();
|
||||
// Load config
|
||||
LoadSettings();
|
||||
|
||||
// Initialize Discord RPC
|
||||
InitializeDiscord();
|
||||
// Initialize Discord RPC
|
||||
InitializeDiscord();
|
||||
|
||||
// Updater
|
||||
g_updaterThread=std::thread(RunAutoUpdaterOnce);
|
||||
g_updaterThread.detach();
|
||||
// Updater
|
||||
g_updaterThread = std::thread(RunAutoUpdaterOnce);
|
||||
g_updaterThread.detach();
|
||||
|
||||
g_hInst = GetModuleHandle(nullptr);
|
||||
g_darkBrush = CreateSolidBrush(RGB(0,0,0));
|
||||
g_hInst = GetModuleHandle(nullptr);
|
||||
g_darkBrush = CreateSolidBrush(RGB(0, 0, 0));
|
||||
|
||||
// Register main window class
|
||||
WNDCLASSEX wcex={0};
|
||||
wcex.cbSize=sizeof(WNDCLASSEX);
|
||||
wcex.style=CS_HREDRAW|CS_VREDRAW;
|
||||
wcex.lpfnWndProc=WndProc;
|
||||
wcex.hInstance=g_hInst;
|
||||
wcex.hCursor=LoadCursor(nullptr, IDC_ARROW);
|
||||
wcex.hbrBackground=g_darkBrush;
|
||||
wcex.lpszClassName=szWindowClass;
|
||||
if(!RegisterClassEx(&wcex)){
|
||||
AppendToCrashLog(L"[BOOT]: RegisterClassEx failed!");
|
||||
return 1;
|
||||
}
|
||||
// Register main window class
|
||||
WNDCLASSEX wcex = {0};
|
||||
wcex.cbSize = sizeof(WNDCLASSEX);
|
||||
wcex.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wcex.lpfnWndProc = WndProc;
|
||||
wcex.hInstance = g_hInst;
|
||||
wcex.hCursor = LoadCursor(nullptr, IDC_ARROW);
|
||||
wcex.hbrBackground = g_darkBrush;
|
||||
wcex.lpszClassName = szWindowClass;
|
||||
if (!RegisterClassEx(&wcex)) {
|
||||
AppendToCrashLog(L"[BOOT]: RegisterClassEx failed!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
g_hWnd = CreateWindow(szWindowClass, szTitle,
|
||||
WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT,
|
||||
1200, 900,
|
||||
nullptr, nullptr,
|
||||
g_hInst, nullptr);
|
||||
if(!g_hWnd){
|
||||
AppendToCrashLog(L"[BOOT]: CreateWindow failed!");
|
||||
return 1;
|
||||
}
|
||||
g_hWnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
|
||||
CW_USEDEFAULT, CW_USEDEFAULT, 1200, 900, nullptr,
|
||||
nullptr, g_hInst, nullptr);
|
||||
if (!g_hWnd) {
|
||||
AppendToCrashLog(L"[BOOT]: CreateWindow failed!");
|
||||
return 1;
|
||||
}
|
||||
|
||||
//Add PlayPause Hotkey
|
||||
if (!RegisterHotKey(g_hWnd, 1, 0, VK_MEDIA_PLAY_PAUSE)) {
|
||||
AppendToCrashLog(L"[BOOT]: Failed to register hotkey!");
|
||||
}
|
||||
// Add PlayPause Hotkey
|
||||
if (!RegisterHotKey(g_hWnd, 1, 0, VK_MEDIA_PLAY_PAUSE)) {
|
||||
AppendToCrashLog(L"[BOOT]: Failed to register hotkey!");
|
||||
}
|
||||
|
||||
// Scale Values with DPI
|
||||
ScaleWithDPI();
|
||||
LoadCustomMenuFont();
|
||||
// Scale Values with DPI
|
||||
ScaleWithDPI();
|
||||
LoadCustomMenuFont();
|
||||
|
||||
// Load Saved position
|
||||
WINDOWPLACEMENT wp;
|
||||
if (LoadWindowPlacement(wp)) {
|
||||
SetWindowPlacement(g_hWnd, &wp);
|
||||
ShowWindow(g_hWnd, wp.showCmd);
|
||||
UpdateWindow(g_hWnd);
|
||||
} else {
|
||||
ShowWindow(g_hWnd,SW_SHOW);
|
||||
UpdateWindow(g_hWnd);
|
||||
}
|
||||
// Load Saved position
|
||||
WINDOWPLACEMENT wp;
|
||||
if (LoadWindowPlacement(wp)) {
|
||||
SetWindowPlacement(g_hWnd, &wp);
|
||||
ShowWindow(g_hWnd, wp.showCmd);
|
||||
UpdateWindow(g_hWnd);
|
||||
} else {
|
||||
ShowWindow(g_hWnd, SW_SHOW);
|
||||
UpdateWindow(g_hWnd);
|
||||
}
|
||||
|
||||
// create splash
|
||||
CreateSplashScreen(g_hWnd);
|
||||
// create splash
|
||||
CreateSplashScreen(g_hWnd);
|
||||
|
||||
// init mpv
|
||||
if(!InitMPV(g_hWnd)){
|
||||
DestroyWindow(g_hWnd);
|
||||
return 1;
|
||||
}
|
||||
// init mpv
|
||||
if (!InitMPV(g_hWnd)) {
|
||||
DestroyWindow(g_hWnd);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// node
|
||||
if(g_streamingServer){
|
||||
StartNodeServer();
|
||||
}
|
||||
// node
|
||||
if (g_streamingServer) {
|
||||
StartNodeServer();
|
||||
}
|
||||
|
||||
// webview
|
||||
InitWebView2(g_hWnd);
|
||||
// webview
|
||||
InitWebView2(g_hWnd);
|
||||
|
||||
// message loop
|
||||
MSG msg;
|
||||
while(GetMessage(&msg, nullptr, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
// message loop
|
||||
MSG msg;
|
||||
while (GetMessage(&msg, nullptr, 0, 0)) {
|
||||
TranslateMessage(&msg);
|
||||
DispatchMessage(&msg);
|
||||
|
||||
// Run Discord RPC callbacks
|
||||
Discord_RunCallbacks();
|
||||
}
|
||||
// Run Discord RPC callbacks
|
||||
Discord_RunCallbacks();
|
||||
}
|
||||
|
||||
if(g_darkBrush){
|
||||
DeleteObject(g_darkBrush);
|
||||
g_darkBrush=nullptr;
|
||||
}
|
||||
std::cout<<"Exiting...\n";
|
||||
return (int)msg.wParam;
|
||||
if (g_darkBrush) {
|
||||
DeleteObject(g_darkBrush);
|
||||
g_darkBrush = nullptr;
|
||||
}
|
||||
std::cout << "Exiting...\n";
|
||||
return (int)msg.wParam;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
#include "helpers.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <shellscalingapi.h>
|
||||
#include <tlhelp32.h>
|
||||
|
|
@ -244,4 +245,104 @@ void ScaleWithDPI() {
|
|||
g_tray_sepH = ScaleValue(g_tray_sepH);
|
||||
g_tray_w = ScaleValue(g_tray_w);
|
||||
g_font_height = ScaleValue(g_font_height);
|
||||
}
|
||||
|
||||
// ---- UTF-8 file read
|
||||
bool ReadFileUtf8(const std::wstring& path, std::string& out)
|
||||
{
|
||||
std::ifstream f(path, std::ios::binary);
|
||||
if (!f) return false;
|
||||
f.seekg(0, std::ios::end);
|
||||
std::streamsize size = f.tellg();
|
||||
f.seekg(0, std::ios::beg);
|
||||
out.resize(static_cast<size_t>(size));
|
||||
if (size > 0) f.read(&out[0], size);
|
||||
return true;
|
||||
}
|
||||
|
||||
// ---- tiny Base64
|
||||
std::string Base64Encode(const std::string& in)
|
||||
{
|
||||
static const char* T = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
|
||||
std::string out;
|
||||
out.reserve(((in.size() + 2) / 3) * 4);
|
||||
int val = 0, valb = -6;
|
||||
for (uint8_t c : in) {
|
||||
val = (val << 8) + c;
|
||||
valb += 8;
|
||||
while (valb >= 0) {
|
||||
out.push_back(T[(val >> valb) & 0x3F]);
|
||||
valb -= 6;
|
||||
}
|
||||
}
|
||||
if (valb > -6) out.push_back(T[((val << 8) >> (valb + 8)) & 0x3F]);
|
||||
while (out.size() % 4) out.push_back('=');
|
||||
return out;
|
||||
}
|
||||
|
||||
// ---- wrap CSS text into a safe injector (decodes UTF-8 from base64)
|
||||
std::wstring MakeInjectCssScript(const std::wstring& idSafe, const std::string& cssUtf8)
|
||||
{
|
||||
const std::string b64 = Base64Encode(cssUtf8);
|
||||
const std::wstring wb64 = Utf8ToWstring(b64);
|
||||
std::wstringstream ss;
|
||||
|
||||
ss <<
|
||||
L"(function(){try{"
|
||||
L"if(window.top!==window)return;"
|
||||
L"var id='webmods-css-" << idSafe << L"';"
|
||||
L"function inject(){"
|
||||
L"try{"
|
||||
L"var root=document.head||document.documentElement||document.body;"
|
||||
L"if(!root){"
|
||||
L"document.addEventListener('DOMContentLoaded',inject,{once:true});"
|
||||
L"document.addEventListener('readystatechange',function(){"
|
||||
L"if(document.readyState==='interactive'||document.readyState==='complete')inject();"
|
||||
L"},{once:true});"
|
||||
L"setTimeout(inject,25);"
|
||||
L"return;"
|
||||
L"}"
|
||||
L"if(document.getElementById(id))return;"
|
||||
L"var bin=atob('" << wb64 << L"');"
|
||||
L"var bytes=new Uint8Array(bin.length);"
|
||||
L"for(var i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);"
|
||||
L"var css='';"
|
||||
L"try{css=new TextDecoder('utf-8').decode(bytes);}catch(e){css=decodeURIComponent(escape(bin));}"
|
||||
L"var s=document.createElement('style');"
|
||||
L"s.id=id;"
|
||||
L"s.textContent=css;"
|
||||
L"root.appendChild(s);"
|
||||
L"}catch(e){console.error('webmods css inject tick failed:',e);setTimeout(inject,50);}"
|
||||
L"}"
|
||||
L"inject();"
|
||||
L"}catch(e){console.error('webmods css inject failed:',e);}})();";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
|
||||
// ---- wrap JS text into a safe executor
|
||||
std::wstring MakeInjectJsScript(const std::wstring&,const std::string& jsUtf8)
|
||||
{
|
||||
const std::string b64 = Base64Encode(jsUtf8);
|
||||
const std::wstring wb64 = Utf8ToWstring(b64);
|
||||
std::wstringstream ss;
|
||||
|
||||
ss <<
|
||||
L"(function(){try{"
|
||||
L"if(window.top!==window)return;"
|
||||
L"function run(){"
|
||||
L"try{"
|
||||
L"var bin=atob('" << wb64 << L"');"
|
||||
L"var bytes=new Uint8Array(bin.length);"
|
||||
L"for(var i=0;i<bin.length;i++)bytes[i]=bin.charCodeAt(i);"
|
||||
L"var js='';"
|
||||
L"try{js=new TextDecoder('utf-8').decode(bytes);}catch(e){js=decodeURIComponent(escape(bin));}"
|
||||
L"(0,eval)(js);"
|
||||
L"}catch(e){console.error('webmods js exec tick failed:',e);setTimeout(run,25);}"
|
||||
L"}"
|
||||
L"run();"
|
||||
L"}catch(e){console.error('webmods js exec failed:',e);}})();";
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
|
@ -11,6 +11,8 @@ std::wstring Utf8ToWstring(const std::string& utf8Str);
|
|||
std::string decodeURIComponent(const std::string& encoded);
|
||||
std::wstring GetExeDirectory();
|
||||
std::wstring GetFirstReachableUrl();
|
||||
std::wstring MakeInjectCssScript(const std::wstring& idSafe, const std::string& cssUtf8);
|
||||
std::wstring MakeInjectJsScript(const std::wstring& idSafe, const std::string& jsUtf8);
|
||||
bool FileExists(const std::wstring& path);
|
||||
bool DirectoryExists(const std::wstring& dirPath);
|
||||
bool IsDuplicateProcessRunning(const std::vector<std::wstring>& targetProcesses);
|
||||
|
|
@ -18,5 +20,6 @@ bool isSubtitle(const std::wstring& filePath);
|
|||
bool URLContainsAny(const std::wstring& url);
|
||||
bool FetchAndParseWhitelist();
|
||||
void ScaleWithDPI();
|
||||
bool ReadFileUtf8(const std::wstring& path, std::string& out);
|
||||
|
||||
#endif // HELPERS_H
|
||||
|
|
|
|||
|
|
@ -289,6 +289,8 @@ void InitWebView2(HWND hWnd)
|
|||
g_webview->AddScriptToExecuteOnDocumentCreated(EXEC_SHELL_SCRIPT,nullptr);
|
||||
g_webview->AddScriptToExecuteOnDocumentCreated(INJECTED_KEYDOWN_SCRIPT,nullptr);
|
||||
|
||||
SetupWebMods();
|
||||
|
||||
SetupExtensions();
|
||||
SetupWebMessageHandler();
|
||||
|
||||
|
|
@ -627,6 +629,68 @@ static void SetupExtensions()
|
|||
}
|
||||
}
|
||||
|
||||
static void SetupWebMods()
|
||||
{
|
||||
if (!g_webview) return;
|
||||
|
||||
wchar_t buf[MAX_PATH];
|
||||
GetModuleFileNameW(nullptr, buf, MAX_PATH);
|
||||
std::wstring exeDir = buf;
|
||||
size_t pos = exeDir.find_last_of(L"\\/");
|
||||
if (pos != std::wstring::npos) exeDir.erase(pos);
|
||||
|
||||
const std::filesystem::path root = std::filesystem::path(exeDir) / L"portable_config" / L"webmods";
|
||||
if (!std::filesystem::exists(root) || !std::filesystem::is_directory(root)) {
|
||||
std::wcout << L"[WEBMODS] Folder not found: " << root.wstring() << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::vector<std::filesystem::path> cssFiles, jsFiles;
|
||||
for (const auto& e : std::filesystem::recursive_directory_iterator(root)) {
|
||||
if (!e.is_regular_file()) continue;
|
||||
auto ext = e.path().extension().wstring();
|
||||
std::transform(ext.begin(), ext.end(), ext.begin(), ::towlower);
|
||||
if (ext == L".map" || ext == L".bak" || ext == L".tmp") continue;
|
||||
if (ext == L".css") cssFiles.push_back(e.path());
|
||||
else if (ext == L".js") jsFiles.push_back(e.path());
|
||||
}
|
||||
|
||||
auto relStr = [&](const std::filesystem::path& p){
|
||||
try { return std::filesystem::relative(p, root).wstring(); }
|
||||
catch(...) { return p.wstring(); }
|
||||
};
|
||||
auto sorter = [&](const std::filesystem::path& a, const std::filesystem::path& b){
|
||||
auto ra = relStr(a), rb = relStr(b);
|
||||
return _wcsicmp(ra.c_str(), rb.c_str()) < 0;
|
||||
};
|
||||
std::sort(cssFiles.begin(), cssFiles.end(), sorter);
|
||||
std::sort(jsFiles.begin(), jsFiles.end(), sorter);
|
||||
|
||||
auto makeId = [&](const std::filesystem::path& p){
|
||||
std::wstring id = relStr(p);
|
||||
for (auto& ch : id) if (!iswalnum(ch)) ch = L'_';
|
||||
return id;
|
||||
};
|
||||
|
||||
for (const auto& p : cssFiles) {
|
||||
std::string content;
|
||||
if (!ReadFileUtf8(p.wstring(), content)) continue;
|
||||
const std::wstring id = makeId(p);
|
||||
const std::wstring script = MakeInjectCssScript(id, content);
|
||||
g_webview->AddScriptToExecuteOnDocumentCreated(script.c_str(), nullptr);
|
||||
std::wcout << L"[WEBMODS] CSS: " << relStr(p) << std::endl;
|
||||
}
|
||||
|
||||
for (const auto& p : jsFiles) {
|
||||
std::string content;
|
||||
if (!ReadFileUtf8(p.wstring(), content)) continue;
|
||||
const std::wstring id = makeId(p);
|
||||
const std::wstring script = MakeInjectJsScript(id, content);
|
||||
g_webview->AddScriptToExecuteOnDocumentCreated(script.c_str(), nullptr);
|
||||
std::wcout << L"[WEBMODS] JS: " << relStr(p) << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void refreshWeb(const bool refreshAll) {
|
||||
if (g_webviewProfile && refreshAll)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -9,5 +9,6 @@ void WaitAndRefreshIfNeeded();
|
|||
void refreshWeb(bool refreshAll);
|
||||
static void SetupWebMessageHandler();
|
||||
static void SetupExtensions();
|
||||
static void SetupWebMods();
|
||||
|
||||
#endif // WEBVIEW_H
|
||||
|
|
|
|||
BIN
utils/webmods/liquid-glass-theme-v1.rar
Normal file
BIN
utils/webmods/liquid-glass-theme-v1.rar
Normal file
Binary file not shown.
Loading…
Reference in a new issue