xdxb commited on
Commit
22fc289
·
verified ·
1 Parent(s): ce2e6c7

Create celery_worker.py

Browse files
Files changed (1) hide show
  1. celery_worker.py +178 -0
celery_worker.py ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from celery import Celery
2
+ import os
3
+ from dotenv import load_dotenv
4
+ import requests
5
+ import time
6
+ import random
7
+ import logging
8
+ import uuid
9
+ from fake_useragent import UserAgent
10
+ import re
11
+
12
+ # Load environment variables
13
+ load_dotenv()
14
+
15
+ # Configure logging
16
+ logging.basicConfig(
17
+ level=logging.INFO,
18
+ format='%(asctime)s - %(name)s - %(levelname)s - %(message)s'
19
+ )
20
+ logger = logging.getLogger(__name__)
21
+
22
+ # Initialize User-Agent
23
+ ua = UserAgent()
24
+
25
+ # Create Celery instance
26
+ celery = Celery('ttsfm',
27
+ broker=os.getenv('CELERY_BROKER_URL', 'redis://localhost:6379/0'),
28
+ backend=os.getenv('CELERY_RESULT_BACKEND', 'redis://localhost:6379/0'))
29
+
30
+ # Celery Configuration
31
+ celery.conf.update(
32
+ task_serializer='json',
33
+ accept_content=['json'],
34
+ result_serializer='json',
35
+ timezone='UTC',
36
+ enable_utc=True,
37
+ task_track_started=True,
38
+ # Read settings from environment variables with defaults
39
+ task_time_limit=int(os.getenv('CELERY_TASK_TIME_LIMIT', '300')), # 5 minutes max
40
+ worker_prefetch_multiplier=int(os.getenv('CELERY_WORKER_PREFETCH_MULTIPLIER', '1')), # Process one task at a time
41
+ worker_max_tasks_per_child=int(os.getenv('CELERY_WORKER_MAX_TASKS_PER_CHILD', '1000')), # Restart worker after 1000 tasks
42
+ )
43
+
44
+ def _get_headers():
45
+ """Generate realistic browser headers with rotation"""
46
+
47
+ # Get a random User-Agent
48
+ user_agent = ua.random
49
+
50
+ # Base headers common to most browsers
51
+ headers = {
52
+ "Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
53
+ "Accept-Encoding": "gzip, deflate, br",
54
+ "Accept-Language": random.choice(["en-US,en;q=0.9", "en-GB,en;q=0.8", "en-CA,en;q=0.7"]),
55
+ "Cache-Control": "no-cache",
56
+ "Dnt": "1", # Do Not Track
57
+ "Pragma": "no-cache",
58
+ "Referer": "https://www.openai.fm/",
59
+ "Sec-Fetch-Dest": "empty",
60
+ "Sec-Fetch-Mode": "cors",
61
+ "Sec-Fetch-Site": "same-origin",
62
+ "User-Agent": user_agent,
63
+ "X-Requested-With": "XMLHttpRequest", # Often used in AJAX requests
64
+ }
65
+
66
+ # Add browser-specific headers (Sec-CH-UA) if applicable
67
+ # These are primarily for Chromium-based browsers (Chrome, Edge, Opera, etc.)
68
+ if 'chrome' in user_agent.lower() or 'edge' in user_agent.lower() or 'chromium' in user_agent.lower():
69
+ # Extract major version number (handle cases where it might not be present)
70
+ version_match = re.search(r'(?:Chrome|Edge|Chromium)/(\d+)', user_agent)
71
+ major_version = version_match.group(1) if version_match else "121" # Default if not found
72
+
73
+ brands = []
74
+ if 'google chrome' in user_agent.lower():
75
+ brands.append(f'"Google Chrome";v="{major_version}"')
76
+ brands.append(f'"Chromium";v="{major_version}"')
77
+ brands.append('"Not A(Brand";v="99"')
78
+ elif 'microsoft edge' in user_agent.lower():
79
+ brands.append(f'"Microsoft Edge";v="{major_version}"')
80
+ brands.append(f'"Chromium";v="{major_version}"')
81
+ brands.append('"Not A(Brand";v="99"')
82
+ else: # Generic Chromium or others
83
+ brands.append(f'"Chromium";v="{major_version}"')
84
+ brands.append('"Not A(Brand";v="8"')
85
+
86
+
87
+ headers["Sec-Ch-Ua"] = ", ".join(brands)
88
+ headers["Sec-Ch-Ua-Mobile"] = "?0" # Assuming desktop
89
+ headers["Sec-Ch-Ua-Platform"] = random.choice(['"Windows"', '"macOS"', '"Linux"'])
90
+
91
+ # Add Upgrade-Insecure-Requests sometimes (common for initial navigation)
92
+ if random.random() < 0.5:
93
+ headers["Upgrade-Insecure-Requests"] = "1"
94
+
95
+ # Use Authority or Host - Authority is more common with HTTP/2
96
+ headers["Authority"] = "www.openai.fm" # Prefer Authority for HTTP/2
97
+
98
+ return headers
99
+
100
+ def _get_random_delay():
101
+ """Get random delay time (1-5 seconds) with jitter"""
102
+ base_delay = random.uniform(1, 5)
103
+ jitter = random.uniform(0.1, 0.5)
104
+ return base_delay + jitter
105
+
106
+ @celery.task(bind=True, name='tasks.process_tts_request')
107
+ def process_tts_request(self, task_data):
108
+ """Process a TTS request and return the audio data"""
109
+ max_retries = 3
110
+ retry_count = 0
111
+ base_delay = 1
112
+ verify_ssl = os.getenv("VERIFY_SSL", "true").lower() != "false"
113
+
114
+ while retry_count < max_retries:
115
+ try:
116
+ # Add random delay between requests for more natural behavior
117
+ time.sleep(_get_random_delay())
118
+
119
+ logger.info(f"Sending request to OpenAI.fm with data: {task_data['data']}")
120
+
121
+ # Add generation ID to request data
122
+ task_data['data']['generation'] = str(uuid.uuid4())
123
+
124
+ # Check format setting
125
+ if 'format' in task_data['data']:
126
+ logger.info(f"Requesting audio in format: {task_data['data']['format']}")
127
+
128
+ response = requests.post(
129
+ "https://www.openai.fm/api/generate",
130
+ data=task_data['data'],
131
+ headers=_get_headers(),
132
+ timeout=30,
133
+ verify=verify_ssl
134
+ )
135
+
136
+ if response.status_code == 403:
137
+ logger.warning("Received 403 Forbidden from OpenAI.fm")
138
+ retry_count += 1
139
+ time.sleep(base_delay * (2 ** retry_count)) # Exponential backoff
140
+ continue
141
+
142
+ if response.status_code == 429:
143
+ logger.warning("Rate limited by OpenAI.fm")
144
+ retry_after = int(response.headers.get('Retry-After', 60))
145
+ self.retry(countdown=retry_after)
146
+
147
+ if response.status_code == 503:
148
+ logger.warning("Service unavailable from OpenAI.fm")
149
+ retry_count += 1
150
+ time.sleep(base_delay * (2 ** retry_count))
151
+ continue
152
+
153
+ if response.status_code != 200:
154
+ logger.error(f"Error from OpenAI.fm: {response.status_code}")
155
+ error_msg = f"Error from upstream service: {response.status_code}"
156
+ return None, error_msg, response.status_code
157
+
158
+ # Return the audio data, content type, and status code
159
+ return response.content, None, 200
160
+
161
+ except requests.exceptions.Timeout:
162
+ logger.error("Request timeout")
163
+ retry_count += 1
164
+ time.sleep(base_delay * (2 ** retry_count))
165
+ except requests.exceptions.RequestException as e:
166
+ logger.error(f"Network error: {str(e)}")
167
+ retry_count += 1
168
+ time.sleep(base_delay * (2 ** retry_count))
169
+ except Exception as e:
170
+ logger.error(f"Error processing TTS request: {str(e)}")
171
+ retry_count += 1
172
+ time.sleep(base_delay * (2 ** retry_count))
173
+ if retry_count >= max_retries:
174
+ return None, str(e), 500
175
+
176
+ # If we've exhausted retries
177
+ logger.error("Exhausted retries for TTS request")
178
+ return None, "Failed to process request after multiple retries", 500