aboutsummaryrefslogblamecommitdiffstats
path: root/src/ctx.rs
blob: 681230e901699e6cf0a8d79fd1ce18e3e20a6219 (plain) (tree)
1
2
3
4
5
6
7
8
9
10


                             
                               
                           
                         

               

                                 
 






                                         
 







                                     
 




























































                                                                                                                                            



                                                        
                  








































                                                                                                                                                                                  
              
















                                                                                         
     
   
 
use reqwest::StatusCode;
use super::deserialize as de;
use std::sync::Arc;
use std::collections::BTreeMap;
use super::font::FontQuery;
use super::config::STATE;

pub mod model {
  use std::sync::Arc;
  use std::collections::BTreeMap;

  /// The theme representation of a user.
  #[derive(serde::Serialize, Debug)]
  pub struct User {
    /// Their username.
    pub name: Arc<str>,
    /// Their "Display Name".
    pub realname: Arc<str>,

    /// Total scrobbles.
    pub scrobble_count: u64,
    /// Number of artists in library.
    pub artist_count: u64,
    /// Number of tracks in library.
    pub track_count: u64,
    /// Number of albums in library.
    pub album_count: u64,

    /// Link to user's profile picture.
    pub image_url: Arc<str>,

    /// Link to user's profile.
    pub url: Arc<str>
  }

  /// The theme representation of an artist
  #[derive(serde::Serialize, Debug)]
  pub struct Artist {
    /// The artist's name.
    pub name: Arc<str>,

    /// A link to their current image.
    pub image_url: Arc<str>,
    /// A link to their last.fm page.
    pub url: Arc<str>
  }

  /// The theme representation of a user's most recently scrobbled track.
  #[derive(serde::Serialize, Debug)]
  pub struct Scrobble {
    /// The name of the track.
    pub name: Arc<str>,
    /// The name of its album.
    pub album: Arc<str>,
    /// The artist who made it.
    pub artist: Artist,

    /// A link to the track image.
    pub image_url: Arc<str>,
    /// True if the user is currently scrobbling it, false if it's just the most recently played track.
    pub now_playing: bool,
    /// A link to the track's last.fm page.
    pub url: Arc<str>,

    /// True if the user has loved the track.
    pub loved: bool
  }

  /// The user-specified font request parameters
  #[derive(serde::Serialize, Debug)]
  #[serde(untagged)]
  pub enum Font {
    /// A font that requires additional CSS to load properly.
    External { css: Arc<str>, name: Arc<str> },
    /// A font that is w3c standard, or widely installed.
    Name { name: Arc<str> },
  }

  /// The context passed in to all themes.
  ///
  /// Serialized as untagged, so themes should check if `error` is set and decide whether to show an error state or try rendering user info.
  #[derive(serde::Serialize, Debug)]
  #[serde(untagged)]
  pub enum Data {
    /// Contains text explaining a potential error.
    Error { error: &'static str },
    /// Contains data about a user and what they're listening to.
    Data { user: User, scrobble: Scrobble, font: Option<Font>, query: BTreeMap<String, String>,  }
  }
}
#[derive(Debug)]
pub struct ResponseCtx(pub model::Data, pub StatusCode);

impl ResponseCtx {
  pub async fn create(api_result: Result<Arc<(de::User, de::Track)>, (StatusCode, &'static str)>, font_query: Option<FontQuery>, query: BTreeMap<String, String>) -> ResponseCtx {
    match api_result {
      Ok(a) => {
        let (user, track) = a.as_ref();
        ResponseCtx(model::Data::Data {
          user: model::User {
            name: user.name.clone(),
            realname: user.realname.clone(),

            scrobble_count: user.playcount,
            artist_count: user.artist_count,
            track_count: user.track_count,
            album_count: user.track_count,

            image_url: user.images.iter().max_by(|a, b| a.size.cmp(&b.size)).map(|a| a.url.clone()).unwrap_or_else(|| "".into()),

            url: user.url.clone()
          },
          scrobble: model::Scrobble {
            name: track.name.clone(),
            album: track.album.name.clone(),
            artist: model::Artist {
              name: track.artist.name.clone(),
              image_url: track.artist.images.iter().max_by(|a, b| a.size.cmp(&b.size)).map(|a| a.url.clone()).unwrap_or_else(|| "".into()),
              url: track.artist.url.clone().unwrap_or_else(|| "".into())
            },
            image_url: track.images.iter().max_by(|a, b| a.size.cmp(&b.size)).map(|a| a.url.clone()).unwrap_or_else(|| "".into()),
            now_playing: track.attr.nowplaying,
            url: track.url.clone(),
            loved: track.loved.unwrap_or(false)
          },
          font: match font_query {
            Some(FontQuery { google_font: Some(f), .. }) if STATE.has_google_api_key() => {
              let css = match STATE.get_fontinfo(&f.to_string()).await {
                Ok(css) => css,
                Err((status, error)) => { return ResponseCtx(model::Data::Error {error}, status); }
              };
              Some(model::Font::External {
                css,
                name: f
              })
            },
            Some(FontQuery { include_font: Some(f), .. }) => Some(
              model::Font::External {
                css: format!(
                  "@font-face {{ font-family: 'included_font'; src: url('{}'); }}",
                  f.replace("\\", "\\\\")
                   .replace("'", "\\'")).into(),
                name: "included_font".into()
            }),
            Some(FontQuery { font: Some(s), .. }) => Some(model::Font::Name { name: s }),
            _ => None,
          },
          query
        }, StatusCode::OK)
      },
      Err((status, error)) => {
        ResponseCtx(model::Data::Error {error}, status)
      }
    }
  }
}