Browse Source

Support media envelopes and feed w/o transparent shares

main
Leonora Tindall 4 weeks ago
parent
commit
95f6887eda
Signed by: nora GPG Key ID: 7A8B52EC67E09AAF
  1. 1
      README.md
  2. 16
      src/cohost_posts.rs
  3. 20
      src/main.rs
  4. 26
      src/syndication.rs
  5. 5
      static/index.html

1
README.md

@ -17,6 +17,7 @@ ports to use for development and deployment.
- [x] RSS feeds for projects
- [x] Index page explaining what's going on
- [x] Better support for transparent shares
- [x] Add feed without shares
- [ ] More robust parsing (defaults for all!)
- [ ] RSS feeds for tags
- [x] Atom Extension pagination support

16
src/cohost_posts.rs

@ -44,14 +44,9 @@ pub struct CohostPost {
default
)]
pub url: String,
#[serde(
deserialize_with = "deserialize_null_default",
default
)]
#[serde(deserialize_with = "deserialize_null_default", default)]
pub blocks: Vec<CohostPostBlock>,
#[serde(
rename = "transparentShareOfPostId",
)]
#[serde(rename = "transparentShareOfPostId")]
pub transparent_share_of_post_id: Option<u64>,
#[serde(rename = "postingProject")]
pub poster: CohostPostingProject,
@ -93,7 +88,6 @@ pub struct CohostPostLink {
pub t_type: String,
}
#[derive(Debug, Clone, Deserialize)]
pub struct CohostPostBlock {
pub attachment: Option<CohostPostAttachment>,
@ -101,7 +95,11 @@ pub struct CohostPostBlock {
#[derive(Debug, Clone, Deserialize)]
pub struct CohostPostAttachment {
#[serde(rename = "fileURL", deserialize_with = "deserialize_null_default", default)]
#[serde(
rename = "fileURL",
deserialize_with = "deserialize_null_default",
default
)]
pub file_url: String,
}

20
src/main.rs

@ -194,12 +194,22 @@ async fn get_project_data(project_id: String) -> Result<CohostAccount, ErrorResp
}
}
#[get("/<project>/originals.rss")]
async fn syndication_originals_rss_route(project: String) -> Result<RssResponse, ErrorResponse> {
let project_data = get_project_data(project.clone()).await?;
let page_data = get_full_post_data(project.clone()).await?;
Ok(RssResponse {
inner: syndication::channel_for_posts_page(project.clone(), project_data, page_data, true)
.to_string(),
})
}
#[get("/<project>/feed.rss")]
async fn syndication_rss_route(project: String) -> Result<RssResponse, ErrorResponse> {
let project_data = get_project_data(project.clone()).await?;
let page_data = get_full_post_data(project.clone()).await?;
Ok(RssResponse {
inner: syndication::channel_for_posts_page(project.clone(), project_data, page_data)
inner: syndication::channel_for_posts_page(project.clone(), project_data, page_data, false)
.to_string(),
})
}
@ -244,7 +254,13 @@ async fn main() -> Result<(), Box<dyn Error>> {
let _rocket = rocket::build()
.mount(
&ARGS.base_url,
routes![index, webfinger_route, syndication_rss_route, post_md_route],
routes![
index,
webfinger_route,
syndication_rss_route,
syndication_originals_rss_route,
post_md_route
],
)
.ignite()
.await?

26
src/syndication.rs

@ -22,11 +22,16 @@ pub fn channel_for_posts_page(
project_name: impl AsRef<str>,
project: CohostAccount,
mut page: CohostPostsPage,
originals_only: bool,
) -> Channel {
let project_name = project_name.as_ref().clone();
let mut builder = rss::ChannelBuilder::default();
builder
.title(format!("{} Cohost Posts", project.display_name))
.title(format!(
"{} Cohost Posts{}",
project.display_name,
if originals_only { "" } else { " and Shares" }
))
.description(project.description)
.generator(Some(format!(
"{} {}",
@ -73,7 +78,13 @@ pub fn channel_for_posts_page(
let mut body_text = String::new();
if let Some(shared_post_id) = item.transparent_share_of_post_id {
body_text.push_str(&format!("(share of post {} without any commentary)\n\n---\n\n", shared_post_id));
if originals_only {
continue;
}
body_text.push_str(&format!(
"(share of post {} without any commentary)\n\n---\n\n",
shared_post_id
));
} else if item.share_tree.len() == 1 {
body_text.push_str("(in reply to another post)\n\n---\n\n")
} else if item.share_tree.len() > 1 {
@ -113,9 +124,16 @@ pub fn channel_for_posts_page(
item_builder.content(html_output);
for attachment in item.blocks.into_iter().filter_map(|block| block.attachment) {
use rss::EnclosureBuilder;
use mime_guess::from_path as guess_mime_from_path;
let enclosure = EnclosureBuilder::default().mime_type(guess_mime_from_path(&attachment.file_url).first_or_octet_stream().to_string()).url(attachment.file_url).build();
use rss::EnclosureBuilder;
let enclosure = EnclosureBuilder::default()
.mime_type(
guess_mime_from_path(&attachment.file_url)
.first_or_octet_stream()
.to_string(),
)
.url(attachment.file_url)
.build();
item_builder.enclosure(enclosure);
}

5
static/index.html

@ -52,8 +52,9 @@
<h2>Standard Data from Cohost Posts and Projects</h2>
<p>
<h3>Project RSS Feeds</h3>
Go to <code>/project_name/feed.rss</code> to get a feed for a project.
For example, <a href="/noracodes/feed.rss"><code>/noracodes/feed.rss</code></a> will give you the feed for my page.
Go to <code>/project_name/feed.rss</code> to get a feed for a project, or <code>/project_name/originals.rss</code> for just original posts (including shared posts with commentary).
For example, <a href="/noracodes/feed.rss"><code>/noracodes/feed.rss</code></a> will give you the feed for my page,
or <a href="/noracodes/original.rss"><code>/noracodes/feed.rss</code></a> for just my original posts.
</p>
<p>
<h3>Markdown Extraction</h3>

Loading…
Cancel
Save