fix: remove invalid fastapi.middleware.base import (ModuleNotFoundError)
This commit is contained in:
172
jd-webgui/app.py
172
jd-webgui/app.py
@@ -19,7 +19,6 @@ from typing import Any, Dict, List, Optional, Tuple
|
|||||||
|
|
||||||
import paramiko
|
import paramiko
|
||||||
from fastapi import FastAPI, Form, Request
|
from fastapi import FastAPI, Form, Request
|
||||||
from fastapi.middleware.base import BaseHTTPMiddleware
|
|
||||||
from fastapi.responses import HTMLResponse, RedirectResponse
|
from fastapi.responses import HTMLResponse, RedirectResponse
|
||||||
from fastapi.staticfiles import StaticFiles
|
from fastapi.staticfiles import StaticFiles
|
||||||
from myjdapi import Myjdapi
|
from myjdapi import Myjdapi
|
||||||
@@ -55,9 +54,9 @@ MD5_DIR = os.environ.get("MD5_DIR", "/md5").rstrip("/")
|
|||||||
BASIC_AUTH_USER = os.environ.get("BASIC_AUTH_USER", "")
|
BASIC_AUTH_USER = os.environ.get("BASIC_AUTH_USER", "")
|
||||||
BASIC_AUTH_PASS = os.environ.get("BASIC_AUTH_PASS", "")
|
BASIC_AUTH_PASS = os.environ.get("BASIC_AUTH_PASS", "")
|
||||||
|
|
||||||
POLL_SECONDS = float(os.environ.get("POLL_SECONDS", "5"))
|
POLL_SECONDS = float(os.environ.get("POLL_SECONDS", "5"))
|
||||||
MIN_VIDEO_SIZE_MB = int(os.environ.get("MIN_VIDEO_SIZE_MB", "200"))
|
MIN_VIDEO_SIZE_MB = int(os.environ.get("MIN_VIDEO_SIZE_MB", "200"))
|
||||||
MIN_VIDEO_BYTES = MIN_VIDEO_SIZE_MB * 1024 * 1024
|
MIN_VIDEO_BYTES = MIN_VIDEO_SIZE_MB * 1024 * 1024
|
||||||
|
|
||||||
# JDownloader writes here inside container
|
# JDownloader writes here inside container
|
||||||
JD_OUTPUT_PATH = "/output"
|
JD_OUTPUT_PATH = "/output"
|
||||||
@@ -205,9 +204,9 @@ class Job:
|
|||||||
def ensure_env():
|
def ensure_env():
|
||||||
missing = []
|
missing = []
|
||||||
for k, v in [
|
for k, v in [
|
||||||
("MYJD_EMAIL", MYJD_EMAIL),
|
("MYJD_EMAIL", MYJD_EMAIL),
|
||||||
("MYJD_PASSWORD", MYJD_PASSWORD),
|
("MYJD_PASSWORD", MYJD_PASSWORD),
|
||||||
("JELLYFIN_USER", JELLYFIN_USER),
|
("JELLYFIN_USER", JELLYFIN_USER),
|
||||||
("JELLYFIN_SSH_KEY", JELLYFIN_SSH_KEY),
|
("JELLYFIN_SSH_KEY", JELLYFIN_SSH_KEY),
|
||||||
]:
|
]:
|
||||||
if not v:
|
if not v:
|
||||||
@@ -243,14 +242,12 @@ def get_device():
|
|||||||
|
|
||||||
wanted = (MYJD_DEVICE or "").strip()
|
wanted = (MYJD_DEVICE or "").strip()
|
||||||
|
|
||||||
# wait up to 30s for device to become ONLINE
|
|
||||||
deadline = time.time() + 30
|
deadline = time.time() + 30
|
||||||
last = None
|
last = None
|
||||||
while time.time() < deadline:
|
while time.time() < deadline:
|
||||||
devs = jd.list_devices() or []
|
devs = jd.list_devices() or []
|
||||||
last = devs
|
last = devs
|
||||||
|
|
||||||
# pick by name (or first)
|
|
||||||
def pick():
|
def pick():
|
||||||
if wanted:
|
if wanted:
|
||||||
for d in devs:
|
for d in devs:
|
||||||
@@ -358,7 +355,6 @@ def is_video_file(path: str) -> bool:
|
|||||||
DEMO_PATTERNS = {"big_buck_bunny", "bigbuckbunny", "big buck bunny", "bbb_sunflower"}
|
DEMO_PATTERNS = {"big_buck_bunny", "bigbuckbunny", "big buck bunny", "bbb_sunflower"}
|
||||||
|
|
||||||
def is_demo_link(name: str) -> bool:
|
def is_demo_link(name: str) -> bool:
|
||||||
"""Detect JDownloader demo/fallback videos (e.g. Big Buck Bunny)."""
|
|
||||||
lower = name.lower().replace("-", "_").replace(".", " ")
|
lower = name.lower().replace("-", "_").replace(".", " ")
|
||||||
return any(p in lower for p in DEMO_PATTERNS)
|
return any(p in lower for p in DEMO_PATTERNS)
|
||||||
|
|
||||||
@@ -401,22 +397,21 @@ def pick_library_target(library_choice: str, filename: str, package_name: str) -
|
|||||||
return JELLYFIN_MOVIES_DIR or JELLYFIN_DEST_DIR
|
return JELLYFIN_MOVIES_DIR or JELLYFIN_DEST_DIR
|
||||||
if library_choice == "series":
|
if library_choice == "series":
|
||||||
return JELLYFIN_SERIES_DIR or JELLYFIN_DEST_DIR
|
return JELLYFIN_SERIES_DIR or JELLYFIN_DEST_DIR
|
||||||
# auto
|
|
||||||
if SERIES_RE.search(filename) or SERIES_RE.search(package_name or ""):
|
if SERIES_RE.search(filename) or SERIES_RE.search(package_name or ""):
|
||||||
return JELLYFIN_SERIES_DIR or JELLYFIN_DEST_DIR
|
return JELLYFIN_SERIES_DIR or JELLYFIN_DEST_DIR
|
||||||
return JELLYFIN_MOVIES_DIR or JELLYFIN_DEST_DIR
|
return JELLYFIN_MOVIES_DIR or JELLYFIN_DEST_DIR
|
||||||
|
|
||||||
def build_remote_paths(job_library: str, package_name: str, local_file: str) -> Tuple[str, str]:
|
def build_remote_paths(job_library: str, package_name: str, local_file: str) -> Tuple[str, str]:
|
||||||
filename = os.path.basename(local_file)
|
filename = os.path.basename(local_file)
|
||||||
base_target = pick_library_target(job_library, filename, package_name)
|
base_target = pick_library_target(job_library, filename, package_name)
|
||||||
|
|
||||||
m = SERIES_RE.search(filename) or SERIES_RE.search(package_name or "")
|
m = SERIES_RE.search(filename) or SERIES_RE.search(package_name or "")
|
||||||
is_series = (job_library == "series") or (job_library == "auto" and m)
|
is_series = (job_library == "series") or (job_library == "auto" and m)
|
||||||
|
|
||||||
if is_series:
|
if is_series:
|
||||||
show_query = package_name or os.path.splitext(filename)[0]
|
show_query = package_name or os.path.splitext(filename)[0]
|
||||||
tv = tmdb_search_tv(show_query) if TMDB_API_KEY else None
|
tv = tmdb_search_tv(show_query) if TMDB_API_KEY else None
|
||||||
show_name = sanitize_name(tv["name"]) if tv and tv.get("name") else sanitize_name(show_query)
|
show_name = sanitize_name(tv["name"]) if tv and tv.get("name") else sanitize_name(show_query)
|
||||||
|
|
||||||
season = int(m.group(1)) if m else 1
|
season = int(m.group(1)) if m else 1
|
||||||
episode = int(m.group(2)) if m else 1
|
episode = int(m.group(2)) if m else 1
|
||||||
@@ -426,15 +421,15 @@ def build_remote_paths(job_library: str, package_name: str, local_file: str) ->
|
|||||||
else:
|
else:
|
||||||
remote_dir = base_target
|
remote_dir = base_target
|
||||||
|
|
||||||
ext = os.path.splitext(filename)[1]
|
ext = os.path.splitext(filename)[1]
|
||||||
remote_filename = f"{show_name} - S{season:02d}E{episode:02d}{ext}"
|
remote_filename = f"{show_name} - S{season:02d}E{episode:02d}{ext}"
|
||||||
return remote_dir, remote_filename
|
return remote_dir, remote_filename
|
||||||
|
|
||||||
movie_query = package_name or os.path.splitext(filename)[0]
|
movie_query = package_name or os.path.splitext(filename)[0]
|
||||||
mv = tmdb_search_movie(movie_query) if TMDB_API_KEY else None
|
mv = tmdb_search_movie(movie_query) if TMDB_API_KEY else None
|
||||||
title = mv.get("title") if mv else None
|
title = mv.get("title") if mv else None
|
||||||
date = mv.get("release_date") if mv else None
|
date = mv.get("release_date") if mv else None
|
||||||
year = date[:4] if isinstance(date, str) and len(date) >= 4 else None
|
year = date[:4] if isinstance(date, str) and len(date) >= 4 else None
|
||||||
|
|
||||||
title_safe = sanitize_name(title) if title else sanitize_name(movie_query)
|
title_safe = sanitize_name(title) if title else sanitize_name(movie_query)
|
||||||
year_safe = year if year else ""
|
year_safe = year if year else ""
|
||||||
@@ -445,7 +440,7 @@ def build_remote_paths(job_library: str, package_name: str, local_file: str) ->
|
|||||||
else:
|
else:
|
||||||
remote_dir = base_target
|
remote_dir = base_target
|
||||||
|
|
||||||
ext = os.path.splitext(filename)[1]
|
ext = os.path.splitext(filename)[1]
|
||||||
remote_filename = f"{title_safe} ({year_safe}){ext}".strip() if year_safe else f"{title_safe}{ext}"
|
remote_filename = f"{title_safe} ({year_safe}){ext}".strip() if year_safe else f"{title_safe}{ext}"
|
||||||
return remote_dir, remote_filename
|
return remote_dir, remote_filename
|
||||||
|
|
||||||
@@ -490,28 +485,18 @@ def call_raw_jd_api(dev, endpoints: List[str], payloads: List[Dict[str, Any]]) -
|
|||||||
def cancel_job(dev, jobid: str) -> str:
|
def cancel_job(dev, jobid: str) -> str:
|
||||||
links, pkg_map = query_links_and_packages(dev, jobid)
|
links, pkg_map = query_links_and_packages(dev, jobid)
|
||||||
link_ids = [l.get("uuid") for l in links if l.get("uuid") is not None]
|
link_ids = [l.get("uuid") for l in links if l.get("uuid") is not None]
|
||||||
pkg_ids = [p for p in pkg_map]
|
pkg_ids = list(pkg_map)
|
||||||
|
|
||||||
msgs = []
|
|
||||||
for ep, pl in [
|
for ep, pl in [
|
||||||
("downloads/remove_links", {"linkIds": link_ids, "packageIds": []}),
|
("downloads/remove_links", {"linkIds": link_ids, "packageIds": []}),
|
||||||
("downloadcontroller/remove_links", {"linkIds": link_ids, "packageIds": []}),
|
("downloadcontroller/remove_links", {"linkIds": link_ids, "packageIds": []}),
|
||||||
]:
|
("downloads/remove_links", {"linkIds": [], "packageIds": pkg_ids}),
|
||||||
try:
|
|
||||||
call_raw_jd_api(dev, [ep], [pl])
|
|
||||||
except Exception:
|
|
||||||
pass
|
|
||||||
|
|
||||||
for ep, pl in [
|
|
||||||
("downloads/remove_links", {"linkIds": [], "packageIds": pkg_ids}),
|
|
||||||
("downloadcontroller/remove_links", {"linkIds": [], "packageIds": pkg_ids}),
|
("downloadcontroller/remove_links", {"linkIds": [], "packageIds": pkg_ids}),
|
||||||
]:
|
]:
|
||||||
try:
|
try:
|
||||||
call_raw_jd_api(dev, [ep], [pl])
|
call_raw_jd_api(dev, [ep], [pl])
|
||||||
except Exception:
|
except Exception:
|
||||||
pass
|
pass
|
||||||
|
return "Download abgebrochen."
|
||||||
return " ".join(msgs) if msgs else "Download abgebrochen."
|
|
||||||
|
|
||||||
def try_remove_from_jd(dev, links: List[Dict[str, Any]], pkg_map: Dict[Any, Dict[str, Any]]) -> str:
|
def try_remove_from_jd(dev, links: List[Dict[str, Any]], pkg_map: Dict[Any, Dict[str, Any]]) -> str:
|
||||||
link_ids = [l.get("uuid") for l in links if l.get("uuid") is not None]
|
link_ids = [l.get("uuid") for l in links if l.get("uuid") is not None]
|
||||||
@@ -536,12 +521,12 @@ def try_remove_from_jd(dev, links: List[Dict[str, Any]], pkg_map: Dict[Any, Dict
|
|||||||
# ============================================================
|
# ============================================================
|
||||||
def query_links_and_packages(dev, jobid: str) -> Tuple[List[Dict[str, Any]], Dict[Any, Dict[str, Any]]]:
|
def query_links_and_packages(dev, jobid: str) -> Tuple[List[Dict[str, Any]], Dict[Any, Dict[str, Any]]]:
|
||||||
links = dev.downloads.query_links([{
|
links = dev.downloads.query_links([{
|
||||||
"jobUUIDs": [int(jobid)] if jobid.isdigit() else [jobid],
|
"jobUUIDs": [int(jobid)] if jobid.isdigit() else [jobid],
|
||||||
"maxResults": -1,
|
"maxResults": -1,
|
||||||
"startAt": 0,
|
"startAt": 0,
|
||||||
"name": True,
|
"name": True,
|
||||||
"finished": True,
|
"finished": True,
|
||||||
"running": True,
|
"running": True,
|
||||||
"bytesLoaded": True,
|
"bytesLoaded": True,
|
||||||
"bytesTotal": True,
|
"bytesTotal": True,
|
||||||
"bytes": True,
|
"bytes": True,
|
||||||
@@ -554,7 +539,7 @@ def query_links_and_packages(dev, jobid: str) -> Tuple[List[Dict[str, Any]], Dic
|
|||||||
pkg_ids = sorted({l.get("packageUUID") for l in links if l.get("packageUUID") is not None})
|
pkg_ids = sorted({l.get("packageUUID") for l in links if l.get("packageUUID") is not None})
|
||||||
pkgs = dev.downloads.query_packages([{
|
pkgs = dev.downloads.query_packages([{
|
||||||
"packageUUIDs": pkg_ids,
|
"packageUUIDs": pkg_ids,
|
||||||
"maxResults": -1,
|
"maxResults": -1,
|
||||||
"startAt": 0,
|
"startAt": 0,
|
||||||
"saveTo": True,
|
"saveTo": True,
|
||||||
"uuid": True,
|
"uuid": True,
|
||||||
@@ -618,12 +603,12 @@ def _filter_linkgrabber(dev, jobid: str) -> Tuple[int, int]:
|
|||||||
links = []
|
links = []
|
||||||
try:
|
try:
|
||||||
links = dev.linkgrabber.query_links([{
|
links = dev.linkgrabber.query_links([{
|
||||||
"jobUUIDs": [int(jobid)] if str(jobid).isdigit() else [jobid],
|
"jobUUIDs": [int(jobid)] if str(jobid).isdigit() else [jobid],
|
||||||
"maxResults": -1,
|
"maxResults": -1,
|
||||||
"startAt": 0,
|
"startAt": 0,
|
||||||
"name": True,
|
"name": True,
|
||||||
"size": True,
|
"size": True,
|
||||||
"uuid": True,
|
"uuid": True,
|
||||||
"packageUUID": True,
|
"packageUUID": True,
|
||||||
}]) or []
|
}]) or []
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -682,8 +667,8 @@ def worker(jobid: str):
|
|||||||
job = jobs.get(jobid)
|
job = jobs.get(jobid)
|
||||||
if job:
|
if job:
|
||||||
if accepted == 0:
|
if accepted == 0:
|
||||||
job.status = "failed"
|
job.status = "failed"
|
||||||
job.message = (
|
job.message = (
|
||||||
f"Keine Video-Dateien \u2265 {MIN_VIDEO_SIZE_MB} MB gefunden "
|
f"Keine Video-Dateien \u2265 {MIN_VIDEO_SIZE_MB} MB gefunden "
|
||||||
f"({rejected} Link(s) verworfen)."
|
f"({rejected} Link(s) verworfen)."
|
||||||
)
|
)
|
||||||
@@ -699,8 +684,8 @@ def worker(jobid: str):
|
|||||||
if job.cancel_requested:
|
if job.cancel_requested:
|
||||||
cancel_msg = cancel_job(dev, jobid)
|
cancel_msg = cancel_job(dev, jobid)
|
||||||
with lock:
|
with lock:
|
||||||
job.status = "canceled"
|
job.status = "canceled"
|
||||||
job.message = cancel_msg or "Download abgebrochen und Dateien entfernt."
|
job.message = cancel_msg or "Download abgebrochen und Dateien entfernt."
|
||||||
job.progress = 0.0
|
job.progress = 0.0
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -708,8 +693,8 @@ def worker(jobid: str):
|
|||||||
|
|
||||||
if not links:
|
if not links:
|
||||||
with lock:
|
with lock:
|
||||||
job.status = "collecting"
|
job.status = "collecting"
|
||||||
job.message = "Warte auf Link-Crawler\u2026"
|
job.message = "Warte auf Link-Crawler\u2026"
|
||||||
job.progress = 0.0
|
job.progress = 0.0
|
||||||
time.sleep(POLL_SECONDS)
|
time.sleep(POLL_SECONDS)
|
||||||
continue
|
continue
|
||||||
@@ -718,9 +703,9 @@ def worker(jobid: str):
|
|||||||
if all_demo and not is_demo_link(job.url):
|
if all_demo and not is_demo_link(job.url):
|
||||||
cancel_msg = cancel_job(dev, jobid)
|
cancel_msg = cancel_job(dev, jobid)
|
||||||
with lock:
|
with lock:
|
||||||
job.status = "failed"
|
job.status = "failed"
|
||||||
base_msg = "JDownloader lieferte das Demo-Video Big Buck Bunny statt des gew\u00fcnschten Links."
|
base_msg = "JDownloader lieferte das Demo-Video Big Buck Bunny statt des gew\u00fcnschten Links."
|
||||||
job.message = f"{base_msg} {cancel_msg}" if cancel_msg else base_msg
|
job.message = f"{base_msg} {cancel_msg}" if cancel_msg else base_msg
|
||||||
job.progress = 0.0
|
job.progress = 0.0
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -728,9 +713,9 @@ def worker(jobid: str):
|
|||||||
if not all_finished:
|
if not all_finished:
|
||||||
progress = calculate_progress(links)
|
progress = calculate_progress(links)
|
||||||
with lock:
|
with lock:
|
||||||
job.status = "downloading"
|
job.status = "downloading"
|
||||||
done = sum(1 for l in links if l.get("finished"))
|
done = sum(1 for l in links if l.get("finished"))
|
||||||
job.message = f"Download l\u00e4uft\u2026 ({done}/{len(links)} fertig)"
|
job.message = f"Download l\u00e4uft\u2026 ({done}/{len(links)} fertig)"
|
||||||
job.progress = progress
|
job.progress = progress
|
||||||
time.sleep(POLL_SECONDS)
|
time.sleep(POLL_SECONDS)
|
||||||
continue
|
continue
|
||||||
@@ -740,16 +725,16 @@ def worker(jobid: str):
|
|||||||
|
|
||||||
if not video_files:
|
if not video_files:
|
||||||
with lock:
|
with lock:
|
||||||
job.status = "failed"
|
job.status = "failed"
|
||||||
job.message = "Keine Video-Datei gefunden (Whitelist)."
|
job.message = "Keine Video-Datei gefunden (Whitelist)."
|
||||||
job.progress = 0.0
|
job.progress = 0.0
|
||||||
return
|
return
|
||||||
|
|
||||||
valid_videos = [p for p in video_files if ffprobe_ok(p)]
|
valid_videos = [p for p in video_files if ffprobe_ok(p)]
|
||||||
if not valid_videos:
|
if not valid_videos:
|
||||||
with lock:
|
with lock:
|
||||||
job.status = "failed"
|
job.status = "failed"
|
||||||
job.message = "ffprobe: keine g\u00fcltige Video-Datei."
|
job.message = "ffprobe: keine g\u00fcltige Video-Datei."
|
||||||
job.progress = 0.0
|
job.progress = 0.0
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -769,8 +754,8 @@ def worker(jobid: str):
|
|||||||
valid_videos = renamed
|
valid_videos = renamed
|
||||||
|
|
||||||
with lock:
|
with lock:
|
||||||
job.status = "upload"
|
job.status = "upload"
|
||||||
job.message = f"Download fertig. MD5/Upload/Verify f\u00fcr {len(valid_videos)} Datei(en)\u2026"
|
job.message = f"Download fertig. MD5/Upload/Verify f\u00fcr {len(valid_videos)} Datei(en)\u2026"
|
||||||
job.progress = 100.0
|
job.progress = 100.0
|
||||||
|
|
||||||
ssh = ssh_connect()
|
ssh = ssh_connect()
|
||||||
@@ -790,7 +775,6 @@ def worker(jobid: str):
|
|||||||
if remote_md5.lower() != md5_hex.lower():
|
if remote_md5.lower() != md5_hex.lower():
|
||||||
raise RuntimeError(f"MD5 mismatch for {os.path.basename(f)}: local={md5_hex} remote={remote_md5}")
|
raise RuntimeError(f"MD5 mismatch for {os.path.basename(f)}: local={md5_hex} remote={remote_md5}")
|
||||||
|
|
||||||
# Cleanup local
|
|
||||||
try:
|
try:
|
||||||
os.remove(f)
|
os.remove(f)
|
||||||
except Exception:
|
except Exception:
|
||||||
@@ -809,8 +793,8 @@ def worker(jobid: str):
|
|||||||
jellyfin_refresh_library()
|
jellyfin_refresh_library()
|
||||||
|
|
||||||
with lock:
|
with lock:
|
||||||
job.status = "finished"
|
job.status = "finished"
|
||||||
job.message = "Upload + MD5 OK. " + (jd_cleanup_msg or "JDownloader: Paket/Links entfernt.")
|
job.message = "Upload + MD5 OK. " + (jd_cleanup_msg or "JDownloader: Paket/Links entfernt.")
|
||||||
job.progress = 100.0
|
job.progress = 100.0
|
||||||
return
|
return
|
||||||
|
|
||||||
@@ -818,8 +802,8 @@ def worker(jobid: str):
|
|||||||
with lock:
|
with lock:
|
||||||
job = jobs.get(jobid)
|
job = jobs.get(jobid)
|
||||||
if job:
|
if job:
|
||||||
job.status = "failed"
|
job.status = "failed"
|
||||||
job.message = str(e)
|
job.message = str(e)
|
||||||
job.progress = 0.0
|
job.progress = 0.0
|
||||||
|
|
||||||
# ============================================================
|
# ============================================================
|
||||||
@@ -848,13 +832,13 @@ def render_jobs_page(job_list: list, log_lines: list) -> str:
|
|||||||
rows = ""
|
rows = ""
|
||||||
for j in reversed(job_list):
|
for j in reversed(job_list):
|
||||||
bar_color = {
|
bar_color = {
|
||||||
"finished": "#4caf50",
|
"finished": "#4caf50",
|
||||||
"failed": "#f44336",
|
"failed": "#f44336",
|
||||||
"canceled": "#9e9e9e",
|
"canceled": "#9e9e9e",
|
||||||
"uploading": "#2196f3",
|
"uploading": "#2196f3",
|
||||||
"upload": "#2196f3",
|
"upload": "#2196f3",
|
||||||
"downloading":"#ff9800",
|
"downloading": "#ff9800",
|
||||||
"collecting": "#9c27b0",
|
"collecting": "#9c27b0",
|
||||||
}.get(j.status, "#607d8b")
|
}.get(j.status, "#607d8b")
|
||||||
rows += (
|
rows += (
|
||||||
f"<tr>"
|
f"<tr>"
|
||||||
@@ -913,7 +897,6 @@ def render_jobs_page(job_list: list, log_lines: list) -> str:
|
|||||||
</html>"""
|
</html>"""
|
||||||
|
|
||||||
def render_page(message: str = "", error: str = "") -> str:
|
def render_page(message: str = "", error: str = "") -> str:
|
||||||
video_list = ", ".join(sorted(VIDEO_EXTS))
|
|
||||||
return f"""<!DOCTYPE html>
|
return f"""<!DOCTYPE html>
|
||||||
<html lang='de'>
|
<html lang='de'>
|
||||||
<head>
|
<head>
|
||||||
@@ -958,11 +941,11 @@ def render_page(message: str = "", error: str = "") -> str:
|
|||||||
</html>"""
|
</html>"""
|
||||||
|
|
||||||
def render_proxies_page(
|
def render_proxies_page(
|
||||||
socks5_in: str = "",
|
socks5_in: str = "",
|
||||||
socks4_in: str = "",
|
socks4_in: str = "",
|
||||||
out_text: str = "",
|
out_text: str = "",
|
||||||
export_path: str = "",
|
export_path: str = "",
|
||||||
error: str = "",
|
error: str = "",
|
||||||
) -> str:
|
) -> str:
|
||||||
return f"""<!DOCTYPE html>
|
return f"""<!DOCTYPE html>
|
||||||
<html lang='de'>
|
<html lang='de'>
|
||||||
@@ -1026,13 +1009,13 @@ def build_jdproxies_payload(text: str) -> Dict[str, Any]:
|
|||||||
"https": "HTTPS",
|
"https": "HTTPS",
|
||||||
}
|
}
|
||||||
for line in text.splitlines():
|
for line in text.splitlines():
|
||||||
line = line.strip()
|
line = line.strip()
|
||||||
if not line:
|
if not line:
|
||||||
continue
|
continue
|
||||||
parsed = urllib.parse.urlparse(line)
|
parsed = urllib.parse.urlparse(line)
|
||||||
proto = (parsed.scheme or "").lower()
|
proto = (parsed.scheme or "").lower()
|
||||||
host = parsed.hostname or ""
|
host = parsed.hostname or ""
|
||||||
port = parsed.port or 1080
|
port = parsed.port or 1080
|
||||||
if not host:
|
if not host:
|
||||||
continue
|
continue
|
||||||
jd_type = type_map.get(proto, "SOCKS5")
|
jd_type = type_map.get(proto, "SOCKS5")
|
||||||
@@ -1131,7 +1114,6 @@ def proxies_get():
|
|||||||
socks4_in = fetch_proxy_list(
|
socks4_in = fetch_proxy_list(
|
||||||
"https://api.proxyscrape.com/v4/free-proxy-list/get?request=displayproxies&protocol=socks4&timeout=10000&country=all&ssl=yes&anonymity=elite&skip=0&limit=2000"
|
"https://api.proxyscrape.com/v4/free-proxy-list/get?request=displayproxies&protocol=socks4&timeout=10000&country=all&ssl=yes&anonymity=elite&skip=0&limit=2000"
|
||||||
)
|
)
|
||||||
|
|
||||||
s5 = format_proxy_lines(socks5_in, "socks5")
|
s5 = format_proxy_lines(socks5_in, "socks5")
|
||||||
s4 = format_proxy_lines(socks4_in, "socks4")
|
s4 = format_proxy_lines(socks4_in, "socks4")
|
||||||
combined = "\n".join([x for x in [s5, s4] if x.strip()])
|
combined = "\n".join([x for x in [s5, s4] if x.strip()])
|
||||||
@@ -1150,9 +1132,8 @@ def proxies_post(
|
|||||||
socks4_in: str = Form(""),
|
socks4_in: str = Form(""),
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
s5 = format_proxy_lines(socks5_in, "socks5")
|
s5 = format_proxy_lines(socks5_in, "socks5")
|
||||||
s4 = format_proxy_lines(socks4_in, "socks4")
|
s4 = format_proxy_lines(socks4_in, "socks4")
|
||||||
|
|
||||||
combined = "\n".join([x for x in [s5, s4] if x.strip()])
|
combined = "\n".join([x for x in [s5, s4] if x.strip()])
|
||||||
return HTMLResponse(render_proxies_page(
|
return HTMLResponse(render_proxies_page(
|
||||||
socks5_in=socks5_in,
|
socks5_in=socks5_in,
|
||||||
@@ -1175,16 +1156,13 @@ def proxies_save(
|
|||||||
socks4_in: str = Form(""),
|
socks4_in: str = Form(""),
|
||||||
):
|
):
|
||||||
try:
|
try:
|
||||||
s5 = format_proxy_lines(socks5_in, "socks5")
|
s5 = format_proxy_lines(socks5_in, "socks5")
|
||||||
s4 = format_proxy_lines(socks4_in, "socks4")
|
s4 = format_proxy_lines(socks4_in, "socks4")
|
||||||
|
|
||||||
combined = "\n".join([x for x in [s5, s4] if x.strip()])
|
combined = "\n".join([x for x in [s5, s4] if x.strip()])
|
||||||
payload = build_jdproxies_payload(combined)
|
payload = build_jdproxies_payload(combined)
|
||||||
|
|
||||||
os.makedirs(os.path.dirname(PROXY_EXPORT_PATH), exist_ok=True)
|
os.makedirs(os.path.dirname(PROXY_EXPORT_PATH), exist_ok=True)
|
||||||
with open(PROXY_EXPORT_PATH, "w", encoding="utf-8") as fh:
|
with open(PROXY_EXPORT_PATH, "w", encoding="utf-8") as fh:
|
||||||
json.dump(payload, fh, indent=2, ensure_ascii=False)
|
json.dump(payload, fh, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
return HTMLResponse(render_proxies_page(
|
return HTMLResponse(render_proxies_page(
|
||||||
socks5_in=socks5_in,
|
socks5_in=socks5_in,
|
||||||
socks4_in=socks4_in,
|
socks4_in=socks4_in,
|
||||||
|
|||||||
Reference in New Issue
Block a user