diff --git a/WINDOWS_BUILD_README.md b/WINDOWS_BUILD_README.md new file mode 100644 index 0000000..fe54cbe --- /dev/null +++ b/WINDOWS_BUILD_README.md @@ -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 diff --git a/backend/batchtube-launcher.py b/backend/batchtube-launcher.py new file mode 100644 index 0000000..c81dc3b --- /dev/null +++ b/backend/batchtube-launcher.py @@ -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() diff --git a/backend/batchtube-windows.spec b/backend/batchtube-windows.spec new file mode 100644 index 0000000..c2780c7 --- /dev/null +++ b/backend/batchtube-windows.spec @@ -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', +) diff --git a/backend/config.py b/backend/config.py index 0e2eca3..91b0d03 100644 --- a/backend/config.py +++ b/backend/config.py @@ -1,38 +1,38 @@ -""" -config.py - Default configuration for YouTube SIPOC Tool -""" -import os - -# ── API Settings ────────────────────────────────────────────────────────────── -OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "") -OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1" -OPENROUTER_MODEL = os.environ.get("OPENROUTER_MODEL", "openai/gpt-4o-mini") - -# ── Server Settings ─────────────────────────────────────────────────────────── -API_HOST = "127.0.0.1" -API_PORT = 8765 - -# ── Download / Bot Avoidance ────────────────────────────────────────────────── -DELAY_MIN_SECONDS = 2.0 # minimum random pause between artifacts -DELAY_MAX_SECONDS = 7.0 # maximum random pause between artifacts -MAX_RETRIES = 3 # max retry attempts per failed artifact - -# ── Video Download ──────────────────────────────────────────────────────────── -DEFAULT_VIDEO_RESOLUTION = "1080" # used if not specified by user -AVAILABLE_RESOLUTIONS = ["best", "4320", "2160", "1440", "1080", "720", "480", "360", "240", "144"] - -# ── Summary ─────────────────────────────────────────────────────────────────── -SUMMARY_SYSTEM_PROMPT = ( - "You are a helpful assistant that creates concise, well-structured summaries of video transcripts. " - "Include key points, main topics, and any actionable insights. Format with bullet points where appropriate." -) -SUMMARY_MAX_TOKENS = 1024 - -# ── File Naming ─────────────────────────────────────────────────────────────── -MAX_TITLE_LENGTH = 80 # characters - truncate long titles in filenames - -# ── Database ────────────────────────────────────────────────────────────────── -DB_PATH = os.path.join(os.path.dirname(__file__), "youtube_sipoc.db") - -# ── Artifact Types ──────────────────────────────────────────────────────────── -ARTIFACT_TYPES = ["title", "stats", "transcript", "summary", "thumbnail", "mp3", "mp4"] +""" +config.py - Default configuration for YouTube SIPOC Tool +""" +import os + +# ── API Settings ────────────────────────────────────────────────────────────── +OPENROUTER_API_KEY = os.environ.get("OPENROUTER_API_KEY", "") +OPENROUTER_BASE_URL = "https://openrouter.ai/api/v1" +OPENROUTER_MODEL = os.environ.get("OPENROUTER_MODEL", "openai/gpt-4o-mini") + +# ── Server Settings ─────────────────────────────────────────────────────────── +API_HOST = "127.0.0.1" +API_PORT = 8765 + +# ── Download / Bot Avoidance ────────────────────────────────────────────────── +DELAY_MIN_SECONDS = 2.0 # minimum random pause between artifacts +DELAY_MAX_SECONDS = 7.0 # maximum random pause between artifacts +MAX_RETRIES = 3 # max retry attempts per failed artifact + +# ── Video Download ──────────────────────────────────────────────────────────── +DEFAULT_VIDEO_RESOLUTION = "1080" # used if not specified by user +AVAILABLE_RESOLUTIONS = ["best", "4320", "2160", "1440", "1080", "720", "480", "360", "240", "144"] + +# ── Summary ─────────────────────────────────────────────────────────────────── +SUMMARY_SYSTEM_PROMPT = ( + "You are a helpful assistant that creates concise, well-structured summaries of video transcripts. " + "Include key points, main topics, and any actionable insights. Format with bullet points where appropriate." +) +SUMMARY_MAX_TOKENS = 1024 + +# ── File Naming ─────────────────────────────────────────────────────────────── +MAX_TITLE_LENGTH = 80 # characters - truncate long titles in filenames + +# ── Database ────────────────────────────────────────────────────────────────── +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"] diff --git a/backend/version_info.txt b/backend/version_info.txt new file mode 100644 index 0000000..482ee45 --- /dev/null +++ b/backend/version_info.txt @@ -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])]) + ] +) diff --git a/build_windows.bat b/build_windows.bat new file mode 100644 index 0000000..e2f036b --- /dev/null +++ b/build_windows.bat @@ -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 diff --git a/installer.nsi b/installer.nsi new file mode 100644 index 0000000..2d5ecc0 --- /dev/null +++ b/installer.nsi @@ -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