fix: Windows-safe DB_PATH via env var, add Windows installer scripts

This commit is contained in:
2026-03-07 13:43:35 +01:00
parent fd8b4ef1f9
commit 069dfc0ae4
7 changed files with 364 additions and 38 deletions

116
WINDOWS_BUILD_README.md Normal file
View File

@@ -0,0 +1,116 @@
# BatchTube Windows Build Guide
Intentures LLC | https://batchtube.com | v1.0.0
## Files In This Package
| File | Location | Purpose |
|------|----------|---------|
| batchtube-launcher.py | backend/ | Windows entry point - starts FastAPI + opens browser |
| batchtube-windows.spec | backend/ | PyInstaller spec - single-file EXE with all deps |
| version_info.txt | backend/ | Windows EXE metadata |
| installer.nsi | project root | NSIS installer script |
| build_windows.bat | project root | One-click build script |
## Prerequisites
- Python 3.11+ (64-bit) - https://python.org/downloads
- NSIS 3.x - https://nsis.sourceforge.io/
- Git - https://git-scm.com
## Quick Build
Double-click build_windows.bat - it handles everything:
1. Creates virtual environment
2. Installs all Python dependencies
3. Downloads yt-dlp.exe
4. Runs PyInstaller -> backend/dist/BatchTube.exe
5. Runs NSIS -> BatchTube-Setup-1.0.0.exe
## Manual Build Steps
### Step 1 - Get Source
scp -r root@212.28.188.26:/root/batchtube-tauri ./batchtube
cd batchtube
### Step 2 - Fix config.py DB Path
Edit backend/config.py - replace the DB_PATH line:
# Before:
DB_PATH = os.path.join(os.path.dirname(__file__), "youtube_sipoc.db")
# After:
DB_PATH = os.environ.get(
'BATCHTUBE_DB_PATH',
os.path.join(os.path.dirname(__file__), "youtube_sipoc.db")
)
### Step 3 - Install Dependencies
cd backend
python -m venv venv
venv\Scripts\activate
pip install -r requirements.txt
pip install pyinstaller
### Step 4 - Download yt-dlp
mkdir ..\tools
curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe -o ..\tools\yt-dlp.exe
### Step 5 - Build EXE
pyinstaller batchtube-windows.spec --clean
# Output: backend/dist/BatchTube.exe
### Step 6 - Test EXE
dist\BatchTube.exe
# Browser should open to http://127.0.0.1:8765
### Step 7 - Build Installer
cd ..
makensis installer.nsi
# Output: BatchTube-Setup-1.0.0.exe
## Troubleshooting
| Problem | Fix |
|---------|-----|
| ModuleNotFoundError at runtime | Add to hiddenimports in batchtube-windows.spec |
| Web UI 404 | Check datas path ('web','backend/web') in spec |
| DB crash on launch | Apply config.py DB_PATH fix in Step 2 |
| App closes instantly | Set console=True in spec temporarily to see errors |
| SmartScreen warning | Code-sign BatchTube.exe with a Windows cert |
## After Install
| Path | Contents |
|------|----------|
| C:\Program Files\BatchTube\BatchTube.exe | Application |
| %APPDATA%\BatchTube\batchtube.db | SQLite database |
| Start Menu -> BatchTube | Launch shortcut |
| Desktop | Launch shortcut |
## GitHub Actions - Automated Windows Build
Add to .github/workflows/build.yml:
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with: { python-version: '3.11' }
- run: pip install -r backend/requirements.txt pyinstaller
- run: cd backend && pyinstaller batchtube-windows.spec --clean
- run: choco install nsis -y && makensis installer.nsi
- uses: actions/upload-artifact@v4
with:
name: BatchTube-Windows
path: BatchTube-Setup-*.exe
---
Generated 2026-03-07 by Agent Developer - Intentures LLC

View File

@@ -0,0 +1,34 @@
import sys, os, time, socket, threading, webbrowser
if getattr(sys, 'frozen', False):
BASE_DIR = sys._MEIPASS
APP_DIR = os.path.dirname(sys.executable)
else:
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
APP_DIR = BASE_DIR
BACKEND_DIR = os.path.join(BASE_DIR,'backend') if os.path.isdir(os.path.join(BASE_DIR,'backend')) else BASE_DIR
sys.path.insert(0, BACKEND_DIR)
sys.path.insert(0, BASE_DIR)
APPDATA_DIR = os.path.join(os.environ.get('APPDATA', APP_DIR), 'BatchTube')
os.makedirs(APPDATA_DIR, exist_ok=True)
os.environ['BATCHTUBE_DB_PATH'] = os.path.join(APPDATA_DIR, 'batchtube.db')
for c in [os.path.join(APP_DIR,'yt-dlp.exe'), os.path.join(BASE_DIR,'yt-dlp.exe')]:
if os.path.exists(c): os.environ['YTDLP_PATH'] = c; break
for d in [os.path.join(APP_DIR,'ffmpeg'), os.path.join(BASE_DIR,'ffmpeg')]:
if os.path.isdir(d): os.environ['PATH'] = d + os.pathsep + os.environ.get('PATH',''); break
HOST, PORT = '127.0.0.1', 8765
URL = f'http://{HOST}:{PORT}'
def is_open(h, p, t=0.5):
try:
with socket.create_connection((h,p), timeout=t): return True
except: return False
def open_browser():
for _ in range(60):
if is_open(HOST, PORT): time.sleep(0.3); webbrowser.open(URL); return
time.sleep(0.5)
def run_server():
import uvicorn
os.chdir(BACKEND_DIR)
uvicorn.run('api_server:app', host=HOST, port=PORT, log_level='warning', access_log=False)
if __name__ == '__main__':
threading.Thread(target=open_browser, daemon=True).start()
run_server()

View File

@@ -0,0 +1,54 @@
# batchtube-windows.spec
# PyInstaller spec for Windows single-file EXE
# Run from backend/ directory: pyinstaller batchtube-windows.spec --clean
# Output: dist/BatchTube.exe
from PyInstaller.utils.hooks import collect_all
block_cipher = None
datas, binaries, hiddenimports = [], [], []
for pkg in ['yt_dlp','uvicorn','fastapi','starlette','anyio','sse_starlette','youtube_transcript_api','openai','pydantic']:
d,b,h = collect_all(pkg)
datas+=d; binaries+=b; hiddenimports+=h
datas += [('web','backend/web')]
hiddenimports += [
'uvicorn.logging','uvicorn.loops','uvicorn.loops.auto','uvicorn.loops.asyncio',
'uvicorn.protocols','uvicorn.protocols.http','uvicorn.protocols.http.auto',
'uvicorn.protocols.http.h11_impl','uvicorn.protocols.http.httptools_impl',
'uvicorn.protocols.websockets','uvicorn.protocols.websockets.auto',
'uvicorn.lifespan','uvicorn.lifespan.off','uvicorn.lifespan.on',
'fastapi','fastapi.middleware.cors','starlette.middleware.cors',
'sse_starlette','sse_starlette.sse',
'pydantic','pydantic.v1','pydantic_core','annotated_types','typing_extensions',
'h11','httptools','httpx','anyio','anyio._backends._asyncio','sniffio',
'multipart','python_multipart','aiofiles','aiofiles.os','aiofiles.threadpool',
'yt_dlp','yt_dlp.extractor','yt_dlp.downloader','yt_dlp.postprocessor',
'youtube_transcript_api','youtube_transcript_api._transcripts',
'openai','openai.resources',
'requests','urllib3','certifi','charset_normalizer',
'slugify','python_slugify','tqdm','tqdm.auto',
'sqlite3','_sqlite3','asyncio','concurrent.futures','threading','queue',
'websockets','click','rich','watchfiles','mcp',
'api_server','worker','database','extractors','config',
]
a = Analysis(
['batchtube-launcher.py'],
pathex=['.'],
binaries=binaries,
datas=datas,
hiddenimports=hiddenimports,
hookspath=[],
runtime_hooks=[],
excludes=['tkinter','matplotlib','numpy','pandas','torch','tensorflow','PyQt5','PyQt6'],
cipher=block_cipher,
noarchive=False,
)
pyz = PYZ(a.pure, a.zipped_data, cipher=block_cipher)
exe = EXE(
pyz, a.scripts, a.binaries, a.zipfiles, a.datas, [],
name='BatchTube',
debug=False, strip=False, upx=True,
runtime_tmpdir=None,
console=False,
icon='../src-tauri/icons/icon.ico',
version='version_info.txt',
)

View File

@@ -32,7 +32,7 @@ SUMMARY_MAX_TOKENS = 1024
MAX_TITLE_LENGTH = 80 # characters - truncate long titles in filenames MAX_TITLE_LENGTH = 80 # characters - truncate long titles in filenames
# ── Database ────────────────────────────────────────────────────────────────── # ── Database ──────────────────────────────────────────────────────────────────
DB_PATH = os.path.join(os.path.dirname(__file__), "youtube_sipoc.db") DB_PATH = os.environ.get("BATCHTUBE_DB_PATH", os.path.join(os.path.dirname(__file__), "youtube_sipoc.db"))
# ── Artifact Types ──────────────────────────────────────────────────────────── # ── Artifact Types ────────────────────────────────────────────────────────────
ARTIFACT_TYPES = ["title", "stats", "transcript", "summary", "thumbnail", "mp3", "mp4"] ARTIFACT_TYPES = ["title", "stats", "transcript", "summary", "thumbnail", "mp3", "mp4"]

16
backend/version_info.txt Normal file
View File

@@ -0,0 +1,16 @@
VSVersionInfo(
ffi=FixedFileInfo(filevers=(1,0,0,0),prodvers=(1,0,0,0),mask=0x3f,flags=0x0,OS=0x40004,fileType=0x1,subtype=0x0,date=(0,0)),
kids=[
StringFileInfo([StringTable(u'040904B0',[
StringStruct(u'CompanyName',u'Intentures LLC'),
StringStruct(u'FileDescription',u'BatchTube - Batch YouTube Content Extractor'),
StringStruct(u'FileVersion',u'1.0.0.0'),
StringStruct(u'InternalName',u'BatchTube'),
StringStruct(u'LegalCopyright',u'Copyright (C) 2026 Intentures LLC'),
StringStruct(u'OriginalFilename',u'BatchTube.exe'),
StringStruct(u'ProductName',u'BatchTube'),
StringStruct(u'ProductVersion',u'1.0.0.0'),
])]),
VarFileInfo([VarStruct(u'Translation',[1033,1200])])
]
)

45
build_windows.bat Normal file
View File

@@ -0,0 +1,45 @@
@echo off
title BatchTube Windows Build
echo.
echo ============================================================
echo BatchTube Windows Installer Builder v1.0.0
echo Intentures LLC - https://batchtube.com
echo ============================================================
echo.
python --version >nul 2>&1
if errorlevel 1 (echo ERROR: Python not found. Install Python 3.11+ && pause && exit /b 1)
echo [1/5] Creating virtual environment...
if exist build_venv rmdir /s /q build_venv
python -m venv build_venv
call build_venv\Scripts\activate.bat
echo [2/5] Installing dependencies...
pip install --upgrade pip -q
pip install -r backend\requirements.txt -q
pip install pyinstaller -q
echo [3/5] Downloading yt-dlp...
if not exist tools mkdir tools
curl -L https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp.exe -o tools\yt-dlp.exe
echo [4/5] Building with PyInstaller...
cd backend
pyinstaller batchtube-windows.spec --clean --noconfirm
cd ..
if not exist backend\dist\BatchTube.exe (echo ERROR: Build failed && pause && exit /b 1)
echo Build successful: backend\dist\BatchTube.exe
echo [5/5] Creating NSIS installer...
makensis /VERSION >nul 2>&1
if errorlevel 1 (
echo NSIS not found - install from https://nsis.sourceforge.io/
echo Your EXE is ready at: backend\dist\BatchTube.exe
) else (
makensis installer.nsi
echo.
echo SUCCESS: BatchTube-Setup-1.0.0.exe created
)
pause

61
installer.nsi Normal file
View File

@@ -0,0 +1,61 @@
; BatchTube NSIS Installer Script - Updated for backend/ structure
; Requires NSIS 3.x - https://nsis.sourceforge.io/
!define APP_NAME "BatchTube"
!define APP_VERSION "1.0.0"
!define APP_PUBLISHER "Intentures LLC"
!define APP_URL "https://batchtube.com"
!define APP_EXE "BatchTube.exe"
!define INSTALL_DIR "$PROGRAMFILES64\BatchTube"
Name "${APP_NAME} ${APP_VERSION}"
OutFile "BatchTube-Setup-${APP_VERSION}.exe"
InstallDir "${INSTALL_DIR}"
InstallDirRegKey HKLM "Software\${APP_NAME}" "Install_Dir"
RequestExecutionLevel admin
!include "MUI2.nsh"
!define MUI_ABORTWARNING
!define MUI_ICON "..\src-tauri\icons\icon.ico"
!define MUI_UNICON "..\src-tauri\icons\icon.ico"
!insertmacro MUI_PAGE_WELCOME
!insertmacro MUI_PAGE_LICENSE "..\LICENSE.txt"
!insertmacro MUI_PAGE_DIRECTORY
!insertmacro MUI_PAGE_INSTFILES
!define MUI_FINISHPAGE_RUN "$INSTDIR\${APP_EXE}"
!define MUI_FINISHPAGE_RUN_TEXT "Launch BatchTube now"
!insertmacro MUI_PAGE_FINISH
!insertmacro MUI_UNPAGE_CONFIRM
!insertmacro MUI_UNPAGE_INSTFILES
!insertmacro MUI_LANGUAGE "English"
Section "BatchTube" SecMain
SetOutPath "$INSTDIR"
File "backend\dist\BatchTube.exe"
; Optional: bundle yt-dlp
; File "tools\yt-dlp.exe"
; Optional: bundle ffmpeg
; File /r "tools\ffmpeg"
WriteRegStr HKLM "Software\${APP_NAME}" "Install_Dir" "$INSTDIR"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "DisplayName" "${APP_NAME}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "UninstallString" '"$INSTDIR\Uninstall.exe"'
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "DisplayVersion" "${APP_VERSION}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "Publisher" "${APP_PUBLISHER}"
WriteRegStr HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "URLInfoAbout" "${APP_URL}"
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "NoModify" 1
WriteRegDWORD HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}" "NoRepair" 1
WriteUninstaller "$INSTDIR\Uninstall.exe"
CreateShortcut "$DESKTOP\BatchTube.lnk" "$INSTDIR\${APP_EXE}"
CreateDirectory "$SMPROGRAMS\BatchTube"
CreateShortcut "$SMPROGRAMS\BatchTube\BatchTube.lnk" "$INSTDIR\${APP_EXE}"
CreateShortcut "$SMPROGRAMS\BatchTube\Uninstall BatchTube.lnk" "$INSTDIR\Uninstall.exe"
SectionEnd
Section "Uninstall"
RMDir /r "$INSTDIR"
Delete "$DESKTOP\BatchTube.lnk"
RMDir /r "$SMPROGRAMS\BatchTube"
DeleteRegKey HKLM "Software\Microsoft\Windows\CurrentVersion\Uninstall\${APP_NAME}"
DeleteRegKey HKLM "Software\${APP_NAME}"
SectionEnd