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/free-dive-to-the-future/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 2 - A Promise on a Shooting Star!
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
Bebas! -Menyelam ke Future-
Free!: Dive to the Future - Haruka Nanase, sekarang kuliah di Tokyo, memiliki pertemuan tak terduga dengan Shiina Asahi, seorang teman lama dan rekan satu tim dari masa sekolah menengah. Pertemuan ini membawa kenangan lama dan menyakitkan ke permukaan, sementara tantangan baru menunggu di jalan untuk bersaing pada tingkat internasional.
Nonton Free!: Dive to the Future Episode 2 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 Free!: Dive to the Future dengan subtitle Indonesia di perangkat kamu.
Kyoto Animation
Free!: Dive to the Future episode 2 subtitle indonesia, download Free!: Dive to the Future episode 2 subtitle indonesia, unduh Free!: Dive to the Future episode 2 subtitle indonesia, streaming Free!: Dive to the Future episode 2 subtitle indonesia, nonton Free!: Dive to the Future episode 2 subtitle indonesia, anime Free!: Dive to the Future episode 2 subtitle indonesia, download anime Free!: Dive to the Future episode 2 subtitle indonesia, unduh anime Free!: Dive to the Future episode 2 subtitle indonesia, streaming anime Free!: Dive to the Future episode 2 subtitle indonesia, nonton anime Free!: Dive to the Future episode 2 subtitle indonesia, Free!: Dive to the Future episode 2 sub indo, download Free!: Dive to the Future episode 2 sub indo, unduh Free!: Dive to the Future episode 2 sub indo, streaming Free!: Dive to the Future episode 2 sub indo, nonton Free!: Dive to the Future episode 2 sub indo, anime Free!: Dive to the Future episode 2 sub indo, download anime Free!: Dive to the Future episode 2 sub indo, unduh anime Free!: Dive to the Future episode 2 sub indo, streaming anime Free!: Dive to the Future episode 2 sub indo, nonton anime Free!: Dive to the Future episode 2 sub indo, Free!: Dive to the Future episode 2 takarir indonesia, download Free!: Dive to the Future episode 2 takarir indonesia, unduh Free!: Dive to the Future episode 2 takarir indonesia, streaming Free!: Dive to the Future episode 2 takarir indonesia, nonton Free!: Dive to the Future episode 2 takarir indonesia, anime Free!: Dive to the Future episode 2 takarir indonesia, download anime Free!: Dive to the Future episode 2 takarir indonesia, unduh anime Free!: Dive to the Future episode 2 takarir indonesia, streaming anime Free!: Dive to the Future episode 2 takarir indonesia, nonton anime Free!: Dive to the Future episode 2 takarir indonesia, Free!: Dive to the Future episode 2 takarir indo, download Free!: Dive to the Future episode 2 takarir indo, unduh Free!: Dive to the Future episode 2 takarir indo, streaming Free!: Dive to the Future episode 2 takarir indo, nonton Free!: Dive to the Future episode 2 takarir indo, anime Free!: Dive to the Future episode 2 takarir indo, download anime Free!: Dive to the Future episode 2 takarir indo, unduh anime Free!: Dive to the Future episode 2 takarir indo, streaming anime Free!: Dive to the Future episode 2 takarir indo, nonton anime Free!: Dive to the Future episode 2 takarir indo, Free!: Dive to the Future episode 2 subtitle indo, download Free!: Dive to the Future episode 2 subtitle indo, unduh Free!: Dive to the Future episode 2 subtitle indo, streaming Free!: Dive to the Future episode 2 subtitle indo, nonton Free!: Dive to the Future episode 2 subtitle indo, anime Free!: Dive to the Future episode 2 subtitle indo, download anime Free!: Dive to the Future episode 2 subtitle indo, unduh anime Free!: Dive to the Future episode 2 subtitle indo, streaming anime Free!: Dive to the Future episode 2 subtitle indo, nonton anime Free!: Dive to the Future episode 2 subtitle indo
Komentar (0)
atau untuk berkomentar.
Belum ada komentar. Jadilah yang pertama!