0;
var canB2 = !!(d.stream_urls && d.stream_urls.length);
var canGd = hasGd;
var canHls = !!d.hls_url;
this._prevServer = this.server;
// Fullscreen bisa native (document.fullscreenElement) ATAU
// mode fallback CSS Plyr (`.plyr--fullscreen-fallback`, dipakai
// WebView / browser tanpa Fullscreen API). Kalau hanya cek
// document.fullscreenElement, di mode fallback ini nilainya
// null -> re-enter tak pernah jalan -> keluar fullscreen.
this._wasFullscreen = !!(document.fullscreenElement
|| document.querySelector('.plyr--fullscreen-fallback'));
const c = document.getElementById('anime-player-container');
if (c) {
var spriteUrl = d.sprite_url ? d.sprite_url : null;
window.b2PlayerOptions = {
sources: d.stream_urls,
gdriveSources: gdSrcs,
episodeId: d.id,
prevUrl: d.prev_url,
nextUrl: d.next_url,
skipIntroAt: null,
posterUrl: d.poster_url,
title: epTitle,
slug: slug,
currentEp: d.episode_number,
episodes: window.b2PlayerOptions?.episodes || [],
epTotal: window.b2PlayerOptions?.epTotal || 0,
epPerPage: window.b2PlayerOptions?.epPerPage || 50,
epCurrentPage: window.b2PlayerOptions?.epCurrentPage || 1,
logoUrl: window.b2PlayerOptions?.logoUrl || null,
spriteUrl: spriteUrl,
};
var _pp = window.b2PlayerOptions.epPerPage || 52;
var _eps = window.b2PlayerOptions.episodes || [];
var _epIdx = _eps.findIndex(function(e) { return e.id === d.id; });
window.b2PlayerOptions.epCurrentPage = _epIdx >= 0 ? Math.floor(_epIdx / _pp) + 1 : 1;
}
this.gdriveSources = gdSrcs;
if (window.b2PlayerOptions) window.b2PlayerOptions.hlsUrl = d.hls_url || null;
// Resolve the target server BEFORE touching any player, otherwise the
// previous episode keeps playing inside a display:none frame (double audio).
var order = (this.server === 'hls') ? ['hls', 'b2', 'gdrive'] : ['b2', 'gdrive', 'hls'];
var picked = null;
for (var oi = 0; oi < order.length; oi++) {
if (order[oi] === 'b2' && canB2) { picked = 'b2'; break; }
if (order[oi] === 'gdrive' && canGd) { picked = 'gdrive'; break; }
if (order[oi] === 'hls' && canHls) { picked = 'hls'; break; }
}
if (!picked) picked = canB2 ? 'b2' : (canGd ? 'gdrive' : (canHls ? 'hls' : this.server));
this.server = picked;
this.pauseInactivePlayers();
// Player langsung mencoba primary GDrive. Kalau kena 403,
// player.js (_handleFailure) yang menampilkan overlay dan
// memicu mirror on-demand — jadi pindah episode tidak lagi
// tertahan menunggu mirror dibuat.
if (picked === 'hls' || (!canB2 && !canGd)) {
this.killDirectPlayer();
} else if (c) {
var gdNow = (picked === 'gdrive');
var srces = gdNow ? gdSrcs : d.stream_urls;
if (gdNow) this.gdriveLoaded = false; else this.b2Loaded = false;
if (c._player && c._player.switchSource) {
c._player.isGdrive = gdNow;
c._player.switchSource(
srces, d.prev_url, d.next_url, d.poster_url, epTitle,
slug, window.b2PlayerOptions.episodes, d.episode_number,
window.b2PlayerOptions.epCurrentPage, window.b2PlayerOptions.epPerPage,
window.b2PlayerOptions.epTotal, d.id, spriteUrl
);
const v = c.querySelector('#anime-video');
if (v) {
let _done = false;
const _ready = () => { if (_done) return; _done = true; if (gdNow) this.gdriveLoaded = true; else this.b2Loaded = true; };
v.addEventListener('playing', _ready, { once: true });
v.addEventListener('canplay', _ready, { once: true });
setTimeout(_ready, 8000);
} else { if (gdNow) this.gdriveLoaded = true; else this.b2Loaded = true; }
} else {
c.querySelectorAll('video').forEach((v) => this._hardStopMedia(v));
c.innerHTML = '';
this.$nextTick(() => {
if (gdNow) window.initAnimePlayer(c, Object.assign({}, window.b2PlayerOptions, { sources: srces, isGdrive: true }));
else window.initAnimePlayer(c, window.b2PlayerOptions);
if (gdNow) this.gdriveLoaded = true; else this.b2Loaded = true;
});
}
}
// HLS player: (re)source it only after the server is resolved.
if (this.server === 'hls') {
this.hlsLoaded = false;
const hc = document.getElementById('hls-player-container');
// A new episode is an explicit play intent -- unless the
// user paused while this switch was still in flight.
if (hc && (hc._hlsPauseEpoch || 0) === hcEp0) hc._hlsUserPaused = false;
if (!canHls) {
this.killHlsPlayer();
} else if (hc && typeof hc.switchHlsSource === 'function') {
hc.switchHlsSource(d.hls_url);
this.hlsLoaded = true;
} else {
this.$nextTick(() => this.loadHls());
}
} else {
this._pauseHls();
}
// 1. Release date
if (this.$refs.epReleaseDate) {
var dt = this.$refs.epReleaseDate;
if (d.formatted_release_date) {
dt.querySelector('span') ? dt.querySelector('span').textContent = d.formatted_release_date : dt.appendChild(document.createTextNode(d.formatted_release_date));
dt.style.display = '';
} else {
dt.style.display = 'none';
}
}
// 3. Duration
if (this.$refs.epDuration) {
if (d.duration) {
this.$refs.epDuration.querySelector('span').textContent = d.duration;
this.$refs.epDuration.style.display = '';
} else {
this.$refs.epDuration.style.display = 'none';
}
}
// 4. Description
if (this.$refs.epDescription) {
var descP = this.$refs.epDescription.querySelector('p');
if (descP) {
if (d.description) {
descP.innerHTML = '
Episode ' + epLabel + ' - ' + d.description;
this.$refs.epDescription.style.display = '';
} else {
this.$refs.epDescription.style.display = 'none';
}
}
}
// 5. Like button
var likeBtn = document.getElementById('like-btn');
if (likeBtn) {
likeBtn.dataset.episodeId = d.id;
likeBtn.dataset.liked = d.is_liked ? '1' : '0';
var lc = document.getElementById('like-count');
if (lc) lc.textContent = d.likes_count || '0';
var icon = likeBtn.querySelector('svg');
if (d.is_liked) {
likeBtn.classList.add('liked');
if (icon) icon.setAttribute('fill', 'currentColor');
} else {
likeBtn.classList.remove('liked');
if (icon) icon.setAttribute('fill', 'none');
}
}
// 7. Update current episode ID for pagination
this.currentEpisodeId = d.id;
// 9. HLS / GDrive / B2 tabs — source + player already resolved above
this.hasHls = canHls;
this.hasGdrive = canGd;
this.hasB2Stream = canB2;
if (this._wasFullscreen && this._prevServer !== this.server) {
const target = this._isDirect()
? document.getElementById('anime-player-container')
: document.getElementById('hls-player-container');
this._reenterFullscreen(target, 0);
}
var posterOverlays = document.querySelectorAll('.watch-player-frame [style*="background-image"]');
if (d.poster_url) {
for (var i = 0; i < posterOverlays.length; i++) {
posterOverlays[i].style.backgroundImage = 'url(\'' + d.poster_url + '\')';
}
}
// 10. Prev/Next buttons
if (this.$refs.prevBtn) {
if (d.prev_url) { this.$refs.prevBtn.href = d.prev_url; this.$refs.prevBtn.classList.remove('hidden'); }
else { this.$refs.prevBtn.classList.add('hidden'); }
}
if (this.$refs.nextBtn) {
if (d.next_url) { this.$refs.nextBtn.href = d.next_url; this.$refs.nextBtn.classList.remove('hidden'); }
else { this.$refs.nextBtn.classList.add('hidden'); }
}
// 11. Browser URL
history.pushState(null, '', '/anime/' + slug + '/episode/' + d.episode_number);
// 12. Breadcrumb episode
var breadcrumbEpisode = document.querySelector('#watch-breadcrumb .crumb-seg:last-child .crumb-label');
if (breadcrumbEpisode) {
breadcrumbEpisode.textContent = false ? 'Movie' : 'Ep ' + epLabel;
}
// 13. Episode chips active state
document.querySelectorAll('.ep-chip').forEach(function(el) {
el.classList.remove('active');
if (parseInt(el.dataset.ep) === d.episode_number) {
el.classList.add('active');
}
});
// 12b. Full grid items active state (showAllEpisodes)
document.querySelectorAll('.ep-btn, .ep-btn-sidebar').forEach(function(el) {
if (parseInt(el.dataset.ep) === d.episode_number) {
el.classList.add('bg-purple-600', 'text-white');
el.classList.remove('bg-gray-800', 'text-gray-400', 'border', 'border-gray-700/50', 'hover:bg-gray-700', 'bg-gray-700/50');
} else {
el.classList.remove('bg-purple-600', 'text-white');
el.classList.add('bg-gray-800', 'text-gray-400', 'border', 'border-gray-700/50', 'hover:bg-gray-700');
}
});
// 13. Sidebar episodes active state
document.querySelectorAll('.sidebar-ep-item').forEach(function(el) {
el.classList.remove('active', 'bg-purple-600/10');
el.classList.add('hover:bg-white/5');
if (parseInt(el.dataset.ep) === d.episode_number) {
el.classList.remove('hover:bg-white/5');
el.classList.add('active', 'bg-purple-600/10');
}
});
// 14. Repost button
var rpBtn = document.getElementById('repost-btn');
if (rpBtn) rpBtn.dataset.episodeId = d.id;
// 15. Download link
var dlLink = document.getElementById('download-link');
if (dlLink) dlLink.href = '/anime/' + slug + '/episode/' + d.episode_number + '/download';
// 16. Report forms
document.querySelectorAll('.report-form').forEach(function(f) {
f.action = '/episode/' + d.id + '/report';
});
// 17. Comments
if (d.comments_html) {
var csEl = document.getElementById('comment-section');
if (csEl) csEl.outerHTML = d.comments_html;
}
// 18. Episode Berikutnya
var nexEl = document.getElementById('next-episode-container');
if (nexEl) {
nexEl.innerHTML = d.next_episode_html || '';
nexEl.hidden = !(d.next_episode_html || '').trim();
}
// Safety net: only the resolved server may keep playing.
this.pauseInactivePlayers();
this._loading = false;
})
.catch(() => { this._loading = false; });
},
// Keluar dari fullscreen (native maupun fallback CSS) pada player
// selain `keep`.
_exitOtherFullscreens(keep) {
const keepPlayer = keep ? (keep._player ? keep._player.player : keep._hlsPlayer) : null;
const keepTarget = keepPlayer && keepPlayer.fullscreen ? keepPlayer.fullscreen.target : null;
['anime-player-container', 'hls-player-container'].forEach((id) => {
const oc = document.getElementById(id);
if (!oc || oc === keep) return;
const op = oc._player ? oc._player.player : oc._hlsPlayer;
try {
if (!op || !op.fullscreen || !op.fullscreen.active) return;
// Target fullscreen sama (`.player-fs-shell` dipakai bersama
// kedua player) -> JANGAN di-exit; itu justru menutup
// fullscreen yang sedang aktif.
if (keepTarget && op.fullscreen.target === keepTarget) return;
op.fullscreen.exit();
} catch (e) {}
});
},
_reenterFullscreen(container, attempts) {
if (!container) return;
attempts = attempts || 0;
if (attempts > 60) return;
const retry = (delay) => setTimeout(() => this._reenterFullscreen(container, attempts + 1), delay || 200);
const pe = container.querySelector('.plyr');
const p = container._player ? (container._player.player || null) : (container._hlsPlayer || null);
if (!p || !p.fullscreen) { retry(); return; }
// Sudah fullscreen di player ini (native atau fallback CSS)?
if (p.fullscreen.active) {
const nf = document.fullscreenElement;
if (!nf || !pe || nf === pe || pe.contains(nf) || nf.contains(pe)) return;
}
// Player yang baru ditinggalkan harus keluar dulu -- baik native
// (document.fullscreenElement) maupun fallback CSS (class), karena
// di mode fallback document.fullscreenElement selalu null.
this._exitOtherFullscreens(container);
// Native: tunggu sampai browser benar-benar melepas fullscreen.
if (document.fullscreenElement) { retry(); return; }
// Frame tujuan harus sudah ter-render (Alpine x-show), kalau tidak
// fullscreen ke elemen yang belum tampil akan kosong.
const frame = container.closest('.watch-player-frame');
if (frame && window.getComputedStyle(frame).display === 'none') { retry(); return; }
if (!pe) { retry(); return; }
// Plyr's fullscreen.enter() TIDAK mengembalikan promise -> jangan pakai
// .catch() (dulu melempar TypeError dan bikin re-enter tak pernah jalan).
// requestFullscreen juga bisa ditolak (butuh user gesture), jadi hasilnya
// dipantau lewat retry, bukan dianggap sukses begitu saja.
try {
if (!p.fullscreen.active) p.fullscreen.enter();
} catch (e) {
retry(300);
return;
}
retry(300);
},
loadPage(page) {
if (this._loadingGrid) return;
this._loadingGrid = true;
const params = new URLSearchParams({
page: page || 1,
ep: this.currentEpisodeId,
sort: this.epSort,
search: this.epSearch
});
fetch('/anime/ghost-concert-missing-songs/episodes/grid?' + params.toString())
.then(r => r.json())
.then(d => {
if (this.$refs.sidebarEpList) this.$refs.sidebarEpList.innerHTML = d.sidebar_episodes;
if (this.$refs.sidebarPag) this.$refs.sidebarPag.innerHTML = d.pagination;
this._loadingGrid = false;
})
.catch(() => { this._loadingGrid = false; });
},
applyEpSearch() {
this.loadPage(1);
}
}"
class="">
EP 9 - Master Thyself / Rule Through Discipline
Info
Komentar (0)
Repost
Download
Report
Laporkan Masalah
Pilih alasan untuk melaporkan episode ini.
Batal
Laporkan Masalah
Jelaskan masalah yang kamu temui agar tim kami bisa memperbaikinya.
Share
Konser Hantu: Lagu Hilang
Ghost Concert: Missing Songs - The year is 2045, in a world where songs have been banned. Instead of humans playing instruments and creating music, a music app titled "MiucS" is in charge of these things. High school student Seria goes out with her friends one day, but she happens to hear someone singing, which is supposed to be prohibited. What she finds is a lone ghost, a "Great Ghost" who has appeared from outside this world. Now Seria must decide whether to stay silent or risk everything to bring music back to the world.
(Source: Anime News Network, Crunchyroll, edited)
Nonton Ghost Concert: Missing Songs Episode 9 Subtitle Indonesia secara online dengan berbagai pilihan kualitas video, mulai dari 360p, 480p, 720p hingga 1080p. Pilih server dan resolusi yang sesuai untuk menikmati episode Ghost Concert: Missing Songs dengan subtitle Indonesia di perangkat kamu.
ENGI
Ghost Concert: Missing Songs episode 9 subtitle indonesia, download Ghost Concert: Missing Songs episode 9 subtitle indonesia, unduh Ghost Concert: Missing Songs episode 9 subtitle indonesia, streaming Ghost Concert: Missing Songs episode 9 subtitle indonesia, nonton Ghost Concert: Missing Songs episode 9 subtitle indonesia, anime Ghost Concert: Missing Songs episode 9 subtitle indonesia, download anime Ghost Concert: Missing Songs episode 9 subtitle indonesia, unduh anime Ghost Concert: Missing Songs episode 9 subtitle indonesia, streaming anime Ghost Concert: Missing Songs episode 9 subtitle indonesia, nonton anime Ghost Concert: Missing Songs episode 9 subtitle indonesia, Ghost Concert: Missing Songs episode 9 sub indo, download Ghost Concert: Missing Songs episode 9 sub indo, unduh Ghost Concert: Missing Songs episode 9 sub indo, streaming Ghost Concert: Missing Songs episode 9 sub indo, nonton Ghost Concert: Missing Songs episode 9 sub indo, anime Ghost Concert: Missing Songs episode 9 sub indo, download anime Ghost Concert: Missing Songs episode 9 sub indo, unduh anime Ghost Concert: Missing Songs episode 9 sub indo, streaming anime Ghost Concert: Missing Songs episode 9 sub indo, nonton anime Ghost Concert: Missing Songs episode 9 sub indo, Ghost Concert: Missing Songs episode 9 takarir indonesia, download Ghost Concert: Missing Songs episode 9 takarir indonesia, unduh Ghost Concert: Missing Songs episode 9 takarir indonesia, streaming Ghost Concert: Missing Songs episode 9 takarir indonesia, nonton Ghost Concert: Missing Songs episode 9 takarir indonesia, anime Ghost Concert: Missing Songs episode 9 takarir indonesia, download anime Ghost Concert: Missing Songs episode 9 takarir indonesia, unduh anime Ghost Concert: Missing Songs episode 9 takarir indonesia, streaming anime Ghost Concert: Missing Songs episode 9 takarir indonesia, nonton anime Ghost Concert: Missing Songs episode 9 takarir indonesia, Ghost Concert: Missing Songs episode 9 takarir indo, download Ghost Concert: Missing Songs episode 9 takarir indo, unduh Ghost Concert: Missing Songs episode 9 takarir indo, streaming Ghost Concert: Missing Songs episode 9 takarir indo, nonton Ghost Concert: Missing Songs episode 9 takarir indo, anime Ghost Concert: Missing Songs episode 9 takarir indo, download anime Ghost Concert: Missing Songs episode 9 takarir indo, unduh anime Ghost Concert: Missing Songs episode 9 takarir indo, streaming anime Ghost Concert: Missing Songs episode 9 takarir indo, nonton anime Ghost Concert: Missing Songs episode 9 takarir indo, Ghost Concert: Missing Songs episode 9 subtitle indo, download Ghost Concert: Missing Songs episode 9 subtitle indo, unduh Ghost Concert: Missing Songs episode 9 subtitle indo, streaming Ghost Concert: Missing Songs episode 9 subtitle indo, nonton Ghost Concert: Missing Songs episode 9 subtitle indo, anime Ghost Concert: Missing Songs episode 9 subtitle indo, download anime Ghost Concert: Missing Songs episode 9 subtitle indo, unduh anime Ghost Concert: Missing Songs episode 9 subtitle indo, streaming anime Ghost Concert: Missing Songs episode 9 subtitle indo, nonton anime Ghost Concert: Missing Songs episode 9 subtitle indo
Komentar (0)
atau untuk berkomentar.
Belum ada komentar. Jadilah yang pertama!