mirror of
https://github.com/got-feedBack/feedBack.git
synced 2026-08-10 18:59:56 +00:00
rename: slopsmith → feedBack, byron → got-feedBack (#537)
* Update GitHub repo references from feedback* to feedBack* * rename: slopsmith -> feedBack, byron -> got-feedBack Renames across the entire codebase: - slopsmith/Slopsmith/SLOPSMITH/SlopSmith -> feedBack/FeedBack/FEEDBACK/FeedBack - byron/Byron/Byrongamatos -> got-feedBack/got-feedBack/got-feedBack - /home/byron/ -> /opt/got-feedBack/ - byron@ougsoft.com -> hi@got-feedBack.org - github.com/byrongamatos/ -> github.com/got-feedback/ - com.byron. -> com.got-feedback. - SLOPSMITH_ env vars -> FEEDBACK_ with backward-compat fallback - Protocol/storage strings migrated with read-old/write-new pattern - window.slopsmith JS API -> window.feedBack (canonical) + backward-compat alias Refs: #rename-slopsmith * rename: complete regen against current main + fix backward-compat alias Regenerated the slopsmith->feedBack / byron->got-feedBack rename on top of current main (3 commits had landed since the branch: #572/#554/#574), resolving the four content conflicts in favour of main's newer content (autoplay/auto-exit, accuracy-badge, Virtuoso re-home, feedpak badge). Completion fixes on top of the mechanical rename: - Re-apply rename to post-branch content the original rename never saw: window.slopsmith(.Tour) consumers in lessons.js / notifications.js / onboarding-tour.js, and the matching JS + python tests (autoplay_exit, progression_*, test_feedpak_extension FEEDBACK_* env vars). The test env vars now match server.py (which reads FEEDBACK_SYNC_STARTUP / FEEDBACK_SKIP_STARTUP_TASKS), so the sync-startup test exercises the real path again. - Restore the window.slopsmith backward-compat alias dropped during conflict resolution, and move the bus aliases to AFTER the _feedBackExisting merge block so they reference the fully-assembled object (also fixes the loop_api.test.js API-surface regex, which the original PR latently broke). - Drop the stray empty data/web_library.db (runtime DB lives in CONFIG_DIR) and gitignore it. - Fix stale tone-source test: feed[dB]ack -> fee[dB]ack to match shipped source labels. Verified locally (org CI billing-blocked): JS 819/819 pass; pytest 1669 passed / 1683 collected with 0 import errors; zero residual slopsmith/byron except the two intentional window.slopsmith aliases. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * rename: implement advertised backward-compat + prune dead community plugins Address gaps where PR #537's "Backward compatibility" section was advertised but not implemented, and clean up the community plugin list. Env vars (FEEDBACK_* canonical, legacy SLOPSMITH_* honoured): - New lib/env_compat.py (getenv_compat / env_flag_compat) + tests. server.py (_env_flag + all FEEDBACK_* reads), diagnostics_hardware, gp2midi and tailwind_rebuild now resolve the legacy alias, so existing SLOPSMITH_UI / SLOPSMITH_PLUGINS_DIR / etc. deployments keep working. - Fix the rename collapsing plugins/__init__.py and minigames/routes.py from `FEEDBACK_PLUGINS_DIR or SLOPSMITH_PLUGINS_DIR` into a redundant `FEEDBACK_ or FEEDBACK_` (the fallback was silently lost). Storage (app.js update-channel): - Read feedBack-update-channel, fall back to legacy slopsmith-update-channel, and clear the legacy key on write — so a user's update-channel preference survives the rename instead of resetting to "stable". Community plugin list (README): the rename rewrote third-party repo URLs we don't own. Probed every one; their owners never renamed, so: - Restore the 13 live community plugins to their real slopsmith-* names. - Prune 6 that are 404 to the public (topkoa splitscreen/stems, OmikronApex tuner, Jafz2001 nam-rig-builder, DeathlySin song-preview, Erikcb91 shuffle). - Fix a pre-existing Guitar Theory clone-command typo (nam-tone -> guitar-theory). Verified: env_compat 7/7, JS 819/819, pytest 1690 collected / 0 import errors, rename-sensitive + startup suites green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: byrongamatos <xasiklas@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.8
byrongamatos
parent
a8ad02739a
commit
af2949677a
@@ -1,4 +1,4 @@
|
||||
"""Slopsmith — FastAPI backend serving highway viewer + library."""
|
||||
"""FeedBack — FastAPI backend serving highway viewer + library."""
|
||||
|
||||
import asyncio
|
||||
import bisect
|
||||
@@ -15,9 +15,10 @@ from pathlib import Path
|
||||
from typing import Any, ClassVar
|
||||
|
||||
from logging_setup import configure_logging
|
||||
from env_compat import getenv_compat
|
||||
configure_logging()
|
||||
|
||||
log = logging.getLogger("slopsmith.server")
|
||||
log = logging.getLogger("feedBack.server")
|
||||
|
||||
from fastapi import Body, FastAPI, WebSocket, WebSocketDisconnect, UploadFile, File, HTTPException, Query
|
||||
from fastapi.concurrency import run_in_threadpool
|
||||
@@ -67,7 +68,7 @@ import xml.etree.ElementTree as ET
|
||||
import structlog
|
||||
from fastapi import Request
|
||||
|
||||
app = FastAPI(title="Slopsmith")
|
||||
app = FastAPI(title="FeedBack")
|
||||
|
||||
# Plugins that maintain session stores can register a cleanup callback here.
|
||||
# The demo-mode janitor calls every registered hook once per hour so stale
|
||||
@@ -230,17 +231,17 @@ _DEMO_BLOCKED: list[tuple[str, re.Pattern]] = [
|
||||
|
||||
@app.middleware("http")
|
||||
async def _demo_mode_guard(request: Request, call_next):
|
||||
if os.environ.get("SLOPSMITH_DEMO_MODE") == "1":
|
||||
if getenv_compat("FEEDBACK_DEMO_MODE") or getenv_compat("FEEDBACK_DEMO_MODE") == "1":
|
||||
path = request.url.path
|
||||
for method, pattern in _DEMO_BLOCKED:
|
||||
if request.method == method and pattern.match(path):
|
||||
return JSONResponse({"error": "demo mode: read-only"}, status_code=403)
|
||||
response = await call_next(request)
|
||||
if request.method == "GET" and path == "/" and "slopsmith_demo_session" not in request.cookies:
|
||||
if request.method == "GET" and path == "/" and "feedBack_demo_session" not in request.cookies:
|
||||
forwarded_proto = (request.headers.get("x-forwarded-proto") or "").split(",")[0].strip()
|
||||
is_secure = request.url.scheme == "https" or forwarded_proto.lower() == "https"
|
||||
response.set_cookie(
|
||||
"slopsmith_demo_session", str(uuid.uuid4()),
|
||||
"feedBack_demo_session", str(uuid.uuid4()),
|
||||
max_age=86400, httponly=True, samesite="lax",
|
||||
secure=is_secure,
|
||||
)
|
||||
@@ -275,11 +276,11 @@ SLOPPAK_CACHE_DIR = CONFIG_DIR / "sloppak_cache"
|
||||
|
||||
|
||||
def _env_flag(name: str) -> bool:
|
||||
"""Parse a conventional boolean env flag."""
|
||||
return os.environ.get(name, "").strip().lower() in {"1", "true", "yes", "on"}
|
||||
"""Parse a conventional boolean env flag (honours legacy SLOPSMITH_* alias)."""
|
||||
return (getenv_compat(name, "") or "").strip().lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
|
||||
# Canonical Tuning-filter grouping key (slopsmith#867). tuning_name collapses
|
||||
# Canonical Tuning-filter grouping key (feedBack#867). tuning_name collapses
|
||||
# every non-standard tuning to "Custom Tuning"; for those rows we key on the
|
||||
# raw offsets so distinct customs stay distinct, while named tunings keep
|
||||
# grouping by name (stable across the offsets-column migration). Used by both
|
||||
@@ -391,14 +392,14 @@ class MetadataDB:
|
||||
for ddl in (
|
||||
"ALTER TABLE songs ADD COLUMN format TEXT DEFAULT 'archive'",
|
||||
"ALTER TABLE songs ADD COLUMN stem_count INTEGER DEFAULT 0",
|
||||
# slopsmith#129: per-stem filter needs the id list, not just count.
|
||||
# feedBack#129: per-stem filter needs the id list, not just count.
|
||||
"ALTER TABLE songs ADD COLUMN stem_ids TEXT DEFAULT '[]'",
|
||||
# slopsmith#69 + #22: denormalized canonical tuning name + numeric
|
||||
# feedBack#69 + #22: denormalized canonical tuning name + numeric
|
||||
# sort key (sum of offsets). The existing `tuning` text column
|
||||
# stays — these are caches, repopulated on rescan.
|
||||
"ALTER TABLE songs ADD COLUMN tuning_name TEXT DEFAULT ''",
|
||||
"ALTER TABLE songs ADD COLUMN tuning_sort_key INTEGER DEFAULT 0",
|
||||
# slopsmith#867: raw per-string offsets (space-joined ints) so the
|
||||
# feedBack#867: raw per-string offsets (space-joined ints) so the
|
||||
# v3 client can render target notes and the Tuning filter can keep
|
||||
# distinct custom tunings distinct (tuning_name collapses them all
|
||||
# to "Custom Tuning"). Cache; repopulated on rescan.
|
||||
@@ -1582,7 +1583,7 @@ class MetadataDB:
|
||||
# Manifest-allowed filter values. Whitelisted before binding so a
|
||||
# malformed query string can't push arbitrary text through to SQL —
|
||||
# parameters are bound, but capping the input space is still cheap
|
||||
# defense-in-depth (see slopsmith#129).
|
||||
# defense-in-depth (see feedBack#129).
|
||||
_ALLOWED_ARRANGEMENT_NAMES = {"Lead", "Rhythm", "Bass", "Combo"}
|
||||
# Per-smart-type list of (sql_op, sql_param) pairs appended to the SQL
|
||||
# name-fallback branch (key-absent smart_name). Covers legacy raw names
|
||||
@@ -1622,7 +1623,7 @@ class MetadataDB:
|
||||
naming_mode: str = "legacy") -> tuple[str, list]:
|
||||
"""Shared WHERE-clause builder for query_page / query_artists /
|
||||
query_stats. Returns (where_sql, params). Leading 'WHERE' is
|
||||
included so callers paste it directly. See slopsmith#129/#69.
|
||||
included so callers paste it directly. See feedBack#129/#69.
|
||||
"""
|
||||
where = "WHERE title != ''"
|
||||
params: list = []
|
||||
@@ -1804,7 +1805,7 @@ class MetadataDB:
|
||||
"title": "title COLLATE NOCASE", "title-desc": "title COLLATE NOCASE DESC",
|
||||
"recent": "mtime DESC",
|
||||
# Tuning sort uses musical distance from E Standard
|
||||
# (slopsmith#22 — was alphabetical). `tuning_sort_key` is
|
||||
# (feedBack#22 — was alphabetical). `tuning_sort_key` is
|
||||
# the sum of per-string offsets, so |sort_key| is the
|
||||
# magnitude of the down/up-tune. ABS ascending puts E
|
||||
# Standard (0) first, then ±2 (Drop D, F Standard), then
|
||||
@@ -1832,7 +1833,7 @@ class MetadataDB:
|
||||
"COALESCE(tuning_sort_key, 0) ASC, "
|
||||
"COALESCE(tuning_name, '') COLLATE NOCASE"
|
||||
),
|
||||
# Year sort (slopsmith#128). Empty-year rows pushed to the
|
||||
# Year sort (feedBack#128). Empty-year rows pushed to the
|
||||
# bottom for both directions; otherwise CAST so '2010' >
|
||||
# '2005' rather than alphabetic.
|
||||
"year": "(year = '') ASC, CAST(year AS INTEGER) ASC",
|
||||
@@ -2950,7 +2951,7 @@ _scan_status = dict(_SCAN_STATUS_INIT)
|
||||
_STARTUP_STATUS_INIT = {
|
||||
"running": True,
|
||||
"phase": "booting",
|
||||
"message": "Starting Slopsmith server...",
|
||||
"message": "Starting FeedBack server...",
|
||||
"current_plugin": "",
|
||||
"loaded": 0,
|
||||
"total": 0,
|
||||
@@ -3034,13 +3035,13 @@ def _make_scan_executor():
|
||||
mp_ctx = multiprocessing.get_context("spawn")
|
||||
# Default to one worker per core so CPU-bound metadata parsing uses the
|
||||
# whole machine (the point of moving to processes).
|
||||
# SLOPSMITH_MAX_SCAN_WORKERS (set by the Desktop launcher to cap memory
|
||||
# FEEDBACK_MAX_SCAN_WORKERS (set by the Desktop launcher to cap memory
|
||||
# usage on low-RAM machines — e.g. 8 GB M2 MacBook Air) takes priority;
|
||||
# SCAN_MAX_WORKERS is a legacy override for Docker/bare installs.
|
||||
# A malformed override falls back to the core count rather than crashing.
|
||||
try:
|
||||
max_workers = int(
|
||||
os.environ.get("SLOPSMITH_MAX_SCAN_WORKERS")
|
||||
getenv_compat("FEEDBACK_MAX_SCAN_WORKERS")
|
||||
or os.environ.get("SCAN_MAX_WORKERS")
|
||||
or (os.cpu_count() or 1)
|
||||
)
|
||||
@@ -3060,14 +3061,14 @@ def _make_scan_executor():
|
||||
_BUILTIN_DIAGNOSTIC_SUBDIR = "diagnostics-builtin"
|
||||
_BUILTIN_DIAGNOSTIC_SOURCES: list[tuple[str, str]] = [
|
||||
(
|
||||
"slopsmith-diagnostic-basic-guitar.sloppak",
|
||||
"docs/diagnostics/slopsmith-diagnostic-basic-guitar.sloppak",
|
||||
"feedBack-diagnostic-basic-guitar.sloppak",
|
||||
"docs/diagnostics/feedBack-diagnostic-basic-guitar.sloppak",
|
||||
),
|
||||
]
|
||||
|
||||
|
||||
def _slopsmith_server_root() -> Path:
|
||||
"""Directory containing server.py (repo root in dev; resources/slopsmith when bundled)."""
|
||||
def _feedBack_server_root() -> Path:
|
||||
"""Directory containing server.py (repo root in dev; resources/feedBack when bundled)."""
|
||||
return Path(__file__).resolve().parent
|
||||
|
||||
|
||||
@@ -3079,7 +3080,7 @@ def _builtin_diagnostic_filename() -> str:
|
||||
|
||||
# Progression content (spec 010): bundled JSON under data/progression/ (paths,
|
||||
# quest pools, shop catalog). Loaded lazily-once; invalid entries are logged
|
||||
# warnings, never fatal. SLOPSMITH_PROGRESSION_DATA overrides the root (tests).
|
||||
# warnings, never fatal. FEEDBACK_PROGRESSION_DATA overrides the root (tests).
|
||||
_progression_content: dict | None = None
|
||||
_progression_content_lock = threading.Lock()
|
||||
|
||||
@@ -3090,8 +3091,8 @@ def _get_progression_content() -> dict:
|
||||
with _progression_content_lock:
|
||||
if _progression_content is None:
|
||||
import progression as progression_mod
|
||||
root = os.environ.get("SLOPSMITH_PROGRESSION_DATA") or (
|
||||
_slopsmith_server_root() / "data" / "progression"
|
||||
root = getenv_compat("FEEDBACK_PROGRESSION_DATA") or (
|
||||
_feedBack_server_root() / "data" / "progression"
|
||||
)
|
||||
content, warnings = progression_mod.load_content(root)
|
||||
for warning in warnings:
|
||||
@@ -3115,7 +3116,7 @@ def _seed_builtin_diagnostic_sloppaks(dlc: Path | None = None) -> None:
|
||||
log.debug("Builtin diagnostic seed: no DLC folder configured, skipping")
|
||||
return
|
||||
|
||||
root = _slopsmith_server_root()
|
||||
root = _feedBack_server_root()
|
||||
dest_dir = dlc / _BUILTIN_DIAGNOSTIC_SUBDIR
|
||||
# Refuse a symlinked seed directory: mkdir(exist_ok=True) would accept
|
||||
# it and copies would land at the link target, outside the DLC tree.
|
||||
@@ -3417,7 +3418,7 @@ async def startup_events():
|
||||
# phase so any frontend startup waiter that observes the lifespan also
|
||||
# unblocks cleanly (the SSE/poll client treats only `complete` and
|
||||
# `error` as terminal when `running` becomes false).
|
||||
if _env_flag("SLOPSMITH_SKIP_STARTUP_TASKS"):
|
||||
if _env_flag("FEEDBACK_SKIP_STARTUP_TASKS"):
|
||||
log.info("[startup] Skipping plugin load and background scan")
|
||||
# Tests pop `server` from sys.modules across runs, but the `plugins`
|
||||
# module is not reloaded — so LOADED_PLUGINS can carry stale entries
|
||||
@@ -3432,7 +3433,7 @@ async def startup_events():
|
||||
_set_startup_status(
|
||||
running=False,
|
||||
phase="complete",
|
||||
message="Startup tasks skipped (SLOPSMITH_SKIP_STARTUP_TASKS).",
|
||||
message="Startup tasks skipped (FEEDBACK_SKIP_STARTUP_TASKS).",
|
||||
error=None,
|
||||
current_plugin="",
|
||||
loaded=0,
|
||||
@@ -3488,7 +3489,7 @@ async def startup_events():
|
||||
|
||||
# Load plugins asynchronously so HTTP routes and the desktop window can
|
||||
# come up immediately while heavy plugin imports/install steps continue.
|
||||
_sync_mode = os.environ.get("SLOPSMITH_SYNC_STARTUP", "").lower() in {"1", "true", "yes", "on"}
|
||||
_sync_mode = getenv_compat("FEEDBACK_SYNC_STARTUP", "").lower() in {"1", "true", "yes", "on"}
|
||||
|
||||
def _load_plugins_background():
|
||||
try:
|
||||
@@ -3677,7 +3678,7 @@ async def startup_events():
|
||||
route_setup_fn=_route_setup_on_main)
|
||||
# Self-heal a freshly recreated container: its filesystem reset to
|
||||
# the image-baked sheet (in-tree plugins only), but a mounted
|
||||
# SLOPSMITH_PLUGINS_DIR may carry user-installed plugins whose
|
||||
# FEEDBACK_PLUGINS_DIR may carry user-installed plugins whose
|
||||
# classes aren't in it. Run in its OWN daemon thread so the startup
|
||||
# status can flip to "complete" immediately rather than waiting on
|
||||
# the (up to 120s) Tailwind subprocess. No-op when there are no user
|
||||
@@ -3723,7 +3724,7 @@ async def startup_events():
|
||||
threading.Thread(target=_load_plugins_background, daemon=True).start()
|
||||
|
||||
global _DEMO_JANITOR_STARTED, _DEMO_JANITOR_THREAD
|
||||
if os.environ.get("SLOPSMITH_DEMO_MODE") == "1" and not _DEMO_JANITOR_STARTED:
|
||||
if getenv_compat("FEEDBACK_DEMO_MODE") or getenv_compat("FEEDBACK_DEMO_MODE") == "1" and not _DEMO_JANITOR_STARTED:
|
||||
_DEMO_JANITOR_STARTED = True
|
||||
_DEMO_JANITOR_STOP.clear()
|
||||
def _janitor():
|
||||
@@ -3830,7 +3831,7 @@ def get_version():
|
||||
version = version_file.read_text().strip()
|
||||
except (OSError, UnicodeDecodeError):
|
||||
pass
|
||||
default_source_url = "https://github.com/got-feedback/feedback"
|
||||
default_source_url = "https://github.com/got-feedback/feedBack"
|
||||
# APP_SOURCE_URL / APP_LICENSE_URL flow straight into <a href> in the UI,
|
||||
# so validate with urllib.parse rather than a bare prefix check — a prefix
|
||||
# check accepts malformed values like "https://" (no host) which produce
|
||||
@@ -4514,7 +4515,7 @@ async def list_tuning_names(provider: str = "local"):
|
||||
"""Distinct tuning names present in the library, with per-tuning
|
||||
counts. Powers the tuning multi-select. Sorted by `tuning_sort_key`
|
||||
so names appear in the same musical order the sort uses
|
||||
(slopsmith#22) — E Standard first, then nearest neighbors."""
|
||||
(feedBack#22) — E Standard first, then nearest neighbors."""
|
||||
library_provider = _get_library_provider(provider)
|
||||
_require_library_provider_capability(library_provider, "library.read")
|
||||
return await _call_library_provider_async(library_provider, "tuning_names")
|
||||
@@ -5573,7 +5574,7 @@ def save_settings(data: dict):
|
||||
return {"message": ". ".join(messages) if messages else "Settings saved"}
|
||||
|
||||
|
||||
# ── Settings export/import (slopsmith#113) ───────────────────────────────────
|
||||
# ── Settings export/import (feedBack#113) ───────────────────────────────────
|
||||
|
||||
# Bumped only when the bundle JSON shape changes incompatibly. Importer
|
||||
# refuses anything but this exact value — version mismatches are warned
|
||||
@@ -5945,7 +5946,7 @@ def _atomic_write_file(target: Path, payload: bytes):
|
||||
def export_settings():
|
||||
"""Build a settings bundle covering server config + opted-in plugin
|
||||
server-side files. Frontend layers in `local_storage` before
|
||||
triggering the download. See slopsmith#113."""
|
||||
triggering the download. See feedBack#113."""
|
||||
import datetime
|
||||
from plugins import LOADED_PLUGINS, PLUGINS_LOCK
|
||||
|
||||
@@ -5968,11 +5969,11 @@ def export_settings():
|
||||
bundle = {
|
||||
"schema": SETTINGS_BUNDLE_SCHEMA,
|
||||
"exported_at": now.strftime("%Y-%m-%dT%H:%M:%SZ"),
|
||||
"slopsmith_version": _running_version(),
|
||||
"feedBack_version": _running_version(),
|
||||
"server_config": server_config,
|
||||
"plugin_server_configs": plugin_blocks,
|
||||
}
|
||||
filename = f"slopsmith-settings-{now.strftime('%Y-%m-%d')}.json"
|
||||
filename = f"feedBack-settings-{now.strftime('%Y-%m-%d')}.json"
|
||||
return JSONResponse(
|
||||
bundle,
|
||||
headers={"Content-Disposition": f'attachment; filename="{filename}"'},
|
||||
@@ -5984,7 +5985,7 @@ def import_settings(bundle: dict):
|
||||
"""Apply a previously exported settings bundle. Validates the entire
|
||||
bundle in phase 1 (no disk writes); only on full success does
|
||||
phase 2 commit each file via temp+rename. The frontend reads
|
||||
`local_storage` itself — server ignores it. See slopsmith#113."""
|
||||
`local_storage` itself — server ignores it. See feedBack#113."""
|
||||
from plugins import LOADED_PLUGINS, PLUGINS_LOCK
|
||||
|
||||
if not isinstance(bundle, dict):
|
||||
@@ -6022,7 +6023,7 @@ def import_settings(bundle: dict):
|
||||
)
|
||||
|
||||
warnings: list[str] = []
|
||||
bundle_version = bundle.get("slopsmith_version")
|
||||
bundle_version = bundle.get("feedBack_version")
|
||||
running = _running_version()
|
||||
if bundle_version and bundle_version != running:
|
||||
warnings.append(
|
||||
@@ -6155,7 +6156,7 @@ def import_settings(bundle: dict):
|
||||
}
|
||||
|
||||
|
||||
# ── Diagnostic bundle export (slopsmith#166) ──────────────────────────
|
||||
# ── Diagnostic bundle export (feedBack#166) ──────────────────────────
|
||||
#
|
||||
# One-click "Export Diagnostics" in Settings produces a redacted zip
|
||||
# combining server logs, system info, hardware (CPU/GPU/RAM), plugin
|
||||
@@ -6179,11 +6180,11 @@ def _diag_plugins_roots() -> list[Path]:
|
||||
"""Return all plugin root directories for orphan scanning.
|
||||
|
||||
Includes both the built-in ``plugins/`` directory and
|
||||
``SLOPSMITH_PLUGINS_DIR`` when set, so user-installed plugins and
|
||||
``FEEDBACK_PLUGINS_DIR`` when set, so user-installed plugins and
|
||||
orphans in the external dir are reflected in the bundle.
|
||||
"""
|
||||
roots: list[Path] = []
|
||||
user_dir = os.environ.get("SLOPSMITH_PLUGINS_DIR", "").strip()
|
||||
user_dir = getenv_compat("FEEDBACK_PLUGINS_DIR", "").strip()
|
||||
if user_dir:
|
||||
p = Path(user_dir)
|
||||
if p.is_dir():
|
||||
@@ -6363,7 +6364,7 @@ def export_diagnostics(payload: dict = Body(default_factory=dict)):
|
||||
)
|
||||
|
||||
zip_bytes, filename, _manifest = _diag_build(
|
||||
slopsmith_version=_running_version(),
|
||||
feedBack_version=_running_version(),
|
||||
config_dir=CONFIG_DIR,
|
||||
dlc_dir=_get_dlc_dir(),
|
||||
log_file=_diag_log_file(),
|
||||
@@ -6409,7 +6410,7 @@ def preview_diagnostics(
|
||||
with PLUGINS_LOCK:
|
||||
plugins_snapshot = list(LOADED_PLUGINS)
|
||||
return _diag_preview(
|
||||
slopsmith_version=_running_version(),
|
||||
feedBack_version=_running_version(),
|
||||
config_dir=CONFIG_DIR,
|
||||
dlc_dir=_get_dlc_dir(),
|
||||
log_file=_diag_log_file(),
|
||||
@@ -7076,7 +7077,7 @@ async def highway_ws(websocket: WebSocket, filename: str, arrangement: int = -1,
|
||||
"audio_error": audio_error,
|
||||
"tuning": arr.tuning,
|
||||
# Number of strings on the active arrangement
|
||||
# (slopsmith-plugin-3dhighway#7). arrangement XML / archive sources
|
||||
# (feedBack-plugin-3dhighway#7). arrangement XML / archive sources
|
||||
# always emit `tuning` as length 6 with zero-padding for
|
||||
# unused string slots, so `len(arr.tuning)` is unreliable
|
||||
# there; sloppak / GP-imported sources may instead carry
|
||||
@@ -7570,7 +7571,7 @@ async def highway_ws(websocket: WebSocket, filename: str, arrangement: int = -1,
|
||||
})
|
||||
|
||||
# Per-phrase difficulty data for the master-difficulty slider
|
||||
# (slopsmith#48). Only sent when the source chart had multiple
|
||||
# (feedBack#48). Only sent when the source chart had multiple
|
||||
# `<level>` tiers — single-level charts (GP converter, older
|
||||
# sloppaks without phrase data) produce arr.phrases=None, and the
|
||||
# frontend treats the missing message as "slider disabled".
|
||||
@@ -7681,9 +7682,9 @@ app.mount("/static", StaticFiles(directory=str(STATIC_DIR)), name="static")
|
||||
def index():
|
||||
# fee[dB]ack v0.3.0: the v3 shell is now the DEFAULT at `/`. The classic v2
|
||||
# UI remains fully available as a fallback — opt back in with
|
||||
# SLOPSMITH_UI=v2 (or =legacy), or hit the dedicated /v2 route below (which
|
||||
# FEEDBACK_UI=v2 (or =legacy), or hit the dedicated /v2 route below (which
|
||||
# serves it regardless of the env var).
|
||||
if os.environ.get("SLOPSMITH_UI") in ("v2", "legacy"):
|
||||
if getenv_compat("FEEDBACK_UI") or getenv_compat("FEEDBACK_UI") in ("v2", "legacy"):
|
||||
return FileResponse(str(STATIC_DIR / "index.html"))
|
||||
return FileResponse(str(STATIC_DIR / "v3" / "index.html"))
|
||||
|
||||
@@ -7698,5 +7699,5 @@ def index_v3():
|
||||
@app.get("/v2")
|
||||
def index_v2():
|
||||
# Always serve the classic v2 UI, independent of the env var, so the
|
||||
# fallback is reachable without flipping SLOPSMITH_UI.
|
||||
# fallback is reachable without flipping FEEDBACK_UI.
|
||||
return FileResponse(str(STATIC_DIR / "index.html"))
|
||||
|
||||
Reference in New Issue
Block a user