diff --git a/src/main.py b/src/main.py index 3fdd2b2..0ea6b90 100644 --- a/src/main.py +++ b/src/main.py @@ -56,6 +56,39 @@ def admin_list_podcasts(session: SessionDep, request: Request): ) +@app.get("/admin/new") +def admin_create_podcast(request: Request): + return templates.TemplateResponse( + request=request, + name="admin_new.html.j2", + ) + + +@app.post("/admin/new") +def admin_create_podcast_post( + session: SessionDep, + request: Request, + name: Annotated[str, Form()], +): + if name.strip() == "": + return templates.TemplateResponse( + request, + name="admin_error.html.j2", + status_code=400, + context={ + "title": "Invalid entry", + "description": "Name must not be empty", + }, + ) + + podcast = models.Podcast(name=name, description=name) + + session.add(podcast) + session.commit() + + return RedirectResponse(f"/admin/{podcast.id}", status_code=303) + + @app.get("/admin/{podcast_id}") def admin_list_podcast(session: SessionDep, request: Request, podcast_id: str): podcast = session.exec( @@ -63,7 +96,15 @@ def admin_list_podcast(session: SessionDep, request: Request, podcast_id: str): ).first() if podcast is None: - raise HTTPException(status_code=404, detail="Podcast not found") + return templates.TemplateResponse( + request, + name="admin_error.html.j2", + status_code=400, + context={ + "title": "Not found", + "description": "The podcast was not found.", + }, + ) episodes = podcast.episodes episodes.sort(key=lambda e: e.publish_date, reverse=True) @@ -170,29 +211,11 @@ async def admin_upload_episode( @app.get("/admin/{podcast_id}/{episode_id}/delete") -def admin_delete_episode(session: SessionDep, podcast_id: str, episode_id: str): - episode = session.exec( - select(models.PodcastEpisode).where( - and_( - models.PodcastEpisode.id == episode_id, - models.PodcastEpisode.podcast_id == podcast_id, - ) - ) - ).first() - - if episode is None: - raise HTTPException(status_code=404, detail="Episode or podcast not found") - - (settings.directory / f"{episode_id}.m4a").unlink() - session.delete(episode) - session.commit() - - return RedirectResponse(f"/admin/{podcast_id}", status_code=303) - - -@app.get("/admin/{podcast_id}/{episode_id}/edit") -def admin_edit_episode( - session: SessionDep, request: Request, podcast_id: str, episode_id: str +def admin_delete_episode( + session: SessionDep, + request: Request, + podcast_id: str, + episode_id: str, ): episode = session.exec( select(models.PodcastEpisode).where( @@ -204,7 +227,49 @@ def admin_edit_episode( ).first() if episode is None: - raise HTTPException(status_code=404, detail="Episode or podcast not found") + return templates.TemplateResponse( + request, + name="admin_error.html.j2", + status_code=404, + context={ + "title": "Not found", + "description": "The episode or podcast was not found.", + }, + ) + + (settings.directory / f"{episode_id}.m4a").unlink() + session.delete(episode) + session.commit() + + return RedirectResponse(f"/admin/{podcast_id}", status_code=303) + + +@app.get("/admin/{podcast_id}/{episode_id}/edit") +def admin_edit_episode( + session: SessionDep, + request: Request, + podcast_id: str, + episode_id: str, +): + episode = session.exec( + select(models.PodcastEpisode).where( + and_( + models.PodcastEpisode.id == episode_id, + models.PodcastEpisode.podcast_id == podcast_id, + ) + ) + ).first() + + if episode is None: + return templates.TemplateResponse( + request, + name="admin_error.html.j2", + status_code=404, + context={ + "title": "Not found", + "description": "The episode or podcast was not found.", + }, + ) return templates.TemplateResponse( request=request, @@ -216,6 +281,7 @@ def admin_edit_episode( @app.post("/admin/{podcast_id}/{episode_id}/edit") def admin_edit_episode_post( session: SessionDep, + request: Request, podcast_id: str, episode_id: str, name: Annotated[str, Form()], @@ -231,7 +297,15 @@ def admin_edit_episode_post( ).first() if episode is None: - raise HTTPException(status_code=404, detail="Episode or podcast not found") + return templates.TemplateResponse( + request, + name="admin_error.html.j2", + status_code=404, + context={ + "title": "Not found", + "description": "The episode or podcast was not found.", + }, + ) if name.strip() != "": episode.name = name @@ -254,7 +328,15 @@ def admin_edit_podcast(session: SessionDep, request: Request, podcast_id: str): ).first() if podcast is None: - raise HTTPException(status_code=404, detail="Podcast not found") + return templates.TemplateResponse( + request, + name="admin_error.html.j2", + status_code=404, + context={ + "title": "Not found", + "description": "The podcast was not found.", + }, + ) return templates.TemplateResponse( request=request, @@ -266,6 +348,7 @@ def admin_edit_podcast(session: SessionDep, request: Request, podcast_id: str): @app.post("/admin/{podcast_id}/edit") def admin_edit_podcast_post( session: SessionDep, + request: Request, podcast_id: str, name: Annotated[str, Form()], description: Annotated[str, Form()], @@ -276,7 +359,15 @@ def admin_edit_podcast_post( ).first() if podcast is None: - raise HTTPException(status_code=404, detail="Podcast not found") + return templates.TemplateResponse( + request, + name="admin_error.html.j2", + status_code=404, + context={ + "title": "Not found", + "description": "The podcast was not found.", + }, + ) if name.strip() != "": podcast.name = name @@ -285,23 +376,38 @@ def admin_edit_podcast_post( if image is not None and image.size > 0: if not (image.filename.endswith(".jpg") or image.filename.endswith(".png")): - raise HTTPException( + return templates.TemplateResponse( + request, + name="admin_error.html.j2", status_code=400, - detail="The uploaded podcast image must be a jpg or png", + context={ + "title": "Invalid entry", + "description": "The uploaded podcast image must be a jpg or png.", + }, ) im = Image.open(image.file) if im.size[0] != im.size[1] or im.size[0] < 1400 or im.size[0] > 3000: - raise HTTPException( + return templates.TemplateResponse( + request, + name="admin_error.html.j2", status_code=400, - detail="The uploaded podcast image must be square and between 1400x1400px and 3000x3000px in size", + context={ + "title": "Invalid entry", + "description": "The uploaded podcast image must be square and between 1400x1400px and 3000x3000px in size.", + }, ) if im.mode != "RGB": - raise HTTPException( + return templates.TemplateResponse( + request, + name="admin_error.html.j2", status_code=400, - detail="The uploaded podcast image must be in RGB format", + context={ + "title": "Invalid entry", + "description": "The uploaded podcast image must be in RGB format.", + }, ) if podcast.image_filename is not None: @@ -318,7 +424,7 @@ def admin_edit_podcast_post( @app.get("/{podcast_id}.xml") -def get_feed(session: SessionDep, request: Request, podcast_id: str): +def get_podcast_feed(session: SessionDep, request: Request, podcast_id: str): podcast = session.exec( select(models.Podcast).where(models.Podcast.id == podcast_id) ).first() diff --git a/src/models.py b/src/models.py index 8f0a8ab..bf46ab0 100644 --- a/src/models.py +++ b/src/models.py @@ -14,7 +14,7 @@ class Podcast(SQLModel, table=True): name: str description: str explicit: bool = Field(default=True) - image_filename: Optional[str] = Field(default=False) + image_filename: Optional[str] = Field(default=None) episodes: list["PodcastEpisode"] = Relationship(back_populates="podcast") diff --git a/src/templates/admin_episode_edit.html.j2 b/src/templates/admin_episode_edit.html.j2 index f0c4c43..bec69fe 100644 --- a/src/templates/admin_episode_edit.html.j2 +++ b/src/templates/admin_episode_edit.html.j2 @@ -1,5 +1,6 @@ -{% extends 'layout.html.j2' %} +{% extends 'admin_layout.html.j2' %} {% block content %} +{{ super() }}

{{ episode.name }}

diff --git a/src/templates/admin_error.html.j2 b/src/templates/admin_error.html.j2 new file mode 100644 index 0000000..5d588b6 --- /dev/null +++ b/src/templates/admin_error.html.j2 @@ -0,0 +1,7 @@ +{% extends 'admin_layout.html.j2' %} +{% block content %} +{{ super() }} +

{{ title }}

+

{{ description }}

+

Go Back

+{% endblock %} diff --git a/src/templates/admin_feed.html.j2 b/src/templates/admin_feed.html.j2 index c79f137..f5fc71f 100644 --- a/src/templates/admin_feed.html.j2 +++ b/src/templates/admin_feed.html.j2 @@ -1,5 +1,6 @@ -{% extends 'layout.html.j2' %} +{% extends 'admin_layout.html.j2' %} {% block content %} +{{ super() }} {% if podcast.image_filename %}

@@ -74,6 +75,7 @@ function setFormEnabled(enabled) { submitButton.disabled = !enabled; fileInput.disabled = !enabled; + resp.setAttribute("aria-busy", JSON.stringify(!enabled)); } window.go = () => { diff --git a/src/templates/admin_feed_edit.html.j2 b/src/templates/admin_feed_edit.html.j2 index a19edac..7940f6d 100644 --- a/src/templates/admin_feed_edit.html.j2 +++ b/src/templates/admin_feed_edit.html.j2 @@ -1,5 +1,6 @@ -{% extends 'layout.html.j2' %} +{% extends 'admin_layout.html.j2' %} {% block content %} +{{ super() }}

{{ podcast.name }}

diff --git a/src/templates/admin_feeds.html.j2 b/src/templates/admin_feeds.html.j2 index b4b5753..6b4fd7f 100644 --- a/src/templates/admin_feeds.html.j2 +++ b/src/templates/admin_feeds.html.j2 @@ -1,6 +1,10 @@ -{% extends 'layout.html.j2' %} +{% extends 'admin_layout.html.j2' %} {% block content %} +{{ super() }}

Podcasts

+

+ Actions: Create +

    {% for podcast in podcasts %}
  • {{ podcast.name }}
  • diff --git a/src/templates/admin_layout.html.j2 b/src/templates/admin_layout.html.j2 new file mode 100644 index 0000000..0eea34b --- /dev/null +++ b/src/templates/admin_layout.html.j2 @@ -0,0 +1,11 @@ +{% extends 'layout.html.j2' %} +{% block content %} + +{% endblock %} diff --git a/src/templates/admin_new.html.j2 b/src/templates/admin_new.html.j2 new file mode 100644 index 0000000..428e0d2 --- /dev/null +++ b/src/templates/admin_new.html.j2 @@ -0,0 +1,15 @@ +{% extends 'admin_layout.html.j2' %} +{% block content %} +{{ super() }} +

    New Podcast

    + +
    + +
    + + + +{% endblock %}