Lazy implementation of .opml import. Needs cleanup and better streamlining of events.

This commit is contained in:
Gabriel 2025-12-10 00:22:10 -05:00
parent 60fd547a6d
commit 8dbda35b18
5 changed files with 81 additions and 7 deletions

View file

@ -13,6 +13,7 @@ directories = "6.0.0"
chrono = "0.4.41" chrono = "0.4.41"
rss_content = { git = "https://code.gabe.rocks/gabriel/rss_content", version = "0.1.1" } rss_content = { git = "https://code.gabe.rocks/gabriel/rss_content", version = "0.1.1" }
url = "2.5.4" url = "2.5.4"
opml = "1.1.6"
#rfd = "0.15.4" (for importing files) #rfd = "0.15.4" (for importing files)
[profile.dev] [profile.dev]
debug=true debug=true

View file

@ -2,6 +2,7 @@ use super::files::*;
use super::net::*; use super::net::*;
use chrono::DateTime; use chrono::DateTime;
use chrono::Utc; use chrono::Utc;
use rss::Channel;
use rusqlite::Row; use rusqlite::Row;
//Maybe use a different time? //Maybe use a different time?
use rusqlite::{Connection, Result}; use rusqlite::{Connection, Result};
@ -100,7 +101,17 @@ pub fn get_feed_id_by_url(url: &str) -> Option<usize> {
} }
} }
pub fn add_feed(url: &str) -> Option<usize> { pub fn add_feed(url: &str) -> Option<usize> {
let feed = load_rss(url).unwrap(); let mut feed: Channel;
match load_rss(url) {
Some(f) => {
feed = f;
}
None => {
return None;
}
}
let time = Utc::now().to_rfc2822(); let time = Utc::now().to_rfc2822();
let image = if let Some(i) = feed.image() { let image = if let Some(i) = feed.image() {
i.url().to_owned() i.url().to_owned()

View file

@ -29,7 +29,9 @@ pub fn url_network(url: &str) -> Network {
return Network::Clearnet; return Network::Clearnet;
} }
} }
None => {return Network::Unknown;} None => {
return Network::Unknown;
}
}, },
Err(e) => { Err(e) => {
return Network::Clearnet; return Network::Clearnet;
@ -96,6 +98,36 @@ fn get_content(url: &str) -> Option<String> {
} }
} }
pub fn retrieve_opml(url: &str) -> Vec<Url> {
match get_content(url) {
Some(c) => match opml::OPML::from_str(&c) {
Ok(list) => {
let mut links = Vec::new();
for link in list.body.outlines {
match link.html_url {
Some(l) => match Url::parse(&l) {
Ok(u) => {
links.push(u);
}
Err(_) => {}
},
None => {}
}
}
return links;
}
Err(e) => {
println!("Failed to parse OPML: {}\n{}", c, e);
return Vec::new();
}
},
None => {
return Vec::new();
}
}
todo!()
}
#[test] #[test]
fn get_onion_content() { fn get_onion_content() {
get_content("http://gabriel262me3lgv3w7xohtesg3laoojmtye644pwirhdm73qmedmsqd.onion/rss"); get_content("http://gabriel262me3lgv3w7xohtesg3laoojmtye644pwirhdm73qmedmsqd.onion/rss");
@ -147,7 +179,8 @@ pub fn load_rss(url: &str) -> Option<Channel> {
} }
}, },
Err(err) => { Err(err) => {
panic!("Error loading feed.:{}", err) println!("Error loading feed.:{}", err);
return None;
} }
} }
} }
@ -202,7 +235,6 @@ fn is_media(link: &str) -> bool {
false false
} }
pub fn parse_links(content: &str) -> Vec<String> { pub fn parse_links(content: &str) -> Vec<String> {
let mut result = Vec::new(); let mut result = Vec::new();
let frag = Html::parse_fragment(content); let frag = Html::parse_fragment(content);

View file

@ -1,4 +1,5 @@
use crate::db::FeedItem; use crate::db::FeedItem;
use crate::net;
use crate::widgets::content_view; use crate::widgets::content_view;
use crate::widgets::media_view; use crate::widgets::media_view;
use crate::widgets::navbar; use crate::widgets::navbar;
@ -16,6 +17,7 @@ use iced::{
}; };
use rss_content::parse_content; use rss_content::parse_content;
use rss_content::Content; use rss_content::Content;
use url::Url;
const ICON: &[u8] = include_bytes!("../assets/icon_placeholder.png"); const ICON: &[u8] = include_bytes!("../assets/icon_placeholder.png");
pub fn user_interface() -> iced::Result { pub fn user_interface() -> iced::Result {
@ -69,6 +71,7 @@ pub enum Message {
AddFeed(String), AddFeed(String),
RemoveFeed(usize), RemoveFeed(usize),
LoadItem(usize), LoadItem(usize),
ProcessOPML(String),
FieldUpdated(AppField, String), FieldUpdated(AppField, String),
LinkClicked(String), LinkClicked(String),
Done(String), Done(String),
@ -78,6 +81,18 @@ pub enum Message {
#[derive(Debug, Clone)] #[derive(Debug, Clone)]
pub enum AppField { pub enum AppField {
FeedInput, FeedInput,
OPMLInput,
}
async fn add_multiple_feeds_background(url:String) -> String {
println!("Adding feeds.");
let links = net::retrieve_opml(&url);
for link in links {
add_feed_background(link.to_string()).await;
}
"Done adding feeds.".to_string()
} }
async fn add_feed_background(url: String) -> String { async fn add_feed_background(url: String) -> String {
@ -103,6 +118,11 @@ fn update(state: &mut State, mes: Message) -> Task<Message> {
Task::none() Task::none()
} }
Message::ProcessOPML(url) => {
state.feed_input = "".to_string();
Task::perform(add_multiple_feeds_background(url), Message::Done)
}
Message::AddFeed(f) => { Message::AddFeed(f) => {
state.feed_input = "".to_string(); state.feed_input = "".to_string();
Task::perform(add_feed_background(f.to_string()), Message::Done) Task::perform(add_feed_background(f.to_string()), Message::Done)
@ -138,6 +158,9 @@ fn update(state: &mut State, mes: Message) -> Task<Message> {
match field { match field {
AppField::FeedInput => { AppField::FeedInput => {
state.feed_input = value; state.feed_input = value;
},
AppField::OPMLInput => {
state.feed_input = value;
} }
} }
Task::none() Task::none()
@ -203,6 +226,7 @@ fn item_view(state: &State) -> Element<'_, Message> {
}; };
container(column!( container(column!(
widgets::navbar(state), widgets::navbar(state),
text(title).size(34),
media_view(state), media_view(state),
content_view(state), content_view(state),
)) ))
@ -249,6 +273,12 @@ fn testing(state: &State) -> Element<'_, Message> {
) )
.spacing(5) .spacing(5)
.padding(10), .padding(10),
row!(
text_input("OPML Url",&state.feed_input)
.on_input(|val| Message::FieldUpdated(AppField::OPMLInput,val))
.width(300),
button("Add feeds from .OPML").on_press(Message::ProcessOPML(state.feed_input.clone()))
),
button("Wipe DB").on_press(Message::ResetDB), button("Wipe DB").on_press(Message::ResetDB),
button("go back!").on_press(Message::ChangePage(Page::Home)) button("go back!").on_press(Message::ChangePage(Page::Home))
) )