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

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

@@ -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"]

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])])
]
)