-
-
Notifications
You must be signed in to change notification settings - Fork 331
When the DI webserver replies 403, try the next URL in the list before giving up #2984
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -634,16 +634,49 @@ async def _get_stream_url(self, network_key: str, channel_key: str) -> str: | |
| for i, url in enumerate(playlist): | ||
| self.logger.debug("%s: Available stream URL %d: %s", self.domain, i + 1, url) | ||
|
|
||
| # Use the first URL - Digitally Incorporated typically returns them in priority order | ||
| stream_url: str = str(playlist[0]) | ||
| self.logger.debug("%s: Selected stream URL: %s", self.domain, stream_url) | ||
|
|
||
| # Validate the stream URL | ||
| if not stream_url or not isinstance(stream_url, str): | ||
| msg = f"{self.domain}: Invalid stream URL received: {stream_url}" | ||
| # Try each URL until one responds without 403 (forbidden) | ||
| candidate_urls = [str(url) for url in playlist if isinstance(url, str) and str(url).strip()] | ||
| if not candidate_urls: | ||
| msg = f"{self.domain}: No valid stream URLs received from Digitally Incorporated API" | ||
| raise MediaNotFoundError(msg) | ||
|
|
||
| return stream_url | ||
| total_candidates = len(candidate_urls) | ||
| for idx, stream_url in enumerate(candidate_urls, start=1): | ||
| try: | ||
| timeout = aiohttp.ClientTimeout(total=10) | ||
| async with self.mass.http_session.head( | ||
| stream_url, allow_redirects=True, timeout=timeout | ||
| ) as resp: | ||
| if resp.status == 403: | ||
| self.logger.warning( | ||
| "%s: Stream URL %s returned 403 (candidate %d/%d), trying next", | ||
| self.domain, | ||
| stream_url, | ||
| idx, | ||
| total_candidates, | ||
| ) | ||
| continue | ||
| resp.raise_for_status() | ||
| self.logger.debug( | ||
| "%s: Selected stream URL %d/%d: %s", | ||
| self.domain, | ||
| idx, | ||
| total_candidates, | ||
| stream_url, | ||
| ) | ||
| return stream_url | ||
|
|
||
| except aiohttp.ClientError as err: | ||
| self.logger.debug( | ||
| "%s: Stream URL %s check failed (%s), trying next", | ||
| self.domain, | ||
| stream_url, | ||
| err, | ||
| ) | ||
| continue | ||
|
|
||
| msg = f"{self.domain}: Unable to get working stream URL after {total_candidates} attempts" | ||
| raise MediaNotFoundError(msg) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this whole thing seems a little over the top to me, there's a lot of extra stuff happening, conversions to string after determining we already have a string, and looping over the playlist multiple times. I have nicholas-gh#1 as an alternative that should work and be simpler.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @OzGav if you prefer I can submit that as a PR directly here, just thought it would be courteous to @nicholas-gh to do it this way. |
||
|
|
||
| except (ProviderUnavailableError, MediaNotFoundError): | ||
| # Re-raise provider/media errors as-is (they already have domain prefix) | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this section says it's trying each URL to see if it returns a 403, but that's happening later. this just checks if it's a valid string, converts the string to a string, checks if it's not an empty string after being stripped, then returns the unstripped url converted to a tring again, for each candidate. this seems like a rather confusing way to just check the type of each candidate and make sure it has content.