mirror of
https://github.com/Death916/deathclock.git
synced 2026-04-10 03:04:40 -07:00
Merge branch 'modules' of https://github.com/death916/deathclock into modules
This commit is contained in:
commit
765a02e8d4
4 changed files with 59 additions and 36 deletions
Binary file not shown.
|
|
@ -7,12 +7,12 @@ from clock_module import ClockModule
|
||||||
|
|
||||||
def create_app():
|
def create_app():
|
||||||
app = Dash(__name__)
|
app = Dash(__name__)
|
||||||
|
|
||||||
app.layout = html.Div([
|
app.layout = html.Div([
|
||||||
html.H1(id='clock-display', style={'textAlign': 'center', 'cursor': 'pointer'}),
|
html.H1(id='clock-display', style={'textAlign': 'center', 'cursor': 'pointer'}),
|
||||||
dcc.Input(id='time-input', type='time', style={'display': 'none', 'margin': '0 auto'}),
|
dcc.Input(id='time-input', type='time', style={'display': 'none', 'margin': '0 auto'}),
|
||||||
html.Div(id='output-selected-time', style={'textAlign': 'center', 'marginBottom': '20px'}),
|
html.Div(id='output-selected-time', style={'textAlign': 'center', 'marginBottom': '20px'}),
|
||||||
|
|
||||||
html.Div([
|
html.Div([
|
||||||
html.Div([
|
html.Div([
|
||||||
html.Div([
|
html.Div([
|
||||||
|
|
@ -22,26 +22,27 @@ def create_app():
|
||||||
html.Div(id='weather-display')
|
html.Div(id='weather-display')
|
||||||
], id='scores-weather-container'),
|
], id='scores-weather-container'),
|
||||||
]),
|
]),
|
||||||
|
|
||||||
html.Div(id='news-ticker'),
|
html.Div(id='news-ticker'),
|
||||||
|
|
||||||
dcc.Interval(id='clock-interval', interval=60000, n_intervals=0),
|
dcc.Interval(id='clock-interval', interval=60000, n_intervals=0),
|
||||||
dcc.Interval(id='weather-interval', interval=150000, n_intervals=0),
|
dcc.Interval(id='weather-interval', interval=150000, n_intervals=0),
|
||||||
dcc.Interval(id='news-interval', interval=300000, n_intervals=0),
|
dcc.Interval(id='news-interval', interval=300000, n_intervals=0),
|
||||||
dcc.Interval(id='nba-interval', interval=300000, n_intervals=0)
|
dcc.Interval(id='nba-interval', interval=300000, n_intervals=0)
|
||||||
])
|
])
|
||||||
|
|
||||||
ClockModule(app)
|
ClockModule(app)
|
||||||
WeatherModule(app)
|
WeatherModule(app)
|
||||||
NewsModule(app)
|
NewsModule(app)
|
||||||
ScoresModule(app)
|
ScoresModule(app)
|
||||||
alarm_module = AlarmModule(app)
|
alarm_module = AlarmModule(app)
|
||||||
|
|
||||||
def check_alarms():
|
def check_alarms():
|
||||||
trigg = alarm_module.alarm_obj.check_alarm()
|
trigg = alarm_module.alarm_obj.check_alarm()
|
||||||
if trigg:
|
if trigg:
|
||||||
print("ALARM TRIGGERED!")
|
print("ALARM TRIGGERED!")
|
||||||
|
|
||||||
check_alarms()
|
check_alarms()
|
||||||
|
|
||||||
return app
|
return app
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
||||||
|
|
@ -13,37 +13,43 @@ class News:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self._news_dict = {}
|
self._news_dict = {}
|
||||||
self._news_dict_length = 0
|
self._news_dict_length = 0
|
||||||
socket.setdefaulttimeout(10) # Set default timeout for socket operations
|
socket.setdefaulttimeout(10) # Set default timeout for socket operations
|
||||||
|
|
||||||
async def _fetch_feed(self, session, feed):
|
async def _fetch_feed(self, session, feed):
|
||||||
"""Fetches and parses a single feed asynchronously."""
|
"""Fetches and parses a single feed asynchronously."""
|
||||||
max_entries = 10 # Maximum number of entries to fetch from each feed
|
max_entries = 10 # Maximum number of entries to fetch from each feed
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with session.get(feed) as response:
|
# Add timeout to the request
|
||||||
|
timeout = aiohttp.ClientTimeout(total=5)
|
||||||
|
async with session.get(feed, timeout=timeout) as response:
|
||||||
if response.status != 200:
|
if response.status != 200:
|
||||||
print(f"Skip feed {feed}: status {response.status}")
|
print(f"Skip feed {feed}: status {response.status}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
text = await response.text()
|
text = await response.text()
|
||||||
d = feedparser.parse(text)
|
d = feedparser.parse(text)
|
||||||
|
|
||||||
if hasattr(d, 'status') and d.status != 200:
|
if hasattr(d, 'status') and d.status != 200:
|
||||||
print(f"Skip feed {feed}: status {d.status}")
|
print(f"Skip feed {feed}: status {d.status}")
|
||||||
return []
|
return []
|
||||||
|
|
||||||
feed_entries = []
|
feed_entries = []
|
||||||
# Limit the number of entries parsed
|
# Limit the number of entries parsed
|
||||||
for i, post in enumerate(d.entries):
|
for i, post in enumerate(d.entries):
|
||||||
if i >= max_entries:
|
if i >= max_entries:
|
||||||
break # Stop parsing if we've reached the limit
|
break # Stop parsing if we've reached the limit
|
||||||
|
|
||||||
feed_entries.append({
|
feed_entries.append({
|
||||||
'title': post.title,
|
'title': post.title,
|
||||||
'source': d.feed.title if hasattr(d.feed, 'title') else 'Unknown',
|
'source': d.feed.title if hasattr(d.feed, 'title') else 'Unknown',
|
||||||
'publish_date': post.published if hasattr(post, 'published') else '',
|
'publish_date': post.published if hasattr(post, 'published') else '',
|
||||||
'summary': post.summary if hasattr(post, 'summary') else ''
|
'summary': post.summary if hasattr(post, 'summary') else ''
|
||||||
})
|
})
|
||||||
|
|
||||||
print(f"Added {len(feed_entries)} entries from {feed}")
|
print(f"Added {len(feed_entries)} entries from {feed}")
|
||||||
return feed_entries
|
return feed_entries
|
||||||
|
|
||||||
except aiohttp.ClientError as e:
|
except aiohttp.ClientError as e:
|
||||||
print(f"Error processing feed {feed}: {e}")
|
print(f"Error processing feed {feed}: {e}")
|
||||||
return []
|
return []
|
||||||
|
|
@ -56,7 +62,7 @@ class News:
|
||||||
feeds = []
|
feeds = []
|
||||||
self._news_dict = {}
|
self._news_dict = {}
|
||||||
self._news_dict_length = 0
|
self._news_dict_length = 0
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with aiofiles.open("feeds.txt", "r") as f:
|
async with aiofiles.open("feeds.txt", "r") as f:
|
||||||
async for line in f:
|
async for line in f:
|
||||||
|
|
@ -64,28 +70,32 @@ class News:
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error reading feeds.txt: {e}")
|
print(f"Error reading feeds.txt: {e}")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
|
# Limit the number of feeds to process at once
|
||||||
|
if len(feeds) > 10:
|
||||||
|
feeds = random.sample(feeds, 10)
|
||||||
|
|
||||||
print("Getting news entries...")
|
print("Getting news entries...")
|
||||||
async with aiohttp.ClientSession() as session:
|
timeout = aiohttp.ClientTimeout(total=15)
|
||||||
|
async with aiohttp.ClientSession(timeout=timeout) as session:
|
||||||
tasks = [self._fetch_feed(session, feed) for feed in feeds]
|
tasks = [self._fetch_feed(session, feed) for feed in feeds]
|
||||||
all_feed_entries_list = await asyncio.gather(*tasks)
|
all_feed_entries_list = await asyncio.gather(*tasks, return_exceptions=True)
|
||||||
|
|
||||||
all_entries = []
|
all_entries = []
|
||||||
for feed_entries in all_feed_entries_list:
|
for result in all_feed_entries_list:
|
||||||
if feed_entries:
|
if isinstance(result, list) and result:
|
||||||
#Now just add the entries, because we are already limited
|
all_entries.extend(result)
|
||||||
all_entries.extend(feed_entries)
|
|
||||||
|
|
||||||
if not all_entries:
|
if not all_entries:
|
||||||
print("No entries collected")
|
print("No entries collected")
|
||||||
return {}
|
return {}
|
||||||
|
|
||||||
if len(all_entries) > 30:
|
if len(all_entries) > 30:
|
||||||
all_entries = random.sample(all_entries, 30)
|
all_entries = random.sample(all_entries, 30)
|
||||||
|
|
||||||
for entry in all_entries:
|
for entry in all_entries:
|
||||||
self._news_dict[entry['title']] = entry
|
self._news_dict[entry['title']] = entry
|
||||||
|
|
||||||
try:
|
try:
|
||||||
async with aiofiles.open("news.txt", "w") as f:
|
async with aiofiles.open("news.txt", "w") as f:
|
||||||
print("Writing news to file...")
|
print("Writing news to file...")
|
||||||
|
|
@ -93,5 +103,5 @@ class News:
|
||||||
await f.write(f"[{entry['publish_date']}] {entry['source']}: {entry['title']}\n")
|
await f.write(f"[{entry['publish_date']}] {entry['source']}: {entry['title']}\n")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"Error writing to news.txt: {e}")
|
print(f"Error writing to news.txt: {e}")
|
||||||
|
|
||||||
return self._news_dict
|
return self._news_dict
|
||||||
|
|
|
||||||
|
|
@ -9,16 +9,16 @@ class NewsModule:
|
||||||
self.app = app
|
self.app = app
|
||||||
self.news_obj = self.get_news_object()
|
self.news_obj = self.get_news_object()
|
||||||
self._last_news_update = datetime.datetime(2000, 1, 1)
|
self._last_news_update = datetime.datetime(2000, 1, 1)
|
||||||
self._cached_news = self.create_loading_message() # Initial loading message
|
self._cached_news = self.create_loading_message() # Initial loading message
|
||||||
self._initial_run = True
|
self._initial_run = True
|
||||||
self.setup_callbacks()
|
self.setup_callbacks()
|
||||||
|
|
||||||
def get_news_object(self):
|
def get_news_object(self):
|
||||||
return News()
|
return News()
|
||||||
|
|
||||||
def create_loading_message(self):
|
def create_loading_message(self):
|
||||||
return html.Div("Loading...")
|
return html.Div("Loading...")
|
||||||
|
|
||||||
def setup_callbacks(self):
|
def setup_callbacks(self):
|
||||||
@self.app.callback(
|
@self.app.callback(
|
||||||
Output('news-ticker', 'children'),
|
Output('news-ticker', 'children'),
|
||||||
|
|
@ -29,13 +29,25 @@ class NewsModule:
|
||||||
return self._cached_news
|
return self._cached_news
|
||||||
|
|
||||||
current_time = datetime.datetime.now()
|
current_time = datetime.datetime.now()
|
||||||
|
time_since_update = (current_time - self._last_news_update).total_seconds()
|
||||||
|
|
||||||
|
# Only update if it's been more than 5 minutes or it's the initial run
|
||||||
|
if time_since_update < 300 and not self._initial_run:
|
||||||
|
return self._cached_news
|
||||||
|
|
||||||
try:
|
try:
|
||||||
print("UPDATING NEWS...")
|
print("UPDATING NEWS...")
|
||||||
# Execute the async function with asyncio.run
|
# Create a new event loop for this request
|
||||||
headlines_dict = asyncio.run(self.news_obj.get_news())
|
loop = asyncio.new_event_loop()
|
||||||
|
asyncio.set_event_loop(loop)
|
||||||
|
headlines_dict = loop.run_until_complete(self.news_obj.get_news())
|
||||||
|
loop.close()
|
||||||
|
|
||||||
combined_items = " | ".join([f"{data['source']}: {headline}"
|
if not headlines_dict:
|
||||||
for headline, data in headlines_dict.items()])
|
return html.Div("No news available at this time.", className="ticker")
|
||||||
|
|
||||||
|
combined_items = " | ".join([f"{data['source']}: {headline}"
|
||||||
|
for headline, data in headlines_dict.items()])
|
||||||
text_px = len(combined_items) * 8
|
text_px = len(combined_items) * 8
|
||||||
scroll_speed = 75
|
scroll_speed = 75
|
||||||
duration = max(text_px / scroll_speed, 20)
|
duration = max(text_px / scroll_speed, 20)
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue