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
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

## [v3.1.12] - 2026-05-17

- 🚨 Fixed Dependabot security alerts

## [v3.1.11] - 2026-05-17

- 🔧 Fixed MLOL automation after the May 2026 redesign changed clean-page login, Edicola navigation, PressReader launch, and logout selectors
Expand Down
11 changes: 5 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "pressreadmeplease"
version = "3.1.11"
version = "3.1.12"
description = "A process for automating PressReader weekly token automagically."
authors = [{ name = "tatoalo", email = "apogliaghi@gmail.com" }]
license = "MIT"
Expand All @@ -10,15 +10,14 @@ dependencies = [
"mock==5.2.0",
"playwright==1.58.0",
"pydantic==2.12.5",
"pytest==9.0.2",
"diskcache==5.6.3",
"pytest==9.0.3",
"pyvirtualdisplay==3.0.0",
"requests==2.32.5",
"requests==2.33.0",
"tomlkit==0.14.0",
"typing-extensions==4.15.0",
"urllib3==2.6.3",
"urllib3==2.7.0",
"certifi==2026.2.25",
"pillow==12.1.1",
"pillow==12.2.0",
"imagehash==4.3.2",
"logfire==4.33.0",
]
Expand Down
52 changes: 47 additions & 5 deletions src/cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,60 @@
This is a disk-based cache, we do have TTL as well.
"""

from diskcache import Cache
from src import CACHE_DIR, logging

import json
from pathlib import Path
from time import time

from PIL import Image
import imagehash
from PIL import Image

from src import CACHE_DIR, logging


CACHE_TTL_SECONDS = 2628000 # 30 days

ModalCache = Cache(CACHE_DIR)

class HashCache:
def __init__(self, cache_dir: Path):
self.cache_file = Path(cache_dir) / "modal_hashes.json"

def set(self, key: str, value: int, expire: int) -> None:
del value
records = self._load()
records[key] = time() + expire
self._write(records)

def __contains__(self, key: str) -> bool:
records = self._load()
expires_at = records.get(key)
if expires_at is None:
return False
if expires_at <= time():
records.pop(key, None)
self._write(records)
return False
return True

def _load(self) -> dict[str, float]:
if not self.cache_file.exists():
return {}
try:
raw_records = json.loads(self.cache_file.read_text())
except (json.JSONDecodeError, OSError) as e:
logging.error(f"Failed to read modal hash cache: {e}")
return {}
return {
str(key): float(value)
for key, value in raw_records.items()
if isinstance(value, int | float)
}

def _write(self, records: dict[str, float]) -> None:
self.cache_file.parent.mkdir(parents=True, exist_ok=True)
self.cache_file.write_text(json.dumps(records, sort_keys=True))


ModalCache = HashCache(CACHE_DIR)


def remember(h: str, ttl_seconds: int = CACHE_TTL_SECONDS) -> None:
Expand Down
6 changes: 3 additions & 3 deletions tests/test_cache.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
import time

from PIL import Image
from diskcache import Cache

from src import cache

Expand All @@ -18,9 +17,9 @@ class TestCache(TestCase):
def setUp(self) -> None:
"""Set up test fixtures before each test method."""
# Use a temporary cache directory for testing
self.temp_cache_dir = tempfile.mkdtemp()
self.temp_cache_dir = tempfile.TemporaryDirectory()
self.original_cache = cache.ModalCache
cache.ModalCache = Cache(self.temp_cache_dir)
cache.ModalCache = cache.HashCache(Path(self.temp_cache_dir.name))

# Create test images
self.test_image_path = TEST_PATH / "test_modal_1.png"
Expand Down Expand Up @@ -58,6 +57,7 @@ def tearDown(self) -> None:
"""Clean up test fixtures after each test method."""
# Restore original cache
cache.ModalCache = self.original_cache
self.temp_cache_dir.cleanup()

# Remove test images
if self.test_image_path.exists():
Expand Down
Loading