deckbuilder 1.0.0b1__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.
- deckbuilder/__init__.py +22 -0
- deckbuilder/cli.py +544 -0
- deckbuilder/cli_tools.py +739 -0
- deckbuilder/engine.py +1546 -0
- deckbuilder/image_handler.py +291 -0
- deckbuilder/layout_intelligence.json +288 -0
- deckbuilder/layout_intelligence.py +398 -0
- deckbuilder/naming_conventions.py +541 -0
- deckbuilder/placeholder_types.py +101 -0
- deckbuilder/placekitten_integration.py +280 -0
- deckbuilder/structured_frontmatter.py +862 -0
- deckbuilder/table_styles.py +37 -0
- deckbuilder-1.0.0b1.dist-info/METADATA +378 -0
- deckbuilder-1.0.0b1.dist-info/RECORD +37 -0
- deckbuilder-1.0.0b1.dist-info/WHEEL +5 -0
- deckbuilder-1.0.0b1.dist-info/entry_points.txt +3 -0
- deckbuilder-1.0.0b1.dist-info/licenses/LICENSE +201 -0
- deckbuilder-1.0.0b1.dist-info/top_level.txt +4 -0
- mcp_server/__init__.py +9 -0
- mcp_server/content_analysis.py +436 -0
- mcp_server/content_optimization.py +822 -0
- mcp_server/layout_recommendations.py +595 -0
- mcp_server/main.py +550 -0
- mcp_server/tools.py +492 -0
- placekitten/README.md +561 -0
- placekitten/__init__.py +44 -0
- placekitten/core.py +184 -0
- placekitten/filters.py +183 -0
- placekitten/images/ACuteKitten-1.png +0 -0
- placekitten/images/ACuteKitten-2.png +0 -0
- placekitten/images/ACuteKitten-3.png +0 -0
- placekitten/images/TwoKitttens Playing-1.png +0 -0
- placekitten/images/TwoKitttens Playing-2.png +0 -0
- placekitten/images/TwoKitttensSleeping-1.png +0 -0
- placekitten/processor.py +262 -0
- placekitten/smart_crop.py +314 -0
- shared/__init__.py +9 -0
@@ -0,0 +1,280 @@
|
|
1
|
+
"""
|
2
|
+
PlaceKittenIntegration - Bridge between PlaceKitten library and Deckbuilder engine.
|
3
|
+
|
4
|
+
This module provides the PlaceKittenIntegration class that generates professional
|
5
|
+
fallback images using PlaceKitten when user-provided images are missing or invalid.
|
6
|
+
"""
|
7
|
+
|
8
|
+
from typing import Dict, Optional, Tuple
|
9
|
+
|
10
|
+
from .image_handler import ImageHandler
|
11
|
+
|
12
|
+
try:
|
13
|
+
from placekitten import PlaceKitten
|
14
|
+
|
15
|
+
PLACEKITTEN_AVAILABLE = True
|
16
|
+
except ImportError:
|
17
|
+
PLACEKITTEN_AVAILABLE = False
|
18
|
+
print("Warning: PlaceKitten library not available. Image fallbacks will be disabled.")
|
19
|
+
|
20
|
+
|
21
|
+
class PlaceKittenIntegration:
|
22
|
+
"""
|
23
|
+
Bridge between PlaceKitten library and Deckbuilder engine.
|
24
|
+
|
25
|
+
Generates professional fallback images with business-appropriate styling
|
26
|
+
when user-provided images are missing or invalid.
|
27
|
+
"""
|
28
|
+
|
29
|
+
def __init__(self, image_handler: ImageHandler):
|
30
|
+
"""
|
31
|
+
Initialize PlaceKittenIntegration with image handler.
|
32
|
+
|
33
|
+
Args:
|
34
|
+
image_handler: ImageHandler instance for caching and processing
|
35
|
+
"""
|
36
|
+
self.image_handler = image_handler
|
37
|
+
|
38
|
+
# Initialize PlaceKitten if available
|
39
|
+
if PLACEKITTEN_AVAILABLE:
|
40
|
+
self.pk = PlaceKitten()
|
41
|
+
else:
|
42
|
+
self.pk = None
|
43
|
+
|
44
|
+
# Professional styling configuration
|
45
|
+
self.professional_config = self._get_professional_styling()
|
46
|
+
|
47
|
+
def is_available(self) -> bool:
|
48
|
+
"""
|
49
|
+
Check if PlaceKitten integration is available.
|
50
|
+
|
51
|
+
Returns:
|
52
|
+
bool: True if PlaceKitten library is available and ready
|
53
|
+
"""
|
54
|
+
return PLACEKITTEN_AVAILABLE and self.pk is not None
|
55
|
+
|
56
|
+
def generate_fallback(
|
57
|
+
self, dimensions: Tuple[int, int], context: Optional[Dict] = None
|
58
|
+
) -> Optional[str]:
|
59
|
+
"""
|
60
|
+
Generate professional fallback image with business-appropriate styling.
|
61
|
+
|
62
|
+
Args:
|
63
|
+
dimensions: Target (width, height) for the image
|
64
|
+
context: Optional context information for consistent generation
|
65
|
+
|
66
|
+
Returns:
|
67
|
+
str: Path to generated fallback image, or None if generation failed
|
68
|
+
"""
|
69
|
+
if not self.is_available():
|
70
|
+
return None
|
71
|
+
|
72
|
+
try:
|
73
|
+
width, height = dimensions
|
74
|
+
|
75
|
+
# Generate cache key for fallback image
|
76
|
+
cache_key = self._generate_fallback_cache_key(dimensions, context)
|
77
|
+
cached_path = self.image_handler._get_cached_image(cache_key)
|
78
|
+
|
79
|
+
if cached_path:
|
80
|
+
return cached_path
|
81
|
+
|
82
|
+
# Generate new fallback image
|
83
|
+
return self._create_fallback_image(width, height, cache_key, context)
|
84
|
+
|
85
|
+
except Exception as e:
|
86
|
+
print(f"Warning: PlaceKitten fallback generation failed: {e}")
|
87
|
+
return None
|
88
|
+
|
89
|
+
def _create_fallback_image(
|
90
|
+
self, width: int, height: int, cache_key: str, context: Optional[Dict] = None
|
91
|
+
) -> Optional[str]:
|
92
|
+
"""
|
93
|
+
Create new fallback image with professional styling.
|
94
|
+
|
95
|
+
Args:
|
96
|
+
width: Target width
|
97
|
+
height: Target height
|
98
|
+
cache_key: Cache key for storing result
|
99
|
+
context: Optional context for generation
|
100
|
+
|
101
|
+
Returns:
|
102
|
+
str: Path to generated image, or None if failed
|
103
|
+
"""
|
104
|
+
try:
|
105
|
+
# Select consistent image based on context
|
106
|
+
image_id = self._select_image_id(context)
|
107
|
+
|
108
|
+
# Generate base image
|
109
|
+
processor = self.pk.generate(image_id=image_id)
|
110
|
+
|
111
|
+
# Apply professional styling pipeline
|
112
|
+
styled_processor = self._apply_professional_styling(processor, width, height)
|
113
|
+
|
114
|
+
# Save to cache with high quality
|
115
|
+
output_path = self.image_handler.cache_dir / f"{cache_key}.jpg"
|
116
|
+
final_path = styled_processor.save(str(output_path))
|
117
|
+
|
118
|
+
return final_path
|
119
|
+
|
120
|
+
except Exception as e:
|
121
|
+
print(f"Warning: Failed to create fallback image: {e}")
|
122
|
+
return None
|
123
|
+
|
124
|
+
def _apply_professional_styling(self, processor, width: int, height: int):
|
125
|
+
"""
|
126
|
+
Apply professional styling pipeline to PlaceKitten image.
|
127
|
+
|
128
|
+
Args:
|
129
|
+
processor: PlaceKitten ImageProcessor instance
|
130
|
+
width: Target width
|
131
|
+
height: Target height
|
132
|
+
|
133
|
+
Returns:
|
134
|
+
Styled ImageProcessor ready for saving
|
135
|
+
"""
|
136
|
+
config = self.professional_config
|
137
|
+
|
138
|
+
# Smart crop to exact dimensions
|
139
|
+
styled = processor.smart_crop(
|
140
|
+
width=width, height=height, strategy=config["smart_crop_strategy"]
|
141
|
+
)
|
142
|
+
|
143
|
+
# Apply business-appropriate grayscale filter
|
144
|
+
styled = styled.apply_filter(config["base_filter"])
|
145
|
+
|
146
|
+
# Enhance contrast for professional appearance
|
147
|
+
if config["contrast_adjustment"] != 100:
|
148
|
+
styled = styled.apply_filter("contrast", value=config["contrast_adjustment"])
|
149
|
+
|
150
|
+
# Subtle brightness adjustment if needed
|
151
|
+
if config["brightness_adjustment"] != 100:
|
152
|
+
styled = styled.apply_filter("brightness", value=config["brightness_adjustment"])
|
153
|
+
|
154
|
+
return styled
|
155
|
+
|
156
|
+
def _select_image_id(self, context: Optional[Dict] = None) -> int:
|
157
|
+
"""
|
158
|
+
Select consistent image ID based on context.
|
159
|
+
|
160
|
+
Args:
|
161
|
+
context: Optional context with slide information
|
162
|
+
|
163
|
+
Returns:
|
164
|
+
int: Image ID (1-based) for consistent selection
|
165
|
+
"""
|
166
|
+
if not context:
|
167
|
+
return 1 # Default to first image
|
168
|
+
|
169
|
+
# Use slide index for consistency within presentations
|
170
|
+
if "slide_index" in context:
|
171
|
+
slide_index = context["slide_index"]
|
172
|
+
# Cycle through available images based on slide index
|
173
|
+
available_images = self.pk.get_image_count()
|
174
|
+
return (slide_index % available_images) + 1
|
175
|
+
|
176
|
+
# Use layout type for consistency
|
177
|
+
if "layout" in context:
|
178
|
+
layout = context["layout"]
|
179
|
+
# Simple hash-based selection for consistent results
|
180
|
+
layout_hash = hash(layout) % self.pk.get_image_count()
|
181
|
+
return layout_hash + 1
|
182
|
+
|
183
|
+
return 1 # Default fallback
|
184
|
+
|
185
|
+
def _generate_fallback_cache_key(
|
186
|
+
self, dimensions: Tuple[int, int], context: Optional[Dict] = None
|
187
|
+
) -> str:
|
188
|
+
"""
|
189
|
+
Generate cache key for fallback image.
|
190
|
+
|
191
|
+
Args:
|
192
|
+
dimensions: Target dimensions
|
193
|
+
context: Optional context information
|
194
|
+
|
195
|
+
Returns:
|
196
|
+
str: Cache key for consistent fallback generation
|
197
|
+
"""
|
198
|
+
width, height = dimensions
|
199
|
+
|
200
|
+
# Base key with dimensions and styling
|
201
|
+
key_parts = [
|
202
|
+
"placekitten_fallback",
|
203
|
+
f"{width}x{height}",
|
204
|
+
self.professional_config["base_filter"],
|
205
|
+
f'contrast{self.professional_config["contrast_adjustment"]}',
|
206
|
+
f'brightness{self.professional_config["brightness_adjustment"]}',
|
207
|
+
]
|
208
|
+
|
209
|
+
# Add context-based components for consistency
|
210
|
+
if context:
|
211
|
+
if "slide_index" in context:
|
212
|
+
key_parts.append(f'slide{context["slide_index"]}')
|
213
|
+
elif "layout" in context:
|
214
|
+
key_parts.append(f'layout{hash(context["layout"])}')
|
215
|
+
|
216
|
+
return "_".join(str(part) for part in key_parts)
|
217
|
+
|
218
|
+
def _get_professional_styling(self) -> Dict:
|
219
|
+
"""
|
220
|
+
Get professional styling configuration for business presentations.
|
221
|
+
|
222
|
+
Returns:
|
223
|
+
dict: Configuration for professional image styling
|
224
|
+
"""
|
225
|
+
return {
|
226
|
+
"base_filter": "grayscale", # Business-appropriate monochrome
|
227
|
+
"contrast_adjustment": 95, # Subtle contrast reduction
|
228
|
+
"brightness_adjustment": 105, # Slight brightness boost
|
229
|
+
"smart_crop_strategy": "haar-face", # Face-priority cropping
|
230
|
+
}
|
231
|
+
|
232
|
+
def get_fallback_info(
|
233
|
+
self, dimensions: Tuple[int, int], context: Optional[Dict] = None
|
234
|
+
) -> Dict:
|
235
|
+
"""
|
236
|
+
Get information about fallback image that would be generated.
|
237
|
+
|
238
|
+
Args:
|
239
|
+
dimensions: Target dimensions
|
240
|
+
context: Optional context information
|
241
|
+
|
242
|
+
Returns:
|
243
|
+
dict: Information about the fallback image
|
244
|
+
"""
|
245
|
+
if not self.is_available():
|
246
|
+
return {"available": False, "reason": "PlaceKitten library not available"}
|
247
|
+
|
248
|
+
width, height = dimensions
|
249
|
+
image_id = self._select_image_id(context)
|
250
|
+
cache_key = self._generate_fallback_cache_key(dimensions, context)
|
251
|
+
cached_path = self.image_handler._get_cached_image(cache_key)
|
252
|
+
|
253
|
+
return {
|
254
|
+
"available": True,
|
255
|
+
"dimensions": dimensions,
|
256
|
+
"image_id": image_id,
|
257
|
+
"styling": self.professional_config,
|
258
|
+
"cached": cached_path is not None,
|
259
|
+
"cache_key": cache_key,
|
260
|
+
}
|
261
|
+
|
262
|
+
def cleanup_fallback_cache(self):
|
263
|
+
"""Clean up cached fallback images to free space."""
|
264
|
+
try:
|
265
|
+
# Remove all PlaceKitten fallback images from cache
|
266
|
+
fallback_files = list(self.image_handler.cache_dir.glob("placekitten_fallback_*.jpg"))
|
267
|
+
|
268
|
+
removed_count = 0
|
269
|
+
for file_path in fallback_files:
|
270
|
+
try:
|
271
|
+
file_path.unlink()
|
272
|
+
removed_count += 1
|
273
|
+
except Exception:
|
274
|
+
continue # nosec - Continue processing other files if one fails
|
275
|
+
|
276
|
+
return {"removed_files": removed_count, "total_fallback_files": len(fallback_files)}
|
277
|
+
|
278
|
+
except Exception as e:
|
279
|
+
print(f"Warning: Fallback cache cleanup failed: {e}")
|
280
|
+
return {"removed_files": 0, "total_fallback_files": 0}
|