add small player widget
This commit is contained in:
		
							
								
								
									
										175
									
								
								hipsterfy/static/scan.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										175
									
								
								hipsterfy/static/scan.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,175 @@
 | 
			
		||||
<!DOCTYPE html>
 | 
			
		||||
<html lang="de">
 | 
			
		||||
<head>
 | 
			
		||||
  <meta charset="UTF-8">
 | 
			
		||||
  <title>Spotify QR-Scanner</title>
 | 
			
		||||
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
			
		||||
  <style>
 | 
			
		||||
    html, body {
 | 
			
		||||
      margin: 0; padding: 0;
 | 
			
		||||
      font-family: sans-serif;
 | 
			
		||||
      background: #181818;
 | 
			
		||||
      color: #fff;
 | 
			
		||||
      height: 100%;
 | 
			
		||||
    }
 | 
			
		||||
    #app {
 | 
			
		||||
      display: flex;
 | 
			
		||||
      flex-direction: column;
 | 
			
		||||
      align-items: center;
 | 
			
		||||
      justify-content: flex-start;
 | 
			
		||||
      min-height: 100vh;
 | 
			
		||||
      padding: 1em;
 | 
			
		||||
    }
 | 
			
		||||
    #video {
 | 
			
		||||
      width: 90vw;
 | 
			
		||||
      max-width: 400px;
 | 
			
		||||
      border-radius: 12px;
 | 
			
		||||
      margin: 1em 0;
 | 
			
		||||
      background: #222;
 | 
			
		||||
    }
 | 
			
		||||
    #result {
 | 
			
		||||
      margin: 1em 0;
 | 
			
		||||
      word-break: break-all;
 | 
			
		||||
    }
 | 
			
		||||
    #backBtn {
 | 
			
		||||
      margin-top: 1em;
 | 
			
		||||
      padding: 0.7em 1.5em;
 | 
			
		||||
      border: none;
 | 
			
		||||
      border-radius: 8px;
 | 
			
		||||
      background: #1DB954;
 | 
			
		||||
      color: #fff;
 | 
			
		||||
      font-size: 1.1em;
 | 
			
		||||
      cursor: pointer;
 | 
			
		||||
      display: none;
 | 
			
		||||
    }
 | 
			
		||||
    iframe {
 | 
			
		||||
      border: none;
 | 
			
		||||
      border-radius: 12px;
 | 
			
		||||
      margin-top: 1em;
 | 
			
		||||
      width: 90vw;
 | 
			
		||||
      max-width: 400px;
 | 
			
		||||
      height: 152px; /* Spotify empfiehlt 152px für volle Vorschau */
 | 
			
		||||
      background: #222;
 | 
			
		||||
    }
 | 
			
		||||
  </style>
 | 
			
		||||
</head>
 | 
			
		||||
<body>
 | 
			
		||||
  <div id="app">
 | 
			
		||||
    <h2>Spotify QR-Scanner</h2>
 | 
			
		||||
    <video id="video" autoplay playsinline></video>
 | 
			
		||||
    <div id="result"></div>
 | 
			
		||||
    <button id="backBtn">Zurück zum Scanner</button>
 | 
			
		||||
    <div id="embed"></div>
 | 
			
		||||
  </div>
 | 
			
		||||
  <script src="https://unpkg.com/jsqr/dist/jsQR.js"></script>
 | 
			
		||||
  <script>
 | 
			
		||||
    const video = document.getElementById('video');
 | 
			
		||||
    const resultDiv = document.getElementById('result');
 | 
			
		||||
    const embedDiv = document.getElementById('embed');
 | 
			
		||||
    const backBtn = document.getElementById('backBtn');
 | 
			
		||||
    let scanning = true;
 | 
			
		||||
    let stream = null;
 | 
			
		||||
    let animationId = null;
 | 
			
		||||
 | 
			
		||||
    // Extrahiere die Track-ID aus beliebigen Spotify-Links
 | 
			
		||||
    function extractSpotifyTrackId(url) {
 | 
			
		||||
      try {
 | 
			
		||||
        // Suche nach /track/<id> oder /embed/track/<id>
 | 
			
		||||
        const match = url.match(/(?:embed\/)?track\/([a-zA-Z0-9]+)/);
 | 
			
		||||
        if (match) {
 | 
			
		||||
          return match[1];
 | 
			
		||||
        }
 | 
			
		||||
      } catch {}
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function isSpotifyUrl(url) {
 | 
			
		||||
      try {
 | 
			
		||||
        const u = new URL(url);
 | 
			
		||||
        return u.hostname.endsWith('spotify.com');
 | 
			
		||||
      } catch {
 | 
			
		||||
        return false;
 | 
			
		||||
      }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function getSpotifyEmbed(url) {
 | 
			
		||||
      const trackId = extractSpotifyTrackId(url);
 | 
			
		||||
      if (trackId) {
 | 
			
		||||
        // Die Vorschau ist immer das offizielle Spotify-Embed, keine App-Credentials!
 | 
			
		||||
        return `https://open.spotify.com/embed/track/${trackId}`;
 | 
			
		||||
      }
 | 
			
		||||
      return null;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    async function startScanner() {
 | 
			
		||||
      scanning = true;
 | 
			
		||||
      embedDiv.innerHTML = '';
 | 
			
		||||
      resultDiv.textContent = '';
 | 
			
		||||
      backBtn.style.display = 'none';
 | 
			
		||||
      if (!stream) {
 | 
			
		||||
        try {
 | 
			
		||||
          stream = await navigator.mediaDevices.getUserMedia({ video: { facingMode: "environment" } });
 | 
			
		||||
          video.srcObject = stream;
 | 
			
		||||
        } catch (e) {
 | 
			
		||||
          resultDiv.textContent = "Kamera konnte nicht gestartet werden.";
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      video.play();
 | 
			
		||||
      requestAnimationFrame(tick);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function stopScanner() {
 | 
			
		||||
      scanning = false;
 | 
			
		||||
      if (animationId) cancelAnimationFrame(animationId);
 | 
			
		||||
      video.pause();
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function tick() {
 | 
			
		||||
      if (!scanning) return;
 | 
			
		||||
      if (video.readyState === video.HAVE_ENOUGH_DATA) {
 | 
			
		||||
        const canvas = document.createElement('canvas');
 | 
			
		||||
        canvas.width = video.videoWidth;
 | 
			
		||||
        canvas.height = video.videoHeight;
 | 
			
		||||
        const ctx = canvas.getContext('2d');
 | 
			
		||||
        ctx.drawImage(video, 0, 0, canvas.width, canvas.height);
 | 
			
		||||
        const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
 | 
			
		||||
        const code = jsQR(imageData.data, imageData.width, imageData.height);
 | 
			
		||||
        if (code && code.data) {
 | 
			
		||||
          handleResult(code.data);
 | 
			
		||||
          return;
 | 
			
		||||
        }
 | 
			
		||||
      }
 | 
			
		||||
      animationId = requestAnimationFrame(tick);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    function handleResult(data) {
 | 
			
		||||
      stopScanner();
 | 
			
		||||
      if (isSpotifyUrl(data)) {
 | 
			
		||||
        const embedUrl = getSpotifyEmbed(data);
 | 
			
		||||
        if (embedUrl) {
 | 
			
		||||
          resultDiv.innerHTML = `<span>Spotify-Link erkannt:</span><br><a href="${data}" target="_blank" style="color:#1DB954">${data}</a>`;
 | 
			
		||||
          embedDiv.innerHTML = `<iframe src="${embedUrl}" allow="encrypted-media"></iframe>`;
 | 
			
		||||
        } else {
 | 
			
		||||
          resultDiv.textContent = "Spotify-Link erkannt, aber kein unterstützter Typ.";
 | 
			
		||||
        }
 | 
			
		||||
      } else {
 | 
			
		||||
        resultDiv.textContent = "Kein gültiger Spotify-Link erkannt.";
 | 
			
		||||
      }
 | 
			
		||||
      backBtn.style.display = 'inline-block';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    backBtn.onclick = () => {
 | 
			
		||||
      startScanner();
 | 
			
		||||
    };
 | 
			
		||||
 | 
			
		||||
    window.onload = startScanner;
 | 
			
		||||
    window.onpagehide = () => {
 | 
			
		||||
      if (stream) {
 | 
			
		||||
        stream.getTracks().forEach(track => track.stop());
 | 
			
		||||
        stream = null;
 | 
			
		||||
      }
 | 
			
		||||
    };
 | 
			
		||||
  </script>
 | 
			
		||||
</body>
 | 
			
		||||
</html>
 | 
			
		||||
		Reference in New Issue
	
	Block a user