From 85cc0b06b2232ac2384b5fc2f305f87021d02f95 Mon Sep 17 00:00:00 2001 From: Paul van Tilburg Date: Tue, 24 May 2022 09:58:57 +0200 Subject: [PATCH] Second round of rework to make more podcast clients happy * Prefix the feed handler path with '/feed' * Mount both handlers under `/` now that they have a prefix in the path * Provide the backend to the feed and download handler in their paths * Use the ID as filename download handler, also add an extension --- src/lib.rs | 54 ++++++++++++++++++++++++++++++------------------------ 1 file changed, 30 insertions(+), 24 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 3728576..58458b1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -7,12 +7,14 @@ )] #![deny(missing_docs)] +use std::path::PathBuf; + use chrono::{DateTime, NaiveDateTime, Utc}; -use reqwest::Url; use rocket::fairing::AdHoc; +use rocket::http::uri::Absolute; use rocket::response::Redirect; use rocket::serde::{Deserialize, Serialize}; -use rocket::{get, routes, Build, Responder, Rocket, State}; +use rocket::{get, routes, uri, Build, Responder, Rocket, State}; use rss::extension::itunes::ITunesItemExtensionBuilder; use rss::{ CategoryBuilder, ChannelBuilder, EnclosureBuilder, GuidBuilder, ImageBuilder, ItemBuilder, @@ -24,6 +26,7 @@ pub(crate) mod mixcloud; #[derive(Debug, Deserialize, Serialize)] #[serde(crate = "rocket::serde")] pub(crate) struct Config { + /// The URL at which the application is hosted or proxied from. #[serde(default)] url: String, } @@ -33,9 +36,22 @@ pub(crate) struct Config { #[response(content_type = "application/xml")] struct RssFeed(String); +/// Retrieves a download using youtube-dl and redirection. +#[get("/download//")] +pub(crate) async fn download(file: PathBuf, backend: &str) -> Option { + match backend { + "mixcloud" => { + let key = format!("/{}/", file.with_extension("").to_string_lossy()); + + mixcloud::redirect_url(&key).await.map(Redirect::to) + } + _ => None, + } +} + /// Handler for retrieving the RSS feed of an Mixcloud user. -#[get("/")] -async fn feed(username: &str, config: &State) -> Option { +#[get("/feed//")] +async fn feed(backend: &str, username: &str, config: &State) -> Option { let user = mixcloud::get_user(username).await?; let cloudcasts = mixcloud::get_cloudcasts(username).await?; let mut last_build = DateTime::::from_utc(NaiveDateTime::from_timestamp(0, 0), Utc); @@ -49,10 +65,12 @@ async fn feed(username: &str, config: &State) -> Option { .into_iter() .map(|cloudcast| { let slug = cloudcast.slug; - let mut url = Url::parse(&config.url).unwrap(); - url.set_path(&format!("{}/download", &url.path()[1..])); - url.query_pairs_mut().append_pair("backend", "mixcloud"); - url.query_pairs_mut().append_pair("id", &cloudcast.key); + let mut file = PathBuf::from(cloudcast.key.trim_end_matches('/')); + file.set_extension("m4a"); // FIXME: Don't hardcode the extension! + let url = uri!( + Absolute::parse(&config.url).expect("valid URL"), + download(backend = backend, file = file) + ); let description = format!("Taken from Mixcloud: {}", cloudcast.url); let keywords = cloudcast .tags @@ -72,12 +90,10 @@ async fn feed(username: &str, config: &State) -> Option { }) .collect::>(); + let length = mixcloud::estimated_file_size(cloudcast.audio_length); let enclosure = EnclosureBuilder::default() - .url(url) - .length(format!( - "{}", - mixcloud::estimated_file_size(cloudcast.audio_length) - )) + .url(url.to_string()) + .length(format!("{}", length)) .mime_type(String::from(mixcloud::default_file_type())) .build(); let guid = GuidBuilder::default().value(slug).permalink(false).build(); @@ -123,19 +139,9 @@ async fn feed(username: &str, config: &State) -> Option { Some(feed) } -/// Retrieves a download using youtube-dl. -#[get("/?&")] -pub(crate) async fn download(backend: &str, id: &str) -> Option { - match backend { - "mixcloud" => mixcloud::redirect_url(id).await.map(Redirect::to), - _ => None, - } -} - /// Sets up Rocket. pub fn setup() -> Rocket { rocket::build() - .mount("/mixcloud", routes![feed]) - .mount("/download", routes![download]) + .mount("/", routes![download, feed]) .attach(AdHoc::config::()) }