pirates/index.html

1671 lines
No EOL
67 KiB
HTML

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta property="og:title" content="Watermelon Movies 🍉">
<meta property="og:description" content="The jews took hollywood, we're stealing it back">
<meta property="og:image" content="">
<meta property="og:url" content="">
<meta name="twitter:card" content="summary_large_image">
<title>Watermelon Movies 🍉</title>
<link rel="icon" type="image/x-icon" href="favicon_watermelon_icon.ico">
<link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
<!-- iOS Meta Tags -->
<link rel="manifest" href="/manifest.json">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="default">
<meta name="apple-mobile-web-app-title" content="Watermelon Movies 🍉">
<link rel="apple-touch-icon" href="/icons/apple-touch-icon.png">
<style>
/* Custom styles for movie tiles */
.movie-tile {
transition: transform 0.3s;
cursor: pointer;
background: rgb(232, 222, 222);
border-radius: 15px;
padding: 10px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
}
.movie-tile:hover {
transform: scale(1.05);
}
/* Background gradient color */
body {
background: linear-gradient(to right, #32cd32 50%, #ff6347 50%);
font-family: 'Courier New', Courier, monospace;
overflow-y: auto;
max-height: 100vh;
}
.dark-mode body {
background: linear-gradient(to right, #000000 50%, #333333 50%);
color: white;
}
.translate-x-full {
transform: translateX(100%);
}
.dark-mode .movie-tile {
background: #444;
color: white;
}
.dark-mode .search-input {
background-color: #333;
color: white;
}
.dark-mode .video-modal-content {
background-color: #222;
}
.dark-mode .stream-btn, .dark-mode .trailer-btn {
background-color: #555;
}
.dark-mode .stream-btn:hover, .dark-mode .trailer-btn:hover {
background-color: #666;
}
.night-light body {
background: linear-gradient(to right, #f5deb3 50%, #deb887 50%);
color: black;
}
.night-light .video-modal-content {
background-color: #f5deb3;
}
.night-light .movie-tile {
background: #f5deb3;
color: black;
}
.night-light .search-input {
background-color: #deb887;
color: black;
}
.night-light .stream-btn, .night-light .trailer-btn {
background-color: #daa520;
}
.night-light .stream-btn:hover, .night-light .trailer-btn:hover {
background-color: #cd853f;
}
.video-modal {
display: none;
position: fixed;
z-index: 1000;
left: 0;
top: 0;
width: 100%;
height: 100%;
overflow: auto;
background-color: rgba(0,0,0,0.4);
}
.video-modal-content {
position: fixed;
top: 10%;
left: 5%;
width: 90%;
background-color: #000000;
background-size: cover;
padding: 20px;
border: 1px solid #888;
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
border-radius: 25px;
border: 4px solid black;
}
.close {
position: absolute;
right: 36px;
top: 20px;
font-size: 50px;
color: #aaa;
cursor: pointer;
}
.close:hover,
.close:focus {
color: deeppink;
text-decoration: none;
}
@media (max-width: 600px) {
.video-modal-content {
padding: 10px;
font-size: 14px;
height: 70vh;
}
.close {
font-size: 24px;
}
}
.movie-tile img {
border-radius: 10px;
margin-bottom: 10px;
}
.movie-tile h3, .movie-tile p {
color: black;
text-align: left;
margin: 5px 0;
}
.dark-mode .movie-tile h3, .dark-mode .movie-tile p {
color: white;
}
.night-light .movie-tile h3, .night-light .movie-tile p {
color: black;
}
.media-label {
display: inline-block;
padding: 4px 8px;
background-color: #ff6347;
color: #fff;
border-radius: 4px;
font-size: 0.75rem;
position: absolute;
top: 10px;
right: 10px;
}
.stream-btn, .trailer-btn {
padding: 5px 10px;
margin: 5px;
border: none;
border-radius: 4px;
cursor: pointer;
}
.trailer-btn {
background-color: #32cd32;
color: white;
}
.trailer-btn:hover {
background-color: #28a745;
}
.share-btn {
background-color: rgb(250, 217, 0);
color: black;
padding: 5px 10px;
margin: 16px 5px 5px 0px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.5);
display: inline-flex;
align-items: center;
justify-content: center;
}
.share-btn:hover {
background-color: rgb(255, 217, 0);
}
.popcorn {
margin-left: 5px;
display: inline-block;
animation: popcornBounce 1s ease-in-out infinite;
}
@keyframes popcornBounce {
0%, 100% {
transform: translateY(0) rotate(0deg);
filter: drop-shadow(0 0 0 transparent);
}
50% {
transform: translateY(-10px) rotate(360deg);
filter: drop-shadow(0 5px 5px rgba(0, 0, 0, 0.4));
}
}
.melon {
width: 6em;
height: 6em;
background: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/21542/melon.svg) no-repeat no-repeat;
background-size: 48em;
background-position: 0% 0%;
animation: melonSequence 0.8s steps(7) infinite reverse;
margin: 0em auto;
}
@keyframes melonSequence {
from { background-position: 0% 0%; }
to { background-position: 100% 0%; }
}
/* Season and episode selectors */
.season-selector, .episode-selector {
margin-top: 8px;
display: flex;
flex-wrap: wrap;
gap: 15px; /* Increased spacing between buttons */
max-height: 150px;
overflow-y: auto;
padding: 12px;
background-color: rgba(0,0,0,0.2);
border-radius: 8px;
}
/* Add more spacing between season and episode selectors */
.episode-selector {
margin-top: 16px;
}
.season-btn, .episode-btn {
padding: 8px 16px;
background-color: #4CAF50;
color: white;
border: none;
border-radius: 4px;
cursor: pointer;
/* Add min-width to ensure consistent spacing */
min-width: 40px;
text-align: center;
/* Make buttons not touch each other */
margin: 3px;
}
.season-btn:hover, .episode-btn:hover {
background-color: #3e8e41;
}
.season-btn.active, .episode-btn.active {
background-color: #ff6347;
}
/* Dark mode styles for buttons */
.dark-mode .season-btn, .dark-mode .episode-btn {
background-color: #555;
color: white;
}
.dark-mode .season-btn:hover, .dark-mode .episode-btn:hover {
background-color: #666;
}
.dark-mode .season-btn.active, .dark-mode .episode-btn.active {
background-color: #ff6347;
}
/* Night light styles for buttons */
.night-light .season-btn, .night-light .episode-btn {
background-color: #daa520;
color: black;
}
.night-light .season-btn:hover, .night-light .episode-btn:hover {
background-color: #cd853f;
}
.night-light .season-btn.active, .night-light .episode-btn.active {
background-color: #ff6347;
}
.alt-source-btn {
background-color: #ff6347;
color: white;
padding: 6px 12px;
margin: 5px;
border: none;
border-radius: 4px;
cursor: pointer;
font-weight: bold;
}
.alt-source-btn:hover {
background-color: #ff4500;
}
/* Toggle styles */
.dot {
position: absolute;
left: 1px;
top: 1px;
background-color: white;
width: 4px;
height: 4px;
border-radius: 50%;
transition: transform 0.3s;
}
.dot-night {
position: absolute;
left: 1px;
top: 1px;
background-color: white;
width: 4px;
height: 4px;
border-radius: 50%;
transition: transform 0.3s;
}
/* Dark mode styles for toggle labels */
.dark-mode .toggle-label {
color: white;
}
/* Progress indicator */
#progressIndicator {
position: fixed;
top: 4px;
right: 4px;
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 8px 16px;
border-radius: 8px;
z-index: 50;
display: none;
transition: opacity 0.3s;
}
.coming-soon-slider {
scroll-snap-type: x mandatory;
overflow-x: scroll;
-webkit-overflow-scrolling: touch;
}
.slider-container {
display: flex;
gap: 1rem;
-ms-overflow-style: none;
scrollbar-width: none;
}
.slider-container::-webkit-scrollbar {
display: none;
}
.slider-item {
flex: 0 0 auto;
width: 200px;
scroll-snap-align: start;
}
.slider-item img {
width: 100%;
height: auto;
border-radius: 0.5rem;
}
.category-slider {
display: flex;
gap: 1rem;
-ms-overflow-style: none;
scrollbar-width: none;
overflow-x: auto;
scroll-behavior: smooth;
scroll-snap-type: x mandatory;
}
.category-slider::-webkit-scrollbar {
display: none;
}
.category-item {
flex: 0 0 auto;
width: 180px;
scroll-snap-align: start;
position: relative;
}
.category-item img {
width: 100%;
height: auto;
border-radius: 0.5rem;
transition: transform 0.2s;
}
.category-item:hover img {
transform: scale(1.05);
}
.rating-badge {
position: absolute;
top: 5px;
right: 5px;
background-color: rgba(0,0,0,0.7);
color: white;
padding: 2px 6px;
border-radius: 4px;
font-size: 0.8rem;
}
/* Placeholder shimmer effect for loading */
.placeholder {
background: #f6f7f8;
background: linear-gradient(to right, #eee 8%, #ddd 18%, #eee 33%);
background-size: 800px 104px;
position: relative;
animation: placeholderShimmer 1.5s infinite linear forwards;
border-radius: 8px;
}
@keyframes placeholderShimmer {
0% { background-position: -468px 0 }
100% { background-position: 468px 0 }
}
.placeholder-item {
height: 250px;
width: 100%;
margin-bottom: 10px;
}
/* Movie details modal styles */
.details-modal {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(0, 0, 0, 0.8);
z-index: 1100;
display: flex;
justify-content: center;
align-items: center;
overflow-y: auto;
padding: 20px;
}
.details-content {
background-color: white;
width: 90%;
max-width: 900px;
border-radius: 12px;
overflow: hidden;
max-height: 90vh;
display: flex;
flex-direction: column;
}
.dark-mode .details-content {
background-color: #333;
color: white;
}
.night-light .details-content {
background-color: #f5deb3;
color: black;
}
.details-backdrop {
position: relative;
height: 300px;
background-size: cover;
background-position: center;
}
.backdrop-overlay {
position: absolute;
bottom: 0;
left: 0;
right: 0;
height: 60%;
background: linear-gradient(to top, rgba(0,0,0,0.9), transparent);
padding: 20px;
display: flex;
flex-direction: column;
justify-content: flex-end;
}
.details-body {
padding: 20px;
overflow-y: auto;
}
.details-close {
position: absolute;
top: 15px;
right: 15px;
width: 36px;
height: 36px;
background-color: rgba(0,0,0,0.6);
color: white;
border: none;
border-radius: 50%;
font-size: 20px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.genre-tag {
display: inline-block;
padding: 4px 10px;
margin: 0 5px 5px 0;
background-color: rgba(255,255,255,0.1);
border-radius: 20px;
font-size: 0.8rem;
}
.dark-mode .genre-tag {
background-color: rgba(255,255,255,0.2);
}
.night-light .genre-tag {
background-color: rgba(0,0,0,0.1);
}
.action-buttons {
display: flex;
gap: 10px;
margin-top: 20px;
margin-bottom: 20px;
}
.action-button {
flex: 1;
padding: 10px;
border: none;
border-radius: 5px;
font-weight: bold;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
}
.watch-button {
background-color: #32cd32;
color: white;
}
.trailer-button {
background-color: #ff6347;
color: white;
}
.cast-section {
margin-top: 20px;
}
.cast-list {
display: flex;
gap: 15px;
overflow-x: auto;
padding-bottom: 10px;
}
.cast-item {
flex: 0 0 auto;
width: 100px;
text-align: center;
}
.cast-image {
width: 80px;
height: 80px;
border-radius: 50%;
object-fit: cover;
margin-bottom: 8px;
}
/* Picture-in-picture trailer styles */
.pip-container {
position: fixed;
bottom: 20px;
right: 20px;
width: 300px;
background-color: black;
border-radius: 8px;
overflow: hidden;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
z-index: 1200;
}
.pip-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px 12px;
background-color: rgba(0, 0, 0, 0.7);
}
.pip-title {
color: white;
font-size: 14px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-right: 10px;
}
.pip-close {
background: none;
border: none;
color: white;
font-size: 20px;
cursor: pointer;
padding: 0;
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.pip-video {
width: 100%;
height: 169px; /* 16:9 aspect ratio for 300px width */
}
</style>
</head>
<body class="text-black">
<div class="container mx-auto px-4">
<h1 class="text-6xl font-bold my-8 text-left">
<a href="/" style="cursor: pointer; text-decoration: none; color: inherit;">
Watermelon Movies 🍉
</a>
</h1>
<h2 class="text-left">🤘.The Jews took hollywood...we're stealing it back</h2>
<div class="melon"></div>
<div class="relative text-gray-600">
<form id="searchForm" action="#">
<input type="search" name="search" placeholder="Search" class="bg-white h-10 px-5 pr-10 rounded-full text-sm focus:outline-none">
<button type="submit" class="absolute right-0 top-0 mt-3 mr-4">
<svg class="h-4 w-4 fill-current" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" id="Capa_1" x="0px" y="0px" viewBox="0 0 56.966 56.966" style="enable-background:new 0 0 56.966 56.966;" xml:space="preserve" width="512px" height="512px">
<path d="M55.146,51.887L41.588,37.786c3.486-4.144,5.396-9.358,5.396-14.786c0-12.682-10.318-23-23-23s-23,10.318-23,23 s10.318,23,23,23c4.761,0,9.298-1.436,13.177-4.162l13.661,14.208c0.571,0.593,1.339,0.92,2.162,0.92 c0.779,0,1.518-0.297,2.079-0.837C56.255,54.982,56.293,53.08,55.146,51.887z M23.984,6c9.374,0,17,7.626,17,17s-7.626,17-17,17 s-17-7.626-17-17S14.61,6,23.984,6z"/>
</svg>
</button>
</form>
</div>
<br>
<div id="searchResults" class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4"></div>
<div id="movies" class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4"></div>
</div>
<!-- Video Modal -->
<div id="videoModal" class="video-modal">
<div class="video-modal-content">
<span class="close">&times;</span>
<div style="border-radius: 10px; width: 100%; overflow: hidden;">
<div id="iframeContainer">
<iframe id="videoIframe" width="100%" height="300px" frameborder="0" allowfullscreen></iframe>
</div>
</div>
<!-- Season and Episode selectors (hidden by default) -->
<div id="seasonContainer" class="season-selector" style="display: none;"></div>
<div id="episodeContainer" class="episode-selector" style="display: none;"></div>
<div class="flex justify-end items-center mt-4 space-x-4">
<label for="night-light-toggle" class="flex items-center cursor-pointer mt-3">
<div class="relative">
<input type="checkbox" id="night-light-toggle" class="sr-only">
<div class="block bg-yellow-100 w-10 h-6 rounded-full"></div>
<div class="dot-night absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition transform"></div>
</div>
<div class="ml-2 mr-2 text-gray-700 toggle-label">Night Light</div>
</label>
<label for="dark-mode-toggle" class="flex items-center cursor-pointer ml-4 mt-3">
<div class="relative">
<input type="checkbox" id="dark-mode-toggle" class="sr-only">
<div class="bg-gray-400 w-10 h-6 rounded-full"></div>
<div class="dot absolute left-1 top-1 bg-white w-4 h-4 rounded-full transition transform"></div>
</div>
<div class="mr-2 text-gray-700 toggle-label">Dark Mode</div>
</label>
<button id="altSourceBtn" class="alt-source-btn">Try Alternate Source</button>
<button class="share-btn" onclick="navigator.clipboard.writeText(window.location.href)">
Pass the<span class="popcorn">🍿</span>
</button>
</div>
</div>
</div>
<!-- Category Sections - Will be dynamically populated -->
<div id="categoryContainer">
<!-- Categories will be added here by JavaScript -->
</div>
<div id="randomMovies" class="grid grid-cols-1 md:grid-cols-3 lg:grid-cols-5 gap-4 my-4">
<!-- Random movies will load here -->
</div>
<!-- Coming Soon Section -->
<div id="comingSoon" class="my-8">
<h2 class="text-2xl font-bold mb-4 text-left pl-4">Coming Soon</h2>
<div class="slider-container flex overflow-x-auto pl-4 pr-2 py-2 space-x-4">
<!-- Slider items will be added here by JavaScript -->
</div>
</div>
<!-- Progress indicator -->
<div id="progressIndicator"></div>
<script>
const apiKey = '8bb85bb00bf81f0ad511a7609d736c7f'; // TMDB API Key
const youtubeApiKey = 'AIzaSyAG7vJ284HAxvZ5caDhPISIZTPKXZsyKi8'; // YouTube API Key
// Current state variables for season/episode selection
let currentMediaType = null;
let currentTmdbId = null;
let currentImdbId = null;
let currentSeason = 1;
let currentEpisode = 1;
let currentSource = 'vsembed.ru'; // Default source
// Genre IDs mapping for creating meaningful categories
const genreMap = {
action: 28,
adventure: 12,
animation: 16,
comedy: 35,
crime: 80,
documentary: 99,
drama: 18,
family: 10751,
fantasy: 14,
history: 36,
horror: 27,
music: 10402,
mystery: 9648,
romance: 10749,
scienceFiction: 878,
thriller: 53,
war: 10752,
western: 37
};
// Cache for API responses
const apiCache = {
data: {},
get: function(key) {
return this.data[key];
},
set: function(key, value) {
this.data[key] = value;
}
};
// Set up dark mode and night light toggles
const toggleSwitch = document.getElementById('dark-mode-toggle');
const nightLightSwitch = document.getElementById('night-light-toggle');
const bodyElement = document.body;
const dotElement = document.querySelector('.dot');
const dotNightElement = document.querySelector('.dot-night');
toggleSwitch.addEventListener('change', () => {
if (toggleSwitch.checked) {
bodyElement.classList.add('dark-mode');
dotElement.classList.add('translate-x-full');
dotElement.classList.add('bg-gray-800');
// Ensure night light is disabled when dark mode is enabled
nightLightSwitch.checked = false;
bodyElement.classList.remove('night-light');
dotNightElement.classList.remove('translate-x-full');
dotNightElement.classList.remove('bg-yellow-500');
} else {
bodyElement.classList.remove('dark-mode');
dotElement.classList.remove('translate-x-full');
dotElement.classList.remove('bg-gray-800');
}
});
nightLightSwitch.addEventListener('change', () => {
if (nightLightSwitch.checked) {
bodyElement.classList.add('night-light');
dotNightElement.classList.add('translate-x-full');
dotNightElement.classList.add('bg-yellow-500');
// Ensure dark mode is disabled when night light is enabled
toggleSwitch.checked = false;
bodyElement.classList.remove('dark-mode');
dotElement.classList.remove('translate-x-full');
dotElement.classList.remove('bg-gray-800');
} else {
bodyElement.classList.remove('night-light');
dotNightElement.classList.remove('translate-x-full');
dotNightElement.classList.remove('bg-yellow-500');
}
});
document.getElementById('searchForm').addEventListener('submit', function(e) {
e.preventDefault();
const searchQuery = document.querySelector('[name="search"]').value.trim();
if (searchQuery) {
searchMedia(searchQuery);
}
});
function searchMedia(query) {
showProgress('Searching...');
const url = `https://api.themoviedb.org/3/search/multi?api_key=${apiKey}&query=${encodeURIComponent(query)}`;
fetch(url)
.then(response => response.json())
.then(data => {
displaySearchResults(data.results, 'searchResults');
showProgress('Search complete');
})
.catch(error => {
console.error('Search failed:', error);
showProgress('Search failed');
});
}
function displaySearchResults(results, containerId) {
const container = document.getElementById(containerId);
container.innerHTML = ''; // Clear previous results
if (results.length === 0) {
container.innerHTML = '<div class="col-span-full text-center py-4">No results found</div>';
return;
}
results.forEach(result => {
if ((result.media_type === 'movie' || result.media_type === 'tv') && result.poster_path) {
const title = result.title || result.name;
const id = result.id;
const posterPath = `https://image.tmdb.org/t/p/w500${result.poster_path}`;
const overview = result.overview || 'No description available.';
const mediaType = result.media_type;
const movieTile = document.createElement('div');
movieTile.className = 'movie-tile';
movieTile.innerHTML = `
<div class="relative">
<img src="${posterPath}" alt="Poster for ${title}" style="width:100%; height:auto;" loading="lazy">
<div class="media-label">${mediaType.toUpperCase()}</div>
${result.vote_average ? `<div class="rating-badge">${result.vote_average.toFixed(1)} ⭐</div>` : ''}
</div>
<h3>${title}</h3>
<p>${overview.substring(0, 100)}${overview.length > 100 ? '...' : ''}</p>
<div class="flex justify-between mt-3">
<button class="watch-btn bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded text-sm" onclick="openModal('${mediaType}', '${id}')">Watch</button>
<button class="info-btn bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-sm" onclick="openMovieDetails('${mediaType}', ${id})">Details</button>
</div>
`;
container.appendChild(movieTile);
}
});
}
function fetchTrailer(title, movieId = null, mediaType = 'movie') {
showProgress('Finding trailer...');
// If we have a movieId, get trailer directly from TMDB
if (movieId) {
const detailsUrl = `https://api.themoviedb.org/3/${mediaType}/${movieId}/videos?api_key=${apiKey}`;
fetch(detailsUrl)
.then(response => response.json())
.then(data => {
const trailer = data.results.find(video =>
(video.type === 'Trailer' || video.type === 'Teaser') &&
video.site === 'YouTube'
);
if (trailer) {
openTrailerInPictureInPicture(trailer.key, title);
showProgress('Trailer loaded');
} else {
// Fall back to YouTube search if no trailer in TMDB
searchYouTubeForTrailer(title);
}
})
.catch(error => {
console.error('Error fetching TMDB trailer:', error);
// Fall back to YouTube search
searchYouTubeForTrailer(title);
});
} else {
searchYouTubeForTrailer(title);
}
}
function searchYouTubeForTrailer(title) {
const youtubeSearchUrl = `https://www.googleapis.com/youtube/v3/search?part=snippet&maxResults=1&q=${encodeURIComponent(title + ' official trailer')}&key=${youtubeApiKey}`;
fetch(youtubeSearchUrl)
.then(response => response.json())
.then(data => {
if (data.items && data.items.length > 0) {
const videoId = data.items[0].id.videoId;
openTrailerInPictureInPicture(videoId, title);
showProgress('Trailer loaded');
} else {
alert('Trailer not found.');
showProgress('Trailer not found');
}
})
.catch(error => {
console.error('Error fetching trailer:', error);
showProgress('Error loading trailer');
});
}
function openTrailerInPictureInPicture(videoId, title) {
// Remove any existing pip container
const existingPip = document.querySelector('.pip-container');
if (existingPip) {
existingPip.remove();
}
// Create the PiP container
const pipContainer = document.createElement('div');
pipContainer.className = 'pip-container';
pipContainer.innerHTML = `
<div class="pip-header">
<div class="pip-title">Trailer: ${title}</div>
<button class="pip-close">&times;</button>
</div>
<iframe class="pip-video" src="https://www.youtube.com/embed/${videoId}?autoplay=1" frameborder="0" allowfullscreen></iframe>
`;
// Add to the DOM
document.body.appendChild(pipContainer);
// Add event listener to close button
const closeButton = pipContainer.querySelector('.pip-close');
closeButton.addEventListener('click', () => {
pipContainer.remove();
});
// Make the PiP container draggable
makeDraggable(pipContainer);
}
function makeDraggable(element) {
let pos1 = 0, pos2 = 0, pos3 = 0, pos4 = 0;
const header = element.querySelector('.pip-header');
if (header) {
header.onmousedown = dragMouseDown;
} else {
element.onmousedown = dragMouseDown;
}
function dragMouseDown(e) {
e = e || window.event;
e.preventDefault();
// Get the mouse cursor position at startup
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
// Call function when mouse moves
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
// Calculate the new cursor position
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
// Set the element's new position
element.style.top = (element.offsetTop - pos2) + "px";
element.style.left = (element.offsetLeft - pos1) + "px";
element.style.right = "auto"; // Clear right position when dragging
element.style.bottom = "auto"; // Clear bottom position when dragging
}
function closeDragElement() {
// Stop moving when mouse button is released
document.onmouseup = null;
document.onmousemove = null;
}
}
function openTrailerModal(videoId) {
const videoUrl = `https://www.youtube.com/embed/${videoId}`;
const modal = document.getElementById("videoModal");
const videoIframe = document.getElementById("videoIframe");
// Hide season/episode selectors for trailers
document.getElementById('seasonContainer').style.display = 'none';
document.getElementById('episodeContainer').style.display = 'none';
modal.style.display = "block";
videoIframe.src = videoUrl;
}
// Simple version of updateMetaTags
function updateMetaTags(title, description, posterPath) {
document.querySelector('meta[property="og:title"]').content = title;
document.querySelector('meta[property="og:description"]').content = description;
document.querySelector('meta[property="og:image"]').content = posterPath ? `https://image.tmdb.org/t/p/w500${posterPath}` : '';
document.title = title;
}
// Detailed movie info modal
async function openMovieDetails(mediaType, id) {
try {
showProgress('Loading details...');
// Remove any existing details modal
const existingModal = document.getElementById('movieDetailsModal');
if (existingModal) {
existingModal.remove();
}
// Fetch complete movie details with append_to_response to minimize API calls
const url = `https://api.themoviedb.org/3/${mediaType}/${id}?api_key=${apiKey}&append_to_response=credits,videos,release_dates`;
const response = await fetch(url);
const movie = await response.json();
// Create the modal
const detailsModal = document.createElement('div');
detailsModal.className = 'details-modal';
detailsModal.id = 'movieDetailsModal';
// Find a trailer if available
const trailer = movie.videos?.results.find(video =>
(video.type === 'Trailer' || video.type === 'Teaser') &&
video.site === 'YouTube'
);
// Get certification (rating)
let certification = '';
if (mediaType === 'movie' && movie.release_dates?.results) {
const usReleases = movie.release_dates.results.find(country => country.iso_3166_1 === 'US');
if (usReleases && usReleases.release_dates.length > 0) {
certification = usReleases.release_dates.find(r => r.certification)?.certification || '';
}
}
// Prepare content
const title = movie.title || movie.name;
const backdropPath = movie.backdrop_path ?
`https://image.tmdb.org/t/p/w1280${movie.backdrop_path}` :
(movie.poster_path ? `https://image.tmdb.org/t/p/w780${movie.poster_path}` : '');
detailsModal.innerHTML = `
<div class="details-content">
<div class="details-backdrop" style="background-image: url('${backdropPath}')">
<button class="details-close">&times;</button>
<div class="backdrop-overlay">
<h1 class="text-white text-3xl font-bold">${title}</h1>
<div class="flex flex-wrap mt-2">
${movie.release_date ? `<span class="text-white mr-4">${new Date(movie.release_date).getFullYear()}</span>` : ''}
${movie.runtime ? `<span class="text-white mr-4">${Math.floor(movie.runtime / 60)}h ${movie.runtime % 60}m</span>` : ''}
${certification ? `<span class="text-white mr-4">${certification}</span>` : ''}
${movie.vote_average ? `<span class="text-white mr-4">${movie.vote_average.toFixed(1)} ⭐</span>` : ''}
</div>
</div>
</div>
<div class="details-body">
<div class="flex flex-wrap mb-4">
${movie.genres.map(genre => `<span class="genre-tag">${genre.name}</span>`).join('')}
</div>
<p class="mb-6">${movie.overview}</p>
<div class="action-buttons">
<button class="action-button watch-button" onclick="openModal('${mediaType}', ${id})">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM9.555 7.168A1 1 0 008 8v4a1 1 0 001.555.832l3-2a1 1 0 000-1.664l-3-2z" clip-rule="evenodd" />
</svg>
Watch Now
</button>
${trailer ? `
<button class="action-button trailer-button" onclick="openTrailerInPictureInPicture('${trailer.key}', '${title.replace(/'/g, "\\'")}')">
<svg xmlns="http://www.w3.org/2000/svg" class="h-5 w-5 mr-2" viewBox="0 0 20 20" fill="currentColor">
<path d="M2 6a2 2 0 012-2h6a2 2 0 012 2v8a2 2 0 01-2 2H4a2 2 0 01-2-2V6zM14.553 7.106A1 1 0 0014 8v4a1 1 0 00.553.894l2 1A1 1 0 0018 13V7a1 1 0 00-1.447-.894l-2 1z" />
</svg>
Watch Trailer
</button>
` : ''}
</div>
${movie.credits && movie.credits.cast && movie.credits.cast.length > 0 ? `
<div class="cast-section">
<h2 class="text-xl font-bold mb-4">Cast</h2>
<div class="cast-list">
${movie.credits.cast.slice(0, 8).map(person => `
<div class="cast-item">
<img
src="${person.profile_path ? `https://image.tmdb.org/t/p/w185${person.profile_path}` : 'https://via.placeholder.com/80x80'}"
alt="${person.name}"
class="cast-image"
onerror="this.src='https://via.placeholder.com/80x80?text=${encodeURIComponent(person.name[0])}'"
>
<div class="text-sm font-medium">${person.name}</div>
<div class="text-xs text-gray-500">${person.character}</div>
</div>
`).join('')}
</div>
</div>
` : ''}
</div>
</div>
`;
// Add to the DOM
document.body.appendChild(detailsModal);
// Add event listener to close button
const closeBtn = detailsModal.querySelector('.details-close');
closeBtn.addEventListener('click', () => {
detailsModal.remove();
});
// Close on click outside content
detailsModal.addEventListener('click', (e) => {
if (e.target === detailsModal) {
detailsModal.remove();
}
});
showProgress('Details loaded');
} catch (error) {
console.error('Error loading movie details:', error);
showProgress('Error loading details');
}
}
// Improved openModal function with season/episode selector
async function openModal(mediaType, tmdbId, season = 1, episode = 1) {
try {
showProgress('Loading content...');
// Store current state
currentMediaType = mediaType;
currentTmdbId = tmdbId;
currentSeason = parseInt(season);
currentEpisode = parseInt(episode);
// First get the IMDB ID from TMDB
const detailsUrl = `https://api.themoviedb.org/3/${mediaType}/${tmdbId}/external_ids?api_key=${apiKey}`;
const response = await fetch(detailsUrl);
const data = await response.json();
currentImdbId = data.imdb_id;
if (!currentImdbId) {
console.error('No IMDB ID found');
showProgress('Error: No content ID found');
return;
}
// Show modal and load video
const modal = document.getElementById("videoModal");
modal.style.display = "block";
// Load the appropriate source
loadVideoSource();
// If it's a TV show, fetch and display season/episode selectors
if (mediaType === 'tv') {
// Show season selector
await fetchAndDisplaySeasons(tmdbId);
// Show episode selector for selected season
await fetchAndDisplayEpisodes(tmdbId, currentSeason);
} else {
// Hide season/episode selectors for movies
document.getElementById('seasonContainer').style.display = 'none';
document.getElementById('episodeContainer').style.display = 'none';
}
// Fetch title and metadata
const titleResponse = await fetch(`https://api.themoviedb.org/3/${mediaType}/${tmdbId}?api_key=${apiKey}`);
const titleData = await titleResponse.json();
const title = titleData.title || titleData.name;
updateMetaTags(title, titleData.overview, titleData.poster_path);
// Update URL
let newUrl = `${window.location.pathname}?type=${mediaType}&id=${tmdbId}`;
if (mediaType === 'tv') {
newUrl += `&season=${currentSeason}&episode=${currentEpisode}`;
}
window.history.pushState({ path: newUrl }, '', newUrl);
showProgress('Content loaded');
} catch (error) {
console.error('Error in openModal:', error);
showProgress('Error loading content');
}
}
// Function to load video from current source
function loadVideoSource() {
let videoUrl;
if (currentSource === 'vsembed.ru') {
videoUrl = 'https://vsembed.ru/embed/';
if (currentMediaType === 'movie') {
videoUrl += `movie/${currentImdbId}`;
} else if (currentMediaType === 'tv') {
videoUrl += `tv/${currentImdbId}/${currentSeason}/${currentEpisode}`;
}
} else {
videoUrl = 'https://vsembed.su/embed/';
if (currentMediaType === 'movie') {
videoUrl += `movie/${currentTmdbId}`;
} else if (currentMediaType === 'tv') {
videoUrl += `tv/?season=${currentSeason}&episode=${currentEpisode}&${currentTmdbId}`;
}
}
document.getElementById("videoIframe").src = videoUrl;
}
// Fetch and display available seasons
async function fetchAndDisplaySeasons(tmdbId) {
try {
const seasonsUrl = `https://api.themoviedb.org/3/tv/${tmdbId}?api_key=${apiKey}`;
const response = await fetch(seasonsUrl);
const data = await response.json();
const seasonContainer = document.getElementById('seasonContainer');
seasonContainer.innerHTML = '<div class="font-bold mb-2">Seasons:</div>';
seasonContainer.style.display = 'block';
// Create season buttons
if (data.seasons && data.seasons.length > 0) {
data.seasons.forEach(season => {
if (season.season_number > 0) { // Skip season 0 (specials)
const seasonBtn = document.createElement('button');
seasonBtn.className = `season-btn ${season.season_number === currentSeason ? 'active' : ''}`;
seasonBtn.textContent = `${season.season_number}`;
seasonBtn.onclick = () => {
// Update active button styling
document.querySelectorAll('.season-btn').forEach(btn => btn.classList.remove('active'));
seasonBtn.classList.add('active');
// Update current season and fetch episodes
currentSeason = season.season_number;
fetchAndDisplayEpisodes(tmdbId, currentSeason);
// Reset to episode 1 when changing seasons
currentEpisode = 1;
// Reload video with new season
loadVideoSource();
// Update URL
let newUrl = `${window.location.pathname}?type=tv&id=${tmdbId}&season=${currentSeason}&episode=${currentEpisode}`;
window.history.pushState({ path: newUrl }, '', newUrl);
};
seasonContainer.appendChild(seasonBtn);
}
});
}
} catch (error) {
console.error('Error fetching seasons:', error);
showProgress('Error loading seasons');
}
}
// Fetch and display episodes for selected season
async function fetchAndDisplayEpisodes(tmdbId, seasonNumber) {
try {
const episodesUrl = `https://api.themoviedb.org/3/tv/${tmdbId}/season/${seasonNumber}?api_key=${apiKey}`;
const response = await fetch(episodesUrl);
const data = await response.json();
const episodeContainer = document.getElementById('episodeContainer');
episodeContainer.innerHTML = '<div class="font-bold mb-2">Episodes:</div>';
episodeContainer.style.display = 'block';
// Create episode buttons
if (data.episodes && data.episodes.length > 0) {
data.episodes.forEach(episode => {
const episodeBtn = document.createElement('button');
episodeBtn.className = `episode-btn ${episode.episode_number === currentEpisode ? 'active' : ''}`;
episodeBtn.textContent = `${episode.episode_number}`;
episodeBtn.onclick = () => {
// Update active button styling
document.querySelectorAll('.episode-btn').forEach(btn => btn.classList.remove('active'));
episodeBtn.classList.add('active');
// Update current episode
currentEpisode = episode.episode_number;
// Reload video with new episode
loadVideoSource();
// Update URL
let newUrl = `${window.location.pathname}?type=tv&id=${tmdbId}&season=${currentSeason}&episode=${currentEpisode}`;
window.history.pushState({ path: newUrl }, '', newUrl);
};
episodeContainer.appendChild(episodeBtn);
});
}
} catch (error) {
console.error('Error fetching episodes:', error);
showProgress('Error loading episodes');
}
}
// Set up alternate source button
document.getElementById('altSourceBtn').addEventListener('click', function() {
// Toggle between sources
currentSource = currentSource === 'vsembed.ru' ? 'vsembed.su' : 'vsembed.ru';
this.textContent = `Try ${currentSource === 'vsembed.ru' ? 'vsembed.su' : 'vsembed.ru'}`;
// Reload with new source
loadVideoSource();
showProgress(`Switched to ${currentSource}`);
});
const modal = document.getElementById("videoModal");
const span = document.getElementsByClassName("close")[0];
const videoIframe = document.getElementById("videoIframe");
span.onclick = function() {
modal.style.display = "none";
videoIframe.src = "";
}
window.onclick = function(event) {
if (event.target == modal) {
modal.style.display = "none";
videoIframe.src = "";
}
}
function showProgress(message) {
const progress = document.getElementById('progressIndicator');
progress.textContent = message;
progress.style.display = 'block';
// Auto-hide after 2 seconds
setTimeout(() => {
progress.style.display = 'none';
}, 2000);
}
// Function to create a category section
function createCategorySection(categoryTitle) {
const categorySection = document.createElement('div');
categorySection.className = 'my-8';
categorySection.innerHTML = `
<h2 class="text-2xl font-bold mb-4 text-left pl-4">${categoryTitle}</h2>
<div class="category-slider flex overflow-x-auto pl-4 pr-2 py-2 space-x-4">
<div class="placeholder-item placeholder" style="width: 180px; height: 270px;"></div>
<div class="placeholder-item placeholder" style="width: 180px; height: 270px;"></div>
<div class="placeholder-item placeholder" style="width: 180px; height: 270px;"></div>
<div class="placeholder-item placeholder" style="width: 180px; height: 270px;"></div>
<div class="placeholder-item placeholder" style="width: 180px; height: 270px;"></div>
</div>
`;
// Insert into the category container
const categoryContainer = document.getElementById('categoryContainer');
categoryContainer.appendChild(categorySection);
return categorySection;
}
// Function to display movies in a category
function displayCategoryMovies(movies, categoryTitle) {
// Find existing category section or create a new one
let categorySection = Array.from(document.querySelectorAll('h2'))
.find(h => h.textContent === categoryTitle)?.parentElement;
if (!categorySection) {
categorySection = createCategorySection(categoryTitle);
}
// Get the slider container
const container = categorySection.querySelector('.category-slider');
container.innerHTML = '';
// If no movies, show a message
if (!movies || movies.length === 0) {
container.innerHTML = '<div class="w-full text-center py-4">No movies found for this category</div>';
return;
}
// Take only the first 10 movies for performance
movies.slice(0, 10).forEach(movie => {
if (movie.poster_path) {
const movieCard = document.createElement('div');
movieCard.className = 'category-item';
// Add vote average as a rating badge if available
const rating = movie.vote_average ?
`<div class="rating-badge">${movie.vote_average.toFixed(1)} ⭐</div>` : '';
// Enhanced movie card with info button
movieCard.innerHTML = `
<div class="relative">
<img src="https://image.tmdb.org/t/p/w342${movie.poster_path}" alt="${movie.title}" loading="lazy">
${rating}
</div>
<h3 class="text-sm font-medium mt-2">${movie.title}</h3>
<div class="flex justify-between mt-2">
<button class="text-xs bg-green-500 hover:bg-green-600 text-white px-2 py-1 rounded" onclick="openModal('movie', ${movie.id})">
Watch
</button>
<button class="text-xs bg-blue-500 hover:bg-blue-600 text-white px-2 py-1 rounded" onclick="openMovieDetails('movie', ${movie.id})">
Details
</button>
</div>
`;
container.appendChild(movieCard);
}
});
}
// Function to get movies by genre
function fetchMoviesByGenre(genreId, categoryTitle) {
const url = `https://api.themoviedb.org/3/discover/movie?api_key=${apiKey}&with_genres=${genreId}&sort_by=popularity.desc`;
fetch(url)
.then(response => response.json())
.then(data => {
displayCategoryMovies(data.results, categoryTitle);
})
.catch(error => {
console.error(`Error fetching genre movies:`, error);
showProgress(`Failed to load ${categoryTitle}`);
});
}
// Function to get high rated movies
function fetchHighRatedMovies(categoryTitle) {
const url = `https://api.themoviedb.org/3/movie/top_rated?api_key=${apiKey}&language=en-US&page=1`;
fetch(url)
.then(response => response.json())
.then(data => {
displayCategoryMovies(data.results, categoryTitle);
})
.catch(error => {
console.error(`Error fetching high rated movies:`, error);
showProgress(`Failed to load ${categoryTitle}`);
});
}
// Function to get recent releases
function fetchRecentReleases(categoryTitle) {
const url = `https://api.themoviedb.org/3/movie/now_playing?api_key=${apiKey}&language=en-US&page=1`;
fetch(url)
.then(response => response.json())
.then(data => {
displayCategoryMovies(data.results, categoryTitle);
})
.catch(error => {
console.error(`Error fetching recent releases:`, error);
showProgress(`Failed to load ${categoryTitle}`);
});
}
// Additional function to get action movies with high popularity
function fetchActionExplosionMovies(categoryTitle) {
const url = `https://api.themoviedb.org/3/discover/movie?api_key=${apiKey}&with_genres=${genreMap.action}&sort_by=popularity.desc&vote_count.gte=500`;
fetch(url)
.then(response => response.json())
.then(data => {
displayCategoryMovies(data.results, categoryTitle);
})
.catch(error => {
console.error(`Error fetching explosion movies:`, error);
showProgress(`Failed to load ${categoryTitle}`);
});
}
// Function to setup all dynamic categories
function setupCategories() {
// Create category sections with placeholders first
createCategorySection('💥 Action-Packed Thrills');
createCategorySection('🏆 Critics\' Favorites');
createCategorySection('🆕 Fresh Off The Press');
createCategorySection('🍿 Crowd Pleasers');
createCategorySection('👻 Spooky Stuff');
createCategorySection('🦖 So Bad They\'re Extinct');
createCategorySection('🧠 Movies That Make No Sense');
createCategorySection('😴 Perfect For Napping');
createCategorySection('🔪 Don\'t Watch Alone');
createCategorySection('🌈 Movies Your Parents Hated');
createCategorySection('🚗 Explosions Go Boom');
createCategorySection('👽 Weird Stuff From Space');
// Fetch movies for each category
fetchMoviesByGenre(genreMap.action, '💥 Action-Packed Thrills');
fetchHighRatedMovies('🏆 Critics\' Favorites');
fetchRecentReleases('🆕 Fresh Off The Press');
fetchMoviesByGenre(genreMap.comedy, '🍿 Crowd Pleasers');
fetchMoviesByGenre(genreMap.horror, '👻 Spooky Stuff');
fetchMoviesByGenre(genreMap.western, '🦖 So Bad They\'re Extinct');
fetchMoviesByGenre(genreMap.scienceFiction, '🧠 Movies That Make No Sense');
fetchMoviesByGenre(genreMap.documentary, '😴 Perfect For Napping');
fetchMoviesByGenre(genreMap.thriller, '🔪 Don\'t Watch Alone');
fetchMoviesByGenre(genreMap.family, '🌈 Movies Your Parents Hated');
fetchActionExplosionMovies('🚗 Explosions Go Boom');
fetchMoviesByGenre(genreMap.scienceFiction, '👽 Weird Stuff From Space');
}
document.addEventListener('DOMContentLoaded', function() {
// Setup all categories
setupCategories();
// Fetch movies for the main grid and coming soon section
fetchRandomMovies();
fetchMovies();
// Set up keyboard shortcuts
document.addEventListener('keydown', function(e) {
if (e.key === '/' && document.activeElement.tagName !== 'INPUT') {
e.preventDefault();
document.querySelector('[name="search"]').focus();
} else if (e.key === 'Escape') {
// Close any open modals
const videoModal = document.getElementById("videoModal");
if (videoModal.style.display === 'block') {
videoModal.style.display = "none";
videoIframe.src = "";
}
// Close any details modal
const detailsModal = document.getElementById('movieDetailsModal');
if (detailsModal) {
detailsModal.remove();
}
// Close any pip trailers
const pipContainer = document.querySelector('.pip-container');
if (pipContainer) {
pipContainer.remove();
}
}
});
// Process URL parameters if present
const urlParams = new URLSearchParams(window.location.search);
const mediaType = urlParams.get('type');
const id = urlParams.get('id');
const season = urlParams.get('season') || 1;
const episode = urlParams.get('episode') || 1;
if (mediaType && id) {
openModal(mediaType, id, parseInt(season), parseInt(episode));
}
});
function fetchRandomMovies() {
fetch(`https://api.themoviedb.org/3/movie/popular?api_key=${apiKey}&language=en-US&page=1`)
.then(response => response.json())
.then(data => {
displayMovies(data.results, 'randomMovies');
})
.catch(error => console.error('Error fetching random movies:', error));
}
function fetchMovies() {
fetch(`https://api.themoviedb.org/3/movie/upcoming?api_key=${apiKey}&language=en`)
.then(response => response.json())
.then(data => displayComingSoon(data.results))
.catch(error => console.error('Fetching movies failed:', error));
}
function displayComingSoon(movies) {
const container = document.querySelector('.slider-container');
container.innerHTML = '';
movies.forEach(movie => {
if (movie.poster_path) {
const movieCard = document.createElement('div');
movieCard.className = 'slider-item';
movieCard.innerHTML = `
<div class="relative">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="${movie.title}" class="rounded-lg w-full h-auto" loading="lazy">
${movie.vote_average ? `<div class="rating-badge">${movie.vote_average.toFixed(1)} ⭐</div>` : ''}
</div>
<h3 class="text-lg font-semibold mt-2">${movie.title}</h3>
<div class="flex justify-between mt-2">
<button class="bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded text-sm" onclick="openModal('movie', '${movie.id}')">Watch</button>
<button class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-sm" onclick="openMovieDetails('movie', ${movie.id})">Details</button>
</div>
`;
container.appendChild(movieCard);
}
});
}
function displayMovies(movies, containerId) {
const container = document.getElementById(containerId);
container.innerHTML = ''; // Clear previous results
movies.forEach(movie => {
if (movie.poster_path) {
const movieTile = document.createElement('div');
movieTile.className = 'movie-tile';
movieTile.innerHTML = `
<div class="relative">
<img src="https://image.tmdb.org/t/p/w500${movie.poster_path}" alt="${movie.title}" class="rounded-lg w-full h-auto" loading="lazy">
${movie.vote_average ? `<div class="rating-badge">${movie.vote_average.toFixed(1)} ⭐</div>` : ''}
</div>
<h3>${movie.title}</h3>
<p>${movie.overview ? movie.overview.substring(0, 100) + (movie.overview.length > 100 ? '...' : '') : 'No description available.'}</p>
<div class="flex justify-between mt-3">
<button class="bg-green-500 hover:bg-green-600 text-white px-3 py-1 rounded text-sm" onclick="openModal('movie', '${movie.id}')">Watch</button>
<button class="bg-blue-500 hover:bg-blue-600 text-white px-3 py-1 rounded text-sm" onclick="openMovieDetails('movie', ${movie.id})">Details</button>
</div>
`;
container.appendChild(movieTile);
}
});
}
//Web App, Add to homescreen Service Worker
if ('serviceWorker' in navigator) {
window.addEventListener('load', function() {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
console.log('Service Worker registered with scope:', registration.scope);
}).catch(function(error) {
console.log('Service Worker registration failed:', error);
});
});
}
</script>
</body>
</html>