From 4527504c800085e293b28132462eeaab6efc9898 Mon Sep 17 00:00:00 2001 From: Cody Cook Date: Mon, 16 Jun 2025 23:07:36 -0700 Subject: [PATCH] Add podgrab featureset --- app/models/podcast.py | 6 +++++- app/services/podcast_search.py | 26 +++++++++++++++++--------- app/services/podcast_updater.py | 14 +++++++++++++- app/web/routes/podcasts.py | 6 ++++++ templates/podcasts/view.html | 30 ++++++++++++++++++++++++++++++ 5 files changed, 71 insertions(+), 11 deletions(-) diff --git a/app/models/podcast.py b/app/models/podcast.py index 33969d9..82f844b 100644 --- a/app/models/podcast.py +++ b/app/models/podcast.py @@ -110,6 +110,8 @@ class Episode(db.Model): downloaded = db.Column(db.Boolean, default=False) file_path = db.Column(db.String(512)) explicit = db.Column(db.Boolean, nullable=True) # Whether the episode is marked as explicit + download_error = db.Column(db.String(255), nullable=True) # Error message if download failed + status_code = db.Column(db.Integer, nullable=True) # HTTP status code from last download attempt def __repr__(self): return f'' @@ -133,5 +135,7 @@ class Episode(db.Model): 'guid': self.guid, 'downloaded': self.downloaded, 'file_path': self.file_path, - 'explicit': self.explicit + 'explicit': self.explicit, + 'download_error': self.download_error, + 'status_code': self.status_code } diff --git a/app/services/podcast_search.py b/app/services/podcast_search.py index a7a9df4..0223a4d 100644 --- a/app/services/podcast_search.py +++ b/app/services/podcast_search.py @@ -351,21 +351,29 @@ def get_podcast_episodes(feed_url): # Check if the URL is accessible if head_response.status_code >= 400: logger.warning(f"Audio URL returned status code {head_response.status_code}: {episode['audio_url']}") - continue + # Instead of skipping, add the episode with error information + episode['download_error'] = f"Server returned status code {head_response.status_code}" + episode['status_code'] = head_response.status_code + else: + # Check if the content type is audio + content_type = head_response.headers.get('Content-Type', '') + if not content_type.startswith('audio/') and 'application/octet-stream' not in content_type: + logger.warning(f"Audio URL has non-audio content type: {content_type}") + # Don't skip here as some servers might not report the correct content type + episode['download_error'] = f"Non-audio content type: {content_type}" + else: + # If we got here, the audio URL is valid with no issues + episode['download_error'] = None + episode['status_code'] = head_response.status_code - # Check if the content type is audio - content_type = head_response.headers.get('Content-Type', '') - if not content_type.startswith('audio/') and 'application/octet-stream' not in content_type: - logger.warning(f"Audio URL has non-audio content type: {content_type}") - # Don't skip here as some servers might not report the correct content type - - # If we got here, the audio URL is probably valid + # Add the episode regardless of status code episodes.append(episode) - logger.debug(f"Added episode with valid audio URL: {episode['title']}") + logger.debug(f"Added episode: {episode['title']} (Status: {episode.get('status_code')})") except Exception as e: # If we can't validate the URL, still add the episode but log a warning logger.warning(f"Could not validate audio URL: {str(e)}") + episode['download_error'] = f"Could not validate URL: {str(e)}" episodes.append(episode) logger.debug(f"Added episode with unvalidated audio URL: {episode['title']}") else: diff --git a/app/services/podcast_updater.py b/app/services/podcast_updater.py index 573d60a..b6e6bbb 100644 --- a/app/services/podcast_updater.py +++ b/app/services/podcast_updater.py @@ -79,6 +79,16 @@ def update_podcast(podcast_id, progress_callback=None): if not episodes: logger.warning(f"No episodes found for podcast: {podcast.title}") stats['feed_status'] = 'no_episodes' + else: + # Check if all episodes have download errors + error_episodes = [ep for ep in episodes if ep.get('download_error')] + if len(error_episodes) == len(episodes): + logger.warning(f"All {len(episodes)} episodes have download errors for podcast: {podcast.title}") + stats['feed_status'] = 'all_episodes_have_errors' + # Store the most common error for reporting + if error_episodes: + stats['error_message'] = error_episodes[0].get('download_error', 'Unknown error') + stats['status_code'] = error_episodes[0].get('status_code') # Check if we need to refresh the feed URL from iTunes if podcast.external_id: @@ -132,7 +142,9 @@ def update_podcast(podcast_id, progress_callback=None): episode_number=episode_data.get('episode_number'), guid=episode_data['guid'], downloaded=False, - explicit=episode_data.get('explicit') # Explicit flag + explicit=episode_data.get('explicit'), # Explicit flag + download_error=episode_data.get('download_error'), # Error message if download failed + status_code=episode_data.get('status_code') # HTTP status code ) db.session.add(episode) diff --git a/app/web/routes/podcasts.py b/app/web/routes/podcasts.py index 19eba47..b50b47e 100644 --- a/app/web/routes/podcasts.py +++ b/app/web/routes/podcasts.py @@ -90,6 +90,12 @@ def add(podcast_id): if stats and stats.get('new_episodes', 0) > 0: flash(f'Podcast added successfully! Found {stats["new_episodes"]} episodes.', 'success') + elif stats and stats.get('feed_status') == 'all_episodes_have_errors': + error_msg = stats.get('error_message', 'Unknown error') + status_code = stats.get('status_code', '') + status_info = f" (Status code: {status_code})" if status_code else "" + flash(f'Podcast added successfully! Found episodes but all have download issues: {error_msg}{status_info}. You can try updating later.', 'warning') + logger.warning(f"All episodes have download errors for podcast: {podcast.title}") else: flash('Podcast added successfully! No episodes found yet. The feed might be empty or inaccessible.', 'info') logger.warning(f"No episodes found for podcast: {podcast.title}") diff --git a/templates/podcasts/view.html b/templates/podcasts/view.html index 523dd29..80eb10c 100644 --- a/templates/podcasts/view.html +++ b/templates/podcasts/view.html @@ -188,6 +188,8 @@ {% if episode.downloaded %} Downloaded + {% elif episode.download_error %} + Error {{ episode.status_code }} {% else %} Available {% endif %} @@ -203,6 +205,9 @@ {% if episode.audio_url %} Stream {% endif %} + {% if episode.download_error %} + + {% endif %} {% endfor %} @@ -259,6 +264,8 @@ {% if episode.downloaded %} Downloaded + {% elif episode.download_error %} + Error {{ episode.status_code }} {% else %} Available {% endif %} @@ -274,6 +281,9 @@ {% if episode.audio_url %} Stream {% endif %} + {% if episode.download_error %} + + {% endif %} {% endfor %} @@ -293,6 +303,26 @@ {% endblock %} {% block scripts %} +