fixed url

This commit is contained in:
tejaspanchall
2025-06-23 18:39:40 +05:30
parent 730f203aa0
commit 2064170141
6 changed files with 164 additions and 115 deletions

View File

@@ -68,36 +68,16 @@ export default function WatchPage() {
console.log('[Watch] Raw episodeId from URL:', episodeId); console.log('[Watch] Raw episodeId from URL:', episodeId);
// Extract animeId from the episodeId parameter // Extract animeId from the episodeId parameter
// Handle different possible formats: // The API response contains episode.id in the format "anime-id?ep=episode-number"
// 1. anime-name?ep=episode-number (standard format) let extractedAnimeId = episodeId;
// 2. anime-name-episode-number (legacy format)
let extractedAnimeId; // If the ID contains a query parameter, extract just the anime ID
let episodeNumber; if (episodeId.includes('?')) {
extractedAnimeId = episodeId.split('?')[0];
if (episodeId.includes('?ep=')) {
// Format: anime-name?ep=episode-number
const [baseId, queryString] = episodeId.split('?');
extractedAnimeId = baseId;
episodeNumber = queryString.replace('ep=', '');
console.log(`[Watch] Format detected: standard (anime-name?ep=episode-number)`);
} else if (episodeId.includes('-')) {
// Format: anime-name-episode-number
const match = episodeId.match(/^(.*?)-(\d+)$/);
if (match) {
extractedAnimeId = match[1];
episodeNumber = match[2];
console.log(`[Watch] Format detected: legacy (anime-name-episode-number)`);
}
} }
if (extractedAnimeId) { setAnimeId(extractedAnimeId);
setAnimeId(extractedAnimeId); console.log('[Watch] Extracted anime ID:', extractedAnimeId);
console.log('[Watch] Extracted anime ID:', extractedAnimeId);
console.log('[Watch] Extracted episode number:', episodeNumber);
} else {
console.warn('[Watch] Could not extract anime ID from episode ID:', episodeId);
}
setCurrentEpisodeId(episodeId); setCurrentEpisodeId(episodeId);
} }
@@ -302,34 +282,14 @@ export default function WatchPage() {
setEpisodes(episodesData.episodes); setEpisodes(episodesData.episodes);
// Find current episode in episode list // Find current episode in episode list
// Handle both formats: anime-name?ep=episode-number or anime-name-episode-number
const findCurrentEpisode = () => { const findCurrentEpisode = () => {
// First, try to find the episode by direct ID match // Find the episode by direct ID match
const directMatch = episodesData.episodes.find(ep => ep.id === currentEpisodeId); const directMatch = episodesData.episodes.find(ep => ep.id === currentEpisodeId);
if (directMatch) { if (directMatch) {
console.log('[Watch] Found episode by direct ID match:', directMatch.number); console.log('[Watch] Found episode by direct ID match:', directMatch.number);
return directMatch; return directMatch;
} }
// As a fallback, try to match by episode number
// Extract episode number from the URL if it's in the format anime-id?ep=number
if (currentEpisodeId.includes('?ep=')) {
const [, queryString] = currentEpisodeId.split('?');
if (queryString) {
const episodeNumber = queryString.replace('ep=', '');
console.log('[Watch] Trying to find by episode number:', episodeNumber);
const numberMatch = episodesData.episodes.find(ep =>
ep.number && ep.number.toString() === episodeNumber.toString()
);
if (numberMatch) {
console.log('[Watch] Found episode by number:', numberMatch.number);
return numberMatch;
}
}
}
// If no match found, return first episode as fallback // If no match found, return first episode as fallback
console.warn('[Watch] Could not find matching episode, falling back to first episode'); console.warn('[Watch] Could not find matching episode, falling back to first episode');
return episodesData.episodes[0]; return episodesData.episodes[0];
@@ -407,7 +367,7 @@ export default function WatchPage() {
// from the API response (animeId?ep=episodeNumber) // from the API response (animeId?ep=episodeNumber)
// Update the URL using history API // Update the URL using history API
const newUrl = `/watch/${encodeURIComponent(newEpisodeId)}`; const newUrl = `/watch/${newEpisodeId}`;
window.history.pushState({ episodeId: newEpisodeId }, '', newUrl); window.history.pushState({ episodeId: newEpisodeId }, '', newUrl);
// Update state to trigger video reload // Update state to trigger video reload

View File

@@ -8,7 +8,7 @@ import { fetchAnimeEpisodes } from '@/lib/api';
export default function AnimeCard({ anime, isRecent }) { export default function AnimeCard({ anime, isRecent }) {
const [imageError, setImageError] = useState(false); const [imageError, setImageError] = useState(false);
const [firstEpisodeId, setFirstEpisodeId] = useState(null); const [firstEpisodeId, setFirstEpisodeId] = useState(null);
const [isHovered, setIsHovered] = useState(false); const [isLoading, setIsLoading] = useState(false);
const timerRef = useRef(null); const timerRef = useRef(null);
if (!anime) return null; if (!anime) return null;
@@ -18,40 +18,59 @@ export default function AnimeCard({ anime, isRecent }) {
setImageError(true); setImageError(true);
}; };
// Fetch first episode ID when component is hovered // Fetch first episode ID when component mounts for recent anime
useEffect(() => { useEffect(() => {
const fetchFirstEpisode = async () => { const fetchFirstEpisode = async () => {
if (anime?.id && isHovered && !firstEpisodeId) { // Only fetch for recent anime and if we don't already have the episode ID
if (isRecent && anime?.id && !firstEpisodeId && !isLoading) {
setIsLoading(true);
try { try {
console.log(`[AnimeCard] Fetching episodes for anime: ${anime.id}`);
const response = await fetchAnimeEpisodes(anime.id); const response = await fetchAnimeEpisodes(anime.id);
console.log(`[AnimeCard] Episodes response for ${anime.name}:`, response);
if (response.episodes && response.episodes.length > 0) { if (response.episodes && response.episodes.length > 0) {
// Get the first episode's episodeId // Check for the episode ID in the format expected by the watch page
setFirstEpisodeId(response.episodes[0].episodeId); const firstEp = response.episodes[0];
console.log(`[AnimeCard] First episode ID for ${anime.name}: ${response.episodes[0].episodeId}`); if (firstEp.id) {
setFirstEpisodeId(firstEp.id);
console.log(`[AnimeCard] First episode ID (id) for ${anime.name}: ${firstEp.id}`);
} else if (firstEp.episodeId) {
setFirstEpisodeId(firstEp.episodeId);
console.log(`[AnimeCard] First episode ID (episodeId) for ${anime.name}: ${firstEp.episodeId}`);
} else {
// Create a fallback ID if neither id nor episodeId are available
const fallbackId = `${anime.id}?ep=1`;
setFirstEpisodeId(fallbackId);
console.log(`[AnimeCard] Using fallback ID for ${anime.name}: ${fallbackId}`);
}
} else if (anime.id) {
// If no episodes found, create a fallback ID
const fallbackId = `${anime.id}?ep=1`;
setFirstEpisodeId(fallbackId);
console.log(`[AnimeCard] No episodes found for ${anime.name}, using fallback ID: ${fallbackId}`);
} }
} catch (error) { } catch (error) {
console.error(`[AnimeCard] Error fetching episodes for ${anime.id}:`, error); console.error(`[AnimeCard] Error fetching episodes for ${anime.id}:`, error);
// Even on error, try to use fallback
if (anime.id) {
const fallbackId = `${anime.id}?ep=1`;
setFirstEpisodeId(fallbackId);
console.log(`[AnimeCard] Error for ${anime.name}, using fallback ID: ${fallbackId}`);
}
} finally {
setIsLoading(false);
} }
} }
}; };
fetchFirstEpisode(); fetchFirstEpisode();
}, [anime?.id, isHovered, firstEpisodeId]);
// Clean up timer if component unmounts
const handleMouseEnter = () => { return () => {
// Clear any existing timers if (timerRef.current) clearTimeout(timerRef.current);
if (timerRef.current) clearTimeout(timerRef.current); };
// Set a small delay to prevent API calls for quick mouseovers }, [anime?.id, anime?.name, isRecent, firstEpisodeId, isLoading]);
timerRef.current = setTimeout(() => {
setIsHovered(true);
}, 300); // Delay to prevent unnecessary API calls
};
const handleMouseLeave = () => {
// Clear the timer if the user moves the mouse away quickly
if (timerRef.current) clearTimeout(timerRef.current);
setIsHovered(false);
};
// Get image URL with fallback // Get image URL with fallback
const imageSrc = imageError ? '/images/placeholder.png' : anime.poster; const imageSrc = imageError ? '/images/placeholder.png' : anime.poster;
@@ -60,21 +79,17 @@ export default function AnimeCard({ anime, isRecent }) {
const infoLink = `/anime/${anime.id}`; const infoLink = `/anime/${anime.id}`;
// Build the watch URL based on the first episode ID or fallback // Build the watch URL based on the first episode ID or fallback
const watchLink = isRecent ? ( const watchLink = isRecent && firstEpisodeId
firstEpisodeId ? `/watch/${firstEpisodeId}`
? `/watch/${firstEpisodeId}` : isRecent
: `/watch/${anime.id}?ep=${anime.episodes?.sub || anime.episodes?.dub || 1}` ? `/anime/${anime.id}` // Temporarily link to info page while loading
) : infoLink; : `/anime/${anime.id}`; // Non-recent anime always link to info
return ( return (
<div <div className="anime-card w-full flex flex-col">
className="anime-card w-full flex flex-col" {/* Image card linking to watch page for recent anime, or info page otherwise */}
onMouseEnter={handleMouseEnter}
onMouseLeave={handleMouseLeave}
>
{/* Image card linking to watch page */}
<Link <Link
href={watchLink} href={isRecent ? watchLink : infoLink}
className="block w-full rounded-lg overflow-hidden transition-transform duration-300 hover:scale-[1.02] group" className="block w-full rounded-lg overflow-hidden transition-transform duration-300 hover:scale-[1.02] group"
prefetch={false} prefetch={false}
> >

View File

@@ -30,14 +30,45 @@ export default function AnimeDetails({ anime }) {
if (anime?.info?.id) { if (anime?.info?.id) {
setIsLoadingEpisodes(true); setIsLoadingEpisodes(true);
try { try {
console.log(`[AnimeDetails] Fetching episodes for anime: ${anime.info.id}`);
const response = await fetchAnimeEpisodes(anime.info.id); const response = await fetchAnimeEpisodes(anime.info.id);
console.log('[AnimeDetails] Episodes response:', response);
if (response.episodes && response.episodes.length > 0) { if (response.episodes && response.episodes.length > 0) {
// Get the first episode's episodeId // Log the first episode to check its structure
setFirstEpisodeId(response.episodes[0].episodeId); console.log('[AnimeDetails] First episode:', response.episodes[0]);
console.log(`[AnimeDetails] First episode ID: ${response.episodes[0].episodeId}`);
// Get the first episode's id
const firstEp = response.episodes[0];
if (firstEp.id) {
setFirstEpisodeId(firstEp.id);
console.log(`[AnimeDetails] First episode ID found: ${firstEp.id}`);
} else if (firstEp.episodeId) {
// Fallback to episodeId if id is not available
setFirstEpisodeId(firstEp.episodeId);
console.log(`[AnimeDetails] Falling back to episodeId: ${firstEp.episodeId}`);
} else {
// If no episode ID is found in the API response, create a fallback ID
const fallbackId = `${anime.info.id}?ep=1`;
setFirstEpisodeId(fallbackId);
console.log(`[AnimeDetails] Using fallback ID: ${fallbackId}`);
}
} else if (anime.info.id) {
// If no episodes found but anime ID is available, use fallback
const fallbackId = `${anime.info.id}?ep=1`;
setFirstEpisodeId(fallbackId);
console.log(`[AnimeDetails] No episodes found, using fallback ID: ${fallbackId}`);
} else {
console.warn('[AnimeDetails] No episodes found and no anime ID available');
} }
} catch (error) { } catch (error) {
console.error('[AnimeDetails] Error fetching episodes:', error); console.error('[AnimeDetails] Error fetching episodes:', error);
// Even on error, try to use fallback
if (anime.info.id) {
const fallbackId = `${anime.info.id}?ep=1`;
setFirstEpisodeId(fallbackId);
console.log(`[AnimeDetails] Error occurred, using fallback ID: ${fallbackId}`);
}
} finally { } finally {
setIsLoadingEpisodes(false); setIsLoadingEpisodes(false);
} }
@@ -47,6 +78,11 @@ export default function AnimeDetails({ anime }) {
fetchFirstEpisode(); fetchFirstEpisode();
}, [anime?.info?.id]); }, [anime?.info?.id]);
// Add a useEffect to debug when and why firstEpisodeId changes
useEffect(() => {
console.log('[AnimeDetails] firstEpisodeId changed:', firstEpisodeId);
}, [firstEpisodeId]);
if (!anime?.info) { if (!anime?.info) {
return null; return null;
} }
@@ -55,10 +91,13 @@ export default function AnimeDetails({ anime }) {
const hasCharacters = info.characterVoiceActor?.length > 0 || info.charactersVoiceActors?.length > 0; const hasCharacters = info.characterVoiceActor?.length > 0 || info.charactersVoiceActors?.length > 0;
const hasVideos = info.promotionalVideos && info.promotionalVideos.length > 0; const hasVideos = info.promotionalVideos && info.promotionalVideos.length > 0;
// Build the watch URL based on the first episode ID or fallback // Build the watch URL based on the first episode ID
const watchUrl = firstEpisodeId const watchUrl = firstEpisodeId
? `/watch/${firstEpisodeId}` ? `/watch/${firstEpisodeId}`
: `/watch/${info.id}?ep=1`; // Fallback to old format if API fetch fails : ''; // Empty string if no episodes available - this shouldn't happen with our fallback
// Add debug log here
console.log('[AnimeDetails] Rendered with watchUrl:', watchUrl, 'firstEpisodeId:', firstEpisodeId);
// Video modal for promotional videos // Video modal for promotional videos
const VideoModal = ({ video, onClose }) => { const VideoModal = ({ video, onClose }) => {
@@ -236,8 +275,8 @@ export default function AnimeDetails({ anime }) {
</div> </div>
</div> </div>
{/* Watch Button - Full Width on Mobile */} {/* Watch Button - Mobile */}
{info.stats?.episodes && (info.stats.episodes.sub > 0 || info.stats.episodes.dub > 0) && ( {firstEpisodeId && (
<Link <Link
href={watchUrl} href={watchUrl}
className="bg-[#ffffff] text-[var(--background)] px-4 py-2.5 rounded-xl mt-3 hover:opacity-90 transition-opacity flex items-center justify-center font-medium text-sm w-full shadow-lg" className="bg-[#ffffff] text-[var(--background)] px-4 py-2.5 rounded-xl mt-3 hover:opacity-90 transition-opacity flex items-center justify-center font-medium text-sm w-full shadow-lg"
@@ -273,7 +312,7 @@ export default function AnimeDetails({ anime }) {
</div> </div>
{/* Watch Button - Desktop */} {/* Watch Button - Desktop */}
{info.stats?.episodes && (info.stats.episodes.sub > 0 || info.stats.episodes.dub > 0) && ( {firstEpisodeId && (
<Link <Link
href={watchUrl} href={watchUrl}
className="bg-[#ffffff] text-[var(--background)] px-6 py-3 rounded-xl mt-4 hover:opacity-90 transition-opacity flex items-center justify-center font-medium text-base w-full shadow-lg" className="bg-[#ffffff] text-[var(--background)] px-6 py-3 rounded-xl mt-4 hover:opacity-90 transition-opacity flex items-center justify-center font-medium text-base w-full shadow-lg"

View File

@@ -24,10 +24,7 @@ export default function EpisodeList({ episodes, currentEpisode, onEpisodeClick,
setActiveEpisodeId(urlEpisodeId); setActiveEpisodeId(urlEpisodeId);
// Find the episode and update page // Find the episode and update page
// Compare with both ?ep= format and plain format const episode = episodes.find(ep => ep.id === urlEpisodeId);
const episode = episodes.find(ep => {
return normalizeEpisodeId(ep.id) === normalizeEpisodeId(urlEpisodeId);
});
if (episode) { if (episode) {
const pageNumber = Math.ceil(episode.number / episodesPerPage); const pageNumber = Math.ceil(episode.number / episodesPerPage);
@@ -52,13 +49,6 @@ export default function EpisodeList({ episodes, currentEpisode, onEpisodeClick,
}; };
}, [episodes, episodesPerPage]); }, [episodes, episodesPerPage]);
// Helper function for episode ID comparison
// The API returns episode IDs in the format: animeId?ep=episodeNumber
const normalizeEpisodeId = (id) => {
// Simply return the ID as-is since the API already provides the correct format
return id || '';
};
const filteredEpisodes = useMemo(() => { const filteredEpisodes = useMemo(() => {
if (!searchQuery) return episodes; if (!searchQuery) return episodes;
const query = searchQuery.toLowerCase(); const query = searchQuery.toLowerCase();
@@ -81,7 +71,7 @@ export default function EpisodeList({ episodes, currentEpisode, onEpisodeClick,
const isCurrentEpisode = (episode) => { const isCurrentEpisode = (episode) => {
if (!episode || !episode.id || !activeEpisodeId) return false; if (!episode || !episode.id || !activeEpisodeId) return false;
return normalizeEpisodeId(episode.id) === normalizeEpisodeId(activeEpisodeId); return episode.id === activeEpisodeId;
}; };
const handleEpisodeSelect = (episode, e) => { const handleEpisodeSelect = (episode, e) => {

View File

@@ -19,6 +19,7 @@ const SpotlightCarousel = ({ items = [] }) => {
const [autoplay, setAutoplay] = useState(true); const [autoplay, setAutoplay] = useState(true);
const [progress, setProgress] = useState(0); const [progress, setProgress] = useState(0);
const [episodeIds, setEpisodeIds] = useState({}); const [episodeIds, setEpisodeIds] = useState({});
const [loadingItems, setLoadingItems] = useState({});
const intervalRef = useRef(null); const intervalRef = useRef(null);
const progressIntervalRef = useRef(null); const progressIntervalRef = useRef(null);
@@ -30,28 +31,73 @@ const SpotlightCarousel = ({ items = [] }) => {
// Fetch first episode IDs for all spotlight items // Fetch first episode IDs for all spotlight items
useEffect(() => { useEffect(() => {
const fetchEpisodeData = async () => { const fetchEpisodeData = async () => {
const episodeData = {}; // Create a copy to track what we're loading
const newLoadingItems = { ...loadingItems };
const episodeData = { ...episodeIds };
for (const item of items) { for (const item of items) {
if (item.id) { // Skip if we already have the episode ID or if it's already loading
if (item.id && !episodeData[item.id] && !newLoadingItems[item.id]) {
newLoadingItems[item.id] = true;
}
}
// Update loading state
setLoadingItems(newLoadingItems);
// Process items that need to be loaded
for (const item of items) {
if (item.id && !episodeData[item.id] && newLoadingItems[item.id]) {
try { try {
console.log(`[SpotlightCarousel] Fetching episodes for anime: ${item.id}`);
const response = await fetchAnimeEpisodes(item.id); const response = await fetchAnimeEpisodes(item.id);
console.log(`[SpotlightCarousel] Episodes response for ${item.name}:`, response);
if (response.episodes && response.episodes.length > 0) { if (response.episodes && response.episodes.length > 0) {
episodeData[item.id] = response.episodes[0].episodeId; // Check for episode ID in the expected format
const firstEp = response.episodes[0];
if (firstEp.id) {
episodeData[item.id] = firstEp.id;
console.log(`[SpotlightCarousel] Found episode ID (id) for ${item.name}: ${firstEp.id}`);
} else if (firstEp.episodeId) {
episodeData[item.id] = firstEp.episodeId;
console.log(`[SpotlightCarousel] Found episode ID (episodeId) for ${item.name}: ${firstEp.episodeId}`);
} else {
// Create a fallback ID if neither id nor episodeId are available
episodeData[item.id] = `${item.id}?ep=1`;
console.log(`[SpotlightCarousel] Using fallback ID for ${item.name}: ${item.id}?ep=1`);
}
} else {
// If no episodes, use a fallback
episodeData[item.id] = `${item.id}?ep=1`;
console.log(`[SpotlightCarousel] No episodes for ${item.name}, using fallback: ${item.id}?ep=1`);
} }
} catch (error) { } catch (error) {
console.error(`[SpotlightCarousel] Error fetching episodes for ${item.id}:`, error); console.error(`[SpotlightCarousel] Error fetching episodes for ${item.id}:`, error);
// Even on error, try to use fallback
episodeData[item.id] = `${item.id}?ep=1`;
} finally {
// Mark as no longer loading
newLoadingItems[item.id] = false;
} }
} }
} }
// Update states
setEpisodeIds(episodeData); setEpisodeIds(episodeData);
setLoadingItems(newLoadingItems);
}; };
if (items && items.length > 0) { if (items && items.length > 0) {
fetchEpisodeData(); fetchEpisodeData();
} }
}, [items]);
// Clean up function
return () => {
if (intervalRef.current) clearTimeout(intervalRef.current);
if (progressIntervalRef.current) clearInterval(progressIntervalRef.current);
};
}, [items, episodeIds, loadingItems]);
// Autoplay functionality // Autoplay functionality
useEffect(() => { useEffect(() => {
@@ -114,7 +160,7 @@ const SpotlightCarousel = ({ items = [] }) => {
// Get the watch URL for the current item // Get the watch URL for the current item
const watchUrl = episodeIds[currentItem.id] const watchUrl = episodeIds[currentItem.id]
? `/watch/${episodeIds[currentItem.id]}` ? `/watch/${episodeIds[currentItem.id]}`
: `/watch/${currentItem.id}?ep=1`; // Fallback to old format if API fetch fails : `/anime/${currentItem.id}`; // Direct to anime info if no episode ID
return ( return (
<div className="w-full mb-6 md:mb-10 spotlight-carousel"> <div className="w-full mb-6 md:mb-10 spotlight-carousel">
@@ -228,8 +274,9 @@ const SpotlightCarousel = ({ items = [] }) => {
{/* Buttons - Below title on mobile, right side on desktop */} {/* Buttons - Below title on mobile, right side on desktop */}
<div className="flex items-center space-x-2 md:space-x-4 mt-1 md:mt-0 md:absolute md:bottom-8 md:right-8"> <div className="flex items-center space-x-2 md:space-x-4 mt-1 md:mt-0 md:absolute md:bottom-8 md:right-8">
{/* Watch button - Uses episodeIds[anime.id] if available, otherwise links to anime details */}
<Link <Link
href={watchUrl} href={episodeIds[anime.id] ? `/watch/${episodeIds[anime.id]}` : `/anime/${anime.id}`}
className="bg-white hover:bg-gray-200 text-[#0a0a0a] font-medium text-xs md:text-base px-3 md:px-6 py-1.5 md:py-2 rounded flex items-center space-x-1.5 md:space-x-2 transition-colors" className="bg-white hover:bg-gray-200 text-[#0a0a0a] font-medium text-xs md:text-base px-3 md:px-6 py-1.5 md:py-2 rounded flex items-center space-x-1.5 md:space-x-2 transition-colors"
> >
<svg xmlns="http://www.w3.org/2000/svg" className="h-3.5 w-3.5 md:h-5 md:w-5" viewBox="0 0 20 20" fill="currentColor"> <svg xmlns="http://www.w3.org/2000/svg" className="h-3.5 w-3.5 md:h-5 md:w-5" viewBox="0 0 20 20" fill="currentColor">

View File

@@ -463,8 +463,8 @@ export const fetchAnimeEpisodes = async (animeId) => {
return { episodes: [] }; return { episodes: [] };
} }
// API already returns episodeId in correct format (animeId?ep=episodeNumber) // API returns episodes with episode.id in the format (animeId?ep=episodeNumber)
// So we simply use it directly without any formatting // We use this id directly for all episode operations
return { return {
episodes: data.data.episodes || [], episodes: data.data.episodes || [],
@@ -485,8 +485,7 @@ export const fetchEpisodeServers = async (episodeId) => {
console.log(`[API] Processing episode ID: ${episodeId}`); console.log(`[API] Processing episode ID: ${episodeId}`);
// episodeId should already be in the correct format (animeId?ep=episodeNumber) // Use the episode.id directly - it's in the format "animeId?ep=episodeNumber"
// from the API response, so we use it directly
const apiUrl = `${API_BASE_URL}/episode/servers?animeEpisodeId=${encodeURIComponent(episodeId)}`; const apiUrl = `${API_BASE_URL}/episode/servers?animeEpisodeId=${encodeURIComponent(episodeId)}`;
console.log(`[API Call] Fetching servers from: ${apiUrl}`); console.log(`[API Call] Fetching servers from: ${apiUrl}`);
@@ -543,8 +542,7 @@ export const fetchEpisodeSources = async (episodeId, dub = false, server = 'hd-2
console.log(`[API] Processing episode ID for sources: ${episodeId}`); console.log(`[API] Processing episode ID for sources: ${episodeId}`);
// episodeId should already be in the correct format (animeId?ep=episodeNumber) // Use the episode.id directly - it's in the format "animeId?ep=episodeNumber"
// from the API response, so we use it directly
const category = dub ? 'dub' : 'sub'; const category = dub ? 'dub' : 'sub';
const serverName = server || 'hd-2'; // Default to hd-2 if server is null or empty const serverName = server || 'hd-2'; // Default to hd-2 if server is null or empty
const apiUrl = `${API_BASE_URL}/episode/sources?animeEpisodeId=${encodeURIComponent(episodeId)}&category=${category}&server=${serverName}`; const apiUrl = `${API_BASE_URL}/episode/sources?animeEpisodeId=${encodeURIComponent(episodeId)}&category=${category}&server=${serverName}`;