summaryrefslogtreecommitdiff
path: root/Biz
diff options
context:
space:
mode:
Diffstat (limited to 'Biz')
-rw-r--r--Biz/PodcastItLater/Core.py28
-rw-r--r--Biz/PodcastItLater/Episode.py33
-rw-r--r--Biz/PodcastItLater/Web.py48
3 files changed, 109 insertions, 0 deletions
diff --git a/Biz/PodcastItLater/Core.py b/Biz/PodcastItLater/Core.py
index 7b48ac2..c625e11 100644
--- a/Biz/PodcastItLater/Core.py
+++ b/Biz/PodcastItLater/Core.py
@@ -1028,6 +1028,34 @@ class Database: # noqa: PLR0904
return cursor.fetchone() is not None
@staticmethod
+ def track_episode_metric(
+ episode_id: int,
+ event_type: str,
+ user_id: int | None = None,
+ ) -> None:
+ """Track an episode metric event.
+
+ Args:
+ episode_id: ID of the episode
+ event_type: Type of event ('added', 'played', 'downloaded')
+ user_id: Optional user ID (None for anonymous events)
+ """
+ with Database.get_connection() as conn:
+ cursor = conn.cursor()
+ cursor.execute(
+ "INSERT INTO episode_metrics (episode_id, user_id, event_type) "
+ "VALUES (?, ?, ?)",
+ (episode_id, user_id, event_type),
+ )
+ conn.commit()
+ logger.info(
+ "Tracked %s event for episode %d (user: %s)",
+ event_type,
+ episode_id,
+ user_id or "anonymous",
+ )
+
+ @staticmethod
def get_user_episodes(user_id: int) -> list[dict[str, Any]]:
"""Get all episodes in a user's feed."""
with Database.get_connection() as conn:
diff --git a/Biz/PodcastItLater/Episode.py b/Biz/PodcastItLater/Episode.py
index a06b8d9..a516f65 100644
--- a/Biz/PodcastItLater/Episode.py
+++ b/Biz/PodcastItLater/Episode.py
@@ -196,6 +196,7 @@ class EpisodeDetailPageAttrs(Attrs):
creator_email: str | None
user: dict[str, typing.Any] | None
base_url: str
+ user_has_episode: bool
class EpisodeDetailPage(Component[AnyChildren, EpisodeDetailPageAttrs]):
@@ -208,6 +209,7 @@ class EpisodeDetailPage(Component[AnyChildren, EpisodeDetailPageAttrs]):
creator_email = self.attrs.get("creator_email")
user = self.attrs.get("user")
base_url = self.attrs["base_url"]
+ user_has_episode = self.attrs.get("user_has_episode", False)
share_url = f"{base_url}/episode/{episode_sqid}"
duration_str = UI.format_duration(episode.get("duration"))
@@ -305,6 +307,37 @@ class EpisodeDetailPage(Component[AnyChildren, EpisodeDetailPageAttrs]):
),
# Share button
ShareButton(share_url=share_url),
+ # Add to feed button (logged-in users without episode)
+ html.div(
+ html.div(
+ html.div(
+ html.h5(
+ html.i(classes=["bi", "bi-plus-circle", "me-2"]),
+ "Add to Your Feed",
+ classes=["card-title", "mb-3"],
+ ),
+ html.p(
+ "Save this episode to your personal feed "
+ "to listen later.",
+ classes=["text-muted", "mb-3"],
+ ),
+ html.button(
+ html.i(classes=["bi", "bi-plus-lg", "me-1"]),
+ "Add to My Feed",
+ hx_post=f"/episode/{episode['id']}/add-to-feed",
+ hx_target="#add-to-feed-result",
+ hx_swap="innerHTML",
+ classes=["btn", "btn-primary"],
+ ),
+ html.div(id="add-to-feed-result", classes=["mt-2"]),
+ classes=["card-body"],
+ ),
+ classes=["card"],
+ ),
+ classes=["mb-4"],
+ )
+ if user and not user_has_episode
+ else html.div(),
# Back to home link
html.div(
html.a(
diff --git a/Biz/PodcastItLater/Web.py b/Biz/PodcastItLater/Web.py
index 97ec439..7c85e0b 100644
--- a/Biz/PodcastItLater/Web.py
+++ b/Biz/PodcastItLater/Web.py
@@ -1454,8 +1454,13 @@ def episode_detail(
# Check if current user is logged in
user_id = request.session.get("user_id")
user = None
+ user_has_episode = False
if user_id:
user = Core.Database.get_user_by_id(user_id)
+ user_has_episode = Core.Database.user_has_episode(
+ user_id,
+ episode_id,
+ )
return Episode.EpisodeDetailPage(
episode=episode,
@@ -1463,6 +1468,7 @@ def episode_detail(
creator_email=creator_email,
user=user,
base_url=BASE_URL,
+ user_has_episode=user_has_episode,
)
except (ValueError, KeyError) as e:
@@ -1611,6 +1617,48 @@ app.post("/admin/episode/{episode_id}/toggle-public")(
)
+@app.post("/episode/{episode_id}/add-to-feed")
+def add_episode_to_feed(request: Request, episode_id: int) -> Response:
+ """Add an episode to the user's feed."""
+ # Check if user is logged in
+ user_id = request.session.get("user_id")
+ if not user_id:
+ return Response(
+ '<div class="alert alert-warning">Please login first</div>',
+ status_code=200,
+ )
+
+ # Check if episode exists
+ episode = Core.Database.get_episode_by_id(episode_id)
+ if not episode:
+ return Response(
+ '<div class="alert alert-danger">Episode not found</div>',
+ status_code=404,
+ )
+
+ # Check if user already has this episode
+ if Core.Database.user_has_episode(user_id, episode_id):
+ return Response(
+ '<div class="alert alert-info">Already in your feed</div>',
+ status_code=200,
+ )
+
+ # Add episode to user's feed
+ Core.Database.add_episode_to_user(user_id, episode_id)
+
+ # Track the "added" event
+ Core.Database.track_episode_metric(episode_id, "added", user_id)
+
+ return Response(
+ '<div class="alert alert-success">'
+ '<i class="bi bi-check-circle me-2"></i>'
+ "Added to your feed! "
+ '<a href="/" class="alert-link">View your feed</a>'
+ "</div>",
+ status_code=200,
+ )
+
+
class BaseWebTest(Test.TestCase):
"""Base class for web tests with database setup."""