Spaces:
Sleeping
Sleeping
| import html | |
| from urllib.parse import urlparse | |
| class PreviewGenerator: | |
| def __init__(self, metadata): | |
| self.metadata = metadata | |
| def generate_google_preview(self, url): | |
| """Generate Google search result preview""" | |
| title = self.metadata.get('title', 'Untitled Page') | |
| description = self.metadata.get('description', 'No description available.') | |
| # Truncate title and description for Google display | |
| display_title = title[:60] + "..." if len(title) > 60 else title | |
| display_description = description[:160] + "..." if len(description) > 160 else description | |
| # Parse URL for display | |
| parsed_url = urlparse(url) | |
| display_url = f"{parsed_url.netloc}{parsed_url.path}" | |
| preview_html = f""" | |
| <div class="preview-card" style=" | |
| font-family: arial, sans-serif; | |
| max-width: 100%; | |
| width: 100%; | |
| margin: 10px 0; | |
| padding: 15px; | |
| border: 1px solid #e0e0e0; | |
| border-radius: 8px; | |
| background-color: #ffffff; | |
| box-sizing: border-box; | |
| overflow-wrap: break-word; | |
| "> | |
| <div style=" | |
| color: #1a0dab; | |
| font-size: clamp(16px, 4vw, 20px); | |
| line-height: 1.3; | |
| margin-bottom: 2px; | |
| text-decoration: none; | |
| word-break: break-word; | |
| "> | |
| {html.escape(display_title)} | |
| </div> | |
| <div style=" | |
| color: #006621; | |
| font-size: clamp(12px, 3vw, 14px); | |
| line-height: 1.3; | |
| margin-bottom: 5px; | |
| word-break: break-all; | |
| "> | |
| {html.escape(display_url)} | |
| </div> | |
| <div style=" | |
| color: #545454; | |
| font-size: clamp(12px, 3vw, 14px); | |
| line-height: 1.4; | |
| word-break: break-word; | |
| "> | |
| {html.escape(display_description)} | |
| </div> | |
| </div> | |
| """ | |
| return preview_html | |
| def generate_facebook_preview(self, url): | |
| """Generate Facebook preview""" | |
| title = self.metadata.get('og:title') or self.metadata.get('title', 'Untitled Page') | |
| description = self.metadata.get('og:description') or self.metadata.get('description', 'No description available.') | |
| image = self.metadata.get('og:image', '') | |
| site_name = self.metadata.get('og:site_name', '') | |
| # Parse URL for display | |
| parsed_url = urlparse(url) | |
| display_url = parsed_url.netloc.upper() | |
| preview_html = f""" | |
| <div class="preview-card" style=" | |
| font-family: Helvetica, Arial, sans-serif; | |
| max-width: 100%; | |
| width: 100%; | |
| border: 1px solid #e1e5e9; | |
| border-radius: 8px; | |
| overflow: hidden; | |
| background-color: #ffffff; | |
| margin: 10px 0; | |
| box-sizing: border-box; | |
| "> | |
| """ | |
| # Image section with better design | |
| if image: | |
| preview_html += f""" | |
| <div style=" | |
| width: 100%; | |
| height: clamp(200px, 30vw, 261px); | |
| background: linear-gradient(135deg, #4267B2 0%, #898F9C 100%); | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| color: white; | |
| font-size: clamp(12px, 3vw, 14px); | |
| position: relative; | |
| overflow: hidden; | |
| "> | |
| <div style=" | |
| background: rgba(255,255,255,0.1); | |
| border-radius: 12px; | |
| padding: 1rem; | |
| text-align: center; | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255,255,255,0.2); | |
| max-width: 80%; | |
| "> | |
| <div style="font-size: 2rem; margin-bottom: 0.5rem;">πΌοΈ</div> | |
| <div style="font-weight: 500; word-break: break-all;"> | |
| {html.escape(image[:50] + "..." if len(image) > 50 else image)} | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| else: | |
| preview_html += f""" | |
| <div style=" | |
| width: 100%; | |
| height: clamp(150px, 20vw, 200px); | |
| background: linear-gradient(135deg, #f0f2f5 0%, #e4e6ea 100%); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: #65676b; | |
| font-size: clamp(12px, 3vw, 14px); | |
| border-bottom: 1px solid #e4e6ea; | |
| "> | |
| <div style="text-align: center; opacity: 0.7;"> | |
| <div style="font-size: 2rem; margin-bottom: 0.5rem;">π·</div> | |
| <div>No image available</div> | |
| </div> | |
| </div> | |
| """ | |
| # Content section with enhanced design | |
| preview_html += f""" | |
| <div style=" | |
| padding: clamp(12px, 3vw, 16px); | |
| background: linear-gradient(180deg, #ffffff 0%, #f8f9fa 100%); | |
| position: relative; | |
| "> | |
| <div style=" | |
| color: #606770; | |
| font-size: clamp(10px, 2.5vw, 11px); | |
| text-transform: uppercase; | |
| margin-bottom: 8px; | |
| word-break: break-all; | |
| font-weight: 600; | |
| letter-spacing: 0.5px; | |
| "> | |
| π {html.escape(display_url)} | |
| </div> | |
| <div style=" | |
| color: #1d2129; | |
| font-size: clamp(15px, 4vw, 18px); | |
| font-weight: 700; | |
| line-height: 1.2; | |
| margin-bottom: 8px; | |
| word-break: break-word; | |
| display: -webkit-box; | |
| -webkit-line-clamp: 2; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| "> | |
| {html.escape(title[:95] + "..." if len(title) > 95 else title)} | |
| </div> | |
| <div style=" | |
| color: #65676b; | |
| font-size: clamp(13px, 3.2vw, 15px); | |
| line-height: 1.4; | |
| word-break: break-word; | |
| display: -webkit-box; | |
| -webkit-line-clamp: 3; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| "> | |
| {html.escape(description[:140] + "..." if len(description) > 140 else description)} | |
| </div> | |
| <div style=" | |
| position: absolute; | |
| bottom: 8px; | |
| right: 12px; | |
| font-size: 10px; | |
| color: #8a8d91; | |
| opacity: 0.7; | |
| "> | |
| Facebook Preview | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| return preview_html | |
| def generate_twitter_preview(self, url): | |
| """Generate Twitter card preview""" | |
| card_type = self.metadata.get('twitter:card', 'summary') | |
| title = self.metadata.get('twitter:title') or self.metadata.get('og:title') or self.metadata.get('title', 'Untitled Page') | |
| description = self.metadata.get('twitter:description') or self.metadata.get('og:description') or self.metadata.get('description', 'No description available.') | |
| image = self.metadata.get('twitter:image') or self.metadata.get('og:image', '') | |
| # Parse URL for display | |
| parsed_url = urlparse(url) | |
| display_url = parsed_url.netloc | |
| preview_html = f""" | |
| <div class="preview-card" style=" | |
| font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif; | |
| max-width: 100%; | |
| width: 100%; | |
| border: 1px solid #e1e8ed; | |
| border-radius: 16px; | |
| overflow: hidden; | |
| background-color: #ffffff; | |
| margin: 10px 0; | |
| box-sizing: border-box; | |
| "> | |
| """ | |
| # Enhanced image section for Twitter | |
| if image and card_type in ['summary_large_image', 'summary']: | |
| image_height = "clamp(200px, 35vw, 250px)" if card_type == "summary_large_image" else "clamp(120px, 25vw, 150px)" | |
| preview_html += f""" | |
| <div style=" | |
| width: 100%; | |
| height: {image_height}; | |
| background: linear-gradient(135deg, #1DA1F2 0%, #0d8bd9 50%, #657786 100%); | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| color: white; | |
| font-size: clamp(12px, 3vw, 14px); | |
| position: relative; | |
| overflow: hidden; | |
| "> | |
| <div style=" | |
| background: rgba(255,255,255,0.15); | |
| border-radius: 15px; | |
| padding: clamp(0.75rem, 3vw, 1.2rem); | |
| text-align: center; | |
| backdrop-filter: blur(10px); | |
| border: 1px solid rgba(255,255,255,0.3); | |
| max-width: 85%; | |
| box-shadow: 0 4px 20px rgba(0,0,0,0.1); | |
| "> | |
| <div style="font-size: clamp(1.5rem, 5vw, 2rem); margin-bottom: 0.5rem;">π¦</div> | |
| <div style="font-weight: 600; word-break: break-all; font-size: clamp(11px, 2.8vw, 13px);"> | |
| {html.escape(image[:60] + "..." if len(image) > 60 else image)} | |
| </div> | |
| </div> | |
| <div style=" | |
| position: absolute; | |
| top: 10px; | |
| right: 15px; | |
| background: rgba(255,255,255,0.2); | |
| border-radius: 20px; | |
| padding: 4px 8px; | |
| font-size: 10px; | |
| font-weight: 500; | |
| "> | |
| {card_type.replace('_', ' ').title()} | |
| </div> | |
| </div> | |
| """ | |
| else: | |
| preview_html += f""" | |
| <div style=" | |
| width: 100%; | |
| height: clamp(100px, 20vw, 130px); | |
| background: linear-gradient(135deg, #f7f9fa 0%, #e1e8ed 100%); | |
| display: flex; | |
| align-items: center; | |
| justify-content: center; | |
| color: #657786; | |
| font-size: clamp(12px, 3vw, 14px); | |
| border-bottom: 1px solid #e1e8ed; | |
| "> | |
| <div style="text-align: center; opacity: 0.6;"> | |
| <div style="font-size: clamp(1.5rem, 4vw, 2rem); margin-bottom: 0.5rem;">π¦</div> | |
| <div>No image available</div> | |
| </div> | |
| </div> | |
| """ | |
| # Enhanced content section for Twitter | |
| preview_html += f""" | |
| <div style=" | |
| padding: clamp(12px, 3.5vw, 18px); | |
| background: linear-gradient(180deg, #ffffff 0%, #f7f9fa 100%); | |
| position: relative; | |
| border-top: 1px solid #e1e8ed; | |
| "> | |
| <div style=" | |
| color: #657786; | |
| font-size: clamp(12px, 3vw, 14px); | |
| margin-bottom: 6px; | |
| word-break: break-all; | |
| font-weight: 500; | |
| display: flex; | |
| align-items: center; | |
| gap: 6px; | |
| "> | |
| <span style="font-size: 12px;">π</span> | |
| {html.escape(display_url)} | |
| </div> | |
| <div style=" | |
| color: #0f1419; | |
| font-size: clamp(15px, 4vw, 17px); | |
| font-weight: 700; | |
| line-height: 1.25; | |
| margin-bottom: 8px; | |
| word-break: break-word; | |
| display: -webkit-box; | |
| -webkit-line-clamp: 2; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| "> | |
| {html.escape(title[:75] + "..." if len(title) > 75 else title)} | |
| </div> | |
| <div style=" | |
| color: #536471; | |
| font-size: clamp(14px, 3.2vw, 15px); | |
| line-height: 1.4; | |
| word-break: break-word; | |
| display: -webkit-box; | |
| -webkit-line-clamp: 3; | |
| -webkit-box-orient: vertical; | |
| overflow: hidden; | |
| margin-bottom: 8px; | |
| "> | |
| {html.escape(description[:130] + "..." if len(description) > 130 else description)} | |
| </div> | |
| <div style=" | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| margin-top: 12px; | |
| padding-top: 8px; | |
| border-top: 1px solid #eff3f4; | |
| "> | |
| <div style=" | |
| font-size: 10px; | |
| color: #8b98a5; | |
| opacity: 0.8; | |
| "> | |
| Twitter Card Preview | |
| </div> | |
| <div style=" | |
| display: flex; | |
| gap: 12px; | |
| opacity: 0.6; | |
| "> | |
| <span style="font-size: 14px;">π¬</span> | |
| <span style="font-size: 14px;">π</span> | |
| <span style="font-size: 14px;">β€οΈ</span> | |
| <span style="font-size: 14px;">π€</span> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| """ | |
| return preview_html | |