Esmaill1 commited on
Commit
aa82c4b
·
1 Parent(s): ab467eb

UI: Switch to Arabic language

Browse files
DEPLOYMENT.md ADDED
@@ -0,0 +1,91 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Hugging Face Spaces Deployment Guide
2
+
3
+ This project is configured for deployment on **Hugging Face Spaces** using the **Docker SDK**.
4
+
5
+ ## Prerequisites
6
+
7
+ 1. A [Hugging Face Account](https://huggingface.co/join).
8
+ 2. [Git](https://git-scm.com/downloads) installed on your machine.
9
+
10
+ ---
11
+
12
+ ## 1. Create a New Space
13
+
14
+ 1. Go to [huggingface.co/new-space](https://huggingface.co/new-space).
15
+ 2. **Space Name**: `rmbg-studio` (or your preferred name).
16
+ 3. **License**: `MIT`.
17
+ 4. **SDK**: Select **Docker** (This is crucial!).
18
+ 5. **Hardware**: `CPU basic` (Free) is sufficient, though `CPU upgrade` or `T4 small` will be faster.
19
+ 6. **Visibility**: Public or Private.
20
+ 7. Click **Create Space**.
21
+
22
+ ---
23
+
24
+ ## 2. Connect and Push Code
25
+
26
+ Open your terminal in the project folder and run these commands (replace `YOUR_USERNAME` with your HF username):
27
+
28
+ ```bash
29
+ # 1. Initialize Git (if not already done)
30
+ git init
31
+
32
+ # 2. Add the Hugging Face remote
33
+ git remote add origin https://huggingface.co/spaces/YOUR_USERNAME/rmbg-studio
34
+
35
+ # 3. Add all files
36
+ git add .
37
+ git add -f Dockerfile
38
+
39
+ # 4. Commit
40
+ git commit -m "Initial commit"
41
+
42
+ # 5. Push to deploy
43
+ git push -u origin main
44
+ ```
45
+
46
+ **Note:** If you get an error about "unrelated histories", use `git push -f origin main` to force overwrite the initial empty repo.
47
+
48
+ ---
49
+
50
+ ## 3. Important Configurations
51
+
52
+ ### Binary Files (Images/Icons)
53
+ Hugging Face prohibits pushing large binary files (like images) directly to git without LFS. To avoid errors, this project's `.gitignore` excludes:
54
+ - `*.png`, `*.jpg`, `*.jpeg`
55
+ - `icon.png`
56
+ - `screenshots/`
57
+
58
+ If you *need* to upload these, you must set up [Git LFS](https://git-lfs.com/) first.
59
+
60
+ ### Dependencies (`Dockerfile`)
61
+ The included `Dockerfile` is optimized for Spaces:
62
+ - Base Image: `python:3.10-slim`
63
+ - System Libs: `libgl1`, `libglib2.0-0` (required for OpenCV)
64
+ - User: Runs as non-root user `user` (ID 1000) for security.
65
+ - Output: Creates `/app/output_images` with correct permissions.
66
+
67
+ ---
68
+
69
+ ## 4. Updates & Troubleshooting
70
+
71
+ ### How to Update
72
+ Whenever you make code changes:
73
+ ```bash
74
+ git add .
75
+ git commit -m "Describe your changes"
76
+ git push
77
+ ```
78
+ The Space will automatically rebuild and restart.
79
+
80
+ ### Common Errors
81
+
82
+ **Error: `libgl1-mesa-glx` not found**
83
+ - **Cause**: Deprecated package in newer Debian versions.
84
+ - **Fix**: Use `libgl1` instead. (Already fixed in current `Dockerfile`).
85
+
86
+ **Error: Push rejected (binary files)**
87
+ - **Cause**: Trying to upload images without LFS.
88
+ - **Fix**: Run `git rm --cached icon.png` (and other images) to stop tracking them, then commit and push.
89
+
90
+ **Error: Application Error / Build Failed**
91
+ - Go to your Space's **Logs** tab to see the detailed error message. most issues are missing `requirements.txt` packages or path issues.
app/static/app.js CHANGED
@@ -53,7 +53,7 @@ document.addEventListener('DOMContentLoaded', () => {
53
  // Skip non-images
54
  if (!file.type.startsWith('image/')) continue;
55
 
56
- progressText.textContent = `Processing ${i + 1}/${totalFiles}: ${file.name}`;
57
 
58
  const formData = new FormData();
59
  formData.append('images', file); // API expects list but we act like size 1 batch
@@ -70,7 +70,7 @@ document.addEventListener('DOMContentLoaded', () => {
70
  }
71
  } catch (error) {
72
  console.error(`Error processing ${file.name}:`, error);
73
- showToast(`Failed to process ${file.name}`, 'error');
74
  }
75
 
76
  // Update Progress
@@ -79,11 +79,11 @@ document.addEventListener('DOMContentLoaded', () => {
79
  progressBar.style.width = `${percent}%`;
80
  }
81
 
82
- progressText.textContent = 'Processing Complete!';
83
  setTimeout(() => {
84
  loadingContainer.style.display = 'none';
85
  }, 1500);
86
- showToast(`Processed ${processedCount} images!`);
87
  }
88
 
89
  function createResultCard(result) {
@@ -93,14 +93,14 @@ document.addEventListener('DOMContentLoaded', () => {
93
  card.innerHTML = `
94
  <div class="result-image-wrapper">
95
  <img src="${result.url}" alt="${result.original_name}">
96
- <button class="delete-btn" title="Remove image">
97
  <i class="ph ph-x"></i>
98
  </button>
99
  </div>
100
  <div class="result-footer">
101
  <div class="file-name" title="${result.original_name}">${result.original_name}</div>
102
  <a href="${result.url}" download="${result.filename}" class="btn btn-primary" style="padding: 0.4rem 0.8rem; font-size: 0.8rem;">
103
- <i class="ph ph-download-simple"></i> Save
104
  </a>
105
  </div>
106
  `;
@@ -122,7 +122,7 @@ document.addEventListener('DOMContentLoaded', () => {
122
 
123
  // Clear All Logic
124
  clearAllBtn.addEventListener('click', () => {
125
- if(confirm('Are you sure you want to clear all processed images?')) {
126
  resultsGrid.innerHTML = '';
127
  resultsHeader.style.display = 'none';
128
  }
@@ -134,7 +134,7 @@ document.addEventListener('DOMContentLoaded', () => {
134
  if (cards.length === 0) return;
135
 
136
  downloadAllBtn.disabled = true;
137
- downloadAllBtn.innerHTML = '<div class="spinner" style="width:16px;height:16px;border-width:2px;display:inline-block"></div> Zipping...';
138
 
139
  const filenames = Array.from(cards).map(card => card.dataset.filename);
140
 
@@ -155,16 +155,16 @@ document.addEventListener('DOMContentLoaded', () => {
155
  a.click();
156
  window.URL.revokeObjectURL(url);
157
  a.remove();
158
- showToast('Batch download started!');
159
  } else {
160
- showToast('Failed to create zip', 'error');
161
  }
162
  } catch (error) {
163
  console.error(error);
164
- showToast('Network error during zip', 'error');
165
  } finally {
166
  downloadAllBtn.disabled = false;
167
- downloadAllBtn.innerHTML = '<i class="ph ph-download-simple"></i> Download All (ZIP)';
168
  }
169
  });
170
 
 
53
  // Skip non-images
54
  if (!file.type.startsWith('image/')) continue;
55
 
56
+ progressText.textContent = `جاري المعالجة ${i + 1}/${totalFiles}: ${file.name}`;
57
 
58
  const formData = new FormData();
59
  formData.append('images', file); // API expects list but we act like size 1 batch
 
70
  }
71
  } catch (error) {
72
  console.error(`Error processing ${file.name}:`, error);
73
+ showToast(`فشل معالجة ${file.name}`, 'error');
74
  }
75
 
76
  // Update Progress
 
79
  progressBar.style.width = `${percent}%`;
80
  }
81
 
82
+ progressText.textContent = 'اكتملت المعالجة!';
83
  setTimeout(() => {
84
  loadingContainer.style.display = 'none';
85
  }, 1500);
86
+ showToast(`تمت معالجة ${processedCount} صورة!`);
87
  }
88
 
89
  function createResultCard(result) {
 
93
  card.innerHTML = `
94
  <div class="result-image-wrapper">
95
  <img src="${result.url}" alt="${result.original_name}">
96
+ <button class="delete-btn" title="حذف الصورة">
97
  <i class="ph ph-x"></i>
98
  </button>
99
  </div>
100
  <div class="result-footer">
101
  <div class="file-name" title="${result.original_name}">${result.original_name}</div>
102
  <a href="${result.url}" download="${result.filename}" class="btn btn-primary" style="padding: 0.4rem 0.8rem; font-size: 0.8rem;">
103
+ <i class="ph ph-download-simple"></i> حفظ
104
  </a>
105
  </div>
106
  `;
 
122
 
123
  // Clear All Logic
124
  clearAllBtn.addEventListener('click', () => {
125
+ if(confirm('هل أنت متأكد من مسح جميع الصور المعالجة؟')) {
126
  resultsGrid.innerHTML = '';
127
  resultsHeader.style.display = 'none';
128
  }
 
134
  if (cards.length === 0) return;
135
 
136
  downloadAllBtn.disabled = true;
137
+ downloadAllBtn.innerHTML = '<div class="spinner" style="width:16px;height:16px;border-width:2px;display:inline-block"></div> جاري الضغط...';
138
 
139
  const filenames = Array.from(cards).map(card => card.dataset.filename);
140
 
 
155
  a.click();
156
  window.URL.revokeObjectURL(url);
157
  a.remove();
158
+ showToast('بدأ تحميل الملف المضغوط!');
159
  } else {
160
+ showToast('فشل إنشاء الملف المضغوط', 'error');
161
  }
162
  } catch (error) {
163
  console.error(error);
164
+ showToast('خطأ في الشبكة', 'error');
165
  } finally {
166
  downloadAllBtn.disabled = false;
167
+ downloadAllBtn.innerHTML = '<i class="ph ph-download-simple"></i> تحميل الكل (ZIP)';
168
  }
169
  });
170
 
app/static/style.css CHANGED
@@ -11,7 +11,7 @@
11
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
12
  --shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06);
13
  --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05);
14
- --font-main: 'Outfit', sans-serif;
15
  --radius: 12px;
16
  --radius-lg: 16px;
17
  }
@@ -39,11 +39,11 @@ body {
39
  .sidebar {
40
  width: 280px;
41
  background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
42
- border-right: 1px solid var(--border);
43
  display: flex;
44
  flex-direction: column;
45
  padding: 1.5rem;
46
- box-shadow: 2px 0 10px rgba(0,0,0,0.03);
47
  }
48
 
49
  .logo {
@@ -86,7 +86,7 @@ body {
86
  cursor: pointer;
87
  border-radius: var(--radius);
88
  transition: all 0.2s ease;
89
- text-align: left;
90
  }
91
 
92
  .nav-item i {
@@ -380,7 +380,7 @@ body {
380
  .delete-btn {
381
  position: absolute;
382
  top: 12px;
383
- right: 12px;
384
  background: rgba(255, 255, 255, 0.95);
385
  border: none;
386
  color: #ef4444;
@@ -435,7 +435,7 @@ body {
435
  .toast {
436
  position: fixed;
437
  bottom: 24px;
438
- right: 24px;
439
  padding: 1rem 1.5rem;
440
  border-radius: var(--radius);
441
  box-shadow: var(--shadow-lg);
 
11
  --shadow-sm: 0 1px 2px rgba(0,0,0,0.05);
12
  --shadow: 0 4px 6px -1px rgba(0,0,0,0.1), 0 2px 4px -1px rgba(0,0,0,0.06);
13
  --shadow-lg: 0 10px 15px -3px rgba(0,0,0,0.1), 0 4px 6px -2px rgba(0,0,0,0.05);
14
+ --font-main: 'Cairo', sans-serif;
15
  --radius: 12px;
16
  --radius-lg: 16px;
17
  }
 
39
  .sidebar {
40
  width: 280px;
41
  background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
42
+ border-left: 1px solid var(--border); /* Changed from border-right */
43
  display: flex;
44
  flex-direction: column;
45
  padding: 1.5rem;
46
+ box-shadow: -2px 0 10px rgba(0,0,0,0.03); /* Flipped shadow direction */
47
  }
48
 
49
  .logo {
 
86
  cursor: pointer;
87
  border-radius: var(--radius);
88
  transition: all 0.2s ease;
89
+ text-align: right; /* Changed from left */
90
  }
91
 
92
  .nav-item i {
 
380
  .delete-btn {
381
  position: absolute;
382
  top: 12px;
383
+ left: 12px; /* Changed from right to left for RTL */
384
  background: rgba(255, 255, 255, 0.95);
385
  border: none;
386
  color: #ef4444;
 
435
  .toast {
436
  position: fixed;
437
  bottom: 24px;
438
+ left: 24px; /* Changed from right to left */
439
  padding: 1rem 1.5rem;
440
  border-radius: var(--radius);
441
  box-shadow: var(--shadow-lg);
app/templates/index.html CHANGED
@@ -1,14 +1,14 @@
1
  <!DOCTYPE html>
2
- <html lang="en">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>RMBG-2 Studio | AI Background Removal</title>
7
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
8
  <!-- Phosphor Icons -->
9
  <script src="https://unpkg.com/@phosphor-icons/web"></script>
10
- <!-- Google Fonts -->
11
- <link href="https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700&display=swap" rel="stylesheet">
12
  </head>
13
  <body>
14
  <div class="app-container">
@@ -22,14 +22,14 @@
22
  <nav class="nav-menu">
23
  <button class="nav-item active">
24
  <i class="ph ph-eraser"></i>
25
- Remove Background
26
  </button>
27
  </nav>
28
 
29
  <div class="sidebar-footer">
30
  <div class="status-indicator" id="status-indicator">
31
  <span class="dot"></span>
32
- <span class="text">Ready</span>
33
  </div>
34
  </div>
35
  </aside>
@@ -37,8 +37,8 @@
37
  <!-- Main Content -->
38
  <main class="main-content">
39
  <header class="section-header">
40
- <h1>Remove Background</h1>
41
- <p>Upload one or more images to remove backgrounds instantly.</p>
42
  </header>
43
 
44
  <div class="workspace">
@@ -46,9 +46,9 @@
46
  <input type="file" id="file-input" accept="image/*" multiple hidden>
47
  <div class="upload-content">
48
  <i class="ph ph-cloud-arrow-up"></i>
49
- <h3>Upload Images</h3>
50
- <p>Drag & drop folders or files here</p>
51
- <span class="supported-formats">JPG, PNG, WEBP (Max 50MB)</span>
52
  </div>
53
  </div>
54
 
@@ -56,17 +56,17 @@
56
  <div class="loading-bar">
57
  <div class="progress" id="progress-bar"></div>
58
  </div>
59
- <span id="progress-text">Processing...</span>
60
  </div>
61
 
62
  <div class="results-header" id="results-header" style="display: none;">
63
- <h2>Processed Images</h2>
64
  <div class="results-actions">
65
  <button id="download-all-btn" class="btn btn-primary">
66
- <i class="ph ph-download-simple"></i> Download All (ZIP)
67
  </button>
68
  <button id="clear-all-btn" class="btn btn-secondary">
69
- <i class="ph ph-trash"></i> Clear All
70
  </button>
71
  </div>
72
  </div>
 
1
  <!DOCTYPE html>
2
+ <html lang="ar" dir="rtl">
3
  <head>
4
  <meta charset="UTF-8">
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>RMBG-2 Studio | إزالة الخلفية بالذكاء الاصطناعي</title>
7
  <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
8
  <!-- Phosphor Icons -->
9
  <script src="https://unpkg.com/@phosphor-icons/web"></script>
10
+ <!-- Google Fonts: Cairo -->
11
+ <link href="https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;500;600;700&display=swap" rel="stylesheet">
12
  </head>
13
  <body>
14
  <div class="app-container">
 
22
  <nav class="nav-menu">
23
  <button class="nav-item active">
24
  <i class="ph ph-eraser"></i>
25
+ إزالة الخلفية
26
  </button>
27
  </nav>
28
 
29
  <div class="sidebar-footer">
30
  <div class="status-indicator" id="status-indicator">
31
  <span class="dot"></span>
32
+ <span class="text">جاهز</span>
33
  </div>
34
  </div>
35
  </aside>
 
37
  <!-- Main Content -->
38
  <main class="main-content">
39
  <header class="section-header">
40
+ <h1>إزالة الخلفية</h1>
41
+ <p>قم برفع صورة أو أكثر لإزالة الخلفية فوراً.</p>
42
  </header>
43
 
44
  <div class="workspace">
 
46
  <input type="file" id="file-input" accept="image/*" multiple hidden>
47
  <div class="upload-content">
48
  <i class="ph ph-cloud-arrow-up"></i>
49
+ <h3>رفع الصور</h3>
50
+ <p>اسحب وأفلت الملفات هنا</p>
51
+ <span class="supported-formats">JPG, PNG, WEBP (بحد أقصى 50 ميجابايت)</span>
52
  </div>
53
  </div>
54
 
 
56
  <div class="loading-bar">
57
  <div class="progress" id="progress-bar"></div>
58
  </div>
59
+ <span id="progress-text">جاري المعالجة...</span>
60
  </div>
61
 
62
  <div class="results-header" id="results-header" style="display: none;">
63
+ <h2>الصور المعالجة</h2>
64
  <div class="results-actions">
65
  <button id="download-all-btn" class="btn btn-primary">
66
+ <i class="ph ph-download-simple"></i> تحميل الكل (ZIP)
67
  </button>
68
  <button id="clear-all-btn" class="btn btn-secondary">
69
+ <i class="ph ph-trash"></i> مسح الكل
70
  </button>
71
  </div>
72
  </div>