Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
31 changes: 27 additions & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -66,17 +66,40 @@ extend-exclude = [".local"]
[tool.ruff.lint]
select = [
"F", "I", "UP", "B", "C4", "SIM", "PTH", "RET", "PIE", "FURB", "PERF", "TRY",
"ANN001", "ANN201", "ANN202", "ANN204",
"A", "DTZ", "N",
"BLE001",
"ANN001", "ANN002", "ANN003", "ANN201", "ANN202", "ANN204",
"E402",
"ERA001",
"E731",
"FBT002", "FBT003",
"S101",
"S110",
"S105", "S108",
"S311", "S324",
"S607", "S608",
"G003", "G004",
"PGH003",
"PLC0414",
"PLR1714",
"PLW1510",
"PLW2901",
"PLW0108",
"PT028",
"PYI034",
"Q000",
"RUF005", "RUF012", "RUF013", "RUF059", "RUF100",
"TC001", "TC003",
"TC001", "TC002", "TC003",
]
ignore = ["D", "EM", "FBT", "PLR2004", "UP035", "TRY003", "E501", "TD002", "S104", "S603"]

[tool.ruff.lint.per-file-ignores]
"tests/**/*.py" = [
"ANN",
"S101",
"S105",
"S108",
"S311",
]
ignore = ["UP035", "TRY003", "E501"]

[tool.basedpyright]
include = ["shelfmark"]
Expand Down
6 changes: 5 additions & 1 deletion shelfmark/api/websocket.py
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
"""WebSocket manager for real-time status updates."""

from __future__ import annotations

import logging
import threading
from typing import TYPE_CHECKING, Any

from flask import Flask
from flask_socketio import SocketIO, join_room, leave_room

if TYPE_CHECKING:
from collections.abc import Callable

from flask import Flask

logger = logging.getLogger(__name__)


class WebSocketManager:
"""Manages WebSocket connections and broadcasts."""

def __init__(self) -> None:
"""Initialize in-memory connection and room tracking."""
self.socketio: SocketIO | None = None
self._enabled = False
self._connection_count = 0
Expand Down
2 changes: 1 addition & 1 deletion shelfmark/bypass/__init__.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
"""Cloudflare bypass utilities."""


class BypassCancelledException(Exception):
class BypassCancelledError(Exception):
"""Raised when a bypass operation is cancelled."""
7 changes: 4 additions & 3 deletions shelfmark/bypass/external_bypasser.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

import requests

from shelfmark.bypass import BypassCancelledException
from shelfmark.bypass import BypassCancelledError
from shelfmark.core.config import config
from shelfmark.core.logger import setup_logger
from shelfmark.core.utils import normalize_http_url
Expand All @@ -18,6 +18,7 @@
from shelfmark.download import network

logger = setup_logger(__name__)
_RNG = random.SystemRandom()

# Timeout constants (seconds)
CONNECT_TIMEOUT = 10
Expand Down Expand Up @@ -102,7 +103,7 @@ def _check_cancelled(cancel_flag: Event | None, context: str) -> None:
if cancel_flag and cancel_flag.is_set():
logger.info("External bypasser cancelled %s", context)
msg = "Bypass cancelled"
raise BypassCancelledException(msg)
raise BypassCancelledError(msg)


def _sleep_with_cancellation(seconds: float, cancel_flag: Event | None) -> None:
Expand Down Expand Up @@ -136,7 +137,7 @@ def get_bypassed_page(
if attempt == MAX_RETRY:
break

delay = min(BACKOFF_CAP, BACKOFF_BASE * (2 ** (attempt - 1))) + random.random()
delay = min(BACKOFF_CAP, BACKOFF_BASE * (2 ** (attempt - 1))) + _RNG.random()
logger.info(
"External bypasser attempt %s/%s failed, retrying in %.1fs",
attempt,
Expand Down
6 changes: 5 additions & 1 deletion shelfmark/bypass/fingerprint.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@

# Current screen size (module-level singleton)
_current_screen_size: tuple[int, int] | None = None
_RNG = random.SystemRandom()


def get_screen_size() -> tuple[int, int]:
"""Return the current synthetic screen size, generating one if needed."""
global _current_screen_size
if _current_screen_size is None:
_current_screen_size = _generate_screen_size()
Expand All @@ -36,6 +38,7 @@ def get_screen_size() -> tuple[int, int]:


def rotate_screen_size() -> tuple[int, int]:
"""Rotate to a new synthetic screen size and return it."""
global _current_screen_size
old_size = _current_screen_size
_current_screen_size = _generate_screen_size()
Expand All @@ -56,11 +59,12 @@ def rotate_screen_size() -> tuple[int, int]:


def clear_screen_size() -> None:
"""Clear the cached synthetic screen size."""
global _current_screen_size
_current_screen_size = None


def _generate_screen_size() -> tuple[int, int]:
resolutions = [(w, h) for w, h, _ in COMMON_RESOLUTIONS]
weights = [weight for _, _, weight in COMMON_RESOLUTIONS]
return random.choices(resolutions, weights=weights)[0]
return _RNG.choices(resolutions, weights=weights)[0]
Loading
Loading