File size: 4,374 Bytes
41d16b3
 
333dd2f
41d16b3
 
 
8354653
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70d9e6c
 
 
 
 
 
 
 
 
 
8354653
 
41d16b3
70d9e6c
4595db8
 
 
333dd2f
8354653
 
 
 
 
214c544
 
 
 
 
 
 
 
 
 
333dd2f
214c544
333dd2f
214c544
 
 
333dd2f
214c544
 
 
8354653
 
 
 
 
333dd2f
 
 
 
 
 
 
 
4595db8
41d16b3
 
 
4595db8
41d16b3
995884c
 
 
c9672b1
995884c
 
41d16b3
 
 
 
 
333dd2f
 
 
 
 
 
41d16b3
 
 
 
 
 
4595db8
333dd2f
4595db8
 
333dd2f
 
 
 
 
 
4595db8
 
c9672b1
 
 
 
 
 
41d16b3
 
 
 
 
 
5ae5432
 
 
 
99e1f27
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
import os
from pathlib import Path
from typing import List

from ..database import DATA_DIR

APP_MODE_RAW = os.environ.get("APP_MODE", "").strip().lower()
_SPACE_ID = os.environ.get("SPACE_ID", "").strip().lower()


def is_hf_hosted() -> bool:
    return bool(_SPACE_ID)


def _is_dda_mode() -> bool:
    if APP_MODE_RAW == "legacy":
        return False
    if APP_MODE_RAW == "dda":
        return True
    # APP_MODE unset: auto-enable on satdetect-dev only (production satdetect stays legacy)
    return _SPACE_ID.endswith("/satdetect-dev")


def get_public_base_url() -> str:
    """Public URL for deep links (email, share). Override with PUBLIC_BASE_URL."""
    explicit = os.environ.get("PUBLIC_BASE_URL", "").strip().rstrip("/")
    if explicit:
        return explicit
    if _SPACE_ID:
        return f"https://{_SPACE_ID.replace('/', '-')}.hf.space"
    return "http://localhost:7860"


IS_DDA_MODE = _is_dda_mode()
APP_MODE = APP_MODE_RAW or ("dda" if IS_DDA_MODE else "legacy")


# Project root: change_detection_webapp/
PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent


def get_writable_library_root() -> Path:
    """Persistent folder for uploads (HF: /home/appuser/data/library_sources)."""
    return (DATA_DIR / "library_sources").resolve()


def get_storage_root() -> Path:
    """Tree library root (doc: root_directory / STORAGE_ROOT)."""
    explicit = os.environ.get("STORAGE_ROOT", "").strip()
    if explicit:
        return Path(explicit).resolve()
    if os.environ.get("LOCAL_LIBRARY_ROOT"):
        return Path(os.environ["LOCAL_LIBRARY_ROOT"]).resolve()
    return get_writable_library_root()


def get_library_roots() -> List[Path]:
    """Folders scanned for library images (tree storage root)."""
    roots: List[Path] = []
    sr = get_storage_root()
    if sr not in roots:
        roots.append(sr)
    if os.environ.get("LOCAL_LIBRARY_ROOT"):
        p = Path(os.environ["LOCAL_LIBRARY_ROOT"]).resolve()
        if p not in roots:
            roots.append(p)
    if is_hf_hosted():
        wr = get_writable_library_root()
        if wr not in roots:
            roots.append(wr)
    for candidate in (get_writable_library_root(), PROJECT_ROOT / "library_sources"):
        resolved = candidate.resolve()
        if resolved not in roots:
            roots.append(resolved)
    return roots


# Primary root (shown in UI) — first entry from get_library_roots()
LOCAL_LIBRARY_ROOT = get_library_roots()[0]

LIBRARY_DIR = DATA_DIR / "library"
THUMBS_DIR = LIBRARY_DIR / "thumbs"
PREVIEWS_DIR = LIBRARY_DIR / "previews"
LOCAL_THUMB_CACHE = DATA_DIR / "library_cache" / "thumbs"

# Library upload limit (default 15 GB; override with MAX_GEOTIFF_MB / MAX_IMAGE_MB)
_MAX_UPLOAD_MB_DEFAULT = "15360"  # 15 * 1024
MAX_GEOTIFF_BYTES = int(os.environ.get("MAX_GEOTIFF_MB", _MAX_UPLOAD_MB_DEFAULT)) * 1024 * 1024

# PNG/JPEG use the same cap unless MAX_IMAGE_MB is set separately
MAX_IMAGE_BYTES = int(os.environ.get("MAX_IMAGE_MB", _MAX_UPLOAD_MB_DEFAULT)) * 1024 * 1024

ALLOWED_EXTENSIONS = {".tif", ".tiff", ".png", ".jpg", ".jpeg"}


def ensure_library_dirs() -> None:
    for root in get_library_roots():
        try:
            root.mkdir(parents=True, exist_ok=True)
        except OSError:
            pass
    for d in (LIBRARY_DIR, THUMBS_DIR, PREVIEWS_DIR, LOCAL_THUMB_CACHE):
        try:
            d.mkdir(parents=True, exist_ok=True)
        except OSError:
            pass


def ensure_local_year_folders() -> None:
    """Create default year folders under each library root if missing."""
    from datetime import datetime
    current = datetime.now().year
    for root in get_library_roots():
        for year in (current - 1, current, current + 1):
            try:
                (root / str(year)).mkdir(parents=True, exist_ok=True)
            except OSError:
                pass


def max_upload_bytes_for_extension(ext: str) -> int:
    if ext in (".tif", ".tiff"):
        return MAX_GEOTIFF_BYTES
    return MAX_IMAGE_BYTES


def geotiff_io_available() -> bool:
    try:
        import rasterio  # noqa: F401
        return True
    except ImportError:
        return False


def get_detection_max_side() -> int:
    """Max pixel dimension for GeoTIFF load + detection pipeline (higher = sharper, more RAM)."""
    from ..detection_config import get_detection_max_side as _central
    return _central()