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


                        
                                 
                          
                                  

               

                                 
 






                                         
 







                                     
 













                                           
                              


































                                                                                                       







                                        




                                                                                                                                            
                 


                                                                 






                                              
   
 

                           
                                                                                                                                                                     























                                                                                                                                           
            














                                                                                                                                                                                                                                         
            















                                                                                       
     
   
 
// SPDX-License-Identifier: AGPL-3.0-only
use std::collections::BTreeMap;

use reqwest::StatusCode;

use super::cache::user::UserInfo;
use super::config::CONFIG;
use crate::cache::font::FontQuery;

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> },
  }

  #[derive(serde::Serialize, Debug)]
  pub struct Data {
    pub user: User,
    pub scrobble: Scrobble,
    pub font: Option<Font>,
    pub query: BTreeMap<String, String>,
  }

  /// 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 Root {
    /// Contains text explaining a potential error.
    Error { error: &'static str },
    /// Contains data about a user and what they're listening to.
    Data { #[serde(flatten)] data: Box<Data> }
  }

  impl From<Data> for Root {
    fn from(v: Data) -> Self {
      Self::Data { data: Box::new(v) }
    }
  }
}
pub use model::Root as Ctx;

pub async fn get_ctx(api_result: Result<UserInfo, (StatusCode, &'static str)>, font_query: Option<FontQuery>, query: BTreeMap<String, String>) -> (Ctx, StatusCode) {
  match api_result {
    Ok(a) => {
      let (user, track, trackstub) = a.as_ref();
      ((model::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.title.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()).filter(|s| !s.is_empty()).unwrap_or_else(|| "https://lastfm.freetls.fastly.net/i/u/128s/4128a6eb29f94943c9d206c08e625904.jpg".into()),
          now_playing: trackstub.attr.nowplaying,
          url: track.url.clone(),
          loved: track.loved.unwrap_or(false)
        },
        font: match font_query {
          Some(FontQuery { google_font: Some(f), .. }) if CONFIG.has_google_api_key() => {
            let css = match crate::cache::font::get_fontinfo(&f.to_string()).await {
              Ok(css) => css,
              Err((status, error)) => { return (model::Root::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
      }).into(), StatusCode::OK)
    },
    Err((status, error)) => {
      (model::Root::Error {error}, status)
    }
  }
}