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
28 changes: 28 additions & 0 deletions backend/app/config/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@
else:
DATABASE_PATH = os.path.join(user_data_dir("PictoPy"), "database", "PictoPy.db")
THUMBNAIL_IMAGES_PATH = os.path.join(user_data_dir("PictoPy"), "thumbnails")
VIDEO_FRAMES_PATH = os.path.join(user_data_dir("PictoPy"), "video_frames")
IMAGES_PATH = "./images"


Expand Down Expand Up @@ -175,6 +176,33 @@ def _get_env_int(
}
SEMANTIC_DEFAULT_THRESHOLD = 5e-05

# Video keyframe sampling. One frame every N seconds instead of per-frame
# inference (30fps = 1800 forward passes per minute of video). The interval is
# stretched when a video would exceed the cap, so cost per video is bounded.
# The bounds are the single source of truth for both the sampler default here
# and the user-preferences API schema, so the API can never accept an interval
# the sampler would reject.
VIDEO_FRAME_INTERVAL_MIN = 0.5
VIDEO_FRAME_INTERVAL_MAX = 300.0
VIDEO_FRAME_INTERVAL_SECONDS = _get_env_float(
"VIDEO_FRAME_INTERVAL_SECONDS",
5.0,
min_value=VIDEO_FRAME_INTERVAL_MIN,
max_value=VIDEO_FRAME_INTERVAL_MAX,
)
VIDEO_MAX_FRAMES_PER_VIDEO = _get_env_int(
"VIDEO_MAX_FRAMES_PER_VIDEO", 200, min_value=1
)
# Frames are saved above SigLIP2's largest input (384) so a checkpoint swap
# doesn't require re-extraction.
VIDEO_FRAME_MAX_DIMENSION = _get_env_int("VIDEO_FRAME_MAX_DIMENSION", 640, min_value=64)
# A tag must appear in this many frames to describe the video; drops one-off
# detections from a single unlucky keyframe.
VIDEO_TAG_MIN_FRAME_SUPPORT = _get_env_int(
"VIDEO_TAG_MIN_FRAME_SUPPORT", 2, min_value=1
)
VIDEO_TAG_TOP_K = _get_env_int("VIDEO_TAG_TOP_K", 15, min_value=1)

# Clustering Configuration
PICTO_CLUSTERING_EPS = _get_env_float("PICTO_CLUSTERING_EPS", 0.75, min_value=0.0)
PICTO_CLUSTERING_MIN_SAMPLES = _get_env_int(
Expand Down
23 changes: 14 additions & 9 deletions backend/app/database/folders.py
Original file line number Diff line number Diff line change
Expand Up @@ -408,30 +408,35 @@ def db_get_folder_ids_by_paths(


def db_get_all_folder_details() -> (
List[Tuple[str, str, Optional[str], int, bool, Optional[bool], str, int]]
List[Tuple[str, str, Optional[str], int, bool, Optional[bool], str, int, int]]
):
"""
Get all folder details including folder_id, folder_path, parent_folder_id,
last_modified_time, AI_Tagging, taggingCompleted, and image_count.
last_modified_time, AI_Tagging, taggingCompleted, indexing_status,
image_count and video_count.
Returns list of tuples with all folder information.
"""
conn = sqlite3.connect(DATABASE_PATH)
cursor = conn.cursor()

try:
# COUNT(DISTINCT ...) because joining both media tables multiplies the
# rows: a folder with 3 images and 4 videos yields 12 join rows.
cursor.execute(
"""
SELECT
f.folder_id,
f.folder_path,
f.parent_folder_id,
f.last_modified_time,
f.AI_Tagging,
SELECT
f.folder_id,
f.folder_path,
f.parent_folder_id,
f.last_modified_time,
f.AI_Tagging,
f.taggingCompleted,
f.indexing_status,
COUNT(i.id) as image_count
COUNT(DISTINCT i.id) as image_count,
COUNT(DISTINCT v.id) as video_count
FROM folders f
LEFT JOIN images i ON f.folder_id = i.folder_id
LEFT JOIN videos v ON f.folder_id = v.folder_id
GROUP BY f.folder_id
ORDER BY f.folder_path
"""
Expand Down
Loading
Loading