Proxy endpoint for SD program poster images. Nginx caches the response.
(self, request, pk=None)
| 501 | |
| 502 | @action(detail=True, methods=['get'], url_path='poster', permission_classes=[AllowAny]) |
| 503 | def poster(self, request, pk=None): |
| 504 | """Proxy endpoint for SD program poster images. Nginx caches the response.""" |
| 505 | import requests as http_requests |
| 506 | from apps.epg.tasks import SD_BASE_URL |
| 507 | from core.utils import dispatcharr_http_headers |
| 508 | |
| 509 | program = self.get_object() |
| 510 | poster_sd_url = (program.custom_properties or {}).get('sd_icon') |
| 511 | if not poster_sd_url: |
| 512 | return Response(status=status.HTTP_404_NOT_FOUND) |
| 513 | |
| 514 | source = program.epg.epg_source if program.epg else None |
| 515 | if not source or source.source_type != 'schedules_direct': |
| 516 | return Response(status=status.HTTP_404_NOT_FOUND) |
| 517 | |
| 518 | error_cache = ProgramViewSet._sd_poster_error_cache.get(source.id) |
| 519 | if error_cache and time.time() < error_cache['until']: |
| 520 | return Response( |
| 521 | {'error': f"SD temporarily unavailable: {error_cache['reason']}"}, |
| 522 | status=status.HTTP_503_SERVICE_UNAVAILABLE, |
| 523 | ) |
| 524 | |
| 525 | cached = ProgramViewSet._sd_poster_token_cache.get(source.id) |
| 526 | token = cached['token'] if cached and time.time() < cached['expires'] else None |
| 527 | |
| 528 | if not token: |
| 529 | sha1_password = hashlib.sha1(source.password.encode('utf-8')).hexdigest() |
| 530 | try: |
| 531 | auth_resp = http_requests.post( |
| 532 | f"{SD_BASE_URL}/token", |
| 533 | json={'username': source.username, 'password': sha1_password}, |
| 534 | headers=dispatcharr_http_headers(), |
| 535 | timeout=10, |
| 536 | ) |
| 537 | auth_data = auth_resp.json() |
| 538 | token = auth_data.get('token') |
| 539 | if not token: |
| 540 | ProgramViewSet._sd_poster_error_cache[source.id] = { |
| 541 | 'until': time.time() + 3600, |
| 542 | 'reason': auth_data.get('message', 'Authentication failed'), |
| 543 | } |
| 544 | return Response(status=status.HTTP_502_BAD_GATEWAY) |
| 545 | token_expires = auth_data.get('tokenExpires', time.time() + 86400) |
| 546 | ProgramViewSet._sd_poster_token_cache[source.id] = { |
| 547 | 'token': token, |
| 548 | 'expires': token_expires, |
| 549 | } |
| 550 | except http_requests.exceptions.RequestException: |
| 551 | ProgramViewSet._sd_poster_error_cache[source.id] = { |
| 552 | 'until': time.time() + 300, |
| 553 | 'reason': 'Network error reaching Schedules Direct', |
| 554 | } |
| 555 | return Response(status=status.HTTP_502_BAD_GATEWAY) |
| 556 | |
| 557 | try: |
| 558 | img_resp = http_requests.get( |
| 559 | poster_sd_url, |
| 560 | headers=dispatcharr_http_headers(token=token, content_type=None), |
nothing calls this directly
no test coverage detected