diff --git a/README.md b/README.md
index d8b7158867447a9cb3066bf45609dab18b04aa1d..ee31f0f7ce759af0c0efede2661d25064d6b1498 100644
--- a/README.md
+++ b/README.md
@@ -4,11 +4,13 @@
 
 ## TO-DO
 - [ ]   check that share urls/uris work and share into the spotify app
+- [ ]   an entire podcast has a rating. maybe give the episode the podcast rating use this to load it https://api-partner.spotify.com/pathfinder/v1/query?operationName=queryShowMetadataV2&variables=%7B%22uri%22%3A%22spotify%3Ashow%3A5VzFvh1JlEhBMS6ZHZ8CNO%22%7D&extensions=%7B%22persistedQuery%22%3A%7B%22version%22%3A1%2C%22sha256Hash%22%3A%225fb034a236a3e8301e9eca0e23def3341ed66c891ea2d4fea374c091dc4b4a6a%22%7D%7D
+- [ ]   there is data about the number of people following a podcast somewhere that shows up in the mobile app
+- [ ]   test the logged out version of the plugin
 
 ## Grayjay Bugs
-- [ ]   none
-
-
-
-
-
+- [ ]   RatingScaler doesn't work
+- [ ]   websockets are hard to use
+- [ ]   datetime doesn't display for playlists
+- [ ]   there is no way to get to the creator of a playlist
+- [ ]   the pager that goes in the contents property of a playlist doesn't ever call the nextPage method
\ No newline at end of file
diff --git a/build/SpotifyConfig.json b/build/SpotifyConfig.json
index 10df0b703793d9c2a3ef99efc3136e7e97487fd1..69510908ac3b7b0a3c7700b42084bcf5763ff524 100644
--- a/build/SpotifyConfig.json
+++ b/build/SpotifyConfig.json
@@ -22,7 +22,8 @@
         "spclient.wg.spotify.com",
         "gue1-spclient.spotify.com",
         "seektables.scdn.co",
-        "api-partner.spotify.com"
+        "api-partner.spotify.com",
+        "apresolve.spotify.com"
     ],
     "authentication": {
 		"loginUrl": "https://accounts.spotify.com/en/login",
diff --git a/build/SpotifyScript.js b/build/SpotifyScript.js
index 34b9ecccd63ecfd7c77fa50aa93c6907cc5e95ff..53fa2cc168fa2a647107e9889c4862d0a893a215 100644
--- a/build/SpotifyScript.js
+++ b/build/SpotifyScript.js
@@ -1,11 +1,15 @@
 const CONTENT_REGEX = /^https:\/\/open\.spotify\.com\/(track|episode)\/([a-zA-Z0-9]*)($|\/)/;
+const PLAYLIST_REGEX = /^https:\/\/open\.spotify\.com\/(album|playlist)\/([a-zA-Z0-9]*)($|\/)/;
 const SONG_URL_PREFIX = "https://open.spotify.com/track/";
+const PODCAST_URL_PREFIX = "https://open.spotify.com/show/";
+const ARTIST_URL_PREFIX = "https://open.spotify.com/artist/";
+const ALBUM_URL_PREFIX = "https://open.spotify.com/album/";
+const QUERY_URL = "https://api-partner.spotify.com/pathfinder/v1/query";
 const IMAGE_URL_PREFIX = "https://i.scdn.co/image/";
 const PLATFORM = "Spotify";
 // const USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64; rv:124.0) Gecko/20100101 Firefox/124.0" as const
 const HARDCODED_ZERO = 0;
 const HARDCODED_EMPTY_STRING = "";
-const EMPTY_AUTHOR = new PlatformAuthorLink(new PlatformID(PLATFORM, "", plugin.config.id), "", "");
 const local_http = http;
 // const local_utility = utility
 // set missing constants
@@ -32,9 +36,9 @@ source.getHome = getHome;
 // source.searchChannelContents = searchChannelContents
 source.isContentDetailsUrl = isContentDetailsUrl;
 source.getContentDetails = getContentDetails;
-// source.isPlaylistUrl = isPlaylistUrl
+source.isPlaylistUrl = isPlaylistUrl;
 // source.searchPlaylists = searchPlaylists
-// source.getPlaylist = getPlaylist
+source.getPlaylist = getPlaylist;
 // source.getComments = getComments
 // source.getSubComments = getSubComments
 // source.getLiveChatWindow = getLiveChatWindow
@@ -139,6 +143,12 @@ function enable(conf, settings, savedState) {
             license_uri: license_uri
         };
     }
+    if (is_premium()) {
+        local_state = {
+            bearer_token: "BQB5uzdWBsXahudafNcc3RR7kExwq4vpsbkSCOuGkn06aYQ8it6x-M5PmaVi2gapw5NgXMO4tlDSenQcCqv2dQg94a_4fsi11yX5qkAeqW0f_bRNHZ3cg1QlJgX8kKnOmEs5I8jmhY2pR0k8ParLvLZt7tVQYVceei3NJM4w4oKr6thqYyCST-3BHJximVhvT5_cmMrFac5VBWkgioQPxNUSO1U6ICi0hN2W5WMYg8KjdrjCPKfFiYTE3Z9myO0fGI13o1uWzNRrXHc075HuOYvvv_5UbobXPyPVbSEfLqGuaPstmN8Ubj7XV6FXYPnvNSNnJlLx1GwLx2EoyA",
+            license_uri: local_state.license_uri
+        };
+    }
 }
 //#endregion
 function disable() {
@@ -149,30 +159,72 @@ function saveState() {
 }
 //#region home
 function getHome() {
-    const song_uri_id = "6XXxKsu3RJeN3ZvbMYrgQW";
-    const song_url = `${SONG_URL_PREFIX}${song_uri_id}`;
-    const { url: metadata_url, headers: metadata_headers } = song_metadata_args(song_uri_id);
-    const song_metadata_response = JSON.parse(local_http.GET(metadata_url, metadata_headers, false).body);
-    const first_artist = song_metadata_response.artist[0];
+    const playlists = [
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "the coolest album of all time",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/album/6BzxX6zkDsYKFJ04ziU5xQ",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        }),
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "dayly mix mix 1111",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/playlist/37i9dQZF1E38112qhvV3BT",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        }),
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "tines for two",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/playlist/4ClcTeCoE9aPMhy0CLoD9P",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        })
+    ];
+    return new ContentPager(playlists, false);
+    /*
+    const song_uri_id = "6XXxKsu3RJeN3ZvbMYrgQW"
+    const song_url = `${SONG_URL_PREFIX}${song_uri_id}`
+
+    const { url: metadata_url, headers: metadata_headers } = song_metadata_args(song_uri_id)
+    const song_metadata_response: SongMetadataResponse = JSON.parse(local_http.GET(metadata_url, metadata_headers, false).body)
+    const first_artist = song_metadata_response.artist[0]
     if (first_artist === undefined) {
-        throw new ScriptException("missing artist");
+        throw new ScriptException("missing artist")
     }
     //https://spclient.wg.spotify.com/metadata/4/track/e4eac7232f3d48fb965b5a03c49eb93a
     const songs = [new PlatformVideo({
-            id: new PlatformID(PLATFORM, song_uri_id, plugin.config.id),
-            name: song_metadata_response.name,
-            author: new PlatformAuthorLink(new PlatformID(PLATFORM, first_artist.gid, plugin.config.id), first_artist.name, "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
-            url: song_url,
-            thumbnails: new Thumbnails(song_metadata_response.album.cover_group.image.map(function (image) {
-                return new Thumbnail(`${IMAGE_URL_PREFIX}${image.file_id}`, image.height);
-            })),
-            duration: song_metadata_response.duration / 1000,
-            viewCount: HARDCODED_ZERO,
-            isLive: false,
-            shareUrl: song_metadata_response.canonical_uri,
-            // readonly uploadDate?: number
-        })];
-    return new VideoPager(songs, false);
+        id: new PlatformID(PLATFORM, song_uri_id, plugin.config.id),
+        name: song_metadata_response.name,
+        author: new PlatformAuthorLink(new PlatformID(PLATFORM, first_artist.gid, plugin.config.id), first_artist.name, "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+        url: song_url,
+        thumbnails: new Thumbnails(song_metadata_response.album.cover_group.image.map(function (image) {
+            return new Thumbnail(`${IMAGE_URL_PREFIX}${image.file_id}`, image.height)
+        })),
+        duration: song_metadata_response.duration / 1000,
+        viewCount: HARDCODED_ZERO,
+        isLive: false,
+        shareUrl: song_metadata_response.canonical_uri,
+        // readonly uploadDate?: number
+    })]
+    return new VideoPager(songs, false)
+    */
 }
 //#endregion
 //#region content
@@ -202,30 +254,101 @@ function getContentDetails(url) {
         case "track": {
             const song_url = `${SONG_URL_PREFIX}${content_uri_id}`;
             const { url: metadata_url, headers: metadata_headers } = song_metadata_args(content_uri_id);
-            const song_metadata_response = JSON.parse(local_http.GET(metadata_url, metadata_headers, false).body);
-            const first_artist = song_metadata_response.artist[0];
+            const { url: track_metadata_url, headers: _track_metadata_headers } = track_metadata_args(content_uri_id);
+            const batch = local_http
+                .batch()
+                .GET(metadata_url, metadata_headers, false)
+                .GET(track_metadata_url, _track_metadata_headers, false);
+            if (is_premium()) {
+                const { url, headers } = lyrics_args(content_uri_id);
+                batch.GET(url, headers, false);
+            }
+            const results = batch
+                .execute();
+            if (results[0] === undefined || results[1] === undefined) {
+                throw new ScriptException("unreachable");
+            }
+            const song_metadata_response = JSON.parse(results[0].body);
+            const track_metadata_response = JSON.parse(results[1].body);
+            const first_artist = track_metadata_response.data.trackUnion.firstArtist.items[0];
             if (first_artist === undefined) {
                 throw new ScriptException("missing artist");
             }
-            const artist_url = "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m";
+            const artist_url = `https://open.spotify.com/artist/${first_artist.id}`;
+            const highest_quality_artist_cover_art = first_artist.visuals.avatarImage.sources.reduce(function (accumulator, current) {
+                return accumulator.height > current.height ? accumulator : current;
+            });
+            let subtitles = [];
+            if (results[2] !== undefined) {
+                const lyrics_response = JSON.parse(results[2].body);
+                const subtitle_name = function () {
+                    switch (lyrics_response.lyrics.language) {
+                        case "en":
+                            return "English";
+                        default:
+                            throw assert_no_fall_through(lyrics_response.lyrics.language, "unreachable");
+                    }
+                }();
+                const convert = milliseconds_to_WebVTT_timestamp;
+                let vtt_text = `WEBVTT ${subtitle_name}\n`;
+                vtt_text += "\n";
+                lyrics_response.lyrics.lines.forEach(function (line, index) {
+                    const next = lyrics_response.lyrics.lines[index + 1];
+                    let end = next?.startTimeMs;
+                    if (end === undefined) {
+                        end = track_metadata_response.data.trackUnion.duration.totalMilliseconds.toString();
+                    }
+                    vtt_text += `${convert(parseInt(line.startTimeMs))} --> ${convert(parseInt(end))}\n`;
+                    vtt_text += `${line.words}\n`;
+                    vtt_text += "\n";
+                });
+                subtitles = [{
+                        url: song_url,
+                        name: subtitle_name,
+                        getSubtitles() {
+                            return vtt_text;
+                        },
+                        format: "text/vtt",
+                    }];
+            }
             const format = is_premium() ? "MP4_256" : "MP4_128";
             const maybe_file_id = song_metadata_response.file.find(function (file) { return file.format === format; })?.file_id;
             if (maybe_file_id === undefined) {
                 throw new ScriptException("missing expected format");
             }
             const { url, headers } = file_manifest_args(maybe_file_id);
-            const file_manifest = JSON.parse(local_http.GET(url, headers, false).body);
-            const duration = song_metadata_response.duration / 1000;
+            const { url: artist_metadata_url, headers: artist_metadata_headers } = artist_metadata_args(first_artist.id);
+            const second_results = local_http
+                .batch()
+                .GET(url, headers, false)
+                .GET(artist_metadata_url, artist_metadata_headers, false)
+                .execute();
+            if (second_results[0] === undefined || second_results[1] === undefined) {
+                throw new ScriptException("unreachable");
+            }
+            const file_manifest = JSON.parse(second_results[0].body);
+            const artist_metadata_response = JSON.parse(second_results[1].body);
+            const duration = track_metadata_response.data.trackUnion.duration.totalMilliseconds / 1000;
             const file_url = file_manifest.cdnurl[0];
             if (file_url === undefined) {
                 throw new ScriptException("unreachable");
             }
+            const codecs = "mp4a.40.2";
             const audio_sources = [new AudioUrlWidevineSource({
                     //audio/mp4; codecs="mp4a.40.2
-                    name: format,
-                    bitrate: HARDCODED_ZERO,
+                    name: codecs,
+                    bitrate: function (format) {
+                        switch (format) {
+                            case "MP4_128":
+                                return 128000;
+                            case "MP4_256":
+                                return 256000;
+                            default:
+                                throw assert_no_fall_through(format, "unreachable");
+                        }
+                    }(format),
                     container: "audio/mp4",
-                    codecs: "mp4a.40.2",
+                    codecs,
                     duration,
                     url: file_url,
                     language: Language.UNKNOWN,
@@ -235,20 +358,20 @@ function getContentDetails(url) {
             return new PlatformVideoDetails({
                 id: new PlatformID(PLATFORM, content_uri_id, plugin.config.id),
                 name: song_metadata_response.name,
-                author: new PlatformAuthorLink(new PlatformID(PLATFORM, first_artist.gid, plugin.config.id), first_artist.name, artist_url),
+                author: new PlatformAuthorLink(new PlatformID(PLATFORM, first_artist.id, plugin.config.id), first_artist.profile.name, artist_url, highest_quality_artist_cover_art.url, artist_metadata_response.data.artistUnion.stats.monthlyListeners),
                 url: song_url,
                 thumbnails: new Thumbnails(song_metadata_response.album.cover_group.image.map(function (image) {
                     return new Thumbnail(`${IMAGE_URL_PREFIX}${image.file_id}`, image.height);
                 })),
                 duration,
-                viewCount: HARDCODED_ZERO,
+                viewCount: parseInt(track_metadata_response.data.trackUnion.playcount),
                 isLive: false,
                 shareUrl: song_metadata_response.canonical_uri,
-                // readonly uploadDate?: number
+                datetime: new Date(track_metadata_response.data.trackUnion.albumOfTrack.date.isoString).getTime() / 1000,
                 description: HARDCODED_EMPTY_STRING,
                 video: new UnMuxVideoSourceDescriptor([], audio_sources),
-                rating: new RatingLikes(HARDCODED_ZERO)
-                // readonly subtitles?: ISubtitleSource[]
+                rating: new RatingLikes(HARDCODED_ZERO),
+                subtitles
             });
         }
         case "episode": {
@@ -264,13 +387,56 @@ function getContentDetails(url) {
             }
             const transcript_response = JSON.parse(responses[0].body);
             const episode_metadata_response = JSON.parse(responses[1].body);
+            if (episode_metadata_response.data.episodeUnionV2.mediaTypes.length === 2) {
+                function assert_video(_mediaTypes) { }
+                assert_video(episode_metadata_response.data.episodeUnionV2.mediaTypes);
+                //TODO since we don't use the transcript we should only load it when audio only podcasts are played
+                // TODO handle video podcasts. Grayjay doesn't currently support the websocket functionality necessary
+                // the basic process to get the video play info is
+                // connect to the websocket wss://gue1-dealer.spotify.com/?access_token=<bearer-token> 
+                // register the device https://gue1-spclient.spotify.com/track-playback/v1/devices
+                //      generate the device id using code found in the min js like this
+                /*
+                        web player js
+                        const t = Math.ceil(e / 2);
+                        return function(e) {
+                            let t = "";
+                            for (let n = 0; n < e.length; n++) {
+                                const i = e[n];
+                                i < 16 && (t += "0"),
+                                t += i.toString(16)
+                            }
+                            return t
+                        }(Oe(t))
+                */
+                // load devices info https://gue1-spclient.spotify.com/connect-state/v1/devices/hobs_aced97d86694f14d304dd4e6f1f7f8c3bff
+                // transfer to our device https://gue1-spclient.spotify.com/connect-state/v1/connect/transfer/from/9a7079bd5b5605839c1d9080d0f4368bfcd6d2eb/to/aced97d86694f14d304dd4e6f1f7f8c3bff
+                // signal the play of the given podcast (not quite sure how this works :/)
+                // recieve the video play info via the websocket connection
+                //
+            }
             const format = "MP4_128";
             const maybe_file_id = episode_metadata_response.data.episodeUnionV2.audio.items.find(function (file) { return file.format === format; })?.fileId;
             if (maybe_file_id === undefined) {
                 throw new ScriptException("missing expected format");
             }
+            const limited_podcast_metadata = episode_metadata_response.data.episodeUnionV2.podcastV2.data;
+            const podcast_uri_id = id_from_uri(limited_podcast_metadata.uri);
+            const highest_quality_cover_art = limited_podcast_metadata.coverArt.sources.reduce(function (accumulator, current) {
+                return accumulator.height > current.height ? accumulator : current;
+            });
             const { url: manifest_url, headers: manifest_headers } = file_manifest_args(maybe_file_id);
-            const file_manifest = JSON.parse(local_http.GET(manifest_url, manifest_headers, false).body);
+            const { url: podcast_metadata_url, headers: podcast_metadata_headers } = podcast_metadata_args(podcast_uri_id);
+            const results = local_http
+                .batch()
+                .GET(podcast_metadata_url, podcast_metadata_headers, false)
+                .GET(manifest_url, manifest_headers, false)
+                .execute();
+            if (results[0] === undefined || results[1] === undefined) {
+                throw new ScriptException("unreachable");
+            }
+            const full_podcast_metadata = JSON.parse(results[0].body);
+            const file_manifest = JSON.parse(results[1].body);
             const duration = episode_metadata_response.data.episodeUnionV2.duration.totalMilliseconds / 1000;
             const file_url = file_manifest.cdnurl[0];
             if (file_url === undefined) {
@@ -315,7 +481,7 @@ function getContentDetails(url) {
             return new PlatformVideoDetails({
                 id: new PlatformID(PLATFORM, content_uri_id, plugin.config.id),
                 name: episode_metadata_response.data.episodeUnionV2.name,
-                author: EMPTY_AUTHOR,
+                author: new PlatformAuthorLink(new PlatformID(PLATFORM, podcast_uri_id, plugin.config.id), limited_podcast_metadata.name, `${PODCAST_URL_PREFIX}${podcast_uri_id}`, highest_quality_cover_art.url),
                 url: episode_url,
                 thumbnails: new Thumbnails(episode_metadata_response.data.episodeUnionV2.coverArt.sources.map(function (image) {
                     return new Thumbnail(image.url, image.height);
@@ -324,10 +490,10 @@ function getContentDetails(url) {
                 viewCount: HARDCODED_ZERO,
                 isLive: false,
                 shareUrl: episode_metadata_response.data.episodeUnionV2.uri,
-                uploadDate: new Date(episode_metadata_response.data.episodeUnionV2.releaseDate.isoString).getTime() / 1000,
+                datetime: new Date(episode_metadata_response.data.episodeUnionV2.releaseDate.isoString).getTime() / 1000,
                 description: episode_metadata_response.data.episodeUnionV2.htmlDescription,
                 video: new UnMuxVideoSourceDescriptor([], audio_sources),
-                rating: new RatingLikes(HARDCODED_ZERO),
+                rating: new RatingScaler(full_podcast_metadata.data.podcastUnionV2.rating.averageRating.average),
                 subtitles: [{
                         url: episode_url,
                         name: subtitle_name,
@@ -342,6 +508,22 @@ function getContentDetails(url) {
             throw assert_no_fall_through(content_type, "unreachable");
     }
 }
+function podcast_metadata_args(podcast_uri_id) {
+    const variables = JSON.stringify({
+        uri: `spotify:show:${podcast_uri_id}`
+    });
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "5fb034a236a3e8301e9eca0e23def3341ed66c891ea2d4fea374c091dc4b4a6a"
+        }
+    });
+    const url = new URL(QUERY_URL);
+    url.searchParams.set("operationName", "queryShowMetadataV2");
+    url.searchParams.set("variables", variables);
+    url.searchParams.set("extensions", extensions);
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } };
+}
 function transcript_args(episode_uri_id) {
     const transcript_url_prefix = "https://spclient.wg.spotify.com/transcript-read-along/v2/episode/";
     const url = new URL(`${transcript_url_prefix}${episode_uri_id}`);
@@ -351,6 +533,17 @@ function transcript_args(episode_uri_id) {
         headers: { Authorization: `Bearer ${local_state.bearer_token}` }
     };
 }
+function lyrics_args(song_uri_id) {
+    const url = new URL(`https://spclient.wg.spotify.com/color-lyrics/v2/track/${song_uri_id}`);
+    return {
+        url: url.toString(),
+        headers: {
+            Accept: "application/json",
+            "app-platform": "WebPlayer",
+            Authorization: `Bearer ${local_state.bearer_token}`
+        }
+    };
+}
 function file_manifest_args(file_id) {
     const file_manifest_url_prefix = "https://gue1-spclient.spotify.com/storage-resolve/v2/files/audio/interactive/10/";
     const file_manifest_params = "?product=9&alt=json";
@@ -360,7 +553,6 @@ function file_manifest_args(file_id) {
     };
 }
 function episode_metadata_args(episode_uri_id) {
-    const episode_metadata_url_prefix = "https://api-partner.spotify.com/pathfinder/v1/query";
     const variables = JSON.stringify({
         uri: `spotify:episode:${episode_uri_id}`
     });
@@ -370,12 +562,28 @@ function episode_metadata_args(episode_uri_id) {
             sha256Hash: "9697538fe993af785c10725a40bb9265a20b998ccd2383bd6f586e01303824e9"
         }
     });
-    const url = new URL(episode_metadata_url_prefix);
+    const url = new URL(QUERY_URL);
     url.searchParams.set("operationName", "getEpisodeOrChapter");
     url.searchParams.set("variables", variables);
     url.searchParams.set("extensions", extensions);
     return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } };
 }
+function track_metadata_args(song_uri_id) {
+    const variables = JSON.stringify({
+        uri: `spotify:track:${song_uri_id}`
+    });
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "ae85b52abb74d20a4c331d4143d4772c95f34757bfa8c625474b912b9055b5c0"
+        }
+    });
+    const url = new URL(QUERY_URL);
+    url.searchParams.set("operationName", "getTrack");
+    url.searchParams.set("variables", variables);
+    url.searchParams.set("extensions", extensions);
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } };
+}
 function song_metadata_args(song_uri_id) {
     const song_metadata_url = "https://spclient.wg.spotify.com/metadata/4/track/";
     return {
@@ -386,8 +594,317 @@ function song_metadata_args(song_uri_id) {
         }
     };
 }
+function artist_metadata_args(artist_uri_id) {
+    const variables = JSON.stringify({
+        uri: `spotify:artist:${artist_uri_id}`,
+        locale: "",
+        includePrerelease: true
+    });
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "da986392124383827dc03cbb3d66c1de81225244b6e20f8d78f9f802cc43df6e"
+        }
+    });
+    const url = new URL(QUERY_URL);
+    url.searchParams.set("operationName", "queryArtistOverview");
+    url.searchParams.set("variables", variables);
+    url.searchParams.set("extensions", extensions);
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } };
+}
+//#endregion
+//#region playlists
+// https://open.spotify.com/album/6BzxX6zkDsYKFJ04ziU5xQ
+// https://open.spotify.com/playlist/37i9dQZF1E38112qhvV3BT
+function isPlaylistUrl(url) {
+    return PLAYLIST_REGEX.test(url);
+}
+function getPlaylist(url) {
+    const match_result = url.match(PLAYLIST_REGEX);
+    if (match_result === null) {
+        throw new ScriptException("regex error");
+    }
+    const maybe_playlist_type = match_result[1];
+    if (maybe_playlist_type === undefined) {
+        throw new ScriptException("regex error");
+    }
+    const playlist_type = maybe_playlist_type;
+    const playlist_uri_id = match_result[2];
+    if (playlist_uri_id === undefined) {
+        throw new ScriptException("regex error");
+    }
+    switch (playlist_type) {
+        case "album": {
+            // if the author is the same as the album then include the artist pick otherwise nothing
+            // TODO we could load in extra info for all the other artists but it might be hard to do that in a request efficient way
+            const pagination_limit = 50;
+            const offset = 0;
+            const { url, headers } = album_metadata_args(playlist_uri_id, offset, pagination_limit);
+            const album_metadata_response = JSON.parse(local_http.GET(url, headers, false).body);
+            const album_artist = album_metadata_response.data.albumUnion.artists.items[0];
+            if (album_artist === undefined) {
+                throw new ScriptException("missing album artist");
+            }
+            const unix_time = new Date(album_metadata_response.data.albumUnion.date.isoString).getTime() / 1000;
+            return new PlatformPlaylistDetails({
+                id: new PlatformID(PLATFORM, playlist_uri_id, plugin.config.id),
+                name: album_metadata_response.data.albumUnion.name,
+                author: new PlatformAuthorLink(new PlatformID(PLATFORM, album_artist.id, plugin.config.id), album_artist.profile.name, `${ARTIST_URL_PREFIX}${album_artist.id}`, album_artist.visuals.avatarImage.sources[album_artist.visuals.avatarImage.sources.length - 1]?.url),
+                datetime: unix_time,
+                url: `${ALBUM_URL_PREFIX}${playlist_uri_id}`,
+                videoCount: album_metadata_response.data.albumUnion.tracks.totalCount,
+                contents: new AlbumPager(playlist_uri_id, offset, pagination_limit, album_metadata_response, album_artist, unix_time)
+            });
+        }
+        case "playlist": {
+            if (!bridge.isLoggedIn()) {
+                throw new LoginRequiredException("login to open playlists");
+            }
+            const pagination_limit = 25;
+            const offset = 0;
+            const { url, headers } = fetch_playlist_args(playlist_uri_id, offset, pagination_limit);
+            const playlist_response = JSON.parse(local_http.GET(url, headers, false).body);
+            const owner = playlist_response.data.playlistV2.ownerV2.data;
+            return new PlatformPlaylistDetails({
+                id: new PlatformID(PLATFORM, playlist_uri_id, plugin.config.id),
+                name: playlist_response.data.playlistV2.name,
+                author: new PlatformAuthorLink(new PlatformID(PLATFORM, owner.username, plugin.config.id), owner.name, `${ARTIST_URL_PREFIX}${owner.username}`, owner.avatar?.sources[owner.avatar.sources.length - 1]?.url),
+                url: `${ALBUM_URL_PREFIX}${playlist_uri_id}`,
+                videoCount: playlist_response.data.playlistV2.content.totalCount,
+                contents: new SpotifyPlaylistPager(playlist_uri_id, offset, pagination_limit, playlist_response)
+            });
+        }
+        default: {
+            throw assert_no_fall_through(playlist_type, "unreachable");
+        }
+    }
+}
+class SpotifyPlaylistPager extends VideoPager {
+    playlist_uri_id;
+    pagination_limit;
+    offset;
+    total_tracks;
+    constructor(playlist_uri_id, offset, pagination_limit, playlist_response) {
+        const total_tracks = playlist_response.data.playlistV2.content.totalCount;
+        const songs = format_playlist_tracks(playlist_response.data.playlistV2.content);
+        super(songs, total_tracks > offset + pagination_limit);
+        this.playlist_uri_id = playlist_uri_id;
+        this.pagination_limit = pagination_limit;
+        this.offset = offset + pagination_limit;
+        this.total_tracks = total_tracks;
+    }
+    nextPage() {
+        const { url, headers } = fetch_playlist_contents_args(this.playlist_uri_id, this.offset, this.pagination_limit);
+        const playlist_content_response = JSON.parse(local_http.GET(url, headers, false).body);
+        const songs = format_playlist_tracks(playlist_content_response.data.playlistV2.content);
+        this.results = songs;
+        this.hasMore = this.total_tracks > this.offset + this.pagination_limit;
+        this.offset += this.pagination_limit;
+        return this;
+    }
+    hasMorePagers() {
+        return this.hasMore;
+    }
+}
+function format_playlist_tracks(content) {
+    return content.items.map(function (playlist_track_metadata) {
+        const song = playlist_track_metadata.itemV2.data;
+        const track_uri_id = id_from_uri(song.uri);
+        const artist = song.artists.items[0];
+        if (artist === undefined) {
+            throw new ScriptException("missing artist");
+        }
+        const url = `${SONG_URL_PREFIX}${track_uri_id}`;
+        return new PlatformVideo({
+            id: new PlatformID(PLATFORM, track_uri_id, plugin.config.id),
+            name: song.name,
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, id_from_uri(artist.uri), plugin.config.id), artist.profile.name, `${ARTIST_URL_PREFIX}${id_from_uri(artist.uri)}`
+            // TODO figure out a way to get the artist thumbnail
+            ),
+            url,
+            thumbnails: new Thumbnails(song.albumOfTrack.coverArt.sources.map(function (source) {
+                return new Thumbnail(source.url, source.height);
+            })),
+            duration: song.trackDuration.totalMilliseconds / 1000,
+            viewCount: parseInt(song.playcount),
+            isLive: false,
+            shareUrl: url,
+            datetime: new Date(playlist_track_metadata.addedAt.isoString).getTime() / 1000
+        });
+    });
+}
+/**
+ *
+ * @param playlist_uri_id
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns
+ */
+function fetch_playlist_contents_args(playlist_uri_id, offset, limit) {
+    const variables = JSON.stringify({
+        uri: `spotify:playlist:${playlist_uri_id}`,
+        offset: offset,
+        limit: limit
+    });
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "91d4c2bc3e0cd1bc672281c4f1f59f43ff55ba726ca04a45810d99bd091f3f0e"
+        }
+    });
+    const url = new URL(QUERY_URL);
+    url.searchParams.set("operationName", "fetchPlaylistContents");
+    url.searchParams.set("variables", variables);
+    url.searchParams.set("extensions", extensions);
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } };
+}
+/**
+ *
+ * @param playlist_uri_id
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns
+ */
+function fetch_playlist_args(playlist_uri_id, offset, limit) {
+    const variables = JSON.stringify({
+        uri: `spotify:playlist:${playlist_uri_id}`,
+        offset: offset,
+        limit: limit
+    });
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "91d4c2bc3e0cd1bc672281c4f1f59f43ff55ba726ca04a45810d99bd091f3f0e"
+        }
+    });
+    const url = new URL(QUERY_URL);
+    url.searchParams.set("operationName", "fetchPlaylist");
+    url.searchParams.set("variables", variables);
+    url.searchParams.set("extensions", extensions);
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } };
+}
+class AlbumPager extends VideoPager {
+    album_uri_id;
+    pagination_limit;
+    offset;
+    thumbnails;
+    album_artist;
+    unix_time;
+    total_tracks;
+    constructor(album_uri_id, offset, pagination_limit, album_metadata_response, album_artist, unix_time) {
+        const total_tracks = album_metadata_response.data.albumUnion.tracks.totalCount;
+        const thumbnails = new Thumbnails(album_metadata_response.data.albumUnion.coverArt.sources.map(function (source) {
+            return new Thumbnail(source.url, source.height);
+        }));
+        const songs = format_album_tracks(album_metadata_response.data.albumUnion.tracks, thumbnails, album_artist, unix_time);
+        super(songs, total_tracks > offset + pagination_limit);
+        this.album_uri_id = album_uri_id;
+        this.pagination_limit = pagination_limit;
+        this.offset = offset + pagination_limit;
+        this.thumbnails = thumbnails;
+        this.album_artist = album_artist;
+        this.unix_time = unix_time;
+        this.total_tracks = total_tracks;
+    }
+    nextPage() {
+        const { url, headers } = album_tracks_args(this.album_uri_id, this.offset, this.pagination_limit);
+        const album_tracks_response = JSON.parse(local_http.GET(url, headers, false).body);
+        const songs = format_album_tracks(album_tracks_response.data.albumUnion.tracks, this.thumbnails, this.album_artist, this.unix_time);
+        this.results = songs;
+        this.hasMore = this.total_tracks > this.offset + this.pagination_limit;
+        this.offset += this.pagination_limit;
+        return this;
+    }
+    hasMorePagers() {
+        return this.hasMore;
+    }
+}
+function format_album_tracks(tracks, thumbnails, album_artist, unix_time) {
+    return tracks.items.map(function (track) {
+        const track_uri_id = id_from_uri(track.track.uri);
+        const artist = track.track.artists.items[0];
+        if (artist === undefined) {
+            throw new ScriptException("missing artist");
+        }
+        const url = `${SONG_URL_PREFIX}${track_uri_id}`;
+        return new PlatformVideo({
+            id: new PlatformID(PLATFORM, track_uri_id, plugin.config.id),
+            name: track.track.name,
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, id_from_uri(artist.uri), plugin.config.id), artist.profile.name, `${ARTIST_URL_PREFIX}${id_from_uri(artist.uri)}`, id_from_uri(artist.uri) === album_artist.id ? album_artist.visuals.avatarImage.sources[album_artist.visuals.avatarImage.sources.length - 1]?.url : undefined),
+            url,
+            thumbnails,
+            duration: track.track.duration.totalMilliseconds / 1000,
+            viewCount: parseInt(track.track.playcount),
+            isLive: false,
+            shareUrl: url,
+            datetime: unix_time
+        });
+    });
+}
+/**
+ *
+ * @param album_uri_id
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns
+ */
+function album_tracks_args(album_uri_id, offset, limit) {
+    const variables = JSON.stringify({
+        uri: `spotify:album:${album_uri_id}`,
+        offset: offset,
+        limit: limit
+    });
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "469874edcad37b7a379d4f22f0083a49ea3d6ae097916120d9bbe3e36ca79e9d"
+        }
+    });
+    const url = new URL(QUERY_URL);
+    url.searchParams.set("operationName", "queryAlbumTracks");
+    url.searchParams.set("variables", variables);
+    url.searchParams.set("extensions", extensions);
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } };
+}
+/**
+ *
+ * @param album_uri_id
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns
+ */
+function album_metadata_args(album_uri_id, offset, limit) {
+    const variables = JSON.stringify({
+        uri: `spotify:album:${album_uri_id}`,
+        locale: "",
+        offset: offset,
+        limit: limit
+    });
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "469874edcad37b7a379d4f22f0083a49ea3d6ae097916120d9bbe3e36ca79e9d"
+        }
+    });
+    const url = new URL(QUERY_URL);
+    url.searchParams.set("operationName", "getAlbum");
+    url.searchParams.set("variables", variables);
+    url.searchParams.set("extensions", extensions);
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } };
+}
 //#endregion
 //#region utilities
+function id_from_uri(uri) {
+    const match_result = uri.match(/^spotify:(show|album|track|artist):([0-9a-zA-Z]*)$/);
+    if (match_result === null) {
+        throw new ScriptException("regex error");
+    }
+    const uri_id = match_result[2];
+    if (uri_id === undefined) {
+        throw new ScriptException("regex error");
+    }
+    return uri_id;
+}
 /**
  * Converts seconds to the timestamp format used in WebVTT
  * @param seconds
@@ -411,6 +928,7 @@ function assert_no_fall_through(value, exception_message) {
     return;
 }
 //#endregion
+//#region bad
 function is_premium() {
     return false;
 }
@@ -478,6 +996,7 @@ function get_gid(song_uri_id) {
             c ? null : ee[s >>> 24] + ee[s >>> 16 & 255] + ee[s >>> 8 & 255] + ee[255 & s] + ee[a >>> 24] + ee[a >>> 16 & 255] + ee[a >>> 8 & 255] + ee[255 & a] + ee[r >>> 24] + ee[r >>> 16 & 255] + ee[r >>> 8 & 255] + ee[255 & r] + ee[o >>> 24] + ee[o >>> 16 & 255] + ee[o >>> 8 & 255] + ee[255 & o];
     }(song_uri_id) : song_uri_id;
 }
+//#endregion
 // export statements are removed during build step
 // used for unit testing in SpotifyScript.test.ts
 // export { get_gid, assert_never, log_passthrough };
diff --git a/build/SpotifyScript.js.map b/build/SpotifyScript.js.map
index 33885938a918941a11d10f677c0afc6fd0c64c5e..c6ad143e8663f2ba851c1940515dbe24441df00b 100644
--- a/build/SpotifyScript.js.map
+++ b/build/SpotifyScript.js.map
@@ -1 +1 @@
-{"version":3,"file":"SpotifyScript.js","sourceRoot":"http://localhost:8080/","sources":["SpotifyScript.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"SpotifyScript.js","sourceRoot":"http://localhost:8080/","sources":["SpotifyScript.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/build/SpotifyScript.ts b/build/SpotifyScript.ts
index 318168054e1285ee43bed60b5d9be7fe683a786f..cc3c00b2a9c95d3f618fa38e5307a06744bdf7b4 100644
--- a/build/SpotifyScript.ts
+++ b/build/SpotifyScript.ts
@@ -1,18 +1,34 @@
 //#region constants
 import {
+    type AlbumResponse,
+    type AlbumTracksResponse,
+    type ArtistDetails,
+    type ArtistMetadataResponse,
     type ContentType,
     type EpisodeMetadataResponse,
     type FileManifestResponse,
     type GetLicenseResponse,
+    type LyricsResponse,
+    type PlaylistContent,
+    type PlaylistContentResponse,
+    type PlaylistResponse,
+    type PlaylistType,
+    type PodcastMetadataResponse,
     type Settings,
     type SongMetadataResponse,
-    // type SpotifySource,
     type State,
+    type TrackMetadataResponse,
+    type Tracks,
     type TranscriptResponse,
 } from "./types.js"
 
 const CONTENT_REGEX = /^https:\/\/open\.spotify\.com\/(track|episode)\/([a-zA-Z0-9]*)($|\/)/
+const PLAYLIST_REGEX = /^https:\/\/open\.spotify\.com\/(album|playlist)\/([a-zA-Z0-9]*)($|\/)/
 const SONG_URL_PREFIX = "https://open.spotify.com/track/" as const
+const PODCAST_URL_PREFIX = "https://open.spotify.com/show/" as const
+const ARTIST_URL_PREFIX = "https://open.spotify.com/artist/" as const
+const ALBUM_URL_PREFIX = "https://open.spotify.com/album/" as const
+const QUERY_URL = "https://api-partner.spotify.com/pathfinder/v1/query" as const
 const IMAGE_URL_PREFIX = "https://i.scdn.co/image/" as const
 
 const PLATFORM = "Spotify" as const
@@ -20,7 +36,6 @@ const PLATFORM = "Spotify" as const
 
 const HARDCODED_ZERO = 0 as const
 const HARDCODED_EMPTY_STRING = "" as const
-const EMPTY_AUTHOR = new PlatformAuthorLink(new PlatformID(PLATFORM, "", plugin.config.id), "", "")
 
 const local_http = http
 // const local_utility = utility
@@ -56,9 +71,9 @@ source.getHome = getHome
 source.isContentDetailsUrl = isContentDetailsUrl
 source.getContentDetails = getContentDetails
 
-// source.isPlaylistUrl = isPlaylistUrl
+source.isPlaylistUrl = isPlaylistUrl
 // source.searchPlaylists = searchPlaylists
-// source.getPlaylist = getPlaylist
+source.getPlaylist = getPlaylist
 
 // source.getComments = getComments
 // source.getSubComments = getSubComments
@@ -178,6 +193,15 @@ function enable(conf: SourceConfig, settings: Settings, savedState: string | nul
             license_uri: license_uri
         }
     }
+    if (is_premium()) {
+        local_state = {
+            bearer_token: "BQB5uzdWBsXahudafNcc3RR7kExwq4vpsbkSCOuGkn06aYQ8it6x-M5PmaVi2gapw5NgXMO4tlDSenQcCqv2dQg94a_4fsi11yX5qkAeqW0f_bRNHZ3cg1QlJgX8kKnOmEs5I8jmhY2pR0k8ParLvLZt7tVQYVceei3NJM4w4oKr6thqYyCST-3BHJximVhvT5_cmMrFac5VBWkgioQPxNUSO1U6ICi0hN2W5WMYg8KjdrjCPKfFiYTE3Z9myO0fGI13o1uWzNRrXHc075HuOYvvv_5UbobXPyPVbSEfLqGuaPstmN8Ubj7XV6FXYPnvNSNnJlLx1GwLx2EoyA",
+            license_uri: local_state.license_uri
+        }
+    }
+
+
+
 }
 //#endregion
 
@@ -191,6 +215,46 @@ function saveState() {
 
 //#region home
 function getHome() {
+    const playlists = [
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "the coolest album of all time",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/album/6BzxX6zkDsYKFJ04ziU5xQ",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        }),
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "dayly mix mix 1111",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/playlist/37i9dQZF1E38112qhvV3BT",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        }),
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "tines for two",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/playlist/4ClcTeCoE9aPMhy0CLoD9P",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        })
+    ]
+    return new ContentPager(playlists, false)
+    /*
     const song_uri_id = "6XXxKsu3RJeN3ZvbMYrgQW"
     const song_url = `${SONG_URL_PREFIX}${song_uri_id}`
 
@@ -216,6 +280,7 @@ function getHome() {
         // readonly uploadDate?: number
     })]
     return new VideoPager(songs, false)
+    */
 }
 //#endregion
 
@@ -248,12 +313,65 @@ function getContentDetails(url: string) {
             const song_url = `${SONG_URL_PREFIX}${content_uri_id}`
 
             const { url: metadata_url, headers: metadata_headers } = song_metadata_args(content_uri_id)
-            const song_metadata_response: SongMetadataResponse = JSON.parse(local_http.GET(metadata_url, metadata_headers, false).body)
-            const first_artist = song_metadata_response.artist[0]
+            const { url: track_metadata_url, headers: _track_metadata_headers } = track_metadata_args(content_uri_id)
+            const batch = local_http
+                .batch()
+                .GET(metadata_url, metadata_headers, false)
+                .GET(track_metadata_url, _track_metadata_headers, false)
+            if (is_premium()) {
+                const { url, headers } = lyrics_args(content_uri_id)
+                batch.GET(url, headers, false)
+            }
+            const results = batch
+                .execute()
+            if (results[0] === undefined || results[1] === undefined) {
+                throw new ScriptException("unreachable")
+            }
+            const song_metadata_response: SongMetadataResponse = JSON.parse(results[0].body)
+            const track_metadata_response: TrackMetadataResponse = JSON.parse(results[1].body)
+            const first_artist = track_metadata_response.data.trackUnion.firstArtist.items[0]
             if (first_artist === undefined) {
                 throw new ScriptException("missing artist")
             }
-            const artist_url = "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"
+            const artist_url = `https://open.spotify.com/artist/${first_artist.id}`
+            const highest_quality_artist_cover_art = first_artist.visuals.avatarImage.sources.reduce(function (accumulator, current) {
+                return accumulator.height > current.height ? accumulator : current
+            })
+
+            let subtitles: ISubtitleSource[] = []
+
+            if (results[2] !== undefined) {
+                const lyrics_response: LyricsResponse = JSON.parse(results[2].body)
+                const subtitle_name = function () {
+                    switch (lyrics_response.lyrics.language) {
+                        case "en":
+                            return "English"
+                        default:
+                            throw assert_no_fall_through(lyrics_response.lyrics.language, "unreachable")
+                    }
+                }()
+                const convert = milliseconds_to_WebVTT_timestamp
+                let vtt_text = `WEBVTT ${subtitle_name}\n`
+                vtt_text += "\n"
+                lyrics_response.lyrics.lines.forEach(function (line, index) {
+                    const next = lyrics_response.lyrics.lines[index + 1]
+                    let end = next?.startTimeMs
+                    if (end === undefined) {
+                        end = track_metadata_response.data.trackUnion.duration.totalMilliseconds.toString()
+                    }
+                    vtt_text += `${convert(parseInt(line.startTimeMs))} --> ${convert(parseInt(end))}\n`
+                    vtt_text += `${line.words}\n`
+                    vtt_text += "\n"
+                })
+                subtitles = [{
+                    url: song_url,
+                    name: subtitle_name,
+                    getSubtitles() {
+                        return vtt_text
+                    },
+                    format: "text/vtt",
+                }]
+            }
 
             const format = is_premium() ? "MP4_256" : "MP4_128"
 
@@ -263,20 +381,40 @@ function getContentDetails(url: string) {
             }
 
             const { url, headers } = file_manifest_args(maybe_file_id)
-            const file_manifest: FileManifestResponse = JSON.parse(local_http.GET(url, headers, false).body)
+            const { url: artist_metadata_url, headers: artist_metadata_headers } = artist_metadata_args(first_artist.id)
+            const second_results = local_http
+                .batch()
+                .GET(url, headers, false)
+                .GET(artist_metadata_url, artist_metadata_headers, false)
+                .execute()
+            if (second_results[0] === undefined || second_results[1] === undefined) {
+                throw new ScriptException("unreachable")
+            }
+            const file_manifest: FileManifestResponse = JSON.parse(second_results[0].body)
+            const artist_metadata_response: ArtistMetadataResponse = JSON.parse(second_results[1].body)
 
-            const duration = song_metadata_response.duration / 1000
+            const duration = track_metadata_response.data.trackUnion.duration.totalMilliseconds / 1000
 
             const file_url = file_manifest.cdnurl[0]
             if (file_url === undefined) {
                 throw new ScriptException("unreachable")
             }
+            const codecs = "mp4a.40.2"
             const audio_sources = [new AudioUrlWidevineSource({
                 //audio/mp4; codecs="mp4a.40.2
-                name: format,
-                bitrate: HARDCODED_ZERO,
+                name: codecs,
+                bitrate: function (format: "MP4_128" | "MP4_256") {
+                    switch (format) {
+                        case "MP4_128":
+                            return 128000
+                        case "MP4_256":
+                            return 256000
+                        default:
+                            throw assert_no_fall_through(format, "unreachable")
+                    }
+                }(format),
                 container: "audio/mp4",
-                codecs: "mp4a.40.2",
+                codecs,
                 duration,
                 url: file_url,
                 language: Language.UNKNOWN,
@@ -287,20 +425,26 @@ function getContentDetails(url: string) {
             return new PlatformVideoDetails({
                 id: new PlatformID(PLATFORM, content_uri_id, plugin.config.id),
                 name: song_metadata_response.name,
-                author: new PlatformAuthorLink(new PlatformID(PLATFORM, first_artist.gid, plugin.config.id), first_artist.name, artist_url),
+                author: new PlatformAuthorLink(
+                    new PlatformID(PLATFORM, first_artist.id, plugin.config.id),
+                    first_artist.profile.name,
+                    artist_url,
+                    highest_quality_artist_cover_art.url,
+                    artist_metadata_response.data.artistUnion.stats.monthlyListeners
+                ),
                 url: song_url,
                 thumbnails: new Thumbnails(song_metadata_response.album.cover_group.image.map(function (image) {
                     return new Thumbnail(`${IMAGE_URL_PREFIX}${image.file_id}`, image.height)
                 })),
                 duration,
-                viewCount: HARDCODED_ZERO,
+                viewCount: parseInt(track_metadata_response.data.trackUnion.playcount),
                 isLive: false,
                 shareUrl: song_metadata_response.canonical_uri,
-                // readonly uploadDate?: number
+                datetime: new Date(track_metadata_response.data.trackUnion.albumOfTrack.date.isoString).getTime() / 1000,
                 description: HARDCODED_EMPTY_STRING,
                 video: new UnMuxVideoSourceDescriptor([], audio_sources),
-                rating: new RatingLikes(HARDCODED_ZERO)
-                // readonly subtitles?: ISubtitleSource[]
+                rating: new RatingLikes(HARDCODED_ZERO),
+                subtitles
             })
         }
 
@@ -319,14 +463,60 @@ function getContentDetails(url: string) {
             const transcript_response: TranscriptResponse = JSON.parse(responses[0].body)
             const episode_metadata_response: EpisodeMetadataResponse = JSON.parse(responses[1].body)
 
+            if (episode_metadata_response.data.episodeUnionV2.mediaTypes.length === 2) {
+                function assert_video(_mediaTypes: ["AUDIO", "VIDEO"]) { }
+                assert_video(episode_metadata_response.data.episodeUnionV2.mediaTypes)
+                //TODO since we don't use the transcript we should only load it when audio only podcasts are played
+
+                // TODO handle video podcasts. Grayjay doesn't currently support the websocket functionality necessary
+                // the basic process to get the video play info is
+                // connect to the websocket wss://gue1-dealer.spotify.com/?access_token=<bearer-token> 
+                // register the device https://gue1-spclient.spotify.com/track-playback/v1/devices
+                //      generate the device id using code found in the min js like this
+                /*
+                        web player js
+                        const t = Math.ceil(e / 2);
+                        return function(e) {
+                            let t = "";
+                            for (let n = 0; n < e.length; n++) {
+                                const i = e[n];
+                                i < 16 && (t += "0"),
+                                t += i.toString(16)
+                            }
+                            return t
+                        }(Oe(t))
+                */
+                // load devices info https://gue1-spclient.spotify.com/connect-state/v1/devices/hobs_aced97d86694f14d304dd4e6f1f7f8c3bff
+                // transfer to our device https://gue1-spclient.spotify.com/connect-state/v1/connect/transfer/from/9a7079bd5b5605839c1d9080d0f4368bfcd6d2eb/to/aced97d86694f14d304dd4e6f1f7f8c3bff
+                // signal the play of the given podcast (not quite sure how this works :/)
+                // recieve the video play info via the websocket connection
+                //
+            }
+
             const format = "MP4_128"
             const maybe_file_id = episode_metadata_response.data.episodeUnionV2.audio.items.find(function (file) { return file.format === format })?.fileId
             if (maybe_file_id === undefined) {
                 throw new ScriptException("missing expected format")
             }
 
+            const limited_podcast_metadata = episode_metadata_response.data.episodeUnionV2.podcastV2.data
+            const podcast_uri_id = id_from_uri(limited_podcast_metadata.uri)
+            const highest_quality_cover_art = limited_podcast_metadata.coverArt.sources.reduce(function (accumulator, current) {
+                return accumulator.height > current.height ? accumulator : current
+            })
+
             const { url: manifest_url, headers: manifest_headers } = file_manifest_args(maybe_file_id)
-            const file_manifest: FileManifestResponse = JSON.parse(local_http.GET(manifest_url, manifest_headers, false).body)
+            const { url: podcast_metadata_url, headers: podcast_metadata_headers } = podcast_metadata_args(podcast_uri_id)
+            const results = local_http
+                .batch()
+                .GET(podcast_metadata_url, podcast_metadata_headers, false)
+                .GET(manifest_url, manifest_headers, false)
+                .execute()
+            if (results[0] === undefined || results[1] === undefined) {
+                throw new ScriptException("unreachable")
+            }
+            const full_podcast_metadata: PodcastMetadataResponse = JSON.parse(results[0].body)
+            const file_manifest: FileManifestResponse = JSON.parse(results[1].body)
 
             const duration = episode_metadata_response.data.episodeUnionV2.duration.totalMilliseconds / 1000
 
@@ -376,7 +566,12 @@ function getContentDetails(url: string) {
             return new PlatformVideoDetails({
                 id: new PlatformID(PLATFORM, content_uri_id, plugin.config.id),
                 name: episode_metadata_response.data.episodeUnionV2.name,
-                author: EMPTY_AUTHOR,
+                author: new PlatformAuthorLink(
+                    new PlatformID(PLATFORM, podcast_uri_id, plugin.config.id),
+                    limited_podcast_metadata.name,
+                    `${PODCAST_URL_PREFIX}${podcast_uri_id}`,
+                    highest_quality_cover_art.url,
+                ),
                 url: episode_url,
                 thumbnails: new Thumbnails(episode_metadata_response.data.episodeUnionV2.coverArt.sources.map(function (image) {
                     return new Thumbnail(image.url, image.height)
@@ -385,10 +580,10 @@ function getContentDetails(url: string) {
                 viewCount: HARDCODED_ZERO,
                 isLive: false,
                 shareUrl: episode_metadata_response.data.episodeUnionV2.uri,
-                uploadDate: new Date(episode_metadata_response.data.episodeUnionV2.releaseDate.isoString).getTime() / 1000,
+                datetime: new Date(episode_metadata_response.data.episodeUnionV2.releaseDate.isoString).getTime() / 1000,
                 description: episode_metadata_response.data.episodeUnionV2.htmlDescription,
                 video: new UnMuxVideoSourceDescriptor([], audio_sources),
-                rating: new RatingLikes(HARDCODED_ZERO),
+                rating: new RatingScaler(full_podcast_metadata.data.podcastUnionV2.rating.averageRating.average),
                 subtitles: [{
                     url: episode_url,
                     name: subtitle_name,
@@ -405,6 +600,23 @@ function getContentDetails(url: string) {
     }
 }
 
+function podcast_metadata_args(podcast_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:show:${podcast_uri_id}`
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "5fb034a236a3e8301e9eca0e23def3341ed66c891ea2d4fea374c091dc4b4a6a"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "queryShowMetadataV2")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+
 function transcript_args(episode_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
     const transcript_url_prefix = "https://spclient.wg.spotify.com/transcript-read-along/v2/episode/"
     const url = new URL(`${transcript_url_prefix}${episode_uri_id}`)
@@ -415,6 +627,24 @@ function transcript_args(episode_uri_id: string): { readonly url: string, readon
     }
 }
 
+function lyrics_args(song_uri_id: string): {
+    readonly url: string, readonly headers: {
+        Authorization: string,
+        Accept: string,
+        "app-platform": "WebPlayer"
+    }
+} {
+    const url = new URL(`https://spclient.wg.spotify.com/color-lyrics/v2/track/${song_uri_id}`)
+    return {
+        url: url.toString(),
+        headers: {
+            Accept: "application/json",
+            "app-platform": "WebPlayer",
+            Authorization: `Bearer ${local_state.bearer_token}`
+        }
+    }
+}
+
 function file_manifest_args(file_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
     const file_manifest_url_prefix = "https://gue1-spclient.spotify.com/storage-resolve/v2/files/audio/interactive/10/"
     const file_manifest_params = "?product=9&alt=json"
@@ -425,7 +655,6 @@ function file_manifest_args(file_id: string): { readonly url: string, readonly h
 }
 
 function episode_metadata_args(episode_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
-    const episode_metadata_url_prefix = "https://api-partner.spotify.com/pathfinder/v1/query"
     const variables = JSON.stringify({
         uri: `spotify:episode:${episode_uri_id}`
     })
@@ -435,13 +664,30 @@ function episode_metadata_args(episode_uri_id: string): { readonly url: string,
             sha256Hash: "9697538fe993af785c10725a40bb9265a20b998ccd2383bd6f586e01303824e9"
         }
     })
-    const url = new URL(episode_metadata_url_prefix)
+    const url = new URL(QUERY_URL)
     url.searchParams.set("operationName", "getEpisodeOrChapter")
     url.searchParams.set("variables", variables)
     url.searchParams.set("extensions", extensions)
     return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
 }
 
+function track_metadata_args(song_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:track:${song_uri_id}`
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "ae85b52abb74d20a4c331d4143d4772c95f34757bfa8c625474b912b9055b5c0"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "getTrack")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+
 function song_metadata_args(song_uri_id: string): {
     readonly url: string,
     readonly headers: {
@@ -458,9 +704,354 @@ function song_metadata_args(song_uri_id: string): {
         }
     }
 }
+
+function artist_metadata_args(artist_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:artist:${artist_uri_id}`,
+        locale: "",
+        includePrerelease: true
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "da986392124383827dc03cbb3d66c1de81225244b6e20f8d78f9f802cc43df6e"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "queryArtistOverview")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+//#endregion
+
+//#region playlists
+// https://open.spotify.com/album/6BzxX6zkDsYKFJ04ziU5xQ
+// https://open.spotify.com/playlist/37i9dQZF1E38112qhvV3BT
+function isPlaylistUrl(url: string): boolean {
+    return PLAYLIST_REGEX.test(url)
+}
+function getPlaylist(url: string): PlatformPlaylistDetails {
+    const match_result = url.match(PLAYLIST_REGEX)
+    if (match_result === null) {
+        throw new ScriptException("regex error")
+    }
+    const maybe_playlist_type = match_result[1]
+    if (maybe_playlist_type === undefined) {
+        throw new ScriptException("regex error")
+    }
+    const playlist_type: PlaylistType = maybe_playlist_type as PlaylistType
+    const playlist_uri_id = match_result[2]
+    if (playlist_uri_id === undefined) {
+        throw new ScriptException("regex error")
+    }
+    switch (playlist_type) {
+        case "album": {
+            // if the author is the same as the album then include the artist pick otherwise nothing
+            // TODO we could load in extra info for all the other artists but it might be hard to do that in a request efficient way
+
+            const pagination_limit = 50 as const
+            const offset = 0
+
+            const { url, headers } = album_metadata_args(playlist_uri_id, offset, pagination_limit)
+            const album_metadata_response: AlbumResponse = JSON.parse(local_http.GET(url, headers, false).body)
+            const album_artist = album_metadata_response.data.albumUnion.artists.items[0]
+            if (album_artist === undefined) {
+                throw new ScriptException("missing album artist")
+            }
+            const unix_time = new Date(album_metadata_response.data.albumUnion.date.isoString).getTime() / 1000
+
+            return new PlatformPlaylistDetails({
+                id: new PlatformID(PLATFORM, playlist_uri_id, plugin.config.id),
+                name: album_metadata_response.data.albumUnion.name,
+                author: new PlatformAuthorLink(
+                    new PlatformID(PLATFORM, album_artist.id, plugin.config.id),
+                    album_artist.profile.name,
+                    `${ARTIST_URL_PREFIX}${album_artist.id}`,
+                    album_artist.visuals.avatarImage.sources[album_artist.visuals.avatarImage.sources.length - 1]?.url
+                ),
+                datetime: unix_time,
+                url: `${ALBUM_URL_PREFIX}${playlist_uri_id}`,
+                videoCount: album_metadata_response.data.albumUnion.tracks.totalCount,
+                contents: new AlbumPager(playlist_uri_id, offset, pagination_limit, album_metadata_response, album_artist, unix_time)
+            })
+        }
+        case "playlist": {
+            if (!bridge.isLoggedIn()) {
+                throw new LoginRequiredException("login to open playlists")
+            }
+            const pagination_limit = 25 as const
+            const offset = 0
+
+            const { url, headers } = fetch_playlist_args(playlist_uri_id, offset, pagination_limit)
+            const playlist_response: PlaylistResponse = JSON.parse(local_http.GET(url, headers, false).body)
+            const owner = playlist_response.data.playlistV2.ownerV2.data
+
+            return new PlatformPlaylistDetails({
+                id: new PlatformID(PLATFORM, playlist_uri_id, plugin.config.id),
+                name: playlist_response.data.playlistV2.name,
+                author: new PlatformAuthorLink(
+                    new PlatformID(PLATFORM, owner.username, plugin.config.id),
+                    owner.name,
+                    `${ARTIST_URL_PREFIX}${owner.username}`,
+                    owner.avatar?.sources[owner.avatar.sources.length - 1]?.url
+                ),
+                url: `${ALBUM_URL_PREFIX}${playlist_uri_id}`,
+                videoCount: playlist_response.data.playlistV2.content.totalCount,
+                contents: new SpotifyPlaylistPager(playlist_uri_id, offset, pagination_limit, playlist_response)
+            })
+        }
+        default: {
+            throw assert_no_fall_through(playlist_type, "unreachable")
+        }
+    }
+}
+class SpotifyPlaylistPager extends VideoPager {
+    private offset: number
+    private readonly total_tracks: number
+    constructor(
+        private readonly playlist_uri_id: string,
+        offset: number,
+        private readonly pagination_limit: number,
+        playlist_response: PlaylistResponse
+    ) {
+        const total_tracks = playlist_response.data.playlistV2.content.totalCount
+
+        const songs = format_playlist_tracks(playlist_response.data.playlistV2.content)
+
+        super(songs, total_tracks > offset + pagination_limit)
+        this.offset = offset + pagination_limit
+        this.total_tracks = total_tracks
+    }
+    override nextPage(this: SpotifyPlaylistPager): SpotifyPlaylistPager {
+        const { url, headers } = fetch_playlist_contents_args(this.playlist_uri_id, this.offset, this.pagination_limit)
+        const playlist_content_response: PlaylistContentResponse = JSON.parse(local_http.GET(url, headers, false).body)
+
+        const songs = format_playlist_tracks(playlist_content_response.data.playlistV2.content)
+        this.results = songs
+        this.hasMore = this.total_tracks > this.offset + this.pagination_limit
+        this.offset += this.pagination_limit
+        return this
+    }
+    override hasMorePagers(this: SpotifyPlaylistPager): boolean {
+        return this.hasMore
+    }
+}
+function format_playlist_tracks(content: PlaylistContent) {
+    return content.items.map(function (playlist_track_metadata) {
+        const song = playlist_track_metadata.itemV2.data
+        const track_uri_id = id_from_uri(song.uri)
+        const artist = song.artists.items[0]
+        if (artist === undefined) {
+            throw new ScriptException("missing artist")
+        }
+        const url = `${SONG_URL_PREFIX}${track_uri_id}`
+        return new PlatformVideo({
+            id: new PlatformID(PLATFORM, track_uri_id, plugin.config.id),
+            name: song.name,
+            author: new PlatformAuthorLink(
+                new PlatformID(PLATFORM, id_from_uri(artist.uri), plugin.config.id),
+                artist.profile.name,
+                `${ARTIST_URL_PREFIX}${id_from_uri(artist.uri)}`
+                // TODO figure out a way to get the artist thumbnail
+            ),
+            url,
+            thumbnails: new Thumbnails(song.albumOfTrack.coverArt.sources.map(function (source) {
+                return new Thumbnail(source.url, source.height)
+            })),
+            duration: song.trackDuration.totalMilliseconds / 1000,
+            viewCount: parseInt(song.playcount),
+            isLive: false,
+            shareUrl: url,
+            datetime: new Date(playlist_track_metadata.addedAt.isoString).getTime() / 1000
+        })
+    })
+}
+/**
+ * 
+ * @param playlist_uri_id 
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns 
+ */
+function fetch_playlist_contents_args(playlist_uri_id: string, offset: number, limit: number): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:playlist:${playlist_uri_id}`,
+        offset: offset,
+        limit: limit
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "91d4c2bc3e0cd1bc672281c4f1f59f43ff55ba726ca04a45810d99bd091f3f0e"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "fetchPlaylistContents")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+/**
+ * 
+ * @param playlist_uri_id 
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns 
+ */
+function fetch_playlist_args(playlist_uri_id: string, offset: number, limit: number): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:playlist:${playlist_uri_id}`,
+        offset: offset,
+        limit: limit
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "91d4c2bc3e0cd1bc672281c4f1f59f43ff55ba726ca04a45810d99bd091f3f0e"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "fetchPlaylist")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+class AlbumPager extends VideoPager {
+    private offset: number
+    private readonly thumbnails: Thumbnails
+    private readonly album_artist: ArtistDetails
+    private readonly unix_time: number
+    private readonly total_tracks: number
+    constructor(
+        private readonly album_uri_id: string,
+        offset: number,
+        private readonly pagination_limit: number,
+        album_metadata_response: AlbumResponse,
+        album_artist: ArtistDetails,
+        unix_time: number,
+    ) {
+        const total_tracks = album_metadata_response.data.albumUnion.tracks.totalCount
+        const thumbnails = new Thumbnails(album_metadata_response.data.albumUnion.coverArt.sources.map(function (source) {
+            return new Thumbnail(source.url, source.height)
+        }))
+
+        const songs = format_album_tracks(album_metadata_response.data.albumUnion.tracks, thumbnails, album_artist, unix_time)
+
+        super(songs, total_tracks > offset + pagination_limit)
+        this.offset = offset + pagination_limit
+        this.thumbnails = thumbnails
+        this.album_artist = album_artist
+        this.unix_time = unix_time
+        this.total_tracks = total_tracks
+    }
+    override nextPage(this: AlbumPager): AlbumPager {
+        const { url, headers } = album_tracks_args(this.album_uri_id, this.offset, this.pagination_limit)
+        const album_tracks_response: AlbumTracksResponse = JSON.parse(local_http.GET(url, headers, false).body)
+
+        const songs = format_album_tracks(album_tracks_response.data.albumUnion.tracks, this.thumbnails, this.album_artist, this.unix_time)
+        this.results = songs
+        this.hasMore = this.total_tracks > this.offset + this.pagination_limit
+        this.offset += this.pagination_limit
+        return this
+    }
+    override hasMorePagers(this: AlbumPager): boolean {
+        return this.hasMore
+    }
+}
+function format_album_tracks(tracks: Tracks, thumbnails: Thumbnails, album_artist: ArtistDetails, unix_time: number) {
+    return tracks.items.map(function (track) {
+        const track_uri_id = id_from_uri(track.track.uri)
+        const artist = track.track.artists.items[0]
+        if (artist === undefined) {
+            throw new ScriptException("missing artist")
+        }
+        const url = `${SONG_URL_PREFIX}${track_uri_id}`
+        return new PlatformVideo({
+            id: new PlatformID(PLATFORM, track_uri_id, plugin.config.id),
+            name: track.track.name,
+            author: new PlatformAuthorLink(
+                new PlatformID(PLATFORM, id_from_uri(artist.uri), plugin.config.id),
+                artist.profile.name,
+                `${ARTIST_URL_PREFIX}${id_from_uri(artist.uri)}`,
+                id_from_uri(artist.uri) === album_artist.id ? album_artist.visuals.avatarImage.sources[album_artist.visuals.avatarImage.sources.length - 1]?.url : undefined
+            ),
+            url,
+            thumbnails,
+            duration: track.track.duration.totalMilliseconds / 1000,
+            viewCount: parseInt(track.track.playcount),
+            isLive: false,
+            shareUrl: url,
+            datetime: unix_time
+        })
+    })
+}
+/**
+ * 
+ * @param album_uri_id 
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns 
+ */
+function album_tracks_args(album_uri_id: string, offset: number, limit: number): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:album:${album_uri_id}`,
+        offset: offset,
+        limit: limit
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "469874edcad37b7a379d4f22f0083a49ea3d6ae097916120d9bbe3e36ca79e9d"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "queryAlbumTracks")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+/**
+ * 
+ * @param album_uri_id 
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns 
+ */
+function album_metadata_args(album_uri_id: string, offset: number, limit: number): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:album:${album_uri_id}`,
+        locale: "",
+        offset: offset,
+        limit: limit
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "469874edcad37b7a379d4f22f0083a49ea3d6ae097916120d9bbe3e36ca79e9d"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "getAlbum")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
 //#endregion
 
 //#region utilities
+function id_from_uri(uri: string): string {
+    const match_result = uri.match(/^spotify:(show|album|track|artist):([0-9a-zA-Z]*)$/)
+    if (match_result === null) {
+        throw new ScriptException("regex error")
+    }
+    const uri_id = match_result[2]
+    if (uri_id === undefined) {
+        throw new ScriptException("regex error")
+    }
+    return uri_id
+}
+
 /**
  * Converts seconds to the timestamp format used in WebVTT
  * @param seconds 
@@ -489,6 +1080,7 @@ function assert_no_fall_through(value: never, exception_message?: string): Scrip
 }
 //#endregion
 
+//#region bad
 function is_premium(): boolean {
     return false
 }
@@ -561,6 +1153,7 @@ function get_gid(song_uri_id: string) {
             c ? null : ee[s >>> 24] + ee[s >>> 16 & 255] + ee[s >>> 8 & 255] + ee[255 & s] + ee[a >>> 24] + ee[a >>> 16 & 255] + ee[a >>> 8 & 255] + ee[255 & a] + ee[r >>> 24] + ee[r >>> 16 & 255] + ee[r >>> 8 & 255] + ee[255 & r] + ee[o >>> 24] + ee[o >>> 16 & 255] + ee[o >>> 8 & 255] + ee[255 & o]
     }(song_uri_id) : song_uri_id
 }
+//#endregion
 
 // export statements are removed during build step
 // used for unit testing in SpotifyScript.test.ts
diff --git a/package-lock.json b/package-lock.json
index 84fd98a119a942d5f0cea36d5f0e498487146c82..7971c7b5310ca6302af0ce227476052bc019dcd6 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,7 @@
             "version": "1.0.0",
             "license": "MPL-2.0",
             "devDependencies": {
-                "@grayjay/plugin": "gitlab:kaidelorenzo/grayjay-plugin-types#c9fcd5e27dd6f04b19e5ac0e03ee46a71f699dfc",
+                "@grayjay/plugin": "gitlab:kaidelorenzo/grayjay-plugin-types#bb6dbb7f72e6e992180f63ad6442a61c9fee2cd9",
                 "@types/node": "^20.12.7",
                 "http-server": "^14.1.1",
                 "npm-check-updates": "^16.14.20"
@@ -39,8 +39,8 @@
         },
         "node_modules/@grayjay/plugin": {
             "version": "1.0.0",
-            "resolved": "git+ssh://git@gitlab.com/kaidelorenzo/grayjay-plugin-types.git#c9fcd5e27dd6f04b19e5ac0e03ee46a71f699dfc",
-            "integrity": "sha512-wvYWXupDyr4kKlTCJbuAuEvi48gRx/RpX6j31m4KeRukCBdV/QuHeS3L+GRVRXIU/ltBraEiNLo2CiMIxY2qiw==",
+            "resolved": "git+ssh://git@gitlab.com/kaidelorenzo/grayjay-plugin-types.git#bb6dbb7f72e6e992180f63ad6442a61c9fee2cd9",
+            "integrity": "sha512-mEzQEm6QhmNSv4Pr0FRl9m2kTygr9MrTNjGYFJyeSlCN3HFbv2+9MSXwrtFhHAaIbxphbOs6pJtIA2VIFg3lJg==",
             "dev": true,
             "dependencies": {
                 "@types/sync-fetch": "^0.4.3",
@@ -499,9 +499,9 @@
             "dev": true
         },
         "node_modules/@types/node": {
-            "version": "20.12.7",
-            "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.7.tgz",
-            "integrity": "sha512-wq0cICSkRLVaf3UGLMGItu/PtdY7oaXaI/RVU+xliKVOtRna3PRY57ZDfztpDL0n11vfymMUnXv8QwYCO7L1wg==",
+            "version": "20.12.8",
+            "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.8.tgz",
+            "integrity": "sha512-NU0rJLJnshZWdE/097cdCBbyW1h4hEg0xpovcoAQYHl8dnEyp/NAOiE45pvc+Bd1Dt+2r94v2eGFpQJ4R7g+2w==",
             "dev": true,
             "dependencies": {
                 "undici-types": "~5.26.4"
diff --git a/package.json b/package.json
index f1e2f1aebba9f24386a20317dab677c496b3971e..7003e963e0ee791b0f20f824d1f562dfb969f2f9 100644
--- a/package.json
+++ b/package.json
@@ -34,7 +34,7 @@
     },
     "type": "module",
     "devDependencies": {
-        "@grayjay/plugin": "gitlab:kaidelorenzo/grayjay-plugin-types#c9fcd5e27dd6f04b19e5ac0e03ee46a71f699dfc",
+        "@grayjay/plugin": "gitlab:kaidelorenzo/grayjay-plugin-types#bb6dbb7f72e6e992180f63ad6442a61c9fee2cd9",
         "@types/node": "^20.12.7",
         "http-server": "^14.1.1",
         "npm-check-updates": "^16.14.20"
diff --git a/src/SpotifyScript.ts b/src/SpotifyScript.ts
index 6fb3db001b3dbd6625e39d4ba79a13f2e7b68517..c70798e8c0660afe31dbd74f3f04bfccf663f6b6 100644
--- a/src/SpotifyScript.ts
+++ b/src/SpotifyScript.ts
@@ -1,18 +1,34 @@
 //#region constants
 import {
+    type AlbumResponse,
+    type AlbumTracksResponse,
+    type ArtistDetails,
+    type ArtistMetadataResponse,
     type ContentType,
     type EpisodeMetadataResponse,
     type FileManifestResponse,
     type GetLicenseResponse,
+    type LyricsResponse,
+    type PlaylistContent,
+    type PlaylistContentResponse,
+    type PlaylistResponse,
+    type PlaylistType,
+    type PodcastMetadataResponse,
     type Settings,
     type SongMetadataResponse,
-    // type SpotifySource,
     type State,
+    type TrackMetadataResponse,
+    type Tracks,
     type TranscriptResponse,
 } from "./types.js"
 
 const CONTENT_REGEX = /^https:\/\/open\.spotify\.com\/(track|episode)\/([a-zA-Z0-9]*)($|\/)/
+const PLAYLIST_REGEX = /^https:\/\/open\.spotify\.com\/(album|playlist)\/([a-zA-Z0-9]*)($|\/)/
 const SONG_URL_PREFIX = "https://open.spotify.com/track/" as const
+const PODCAST_URL_PREFIX = "https://open.spotify.com/show/" as const
+const ARTIST_URL_PREFIX = "https://open.spotify.com/artist/" as const
+const ALBUM_URL_PREFIX = "https://open.spotify.com/album/" as const
+const QUERY_URL = "https://api-partner.spotify.com/pathfinder/v1/query" as const
 const IMAGE_URL_PREFIX = "https://i.scdn.co/image/" as const
 
 const PLATFORM = "Spotify" as const
@@ -20,7 +36,6 @@ const PLATFORM = "Spotify" as const
 
 const HARDCODED_ZERO = 0 as const
 const HARDCODED_EMPTY_STRING = "" as const
-const EMPTY_AUTHOR = new PlatformAuthorLink(new PlatformID(PLATFORM, "", plugin.config.id), "", "")
 
 const local_http = http
 // const local_utility = utility
@@ -56,9 +71,9 @@ source.getHome = getHome
 source.isContentDetailsUrl = isContentDetailsUrl
 source.getContentDetails = getContentDetails
 
-// source.isPlaylistUrl = isPlaylistUrl
+source.isPlaylistUrl = isPlaylistUrl
 // source.searchPlaylists = searchPlaylists
-// source.getPlaylist = getPlaylist
+source.getPlaylist = getPlaylist
 
 // source.getComments = getComments
 // source.getSubComments = getSubComments
@@ -178,6 +193,15 @@ function enable(conf: SourceConfig, settings: Settings, savedState: string | nul
             license_uri: license_uri
         }
     }
+    if (is_premium()) {
+        local_state = {
+            bearer_token: "BQB5uzdWBsXahudafNcc3RR7kExwq4vpsbkSCOuGkn06aYQ8it6x-M5PmaVi2gapw5NgXMO4tlDSenQcCqv2dQg94a_4fsi11yX5qkAeqW0f_bRNHZ3cg1QlJgX8kKnOmEs5I8jmhY2pR0k8ParLvLZt7tVQYVceei3NJM4w4oKr6thqYyCST-3BHJximVhvT5_cmMrFac5VBWkgioQPxNUSO1U6ICi0hN2W5WMYg8KjdrjCPKfFiYTE3Z9myO0fGI13o1uWzNRrXHc075HuOYvvv_5UbobXPyPVbSEfLqGuaPstmN8Ubj7XV6FXYPnvNSNnJlLx1GwLx2EoyA",
+            license_uri: local_state.license_uri
+        }
+    }
+
+
+
 }
 //#endregion
 
@@ -191,6 +215,46 @@ function saveState() {
 
 //#region home
 function getHome() {
+    const playlists = [
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "the coolest album of all time",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/album/6BzxX6zkDsYKFJ04ziU5xQ",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        }),
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "dayly mix mix 1111",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/playlist/37i9dQZF1E38112qhvV3BT",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        }),
+        new PlatformPlaylist({
+            id: new PlatformID(PLATFORM, "an album id", plugin.config.id),
+            name: "tines for two",
+            // thumbnails: new Thumbnails([new Thumbnail("https://i.scdn.co/image/ab6765630000ba8a95af9fcb5c610d710793568a", 11)]),
+            author: new PlatformAuthorLink(new PlatformID(PLATFORM, "an artist id", plugin.config.id), "beyonce", "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"),
+            // datetime: 1714580179,
+            datetime: 1714580179,
+            url: "https://open.spotify.com/playlist/4ClcTeCoE9aPMhy0CLoD9P",
+            videoCount: 11,
+            /** Only usable for IPlatformPlaylistDef not IPlatformPlaylistDetailsDef */
+            thumbnail: "https://i.scdn.co/image/ab6765630000ba8ab3d3d2577970462809eb1145"
+        })
+    ]
+    return new ContentPager(playlists, false)
+    /*
     const song_uri_id = "6XXxKsu3RJeN3ZvbMYrgQW"
     const song_url = `${SONG_URL_PREFIX}${song_uri_id}`
 
@@ -216,6 +280,7 @@ function getHome() {
         // readonly uploadDate?: number
     })]
     return new VideoPager(songs, false)
+    */
 }
 //#endregion
 
@@ -248,12 +313,65 @@ function getContentDetails(url: string) {
             const song_url = `${SONG_URL_PREFIX}${content_uri_id}`
 
             const { url: metadata_url, headers: metadata_headers } = song_metadata_args(content_uri_id)
-            const song_metadata_response: SongMetadataResponse = JSON.parse(local_http.GET(metadata_url, metadata_headers, false).body)
-            const first_artist = song_metadata_response.artist[0]
+            const { url: track_metadata_url, headers: _track_metadata_headers } = track_metadata_args(content_uri_id)
+            const batch = local_http
+                .batch()
+                .GET(metadata_url, metadata_headers, false)
+                .GET(track_metadata_url, _track_metadata_headers, false)
+            if (is_premium()) {
+                const { url, headers } = lyrics_args(content_uri_id)
+                batch.GET(url, headers, false)
+            }
+            const results = batch
+                .execute()
+            if (results[0] === undefined || results[1] === undefined) {
+                throw new ScriptException("unreachable")
+            }
+            const song_metadata_response: SongMetadataResponse = JSON.parse(results[0].body)
+            const track_metadata_response: TrackMetadataResponse = JSON.parse(results[1].body)
+            const first_artist = track_metadata_response.data.trackUnion.firstArtist.items[0]
             if (first_artist === undefined) {
                 throw new ScriptException("missing artist")
             }
-            const artist_url = "https://open.spotify.com/artist/6vWDO969PvNqNYHIOW5v0m"
+            const artist_url = `https://open.spotify.com/artist/${first_artist.id}`
+            const highest_quality_artist_cover_art = first_artist.visuals.avatarImage.sources.reduce(function (accumulator, current) {
+                return accumulator.height > current.height ? accumulator : current
+            })
+
+            let subtitles: ISubtitleSource[] = []
+
+            if (results[2] !== undefined) {
+                const lyrics_response: LyricsResponse = JSON.parse(results[2].body)
+                const subtitle_name = function () {
+                    switch (lyrics_response.lyrics.language) {
+                        case "en":
+                            return "English"
+                        default:
+                            throw assert_no_fall_through(lyrics_response.lyrics.language, "unreachable")
+                    }
+                }()
+                const convert = milliseconds_to_WebVTT_timestamp
+                let vtt_text = `WEBVTT ${subtitle_name}\n`
+                vtt_text += "\n"
+                lyrics_response.lyrics.lines.forEach(function (line, index) {
+                    const next = lyrics_response.lyrics.lines[index + 1]
+                    let end = next?.startTimeMs
+                    if (end === undefined) {
+                        end = track_metadata_response.data.trackUnion.duration.totalMilliseconds.toString()
+                    }
+                    vtt_text += `${convert(parseInt(line.startTimeMs))} --> ${convert(parseInt(end))}\n`
+                    vtt_text += `${line.words}\n`
+                    vtt_text += "\n"
+                })
+                subtitles = [{
+                    url: song_url,
+                    name: subtitle_name,
+                    getSubtitles() {
+                        return vtt_text
+                    },
+                    format: "text/vtt",
+                }]
+            }
 
             const format = is_premium() ? "MP4_256" : "MP4_128"
 
@@ -263,20 +381,40 @@ function getContentDetails(url: string) {
             }
 
             const { url, headers } = file_manifest_args(maybe_file_id)
-            const file_manifest: FileManifestResponse = JSON.parse(local_http.GET(url, headers, false).body)
+            const { url: artist_metadata_url, headers: artist_metadata_headers } = artist_metadata_args(first_artist.id)
+            const second_results = local_http
+                .batch()
+                .GET(url, headers, false)
+                .GET(artist_metadata_url, artist_metadata_headers, false)
+                .execute()
+            if (second_results[0] === undefined || second_results[1] === undefined) {
+                throw new ScriptException("unreachable")
+            }
+            const file_manifest: FileManifestResponse = JSON.parse(second_results[0].body)
+            const artist_metadata_response: ArtistMetadataResponse = JSON.parse(second_results[1].body)
 
-            const duration = song_metadata_response.duration / 1000
+            const duration = track_metadata_response.data.trackUnion.duration.totalMilliseconds / 1000
 
             const file_url = file_manifest.cdnurl[0]
             if (file_url === undefined) {
                 throw new ScriptException("unreachable")
             }
+            const codecs = "mp4a.40.2"
             const audio_sources = [new AudioUrlWidevineSource({
                 //audio/mp4; codecs="mp4a.40.2
-                name: format,
-                bitrate: HARDCODED_ZERO,
+                name: codecs,
+                bitrate: function (format: "MP4_128" | "MP4_256") {
+                    switch (format) {
+                        case "MP4_128":
+                            return 128000
+                        case "MP4_256":
+                            return 256000
+                        default:
+                            throw assert_no_fall_through(format, "unreachable")
+                    }
+                }(format),
                 container: "audio/mp4",
-                codecs: "mp4a.40.2",
+                codecs,
                 duration,
                 url: file_url,
                 language: Language.UNKNOWN,
@@ -287,20 +425,26 @@ function getContentDetails(url: string) {
             return new PlatformVideoDetails({
                 id: new PlatformID(PLATFORM, content_uri_id, plugin.config.id),
                 name: song_metadata_response.name,
-                author: new PlatformAuthorLink(new PlatformID(PLATFORM, first_artist.gid, plugin.config.id), first_artist.name, artist_url),
+                author: new PlatformAuthorLink(
+                    new PlatformID(PLATFORM, first_artist.id, plugin.config.id),
+                    first_artist.profile.name,
+                    artist_url,
+                    highest_quality_artist_cover_art.url,
+                    artist_metadata_response.data.artistUnion.stats.monthlyListeners
+                ),
                 url: song_url,
                 thumbnails: new Thumbnails(song_metadata_response.album.cover_group.image.map(function (image) {
                     return new Thumbnail(`${IMAGE_URL_PREFIX}${image.file_id}`, image.height)
                 })),
                 duration,
-                viewCount: HARDCODED_ZERO,
+                viewCount: parseInt(track_metadata_response.data.trackUnion.playcount),
                 isLive: false,
                 shareUrl: song_metadata_response.canonical_uri,
-                // readonly uploadDate?: number
+                datetime: new Date(track_metadata_response.data.trackUnion.albumOfTrack.date.isoString).getTime() / 1000,
                 description: HARDCODED_EMPTY_STRING,
                 video: new UnMuxVideoSourceDescriptor([], audio_sources),
-                rating: new RatingLikes(HARDCODED_ZERO)
-                // readonly subtitles?: ISubtitleSource[]
+                rating: new RatingLikes(HARDCODED_ZERO),
+                subtitles
             })
         }
 
@@ -319,14 +463,60 @@ function getContentDetails(url: string) {
             const transcript_response: TranscriptResponse = JSON.parse(responses[0].body)
             const episode_metadata_response: EpisodeMetadataResponse = JSON.parse(responses[1].body)
 
+            if (episode_metadata_response.data.episodeUnionV2.mediaTypes.length === 2) {
+                function assert_video(_mediaTypes: ["AUDIO", "VIDEO"]) { }
+                assert_video(episode_metadata_response.data.episodeUnionV2.mediaTypes)
+                //TODO since we don't use the transcript we should only load it when audio only podcasts are played
+
+                // TODO handle video podcasts. Grayjay doesn't currently support the websocket functionality necessary
+                // the basic process to get the video play info is
+                // connect to the websocket wss://gue1-dealer.spotify.com/?access_token=<bearer-token> 
+                // register the device https://gue1-spclient.spotify.com/track-playback/v1/devices
+                //      generate the device id using code found in the min js like this
+                /*
+                        web player js
+                        const t = Math.ceil(e / 2);
+                        return function(e) {
+                            let t = "";
+                            for (let n = 0; n < e.length; n++) {
+                                const i = e[n];
+                                i < 16 && (t += "0"),
+                                t += i.toString(16)
+                            }
+                            return t
+                        }(Oe(t))
+                */
+                // load devices info https://gue1-spclient.spotify.com/connect-state/v1/devices/hobs_aced97d86694f14d304dd4e6f1f7f8c3bff
+                // transfer to our device https://gue1-spclient.spotify.com/connect-state/v1/connect/transfer/from/9a7079bd5b5605839c1d9080d0f4368bfcd6d2eb/to/aced97d86694f14d304dd4e6f1f7f8c3bff
+                // signal the play of the given podcast (not quite sure how this works :/)
+                // recieve the video play info via the websocket connection
+                //
+            }
+
             const format = "MP4_128"
             const maybe_file_id = episode_metadata_response.data.episodeUnionV2.audio.items.find(function (file) { return file.format === format })?.fileId
             if (maybe_file_id === undefined) {
                 throw new ScriptException("missing expected format")
             }
 
+            const limited_podcast_metadata = episode_metadata_response.data.episodeUnionV2.podcastV2.data
+            const podcast_uri_id = id_from_uri(limited_podcast_metadata.uri)
+            const highest_quality_cover_art = limited_podcast_metadata.coverArt.sources.reduce(function (accumulator, current) {
+                return accumulator.height > current.height ? accumulator : current
+            })
+
             const { url: manifest_url, headers: manifest_headers } = file_manifest_args(maybe_file_id)
-            const file_manifest: FileManifestResponse = JSON.parse(local_http.GET(manifest_url, manifest_headers, false).body)
+            const { url: podcast_metadata_url, headers: podcast_metadata_headers } = podcast_metadata_args(podcast_uri_id)
+            const results = local_http
+                .batch()
+                .GET(podcast_metadata_url, podcast_metadata_headers, false)
+                .GET(manifest_url, manifest_headers, false)
+                .execute()
+            if (results[0] === undefined || results[1] === undefined) {
+                throw new ScriptException("unreachable")
+            }
+            const full_podcast_metadata: PodcastMetadataResponse = JSON.parse(results[0].body)
+            const file_manifest: FileManifestResponse = JSON.parse(results[1].body)
 
             const duration = episode_metadata_response.data.episodeUnionV2.duration.totalMilliseconds / 1000
 
@@ -376,7 +566,12 @@ function getContentDetails(url: string) {
             return new PlatformVideoDetails({
                 id: new PlatformID(PLATFORM, content_uri_id, plugin.config.id),
                 name: episode_metadata_response.data.episodeUnionV2.name,
-                author: EMPTY_AUTHOR,
+                author: new PlatformAuthorLink(
+                    new PlatformID(PLATFORM, podcast_uri_id, plugin.config.id),
+                    limited_podcast_metadata.name,
+                    `${PODCAST_URL_PREFIX}${podcast_uri_id}`,
+                    highest_quality_cover_art.url,
+                ),
                 url: episode_url,
                 thumbnails: new Thumbnails(episode_metadata_response.data.episodeUnionV2.coverArt.sources.map(function (image) {
                     return new Thumbnail(image.url, image.height)
@@ -385,10 +580,10 @@ function getContentDetails(url: string) {
                 viewCount: HARDCODED_ZERO,
                 isLive: false,
                 shareUrl: episode_metadata_response.data.episodeUnionV2.uri,
-                uploadDate: new Date(episode_metadata_response.data.episodeUnionV2.releaseDate.isoString).getTime() / 1000,
+                datetime: new Date(episode_metadata_response.data.episodeUnionV2.releaseDate.isoString).getTime() / 1000,
                 description: episode_metadata_response.data.episodeUnionV2.htmlDescription,
                 video: new UnMuxVideoSourceDescriptor([], audio_sources),
-                rating: new RatingLikes(HARDCODED_ZERO),
+                rating: new RatingScaler(full_podcast_metadata.data.podcastUnionV2.rating.averageRating.average),
                 subtitles: [{
                     url: episode_url,
                     name: subtitle_name,
@@ -405,6 +600,23 @@ function getContentDetails(url: string) {
     }
 }
 
+function podcast_metadata_args(podcast_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:show:${podcast_uri_id}`
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "5fb034a236a3e8301e9eca0e23def3341ed66c891ea2d4fea374c091dc4b4a6a"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "queryShowMetadataV2")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+
 function transcript_args(episode_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
     const transcript_url_prefix = "https://spclient.wg.spotify.com/transcript-read-along/v2/episode/"
     const url = new URL(`${transcript_url_prefix}${episode_uri_id}`)
@@ -415,6 +627,24 @@ function transcript_args(episode_uri_id: string): { readonly url: string, readon
     }
 }
 
+function lyrics_args(song_uri_id: string): {
+    readonly url: string, readonly headers: {
+        Authorization: string,
+        Accept: string,
+        "app-platform": "WebPlayer"
+    }
+} {
+    const url = new URL(`https://spclient.wg.spotify.com/color-lyrics/v2/track/${song_uri_id}`)
+    return {
+        url: url.toString(),
+        headers: {
+            Accept: "application/json",
+            "app-platform": "WebPlayer",
+            Authorization: `Bearer ${local_state.bearer_token}`
+        }
+    }
+}
+
 function file_manifest_args(file_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
     const file_manifest_url_prefix = "https://gue1-spclient.spotify.com/storage-resolve/v2/files/audio/interactive/10/"
     const file_manifest_params = "?product=9&alt=json"
@@ -425,7 +655,6 @@ function file_manifest_args(file_id: string): { readonly url: string, readonly h
 }
 
 function episode_metadata_args(episode_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
-    const episode_metadata_url_prefix = "https://api-partner.spotify.com/pathfinder/v1/query"
     const variables = JSON.stringify({
         uri: `spotify:episode:${episode_uri_id}`
     })
@@ -435,13 +664,30 @@ function episode_metadata_args(episode_uri_id: string): { readonly url: string,
             sha256Hash: "9697538fe993af785c10725a40bb9265a20b998ccd2383bd6f586e01303824e9"
         }
     })
-    const url = new URL(episode_metadata_url_prefix)
+    const url = new URL(QUERY_URL)
     url.searchParams.set("operationName", "getEpisodeOrChapter")
     url.searchParams.set("variables", variables)
     url.searchParams.set("extensions", extensions)
     return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
 }
 
+function track_metadata_args(song_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:track:${song_uri_id}`
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "ae85b52abb74d20a4c331d4143d4772c95f34757bfa8c625474b912b9055b5c0"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "getTrack")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+
 function song_metadata_args(song_uri_id: string): {
     readonly url: string,
     readonly headers: {
@@ -458,9 +704,354 @@ function song_metadata_args(song_uri_id: string): {
         }
     }
 }
+
+function artist_metadata_args(artist_uri_id: string): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:artist:${artist_uri_id}`,
+        locale: "",
+        includePrerelease: true
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "da986392124383827dc03cbb3d66c1de81225244b6e20f8d78f9f802cc43df6e"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "queryArtistOverview")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+//#endregion
+
+//#region playlists
+// https://open.spotify.com/album/6BzxX6zkDsYKFJ04ziU5xQ
+// https://open.spotify.com/playlist/37i9dQZF1E38112qhvV3BT
+function isPlaylistUrl(url: string): boolean {
+    return PLAYLIST_REGEX.test(url)
+}
+function getPlaylist(url: string): PlatformPlaylistDetails {
+    const match_result = url.match(PLAYLIST_REGEX)
+    if (match_result === null) {
+        throw new ScriptException("regex error")
+    }
+    const maybe_playlist_type = match_result[1]
+    if (maybe_playlist_type === undefined) {
+        throw new ScriptException("regex error")
+    }
+    const playlist_type: PlaylistType = maybe_playlist_type as PlaylistType
+    const playlist_uri_id = match_result[2]
+    if (playlist_uri_id === undefined) {
+        throw new ScriptException("regex error")
+    }
+    switch (playlist_type) {
+        case "album": {
+            // if the author is the same as the album then include the artist pick otherwise nothing
+            // TODO we could load in extra info for all the other artists but it might be hard to do that in a request efficient way
+
+            const pagination_limit = 50 as const
+            const offset = 0
+
+            const { url, headers } = album_metadata_args(playlist_uri_id, offset, pagination_limit)
+            const album_metadata_response: AlbumResponse = JSON.parse(local_http.GET(url, headers, false).body)
+            const album_artist = album_metadata_response.data.albumUnion.artists.items[0]
+            if (album_artist === undefined) {
+                throw new ScriptException("missing album artist")
+            }
+            const unix_time = new Date(album_metadata_response.data.albumUnion.date.isoString).getTime() / 1000
+
+            return new PlatformPlaylistDetails({
+                id: new PlatformID(PLATFORM, playlist_uri_id, plugin.config.id),
+                name: album_metadata_response.data.albumUnion.name,
+                author: new PlatformAuthorLink(
+                    new PlatformID(PLATFORM, album_artist.id, plugin.config.id),
+                    album_artist.profile.name,
+                    `${ARTIST_URL_PREFIX}${album_artist.id}`,
+                    album_artist.visuals.avatarImage.sources[album_artist.visuals.avatarImage.sources.length - 1]?.url
+                ),
+                datetime: unix_time,
+                url: `${ALBUM_URL_PREFIX}${playlist_uri_id}`,
+                videoCount: album_metadata_response.data.albumUnion.tracks.totalCount,
+                contents: new AlbumPager(playlist_uri_id, offset, pagination_limit, album_metadata_response, album_artist, unix_time)
+            })
+        }
+        case "playlist": {
+            if (!bridge.isLoggedIn()) {
+                throw new LoginRequiredException("login to open playlists")
+            }
+            const pagination_limit = 25 as const
+            const offset = 0
+
+            const { url, headers } = fetch_playlist_args(playlist_uri_id, offset, pagination_limit)
+            const playlist_response: PlaylistResponse = JSON.parse(local_http.GET(url, headers, false).body)
+            const owner = playlist_response.data.playlistV2.ownerV2.data
+
+            return new PlatformPlaylistDetails({
+                id: new PlatformID(PLATFORM, playlist_uri_id, plugin.config.id),
+                name: playlist_response.data.playlistV2.name,
+                author: new PlatformAuthorLink(
+                    new PlatformID(PLATFORM, owner.username, plugin.config.id),
+                    owner.name,
+                    `${ARTIST_URL_PREFIX}${owner.username}`,
+                    owner.avatar?.sources[owner.avatar.sources.length - 1]?.url
+                ),
+                url: `${ALBUM_URL_PREFIX}${playlist_uri_id}`,
+                videoCount: playlist_response.data.playlistV2.content.totalCount,
+                contents: new SpotifyPlaylistPager(playlist_uri_id, offset, pagination_limit, playlist_response)
+            })
+        }
+        default: {
+            throw assert_no_fall_through(playlist_type, "unreachable")
+        }
+    }
+}
+class SpotifyPlaylistPager extends VideoPager {
+    private offset: number
+    private readonly total_tracks: number
+    constructor(
+        private readonly playlist_uri_id: string,
+        offset: number,
+        private readonly pagination_limit: number,
+        playlist_response: PlaylistResponse
+    ) {
+        const total_tracks = playlist_response.data.playlistV2.content.totalCount
+
+        const songs = format_playlist_tracks(playlist_response.data.playlistV2.content)
+
+        super(songs, total_tracks > offset + pagination_limit)
+        this.offset = offset + pagination_limit
+        this.total_tracks = total_tracks
+    }
+    override nextPage(this: SpotifyPlaylistPager): SpotifyPlaylistPager {
+        const { url, headers } = fetch_playlist_contents_args(this.playlist_uri_id, this.offset, this.pagination_limit)
+        const playlist_content_response: PlaylistContentResponse = JSON.parse(local_http.GET(url, headers, false).body)
+
+        const songs = format_playlist_tracks(playlist_content_response.data.playlistV2.content)
+        this.results = songs
+        this.hasMore = this.total_tracks > this.offset + this.pagination_limit
+        this.offset += this.pagination_limit
+        return this
+    }
+    override hasMorePagers(this: SpotifyPlaylistPager): boolean {
+        return this.hasMore
+    }
+}
+function format_playlist_tracks(content: PlaylistContent) {
+    return content.items.map(function (playlist_track_metadata) {
+        const song = playlist_track_metadata.itemV2.data
+        const track_uri_id = id_from_uri(song.uri)
+        const artist = song.artists.items[0]
+        if (artist === undefined) {
+            throw new ScriptException("missing artist")
+        }
+        const url = `${SONG_URL_PREFIX}${track_uri_id}`
+        return new PlatformVideo({
+            id: new PlatformID(PLATFORM, track_uri_id, plugin.config.id),
+            name: song.name,
+            author: new PlatformAuthorLink(
+                new PlatformID(PLATFORM, id_from_uri(artist.uri), plugin.config.id),
+                artist.profile.name,
+                `${ARTIST_URL_PREFIX}${id_from_uri(artist.uri)}`
+                // TODO figure out a way to get the artist thumbnail
+            ),
+            url,
+            thumbnails: new Thumbnails(song.albumOfTrack.coverArt.sources.map(function (source) {
+                return new Thumbnail(source.url, source.height)
+            })),
+            duration: song.trackDuration.totalMilliseconds / 1000,
+            viewCount: parseInt(song.playcount),
+            isLive: false,
+            shareUrl: url,
+            datetime: new Date(playlist_track_metadata.addedAt.isoString).getTime() / 1000
+        })
+    })
+}
+/**
+ * 
+ * @param playlist_uri_id 
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns 
+ */
+function fetch_playlist_contents_args(playlist_uri_id: string, offset: number, limit: number): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:playlist:${playlist_uri_id}`,
+        offset: offset,
+        limit: limit
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "91d4c2bc3e0cd1bc672281c4f1f59f43ff55ba726ca04a45810d99bd091f3f0e"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "fetchPlaylistContents")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+/**
+ * 
+ * @param playlist_uri_id 
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns 
+ */
+function fetch_playlist_args(playlist_uri_id: string, offset: number, limit: number): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:playlist:${playlist_uri_id}`,
+        offset: offset,
+        limit: limit
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "91d4c2bc3e0cd1bc672281c4f1f59f43ff55ba726ca04a45810d99bd091f3f0e"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "fetchPlaylist")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+class AlbumPager extends VideoPager {
+    private offset: number
+    private readonly thumbnails: Thumbnails
+    private readonly album_artist: ArtistDetails
+    private readonly unix_time: number
+    private readonly total_tracks: number
+    constructor(
+        private readonly album_uri_id: string,
+        offset: number,
+        private readonly pagination_limit: number,
+        album_metadata_response: AlbumResponse,
+        album_artist: ArtistDetails,
+        unix_time: number,
+    ) {
+        const total_tracks = album_metadata_response.data.albumUnion.tracks.totalCount
+        const thumbnails = new Thumbnails(album_metadata_response.data.albumUnion.coverArt.sources.map(function (source) {
+            return new Thumbnail(source.url, source.height)
+        }))
+
+        const songs = format_album_tracks(album_metadata_response.data.albumUnion.tracks, thumbnails, album_artist, unix_time)
+
+        super(songs, total_tracks > offset + pagination_limit)
+        this.offset = offset + pagination_limit
+        this.thumbnails = thumbnails
+        this.album_artist = album_artist
+        this.unix_time = unix_time
+        this.total_tracks = total_tracks
+    }
+    override nextPage(this: AlbumPager): AlbumPager {
+        const { url, headers } = album_tracks_args(this.album_uri_id, this.offset, this.pagination_limit)
+        const album_tracks_response: AlbumTracksResponse = JSON.parse(local_http.GET(url, headers, false).body)
+
+        const songs = format_album_tracks(album_tracks_response.data.albumUnion.tracks, this.thumbnails, this.album_artist, this.unix_time)
+        this.results = songs
+        this.hasMore = this.total_tracks > this.offset + this.pagination_limit
+        this.offset += this.pagination_limit
+        return this
+    }
+    override hasMorePagers(this: AlbumPager): boolean {
+        return this.hasMore
+    }
+}
+function format_album_tracks(tracks: Tracks, thumbnails: Thumbnails, album_artist: ArtistDetails, unix_time: number) {
+    return tracks.items.map(function (track) {
+        const track_uri_id = id_from_uri(track.track.uri)
+        const artist = track.track.artists.items[0]
+        if (artist === undefined) {
+            throw new ScriptException("missing artist")
+        }
+        const url = `${SONG_URL_PREFIX}${track_uri_id}`
+        return new PlatformVideo({
+            id: new PlatformID(PLATFORM, track_uri_id, plugin.config.id),
+            name: track.track.name,
+            author: new PlatformAuthorLink(
+                new PlatformID(PLATFORM, id_from_uri(artist.uri), plugin.config.id),
+                artist.profile.name,
+                `${ARTIST_URL_PREFIX}${id_from_uri(artist.uri)}`,
+                id_from_uri(artist.uri) === album_artist.id ? album_artist.visuals.avatarImage.sources[album_artist.visuals.avatarImage.sources.length - 1]?.url : undefined
+            ),
+            url,
+            thumbnails,
+            duration: track.track.duration.totalMilliseconds / 1000,
+            viewCount: parseInt(track.track.playcount),
+            isLive: false,
+            shareUrl: url,
+            datetime: unix_time
+        })
+    })
+}
+/**
+ * 
+ * @param album_uri_id 
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns 
+ */
+function album_tracks_args(album_uri_id: string, offset: number, limit: number): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:album:${album_uri_id}`,
+        offset: offset,
+        limit: limit
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "469874edcad37b7a379d4f22f0083a49ea3d6ae097916120d9bbe3e36ca79e9d"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "queryAlbumTracks")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
+/**
+ * 
+ * @param album_uri_id 
+ * @param offset the track to start loading from in the album (0 is the first track)
+ * @param limit the maximum number of tracks to load information about
+ * @returns 
+ */
+function album_metadata_args(album_uri_id: string, offset: number, limit: number): { readonly url: string, readonly headers: { Authorization: string } } {
+    const variables = JSON.stringify({
+        uri: `spotify:album:${album_uri_id}`,
+        locale: "",
+        offset: offset,
+        limit: limit
+    })
+    const extensions = JSON.stringify({
+        persistedQuery: {
+            version: 1,
+            sha256Hash: "469874edcad37b7a379d4f22f0083a49ea3d6ae097916120d9bbe3e36ca79e9d"
+        }
+    })
+    const url = new URL(QUERY_URL)
+    url.searchParams.set("operationName", "getAlbum")
+    url.searchParams.set("variables", variables)
+    url.searchParams.set("extensions", extensions)
+    return { url: url.toString(), headers: { Authorization: `Bearer ${local_state.bearer_token}` } }
+}
 //#endregion
 
 //#region utilities
+function id_from_uri(uri: string): string {
+    const match_result = uri.match(/^spotify:(show|album|track|artist):([0-9a-zA-Z]*)$/)
+    if (match_result === null) {
+        throw new ScriptException("regex error")
+    }
+    const uri_id = match_result[2]
+    if (uri_id === undefined) {
+        throw new ScriptException("regex error")
+    }
+    return uri_id
+}
+
 /**
  * Converts seconds to the timestamp format used in WebVTT
  * @param seconds 
@@ -489,6 +1080,7 @@ function assert_no_fall_through(value: never, exception_message?: string): Scrip
 }
 //#endregion
 
+//#region bad
 function is_premium(): boolean {
     return false
 }
@@ -561,6 +1153,7 @@ function get_gid(song_uri_id: string) {
             c ? null : ee[s >>> 24] + ee[s >>> 16 & 255] + ee[s >>> 8 & 255] + ee[255 & s] + ee[a >>> 24] + ee[a >>> 16 & 255] + ee[a >>> 8 & 255] + ee[255 & a] + ee[r >>> 24] + ee[r >>> 16 & 255] + ee[r >>> 8 & 255] + ee[255 & r] + ee[o >>> 24] + ee[o >>> 16 & 255] + ee[o >>> 8 & 255] + ee[255 & o]
     }(song_uri_id) : song_uri_id
 }
+//#endregion
 
 // export statements are removed during build step
 // used for unit testing in SpotifyScript.test.ts
diff --git a/src/types.ts b/src/types.ts
index 4213704d8d707629dbe3f8c057acc6166ca2b7be..8c18b32af597546e29cdaaca0ce33adfe0c3bec7 100644
--- a/src/types.ts
+++ b/src/types.ts
@@ -17,6 +17,7 @@ export type State = {
 
 //#region JSON types
 export type ContentType = "track" | "episode"
+export type PlaylistType = "album" | "playlist"
 
 export type TranscriptResponse = {
     readonly section: ({
@@ -41,10 +42,7 @@ export type EpisodeMetadataResponse = {
                 readonly totalMilliseconds: number
             }
             readonly coverArt: {
-                readonly sources: {
-                    readonly url: string
-                    readonly height: number
-                }[]
+                readonly sources: ImageSources
             }
             readonly releaseDate: {
                 readonly isoString: string
@@ -58,10 +56,70 @@ export type EpisodeMetadataResponse = {
                 }[]
             }
             readonly htmlDescription: string
+            readonly podcastV2: {
+                readonly data: {
+                    readonly name: string
+                    /** in this format "spotify:show:5VzFvh1JlEhBMS6ZHZ8CNO" */
+                    readonly uri: string
+                    readonly coverArt: {
+                        readonly sources: ImageSources
+                    }
+                }
+            }
+            readonly mediaTypes: ["AUDIO"] | ["AUDIO", "VIDEO"]
+        }
+    }
+}
+
+export type ArtistMetadataResponse = {
+    readonly data: {
+        readonly artistUnion: {
+            readonly stats: {
+                readonly followers: number
+                readonly monthlyListeners: number
+                readonly worldRank: number
+            }
         }
     }
 }
 
+export type TrackMetadataResponse = {
+    readonly data: {
+        readonly trackUnion: {
+            readonly playcount: string
+            readonly firstArtist: {
+                readonly items: {
+                    readonly id: string
+                    readonly profile: { readonly name: string }
+                    readonly visuals: {
+                        readonly avatarImage: {
+                            readonly sources: ImageSources
+                        }
+                    }
+                }[]
+            }
+            readonly albumOfTrack: {
+                readonly date: {
+                    readonly isoString: string
+                }
+            }
+            readonly duration: {
+                readonly totalMilliseconds: number
+            }
+        }
+    }
+}
+
+export type LyricsResponse = {
+    readonly lyrics: {
+        readonly language: "en"
+        readonly lines: {
+            readonly startTimeMs: string
+            readonly words: string
+        }[]
+    }
+}
+
 export type SongMetadataResponse = {
     readonly name: string
     /** in milliseconds */
@@ -73,16 +131,16 @@ export type SongMetadataResponse = {
                 readonly height: number
             }[]
         }
+        readonly date: {
+            readonly day: number
+            readonly month: number
+            readonly year: number
+        }
     }
     readonly artist: {
         gid: string
         name: string
     }[]
-    readonly date: {
-        readonly day: number
-        readonly month: number
-        readonly year: number
-    }
     readonly canonical_uri: string
     readonly file: {
         readonly file_id: string
@@ -91,6 +149,147 @@ export type SongMetadataResponse = {
     }[]
 }
 
+export type PodcastMetadataResponse = {
+    readonly data: {
+        readonly podcastUnionV2: {
+            readonly rating: {
+                readonly averageRating: {
+                    readonly average: number
+                    readonly totalRatings: number
+                }
+            }
+        }
+    }
+}
+
+export type ArtistDetails = {
+    readonly id: string
+    readonly profile: { readonly name: string }
+    readonly visuals: {
+        readonly avatarImage: {
+            readonly sources: ImageSources
+        }
+    }
+}
+
+export type AlbumTracksResponse = {
+    readonly data: {
+        readonly albumUnion: {
+            readonly tracks: Tracks
+        }
+    }
+}
+
+export type Tracks = {
+    readonly items: {
+        readonly track: {
+            readonly playcount: string
+            readonly name: string
+            readonly duration: { readonly totalMilliseconds: number }
+            readonly artists: {
+                readonly items: {
+                    readonly profile: { readonly name: string }
+                    readonly uri: string
+                }[]
+            }
+            readonly uri: string
+        }
+    }[]
+    readonly totalCount: number
+}
+
+export type AlbumResponse = {
+    readonly data: {
+        readonly albumUnion: {
+            readonly name: string
+            readonly tracks: Tracks
+            readonly artists: {
+                readonly items: ArtistDetails[]
+            }
+            readonly coverArt: {
+                readonly sources: ImageSources
+            }
+            readonly date: {
+                readonly isoString: string
+            }
+
+        }
+    }
+}
+
+export type PlaylistContentResponse = {
+    readonly data: {
+        readonly playlistV2: {
+            readonly content: PlaylistContent
+        }
+    }
+}
+
+type Owner = {
+    readonly data: {
+        readonly name: string
+        readonly username: string
+        readonly avatar: null | {
+            readonly sources: ImageSources
+        }
+
+    }
+}
+
+export type PlaylistResponse = {
+    readonly data: {
+        readonly playlistV2: {
+            readonly name: string
+            readonly ownerV2: Owner
+            readonly images: {
+                readonly items: {
+                    readonly sources: {
+                        readonly url: string
+                        readonly height: number | null
+                    }[]
+                }[]
+            }
+            readonly content: PlaylistContent
+        }
+    }
+
+}
+
+type ImageSources = {
+    readonly height: number
+    readonly url: string
+}[]
+
+export type PlaylistContent = {
+    readonly totalCount: number
+    readonly items: {
+        readonly addedAt: {
+            readonly isoString: string
+        }
+        readonly itemV2: {
+            readonly data: {
+                readonly playcount: string
+                readonly trackDuration: {
+                    readonly totalMilliseconds: number
+                }
+                readonly name: string
+                readonly uri: string
+                readonly albumOfTrack: {
+                    readonly coverArt: {
+                        readonly sources: ImageSources
+                    }
+                }
+                readonly artists: {
+                    readonly items: {
+                        readonly uri: string
+                        readonly profile: { readonly name: string }
+                    }[]
+                }
+            }
+        }
+    }[]
+}
+
 export type FileManifestResponse = {
     readonly cdnurl: string[]
 }