-- {"id":102555,"ver":"1.0.7","libVer":"1.0.0","author":"Zordic"} local json = Require("dkjson") --- Identification number of the extension. local id = 102555 -- Update with your extension ID --- Base URL of the extension. local baseURL = "https://wtr-lab.com/" --- Api URL of the ChapterData. local apiUrl = "https://wtr-lab.com/api/reader/get" local proxyUrl = "https://wtr-lab-proxy.fly.dev/chapter" local Translation_url = "https://translate-pa.googleapis.com/v1/translateHtml" local autkey = "AIzaSyATBXajvzQLTDHEQbcpq0Ihe0vWDHmO520" --- URL of the logo. local imageURL = "https://i.imgur.com/ObQtFVW.png" --MediaType for JSON local mtype = MediaType("application/json; charset=utf-8") local mtype2 = MediaType("application/json+protobuf; charset=utf-8") --- Cloudflare protection status. local hasCloudFlare = false ---@param v Element local text = function(v) return v:text() end --- Search configuration. local hasSearch = true local isSearchIncrementing = true --- Filters configuration. local ORDER_FILTER_ID = 2 local ORDER_PERM = { "view", "name", "date", "reader", "chapter" } local ORDER_VALUE = { "View", "Name", "Addition Date", "Reader", "Chapter" } local SORT_FILTER_ID = 4 local SORT_PERM = { "desc", "asc" } local SORT_VALUE = { "Descending", "Ascending" } local STATUS_FILTER_ID = 3 local FILTER_VALUE = { "All", "Ongoing", "Completed" } local FILTER_PERM = { "all", "ongoing", "completed" } --- Filters configuration. local searchFilters = { DropdownFilter(ORDER_FILTER_ID, "Order by", ORDER_VALUE), DropdownFilter(SORT_FILTER_ID, "Sort by", SORT_VALUE), DropdownFilter(STATUS_FILTER_ID, "Status", FILTER_VALUE) } --- URL handling functions. local function shrinkURL(url, type) return url:gsub(baseURL, ""):gsub("^en", "") end local function expandURL(url, type) url = url:gsub("^/", "") return baseURL .. url end --- Chapter content extraction. local function getPassage(chapterURL) local url = expandURL(chapterURL, KEY_CHAPTER_URL) local doc = GETDocument(url) local script = doc:selectFirst("#__NEXT_DATA__"):html() local data = json.decode(script) local content = data.props.pageProps.serie local payload = { chapter_id = content.chapter.id, language = "en", raw_id = content.chapter.raw_id, translate = "web" } local body = RequestBody(json.encode(payload), mtype) local headers = HeadersBuilder():add("Content-Type", "application/json"):add("Referer", url):build() local response = Request(POST(apiUrl, headers, body)) local responseBody = response:body():string() local jdata = json.decode(responseBody) local encrypyedbody = jdata.data.data.body local proxyPayload = {payload = encrypyedbody} local proxyBody = RequestBody(json.encode(proxyPayload), mtype) local proxyHeaders = HeadersBuilder():add("Content-Type", "application/json"):build() local proxyResponse = Request(POST(proxyUrl, proxyHeaders, proxyBody)) local responseBody = proxyResponse:body():string() local decrypted = json.decode(responseBody) local htmlContent = decrypted local payload2 = { {htmlContent, "zh-CN", "en" }, "wt_lib" } --every 2 eg: body2,headers2 and so on is only for translation qurey if you are usind google translate local body2 = RequestBody(json.encode(payload2), mtype2) local headers2 = HeadersBuilder():add("Content-Type", "application/json+protobuf"):add("Origin",baseURL):add("X-Goog-Api-Key", autkey):build() local response2 = Request(POST(Translation_url, headers2, body2)) local responseBody2 = response2:body():string() local jdata2 = json.decode(responseBody2) local translatedContent = jdata2[1] local html = table.concat(map(translatedContent, function(v) return "
" .. v .. "
" end)) local doc = Document(html) -- Traverse the document to remove emptytags local toRemove = {} doc:traverse(NodeVisitor(function(v) if v:tagName() == "p" and v:text() == "" then toRemove[#toRemove + 1] = v end end, nil, true)) -- Remove the empty
tags
for _, v in ipairs(toRemove) do
v:remove()
end
local pTagList = map(doc:select("p"), text)
local htmlContent = ""
for _, v in pairs(pTagList) do
htmlContent = htmlContent .. "
" .. v
end
return pageOfElem(Document(htmlContent), true)
end
--- Novel parsing function.
local function parseNovel(novelURL)
local url = expandURL(novelURL, KEY_NOVEL_URL)
local doc = GETDocument(url)
local alertElement = doc:selectFirst("div.alert.alert-warning")
local isReleased = true -- Assume the source is released by default
if alertElement and alertElement:text():find("This source is not released yet.") then
isReleased = false -- Mark the source as unreleased if the alert is found
end
local script = doc:selectFirst("#__NEXT_DATA__"):html()
local data = json.decode(script)
local serie = data.props.pageProps.serie.serie_data
local authorElements = doc:select(".serie-info-grid .sig-row:has(.sig-label:matchesOwn(^Author$)) .sig-value a.sig-author-alt")
local authors = authorElements and map(authorElements, text) or {}
if authors == nil or #authors == 0 then
authors = {"Unknown"}
end
local statusText = ""
local statusElement = doc:selectFirst(".serie-info-grid .sig-row:has(.sig-label:matchesOwn(^Status$)) .sig-value")
if statusElement ~= nil then
statusText = statusElement:text()
end
local status = ({
Ongoing = NovelStatus.PUBLISHING,
Completed = NovelStatus.COMPLETED,
})[statusText] or NovelStatus.UNKNOWN
local novelInfo = NovelInfo {
title = serie.data.title,
imageURL = serie.data.image,
description = doc:selectFirst(".description"):text(),
authors = authors,
status = status,
}
if isReleased then
local endNum = serie.chapter_count
local chaplist = baseURL .. 'api/chapters' .. "/" .. serie.raw_id.."?start=1&end=" .. endNum
local chapdoc = GETDocument(chaplist)
local chapterData = json.decode(chapdoc:selectFirst("body"):text())
local chapters = {}
for i, ch in ipairs(chapterData.chapters) do
chapters[#chapters+1] = NovelChapter {
title = ch.title,
link = "serie-" .. serie.raw_id .. "/" .. serie.slug .. "/chapter-" .. ch.order,
order = i
}
end
novelInfo:setChapters(chapters)
else
-- If the source is not released, set an empty chapter list
novelInfo:setChapters({})
end
return novelInfo
end
--- Search function.
local function search(data)
local query = data[QUERY]
local page = data[PAGE]
-- Use json.POST to send a POST request with JSON data
local doc = GETDocument(baseURL .. "novel-finder?text=" .. query .. "&page=" .. page)
local script = doc:selectFirst("#__NEXT_DATA__"):html()
local data = json.decode(script)
local serie = data.props.pageProps.series
-- Map the results to Novel objects
return map(serie, function(v)
return Novel {
title = v.data.title,
link = "serie-" .. v.raw_id .. "/" .. v.slug,
imageURL = v.data.image
}
end)
end
--- Listings configuration.
local listings = {
Listing("Popular Novels", true, function(data)
-- Retrieve filters from the data object
local page = data[PAGE]
local order = data[ORDER_FILTER_ID]
local orderValue = ""
if order ~= nil then
orderValue = ORDER_PERM[order + 1]
end
local sort= data[SORT_FILTER_ID]
local sortValue = ""
if sort ~= nil then
sortValue = SORT_PERM[sort + 1]
end
local status = data[STATUS_FILTER_ID]
local statusValue = ""
if status ~= nil then
statusValue = FILTER_PERM[status + 1]
end
local url = baseURL .. "en/novel-list?orderBy=" .. orderValue .. "&order=" .. sortValue .. "&status=" .. statusValue .. "&page=" .. page
local doc = GETDocument(url)
return map(doc:select(".serie-item"), function(el)
return Novel {
title = el:select(".title-wrap a"):text():gsub(el:select(".rawtitle"):text(), ""),
link = shrinkURL(el:select("a"):attr("href"), KEY_NOVEL_URL),
imageURL = baseURL .. el:select("div.image-wrap.zoom img"):attr("src")
}
end)
end),
Listing("Latest Novels", true, function(data)
local page = data[PAGE]
local url = baseURL .. "en/trending?page=" .. page
local doc = GETDocument(url)
return map(doc:select(".serie-item"), function(el)
return Novel {
title = el:select(".title-wrap a"):text():gsub(el:select(".rawtitle"):text(), ""),
link = shrinkURL(el:select("a"):attr("href"), KEY_NOVEL_URL),
imageURL = baseURL .. el:select("img"):attr("src"),
}
end)
end)
}
return {
id = id,
name = "WTR-LAB",
baseURL = baseURL,
imageURL = imageURL,
listings = listings,
getPassage = getPassage,
parseNovel = parseNovel,
shrinkURL = shrinkURL,
expandURL = expandURL,
hasSearch = hasSearch,
search = search,
searchFilters = searchFilters,
chapterType = ChapterType.HTML
}