GIF89a=( õ' 7IAXKgNgYvYx\%wh…hŽth%ˆs%—x¨}9®Œ©€&©‰%¶†(¹–.¹5·œD¹&Çš)ÇŸ5ǘ;Í£*È¡&Õ²)ׯ7×µ<Ñ»4ï°3ø‘HÖ§KͯT÷¨Yÿšqÿ»qÿÔFØ !ù ' !ÿ NETSCAPE2.0 , =( þÀ“pH,È¤rÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿à°xL.›Ïè´zÍn»ßð¸|N¯Ûïø¼~Ïïûÿ€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§gª«ªE¯°¨¬ª±²Œ¹º¹E¾­”´ÂB¶¯ §Åȸ»ÑD¾¿Á•ÄÅ®° ÝH¾ÒLÀÆDÙ«D¶BÝïðÀ¾DÑÑÔTÌÍíH òGö¨A RÎڐ |¥ ٭&ºìE8œ¹kGÔAÞpx­a¶­ã R2XB®åE8I€Õ6Xî:vT)äžþÀq¦è³¥ì仕F~%xñ  4#ZÔ‰O|-4Bs‘X:= QÉ œš lºÒyXJŠGȦ|s hÏíK–3l7·B|¥$'7Jީܪ‰‡àá”Dæn=Pƒ ¤Òëí‰`䌨ljóá¯Éüv>á–Á¼5 ½.69ûϸd«­ºÀûnlv©‹ªîf{¬ÜãPbŸ  l5‘ޝpß ´ ˜3aÅùäI«O’ý·‘áÞ‡˜¾Æ‚ÙÏiÇÿ‹Àƒ #öó)pâš Þ½ ‘Ý{ó)vmÞü%D~ 6f s}ŃƒDØW Eþ`‡þ À…L8xá†ç˜{)x`X/> Ì}mø‚–RØ‘*|`D=‚Ø_ ^ð5 !_…'aä“OÚ—7âcð`D”Cx`ÝÂ¥ä‹éY¹—F¼¤¥Š?¡Õ™ n@`} lď’ÄÉ@4>ñd œ à‘vÒxNÃ×™@žd=ˆgsžG±æ ´²æud &p8Qñ)ˆ«lXD©øÜéAžHìySun jª×k*D¤LH] †¦§C™Jä–´Xb~ʪwStŽ6K,°£qÁœ:9ت:¨þªl¨@¡`‚ûÚ ».Û¬¯t‹ÆSÉ[:°=Š‹„‘Nåû”Ìî{¿ÂA ‡Rà›ÀÙ6úë°Ÿð0Ä_ ½;ÃϱîÉì^ÇÛÇ#Ëë¼ôº!±Ä˜íUîÅÇ;0L1óÁµö«p% AÀºU̬ݵ¼á%霼€‡¯Á~`ÏG¯»À× ­²± =4ªnpð3¾¤³¯­ü¾¦îuÙuµÙ®|%2ÊIÿür¦#0·ÔJ``8È@S@5ê¢ ö×Þ^`8EÜ]ý.뜃Âç 7 ú ȉÞj œ½Dç zý¸iþœÑÙûÄë!ˆÞÀl§Ïw‹*DçI€nEX¯¬¼ &A¬Go¼QföõFç°¯;é¦÷îŽêJ°îúôF5¡ÌQ|îúöXªæ»TÁÏyñêï]ê² o óÎC=öõ›ÒÓPB@ D×½œä(>èCÂxŽ`±«Ÿ–JЀ»Û á¤±p+eE0`ëŽ`A Ú/NE€Ø†À9‚@¤à H½7”à‡%B‰`Àl*ƒó‘–‡8 2ñ%¸ —€:Ù1Á‰E¸àux%nP1ð!‘ðC)¾P81lÑɸF#ˆ€{´âé°ÈB„0>±û °b¡Š´±O‚3È–Ù()yRpbµ¨E.Z‘D8ÊH@% òŒx+%Ù˜Æcü »¸˜fõ¬b·d`Fê™8èXH"ÉÈ-±|1Ô6iI, 2““¬$+](A*jÐ QTÂo‰.ÛU슬Œã„Ž`¯SN¡–¶Äåyše¯ª’­¬‚´b¦Éož œ)åyâ@Ì®3 ÎtT̉°&Ø+žLÀf"Ø-|žçÔ>‡Ðv¦Ðžì\‚ Q1)Ž@Žh#aP72”ˆ™¨$‚ !ù " , =( …7IAXG]KgNgYvYxR"k\%w]'}hŽth%ˆg+ˆs%—r.—m3šx3˜x¨}9®€&©€+¨‡7§‰%¶†(¹–.¹œD¹&ǘ;Í•&ײ)×»4ïÌ6ò§KÍ þ@‘pH,È¤rÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿à°xL.›Ïè´zÍn»ßð¸|N¯Ûïø¼~Ïïûÿ€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§g «¬ E ±± ¨­¶°ººE Á´”·®C¬²§Ç¶Œ»ÓDÃÕƷ¯Ê±H½ºM×ÁGÚ¬D¶BËÁ½î½DÓôTÏÛßîG»ôõC×CÌ l&âž:'òtU³6ɹ#·Ø)€'Ü.6±&ëÍÈ» K(8p0N?!æ2"ÛˆNIJX>R¼ÐO‚M '¡¨2¸*Ÿþ>#n↠å@‚<[:¡Iïf’ ¤TÚ˘CdbÜÙ“[«ŽEú5MBo¤×@€`@„€Êt W-3 ¶Ÿ¡BíêäjIÝ…Eò9[T…$íêﯧ„…•s»Óȳ¹€ÅÚdc®UUρ#±Ùïldj?´í¼²`\ŽÁðÞu|3'ÖŒ]ë6 ¶S#²‡˜FKLÈ *N E´‘áäŠ$˜›eÄYD„ºq«.è촁ƒs \-ÔjA 9²õ÷å- üúM[Âx(ís÷ì®x€|í¡Ù’p¦‚ ŽkÛTÇDpE@WÜ ²Ç]kŠ1¨ þ€·Yb ÓÁ‰l°*n0 ç™—žzBdОu¾7ĉBl€â‰-ºx~|UåU‰  h*Hœ|e"#"?vpÄiŠe6^ˆ„+qâŠm8 #VÇá ‘å–ÄV„œ|Аè•m"сœn|@›U¶ÆÎž—Špb¥G¨ED”€±Úê2FÌIç? >Éxå Œ± ¡¤„%‘žjŸ‘ꄯ<Ìaà9ijÐ2˜D¦È&›†Z`‚å]wþ¼Â:ç6àB¤7eFJ|õÒ§Õ,¨äàFÇ®cS·Ê¶+B°,‘Þ˜ºNûãØ>PADÌHD¹æž«ÄÀnÌ¥}­#Ë’ë QÀÉSÌÂÇ2ÌXÀ{æk²lQÁ2«ÊðÀ¯w|2Í h‹ÄÂG€,m¾¶ë3ÐÙ6-´ÅE¬L°ÆIij*K½ÀÇqï`DwVÍQXœÚÔpeœ±¬Ñ q˜§Tœ½µƒ°Œìu Â<¶aØ*At¯lmEØ ü ôÛN[P1ÔÛ¦­±$ÜÆ@`ùåDpy¶yXvCAyåB`ŽD¶ 0QwG#¯ æš[^Äþ $ÀÓÝǦ{„L™[±úKÄgÌ;ï£S~¹ìGX.ôgoT.»åˆ°ùŸûù¡?1zö¦Ÿž:ÅgÁ|ìL¹ „®£œŠ‚à0œ]PÁ^p F<"•ç?!,ñ‡N4—…PÄ Á„ö¨Û:Tè@hÀ‹%táÿ:ø-žI<`þ‹p I….)^ 40D#p@ƒj4–؀:²‰1Øâr˜¼F2oW¼#Z†;$Q q” ‘ ÂK¦ñNl#29 !’F@¥Bh·ᏀL!—XFóLH‘Kh¤.«hE&JòG¨¥<™WN!€ÑÙÚˆY„@†>Œž19J" 2,/ &.GXB%ÌRÈ9B6¹W]’î×ÔW¥’IÎ$ ñ‹ÓŒE8YÆ ¼³™ñA5“à®Q.aŸB€&Ø©³ JÁ—! ¦t)K%tœ-¦JF bòNMxLôþ)ÐR¸Ð™‘ èÝ6‘O!THÌ„HÛ ‰ !ù ) , =( …AXKgNgYvYxR"k\%wh…hŽh%ˆg+ˆs%—r.—x3˜x¨}9®€&©€+¨Œ,©‡7§‰%¶†(¹–.¹5·&Çš)ǘ;Í•&×£*Ȳ)ׯ7×»4ï°3øÌ6ò‘HÖ§KÍ»Hó¯T÷¨Yÿ»qÿÇhÿ þÀ”pH,È¤rÉl:ŸÐ¨tJ­Z¯Ø¬vËíz¿à°xL.›Ïè´zÍn»ßð¸|N¯Ûïø¼~Ïïûÿ€‚ƒ„…†‡ˆ‰Š‹ŒŽ‘’“”•–—˜™š›œžŸ ¡¢£¤¥¦§g ª« E$±²¨ª­ · °²½$E$ÂÕ««D· Í ¿¦Ç¶¸ÌŒ¾³CÃÅÆ E ééH½MÛÂGâªD­ çBêêϾD²ÒaÀà€Š1r­ðÓ¤ ÔožzU!L˜C'¾yW½UGtäÇïÙllê0×àÂuGþ)AÀs[þ·xì ÁxO%ƒûX2ó—  P£n›R/¡ÑšHše+êDm?# —‘Ç£6¡8íJ¡ŸâDiäªM¥Ö„ôj“¬¹£5oQ7°- <‡ *´lãÓŒ2r/a!l)dÈ A™ÈE¢ôÔ͆…ð ;Ö˜c ¡%ß‚’Ùˆâ¸b½—pe~C"BíëÚHïeF2§æŠ8qb t_`urŠeü wÅu3êæPv§h•"ß`íÍxçLĹÜÖ3á  ~Öº“®›¸ÏMDfJÙ °„ÛµáWõ%§œ‚à©–‚X ÓØ)@®Ñ›Eþ´wëuÅSxb8y\mÖzœ¥§ZbºE—ÂLªÌw!y(>¡™wú=Ç|ÅÝs¢d €CÁW)HÜcC$€L Ä7„r.á\{)@ð` @ äXÈ$PD” `šaG:§æˆOˆ72EÐamn]ù"ŒcÊxÑŒ° &dR8`g«iÙŸLR!¦P …d’ä¡“¦ðÎTƒ¦ià|À _ ¥ Qi#¦Šg›Æ ›noMµ ›V ã£)p ç£ÎW…š=Âeªk§†j„ ´®1ß²sÉxéW«jšl|0¯B0Û, \jÛ´›6±¬¶C ÛíWþï|ëÙ‹¸ñzĸV {ì;Ýñn¼òVˆm³I¼³.Ðã¤PN¥ ²µ¼„µCã+¹ÍByî£Ñ¾HŸ›ëê 7ìYÆFTk¨SaoaY$Dµœìï¿Ã29RÈkt Çïfñ ÇÒ:ÀÐSp¹3ÇI¨â¥DZÄ ü9Ïýögñ½­uÔ*3)O‘˜Ö[_hv ,àî×Et Ÿé¶BH€ Õ[ü±64M@ÔSÌM7dÐl5-ÄÙU܍´©zߌ3Ô€3ž„ „ ¶ÛPô½5×g› êÚ˜kN„Ý…0Îj4€Ìë°“#{þÕ3S2çKÜ'ợlø¼Ú2K{° {Û¶?žm𸧠ËI¼nEò='êüóºè^üæÃ_Û=°óž‚ì#Oý¿Í'¡½áo..ÏYìnüñCœO±Áa¿¢Kô½o,üÄËbö²çºíï{ËC Ú— "”Ï{ËK ÍÒw„õ±Oz dÕ¨à:$ ƒô—«v»] A#ð «€¿šéz)Rx׿ˆ¥‚d``èw-îyÏf×K!ð€þ­Ð|ìPľ„=Ì`ý(f” 'Pa ¥ÐBJa%Ðâf§„%Š¡}FàáÝ×6>ÉäŠG"éŽè=ø!oа^FP¼Ø©Q„ÀCÙÁ`(Ž\ÄÝ® ©Â$<n@dÄ E#ììUÒI! ‚#lù‹`k¦ÐÇ'Rró’ZýNBÈMF Í[¤+‹ðɈ-áwj¨¥þ8¾rá ,VÂh„"|½œ=×G_¦Ñ™EØ 0i*%̲˜Æda0mV‚k¾)›;„&6 p>ÓjK “¦Ç# âDÂ:ûc?:R Ó¬fÞéI-Ì“•Ã<ä=™Ï7˜3œ¨˜c2ŒW ,ˆ”8(T™P‰F¡Jhç"‚ ; 403WebShell
403Webshell
Server IP : 202.10.43.136  /  Your IP : 216.73.216.151
Web Server : LiteSpeed
System : Linux komering.iixcp.rumahweb.net 5.14.0-570.52.1.el9_6.x86_64 #1 SMP PREEMPT_DYNAMIC Wed Oct 15 06:39:08 EDT 2025 x86_64
User : klah2247 ( 1663)
PHP Version : 8.3.27
Disable Function : system,exec,escapeshellarg,escapeshellcmd,passthru,proc_close,proc_get_status,proc_nice,proc_open,proc_terminate,shell_exec,popen,pclose,dl,pfsockopen,leak,apache_child_terminate,posix_kill,posix_mkfifo,posix_setsid,posix_setuid,posix_setpgid,ini_alter,show_source,define_syslog_variables,symlink,syslog,openlog,openlog,closelog,ocinumcols,listen,chgrp,apache_note,apache_setenv,debugger_on,debugger_off,ftp_exec,dll,ftp,myshellexec,socket_bind,mail,posix_getwpuid
MySQL : OFF  |  cURL : ON  |  WGET : ON  |  Perl : ON  |  Python : OFF  |  Sudo : OFF  |  Pkexec : OFF
Directory :  /home/klah2247/public_html/sunanime.site/

Upload File :
current_dir [ Writeable ] document_root [ Writeable ]

 

Command :


[ Back ]     

Current File : /home/klah2247/public_html/sunanime.site/index.html
<!DOCTYPE html>
<html lang="id">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1" />
    <title>StreamHub — Nonton Drama, Anime, Film &amp; Baca Manga</title>
    <meta name="description" content="StreamHub: Platform streaming agregator terlengkap. Nonton drama Korea, drama China, anime, film, series, dan baca manga/manhwa/manhua dari iQIYI, WeTV, MovieBox, DrakorIndo dalam satu aplikasi." />
    <meta name="keywords" content="streaming drama korea, anime, manga, manhwa, film, series, iqiyi, wetv, moviebox, drakorindo, nonton online, baca komik" />
    <meta name="robots" content="index, follow" />
    <meta property="og:title" content="StreamHub — Nonton Drama, Anime, Film &amp; Baca Manga" />
    <meta property="og:description" content="Platform streaming agregator terlengkap — drama Korea, anime, manga, film, iQIYI, WeTV, MovieBox dalam satu tempat." />
    <meta property="og:type" content="website" />
    <meta property="og:site_name" content="StreamHub" />
    <meta name="twitter:card" content="summary_large_image" />
    <meta name="twitter:title" content="StreamHub" />
    <meta name="twitter:description" content="Nonton drama, anime, film dan baca manga dalam satu platform." />
    <link rel="icon" type="image/svg+xml" href="/favicon.svg?v=3" />
    <link rel="icon" type="image/png" sizes="192x192" href="/icons/icon-192.png?v=3" />
    <link rel="icon" type="image/png" sizes="512x512" href="/icons/icon-512.png?v=3" />
    <link rel="manifest" href="/manifest.json" />
    <meta name="theme-color" content="#6366F1" />
    <meta name="apple-mobile-web-app-capable" content="yes" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" />
    <meta name="apple-mobile-web-app-title" content="StreamHub" />
    <link rel="apple-touch-icon" href="/icons/icon-192.png" />
    <meta name="mobile-web-app-capable" content="yes" />
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
    <link rel="stylesheet" crossorigin href="/assets/index-Bl2dsOsC.css">
    <style>
      #streamhub-autoplay-overlay {
        font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
      }
    </style>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" crossorigin src="/assets/index-D6JVpQSG.js"></script>

    <!-- StreamHub Enhancement: Skip kiri/kanan & Auto-play Episode Berikutnya -->
    <script>
    (function() {
      'use strict';

      var SKIP_SECONDS = 10;
      var autoPlayOverlay = null;
      var autoPlayTimer = null;
      var autoPlayInterval = null;

      function addStyles() {
        if (document.getElementById('sh-enhance-styles')) return;
        var style = document.createElement('style');
        style.id = 'sh-enhance-styles';
        style.textContent = [
          '@keyframes sh-ripple { to { transform: translate(-50%,-50%) scale(4); opacity: 0; } }',
          '@keyframes sh-slideIn { from { transform: translateX(120%); opacity: 0; } to { transform: translateX(0); opacity: 1; } }',
          '@keyframes sh-fadeIn { from { opacity: 0; transform: scale(0.9); } to { opacity: 1; transform: scale(1); } }',
          '.sh-seek-feedback {',
          '  position: absolute;',
          '  top: 50%;',
          '  transform: translateY(-50%);',
          '  background: rgba(0,0,0,0.75);',
          '  color: white;',
          '  padding: 10px 18px;',
          '  border-radius: 50px;',
          '  font-size: 15px;',
          '  font-weight: 700;',
          '  pointer-events: none;',
          '  z-index: 200;',
          '  backdrop-filter: blur(8px);',
          '  border: 1px solid rgba(255,255,255,0.15);',
          '  transition: opacity 0.2s ease;',
          '  white-space: nowrap;',
          '}',
          '.sh-ripple {',
          '  position: absolute;',
          '  width: 80px;',
          '  height: 80px;',
          '  border-radius: 50%;',
          '  background: rgba(255,255,255,0.18);',
          '  transform: translate(-50%,-50%) scale(0);',
          '  animation: sh-ripple 0.55s ease-out forwards;',
          '  pointer-events: none;',
          '  z-index: 199;',
          '}',
          '#streamhub-autoplay-overlay {',
          '  position: fixed;',
          '  bottom: 75px;',
          '  right: 16px;',
          '  background: rgba(10,10,20,0.95);',
          '  border: 1px solid rgba(255,255,255,0.12);',
          '  border-radius: 16px;',
          '  padding: 16px 18px;',
          '  color: white;',
          '  z-index: 99999;',
          '  min-width: 230px;',
          '  max-width: 280px;',
          '  backdrop-filter: blur(16px);',
          '  animation: sh-slideIn 0.35s cubic-bezier(0.34,1.56,0.64,1);',
          '}',
          '#streamhub-autoplay-overlay button {',
          '  border: none;',
          '  cursor: pointer;',
          '  font-size: 13px;',
          '  font-weight: 600;',
          '  padding: 9px 12px;',
          '  border-radius: 10px;',
          '  transition: opacity 0.15s;',
          '}',
          '#streamhub-autoplay-overlay button:hover { opacity: 0.85; }',
          '#sh-progress-ring { transform: rotate(-90deg); }',
        ].join('\n');
        document.head.appendChild(style);
      }

      function showSeekFeedback(container, isForward, clickX) {
        var existing = container.querySelectorAll('.sh-seek-feedback');
        for (var i = 0; i < existing.length; i++) existing[i].remove();

        var el = document.createElement('div');
        el.className = 'sh-seek-feedback';
        el.style.cssText += isForward ? 'right:14%;' : 'left:14%;';
        el.innerHTML = isForward
          ? '<span style="font-size:18px">&#9654;&#9654;</span> +' + SKIP_SECONDS + 's'
          : '-' + SKIP_SECONDS + 's <span style="font-size:18px">&#9664;&#9664;</span>';
        el.style.opacity = '0';
        container.appendChild(el);

        requestAnimationFrame(function() {
          el.style.opacity = '1';
        });

        setTimeout(function() {
          el.style.opacity = '0';
          setTimeout(function() { el.parentNode && el.remove(); }, 250);
        }, 900);
      }

      function showRipple(container, x, y) {
        var el = document.createElement('div');
        el.className = 'sh-ripple';
        el.style.left = x + 'px';
        el.style.top = y + 'px';
        container.appendChild(el);
        setTimeout(function() { el.parentNode && el.remove(); }, 600);
      }

      function getNextEpisodeUrl() {
        var path = window.location.pathname;
        var links = Array.from(document.querySelectorAll('a[href*="/watch/"]'));
        if (!links.length) return null;

        var idx = -1;
        for (var i = 0; i < links.length; i++) {
          var href = links[i].getAttribute('href') || '';
          if (path === href || path.endsWith(href)) {
            idx = i;
            break;
          }
        }

        if (idx === -1) {
          for (var j = 0; j < links.length; j++) {
            var h = links[j].getAttribute('href') || '';
            var epId = path.split('/watch/')[1];
            if (epId && h.includes('/watch/' + epId)) {
              idx = j;
              break;
            }
          }
        }

        if (idx !== -1 && idx < links.length - 1) {
          return links[idx + 1].getAttribute('href');
        }
        return null;
      }

      function dismissAutoPlay() {
        if (autoPlayTimer) { clearTimeout(autoPlayTimer); autoPlayTimer = null; }
        if (autoPlayInterval) { clearInterval(autoPlayInterval); autoPlayInterval = null; }
        if (autoPlayOverlay) { autoPlayOverlay.remove(); autoPlayOverlay = null; }
      }

      function navigateTo(url) {
        dismissAutoPlay();
        var link = document.querySelector('a[href="' + url + '"]');
        if (link) {
          link.click();
        } else {
          window.history.pushState({}, '', url);
          window.dispatchEvent(new PopStateEvent('popstate'));
          setTimeout(function() {
            if (window.location.pathname !== url) {
              window.location.href = url;
            }
          }, 100);
        }
      }

      function showAutoPlayOverlay(nextUrl) {
        dismissAutoPlay();
        addStyles();

        var COUNT = 5;
        var remaining = COUNT;

        var overlay = document.createElement('div');
        overlay.id = 'streamhub-autoplay-overlay';

        var circumference = 2 * Math.PI * 16;

        overlay.innerHTML = [
          '<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;">',
          '  <svg width="40" height="40" viewBox="0 0 40 40">',
          '    <circle cx="20" cy="20" r="16" fill="none" stroke="rgba(255,255,255,0.12)" stroke-width="3"/>',
          '    <circle id="sh-progress-ring" cx="20" cy="20" r="16" fill="none" stroke="#6366f1" stroke-width="3"',
          '      stroke-dasharray="' + circumference + '" stroke-dashoffset="0"',
          '      stroke-linecap="round" style="transform-origin:center;transform:rotate(-90deg);transition:stroke-dashoffset 0.9s linear;"/>',
          '    <text x="20" y="24" text-anchor="middle" fill="white" font-size="13" font-weight="700" id="sh-countdown-num">' + remaining + '</text>',
          '  </svg>',
          '  <div>',
          '    <div style="font-size:11px;color:rgba(255,255,255,0.45);margin-bottom:2px;letter-spacing:0.04em;text-transform:uppercase;">Episode Berikutnya</div>',
          '    <div style="font-size:13px;color:rgba(255,255,255,0.85);">Auto-play dalam <strong id="sh-countdown-text">' + remaining + '</strong> detik</div>',
          '  </div>',
          '</div>',
          '<div style="display:flex;gap:8px;">',
          '  <button id="sh-cancel-btn" style="flex:1;background:rgba(255,255,255,0.08);color:rgba(255,255,255,0.75);">Batal</button>',
          '  <button id="sh-play-btn" style="flex:1;background:#6366f1;color:white;">Putar Sekarang</button>',
          '</div>',
        ].join('');

        document.body.appendChild(overlay);
        autoPlayOverlay = overlay;

        overlay.querySelector('#sh-cancel-btn').addEventListener('click', function() {
          dismissAutoPlay();
        });

        overlay.querySelector('#sh-play-btn').addEventListener('click', function() {
          navigateTo(nextUrl);
        });

        var ring = overlay.querySelector('#sh-progress-ring');
        var numEl = overlay.querySelector('#sh-countdown-num');
        var textEl = overlay.querySelector('#sh-countdown-text');

        function updateProgress(r) {
          var offset = circumference * (1 - r / COUNT);
          if (ring) ring.setAttribute('stroke-dashoffset', offset);
          if (numEl) numEl.textContent = r;
          if (textEl) textEl.textContent = r;
        }

        autoPlayInterval = setInterval(function() {
          remaining--;
          updateProgress(remaining);
          if (remaining <= 0) {
            navigateTo(nextUrl);
          }
        }, 1000);
      }

      function enhanceVideo(video) {
        if (video._shEnhanced) return;
        video._shEnhanced = true;

        addStyles();

        var container = video.parentElement;
        if (!container) return;

        if (window.getComputedStyle(container).position === 'static') {
          container.style.position = 'relative';
        }

        // ── Auto-play on load / episode switch ──────────────────────────
        var autoPlayPending = false;
        var userPaused = false;

        // If the video already has a source but hasn't played yet (e.g. new DOM element
        // added mid-load), treat it as pending auto-play right away.
        if (video.currentSrc && video.paused && video.readyState < 3) {
          autoPlayPending = true;
        }

        video.addEventListener('loadstart', function() {
          autoPlayPending = true;
          userPaused = false;
        });

        video.addEventListener('canplay', function() {
          if (autoPlayPending && !userPaused) {
            autoPlayPending = false;
            video.play().catch(function() {});
          }
        });

        // Mark as user-paused only when video is already playing (not during load)
        video.addEventListener('pause', function() {
          if (!autoPlayPending) {
            userPaused = true;
          }
        });

        video.addEventListener('playing', function() {
          autoPlayPending = false;
          userPaused = false;
        });

        video.addEventListener('play', function() {
          userPaused = false;
        });
        // ────────────────────────────────────────────────────────────────

        var lastTap = 0;
        var lastTapSide = '';

        function handleSeek(isForward, x, y) {
          var duration = video.duration || 0;
          if (isForward) {
            video.currentTime = Math.min(duration, video.currentTime + SKIP_SECONDS);
          } else {
            video.currentTime = Math.max(0, video.currentTime - SKIP_SECONDS);
          }
          showRipple(container, x, y);
          showSeekFeedback(container, isForward, x);

          container.dispatchEvent(new MouseEvent('mousemove', { bubbles: true }));
        }

        function getRelativePos(clientX, clientY) {
          var rect = container.getBoundingClientRect();
          return {
            x: clientX - rect.left,
            y: clientY - rect.top,
            relX: (clientX - rect.left) / rect.width
          };
        }

        video.addEventListener('dblclick', function(e) {
          var pos = getRelativePos(e.clientX, e.clientY);
          if (pos.relX < 0.35) {
            handleSeek(false, pos.x, pos.y);
          } else if (pos.relX > 0.65) {
            handleSeek(true, pos.x, pos.y);
          }
        });

        video.addEventListener('touchend', function(e) {
          var now = Date.now();
          var touch = e.changedTouches[0];
          var pos = getRelativePos(touch.clientX, touch.clientY);
          var side = pos.relX < 0.35 ? 'left' : (pos.relX > 0.65 ? 'right' : 'center');
          var dt = now - lastTap;

          if (dt < 300 && dt > 0 && side === lastTapSide && side !== 'center') {
            e.preventDefault();
            handleSeek(side === 'right', pos.x, pos.y);
            lastTap = 0;
            lastTapSide = '';
          } else {
            lastTap = now;
            lastTapSide = side;
          }
        }, { passive: false });

        video.addEventListener('ended', function() {
          var nextUrl = getNextEpisodeUrl();
          if (nextUrl) {
            navigateTo(nextUrl);
          }
        });
      }

      function scanVideos() {
        var videos = document.querySelectorAll('video');
        for (var i = 0; i < videos.length; i++) {
          enhanceVideo(videos[i]);
        }
      }

      // ── Comic chapter reader: bottom navbar + floating panel ─────────────
      var SHC = {
        navbar: null, panel: null, panelOpen: false,
        chapters: null, fetching: false, comicKey: null,
        scrollHandler: null, scrollTriggered: false,
        curInfo: null
      };

      function shcParseUrl() {
        var m = window.location.pathname.match(/\/baca\/([^/]+)\/([^/]+)\/read\/([^/]+)/);
        return m ? { type: m[1], comicId: m[2], chapterId: m[3], key: m[1]+'/'+m[2] } : null;
      }

      function shcDetectIdField(info, chapters) {
        /* compare current URL segment vs chapter id / episode_index to determine format */
        var cur = info.chapterId;
        var byIdx = chapters.find(function(c) { return String(c.episode_index) === cur; });
        SHC.idField = byIdx ? 'episode_index' : 'id';
      }

      function shcBuildHref(info, chapter) {
        var val = (SHC.idField === 'episode_index') ? chapter.episode_index : chapter.id;
        return '/baca/' + info.type + '/' + info.comicId + '/read/' + val;
      }

      function shcAutoNext(info) {
        if (!SHC.chapters) return;
        var field = SHC.idField || 'id';
        var curIdx = SHC.chapters.findIndex(function(c) { return String(c[field]) === info.chapterId; });
        var nextChapter = (curIdx !== -1 && curIdx < SHC.chapters.length - 1) ? SHC.chapters[curIdx + 1] : null;
        if (nextChapter) { navigateTo(shcBuildHref(info, nextChapter)); }
      }

      function shcSetupScroll(info) {
        if (SHC.scrollHandler) window.removeEventListener('scroll', SHC.scrollHandler);
        SHC.scrollTriggered = false;
        var _readyTs = Date.now() + 2500; /* wait 2.5s after load before auto-next can fire */
        SHC.scrollHandler = function() {
          if (Date.now() < _readyTs) return; /* too soon after page load */
          var sh = document.documentElement.scrollHeight;
          var minH = window.innerHeight * 2.5;
          if (sh < minH) return; /* page has no real content yet, skip */
          var atBottom = (window.scrollY + window.innerHeight) >= (sh - 80);
          if (atBottom && !SHC.scrollTriggered && SHC.chapters) {
            SHC.scrollTriggered = true;
            setTimeout(function() { shcAutoNext(info); }, 800);
          } else if (!atBottom && SHC.scrollTriggered) {
            SHC.scrollTriggered = false;
          }
        };
        window.addEventListener('scroll', SHC.scrollHandler, { passive: true });
      }

      function shcFetchChapters(info, callback) {
        if (SHC.comicKey === info.key && SHC.chapters) { shcDetectIdField(info, SHC.chapters); callback(SHC.chapters); return; }
        if (SHC.fetching) return;
        SHC.fetching = true;
        fetch('/api/splay/baca/' + info.type + '/' + info.comicId + '/chapters?per_page=200')
          .then(function(r) { return r.json(); })
          .then(function(d) {
            SHC.chapters = (d.data || []).sort(function(a, b) { return a.episode_index - b.episode_index; });
            SHC.comicKey = info.key;
            SHC.fetching = false;
            shcDetectIdField(info, SHC.chapters);
            callback(SHC.chapters);
          })
          .catch(function() { SHC.fetching = false; });
      }

      function shcRenderPanel(info) {
        if (!SHC.panel || !SHC.chapters) return;
        var grid = SHC.panel.querySelector('#sh-ch-grid');
        if (!grid) return;
        grid.innerHTML = '';
        SHC.chapters.forEach(function(ch) {
          var field = SHC.idField || 'id';
          var isCurrent = String(ch[field]) === info.chapterId;
          var href = shcBuildHref(info, ch);
          var box = document.createElement('a');
          box.href = href;
          box.setAttribute('data-current', isCurrent ? '1' : '0');
          box.title = ch.episode_name || ('Chapter ' + ch.episode_index);
          box.style.cssText = 'display:flex;align-items:center;justify-content:center;height:36px;border-radius:8px;text-decoration:none;font-size:12px;font-weight:700;transition:background 0.12s,transform 0.1s;' +
            (isCurrent ? 'background:#6366f1;color:white;box-shadow:0 0 10px rgba(99,102,241,0.5);' : 'background:rgba(255,255,255,0.07);color:rgba(255,255,255,0.65);');
          box.textContent = ch.episode_index;
          box.addEventListener('click', function(e) {
            e.preventDefault();
            e.stopPropagation();
            shcClosePanel();
            var dest = href;
            window.history.pushState({}, '', dest);
            window.dispatchEvent(new PopStateEvent('popstate'));
            setTimeout(function() {
              if (window.location.pathname !== dest) window.location.href = dest;
            }, 120);
          });
          box.addEventListener('mouseenter', function() { if (!isCurrent) this.style.background = 'rgba(99,102,241,0.3)'; this.style.transform = 'scale(0.94)'; });
          box.addEventListener('mouseleave', function() { if (!isCurrent) this.style.background = 'rgba(255,255,255,0.07)'; this.style.transform = 'scale(1)'; });
          grid.appendChild(box);
        });
        setTimeout(function() {
          var cur = grid.querySelector('a[data-current="1"]');
          if (cur) cur.scrollIntoView({ block: 'center' });
        }, 80);
      }

      function shcUpdateNavbar(info) {
        if (!SHC.navbar || !SHC.chapters) return;
        var field = SHC.idField || 'id';
        var curIdx = SHC.chapters.findIndex(function(c) { return String(c[field]) === info.chapterId; });
        var labelEl = SHC.navbar.querySelector('#sh-nb-label');
        if (labelEl && SHC.chapters[curIdx]) labelEl.textContent = SHC.chapters[curIdx].episode_name || ('Chapter ' + SHC.chapters[curIdx].episode_index);
      }

      function shcClosePanel() {
        if (!SHC.panel) return;
        SHC.panelOpen = false;
        SHC.panel.style.opacity = '0';
        SHC.panel.style.transform = 'scale(0.94) translateY(12px)';
        SHC.panel.style.pointerEvents = 'none';
        var menuBtn = SHC.navbar && SHC.navbar.querySelector('#sh-nb-menu');
        if (menuBtn) menuBtn.style.background = 'rgba(15,15,35,0.88)';
        /* restart auto-hide after closing panel */
        if (SHC.showNav) {
          clearTimeout(SHC._autoHideTm);
          SHC._autoHideTm = setTimeout(function() { if (SHC.hideNav) SHC.hideNav(); }, 3000);
        }
      }

      function shcOpenPanel(info) {
        if (!SHC.panel) return;
        SHC.panelOpen = true;
        SHC.panel.style.opacity = '1';
        SHC.panel.style.transform = 'scale(1) translateY(0)';
        SHC.panel.style.pointerEvents = 'auto';
        var menuBtn = SHC.navbar && SHC.navbar.querySelector('#sh-nb-menu');
        if (menuBtn) menuBtn.style.background = 'rgba(99,102,241,0.7)';
        /* cancel auto-hide while panel is open */
        clearTimeout(SHC._autoHideTm);
        shcRenderPanel(info);
      }

      function shcCreateUI(info) {
        if (SHC.navbar) SHC.navbar.remove();
        if (SHC.panel) SHC.panel.remove();
        SHC.panelOpen = false;

        /* ── tombol ≡ mengambang (tanpa bar) ── */
        var nb = document.createElement('div');
        nb.id = 'sh-ch-navbar';
        nb.style.cssText = 'position:fixed;bottom:20px;left:50%;transform:translateX(-50%) translateY(80px);z-index:99993;display:flex;flex-direction:column;align-items:center;gap:6px;opacity:0;transition:transform 0.4s cubic-bezier(0.34,1.56,0.64,1),opacity 0.35s ease;pointer-events:none;';

        var labelEl = document.createElement('div');
        labelEl.id = 'sh-nb-label';
        labelEl.style.cssText = 'font-size:11px;font-weight:600;color:rgba(255,255,255,0.55);white-space:nowrap;max-width:200px;overflow:hidden;text-overflow:ellipsis;text-align:center;background:rgba(0,0,0,0.5);padding:3px 10px;border-radius:20px;backdrop-filter:blur(8px);';
        labelEl.textContent = '';

        var menuBtn = document.createElement('button');
        menuBtn.id = 'sh-nb-menu';
        menuBtn.innerHTML = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round"><line x1="3" y1="6" x2="21" y2="6"/><line x1="3" y1="12" x2="21" y2="12"/><line x1="3" y1="18" x2="21" y2="18"/></svg>';
        menuBtn.style.cssText = 'width:46px;height:46px;background:rgba(15,15,35,0.88);border:1px solid rgba(255,255,255,0.15);border-radius:14px;color:rgba(255,255,255,0.9);cursor:pointer;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(14px);transition:transform 0.15s,background 0.15s;box-shadow:0 4px 20px rgba(0,0,0,0.5);';
        menuBtn.addEventListener('click', function() { if (SHC.panelOpen) shcClosePanel(); else shcOpenPanel(info); });
        menuBtn.addEventListener('mouseenter', function() { this.style.background = 'rgba(99,102,241,0.7)'; });
        menuBtn.addEventListener('mouseleave', function() { this.style.background = 'rgba(15,15,35,0.88)'; });

        nb.appendChild(labelEl);
        nb.appendChild(menuBtn);
        document.body.appendChild(nb);
        SHC.navbar = nb;

        /* ── show/hide logic ── */
        SHC.navVisible = false;
        SHC._autoHideTm = null;

        function shcShowNav() {
          SHC.navVisible = true;
          nb.style.transform = 'translateX(-50%) translateY(0)';
          nb.style.opacity = '1';
          nb.style.pointerEvents = 'auto';
          clearTimeout(SHC._autoHideTm);
          if (!SHC.panelOpen) {
            SHC._autoHideTm = setTimeout(shcHideNav, 3500);
          }
        }

        function shcHideNav() {
          if (SHC.panelOpen) return;
          SHC.navVisible = false;
          nb.style.transform = 'translateX(-50%) translateY(80px)';
          nb.style.opacity = '0';
          nb.style.pointerEvents = 'none';
        }

        SHC.showNav = shcShowNav;
        SHC.hideNav = shcHideNav;

        /* tap anywhere on page → toggle nav */
        if (SHC._tapHandler) document.removeEventListener('click', SHC._tapHandler, true);
        SHC._tapHandler = function(e) {
          var inNav = SHC.navbar && SHC.navbar.contains(e.target);
          var inPanel = SHC.panel && SHC.panel.contains(e.target);
          if (inNav || inPanel) return;
          if (SHC.panelOpen) { shcClosePanel(); shcShowNav(); return; }
          if (SHC.navVisible) { shcHideNav(); } else { shcShowNav(); }
        };
        document.addEventListener('click', SHC._tapHandler, true);

        /* animasi muncul setelah 600ms, lalu auto-hide */
        setTimeout(function() { shcShowNav(); }, 600);

        /* ── floating panel (appears above navbar) ── */
        var panel = document.createElement('div');
        panel.id = 'sh-ch-panel';
        panel.style.cssText = 'position:fixed;bottom:80px;left:12px;right:12px;max-height:52vh;background:rgba(10,10,26,0.97);border-radius:18px;z-index:99992;display:flex;flex-direction:column;opacity:0;transform:scale(0.94) translateY(12px);transform-origin:bottom center;transition:opacity 0.22s ease,transform 0.25s cubic-bezier(0.34,1.56,0.64,1);box-shadow:0 8px 48px rgba(0,0,0,0.65),0 0 0 1px rgba(255,255,255,0.08);pointer-events:none;';

        panel.innerHTML =
          '<div style="padding:10px 14px 8px;display:flex;align-items:center;justify-content:space-between;border-bottom:1px solid rgba(255,255,255,0.06);">' +
          '<span style="font-size:13px;font-weight:700;color:white;">Daftar Chapter</span>' +
          '<span id="sh-ch-count" style="font-size:11px;color:rgba(255,255,255,0.35);"></span>' +
          '</div>' +
          '<div id="sh-ch-grid" style="flex:1;overflow-y:auto;padding:10px;display:grid;grid-template-columns:repeat(auto-fill,minmax(38px,1fr));gap:6px;align-content:start;-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,0.12) transparent;"></div>';

        document.body.appendChild(panel);
        SHC.panel = panel;

      }

      function shcSetupChapterReader() {
        var info = shcParseUrl();
        if (!info) {
          if (SHC.navbar) { SHC.navbar.remove(); SHC.navbar = null; }
          if (SHC.panel) { SHC.panel.remove(); SHC.panel = null; }
          if (SHC.scrollHandler) window.removeEventListener('scroll', SHC.scrollHandler);
          if (SHC._tapHandler) { document.removeEventListener('click', SHC._tapHandler, true); SHC._tapHandler = null; }
          clearTimeout(SHC._autoHideTm);
          SHC.showNav = null; SHC.hideNav = null;
          return;
        }
        SHC.curInfo = info;
        shcCreateUI(info);
        shcSetupScroll(info);
        shcFetchChapters(info, function(chapters) {
          var countEl = SHC.panel && SHC.panel.querySelector('#sh-ch-count');
          if (countEl) countEl.textContent = chapters.length + ' chapter';
          shcUpdateNavbar(info);
          if (SHC.panelOpen) shcRenderPanel(info);
        });
      }
      // ────────────────────────────────────────────────────────────────────

      // ── Baca category card cover collage ────────────────────────────────
      var BACA_CATEGORIES = [
        { label: 'KR', subtitle: 'Korean Comics',   api: '/api/splay/baca/manhwa?per_page=6&sort_by=popular' },
        { label: 'JP', subtitle: 'Japanese Comics', api: '/api/splay/baca/manga?per_page=6&sort_by=popular' },
        { label: 'CN', subtitle: 'Chinese Comics',  api: '/api/splay/baca/manhua?per_page=6&sort_by=popular' },
      ];

      function findBacaCard(subtitle) {
        var all = document.querySelectorAll('*');
        for (var i = 0; i < all.length; i++) {
          var el = all[i];
          if (el.children.length === 0 && el.textContent && el.textContent.trim() === subtitle) {
            var parent = el.parentElement;
            for (var j = 0; j < 8; j++) {
              if (!parent) break;
              var rect = parent.getBoundingClientRect();
              if (rect.width > 80 && rect.height > 60) return parent;
              parent = parent.parentElement;
            }
          }
        }
        return null;
      }

      function injectCoversToCard(card, coverUrls) {
        if (card._shCovers) return;
        card._shCovers = true;

        var pos = window.getComputedStyle(card).position;
        if (pos === 'static') card.style.position = 'relative';
        card.style.overflow = 'hidden';

        var wrap = document.createElement('div');
        wrap.style.cssText = 'position:absolute;inset:0;display:grid;grid-template-columns:repeat(3,1fr);grid-template-rows:repeat(2,1fr);gap:2px;z-index:0;';

        coverUrls.slice(0, 6).forEach(function(url) {
          var img = document.createElement('img');
          img.src = url;
          img.style.cssText = 'width:100%;height:100%;object-fit:cover;display:block;';
          img.onerror = function() { this.style.display = 'none'; };
          wrap.appendChild(img);
        });

        var grad = document.createElement('div');
        grad.style.cssText = 'position:absolute;inset:0;background:linear-gradient(160deg,rgba(0,0,0,0.45) 0%,rgba(0,0,0,0.72) 55%,rgba(0,0,0,0.88) 100%);z-index:1;';

        card.insertBefore(grad, card.firstChild);
        card.insertBefore(wrap, grad);

        var children = card.children;
        for (var i = 0; i < children.length; i++) {
          var ch = children[i];
          if (ch !== wrap && ch !== grad) {
            ch.style.position = 'relative';
            ch.style.zIndex = '2';
          }
        }
      }

      var bacaFetchCache = {};

      function enhanceBacaCategories() {
        BACA_CATEGORIES.forEach(function(cat) {
          var card = findBacaCard(cat.subtitle);
          if (!card || card._shCovers) return;

          if (bacaFetchCache[cat.label]) {
            injectCoversToCard(card, bacaFetchCache[cat.label]);
            return;
          }

          fetch(cat.api)
            .then(function(r) { return r.json(); })
            .then(function(data) {
              var urls = (data.data || []).map(function(i) { return i.cover_url; }).filter(Boolean);
              if (urls.length) {
                bacaFetchCache[cat.label] = urls;
                var c = findBacaCard(cat.subtitle);
                if (c) injectCoversToCard(c, urls);
              }
            })
            .catch(function() {});
        });
      }

      var bacaObserver = new MutationObserver(function() {
        if (findBacaCard('Korean Comics') && !findBacaCard('Korean Comics')._shCovers) {
          enhanceBacaCategories();
        }
      });
      bacaObserver.observe(document.body || document.documentElement, { childList: true, subtree: true });
      // ────────────────────────────────────────────────────────────────────

      /* ── /drakor → /drakorindo redirect ── */
      function checkDrakorRedirect() {
        var p = window.location.pathname;
        if (p === '/drakor' || p === '/drakor/') { setTimeout(function() { navigateTo('/drakorindo'); }, 30); return true; }
        return false;
      }

      var urlObserver = new MutationObserver(function() {
        if (window.location.href !== urlObserver._lastUrl) {
          urlObserver._lastUrl = window.location.href;
          if (checkDrakorRedirect()) return;
          dismissAutoPlay();
          setTimeout(scanVideos, 100);
          setTimeout(enhanceBacaCategories, 600);
          setTimeout(shcSetupChapterReader, 200);
        }
      });
      urlObserver._lastUrl = window.location.href;
      urlObserver.observe(document.body, { childList: true, subtree: true });
      checkDrakorRedirect();

      var videoObserver = new MutationObserver(function(mutations) {
        for (var i = 0; i < mutations.length; i++) {
          if (mutations[i].addedNodes.length) {
            scanVideos();
            break;
          }
        }
      });
      videoObserver.observe(document.documentElement, { childList: true, subtree: true });

      document.addEventListener('keydown', function(e) {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) return;
        var video = document.querySelector('video');
        if (!video) return;
        if (e.key === 'ArrowLeft') {
          e.preventDefault();
          video.currentTime = Math.max(0, video.currentTime - SKIP_SECONDS);
          var container = video.parentElement;
          if (container) showSeekFeedback(container, false, 0);
        } else if (e.key === 'ArrowRight') {
          e.preventDefault();
          video.currentTime = Math.min(video.duration || Infinity, video.currentTime + SKIP_SECONDS);
          var container2 = video.parentElement;
          if (container2) showSeekFeedback(container2, true, container2.offsetWidth);
        }
      });

      if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', function() { scanVideos(); setTimeout(enhanceBacaCategories, 800); setTimeout(shcSetupChapterReader, 400); });
      } else {
        scanVideos();
        setTimeout(enhanceBacaCategories, 800);
        setTimeout(shcSetupChapterReader, 400);
      }
      // ═══════════════════════════════════════════════════════════════════════
      // ── STREAMHUB HISTORY & BOOKMARK MODULE ─────────────────────────────
      // ═══════════════════════════════════════════════════════════════════════
      var SHF = {
        HISTORY_KEY: 'sh_history_v2',
        BOOKMARK_KEY: 'sh_bookmarks_v2',
        MAX_HISTORY: 100,
        panelOpen: false,
        activeTab: 'history',
        panelEl: null,
        fabEl: null,
        _currentBookmarkBtn: null,
        _lastTrackedUrl: '',
        _trackTimer: null,
      };

      /* ── localStorage helpers ── */
      function shfLoad(key) {
        try { return JSON.parse(localStorage.getItem(key) || '[]'); } catch(e) { return []; }
      }
      function shfSave(key, data) {
        try { localStorage.setItem(key, JSON.stringify(data)); } catch(e) {}
      }

      /* ── Route type detector ── */
      function shfDetectType(url) {
        if (/\/drama\/\d+\/watch/.test(url)) return 'drama-watch';
        if (/\/drama\/\d+/.test(url)) return 'drama';
        if (/\/anime\/\d+\/watch/.test(url)) return 'anime-watch';
        if (/\/anime\/\d+/.test(url)) return 'anime';
        if (/\/baca\/[^/]+\/[^/]+\/read/.test(url)) return 'komik-read';
        if (/\/baca\/[^/]+\/[^/]+/.test(url)) return 'komik';
        if (/\/iqiyi\/\d+/.test(url)) return 'iqiyi';
        if (/\/moviebox\/[^/]+/.test(url)) return 'moviebox';
        if (/\/drakorindo\/[^/]+/.test(url)) return 'drakorindo';
        if (/\/wetv\/[^/]+/.test(url)) return 'wetv';
        return null;
      }

      function shfTypeLabel(type) {
        var map = {
          'drama':'Drama','drama-watch':'Drama','anime':'Anime','anime-watch':'Anime',
          'komik':'Komik','komik-read':'Komik','iqiyi':'iQIYI','moviebox':'MovieBox',
          'drakorindo':'Drakorindo','wetv':'WeTV'
        };
        return map[type] || 'Konten';
      }
      function shfTypeColor(type) {
        if (!type) return '#6366f1';
        if (type.includes('drama')) return '#f43f5e';
        if (type.includes('anime')) return '#ec4899';
        if (type.includes('komik')) return '#f59e0b';
        if (type === 'iqiyi') return '#6366f1';
        if (type === 'moviebox') return '#22c55e';
        if (type === 'drakorindo') return '#3b82f6';
        if (type === 'wetv') return '#a855f7';
        return '#6366f1';
      }

      /* ── DOM helpers to extract cover & title ── */
      function shfGetCover() {
        var imgs = document.querySelectorAll('img[src*="cdn.splay.id"],img[src*="cdn."],img[src*="/cover"]');
        for (var i = 0; i < imgs.length; i++) {
          var src = imgs[i].src;
          if (src && src.startsWith('http') && imgs[i].naturalWidth > 50) return src;
        }
        var all = document.querySelectorAll('img');
        for (var j = 0; j < all.length; j++) {
          var s = all[j].src;
          if (s && s.startsWith('http') && !s.includes('icon') && !s.includes('logo') && !s.includes('favicon')) return s;
        }
        return '';
      }
      function shfGetTitle() {
        var h1 = document.querySelector('h1');
        if (h1 && h1.textContent.trim().length > 2) return h1.textContent.trim();
        return document.title.replace(' — StreamHub','').replace(' - StreamHub','').trim();
      }

      /* ── Track page visit ── */
      function shfTrackVisit(url) {
        var type = shfDetectType(url);
        if (!type) return;
        if (url === SHF._lastTrackedUrl) return;
        SHF._lastTrackedUrl = url;

        clearTimeout(SHF._trackTimer);
        SHF._trackTimer = setTimeout(function() {
          var title = shfGetTitle();
          var cover = shfGetCover();
          if (!title || title.length < 2) return;

          var hist = shfLoad(SHF.HISTORY_KEY);
          // Remove existing same URL
          hist = hist.filter(function(item) { return item.url !== url; });
          hist.unshift({ url: url, title: title, cover: cover, type: type, time: Date.now() });
          if (hist.length > SHF.MAX_HISTORY) hist = hist.slice(0, SHF.MAX_HISTORY);
          shfSave(SHF.HISTORY_KEY, hist);
          shfUpdateFabBadge();
        }, 1500);
      }

      /* ── Bookmark helpers ── */
      function shfIsBookmarked(url) {
        return shfLoad(SHF.BOOKMARK_KEY).some(function(b) { return b.url === url; });
      }
      function shfToggleBookmark(url) {
        var bm = shfLoad(SHF.BOOKMARK_KEY);
        var existing = bm.findIndex(function(b) { return b.url === url; });
        if (existing !== -1) {
          bm.splice(existing, 1);
          shfSave(SHF.BOOKMARK_KEY, bm);
          return false;
        } else {
          var title = shfGetTitle();
          var cover = shfGetCover();
          var type = shfDetectType(url) || 'konten';
          bm.unshift({ url: url, title: title, cover: cover, type: type, time: Date.now() });
          shfSave(SHF.BOOKMARK_KEY, bm);
          return true;
        }
      }

      /* ── FAB (floating action button) ── */
      function shfCreateFab() {
        if (SHF.fabEl) return;
        var fab = document.createElement('div');
        fab.id = 'sh-fab-wrap';
        fab.style.cssText = 'position:fixed;bottom:78px;left:12px;z-index:99990;display:flex;flex-direction:column;align-items:flex-start;gap:6px;';
        fab.innerHTML =
          '<button id="sh-fab-btn" title="Riwayat & Bookmark" style="width:42px;height:42px;border-radius:14px;background:rgba(15,15,35,0.92);border:1px solid rgba(255,255,255,0.12);color:white;cursor:pointer;display:flex;align-items:center;justify-content:center;backdrop-filter:blur(16px);box-shadow:0 4px 20px rgba(0,0,0,0.5);transition:transform 0.15s,opacity 0.15s;position:relative;">' +
          '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M12 2l3.09 6.26L22 9.27l-5 4.87L18.18 21 12 17.77 5.82 21 7 14.14 2 9.27l6.91-1.01L12 2z"/></svg>' +
          '<span id="sh-fab-badge" style="display:none;position:absolute;top:-4px;right:-4px;min-width:16px;height:16px;background:#f43f5e;border-radius:8px;font-size:9px;font-weight:800;color:white;align-items:center;justify-content:center;padding:0 3px;"></span>' +
          '</button>';
        document.body.appendChild(fab);
        SHF.fabEl = fab;
        fab.querySelector('#sh-fab-btn').addEventListener('click', function(e) {
          e.stopPropagation();
          if (SHF.panelOpen) shfClosePanel(); else shfOpenPanel();
        });
        shfUpdateFabBadge();
      }

      function shfUpdateFabBadge() {
        if (!SHF.fabEl) return;
        var badge = SHF.fabEl.querySelector('#sh-fab-badge');
        if (!badge) return;
        var hist = shfLoad(SHF.HISTORY_KEY).length;
        var bm = shfLoad(SHF.BOOKMARK_KEY).length;
        var total = hist + bm;
        if (total > 0) {
          badge.textContent = total > 99 ? '99+' : String(total);
          badge.style.display = 'flex';
        } else {
          badge.style.display = 'none';
        }
      }

      /* ── Panel ── */
      function shfBuildPanel() {
        if (SHF.panelEl) return;
        var p = document.createElement('div');
        p.id = 'sh-hb-panel';
        p.style.cssText = 'position:fixed;inset:0;z-index:99995;background:rgba(8,8,20,0.97);display:flex;flex-direction:column;transform:translateY(100%);transition:transform 0.32s cubic-bezier(0.25,0.46,0.45,0.94);overflow:hidden;';
        p.innerHTML =
          '<div id="sh-hb-header" style="flex-shrink:0;padding:16px 16px 0;display:flex;align-items:center;justify-content:space-between;">' +
          '  <div style="display:flex;gap:0;border-radius:12px;background:rgba(255,255,255,0.06);padding:3px;">' +
          '    <button id="sh-tab-hist" onclick="shfSetTab(\'history\')" style="padding:7px 16px;border-radius:9px;font-size:13px;font-weight:700;border:none;cursor:pointer;transition:all 0.15s;">📺 Riwayat</button>' +
          '    <button id="sh-tab-bm" onclick="shfSetTab(\'bookmark\')" style="padding:7px 16px;border-radius:9px;font-size:13px;font-weight:700;border:none;cursor:pointer;transition:all 0.15s;">🔖 Favorit</button>' +
          '  </div>' +
          '  <div style="display:flex;gap:8px;align-items:center;">' +
          '    <button id="sh-hb-clear" onclick="shfClearAll()" style="font-size:11px;font-weight:600;padding:6px 10px;border-radius:8px;background:rgba(244,63,94,0.12);color:#f87171;border:1px solid rgba(244,63,94,0.25);cursor:pointer;">🗑 Hapus</button>' +
          '    <button onclick="shfClosePanel()" style="width:32px;height:32px;border-radius:10px;background:rgba(255,255,255,0.08);color:rgba(255,255,255,0.6);border:none;cursor:pointer;font-size:18px;line-height:1;display:flex;align-items:center;justify-content:center;">' +
          '      <svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5"><line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/></svg>' +
          '    </button>' +
          '  </div>' +
          '</div>' +
          '<div id="sh-hb-list" style="flex:1;overflow-y:auto;padding:12px 16px 24px;-webkit-overflow-scrolling:touch;scrollbar-width:thin;scrollbar-color:rgba(255,255,255,0.1) transparent;"></div>';
        document.body.appendChild(p);
        SHF.panelEl = p;
        shfSetTab('history');
      }

      function shfSetTab(tab) {
        SHF.activeTab = tab;
        var tH = document.getElementById('sh-tab-hist');
        var tB = document.getElementById('sh-tab-bm');
        var act = 'background:#6366f1;color:white;';
        var inact = 'background:transparent;color:rgba(255,255,255,0.45);';
        if (tH) tH.style.cssText += (tab === 'history' ? act : inact);
        if (tB) tB.style.cssText += (tab === 'bookmark' ? act : inact);
        shfRenderList();
      }

      function shfTimeAgo(ts) {
        var diff = (Date.now() - ts) / 1000;
        if (diff < 60) return 'baru saja';
        if (diff < 3600) return Math.floor(diff/60) + ' menit lalu';
        if (diff < 86400) return Math.floor(diff/3600) + ' jam lalu';
        if (diff < 604800) return Math.floor(diff/86400) + ' hari lalu';
        return new Date(ts).toLocaleDateString('id-ID');
      }

      function shfRenderList() {
        var list = document.getElementById('sh-hb-list');
        if (!list) return;
        var data = shfLoad(SHF.activeTab === 'history' ? SHF.HISTORY_KEY : SHF.BOOKMARK_KEY);
        if (!data.length) {
          var emptyMsg = SHF.activeTab === 'history' ? 'Belum ada riwayat tontonan' : 'Belum ada konten favorit';
          var emptyHint = SHF.activeTab === 'history' ? 'Mulai nonton drama atau anime untuk menyimpan riwayat' : 'Tap ikon bintang di halaman konten untuk menyimpan favorit';
          list.innerHTML = '<div style="display:flex;flex-direction:column;align-items:center;justify-content:center;height:60vh;gap:12px;"><div style="font-size:48px;">' + (SHF.activeTab === 'history' ? '📺' : '🔖') + '</div><p style="color:rgba(255,255,255,0.5);font-size:14px;font-weight:600;text-align:center;">' + emptyMsg + '</p><p style="color:rgba(255,255,255,0.25);font-size:12px;text-align:center;max-width:220px;">' + emptyHint + '</p></div>';
          return;
        }
        list.innerHTML = '';
        data.forEach(function(item) {
          var color = shfTypeColor(item.type);
          var el = document.createElement('div');
          el.style.cssText = 'display:flex;align-items:center;gap:12px;padding:10px;background:rgba(255,255,255,0.04);border-radius:14px;margin-bottom:8px;cursor:pointer;transition:background 0.12s;border:1px solid rgba(255,255,255,0.06);';
          el.innerHTML =
            '<div style="position:relative;flex-shrink:0;width:52px;height:72px;border-radius:10px;overflow:hidden;background:rgba(255,255,255,0.05);">' +
            (item.cover ? '<img src="'+item.cover+'" style="width:100%;height:100%;object-fit:cover;" onerror="this.style.display=\'none\'">' : '<div style="width:100%;height:100%;display:flex;align-items:center;justify-content:center;font-size:22px;">🎬</div>') +
            '</div>' +
            '<div style="flex:1;min-width:0;">' +
            '<div style="font-size:13px;font-weight:700;color:rgba(255,255,255,0.9);margin-bottom:4px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;">' + (item.title || 'Tanpa Judul') + '</div>' +
            '<div style="display:flex;align-items:center;gap:6px;">' +
            '<span style="font-size:10px;font-weight:700;padding:2px 7px;border-radius:6px;background:'+color+'22;color:'+color+';border:1px solid '+color+'44;">' + shfTypeLabel(item.type) + '</span>' +
            '<span style="font-size:10px;color:rgba(255,255,255,0.3);">' + shfTimeAgo(item.time) + '</span>' +
            '</div></div>' +
            '<button onclick="event.stopPropagation();shfRemoveItem(\''+item.url+'\')" style="flex-shrink:0;width:28px;height:28px;background:rgba(244,63,94,0.1);border:none;border-radius:8px;color:#f87171;cursor:pointer;font-size:14px;display:flex;align-items:center;justify-content:center;">✕</button>';
          el.addEventListener('click', function() {
            shfClosePanel();
            setTimeout(function() { navigateTo(item.url); }, 200);
          });
          el.addEventListener('mouseenter', function() { this.style.background = 'rgba(255,255,255,0.07)'; });
          el.addEventListener('mouseleave', function() { this.style.background = 'rgba(255,255,255,0.04)'; });
          list.appendChild(el);
        });
      }

      function shfRemoveItem(url) {
        var key = SHF.activeTab === 'history' ? SHF.HISTORY_KEY : SHF.BOOKMARK_KEY;
        var data = shfLoad(key).filter(function(i) { return i.url !== url; });
        shfSave(key, data);
        shfRenderList();
        shfUpdateFabBadge();
      }

      function shfClearAll() {
        if (!confirm((SHF.activeTab === 'history' ? 'Hapus semua riwayat?' : 'Hapus semua favorit?'))) return;
        shfSave(SHF.activeTab === 'history' ? SHF.HISTORY_KEY : SHF.BOOKMARK_KEY, []);
        shfRenderList();
        shfUpdateFabBadge();
      }

      function shfOpenPanel() {
        shfBuildPanel();
        SHF.panelOpen = true;
        requestAnimationFrame(function() {
          SHF.panelEl.style.transform = 'translateY(0)';
        });
        shfSetTab(SHF.activeTab);
      }
      function shfClosePanel() {
        if (!SHF.panelEl) return;
        SHF.panelOpen = false;
        SHF.panelEl.style.transform = 'translateY(100%)';
      }

      // make globally accessible (for onclick in innerHTML)
      window.shfSetTab = shfSetTab;
      window.shfClearAll = shfClearAll;
      window.shfClosePanel = shfClosePanel;
      window.shfRemoveItem = shfRemoveItem;

      /* ── Bookmark button injection ── */
      function shfInjectBookmarkBtn() {
        var url = window.location.pathname;
        var type = shfDetectType(url);
        if (!type || type.includes('-watch') || type.includes('-read')) {
          if (SHF._currentBookmarkBtn) { SHF._currentBookmarkBtn.remove(); SHF._currentBookmarkBtn = null; }
          return;
        }
        if (document.getElementById('sh-bookmark-btn')) return;

        // Find top-right action area on detail pages
        var topBtns = document.querySelector('[class*="top-safe"]')
                   || document.querySelector('[class*="absolute top-"]')
                   || null;
        if (!topBtns) return;

        var rightArea = topBtns.querySelector('[class*="flex gap"]') || topBtns.lastElementChild;
        if (!rightArea) return;

        var bookmarked = shfIsBookmarked(url);
        var btn = document.createElement('button');
        btn.id = 'sh-bookmark-btn';
        btn.title = bookmarked ? 'Hapus dari Favorit' : 'Tambah ke Favorit';
        btn.style.cssText = 'width:40px;height:40px;border-radius:50%;background:rgba(0,0,0,0.5);backdrop-filter:blur(12px);border:1px solid rgba(255,255,255,0.1);display:flex;align-items:center;justify-content:center;color:white;cursor:pointer;transition:all 0.2s;';
        btn.innerHTML = bookmarked
          ? '<svg width="18" height="18" viewBox="0 0 24 24" fill="#f59e0b" stroke="#f59e0b" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2z"/></svg>'
          : '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2z"/></svg>';

        btn.addEventListener('click', function(e) {
          e.stopPropagation();
          var added = shfToggleBookmark(url);
          btn.innerHTML = added
            ? '<svg width="18" height="18" viewBox="0 0 24 24" fill="#f59e0b" stroke="#f59e0b" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2z"/></svg>'
            : '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2z"/></svg>';
          btn.title = added ? 'Hapus dari Favorit' : 'Tambah ke Favorit';
          shfUpdateFabBadge();
          // Flash feedback
          btn.style.transform = 'scale(1.25)';
          setTimeout(function() { btn.style.transform = 'scale(1)'; }, 200);
        });

        rightArea.appendChild(btn);
        SHF._currentBookmarkBtn = btn;
      }

      /* ── Route change hook ── */
      function shfOnRouteChange(url) {
        shfTrackVisit(url);
        setTimeout(shfInjectBookmarkBtn, 800);
        setTimeout(shfCreateFab, 300);
        // Update bookmark btn state if url changed
        setTimeout(function() {
          var b = document.getElementById('sh-bookmark-btn');
          if (b) {
            var bk = shfIsBookmarked(url);
            b.innerHTML = bk
              ? '<svg width="18" height="18" viewBox="0 0 24 24" fill="#f59e0b" stroke="#f59e0b" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2z"/></svg>'
              : '<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><path d="M19 21l-7-5-7 5V5a2 2 0 012-2h10a2 2 0 012 2z"/></svg>';
          }
        }, 1200);
      }

      /* ── Hook into existing URL observer ── */
      var _shfOrigLastUrl = window.location.href;
      var shfRouteObserver = new MutationObserver(function() {
        if (window.location.href !== _shfOrigLastUrl) {
          _shfOrigLastUrl = window.location.href;
          shfOnRouteChange(window.location.pathname);
        }
      });
      shfRouteObserver.observe(document.body, { childList: true, subtree: true });

      /* ── Init ── */
      setTimeout(function() {
        shfCreateFab();
        shfOnRouteChange(window.location.pathname);
      }, 800);

      // ────────────────────────────────────────────────────────────────────────

    })();
    </script>
  </body>
</html>

Youez - 2016 - github.com/yon3zu
LinuXploit