{"id":316,"date":"2025-10-18T22:29:40","date_gmt":"2025-10-18T22:29:40","guid":{"rendered":"https:\/\/gltecbrasil.com.br\/?page_id=316"},"modified":"2025-10-19T00:43:34","modified_gmt":"2025-10-19T00:43:34","slug":"pagina-clientes-gl-cast","status":"publish","type":"page","link":"https:\/\/gltecbrasil.com.br\/index.php\/pagina-clientes-gl-cast\/","title":{"rendered":"PAGINA CLIENTES GL CAST"},"content":{"rendered":"\t\t<div data-elementor-type=\"wp-page\" data-elementor-id=\"316\" class=\"elementor elementor-316\">\n\t\t\t\t<div class=\"elementor-element elementor-element-18cabe6 e-flex e-con-boxed e-con e-parent\" data-id=\"18cabe6\" data-element_type=\"container\">\n\t\t\t\t\t<div class=\"e-con-inner\">\n\t\t\t\t<div class=\"elementor-element elementor-element-a5d1763 elementor-widget elementor-widget-html\" data-id=\"a5d1763\" data-element_type=\"widget\" data-widget_type=\"html.default\">\n\t\t\t\t\t<!doctype html>\n<html lang=\"pt-br\">\n<head>\n  <meta charset=\"utf-8\" \/>\n  <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\" \/>\n  <title>R\u00e1dio F\u00e1cil \u2014 Player com V\u00eddeo do YouTube<\/title>\n  <style>\n    :root {\n      --bg1: #0f0c29; \/* roxo escuro *\/\n      --bg2: #302b63; \/* roxo m\u00e9dio *\/\n      --bg3: #24243e; \/* roxo azulado *\/\n      --card: rgba(255,255,255,0.08);\n      --border: rgba(255,255,255,0.15);\n      --text: #f8fafc;\n      --muted: #cbd5e1;\n      --accent: #ffffff;\n    }\n    html, body { height: 100%; }\n    body {\n      margin: 0;\n      font-family: system-ui, -apple-system, Segoe UI, Roboto, Ubuntu, Cantarell, Noto Sans, Arial, \"Apple Color Emoji\", \"Segoe UI Emoji\";\n      color: var(--text);\n      background: radial-gradient(1200px 800px at 20% 10%, #3b82f6 0%, transparent 60%),\n                  radial-gradient(1000px 600px at 80% 80%, #a78bfa 0%, transparent 60%),\n                  linear-gradient(135deg, var(--bg1), var(--bg2), var(--bg3));\n      background-attachment: fixed;\n      display: grid;\n      place-items: center; \/* centraliza tudo na tela *\/\n      padding: 24px;\n    }\n    .wrap {\n      width: min(1100px, 100%);\n      display: grid;\n      grid-template-columns: 1fr;\n      gap: 20px;\n    }\n    @media (min-width: 960px) {\n      .wrap { grid-template-columns: 1fr 1fr; }\n    }\n\n    .card {\n      background: var(--card);\n      border: 1px solid var(--border);\n      backdrop-filter: blur(6px);\n      border-radius: 18px;\n      padding: 20px;\n      box-shadow: 0 10px 40px rgba(0,0,0,0.25);\n    }\n    .title {\n      display: flex; align-items: center; gap: 10px; font-weight: 700; font-size: 20px;\n    }\n    .row { display: flex; gap: 12px; align-items: center; }\n    .row.spread { justify-content: space-between; }\n\n    .btn {\n      appearance: none; border: 1px solid var(--border); background: #111827; color: var(--accent);\n      padding: 10px 16px; border-radius: 14px; cursor: pointer; font-weight: 600;\n    }\n    .btn:hover { filter: brightness(1.08); }\n    .btn:active { transform: translateY(1px); }\n\n    \/* Bot\u00f5es redondos com \u00edcones SVG para Play\/Stop *\/\n    .icon-btn { width: 56px; height: 56px; border-radius: 9999px; display: inline-flex; align-items: center; justify-content: center; border: 1px solid var(--border); background: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0.02)); box-shadow: 0 6px 20px rgba(0,0,0,0.25); cursor: pointer; }\n    .icon-btn:hover { filter: brightness(1.1); transform: translateY(-1px); }\n    .icon-btn:active { transform: translateY(0); }\n    .icon-btn svg { width: 22px; height: 22px; fill: var(--accent); }\n\n    .range { width: 180px; }\n    input[type=\"range\"] { width: 100%; }\n\n    .np-label { font-size: 12px; color: var(--muted); letter-spacing: .08em; text-transform: uppercase; }\n    .np-text { font-size: 18px; font-weight: 600; margin-top: 4px; }\n\n    .hint { font-size: 12px; color: var(--muted); }\n\n    \/* v\u00eddeo centralizado com aspecto 16:9 *\/\n    .video-shell { position: relative; width: 100%; aspect-ratio: 16 \/ 9; border-radius: 16px; overflow: hidden; background: #000; }\n    .video-shell iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; pointer-events: none; }\n\n    .input {\n      background: rgba(255,255,255,0.06); border: 1px solid var(--border); color: var(--text);\n      border-radius: 12px; padding: 10px 12px; width: 100%; outline: none;\n    }\n    .input:focus { box-shadow: 0 0 0 2px rgba(255,255,255,0.15); }\n\n    .switch { display: inline-flex; gap: 8px; align-items: center; font-size: 14px; }\n    .muted-note { font-size: 12px; color: var(--muted); margin-top: 6px; }\n  <\/style>\n<\/head>\n<body>\n  <div class=\"wrap\">\n    <!-- CART\u00c3O \u00c1UDIO \/ CONTROLES -->\n    <section class=\"card\">\n      <div class=\"title\">\ud83d\udcfb R\u00e1dio F\u00e1cil<\/div>\n\n      <div class=\"row\" style=\"margin-top:12px\">\n        <button id=\"btnPlay\" class=\"icon-btn\" title=\"Tocar\">\n          <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M8 5v14l11-7z\"\/><\/svg>\n        <\/button>\n        <button id=\"btnStop\" class=\"icon-btn\" title=\"Parar\" style=\"display:none\">\n          <svg viewBox=\"0 0 24 24\" aria-hidden=\"true\"><path d=\"M6 6h12v12H6z\"\/><\/svg>\n        <\/button>\n        <div class=\"row\" style=\"margin-left:auto\">\n          <span style=\"font-size:14px\">\ud83d\udd0a<\/span>\n          <div class=\"range\"><input id=\"vol\" type=\"range\" min=\"0\" max=\"1\" step=\"0.01\" value=\"0.85\"><\/div>\n        <\/div>\n      <\/div>\n\n      <audio id=\"radio\" preload=\"none\" crossorigin=\"anonymous\"><\/audio>\n\n      <div style=\"margin-top:16px\">\n        <div class=\"np-label\">Agora tocando<\/div>\n        <div id=\"np\" class=\"np-text\">\u2014<\/div>\n        <div id=\"npHint\" class=\"hint\" style=\"margin-top:8px\">Atualizando metadados\u2026<\/div>\n      <\/div>\n    <\/section>\n\n    <!-- CART\u00c3O V\u00cdDEO (YouTube mudo) -->\n    <section id=\"videoCard\" class=\"card\">\n      <div class=\"title\">\ud83c\udfac V\u00eddeo da m\u00fasica (YouTube, mudo)<\/div>\n\n      <div class=\"row\" style=\"margin-top:12px; gap:10px\">\n        <input id=\"manual\" class=\"input\" placeholder=\"Busca manual (ex.: Artista - T\u00edtulo official video)\">\n        <label class=\"switch\"><input id=\"forceManual\" type=\"checkbox\"> Usar busca manual<\/label>\n        <button id=\"btnRefresh\" class=\"btn\">\u21bb Recarregar v\u00eddeo<\/button>\n      <\/div>\n\n      <div class=\"video-shell\" style=\"margin-top:12px\">\n        <iframe id=\"yt\" allow=\"autoplay; encrypted-media; picture-in-picture\" title=\"YouTube video\" style=\"pointer-events:none\"><\/iframe><\/iframe>\n      <\/div>\n      <div class=\"muted-note\">O v\u00eddeo \u00e9 sempre <b>mudo<\/b>. O som vem do streaming da r\u00e1dio.<\/div>\n    <\/section>\n  <\/div>\n\n  <script src=\"https:\/\/www.youtube.com\/iframe_api\"><\/script>\n  <script>\n    \/\/ ========= CONFIG =========\n    const STREAM_URL = \"https:\/\/glaudiotecnology.srv.br\/listen\/r%C3%A1dio_f%C3%A1cil\/radio.mp3\";\n    \/\/ Tente os endpoints do AzuraCast nesta ordem; deixe apenas o que funcionar melhor.\n    const NOW_PLAYING_URLS = [\n      \"https:\/\/glaudiotecnology.srv.br\/api\/nowplaying\/jhonson\",\n      \"https:\/\/glaudiotecnology.srv.br\/api\/nowplaying\/radio_facil\",\n      \"https:\/\/glaudiotecnology.srv.br\/api\/nowplaying\/r%C3%A1dio_f%C3%A1cil\",\n    ];\n    const POLL_INTERVAL = 20000; \/\/ ms\n    const DISABLE_VIDEO = false; \/\/ reativar v\u00eddeo do YouTube\n\n    \/\/ ========= ELEMENTOS =========\n    const elAudio = document.getElementById('radio');\n    const elPlay = document.getElementById('btnPlay');\n    const elStop = document.getElementById('btnStop');\n    const elVol = document.getElementById('vol');\n    const elNP = document.getElementById('np');\n    const elNPHint = document.getElementById('npHint');\n    const elYt = DISABLE_VIDEO ? null : document.getElementById('yt');\n    const elManual = DISABLE_VIDEO ? null : document.getElementById('manual');\n    const elForceManual = DISABLE_VIDEO ? null : document.getElementById('forceManual');\n    const elRefresh = DISABLE_VIDEO ? null : document.getElementById('btnRefresh');\n    if (DISABLE_VIDEO) {\n      const vc = document.getElementById('videoCard');\n      if (vc) vc.style.display = 'none';\n    }\n\n    \/\/ ====== YT Iframe API ======\n    let ytPlayer = null;\n    function onYouTubeIframeAPIReady() {\n      if (DISABLE_VIDEO || !document.getElementById('yt')) return;\n      ytPlayer = new YT.Player('yt', {\n        playerVars: {\n          autoplay: 0,\n          mute: 1,\n          controls: 0,\n          modestbranding: 1,\n          fs: 0,\n          disablekb: 1,\n          iv_load_policy: 3,\n          rel: 0,\n          playsinline: 1,\n          origin: window.location.origin\n        },\n        events: {\n          onReady: (ev) => { try { ev.target.mute(); } catch {} },\n          onStateChange: handleYTState,\n          onError: handleYTError,\n        }\n      });\n    }\n\n    function handleYTError() {\n      \/\/ tenta pular para o pr\u00f3ximo resultado da playlist\n      try { ytPlayer.nextVideo(); } catch {}\n    }\n\n    let lastState = -2;\n    function handleYTState(e) {\n      lastState = e.data; \/\/ -1 unstarted, 0 ended, 1 playing, 2 paused, 3 buffering, 5 cued\n      \/\/ Se ficou parado em estado \"unstarted\" por muito tempo, tenta avan\u00e7ar\n      if (e.data === -1) {\n        setTimeout(() => { if (lastState === -1) { try { ytPlayer.nextVideo(); } catch {} } }, 1500);\n      }\n    }\n\n    \/\/ ========= ESTADO =========\n    let isPlaying = false;\n    let nowRaw = '';\n    let artist, title;\n\n    \/\/ ========= INIT =========\n    elAudio.src = STREAM_URL;\n    elAudio.volume = parseFloat(elVol.value);\n\n    elPlay.addEventListener('click', async () => {\n      try {\n        await elAudio.play();\n        isPlaying = true;\n        elPlay.style.display = 'none';\n        elStop.style.display = '';\n        refreshVideo(); \/\/ no-op se v\u00eddeo estiver desligado\n      } catch (e) {\n        alert('O navegador bloqueou o autoplay. Clique em Tocar novamente.');\n      }\n    });\n\n    elStop.addEventListener('click', () => {\n      elAudio.pause();\n      try { elAudio.currentTime = 0; } catch {}\n      isPlaying = false;\n      elStop.style.display = 'none';\n      elPlay.style.display = '';\n      refreshVideo(); \/\/ no-op se v\u00eddeo estiver desligado\n    });\n\n    elVol.addEventListener('input', () => {\n      elAudio.volume = parseFloat(elVol.value);\n    });\n\n    if (elRefresh) elRefresh.addEventListener('click', () => refreshVideo(true));\n\n    \/\/ ========= NOW PLAYING POLL =========\n    async function fetchNowPlayingOnce() {\n      for (const url of NOW_PLAYING_URLS) {\n        try {\n          const res = await fetch(url, { cache: 'no-store' });\n          if (!res.ok) throw new Error('HTTP ' + res.status);\n          const data = await res.json();\n\n          \/\/ Tenta alguns formatos comuns do AzuraCast\n          const azSong = data?.now_playing?.song || data?.live?.current_song || data?.song;\n          let raw, art, ttl;\n          if (azSong) {\n            art = azSong.artist || undefined;\n            ttl = azSong.title || undefined;\n            raw = azSong.text || [art, ttl].filter(Boolean).join(' - ');\n          }\n\n          \/\/ Fallbacks adicionais\n          if (!raw) {\n            const anyTitle = data?.title || data?.now_playing || data?.track || data?.song;\n            if (anyTitle) {\n              raw = String(anyTitle);\n            }\n          }\n\n          if (raw) {\n            nowRaw = String(raw);\n            const st = nowRaw.split(' - ');\n            artist = st.length >= 2 ? st[0].trim() : undefined;\n            title  = st.length >= 2 ? st.slice(1).join(' - ').trim() : nowRaw.trim();\n            return true;\n          }\n        } catch (e) { \/* tenta o pr\u00f3ximo *\/ }\n      }\n      return false;\n    }\n\n    function makeYouTubeQuery() {\n      if (DISABLE_VIDEO) return '';\n      if (elForceManual && elForceManual.checked && elManual && elManual.value.trim()) return elManual.value.trim();\n      const base = [artist, title].filter(Boolean).join(' - ') || nowRaw || '';\n      if (!base) return '';\n      return base; \/\/ sem sufixo \"official video\" para ampliar resultados\n    }\n\n    function buildYouTubeEmbed(query) {\n      if (DISABLE_VIDEO) return '';\n      \/\/ N\u00e3o usamos mais o src direto; o player API carrega playlist de busca\n      const u = new URL('https:\/\/www.youtube.com\/embed');\n      u.searchParams.set('listType', 'search');\n      u.searchParams.set('list', query);\n      u.searchParams.set('enablejsapi', '1');\n      u.searchParams.set('autoplay', isPlaying ? '1' : '0');\n      u.searchParams.set('mute', '1');\n      u.searchParams.set('controls', '0');\n      u.searchParams.set('modestbranding', '1');\n      u.searchParams.set('fs', '0');\n      u.searchParams.set('disablekb', '1');\n      u.searchParams.set('iv_load_policy', '3');\n      u.searchParams.set('rel', '0');\n      u.searchParams.set('playsinline', '1');\n      return u.toString();\n    }\n\n    function refreshVideo(force = false) {\n      if (DISABLE_VIDEO || !document.getElementById('yt')) return; \/\/ no-op quando v\u00eddeo desativado\n      const baseQ = makeYouTubeQuery();\n      if (!baseQ && !force) return;\n\n      \/\/ Estrat\u00e9gia de tentativas com consultas mais amplas caso o primeiro resultado n\u00e3o toque\n      const candidates = Array.from(new Set([\n        baseQ,\n        baseQ + ' audio',\n        baseQ + ' lyrics',\n        baseQ + ' live',\n        (artist ? artist + ' ' + (title || '') : baseQ),\n        title || baseQ,\n      ].map(s => s.trim()).filter(Boolean)));\n\n      \/\/ Se o player j\u00e1 existe, carrega a primeira consulta; ao receber erro, chamamos nextVideo();\n      const load = (q) => {\n        try {\n          if (ytPlayer && ytPlayer.loadPlaylist) {\n            ytPlayer.loadPlaylist({ listType: 'search', list: q });\n            if (isPlaying) ytPlayer.playVideo();\n          } else {\n            \/\/ Primeiro boot: define o src do iframe\n            const src = buildYouTubeEmbed(q);\n            document.getElementById('yt').src = src;\n          }\n        } catch {}\n      };\n\n      \/\/ Tenta em sequ\u00eancia: se o primeiro termo n\u00e3o funcionar, o usu\u00e1rio pode clicar \"Recarregar\" para tentar o pr\u00f3ximo\n      let idx = 0;\n      load(candidates[idx]);\n\n      if (elRefresh) {\n        elRefresh.onclick = () => {\n          idx = (idx + 1) % candidates.length;\n          load(candidates[idx]);\n        };\n      }\n    }\n\n    async function updateNP() {\n      const ok = await fetchNowPlayingOnce();\n      if (ok) {\n        elNP.textContent = (artist ? (artist + ' \u2014 ') : '') + (title || nowRaw || '');\n        elNPHint.textContent = 'Atualizando a cada ' + (POLL_INTERVAL \/ 1000) + 's (AzuraCast)';\n        refreshVideo(); \/\/ no-op se v\u00eddeo estiver desligado\n      } else {\n        elNPHint.textContent = 'N\u00e3o consegui ler o Now Playing. Verifique CORS e o endpoint.';\n      }\n    }\n\n    \/\/ Primeira carga + polling\n    updateNP();\n    setInterval(updateNP, POLL_INTERVAL);\n  <\/script>\n<\/body>\n<\/html>\n\t\t\t\t<\/div>\n\t\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t\t\t<\/div>\n\t\t","protected":false},"excerpt":{"rendered":"<p>R\u00e1dio F\u00e1cil \u2014 Player com V\u00eddeo do YouTube \ud83d\udcfb R\u00e1dio F\u00e1cil \ud83d\udd0a Agora tocando \u2014 Atualizando metadados\u2026 \ud83c\udfac V\u00eddeo da m\u00fasica (YouTube, mudo) Usar busca manual \u21bb Recarregar v\u00eddeo O v\u00eddeo \u00e9 sempre mudo. O som vem do streaming da r\u00e1dio.<\/p>\n","protected":false},"author":1,"featured_media":0,"parent":0,"menu_order":0,"comment_status":"closed","ping_status":"closed","template":"elementor_canvas","meta":{"footnotes":""},"class_list":["post-316","page","type-page","status-publish","hentry"],"_links":{"self":[{"href":"https:\/\/gltecbrasil.com.br\/index.php\/wp-json\/wp\/v2\/pages\/316","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/gltecbrasil.com.br\/index.php\/wp-json\/wp\/v2\/pages"}],"about":[{"href":"https:\/\/gltecbrasil.com.br\/index.php\/wp-json\/wp\/v2\/types\/page"}],"author":[{"embeddable":true,"href":"https:\/\/gltecbrasil.com.br\/index.php\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/gltecbrasil.com.br\/index.php\/wp-json\/wp\/v2\/comments?post=316"}],"version-history":[{"count":13,"href":"https:\/\/gltecbrasil.com.br\/index.php\/wp-json\/wp\/v2\/pages\/316\/revisions"}],"predecessor-version":[{"id":334,"href":"https:\/\/gltecbrasil.com.br\/index.php\/wp-json\/wp\/v2\/pages\/316\/revisions\/334"}],"wp:attachment":[{"href":"https:\/\/gltecbrasil.com.br\/index.php\/wp-json\/wp\/v2\/media?parent=316"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}