msr-api 0.1.0

Wrapper for Monster-Siren's API
Documentation
use getters2::Getters;
use std::{error::Error, result::Result};

#[derive(serde::Serialize, Getters, Debug)]
pub struct SyncedLine {
    stamp: chrono::Duration,
    content: String,
}

#[derive(serde::Serialize, Getters)]
pub struct Lyrics {
    lines: Vec<SyncedLine>,
}

impl Lyrics {
    pub fn to_lrc(&self) -> String {
        self.lines
            .iter()
            .map(|v| {
                format!(
                    "[{:0>2}:{:0>2}.{:0<3}]{}",
                    v.stamp.num_minutes(),
                    v.stamp.num_seconds() % 60,
                    v.stamp.num_milliseconds() % 1000,
                    v.content
                )
            })
            .collect::<Vec<String>>()
            .join("\n")
    }
}

/**
 * Fetch the lyrics of a song from the url to its lyrics file.
 */
pub async fn from_lyrics_url(url: &str) -> Result<Lyrics, Box<dyn Error>> {
    let lines: Vec<SyncedLine> = reqwest::get(url)
        .await?
        .text()
        .await?
        .lines()
        .filter(|e| e.len() > 0)
        .map(|l| SyncedLine {
            stamp: sync_stamp_to_duration(l.split("]").collect::<Vec<&str>>()[0]).unwrap(),
            content: l.split("]").collect::<Vec<&str>>()[1].trim().to_owned(),
        })
        .collect::<Vec<SyncedLine>>();
    Ok(Lyrics { lines })
}

fn sync_stamp_to_duration(stamp: &str) -> Option<chrono::Duration> {
    let stamp: &str = stamp.trim_matches(|c: char| c == '[' || c == ']');
    let parts: Vec<&str> = stamp.split(":").collect();
    let minutes: i64 = parts[0].parse().ok()?;
    let total_millis: u32 = parts[1]
        .split(".")
        .enumerate()
        .map(|(i, v)| v.parse::<u32>().unwrap() * (1000_u32.pow(1 - i as u32)))
        .sum();
    Some(chrono::Duration::milliseconds(
        total_millis as i64 + minutes * 60000,
    ))
}