{ "id": "mangapill-external", "name": "Mangapill (External)", "description": "", "version": "0.0.1", "type": "manga-provider", "manifestURI": "", "language": "go", "author": "Seanime", "payload": "package main\n\nimport (\n\t\"fmt\"\n\t\"github.com/5rahim/hibike/pkg/extension/manga\"\n\t\"github.com/5rahim/hibike/pkg/util/bypass\"\n\t\"github.com/5rahim/hibike/pkg/util/common\"\n\t\"github.com/5rahim/hibike/pkg/util/similarity\"\n\t\"github.com/gocolly/colly\"\n\t\"github.com/rs/zerolog\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"strconv\"\n\t\"strings\"\n\t\"time\"\n)\n\nconst MangapillProvider = \"mangapill-external\"\n\ntype (\n\tMangapill struct {\n\t\tUrl string\n\t\tClient *http.Client\n\t\tUserAgent string\n\t\tlogger *zerolog.Logger\n\t}\n)\n\nfunc NewProvider(logger *zerolog.Logger) manga.Provider {\n\tc := &http.Client{\n\t\tTimeout: 60 * time.Second,\n\t}\n\tc.Transport = bypass.AddCloudFlareByPass(c.Transport)\n\treturn &Mangapill{\n\t\tUrl: \"https://mangapill.com\",\n\t\tClient: c,\n\t\tUserAgent: \"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3\",\n\t\tlogger: logger,\n\t}\n}\n\n// DEVNOTE: Unique ID\n// Each chapter ID has this format: {number}${slug} -- e.g. 6502-10004000$gokurakugai-chapter-4\n// The chapter ID is split by the $ character to reconstruct the chapter URL for subsequent requests\n\nfunc (mp *Mangapill) Search(opts manga.SearchOptions) (ret []*manga.SearchResult, err error) {\n\tret = make([]*manga.SearchResult, 0)\n\n\tmp.logger.Debug().Str(\"query\", opts.Query).Msg(\"mangapill: Searching manga\")\n\n\turi := fmt.Sprintf(\"%s/search?q=%s\", mp.Url, url.QueryEscape(opts.Query))\n\n\tc := colly.NewCollector(\n\t\tcolly.UserAgent(mp.UserAgent),\n\t)\n\n\tc.WithTransport(mp.Client.Transport)\n\n\tc.OnHTML(\"div.container div.my-3.justify-end > div\", func(e *colly.HTMLElement) {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t}\n\t\t}()\n\t\tresult := &manga.SearchResult{\n\t\t\tProvider: \"mangapill\",\n\t\t}\n\n\t\tresult.ID = strings.Split(e.ChildAttr(\"a\", \"href\"), \"/manga/\")[1]\n\t\tresult.ID = strings.Replace(result.ID, \"/\", \"$\", -1)\n\n\t\ttitle := e.DOM.Find(\"div > a > div.mt-3\").Text()\n\t\tresult.Title = strings.TrimSpace(title)\n\n\t\taltTitles := e.DOM.Find(\"div > a > div.text-xs.text-secondary\").Text()\n\t\tif altTitles != \"\" {\n\t\t\tresult.Synonyms = []string{strings.TrimSpace(altTitles)}\n\t\t}\n\n\t\tcompTitles := []string{result.Title}\n\t\tif len(result.Synonyms) > 0 {\n\t\t\tcompTitles = append(compTitles, result.Synonyms[0])\n\t\t}\n\t\tcompRes, _ := similarity.FindBestMatchWithSorensenDice(opts.Query, compTitles)\n\t\tresult.SearchRating = compRes.Rating\n\n\t\tresult.Image = e.ChildAttr(\"a img\", \"data-src\")\n\n\t\tyearStr := e.DOM.Find(\"div > div.flex > div\").Eq(1).Text()\n\t\tyear, err := strconv.Atoi(strings.TrimSpace(yearStr))\n\t\tif err != nil {\n\t\t\tresult.Year = 0\n\t\t} else {\n\t\t\tresult.Year = year\n\t\t}\n\n\t\tret = append(ret, result)\n\t})\n\n\terr = c.Visit(uri)\n\tif err != nil {\n\t\tmp.logger.Error().Err(err).Msg(\"mangapill: Failed to visit\")\n\t\treturn nil, err\n\t}\n\n\t// code\n\n\tif len(ret) == 0 {\n\t\tmp.logger.Error().Str(\"query\", opts.Query).Msg(\"mangapill: No results found\")\n\t\treturn nil, fmt.Errorf(\"no results found\")\n\t}\n\n\tmp.logger.Info().Int(\"count\", len(ret)).Msg(\"mangapill: Found results\")\n\n\treturn ret, nil\n}\n\nfunc (mp *Mangapill) FindChapters(id string) (ret []*manga.ChapterDetails, err error) {\n\tret = make([]*manga.ChapterDetails, 0)\n\n\tmp.logger.Debug().Str(\"mangaId\", id).Msg(\"mangapill: Finding chapters\")\n\n\turiId := strings.Replace(id, \"$\", \"/\", -1)\n\turi := fmt.Sprintf(\"%s/manga/%s\", mp.Url, uriId)\n\n\tc := colly.NewCollector(\n\t\tcolly.UserAgent(mp.UserAgent),\n\t)\n\n\tc.WithTransport(mp.Client.Transport)\n\n\tc.OnHTML(\"div.container div.border-border div#chapters div.grid-cols-1 a\", func(e *colly.HTMLElement) {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t}\n\t\t}()\n\t\tchapter := &manga.ChapterDetails{\n\t\t\tProvider: MangapillProvider,\n\t\t}\n\n\t\tchapter.ID = strings.Split(e.Attr(\"href\"), \"/chapters/\")[1]\n\t\tchapter.ID = strings.Replace(chapter.ID, \"/\", \"$\", -1)\n\n\t\tchapter.Title = strings.TrimSpace(e.Text)\n\n\t\tsplitTitle := strings.Split(chapter.Title, \"Chapter \")\n\t\tif len(splitTitle) < 2 {\n\t\t\treturn\n\t\t}\n\t\tchapter.Chapter = splitTitle[1]\n\n\t\tret = append(ret, chapter)\n\t})\n\n\terr = c.Visit(uri)\n\tif err != nil {\n\t\tmp.logger.Error().Err(err).Msg(\"mangapill: Failed to visit\")\n\t\treturn nil, err\n\t}\n\n\tif len(ret) == 0 {\n\t\tmp.logger.Error().Str(\"mangaId\", id).Msg(\"mangapill: No chapters found\")\n\t\treturn nil, fmt.Errorf(\"no chapters found\")\n\t}\n\n\tcommon.Reverse(ret)\n\n\tfor i, chapter := range ret {\n\t\tchapter.Index = uint(i)\n\t}\n\n\tmp.logger.Info().Int(\"count\", len(ret)).Msg(\"mangapill: Found chapters\")\n\n\treturn ret, nil\n}\n\nfunc (mp *Mangapill) FindChapterPages(id string) (ret []*manga.ChapterPage, err error) {\n\tret = make([]*manga.ChapterPage, 0)\n\n\tmp.logger.Debug().Str(\"chapterId\", id).Msg(\"mangapill: Finding chapter pages\")\n\n\turiId := strings.Replace(id, \"$\", \"/\", -1)\n\turi := fmt.Sprintf(\"%s/chapters/%s\", mp.Url, uriId)\n\n\tc := colly.NewCollector(\n\t\tcolly.UserAgent(mp.UserAgent),\n\t)\n\n\tc.WithTransport(mp.Client.Transport)\n\n\tc.OnHTML(\"chapter-page\", func(e *colly.HTMLElement) {\n\t\tdefer func() {\n\t\t\tif r := recover(); r != nil {\n\t\t\t}\n\t\t}()\n\t\tpage := &manga.ChapterPage{}\n\n\t\tpage.URL = e.DOM.Find(\"div picture img\").AttrOr(\"data-src\", \"\")\n\t\tif page.URL == \"\" {\n\t\t\treturn\n\t\t}\n\t\tindexStr := e.DOM.Find(\"div[data-summary] > div\").Text()\n\t\tindex, _ := strconv.Atoi(strings.Split(strings.Split(indexStr, \"page \")[1], \"/\")[0])\n\t\tpage.Index = index - 1\n\n\t\tpage.Headers = map[string]string{\n\t\t\t\"Referer\": mp.Url,\n\t\t}\n\n\t\tret = append(ret, page)\n\t})\n\n\terr = c.Visit(uri)\n\tif err != nil {\n\t\tmp.logger.Error().Err(err).Msg(\"mangapill: Failed to visit\")\n\t\treturn nil, err\n\t}\n\n\tif len(ret) == 0 {\n\t\tmp.logger.Error().Str(\"chapterId\", id).Msg(\"mangapill: No pages found\")\n\t\treturn nil, fmt.Errorf(\"no pages found\")\n\t}\n\n\tmp.logger.Info().Int(\"count\", len(ret)).Msg(\"mangapill: Found pages\")\n\n\treturn ret, nil\n\n}\n" }