Chat monitors stream to end
This commit is contained in:
parent
b50a4bad02
commit
38d51636af
4 changed files with 89 additions and 1 deletions
|
|
@ -365,6 +365,7 @@ class ContentDownloader:
|
||||||
max_messages: Optional[int] = None,
|
max_messages: Optional[int] = None,
|
||||||
timeout: Optional[float] = None,
|
timeout: Optional[float] = None,
|
||||||
shutdown_check: Optional[callable] = None,
|
shutdown_check: Optional[callable] = None,
|
||||||
|
stream_monitor = None,
|
||||||
verbose: bool = False) -> bool:
|
verbose: bool = False) -> bool:
|
||||||
"""
|
"""
|
||||||
Download live chat using chat_downloader library as fallback.
|
Download live chat using chat_downloader library as fallback.
|
||||||
|
|
@ -376,6 +377,7 @@ class ContentDownloader:
|
||||||
max_messages: Maximum messages to download (None = unlimited)
|
max_messages: Maximum messages to download (None = unlimited)
|
||||||
timeout: Stop after this many seconds (None = until stream ends)
|
timeout: Stop after this many seconds (None = until stream ends)
|
||||||
shutdown_check: Optional callback function that returns True when shutdown requested
|
shutdown_check: Optional callback function that returns True when shutdown requested
|
||||||
|
stream_monitor: Optional stream monitor to check if stream is still live
|
||||||
verbose: Show chat message previews
|
verbose: Show chat message previews
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -414,7 +416,10 @@ class ContentDownloader:
|
||||||
# The get_chat with output parameter writes to file automatically
|
# The get_chat with output parameter writes to file automatically
|
||||||
# We just need to iterate to trigger the download
|
# We just need to iterate to trigger the download
|
||||||
message_count = 0
|
message_count = 0
|
||||||
print(f'{Fore.CYAN}Receiving chat messages (press Ctrl+C to stop)...{Style.RESET_ALL}')
|
last_check_time = time.time()
|
||||||
|
check_interval = 10.0 # Check if stream is still live every 10 seconds
|
||||||
|
|
||||||
|
print(f'{Fore.CYAN}Receiving chat messages (will stop when stream ends)...{Style.RESET_ALL}')
|
||||||
try:
|
try:
|
||||||
for message in chat:
|
for message in chat:
|
||||||
# Check for shutdown request
|
# Check for shutdown request
|
||||||
|
|
@ -422,6 +427,19 @@ class ContentDownloader:
|
||||||
print(f'\n{Fore.YELLOW}⚠ Chat download stopped by shutdown request{Style.RESET_ALL}')
|
print(f'\n{Fore.YELLOW}⚠ Chat download stopped by shutdown request{Style.RESET_ALL}')
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Periodically check if stream is still live
|
||||||
|
current_time = time.time()
|
||||||
|
if stream_monitor and (current_time - last_check_time) >= check_interval:
|
||||||
|
last_check_time = current_time
|
||||||
|
try:
|
||||||
|
is_live = stream_monitor.is_user_live()
|
||||||
|
if not is_live:
|
||||||
|
print(f'\n{Fore.YELLOW}⚠ Stream ended, stopping chat download{Style.RESET_ALL}')
|
||||||
|
break
|
||||||
|
except Exception as check_error:
|
||||||
|
print(f'\n{Fore.YELLOW}⚠ Could not check stream status: {check_error}{Style.RESET_ALL}')
|
||||||
|
# Continue downloading to avoid false positives from API errors
|
||||||
|
|
||||||
message_count += 1
|
message_count += 1
|
||||||
|
|
||||||
# Show progress every 100 messages
|
# Show progress every 100 messages
|
||||||
|
|
@ -467,6 +485,7 @@ class ContentDownloader:
|
||||||
|
|
||||||
def start_chat_downloader_thread(self, username: str, json_path: str,
|
def start_chat_downloader_thread(self, username: str, json_path: str,
|
||||||
shutdown_check: Optional[callable] = None,
|
shutdown_check: Optional[callable] = None,
|
||||||
|
stream_monitor = None,
|
||||||
verbose: bool = False) -> threading.Thread:
|
verbose: bool = False) -> threading.Thread:
|
||||||
"""
|
"""
|
||||||
Start chat_downloader in a background thread.
|
Start chat_downloader in a background thread.
|
||||||
|
|
@ -475,6 +494,7 @@ class ContentDownloader:
|
||||||
username: Twitch username
|
username: Twitch username
|
||||||
json_path: Path to save chat JSON
|
json_path: Path to save chat JSON
|
||||||
shutdown_check: Callback to check for shutdown
|
shutdown_check: Callback to check for shutdown
|
||||||
|
stream_monitor: Optional stream monitor to check if stream is still live
|
||||||
verbose: Show chat previews
|
verbose: Show chat previews
|
||||||
|
|
||||||
Returns:
|
Returns:
|
||||||
|
|
@ -485,6 +505,7 @@ class ContentDownloader:
|
||||||
self.chat_thread_success = self.download_live_chat_with_chat_downloader(
|
self.chat_thread_success = self.download_live_chat_with_chat_downloader(
|
||||||
username, json_path,
|
username, json_path,
|
||||||
shutdown_check=shutdown_check,
|
shutdown_check=shutdown_check,
|
||||||
|
stream_monitor=stream_monitor,
|
||||||
verbose=verbose
|
verbose=verbose
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
|
||||||
|
|
@ -115,6 +115,34 @@ class StreamMonitor:
|
||||||
print(f'{Fore.YELLOW} {str(e)}{Style.RESET_ALL}')
|
print(f'{Fore.YELLOW} {str(e)}{Style.RESET_ALL}')
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
|
def is_user_live(self) -> bool:
|
||||||
|
"""
|
||||||
|
Check if the configured user is currently live.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
bool: True if user is live, False if offline
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: If API request fails (caller should handle)
|
||||||
|
"""
|
||||||
|
query = f'query{{user(login: "{self.username}") {{stream{{id title}}}}}}'
|
||||||
|
|
||||||
|
try:
|
||||||
|
response = requests.post(
|
||||||
|
TWITCH_GQL_URL,
|
||||||
|
json={'query': query},
|
||||||
|
headers={"Client-ID": TWITCH_GQL_CLIENT_ID},
|
||||||
|
timeout=15
|
||||||
|
)
|
||||||
|
response.raise_for_status()
|
||||||
|
data = response.json()
|
||||||
|
stream_data = data.get('data', {}).get('user', {}).get('stream')
|
||||||
|
return stream_data is not None
|
||||||
|
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
# Don't exit, let caller handle this
|
||||||
|
raise Exception(f"Failed to check if user is live: {str(e)}")
|
||||||
|
|
||||||
def get_latest_vod(self) -> Optional[Dict[str, Any]]:
|
def get_latest_vod(self) -> Optional[Dict[str, Any]]:
|
||||||
"""
|
"""
|
||||||
Get the most recent VOD for the configured user.
|
Get the most recent VOD for the configured user.
|
||||||
|
|
|
||||||
37
run_tests.ps1
Normal file
37
run_tests.ps1
Normal file
|
|
@ -0,0 +1,37 @@
|
||||||
|
# PowerShell script to run Twitch Archive unit tests
|
||||||
|
# Run this script to execute all unit tests
|
||||||
|
|
||||||
|
Write-Host "======================================================================" -ForegroundColor Cyan
|
||||||
|
Write-Host "TWITCH ARCHIVE - Running Unit Tests" -ForegroundColor Cyan
|
||||||
|
Write-Host "======================================================================" -ForegroundColor Cyan
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Check if virtual environment exists and activate it
|
||||||
|
$venvPath = ".\venv314\Scripts\Activate.ps1"
|
||||||
|
if (Test-Path $venvPath) {
|
||||||
|
Write-Host "✓ Activating virtual environment..." -ForegroundColor Green
|
||||||
|
& $venvPath
|
||||||
|
} else {
|
||||||
|
Write-Host "⚠ Virtual environment not found at $venvPath" -ForegroundColor Yellow
|
||||||
|
Write-Host " Continuing with system Python..." -ForegroundColor Yellow
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host ""
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
Write-Host "Running unit tests..." -ForegroundColor Cyan
|
||||||
|
python test_twitch_archive_simple.py
|
||||||
|
|
||||||
|
# Check exit code
|
||||||
|
if ($LASTEXITCODE -eq 0) {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "======================================================================" -ForegroundColor Green
|
||||||
|
Write-Host "✓ ALL TESTS PASSED" -ForegroundColor Green
|
||||||
|
Write-Host "======================================================================" -ForegroundColor Green
|
||||||
|
} else {
|
||||||
|
Write-Host ""
|
||||||
|
Write-Host "======================================================================" -ForegroundColor Red
|
||||||
|
Write-Host "✗ SOME TESTS FAILED" -ForegroundColor Red
|
||||||
|
Write-Host "======================================================================" -ForegroundColor Red
|
||||||
|
exit $LASTEXITCODE
|
||||||
|
}
|
||||||
|
|
@ -1088,6 +1088,7 @@ class TwitchArchiveManager:
|
||||||
archiver.downloader.start_chat_downloader_thread(
|
archiver.downloader.start_chat_downloader_thread(
|
||||||
archiver.username, chat_json_path,
|
archiver.username, chat_json_path,
|
||||||
shutdown_check=lambda: self.shutdown_requested or archiver.shutdown_requested,
|
shutdown_check=lambda: self.shutdown_requested or archiver.shutdown_requested,
|
||||||
|
stream_monitor=archiver.stream_monitor,
|
||||||
verbose=self.verbose
|
verbose=self.verbose
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
|
@ -1116,6 +1117,7 @@ class TwitchArchiveManager:
|
||||||
archiver.downloader.start_chat_downloader_thread(
|
archiver.downloader.start_chat_downloader_thread(
|
||||||
archiver.username, chat_json_path,
|
archiver.username, chat_json_path,
|
||||||
shutdown_check=lambda: self.shutdown_requested or archiver.shutdown_requested,
|
shutdown_check=lambda: self.shutdown_requested or archiver.shutdown_requested,
|
||||||
|
stream_monitor=archiver.stream_monitor,
|
||||||
verbose=self.verbose or self.chat_only
|
verbose=self.verbose or self.chat_only
|
||||||
)
|
)
|
||||||
# Wait for completion
|
# Wait for completion
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue