Fixed unreachable streams reporting stale online status indefinitely.

This commit is contained in:
2026-01-07 11:33:55 -05:00
parent dc0df47257
commit f62764a2b2
3 changed files with 30 additions and 7 deletions

View File

@@ -11,6 +11,7 @@ from mautrix.types import TextMessageEventContent, MessageType
from .owncast_client import OwncastClient
from .database import StreamRepository, SubscriptionRepository
from .models import StreamStatus
from .utils import domainify, sanitize_for_markdown
@@ -209,18 +210,21 @@ class CommandHandler:
body_text += f"- **{safe_stream_name}** \n"
# Add title if stream is online (as a sub-bullet)
if stream_state.online and stream_state.title:
if stream_state.status == StreamStatus.ONLINE and stream_state.title:
safe_title = sanitize_for_markdown(stream_state.title)
body_text += f" - Title: {safe_title} \n"
# Determine status and duration (as a sub-bullet)
if stream_state.online:
if stream_state.status == StreamStatus.ONLINE:
# Stream is online - use last_connect_time
if stream_state.last_connect_time:
duration = self._format_duration(stream_state.last_connect_time)
body_text += f" - Status: Online for {duration} \n"
else:
body_text += f" - Status: Online \n"
elif stream_state.status == StreamStatus.UNKNOWN:
# Stream status is unknown - instance unreachable
body_text += f" - Status: Unknown (instance unreachable) \n"
else:
# Stream is offline - use last_disconnect_time
if stream_state.last_disconnect_time:
@@ -255,11 +259,11 @@ class CommandHandler:
await evt.reply("This room is not subscribed to any Owncast instances.\n\nTo subscribe to an Owncast instance, use `!subscribe <domain>`", markdown=True)
return
# Filter for only live streams
# Filter for only live streams (exclude unknown status)
live_streams = []
for domain in subscribed_domains:
stream_state = await self.stream_repo.get_by_domain(domain)
if stream_state and stream_state.online:
if stream_state and stream_state.status == StreamStatus.ONLINE:
live_streams.append((domain, stream_state))
# Check if there are no live streams

View File

@@ -5,9 +5,18 @@
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.
from dataclasses import dataclass
from enum import Enum
from typing import Optional, List
class StreamStatus(Enum):
"""Represents the status of a stream."""
ONLINE = "online"
OFFLINE = "offline"
UNKNOWN = "unknown"
@dataclass
class StreamState:
"""Represents the state of an Owncast stream."""
@@ -20,9 +29,16 @@ class StreamState:
failure_counter: int = 0
@property
def online(self) -> bool:
"""Returns True if the stream is currently online."""
return self.last_connect_time is not None
def status(self) -> StreamStatus:
"""Returns the stream status considering failure_counter."""
from .utils import UNKNOWN_STATUS_THRESHOLD
if self.failure_counter > UNKNOWN_STATUS_THRESHOLD:
return StreamStatus.UNKNOWN
elif self.last_connect_time is not None:
return StreamStatus.ONLINE
else:
return StreamStatus.OFFLINE
@classmethod
def from_api_response(cls, response: dict, domain: str) -> "StreamState":

View File

@@ -33,6 +33,9 @@ TEMPORARY_OFFLINE_NOTIFICATION_COOLDOWN = 7 * 60 # 7 minutes in seconds
CLEANUP_WARNING_THRESHOLD = 83 * 24 * 60 # 119,520 cycles = 83 days
CLEANUP_DELETE_THRESHOLD = 90 * 24 * 60 # 129,600 cycles = 90 days
# Failure counter threshold for treating stream status as "unknown"
UNKNOWN_STATUS_THRESHOLD = 15
# Maximum field lengths based on Owncast's configuration
# Source: https://github.com/owncast/owncast/blob/master/web/utils/config-constants.tsx
MAX_INSTANCE_TITLE_LENGTH = 255 # Server Name (line 81)