use axum::{extract::{Path, Query}, response::Html}; use crate::layout::layout; use crate::services::{ApiService, parse_sermon_title, format_duration, format_date}; use serde::Deserialize; use std::collections::HashMap; use chrono::Datelike; #[derive(Deserialize)] pub struct ArchiveQuery { collection: Option, } pub async fn sermons_handler() -> Html { let api_service = ApiService::new(); match api_service.get_jellyfin_libraries().await { Ok(libraries) => { if libraries.is_empty() { return render_no_sermons_page(); } let mut collection_data = std::collections::HashMap::new(); for library in &libraries { match api_service.get_jellyfin_sermons(Some(&library.id), Some(6)).await { Ok(mut sermons) => { // Sort sermons by date sermons.sort_by(|a, b| { let get_valid_date = |sermon: &crate::models::JellyfinItem| { let parsed = parse_sermon_title(&sermon.name); if let Some(ref date_str) = parsed.date_from_title { if let Ok(date) = chrono::NaiveDate::parse_from_str(date_str, "%Y-%m-%d") { return date; } } if let Some(ref premiere_date) = sermon.premiere_date { if let Ok(date) = chrono::NaiveDate::parse_from_str(&premiere_date.split('T').next().unwrap_or(""), "%Y-%m-%d") { return date; } } chrono::Utc::now().naive_utc().date() }; let date_a = get_valid_date(a); let date_b = get_valid_date(b); date_b.cmp(&date_a) }); collection_data.insert(library.name.clone(), sermons); }, Err(_) => { collection_data.insert(library.name.clone(), vec![]); } } } Html(layout(&render_sermons_content(collection_data), "Latest Sermons & Live Streams")) }, Err(_) => render_no_sermons_page() } } fn render_no_sermons_page() -> Html { let content = r#"

Latest Sermons & Live Streams

Listen to our most recent inspiring messages from God's Word

Sermons Coming Soon

Sermons are currently being prepared for online streaming.

Please check back later or contact us for more information.

Note: Make sure Jellyfin server credentials are configured properly.

"#; Html(layout(content, "Sermons")) } fn render_sermons_content(collection_data: std::collections::HashMap>) -> String { format!(r#"

Latest Sermons & Live Streams

Listen to our most recent inspiring messages from God's Word. These are the latest sermons and live stream recordings for your spiritual growth.

{}

About Our Sermons

Our sermons focus on the Three Angels' Messages and the teachings of Jesus Christ. Each message is designed to strengthen your faith and deepen your understanding of God's Word.

Sabbath Sermons

Weekly messages during Divine Worship

Prophecy Studies

Deep dives into Biblical prophecy

Practical Christianity

Applying Bible principles to daily life

Special Events

Revival meetings and guest speakers

"#, collection_data.iter().enumerate().map(|(collection_index, (collection_name, sermons))| { format!(r#"
{}
"#, if collection_index > 0 { "background: var(--soft-gray);" } else { "" }, if collection_name == "LiveStreams" { "broadcast-tower" } else { "church" }, if collection_name == "LiveStreams" { "Live Stream Recordings" } else { "Sabbath Sermons" }, if collection_name == "LiveStreams" { "Recorded live streams from our worship services and special events" } else { "Messages from our regular Sabbath worship services" }, collection_name, if collection_name == "LiveStreams" { "Live Streams" } else { "Sermons" }, if sermons.is_empty() { format!(r#"

No {} Available

Check back later for new content in this collection.

"#, collection_name) } else { format!(r#"
{}
"#, sermons.iter().enumerate().map(|(index, sermon)| { let parsed = parse_sermon_title(&sermon.name); format!(r#"

{}

{} {} {}
{} {}
{} {}
"#, (index % 3) + 1, if sermon.media_type.as_ref().unwrap_or(&"Audio".to_string()) == "Audio" { "music" } else { "video" }, parsed.title, if let Some(ref speaker) = parsed.speaker { format!(r#"

{}

"#, speaker) } else { String::new() }, if let Some(ref premiere_date) = sermon.premiere_date { format!(r#"

{}

"#, format_date(premiere_date)) } else if let Some(ref date_from_title) = parsed.date_from_title { format!(r#"

{}

"#, date_from_title) } else { String::new() }, if let Some(ref overview) = sermon.overview { let preview = if overview.len() > 150 { format!("{}...", &overview[..150]) } else { overview.clone() }; format!(r#"

{}

"#, preview) } else { String::new() }, if let Some(ticks) = sermon.run_time_ticks { format!(r#" {} "#, format_duration(ticks)) } else { String::new() }, if sermon.media_type.as_ref().unwrap_or(&"Audio".to_string()) == "Audio" { "music" } else { "video" }, if sermon.media_type.as_ref().unwrap_or(&"Audio".to_string()) == "Audio" { "Audio" } else { "Video" }, sermon.id, if sermon.media_type.as_ref().unwrap_or(&"Audio".to_string()) == "Audio" { "play" } else { "play-circle" }, if sermon.media_type.as_ref().unwrap_or(&"Audio".to_string()) == "Audio" { "Listen" } else { "Watch" }, if collection_name == "LiveStreams" { "Recording" } else { "Sermon" } ) }).collect::>().join("")) }) }).collect::>().join("")) } pub async fn sermon_detail_handler(Path(id): Path) -> Html { let api_service = ApiService::new(); match api_service.get_jellyfin_sermon(&id).await { Ok(Some(sermon)) => { match api_service.authenticate_jellyfin().await { Ok(Some((token, _))) => { let parsed = parse_sermon_title(&sermon.name); let stream_url = api_service.get_jellyfin_stream_url(&sermon.id, &token); let content = format!(r#"
Back to Sermons

{}

{}
{} {}

{}

{}
{}

Share This Sermon

Invite others to listen to this inspiring message:

"#, parsed.title, if let Some(ref speaker) = parsed.speaker { format!(r#"

Speaker: {}

"#, speaker) } else { String::new() }, if let Some(ref premiere_date) = sermon.premiere_date { format!(r#"

{}

"#, format_date(premiere_date)) } else if let Some(ref date_from_title) = parsed.date_from_title { format!(r#"

{}

"#, date_from_title) } else { String::new() }, if let Some(ticks) = sermon.run_time_ticks { format!(r#"

{}

"#, format_duration(ticks)) } else { String::new() }, if sermon.media_type.as_ref().unwrap_or(&"Audio".to_string()) == "Audio" { "music" } else { "video" }, if sermon.media_type.as_ref().unwrap_or(&"Audio".to_string()) == "Audio" { "Audio Sermon" } else { "Video Sermon" }, if sermon.media_type.as_ref().unwrap_or(&"Audio".to_string()) == "Audio" { format!(r#" "#, stream_url) } else { format!(r#" "#, stream_url) }, if let Some(ref overview) = sermon.overview { format!(r#"

Description

{}

"#, overview) } else { String::new() } ); Html(layout(&content, &parsed.title)) }, _ => render_sermon_error("Unable to access sermon content. Please try again later.") } }, Ok(None) => render_sermon_error("The requested sermon could not be found."), Err(_) => render_sermon_error("Unable to load sermon. Please try again later.") } } fn render_sermon_error(message: &str) -> Html { let content = format!(r#"
"#, message); Html(layout(&content, "Error")) } pub async fn sermons_archive_handler(Query(params): Query) -> Html { let api_service = ApiService::new(); match api_service.get_jellyfin_libraries().await { Ok(libraries) => { // If a specific collection is selected, show only that one if let Some(selected_collection) = params.collection { if let Some(library) = libraries.iter().find(|lib| lib.name == selected_collection) { match api_service.get_jellyfin_sermons(Some(&library.id), None).await { Ok(sermons) => { let organized = organize_sermons_by_year_month(&sermons); let mut years: Vec = organized.keys().cloned().collect(); years.sort_by(|a, b| b.parse::().unwrap_or(0).cmp(&a.parse::().unwrap_or(0))); return render_archive_page(&organized, &years, &selected_collection, &libraries); } Err(_) => return render_error_page() } } else { return render_error_page(); } } // Default to showing Sermons collection (skip multi-collection view) let default_collection = libraries.iter() .find(|lib| lib.name == "Sermons") .or_else(|| libraries.first()); if let Some(library) = default_collection { match api_service.get_jellyfin_sermons(Some(&library.id), None).await { Ok(sermons) => { let organized = organize_sermons_by_year_month(&sermons); let mut years: Vec = organized.keys().cloned().collect(); years.sort_by(|a, b| b.parse::().unwrap_or(0).cmp(&a.parse::().unwrap_or(0))); return render_archive_page(&organized, &years, &library.name, &libraries); } Err(_) => return render_error_page() } } else { return render_error_page(); } } Err(_) => return render_error_page() } } fn organize_sermons_by_year_month(sermons: &[crate::models::JellyfinItem]) -> HashMap>> { let mut organized: HashMap>> = HashMap::new(); for sermon in sermons { let parsed = parse_sermon_title(&sermon.name); let date = if let Some(ref date_str) = parsed.date_from_title { // Try parsing the date from title using multiple formats chrono::NaiveDate::parse_from_str(date_str, "%Y-%m-%d") .or_else(|_| chrono::NaiveDate::parse_from_str(date_str, "%m/%d/%Y")) .or_else(|_| chrono::NaiveDate::parse_from_str(date_str, "%m-%d-%Y")) .unwrap_or_else(|_| { // If date parsing fails, use premiere date or created date as fallback if let Some(ref premiere_date) = sermon.premiere_date { chrono::NaiveDate::parse_from_str(&premiere_date[..10], "%Y-%m-%d") .unwrap_or_else(|_| chrono::Utc::now().naive_utc().date()) } else if let Some(ref created_date) = sermon.date_created { chrono::NaiveDate::parse_from_str(&created_date[..10], "%Y-%m-%d") .unwrap_or_else(|_| chrono::Utc::now().naive_utc().date()) } else { chrono::Utc::now().naive_utc().date() } }) } else if let Some(ref premiere_date) = sermon.premiere_date { chrono::NaiveDate::parse_from_str(&premiere_date[..10], "%Y-%m-%d") .unwrap_or_else(|_| chrono::Utc::now().naive_utc().date()) } else if let Some(ref created_date) = sermon.date_created { chrono::NaiveDate::parse_from_str(&created_date[..10], "%Y-%m-%d") .unwrap_or_else(|_| chrono::Utc::now().naive_utc().date()) } else { chrono::Utc::now().naive_utc().date() }; let year = date.year().to_string(); let month = date.format("%B").to_string(); organized .entry(year) .or_insert_with(HashMap::new) .entry(month) .or_insert_with(Vec::new) .push(sermon); } organized } fn render_archive_page( organized: &HashMap>>, years: &[String], selected_collection: &str, libraries: &[crate::models::JellyfinLibrary] ) -> Html { let collection_display_name = if selected_collection == "LiveStreams" { "Live Stream Recordings" } else { "Sabbath Sermons" }; let content = format!(r#"
Back to Latest Sermons

{} Archive

Browse the complete collection organized by year and month.

{}
{}
"#, collection_display_name, libraries.iter().map(|lib| { let display_name = if lib.name == "LiveStreams" { "Live Streams" } else { "Sermons" }; let icon = if lib.name == "LiveStreams" { "broadcast-tower" } else { "church" }; let active_class = if lib.name == selected_collection { " active" } else { "" }; format!(r#" {} "#, lib.name, if active_class.is_empty() { " btn-outline" } else { " btn-primary" }, icon, display_name) }).collect::>().join(""), if years.is_empty() { format!(r#"

No {} Found

This collection doesn't contain any items yet. Please check back later.

"#, collection_display_name) } else { years.iter().map(|year| { let year_data = organized.get(year).unwrap(); let mut months: Vec<&String> = year_data.keys().collect(); months.sort_by(|a, b| { let month_a = chrono::NaiveDate::parse_from_str(&format!("{} 1, 2020", a), "%B %d, %Y").unwrap().month(); let month_b = chrono::NaiveDate::parse_from_str(&format!("{} 1, 2020", b), "%B %d, %Y").unwrap().month(); month_b.cmp(&month_a) }); let total_items: usize = year_data.values().map(|sermons| sermons.len()).sum(); format!(r#"

{}

{} items
"#, year, year, total_items, year, year, months.iter().map(|month| { let month_sermons = year_data.get(*month).unwrap(); let month_id = format!("{}-{}", year, month.replace(" ", "")); format!(r#"

{} {}

{} item{}
"#, month_id, month, year, month_sermons.len(), if month_sermons.len() == 1 { "" } else { "s" }, month_id, month_id, month_sermons.iter().map(|sermon| { let parsed = parse_sermon_title(&sermon.name); let premiere_date = sermon.premiere_date.as_ref().map(|d| format_date(d)).unwrap_or_default(); let default_media_type = "Video".to_string(); let media_type = sermon.media_type.as_ref().unwrap_or(&default_media_type); format!(r#"

{}

{} {} {}
"#, sermon.id, parsed.title, if let Some(speaker) = parsed.speaker { format!(r#"{}"#, speaker) } else { String::new() }, if !premiere_date.is_empty() { format!(r#"{}"#, premiere_date) } else { String::new() }, if media_type == "Audio" { "music" } else { "video" }, media_type) }).collect::>().join("")) }).collect::>().join("")) }).collect::>().join("") }); Html(layout(&content, &format!("{} Archive", collection_display_name))) } fn render_error_page() -> Html { let content = r#"

Archive Unavailable

Unable to load sermon archive at this time. Please check back later or contact us for assistance.

← Back to Latest Sermons
"#; Html(layout(content, "Sermon Archive")) }