Duplicates, delete & storage, users page with J-ID avatars
Backend: - Perceptual hashes (aHash/dHash/pHash/wHash via imagehash, no imgdd) stored on items, computed on upload/download and by the new compute_visual_hashes command - Duplicates API: exact duplicates (multi-location items), visual matches for one item, union-find similarity groups with pagination - Delete API with ownership/staff checks, per-item and per-copy deletion, watched-folder path validation; storage overview and temp cleanup; file list accepts j_ids batches - Staged uploads are flagged visual_match with their library matches (threshold via VISUAL_MATCH_THRESHOLD) - Staff users API: list with upload counts, set role and avatar by J-ID; User.avatar FK with signed avatar URLs - Download threads close their DB connection and stale tasks are reaped, keeping behaviour Gunicorn-friendly Frontend: - /duplicates: exact duplicate groups with per-copy delete, visual similarity controls, search similar to a J-ID, paginated groups with selection, bulk delete and dismiss - /delete: storage cards, delete by J-ID with preview grid, temp cleanup - /users: staff directory with role selects and avatar J-ID inputs - Nav + command palette entries; top-bar avatar; upload cards and the metadata modal show library visual matches
This commit is contained in:
@@ -7,10 +7,70 @@ export interface User {
|
||||
date_joined: string;
|
||||
role: "user" | "uploader" | "staff";
|
||||
can_upload: boolean;
|
||||
avatar_j_id: string | null;
|
||||
avatar_url: string | null;
|
||||
e621_username: string;
|
||||
e621_configured: boolean;
|
||||
}
|
||||
|
||||
export interface AdminUser {
|
||||
id: number;
|
||||
username: string;
|
||||
email: string;
|
||||
role: "user" | "uploader" | "staff";
|
||||
is_staff: boolean;
|
||||
is_superuser: boolean;
|
||||
date_joined: string;
|
||||
avatar_j_id: string | null;
|
||||
avatar_url: string | null;
|
||||
uploads_count: number;
|
||||
}
|
||||
|
||||
export interface DuplicateEntry {
|
||||
j_id: string;
|
||||
md5: string;
|
||||
filename: string;
|
||||
size: number;
|
||||
rating: string;
|
||||
location_count: number;
|
||||
uploaded_by: string | null;
|
||||
e621_post_id: number | null;
|
||||
locations?: { id: number; rel_path: string }[];
|
||||
similarity?: number;
|
||||
}
|
||||
|
||||
export interface VisualGroupsResponse {
|
||||
count: number;
|
||||
page: number;
|
||||
per_page: number;
|
||||
has_next: boolean;
|
||||
threshold: number;
|
||||
algorithms: string[];
|
||||
groups: { size: number; members: DuplicateEntry[] }[];
|
||||
}
|
||||
|
||||
export interface VisualMatchesResponse {
|
||||
target: DuplicateEntry;
|
||||
threshold: number;
|
||||
algorithms: string[];
|
||||
count: number;
|
||||
matches: DuplicateEntry[];
|
||||
}
|
||||
|
||||
export interface StorageInfo {
|
||||
watched_folder: {
|
||||
path: string;
|
||||
total: number;
|
||||
used: number;
|
||||
free: number;
|
||||
library_size: number;
|
||||
percent_used: number;
|
||||
};
|
||||
media: { path: string; size: number };
|
||||
temp: { path: string; size: number; files: number };
|
||||
library_items: number;
|
||||
}
|
||||
|
||||
export interface MediaLocation {
|
||||
id: number;
|
||||
path: string;
|
||||
@@ -148,6 +208,9 @@ export interface TempUpload {
|
||||
custom_tags: string[];
|
||||
custom_notes: string;
|
||||
iqdb_data: E621IqdbCandidate[] | null;
|
||||
visual_matches:
|
||||
| { j_id: string; filename: string; similarity: number }[]
|
||||
| null;
|
||||
library_j_id: string | null;
|
||||
file_url: string | null;
|
||||
preview_url: string | null;
|
||||
|
||||
Reference in New Issue
Block a user