diff --git a/surfsense_backend/alembic/versions/32_add_podcast_staleness_detection.py b/surfsense_backend/alembic/versions/32_add_podcast_staleness_detection.py index 79eab4097..d63e6cba2 100644 --- a/surfsense_backend/alembic/versions/32_add_podcast_staleness_detection.py +++ b/surfsense_backend/alembic/versions/32_add_podcast_staleness_detection.py @@ -1,4 +1,6 @@ -"""Add podcast staleness detection columns +"""Add podcast staleness detection columns to chats and podcasts tables + +This feature allows the system to detect when a podcast is outdated compared to the current state of the chat it was generated from, enabling users to regenerate podcasts when needed. Revision ID: 32 Revises: 31 @@ -18,7 +20,7 @@ depends_on: str | Sequence[str] | None = None def upgrade() -> None: - """Add state_version to chats table and chat_state_version to podcasts table.""" + """Add state_version, chat_state_version, and chat_id to chats and podcasts tables.""" # Add state_version column to chats table with default value of 1 op.add_column( @@ -31,12 +33,18 @@ def upgrade() -> None: "podcasts", sa.Column("chat_state_version", sa.BigInteger(), nullable=True) ) + # Add chat_id column to podcasts table (nullable, set when podcast is generated from a chat) + op.add_column("podcasts", sa.Column("chat_id", sa.Integer(), nullable=True)) + def downgrade() -> None: - """Remove state_version and chat_state_version columns.""" + """Remove state_version, chat_state_version, and chat_id columns.""" # Remove chat_state_version from podcasts table op.drop_column("podcasts", "chat_state_version") + # Remove chat_id from podcasts table + op.drop_column("podcasts", "chat_id") + # Remove state_version from chats table op.drop_column("chats", "state_version") diff --git a/surfsense_backend/app/db.py b/surfsense_backend/app/db.py index d17a0b797..48154a417 100644 --- a/surfsense_backend/app/db.py +++ b/surfsense_backend/app/db.py @@ -205,6 +205,9 @@ class Podcast(BaseModel, TimestampMixin): title = Column(String, nullable=False, index=True) podcast_transcript = Column(JSON, nullable=False, default={}) file_location = Column(String(500), nullable=False, default="") + chat_id = Column( + Integer, ForeignKey("chats.id", ondelete="CASCADE"), nullable=True + ) # If generated from a chat, this will be the chat id, else null ( can be from a document or a chat ) chat_state_version = Column(BigInteger, nullable=True) search_space_id = Column( diff --git a/surfsense_backend/app/routes/podcasts_routes.py b/surfsense_backend/app/routes/podcasts_routes.py index e37bdd190..1772917eb 100644 --- a/surfsense_backend/app/routes/podcasts_routes.py +++ b/surfsense_backend/app/routes/podcasts_routes.py @@ -287,3 +287,33 @@ async def stream_podcast( raise HTTPException( status_code=500, detail=f"Error streaming podcast: {e!s}" ) from e + + +@router.get("/podcasts/by-chat/{chat_id}", response_model=PodcastRead) +async def get_podcast_by_chat_id( + chat_id: int, + session: AsyncSession = Depends(get_async_session), + user: User = Depends(current_active_user), +): + try: + # Get the podcast and check if user has access + result = await session.execute( + select(Podcast) + .join(SearchSpace) + .filter(Podcast.chat_id == chat_id, SearchSpace.user_id == user.id) + ) + podcast = result.scalars().first() + + if not podcast: + raise HTTPException( + status_code=404, + detail="Podcast not found or you don't have permission to access it", + ) + + return podcast + except HTTPException as he: + raise he + except Exception as e: + raise HTTPException( + status_code=500, detail=f"Error fetching podcast: {e!s}" + ) from e diff --git a/surfsense_backend/app/tasks/podcast_tasks.py b/surfsense_backend/app/tasks/podcast_tasks.py index 3548eb043..e333b4d1b 100644 --- a/surfsense_backend/app/tasks/podcast_tasks.py +++ b/surfsense_backend/app/tasks/podcast_tasks.py @@ -145,6 +145,7 @@ async def generate_chat_podcast( file_location=result["final_podcast_file_path"], search_space_id=search_space_id, chat_state_version=chat.state_version, + chat_id=chat.id, ) # Add to session and commit