Skip to content

Commit

Permalink
Merge pull request #97 from SamTV12345/76-messageprogress-bar-for-pod…
Browse files Browse the repository at this point in the history
…casts-imported-with-opml

76 messageprogress bar for podcasts imported with opml
  • Loading branch information
SamTV12345 authored May 1, 2023
2 parents 9552759 + c643297 commit 1fb272d
Show file tree
Hide file tree
Showing 15 changed files with 237 additions and 77 deletions.
4 changes: 3 additions & 1 deletion src/constants/constants.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@ pub enum PodcastType {
AddPodcast,
AddPodcastEpisode,
AddPodcastEpisodes,
RefreshPodcast
RefreshPodcast,
OpmlAdded,
OpmlErrored
}

pub const DEFAULT_SETTINGS: Setting = Setting {
Expand Down
102 changes: 71 additions & 31 deletions src/controllers/podcast_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ use crate::models::user::User;
use crate::mutex::LockResultExt;
use crate::service::file_service::FileService;
use awc::Client as AwcClient;
use crate::models::itunes_models::Podcast;
use crate::models::messages::BroadcastMessage;

#[utoipa::path(
Expand Down Expand Up @@ -278,7 +279,7 @@ pub async fn add_podcast_from_podindex(
}

fn start_download_podindex(id: i32, lobby: Data<Addr<Lobby>>, conn: &mut SqliteConnection)
->Result<(), PodFetchError> {
->Result<Podcast, PodFetchError> {
let rt = tokio::runtime::Runtime::new().unwrap();
rt.block_on(async {
let mut podcast_service = PodcastService::new();
Expand Down Expand Up @@ -447,33 +448,68 @@ async fn insert_outline(

let content = client.get(feed_url).send().unwrap().bytes().unwrap();

let channel = Channel::read_from(&content[..]).expect("Error parsing feed");
let channel = Channel::read_from(&content[..]);

match channel{
Ok(channel)=>{
let mut podcast_service = PodcastService::new();
let mapping_service = MappingService::new();

let image_url = match channel.image {
Some(image) => image.url,
None => {
println!("No image found for podcast. Downloading from {}",environment.server_url
.clone().to_owned() + "ui/default.jpg");
environment.server_url.clone().to_owned() + "ui/default.jpg"
},
};

let inserted_podcast = podcast_service
.handle_insert_of_podcast(
&mut conn.get().unwrap(),
PodcastInsertModel {
feed_url: podcast.clone().xml_url.expect("No feed url"),
title: channel.title,
id: rng.gen::<i32>(),
image_url,
},
mapping_service,
lobby.clone(),
)
.await;
match inserted_podcast {
Ok(podcast)=>{
lobby.do_send(BroadcastMessage{
type_of: PodcastType::OpmlAdded,
message: "Refreshed podcasts".to_string(),
podcast: Option::from(podcast),
podcast_episodes: None,
podcast_episode: None,
})
}
Err(e)=>{
lobby.do_send(BroadcastMessage{
type_of: PodcastType::OpmlErrored,
message: e.to_string(),
podcast: None,
podcast_episodes: None,
podcast_episode: None,
})
}
}
}
Err(e)=>{
lobby.do_send(BroadcastMessage{
type_of: PodcastType::OpmlErrored,
message: e.to_string(),
podcast: None,
podcast_episodes: None,
podcast_episode: None,
})
}
}

let mut podcast_service = PodcastService::new();
let mapping_service = MappingService::new();

let image_url = match channel.image {
Some(image) => image.url,
None => {
println!("No image found for podcast. Downloading from {}",environment.server_url
.clone().to_owned() + "ui/default.jpg");
environment.server_url.clone().to_owned() + "ui/default.jpg"
},
};

podcast_service
.handle_insert_of_podcast(
&mut conn.get().unwrap(),
PodcastInsertModel {
feed_url: podcast.clone().xml_url.expect("No feed url"),
title: channel.title,
id: rng.gen::<i32>(),
image_url,
},
mapping_service,
lobby.clone(),
)
.await.expect("Error inserting podcast");
}

#[derive(Deserialize)]
Expand Down Expand Up @@ -517,15 +553,19 @@ pub(crate) async fn proxy_podcast(

let res = forwarded_req
.send_stream(payload)
.await
.unwrap();
.await;

if res.is_err() {
return HttpResponse::InternalServerError().json("Error proxying podcast");
}

let mut client_resp = HttpResponse::build(res.status());
let unwrapped_res = res.unwrap();
let mut client_resp = HttpResponse::build(unwrapped_res.status());
// Remove `Connection` as per
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Connection#Directives
for (header_name, header_value) in res.headers().iter().filter(|(h, _)| *h != "connection") {
for (header_name, header_value) in unwrapped_res.headers().iter().filter(|(h, _)| *h != "connection") {
client_resp.insert_header((header_name.clone(), header_value.clone()));
}

client_resp.streaming(res)
client_resp.streaming(unwrapped_res)
}
3 changes: 2 additions & 1 deletion src/db.rs
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ use diesel::sql_types::{Text, Timestamp};
use crate::models::episode::{Episode, EpisodeAction};
use crate::models::favorites::Favorite;
use crate::utils::do_retry::do_retry;
use crate::utils::time::opt_or_empty_string;

pub struct DB {
conn: SqliteConnection,
Expand Down Expand Up @@ -202,7 +203,7 @@ impl DB {
url.eq(item.enclosure.unwrap().url),
date_of_recording.eq(inserted_date),
image_url.eq(inserted_image_url),
description.eq(item.description.unwrap()),
description.eq(opt_or_empty_string(item.description)),
))
.get_result::<PodcastEpisode>(conn)
.expect("Error inserting podcast episode");
Expand Down
4 changes: 2 additions & 2 deletions src/service/file_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ impl FileService {
let escaped_title = prepare_podcast_title_to_directory(podcast_title);
if !Path::new(&format!("podcasts/{}", escaped_title)).exists() {
std::fs::create_dir(&format!("podcasts/{}", escaped_title))
.expect("Error creating directory");
.expect(&*("Error creating directory when inserting ".to_owned() + &escaped_title));
Ok(format!("podcasts/{}", escaped_title))
}
else{
Expand Down Expand Up @@ -105,7 +105,7 @@ impl FileService {


pub fn prepare_podcast_title_to_directory(title: &str) ->String {
let re = Regex::new(r"[^a-zA-Z0-9_./]").unwrap();
let re = Regex::new(r"[^a-zA-Z0-9_]").unwrap();
let res = re.replace_all(title, "").to_string();
res.replace("..","")
}
60 changes: 35 additions & 25 deletions src/service/podcast_episode_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -149,38 +149,48 @@ impl PodcastEpisodeService {

match itunes_ext {
Some(itunes_ext) => {
let result =
DB::get_podcast_episode_by_url(conn, &item.enclosure().unwrap().url
.to_string());
let mut duration_episode = 0;
let enclosure = item.enclosure();
match enclosure {
Some(enclosure)=>{
let result =
DB::get_podcast_episode_by_url(conn, &enclosure.url
.to_string());
let mut duration_episode = 0;

if result.unwrap().is_none() {
// Insert new podcast episode
match itunes_ext.clone().duration {
Some(duration) => {
duration_episode = Self::parse_duration(&duration);
if result.unwrap().is_none() {
// Insert new podcast episode
match itunes_ext.clone().duration {
Some(duration) => {
duration_episode = Self::parse_duration(&duration);
}
None => {}
}

let inserted_episode = DB::insert_podcast_episodes(conn,
podcast.clone(),
item.clone(),
itunes_ext.image,
duration_episode as i32,
);
podcast_inserted.push(inserted_episode);
}
None => {}
}

let inserted_episode = DB::insert_podcast_episodes(conn,
podcast.clone(),
item.clone(),
itunes_ext.image,
duration_episode as i32,
);
podcast_inserted.push(inserted_episode);
None => {
log::info!("Skipping episode {} without enclosure.", item.clone().title
.unwrap_or("with no title".to_string()));
continue;
}
}

}
None => {
let opt_enclosure = &item.enclosure;
if opt_enclosure.is_none() {
log::info!("Skipping episode {} without enclosure.", item.clone().title.unwrap_or("with no title".to_string()));
continue;
}
let result = DB::get_podcast_episode_by_url(
conn,
&item
.enclosure()
.expect("A podcast episode needs to have a podcast url")
.url
.to_string(),
);
conn, &opt_enclosure.clone().unwrap().url);
// We can't retrieve the duration of the podcast episode, so we set it to 0

if result.unwrap().is_none() {
Expand Down
10 changes: 6 additions & 4 deletions src/service/rust_service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,8 @@ impl PodcastService {
}

pub async fn insert_podcast_from_podindex(&mut self, conn: &mut SqliteConnection, id: i32,
lobby: Data<Addr<Lobby>>) ->Result<(), PodFetchError>{
lobby: Data<Addr<Lobby>>) ->Result<Podcast,
PodFetchError>{
let mapping_service = MappingService::new();
let resp = self
.client
Expand Down Expand Up @@ -102,15 +103,16 @@ impl PodcastService {
conn: &mut SqliteConnection,
podcast_insert: PodcastInsertModel,
mapping_service: MappingService,
lobby: Data<Addr<Lobby>>) ->Result<(),PodFetchError>{
lobby: Data<Addr<Lobby>>) ->Result<Podcast,PodFetchError>{
let opt_podcast = DB::find_by_rss_feed_url(conn, &podcast_insert.feed_url.clone() );
if opt_podcast.is_some() {
return Err(PodFetchError::podcast_already_exists())
}

let fileservice = FileService::new();

let podcast_directory_created = FileService::create_podcast_directory_exists(&podcast_insert.title.clone(),&podcast_insert.id.clone().to_string());
let podcast_directory_created = FileService::create_podcast_directory_exists(&podcast_insert.title.clone(),
&podcast_insert.id.clone().to_string());

if podcast_directory_created.is_err() {
log::error!("Error creating podcast directory");
Expand Down Expand Up @@ -165,7 +167,7 @@ impl PodcastService {
})
.await
.unwrap();
Ok(())
Ok(inserted_podcast)
}
None => {
panic!("No podcast found")
Expand Down
8 changes: 8 additions & 0 deletions src/utils/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,12 @@ pub fn get_current_timestamp()->i64{

pub fn get_current_timestamp_str()->NaiveDateTime{
Utc::now().naive_utc()
}


pub fn opt_or_empty_string(opt: Option<String>) -> String {
match opt {
Some(s) => s,
None => "".to_string(),
}
}
16 changes: 15 additions & 1 deletion ui/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@ import axios, {AxiosResponse} from "axios";
import {FC, PropsWithChildren, Suspense, useEffect, useState} from "react";
import {Notification} from "./models/Notification";
import {addPodcast, PodcastEpisode, setNotifications, setSelectedEpisodes} from "./store/CommonSlice";
import {checkIfPodcastAdded, checkIfPodcastEpisodeAdded, checkIfPodcastRefreshed} from "./utils/MessageIdentifier";
import {
checkIfOpmlAdded, checkIfOpmlErrored,
checkIfPodcastAdded,
checkIfPodcastEpisodeAdded,
checkIfPodcastRefreshed
} from "./utils/MessageIdentifier";
import {store} from "./store/store";
import {Root} from "./routing/Root";
import {
Expand All @@ -22,6 +27,7 @@ import {LoginComponent} from "./components/LoginComponent";
import {enqueueSnackbar} from "notistack";
import {useTranslation} from "react-i18next";
import {InviteComponent} from "./components/InviteComponent";
import {setMessages, setProgress} from "./store/opmlImportSlice";


export const router = createBrowserRouter(createRoutesFromElements(
Expand Down Expand Up @@ -105,6 +111,14 @@ const App: FC<PropsWithChildren> = ({children}) => {
const podcast = parsed.podcast
enqueueSnackbar(t('podcast-refreshed', {name: podcast.name}), {variant: "success"})
}
else if (checkIfOpmlAdded(parsed)){
dispatch(setProgress([...store.getState().opmlImport.progress,true]))
}
else if (checkIfOpmlErrored(parsed)){
const podcast = parsed
dispatch(setProgress([...store.getState().opmlImport.progress,false]))
dispatch(setMessages([...store.getState().opmlImport.messages, podcast.message]))
}
}


Expand Down
Loading

0 comments on commit 1fb272d

Please sign in to comment.