diff --git a/links/mini_apps_views.py b/links/mini_apps_views.py index 97f8ab0..c9d1286 100644 --- a/links/mini_apps_views.py +++ b/links/mini_apps_views.py @@ -19,44 +19,19 @@ class MiniAppsListView(TemplateView): 'color': '#3498db' }, { + 'name': 'TTS (Text-to-Speech)', + 'description': 'Convert any text or markdown content to high-quality speech audio.', + 'url': 'mini-apps-tts', + 'thumbnail': 'https://images.unsplash.com/photo-1589254065878-42c9da997008?w=400&h=300&fit=crop', + 'icon': 'fas fa-volume-up', + 'color': '#8e44ad' + },{ 'name': 'Weather Dashboard', 'description': 'Real-time weather information with beautiful visualizations and forecasts.', 'url': '#', 'thumbnail': 'https://images.unsplash.com/photo-1504608524841-42fe6f032b4b?w=400&h=300&fit=crop', 'icon': 'fas fa-cloud-sun', 'color': '#e74c3c' - }, - { - 'name': 'Music Player', - 'description': 'Stream and organize your favorite music with an elegant interface.', - 'url': '#', - 'thumbnail': 'https://images.unsplash.com/photo-1493225457124-a3eb161ffa5f?w=400&h=300&fit=crop', - 'icon': 'fas fa-music', - 'color': '#9b59b6' - }, - { - 'name': 'Task Manager', - 'description': 'Organize your tasks and boost productivity with smart scheduling.', - 'url': '#', - 'thumbnail': 'https://images.unsplash.com/photo-1484480974693-6ca0a78fb36b?w=400&h=300&fit=crop', - 'icon': 'fas fa-tasks', - 'color': '#2ecc71' - }, - { - 'name': 'Code Editor', - 'description': 'A lightweight code editor with syntax highlighting and themes.', - 'url': '#', - 'thumbnail': 'https://images.unsplash.com/photo-1461749280684-dccba630e2f6?w=400&h=300&fit=crop', - 'icon': 'fas fa-code', - 'color': '#f39c12' - }, - { - 'name': 'Calculator', - 'description': 'Scientific calculator with advanced mathematical functions.', - 'url': '#', - 'thumbnail': 'https://images.unsplash.com/photo-1611224923853-80b023f02d71?w=400&h=300&fit=crop', - 'icon': 'fas fa-calculator', - 'color': '#1abc9c' } ] @@ -71,3 +46,12 @@ class ImageGalleryView(TemplateView): context = super().get_context_data(**kwargs) # Add any context data needed for the image gallery return context + + +class TTSView(TemplateView): + template_name = 'links/mini_apps/tts.html' + + def get_context_data(self, **kwargs): + context = super().get_context_data(**kwargs) + context['page_title'] = 'Text-to-Speech' + return context diff --git a/links/templates/links/mini_apps/tts.html b/links/templates/links/mini_apps/tts.html new file mode 100644 index 0000000..b9dac46 --- /dev/null +++ b/links/templates/links/mini_apps/tts.html @@ -0,0 +1,783 @@ +{% extends 'base.html' %} +{% load i18n %} +{% load static %} + +{% block title %}{% trans "Text-to-Speech" %} - {% trans "Mini Apps" %}{% endblock %} + +{% block extra_css %} + +{% endblock %} + +{% block content %} +
+ +
+

{% trans "Text-to-Speech" %}

+

{% trans "Convert any text or markdown content to high-quality speech audio" %}

+
+ + +
+ + + +
+ + +
0 / 3000
+
+ + +
+ + + +
+
+ + + +
+{% endblock %} + +{% block extra_js %} + +{% endblock %} diff --git a/links/urls.py b/links/urls.py index 9f8605a..dcc6d34 100644 --- a/links/urls.py +++ b/links/urls.py @@ -51,6 +51,10 @@ urlpatterns = [ # Mini Apps path('ui/mini-apps/', mini_apps_views.MiniAppsListView.as_view(), name='mini-apps-list'), path('ui/mini-apps/image-gallery/', mini_apps_views.ImageGalleryView.as_view(), name='mini-apps-image-gallery'), + path('ui/mini-apps/tts/', mini_apps_views.TTSView.as_view(), name='mini-apps-tts'), + + # TTS API + path('ui/tts-api/', views.generate_tts_api, name='tts-api'), # API Documentation path('ui/api-docs/', diff --git a/links/views.py b/links/views.py index 525fd5d..c1e569c 100644 --- a/links/views.py +++ b/links/views.py @@ -93,7 +93,7 @@ class LinkListView(ListView): # Add pinned mini apps with random colors mini_apps = [ {'name': 'Image Gallery', 'description': 'Browse Images', 'url': 'mini-apps-image-gallery'}, - {'name': 'Coming Soon', 'description': 'New App', 'url': '#'}, + {'name': 'TTS', 'description': 'Convert text to speech', 'url': 'mini-apps-tts'}, {'name': 'Coming Soon', 'description': 'New App', 'url': '#'} ] context['mini_apps'] = [ @@ -543,6 +543,50 @@ def generate_tts(request, post_id): except Exception as e: return JsonResponse({'error': f'Server error: {str(e)}'}, status=500) +@require_http_methods(["POST"]) +def generate_tts_api(request): + """General TTS API that accepts text content""" + try: + import json + + # Get text from request body + if request.content_type == 'application/json': + data = json.loads(request.body) + text_content = data.get('text', '') + else: + text_content = request.POST.get('text', '') + + if not text_content.strip(): + return JsonResponse({'error': 'No text content provided'}, status=400) + + # Clean the text content - remove markdown and HTML + clean_text = clean_text_for_tts(text_content) + + if not clean_text.strip(): + return JsonResponse({'error': 'No valid text content to convert'}, status=400) + + # Call TTS API + import urllib.parse + encoded_text = urllib.parse.quote(clean_text) + tts_url = f"https://home-tts.junv.workers.dev/tts?text={encoded_text}&haha=hahaJunv" + + try: + response = requests.get(tts_url, timeout=30) + response.raise_for_status() + + # Return the audio file + audio_response = HttpResponse(response.content, content_type='audio/mpeg') + audio_response['Content-Disposition'] = f'inline; filename="tts_audio.mp3"' + return audio_response + + except requests.RequestException as e: + return JsonResponse({'error': f'TTS service error: {str(e)}'}, status=500) + + except json.JSONDecodeError: + return JsonResponse({'error': 'Invalid JSON data'}, status=400) + except Exception as e: + return JsonResponse({'error': f'Server error: {str(e)}'}, status=500) + def clean_text_for_tts(content): """Clean markdown and HTML from text for TTS""" # Convert markdown to HTML first