diff --git a/Dockerfile b/Dockerfile index 8bd0c90..2be63ba 100644 --- a/Dockerfile +++ b/Dockerfile @@ -28,6 +28,14 @@ RUN yarn build ##### RUNNER FROM node:18-alpine AS runner + +WORKDIR /tmp + +RUN wget https://github.com/metafates/mangal/releases/download/v3.12.0/mangal_3.12.0_Linux_x86_64.tar.gz && \ + tar xf mangal_3.12.0_Linux_x86_64.tar.gz && \ + mv mangal /usr/bin/mangal && \ + chmod +x /usr/bin/mangal + WORKDIR /app ENV NODE_ENV production @@ -37,19 +45,28 @@ ENV NEXT_TELEMETRY_DISABLED 1 RUN addgroup --system --gid 1001 nodejs RUN adduser --system --uid 1001 nextjs -COPY --from=builder /app/next.config.mjs ./ -COPY --from=builder /app/public ./public -COPY --from=builder /app/package.json ./package.json +COPY --from=builder --chown=nextjs:nodejs /app/next.config.mjs ./ +COPY --from=builder --chown=nextjs:nodejs /app/public ./public +COPY --from=builder --chown=nextjs:nodejs /app/package.json ./package.json -COPY --from=builder /app/.next ./.next -COPY --from=builder /app/prisma ./prisma -COPY --from=builder /app/dist ./dist -COPY --from=builder /app/node_modules ./node_modules +COPY --from=builder --chown=nextjs:nodejs /app/.next ./.next +COPY --from=builder --chown=nextjs:nodejs /app/prisma ./prisma +COPY --from=builder --chown=nextjs:nodejs /app/dist ./dist +COPY --from=builder --chown=nextjs:nodejs /app/node_modules ./node_modules +COPY --from=builder --chown=nextjs:nodejs /app/mangal /home/nextjs/.config/mangal/sources +RUN mkdir /data RUN chown -R nextjs:nodejs /app +RUN chown -R nextjs:nodejs /data USER nextjs EXPOSE 3000 ENV PORT 3000 +ENV MANGAL_METADATA_COMIC_INFO_XML=true +ENV MANGAL_METADATA_FETCH_ANILIST=true +ENV MANGAL_METADATA_SERIES_JSON=true +ENV MANGAL_FORMATS_USE=cbz +ENV MANGAL_DOWNLOADER_DOWNLOAD_COVER=true +ENV MANGAL_DOWNLOADER_REDOWNLOAD_EXISTING=true CMD ["yarn", "prod"] diff --git a/docker-compose.yml b/docker-compose.yml index 69b7dfb..22caa51 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -13,6 +13,8 @@ services: REDIS_HOST: redis env_file: - .env + volumes: + - /home/alperen/Manga:/data/manga depends_on: db: condition: service_healthy diff --git a/mangal/MangaDex.lua b/mangal/MangaDex.lua new file mode 100644 index 0000000..88f5ee6 --- /dev/null +++ b/mangal/MangaDex.lua @@ -0,0 +1,153 @@ +--------------------------------- +-- @name MangaDex +-- @url https://mangadex.org/ +-- @author alperen +-- @license MIT +--------------------------------- + + + + +----- IMPORTS ----- +Http = require("http") +Json = require('json') +Inspect = require('inspect') +HttpUtil = require("http_util") +Strings = require("strings") + +--- END IMPORTS --- + + + + +----- VARIABLES ----- +Client = Http.client() +ApiBase = "https://api.mangadex.org" +Base = "https://mangadex.org" +--- END VARIABLES --- + + + +----- MAIN ----- + +--- Searches for manga with given query. +--[[ +Manga fields: + name - string, required + url - string, required + author - string, optional + genres - string (multiple genres are divided by comma ','), optional + summary - string, optional +--]] +-- @param query Query to search for +-- @return Table of mangas +function SearchManga(query) + local request = Http.request("GET", + ApiBase .. + "/manga?limit=100&offset=0&order[relevance]=desc&availableTranslatedLanguage[]=en&title=" .. + HttpUtil.query_escape(query)) + local result = Client:do_request(request) + local result_body = Json.decode(result['body']) + local mangas = {} + + local i = 1 + + for _, val in pairs(result_body['data']) do + local title = val['attributes']['title']['en'] + local description = val['attributes']['description']['en'] + + if title ~= nil then + local id = val['id'] + local url = Base .. '/title/' .. tostring(id) + local manga = { url = url, name = title, summary = description } + + mangas[i] = manga + i = i + 1 + end + end + + return mangas +end + +--- Gets the list of all manga chapters. +--[[ +Chapter fields: + name - string, required + url - string, required + volume - string, optional + manga_summary - string, optional (in case you can't get it from search page) + manga_author - string, optional + manga_genres - string (multiple genres are divided by comma ','), optional +--]] +-- @param mangaURL URL of the manga +-- @return Table of chapters +function MangaChapters(mangaURL) + local splitResult = Strings.split(mangaURL, "title/") + local mangaId = splitResult[#splitResult] + local request = Http.request("GET", + ApiBase .. "/manga/" .. mangaId .. "/feed?translatedLanguage[]=en&limit=500&offset=0&order[chapter]=asc") + local result = Client:do_request(request) + local result_body = Json.decode(result['body']) + local chapters = {} + + local i = 1 + + for _, val in pairs(result_body['data']) do + local name = val['attributes']['title'] + + if name == nil or name == '' then + name = val['attributes']['chapter'] + end + + if name ~= nil then + local id = val['id'] + local url = Base .. '/chapter/' .. tostring(id) + local chapter = { url = url, name = name, chapter = val['attributes']['chapter'] } + + chapters[i] = chapter + i = i + 1 + end + end + + -- table.sort(chapters, function(a, b) return tonumber(a['chapter']) < tonumber(b['chapter']) end) + + return chapters +end + +--- Gets the list of all pages of a chapter. +--[[ +Page fields: + url - string, required + index - uint, required +--]] +-- @param chapterURL URL of the chapter +-- @return Table of pages +function ChapterPages(chapterURL) + local splitResult = Strings.split(chapterURL, "chapter/") + local chapterId = splitResult[#splitResult] + local request = Http.request("GET", ApiBase .. "/at-home/server/" .. chapterId) + local result = Client:do_request(request) + local result_body = Json.decode(result['body']) + local pages = {} + + local i = 1 + + for _, val in pairs(result_body['chapter']['data']) do + + local page = { index = i, url = result_body['baseUrl'] .. "/data/" .. result_body['chapter']['hash'] .. "/" .. val } + pages[i] = page + i = i + 1 + end + + return pages +end + +--- END MAIN --- + + + + +----- HELPERS ----- +--- END HELPERS --- + +-- ex: ts=4 sw=4 et filetype=lua diff --git a/mangal/Manganato.lua b/mangal/Manganato.lua new file mode 100644 index 0000000..9ceecb8 --- /dev/null +++ b/mangal/Manganato.lua @@ -0,0 +1,124 @@ +---------------------------------- +-- @name Manganato +-- @url https://manganato.com/ +-- @author alperen +-- @license MIT +---------------------------------- + + + + +----- IMPORTS ----- +Html = require("html") +Http = require("http") +HttpUtil = require("http_util") +Inspect = require('inspect') +--- END IMPORTS --- + + + + +----- VARIABLES ----- +Client = Http.client() +Base = "https://readmanganato.com/" +--- END VARIABLES --- + + + +----- MAIN ----- + +--- Searches for manga with given query. +--[[ +Manga fields: + name - string, required + url - string, required + author - string, optional + genres - string (multiple genres are divided by comma ','), optional + summary - string, optional +--]] +-- @param query Query to search for +-- @return Table of mangas +function SearchManga(query) + local request = Http.request("GET", Base .. "/search/story/" .. query:gsub(" ", "_")) + local result = Client:do_request(request) + + local doc = Html.parse(result.body) + local mangas = {} + + doc:find(".search-story-item"):each(function(i, s) + local manga = { name = s:find('.item-title'):first():text(), url = s:find('.item-img'):first():attr("href") } + mangas[i + 1] = manga + end) + + return mangas +end + +--- Gets the list of all manga chapters. +--[[ +Chapter fields: + name - string, required + url - string, required + volume - string, optional + manga_summary - string, optional (in case you can't get it from search page) + manga_author - string, optional + manga_genres - string (multiple genres are divided by comma ','), optional +--]] +-- @param mangaURL URL of the manga +-- @return Table of chapters +function MangaChapters(mangaURL) + local request = Http.request("GET", mangaURL) + local result = Client:do_request(request) + local doc = Html.parse(result.body) + + local chapters = {} + + doc:find(".chapter-name"):each(function(i, s) + local chapter = { name = s:text(), url = s:attr("href") } + chapters[i + 1] = chapter + end) + + Reverse(chapters) + + return chapters +end + +--- Gets the list of all pages of a chapter. +--[[ +Page fields: + url - string, required + index - uint, required +--]] +-- @param chapterURL URL of the chapter +-- @return Table of pages +function ChapterPages(chapterURL) + local request = Http.request("GET", chapterURL) + local result = Client:do_request(request) + local doc = Html.parse(result.body) + + local pages = {} + + doc:find(".container-chapter-reader img"):each(function(i, s) + local page = { index = i, url = s:attr("src") } + pages[i + 1] = page + end) + + return pages +end + +--- END MAIN --- + + +----- HELPERS ----- +function Reverse(t) + local n = #t + local i = 1 + while i < n do + t[i], t[n] = t[n], t[i] + i = i + 1 + n = n - 1 + end +end + +--- END HELPERS --- + +-- ex: ts=4 sw=4 et filetype=lua diff --git a/mangal/Mangasee.lua b/mangal/Mangasee.lua new file mode 100644 index 0000000..642569a --- /dev/null +++ b/mangal/Mangasee.lua @@ -0,0 +1,150 @@ +------------------------------------ +-- @name Mangasee +-- @url https://mangasee123.com/ +-- @author alperen +-- @license MIT +------------------------------------ + + + + +----- IMPORTS ----- +Html = require("html") +Http = require("http") +HttpUtil = require("http_util") +Inspect = require('inspect') +Headless = require("headless") +Strings = require("strings") +Regexp = require("regexp") +--- END IMPORTS --- + + + + +----- VARIABLES ----- +Client = Http.client() +Browser = Headless.browser() +Base = "https://mangasee123.com" +--- END VARIABLES --- + + + +----- MAIN ----- + +--- Searches for manga with given query. +--[[ +Manga fields: + name - string, required + url - string, required + author - string, optional + genres - string (multiple genres are divided by comma ','), optional + summary - string, optional +--]] +-- @param query Query to search for +-- @return Table of mangas +function SearchManga(query) + local page = Browser:page() + page:navigate(Base .. "/search/?name=" .. HttpUtil.query_escape(query)) + page:waitLoad() + + local doc = Html.parse(page:html()) + local mangas = {} + + doc:find(".top-15.ng-scope"):each(function(i, s) + local manga = { name = s:find('.SeriesName[ng-bind-html="Series.s"]'):first():text(), + url = Base .. s:find('.SeriesName[ng-bind-html="Series.s"]'):first():attr("href") } + mangas[i + 1] = manga + end) + + return mangas +end + +--- Gets the list of all manga chapters. +--[[ +Chapter fields: + name - string, required + url - string, required + volume - string, optional + manga_summary - string, optional (in case you can't get it from search page) + manga_author - string, optional + manga_genres - string (multiple genres are divided by comma ','), optional +--]] +-- @param mangaURL URL of the manga +-- @return Table of chapters +function MangaChapters(mangaURL) + local page = Browser:page() + page:navigate(mangaURL) + page:waitLoad() + if page:has('.ShowAllChapters') == true then + page:element('.ShowAllChapters'):click() + end + + local doc = Html.parse(page:html()) + + local chapters = {} + + doc:find(".ChapterLink"):each(function(i, s) + local name = s:find('span'):first():text() + name = Strings.trim(name:gsub("[\r\t\n]+", " "), " ") + local chapter = { name = name, url = Base .. s:attr("href") } + chapters[i + 1] = chapter + end) + + Reverse(chapters) + + return chapters +end + +--- Gets the list of all pages of a chapter. +--[[ +Page fields: + url - string, required + index - uint, required +--]] +-- @param chapterURL URL of the chapter +-- @return Table of pages +function ChapterPages(chapterURL) + local page = Browser:page() + page:navigate(chapterURL) + page:waitLoad() + page:element('.DesktopNav > div > div:nth-child(4) > button'):click() + local doc = Html.parse(page:html()) + + local pages = {} + + local images = {} + doc:find('.img-fluid'):each(function(i, s) + images[i+1] = tostring(s:attr('src')) + end) + + local modal = doc:find("#PageModal"):first() + modal:find('button[ng-click="vm.GoToPage(Page)"]'):each(function(i, s) + + local index = tonumber(Strings.trim(s:text():gsub("[\r\t\n]+", " "), " ")) + if index ~= nil then + local chapterPage = { index = index, url = images[index] } + pages[index] = chapterPage + end + end) + + return pages +end + +--- END MAIN --- + + + + +----- HELPERS ----- +function Reverse(t) + local n = #t + local i = 1 + while i < n do + t[i], t[n] = t[n], t[i] + i = i + 1 + n = n - 1 + end +end +--- END HELPERS --- + +-- ex: ts=4 sw=4 et filetype=lua