Function bodies 212 total
test_ollama_connection function · python · L24-L40 (17 LOC)services/verify.py
def test_ollama_connection() -> dict:
"""Test Ollama connectivity."""
cfg = _get_ollama_config()
if not cfg["url"]:
return {"ok": False, "message": "Ollama URL not configured"}
try:
resp = requests.get(f"{cfg['url']}/api/tags", timeout=10)
resp.raise_for_status()
models = [m["name"] for m in resp.json().get("models", [])]
has_model = any(cfg["model"] in m for m in models)
if has_model:
return {"ok": True, "message": f"Connected - {cfg['model']} available"}
return {"ok": True,
"message": f"Connected but {cfg['model']} not found. "
f"Available: {', '.join(models[:5])}"}
except requests.RequestException as e:
return {"ok": False, "message": str(e)}_find_video_files function · python · L43-L54 (12 LOC)services/verify.py
def _find_video_files(directory: str) -> list[str]:
"""Find all video files in a directory recursively."""
video_exts = MEDIA_TYPES["video"]
videos = []
for root, _dirs, files in os.walk(directory):
for f in files:
ext = os.path.splitext(f)[1].lower()
if ext in video_exts:
videos.append(os.path.join(root, f))
# Sort by size descending (largest = most likely the main video)
videos.sort(key=lambda p: os.path.getsize(p), reverse=True)
return videos_get_video_duration function · python · L57-L68 (12 LOC)services/verify.py
def _get_video_duration(video_path: str) -> float:
"""Get video duration in seconds using ffprobe."""
try:
result = subprocess.run(
["ffprobe", "-v", "error", "-show_entries", "format=duration",
"-of", "json", video_path],
capture_output=True, text=True, timeout=30,
)
data = json.loads(result.stdout)
return float(data["format"]["duration"])
except (subprocess.SubprocessError, KeyError, ValueError, json.JSONDecodeError):
return 0generate_thumbnails function · python · L71-L105 (35 LOC)services/verify.py
def generate_thumbnails(video_path: str, download_id: int,
count: int = 5) -> list[dict]:
"""Generate thumbnail screenshots from a video at evenly spaced intervals."""
duration = _get_video_duration(video_path)
if duration <= 0:
return []
thumb_dir = os.path.join(THUMBNAIL_DIR, str(download_id))
os.makedirs(thumb_dir, exist_ok=True)
thumbnails = []
percentages = [0.1, 0.3, 0.5, 0.7, 0.9][:count]
for i, pct in enumerate(percentages):
timestamp = duration * pct
output_path = os.path.join(thumb_dir, f"thumb_{i:02d}.jpg")
try:
subprocess.run(
["ffmpeg", "-y", "-ss", str(timestamp), "-i", video_path,
"-vframes", "1", "-q:v", str(config.THUMBNAIL_QUALITY),
output_path],
capture_output=True, timeout=30,
)
if os.path.exists(output_path) and os.path.getsize(output_path) > 0:
thumbnails.ap_call_ollama_vision function · python · L108-L130 (23 LOC)services/verify.py
def _call_ollama_vision(image_path: str, prompt: str) -> dict:
"""Call Ollama with a vision model to analyze an image."""
cfg = _get_ollama_config()
with open(image_path, "rb") as f:
image_b64 = base64.b64encode(f.read()).decode("utf-8")
try:
resp = requests.post(
f"{cfg['url']}/api/generate",
json={
"model": cfg["model"],
"prompt": prompt,
"images": [image_b64],
"stream": False,
"options": {"temperature": 0.1},
},
timeout=120,
)
resp.raise_for_status()
return resp.json()
except requests.RequestException as e:
return {"error": str(e)}verify_with_ai function · python · L133-L224 (92 LOC)services/verify.py
def verify_with_ai(download_id: int, expected_title: str,
thumbnails: list[dict]) -> dict:
"""Use AI vision to verify that thumbnails match the expected title."""
if not thumbnails:
return {
"verdict": "failed",
"confidence": 0,
"reasoning": "No thumbnails to verify",
"per_thumbnail": [],
}
prompt = (
f'Look at this screenshot from a video file. '
f'Does this appear to be from "{expected_title}"? '
f'Consider the visual style, any visible text, logos, characters, or settings. '
f'Respond with ONLY valid JSON: '
f'{{"confidence": 0.0 to 1.0, "description": "brief description of what you see"}}'
)
per_thumbnail = []
total_confidence = 0
for thumb in thumbnails:
result = _call_ollama_vision(thumb["path"], prompt)
if "error" in result:
per_thumbnail.append({
"thumbnail": thumb["filename"],
run_verification function · python · L227-L318 (92 LOC)services/verify.py
def run_verification(download_id: int, socketio=None) -> dict:
"""Full verification pipeline for a completed download."""
dl = torrent_db.get_download(download_id)
if not dl:
return {"error": "Download not found"}
download_path = dl["download_path"]
expected_title = dl["expected_title"]
if not download_path or not os.path.exists(download_path):
torrent_db.update_download(download_id,
verification_status="failed",
verification_notes="Download path not found")
return {"error": "Download path not found"}
torrent_db.update_download(download_id, verification_status="verifying")
if socketio:
socketio.emit("torrent:verification_progress", {
"download_id": download_id,
"stage": "finding_video",
"current_thumbnail": 0,
"total_thumbnails": 0,
})
# Find the main video file
if os.path.isfile(downloRepobility · severity-and-effort ranking · https://repobility.com
approve_manually function · python · L321-L324 (4 LOC)services/verify.py
def approve_manually(download_id: int, approved: bool):
"""Manually approve or reject a download's verification."""
status = "approved" if approved else "rejected"
torrent_db.update_download(download_id, verification_status=status)register_events function · python · L7-L15 (9 LOC)sockets/events.py
def register_events(socketio: SocketIO):
@socketio.on("connect")
def on_connect():
pass
@socketio.on("transfer:cancel")
def on_cancel(data):
if transfer_manager is not None:
transfer_manager.cancel()register_torrent_events function · python · L7-L26 (20 LOC)sockets/torrent_events.py
def register_torrent_events(socketio: SocketIO):
@socketio.on("torrent:cancel")
def on_torrent_cancel(data):
if torrent_manager is not None:
download_id = data.get("download_id")
if download_id:
torrent_manager.cancel_download(download_id)
@socketio.on("torrent:verify")
def on_torrent_verify(data):
download_id = data.get("download_id")
if download_id:
import threading
from services import verify
thread = threading.Thread(
target=verify.run_verification,
args=(download_id, socketio),
daemon=True,
)
thread.start()register_tv_events function · python · L8-L36 (29 LOC)sockets/tv_events.py
def register_tv_events(socketio: SocketIO):
@socketio.on("tv:cancel_episode")
def on_cancel_episode(data):
if torrent_manager is None:
return
episode_id = data.get("episode_id")
if not episode_id:
return
from services import tv_db
ep = tv_db.get_episode(episode_id)
if ep and ep.get("download_id"):
torrent_manager.cancel_download(ep["download_id"])
tv_db.update_episode(episode_id, status="cancelled")
@socketio.on("tv:retry_episode")
def on_retry_episode(data):
if torrent_manager is None:
return
episode_id = data.get("episode_id")
if not episode_id:
return
import threading
from services import tv_search
thread = threading.Thread(
target=tv_search.retry_episode,
args=(episode_id, torrent_manager, socketio),
daemon=True,
)
thread.start()tvShowApp function · javascript · L1-L259 (259 LOC)static/js/tv_shows.js
function tvShowApp() {
return {
// Lookup
lookupQuery: '',
lookupResults: [],
lookupLoading: false,
// Selected show
selectedShow: null,
showSeasons: [],
// Season selection
seasonSelections: {},
// Tracked shows
trackedShows: [],
expandedShow: null,
expandedSeason: null,
// Scheduler
schedulerStatus: null,
// Socket.IO
socket: null,
// Toast
toast: { show: false, message: '', type: 'info' },
async init() {
this.socket = io();
this.socket.on('tv:season_search_start', (data) => {
this.showToast(`Searching ${data.show_title} S${String(data.season_number).padStart(2,'0')}...`, 'info');
});
this.socket.on('tv:season_search_progress', (data) => {
// Could update a progress indicator per season
});
this.socket.on('tv:s‹ prevpage 5 / 5