From f6edf620823f29239cc52f60b05267c8f3d983e2 Mon Sep 17 00:00:00 2001 From: Seth Van Niekerk Date: Sun, 11 Jan 2026 14:08:48 -0500 Subject: [PATCH] Big rewrite --- Dockerfile | 38 ++ README.md | 33 + app.py | 323 +++++++++- templates/index.html | 1421 +++++++++++++++++++++++++++++++++++++++--- 4 files changed, 1711 insertions(+), 104 deletions(-) diff --git a/Dockerfile b/Dockerfile index 81e1b55..106f838 100644 --- a/Dockerfile +++ b/Dockerfile @@ -12,10 +12,48 @@ RUN apt-get update && apt-get install -y \ fonts-dejavu-extra \ fonts-liberation \ fonts-liberation2 \ + fonts-noto \ + fonts-freefont-ttf \ fontconfig \ + wget \ + unzip \ + git \ && fc-cache -f -v \ && rm -rf /var/lib/apt/lists/* +# Download and install select Google Fonts from GitHub +RUN mkdir -p /usr/share/fonts/truetype/google-fonts && \ + cd /tmp && \ + # Roboto from official release\ + wget -q https://github.com/google/roboto/releases/download/v2.138/roboto-unhinted.zip && \ + unzip -q roboto-unhinted.zip -d roboto && \ + find roboto -name "*.ttf" -path "*/Roboto-Bold.ttf" -exec cp {} /usr/share/fonts/truetype/google-fonts/ \; && \ + find roboto -name "*.ttf" -path "*/Roboto-Regular.ttf" -exec cp {} /usr/share/fonts/truetype/google-fonts/ \; && \ + find roboto -name "*.ttf" -path "*/Roboto-Black.ttf" -exec cp {} /usr/share/fonts/truetype/google-fonts/ \; && \ + # Clone Google Fonts repo for other fonts (shallow clone)\ + git clone --depth 1 --filter=blob:none --sparse https://github.com/google/fonts.git && \ + cd fonts && \ + git sparse-checkout set ofl/opensans ofl/montserrat ofl/oswald ofl/raleway ofl/lato ofl/poppins ofl/anton ofl/bebasneue ofl/ptsans ofl/nunito && \ + # Copy specific font files\ + find ofl/opensans -name "OpenSans-Bold.ttf" -o -name "OpenSans-Regular.ttf" -o -name "OpenSans-ExtraBold.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/opensans -name "OpenSans*\[wght\].ttf" -exec cp {} /usr/share/fonts/truetype/google-fonts/OpenSans-Variable.ttf \; 2>/dev/null || true && \ + find ofl/montserrat -name "Montserrat-Bold.ttf" -o -name "Montserrat-Regular.ttf" -o -name "Montserrat-Black.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/montserrat -name "Montserrat*\[wght\].ttf" -exec cp {} /usr/share/fonts/truetype/google-fonts/Montserrat-Variable.ttf \; 2>/dev/null || true && \ + find ofl/oswald -name "Oswald-Bold.ttf" -o -name "Oswald-Regular.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/oswald -name "Oswald*\[wght\].ttf" -exec cp {} /usr/share/fonts/truetype/google-fonts/Oswald-Variable.ttf \; 2>/dev/null || true && \ + find ofl/raleway -name "Raleway-Bold.ttf" -o -name "Raleway-Regular.ttf" -o -name "Raleway-Black.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/raleway -name "Raleway*\[wght\].ttf" -exec cp {} /usr/share/fonts/truetype/google-fonts/Raleway-Variable.ttf \; 2>/dev/null || true && \ + find ofl/lato -name "Lato-Bold.ttf" -o -name "Lato-Regular.ttf" -o -name "Lato-Black.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/poppins -name "Poppins-Bold.ttf" -o -name "Poppins-Regular.ttf" -o -name "Poppins-Black.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/anton -name "Anton-Regular.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/bebasneue -name "BebasNeue-Regular.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/ptsans -name "PTSans-Bold.ttf" -o -name "PTSans-Regular.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/nunito -name "Nunito-Bold.ttf" -o -name "Nunito-Regular.ttf" -o -name "Nunito-Black.ttf" | xargs -I {} cp {} /usr/share/fonts/truetype/google-fonts/ 2>/dev/null || true && \ + find ofl/nunito -name "Nunito*\[wght\].ttf" -exec cp {} /usr/share/fonts/truetype/google-fonts/Nunito-Variable.ttf \; 2>/dev/null || true && \ + # Clean up\ + cd /tmp && rm -rf fonts roboto *.zip && \ + fc-cache -f -v + # Set working directory WORKDIR /app diff --git a/README.md b/README.md index 9d855a0..3824262 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,39 @@ A Python web application for adding custom text to TV station and network logos. ## Quick Start +### Using Python Directly + +If you prefer to run the application without Docker: + +1. **Clone the repository (or download the files):** + ```bash + git clone + cd logo-txt + ``` + +2. **Install Python dependencies:** + ```bash + pip install -r requirements.txt + ``` + + Or with a virtual environment (recommended): + ```bash + python3 -m venv venv + source venv/bin/activate # On Windows: venv\Scripts\activate + pip install -r requirements.txt + ``` + +3. **Run the application:** + ```bash + python app.py + ``` + +4. **Access the application:** + Open your browser and navigate to `http://localhost:5001` + +5. **Stop the application:** + Press `Ctrl+C` in the terminal + ### Using Docker Compose (Recommended) 1. **Create a `docker-compose.yml` file:** diff --git a/app.py b/app.py index 94cc003..3f5da73 100644 --- a/app.py +++ b/app.py @@ -99,45 +99,157 @@ def detect_font_from_image(img): return None -def get_font(font_size, detected_font_path=None): +def get_available_fonts(): + """ + Get list of available fonts on the system with their paths and display names + Works on Linux (Docker), macOS, and Windows + """ + font_list = [ + # DejaVu fonts (fonts-dejavu package) - Sans-serif + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', 'name': 'DejaVu Sans Bold'}, + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', 'name': 'DejaVu Sans'}, + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSans-ExtraLight.ttf', 'name': 'DejaVu Sans ExtraLight'}, + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSansCondensed-Bold.ttf', 'name': 'DejaVu Sans Condensed Bold'}, + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSansCondensed.ttf', 'name': 'DejaVu Sans Condensed'}, + # DejaVu fonts - Serif + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSerif-Bold.ttf', 'name': 'DejaVu Serif Bold'}, + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSerif.ttf', 'name': 'DejaVu Serif'}, + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSerifCondensed-Bold.ttf', 'name': 'DejaVu Serif Condensed Bold'}, + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSerifCondensed.ttf', 'name': 'DejaVu Serif Condensed'}, + # DejaVu fonts - Monospace + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSansMono-Bold.ttf', 'name': 'DejaVu Sans Mono Bold'}, + {'path': '/usr/share/fonts/truetype/dejavu/DejaVuSansMono.ttf', 'name': 'DejaVu Sans Mono'}, + # Liberation fonts (fonts-liberation package) - Sans-serif + {'path': '/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf', 'name': 'Liberation Sans Bold'}, + {'path': '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf', 'name': 'Liberation Sans'}, + {'path': '/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Bold.ttf', 'name': 'Liberation Sans Narrow Bold'}, + {'path': '/usr/share/fonts/truetype/liberation/LiberationSansNarrow-Regular.ttf', 'name': 'Liberation Sans Narrow'}, + # Liberation fonts - Serif + {'path': '/usr/share/fonts/truetype/liberation/LiberationSerif-Bold.ttf', 'name': 'Liberation Serif Bold'}, + {'path': '/usr/share/fonts/truetype/liberation/LiberationSerif-Regular.ttf', 'name': 'Liberation Serif'}, + # Liberation fonts - Monospace + {'path': '/usr/share/fonts/truetype/liberation/LiberationMono-Bold.ttf', 'name': 'Liberation Mono Bold'}, + {'path': '/usr/share/fonts/truetype/liberation/LiberationMono-Regular.ttf', 'name': 'Liberation Mono'}, + # Liberation2 fonts (fonts-liberation2 package) + {'path': '/usr/share/fonts/truetype/liberation2/LiberationSans-Bold.ttf', 'name': 'Liberation Sans Bold (v2)'}, + {'path': '/usr/share/fonts/truetype/liberation2/LiberationSans-Regular.ttf', 'name': 'Liberation Sans (v2)'}, + # Noto fonts (fonts-noto package) - Sans-serif + {'path': '/usr/share/fonts/truetype/noto/NotoSans-Bold.ttf', 'name': 'Noto Sans Bold'}, + {'path': '/usr/share/fonts/truetype/noto/NotoSans-Regular.ttf', 'name': 'Noto Sans'}, + {'path': '/usr/share/fonts/truetype/noto/NotoSans-ExtraBold.ttf', 'name': 'Noto Sans ExtraBold'}, + {'path': '/usr/share/fonts/truetype/noto/NotoSans-Light.ttf', 'name': 'Noto Sans Light'}, + # Noto fonts - Serif + {'path': '/usr/share/fonts/truetype/noto/NotoSerif-Bold.ttf', 'name': 'Noto Serif Bold'}, + {'path': '/usr/share/fonts/truetype/noto/NotoSerif-Regular.ttf', 'name': 'Noto Serif'}, + # Noto fonts - Monospace + {'path': '/usr/share/fonts/truetype/noto/NotoMono-Regular.ttf', 'name': 'Noto Mono'}, + # FreeFont (fonts-freefont-ttf package) - Sans-serif + {'path': '/usr/share/fonts/truetype/freefont/FreeSansBold.ttf', 'name': 'FreeSans Bold'}, + {'path': '/usr/share/fonts/truetype/freefont/FreeSans.ttf', 'name': 'FreeSans'}, + # FreeFont - Serif + {'path': '/usr/share/fonts/truetype/freefont/FreeSerifBold.ttf', 'name': 'FreeSerif Bold'}, + {'path': '/usr/share/fonts/truetype/freefont/FreeSerif.ttf', 'name': 'FreeSerif'}, + # FreeFont - Monospace + {'path': '/usr/share/fonts/truetype/freefont/FreeMonoBold.ttf', 'name': 'FreeMono Bold'}, + {'path': '/usr/share/fonts/truetype/freefont/FreeMono.ttf', 'name': 'FreeMono'}, + # Google Fonts - Sans-serif (great for logos) + {'path': '/usr/share/fonts/truetype/google-fonts/Roboto-Black.ttf', 'name': 'Roboto Black'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Roboto-Bold.ttf', 'name': 'Roboto Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Roboto-Regular.ttf', 'name': 'Roboto'}, + {'path': '/usr/share/fonts/truetype/google-fonts/OpenSans-ExtraBold.ttf', 'name': 'Open Sans ExtraBold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/OpenSans-Bold.ttf', 'name': 'Open Sans Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/OpenSans-Regular.ttf', 'name': 'Open Sans'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Montserrat-Black.ttf', 'name': 'Montserrat Black'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Montserrat-Bold.ttf', 'name': 'Montserrat Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Montserrat-Regular.ttf', 'name': 'Montserrat'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Oswald-Bold.ttf', 'name': 'Oswald Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Oswald-Regular.ttf', 'name': 'Oswald'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Raleway-Black.ttf', 'name': 'Raleway Black'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Raleway-Bold.ttf', 'name': 'Raleway Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Raleway-Regular.ttf', 'name': 'Raleway'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Lato-Black.ttf', 'name': 'Lato Black'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Lato-Bold.ttf', 'name': 'Lato Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Lato-Regular.ttf', 'name': 'Lato'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Poppins-Black.ttf', 'name': 'Poppins Black'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Poppins-Bold.ttf', 'name': 'Poppins Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Poppins-Regular.ttf', 'name': 'Poppins'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Anton-Regular.ttf', 'name': 'Anton'}, + {'path': '/usr/share/fonts/truetype/google-fonts/BebasNeue-Regular.ttf', 'name': 'Bebas Neue'}, + {'path': '/usr/share/fonts/truetype/google-fonts/PTSans-Bold.ttf', 'name': 'PT Sans Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/PTSans-Regular.ttf', 'name': 'PT Sans'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Nunito-Black.ttf', 'name': 'Nunito Black'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Nunito-Bold.ttf', 'name': 'Nunito Bold'}, + {'path': '/usr/share/fonts/truetype/google-fonts/Nunito-Regular.ttf', 'name': 'Nunito'}, + + # macOS system fonts (fallback for running outside Docker on Mac) + {'path': '/System/Library/Fonts/Helvetica.ttc', 'name': 'Helvetica'}, + {'path': '/System/Library/Fonts/Supplemental/Arial.ttf', 'name': 'Arial'}, + {'path': '/System/Library/Fonts/Supplemental/Arial Bold.ttf', 'name': 'Arial Bold'}, + {'path': '/System/Library/Fonts/Supplemental/Arial Black.ttf', 'name': 'Arial Black'}, + {'path': '/System/Library/Fonts/Supplemental/Verdana.ttf', 'name': 'Verdana'}, + {'path': '/System/Library/Fonts/Supplemental/Verdana Bold.ttf', 'name': 'Verdana Bold'}, + {'path': '/System/Library/Fonts/Supplemental/Tahoma.ttf', 'name': 'Tahoma'}, + {'path': '/System/Library/Fonts/Supplemental/Tahoma Bold.ttf', 'name': 'Tahoma Bold'}, + {'path': '/System/Library/Fonts/Supplemental/Impact.ttf', 'name': 'Impact'}, + {'path': '/System/Library/Fonts/Supplemental/Times New Roman.ttf', 'name': 'Times New Roman'}, + {'path': '/System/Library/Fonts/Supplemental/Times New Roman Bold.ttf', 'name': 'Times New Roman Bold'}, + + # Windows system fonts (fallback for running outside Docker on Windows) + {'path': 'C:\\Windows\\Fonts\\arial.ttf', 'name': 'Arial (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\arialbd.ttf', 'name': 'Arial Bold (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\ariblk.ttf', 'name': 'Arial Black (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\verdana.ttf', 'name': 'Verdana (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\verdanab.ttf', 'name': 'Verdana Bold (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\tahoma.ttf', 'name': 'Tahoma (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\tahomabd.ttf', 'name': 'Tahoma Bold (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\impact.ttf', 'name': 'Impact (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\times.ttf', 'name': 'Times New Roman (Windows)'}, + {'path': 'C:\\Windows\\Fonts\\timesbd.ttf', 'name': 'Times New Roman Bold (Windows)'}, + ] + + # Filter to only fonts that exist on this system + available = [] + seen_names = set() + for font in font_list: + if os.path.exists(font['path']) and font['name'] not in seen_names: + available.append(font) + seen_names.add(font['name']) + + # If no fonts found, return a placeholder that will trigger default font usage + if not available: + available.append({'path': 'default', 'name': 'System Default'}) + + return available + +def get_font(font_size, font_path=None, detected_font_path=None): """ Get the best available font for text rendering + Falls back to system default if no fonts are available """ try: + # If a specific font was requested and it exists, use it + if font_path and font_path != 'auto' and font_path != 'default' and os.path.exists(font_path): + return ImageFont.truetype(font_path, font_size) + # If we detected a font from the logo, use it if detected_font_path and os.path.exists(detected_font_path): return ImageFont.truetype(detected_font_path, font_size) - # Otherwise try common fonts (prioritize bold/heavy fonts) - font_paths = [ - # macOS paths - '/System/Library/Fonts/Supplemental/Arial Black.ttf', - '/System/Library/Fonts/Supplemental/Impact.ttf', - '/System/Library/Fonts/Supplemental/Arial Bold.ttf', - '/System/Library/Fonts/Helvetica.ttc', - # Linux paths (DejaVu) - '/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf', - '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf', - # Linux paths (Liberation - free alternative to Arial) - '/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf', - '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf', - # Windows paths - 'C:\\Windows\\Fonts\\ariblk.ttf', - 'C:\\Windows\\Fonts\\impact.ttf', - 'C:\\Windows\\Fonts\\arialbd.ttf', - ] - - for font_path in font_paths: - if os.path.exists(font_path): - return ImageFont.truetype(font_path, font_size) + # Otherwise use the first available font + available_fonts = get_available_fonts() + if available_fonts and available_fonts[0]['path'] != 'default': + return ImageFont.truetype(available_fonts[0]['path'], font_size) + # Fall back to PIL's default font + print("Warning: No TrueType fonts found, using PIL default font") return ImageFont.load_default() - except: + except Exception as e: + print(f"Error loading font: {e}. Using default font.") return ImageFont.load_default() def add_text_to_image(image_path, text, position='below', font_size=None, - text_color='white', bg_color=None, padding=None): + text_color='white', bg_color=None, padding=None, font_path=None): """ Add text to an image by expanding the canvas @@ -149,6 +261,7 @@ def add_text_to_image(image_path, text, position='below', font_size=None, text_color: Color of the text bg_color: Background color for the expanded area (transparent if None and image has alpha) padding: Padding around the text (auto if None) + font_path: Specific font path to use (auto if None) Returns: PIL Image object with text added @@ -192,7 +305,7 @@ def add_text_to_image(image_path, text, position='below', font_size=None, detected_font_path = detect_font_from_image(img) # Get the appropriate font - font = get_font(font_size, detected_font_path) + font = get_font(font_size, font_path, detected_font_path) # Create a temporary image to measure text size temp_img = Image.new('RGB', (1, 1)) @@ -260,13 +373,135 @@ def add_text_to_image(image_path, text, position='below', font_size=None, draw = ImageDraw.Draw(new_img) draw.text((text_x, text_y), text, fill=text_color, font=font) - return new_img + # Prepare metadata about what was used + metadata = { + 'font_size_used': font_size, + 'padding_used': padding, + 'font_name_used': None + } + + # Try to get font name + if font_path and font_path != 'auto' and os.path.exists(font_path): + metadata['font_name_used'] = os.path.basename(font_path) + elif detected_font_path and os.path.exists(detected_font_path): + metadata['font_name_used'] = os.path.basename(detected_font_path) + else: + available_fonts = get_available_fonts() + if available_fonts and available_fonts[0]['path'] != 'default': + metadata['font_name_used'] = available_fonts[0]['name'] + + return new_img, metadata @app.route('/') def index(): """Serve the web interface""" return render_template('index.html') +@app.route('/api/fonts', methods=['GET']) +def get_fonts(): + """Get list of available fonts""" + fonts = get_available_fonts() + return jsonify({'fonts': fonts}) + +@app.route('/api/tv-logos', methods=['GET']) +def get_tv_logos(): + """ + Fetch TV logos from github.com/tv-logo/tv-logos repository + + Query parameters: + - search: Search query (fuzzy, case-insensitive) + - country: Filter by country code + """ + try: + # Get query parameters + search_query = request.args.get('search', '').lower() + country_filter = request.args.get('country', '').lower() + + # Use GitHub API to get the repository tree recursively + # Try 'main' branch (modern default) + github_api_url = "https://api.github.com/repos/tv-logo/tv-logos/git/trees/main?recursive=1" + + headers = {'Accept': 'application/vnd.github.v3+json'} + response = requests.get(github_api_url, headers=headers, timeout=30) + + # If 'main' doesn't exist, try 'master' + if response.status_code == 404: + github_api_url = "https://api.github.com/repos/tv-logo/tv-logos/git/trees/master?recursive=1" + response = requests.get(github_api_url, headers=headers, timeout=30) + + response.raise_for_status() + + tree_data = response.json() + + # Determine which branch worked + branch = 'main' if 'main' in github_api_url else 'master' + + logos = [] + countries = set() + + # Process all files in the tree + for item in tree_data.get('tree', []): + path = item.get('path', '') + + # Skip if not an image file + if not path.lower().endswith(('.png', '.jpg', '.jpeg', '.svg')): + continue + + # Parse the path - expected format: countries/COUNTRY/logo.png + path_parts = path.split('/') + + # Extract country from path like: countries/usa/logo.png + country = None + if len(path_parts) >= 3 and path_parts[0] == 'countries': + country = path_parts[1] + countries.add(country) + elif len(path_parts) >= 2: + # Fallback for other structures - use first directory + first_dir = path_parts[0] + if first_dir not in ['.github', 'docs', 'scripts', 'paypal-donate', 'README.md']: + country = first_dir + countries.add(country) + + # Skip if no country identified + if not country: + continue + + # Get filename + filename = path_parts[-1] + logo_name = os.path.splitext(filename)[0] + + # Apply country filter + if country_filter and country.lower() != country_filter: + continue + + # Apply search filter (filename only) + if search_query and search_query not in logo_name.lower(): + continue + + # Construct the raw GitHub URL + raw_url = f"https://raw.githubusercontent.com/tv-logo/tv-logos/{branch}/{path}" + + logos.append({ + 'name': logo_name, + 'country': country, + 'url': raw_url, + 'thumbnail': raw_url, + 'path': path + }) + + # Sort logos by name + logos.sort(key=lambda x: x['name'].lower()) + + # Return ALL logos without limits - frontend will handle pagination + return jsonify({ + 'logos': logos, + 'countries': sorted(list(countries)), + 'total': len(logos) + }) + + except Exception as e: + return jsonify({'error': f'Failed to fetch logos: {str(e)}'}), 500 + @app.route('/api/process', methods=['POST']) def process_image(): """ @@ -329,6 +564,11 @@ def process_image(): if bg_color == '' or bg_color == 'transparent' or bg_color == 'auto': bg_color = None + # Get font path if specified + font_path = request.form.get('font_path', None) + if font_path == '' or font_path == 'auto': + font_path = None + # Save uploaded file filename = secure_filename(file.filename) filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename) @@ -336,14 +576,15 @@ def process_image(): try: # Process the image - result_img = add_text_to_image( + result_img, metadata = add_text_to_image( filepath, text, position, font_size, text_color, bg_color, - padding + padding, + font_path ) # Save to bytes buffer @@ -383,12 +624,17 @@ def process_image(): 'WEBP': 'image/webp' } - return send_file( + response = send_file( img_io, mimetype=mimetype_map.get(output_format, 'image/png'), as_attachment=True, download_name=f'processed_{filename}' ) + response.headers['X-Font-Size-Used'] = str(metadata['font_size_used']) + response.headers['X-Padding-Used'] = str(metadata['padding_used']) + if metadata['font_name_used']: + response.headers['X-Font-Name-Used'] = metadata['font_name_used'] + return response except Exception as e: # Clean up on error @@ -460,6 +706,11 @@ def process_image_url(): if bg_color == '' or bg_color == 'transparent' or bg_color == 'auto': bg_color = None + # Get font path if specified + font_path = request.args.get('font_path', None) + if font_path == '' or font_path == 'auto': + font_path = None + try: # Download the image response = requests.get(image_url, timeout=10, headers={'User-Agent': 'LogoTextAdder/1.0'}) @@ -495,14 +746,15 @@ def process_image_url(): f.write(response.content) # Process the image - result_img = add_text_to_image( + result_img, metadata = add_text_to_image( temp_filepath, text, position, font_size, text_color, bg_color, - padding + padding, + font_path ) # Save to bytes buffer @@ -528,12 +780,17 @@ def process_image_url(): os.remove(temp_filepath) # Return image directly - return send_file( + response = send_file( img_io, mimetype=mimetype, as_attachment=False, download_name=f'logo_{text[:20].replace(" ", "_")}.{output_format.lower()}' ) + response.headers['X-Font-Size-Used'] = str(metadata['font_size_used']) + response.headers['X-Padding-Used'] = str(metadata['padding_used']) + if metadata['font_name_used']: + response.headers['X-Font-Name-Used'] = metadata['font_name_used'] + return response except requests.RequestException as e: return jsonify({'error': f'Failed to download image: {str(e)}'}), 400 diff --git a/templates/index.html b/templates/index.html index bd17ac2..f4ecc5e 100644 --- a/templates/index.html +++ b/templates/index.html @@ -3,7 +3,7 @@ - Logo Text Adder + logo-txt