Enhance image size configuration and integration settings. Default image sizes are now set based on provider and model, with options for featured, desktop, and mobile images. Updated frontend to allow selectable image sizes in settings.

This commit is contained in:
Desktop
2025-11-12 18:43:32 +05:00
parent 07f94f807b
commit c508c888aa
5 changed files with 165 additions and 37 deletions

View File

@@ -185,8 +185,9 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
image_format = config.get('image_format', 'webp')
desktop_enabled = config.get('desktop_enabled', True)
mobile_enabled = config.get('mobile_enabled', True)
# Determine featured image size based on provider
featured_image_size = '1280x832' if provider == 'runware' else '1024x1024'
# Get image sizes from config, with fallback defaults
featured_image_size = config.get('featured_image_size') or ('1280x832' if provider == 'runware' else '1024x1024')
desktop_image_size = config.get('desktop_image_size') or '1024x1024'
logger.info(f"[process_image_generation_queue] Settings loaded:")
logger.info(f" - Provider: {provider}")
@@ -457,8 +458,15 @@ def process_image_generation_queue(self, image_ids: list, account_id: int = None
}
)
# Use featured size for featured images, default for others
image_size = featured_image_size if image.image_type == 'featured' else '1024x1024'
# Use appropriate size based on image type
if image.image_type == 'featured':
image_size = featured_image_size
elif image.image_type == 'desktop':
image_size = desktop_image_size
elif image.image_type == 'mobile':
image_size = '512x512' # Fixed mobile size
else: # in_article or other
image_size = '1024x1024' # Default for in-article images
result = ai_core.generate_image(
prompt=formatted_prompt,

View File

@@ -618,6 +618,19 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
config.setdefault('desktop_enabled', True)
config.setdefault('mobile_enabled', True)
# Set default image sizes based on provider/model
provider = config.get('provider', 'openai')
model = config.get('model', 'dall-e-3')
if not config.get('featured_image_size'):
if provider == 'runware':
config['featured_image_size'] = '1280x832'
else: # openai
config['featured_image_size'] = '1024x1024'
if not config.get('desktop_image_size'):
config['desktop_image_size'] = '1024x1024'
# Get or create integration settings
logger.info(f"[save_settings] Attempting get_or_create for {integration_type} with account {account.id}")
integration_settings, created = IntegrationSettings.objects.get_or_create(
@@ -746,6 +759,10 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
# Get model - try 'model' first, then 'imageModel' as fallback
model = config.get('model') or config.get('imageModel') or 'dall-e-3'
# Set defaults for image sizes if not present
provider = config.get('provider', 'openai')
default_featured_size = '1280x832' if provider == 'runware' else '1024x1024'
return Response({
'success': True,
'config': {
@@ -756,6 +773,8 @@ class IntegrationSettingsViewSet(viewsets.ViewSet):
'image_format': config.get('image_format', 'webp'),
'desktop_enabled': config.get('desktop_enabled', True),
'mobile_enabled': config.get('mobile_enabled', True),
'featured_image_size': config.get('featured_image_size', default_featured_size),
'desktop_image_size': config.get('desktop_image_size', '1024x1024'),
}
}, status=status.HTTP_200_OK)
except IntegrationSettings.DoesNotExist:

View File

@@ -1,5 +1,6 @@
import { Suspense, lazy } from "react";
import { BrowserRouter as Router, Routes, Route, Navigate } from "react-router";
import { HelmetProvider } from "react-helmet-async";
import AppLayout from "./layout/AppLayout";
import { ScrollToTop } from "./components/common/ScrollToTop";
import ProtectedRoute from "./components/auth/ProtectedRoute";
@@ -106,6 +107,7 @@ export default function App() {
<GlobalErrorDisplay />
<LoadingStateMonitor />
<Router>
<HelmetProvider>
<ScrollToTop />
<Routes>
{/* Auth Routes - Public */}
@@ -486,6 +488,7 @@ export default function App() {
{/* Fallback Route */}
<Route path="*" element={<NotFound />} />
</Routes>
</HelmetProvider>
</Router>
</>
);

View File

@@ -5,7 +5,6 @@ import "./styles/igny8-colors.css"; /* IGNY8 custom colors - separate from TailA
import "swiper/swiper-bundle.css";
import "flatpickr/dist/flatpickr.css";
import App from "./App.tsx";
import { AppWrapper } from "./components/common/PageMeta.tsx";
import { ThemeProvider } from "./context/ThemeContext.tsx";
import { ToastProvider } from "./components/ui/toast/ToastContainer.tsx";
import { HeaderMetricsProvider } from "./context/HeaderMetricsContext.tsx";
@@ -17,9 +16,7 @@ createRoot(document.getElementById("root")!).render(
<ThemeProvider>
<HeaderMetricsProvider>
<ToastProvider>
<AppWrapper>
<App />
</AppWrapper>
</ToastProvider>
</HeaderMetricsProvider>
</ThemeProvider>

View File

@@ -63,6 +63,8 @@ interface IntegrationConfig {
image_format?: string; // 'webp', 'jpg', 'png'
desktop_enabled?: boolean;
mobile_enabled?: boolean;
featured_image_size?: string; // e.g., '1280x832', '1024x1024'
desktop_image_size?: string; // e.g., '1024x1024', '512x512'
}
export default function Integration() {
@@ -91,6 +93,8 @@ export default function Integration() {
image_format: 'webp', // 'webp', 'jpg', 'png'
desktop_enabled: true,
mobile_enabled: true,
featured_image_size: '1024x1024', // Default, will be set based on provider/model
desktop_image_size: '1024x1024', // Default, will be set based on provider/model
},
});
@@ -382,17 +386,26 @@ export default function Integration() {
// For image_generation, map service to provider and ensure all settings are included
let configToSave = { ...config };
if (selectedIntegration === 'image_generation') {
// Determine default sizes based on provider/model
const currentService = config.service || 'openai';
const currentModel = currentService === 'openai' ? (config.model || 'dall-e-3') : (config.runwareModel || 'runware:97@1');
const availableSizes = getImageSizes(currentService, currentModel);
const defaultFeaturedSize = availableSizes.length > 0 ? availableSizes[0].value : '1024x1024';
const defaultDesktopSize = availableSizes.length > 0 ? availableSizes[0].value : '1024x1024';
configToSave = {
...config,
provider: config.service || config.provider || 'openai', // Map service to provider for backend
// Ensure model is set correctly based on service
model: config.service === 'openai' ? (config.model || 'dall-e-3') : (config.service === 'runware' ? (config.runwareModel || 'runware:97@1') : config.model),
model: currentService === 'openai' ? (config.model || 'dall-e-3') : (currentService === 'runware' ? (config.runwareModel || 'runware:97@1') : config.model),
// Ensure all image settings have defaults
image_type: config.image_type || 'realistic',
max_in_article_images: config.max_in_article_images || 2,
image_format: config.image_format || 'webp',
desktop_enabled: config.desktop_enabled !== undefined ? config.desktop_enabled : true,
mobile_enabled: config.mobile_enabled !== undefined ? config.mobile_enabled : true,
featured_image_size: config.featured_image_size || defaultFeaturedSize,
desktop_image_size: config.desktop_image_size || defaultDesktopSize,
};
}
@@ -480,6 +493,33 @@ export default function Integration() {
return [];
};
// Get available image sizes with prices based on provider and model
const getImageSizes = useCallback((provider: string, model: string) => {
if (provider === 'runware') {
return [
{ value: '1280x832', label: '1280×832 pixels - $0.009', price: 0.009 },
{ value: '1024x1024', label: '1024×1024 pixels - $0.009', price: 0.009 },
{ value: '512x512', label: '512×512 pixels - $0.006', price: 0.006 },
];
} else if (provider === 'openai') {
if (model === 'dall-e-2') {
return [
{ value: '256x256', label: '256×256 pixels - $0.016', price: 0.016 },
{ value: '512x512', label: '512×512 pixels - $0.018', price: 0.018 },
{ value: '1024x1024', label: '1024×1024 pixels - $0.02', price: 0.02 },
];
} else if (model === 'dall-e-3') {
return [
{ value: '1024x1024', label: '1024×1024 pixels - $0.04', price: 0.04 },
];
}
}
// Default fallback
return [
{ value: '1024x1024', label: '1024×1024 pixels', price: 0 },
];
}, []);
const getSettingsFields = useCallback((integrationId: string): FormField[] => {
const config = integrations[integrationId];
@@ -666,6 +706,41 @@ export default function Integration() {
return [];
}, [integrations]);
// Update image sizes when service/model changes
useEffect(() => {
if (selectedIntegration !== 'image_generation' || !showSettingsModal) return;
const config = integrations[selectedIntegration];
if (!config) return;
const service = config.service || 'openai';
const model = service === 'openai' ? (config.model || 'dall-e-3') : (config.runwareModel || 'runware:97@1');
const availableSizes = getImageSizes(service, model);
if (availableSizes.length > 0) {
const defaultSize = availableSizes[0].value;
const currentFeaturedSize = config.featured_image_size;
const currentDesktopSize = config.desktop_image_size;
// Check if current sizes are valid for the new provider/model
const validSizes = availableSizes.map(s => s.value);
const needsUpdate =
!currentFeaturedSize || !validSizes.includes(currentFeaturedSize) ||
!currentDesktopSize || !validSizes.includes(currentDesktopSize);
if (needsUpdate) {
setIntegrations({
...integrations,
[selectedIntegration]: {
...config,
featured_image_size: validSizes.includes(currentFeaturedSize || '') ? currentFeaturedSize : defaultSize,
desktop_image_size: validSizes.includes(currentDesktopSize || '') ? currentDesktopSize : defaultSize,
},
});
}
}
}, [integrations[selectedIntegration]?.service, integrations[selectedIntegration]?.model, integrations[selectedIntegration]?.runwareModel, selectedIntegration, showSettingsModal, getImageSizes]);
// Memoize custom body for image generation modal to prevent infinite loops
const imageGenerationCustomBody = useMemo(() => {
if (selectedIntegration !== 'image_generation' || !showSettingsModal) return undefined;
@@ -726,24 +801,38 @@ export default function Integration() {
Max Images
</Label>
{/* Featured Image (full width) */}
{/* Featured Image (full width) - Selectable */}
<div className="p-3 rounded-lg border border-gray-200 dark:border-gray-700 bg-gradient-to-r from-purple-500 to-blue-500 text-white">
<div className="flex items-center justify-between">
<div>
<div className="flex items-center justify-between mb-2">
<div className="font-medium">Featured Image</div>
<div className="text-xs opacity-90">1280×832 pixels</div>
</div>
<div className="text-xs bg-white/20 px-2 py-1 rounded">
Always Enabled
</div>
</div>
<div className="w-full [&_.igny8-select-styled]:bg-white/10 [&_.igny8-select-styled]:border-white/20 [&_.igny8-select-styled]:text-white [&_.igny8-select-styled]:placeholder:text-white/70 [&_.igny8-select-styled]:focus:border-white/40 [&_.igny8-select-styled]:focus:ring-white/20">
<SelectDropdown
options={getImageSizes(service, service === 'openai' ? (integrations[selectedIntegration]?.model || 'dall-e-3') : (integrations[selectedIntegration]?.runwareModel || 'runware:97@1'))}
value={integrations[selectedIntegration]?.featured_image_size || '1024x1024'}
onChange={(value) => {
setIntegrations({
...integrations,
[selectedIntegration]: {
...integrations[selectedIntegration],
featured_image_size: value,
},
});
}}
className="w-full"
/>
</div>
</div>
</div>
{/* Row 2: Desktop & Mobile Images (2 columns) */}
<div className="grid grid-cols-2 gap-4">
{/* Desktop Images Checkbox */}
<div className="flex items-center gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700">
{/* Desktop Images Checkbox with Size Selector */}
<div className="p-3 rounded-lg border border-gray-200 dark:border-gray-700 space-y-2">
<div className="flex items-center gap-3">
<Checkbox
checked={integrations[selectedIntegration]?.desktop_enabled !== false}
onChange={(checked) => {
@@ -756,17 +845,29 @@ export default function Integration() {
});
}}
/>
<div>
<Label className="font-medium text-gray-700 dark:text-gray-300">
Desktop Images
</Label>
<div className="text-xs text-gray-500 dark:text-gray-400">
1024×1024 pixels
</div>
</div>
{integrations[selectedIntegration]?.desktop_enabled !== false && (
<SelectDropdown
options={getImageSizes(service, service === 'openai' ? (integrations[selectedIntegration]?.model || 'dall-e-3') : (integrations[selectedIntegration]?.runwareModel || 'runware:97@1'))}
value={integrations[selectedIntegration]?.desktop_image_size || '1024x1024'}
onChange={(value) => {
setIntegrations({
...integrations,
[selectedIntegration]: {
...integrations[selectedIntegration],
desktop_image_size: value,
},
});
}}
className="w-full"
/>
)}
</div>
{/* Mobile Images Checkbox */}
{/* Mobile Images Checkbox - Fixed to 512x512 */}
<div className="flex items-center gap-3 p-3 rounded-lg border border-gray-200 dark:border-gray-700">
<Checkbox
checked={integrations[selectedIntegration]?.mobile_enabled !== false}
@@ -785,7 +886,7 @@ export default function Integration() {
Mobile Images
</Label>
<div className="text-xs text-gray-500 dark:text-gray-400">
960×1280 pixels
512×512 pixels
</div>
</div>
</div>