ollamadiffuser 1.1.4__py3-none-any.whl → 1.1.6__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -4,7 +4,7 @@ OllamaDiffuser - Local AI Image Generation with Ollama-style CLI
4
4
  A tool for managing and running Stable Diffusion, FLUX.1, and other AI image generation models locally.
5
5
  """
6
6
 
7
- __version__ = "1.1.4"
7
+ __version__ = "1.1.6"
8
8
  __author__ = "OllamaDiffuser Team"
9
9
  __email__ = "ollamadiffuser@gmail.com"
10
10
  __description__ = "🎨 Local AI Image Generation with Ollama-style CLI for Stable Diffusion, FLUX.1, and LoRA support"
@@ -31,6 +31,13 @@ def verify_deps():
31
31
  except ImportError:
32
32
  deps_status['controlnet-aux'] = "❌ Missing"
33
33
 
34
+ # MediaPipe check (optional but recommended for full ControlNet functionality)
35
+ try:
36
+ import mediapipe
37
+ deps_status['mediapipe'] = f"✅ Installed (v{mediapipe.__version__})"
38
+ except ImportError:
39
+ deps_status['mediapipe'] = "⚠️ Optional (recommended for face/pose ControlNet)"
40
+
34
41
  # Torch check
35
42
  try:
36
43
  import torch
@@ -51,15 +58,18 @@ def verify_deps():
51
58
  table.add_column("Status", style="white")
52
59
 
53
60
  missing_deps = []
61
+ optional_deps = []
54
62
  for dep, status in deps_status.items():
55
63
  table.add_row(dep, status)
56
64
  if "❌ Missing" in status:
57
65
  missing_deps.append(dep)
66
+ elif "⚠️ Optional" in status:
67
+ optional_deps.append(dep)
58
68
 
59
69
  console.print(table)
60
70
 
61
71
  if missing_deps:
62
- console.print(f"\n⚠️ [bold yellow]{len(missing_deps)} dependencies are missing[/bold yellow]")
72
+ console.print(f"\n⚠️ [bold yellow]{len(missing_deps)} required dependencies are missing[/bold yellow]")
63
73
 
64
74
  if click.confirm("\nWould you like to install missing dependencies?"):
65
75
  for dep in missing_deps:
@@ -82,7 +92,23 @@ def verify_deps():
82
92
  console.print(f"❌ Failed to install {dep}: {e}")
83
93
 
84
94
  console.print("\n🔄 Re-run 'ollamadiffuser verify-deps' to check status")
85
- else:
95
+
96
+ if optional_deps:
97
+ console.print(f"\n💡 [bold blue]{len(optional_deps)} optional dependencies available for enhanced functionality[/bold blue]")
98
+
99
+ if click.confirm("\nWould you like to install optional dependencies for full ControlNet support?"):
100
+ for dep in optional_deps:
101
+ console.print(f"\n📦 Installing {dep}...")
102
+
103
+ try:
104
+ subprocess.check_call([
105
+ sys.executable, "-m", "pip", "install", dep
106
+ ])
107
+ console.print(f"✅ {dep} installed successfully")
108
+ except subprocess.CalledProcessError as e:
109
+ console.print(f"❌ Failed to install {dep}: {e}")
110
+
111
+ if not missing_deps and not optional_deps:
86
112
  console.print("\n🎉 [bold green]All dependencies are installed![/bold green]")
87
113
 
88
114
  # Check ControlNet preprocessors
@@ -96,6 +122,10 @@ def verify_deps():
96
122
  console.print("⚠️ ControlNet preprocessors not fully available")
97
123
  except Exception as e:
98
124
  console.print(f"❌ Error testing preprocessors: {e}")
125
+
126
+ # Show warning suppression tip
127
+ console.print("\n💡 [bold blue]Tip:[/bold blue] To suppress harmless import warnings, run:")
128
+ console.print(" [cyan]export PYTHONWARNINGS=\"ignore::UserWarning,ignore::FutureWarning\"[/cyan]")
99
129
 
100
130
  @click.command()
101
131
  def doctor():
@@ -130,4 +160,68 @@ def doctor():
130
160
  # Run dependency check
131
161
  console.print("\n" + "="*50)
132
162
  ctx = click.Context(verify_deps)
133
- ctx.invoke(verify_deps)
163
+ ctx.invoke(verify_deps)
164
+
165
+ @click.command()
166
+ @click.option('--force', is_flag=True, help='Force recreation of all samples even if they exist')
167
+ def create_samples(force):
168
+ """Create ControlNet sample images for the Web UI"""
169
+ console.print("\n🎨 [bold blue]Creating ControlNet Sample Images[/bold blue]")
170
+
171
+ try:
172
+ from pathlib import Path
173
+ from ..ui.web import ensure_samples_exist
174
+
175
+ # Get samples directory path
176
+ samples_dir = Path(__file__).parent.parent / "ui" / "samples"
177
+
178
+ if force:
179
+ console.print("🗑️ Removing existing samples (force mode)")
180
+ import shutil
181
+ if samples_dir.exists():
182
+ shutil.rmtree(samples_dir)
183
+
184
+ console.print(f"📁 Samples directory: {samples_dir}")
185
+
186
+ # Create samples
187
+ ensure_samples_exist(samples_dir)
188
+
189
+ if samples_dir.exists():
190
+ # Count created samples
191
+ sample_count = 0
192
+ for sample_type in ['canny', 'depth', 'openpose', 'scribble']:
193
+ type_dir = samples_dir / sample_type
194
+ if type_dir.exists():
195
+ sample_count += len(list(type_dir.glob('*.png')))
196
+
197
+ console.print(f"\n✅ [bold green]Successfully created {sample_count} sample images![/bold green]")
198
+ console.print(f"📂 Samples saved to: {samples_dir}")
199
+
200
+ # Show sample types
201
+ table = Table(title="Created Sample Types")
202
+ table.add_column("Type", style="cyan")
203
+ table.add_column("Count", style="white")
204
+ table.add_column("Description", style="green")
205
+
206
+ descriptions = {
207
+ 'canny': 'Edge detection control',
208
+ 'depth': 'Depth map control',
209
+ 'openpose': 'Pose estimation control',
210
+ 'scribble': 'Sketch/scribble control'
211
+ }
212
+
213
+ for sample_type in ['canny', 'depth', 'openpose', 'scribble']:
214
+ type_dir = samples_dir / sample_type
215
+ if type_dir.exists():
216
+ count = len(list(type_dir.glob('*.png')))
217
+ table.add_row(sample_type.title(), str(count), descriptions.get(sample_type, ''))
218
+
219
+ console.print(table)
220
+ console.print("\n💡 These samples will appear in the Web UI for easy ControlNet testing!")
221
+ else:
222
+ console.print("❌ [bold red]Failed to create samples directory[/bold red]")
223
+
224
+ except Exception as e:
225
+ console.print(f"❌ [bold red]Error creating samples: {e}[/bold red]")
226
+ import traceback
227
+ console.print(f"[dim]{traceback.format_exc()}[/dim]")
@@ -943,6 +943,7 @@ def version():
943
943
 
944
944
  rprint("\n[dim]For help: ollamadiffuser --help[/dim]")
945
945
  rprint("[dim]For diagnostics: ollamadiffuser doctor[/dim]")
946
+ rprint("[dim]For ControlNet samples: ollamadiffuser create-samples[/dim]")
946
947
 
947
948
  @cli.command(name='verify-deps')
948
949
  def verify_deps_cmd():
@@ -958,5 +959,13 @@ def doctor():
958
959
  ctx = click.Context(doctor)
959
960
  ctx.invoke(doctor)
960
961
 
962
+ @cli.command(name='create-samples')
963
+ @click.option('--force', is_flag=True, help='Force recreation of all samples even if they exist')
964
+ def create_samples_cmd(force):
965
+ """Create ControlNet sample images for the Web UI"""
966
+ from .commands import create_samples
967
+ ctx = click.Context(create_samples)
968
+ ctx.invoke(create_samples, force=force)
969
+
961
970
  if __name__ == '__main__':
962
971
  cli()
Binary file
@@ -0,0 +1,130 @@
1
+ {
2
+ "canny": {
3
+ "geometric_shapes.png": {
4
+ "title": "Geometric Shapes",
5
+ "description": "Perfect for generating architectural elements, logos, or geometric art",
6
+ "good_for": [
7
+ "architecture",
8
+ "logos",
9
+ "geometric patterns",
10
+ "modern art"
11
+ ]
12
+ },
13
+ "house_outline.png": {
14
+ "title": "House Outline",
15
+ "description": "Great for generating buildings, houses, or architectural scenes",
16
+ "good_for": [
17
+ "buildings",
18
+ "houses",
19
+ "architecture",
20
+ "real estate"
21
+ ]
22
+ },
23
+ "portrait_outline.png": {
24
+ "title": "Portrait Silhouette",
25
+ "description": "Ideal for generating portraits, characters, or people",
26
+ "good_for": [
27
+ "portraits",
28
+ "characters",
29
+ "people",
30
+ "headshots"
31
+ ]
32
+ }
33
+ },
34
+ "depth": {
35
+ "radial_gradient.png": {
36
+ "title": "Radial Depth",
37
+ "description": "Perfect for centered subjects with depth, like portraits or objects",
38
+ "good_for": [
39
+ "portraits",
40
+ "centered objects",
41
+ "product photography",
42
+ "focus effects"
43
+ ]
44
+ },
45
+ "linear_perspective.png": {
46
+ "title": "Linear Perspective",
47
+ "description": "Great for landscapes, roads, or scenes with distance",
48
+ "good_for": [
49
+ "landscapes",
50
+ "roads",
51
+ "horizons",
52
+ "perspective scenes"
53
+ ]
54
+ },
55
+ "sphere_3d.png": {
56
+ "title": "3D Sphere",
57
+ "description": "Ideal for round objects, balls, or 3D elements",
58
+ "good_for": [
59
+ "spheres",
60
+ "balls",
61
+ "3D objects",
62
+ "rounded elements"
63
+ ]
64
+ }
65
+ },
66
+ "openpose": {
67
+ "standing_pose.png": {
68
+ "title": "Standing Pose",
69
+ "description": "Basic standing position, great for portraits and character art",
70
+ "good_for": [
71
+ "standing portraits",
72
+ "character design",
73
+ "fashion",
74
+ "formal poses"
75
+ ]
76
+ },
77
+ "running_pose.png": {
78
+ "title": "Running Pose",
79
+ "description": "Dynamic action pose, perfect for sports or movement scenes",
80
+ "good_for": [
81
+ "sports",
82
+ "action scenes",
83
+ "dynamic poses",
84
+ "movement"
85
+ ]
86
+ },
87
+ "sitting_pose.png": {
88
+ "title": "Sitting Pose",
89
+ "description": "Relaxed sitting position, ideal for casual or indoor scenes",
90
+ "good_for": [
91
+ "casual portraits",
92
+ "indoor scenes",
93
+ "relaxed poses",
94
+ "sitting figures"
95
+ ]
96
+ }
97
+ },
98
+ "scribble": {
99
+ "tree_sketch.png": {
100
+ "title": "Tree Sketch",
101
+ "description": "Simple tree drawing, great for nature and landscape scenes",
102
+ "good_for": [
103
+ "nature",
104
+ "landscapes",
105
+ "trees",
106
+ "outdoor scenes"
107
+ ]
108
+ },
109
+ "face_sketch.png": {
110
+ "title": "Face Sketch",
111
+ "description": "Basic face outline, perfect for portrait generation",
112
+ "good_for": [
113
+ "portraits",
114
+ "faces",
115
+ "character art",
116
+ "headshots"
117
+ ]
118
+ },
119
+ "car_sketch.png": {
120
+ "title": "Car Sketch",
121
+ "description": "Simple vehicle outline, ideal for automotive or transportation themes",
122
+ "good_for": [
123
+ "cars",
124
+ "vehicles",
125
+ "transportation",
126
+ "automotive"
127
+ ]
128
+ }
129
+ }
130
+ }
ollamadiffuser/ui/web.py CHANGED
@@ -19,6 +19,260 @@ logger = logging.getLogger(__name__)
19
19
  templates_dir = Path(__file__).parent / "templates"
20
20
  templates = Jinja2Templates(directory=str(templates_dir))
21
21
 
22
+ def ensure_samples_exist(samples_dir: Path):
23
+ """Ensure ControlNet sample images exist, create them if missing"""
24
+ try:
25
+ # Check if samples directory exists and has content
26
+ if not samples_dir.exists() or not any(samples_dir.iterdir()):
27
+ logger.info("Creating ControlNet sample images...")
28
+ samples_dir.mkdir(exist_ok=True)
29
+
30
+ # Import and run the sample creation functions from the standalone script
31
+ # We'll reimplement the functions here to avoid importing the standalone script
32
+ _create_controlnet_samples(samples_dir)
33
+ logger.info("ControlNet sample images created successfully")
34
+ else:
35
+ # Check if all required sample types exist
36
+ required_types = ['canny', 'depth', 'openpose', 'scribble']
37
+ missing_types = []
38
+
39
+ for sample_type in required_types:
40
+ type_dir = samples_dir / sample_type
41
+ if not type_dir.exists() or not any(type_dir.iterdir()):
42
+ missing_types.append(sample_type)
43
+
44
+ if missing_types:
45
+ logger.info(f"Creating missing sample types: {missing_types}")
46
+ _create_controlnet_samples(samples_dir, only_types=missing_types)
47
+ logger.info("Missing sample types created successfully")
48
+ except Exception as e:
49
+ logger.warning(f"Failed to create sample images: {e}")
50
+
51
+ def _create_controlnet_samples(samples_dir: Path, only_types=None):
52
+ """Create ControlNet sample images"""
53
+ import numpy as np
54
+ import math
55
+ from PIL import ImageDraw
56
+
57
+ # Create sample directories
58
+ sample_types = only_types or ['canny', 'depth', 'openpose', 'scribble']
59
+ for sample_type in sample_types:
60
+ (samples_dir / sample_type).mkdir(exist_ok=True)
61
+
62
+ if 'canny' in sample_types:
63
+ # 1. Simple geometric shapes
64
+ img = Image.new('RGB', (512, 512), 'white')
65
+ draw = ImageDraw.Draw(img)
66
+ draw.rectangle([50, 50, 200, 150], outline='black', width=3)
67
+ draw.ellipse([300, 50, 450, 200], outline='black', width=3)
68
+ draw.polygon([(100, 300), (200, 200), (300, 300)], outline='black', width=3)
69
+ draw.polygon([(400, 250), (450, 300), (400, 350), (350, 300)], outline='black', width=3)
70
+ img.save(samples_dir / 'canny' / 'geometric_shapes.png')
71
+
72
+ # 2. Simple house outline
73
+ img = Image.new('RGB', (512, 512), 'white')
74
+ draw = ImageDraw.Draw(img)
75
+ draw.rectangle([150, 250, 350, 400], outline='black', width=3)
76
+ draw.polygon([(130, 250), (250, 150), (370, 250)], outline='black', width=3)
77
+ draw.rectangle([220, 320, 280, 400], outline='black', width=2)
78
+ draw.rectangle([170, 280, 210, 320], outline='black', width=2)
79
+ draw.rectangle([290, 280, 330, 320], outline='black', width=2)
80
+ draw.rectangle([300, 170, 330, 220], outline='black', width=2)
81
+ img.save(samples_dir / 'canny' / 'house_outline.png')
82
+
83
+ # 3. Portrait silhouette
84
+ img = Image.new('RGB', (512, 512), 'white')
85
+ draw = ImageDraw.Draw(img)
86
+ draw.ellipse([180, 100, 330, 280], outline='black', width=3)
87
+ draw.rectangle([235, 280, 275, 320], outline='black', width=3)
88
+ draw.arc([150, 300, 360, 450], start=0, end=180, fill='black', width=3)
89
+ img.save(samples_dir / 'canny' / 'portrait_outline.png')
90
+
91
+ if 'depth' in sample_types:
92
+ # 1. Radial gradient
93
+ img = Image.new('RGB', (512, 512), 'white')
94
+ pixels = np.zeros((512, 512, 3), dtype=np.uint8)
95
+ center_x, center_y = 256, 256
96
+ max_distance = 200
97
+ for y in range(512):
98
+ for x in range(512):
99
+ distance = min(np.sqrt((x - center_x)**2 + (y - center_y)**2), max_distance)
100
+ intensity = int(255 * (1 - distance / max_distance))
101
+ pixels[y, x] = [intensity, intensity, intensity]
102
+ Image.fromarray(pixels).save(samples_dir / 'depth' / 'radial_gradient.png')
103
+
104
+ # 2. Linear perspective
105
+ pixels = np.zeros((512, 512, 3), dtype=np.uint8)
106
+ for y in range(512):
107
+ intensity = int(255 * (y / 512))
108
+ pixels[y, :] = [intensity, intensity, intensity]
109
+ Image.fromarray(pixels).save(samples_dir / 'depth' / 'linear_perspective.png')
110
+
111
+ # 3. Simple 3D sphere
112
+ pixels = np.zeros((512, 512, 3), dtype=np.uint8)
113
+ center_x, center_y = 256, 256
114
+ radius = 150
115
+ for y in range(512):
116
+ for x in range(512):
117
+ dx = x - center_x
118
+ dy = y - center_y
119
+ distance = np.sqrt(dx**2 + dy**2)
120
+ if distance <= radius:
121
+ z = np.sqrt(radius**2 - distance**2)
122
+ intensity = int(255 * (z / radius))
123
+ pixels[y, x] = [intensity, intensity, intensity]
124
+ Image.fromarray(pixels).save(samples_dir / 'depth' / 'sphere_3d.png')
125
+
126
+ if 'openpose' in sample_types:
127
+ # 1. Standing pose
128
+ img = Image.new('RGB', (512, 512), 'black')
129
+ draw = ImageDraw.Draw(img)
130
+ draw.ellipse([240, 80, 270, 110], fill='white')
131
+ draw.line([255, 110, 255, 250], fill='white', width=4)
132
+ draw.line([255, 150, 200, 200], fill='white', width=4)
133
+ draw.line([255, 150, 310, 200], fill='white', width=4)
134
+ draw.line([255, 250, 220, 350], fill='white', width=4)
135
+ draw.line([255, 250, 290, 350], fill='white', width=4)
136
+ img.save(samples_dir / 'openpose' / 'standing_pose.png')
137
+
138
+ # 2. Running pose
139
+ img = Image.new('RGB', (512, 512), 'black')
140
+ draw = ImageDraw.Draw(img)
141
+ draw.ellipse([240, 80, 270, 110], fill='white')
142
+ draw.line([255, 110, 270, 250], fill='white', width=4)
143
+ draw.line([255, 150, 180, 180], fill='white', width=4)
144
+ draw.line([255, 150, 320, 120], fill='white', width=4)
145
+ draw.line([270, 250, 240, 350], fill='white', width=4)
146
+ draw.line([270, 250, 320, 320], fill='white', width=4)
147
+ img.save(samples_dir / 'openpose' / 'running_pose.png')
148
+
149
+ # 3. Sitting pose
150
+ img = Image.new('RGB', (512, 512), 'black')
151
+ draw = ImageDraw.Draw(img)
152
+ draw.ellipse([240, 100, 270, 130], fill='white')
153
+ draw.line([255, 130, 255, 220], fill='white', width=4)
154
+ draw.line([255, 170, 200, 220], fill='white', width=4)
155
+ draw.line([255, 170, 310, 220], fill='white', width=4)
156
+ draw.line([255, 220, 220, 280], fill='white', width=4)
157
+ draw.line([220, 280, 200, 350], fill='white', width=4)
158
+ draw.line([255, 220, 290, 280], fill='white', width=4)
159
+ draw.line([290, 280, 310, 350], fill='white', width=4)
160
+ img.save(samples_dir / 'openpose' / 'sitting_pose.png')
161
+
162
+ if 'scribble' in sample_types:
163
+ # 1. Simple tree sketch
164
+ img = Image.new('RGB', (512, 512), 'white')
165
+ draw = ImageDraw.Draw(img)
166
+ draw.line([256, 400, 256, 250], fill='black', width=8)
167
+ points = []
168
+ for i in range(20):
169
+ angle = (i / 20) * 2 * math.pi
170
+ radius = 80 + 20 * math.sin(i * 3)
171
+ x = 256 + radius * math.cos(angle)
172
+ y = 200 + radius * math.sin(angle) * 0.8
173
+ points.append((x, y))
174
+ for i in range(len(points)):
175
+ next_i = (i + 1) % len(points)
176
+ draw.line([points[i], points[next_i]], fill='black', width=3)
177
+ img.save(samples_dir / 'scribble' / 'tree_sketch.png')
178
+
179
+ # 2. Simple face sketch
180
+ img = Image.new('RGB', (512, 512), 'white')
181
+ draw = ImageDraw.Draw(img)
182
+ draw.ellipse([180, 150, 330, 320], outline='black', width=3)
183
+ draw.ellipse([210, 200, 230, 220], outline='black', width=2)
184
+ draw.ellipse([280, 200, 300, 220], outline='black', width=2)
185
+ draw.line([255, 230, 255, 250], fill='black', width=2)
186
+ draw.line([255, 250, 245, 260], fill='black', width=2)
187
+ draw.arc([230, 270, 280, 300], start=0, end=180, fill='black', width=2)
188
+ img.save(samples_dir / 'scribble' / 'face_sketch.png')
189
+
190
+ # 3. Simple car sketch
191
+ img = Image.new('RGB', (512, 512), 'white')
192
+ draw = ImageDraw.Draw(img)
193
+ draw.rectangle([100, 250, 400, 320], outline='black', width=3)
194
+ draw.rectangle([150, 200, 350, 250], outline='black', width=3)
195
+ draw.ellipse([130, 320, 170, 360], outline='black', width=3)
196
+ draw.ellipse([330, 320, 370, 360], outline='black', width=3)
197
+ draw.rectangle([170, 210, 220, 240], outline='black', width=2)
198
+ draw.rectangle([280, 210, 330, 240], outline='black', width=2)
199
+ img.save(samples_dir / 'scribble' / 'car_sketch.png')
200
+
201
+ # Create metadata file
202
+ metadata = {
203
+ "canny": {
204
+ "geometric_shapes.png": {
205
+ "title": "Geometric Shapes",
206
+ "description": "Perfect for generating architectural elements, logos, or geometric art",
207
+ "good_for": ["architecture", "logos", "geometric patterns", "modern art"]
208
+ },
209
+ "house_outline.png": {
210
+ "title": "House Outline",
211
+ "description": "Great for generating buildings, houses, or architectural scenes",
212
+ "good_for": ["buildings", "houses", "architecture", "real estate"]
213
+ },
214
+ "portrait_outline.png": {
215
+ "title": "Portrait Silhouette",
216
+ "description": "Ideal for generating portraits, characters, or people",
217
+ "good_for": ["portraits", "characters", "people", "headshots"]
218
+ }
219
+ },
220
+ "depth": {
221
+ "radial_gradient.png": {
222
+ "title": "Radial Depth",
223
+ "description": "Perfect for centered subjects with depth, like portraits or objects",
224
+ "good_for": ["portraits", "centered objects", "product photography", "focus effects"]
225
+ },
226
+ "linear_perspective.png": {
227
+ "title": "Linear Perspective",
228
+ "description": "Great for landscapes, roads, or scenes with distance",
229
+ "good_for": ["landscapes", "roads", "horizons", "perspective scenes"]
230
+ },
231
+ "sphere_3d.png": {
232
+ "title": "3D Sphere",
233
+ "description": "Ideal for round objects, balls, or 3D elements",
234
+ "good_for": ["spheres", "balls", "3D objects", "rounded elements"]
235
+ }
236
+ },
237
+ "openpose": {
238
+ "standing_pose.png": {
239
+ "title": "Standing Pose",
240
+ "description": "Basic standing position, great for portraits and character art",
241
+ "good_for": ["standing portraits", "character design", "fashion", "formal poses"]
242
+ },
243
+ "running_pose.png": {
244
+ "title": "Running Pose",
245
+ "description": "Dynamic action pose, perfect for sports or movement scenes",
246
+ "good_for": ["sports", "action scenes", "dynamic poses", "movement"]
247
+ },
248
+ "sitting_pose.png": {
249
+ "title": "Sitting Pose",
250
+ "description": "Relaxed sitting position, ideal for casual or indoor scenes",
251
+ "good_for": ["casual portraits", "indoor scenes", "relaxed poses", "sitting figures"]
252
+ }
253
+ },
254
+ "scribble": {
255
+ "tree_sketch.png": {
256
+ "title": "Tree Sketch",
257
+ "description": "Simple tree drawing, great for nature and landscape scenes",
258
+ "good_for": ["nature", "landscapes", "trees", "outdoor scenes"]
259
+ },
260
+ "face_sketch.png": {
261
+ "title": "Face Sketch",
262
+ "description": "Basic face outline, perfect for portrait generation",
263
+ "good_for": ["portraits", "faces", "character art", "headshots"]
264
+ },
265
+ "car_sketch.png": {
266
+ "title": "Car Sketch",
267
+ "description": "Simple vehicle outline, ideal for automotive or transportation themes",
268
+ "good_for": ["cars", "vehicles", "transportation", "automotive"]
269
+ }
270
+ }
271
+ }
272
+
273
+ with open(samples_dir / "metadata.json", 'w') as f:
274
+ json.dump(metadata, f, indent=2)
275
+
22
276
  def create_ui_app() -> FastAPI:
23
277
  """Create Web UI application"""
24
278
  app = FastAPI(title="OllamaDiffuser Web UI")
@@ -27,6 +281,10 @@ def create_ui_app() -> FastAPI:
27
281
  samples_dir = Path(__file__).parent / "samples"
28
282
  logger.info(f"Samples directory: {samples_dir}")
29
283
  logger.info(f"Samples directory exists: {samples_dir.exists()}")
284
+
285
+ # Ensure samples exist before mounting
286
+ ensure_samples_exist(samples_dir)
287
+
30
288
  if samples_dir.exists():
31
289
  logger.info(f"Mounting samples directory: {samples_dir}")
32
290
  app.mount("/samples", StaticFiles(directory=str(samples_dir)), name="samples")
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ollamadiffuser
3
- Version: 1.1.4
3
+ Version: 1.1.6
4
4
  Summary: 🎨 Local AI Image Generation with Ollama-style CLI for Stable Diffusion, FLUX.1, and LoRA support
5
5
  Home-page: https://github.com/ollamadiffuser/ollamadiffuser
6
6
  Author: OllamaDiffuser Team
@@ -0,0 +1,42 @@
1
+ ollamadiffuser/__init__.py,sha256=DWqHxjTy1ekkwx7FhVOWIannwUuINWT2aPNKNOEcC7o,1127
2
+ ollamadiffuser/__main__.py,sha256=tNWMvEHq4ddtKLp7DrhIoOdnFw3F8RNrETC_u5xpkFI,141
3
+ ollamadiffuser/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ ollamadiffuser/api/server.py,sha256=4-3gT8W1404bxvJ7y9htvKbd2yxrrbtAUvT7shOlJss,17679
5
+ ollamadiffuser/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
+ ollamadiffuser/cli/commands.py,sha256=Pe0vyfGiffwd10QlVxBCTtNnMqHi8nJ3oNn_k8nAi5k,8903
7
+ ollamadiffuser/cli/main.py,sha256=Iea_jVRu_Z3CViIcRTZM_SQJ7jtx91ZNaRvilxXqfqk,41908
8
+ ollamadiffuser/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
+ ollamadiffuser/core/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ ollamadiffuser/core/config/settings.py,sha256=VhI1vLGmOAQ7-XtyHrT5KoMpcGeGt-Mij-9NxX_ZKsI,4881
11
+ ollamadiffuser/core/inference/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
+ ollamadiffuser/core/inference/engine.py,sha256=ky76lAjWexlrgmHSZZILa3FPQP7xx2WQ0_DdW34M9Xk,57808
13
+ ollamadiffuser/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
+ ollamadiffuser/core/models/manager.py,sha256=vO1Az_aO5lZKMgSyK_6j2wT5nzPMowZgKhcH2mQVLkI,24139
15
+ ollamadiffuser/core/utils/__init__.py,sha256=ZdXZWX1hfDnnV6OmRD6UStNljDJIQ892da2CtC-zdDw,31
16
+ ollamadiffuser/core/utils/controlnet_preprocessors.py,sha256=v21X_Bk-a4gKbUZUKoeP2W8TSGlv-ST8IYNsn3NrZ2c,15446
17
+ ollamadiffuser/core/utils/download_utils.py,sha256=DvCt-cjH6WSBJniJT112b4a9AUzlwOYhQtPuEfISmtM,20961
18
+ ollamadiffuser/core/utils/lora_manager.py,sha256=SrZydPSGJqCS_Vek35bEdG2Q51qCOLZmPvnNzUjjIN0,14328
19
+ ollamadiffuser/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
+ ollamadiffuser/ui/web.py,sha256=hK41sF9uVR_14lc4AlTXaGjw5m7aiJXpVp1cSOOmkT8,27715
21
+ ollamadiffuser/ui/samples/.DS_Store,sha256=LcBPjptcaMu-icn9SdgIBfMMGa2R7DzMwMVd1cODPiU,6148
22
+ ollamadiffuser/ui/samples/metadata.json,sha256=ZDlbE__qlihTdwv8Ao_1Hs0557Qqdksduo_OC9VxX3o,3286
23
+ ollamadiffuser/ui/samples/canny/geometric_shapes.png,sha256=-rkuIgFlS3be4WqIjC8D_H6ltIlQf08hXNKPbz2i48I,3833
24
+ ollamadiffuser/ui/samples/canny/house_outline.png,sha256=UvEh9A8e-cJiuMagggugowhkYw_YKMf0eCfU-Iw3q9A,2703
25
+ ollamadiffuser/ui/samples/canny/portrait_outline.png,sha256=FIhkgBsw0ermJFtLfP0AekszkU57feXK38uENIV1ZnE,3152
26
+ ollamadiffuser/ui/samples/depth/linear_perspective.png,sha256=pJV1wQD-4m5nGU4f93PVRlhqx2p8WhaaYVpKHnu-n1o,1943
27
+ ollamadiffuser/ui/samples/depth/radial_gradient.png,sha256=QXxZDtFyn6iaXkrhVuAQCnPqju1EIrqQbVH2cKYGR5E,18464
28
+ ollamadiffuser/ui/samples/depth/sphere_3d.png,sha256=43Zm8YTtZ__iuPUXoIynhF0AiWaQlPYpcPtQov0GZuI,20842
29
+ ollamadiffuser/ui/samples/openpose/running_pose.png,sha256=T_5qMe4wW-fAae0NA4-ma7f9D5wbXT3vROJOhI_773g,2266
30
+ ollamadiffuser/ui/samples/openpose/sitting_pose.png,sha256=JO7F_IoS8eUbyl-J68fIHQ0cj35XMVvWKJ1Yez_dYZ4,2426
31
+ ollamadiffuser/ui/samples/openpose/standing_pose.png,sha256=wfgI2aIPi_TpXYgvuan7mUjx7RwmyC0jUKT5A3idPz4,2326
32
+ ollamadiffuser/ui/samples/scribble/car_sketch.png,sha256=Y7pUIDrWZf3z9pJamNhlx5-nGiA3B64eEvW14qLz5TY,2244
33
+ ollamadiffuser/ui/samples/scribble/face_sketch.png,sha256=MVVYy_aS48xoS_RnIDzLUaDQ8m_y55TuAAP7X5P_xtk,3025
34
+ ollamadiffuser/ui/samples/scribble/tree_sketch.png,sha256=3P-NGgW25xRwreDxiBYKcDhd2oHZAwKSkjNVM5oPTWY,3017
35
+ ollamadiffuser/ui/templates/index.html,sha256=qTQVFxiTbeZ90O-iNqWC_4pYP6yyIs2z6U69VJPqAB4,38176
36
+ ollamadiffuser/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
37
+ ollamadiffuser-1.1.6.dist-info/licenses/LICENSE,sha256=cnGL9l2P510Uk3TCnv62kot6vAfdSawhOZh7Y-oYoIE,1071
38
+ ollamadiffuser-1.1.6.dist-info/METADATA,sha256=bDoFxbnLvkp40zA1PIkGDXBvznaS7FQ2PEKPimPLfJI,17070
39
+ ollamadiffuser-1.1.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
40
+ ollamadiffuser-1.1.6.dist-info/entry_points.txt,sha256=tHXXO3N0GSnIobDe_eSOLfHPjjVFjeTg2Fd-APoD6sY,64
41
+ ollamadiffuser-1.1.6.dist-info/top_level.txt,sha256=97wOGgTCxDE765Nr_o7B4Kwr_M_jy8fCCeQ81sMKlC4,15
42
+ ollamadiffuser-1.1.6.dist-info/RECORD,,
@@ -1,28 +0,0 @@
1
- ollamadiffuser/__init__.py,sha256=45W-F0Z4mYKdyHDKG_s1yfdAn72o2ZgFDMR2y_ylm0Q,1127
2
- ollamadiffuser/__main__.py,sha256=tNWMvEHq4ddtKLp7DrhIoOdnFw3F8RNrETC_u5xpkFI,141
3
- ollamadiffuser/api/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
- ollamadiffuser/api/server.py,sha256=4-3gT8W1404bxvJ7y9htvKbd2yxrrbtAUvT7shOlJss,17679
5
- ollamadiffuser/cli/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
6
- ollamadiffuser/cli/commands.py,sha256=qEYCAt07O37kAPBNlhYwyS9rEZWw-rw5UgtiqWoZGdo,4715
7
- ollamadiffuser/cli/main.py,sha256=f1jopRvZsfgQ49gvb-iTMCP_XCmHpuSnTUrYqB4Mp5w,41481
8
- ollamadiffuser/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
- ollamadiffuser/core/config/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
- ollamadiffuser/core/config/settings.py,sha256=VhI1vLGmOAQ7-XtyHrT5KoMpcGeGt-Mij-9NxX_ZKsI,4881
11
- ollamadiffuser/core/inference/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
12
- ollamadiffuser/core/inference/engine.py,sha256=ky76lAjWexlrgmHSZZILa3FPQP7xx2WQ0_DdW34M9Xk,57808
13
- ollamadiffuser/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
14
- ollamadiffuser/core/models/manager.py,sha256=vO1Az_aO5lZKMgSyK_6j2wT5nzPMowZgKhcH2mQVLkI,24139
15
- ollamadiffuser/core/utils/__init__.py,sha256=ZdXZWX1hfDnnV6OmRD6UStNljDJIQ892da2CtC-zdDw,31
16
- ollamadiffuser/core/utils/controlnet_preprocessors.py,sha256=v21X_Bk-a4gKbUZUKoeP2W8TSGlv-ST8IYNsn3NrZ2c,15446
17
- ollamadiffuser/core/utils/download_utils.py,sha256=DvCt-cjH6WSBJniJT112b4a9AUzlwOYhQtPuEfISmtM,20961
18
- ollamadiffuser/core/utils/lora_manager.py,sha256=SrZydPSGJqCS_Vek35bEdG2Q51qCOLZmPvnNzUjjIN0,14328
19
- ollamadiffuser/ui/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
20
- ollamadiffuser/ui/web.py,sha256=xZ5Ja47B-51LRyadfC-gW_aE_B3D571RgpQX0RDzVxM,15290
21
- ollamadiffuser/ui/templates/index.html,sha256=qTQVFxiTbeZ90O-iNqWC_4pYP6yyIs2z6U69VJPqAB4,38176
22
- ollamadiffuser/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
23
- ollamadiffuser-1.1.4.dist-info/licenses/LICENSE,sha256=cnGL9l2P510Uk3TCnv62kot6vAfdSawhOZh7Y-oYoIE,1071
24
- ollamadiffuser-1.1.4.dist-info/METADATA,sha256=_eDNrc6jcH9pYlMCvRLz4tFQvY2zCpEUFelEFAhEApo,17070
25
- ollamadiffuser-1.1.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
26
- ollamadiffuser-1.1.4.dist-info/entry_points.txt,sha256=tHXXO3N0GSnIobDe_eSOLfHPjjVFjeTg2Fd-APoD6sY,64
27
- ollamadiffuser-1.1.4.dist-info/top_level.txt,sha256=97wOGgTCxDE765Nr_o7B4Kwr_M_jy8fCCeQ81sMKlC4,15
28
- ollamadiffuser-1.1.4.dist-info/RECORD,,