{"id":1663,"date":"2026-10-24T15:03:17","date_gmt":"2026-10-24T13:03:17","guid":{"rendered":"https:\/\/betterthansilence.de\/radio\/?p=1663"},"modified":"2026-10-24T15:38:56","modified_gmt":"2026-10-24T13:38:56","slug":"reinschnuppern","status":"publish","type":"post","link":"https:\/\/betterthansilence.de\/radio\/reinschnuppern\/","title":{"rendered":"ALLE SONGS &#8211; SOFORT REINH\u00d6REN"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Hier findet ihr alle Songs, die wir auf BETTER THAN SILENCE bisher gespielt haben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein KLICK auf den TIDAL-Miniplayer und ihr k\u00f6nnt reinh\u00f6ren (oder komplett h\u00f6ren, falls ihr TIDAL habt).<\/p>\n\n\n\n<style>\n  .music-container {\n    font-family: inherit;\n    font-size: var(--wp--preset--font-size--small, 0.875rem);\n    margin: 20px 0;\n  }\n\n  .music-stats {\n    font-size: 1em;\n    font-weight: 600;\n    margin-bottom: 10px;\n    color: var(--wp--preset--color--primary, #222222);\n  }\n\n  .music-search {\n    width: 100%;\n    padding: 8px 12px;\n    margin-bottom: 12px;\n    border: 1px solid #ccc;\n    border-radius: 5px;\n    font-size: inherit;\n    box-sizing: border-box;\n  }\n\n  .music-table-wrap {\n    width: 100%;\n    overflow-x: auto;\n  }\n\n  .music-table {\n    width: 100%;\n    min-width: 450px;\n    table-layout: fixed;\n    border-collapse: collapse;\n    margin-top: 10px;\n    font-size: inherit;\n  }\n\n  .music-table th {\n    background-color: var(--wp--preset--color--primary, #222222);\n    color: var(--wp--preset--color--white, #ffffff);\n    padding: 8px 10px;\n    text-align: left;\n    cursor: pointer;\n    user-select: none;\n  }\n\n  .music-table th:hover {\n    opacity: 0.9;\n  }\n\n  .music-table th.sort-asc::after {\n    content: \" \u25b2\";\n    font-size: 0.8em;\n  }\n\n  .music-table th.sort-desc::after {\n    content: \" \u25bc\";\n    font-size: 0.8em;\n  }\n\n  .music-table td {\n    padding: 10px;\n    border-bottom: 1px solid #e0e0e0;\n    vertical-align: top;\n    overflow-wrap: anywhere;\n  }\n\n  .music-table tr:nth-child(even) {\n    background-color: #f9f9f9;\n  }\n\n  .music-table th:first-child,\n  .music-table td:first-child {\n    width: 330px;\n  }\n\n  .music-table th:last-child,\n  .music-table td:last-child {\n    width: 120px;\n    text-align: center;\n  }\n\n  .music-tidal-player {\n    display: block;\n    width: 300px;\n    max-width: 100%;\n    height: 100px;\n    border: 0;\n    margin: 0 0 8px;\n  }\n\n  .music-track-info {\n    display: block;\n    line-height: 1.4;\n  }\n\n  @media (max-width: 480px) {\n    .music-table {\n      min-width: 420px;\n    }\n\n    .music-table th:first-child,\n    .music-table td:first-child {\n      width: 300px;\n    }\n\n    .music-table th:last-child,\n    .music-table td:last-child {\n      width: 120px;\n    }\n\n    .music-tidal-player {\n      width: 260px;\n      height: 100px;\n    }\n  }\n<\/style>\n\n<div class=\"music-container\">\n  <div id=\"musicStats\" class=\"music-stats\">Lade Statistik&#8230;<\/div>\n\n  <input\n    type=\"text\"\n    id=\"musicSearch\"\n    class=\"music-search\"\n    placeholder=\"\ud83d\udd0d Suche nach Interpret, Titel, Album oder Sendung...\"\n  >\n\n  <div class=\"music-table-wrap\">\n    <table class=\"music-table\">\n      <thead>\n        <tr>\n          <th onclick=\"sortTable('title')\" id=\"th-title\">Interpret \u2013 Titel (Album)<\/th>\n          <th onclick=\"sortTable('playlist')\" id=\"th-playlist\">Sendung<\/th>\n        <\/tr>\n      <\/thead>\n      <tbody id=\"musicTableBody\">\n        <tr>\n          <td colspan=\"2\">Lade Musikbibliothek&#8230;<\/td>\n        <\/tr>\n      <\/tbody>\n    <\/table>\n  <\/div>\n<\/div>\n\n<script>\n  const csvUrl =\n    'https:\/\/betterthansilence.de\/wp-content\/uploads\/csv\/MyDeezerLibrary.csv';\n\n  let globalData = [];\n  let currentSortKey = '';\n  let currentSortAsc = true;\n\n  function loadPapaParse() {\n    return new Promise((resolve, reject) => {\n      if (window.Papa) {\n        resolve();\n        return;\n      }\n\n      const script = document.createElement('script');\n      script.src =\n        'https:\/\/cdn.jsdelivr.net\/npm\/papaparse@5.4.1\/papaparse.min.js';\n\n      script.onload = () => resolve();\n      script.onerror = () => {\n        reject(new Error('PapaParse konnte nicht geladen werden.'));\n      };\n\n      document.head.appendChild(script);\n    });\n  }\n\n  function getValue(row, keys) {\n    for (const key of keys) {\n      if (row[key] !== undefined && row[key] !== null) {\n        return row[key];\n      }\n    }\n\n    return '';\n  }\n\n  function getTitle(row) {\n    return getValue(row, ['Track name', 'Titel', 'Title', 'Track']);\n  }\n\n  function getArtist(row) {\n    return getValue(row, ['Artist name', 'Interpret', 'Artist']);\n  }\n\n  function getAlbum(row) {\n    return getValue(row, ['Album', 'Album name']);\n  }\n\n  function getPlaylist(row) {\n    const rawValue = getValue(row, [\n      'Playlist name',\n      'Playlist-Nummer',\n      'Sendung',\n      'Playlist'\n    ]);\n\n    if (!rawValue) return '';\n\n    const match = String(rawValue).match(\/(?:#|NI)?\\s*(\\d+)\/i);\n    return match ? parseInt(match[1], 10) : rawValue;\n  }\n\n  function getTidalId(row) {\n    return String(\n      getValue(row, ['Tidal - id', 'Tidal - ID', 'Tidal ID'])\n    ).trim();\n  }\n\n  function updateStats() {\n    const sendungenSet = new Set();\n\n    globalData.forEach(row => {\n      const sendung = getPlaylist(row);\n\n      if (sendung !== '') {\n        sendungenSet.add(sendung);\n      }\n    });\n\n    const statsElem = document.getElementById('musicStats');\n\n    if (globalData.length > 0) {\n      statsElem.textContent =\n        `\ud83c\udfb5 ${globalData.length} Songs aus ${sendungenSet.size} Sendungen`;\n    } else {\n      statsElem.textContent = '';\n    }\n  }\n\n  function renderData(data) {\n    const tbody = document.getElementById('musicTableBody');\n    tbody.replaceChildren();\n\n    if (!data || data.length === 0) {\n      const tr = document.createElement('tr');\n      const td = document.createElement('td');\n\n      td.colSpan = 2;\n      td.textContent = 'Keine Daten gefunden.';\n      tr.appendChild(td);\n      tbody.appendChild(tr);\n      return;\n    }\n\n    data.forEach(row => {\n      const title = String(getTitle(row));\n      const artist = String(getArtist(row));\n      const album = String(getAlbum(row));\n      const playlistNum = getPlaylist(row);\n      const tidalId = getTidalId(row);\n\n      if (!title && !artist) return;\n\n      const tr = document.createElement('tr');\n\n      tr.dataset.search = [\n        artist,\n        title,\n        album,\n        playlistNum,\n        tidalId\n      ].join(' ').toLowerCase();\n\n      \/\/ Erste Zelle: Player und darunter \u201eInterpret \u2013 Titel (Album)\u201c.\n      const trackCell = document.createElement('td');\n\n      if (\/^[\\w-]+$\/.test(tidalId)) {\n        const player = document.createElement('iframe');\n\n        player.src =\n          `https:\/\/embed.tidal.com\/tracks\/${encodeURIComponent(tidalId)}`;\n        player.title = `TIDAL-Player: ${title || 'Track'}`;\n        player.width = '300';\n        player.height = '100';\n        player.loading = 'lazy';\n        player.setAttribute('allow', 'encrypted-media *; fullscreen *;');\n        player.setAttribute('allowfullscreen', '');\n        player.setAttribute('frameborder', '0');\n        player.className = 'music-tidal-player';\n\n        trackCell.appendChild(player);\n      }\n\n      const trackInfo = document.createElement('span');\n      trackInfo.className = 'music-track-info';\n\n      let displayText = '';\n\n      if (artist && title) {\n        displayText = `${artist} \u2013 ${title}`;\n      } else {\n        displayText = artist || title;\n      }\n\n      if (album) {\n        displayText += ` (${album})`;\n      }\n\n      trackInfo.textContent = displayText;\n      trackCell.appendChild(trackInfo);\n\n      \/\/ Zweite Zelle: Sendungsnummer.\n      const playlistCell = document.createElement('td');\n      playlistCell.textContent = String(playlistNum);\n\n      tr.append(trackCell, playlistCell);\n      tbody.appendChild(tr);\n    });\n\n    filterMusicTable();\n  }\n\n  function sortTable(key) {\n    if (currentSortKey === key) {\n      currentSortAsc = !currentSortAsc;\n    } else {\n      currentSortKey = key;\n      currentSortAsc = true;\n    }\n\n    document.querySelectorAll('.music-table th').forEach(th => {\n      th.classList.remove('sort-asc', 'sort-desc');\n    });\n\n    const activeHeader = document.getElementById('th-' + key);\n\n    if (activeHeader) {\n      activeHeader.classList.add(\n        currentSortAsc ? 'sort-asc' : 'sort-desc'\n      );\n    }\n\n    globalData.sort((a, b) => {\n      let valA;\n      let valB;\n\n      if (key === 'title') {\n        valA = `${getArtist(a)} ${getTitle(a)} ${getAlbum(a)}`;\n        valB = `${getArtist(b)} ${getTitle(b)} ${getAlbum(b)}`;\n      } else {\n        valA = getPlaylist(a);\n        valB = getPlaylist(b);\n\n        if (typeof valA === 'number' && typeof valB === 'number') {\n          return currentSortAsc ? valA - valB : valB - valA;\n        }\n      }\n\n      const comparison = String(valA).localeCompare(\n        String(valB),\n        undefined,\n        { numeric: true, sensitivity: 'base' }\n      );\n\n      return currentSortAsc ? comparison : -comparison;\n    });\n\n    renderData(globalData);\n  }\n\n  function filterMusicTable() {\n    const searchInput = document.getElementById('musicSearch');\n    const filter = searchInput.value.toLowerCase().trim();\n    const rows = document.querySelectorAll('#musicTableBody tr');\n    let visibleCount = 0;\n\n    rows.forEach(row => {\n      const searchableText =\n        row.dataset.search || row.innerText.toLowerCase();\n      const matches = searchableText.includes(filter);\n\n      row.style.display = matches ? '' : 'none';\n\n      if (matches) {\n        visibleCount++;\n      }\n    });\n\n    const statsElem = document.getElementById('musicStats');\n\n    if (filter !== '') {\n      statsElem.textContent =\n        `\ud83d\udd0d Zeige ${visibleCount} von ${globalData.length} Songs`;\n    } else {\n      updateStats();\n    }\n  }\n\n  function showLoadError() {\n    const tbody = document.getElementById('musicTableBody');\n    const tr = document.createElement('tr');\n    const td = document.createElement('td');\n\n    td.colSpan = 2;\n    td.style.color = 'red';\n    td.textContent = 'Fehler beim Laden der CSV-Datei.';\n\n    tr.appendChild(td);\n    tbody.replaceChildren(tr);\n\n    document.getElementById('musicStats').textContent = '';\n  }\n\n  async function initMusicTable() {\n    try {\n      await loadPapaParse();\n\n      const response = await fetch(csvUrl);\n\n      if (!response.ok) {\n        throw new Error('HTTP Fehler: ' + response.status);\n      }\n\n      const csvText = await response.text();\n\n      Papa.parse(csvText, {\n        header: true,\n        skipEmptyLines: true,\n\n        complete: function(results) {\n          if (results.errors && results.errors.length > 0) {\n            console.warn('CSV-Hinweise:', results.errors);\n          }\n\n          globalData = results.data.filter(row => {\n            return getTitle(row) || getArtist(row);\n          });\n\n          updateStats();\n          sortTable('title');\n        },\n\n        error: function(error) {\n          console.error('CSV-Fehler:', error);\n          showLoadError();\n        }\n      });\n    } catch (error) {\n      console.error('Fehler beim Laden:', error);\n      showLoadError();\n    }\n  }\n\n  document\n    .getElementById('musicSearch')\n    .addEventListener('input', filterMusicTable);\n\n  initMusicTable();\n<\/script>\n","protected":false},"excerpt":{"rendered":"<p>Jetzt k\u00f6nnt ihr auch direkt in alle gespielten Songs reinh\u00f6ren &#8230;<\/p>\n","protected":false},"author":1,"featured_media":49,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[],"class_list":["post-1663","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-beitrag"],"_links":{"self":[{"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/posts\/1663","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/comments?post=1663"}],"version-history":[{"count":10,"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/posts\/1663\/revisions"}],"predecessor-version":[{"id":1677,"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/posts\/1663\/revisions\/1677"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/media\/49"}],"wp:attachment":[{"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/media?parent=1663"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/categories?post=1663"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/betterthansilence.de\/radio\/wp-json\/wp\/v2\/tags?post=1663"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}