fix: Windows-safe DB_PATH via env var, add Windows installer scripts
This commit is contained in:
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',
|
||||
)
|
||||
@@ -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
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])])
|
||||
]
|
||||
)
|
||||
Reference in New Issue
Block a user