diff --git a/src/zh/happymh/build.gradle b/src/zh/happymh/build.gradle index c33b7e535..8b96fab62 100644 --- a/src/zh/happymh/build.gradle +++ b/src/zh/happymh/build.gradle @@ -1,7 +1,7 @@ ext { extName = 'Happymh' extClass = '.Happymh' - extVersionCode = 17 + extVersionCode = 18 } apply from: "$rootDir/common.gradle" diff --git a/src/zh/happymh/src/eu/kanade/tachiyomi/extension/zh/happymh/Filters.kt b/src/zh/happymh/src/eu/kanade/tachiyomi/extension/zh/happymh/Filters.kt new file mode 100644 index 000000000..854f6a2be --- /dev/null +++ b/src/zh/happymh/src/eu/kanade/tachiyomi/extension/zh/happymh/Filters.kt @@ -0,0 +1,196 @@ +package eu.kanade.tachiyomi.extension.zh.happymh + +import eu.kanade.tachiyomi.source.model.Filter + +open class UriPartFilter( + name: String, + val key: String, + private val pairs: List>, +) : + Filter.Select(name, pairs.map { it.first }.toTypedArray()) { + val selected: String + get() = pairs[state].second +} + +class GenreFilter : UriPartFilter( + "分类", + "genre", + listOf( + "全部" to "", + "热血" to "rexue", + "格斗" to "gedou", + "武侠" to "wuxia", + "魔幻" to "mohuan", + "魔法" to "mofa", + "冒险" to "maoxian", + "爱情" to "aiqing", + "搞笑" to "gaoxiao", + "校园" to "xiaoyuan", + "科幻" to "kehuan", + "后宫" to "hougong", + "励志" to "lizhi", + "职场" to "zhichang", + "美食" to "meishi", + "社会" to "shehui", + "黑道" to "heidao", + "战争" to "zhanzheng", + "历史" to "lishi", + "悬疑" to "xuanyi", + "竞技" to "jingji", + "体育" to "tiyu", + "恐怖" to "kongbu", + "推理" to "tuili", + "生活" to "shenghuo", + "伪娘" to "weiniang", + "治愈" to "zhiyu", + "神鬼" to "shengui", + "四格" to "sige", + "百合" to "baihe", + "耽美" to "danmei", + "舞蹈" to "wudao", + "侦探" to "zhentan", + "宅男" to "zhainan", + "音乐" to "yinyue", + "萌系" to "mengxi", + "古风" to "gufeng", + "恋爱" to "lianai", + "都市" to "dushi", + "性转" to "xingzhuan", + "穿越" to "chuanyue", + "游戏" to "youxi", + "其他" to "qita", + "爱妻" to "aiqi", + "日常" to "richang", + "腹黑" to "fuhei", + "古装" to "guzhuang", + "仙侠" to "xianxia", + "生化" to "shenghua", + "修仙" to "xiuxian", + "情感" to "qinggan", + "改编" to "gaibian", + "纯爱" to "chunai", + "唯美" to "weimei", + "蔷薇" to "qiangwei", + "明星" to "mingxing", + "猎奇" to "lieqi", + "青春" to "qingchun", + "幻想" to "huanxiang", + "惊奇" to "jingqi", + "彩虹" to "caihong", + "奇闻" to "qiwen", + "权谋" to "quanmou", + "宅斗" to "zhaidou", + "限制级" to "xianzhiji", + "装逼" to "zhuangbi", + "浪漫" to "langman", + "偶像" to "ouxiang", + "大女主" to "danvzhu", + "复仇" to "fuchou", + "虐心" to "nuexin", + "恶搞" to "egao", + "灵异" to "lingyi", + "惊险" to "jingxian", + "宠爱" to "chongai", + "逆袭" to "nixi", + "妖怪" to "yaoguai", + "暧昧" to "aimei", + "同人" to "tongren", + "架空" to "jiakong", + "真人" to "zhenren", + "动作" to "dongzuo", + "橘味" to "juwei", + "宫斗" to "gongdou", + "脑洞" to "naodong", + "漫改" to "mangai", + "战斗" to "zhandou", + "丧尸" to "sangshi", + "美少女" to "meishaonv", + "怪物" to "guaiwu", + "系统" to "xitong", + "智斗" to "zhidou", + "机甲" to "jijia", + "高甜" to "gaotian", + "僵尸" to "jiangshi", + "致郁" to "zhiyu", + "电竞" to "dianjing", + "神魔" to "shenmo", + "异能" to "yineng", + "末日" to "mori", + "乙女" to "yinv", + "豪快" to "haokuai", + "奇幻" to "qihuan", + "绅士" to "shenshi", + "正能量" to "zhengnengliang", + "宫廷" to "gongting", + "亲情" to "qinqing", + "养成" to "yangcheng", + "剧情" to "juqing", + "轻小说" to "qingxiaoshuo", + "暗黑" to "anhei", + "长条" to "changtiao", + "玄幻" to "xuanhuan", + "霸总" to "bazong", + "欧皇" to "ouhuang", + "生存" to "shengcun", + "异世界" to "yishijie", + "其它" to "qita", + "C99" to "C99", + "节操" to "jiecao", + "AA" to "AA", + "影视化" to "yingshihua", + "欧风" to "oufeng", + "女神" to "nvshen", + "爽感" to "shuanggan", + "转生" to "zhuansheng", + "异形" to "yixing", + "反套路" to "fantaolu", + "双男主" to "shuangnanzhu", + "无敌流" to "wudiliu", + "性转换" to "xingzhuanhuan", + "重生" to "zhongsheng", + "血腥" to "xuexing", + "奇遇" to "qiyu", + "泛爱" to "fanai", + "软萌" to "ruanmeng", + "小天使" to "xiaotianshi", + "邪恶" to "xiee", + "後宫" to "hougong", + ), +) + +class AreaFilter : UriPartFilter( + "地区", + "area", + listOf( + "全部" to "", + "内地" to "china", + "日本" to "japan", + "港台" to "hongkong", + "欧美" to "europe", + "韩国" to "korea", + "其他" to "other", + ), +) + +class AudienceFilter : UriPartFilter( + "受众", + "audience", + listOf( + "全部" to "", + "少年" to "shaonian", + "少女" to "shaonv", + "青年" to "qingnian", + "BL" to "BL", + "GL" to "GL", + ), +) + +class StatusFilter : UriPartFilter( + "状态", + "series_status", + listOf( + "全部" to "-1", + "连载中" to "0", + "完结" to "1", + ), +) diff --git a/src/zh/happymh/src/eu/kanade/tachiyomi/extension/zh/happymh/Happymh.kt b/src/zh/happymh/src/eu/kanade/tachiyomi/extension/zh/happymh/Happymh.kt index d1c2041e5..4dfceb7d7 100644 --- a/src/zh/happymh/src/eu/kanade/tachiyomi/extension/zh/happymh/Happymh.kt +++ b/src/zh/happymh/src/eu/kanade/tachiyomi/extension/zh/happymh/Happymh.kt @@ -88,8 +88,8 @@ class Happymh : HttpSource(), ConfigurableSource { // Requires login, otherwise result is the same as latest updates override fun popularMangaRequest(page: Int): Request { - val header = headersBuilder().add("referer", "$baseUrl/latest").build() - return GET("$baseUrl/apis/c/index?pn=$page&series_status=-1&order=views", header) + val headers = headersBuilder().add("Referer", "$baseUrl/latest").build() + return GET("$baseUrl/apis/c/index?pn=$page&series_status=-1&order=views", headers) } override fun popularMangaParse(response: Response): MangasPage { @@ -110,8 +110,8 @@ class Happymh : HttpSource(), ConfigurableSource { // Latest override fun latestUpdatesRequest(page: Int): Request { - val header = headersBuilder().add("referer", "$baseUrl/latest").build() - return GET("$baseUrl/apis/c/index?pn=$page&series_status=-1&order=last_date", header) + val headers = headersBuilder().add("Referer", "$baseUrl/latest").build() + return GET("$baseUrl/apis/c/index?pn=$page&series_status=-1&order=last_date", headers) } override fun latestUpdatesParse(response: Response): MangasPage = popularMangaParse(response) @@ -119,22 +119,46 @@ class Happymh : HttpSource(), ConfigurableSource { // Search override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val body = FormBody.Builder() - .addEncoded("searchkey", query) - .add("v", "v2.13") - .build() + if (query.isNotEmpty()) { + val body = FormBody.Builder() + .addEncoded("searchkey", query) + .add("v", "v2.13") + .build() - val header = headersBuilder() - .add("referer", "$baseUrl/sssearch") - .build() + val headers = headersBuilder() + .add("Referer", "$baseUrl/sssearch") + .build() - return POST("$baseUrl/v2.0/apis/manga/ssearch", header, body) + return POST("$baseUrl/v2.0/apis/manga/ssearch", headers, body) + } + val url = "$baseUrl/apis/c/index".toHttpUrl().newBuilder() + filters.filterIsInstance().forEach { + if (it.selected.isNotEmpty()) { + url.addQueryParameter(it.key, it.selected) + } + } + val headers = headersBuilder().add("Referer", "$baseUrl/latest/${url.build().query}").build() + url.addQueryParameter("pn", page.toString()) + return GET(url.build(), headers) } override fun searchMangaParse(response: Response): MangasPage { + if (response.request.url.encodedPath.contains("/apis/c/index")) { + // for filter response + return popularMangaParse(response) + } return MangasPage(popularMangaParse(response).mangas, false) } + override fun getFilterList(): FilterList { + return FilterList( + GenreFilter(), + AreaFilter(), + AudienceFilter(), + StatusFilter(), + ) + } + // Details override fun mangaDetailsParse(response: Response): SManga = SManga.create().apply { @@ -173,7 +197,8 @@ class Happymh : HttpSource(), ConfigurableSource { if (it.isPageEnd()) { Observable.just(page to it) } else { - Observable.just(page to it).concatWith(fetchChapterByPageAsObservable(manga, page + 1)) + Observable.just(page to it) + .concatWith(fetchChapterByPageAsObservable(manga, page + 1)) } } } @@ -190,18 +215,11 @@ class Happymh : HttpSource(), ConfigurableSource { name = data.chapterName // create a dummy chapter url : /comic_id/dummy_mark/chapter_id#expect_page url = "/$comicId/$DUMMY_CHAPTER_MARK/${data.id}#${it.first}" - chapter_number = data.order.toFloat() } } } .toList() - .map { it.flatten().sortedByDescending { chapter -> chapter.chapter_number } } - .map { - // remove order mark - it.onEach { chapter -> - chapter.chapter_number = -1f - } - } + .map { it.flatten().reversed() } } override fun chapterListParse(response: Response) = throw UnsupportedOperationException() @@ -240,11 +258,11 @@ class Happymh : HttpSource(), ConfigurableSource { val code = fetchChapterCode(chapter) ?: throw Exception("找不到章节地址,请尝试刷新章节列表") val url = "$baseUrl/v2.0/apis/manga/reading?code=$code&v=v3.1818134" // Some chapters return 403 without this header - val header = headersBuilder() + val headers = headersBuilder() .add("X-Requested-With", "XMLHttpRequest") .set("Referer", baseUrl + chapter.url) .build() - return GET(url, header) + return GET(url, headers) } override fun pageListParse(response: Response): List { @@ -259,10 +277,10 @@ class Happymh : HttpSource(), ConfigurableSource { override fun imageUrlParse(response: Response): String = throw UnsupportedOperationException() override fun imageRequest(page: Page): Request { - val header = headersBuilder() + val headers = headersBuilder() .set("Referer", "$baseUrl/") .build() - return GET(page.imageUrl!!, header) + return GET(page.imageUrl!!, headers) } override fun setupPreferenceScreen(screen: PreferenceScreen) {