fix: Windows-safe DB_PATH via env var, add Windows installer scripts
This commit is contained in:
116
WINDOWS_BUILD_README.md
Normal file
116
WINDOWS_BUILD_README.md
Normal 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
|
||||
34
backend/batchtube-launcher.py
Normal file
34
backend/batchtube-launcher.py
Normal 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()
|
||||
54
backend/batchtube-windows.spec
Normal file
54
backend/batchtube-windows.spec
Normal 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',
|
||||
)
|
||||
@@ -32,7 +32,7 @@ SUMMARY_MAX_TOKENS = 1024
|
||||
MAX_TITLE_LENGTH = 80 # characters - truncate long titles in filenames
|
||||
|
||||
# ── 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 = ["title", "stats", "transcript", "summary", "thumbnail", "mp3", "mp4"]
|
||||
|
||||
16
backend/version_info.txt
Normal file
16
backend/version_info.txt
Normal 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
45
build_windows.bat
Normal 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
61
installer.nsi
Normal 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
|
||||
Reference in New Issue
Block a user