Dizi ve filmleri eşleştirme modu eklendi.

This commit is contained in:
2025-10-29 23:58:55 +03:00
parent 279adf12e9
commit 8d7605969b
5 changed files with 1574 additions and 214 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -304,13 +304,13 @@ async function loadMovies() {
function posterUrl(movie) {
if (!movie.poster) return null;
const token = localStorage.getItem("token");
return `${API}${movie.poster}?token=${token}`;
return `${API}${movie.poster}?token=${token}&t=${Date.now()}`;
}
function backdropUrl(movie) {
if (!movie.backdrop) return null;
const token = localStorage.getItem("token");
return `${API}${movie.backdrop}?token=${token}`;
return `${API}${movie.backdrop}?token=${token}&t=${Date.now()}`;
}
async function refreshMovies() {
@@ -339,6 +339,16 @@ async function loadMovies() {
const handleKey = (event) => {
if (!showPlayerModal) return;
if (isEditableTarget(event.target)) return;
const isCmd = event.metaKey || event.ctrlKey;
if (isCmd && event.key.toLowerCase() === "a") {
// Text input'larda çalıştırma
if (isEditableTarget(event.target)) return;
event.preventDefault();
// Movies sayfasında tümünü seçme işlevi yok, sadece engelleme yapıyoruz
return;
}
if (event.key === "Escape") {
event.preventDefault();
closePlayer();

View File

@@ -69,7 +69,7 @@ let canPlayNext = false;
function assetUrl(pathname) {
if (!pathname) return null;
const token = localStorage.getItem("token");
return `${API}${pathname}?token=${token}`;
return `${API}${pathname}?token=${token}&t=${Date.now()}`;
}
function posterUrl(show) {
@@ -250,7 +250,7 @@ let canPlayNext = false;
function mapEpisodeToPlayerItem(show, episode) {
if (!show || !episode?.videoPath) return null;
const name = episode.videoPath;
const name = (episode.videoPath || "").replace(/^\/+/, "");
const ext = name.split(".").pop()?.toLowerCase() || "";
const inferredType = ext ? `video/${ext}` : "video/mp4";
const size =
@@ -276,6 +276,7 @@ let canPlayNext = false;
$: selectedName = selectedVideo?.name ?? "";
$: encName = selectedName ? encodeURIComponent(selectedName) : "";
$: downloadHref = encName ? `${API}/downloads/${encName}` : "#";
$: selectedLabel = selectedVideo?.episode
? `${selectedVideo.show.title} · ${formatEpisodeCode(
selectedVideo.episode
@@ -346,6 +347,18 @@ async function openVideoAtIndex(index) {
}
}
}
// Video element'in yüklendiğinden emin ol
await tick();
setTimeout(() => {
if (videoEl) {
console.log("Video element found after timeout:", videoEl);
console.log("Video src:", videoEl.src);
console.log("Video readyState:", videoEl.readyState);
} else {
console.error("Video element not found after timeout");
}
}, 1000);
}
function stopCurrentVideo() {
@@ -375,7 +388,8 @@ async function openVideoAtIndex(index) {
function getVideoURL() {
if (!selectedName) return "";
const token = localStorage.getItem("token");
return `${API}/media/${encName}?token=${token}`;
// selectedName zaten encode edilmiş, tekrar encode etme
return `${API}/media/${selectedName}?token=${token}`;
}
function playEpisodeFromCard(episode) {
@@ -532,6 +546,16 @@ async function openVideoAtIndex(index) {
const handleKey = (event) => {
if (!showPlayerModal) return;
if (isEditableTarget(event.target)) return;
const isCmd = event.metaKey || event.ctrlKey;
if (isCmd && event.key.toLowerCase() === "a") {
// Text input'larda çalıştırma
if (isEditableTarget(event.target)) return;
event.preventDefault();
// TvShows sayfasında tümünü seçme işlevi yok, sadece engelleme yapıyoruz
return;
}
if (event.key === "Escape") {
event.preventDefault();
closePlayer();
@@ -787,8 +811,13 @@ async function openVideoAtIndex(index) {
src={getVideoURL()}
class="video-element"
playsinline
controls={false}
preload="metadata"
crossorigin="anonymous"
type={selectedVideo?.type || "video/mp4"}
on:timeupdate={updateProgress}
on:loadedmetadata={async () => {
console.log("Video metadata loaded");
isPlaying = false;
currentTime = 0;
updateDuration();
@@ -805,7 +834,37 @@ async function openVideoAtIndex(index) {
isPlaying = false;
}
}}
on:loadeddata={() => {
console.log("Video data loaded");
}}
on:canplay={() => {
console.log("Video can play");
}}
on:error={(e) => {
console.error("Video error:", e);
}}
on:ended={() => (isPlaying = false)}
on:loadstart={() => {
console.log("Video load start");
}}
on:canplaythrough={() => {
console.log("Video can play through");
}}
on:stalled={() => {
console.log("Video stalled");
}}
on:suspend={() => {
console.log("Video suspended");
}}
on:abort={() => {
console.log("Video aborted");
}}
on:emptied={() => {
console.log("Video emptied");
}}
on:waiting={() => {
console.log("Video waiting");
}}
>
{#if subtitleURL}
<track
@@ -860,7 +919,7 @@ async function openVideoAtIndex(index) {
<i class="fa-solid fa-expand"></i>
</button>
<a
href={selectedName ? `${API}/downloads/${selectedName}` : "#"}
href={downloadHref}
download={selectedName || undefined}
class="control-btn"
title="Download"
@@ -1235,15 +1294,26 @@ async function openVideoAtIndex(index) {
background: rgba(0, 0, 0, 0.25);
border-radius: 16px;
scrollbar-width: thin;
scrollbar-color: rgba(255, 255, 255, 0.3) rgba(0, 0, 0, 0.1);
}
.episode-list::-webkit-scrollbar {
width: 6px;
width: 8px;
}
.episode-list::-webkit-scrollbar-track {
background: rgba(0, 0, 0, 0.1);
border-radius: 4px;
}
.episode-list::-webkit-scrollbar-thumb {
background: rgba(255, 255, 255, 0.18);
border-radius: 999px;
background: rgba(255, 255, 255, 0.3);
border-radius: 4px;
border: 1px solid rgba(255, 255, 255, 0.1);
}
.episode-list::-webkit-scrollbar-thumb:hover {
background: rgba(255, 255, 255, 0.5);
}
.episode-card {
@@ -1345,98 +1415,117 @@ async function openVideoAtIndex(index) {
padding: 20px;
}
/* 🎞️ Film oynatıcı ile aynı düzen */
.modal-overlay {
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.75);
backdrop-filter: blur(4px);
background: rgba(0, 0, 0, 0.35);
display: flex;
align-items: center;
justify-content: center;
z-index: 5000;
padding: 32px;
z-index: 6000;
}
.modal-content {
width: min(960px, 95vw);
background: #0f0f0f;
border-radius: 16px;
padding: 18px;
width: 70%;
height: 70%;
background: #1a1a1a;
border-radius: 12px;
display: flex;
flex-direction: column;
gap: 16px;
color: #f5f5f5;
position: relative;
min-height: 0;
overflow: hidden;
box-shadow: 0 0 30px rgba(0, 0, 0, 0.8);
}
.modal-header {
display: flex;
justify-content: space-between;
align-items: baseline;
gap: 12px;
align-items: center;
background: #2a2a2a;
padding: 10px 16px;
color: #fff;
font-size: 16px;
font-weight: 500;
flex-shrink: 0;
}
.video-title {
font-size: 16px;
flex: 1;
text-align: center;
font-weight: 600;
line-height: 1.4;
}
.video-meta {
font-size: 13px;
color: #b5b5b5;
color: #ccc;
}
.custom-player {
background: #000;
border-radius: 12px;
overflow: hidden;
flex: 1;
display: flex;
flex-direction: column;
gap: 12px;
justify-content: space-between;
min-height: 0;
background: #000;
}
.video-element {
flex: 1 1 auto;
width: 100%;
max-height: 60vh;
height: auto;
max-height: 100%;
min-height: 0;
object-fit: contain;
background: #000;
border: none;
outline: none;
}
.video-element:focus {
outline: none !important;
box-shadow: none !important;
}
.controls {
background: #1c1c1c;
padding: 10px 16px;
display: flex;
flex-direction: column;
gap: 8px;
padding: 0 14px 14px;
flex-shrink: 0;
border-top: 1px solid #333;
}
.top-controls {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
flex-shrink: 0;
}
.left-controls {
display: inline-flex;
display: flex;
align-items: center;
gap: 8px;
gap: 10px;
}
.control-btn {
background: none;
border: none;
color: #f5f5f5;
font-size: 16px;
color: #fff;
font-size: 18px;
cursor: pointer;
transition: color 0.2s ease;
transition: opacity 0.2s;
}
.control-btn:hover {
color: #f5b333;
opacity: 0.7;
}
.control-btn[disabled],
.control-btn[disabled]:hover {
color: rgba(245, 245, 245, 0.35);
.control-btn[disabled] {
opacity: 0.35;
cursor: default;
pointer-events: none;
}
@@ -1447,59 +1536,56 @@ async function openVideoAtIndex(index) {
gap: 10px;
}
.volume-slider,
.progress-slider {
appearance: none;
.volume-slider {
-webkit-appearance: none;
width: 100px;
height: 4px;
border-radius: 2px;
background: linear-gradient(
to right,
#f5b333 var(--fill, 100%),
rgba(255, 255, 255, 0.24) var(--fill, 100%)
#ff3b30 calc(var(--fill, 100%) * 1%),
rgba(255, 255, 255, 0.3) calc(var(--fill, 100%) * 1%)
);
border-radius: 999px;
height: 4px;
outline: none;
cursor: pointer;
transition: background 0.2s ease;
}
.volume-slider {
width: 90px;
}
.progress-slider {
width: 100%;
}
.volume-slider::-webkit-slider-thumb,
.progress-slider::-webkit-slider-thumb {
appearance: none;
.volume-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 12px;
height: 12px;
border-radius: 50%;
background: #f5b333;
background: #fff;
cursor: pointer;
margin-top: -4px;
transition: transform 0.2s ease;
}
.volume-slider::-webkit-slider-thumb:hover {
transform: scale(1.3);
}
.bottom-controls {
display: flex;
align-items: center;
justify-content: space-between;
gap: 12px;
font-size: 13px;
color: #d4d4d4;
flex-shrink: 0;
}
.global-close-btn {
position: absolute;
top: 18px;
right: 28px;
background: rgba(0, 0, 0, 0.55);
color: #f5f5f5;
border: none;
border-radius: 50%;
width: 36px;
height: 36px;
display: grid;
place-items: center;
.progress-slider {
flex: 1;
cursor: pointer;
font-size: 17px;
accent-color: #27ae60;
}
.time {
color: #ccc;
font-size: 13px;
min-width: 90px;
text-align: right;
white-space: nowrap;
}
@media (max-width: 860px) {