diff --git a/.gitignore b/.gitignore index fd3dbb5..0f13ab6 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,8 @@ # next.js /.next/ /out/ +websocket.js +test.js # production /build diff --git a/package-lock.json b/package-lock.json index 562ead8..f8e9863 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,8 @@ "name": "my-anime-site", "version": "0.1.0", "dependencies": { + "@headlessui/react": "^2.1.2", + "@heroicons/react": "^2.1.5", "axios": "^1.7.2", "bcryptjs": "^2.4.3", "cache-manager": "^5.6.1", @@ -28,11 +30,13 @@ "react-cookie-consent": "^9.0.0", "react-dom": "^18", "react-icons": "^5.2.1", + "react-iframe": "^1.8.5", "react-markdown": "^9.0.1", "react-modal": "^3.16.1", "react-player": "^2.16.0", "redis": "^4.6.14", "sharp": "^0.33.4", + "socket.io-client": "^4.7.5", "swr": "^2.2.5", "tls": "^0.0.1" }, @@ -131,6 +135,80 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@floating-ui/core": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.6.4.tgz", + "integrity": "sha512-a4IowK4QkXl4SCWTGUR0INAfEOX3wtsYw3rKK5InQEHMGObkR8Xk44qYQD9P4r6HHw0iIfK6GUKECmY8sTkqRA==", + "dependencies": { + "@floating-ui/utils": "^0.2.4" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.6.7", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.6.7.tgz", + "integrity": "sha512-wmVfPG5o2xnKDU4jx/m4w5qva9FWHcnZ8BvzEe90D/RpwsJaTAVYPEPdQ8sbr/N8zZTAHlZUTQdqg8ZUbzHmng==", + "dependencies": { + "@floating-ui/core": "^1.6.0", + "@floating-ui/utils": "^0.2.4" + } + }, + "node_modules/@floating-ui/react": { + "version": "0.26.19", + "resolved": "https://registry.npmjs.org/@floating-ui/react/-/react-0.26.19.tgz", + "integrity": "sha512-Jk6zITdjjIvjO/VdQFvpRaD3qPwOHH6AoDHxjhpy+oK4KFgaSP871HYWUAPdnLmx1gQ+w/pB312co3tVml+BXA==", + "dependencies": { + "@floating-ui/react-dom": "^2.1.1", + "@floating-ui/utils": "^0.2.4", + "tabbable": "^6.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/react-dom": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.1.1.tgz", + "integrity": "sha512-4h84MJt3CHrtG18mGsXuLCHMrug49d7DFkU0RMIyshRveBeyV2hmV/pDaF2Uxtu8kgq5r46llp5E5FQiR0K2Yg==", + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "peerDependencies": { + "react": ">=16.8.0", + "react-dom": ">=16.8.0" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.4.tgz", + "integrity": "sha512-dWO2pw8hhi+WrXq1YJy2yCuWoL20PddgGaqTgVe4cOS9Q6qklXCiA1tJEqX6BEwRNSCP84/afac9hd4MS+zEUA==" + }, + "node_modules/@headlessui/react": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/@headlessui/react/-/react-2.1.2.tgz", + "integrity": "sha512-Kb3hgk9gRNRcTZktBrKdHhF3xFhYkca1Rk6e1/im2ENf83dgN54orMW0uSKTXFnUpZOUFZ+wcY05LlipwgZIFQ==", + "dependencies": { + "@floating-ui/react": "^0.26.16", + "@react-aria/focus": "^3.17.1", + "@react-aria/interactions": "^3.21.3", + "@tanstack/react-virtual": "^3.8.1" + }, + "engines": { + "node": ">=10" + }, + "peerDependencies": { + "react": "^18", + "react-dom": "^18" + } + }, + "node_modules/@heroicons/react": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@heroicons/react/-/react-2.1.5.tgz", + "integrity": "sha512-FuzFN+BsHa+7OxbvAERtgBTNeZpUjgM/MIizfVkSCL2/edriN0Hx/DWRCR//aPYwO5QX/YlgLGXk+E3PcfZwjA==", + "peerDependencies": { + "react": ">= 16" + } + }, "node_modules/@humanwhocodes/config-array": { "version": "0.11.14", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", @@ -899,6 +977,83 @@ "node": ">=14" } }, + "node_modules/@react-aria/focus": { + "version": "3.17.1", + "resolved": "https://registry.npmjs.org/@react-aria/focus/-/focus-3.17.1.tgz", + "integrity": "sha512-FLTySoSNqX++u0nWZJPPN5etXY0WBxaIe/YuL/GTEeuqUIuC/2bJSaw5hlsM6T2yjy6Y/VAxBcKSdAFUlU6njQ==", + "dependencies": { + "@react-aria/interactions": "^3.21.3", + "@react-aria/utils": "^3.24.1", + "@react-types/shared": "^3.23.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-aria/interactions": { + "version": "3.21.3", + "resolved": "https://registry.npmjs.org/@react-aria/interactions/-/interactions-3.21.3.tgz", + "integrity": "sha512-BWIuf4qCs5FreDJ9AguawLVS0lV9UU+sK4CCnbCNNmYqOWY+1+gRXCsnOM32K+oMESBxilAjdHW5n1hsMqYMpA==", + "dependencies": { + "@react-aria/ssr": "^3.9.4", + "@react-aria/utils": "^3.24.1", + "@react-types/shared": "^3.23.1", + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-aria/ssr": { + "version": "3.9.4", + "resolved": "https://registry.npmjs.org/@react-aria/ssr/-/ssr-3.9.4.tgz", + "integrity": "sha512-4jmAigVq409qcJvQyuorsmBR4+9r3+JEC60wC+Y0MZV0HCtTmm8D9guYXlJMdx0SSkgj0hHAyFm/HvPNFofCoQ==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "engines": { + "node": ">= 12" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-aria/utils": { + "version": "3.24.1", + "resolved": "https://registry.npmjs.org/@react-aria/utils/-/utils-3.24.1.tgz", + "integrity": "sha512-O3s9qhPMd6n42x9sKeJ3lhu5V1Tlnzhu6Yk8QOvDuXf7UGuUjXf9mzfHJt1dYzID4l9Fwm8toczBzPM9t0jc8Q==", + "dependencies": { + "@react-aria/ssr": "^3.9.4", + "@react-stately/utils": "^3.10.1", + "@react-types/shared": "^3.23.1", + "@swc/helpers": "^0.5.0", + "clsx": "^2.0.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-stately/utils": { + "version": "3.10.1", + "resolved": "https://registry.npmjs.org/@react-stately/utils/-/utils-3.10.1.tgz", + "integrity": "sha512-VS/EHRyicef25zDZcM/ClpzYMC5i2YGN6uegOeQawmgfGjb02yaCX0F0zR69Pod9m2Hr3wunTbtpgVXvYbZItg==", + "dependencies": { + "@swc/helpers": "^0.5.0" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, + "node_modules/@react-types/shared": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@react-types/shared/-/shared-3.23.1.tgz", + "integrity": "sha512-5d+3HbFDxGZjhbMBeFHRQhexMFt4pUce3okyRtUVKbbedQFUrtXSBg9VszgF2RTeQDKDkMCIQDtz5ccP/Lk1gw==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0-rc.1 || ^18.0.0" + } + }, "node_modules/@redis/bloom": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@redis/bloom/-/bloom-1.2.0.tgz", @@ -958,6 +1113,11 @@ "integrity": "sha512-qC/xYId4NMebE6w/V33Fh9gWxLgURiNYgVNObbJl2LZv0GUUItCcCqC5axQSwRaAgaxl2mELq1rMzlswaQ0Zxg==", "dev": true }, + "node_modules/@socket.io/component-emitter": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@socket.io/component-emitter/-/component-emitter-3.1.2.tgz", + "integrity": "sha512-9BCxFwvbGg/RsZK9tjXd8s4UcwR0MWeFQ1XEKIQVVvAGJyINdrqKMcTRyLoK8Rse1GjzLV9cwjWV1olXRWEXVA==" + }, "node_modules/@swc/counter": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@swc/counter/-/counter-0.1.3.tgz", @@ -972,6 +1132,31 @@ "tslib": "^2.4.0" } }, + "node_modules/@tanstack/react-virtual": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@tanstack/react-virtual/-/react-virtual-3.8.3.tgz", + "integrity": "sha512-9ICwbDUUzN99CJIGc373i8NLoj6zFTKI2Hlcmo0+lCSAhPQ5mxq4dGOMKmLYoEFyHcGQ64Bd6ZVbnPpM6lNK5w==", + "dependencies": { + "@tanstack/virtual-core": "3.8.3" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + }, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0", + "react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tanstack/virtual-core": { + "version": "3.8.3", + "resolved": "https://registry.npmjs.org/@tanstack/virtual-core/-/virtual-core-3.8.3.tgz", + "integrity": "sha512-vd2A2TnM5lbnWZnHi9B+L2gPtkSeOtJOAw358JqokIH1+v2J7vUAzFVPwB/wrye12RFOurffXu33plm4uQ+JBQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/tannerlinsley" + } + }, "node_modules/@types/debug": { "version": "4.1.12", "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", @@ -1871,6 +2056,14 @@ "node": ">=0.8" } }, + "node_modules/clsx": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "engines": { + "node": ">=6" + } + }, "node_modules/cluster-key-slot": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/cluster-key-slot/-/cluster-key-slot-1.1.2.tgz", @@ -2394,6 +2587,26 @@ "node": ">= 0.8" } }, + "node_modules/engine.io-client": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", + "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1", + "engine.io-parser": "~5.2.1", + "ws": "~8.17.1", + "xmlhttprequest-ssl": "~2.0.0" + } + }, + "node_modules/engine.io-parser": { + "version": "5.2.3", + "resolved": "https://registry.npmjs.org/engine.io-parser/-/engine.io-parser-5.2.3.tgz", + "integrity": "sha512-HqD3yTBfnBxIrbnM1DoD6Pcq8NECnh8d4As1Qgh0z5Gg3jRRIqijury0CL3ghu/edArpUYiYqQiDUQBIs4np3Q==", + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/enhanced-resolve": { "version": "5.17.0", "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.0.tgz", @@ -6453,6 +6666,17 @@ "react": "*" } }, + "node_modules/react-iframe": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/react-iframe/-/react-iframe-1.8.5.tgz", + "integrity": "sha512-F4cQJGs3ydaG6fJWfuz9yLwOU0Trzl6kttXuUG+vYwosH8enOOFxZWEDQCSbNVO8ayjfYZeqLxEvdvcsSy4GvA==", + "dependencies": { + "object-assign": "^4.1.1" + }, + "peerDependencies": { + "react": ">=16.x.x" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -7027,6 +7251,32 @@ "node": ">=8" } }, + "node_modules/socket.io-client": { + "version": "4.7.5", + "resolved": "https://registry.npmjs.org/socket.io-client/-/socket.io-client-4.7.5.tgz", + "integrity": "sha512-sJ/tqHOCe7Z50JCBCXrsY3I2k03iOiUe+tj1OmKeD2lXPiGH/RUCdTZFoqVyN7l1MnpIzPrGtLcijffmeouNlQ==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.2", + "engine.io-client": "~6.5.2", + "socket.io-parser": "~4.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/socket.io-parser": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/socket.io-parser/-/socket.io-parser-4.2.4.tgz", + "integrity": "sha512-/GbIKmo8ioc+NIWIhwdecY0ge+qVBSMdgxGygevmdHj24bsfgtCmcUUcQ5ZzcylGFHsN3k4HB4Cgkl96KVnuew==", + "dependencies": { + "@socket.io/component-emitter": "~3.1.0", + "debug": "~4.3.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -7390,6 +7640,11 @@ "react": "^16.11.0 || ^17.0.0 || ^18.0.0" } }, + "node_modules/tabbable": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/tabbable/-/tabbable-6.2.0.tgz", + "integrity": "sha512-Cat63mxsVJlzYvN51JmVXIgNoUokrIaT2zLclCXjRd8boZ0004U4KCs/sToJ75C6sdlByWxpYnb5Boif1VSFew==" + }, "node_modules/tailwindcss": { "version": "3.4.4", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.4.4.tgz", @@ -8066,6 +8321,34 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", "dev": true }, + "node_modules/ws": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": ">=5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/xmlhttprequest-ssl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest-ssl/-/xmlhttprequest-ssl-2.0.0.tgz", + "integrity": "sha512-QKxVRxiRACQcVuQEYFsI1hhkrMlrXHPegbbd1yn9UHOmRxY+si12nQYzri3vbzt8VdTTRviqcKxcyllFas5z2A==", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", diff --git a/package.json b/package.json index 66c032b..01bf0ea 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,8 @@ "lint": "next lint" }, "dependencies": { + "@headlessui/react": "^2.1.2", + "@heroicons/react": "^2.1.5", "axios": "^1.7.2", "bcryptjs": "^2.4.3", "cache-manager": "^5.6.1", @@ -30,11 +32,13 @@ "react-cookie-consent": "^9.0.0", "react-dom": "^18", "react-icons": "^5.2.1", + "react-iframe": "^1.8.5", "react-markdown": "^9.0.1", "react-modal": "^3.16.1", "react-player": "^2.16.0", "redis": "^4.6.14", "sharp": "^0.33.4", + "socket.io-client": "^4.7.5", "swr": "^2.2.5", "tls": "^0.0.1" }, diff --git a/src/components/EpisodePlayer.js b/src/components/EpisodePlayer.js index e54608a..0f6adc1 100644 --- a/src/components/EpisodePlayer.js +++ b/src/components/EpisodePlayer.js @@ -1,70 +1,56 @@ -import React, { useState, useEffect, useMemo, useRef } from 'react'; -import axios from 'axios'; -import CustomDropdown from '@/components/CustomDropdown'; -import ReactPlayer from 'react-player'; +// EpisodePlayer.jsx +import React, { useState, useEffect } from 'react'; +import ReactIframe from 'react-iframe'; -const EpisodePlayer = ({ episode, selectedQuality, setSelectedQuality, setSelectedServer, selectedServer }) => { - const [sources, setSources] = useState({}); +const EpisodePlayer = ({ episode, setSelectedServer, selectedServer }) => { const [loading, setLoading] = useState(true); const [playerUrl, setPlayerUrl] = useState(''); - const iframeRef = useRef(null); - const cache = useRef({}); + const [episodeLoaded, setEpisodeLoaded] = useState(false); + const [prefetchedEpisode, setPrefetchedEpisode] = useState(null); useEffect(() => { - if (episode && episode.animeId && episode.episodeNumber) { - const fetchSources = async () => { - setLoading(true); // Start loading when fetching sources - const cacheKey = `${episode.animeId}-${episode.episodeNumber}`; - if (cache.current[cacheKey]) { - setSources(cache.current[cacheKey]); - setLoading(false); - } else { - try { - const response = await axios.get(`https://player.nekonode.net/sources?anime_id=${episode.animeId}&episode=${episode.episodeNumber}`); - setSources(response.data); - cache.current[cacheKey] = response.data; // Cache the response - setLoading(false); // Stop loading when sources are fetched - } catch (error) { - console.error('Error fetching sources:', error); - setSources({}); - setLoading(false); // Stop loading even if there is an error - } - } - }; - - fetchSources(); - } - }, [episode]); - - const serverSources = useMemo(() => (Array.isArray(sources[selectedServer]) ? sources[selectedServer] : []), [sources, selectedServer]); - - useEffect(() => { - if (!episode || !episode.animeId || !episode.episodeNumber || serverSources.length === 0) { + if (!episode || !episode.episodeId) { + console.log('No episode or episode ID available'); setPlayerUrl(''); + setLoading(false); return; } - let source = serverSources.find(source => source.quality === selectedQuality); - if (!source) { - if (selectedServer === 'gogocdn') { - source = serverSources.find(source => source.quality === 'backup') || serverSources[0]; - } else if (selectedServer === 'streamwish') { - source = serverSources.find(source => source.quality === 'default') || serverSources[0]; + console.log('Loading new episode...'); + setLoading(true); + setEpisodeLoaded(false); + + // Simulate episode loading + const timer = setTimeout(() => { + setEpisodeLoaded(true); + }, 1000); // Simulate 1-second episode loading time + + return () => clearTimeout(timer); + }, [selectedServer, episode]); + + useEffect(() => { + if (episodeLoaded) { + console.log('Setting player URL'); + const url = `http://localhost:4000/?anime_id=${episode.episodeId}&server=${selectedServer}`; + if (playerUrl !== url) { + setPlayerUrl(url); } + setLoading(false); } + }, [episodeLoaded, selectedServer, episode, playerUrl]); - if (!source) { - console.error('No suitable source found for the selected server and quality.'); - setPlayerUrl(''); - } else { - const animeId = episode.animeId; - const episodeNumber = episode.episodeNumber; - const quality = source.quality; - setPlayerUrl(`https://player.nekonode.net/?anime_id=${animeId}&episode=${episodeNumber}&quality=${quality}&server=${selectedServer}`); + useEffect(() => { + // Pre-fetch the next episode data (example implementation) + if (episode && episode.nextEpisodeId) { + fetch(`http://localhost:4000/next_episode?anime_id=${episode.nextEpisodeId}`) + .then(response => response.json()) + .then(data => setPrefetchedEpisode(data)) + .catch(error => console.error('Error pre-fetching next episode:', error)); } - }, [serverSources, selectedQuality, selectedServer, episode]); + }, [episode]); const handleIframeLoad = () => { + console.log('Iframe loaded'); setLoading(false); }; @@ -73,24 +59,23 @@ const EpisodePlayer = ({ episode, selectedQuality, setSelectedQuality, setSelect

Episode {episode ? episode.episodeNumber : '0'}

{loading && ( -
+
)} - {!loading && playerUrl && ( -
- -
- )} + onLoad={handleIframeLoad} + /> + )} +
{!loading && !playerUrl && (

No suitable source found for the selected server and quality.

@@ -101,7 +86,7 @@ const EpisodePlayer = ({ episode, selectedQuality, setSelectedQuality, setSelect

Select Server:

-

If one server isn't working, Try the next

+

If one server isn't working, try the next

- source.quality) : []} - selectedOption={selectedQuality} - onSelect={setSelectedQuality} - />
); diff --git a/src/components/domains.js b/src/components/domains.js new file mode 100644 index 0000000..ac21505 --- /dev/null +++ b/src/components/domains.js @@ -0,0 +1,48 @@ +import { Fragment } from 'react'; +import { Disclosure, Transition, DisclosureButton, DisclosurePanel } from '@headlessui/react'; +import { ChevronUpIcon } from '@heroicons/react/24/solid'; + +export default function DomainsTable({ domains }) { + return ( +
+

Available Domains

+
+
+

Main Domain

+

{domains.main}

+
+ + {({ open }) => ( + <> + + Mirror Domains + + + + +
    + {domains.mirrors.map((domain, index) => ( +
  • {domain}
  • + ))} +
+
+
+ + )} +
+
+
+ ); +} diff --git a/src/pages/anime/[name].js b/src/pages/anime/[name].js index 5efad6e..ae71048 100644 --- a/src/pages/anime/[name].js +++ b/src/pages/anime/[name].js @@ -15,10 +15,10 @@ const fetcher = url => axios.get(url).then(res => res.data); const AnimePage = () => { const router = useRouter(); - const { name, ep } = router.query; + const { name, ep } = router.query; const { data: animeData, error } = useSWR(name ? `${api}/anime/${name}` : null, fetcher, { - revalidateOnFocus: false, - dedupingInterval: 60000, + revalidateOnFocus: false, + dedupingInterval: 60000, }); const { data: session } = useSession(); const [currentPage, setCurrentPage] = useState(1); @@ -29,11 +29,12 @@ const AnimePage = () => { const [errorMessage, setErrorMessage] = useState(''); const [okMessage, setOkMessage] = useState(''); - const fetchEpisodeDetails = useCallback((episodeNumber) => { - const episodeId = `${name}-episode-${episodeNumber}`; + const fetchEpisodeDetails = useCallback((episode) => { + const { episodeNumber, id } = episode; + console.log('Fetching episode details:', episode); setSelectedEpisode({ episodeNumber, - episodeId, + episodeId: id, animeId: name, sources: [{ quality: selectedQuality }] }); @@ -41,19 +42,19 @@ const AnimePage = () => { useEffect(() => { if (name && animeData) { - const firstEpisode = animeData.episodes[0]?.episodeNumber; - const episodeToPlay = ep ? parseInt(ep, 10) : firstEpisode; + const firstEpisode = animeData.episodes[0]; + const episodeToPlay = ep ? animeData.episodes.find(e => e.episodeNumber === parseInt(ep, 10)) : firstEpisode; fetchEpisodeDetails(episodeToPlay); } }, [name, ep, animeData, fetchEpisodeDetails]); const handleEpisodeSelect = episodeNumber => { - const episodeNum = parseInt(episodeNumber, 10); // Convert to number + const episode = animeData.episodes.find(e => e.episodeNumber === parseInt(episodeNumber, 10)); router.push({ pathname: `/anime/${name}`, - query: { ep: episodeNum } + query: { ep: episodeNumber } }, undefined, { shallow: true }); - fetchEpisodeDetails(episodeNum); + fetchEpisodeDetails(episode); }; const handlePageChange = (direction) => { @@ -63,21 +64,17 @@ const AnimePage = () => { const handleAddToAnimeList = async ({ status }) => { if (session) { try { - // deurl the name so it looks normal const deurl = decodeURIComponent(name); let anime_name = deurl.replace(/-/g, ' '); - // uppercase the first letter of each word - anime_name = anime_name.replace(/\b\w/g, l => l.toUpperCase ()); + anime_name = anime_name.replace(/\b\w/g, l => l.toUpperCase()); const response = await axios.post(`${api}/animelist/add`, { name: anime_name, animeId: name, - image: animeData.animeInfo.image, // Use animeData to get animeInfo.image + image: animeData.animeInfo.image, status, lastWatchedAt: new Date(), }); if (response.status === 200) { - //console.log('Anime added to your list successfully!'); - //setErrorMessage('Anime added to your list successfully!'); setOkMessage('Anime added to your list successfully!'); } } catch (error) { @@ -107,12 +104,12 @@ const AnimePage = () => { const currentEpisodes = episodes.slice((currentPage - 1) * episodesPerPage, currentPage * episodesPerPage); const episodeButtons = currentEpisodes.map(episode => ( -
  • +
  • )); @@ -138,8 +135,8 @@ const AnimePage = () => { onChange={(e) => handleEpisodeSelect(parseInt(e.target.value, 10))} > {currentEpisodes.map(episode => ( - ))} @@ -232,7 +229,16 @@ const AnimeDetails = ({ animeInfo, onAddToListClick, isAuthenticated }) => ( const ImageWrapper = ({ image, title }) => (
    - {title} + {title}
    ); diff --git a/src/pages/api/anime/[animeName].js b/src/pages/api/anime/[animeName].js index e8d9bc1..6cbb523 100644 --- a/src/pages/api/anime/[animeName].js +++ b/src/pages/api/anime/[animeName].js @@ -53,12 +53,18 @@ export default async function handler(req, res) { const episodeTitle = $api(element).find('.name').text().trim(); const episodeNumberMatch = episodeTitle.match(/EP (\d+)/i); const episodeNumber = episodeNumberMatch ? parseInt(episodeNumberMatch[1], 10) : null; + + // Remove / from id and trailing hyphen + let id = episodeUrl.trim(); + id = id.replace(/\//g, '')//.replace(/-episode-\d+/, '').replace(/-$/, ''); + // rmove the last hyphen if (episodeUrl && episodeTitle) { episodes.push({ episodeNumber, title: episodeTitle, - url: `https://gogoanime3.co${episodeUrl.trim()}`, + id: id, + url: `https://gogoanime3.co${episodeUrl.trim()}` }); } }); @@ -129,4 +135,4 @@ export default async function handler(req, res) { console.log('Encoded anime name:', encodedAnimeName); res.status(500).json({ error: 'Failed to retrieve anime' }); } -} +} \ No newline at end of file diff --git a/src/pages/auth/profile.js b/src/pages/auth/profile.js index bfc6b25..f4585e6 100644 --- a/src/pages/auth/profile.js +++ b/src/pages/auth/profile.js @@ -137,32 +137,32 @@ const Dashboard = () => { return (
    -
    -

    Your Dashboard

    +
    +

    Your Dashboard

    -
    +
    {(session.user.role === 'moderator' || session.user.role === 'admin') && ( @@ -174,28 +174,28 @@ const Dashboard = () => { {session.user.name} -

    Name: {session.user.name}

    -

    ID: {session.user.id}

    -

    Created At: {formatDate(session.user.createdAt)}

    +

    Name: {session.user.name}

    +

    ID: {session.user.id}

    +

    Created At: {formatDate(session.user.createdAt)}

    )} {activeTab === 'animelist' && (
    2 ? 'max-h-96 overflow-y-auto pr-4' : ''}`}> {loadingAnimeList ? ( -

    Loading anime list...

    +

    Loading anime list...

    ) : error ? ( -

    {error}

    +

    {error}

    ) : animeList.length === 0 ? ( -

    No anime in your list.

    +

    No anime in your list.

    ) : ( animeList.map((item, index) => ( -
    -
    +
    +
    {item.animeId} { />
    -

    {nametolong(item.name)}

    -

    Status: {capitalize(item.status)}

    -

    Last Time Watched: {formatDate(item.lastWatchedAt)}

    +

    {nametolong(item.name)}

    +

    Status: {capitalize(item.status)}

    +

    Last Time Watched: {formatDate(item.lastWatchedAt)}

    Watch Anime

    -
    +
    diff --git a/src/pages/index.js b/src/pages/index.js index ac2277e..db873db 100644 --- a/src/pages/index.js +++ b/src/pages/index.js @@ -1,5 +1,5 @@ import React, { useState } from 'react'; -import Link from 'next/link'; +import { TabGroup, TabPanel, TabPanels, TabList, Tab } from '@headlessui/react'; import AnimeList from '../components/AnimeList'; import Timetable from '../components/Timetable'; import TopAnimeList from '../components/TopAnimeList'; @@ -14,11 +14,12 @@ const api = process.env.NEXTAUTH_URL; const HomePage = ({ initialLatestAnime, topAnime, initialPage, newsPosts }) => { const [latestAnime, setLatestAnime] = useState(initialLatestAnime); const [page, setPage] = useState(initialPage); + const [type, setType] = useState(1); // 1 for subs, 2 for dubs const handlePagination = async (newPage) => { try { const { data } = await axios.get('/api/latest', { - params: { page: newPage, limit: 12 }, + params: { page: newPage, limit: 12, type }, }); setLatestAnime(data); setPage(newPage); @@ -27,39 +28,85 @@ const HomePage = ({ initialLatestAnime, topAnime, initialPage, newsPosts }) => { } }; + const handleTabChange = async (tabIndex) => { + const newType = tabIndex === 1 ? 2 : 1; // 0 for subbed, 1 for dubbed + setType(newType); + try { + const { data } = await axios.get('/api/latest', { + params: { page: 1, limit: 12, type: newType }, + }); + setLatestAnime(data); + setPage(1); + } catch (error) { + console.error('Error fetching episodes:', error); + } + }; + return (
    {/* Main Content */}
    -
    -

    +
    +

    Recently Updated

    -
    - {page > 1 && ( - - )} - -
    + +
    + + + selected + ? 'py-1 px-2 sm:py-2 sm:px-4 text-sm sm:text-base leading-5 font-medium text-gray-800 bg-yellow-500 rounded transition duration-150 ease-in-out' + : 'py-1 px-2 sm:py-2 sm:px-4 text-sm sm:text-base leading-5 font-medium text-gray-400 hover:text-gray-800 hover:bg-yellow-600 rounded transition duration-150 ease-in-out' + } + > + Sub + + + selected + ? 'py-1 px-2 sm:py-2 sm:px-4 text-sm sm:text-base leading-5 font-medium text-gray-800 bg-yellow-500 rounded transition duration-150 ease-in-out' + : 'py-1 px-2 sm:py-2 sm:px-4 text-sm sm:text-base leading-5 font-medium text-gray-400 hover:text-gray-800 hover:bg-yellow-600 rounded transition duration-150 ease-in-out' + } + > + Dub + + +
    + {page > 1 && ( + + )} + +
    +
    +
    - + + + + + + + + + +

    -
    -
    +
    @@ -73,7 +120,7 @@ export async function getServerSideProps({ query }) { try { const [latestResponse, topAnimeResponse] = await Promise.all([ axios.get(`${api}/api/latest`, { - params: { page: initialPage, limit: 12 }, + params: { page: initialPage, limit: 12, type: 1 }, // default to subbed }), axios.get(`${api}/api/top10`), ]); diff --git a/src/pages/search.js b/src/pages/search.js index 9f98bb6..8ed9c92 100644 --- a/src/pages/search.js +++ b/src/pages/search.js @@ -58,16 +58,19 @@ const SearchResults = () => {