diff --git a/src/client.rs b/src/client.rs
index 0e33ce7c13d4858570ab08b69960585eb16177a7..ef2376a72cd04d0ae5bc7fa92982b82e6637df1c 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -120,31 +120,81 @@ fn generate_playlist_api(playlist: &Vec<Song>) -> Vec<ApiSong> {
             Song::Queued { uuid, url, .. } => ApiSong {
                 uuid: uuid.to_string(),
                 url: url.to_string(),
+                title: None,
+                artist: None,
+                artist_url: None,
+                length: None,
+                thumbnail_url: None,
                 state: ApiSongState::Queued,
             },
-            Song::Downloading { uuid, url, .. } => ApiSong {
+            Song::InfoFetching { uuid, url, .. } => ApiSong {
                 uuid: uuid.to_string(),
                 url: url.to_string(),
+                title: None,
+                artist: None,
+                artist_url: None,
+                length: None,
+                thumbnail_url: None,
+                state: ApiSongState::InfoFetching,
+            },
+            Song::InfoFetched { uuid, url, title, artist, artist_url, length, thumbnail_file, .. } => ApiSong {
+                uuid: uuid.to_string(),
+                url: url.to_string(),
+                title: title.to_owned(),
+                artist: artist.to_owned(),
+                artist_url: if let Some(artist_url) = artist_url { Some(artist_url.to_string()) } else { None },
+                length: length.to_owned(),
+                thumbnail_url: Some(thumbnail_file.to_owned()),
+                state: ApiSongState::InfoFetched,
+            },
+            Song::Downloading { uuid, url, title, artist, artist_url, length, thumbnail_file, .. } => ApiSong {
+                uuid: uuid.to_string(),
+                url: url.to_string(),
+                title: title.to_owned(),
+                artist: artist.to_owned(),
+                artist_url: if let Some(artist_url) = artist_url { Some(artist_url.to_string()) } else { None },
+                length: length.to_owned(),
+                thumbnail_url: Some(thumbnail_file.to_owned()),
                 state: ApiSongState::Downloading,
             },
-            Song::Downloaded { uuid, url, .. } => ApiSong {
+            Song::Downloaded { uuid, url, title, artist, artist_url, length, thumbnail_file, .. } => ApiSong {
                 uuid: uuid.to_string(),
                 url: url.to_string(),
+                title: title.to_owned(),
+                artist: artist.to_owned(),
+                artist_url: if let Some(artist_url) = artist_url { Some(artist_url.to_string()) } else { None },
+                length: length.to_owned(),
+                thumbnail_url: Some(thumbnail_file.to_owned()),
                 state: ApiSongState::Downloaded,
             },
-            Song::Converting { uuid, url, .. } => ApiSong {
+            Song::Converting { uuid, url, title, artist, artist_url, length, thumbnail_file, .. } => ApiSong {
                 uuid: uuid.to_string(),
                 url: url.to_string(),
+                title: title.to_owned(),
+                artist: artist.to_owned(),
+                artist_url: if let Some(artist_url) = artist_url { Some(artist_url.to_string()) } else { None },
+                length: length.to_owned(),
+                thumbnail_url: Some(thumbnail_file.to_owned()),
                 state: ApiSongState::Converting,
             },
-            Song::Ready { uuid, url, .. } => ApiSong {
+            Song::Ready { uuid, url, title, artist, artist_url, length, thumbnail_file, .. } => ApiSong {
                 uuid: uuid.to_string(),
                 url: url.to_string(),
+                title: title.to_owned(),
+                artist: artist.to_owned(),
+                artist_url: if let Some(artist_url) = artist_url { Some(artist_url.to_string()) } else { None },
+                length: length.to_owned(),
+                thumbnail_url: Some(thumbnail_file.to_owned()),
                 state: ApiSongState::Ready,
             },
-            Song::Playing { uuid, url, .. } => ApiSong {
+            Song::Playing { uuid, url, title, artist, artist_url, length, thumbnail_file, .. } => ApiSong {
                 uuid: uuid.to_string(),
                 url: url.to_string(),
+                title: title.to_owned(),
+                artist: artist.to_owned(),
+                artist_url: if let Some(artist_url) = artist_url { Some(artist_url.to_string()) } else { None },
+                length: length.to_owned(),
+                thumbnail_url: Some(thumbnail_file.to_owned()),
                 state: ApiSongState::Playing,
             },
         })
diff --git a/src/downloader.rs b/src/downloader.rs
index cc7448c5f46f4ed5be068d27258a9475c7ad1b2f..fe0a1d584ae53560bfcfd81fec7ff590b760e4e7 100644
--- a/src/downloader.rs
+++ b/src/downloader.rs
@@ -1,5 +1,6 @@
 use camino::Utf8Path;
 use futures_signals::signal::Mutable;
+use serde_json::Value;
 use std::process;
 use std::thread::{sleep, spawn};
 use std::time;
@@ -11,12 +12,14 @@ use crate::util;
 
 const TMP_DOWNLOAD_DIR: &str = "./tmp";
 
+const SONGS_INFO_FETCHING_LIMIT: u32 = 5;
 const SONGS_DOWNLOADING_LIMIT: u32 = 1;
 const SONGS_CONVERTING_LIMIT: u32 = 1;
 const SONGS_READY_TARGET: u32 = 3;
 
 pub fn manage_downloads(playlist: Mutable<Vec<Song>>) -> () {
     loop {
+        let mut songs_info_fetching = 0;
         let mut songs_downloading = 0;
         let mut songs_converting = 0;
         let mut songs_ready = 0;
@@ -24,31 +27,71 @@ pub fn manage_downloads(playlist: Mutable<Vec<Song>>) -> () {
         let mut playlist_guard = playlist.lock_mut();
 
         for song in (*playlist_guard).iter_mut() {
-            if songs_ready >= SONGS_READY_TARGET {
-                break;
-            }
-
             match song {
                 Song::Queued { uuid, url } => {
+                    if songs_info_fetching < SONGS_INFO_FETCHING_LIMIT {
+                        let uuid_clone = uuid.clone();
+                        let url_clone = url.clone();
+
+                        *song = Song::InfoFetching {
+                            uuid: uuid.clone(),
+                            url: url.clone(),
+                        };
+
+                        let playlist = Mutable::clone(&playlist);
+                        spawn(move || fetch_info(playlist, &uuid_clone, &url_clone));
+
+                        songs_info_fetching += 1;
+                    }
+                }
+                Song::InfoFetching { .. } => {
+                    songs_info_fetching += 1;
+                }
+                Song::InfoFetched {
+                    uuid,
+                    url,
+                    title,
+                    artist,
+                    artist_url,
+                    length,
+                    thumbnail_file,
+                } => {
                     if songs_downloading < SONGS_DOWNLOADING_LIMIT
-                        && songs_ready + songs_converting < SONGS_READY_TARGET
+                        && songs_ready + songs_converting + songs_downloading < SONGS_READY_TARGET
                     {
-                        let tmp_file_name = util::random_base64();
-                        let tmp_file_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(tmp_file_name);
-                        let uuid = uuid.clone();
                         let uuid_clone = uuid.clone();
-                        let url = url.clone();
                         let url_clone = url.clone();
+                        let title_clone = title.clone();
+                        let artist_clone = artist.clone();
+                        let artist_url_clone = artist_url.clone();
+                        let length_clone = length.clone();
+                        let thumbnail_file_clone = thumbnail_file.clone();
+
                         *song = Song::Downloading {
-                            uuid: uuid,
-                            url: url,
-                            audio_file: tmp_file_path.to_string(),
+                            uuid: uuid.clone(),
+                            url: url.clone(),
+                            title: title.clone(),
+                            artist: artist.clone(),
+                            artist_url: artist_url.clone(),
+                            length: length.to_owned(),
+                            thumbnail_file: thumbnail_file.clone(),
                         };
 
                         let playlist = Mutable::clone(&playlist);
                         spawn(move || {
-                            download_song(playlist, &uuid_clone, &url_clone, &tmp_file_path)
+                            download_song(
+                                playlist,
+                                &uuid_clone,
+                                &url_clone,
+                                &title_clone,
+                                &artist_clone,
+                                &artist_url_clone,
+                                &length_clone,
+                                &thumbnail_file_clone,
+                            )
                         });
+
+                        songs_downloading += 1;
                     }
                 }
                 Song::Downloading { .. } => {
@@ -57,23 +100,34 @@ pub fn manage_downloads(playlist: Mutable<Vec<Song>>) -> () {
                 Song::Downloaded {
                     uuid,
                     url,
+                    title,
+                    artist,
+                    artist_url,
+                    length,
+                    thumbnail_file,
                     audio_file,
                 } => {
-                    if songs_converting < SONGS_CONVERTING_LIMIT && songs_ready < SONGS_READY_TARGET
+                    if songs_converting < SONGS_CONVERTING_LIMIT
+                        && songs_ready + songs_converting < SONGS_READY_TARGET
                     {
-                        let tmp_file_name = format!("{}.raw", util::random_base64());
-                        let tmp_file_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(tmp_file_name);
-                        let uuid = uuid.clone();
                         let uuid_clone = uuid.clone();
-                        let url = url.clone();
                         let url_clone = url.clone();
-                        let audio_file = audio_file.clone();
+                        let title_clone = title.clone();
+                        let artist_clone = artist.clone();
+                        let artist_url_clone = artist_url.clone();
+                        let length_clone = length.clone();
+                        let thumbnail_file_clone = thumbnail_file.clone();
                         let audio_file_clone = audio_file.clone();
+
                         *song = Song::Converting {
                             uuid: uuid.clone(),
-                            url: url,
-                            audio_file: audio_file,
-                            raw_file: tmp_file_path.to_string(),
+                            url: url.clone(),
+                            title: title.clone(),
+                            artist: artist.clone(),
+                            artist_url: artist_url.clone(),
+                            length: length.to_owned(),
+                            thumbnail_file: thumbnail_file.clone(),
+                            audio_file: audio_file.clone(),
                         };
 
                         let playlist = Mutable::clone(&playlist);
@@ -82,12 +136,17 @@ pub fn manage_downloads(playlist: Mutable<Vec<Song>>) -> () {
                                 playlist,
                                 &uuid_clone,
                                 &url_clone,
+                                &title_clone,
+                                &artist_clone,
+                                &artist_url_clone,
+                                &length_clone,
+                                &thumbnail_file_clone,
                                 &audio_file_clone,
-                                &tmp_file_path,
                             )
                         });
+
+                        songs_converting += 1;
                     }
-                    songs_converting += 1;
                 }
                 Song::Converting { .. } => {
                     songs_converting += 1;
@@ -105,24 +164,114 @@ pub fn manage_downloads(playlist: Mutable<Vec<Song>>) -> () {
     }
 }
 
-fn download_song(playlist: Mutable<Vec<Song>>, uuid: &Uuid, url: &Url, audio_file: &Utf8Path) {
-    println!("Downloading from {} to {}!", url, audio_file);
+fn fetch_info(playlist: Mutable<Vec<Song>>, uuid: &Uuid, url: &Url) {
+    println!("Fetching info for {}!", url);
+
+    let tmp_file_name = util::random_base64();
+    let tmp_file_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(tmp_file_name);
+
+    process::Command::new("yt-dlp")
+        .args([
+            "--output",
+            tmp_file_path.as_str(),
+            "--write-info-json",
+            "--write-thumbnail",
+            "--skip-download",
+            url.as_str(),
+        ])
+        .output()
+        .expect("Failed to fetch info!");
+    let info_json_file = format!("{}.info.json", tmp_file_path);
+
+    let data = std::fs::read_to_string(info_json_file).expect("Could not find info.json!");
+
+    let info_json: Value = serde_json::from_str(&data).expect("Could not parse info.json!");
+
+    let mut title = None;
+    if let Value::String(string) = &info_json["title"] {
+        title = Some(string.to_owned());
+    } else {
+        eprintln!("Could not fetch title!");
+    }
+
+    let mut artist = None;
+    if let Value::String(string) = &info_json["channel"] {
+        artist = Some(string.to_owned());
+    } else {
+        eprintln!("Could not fetch artist!");
+    }
+
+    let mut artist_url = None;
+    if let Value::String(string) = &info_json["channel_url"] {
+        if let Ok(url) = Url::parse(&string) {
+            artist_url = Some(url);
+        } else {
+            eprintln!("Invalid artist_url!");
+        }
+    } else {
+        eprintln!("Could not fetch artist_url!");
+    }
+
+    let mut length = None;
+    if let Value::Number(number) = &info_json["duration"] {
+        length = number.as_u64();
+    } else {
+        eprintln!("Could not fetch artist_url!");
+    }
+    println!("Fetched all info!");
+
+    let thumbnail_file = format!("{}.webm", tmp_file_path);
+
+    let mut playlist = playlist.lock_mut();
+    if let Some(index) = (*playlist).iter().position(|song| song.equals_uuid(uuid)) {
+        playlist[index] = Song::InfoFetched {
+            uuid: uuid.to_owned(),
+            url: url.to_owned(),
+            title: title,
+            artist: artist,
+            artist_url: artist_url,
+            length: length,
+            thumbnail_file: thumbnail_file.to_owned(),
+        };
+    }
+    drop(playlist);
+}
+
+fn download_song(
+    playlist: Mutable<Vec<Song>>,
+    uuid: &Uuid,
+    url: &Url,
+    title: &Option<String>,
+    artist: &Option<String>,
+    artist_url: &Option<Url>,
+    length: &Option<u64>,
+    thumbnail_file: &str,
+) {
+    let audio_file_name = util::random_base64();
+    let audio_file_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(audio_file_name);
+
+    println!("Downloading from {} to {}!", url, audio_file_path);
 
     process::Command::new("yt-dlp")
-        .args(["--output", audio_file.as_str(), url.as_str()])
+        .args(["--output", audio_file_path.as_str(), url.as_str()])
         .output()
         .expect("Failed to download audio!");
 
     println!("Download successful!");
 
-    let audio_file = format!("{}.webm", audio_file);
+    let audio_file_path = format!("{}.webm", audio_file_path);
 
     let mut playlist = playlist.lock_mut();
     if let Some(index) = (*playlist).iter().position(|song| song.equals_uuid(uuid)) {
         playlist[index] = Song::Downloaded {
             uuid: uuid.to_owned(),
             url: url.to_owned(),
-            audio_file: audio_file.to_owned(),
+            title: title.to_owned(),
+            artist: artist.to_owned(),
+            artist_url: artist_url.to_owned(),
+            length: length.to_owned(),
+            thumbnail_file: thumbnail_file.to_owned(),
+            audio_file: audio_file_path.to_owned(),
         };
     }
     drop(playlist);
@@ -132,12 +281,19 @@ fn convert_song(
     playlist: Mutable<Vec<Song>>,
     uuid: &Uuid,
     url: &Url,
+    title: &Option<String>,
+    artist: &Option<String>,
+    artist_url: &Option<Url>,
+    length: &Option<u64>,
+    thumbnail_file: &str,
     audio_file: &str,
-    raw_file: &Utf8Path,
 ) {
     let audio_file = Utf8Path::new(audio_file);
 
-    println!("Converting from {} to {}!", audio_file, raw_file);
+    let raw_file_name = format!("{}.raw", util::random_base64());
+    let raw_file_path = Utf8Path::new(TMP_DOWNLOAD_DIR).join(raw_file_name);
+
+    println!("Converting from {} to {}!", audio_file, raw_file_path);
 
     process::Command::new("ffmpeg")
         .args([
@@ -151,7 +307,7 @@ fn convert_song(
             "384k",
             "-ac",
             "1",
-            raw_file.as_str(),
+            raw_file_path.as_str(),
         ])
         .output()
         .expect("Failed to convert audio!");
@@ -164,7 +320,12 @@ fn convert_song(
         playlist[index] = Song::Ready {
             uuid: uuid.to_owned(),
             url: url.to_owned(),
-            raw_file: raw_file.as_str().to_owned(),
+            title: title.to_owned(),
+            artist: artist.to_owned(),
+            artist_url: artist_url.to_owned(),
+            length: length.to_owned(),
+            thumbnail_file: thumbnail_file.to_owned(),
+            raw_file: raw_file_path.as_str().to_owned(),
         };
     }
     drop(playlist);
diff --git a/src/main.rs b/src/main.rs
index 910662c32f93f6aef9ec21d472198cb3757f251b..0f5ede1d46e2d91caf832be29145e97f6290d832 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -33,12 +33,19 @@ fn main() {
                 .expect("No client token provided!"),
         );
 
-        let bind_host =
-            env::var("SPOCCIFY_BIND_HOST").unwrap_or("localhost".to_owned());
-        let bind_port =
-            env::var("SPOCCIFY_BIND_PORT").unwrap_or("8080".to_owned());
+        let http_bind_host =
+        env::var("SPOCCIFY_HTTP_BIND_HOST").unwrap_or("localhost".to_owned());
+        let http_bind_port =
+            env::var("SPOCCIFY_HTTP_BIND_PORT").unwrap_or("9000".to_owned());
 
-        let bind_address = format!("{}:{}", bind_host, bind_port);
+        let http_bind_address = format!("{}:{}", http_bind_host, http_bind_port);
+
+        let websocket_bind_host =
+            env::var("SPOCCIFY_WEBSOCKET_BIND_HOST").unwrap_or("localhost".to_owned());
+        let websocket_bind_port =
+            env::var("SPOCCIFY_WEBSOCKET_BIND_PORT").unwrap_or("9001".to_owned());
+
+        let websocket_bind_address = format!("{}:{}", websocket_bind_host, websocket_bind_port);
 
         println!("Listening for websocket connections on {}!", bind_address);
 
diff --git a/src/player.rs b/src/player.rs
index ea3439874055b508da409cb2c2ab18143f679321..428d4126f02f4fa62d7dd9ced80375d47b8915e1 100644
--- a/src/player.rs
+++ b/src/player.rs
@@ -86,6 +86,11 @@ fn get_next_song_file(playlist: &Mutable<Vec<Song>>) -> std::io::Result<File> {
     if let Song::Ready {
         uuid,
         url,
+        title,
+        artist,
+        artist_url,
+        length,
+        thumbnail_file,
         raw_file,
     } = &playlist[0]
     {
@@ -96,6 +101,11 @@ fn get_next_song_file(playlist: &Mutable<Vec<Song>>) -> std::io::Result<File> {
         playlist[0] = Song::Playing {
             uuid: uuid.clone(),
             url: url.clone(),
+            title: title.clone(),
+            artist: artist.clone(),
+            artist_url: artist_url.clone(),
+            length: length.to_owned(),
+            thumbnail_file: thumbnail_file.clone(),
             raw_file: raw_file,
         };
         file = File::open(raw_file_clone);
diff --git a/src/song.rs b/src/song.rs
index 968fa938a7f2e2adbfd9a2432ff268eee32345c6..76fd2a5f816fa0364a654be8539f3fc1276fe291 100644
--- a/src/song.rs
+++ b/src/song.rs
@@ -8,30 +8,66 @@ pub enum Song {
         uuid: Uuid,
         url: Url,
     },
+    InfoFetching {
+        uuid: Uuid,
+        url: Url,
+    },
+    InfoFetched {
+        uuid: Uuid,
+        url: Url,
+        title: Option<String>,
+        artist: Option<String>,
+        artist_url: Option<Url>,
+        length: Option<u64>,
+        thumbnail_file: String,
+    },
     Downloading {
         uuid: Uuid,
         url: Url,
-        audio_file: String,
+        title: Option<String>,
+        artist: Option<String>,
+        artist_url: Option<Url>,
+        length: Option<u64>,
+        thumbnail_file: String,
     },
     Downloaded {
         uuid: Uuid,
         url: Url,
+        title: Option<String>,
+        artist: Option<String>,
+        artist_url: Option<Url>,
+        length: Option<u64>,
+        thumbnail_file: String,
         audio_file: String,
     },
     Converting {
         uuid: Uuid,
         url: Url,
+        title: Option<String>,
+        artist: Option<String>,
+        artist_url: Option<Url>,
+        length: Option<u64>,
+        thumbnail_file: String,
         audio_file: String,
-        raw_file: String,
     },
     Ready {
         uuid: Uuid,
         url: Url,
+        title: Option<String>,
+        artist: Option<String>,
+        artist_url: Option<Url>,
+        length: Option<u64>,
+        thumbnail_file: String,
         raw_file: String,
     },
     Playing {
         uuid: Uuid,
         url: Url,
+        title: Option<String>,
+        artist: Option<String>,
+        artist_url: Option<Url>,
+        length: Option<u64>,
+        thumbnail_file: String,
         raw_file: String,
     },
 }
@@ -40,6 +76,8 @@ impl Song {
     pub fn equals_uuid(&self, uuid_compare: &Uuid) -> bool {
         match self {
             Self::Queued { uuid, .. } => uuid == uuid_compare,
+            Self::InfoFetching { uuid, .. } => uuid == uuid_compare,
+            Self::InfoFetched { uuid, .. } => uuid == uuid_compare,
             Self::Downloading { uuid, .. } => uuid == uuid_compare,
             Self::Downloaded { uuid, .. } => uuid == uuid_compare,
             Self::Converting { uuid, .. } => uuid == uuid_compare,
@@ -54,11 +92,18 @@ pub struct ApiSong {
     pub uuid: String,
     pub url: String,
     pub state: ApiSongState,
+    pub title: Option<String>,
+    pub artist: Option<String>,
+    pub artist_url: Option<String>,
+    pub length: Option<u64>,
+    pub thumbnail_url: Option<String>,
 }
 
 #[derive(Debug, Serialize, Deserialize)]
 pub enum ApiSongState {
     Queued,
+    InfoFetching,
+    InfoFetched,
     Downloading,
     Downloaded,
     Converting,