empede/src/main.rs

226 lines
5.4 KiB
Rust
Raw Normal View History

2023-04-25 01:26:27 +00:00
use std::path::Path;
use anyhow::anyhow;
use askama::Template;
use async_std::prelude::*;
use async_std::{
io::{BufReader, WriteExt},
net::TcpStream,
};
use serde::Deserialize;
mod mpd;
#[derive(Template)]
#[template(path = "index.html")]
struct IndexTemplate {
path: Vec<String>,
entries: Vec<mpd::Entry>,
2023-04-26 19:14:14 +00:00
song: Option<mpdrs::Song>,
name: Option<String>,
queue: Vec<mpd::QueueItem>,
2023-04-25 01:26:27 +00:00
}
#[derive(Deserialize, Default)]
#[serde(default)]
struct IndexQuery {
path: String,
}
async fn get_index(req: tide::Request<()>) -> tide::Result {
2023-04-25 01:26:27 +00:00
let query: IndexQuery = req.query()?;
2023-04-25 14:32:35 +00:00
let entries = mpd::ls(&query.path)?;
2023-04-26 19:14:14 +00:00
let queue = mpd::playlist()?;
// TODO dry
let mut mpd = mpd::connect()?;
let song = mpd.currentsong()?;
let name = if let Some(song) = &song {
let name = song.title.as_ref().unwrap_or(&song.file).to_string();
Some(name)
} else {
None
};
2023-04-25 01:26:27 +00:00
let template = IndexTemplate {
path: Path::new(&query.path)
.iter()
.map(|s| s.to_string_lossy().to_string())
.collect(),
entries,
2023-04-26 19:14:14 +00:00
name,
song,
queue,
2023-04-25 01:26:27 +00:00
};
Ok(askama_tide::into_response(&template))
}
#[derive(Template)]
#[template(path = "queue.html")]
struct QueueTemplate {
queue: Vec<mpd::QueueItem>,
}
async fn get_queue(_req: tide::Request<()>) -> tide::Result {
2023-04-25 14:32:35 +00:00
let queue = mpd::playlist()?;
2023-04-25 01:26:27 +00:00
let template = QueueTemplate { queue };
Ok(template.into())
}
2023-04-25 14:32:51 +00:00
#[derive(Template)]
2023-04-25 16:58:34 +00:00
#[template(path = "player.html")]
2023-04-25 14:32:51 +00:00
struct CurrentTemplate {
song: Option<mpdrs::Song>,
name: Option<String>,
}
2023-04-25 16:58:34 +00:00
async fn get_player(_req: tide::Request<()>) -> tide::Result {
2023-04-25 14:32:51 +00:00
let mut mpd = mpd::connect()?;
let song = mpd.currentsong()?;
2023-04-25 14:44:21 +00:00
let mut template = CurrentTemplate {
song: song.clone(),
name: None,
};
2023-04-25 14:32:51 +00:00
if let Some(song) = song {
2023-04-27 02:03:17 +00:00
let name = song.title.unwrap_or(song.file);
2023-04-25 14:32:51 +00:00
template.name = Some(name);
}
Ok(template.into())
}
#[derive(Template)]
#[template(path = "browser.html")]
struct BrowserTemplate {
path: Vec<String>,
entries: Vec<mpd::Entry>,
}
async fn get_browser(req: tide::Request<()>) -> tide::Result {
let query: IndexQuery = req.query()?;
let entries = mpd::ls(&query.path)?;
let template = BrowserTemplate {
path: Path::new(&query.path)
.iter()
.map(|s| s.to_string_lossy().to_string())
.collect(),
entries,
};
Ok(template.into())
}
2023-04-25 01:26:27 +00:00
#[derive(Deserialize)]
struct PostQueueQuery {
path: String,
}
async fn post_queue(req: tide::Request<()>) -> tide::Result {
let query: PostQueueQuery = req.query()?;
2023-04-25 14:32:35 +00:00
mpd::connect()?.add(&query.path)?;
2023-04-25 01:26:27 +00:00
Ok("".into())
}
async fn post_play(_req: tide::Request<()>) -> tide::Result {
2023-04-25 14:32:35 +00:00
mpd::connect()?.play()?;
2023-04-25 01:26:27 +00:00
Ok("".into())
}
2023-04-25 14:32:35 +00:00
2023-04-25 01:26:27 +00:00
async fn post_pause(_req: tide::Request<()>) -> tide::Result {
2023-04-25 14:32:35 +00:00
mpd::connect()?.pause(true)?;
2023-04-25 01:26:27 +00:00
Ok("".into())
}
2023-04-25 14:32:35 +00:00
2023-04-25 01:26:27 +00:00
async fn post_previous(_req: tide::Request<()>) -> tide::Result {
2023-04-25 14:32:35 +00:00
mpd::connect()?.prev()?;
2023-04-25 01:26:27 +00:00
Ok("".into())
}
2023-04-25 14:32:35 +00:00
2023-04-25 01:26:27 +00:00
async fn post_next(_req: tide::Request<()>) -> tide::Result {
2023-04-25 14:32:35 +00:00
mpd::connect()?.next()?;
2023-04-25 01:26:27 +00:00
Ok("".into())
}
2023-04-25 14:32:51 +00:00
async fn get_art(req: tide::Request<()>) -> tide::Result {
let query: IndexQuery = req.query()?;
let resp = if let Ok(art) = mpd::connect()?.albumart(&query.path) {
2023-04-27 02:03:17 +00:00
let mime = infer::get(&art)
.map(|k| k.mime_type())
.unwrap_or("application/octet-stream");
2023-04-25 14:32:51 +00:00
tide::Response::builder(tide::StatusCode::Ok)
.body(art)
2023-04-27 02:03:17 +00:00
.content_type(mime)
2023-04-25 14:32:51 +00:00
.header("cache-control", "max-age=3600")
} else {
tide::Response::builder(tide::StatusCode::NotFound)
};
Ok(resp.into())
}
2023-04-25 01:26:27 +00:00
async fn sse(_req: tide::Request<()>, sender: tide::sse::Sender) -> tide::Result<()> {
// Needs to be async and all async mpd libraries suck
let mut stream = TcpStream::connect(mpd::host()).await?;
2023-04-25 01:26:27 +00:00
let mut reader = BufReader::new(stream.clone());
// skip OK MPD line
2023-04-25 02:04:46 +00:00
// TODO check if it is indeed OK
2023-04-25 01:26:27 +00:00
let mut buffer = String::new();
reader.read_line(&mut buffer).await?;
// Update everything on connect
sender.send("playlist", "", None).await?;
sender.send("player", "", None).await?;
2023-04-25 01:26:27 +00:00
loop {
stream.write_all(b"idle playlist player database\n").await?;
2023-04-25 01:26:27 +00:00
2023-04-25 14:44:05 +00:00
loop {
buffer.clear();
reader.read_line(&mut buffer).await?;
if buffer == "OK\n" {
break;
}
let (_, changed) = buffer
.trim_end()
.split_once(": ")
.ok_or(anyhow!("unexpected response from MPD"))?;
sender.send(changed, "", None).await?;
2023-04-25 01:26:27 +00:00
}
}
}
#[async_std::main]
async fn main() -> tide::Result<()> {
tracing_subscriber::fmt()
.with_max_level(tracing::Level::INFO)
.init();
let mut app = tide::new();
app.with(tide_tracing::TraceMiddleware::new());
2023-04-25 14:32:35 +00:00
app.at("/").get(get_index);
2023-04-25 01:26:27 +00:00
app.at("/queue").get(get_queue);
2023-04-25 16:58:34 +00:00
app.at("/player").get(get_player);
2023-04-25 14:32:51 +00:00
app.at("/art").get(get_art);
app.at("/browser").get(get_browser);
2023-04-25 14:32:35 +00:00
app.at("/sse").get(tide::sse::endpoint(sse));
2023-04-25 01:26:27 +00:00
app.at("/queue").post(post_queue);
app.at("/play").post(post_play);
app.at("/pause").post(post_pause);
app.at("/previous").post(post_previous);
app.at("/next").post(post_next);
2023-04-25 14:32:35 +00:00
2023-04-25 01:26:27 +00:00
app.listen("0.0.0.0:8080").await?;
Ok(())
}