TDN-M commited on
Commit
36d7d16
·
1 Parent(s): 1408b69
Files changed (5) hide show
  1. app.py +8 -1
  2. content_generation.py +10 -1
  3. requirements.txt +31 -12
  4. sports_news.py +218 -0
  5. test_sports_news.py +116 -0
app.py CHANGED
@@ -15,6 +15,7 @@ from TTS.tts.configs.xtts_config import XttsConfig
15
  from TTS.tts.models.xtts import Xtts
16
  from vinorm import TTSnorm
17
  from content_generation import create_content # Nhập hàm create_content từ file content_generation.py
 
18
  from PIL import Image
19
  from pathlib import Path
20
  import requests
@@ -250,6 +251,12 @@ def predict(
250
  generated_text = create_content(prompt, content_type, language)
251
  print(f"Generated text: {generated_text}")
252
  prompt = generated_text
 
 
 
 
 
 
253
  if language not in supported_languages:
254
  metrics_text = gr.Warning(
255
  f"Language you put {language} in is not in our Supported Languages, please choose from dropdown"
@@ -399,7 +406,7 @@ with gr.Blocks(analytics_enabled=False) as demo:
399
  )
400
  content_type_dropdown = gr.Dropdown(
401
  label="Loại nội dung",
402
- choices=["triết lý sống", "Theo yêu cầu"],
403
  value="Theo yêu cầu",
404
  )
405
  ref_gr = gr.Audio(
 
15
  from TTS.tts.models.xtts import Xtts
16
  from vinorm import TTSnorm
17
  from content_generation import create_content # Nhập hàm create_content từ file content_generation.py
18
+ from sports_news import get_sports_news_content # Nhập hàm lấy tin thể thao
19
  from PIL import Image
20
  from pathlib import Path
21
  import requests
 
251
  generated_text = create_content(prompt, content_type, language)
252
  print(f"Generated text: {generated_text}")
253
  prompt = generated_text
254
+ elif content_type in ["tin thể thao", "tin bóng đá"]:
255
+ print("I: Fetching sports news...")
256
+ news_type = "football" if content_type == "tin bóng đá" else "all"
257
+ sports_content = get_sports_news_content(news_type, language, 5)
258
+ print(f"Sports content: {sports_content}")
259
+ prompt = sports_content
260
  if language not in supported_languages:
261
  metrics_text = gr.Warning(
262
  f"Language you put {language} in is not in our Supported Languages, please choose from dropdown"
 
406
  )
407
  content_type_dropdown = gr.Dropdown(
408
  label="Loại nội dung",
409
+ choices=["triết lý sống", "Theo yêu cầu", "tin thể thao", "tin bóng đá"],
410
  value="Theo yêu cầu",
411
  )
412
  ref_gr = gr.Audio(
content_generation.py CHANGED
@@ -1,12 +1,13 @@
1
  # content_generation.py
2
  from groq import Groq
3
  import os
 
4
 
5
  # Lấy API key từ biến môi trường
6
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
7
 
8
  # Danh sách loại nội dung và hướng dẫn mặc định cho từng loại
9
- CONTENT_TYPES = ["triết lý sống", "Theo yêu cầu"]
10
  CONTENT_TYPE_INSTRUCTIONS = {
11
  "Theo yêu cầu": """
12
  Bạn hãy nghe theo yêu cầu của người dùng để viết một kịch bản xuất sắc về văn phong, truyền cảm hứng và có giá trị thông tin. Không bao gồm bất kỳ chỗ giữ chỗ nào trong ngoặc như [Host] hoặc [Guest]. Thiết kế đầu ra của bạn để có thể đọc to -- nó sẽ được chuyển đổi trực tiếp thành âm thanh. Chỉ có một người nói, đó là bạn. Giữ đúng chủ đề và duy trì luồng hấp dẫn.
@@ -17,6 +18,14 @@ Bạn cần viết một kịch bản khiến người nghe bị cuốn vào t
17
  Bạn sẽ chỉ viết nội dung sẽ thu âm thành tiếng mà không cần có thêm các phần chú thích trong ngoặc vuông hay ngoặc tròn hay bất cứ gì khác nằm ngoài phạm vi sẽ thu âm.. Thiết kế đầu ra của bạn để có thể đọc to -- nó sẽ được chuyển đổi trực tiếp thành âm thanh.
18
  Chú trọng vào việc nhấn mạnh những thông điệp quan trọng bằng ngôn từ mạnh mẽ, ấn tượng. Văn phong cần có sự kết hợp giữa sự nhẹ nhàng, tĩnh lặng như một lời tâm sự, nhưng cũng đủ sức khơi gợi suy nghĩ sâu xa. Mỗi đoạn nội dung cần có điểm nhấn đáng nhớ, dễ in sâu vào tâm trí người nghe. Hãy đảm bảo nội dung rõ ràng, mạch lạc, và truyền tải được sự ấm áp, chân thành.
19
  Độ dài kịch bản này là khoảng 1000 từ.
 
 
 
 
 
 
 
 
20
  """
21
  }
22
 
 
1
  # content_generation.py
2
  from groq import Groq
3
  import os
4
+ from sports_news import get_sports_news_content
5
 
6
  # Lấy API key từ biến môi trường
7
  GROQ_API_KEY = os.environ.get("GROQ_API_KEY")
8
 
9
  # Danh sách loại nội dung và hướng dẫn mặc định cho từng loại
10
+ CONTENT_TYPES = ["triết lý sống", "Theo yêu cầu", "tin thể thao", "tin bóng đá"]
11
  CONTENT_TYPE_INSTRUCTIONS = {
12
  "Theo yêu cầu": """
13
  Bạn hãy nghe theo yêu cầu của người dùng để viết một kịch bản xuất sắc về văn phong, truyền cảm hứng và có giá trị thông tin. Không bao gồm bất kỳ chỗ giữ chỗ nào trong ngoặc như [Host] hoặc [Guest]. Thiết kế đầu ra của bạn để có thể đọc to -- nó sẽ được chuyển đổi trực tiếp thành âm thanh. Chỉ có một người nói, đó là bạn. Giữ đúng chủ đề và duy trì luồng hấp dẫn.
 
18
  Bạn sẽ chỉ viết nội dung sẽ thu âm thành tiếng mà không cần có thêm các phần chú thích trong ngoặc vuông hay ngoặc tròn hay bất cứ gì khác nằm ngoài phạm vi sẽ thu âm.. Thiết kế đầu ra của bạn để có thể đọc to -- nó sẽ được chuyển đổi trực tiếp thành âm thanh.
19
  Chú trọng vào việc nhấn mạnh những thông điệp quan trọng bằng ngôn từ mạnh mẽ, ấn tượng. Văn phong cần có sự kết hợp giữa sự nhẹ nhàng, tĩnh lặng như một lời tâm sự, nhưng cũng đủ sức khơi gợi suy nghĩ sâu xa. Mỗi đoạn nội dung cần có điểm nhấn đáng nhớ, dễ in sâu vào tâm trí người nghe. Hãy đảm bảo nội dung rõ ràng, mạch lạc, và truyền tải được sự ấm áp, chân thành.
20
  Độ dài kịch bản này là khoảng 1000 từ.
21
+ """,
22
+ "tin thể thao": """
23
+ Bạn là một MC thể thao chuyên nghiệp, hãy đọc tin thể thao với giọng điệu sôi động, hấp dẫn và đầy nhiệt huyết. Sử dụng ngôn từ phù hợp với từng môn thể thao, tạo cảm giác hứng khởi cho người nghe.
24
+ Hãy đọc tin một cách tự nhiên, không cần thêm bình luận hay chú thích. Tập trung vào việc truyền tải thông tin chính xác và sinh động.
25
+ """,
26
+ "tin bóng đá": """
27
+ Bạn là một bình luận viên bóng đá chuyên nghiệp, hãy đọc tin bóng đá với giọng điệu đầy cảm xúc và nhiệt huyết. Sử dụng thuật ngữ bóng đá phù hợp, tạo không khí sôi động như đang ở sân vận động.
28
+ Đọc tin một cách tự nhiên, sinh động, không cần thêm bình luận cá nhân. Tập trung vào việc truyền tải thông tin về các trận đấu, cầu thủ và giải đấu.
29
  """
30
  }
31
 
requirements.txt CHANGED
@@ -1,18 +1,37 @@
1
- # Preinstall requirements from TTS
2
- TTS @ git+https://github.com/thinhlpg/TTS.git@ff217b3f27b294de194cc59c5119d1e08b06413c
3
  typing-extensions>=4.8.0
 
 
 
 
 
 
 
 
 
 
 
4
  cutlet
5
  mecab-python3==1.0.6
6
  langid
7
- deepspeed
8
- pydub
9
  gradio==4.36.1
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
  groq
11
- opencv-python
12
- moviepy
13
- requests
14
- Pillow
15
- anyio
16
- # Vietnamese 101
17
- vinorm==2.0.7
18
- underthesea==6.8.0
 
1
+ # Core TTS requirements
2
+ TTS>=0.22.0
3
  typing-extensions>=4.8.0
4
+ torch>=1.9.0
5
+ torchaudio>=0.9.0
6
+ huggingface-hub>=0.16.0
7
+
8
+ # Audio processing
9
+ pydub>=0.25.1
10
+ soundfile>=0.12.1
11
+
12
+ # Vietnamese text processing
13
+ vinorm==2.0.7
14
+ underthesea==6.8.0
15
  cutlet
16
  mecab-python3==1.0.6
17
  langid
18
+
19
+ # Web interface
20
  gradio==4.36.1
21
+
22
+ # Image and video processing
23
+ opencv-python>=4.8.0
24
+ moviepy>=1.0.3
25
+ Pillow>=9.0.0
26
+
27
+ # API and web requests
28
+ requests>=2.28.0
29
+ feedparser>=6.0.10
30
+ beautifulsoup4>=4.11.0
31
+ lxml>=4.9.0
32
+
33
+ # Utilities
34
+ anyio>=3.6.0
35
  groq
36
+ numpy>=1.21.0
37
+ pandas>=1.5.0
 
 
 
 
 
 
sports_news.py ADDED
@@ -0,0 +1,218 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Module lấy tin thể thao quốc tế từ các nguồn miễn phí
3
+ Hỗ trợ RSS feeds và API miễn phí
4
+ """
5
+
6
+ import requests
7
+ import feedparser
8
+ from bs4 import BeautifulSoup
9
+ import json
10
+ from datetime import datetime, timedelta
11
+ import re
12
+ from typing import List, Dict, Optional
13
+ import logging
14
+
15
+ # Cấu hình logging
16
+ logging.basicConfig(level=logging.INFO)
17
+ logger = logging.getLogger(__name__)
18
+
19
+ class SportsNewsAggregator:
20
+ """Class tổng hợp tin thể thao từ nhiều nguồn miễn phí"""
21
+
22
+ def __init__(self):
23
+ self.sources = {
24
+ 'bbc_sport': {
25
+ 'url': 'http://feeds.bbci.co.uk/sport/rss.xml',
26
+ 'type': 'rss',
27
+ 'language': 'en'
28
+ },
29
+ 'espn': {
30
+ 'url': 'https://www.espn.com/espn/rss/news',
31
+ 'type': 'rss',
32
+ 'language': 'en'
33
+ },
34
+ 'sky_sports': {
35
+ 'url': 'https://www.skysports.com/rss/12040',
36
+ 'type': 'rss',
37
+ 'language': 'en'
38
+ },
39
+ 'vnexpress_sport': {
40
+ 'url': 'https://vnexpress.net/rss/the-thao.rss',
41
+ 'type': 'rss',
42
+ 'language': 'vi'
43
+ },
44
+ 'thanhnien_sport': {
45
+ 'url': 'https://thanhnien.vn/rss/the-thao.rss',
46
+ 'type': 'rss',
47
+ 'language': 'vi'
48
+ }
49
+ }
50
+
51
+ self.session = requests.Session()
52
+ self.session.headers.update({
53
+ 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36'
54
+ })
55
+
56
+ def get_rss_news(self, source_config: Dict) -> List[Dict]:
57
+ """Lấy tin từ RSS feed"""
58
+ try:
59
+ response = self.session.get(source_config['url'], timeout=10)
60
+ response.raise_for_status()
61
+
62
+ feed = feedparser.parse(response.content)
63
+ news_items = []
64
+
65
+ for entry in feed.entries[:10]: # Lấy 10 tin mới nhất
66
+ # Xử lý thời gian
67
+ pub_date = None
68
+ if hasattr(entry, 'published_parsed') and entry.published_parsed:
69
+ pub_date = datetime(*entry.published_parsed[:6])
70
+ elif hasattr(entry, 'updated_parsed') and entry.updated_parsed:
71
+ pub_date = datetime(*entry.updated_parsed[:6])
72
+
73
+ # Làm sạch nội dung
74
+ summary = self.clean_text(getattr(entry, 'summary', ''))
75
+ title = self.clean_text(getattr(entry, 'title', ''))
76
+
77
+ news_item = {
78
+ 'title': title,
79
+ 'summary': summary,
80
+ 'link': getattr(entry, 'link', ''),
81
+ 'published': pub_date.isoformat() if pub_date else None,
82
+ 'source': source_config.get('name', 'Unknown'),
83
+ 'language': source_config.get('language', 'en')
84
+ }
85
+
86
+ news_items.append(news_item)
87
+
88
+ return news_items
89
+
90
+ except Exception as e:
91
+ logger.error(f"Error fetching RSS from {source_config['url']}: {str(e)}")
92
+ return []
93
+
94
+ def clean_text(self, text: str) -> str:
95
+ """Làm sạch văn bản HTML và ký tự đặc biệt"""
96
+ if not text:
97
+ return ""
98
+
99
+ # Loại bỏ HTML tags
100
+ soup = BeautifulSoup(text, 'html.parser')
101
+ text = soup.get_text()
102
+
103
+ # Loại bỏ ký tự xuống dòng và khoảng trắng thừa
104
+ text = re.sub(r'\s+', ' ', text).strip()
105
+
106
+ return text
107
+
108
+ def get_all_sports_news(self, language: Optional[str] = None, limit: int = 20) -> List[Dict]:
109
+ """Lấy tin thể thao từ tất cả nguồn"""
110
+ all_news = []
111
+
112
+ for source_name, source_config in self.sources.items():
113
+ # Lọc theo ngôn ngữ nếu được chỉ định
114
+ if language and source_config.get('language') != language:
115
+ continue
116
+
117
+ logger.info(f"Fetching news from {source_name}")
118
+ source_config['name'] = source_name
119
+
120
+ if source_config['type'] == 'rss':
121
+ news_items = self.get_rss_news(source_config)
122
+ all_news.extend(news_items)
123
+
124
+ # Sắp xếp theo thời gian (mới nhất trước)
125
+ all_news.sort(key=lambda x: x.get('published', ''), reverse=True)
126
+
127
+ return all_news[:limit]
128
+
129
+ def get_football_news(self, limit: int = 10) -> List[Dict]:
130
+ """Lấy tin bóng đá cụ thể"""
131
+ all_news = self.get_all_sports_news(limit=50)
132
+
133
+ # Lọc tin bóng đá
134
+ football_keywords = [
135
+ 'football', 'soccer', 'fifa', 'premier league', 'champions league',
136
+ 'bóng đá', 'world cup', 'euro', 'manchester', 'liverpool', 'arsenal',
137
+ 'real madrid', 'barcelona', 'psg', 'bayern', 'juventus'
138
+ ]
139
+
140
+ football_news = []
141
+ for news in all_news:
142
+ title_lower = news['title'].lower()
143
+ summary_lower = news['summary'].lower()
144
+
145
+ if any(keyword in title_lower or keyword in summary_lower
146
+ for keyword in football_keywords):
147
+ football_news.append(news)
148
+
149
+ return football_news[:limit]
150
+
151
+ def format_news_for_tts(self, news_items: List[Dict], language: str = 'vi') -> str:
152
+ """Format tin tức thành văn bản phù hợp cho TTS"""
153
+ if not news_items:
154
+ return "Không có tin thể thao mới nào được tìm thấy."
155
+
156
+ if language == 'vi':
157
+ intro = "Tin thể thao quốc tế mới nhất:\n\n"
158
+ else:
159
+ intro = "Latest international sports news:\n\n"
160
+
161
+ formatted_text = intro
162
+
163
+ for i, news in enumerate(news_items[:5], 1): # Chỉ lấy 5 tin đầu
164
+ title = news['title']
165
+ summary = news['summary'][:200] + "..." if len(news['summary']) > 200 else news['summary']
166
+
167
+ if language == 'vi':
168
+ news_text = f"Tin thứ {i}: {title}. {summary}\n\n"
169
+ else:
170
+ news_text = f"News {i}: {title}. {summary}\n\n"
171
+
172
+ formatted_text += news_text
173
+
174
+ return formatted_text
175
+
176
+ # Hàm tiện ích để sử dụng trong app chính
177
+ def get_sports_news_content(news_type: str = 'all', language: str = 'vi', limit: int = 5) -> str:
178
+ """
179
+ Hàm chính để lấy nội dung tin thể thao
180
+
181
+ Args:
182
+ news_type: 'all', 'football', 'basketball', etc.
183
+ language: 'vi' hoặc 'en'
184
+ limit: Số lượng tin tức
185
+
186
+ Returns:
187
+ Chuỗi văn bản đã format cho TTS
188
+ """
189
+ try:
190
+ aggregator = SportsNewsAggregator()
191
+
192
+ if news_type == 'football':
193
+ news_items = aggregator.get_football_news(limit=limit)
194
+ else:
195
+ news_items = aggregator.get_all_sports_news(language=language, limit=limit)
196
+
197
+ return aggregator.format_news_for_tts(news_items, language)
198
+
199
+ except Exception as e:
200
+ logger.error(f"Error getting sports news: {str(e)}")
201
+ if language == 'vi':
202
+ return f"Xin lỗi, có lỗi khi lấy tin thể thao: {str(e)}"
203
+ else:
204
+ return f"Sorry, error getting sports news: {str(e)}"
205
+
206
+ # Test function
207
+ if __name__ == "__main__":
208
+ # Test lấy tin thể thao
209
+ print("Testing sports news aggregator...")
210
+
211
+ content = get_sports_news_content('all', 'vi', 3)
212
+ print("Vietnamese sports news:")
213
+ print(content)
214
+ print("\n" + "="*50 + "\n")
215
+
216
+ content_en = get_sports_news_content('all', 'en', 3)
217
+ print("English sports news:")
218
+ print(content_en)
test_sports_news.py ADDED
@@ -0,0 +1,116 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Test script cho tính năng tin thể thao
4
+ Kiểm tra việc lấy tin từ các nguồn RSS
5
+ """
6
+
7
+ import sys
8
+ import os
9
+ sys.path.append(os.path.dirname(os.path.abspath(__file__)))
10
+
11
+ from sports_news import get_sports_news_content, SportsNewsAggregator
12
+ import asyncio
13
+
14
+ def test_basic_functionality():
15
+ """Test cơ bản cho tính năng tin thể thao"""
16
+ print("🏃‍♂️ Testing Sports News Functionality...")
17
+ print("=" * 50)
18
+
19
+ try:
20
+ # Test lấy tin thể thao tiếng Việt
21
+ print("📰 Testing Vietnamese sports news...")
22
+ vi_content = get_sports_news_content('all', 'vi', 3)
23
+ print("Vietnamese Content:")
24
+ print(vi_content[:500] + "..." if len(vi_content) > 500 else vi_content)
25
+ print("\n" + "-" * 30 + "\n")
26
+
27
+ # Test lấy tin bóng đá
28
+ print("⚽ Testing football news...")
29
+ football_content = get_sports_news_content('football', 'vi', 2)
30
+ print("Football Content:")
31
+ print(football_content[:500] + "..." if len(football_content) > 500 else football_content)
32
+ print("\n" + "-" * 30 + "\n")
33
+
34
+ # Test lấy tin tiếng Anh
35
+ print("🌍 Testing English sports news...")
36
+ en_content = get_sports_news_content('all', 'en', 2)
37
+ print("English Content:")
38
+ print(en_content[:500] + "..." if len(en_content) > 500 else en_content)
39
+ print("\n" + "-" * 30 + "\n")
40
+
41
+ print("✅ All tests completed successfully!")
42
+ return True
43
+
44
+ except Exception as e:
45
+ print(f"❌ Error during testing: {str(e)}")
46
+ return False
47
+
48
+ def test_aggregator_directly():
49
+ """Test trực tiếp SportsNewsAggregator class"""
50
+ print("🔧 Testing SportsNewsAggregator directly...")
51
+ print("=" * 50)
52
+
53
+ try:
54
+ aggregator = SportsNewsAggregator()
55
+
56
+ # Test lấy tin từ một nguồn cụ thể
57
+ print("📡 Testing individual RSS sources...")
58
+
59
+ # Test với VnExpress
60
+ vnexpress_config = {
61
+ 'url': 'https://vnexpress.net/rss/the-thao.rss',
62
+ 'type': 'rss',
63
+ 'language': 'vi',
64
+ 'name': 'vnexpress_sport'
65
+ }
66
+
67
+ news_items = aggregator.get_rss_news(vnexpress_config)
68
+ print(f"VnExpress Sports: Found {len(news_items)} articles")
69
+
70
+ if news_items:
71
+ print("Sample article:")
72
+ print(f"Title: {news_items[0]['title']}")
73
+ print(f"Summary: {news_items[0]['summary'][:200]}...")
74
+
75
+ print("\n" + "-" * 30 + "\n")
76
+
77
+ # Test format cho TTS
78
+ print("🎤 Testing TTS formatting...")
79
+ formatted = aggregator.format_news_for_tts(news_items[:2], 'vi')
80
+ print("Formatted for TTS:")
81
+ print(formatted[:300] + "..." if len(formatted) > 300 else formatted)
82
+
83
+ print("✅ Aggregator test completed successfully!")
84
+ return True
85
+
86
+ except Exception as e:
87
+ print(f"❌ Error during aggregator testing: {str(e)}")
88
+ import traceback
89
+ traceback.print_exc()
90
+ return False
91
+
92
+ def main():
93
+ """Chạy tất cả các test"""
94
+ print("🚀 Starting Sports News Tests...")
95
+ print("=" * 60)
96
+
97
+ # Test cơ bản
98
+ basic_success = test_basic_functionality()
99
+
100
+ print("\n" + "=" * 60 + "\n")
101
+
102
+ # Test aggregator
103
+ aggregator_success = test_aggregator_directly()
104
+
105
+ print("\n" + "=" * 60)
106
+ print("📊 TEST SUMMARY:")
107
+ print(f"Basic functionality: {'✅ PASS' if basic_success else '❌ FAIL'}")
108
+ print(f"Aggregator test: {'✅ PASS' if aggregator_success else '❌ FAIL'}")
109
+
110
+ if basic_success and aggregator_success:
111
+ print("🎉 All tests passed! Sports news feature is ready to use.")
112
+ else:
113
+ print("⚠️ Some tests failed. Please check the errors above.")
114
+
115
+ if __name__ == "__main__":
116
+ main()