Merge pull request #13 from ghoshRitesh12/est-schedule
Add Estimated Schedule feature
This commit is contained in:
@@ -92,6 +92,7 @@
|
|||||||
- [GET Producer Animes](#get-producer-animes)
|
- [GET Producer Animes](#get-producer-animes)
|
||||||
- [GET Genre Animes](#get-genre-animes)
|
- [GET Genre Animes](#get-genre-animes)
|
||||||
- [GET Category Anime](#get-category-anime)
|
- [GET Category Anime](#get-category-anime)
|
||||||
|
- [GET Estimated Schedules](#get-estimated-schedules)
|
||||||
- [GET Anime Episodes](#get-anime-episodes)
|
- [GET Anime Episodes](#get-anime-episodes)
|
||||||
- [GET Anime Episode Servers](#get-anime-episode-servers)
|
- [GET Anime Episode Servers](#get-anime-episode-servers)
|
||||||
- [GET Anime Episode Streaming Links](#get-anime-episode-streaming-links)
|
- [GET Anime Episode Streaming Links](#get-anime-episode-streaming-links)
|
||||||
@@ -705,6 +706,46 @@ console.log(data);
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `GET` Estimated Schedules
|
||||||
|
|
||||||
|
#### Endpoint
|
||||||
|
|
||||||
|
```sh
|
||||||
|
https://api-aniwatch.onrender.com/anime/schedule?date={date}
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Query Parameters
|
||||||
|
|
||||||
|
| Parameter | Type | Description | Required? | Default |
|
||||||
|
| :-----------------: | :----: | :------------------------------------------------------------------: | :-------: | :-----: |
|
||||||
|
| `date (yyyy-mm-dd)` | string | The date of the desired schedule. (months & days must have 2 digits) | Yes | -- |
|
||||||
|
|
||||||
|
#### Request sample
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
const resp = await fetch(
|
||||||
|
"https://api-aniwatch.onrender.com/anime/schedule?date=2023-01-14"
|
||||||
|
);
|
||||||
|
const data = await resp.json();
|
||||||
|
console.log(data);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Response Schema
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
{
|
||||||
|
scheduledAnimes: [
|
||||||
|
{
|
||||||
|
id: string,
|
||||||
|
time: string, // 24 hours format
|
||||||
|
name: string,
|
||||||
|
jname: string,
|
||||||
|
},
|
||||||
|
{...}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
### `GET` Anime Episodes
|
### `GET` Anime Episodes
|
||||||
|
|
||||||
#### Endpoint
|
#### Endpoint
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
import createHttpError from "http-errors";
|
||||||
|
import { type RequestHandler } from "express";
|
||||||
|
import { scrapeEstimatedSchedule } from "../parsers/index.js";
|
||||||
|
import { type EstimatedScheduleQueryParams } from "../models/controllers/index.js";
|
||||||
|
|
||||||
|
// /anime/schedule?date=${date}
|
||||||
|
const getEstimatedSchedule: RequestHandler<
|
||||||
|
unknown,
|
||||||
|
Awaited<ReturnType<typeof scrapeEstimatedSchedule>>,
|
||||||
|
unknown,
|
||||||
|
EstimatedScheduleQueryParams
|
||||||
|
> = async (req, res, next) => {
|
||||||
|
try {
|
||||||
|
const dateQuery = req.query.date
|
||||||
|
? decodeURIComponent(req.query.date as string)
|
||||||
|
: null;
|
||||||
|
|
||||||
|
if (dateQuery === null) {
|
||||||
|
throw createHttpError.BadRequest("Date payload required");
|
||||||
|
}
|
||||||
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(dateQuery)) {
|
||||||
|
throw createHttpError.BadRequest(
|
||||||
|
"Invalid date payload format. Months and days must have 2 digits"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await scrapeEstimatedSchedule(dateQuery);
|
||||||
|
|
||||||
|
res.status(200).json(data);
|
||||||
|
} catch (err: any) {
|
||||||
|
console.error(err);
|
||||||
|
next(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default getEstimatedSchedule;
|
||||||
@@ -6,6 +6,7 @@ import getAnimeCategory from "./animeCategory.controller.js";
|
|||||||
import getProducerAnimes from "./animeProducer.controller.js";
|
import getProducerAnimes from "./animeProducer.controller.js";
|
||||||
import getEpisodeServers from "./episodeServers.controller.js";
|
import getEpisodeServers from "./episodeServers.controller.js";
|
||||||
import getAnimeAboutInfo from "./animeAboutInfo.controller.js";
|
import getAnimeAboutInfo from "./animeAboutInfo.controller.js";
|
||||||
|
import getEstimatedSchedule from "./estimatedSchedule.controller.js";
|
||||||
import getAnimeEpisodeSources from "./animeEpisodeSrcs.controller.js";
|
import getAnimeEpisodeSources from "./animeEpisodeSrcs.controller.js";
|
||||||
import getAnimeSearchSuggestion from "./animeSearchSuggestion.controller.js";
|
import getAnimeSearchSuggestion from "./animeSearchSuggestion.controller.js";
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ export {
|
|||||||
getEpisodeServers,
|
getEpisodeServers,
|
||||||
getProducerAnimes,
|
getProducerAnimes,
|
||||||
getAnimeAboutInfo,
|
getAnimeAboutInfo,
|
||||||
|
getEstimatedSchedule,
|
||||||
getAnimeEpisodeSources,
|
getAnimeEpisodeSources,
|
||||||
getAnimeSearchSuggestion,
|
getAnimeSearchSuggestion,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export type EstimatedScheduleQueryParams = {
|
||||||
|
date?: string;
|
||||||
|
};
|
||||||
@@ -15,6 +15,7 @@ import type { AnimeEpisodePathParams } from "./animeEpisodes.js";
|
|||||||
import type { EpisodeServersQueryParams } from "./episodeServers.js";
|
import type { EpisodeServersQueryParams } from "./episodeServers.js";
|
||||||
import type { AnimeAboutInfoQueryParams } from "./animeAboutInfo.js";
|
import type { AnimeAboutInfoQueryParams } from "./animeAboutInfo.js";
|
||||||
import type { AnimeEpisodeSrcsQueryParams } from "./animeEpisodeSrcs.js";
|
import type { AnimeEpisodeSrcsQueryParams } from "./animeEpisodeSrcs.js";
|
||||||
|
import type { EstimatedScheduleQueryParams } from "./estimatedSchedule.js";
|
||||||
import type { AnimeSearchSuggestQueryParams } from "./animeSearchSuggestion.js";
|
import type { AnimeSearchSuggestQueryParams } from "./animeSearchSuggestion.js";
|
||||||
|
|
||||||
export type {
|
export type {
|
||||||
@@ -29,5 +30,6 @@ export type {
|
|||||||
AnimeAboutInfoQueryParams,
|
AnimeAboutInfoQueryParams,
|
||||||
EpisodeServersQueryParams,
|
EpisodeServersQueryParams,
|
||||||
AnimeEpisodeSrcsQueryParams,
|
AnimeEpisodeSrcsQueryParams,
|
||||||
|
EstimatedScheduleQueryParams,
|
||||||
AnimeSearchSuggestQueryParams,
|
AnimeSearchSuggestQueryParams,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
type EstimatedSchedule = {
|
||||||
|
id: string | null;
|
||||||
|
time: string | null;
|
||||||
|
name: string | null;
|
||||||
|
jname: string | null;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ScrapedEstimatedSchedule = {
|
||||||
|
scheduledAnimes: Array<EstimatedSchedule>;
|
||||||
|
};
|
||||||
@@ -6,6 +6,7 @@ import type { ScrapedProducerAnime } from "./animeProducer.js";
|
|||||||
import type { ScrapedEpisodeServers } from "./episodeServers.js";
|
import type { ScrapedEpisodeServers } from "./episodeServers.js";
|
||||||
import type { ScrapedAnimeAboutInfo } from "./animeAboutInfo.js";
|
import type { ScrapedAnimeAboutInfo } from "./animeAboutInfo.js";
|
||||||
import type { ScrapedAnimeSearchResult } from "./animeSearch.js";
|
import type { ScrapedAnimeSearchResult } from "./animeSearch.js";
|
||||||
|
import type { ScrapedEstimatedSchedule } from "./estimatedSchedule.js";
|
||||||
import type { ScrapedAnimeEpisodesSources } from "./animeEpisodeSrcs.js";
|
import type { ScrapedAnimeEpisodesSources } from "./animeEpisodeSrcs.js";
|
||||||
import type { ScrapedAnimeSearchSuggestion } from "./animeSearchSuggestion.js";
|
import type { ScrapedAnimeSearchSuggestion } from "./animeSearchSuggestion.js";
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ export type {
|
|||||||
ScrapedEpisodeServers,
|
ScrapedEpisodeServers,
|
||||||
ScrapedAnimeAboutInfo,
|
ScrapedAnimeAboutInfo,
|
||||||
ScrapedAnimeSearchResult,
|
ScrapedAnimeSearchResult,
|
||||||
|
ScrapedEstimatedSchedule,
|
||||||
ScrapedAnimeEpisodesSources,
|
ScrapedAnimeEpisodesSources,
|
||||||
ScrapedAnimeSearchSuggestion,
|
ScrapedAnimeSearchSuggestion,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -0,0 +1,67 @@
|
|||||||
|
import {
|
||||||
|
SRC_HOME_URL,
|
||||||
|
SRC_AJAX_URL,
|
||||||
|
USER_AGENT_HEADER,
|
||||||
|
ACCEPT_ENCODING_HEADER,
|
||||||
|
} from "../utils/index.js";
|
||||||
|
import axios, { AxiosError } from "axios";
|
||||||
|
import createHttpError, { type HttpError } from "http-errors";
|
||||||
|
import { load, type CheerioAPI, type SelectorType } from "cheerio";
|
||||||
|
import { type ScrapedEstimatedSchedule } from "../models/parsers/index.js";
|
||||||
|
|
||||||
|
// /anime/schedule?date=${date}
|
||||||
|
async function scrapeEstimatedSchedule(
|
||||||
|
date: string
|
||||||
|
): Promise<ScrapedEstimatedSchedule | HttpError> {
|
||||||
|
const res: ScrapedEstimatedSchedule = {
|
||||||
|
scheduledAnimes: [],
|
||||||
|
};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const estScheduleURL =
|
||||||
|
`${SRC_AJAX_URL}/schedule/list?tzOffset=-330&date=${date}` as const;
|
||||||
|
|
||||||
|
const mainPage = await axios.get(estScheduleURL, {
|
||||||
|
headers: {
|
||||||
|
Accept: "*/*",
|
||||||
|
Referer: SRC_HOME_URL,
|
||||||
|
"User-Agent": USER_AGENT_HEADER,
|
||||||
|
"X-Requested-With": "XMLHttpRequest",
|
||||||
|
"Accept-Encoding": ACCEPT_ENCODING_HEADER,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const $: CheerioAPI = load(mainPage?.data?.html);
|
||||||
|
|
||||||
|
const selector: SelectorType = "li";
|
||||||
|
|
||||||
|
if ($(selector)?.text()?.trim()?.includes("No data to display")) {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
$(selector).each((_, el) => {
|
||||||
|
res.scheduledAnimes.push({
|
||||||
|
id: $(el)?.find("a")?.attr("href")?.slice(1)?.trim() || null,
|
||||||
|
time: $(el)?.find("a .time")?.text()?.trim() || null,
|
||||||
|
name: $(el)?.find("a .film-name.dynamic-name")?.text()?.trim() || null,
|
||||||
|
jname:
|
||||||
|
$(el)
|
||||||
|
?.find("a .film-name.dynamic-name")
|
||||||
|
?.attr("data-jname")
|
||||||
|
?.trim() || null,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
return res;
|
||||||
|
} catch (err: any) {
|
||||||
|
if (err instanceof AxiosError) {
|
||||||
|
throw createHttpError(
|
||||||
|
err?.response?.status || 500,
|
||||||
|
err?.response?.statusText || "Something went wrong"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
throw createHttpError.InternalServerError(err?.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default scrapeEstimatedSchedule;
|
||||||
@@ -6,6 +6,7 @@ import scrapeAnimeCategory from "./animeCategory.js";
|
|||||||
import scrapeProducerAnimes from "./animeProducer.js";
|
import scrapeProducerAnimes from "./animeProducer.js";
|
||||||
import scrapeEpisodeServers from "./episodeServers.js";
|
import scrapeEpisodeServers from "./episodeServers.js";
|
||||||
import scrapeAnimeAboutInfo from "./animeAboutInfo.js";
|
import scrapeAnimeAboutInfo from "./animeAboutInfo.js";
|
||||||
|
import scrapeEstimatedSchedule from "./estimatedSchedule.js";
|
||||||
import scrapeAnimeEpisodeSources from "./animeEpisodeSrcs.js";
|
import scrapeAnimeEpisodeSources from "./animeEpisodeSrcs.js";
|
||||||
import scrapeAnimeSearchSuggestion from "./animeSearchSuggestion.js";
|
import scrapeAnimeSearchSuggestion from "./animeSearchSuggestion.js";
|
||||||
|
|
||||||
@@ -18,6 +19,7 @@ export {
|
|||||||
scrapeEpisodeServers,
|
scrapeEpisodeServers,
|
||||||
scrapeProducerAnimes,
|
scrapeProducerAnimes,
|
||||||
scrapeAnimeAboutInfo,
|
scrapeAnimeAboutInfo,
|
||||||
|
scrapeEstimatedSchedule,
|
||||||
scrapeAnimeEpisodeSources,
|
scrapeAnimeEpisodeSources,
|
||||||
scrapeAnimeSearchSuggestion,
|
scrapeAnimeSearchSuggestion,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import {
|
|||||||
getEpisodeServers,
|
getEpisodeServers,
|
||||||
getProducerAnimes,
|
getProducerAnimes,
|
||||||
getAnimeAboutInfo,
|
getAnimeAboutInfo,
|
||||||
|
getEstimatedSchedule,
|
||||||
getAnimeEpisodeSources,
|
getAnimeEpisodeSources,
|
||||||
getAnimeSearchSuggestion,
|
getAnimeSearchSuggestion,
|
||||||
} from "../controllers/index.js";
|
} from "../controllers/index.js";
|
||||||
@@ -42,6 +43,9 @@ router.get("/servers", getEpisodeServers);
|
|||||||
// /anime/episode-srcs?id=${episodeId}?server=${server}&category=${category (dub or sub)}
|
// /anime/episode-srcs?id=${episodeId}?server=${server}&category=${category (dub or sub)}
|
||||||
router.get("/episode-srcs", getAnimeEpisodeSources);
|
router.get("/episode-srcs", getAnimeEpisodeSources);
|
||||||
|
|
||||||
|
// /anime/schedule?date=${date}
|
||||||
|
router.get("/schedule", getEstimatedSchedule);
|
||||||
|
|
||||||
// /anime/producer/${name}?page=${page}
|
// /anime/producer/${name}?page=${page}
|
||||||
router.get("/producer/:name", getProducerAnimes);
|
router.get("/producer/:name", getProducerAnimes);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user