diff options
| -rw-r--r-- | hg.css | 210 | ||||
| -rw-r--r-- | index.html | 70 | ||||
| -rw-r--r-- | player.js | 217 | 
3 files changed, 473 insertions, 24 deletions
| @@ -325,6 +325,10 @@ canvas {    color: #ffff;    right: 0;    margin-right: 5px; +  padding: 5px; +  background: rgba(0, 0, 0, 0.5); +  border-top-left-radius: 5px; +  border-top-right-radius: 5px;  }  .ERRORS { @@ -344,3 +348,209 @@ canvas {    border: 2px solid red;    margin-top: 10px;  } + +/* Terminal Overlay Styles - ORED Initial Console Emulator Style */ +.terminal-overlay { +  position: fixed; +  top: 0; +  left: 0; +  width: 100%; +  height: 100%; +  background: rgba(192, 192, 192, 0.3); +  backdrop-filter: none; +  z-index: 10001; +  display: flex; +  justify-content: center; +  align-items: center; +} + +.terminal-window { +  width: 80%; +  max-width: 600px; +  height: 65%; +  max-height: 450px; +  min-height: 300px; +  background: #c0c0c0; +  border: 4px outset #c0c0c0; +  border-radius: 0; +  font-family: 'VCR', 'Monaco', 'Courier New', monospace; +  box-shadow: 3px 3px 8px rgba(0, 0, 0, 0.6); +  display: flex; +  flex-direction: column; +  position: relative; +} + +.terminal-header { +  background: #c0c0c0; +  color: #000000; +  padding: 1px 0; +  border-bottom: 2px inset #c0c0c0; +  display: flex; +  justify-content: center; +  align-items: center; +  font-size: 10px; +  font-weight: bold; +  font-family: 'VCR', 'Geneva', 'Helvetica', sans-serif; +  min-height: 16px; +  position: relative; +} + +.terminal-title { +  color: #000000; +  font-size: 10px; +  font-weight: bold; +  text-align: center; +} + +.terminal-close { +  cursor: pointer; +  color: #000000; +  font-size: 10px; +  font-weight: bold; +  width: 16px; +  height: 14px; +  border: 1px outset #c0c0c0; +  background: #c0c0c0; +  text-align: center; +  line-height: 12px; +  border-radius: 0; +  position: absolute; +  right: 2px; +  top: 1px; +} + +.terminal-close:hover { +  background: #b0b0b0; +} + +.terminal-close:active { +  border: 1px inset #c0c0c0; +  background: #a0a0a0; +} + +.terminal-content { +  flex: 1; +  display: flex; +  flex-direction: column; +  padding: 2px; +  overflow: hidden; +  background: #c0c0c0; +} + +#terminal-output { +  flex: 1; +  overflow-y: auto; +  margin-bottom: 4px; +  color: #000000; +  font-size: 9px; +  line-height: 1.1; +  font-family: 'VCR', 'Monaco', 'Courier New', monospace; +  background: #c0c0c0; +  border: 2px inset #c0c0c0; +  padding: 2px 4px; +  white-space: pre-wrap; +} + +.terminal-line { +  margin-bottom: 0; +  word-wrap: break-word; +  color: #000000; +  font-size: 9px; +  line-height: 1.1; +} + +.terminal-input-line { +  display: flex; +  align-items: center; +  color: #000000; +  font-size: 9px; +  background: #c0c0c0; +  border: 2px inset #c0c0c0; +  padding: 1px 4px; +  min-height: 14px; +} + +.terminal-prompt { +  color: #000000; +  margin-right: 2px; +  font-weight: normal; +  font-family: 'VCR', 'Monaco', 'Courier New', monospace; +  font-size: 9px; +} + +.terminal-input { +  flex: 1; +  background: transparent; +  border: none; +  color: #000000; +  font-family: 'VCR', 'Monaco', 'Courier New', monospace; +  font-size: 9px; +  outline: none; +  caret-color: #000000; +  line-height: 1.1; +} + +.terminal-input::selection { +  background: #000080; +  color: #ffffff; +} + +.terminal-btn { +  float: right; +  margin-right: 10px; +  font-size: 16px; +  color: #565656; +} + +.terminal-btn:hover { +  color: #000000; +} + +.terminal-overlay.hidden { +  display: none; +} + +/* Custom scrollbar for terminal output - Classic style */ +#terminal-output::-webkit-scrollbar { +  width: 16px; +} + +#terminal-output::-webkit-scrollbar-track { +  background: #c0c0c0; +  border: 1px inset #c0c0c0; +} + +#terminal-output::-webkit-scrollbar-thumb { +  background: linear-gradient(to bottom, #e0e0e0 0%, #c0c0c0 50%, #a0a0a0 100%); +  border: 1px outset #c0c0c0; +  border-radius: 0; +} + +#terminal-output::-webkit-scrollbar-thumb:hover { +  background: linear-gradient(to bottom, #d0d0d0 0%, #b0b0b0 50%, #909090 100%); +} + +#terminal-output::-webkit-scrollbar-thumb:active { +  border: 1px inset #c0c0c0; +  background: linear-gradient(to bottom, #a0a0a0 0%, #b0b0b0 50%, #d0d0d0 100%); +} + +#terminal-output::-webkit-scrollbar-corner { +  background: #c0c0c0; +} + +#terminal-output::-webkit-scrollbar-button { +  width: 16px; +  height: 16px; +  background: linear-gradient(to bottom, #e0e0e0 0%, #c0c0c0 50%, #a0a0a0 100%); +  border: 1px outset #c0c0c0; +} + +#terminal-output::-webkit-scrollbar-button:hover { +  background: linear-gradient(to bottom, #d0d0d0 0%, #b0b0b0 50%, #909090 100%); +} + +#terminal-output::-webkit-scrollbar-button:active { +  border: 1px inset #c0c0c0; +  background: linear-gradient(to bottom, #a0a0a0 0%, #b0b0b0 50%, #d0d0d0 100%); +} @@ -16,29 +16,32 @@      <div class="modal" id="intro-modal">        <div class="modal-bg modal-exit"></div>        <div class="modal-container"> -        <h3 class="text-center">Welcome to Retrowave Player</h3> -        <p class="mt-5">Here are the controls:</p> +        <h3 class="text-center">WELCOME TO RETROWAVE PLAYER</h3> +        <p class="mt-5">HERE ARE THE CONTROLS:</p>          <h4 class="mt-5"> -          Play/Pause <span class="controls">SPACE or Click or Touch</span> +          PLAY/PAUSE <span class="controls">SPACE OR CLICK OR TOUCH</span>          </h4>          <h4 class="mt-5"> -          Volume Up <span class="controls">w or Swipe Up</span> +          VOLUME UP <span class="controls">W OR SWIPE UP</span>          </h4>          <h4 class="mt-5"> -          Volume Down <span class="controls">s or Swipe Down</span> +          VOLUME DOWN <span class="controls">S OR SWIPE DOWN</span>          </h4>          <h4 class="mt-5"> -          Next Track<span class="controls"> n or Press the Refresh Button</span> +          NEXT TRACK<span class="controls"> N OR PRESS THE REFRESH BUTTON</span>          </h4>          <h4 class="mt-5"> -          Toggle Controls<span class="controls"> h</span> +          TOGGLE CONTROLS<span class="controls"> H</span> +        </h4> +        <h4 class="mt-5"> +          OPEN TERMINAL<span class="controls"> T</span>          </h4>          <p class="mt-5"> -          (You can download the music history of your current browser by -          clicking the history link on the player) +          (YOU CAN DOWNLOAD THE MUSIC HISTORY OF YOUR CURRENT BROWSER BY +          CLICKING THE HISTORY LINK ON THE PLAYER)          </p> -        <p class="mt-5">All the music is fetched from retrowave.ru</p> -        <div id="warning">⚠️ WARNING: This application contains strobing/flashing lights</div> +        <p class="mt-5">ALL THE MUSIC IS FETCHED FROM RETROWAVE.RU</p> +        <div id="warning">⚠️ WARNING: THIS APPLICATION CONTAINS STROBING/FLASHING LIGHTS</div>          <button class="modal-close modal-exit" id="initButton">X</button>        </div>      </div> @@ -78,22 +81,23 @@            </svg>          </div>          <div class="no-pause buttons refresh toggleable" title="NEXT TRACK">🔃</div> +        <div class="no-pause buttons terminal-btn toggleable" title="OPEN TERMINAL">⌨️</div>          <div class="title-content"> -          <div class="intro">Now Playing</div> +          <div class="intro">NOW PLAYING</div>            <hr /> -          <h3 id="track-name">Retrowave Player</h3> +          <h3 id="track-name">RETROWAVE PLAYER</h3>          </div>          <div class="gradient-overlay"></div>          <div class="color-overlay"></div>          <div class="footer toggleable"> -          <div title="DOWNLOAD YOUR PLAYLIST HISTORY" id="history" class="no-pause footer-items">history</div> +          <div title="DOWNLOAD YOUR PLAYLIST HISTORY" id="history" class="no-pause footer-items">HISTORY</div>            <div id="source">              <a                title="SOURCE CODE"                class="no-pause footer-items"                href="https://git.indrajith.dev/retrowave-player/"                target="_blank" -              >source code</a +              >SOURCE CODE</a              >            </div>            <div id="retrowaveru" class="no-pause"> @@ -102,7 +106,7 @@                class="no-pause footer-items"                href="http://retrowave.ru/"                target="_blank" -              >retrowave.ru</a +              >RETROWAVE.RU</a              >            </div>            <div id="guestbook" class="no-pause"> @@ -111,13 +115,45 @@                class="no-pause footer-items"                href="https://indrajith.atabook.org/"                target="_blank" -              >guestbook</a +              >GUESTBOOK</a              >            </div>          </div>          <!-- <div id="upload-info" class="no-pause" title="Upload a playlist (downloaded from history)">Upload</div> -->        </div>      </div> +    <div id="terminal-overlay" class="terminal-overlay hidden"> +      <div class="terminal-window"> +        <div class="terminal-header"> +          <span class="terminal-title">Retrowave Terminal</span> +          <span class="terminal-close">□</span> +        </div> +        <div class="terminal-content"> +          <div id="terminal-output"> +            <div class="terminal-line">Welcome to Retrowave Terminal</div> +            <div class="terminal-line">Type 'help' for available commands</div> +            <div class="terminal-line"></div> +            <div class="terminal-line">Available commands:</div> +            <div class="terminal-line">  help          - Show this help message</div> +            <div class="terminal-line">  play          - Start/resume playback</div> +            <div class="terminal-line">  pause         - Pause playback</div> +            <div class="terminal-line">  next          - Skip to next track</div> +            <div class="terminal-line">  volume [0-10] - Set volume (0-10)</div> +            <div class="terminal-line">  status        - Show current track info</div> +            <div class="terminal-line">  history       - Download playlist history</div> +            <div class="terminal-line">  effect        - Change visual effect</div> +            <div class="terminal-line">  fullscreen    - Toggle fullscreen mode</div> +            <div class="terminal-line">  clear         - Clear terminal</div> +            <div class="terminal-line">  exit          - Close terminal</div> +            <div class="terminal-line"></div> +          </div> +          <div class="terminal-input-line"> +            <span class="terminal-prompt">indrajith@retrowave:$ </span> +            <input type="text" id="terminal-input" class="terminal-input" autocomplete="off" spellcheck="false"> +          </div> +        </div> +      </div> +    </div>      <div id="codef-canvas"></div>      <div class="OVR hidden"></div>      <div class="ERRORS hidden"></div> @@ -9,6 +9,11 @@    let refreshBtn = document.querySelector(".refresh");    let ovr = document.querySelector(".OVR");    let fullScreenBtn = document.querySelector(".fullscreen"); +  let terminalBtn = document.querySelector(".terminal-btn"); +  let terminalOverlay = document.getElementById("terminal-overlay"); +  let terminalInput = document.getElementById("terminal-input"); +  let terminalOutput = document.getElementById("terminal-output"); +  let terminalClose = document.querySelector(".terminal-close");    let errorEl = document.querySelector(".ERRORS");    let currentEffectIndex = 0;    const modalEl = document.getElementById("intro-modal"); @@ -113,15 +118,15 @@    });    document.addEventListener("keyup", (event) => { +    if (!terminalOverlay.classList.contains("hidden")) { +      return; +    } +      const { key } = event;      switch (key) {        case " ":          togglePlay();          break; -      case "a": -        break; -      case "d": -        break;        case "w":          volumeUp();          break; @@ -143,6 +148,9 @@        case "e":          rotateHydraEffect();          break; +      case "t": +        toggleTerminal(); +        break;      }    }); @@ -255,10 +263,11 @@    }    function updateInfo(trackDetails) { -    titleEl.innerText = trackDetails.title; -    document.title = `Now Playing... ${trackDetails.title}`; +    const trackTitle = trackDetails.title.toString().toUpperCase() || "Unknown Track"; +    titleEl.innerText = trackTitle; +    document.title = `Now Playing... ${trackTitle}`;      coverArtEl.style.backgroundImage = `url("${retroWaveRu}${trackDetails.artworkUrl}")`; -    ovr.innerHTML = `${trackDetails.title}`; +    ovr.innerHTML = `> ${trackTitle}`;    }    function getHistory() { @@ -324,6 +333,27 @@ https://retrowave.ru/${musicData.streamUrl}      fullScreenBtn.addEventListener("click", () => {        toggleFullScreen();      }); + +    terminalBtn.addEventListener("click", () => { +      toggleTerminal(); +    }); + +    terminalClose.addEventListener("click", () => { +      closeTerminal(); +    }); + +    terminalInput.addEventListener("keydown", (e) => { +      if (e.key === "Enter") { +        executeCommand(terminalInput.value.trim()); +        terminalInput.value = ""; +      } +    }); + +    document.addEventListener("keydown", (e) => { +      if (e.key === "Escape" && !terminalOverlay.classList.contains("hidden")) { +        closeTerminal(); +      } +    });    }    async function toggleFullScreen() { @@ -469,4 +499,177 @@ https://retrowave.ru/${musicData.streamUrl}        );      }    } + +  function toggleTerminal() { +    terminalOverlay.classList.toggle("hidden"); +    if (!terminalOverlay.classList.contains("hidden")) { +      terminalInput.focus(); +    } +  } + +  function closeTerminal() { +    terminalOverlay.classList.add("hidden"); +  } + +  function addTerminalLine(text, isCommand = false) { +    const line = document.createElement("div"); +    line.className = "terminal-line"; +    if (isCommand) { +      line.innerHTML = `<span style="color: #000000; font-weight: normal;">indrajith@retrowave:$ ${text}</span>`; +    } else { +      line.textContent = text; +    } +    terminalOutput.appendChild(line); +    terminalOutput.scrollTop = terminalOutput.scrollHeight; +  } + +  function executeCommand(command) { +    if (!command) return; +     +    addTerminalLine(command, true); +     +    const args = command.toLowerCase().split(' '); +    const cmd = args[0]; +     +    switch (cmd) { +      case 'help': +        addTerminalLine('Available commands:'); +        addTerminalLine('  help              - Show this help message'); +        addTerminalLine('  play              - Start/resume playback'); +        addTerminalLine('  pause             - Pause playback'); +        addTerminalLine('  next              - Skip to next track'); +        addTerminalLine('  volume [0-10]     - Set volume (0-10)'); +        addTerminalLine('  status            - Show current track info'); +        addTerminalLine('  history           - Download playlist history'); +        addTerminalLine('  effect [list|name] - Change/list visual effects'); +        addTerminalLine('  fullscreen        - Toggle fullscreen mode'); +        addTerminalLine('  clear             - Clear terminal'); +        addTerminalLine('  exit              - Close terminal'); +        break; +         +      case 'play': +        if (howlerInstance && !isPlaying) { +          togglePlay(); +          addTerminalLine('Playback resumed.'); +        } else if (isPlaying) { +          addTerminalLine('Already playing.'); +        } else { +          addTerminalLine('No track loaded.'); +        } +        break; +         +      case 'pause': +        if (howlerInstance && isPlaying) { +          togglePlay(); +          addTerminalLine('Playback paused.'); +        } else { +          addTerminalLine('Not currently playing.'); +        } +        break; +         +      case 'next': +        if (howlerInstance) { +          playNextTrack(); +          addTerminalLine('Skipped to next track.'); +        } else { +          addTerminalLine('No track loaded.'); +        } +        break; +         +      case 'volume': +        if (args[1]) { +          const vol = parseInt(args[1]); +          if (vol >= 0 && vol <= 10) { +            volume = vol / 10; +            if (howlerInstance) setVolume(); +            addTerminalLine(`Volume set to ${vol}/10`); +          } else { +            addTerminalLine('Volume must be between 0 and 10.'); +          } +        } else { +          addTerminalLine(`Current volume: ${Math.round(volume * 10)}/10`); +        } +        break; +         +      case 'status': +        if (currentTracks.length > 0) { +          const track = currentTracks[0]; +          addTerminalLine(`Now playing: ${track.title}`); +          addTerminalLine(`Status: ${isPlaying ? 'Playing' : 'Paused'}`); +          addTerminalLine(`Volume: ${Math.round(volume * 10)}/10`); +        } else { +          addTerminalLine('No track loaded.'); +        } +        break; +         +      case 'history': +        downloadHistory(); +        addTerminalLine('Playlist history downloaded.'); +        break; +         +      case 'effect': +        if (args[1]) { +          if (args[1] === 'list') { +            addTerminalLine('Available effects:'); +            addTerminalLine('  vernoi    - Voronoi pattern effect'); +            addTerminalLine('  waveyzz   - Wave synthesis effect'); +            addTerminalLine('  oscrotate - Oscillating rotation effect'); +            addTerminalLine('Usage: effect <effect_name> or effect list'); +          } else { +            const effectName = args[1].toLowerCase(); +            let effectIndex = -1; +             +            switch (effectName) { +              case 'vernoi': +                effectIndex = 0; +                break; +              case 'waveyzz': +                effectIndex = 1; +                break; +              case 'oscrotate': +                effectIndex = 2; +                break; +              default: +                addTerminalLine(`Unknown effect: ${effectName}`); +                addTerminalLine('Type "effect list" to see available effects.'); +                break; +            } +             +            if (effectIndex !== -1) { +              try { +                hydraEffects[effectIndex](); +                currentEffectIndex = effectIndex; +                addTerminalLine(`Effect changed to: ${effectName}`); +              } catch (error) { +                addTerminalLine('Failed to apply effect.'); +              } +            } +          } +        } else { +          rotateHydraEffect(); +          addTerminalLine('Visualization changed to next effect.'); +        } +        break; +         +      case 'fullscreen': +        toggleFullScreen(); +        addTerminalLine('Toggled fullscreen mode.'); +        break; +         +      case 'clear': +        terminalOutput.innerHTML = ''; +        break; +         +      case 'exit': +        closeTerminal(); +        break; +         +      default: +        addTerminalLine(`Command not found: ${cmd}`); +        addTerminalLine('Type "help" for available commands.'); +        break; +    } +     +    addTerminalLine(''); +  }  })(); | 
