ChBysk commited on
Commit
61b6204
·
verified ·
1 Parent(s): e35f67f

Upload folder using huggingface_hub

Browse files
ROADMAP.md CHANGED
@@ -7,27 +7,31 @@ This document outlines a roadmap and future development plan for the **Imageboar
7
  ## 🚀 1. Feature Enhancements
8
 
9
  ### 1.1 Support for Authenticated APIs (API Keys & Username)
10
- * **Goal:** Allow users to safely search and download from private or rate-limited sites (like Danbooru premium tiers, e621 personal accounts).
11
- * **Implementation:**
 
12
  - Add credential fields (API Key / Password Hash, Username) to the **Add Site** dialog.
13
  - Securely transmit credentials to the server using HTTP headers or request bodies.
14
  - Store credentials locally in the browser's `localStorage` or session-only cookies so they are never stored globally on the server.
15
 
16
  ### 1.2 Persistent Site Configuration
17
- * **Goal:** Avoid losing newly added schemas when refreshing or clearing session storage.
18
- * **Implementation:**
 
19
  - Sync the list of added custom sites with IndexedDB or `localStorage`.
20
  - Provide an **Export/Import Sites** button to allow users to backup their custom configurations as JSON.
21
 
22
  ### 1.3 Advanced Query Builder
23
- * **Goal:** Help non-technical users build complex booru search queries.
24
- * **Implementation:**
 
25
  - Add UI controls for searching by rating (Safe, Questionable, Explicit), score range, upload date, or file extension.
26
  - Autocomplete / tag suggestion box by querying the active site's tag API endpoint (e.g., `/tags.json?search[name_matches]=*`).
27
 
28
  ### 1.4 Enhanced Lightbox & Media Viewer
29
- * **Goal:** Support video playback and metadata exploration.
30
- * **Implementation:**
 
31
  - Auto-detect video extensions (`.webm`, `.mp4`) in the Lightbox and render a native HTML `<video>` player with volume and speed controls.
32
  - Show side-panel info: Artist, Character, Copyright, and Meta tags. Click a tag to search.
33
 
@@ -36,14 +40,16 @@ This document outlines a roadmap and future development plan for the **Imageboar
36
  ## 🛠️ 2. Architectural & Performance Improvements
37
 
38
  ### 2.1 Concurrent / Streamed Server Downloads
39
- * **Goal:** Speed up ZIP compilation and handle massive file downloads without causing OOM (Out Of Memory) crashes.
40
- * **Implementation:**
 
41
  - Implement streaming ZIP using `fflate` on the server so bytes are sent back to the client immediately as they download, rather than buffering entire gigabytes in server memory.
42
  - Limit concurrent download workers per request to avoid IP bans from image hosters.
43
 
44
  ### 2.2 Client-Side Image Caching
45
- * **Goal:** Reduce bandwidth usage and accelerate browsing.
46
- * **Implementation:**
 
47
  - Register a Service Worker to cache thumbnails and UI assets.
48
  - Use `Cache-Control` headers for downloaded image URLs.
49
 
@@ -52,14 +58,16 @@ This document outlines a roadmap and future development plan for the **Imageboar
52
  ## 🧪 3. Quality Assurance & Automated Testing
53
 
54
  ### 3.1 End-to-End Tests with Playwright
55
- * **Goal:** Ensure code modifications do not break key features like site additions or tag searches.
56
- * **Implementation:**
 
57
  - Add E2E test suites inside a `tests/` directory.
58
  - Mock API responses from e621/Danbooru to test search, grid rendering, and selection UI without hitting actual boorus.
59
 
60
  ### 3.2 Unit and API Integration Tests
61
- * **Goal:** Validate parsing logic, schema validation, and URL building helpers.
62
- * **Implementation:**
 
63
  - Write Vitest suites to verify mapping functions in `src/lib/grabber/sites.ts` and zip streaming endpoints.
64
 
65
  ---
@@ -67,6 +75,7 @@ This document outlines a roadmap and future development plan for the **Imageboar
67
  ## 🌐 4. Deployment & Infrastructure Optimization
68
 
69
  ### 4.1 Hugging Face Storage Integration
70
- * **Goal:** Retain user session data or cache assets on persistent storage.
71
- * **Implementation:**
 
72
  - Connect a Hugging Face Dataset or persistent Space storage to backup logs or temporary configuration files if requested.
 
7
  ## 🚀 1. Feature Enhancements
8
 
9
  ### 1.1 Support for Authenticated APIs (API Keys & Username)
10
+
11
+ - **Goal:** Allow users to safely search and download from private or rate-limited sites (like Danbooru premium tiers, e621 personal accounts).
12
+ - **Implementation:**
13
  - Add credential fields (API Key / Password Hash, Username) to the **Add Site** dialog.
14
  - Securely transmit credentials to the server using HTTP headers or request bodies.
15
  - Store credentials locally in the browser's `localStorage` or session-only cookies so they are never stored globally on the server.
16
 
17
  ### 1.2 Persistent Site Configuration
18
+
19
+ - **Goal:** Avoid losing newly added schemas when refreshing or clearing session storage.
20
+ - **Implementation:**
21
  - Sync the list of added custom sites with IndexedDB or `localStorage`.
22
  - Provide an **Export/Import Sites** button to allow users to backup their custom configurations as JSON.
23
 
24
  ### 1.3 Advanced Query Builder
25
+
26
+ - **Goal:** Help non-technical users build complex booru search queries.
27
+ - **Implementation:**
28
  - Add UI controls for searching by rating (Safe, Questionable, Explicit), score range, upload date, or file extension.
29
  - Autocomplete / tag suggestion box by querying the active site's tag API endpoint (e.g., `/tags.json?search[name_matches]=*`).
30
 
31
  ### 1.4 Enhanced Lightbox & Media Viewer
32
+
33
+ - **Goal:** Support video playback and metadata exploration.
34
+ - **Implementation:**
35
  - Auto-detect video extensions (`.webm`, `.mp4`) in the Lightbox and render a native HTML `<video>` player with volume and speed controls.
36
  - Show side-panel info: Artist, Character, Copyright, and Meta tags. Click a tag to search.
37
 
 
40
  ## 🛠️ 2. Architectural & Performance Improvements
41
 
42
  ### 2.1 Concurrent / Streamed Server Downloads
43
+
44
+ - **Goal:** Speed up ZIP compilation and handle massive file downloads without causing OOM (Out Of Memory) crashes.
45
+ - **Implementation:**
46
  - Implement streaming ZIP using `fflate` on the server so bytes are sent back to the client immediately as they download, rather than buffering entire gigabytes in server memory.
47
  - Limit concurrent download workers per request to avoid IP bans from image hosters.
48
 
49
  ### 2.2 Client-Side Image Caching
50
+
51
+ - **Goal:** Reduce bandwidth usage and accelerate browsing.
52
+ - **Implementation:**
53
  - Register a Service Worker to cache thumbnails and UI assets.
54
  - Use `Cache-Control` headers for downloaded image URLs.
55
 
 
58
  ## 🧪 3. Quality Assurance & Automated Testing
59
 
60
  ### 3.1 End-to-End Tests with Playwright
61
+
62
+ - **Goal:** Ensure code modifications do not break key features like site additions or tag searches.
63
+ - **Implementation:**
64
  - Add E2E test suites inside a `tests/` directory.
65
  - Mock API responses from e621/Danbooru to test search, grid rendering, and selection UI without hitting actual boorus.
66
 
67
  ### 3.2 Unit and API Integration Tests
68
+
69
+ - **Goal:** Validate parsing logic, schema validation, and URL building helpers.
70
+ - **Implementation:**
71
  - Write Vitest suites to verify mapping functions in `src/lib/grabber/sites.ts` and zip streaming endpoints.
72
 
73
  ---
 
75
  ## 🌐 4. Deployment & Infrastructure Optimization
76
 
77
  ### 4.1 Hugging Face Storage Integration
78
+
79
+ - **Goal:** Retain user session data or cache assets on persistent storage.
80
+ - **Implementation:**
81
  - Connect a Hugging Face Dataset or persistent Space storage to backup logs or temporary configuration files if requested.
src/components/grabber/AddSiteDialog.tsx CHANGED
@@ -47,6 +47,9 @@ const DEFAULT: SiteConfig = {
47
  export function AddSiteDialog({ open, onOpenChange, onAdd }: Props) {
48
  const [s, setS] = useState<SiteConfig>(DEFAULT);
49
  const [headersText, setHeadersText] = useState("");
 
 
 
50
  const set = <K extends keyof SiteConfig>(k: K, v: SiteConfig[K]) =>
51
  setS((p) => ({ ...p, [k]: v }));
52
  const setField = (k: keyof SiteConfig["fields"], v: string) =>
@@ -54,6 +57,60 @@ export function AddSiteDialog({ open, onOpenChange, onAdd }: Props) {
54
  const setParam = (k: keyof SiteConfig["params"], v: string) =>
55
  setS((p) => ({ ...p, params: { ...p.params, [k]: v } }));
56
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
57
  const submit = () => {
58
  let headers: Record<string, string> | undefined;
59
  if (headersText.trim()) {
@@ -74,6 +131,29 @@ export function AddSiteDialog({ open, onOpenChange, onAdd }: Props) {
74
  <DialogHeader>
75
  <DialogTitle>Add Site (schema)</DialogTitle>
76
  </DialogHeader>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
77
  <div className="grid grid-cols-2 gap-3">
78
  <Field label="ID (unique)">
79
  <Input value={s.id} onChange={(e) => set("id", e.target.value)} />
 
47
  export function AddSiteDialog({ open, onOpenChange, onAdd }: Props) {
48
  const [s, setS] = useState<SiteConfig>(DEFAULT);
49
  const [headersText, setHeadersText] = useState("");
50
+ const [pasteSchemaText, setPasteSchemaText] = useState("");
51
+ const [parseError, setParseError] = useState("");
52
+
53
  const set = <K extends keyof SiteConfig>(k: K, v: SiteConfig[K]) =>
54
  setS((p) => ({ ...p, [k]: v }));
55
  const setField = (k: keyof SiteConfig["fields"], v: string) =>
 
57
  const setParam = (k: keyof SiteConfig["params"], v: string) =>
58
  setS((p) => ({ ...p, params: { ...p.params, [k]: v } }));
59
 
60
+ const handlePasteSchema = () => {
61
+ setParseError("");
62
+ if (!pasteSchemaText.trim()) return;
63
+ try {
64
+ const parsed = JSON.parse(pasteSchemaText);
65
+ if (typeof parsed !== "object" || parsed === null) {
66
+ throw new Error("Schema must be an object");
67
+ }
68
+
69
+ const merged: SiteConfig = {
70
+ id: parsed.id || s.id,
71
+ name: parsed.name || s.name,
72
+ baseUrl: parsed.baseUrl || s.baseUrl,
73
+ searchPath: parsed.searchPath || s.searchPath,
74
+ params: {
75
+ tags: parsed.params?.tags || s.params.tags,
76
+ page: parsed.params?.page || s.params.page,
77
+ limit: parsed.params?.limit || s.params.limit,
78
+ },
79
+ postsPath: typeof parsed.postsPath === "string" ? parsed.postsPath : s.postsPath,
80
+ fields: {
81
+ id: parsed.fields?.id || s.fields.id,
82
+ previewUrl: parsed.fields?.previewUrl || s.fields.previewUrl,
83
+ fileUrl: parsed.fields?.fileUrl || s.fields.fileUrl,
84
+ ext: parsed.fields?.ext || s.fields.ext,
85
+ tags: parsed.fields?.tags || s.fields.tags,
86
+ source: parsed.fields?.source || s.fields.source,
87
+ rating: parsed.fields?.rating || s.fields.rating,
88
+ score: parsed.fields?.score || s.fields.score,
89
+ sampleUrl: parsed.fields?.sampleUrl || s.fields.sampleUrl,
90
+ fileSize: parsed.fields?.fileSize || s.fields.fileSize,
91
+ width: parsed.fields?.width || s.fields.width,
92
+ height: parsed.fields?.height || s.fields.height,
93
+ md5: parsed.fields?.md5 || s.fields.md5,
94
+ uploader: parsed.fields?.uploader || s.fields.uploader,
95
+ createdAt: parsed.fields?.createdAt || s.fields.createdAt,
96
+ },
97
+ };
98
+
99
+ if (parsed.headers && typeof parsed.headers === "object") {
100
+ setHeadersText(JSON.stringify(parsed.headers, null, 2));
101
+ }
102
+
103
+ setS(merged);
104
+ setPasteSchemaText("");
105
+ } catch (e: unknown) {
106
+ if (e instanceof Error) {
107
+ setParseError(e.message);
108
+ } else {
109
+ setParseError("Failed to parse JSON");
110
+ }
111
+ }
112
+ };
113
+
114
  const submit = () => {
115
  let headers: Record<string, string> | undefined;
116
  if (headersText.trim()) {
 
131
  <DialogHeader>
132
  <DialogTitle>Add Site (schema)</DialogTitle>
133
  </DialogHeader>
134
+
135
+ {/* New Paste Schema Input Box */}
136
+ <div className="border p-3 rounded-md bg-muted/30 mb-4">
137
+ <Label className="text-xs font-semibold mb-1 block">
138
+ Paste Site Configuration JSON (API Support)
139
+ </Label>
140
+ <div className="flex gap-2">
141
+ <Textarea
142
+ rows={3}
143
+ value={pasteSchemaText}
144
+ onChange={(e) => setPasteSchemaText(e.target.value)}
145
+ placeholder={`Paste JSON schema here, e.g.:\n{\n "id": "danbooru",\n "name": "Danbooru",\n "baseUrl": "https://danbooru.donmai.us",\n "fields": { "fileUrl": "file_url" }\n}`}
146
+ className="font-mono text-xs flex-1"
147
+ />
148
+ <Button variant="secondary" size="sm" onClick={handlePasteSchema} className="self-end">
149
+ Apply Schema
150
+ </Button>
151
+ </div>
152
+ {parseError && (
153
+ <div className="text-xs text-destructive mt-1 font-semibold">{parseError}</div>
154
+ )}
155
+ </div>
156
+
157
  <div className="grid grid-cols-2 gap-3">
158
  <Field label="ID (unique)">
159
  <Input value={s.id} onChange={(e) => set("id", e.target.value)} />