File size: 3,366 Bytes
11acfd9
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
import {
  SRC_SEARCH_URL,
  ACCEPT_HEADER,
  USER_AGENT_HEADER,
  ACCEPT_ENCODING_HEADER,
  extractAnimes,
  getSearchFilterValue,
  extractMostPopularAnimes,
  getSearchDateFilterValue,
} 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 { ScrapedAnimeSearchResult } from "../types/parsers/index.js";
import type { SearchFilters, FilterKeys } from "../types/controllers/index.js";

// /anime/search?q=${query}&page=${page}
async function scrapeAnimeSearch(
  q: string,
  page: number = 1,
  filters: SearchFilters
): Promise<ScrapedAnimeSearchResult | HttpError> {
  const res: ScrapedAnimeSearchResult = {
    animes: [],
    mostPopularAnimes: [],
    currentPage: Number(page),
    hasNextPage: false,
    totalPages: 1,
    searchQuery: q,
    searchFilters: filters,
  };

  try {
    const url = new URL(SRC_SEARCH_URL);
    url.searchParams.set("keyword", q);
    url.searchParams.set("page", `${page}`);
    url.searchParams.set("sort", "default");

    for (const key in filters) {
      if (key.includes("_date")) {
        const dates = getSearchDateFilterValue(
          key === "start_date",
          filters[key as keyof SearchFilters] || ""
        );
        if (!dates) continue;

        dates.map((dateParam) => {
          const [key, val] = dateParam.split("=");
          url.searchParams.set(key, val);
        });
        continue;
      }

      const filterVal = getSearchFilterValue(
        key as FilterKeys,
        filters[key as keyof SearchFilters] || ""
      );
      filterVal && url.searchParams.set(key, filterVal);
    }

    const mainPage = await axios.get(url.href, {
      headers: {
        "User-Agent": USER_AGENT_HEADER,
        "Accept-Encoding": ACCEPT_ENCODING_HEADER,
        Accept: ACCEPT_HEADER,
      },
    });

    const $: CheerioAPI = load(mainPage.data);

    const selector: SelectorType =
      "#main-content .tab-content .film_list-wrap .flw-item";

    res.hasNextPage =
      $(".pagination > li").length > 0
        ? $(".pagination li.active").length > 0
          ? $(".pagination > li").last().hasClass("active")
            ? false
            : true
          : false
        : false;

    res.totalPages =
      Number(
        $('.pagination > .page-item a[title="Last"]')
          ?.attr("href")
          ?.split("=")
          .pop() ??
          $('.pagination > .page-item a[title="Next"]')
            ?.attr("href")
            ?.split("=")
            .pop() ??
          $(".pagination > .page-item.active a")?.text()?.trim()
      ) || 1;

    res.animes = extractAnimes($, selector);

    if (res.animes.length === 0 && !res.hasNextPage) {
      res.totalPages = 0;
    }

    const mostPopularSelector: SelectorType =
      "#main-sidebar .block_area.block_area_sidebar.block_area-realtime .anif-block-ul ul li";
    res.mostPopularAnimes = extractMostPopularAnimes($, mostPopularSelector);

    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 scrapeAnimeSearch;