fix: port-in-use check and remove packaging dependency
This commit is contained in:
@@ -43,7 +43,6 @@ CORE_DEPS=(
|
|||||||
"pyyaml>=6.0"
|
"pyyaml>=6.0"
|
||||||
"mutagen>=1.47.0"
|
"mutagen>=1.47.0"
|
||||||
"pillow>=10.0.0"
|
"pillow>=10.0.0"
|
||||||
"packaging>=23.0"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Windows-specific dependencies
|
# Windows-specific dependencies
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import logging
|
import logging
|
||||||
|
import socket
|
||||||
import sys
|
import sys
|
||||||
from contextlib import asynccontextmanager
|
from contextlib import asynccontextmanager
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
@@ -259,6 +260,19 @@ def main():
|
|||||||
print("\nAuthentication is DISABLED (no tokens configured)")
|
print("\nAuthentication is DISABLED (no tokens configured)")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# Check if port is available before starting
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
|
||||||
|
try:
|
||||||
|
sock.bind((args.host if args.host != "0.0.0.0" else "127.0.0.1", args.port))
|
||||||
|
except OSError:
|
||||||
|
print(
|
||||||
|
f"ERROR: Port {args.port} is already in use. "
|
||||||
|
f"Another instance of Media Server may be running.\n"
|
||||||
|
f"Stop the other process or use --port to pick a different port.",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
from .tray import PYSTRAY_AVAILABLE, TrayManager
|
from .tray import PYSTRAY_AVAILABLE, TrayManager
|
||||||
|
|
||||||
use_tray = PYSTRAY_AVAILABLE and not args.no_tray
|
use_tray = PYSTRAY_AVAILABLE and not args.no_tray
|
||||||
|
|||||||
@@ -3,10 +3,9 @@
|
|||||||
import asyncio
|
import asyncio
|
||||||
import logging
|
import logging
|
||||||
import re
|
import re
|
||||||
|
from functools import total_ordering
|
||||||
from typing import Any, Optional
|
from typing import Any, Optional
|
||||||
|
|
||||||
from packaging.version import Version
|
|
||||||
|
|
||||||
from .release_provider import ReleaseProvider
|
from .release_provider import ReleaseProvider
|
||||||
from .websocket_manager import ws_manager
|
from .websocket_manager import ws_manager
|
||||||
|
|
||||||
@@ -15,23 +14,67 @@ logger = logging.getLogger(__name__)
|
|||||||
_PRE_PATTERN = re.compile(
|
_PRE_PATTERN = re.compile(
|
||||||
r"^(\d+\.\d+\.\d+)[-.]?(alpha|beta|rc)[.-]?(\d+)$", re.IGNORECASE
|
r"^(\d+\.\d+\.\d+)[-.]?(alpha|beta|rc)[.-]?(\d+)$", re.IGNORECASE
|
||||||
)
|
)
|
||||||
_PRE_MAP = {"alpha": "a", "beta": "b", "rc": "rc"}
|
_PRE_ORDER = {"alpha": 0, "beta": 1, "rc": 2}
|
||||||
|
|
||||||
|
|
||||||
def _parse_version(raw: str) -> Version:
|
@total_ordering
|
||||||
"""Normalize a version tag to PEP 440 for correct comparison.
|
class _Version:
|
||||||
|
"""Lightweight PEP 440-ish version for comparison without packaging dep.
|
||||||
|
|
||||||
|
Supports: X.Y.Z and X.Y.Z-{alpha,beta,rc}.N
|
||||||
|
Pre-releases sort before the corresponding stable release.
|
||||||
|
"""
|
||||||
|
|
||||||
|
__slots__ = ("_release", "_pre")
|
||||||
|
|
||||||
|
def __init__(self, release: tuple[int, ...], pre: Optional[tuple[int, int]]) -> None:
|
||||||
|
self._release = release
|
||||||
|
self._pre = pre
|
||||||
|
|
||||||
|
def __eq__(self, other: object) -> bool:
|
||||||
|
if not isinstance(other, _Version):
|
||||||
|
return NotImplemented
|
||||||
|
return self._release == other._release and self._pre == other._pre
|
||||||
|
|
||||||
|
def __lt__(self, other: object) -> bool:
|
||||||
|
if not isinstance(other, _Version):
|
||||||
|
return NotImplemented
|
||||||
|
if self._release != other._release:
|
||||||
|
return self._release < other._release
|
||||||
|
# No pre-release (stable) is greater than any pre-release
|
||||||
|
if self._pre is None and other._pre is None:
|
||||||
|
return False
|
||||||
|
if self._pre is not None and other._pre is None:
|
||||||
|
return True
|
||||||
|
if self._pre is None and other._pre is not None:
|
||||||
|
return False
|
||||||
|
return self._pre < other._pre # type: ignore[operator]
|
||||||
|
|
||||||
|
def __repr__(self) -> str:
|
||||||
|
v = ".".join(str(p) for p in self._release)
|
||||||
|
if self._pre is not None:
|
||||||
|
labels = {0: "alpha", 1: "beta", 2: "rc"}
|
||||||
|
v += f"-{labels[self._pre[0]]}.{self._pre[1]}"
|
||||||
|
return f"_Version('{v}')"
|
||||||
|
|
||||||
|
|
||||||
|
def _parse_version(raw: str) -> _Version:
|
||||||
|
"""Parse a version tag for comparison.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
v0.3.0-alpha.1 → 0.3.0a1 (pre-release, sorts below 0.3.0)
|
v0.3.0-alpha.1 → (0,3,0) pre=(0,1) (sorts below 0.3.0)
|
||||||
v0.3.0-rc.3 → 0.3.0rc3
|
v0.3.0-rc.3 → (0,3,0) pre=(2,3)
|
||||||
v1.0.0 → 1.0.0
|
v1.0.0 → (1,0,0) pre=None
|
||||||
"""
|
"""
|
||||||
cleaned = raw.lstrip("v").strip()
|
cleaned = raw.lstrip("v").strip()
|
||||||
m = _PRE_PATTERN.match(cleaned)
|
m = _PRE_PATTERN.match(cleaned)
|
||||||
if m:
|
if m:
|
||||||
base, pre_label, pre_num = m.group(1), m.group(2).lower(), m.group(3)
|
base = tuple(int(x) for x in m.group(1).split("."))
|
||||||
cleaned = f"{base}{_PRE_MAP[pre_label]}{pre_num}"
|
pre_label = m.group(2).lower()
|
||||||
return Version(cleaned)
|
pre_num = int(m.group(3))
|
||||||
|
return _Version(base, (_PRE_ORDER[pre_label], pre_num))
|
||||||
|
release = tuple(int(x) for x in cleaned.split("."))
|
||||||
|
return _Version(release, None)
|
||||||
|
|
||||||
|
|
||||||
class UpdateChecker:
|
class UpdateChecker:
|
||||||
|
|||||||
@@ -32,7 +32,6 @@ dependencies = [
|
|||||||
"pyyaml>=6.0",
|
"pyyaml>=6.0",
|
||||||
"mutagen>=1.47.0",
|
"mutagen>=1.47.0",
|
||||||
"pillow>=10.0.0",
|
"pillow>=10.0.0",
|
||||||
"packaging>=23.0",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
[project.optional-dependencies]
|
[project.optional-dependencies]
|
||||||
|
|||||||
Reference in New Issue
Block a user