sokuja / server.js
bilaku's picture
Update server.js
4d2558b verified
Raw
History Blame Contribute Delete
7.8 kB
/**
* SOKUJA API Server
* REST API for the Sokuja Scraper
*/
const express = require('express');
const cors = require('cors');
const SokujaScraper = require('./index');
const app = express();
const PORT = process.env.PORT || 7860;
app.use(cors());
app.use(express.json());
const scraper = new SokujaScraper();
// Middleware for error handling
const asyncHandler = (fn) => (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
/**
* @route GET /
* @description API Info
*/
app.get('/', (req, res) => {
res.json({
name: 'SOKUJA API',
version: '1.0.0',
description: 'Unofficial API for SOKUJA anime streaming',
baseUrl: 'https://x5.sokuja.uk',
endpoints: {
home: '/api/home',
latest: '/api/latest',
ongoing: '/api/ongoing?page=1',
completed: '/api/completed?page=1',
movies: '/api/movies?page=1',
anime: '/api/anime/:slug',
episodes: '/api/anime/:slug/episodes',
episode: '/api/anime/:slug/episode/:number',
schedule: '/api/schedule',
genres: '/api/genres',
genreAnime: '/api/genre/:slug?page=1',
search: '/api/search?q=keyword',
popular: '/api/popular?type=weekly',
batch: '/api/anime/:slug/batch',
stream: '/api/stream?url=xPhpUrl'
}
});
});
/**
* @route GET /api/home
* @description Get home page data (featured, latest, popular)
*/
app.get('/api/home', asyncHandler(async (req, res) => {
const data = await scraper.getHomeComplete();
res.json({
success: true,
data
});
}));
/**
* @route GET /api/latest
* @description Get latest episode updates
*/
app.get('/api/latest', asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const data = await scraper.getLatestUpdates(page);
res.json({
success: true,
page,
data
});
}));
/**
* @route GET /api/ongoing
* @description Get ongoing anime series
*/
app.get('/api/ongoing', asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const data = await scraper.getOngoing(page);
res.json({
success: true,
page,
total: data.total,
count: data.anime.length,
hasNext: data.hasNextPage,
data: data.anime
});
}));
/**
* @route GET /api/completed
* @description Get completed anime series
*/
app.get('/api/completed', asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const data = await scraper.getCompleted(page);
res.json({
success: true,
page,
total: data.total,
count: data.anime.length,
hasNext: data.hasNextPage,
data: data.anime
});
}));
/**
* @route GET /api/movies
* @description Get anime movies
*/
app.get('/api/movies', asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const data = await scraper.getMovies(page);
res.json({
success: true,
page,
total: data.total,
count: data.anime.length,
hasNext: data.hasNextPage,
data: data.anime
});
}));
/**
* @route GET /api/anime/:slug
* @description Get anime detail with episodes
*/
app.get('/api/anime/:slug', asyncHandler(async (req, res) => {
const data = await scraper.getAnimeDetail(req.params.slug);
res.json({
success: true,
data
});
}));
/**
* @route GET /api/anime/:slug/episodes
* @description Get all episodes for an anime
*/
app.get('/api/anime/:slug/episodes', asyncHandler(async (req, res) => {
const anime = await scraper.getAnimeDetail(req.params.slug);
res.json({
success: true,
anime: {
title: anime.title,
slug: req.params.slug
},
count: anime.episodes.length,
episodes: anime.episodes
});
}));
/**
* @route GET /api/anime/:slug/episode/:number
* @description Get specific episode with stream/download links
*/
app.get('/api/anime/:slug/episode/:number', asyncHandler(async (req, res) => {
const data = await scraper.getEpisodeDetail(req.params.slug, req.params.number);
res.json({
success: true,
data
});
}));
/**
* @route GET /api/schedule
* @description Get anime release schedule
*/
app.get('/api/schedule', asyncHandler(async (req, res) => {
const data = await scraper.getSchedule();
res.json({
success: true,
data
});
}));
/**
* @route GET /api/genres
* @description Get all available genres
*/
app.get('/api/genres', asyncHandler(async (req, res) => {
const data = await scraper.getGenres();
res.json({
success: true,
count: data.length,
data
});
}));
/**
* @route GET /api/genre/:slug
* @description Get anime by genre
*/
app.get('/api/genre/:slug', asyncHandler(async (req, res) => {
const page = parseInt(req.query.page) || 1;
const data = await scraper.getByGenre(req.params.slug, page);
res.json({
success: true,
genre: req.params.slug,
page,
total: data.total,
count: data.anime.length,
hasNext: data.hasNextPage,
data: data.anime
});
}));
/**
* @route GET /api/search
* @description Search anime by keyword
*/
app.get('/api/search', asyncHandler(async (req, res) => {
const { q } = req.query;
if (!q) {
return res.status(400).json({
success: false,
message: 'Query parameter "q" is required'
});
}
const data = await scraper.searchAnime(q);
res.json({
success: true,
query: q,
found: data.results.length,
data: data.results
});
}));
/**
* @route GET /api/popular
* @description Get popular anime
*/
app.get('/api/popular', asyncHandler(async (req, res) => {
const type = req.query.type || 'weekly'; // weekly, monthly, alltime
const data = await scraper.getPopular(type);
res.json({
success: true,
type,
count: Array.isArray(data) ? data.length : 0,
data
});
}));
/**
* @route GET /api/anime/:slug/batch
* @description Get batch download links for anime
*/
app.get('/api/anime/:slug/batch', asyncHandler(async (req, res) => {
const data = await scraper.getBatchDownloads(req.params.slug);
res.json({
success: true,
data
});
}));
/**
* @route GET /api/stream
* @description Resolve stream URL from x.php link
*/
app.get('/api/stream', asyncHandler(async (req, res) => {
const { url } = req.query;
if (!url) {
return res.status(400).json({
success: false,
message: 'URL parameter is required'
});
}
const data = await scraper.getStreamUrl(url);
res.json({
success: true,
data
});
}));
/**
* @route GET /api/list
* @description Get complete anime list A-Z
*/
app.get('/api/list', asyncHandler(async (req, res) => {
const data = await scraper.getAnimeListAZ();
res.json({
success: true,
count: data.length,
data
});
}));
// Error handler
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({
success: false,
message: err.message || 'Internal Server Error',
error: process.env.NODE_ENV === 'development' ? err.stack : undefined
});
});
// 404 handler
app.use((req, res) => {
res.status(404).json({
success: false,
message: 'Endpoint not found',
available: '/'
});
});
app.listen(PORT, () => {
console.log(`
╔════════════════════════════════════════╗
β•‘ SOKUJA API Server v1.0.0 β•‘
β•‘ Port: ${PORT} β•‘
β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
Available endpoints:
- GET / β†’ API info
- GET /api/home β†’ Home data
- GET /api/ongoing β†’ Ongoing series
- GET /api/completed β†’ Completed series
- GET /api/anime/:slug β†’ Anime detail
- GET /api/search?q= β†’ Search anime
- GET /api/schedule β†’ Release schedule
`);
});