Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -164,3 +164,4 @@ dataset/train/images
dataset/train/labels
app_logs.txt
.aider*
*.tex
38 changes: 36 additions & 2 deletions src/search_images.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import requests
import json


def search_images(query, api_key, search_engine_id, num_results=10):
Expand All @@ -16,8 +17,8 @@ def search_images(query, api_key, search_engine_id, num_results=10):

response = requests.get(search_url)
if response.status_code != 200:
raise Exception(
f"Failed to fetch images: Status code {response.status_code}, Response: {response.text}")
error_message = _parse_api_error(response)
raise Exception(error_message)

data = response.json()
if 'items' not in data:
Expand All @@ -33,3 +34,36 @@ def search_images(query, api_key, search_engine_id, num_results=10):
break # No more results available

return images


def _parse_api_error(response):
"""Parse Google API error response and return a user-friendly message"""
try:
data = response.json()
if 'error' in data:
error_obj = data['error']

# Handle different error formats
if isinstance(error_obj, dict):
message = error_obj.get('message', 'Unknown error')
code = error_obj.get('code', response.status_code)
status = error_obj.get('status', 'UNKNOWN')

# Map common errors to user-friendly messages
if status == 'PERMISSION_DENIED' or code == 403:
return f"Access Denied: {message} - Please check your Google API credentials and ensure the Custom Search JSON API is enabled in your Google Cloud project."
elif status == 'INVALID_ARGUMENT' or code == 400:
return f"Invalid Request: {message} - Please verify your search query and API configuration."
elif status == 'UNAUTHENTICATED' or code == 401:
return f"Authentication Failed: {message} - Your API key may be invalid or expired."
elif status == 'RESOURCE_EXHAUSTED' or code == 429:
return f"Rate Limited: {message} - You've exceeded your daily search quota. Please try again later."
else:
return f"API Error ({code}): {message}"
else:
return f"API Error: {str(error_obj)}"
except:
pass

# Fallback error message
return f"Failed to fetch images: Status code {response.status_code}. The image search service returned an error. Please verify your API keys and search query."
114 changes: 79 additions & 35 deletions src/templates/base.html
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{% block title %}SearchVision{% endblock %}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
<style>
* {
margin: 0;
Expand All @@ -12,93 +15,134 @@
}

body {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, sans-serif;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
background: #ffffff;
min-height: 100vh;
padding: 20px;
padding: 0;
overflow-x: hidden;
color: #020817;
}

.container {
max-width: 1200px;
margin: 0 auto;
background: white;
border-radius: 16px;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
max-width: 100%;
margin: 0;
background: transparent;
border-radius: 0;
box-shadow: none;
overflow: hidden;
display: flex;
flex-direction: column;
min-height: 100vh;
}

header {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
padding: 30px;
background: transparent;
color: #020817;
padding: 40px 20px;
text-align: center;
position: relative;
z-index: 1;
}

header h1 {
font-size: 2.5rem;
margin-bottom: 10px;
margin-bottom: 8px;
font-weight: 600;
letter-spacing: -0.5px;
color: #020817;
}

header p {
opacity: 0.9;
font-size: 1.1rem;
opacity: 0.6;
font-size: 1rem;
color: #64748b;
font-weight: 400;
}

main {
padding: 40px;
padding: 0;
flex: 1;
display: flex;
align-items: center;
justify-content: center;
}

{% block styles %}{% endblock %}


.btn {
display: inline-block;
padding: 14px 32px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border: none;
border-radius: 8px;
font-size: 1rem;
font-weight: 600;
padding: 10px 24px;
background: #020817;
color: #ffffff;
border: 1px solid #020817;
border-radius: 6px;
font-size: 0.95rem;
font-weight: 500;
cursor: pointer;
transition: transform 0.2s, box-shadow 0.2s;
transition: all 0.2s ease;
text-decoration: none;
margin-top: 20px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}

.btn:hover {
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(102, 126, 234, 0.4);
background: #1e293b;
border-color: #1e293b;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}

.btn:active {
transform: scale(0.98);
}

.btn-secondary {
background: #6c757d;
background: #e2e8f0;
color: #020817;
border-color: #e2e8f0;
}

.btn-secondary:hover {
background: #cbd5e1;
border-color: #cbd5e1;
}

.btn-success {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
background: #16a34a;
border-color: #16a34a;
color: #ffffff;
}

.btn-success:hover {
background: #15803d;
border-color: #15803d;
}

.error-message {
background: #fee;
border: 1px solid #fcc;
color: #c33;
background: #fee2e2;
border: 1px solid #fecaca;
color: #991b1b;
padding: 16px;
border-radius: 8px;
border-radius: 6px;
margin: 20px 0;
font-size: 0.95rem;
}


footer {
background: #f8f9fa;
padding: 20px;
background: #f8fafc;
padding: 24px;
text-align: center;
color: #666;
color: #64748b;
border-top: 1px solid #e2e8f0;
font-size: 0.9rem;
}
</style>
</head>
<body>
<div class="container">
<header>
<h1>🔍 SearchVision</h1>
<h1>SearchVision</h1>
<p>Train your own object detection model in minutes</p>
</header>
<main>
Expand Down
Loading
Loading