Spotify Mini Player

An enhancement to the spotify web player to make it a usable player in small window sizes. Especially useful when installing the web player as a standalone web app.

// ==UserScript==
// @name         Spotify Mini Player
// @namespace    Spotify
// @version      0.30
// @description  An enhancement to the spotify web player to make it a usable player in small window sizes. Especially useful when installing the web player as a standalone web app.
// @author       designakt
// @match        *://open.spotify.com/*
// @icon         https://developer.spotify.com/assets/branding-guidelines/[email protected]
// @run-at       document-start
// @grant        none
// @license      GNU GPLv3
// ==/UserScript==

(function() {
    'use strict';
    var customCSS = `
@media screen and (max-width: 799px), screen and (max-height: 564px) {
  body {
    min-height: inherit;
    min-width: inherit;
  }

  /* Playlist Container */
  div:has(> div > div > div > div > button[data-testid="play-button"]) {
    border: 0px solid green;
    padding: 0;
    margin-bottom: 8px;
    width: 80px;
    height: 80px;
    overflow: hidden;
  }

  div:has(> div > div > div > button[data-testid="play-button"])
    > div:nth-child(1) {
    border: 0px solid red;
    width: 80px;
    height: 80px;
  }

  div:has(> div > div > div > button[data-testid="play-button"])
    > div:nth-child(2) {
    padding: 0;
    width: 0;
  }

  /* Play-Button Wrapper - might just be a helper to remove later... */
  div:has(> div > button[data-testid="play-button"]) {
    border: 0px solid red;
    border-radius: unset;
    pointer-events: auto;
    position: absolute !important;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    margin: 0 !important;
    transform: none;
  }

  div:has(> button[data-testid="play-button"]) {
    border: 0px solid red;
    width: 100%;
    height: 100%;
  }

  button[data-testid="play-button"] {
    border: 0px solid yellow;
    border-radius: unset;
    width: 100%;
    height: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
  }

  button[data-testid="play-button"] > span {
    transform: scale(0.5);
    transition: all 300ms;
  }

  button[data-testid="play-button"]:hover > span {
    transform: scale(0.6) !important;
  }

  /* Playlist Group */
  div:has(> div > div > div > div > div > button[data-testid="play-button"]) {
    border: 0px solid red;
    display: block;
    width: calc(175px * 4) !important;
    /* width determins how many items are loaded */
    min-width: 80px !important;
  }

  /* No Playlist Section */
  section:not(button[data-testid="play-button"]) {
    visibility: hidden;
    height: 0;
    min-height: 0;
  }

  /* Playlist Group Section */
  section:has(
      > div > div > div > div > div > div > button[data-testid="play-button"]
    ) {
    border: 0px solid red;
    width: 80px;
    visibility: visible;
    height: auto;
  }

  /* Sections Group */
  section:has(section button[data-testid="play-button"]) {
    visibility: visible;
    height: auto;
  }

  /* Section Headlines and spacing */
  section h2,
  section h2 a {
    font-size: 1rem !important;
    white-space: break-spaces !important;
    pointer-events: none;
  }

  section div:has(> div > h2) {
    border: 0px solid firebrick;
    margin-bottom: 0 !important;
  }

  /* hide more link next to headlines */
  a > span[data-encore-id="type"] {
    display: none;
  }

  div:has(
      > section
        > div
        > div
        > div
        > div
        > div
        > div
        > button[data-testid="play-button"]
    ) {
    border: 0px solid blue;
    width: calc(80px + 4px + 4px);
    gap: 0;
    padding: 0;
    padding-left: 4px;
    padding-top: 4px;
    overflow-x: hidden;
    overflow-y: scroll;
    height: 100%;
    position: absolute;
    top: 0;
    left: 0;
    background: var(--background-base);
  }

  div:has(
      > section
        > div
        > div
        > div
        > div
        > div
        > div
        > button[data-testid="play-button"]
    )::-webkit-scrollbar {
    width: 4px !important;
  }

  .main-view-container__mh-footer-container {
    margin-top: 40px;
    display: none;
  }

  /* Now Playing Area */

  .Root__top-container,
  div#main > div > div:nth-child(2) {
    overflow: hidden;
    row-gap: 0;
    padding: 0;
    display: grid;
    grid-template-columns: max-content 1fr;
    grid-template-rows: 1fr 0;
    grid-auto-columns: 0;
    grid-auto-rows: 0;
    gap: 0px 0px;
    grid-template-areas: "playlist-bar now-playing-bar";
  }

  div:has(> div.main-view-container) {
    border: 0px solid red;
    grid-area: playlist-bar !important;
    width: 15px;
    height: 100%;
    border-radius: 0;
    background: var(--background-base);
    transition: all 0.2s ease-out;
    opacity: 0.5;

    overflow: hidden;
  }

  div:has(> div.main-view-container):hover {
    width: calc(80px + 4px + 4px);
    opacity: 1;
  }

  div:has(> div.main-view-container)::before {
    content: "";
    position: absolute;
    top: 0;
    width: 84px;
    height: 8px;
    background: linear-gradient(var(--background-base), rgba(0, 0, 0, 0.001));
    background: linear-gradient(
      to bottom,
      hsl(0, 0%, 7%) 0%,
      hsla(0, 0%, 7%, 0.987) 8.1%,
      hsla(0, 0%, 7%, 0.896) 22.5%,
      hsla(0, 0%, 7%, 0.741) 35.3%,
      hsla(0, 0%, 7%, 0.55) 47.1%,
      hsla(0, 0%, 7%, 0.352) 58.8%,
      hsla(0, 0%, 7%, 0.175) 71%,
      hsla(0, 0%, 7%, 0.049) 84.5%,
      hsla(0, 0%, 7%, 0) 100%
    );
    z-index: 1;
  }

  div:has(> div.main-view-container)::after {
    content: "";
    position: absolute;
    bottom: 0;
    width: 84px;
    height: 20px;
    background: linear-gradient(rgba(0, 0, 0, 0.001), var(--background-base));
    /* transparent keyword is broken in Safari */
    background: linear-gradient(
      to top,
      hsl(0, 0%, 7%) 0%,
      hsla(0, 0%, 7%, 0.987) 8.1%,
      hsla(0, 0%, 7%, 0.896) 22.5%,
      hsla(0, 0%, 7%, 0.741) 35.3%,
      hsla(0, 0%, 7%, 0.55) 47.1%,
      hsla(0, 0%, 7%, 0.352) 58.8%,
      hsla(0, 0%, 7%, 0.175) 71%,
      hsla(0, 0%, 7%, 0.049) 84.5%,
      hsla(0, 0%, 7%, 0) 100%
    );
  }

  div:has(> div.main-view-container) > div::after {
    background: linear-gradient(
      to left,
      hsl(0, 0%, 7%) 0%,
      hsla(0, 0%, 7%, 0.987) 8.1%,
      hsla(0, 0%, 7%, 0.896) 22.5%,
      hsla(0, 0%, 7%, 0.741) 35.3%,
      hsla(0, 0%, 7%, 0.55) 47.1%,
      hsla(0, 0%, 7%, 0.352) 58.8%,
      hsla(0, 0%, 7%, 0.175) 71%,
      hsla(0, 0%, 7%, 0.049) 84.5%,
      hsla(0, 0%, 7%, 0) 100%
    );
    content: "";
    position: absolute;
    top: 0;
    width: 20%;
    height: 100vh;
    right: 0;
    opacity: 0.2;
    transition: all 0.2s step-start;
  }

  div:has(> div.main-view-container):hover > div::after {
    right: 4px;
    transition: all 0.2s step-end;
  }

  .Root__now-playing-bar,
  div:has(> footer) {
    background-position: center;
    background-repeat: no-repeat;
    background-size: cover;
    background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mOU+A8AATUBGe90iuoAAAAASUVORK5CYII=);
    transition: background-image 6s ease-out;
    z-index: auto !important;
  }

  .Root__now-playing-bar footer,
  footer {
    background-color: rgba(24, 24, 24, 0.4);
    backdrop-filter: blur(50px);
  }

  .Root__now-playing-bar,
  .Root__now-playing-bar footer,
  .Root__now-playing-bar footer > div,
  div:has(> footer),
  footer,
  footer > div {
    width: 100%;
    min-width: 300px !important;
    height: 100vh;
    min-height: 100vh;
  }

  /* Now Playing Bar Layout to vertical */
  .Root__now-playing-bar footer > div,
  footer > div {
    flex-direction: column !important;
    justify-content: center !important;
    gap: 16px;
    padding: 0 !important;
  }

  /* Widen Now Playing Bar content areas */
  .Root__now-playing-bar footer > div > div,
  footer > div > div {
    width: 70% !important;
  }

  /* Cover,Title,Artist block */
  .Root__now-playing-bar footer > div > div:first-child > div,
  footer > div > div:first-child > div {
    justify-content: space-between;
    gap: 8px;
    transform: none;
    transition: none;
  }

  /* Cover box */
  footer > div > div:first-child > div > div:first-child {
    transform: none;
    box-shadow: 0 1px 15px rgba(0, 0, 0, 0.3);
  }

  /* Cover Expand Button */
  .Root__now-playing-bar
    footer
    > div
    > div:first-child
    > div
    > div:first-child
    > button,
  footer > div > div:first-child > div > div:first-child > button {
    display: none;
  }

  /* Cover Link */
  .Root__now-playing-bar
    footer
    > div
    > div:first-child
    > div
    > div:first-child
    img,
  footer > div > div:first-child > div > div:first-child > div > a {
    cursor: default;
  }

  /* Title, Artist box */
  .Root__now-playing-bar
    footer
    > div
    > div:first-child
    > div
    > div:nth-child(2),
  footer > div > div:first-child > div > div:nth-child(2) {
    width: 100%;
  }

  /* Playback block */
  .playback-bar > div:first-child {
    min-width: 25px;
  }

  .playback-bar > div:last-child {
    min-width: 30px;
  }

  /* Volume block */
  .Root__now-playing-bar footer > div > div:last-child,
  footer > div > div:last-child {
    justify-content: center;
  }

  footer > div > div:last-child > div {
    flex-direction: row-reverse;
  }

  .Root__now-playing-bar footer > div > div:last-child > div .volume-bar,
  footer > div > div:last-child > div .volume-bar {
    width: 100%;
    flex-basis: auto;
  }

  button[data-testid="control-button-npv"],
  button[data-testid="lyrics-button"],
  button[data-testid="control-button-queue"],
  div[data-testid="indicator"] {
    display: none;
  }

  /* Hide Spotify's own scroll bars */
  .os-scrollbar,
  .os-scrollbar-corner {
    display: none;
  }
}

@media screen and (min-width: 800px) and (min-height: 565px) {
  div:has(> footer) {
    background-image: none !important;
  }
}
`;
    // apply above css
    addGlobalStyle(customCSS);

    // wait for document to complete
    var interval = setInterval(function() {
        if(document.readyState === 'complete') {
            clearInterval(interval);
            init();
        }
    }, 100);

})();

function addGlobalStyle(css) {
    var head, style;
    head = document.getElementsByTagName('head')[0];
    if (!head) { return; }
    style = document.createElement('style');
    style.type = 'text/css';
    style.innerHTML = css;
    head.appendChild(style);
}

function init() {
    var item = null;
    var itemSRC = null;
    var bglayer = null;
    // regualarly check for cover art (if it exists and if it has changed)
    var interval = setInterval(function() {
        // see if there is a cover art
        item = document.querySelector('img[data-testid="cover-art-image"]');
        // check if there is a cover art
        if(item == null) {
            //console.log("searching for cover art");
        }
        // check if cover art has changed
        else if(item.src != itemSRC) {
            // store cover art url
            itemSRC = item.src;
            //console.log("found new cover art: " + item.src);
            // select background element to apply cover art to
            bglayer = document.querySelector('div:has(> footer)');
            // set cover art as background image
            bglayer.style.backgroundImage = "url(" + itemSRC + ")";
            console.log("applied new cover art to background");
        }
    }, 1000);
}