-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #1 from wyatt-avilla/yew
Yew merge
- Loading branch information
Showing
48 changed files
with
1,605 additions
and
270 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -19,3 +19,7 @@ Cargo.lock | |
|
||
# env file containing API keys | ||
.env | ||
|
||
# wasm related | ||
/frontend/dist/ | ||
/frontend/index.html |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,16 +1,6 @@ | ||
workspace = { members = ["backend", "config", "frontend", "types"] } | ||
|
||
[package] | ||
name = "pulse" | ||
version = "0.1.0" | ||
edition = "2021" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
cached = {version = "0.51.4", features = ["async"]} | ||
dotenv = "0.15.0" | ||
regex = "1.10.5" | ||
reqwest = "0.12.4" | ||
scraper = "0.19.0" | ||
serde_json = "1.0.117" | ||
tokio = { version = "1.38.0", features = ["full"] } | ||
url = "2.5.1" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
[package] | ||
name = "backend" | ||
version = "0.1.0" | ||
edition = "2021" | ||
build = "build.rs" | ||
|
||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html | ||
|
||
[dependencies] | ||
types = { path = "../types" } | ||
config = { path = "../config" } | ||
actix-files = "0.6.6" | ||
actix-web = "4.8.0" | ||
cached = { version = "0.51.4", features = ["async"] } | ||
rand = "0.8.5" | ||
regex = "1.10.5" | ||
reqwest = "0.12.5" | ||
scraper = "0.19.0" | ||
serde_json = "1.0.117" | ||
url = "2.5.2" | ||
yew = "0.21.0" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
use std::fs; | ||
use std::path::Path; | ||
|
||
fn main() { | ||
let dist_dir = Path::new("../frontend/dist"); | ||
if !dist_dir.exists() || fs::read_dir(dist_dir).map_or(true, |entries| entries.count() == 0) { | ||
println!("cargo:warning=Unable to serve frontened"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
pub mod github; | ||
pub mod goodreads; | ||
pub mod lastfm; | ||
pub mod letterboxd; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
use cached::proc_macro::once; | ||
use reqwest::{self, header}; | ||
use types::Commit; | ||
|
||
// 15 min | ||
#[once(result = true, time = 900, sync_writes = true)] | ||
pub async fn fetch_newest( | ||
username: &str, | ||
n: u32, | ||
) -> Result<std::vec::Vec<Commit>, Box<dyn std::error::Error>> { | ||
println!("Fetching data from github api..."); | ||
let url = format!("https://api.github.com/users/{username}/events"); | ||
|
||
let client = reqwest::Client::new(); | ||
let response = client | ||
.get(&url) | ||
.header(header::USER_AGENT, "feframe") | ||
.send() | ||
.await? | ||
.text() | ||
.await?; | ||
|
||
let json: serde_json::Value = serde_json::from_str(&response) | ||
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?; | ||
|
||
let json_array = match json.as_array() { | ||
Some(json_array) => json_array.clone(), | ||
None => return Ok(Vec::new()), | ||
}; | ||
|
||
let push_events: Vec<_> = json_array | ||
.iter() | ||
.filter(|&event| event["type"] == "PushEvent") | ||
.cloned() | ||
.collect(); | ||
|
||
Ok(push_events | ||
.iter() | ||
.filter_map(|event| { | ||
let commit = &event["payload"]["commits"][0]; | ||
let repository_name = event["repo"]["name"].as_str()?.to_string(); | ||
let repository_link = format!("https://github.com/{repository_name}"); | ||
|
||
Some(Commit { | ||
message: commit["message"].as_str()?.to_string(), | ||
url: format!("{repository_link}/commit/{}", commit["sha"].as_str()?), | ||
repository_name, | ||
repository_link, | ||
}) | ||
}) | ||
.take(n as usize) | ||
.collect()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,107 @@ | ||
use cached::proc_macro::once; | ||
use scraper::{Html, Selector}; | ||
use types::Movie; | ||
|
||
fn parse_image(html: &Html) -> Result<String, Box<dyn std::error::Error>> { | ||
let img_selector = | ||
Selector::parse("div.react-component.poster.film-poster img.image[src]").unwrap(); | ||
|
||
Ok(html | ||
.select(&img_selector) | ||
.next() | ||
.ok_or("Image source not found in HTML")? | ||
.attr("src") | ||
.ok_or("Image source attribute not found in HTML")? | ||
.to_string()) | ||
} | ||
|
||
fn parse_release_year(html: &Html) -> Result<String, Box<dyn std::error::Error>> { | ||
let img_selector = Selector::parse("div.react-component.poster.film-poster").unwrap(); | ||
|
||
Ok(html | ||
.select(&img_selector) | ||
.next() | ||
.ok_or("Image source not found in HTML")? | ||
.attr("data-film-release-year") | ||
.ok_or("Image source attribute not found in HTML")? | ||
.to_string()) | ||
} | ||
|
||
// 1 day | ||
#[once(result = true, time = 86400, sync_writes = true)] | ||
pub async fn fetch_newest( | ||
username: &str, | ||
n: u32, | ||
) -> Result<std::vec::Vec<Movie>, Box<dyn std::error::Error>> { | ||
println!("Parsing letterboxd profile html..."); | ||
let url = format!("https://letterboxd.com/{username}/films/by/rated-date/"); | ||
let html = Html::parse_document( | ||
&reqwest::get(&url) | ||
.await | ||
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)? | ||
.text() | ||
.await | ||
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?, | ||
); | ||
|
||
let row_selector = Selector::parse("li.poster-container").unwrap(); | ||
|
||
let div_selector = Selector::parse("div.really-lazy-load").unwrap(); | ||
let rating_selector = Selector::parse("span.rating").unwrap(); | ||
let img_selector = Selector::parse("img.image").unwrap(); | ||
|
||
let movie_iter = html | ||
.select(&row_selector) | ||
.filter_map(|row| { | ||
let title = row | ||
.select(&img_selector) | ||
.next()? | ||
.value() | ||
.attr("alt")? | ||
.to_string(); | ||
|
||
let rating = row | ||
.select(&rating_selector) | ||
.next() | ||
.map(|r| r.inner_html()) | ||
.filter(|r| !r.is_empty())?; | ||
|
||
let div_val = row.select(&div_selector).next()?.value(); | ||
|
||
let link = div_val.attr("data-target-link")?; | ||
|
||
let slug = div_val.attr("data-film-slug")?; | ||
|
||
Some(Movie { | ||
title, | ||
rating, | ||
release_year: String::new(), | ||
url: format! {"https://letterboxd.com{link}"}, | ||
poster_url: slug.to_string(), // icky, just store the slug in here for now xd | ||
}) | ||
}) | ||
.take(n as usize); | ||
|
||
// no async closures :( | ||
let mut movies = Vec::new(); | ||
for mut movie in movie_iter { | ||
let html = Html::parse_document( | ||
&reqwest::get(format!( | ||
"https://letterboxd.com/ajax/poster/film/{}/std/70x105/", | ||
movie.poster_url // aka slug | ||
)) | ||
.await | ||
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)? | ||
.text() | ||
.await | ||
.map_err(|err| Box::new(err) as Box<dyn std::error::Error>)?, | ||
); | ||
|
||
movie.poster_url = parse_image(&html)?; | ||
movie.release_year = parse_release_year(&html)?; | ||
|
||
movies.push(movie); | ||
} | ||
|
||
Ok(movies) | ||
} |
Oops, something went wrong.