Skip to content

Commit

Permalink
Merge pull request #173 from koikiss-dev/TokyoTF
Browse files Browse the repository at this point in the history
Add hentaila.com
  • Loading branch information
koikiss-dev authored Jul 24, 2024
2 parents dabfffe + 158314d commit a3185d2
Show file tree
Hide file tree
Showing 6 changed files with 196 additions and 5 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# JIMOV - Media Content API

![image description](<.gitbook/assets/JIMOV\_logo (1).png>)
![image description](<./src/images/JIMOV_logo.png>)

[![discordBadge](https://img.shields.io/badge/Chat-Click%20here-7289d9?style=for-the-badge\&logo=discord)](https://discord.com/invite/tyZ39GCX7R)[![documentationBadge](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](https://choosealicense.com/licenses/mit/)[![documentationBadge](https://img.shields.io/badge/Documentation-Click%20here-blue?style=for-the-badge)](https://jimov-api-docs.vercel.app/)

Expand Down
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import tioanime from "../src/routes/v1/anime/tioanime/TioAnimeRoute";
import WcoStream from "../src/routes/v1/anime/wcostream/wcostreamRoutes";
import AnimeBlix from "../src/routes/v1/anime/animeblix/AnimeBlixRoutes";
import Animevostfr from "../src/routes/v1/anime/animevostfr/AnimevostfrRoutes";
import hentaila from "../src/routes/v1/anime/hentaila/HentaiLaRoutes";

/* Manga */
import comick from "../src/routes/v1/manga/comick/ComickRoutes";
Expand Down Expand Up @@ -42,7 +43,7 @@ app.use(tioanime);
app.use(WcoStream);
app.use(AnimeBlix);
app.use(Animevostfr);

app.use(hentaila);
/* anime */

/*Manga*/
Expand Down
30 changes: 30 additions & 0 deletions src/routes/v1/anime/hentaila/HentaiLaRoutes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import { Router } from "express";
import { HentaiLa } from "../../../../scraper/sites/anime/hentaila/HentaiLa";
const Anime = new HentaiLa();
const router = Router();

// Filter
router.get("/anime/hentaila/filter", async (req, res) => {
const { search } = req.query;

const data = await Anime.GetItemByFilter(
search as string
);
res.send(data);
});

// Anime Info +(Episodes list)
router.get("/anime/hentaila/name/:name", async (req, res) => {
const { name } = req.params;
const data = await Anime.GetItemInfo(name);
res.send(data);
});

// Episode Info +(Video Servers)
router.get("/anime/hentaila/episode/:episode", async (req, res) => {
const { episode } = req.params;
const data = await Anime.GetEpisodeServers(episode);
res.send(data);
});

export default router;
139 changes: 139 additions & 0 deletions src/scraper/sites/anime/hentaila/HentaiLa.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
import * as cheerio from "cheerio";
import axios from "axios";
import { AnimeMedia } from "../../../../types/anime";
import { Episode, EpisodeServer } from "../../../../types/episode";

import { ResultSearch, AnimeResult } from "../../../../types/search";
import { AnimeScraperModel } from "../../../../models/AnimeScraperModel";

export class HentaiLa extends AnimeScraperModel {
readonly url = "https://www4.hentaila.com";

async GetItemInfo(anime: string): Promise<AnimeMedia> {
try {
const formatUrl = !anime.includes("hentai-") ? "hentai-" + anime : anime;
const { data } = await axios.get(`${this.url}/${formatUrl}`);
const $ = cheerio.load(data);
const genres = [];
$(".genres a").each((_i, e) => genres.push($(e).text()));

const AnimeInfo: AnimeMedia = {
name: $(".hentai-single .h-header h1.h-title").text().trim(),
url: `/anime/hentaila/name/${anime}`,
synopsis: $(".hentai-single .h-content p").text(),
image: {
url: `${this.url}${$(".hentai-single .h-thumb figure img").attr(
"src"
)}`,
},
genres: [...genres],
nsfw: true,
type: "Anime",
status: $(".h-meta .status-on").text().includes("En Emision")
? "En emisión"
: "Finalizado",
episodes: [],
};

$(".episodes-list article").each((_i, e) => {
const number = Number(
$(e)
.find("h2.h-title")
.text()
.replace(
$(".hentai-single .h-header h1.h-title").text() + " Episodio",
""
)
);
const AnimeEpisode: Episode = {
name: $(e).find("h2.h-title").text(),
num: number,
thumbnail: {
url: `${this.url}${$(e).find(".h-thumb figure img").attr("src")}`,
},
url: `/anime/hentaila/episode/${formatUrl}-${number}`,
};

AnimeInfo.episodes.push(AnimeEpisode);
});

return AnimeInfo;
} catch (error) {
console.log(error);
}
}
async GetEpisodeServers(episode: string): Promise<Episode> {
try {
const number = episode.substring(episode.lastIndexOf("-") + 1);
const anime = episode.substring(0, episode.lastIndexOf("-"));
const formaturl = anime.includes("hentai-")
? anime.replace("hentai-", "")
: anime;
const { data } = await axios.get(
`${this.url}/ver/${formaturl}-${number}`
);
const $ = cheerio.load(data);

const AnimeEpisodeInfo: Episode = {
name: $(".section-header h1.section-title").text(),
url: `/anime/hentaila/episode/${episode}`,
num: Number(number),
servers: [],
};

const video_script = $("script").get().at(-3).children[0].data;
const video_format = eval(
video_script
.slice(
video_script.indexOf("var videos ="),
video_script.lastIndexOf("$(document)")
)
.replace("var videos =", "")
);
for (let index = 0; index < video_format.length; index++) {
const Server: EpisodeServer = {
name: video_format[index][0].includes("Yupi") ? "YourUpload" : video_format[index][0],
url: video_format[index][1],
};
AnimeEpisodeInfo.servers.push(Server);
}

AnimeEpisodeInfo.servers.sort((a, b) => a.name.localeCompare(b.name));
return AnimeEpisodeInfo;
} catch (error) {
console.log(error);
}
}

async GetItemByFilter(
search?: string
): Promise<ResultSearch<AnimeResult>> {
try {
const content = new FormData();
content.append("value", search);
const { data } = await axios.post(`${this.url}/api/search`, content);

const animeSearch: ResultSearch<AnimeResult> = {
nav: {
count: data.length,
current: 1,
next: 0,
hasNext: false,
},
results: [],
};
data.map((e) => {
const animeSearchData: AnimeResult = {
name: e.title,
image: `${this.url}/uploads/portadas/${e.id}.jpg`,
url: `/anime/hentaila/name/${e.slug}`,
type: "Anime",
};
animeSearch.results.push(animeSearchData);
});
return animeSearch;
} catch (error) {
console.log(error);
}
}
}
3 changes: 0 additions & 3 deletions src/scraper/sites/manga/comick/Comick.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,6 @@ export class Comick {

return ResultList;
} catch (error) {
console.log(error);
}
}

Expand Down Expand Up @@ -154,7 +153,6 @@ export class Comick {
}
return MangaInfo;
} catch (error) {
console.log(error);
}
}

Expand Down Expand Up @@ -245,7 +243,6 @@ export class Comick {
return MangaChapterInfoChapter;
}
} catch (error) {
console.log(error);
}
}
}
24 changes: 24 additions & 0 deletions src/test/Hentaila.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { HentaiLa } from "../scraper/sites/anime/hentaila/HentaiLa";

describe("AnimeLatinohd", () => {
let hentaila: HentaiLa;

beforeEach(() => {
hentaila = new HentaiLa();
});

it("should get anime info successfully", async () => {
const animeInfo = await hentaila.GetItemInfo("hentai-korashime-2");
expect(animeInfo.name).toBe("Korashime 2");
expect(animeInfo.image.url).toContain(".jpg");
expect(animeInfo.status).toBe("Finalizado");
expect(animeInfo.synopsis?.length).toBeGreaterThan(0);
expect(animeInfo.genres?.length).toBeGreaterThan(0);
expect(animeInfo.episodes?.length).toBeGreaterThan(0);
});

it("should filter anime successfully", async () => {
const result = await hentaila.GetItemByFilter("na");
expect(result.results.length).toBeGreaterThan(0);
}, 10000);
});

0 comments on commit a3185d2

Please sign in to comment.