From 23543e9fb812c8781dfbc4f28d152f5dc906efc3 Mon Sep 17 00:00:00 2001 From: AwkwardPeak7 <48650614+AwkwardPeak7@users.noreply.github.com> Date: Fri, 19 Sep 2025 11:52:42 +0500 Subject: [PATCH] Remove Comick (#10571) --- src/all/comickfun/AndroidManifest.xml | 27 - .../assets/i18n/messages_en.properties | 33 - .../assets/i18n/messages_pt_br.properties | 33 - src/all/comickfun/build.gradle | 12 - .../comickfun/res/mipmap-hdpi/ic_launcher.png | Bin 4469 -> 0 bytes .../comickfun/res/mipmap-mdpi/ic_launcher.png | Bin 2297 -> 0 bytes .../res/mipmap-xhdpi/ic_launcher.png | Bin 6702 -> 0 bytes .../res/mipmap-xxhdpi/ic_launcher.png | Bin 13193 -> 0 bytes .../res/mipmap-xxxhdpi/ic_launcher.png | Bin 19130 -> 0 bytes .../extension/all/comickfun/Comick.kt | 773 ------------------ .../extension/all/comickfun/ComickFactory.kt | 62 -- .../all/comickfun/ComickUrlActivity.kt | 34 - .../tachiyomi/extension/all/comickfun/Dto.kt | 239 ------ .../extension/all/comickfun/Filters.kt | 208 ----- .../extension/all/comickfun/Helpers.kt | 85 -- 15 files changed, 1506 deletions(-) delete mode 100644 src/all/comickfun/AndroidManifest.xml delete mode 100644 src/all/comickfun/assets/i18n/messages_en.properties delete mode 100644 src/all/comickfun/assets/i18n/messages_pt_br.properties delete mode 100644 src/all/comickfun/build.gradle delete mode 100644 src/all/comickfun/res/mipmap-hdpi/ic_launcher.png delete mode 100644 src/all/comickfun/res/mipmap-mdpi/ic_launcher.png delete mode 100644 src/all/comickfun/res/mipmap-xhdpi/ic_launcher.png delete mode 100644 src/all/comickfun/res/mipmap-xxhdpi/ic_launcher.png delete mode 100644 src/all/comickfun/res/mipmap-xxxhdpi/ic_launcher.png delete mode 100644 src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Comick.kt delete mode 100644 src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFactory.kt delete mode 100644 src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickUrlActivity.kt delete mode 100644 src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Dto.kt delete mode 100644 src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Filters.kt delete mode 100644 src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Helpers.kt diff --git a/src/all/comickfun/AndroidManifest.xml b/src/all/comickfun/AndroidManifest.xml deleted file mode 100644 index 8d9f69c07..000000000 --- a/src/all/comickfun/AndroidManifest.xml +++ /dev/null @@ -1,27 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - diff --git a/src/all/comickfun/assets/i18n/messages_en.properties b/src/all/comickfun/assets/i18n/messages_en.properties deleted file mode 100644 index a76283014..000000000 --- a/src/all/comickfun/assets/i18n/messages_en.properties +++ /dev/null @@ -1,33 +0,0 @@ -ignored_groups_title=Ignored Groups -ignored_groups_summary=Chapters from these groups won't be shown.\nOne group name per line (case-insensitive) -preferred_groups_title=Preferred Groups -preferred_groups_summary=Chapters from these groups will have priority over others (Even if lower score).\nOne group name per line (case-insensitive) -ignored_tags_title=Ignored Tags -ignored_tags_summary=Manga with these tags won't show up when browsing.\nOne tag per line (case-insensitive) -show_alternative_titles_title=Show Alternative Titles -show_alternative_titles_on=Adds alternative titles to the description -show_alternative_titles_off=Does not show alternative titles to the description -include_tags_title=Include Tags -include_tags_on=More specific, but might contain spoilers! -include_tags_off=Only the broader genres -group_tags_title=Group Tags (fork must support grouping) -group_tags_on=Will prefix tags with their type -group_tags_off=List all tags together -update_cover_title=Update Covers -update_cover_on=Keep cover updated -update_cover_off=Prefer first cover -local_title_title=Translated Title -local_title_on=if available -local_title_off=Use the default title from the site -score_position_title=Score Position in the Description -score_position_top=Top -score_position_middle=Middle -score_position_bottom=Bottom -score_position_none=Hide Score -cover_quality_title=Cover Quality -cover_quality_original=Original -cover_quality_compressed=Compressed -cover_quality_web_default=Small (Web Default) -chapter_score_filtering_title=Automatically de-duplicate chapters -chapter_score_filtering_on=For each chapter, only displays the scanlator with the highest score -chapter_score_filtering_off=Does not filterout any chapters based on score (any other scanlator filtering will still apply) diff --git a/src/all/comickfun/assets/i18n/messages_pt_br.properties b/src/all/comickfun/assets/i18n/messages_pt_br.properties deleted file mode 100644 index d34a64f43..000000000 --- a/src/all/comickfun/assets/i18n/messages_pt_br.properties +++ /dev/null @@ -1,33 +0,0 @@ -ignored_groups_title=Grupos Ignorados -ignored_groups_summary=Capítulos desses grupos não serão mostrados.\nUm nome de grupo por linha (não diferencia maiúsculas de minúsculas) -preferred_groups_title=Grupos Preferidos -preferred_groups_summary=Capítulos desses grupos terão prioridade sobre os outros (Mesmo que com nota mais baixa).\nUm nome de grupo por linha (não diferencia maiúsculas de minúsculas) -ignored_tags_title=Tags Ignoradas -ignored_tags_summary=Mangás com essas tags não aparecerão ao navegar.\nUma tag por linha (não diferencia maiúsculas de minúsculas) -show_alternative_titles_title=Mostrar Títulos Alternativos -show_alternative_titles_on=Adiciona títulos alternativos à descrição -show_alternative_titles_off=Não mostra títulos alternativos na descrição -include_tags_title=Incluir Tags -include_tags_on=Mais específicas, mas podem conter spoilers! -include_tags_off=Apenas os gêneros básicos -group_tags_title=Agrupar Tags (necessário fork compatível) -group_tags_on=Irá prefixar tags com o respectivo tipo -group_tags_off=Listar todas as tags juntas -update_cover_title=Atualizar Capas -update_cover_on=Manter capa atualizada -update_cover_off=Preferir a primeira capa -local_title_title=Título Traduzido -local_title_on=se disponível -local_title_off=Usar o título padrão do site -score_position_title=Posição da Nota na Descrição -score_position_top=Topo -score_position_middle=Meio -score_position_bottom=Final -score_position_none=Esconder Nota -cover_quality_title=Qualidade da Capa -cover_quality_original=Original -cover_quality_compressed=Comprimida -cover_quality_web_default=Pequena (Padrão Web) -chapter_score_filtering_title=Desduplicar capítulos automaticamente -chapter_score_filtering_on=Para cada capítulo, exibe apenas o scanlator com a maior nota -chapter_score_filtering_off=Não filtra nenhum capítulo com base na nota (outros filtros de scanlator ainda se aplicarão) diff --git a/src/all/comickfun/build.gradle b/src/all/comickfun/build.gradle deleted file mode 100644 index 456ed8196..000000000 --- a/src/all/comickfun/build.gradle +++ /dev/null @@ -1,12 +0,0 @@ -ext { - extName = 'Comick' - extClass = '.ComickFactory' - extVersionCode = 62 - isNsfw = true -} - -apply from: "$rootDir/common.gradle" - -dependencies { - implementation(project(":lib:i18n")) -} diff --git a/src/all/comickfun/res/mipmap-hdpi/ic_launcher.png b/src/all/comickfun/res/mipmap-hdpi/ic_launcher.png deleted file mode 100644 index f8a3106a53797a446c1e38cf96435739d9f3cbfa..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 4469 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 Eg3uhYF8}}l diff --git a/src/all/comickfun/res/mipmap-xxhdpi/ic_launcher.png b/src/all/comickfun/res/mipmap-xxhdpi/ic_launcher.png deleted file mode 100644 index 9057665f98a8b3190b31228c9182bbbc8eef82b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 13193 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 a{zr5fIE(Sr0bgHi0Mg!rGrsW_N67SJjZD z+@J%2Td^zcJZ?9UIxJeq21ZB`4fl(s5-lKr7EC>XL|V8<3ZUWnRvp!h=8p;8zyetX z8DG-8-RAzi+u{G)?k_CJ>t92EFP(=11a6nJPnU(nCLOw#TJ+FUm+B|N{D0n-aWWON zk~)5zy`@2DXioRPSJywr|3*;|b$snUx?-`-L>1B`C3>iieBQXK_?p|Bb40RevRLx{ z1r3JO%6YK-u9gCzlY_Ac@XnNAkP~#gIUFAz2aTW1{-#M<#EKH#GECqQcXVVHAw@mb z3;$bI^r|y#wiIh6|7D3))OD6GTCjiC4_(t)aMiXnxd^gC4$OiL(L7o6zP$3@eyD*S zMZw*%7>&-biq6g^NKa0Fr{m*$y!HC>@3v$&za#c9#+aJ^sFb4mS*7lVE{*O_vrIW) z#&0%FM0ciB*JE6+vi6gj2~2WSQbJA+yxHS5Ue9Z+)aDD8%ZdZ)RYv%#K?!wfDxw+P4 zDGu0G+%xRfRt3uE_i`ZZppw}8t8%*oAQk8q?lthO%WoBFngorVC(idutQcf~;HpB5 zc4*Y?Ce)bWWB{a>667y#{u$P+ab~9kygSmhfX`CN(U2E1UmhH$ zDZ-tiln5M!)C0lp&+7;|tdJ0ySX%XhJ%m3briDIgRu39ve#kqwn=RGyAGs9TBhv2H z>`>&0^G0mhh<7w|pU*ZV%l z=#bSmYT18xi@XIFs8%%i@zCN6<(4jn=I`e{Z$PT@wVVxsJ5)BMZOy1Z)51+7l7B*a znAawTD2?K;bOlY)?eH-0cY~;==-v?r-ocme0Q)`1jlqK!2hn+`0W!zyI@{_PN8`Hf zVM0^gD=BpX4>#GJ2gFDWA(XcKXWbGDa9iXTUoG9U{SS3^+WnDBeK z{1YZrYmFpl={hhJRYmf8OocXcu@UT&0U=^?)pOp=wgQr*MRa&bk3sRDuN9y^H4*$s zAb<=Y_ml=v_i)#GBS9CKRa!F%&Fqq+$-;D*=pOF7%%>@d$(td z_0^5nfuO}IA%_KpNvB(h;g5qzEpR*-X2{jtrwy*4>SqmD>GW~9>pVJw$-yO_gB7(E ze1MddQlJp*w>bIw*`#EVyn64aOCg3zv#(n9n+WX(HKf1~CO0D!;P%Cskeo02f+py z-s$YH0!ae_I+iG3_pFxb(tvEp9hjca>MKNkpC7qEm!27ewAso%s~+JL??eg4gp&+t zV(;fyc2+4^oa#%#>?|-Z69sgk+ z6W!j{=rD!=R1kNag#k!*-gX@Hnnt1RIuEH45F^f4RU-RpwyK$K(vOY)1OTc3?C{Q|Cr%Kf-% z>H}&NQMb;F1a=4H^ie_sVq2Oy+!yiYDwC7!8#j=c!#T3=b2jd;=kD;e@8X{18i_?F z651W5w0PMuN}cR?0AiM1M-E03Slf0NFde4VLzo*sZtozXUv1`$2BL#N!Rqb{>P#qi0w3X4XIBv4BB_L z%+4DeJKmCixS143@A9~51w1q%!@~~;Q^Pxa?zvP;wsCM9tn6$ZuM|KXFqf0q;daN` z!RVIukA6=;%8dVbz|xVV$TV+-bR_A;oGm?7d*>x_NvWw}|1+)a`3xFf)iXweZCG6; zqh~@bP}E!5cJyp9S}iR9qn=wBG~a;3?e)-#Q?PZW9_)ICAPv~gyt9&o$(_drX6wvw zh7fJAEPO3mxiPx#X$Oaf_RIYgK66Z&9cjlfMBl65o#W(j{|a4|^YJ~8b}0HGKv$YB zf;fPlh$!5{Io^ExX)FcX6Q*K%$J9F^WiQ%TI#Bj`e45?z^*3YudUSn+>{3J=UH)0fdvn5 zWYADQ$IXY7^LmTW+_&=`fV8$lqL!JgK53rc`~qWSLU$3fjO;&tN@;uH69f;9gMe&& zNRF)pZncB~DpKr9^FrX}MI77js@-?FUNGU`(OR&L5+Lm!@EkAW)%@a~aO$S}uYWxw z{w0H1%36lbcfAEZ>)*KvjojzCEeEeB+CicUq#rs)u-Nz!BWygknp`$l8`&hee=Cz# z>FHM}x%~IxWMO-vWT`QqIihrtC{gC02U<~zOF6F>m23JTH@{Al^$_rRA+A378yt?@ zjw5SSEh`@Ge;)sbN0$>bP)x2;p@7HD#tdPF(*1)+m&<1$?Gm;J4NZV? z1&Xx0kiUoqJ~yx?vNT$x?WcH}r9*Eg6}AS0}g8IgASf$~I4k-*QZW(AwRW+YWERvV2!Y zEI?*YrQ>{%BPY*JS=EbdT1H!&z)7*o0~Gg$oV)i~ROWtp2L}e0hl*c068tM^bSGn; z5UTUG!1O_iN~4ep4r2r!n^Ih&ScK%%a(tqd_GUOwTz%b;lefNc8|A>>F;D^_{2UE9 zi3*>!hr05(Y!jW~8o8Lo;tS*HYGrp#GC}^FN&U;Cc{mylHx&@Qc;n*eOP2c|O2yqB zkpJ!o02B8PPMnwK{(Q_imE9f)9hVCP23#5zQ>pU=ST4ln$9tP;}Ks3nNZ56xmBS`_EQGLI;h+H700)TF}BvM(-K0hs4VkfPPCDG+5gMV2HC4iVq!E6Hkcdg7bP?T(C( zXRP)WC1oOKZWVe!rS`tP2bVK61V`I5!mvPTbq=Slj>4i3Kbkx(Pz=gk={f@-eYzz2 zlxt8hq)yw$l7?uzOm$D!33E8_QNKN|S8$rE*M^ENm2XAN_Q;t)dN=M~tl5V+%L7um zpcM|QLYZ4E3Oak9AfGvKaZ8Yic!z7Yk$2V}+F@W3)sRF97N%wqYdGOZ{1Pfg656Q_ z*kYpr;W!+|@={U)-r5L!oqo~m({?1+{Z0cS;xvek`1qV~*b~Uz&7a}X@8I~YkVU>V z0rkR`G}bVxukxI$HY>i|pFYY_o4ktP}tS7MBYs zKQg|9va9dhsW1Dbo0Nw%FlR(5LE>OUxsIoT?Hqdgy#$yt225HNq)WkF=-=Nr%pebs ziy8{bf_8X&clC~Vu3~+0EO9j4K9aL{Yg&#n-dg-kMiL$lv+~y!ObUx3@;Y3CgNuvA ziwzszJ@|RoEF9cjE9>}UcWA;MO@QicwgJ^0SbnV0N%&>%J@Q4t^~39nC)$LTjhZ`Y zbp>dai)OxI!0dR)P{c}KyDUu0#fl4xqaXH5d)kpMCY{KrxWCjSk~U>qY=xnKu4(}{ z*iA--w09bh-Puw20r6_FkdHMKeH_;pLgkk1xvSNzkR?eRoRK+`{{#Y*8KXzm`%0y(K<31n(aVhCsvDzbYp zW2Yr%p1{2Ao#_V%)aTwXgPE$E2d2N>|M+TKw|GoaNog`2`)DE?lOVo7H+#wgjD3BW z6^c=&5B0{q7^O(C`|s~=g@_Kmf~)is3m>gF^PO?V|v7c=)~XWHqwF}0PALS0&x$^=VrfE(2i)1Vrkb{kO!i4FeNKee z2nMl%Kp-sCEvWBkQCYD<1Spp6YsaTQQEbxUeL(~ttJR^;oCK){@iLhPczsDfdjF3O z?9WvV$xwA!;^>}`nIGhiP5Wn92`$Dh)T7*``L-F9YwhW%2)YeDP=z`11j-3BmPRay z%}BAq(Tbz(K*clQPh9Y`x`-dF=zSWBh!3o&j0#m>&^s#8UflHzG7P+LT$~*-H7Va6 z$9~UJ4V4OG`{j!w-T^t25L!tK{$Y4-%5bAAMWlzp(LtU<)gNqva~lJ)G=sfp)|JjH z$Smn;+Y3gEcZC3dHssQd>dD%CZSn69UndCAAu!XacAA~%#~*U~t}Rc@_aoort!v^q zMZ!h_)NTL+>l@{z_NlKbg~SLrOOrNz|FdG} zOt+er!~j}wsQBJoO8d&!vW9kxH}=g9Y&7GD@k{>3=A)E;9v3qUG)EGDE; zE8luFTOt^yAc7h4=g1AGWN11$6CcIOj&h*31!-sd8v=AF{cqQC?Kn@V8f_x;`39y6 zwaW0lt&rW!NeWqVnyaaFJwcnTlUzVLB%@F>doX!qI;Jj6#UBb%-JCT1xOhs^Tlo18 zy-r_v&xDTOje>vF%b=I0ZO0a{p`p1a(pgY8yX;Csoevyde7+>aC(uXj2WKf9pcs>Oa2g-8`2woYi`tWty|sa~GqH;gbtlH{dWukPZkII9jBN zi_`C)zuTNoKR&vmM2T100OH>loc;b^o3lh` z;-9sPi?7^#;ivksBtsz@Zf^XAS}lkqU)*RV3f8`iq=2REr~bprW_9+GwFSYVo6f(c z74R?RbBiv}_FiG6$^!%SK}?n+{37CmJ3YI~9KN>%xja4r@?c=$QOc0hCp6Ac*7xC` z=4*eDtZks&8xVRD+`=Ym^I1Jh$pA*3+?gm$Vw(m3DjxiF+6lGX-be5KYSEfz>$mOB z>#QcDU&AAxC*nQUf&-3M54k(F9}hCDaQJO7`Jh7@xOBLihE6dXlXs^w&({C#%SY#M zAV)+**xx#fP6}=pNpwoZk6Ggzt7e9IKVe64SczP>vP*ISjGhpfK0!El4E8^+BOcmUrab z+PQ*{Ao}q>GlXQj86l!fo+toS_>FO0L@z=7rZs(K>PhT__!n!O4v9#s_MpL5e|F5)5>q}`6@}{ZfSe5_nbN?zF%ikwtOvbdp!<^(}3D`!?TCO4|Ju~ zpkU%+EVX%FsdknB)Kd@hPB3vQVRLX@q47f$@3-J54F%Ax&cF<*%0~%puh8)3en3@Q z#|_0nakj3pX|Q9pJ70b}*78zq>IYDWe1Ji7B`QQ@X1iNNe1EChtL15j`tjomhKSd< zMrD&K-ge*aE~B~+TJ7-jq}Rs83qI%F5-BNy-2Y}_dRRXCg$NBRR#F+8=~Ci{{Rf+o zLX$7?lBjw(|B_7Q@aNU#Q6S-&S&?=|q2^{K+TAT{d%b5r-@w2?fkTkyvk%A;6TA3P zlR>8|1XxtMd!gk=QLw_vEp%y(j1SGt;b9u4P9r*!B_AFaE9xx4VMunON|LXeW2xS3 zN$&`nDt8*4T)z+f29H%+_oHNj+PSn2-^D8d&H(*=^s5QmR_9mvMk{Zf?*orJcFC~X zb5wrLe)WKsAV_467wnmLIR`s@`Ya#$+?xxy?Rv6rj&aNAux6;;>WZ(F@hj6UBZOIX zy+q=|xGHZGp%)B})nsa+_js`rQ#t5<613G1I)0c(fQ0-}S(rSXMtu0X^v3cC` z3`tHw-Z!)qT0YIkSG!6;uh$x}!VwZ%qp>gy;n>n{FxwGfzAd(!F~^#>5mHhA28BV? zX>lRMVxgi%nLdK#@pcJe{ry1i?*-J2$qPwOpp-fG)ZxWkq;5i$Aytdf%e%1_ zD_^8dL6%N@BGzs}Hq;roUHF%RL%S!m!DexcuRAa7lIMdDM^TO@ws=dBJ zKRJfYeHd^fHYs-iT%TV7#SKe8Jv>7`#hqFwib9l#F}r( z8nh@$O3CzWj(c4{eXgFn3BBjNy7si7@&{^%XNL5ZnYhxUNFR6hs7y^Sa@!x1zkXN6 zy^9ov-o9V80EP&Q=!4_gZYIkG8I=(=K$6x!@qlyk)`iv@37PWV)fV1%rVRFBnU%C$l@rr&NO)9C-%B^v?&F|Nw6Zey-uYzK(f~uhpGbgrYjTZZf;wDz z%zsf7a}6%$-APFf_XnH^I6M&!ZhNPB+uN++bVD_iDUL~~o;yFlKOppJQ+fq$lM2Vr z1bE=?X^+9e*0x9j^@H=0gOkb_^v{D^=`P??G8btLU-m5QT_#IK6D;4%0`T=-C35j8 z=TQOsDLmyJFVFe)7v+2@FVahfS6yW13S;fw|NXsst91M$@C907=}t*>aYT^W zpKN9+>K6Zc!mD@Fv-&$f?NZ{gsjt?|L5^3}*Im6G!3pmt>h4Z{Zf&y?MDXG17N8z? zkuQ9FlHTsI!7nTgaN_Lz$s#N0X;OtG>e}_@Kl)$l&STEo`9HM~O`X}lcL;}!YVy^C zAw}aj4yn|t7_B@`JMSf0;Zad$G&7_9S&Cf*q7jZ33W)*5#sl5Y;puO72^aOcy3`e97LUj)N;QG}| zP?V4P&B4FJH`Q60)L}#Ve?ACs78~rpWBa4lLus!s%t})M-dQ%ISAP+AcTrdBUff)v z5|Lu8tqHF`{F|s0WOg$O+&{NXSpc|w!RJ<~@}fzhto2@3sLy^d!9hi2KOWDJnv?O> z;4agG)4l9a?rMKNElx%cA~~)s|AH+q>!OmK|DA(F6!Pc4xOkk`LHnL82}q54?K;{t zVncE6oFHD0(;(AoKM@6cs;KB3RvVVJW}6`u4wq9bW;IgWHOPtmZD_JlZco|p&rA-_DfzL*+PL0`a}jF!KJM>RWOWU`7ii91q{(EIPjK`VhPdi3ehw-kv&7`Ff2@x zf3k$j(G-^KvQlD>hG=98rLhsB@hj^93_~E6MLEyw5E6WpK!VZ(3MS!XU!^?pW`H*h z<#0^2Yb5riRSmO#BaBZk0#b<>>+^Nv>O2sA@$5rY;q!bJ#`xBgb{Eo+&d*FLy%q0mw>EKLbR^L z2<7SC)0fqVW2lvjWFI4dH8^+2pkD8=Vc6(+AhwcPJwDEllYas=3O+xoZ9mZo1BOUD zOnr3lKyOdPvwtx+AE1mKxmhkba7`+G^2R2U0tN;+s&bX^^+WHP+IlV7J!;kLE2C{>Ma2X?REK&emC#cQ4B)sftO z=?f9IpIqQ)rfeb@?Wm@4t@GmfUTLY8*_xzem=evWeH$e=lHp$%a3acLu%gOJF65U} zC|2ZMd&<}^93}M<*k`C_+9?mCB!45uqpX5-^9*jT~syrFvUPqYYxMZ5>`Z!VF%n%&A&NE%dcAg7!<**m!ib!QeU)Xo;OSW1F6 zuAZx~HK3mA;GOOxM~dP5daBdu3ti29FDzqC*Aa!;Hyfk{nf=_5!|G;U{rJrZCl2p5 zY2+fG{2jExdc}$PA@jd>BHUlbkROp&6}m4NsPu{&vHZJ5APx36OrrtXjqUnd(Fw;h zH4$8Bks|RD7cN7PHzz|C*^BY?b|;=foHaF`%XV9$S+Nk;hte3gpy?ndN^1`!hyUjV zz>tJ=#^l@7qY^#Nviqik1@P>pxe5nmd?(-|$H~u6)|XH-HPdhhV``dK97;ZH6SjRY zJ%g*LPG+$pAf(BrQ%PeHz*^jVePpVc_mKbKo=fc}3G7UsPIK`>7glnO7|F5#$-O4%KL60{c6p3Y;##0TXaD!zqwe0yig(duq4OTom%Hwcdb ziQh`lSc)v1M|9CtCKF)HoYKz-Hexr`Hfdos<*V@g_|0{hY*I-T>}Pt2aQ-v&4>(D0 z5bje;1HuSAJ3e!U)Dz^LDJp|+emnt(WHX1i_Qw+1akvn*IfyDh>6%i@S89IJn}PoWR>?ru#|q6@3COdKf<@hgI*P19X|A5nXEIg z$insF-uo~)cBw#sbmiPPBpp0|0xbfUz1v&l-ex1wZzcdtq!uMiH|XM`cS@vb@wpW!=^fEF=|-%>9wc>Usb2aigt zHY=aG!>IBn4!sIgsc7jeIgyo;3GZLih!G4XNs+3zJ#_wSu_!nOo=G|L~rw;JibVWhaBwaoqF z6*b+?$9tRAYpM?Nx{0H+rWytg&a}2W`vXM7%~GjS$(`wvVWD7z_<9eHf5c)IXN2<9 z?(PZw9cC8V=bf?D_1uH)KTTINli$p)uc@D(a!F0vEDe<6(C$-dBNoR^3%xlX&XMnV z=t`^$?wOp4+x+tAaacR}lfvY9lG(TW+3g4 zc{m00eV#p}ZSHBd4Ep7bgJO%jGiOcSW|c%hZa=15?YSA4<7;Gkxex}0Wou>ZkOY^` zT_L1~&yUYOX8n25{BOOmwV>e zOMIMNC+cqeL1OR>$36P7y1Gax<|ukMd=jki=YMr_+fxc%pgTL|}9`+EK7334`!d$MKKDh`O-q z2?RGvJc@qEmeVDx%baL6$?7XG9{qYpd_yKu=IfBH*jk9_pDh=b=AEYV@wt?Dn!5gW z&6Ht}jXtww?WwZaeL!^EtiVe74#w+|3V$;L#n!DjI@B7=*~1*A<=v#+yB&f@JAw&a zmtwoaF^u6<7pHiBJ^C#L6VYRqG=}hPHbyKYZxO>|?%vap`o>7~+c+?~rLg~EM-Nwv zw7iORa)m(sYVzBBX71oKO^nI`1(mh_J=5vDS8CauZW%sPxkZ2qmg5*H7tiCktpx}h zp=b84X7!5ucme(@2f9K zP9LO~6Xfpb7hE4Nmq3}rn!kmGFWKAK4<-(Z`}@4%Cx6Tegj!< zB<(Omlcqe5y)1DhCDQj?o&Ig;BhneYUBvp7Q4(}%6jc2Fcp5KP?Dv1(aF(jIFwny) z#BKDjMedHOReuph&JCheB6`?~WjU^a36_46lB3_ShRIv+IY23A#slV1REfM=`2g9` z-dW6AhGlPme?`Ga97&*?PY0M*4nq3Sr0mj=0>5@r$i<`a82sS(?VD(=T(P23iA=7D zlX~zKDL`|Hag_J{dB_kkZDY&kf_5{Vr6=w9**-IuSu2tCl8z9!~SMW_W8L($XSNY$%fBkFX}lM zvdPxk`eG-8o1Y*j=jQ!}cVKX+!71)US*%d2j1BL#`(;D#7>}6_tV3=T-gtCUMS6Pj zFk$u%Mq9`be92~IQYls$i(QD>bA-!pckJ6dgwfCn0#O8iD@WEk3t(VZd5p!*|C)rm z*AVRw2c00r#dc&RQ1(<%BG5z=XjHmn9Nyck8>C##Kh@|haD(k5qihTxnWOoT6HB>- zyOXx$bhFObKL=APn{TKU5_EZ4=m@|91jAwCjJy1N1}FQ`p`tK`t+vZx-tPlcX?3d@ zkERI5k%pC?6xna_X_bFALAwY_KMjG*((7o7~Y1gbi^F4mdSn`;q!HR1w&+o1vUty+$No(W8#m4kH7>_sdaY-5%QIh z6wu6-ccIJpQGR6Pl2+?B5lE%b1#aXFW zge`^l=bOP_oQg~z;Etb+pp$=HAhv2xF{`+pGohu`ZmF*Sl=6GlnSV{^b)q}Bp?*Rw zOFh|^ElAWg=jH1dWrpkl7?97MffIFtc|RX-xA;8q*>1eYg$Vz_Y}=tv=sxPqW1v${^(4Ja=tm^v{@Zg6>`ciehMK!AI9UG3-GvoMLQfw*VePGzrYz}B;PZa! z4tu_*MhT03k13nhJ>KA~>5Nf{474S3f4$+oOP%r38%DMjwD5e_gbY2vXS?cs9)-s# zt5W{U@zZY^NuIAuTwjD^ir+7Ay;hHu4fr6b1SLz`LH`<`S1jl1CGO6}-QPb7MI<*h za(;*o&G=Pd~D|gMd#8sK@E|+m00eq87rXsvPX;%1Mdy z)NXTxwpzOC&G4%UAetx=Dwuvbx-u`@jxd&HMR3E z4-7|6GjVUj@7U5!!a@UAe{&Nq87?j&!@?&T4CG^qz+JqIjjca@Hho1cBVpt9 z{goyKOq0Nw8>g5{F3T`Cg|iY|{1;yItZ@^KUaK&p&)2JDIL)w`n_` z3H*(Q~oXN6gCi1v{frsga5A0Mucu(xIY$3 z$ct;d^I^w?VlIWO^QX6?=?VV(Mt~7b>>1N@#(tF%Ia?{FL-2mZcK48U*m@Jo~6MSBwn6wUMy!Gw|h`XMIP@Lve>F7O!z zFA~`R_>6-Zo8H1vs!Kuslaz@w;v+AF3P(Mh-i1bwNFL#M9@XRSuy2CXYUgQ5uiO{& zek_cSm%G3Uw8WJw^zqo`E9{l}3A^*68r3HFu!^b(gE&F6BBS4*z*-W{EzBC9PGF*C~~~^dONE?-=T9b8`o$z zllLKjV=P&Kp=>6G_BM;%N(Dj|@AvRY^=Y{$CL!^HP8ywjkK9mTw!){l^P->U4wn)8p}Kb>~v)e7e!kiNShw5ROpF zG$>?{u-)i^zs%uyTPw0dKv_KCa-sq+2O637axL*xWrP-asX9G;t|XE<&0O7`yzpBQ zqCSTI?EFGtx7!>B<&y-5hzZ$}86n#!q;f@07bJ44!TvR00OS966QWHY64MBw+`bYg zGJ&Eo(ZO#>PO)w{7>cIeS&zK8haq$}h!u)}w)7iLy;3b*(Y&fOt`a_tYS=P0TQs4pTEzP! z5fu6UyrdJ$)AaqDlbHKubUke`B7lgAB4OmW7~I*sRfaV;kHwLm#2xwhE@mFE=6UGr;uDoL{~~GY}AOF3Q46ZFn3`mr?|rkL-zPNeX=3*r~4P0YaQS zGtPtFJj!T24A13yG+_2hMtA@CaU4`@RM5DWCW9+!c~dg+-pG=;g=y5L2ZhsT8-(?+ z{)pR1doB+?_u9->9dWOW?Yp4)mp;+a3}NBB9`(FlRNg%1>@~a{xxmjhDdYc7eLZ8p zFb$UE`m7x4pooE&-l7|aD(04Qb zdk|s=j)X|FGkJZ|q*BLvU+!l$iG6WJhTZtC{QZ&Uom)4)|BYw*cLI19LP>eEh*psh z5zTOmv)C#^J)39>7TsL*&B|I>7*aR|P4|mSY+3B);gjw1q*j$=nQV-$7O1b_Tj>Hi zw<-QL2T7!5CeA6#WD|tRWA=_ca64S>Qa`G}Y2$PLB^Ha$fhCopaW<57V%MfCx+|j| zUq0s9MDIB>Q(K`;cy;!a29s9%x{qHE96Y=379Q64BxZge3DMZl;+5gNc0w_o{=zJq z);8Rj<2Iu|f3qNODC>A{**kZjdwb)^I-Q2_?fDzgzw;+i<6>bOo5}bMXz~`eitrFIWmK7X=IAsggE1^bVdgzM$1(rH?c@5xtyQ7B5ZQ{dAwk&MsuHOi zBR@gW>sUqf{AK&wB5})ISSYlbHV%6H&u+19c3$ui_T;6pG)^!rc1@%DSIM2zTYBn* z40g@8;+6lGDpknQ=#A6u6Hgd?uC}~Notbc(wYqFDBrfM2dY^LAtjm>j3B7-~DA??D zvgTVuo$+1-U#hV$cwcF9fs#s5C>A3pC4+FECib%+1%5!s(P}5j zo*LYAL}(5X10|Mog*BY1UO?+=f1*utl!$za=o}>_VIpC~KTMoZaPj*M&Hve|M=t%s zV}9C8CG-m%`Zfy~NL=n_VAnG#!oVXMl(mw>*5s^4p4UvpuNw!cOW_tT%2yEcd&Iit z_yvAJi}v8@gr6+fa%nEP3GZ9-)iujPS|8Fy;g{$}N7why&WqMLfP6PPoctxBN!e<_ zUmQN~uk4*BV|l%h4UUgb&WTuZ5I0)fQINP0E$^ItjWQBT&dfN$NdRz|jo1v^1F08& zB;N)PF@y_vt{atKd|kCnrtbtiJ~c4GmC4;CzGt(@t=X7-zHp-X#`Sb5dnZLJ4*O_t zUI~4=9M8j^uCvBHJ2&4sDA-%O_|yiKUK18+pU}jr+ojRLN=8c>T9}B)Oveqt*;*GNrlK<}$_RlZm4t58BKJx0@iYZ3n}2w4Int^QQ_3-oL%;=ANlnV!R zP(76@-u%`St}1Pl(DIV%OwBI-z|i;7n3+}tTG20!R z_SW5N|JQkQV?=@v>e+IkQu{~#)?LrVPu#fsW@`~!yQ-I(3^)RR_%_$7|8?d)PC#ft zK298;cELB$&k>`$j)8-xi7GOaO+qCgjQBBrL@KyX0|XLFg0=lq|7XbRnwBahB*a6s z(HZL>_bKyny4pm6HHn+cY=f#CGD5XPC&D40`%<_b*RSzOinlIi8wY2SRcN)I8XlXZ z{}HR|{&opOL&tPQ)KS;?XAXCJI9VONNX%#q{&>z?WnZ027aHWXZ1KmpkK?OZ%og~T zy$j<%ePw5nZzYu&m1f6Slp;j&vpKFYXwM%{^nc%N(Y9h=tJ69R*PI(Bhy#g(4*cAHngB#T3hM) zy}Wa;R*c=Ak{!d4V`O^HUw|wvOzbx zB}$><3gknHR5IDq)xgOx@(RW4(6+pRWYeYgS_tS#Za?Nq_UK|a@|4F?g*qrDYN$ZJ zcjLPgLxd!OvknDTM`ovKTZ>Mk9uq;$eHQcW{^+zQg9 zG9DDYb7c2cuF*eBlegJ@Ale1;H@5f1aLb9Ncy_8Ie1ZJ2-5;LOyoq!A&p^w945Rii z8nv#XGI@+BCQ|rv?Q$X%2`d3xxW6SJ9?-C`z;01GeV%fGj`f95ie1*!hp_*7{Gr!r z9ZdB5aQlM2Q=bjm>W;<@cD2bY-T+N{XY-Gv0f@_%DJgtEZ1BgCrM6P7ACb&8O2OL? z507?6v~w^ROEV+L*??ev{2uUtS7QXwV=6^DUE~QYz-<1V0nXc?zz5+=;@P z;}k!6+oRAk*~=qRqJ}HV`MGRJ9HSyB;w5Z=DVz;oR#**`7?;WqGH3l_)B0D0GcGc6 zj*4isa6Wa76c1ERj-PbW0D>^gL_KlL2;FXvu-MHX0nS!6Gdde=CbWhUA47F1V;Y&l zr%y)iIKEqrP}2|?5e0wZ^}1vpu4W6@d49sGM1hxs<%NOj{Q61A2WSz@X=+xF5em6| z&({J&_?^n3FbjpUv{xydBM=W9&7gpF`+*Dp1+_eL3=C7~gK~q^JG^4-(k%m73kg;9kku_1YC7_sgN2agMc(Vr3n>W-`rm14Gq22xI(p z2ou`O%^kBc;M>dBw>2vp(5@}Ug-GZ7a85Htr0qXV>@83`#(0kzv2&S zE|VD52(h89vvYpX6L{{NQf!P$+C4x@$BeX#Itvr`97r}iwKH<#^qij9nQ@jkv_s#{ z)8eCa;lrTgW7W5evazr{u6B3kEB_=`$H^w3=^l0*(oZ%#9T>g$hMZS!qEvJapZxnk zxrOwPjP1UEU&(2a`%5X+?;2cV%PudUaX}LBa{1hC zTcUSlcDdR1bO6+^!$1`QPp)rTyv&O1vq4?ToXRmUMr=wL2XG8+|WRV04HiHgahbNVQai93}jm z4zHxg)&d1amDuGISS6MJOtST8FjH}3&VWEZB(P8UdJh9F5>D3Xu9}B>EnPaqNGb~S zU{W1;RESAEwQ|i?0{^gLepPnIXgOE`F?!iGc>O3-u++M(d2r3$yNH_v= zPm-T}&lC`U!wEAWCv>n6=YcOez#X#Jhz)12CWi` zq63>dwsq7pF?zk>mclciVh6iGNQaPNMDie{BJd%M8$EwpvNPj;Twexx9c{jy`8qbY@w(H8pd z_dp)=>eCq-?XRfEU4Bk>nS>->QuuRYD^q|C-7Io2Y zZb9QQm{Da#8cA(q@(Zyw`g4*DCvRJWaJxHQh?cfW+t{^C-uDPto1>T+Dv^m4k>6b; zcz={DX-=2Qhx&5AiQ3eWQnjVW`RYF5Kpso=IG!`C}!dS|aR2tpQrdGNBDc6AV=X69q}yW(yDMVP<; z{)K07n0PXFWt>N+`421WAI;@`1y+T;(ZSdWnB7(1`BK_;mRr>rar^Jaszr?wW4v!o zAmZU=U3ODnM;8Uoe|{p)=#SrN5K3~i;cJ?&8UKA+G5Pt+KI8s5-NM=y(mFeXn(pAi zP}%&tZ;&ro2$w?Z^%L<(7G+xIt8%Ltt;y@7)Z!WNR2njux2+5zq;yO~Kf5R#eBV}w zv#*al8fdALxTNyzd*PZ93UG#);;4u4fXC(J0d*x=f4E0bQ)VkYA{ zRPvu+1)}t;-$|sb5)9&>^%l$}r9Zy`062^P{TCpLEha9i|E;<9=JSG9Bs7d>v)CAZ zBJTs9%f-Baay+YkW)$aPo)_$x8+zt2uNiXW#rlVBbo>mXOin8TrM#ZP#+v0O#{5r2 zBbbVE+drQ2xjbG0Ox+U(o}9cyyygPP2)M;9!ZOH?$OK%lX|+^5{1w@(T5M*{Co6d0 zwlz^;t+)ABDZ5j*(suhldhv$}%qEz8rH520ScPg=VPiLGiobO;Bpa=+s16R1!(38n zG@;+Kt==jxV|M-j1icAD_M;Cs(mer?5v(na)0VUI5fX9W-FyGS$~D^%5fRB;j-y8m zKzE(lkyCpkqFK6f6ZRiC!MJ#y+@4r6e-f%xDaR^3ed>$W-}g`P`^(_rnHwMd-!dbY zE5z+dNB`@{-|SD6hySnr8?`R$lUg=m@_ZclOOflx)!hSyik3jO>~2gIdg=2FpFX^0 zW041G!q`D*)2gvisvIAjGQ#%^Kmb?1N?H_SPB`hnXv&PGc=cM5%Dhp7nk-osC7;#g z+|(~A+N9f`IDLVsK3qWExY0kMf1fVOSf0hpHn2==RJ;m%v)2|QBg66b)eC%hul}$$ z)ghPVip5jWqIp9dUYlfGxps%8Y9#k26}oXIx3N`Rx`~cr1~(nBb?wv&V@D2Tgv?ir zwGH_C`_|vB1YqP2&~u@)=KhMq#}pixQiPv5whOvWPF3YpOmqyEt=ht}Oi-b-f#Zj_ zsg3^g(8C08B0na>&JHmu?L4lv}J&ZDVEe*@`&|jt@0RD3T%YggQ6Cdo^`xjhXobclh-y>&E3UV{PXmQqLh03yE z(Hd;sxnJ2K>Fh)&BErX$atJfs>Gs9?76I zgLbwyT3pa?ub*B1 zD%qjAjW=X7nDWZAzWxj`?x>g;96fdxYu4>RP*5=Q_TbcJ z&*%h`?g_x}`4t`K0ACO6!W0Z6=3_*M6ebZI62dyChqdK0sSyL2UNy71It@W0LlA)J zTdBV#pUq$dp6VP#myU1DI?O%kHWrDEt#8a)(sSJ`O8}aiVlKk#rULV}UWj+cyb-Le zGaUiw)wxslT9vb!qSI>;PD*wMHIA)e*cyXosq+^`lKLc>&@r=R=09_&wbdfUa`@OX%+UHrvoSfaal<5QNchlbDA1#v;9y z;~Ui^Ji@x6<+TkDrOK z)`CmPvIL+8!!%>cY1Xj?Q6Ot~m0k&eUYes<;Ou>$TzqM4#s$?ot(NxQ2s#H)f5Fr! z3^P|}x2ikB!A;S(MM~ByMDLXYh^A{7bdQK$xJ`*PG@65%y7knKeki2^1-uXe&`l8g z`?pTsadvUiovBsmT8&CJx_gCJGvVaaRjMEd%nnqogI>EJaeACJ4^_%y%>Nn}qIXw~pC8lH&VE49F<7?Hd zG)U(%O~XCYA*O8k%?7r>+^t=35V&&f?%E%E4_~N30F;8`tE)gXCsKcirIHH75rEy$ zp}i{&?A7g{kcr-Zq4cqs&U%h(UofQ3_RG@eO z-;4k_8-U*XAp)@B4B`F1uU%WYV%bJtYcq@uNc&pzvW{ap2;9E=;B3ckgGjUB3}DI- zrbz%s4``|pOapNQK$3t6fb#^|_3qiF{J_3l{zplnELAMc>G^C1W9MzIjbQ9}S*bpy zo!qqb&kkF5?4=alsvaOB08)QA7076?m`ecEo+JSMaGoIM4qY;DVvpLjt4(6r6f9J% z-Wv-6G6Qu(&%s{4d~@E|N%ME6s6OQWfszWu@)Vo0EK3joNdQg_Qc3|*hgL0} zF{pCIvV&A50iTX-?ujfP5G>89tvWs*#7+NBod5RS2Wy9qnXy&{`8of;+4?`H#+W`s zbv1}P58!M8@(S54oc&AJiWSR^&*|Z=HaQxIo zdo?&e>Hezzf0NZ8OA&ykF-%f`h=2%yer&pSYF)BL^ZEmG=klya*_a(2>|tXoPt(M% zY0c@aL}WQapcCHMm{^t>J3KssvS?p8e)9Z=y?-8g41f@xR37@_kl!NQ-x36%X$+GT zP?ZFX08;D&d5`YxOH`{|uA!S-_VRYNwgsdzDgWcPmV>jkr*sN|g!qKe=$IJ4prDZd zZrr|iZr7fJPg1x%ae5B%bs@i5+~1M}z=cL|1~KOqB5A-$1o|8O5P?uV?i_(@n@+dK zI;}K<0B5vv1}q^n8MI{B5{IWB4(W*y=!fI{s)nyg#xJ)fbBthBxTwYkaq&Ui0U1XO z9P+CY07nFtBE~e@D{HrD00C73;9Qv;0dPdXAw3~J{f!HJGx_9Ka~+l<01kmw&jyIg zb1D%>3>@-v5s7eA=X+VUa`NI*5l>o9X zU2yupsvV$;?3#$LN;DRMT3T^kRl`=**wr+At;lbYZOpKpXd1$*grG?r7DxWHk^q_p zwkncqa{aWPLx#nSEHrUdRYnu(2@Gk1_?Gqp8QL#ZDWi|$r`E@#kuqYfvrB1@ z1gr$`4S;SPGW|fnN&x9+FRU~91`x0kz&8N8b;$Gs0V@HdpS`fo - - private val intl by lazy { - Intl( - language = lang, - baseLanguage = "en", - availableLanguages = setOf("en", "pt-BR"), - classLoader = this::class.java.classLoader!!, - ) - } - - private val preferences by getPreferencesLazy { newLineIgnoredGroups() } - - override fun setupPreferenceScreen(screen: PreferenceScreen) { - EditTextPreference(screen.context).apply { - key = IGNORED_GROUPS_PREF - title = intl["ignored_groups_title"] - summary = intl["ignored_groups_summary"] - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putString(IGNORED_GROUPS_PREF, newValue.toString()) - .commit() - } - }.also(screen::addPreference) - - EditTextPreference(screen.context).apply { - key = PREFERRED_GROUPS_PREF - title = intl["preferred_groups_title"] - summary = intl["preferred_groups_summary"] - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putString(PREFERRED_GROUPS_PREF, newValue.toString()) - .commit() - } - }.also(screen::addPreference) - - EditTextPreference(screen.context).apply { - key = IGNORED_TAGS_PREF - title = intl["ignored_tags_title"] - summary = intl["ignored_tags_summary"] - }.also(screen::addPreference) - - SwitchPreferenceCompat(screen.context).apply { - key = SHOW_ALTERNATIVE_TITLES_PREF - title = intl["show_alternative_titles_title"] - summaryOn = intl["show_alternative_titles_on"] - summaryOff = intl["show_alternative_titles_off"] - setDefaultValue(SHOW_ALTERNATIVE_TITLES_DEFAULT) - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putBoolean(SHOW_ALTERNATIVE_TITLES_PREF, newValue as Boolean) - .commit() - } - }.also(screen::addPreference) - - SwitchPreferenceCompat(screen.context).apply { - key = INCLUDE_MU_TAGS_PREF - title = intl["include_tags_title"] - summaryOn = intl["include_tags_on"] - summaryOff = intl["include_tags_off"] - setDefaultValue(INCLUDE_MU_TAGS_DEFAULT) - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putBoolean(INCLUDE_MU_TAGS_PREF, newValue as Boolean) - .commit() - } - }.also(screen::addPreference) - - SwitchPreferenceCompat(screen.context).apply { - key = GROUP_TAGS_PREF - title = intl["group_tags_title"] - summaryOn = intl["group_tags_on"] - summaryOff = intl["group_tags_off"] - setDefaultValue(GROUP_TAGS_DEFAULT) - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putBoolean(GROUP_TAGS_PREF, newValue as Boolean) - .commit() - } - }.also(screen::addPreference) - - SwitchPreferenceCompat(screen.context).apply { - key = FIRST_COVER_PREF - title = intl["update_cover_title"] - summaryOff = intl["update_cover_off"] - summaryOn = intl["update_cover_on"] - setDefaultValue(FIRST_COVER_DEFAULT) - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putBoolean(FIRST_COVER_PREF, newValue as Boolean) - .commit() - } - }.also(screen::addPreference) - - ListPreference(screen.context).apply { - key = COVER_QUALITY_PREF - title = intl["cover_quality_title"] - entries = arrayOf( - intl["cover_quality_original"], - intl["cover_quality_compressed"], - intl["cover_quality_web_default"], - ) - entryValues = arrayOf( - "Original", - "Compressed", - COVER_QUALITY_DEFAULT, - ) - setDefaultValue(COVER_QUALITY_DEFAULT) - summary = "%s" - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putString(COVER_QUALITY_PREF, newValue as String) - .commit() - } - }.also(screen::addPreference) - - SwitchPreferenceCompat(screen.context).apply { - key = LOCAL_TITLE_PREF - title = intl["local_title_title"] - summaryOff = intl["local_title_off"] - summaryOn = intl["local_title_on"] - setDefaultValue(LOCAL_TITLE_DEFAULT) - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putBoolean(LOCAL_TITLE_PREF, newValue as Boolean) - .commit() - } - }.also(screen::addPreference) - - ListPreference(screen.context).apply { - key = SCORE_POSITION_PREF - title = intl["score_position_title"] - summary = "%s" - entries = arrayOf( - intl["score_position_top"], - intl["score_position_middle"], - intl["score_position_bottom"], - intl["score_position_none"], - ) - entryValues = arrayOf(SCORE_POSITION_DEFAULT, "middle", "bottom", "none") - setDefaultValue(SCORE_POSITION_DEFAULT) - - setOnPreferenceChangeListener { _, newValue -> - val selected = newValue as String - val index = findIndexOfValue(selected) - val entry = entryValues[index] as String - - preferences.edit() - .putString(SCORE_POSITION_PREF, entry) - .commit() - } - }.also(screen::addPreference) - - SwitchPreferenceCompat(screen.context).apply { - key = CHAPTER_SCORE_FILTERING_PREF - title = intl["chapter_score_filtering_title"] - summaryOff = intl["chapter_score_filtering_off"] - summaryOn = intl["chapter_score_filtering_on"] - setDefaultValue(CHAPTER_SCORE_FILTERING_DEFAULT) - - setOnPreferenceChangeListener { _, newValue -> - preferences.edit() - .putBoolean(CHAPTER_SCORE_FILTERING_PREF, newValue as Boolean) - .commit() - } - }.also(screen::addPreference) - } - - private val SharedPreferences.ignoredGroups: Set - get() = getString(IGNORED_GROUPS_PREF, "") - ?.lowercase() - ?.split("\n") - ?.map(String::trim) - ?.filter(String::isNotEmpty) - ?.sorted() - .orEmpty() - .toSet() - - private val SharedPreferences.preferredGroups: Set - get() = getString(PREFERRED_GROUPS_PREF, "") - ?.lowercase() - ?.split("\n") - ?.map(String::trim) - ?.filter(String::isNotEmpty) - .orEmpty() - .toSet() - - private val SharedPreferences.ignoredTags: String - get() = getString(IGNORED_TAGS_PREF, "") - ?.split("\n") - ?.map(String::trim) - ?.filter(String::isNotEmpty) - .orEmpty() - .joinToString(",") - - private val SharedPreferences.showAlternativeTitles: Boolean - get() = getBoolean(SHOW_ALTERNATIVE_TITLES_PREF, SHOW_ALTERNATIVE_TITLES_DEFAULT) - - private val SharedPreferences.includeMuTags: Boolean - get() = getBoolean(INCLUDE_MU_TAGS_PREF, INCLUDE_MU_TAGS_DEFAULT) - - private val SharedPreferences.groupTags: Boolean - get() = getBoolean(GROUP_TAGS_PREF, GROUP_TAGS_DEFAULT) - - private val SharedPreferences.updateCover: Boolean - get() = getBoolean(FIRST_COVER_PREF, FIRST_COVER_DEFAULT) - - private val coverQuality: CoverQuality - get() = CoverQuality.valueOf( - preferences.getString(COVER_QUALITY_PREF, COVER_QUALITY_DEFAULT) ?: COVER_QUALITY_DEFAULT, - ) - - private val SharedPreferences.localTitle: String - get() = if (getBoolean( - LOCAL_TITLE_PREF, - LOCAL_TITLE_DEFAULT, - ) - ) { - comickLang.lowercase() - } else { - "all" - } - - private val SharedPreferences.scorePosition: String - get() = getString(SCORE_POSITION_PREF, SCORE_POSITION_DEFAULT) ?: SCORE_POSITION_DEFAULT - - private val SharedPreferences.chapterScoreFiltering: Boolean - get() = getBoolean(CHAPTER_SCORE_FILTERING_PREF, CHAPTER_SCORE_FILTERING_DEFAULT) - - override fun headersBuilder() = Headers.Builder().apply { - add("Referer", "$baseUrl/") - add("User-Agent", "Tachiyomi ${System.getProperty("http.agent")}") - } - - override val client = network.cloudflareClient.newBuilder() - .addNetworkInterceptor(::errorInterceptor) - .addInterceptor(::imageInterceptor) - .rateLimit(5, 6, TimeUnit.SECONDS) // == 50req each (60sec / 1min) - .build() - - private val imageClient = network.cloudflareClient.newBuilder() - .rateLimit(7, 4, TimeUnit.SECONDS) // == 1.75req/1sec == 14req/8sec == 105req/60sec - .build() - - private val smallThumbnailClient = network.cloudflareClient.newBuilder() - .rateLimit(14, 1, TimeUnit.SECONDS) - .build() - - private fun imageInterceptor(chain: Interceptor.Chain): Response { - val request = chain.request() - val url = request.url.toString() - - return if ("comick.pictures" in url && "-s." in url) { - smallThumbnailClient.newCall(request).execute() - } else if ("comick.pictures" in url) { - imageClient.newCall(request).execute() - } else { - chain.proceed(request) - } - } - - private fun errorInterceptor(chain: Interceptor.Chain): Response { - val response = chain.proceed(chain.request()) - - if ( - response.isSuccessful || - "application/json" !in response.header("Content-Type").orEmpty() - ) { - return response - } - - val error = try { - response.parseAs() - } catch (_: Exception) { - null - } - - error?.run { - throw Exception("$name error $statusCode: $message") - } ?: throw Exception("HTTP error ${response.code}") - } - - /** Popular Manga **/ - override fun popularMangaRequest(page: Int): Request { - return searchMangaRequest( - page = page, - query = "", - filters = FilterList( - SortFilter("follow"), - ), - ) - } - - override fun popularMangaParse(response: Response): MangasPage { - val result = response.parseAs>() - return MangasPage( - result.map(SearchManga::toSManga), - hasNextPage = result.size >= LIMIT, - ) - } - - /** Latest Manga **/ - override fun latestUpdatesRequest(page: Int): Request { - return searchMangaRequest( - page = page, - query = "", - filters = FilterList( - SortFilter("uploaded"), - ), - ) - } - - override fun latestUpdatesParse(response: Response) = popularMangaParse(response) - - /** Manga Search **/ - override fun fetchSearchManga( - page: Int, - query: String, - filters: FilterList, - ): Observable { - return if (query.startsWith(SLUG_SEARCH_PREFIX)) { - // url deep link - val slugOrHid = query.substringAfter(SLUG_SEARCH_PREFIX) - val manga = SManga.create().apply { this.url = "/comic/$slugOrHid#" } - fetchMangaDetails(manga).map { - MangasPage(listOf(it), false) - } - } else if (query.isEmpty()) { - // regular filtering without text search - client.newCall(searchMangaRequest(page, query, filters)) - .asObservableSuccess() - .map(::searchMangaParse) - } else { - // text search, no pagination in api - if (page == 1) { - client.newCall(querySearchRequest(query)) - .asObservableSuccess() - .map(::querySearchParse) - } else { - Observable.just(paginatedSearchPage(page)) - } - } - } - - private fun querySearchRequest(query: String): Request { - val url = "$apiUrl/v1.0/search?limit=300&page=1&tachiyomi=true" - .toHttpUrl().newBuilder() - .addQueryParameter("q", query.trim()) - .build() - - return GET(url, headers) - } - - private fun querySearchParse(response: Response): MangasPage { - searchResponse = response.parseAs() - - return paginatedSearchPage(1) - } - - private fun paginatedSearchPage(page: Int): MangasPage { - val end = min(page * LIMIT, searchResponse.size) - val entries = searchResponse.subList((page - 1) * LIMIT, end) - .map(SearchManga::toSManga) - return MangasPage(entries, end < searchResponse.size) - } - - private fun addTagQueryParameters(builder: Builder, tags: String, parameterName: String) { - tags.split(",").filter(String::isNotEmpty).forEach { - builder.addQueryParameter( - parameterName, - it.trim().lowercase().replace(SPACE_AND_SLASH_REGEX, "-") - .replace("'-", "-and-039-").replace("'", "-and-039-"), - ) - } - } - - override fun searchMangaRequest(page: Int, query: String, filters: FilterList): Request { - val url = "$apiUrl/v1.0/search".toHttpUrl().newBuilder().apply { - filters.forEach { it -> - when (it) { - is CompletedFilter -> { - if (it.state) { - addQueryParameter("completed", "true") - } - } - - is GenreFilter -> { - it.state.filter { it.isIncluded() }.forEach { - addQueryParameter("genres", it.value) - } - - it.state.filter { it.isExcluded() }.forEach { - addQueryParameter("excludes", it.value) - } - } - - is DemographicFilter -> { - it.state.filter { it.state }.forEach { - addQueryParameter("demographic", it.value) - } - } - - is TypeFilter -> { - it.state.filter { it.state }.forEach { - addQueryParameter("country", it.value) - } - } - - is SortFilter -> { - addQueryParameter("sort", it.getValue()) - } - - is StatusFilter -> { - if (it.state > 0) { - addQueryParameter("status", it.getValue()) - } - } - - is ContentRatingFilter -> { - if (it.state > 0) { - addQueryParameter("content_rating", it.getValue()) - } - } - - is CreatedAtFilter -> { - if (it.state > 0) { - addQueryParameter("time", it.getValue()) - } - } - - is MinimumFilter -> { - if (it.state.isNotEmpty()) { - addQueryParameter("minimum", it.state) - } - } - - is FromYearFilter -> { - if (it.state.isNotEmpty()) { - addQueryParameter("from", it.state) - } - } - - is ToYearFilter -> { - if (it.state.isNotEmpty()) { - addQueryParameter("to", it.state) - } - } - - is TagFilter -> { - if (it.state.isNotEmpty()) { - addTagQueryParameters(this, it.state, "tags") - } - } - - is ExcludedTagFilter -> { - if (it.state.isNotEmpty()) { - addTagQueryParameters(this, it.state, "excluded-tags") - } - } - - else -> {} - } - } - addTagQueryParameters(this, preferences.ignoredTags, "excluded-tags") - addQueryParameter("tachiyomi", "true") - addQueryParameter("limit", "$LIMIT") - addQueryParameter("page", "$page") - }.build() - - return GET(url, headers) - } - - override fun searchMangaParse(response: Response) = popularMangaParse(response) - - /** Manga Details **/ - override fun mangaDetailsRequest(manga: SManga): Request { - // Migration from slug based urls to hid based ones - if (!manga.url.endsWith("#")) { - throw Exception("Migrate from Comick to Comick") - } - - val mangaUrl = manga.url.removeSuffix("#") - return GET("$apiUrl$mangaUrl?tachiyomi=true", headers) - } - - override fun fetchMangaDetails(manga: SManga): Observable { - return client.newCall(mangaDetailsRequest(manga)) - .asObservableSuccess() - .map { response -> - mangaDetailsParse(response, manga).apply { initialized = true } - } - } - - override fun mangaDetailsParse(response: Response): SManga = - mangaDetailsParse(response, SManga.create()) - - private fun mangaDetailsParse(response: Response, manga: SManga): SManga { - val mangaData = response.parseAs() - if (!preferences.updateCover && manga.thumbnail_url != mangaData.comic.cover) { - val coversUrl = - "$apiUrl/comic/${mangaData.comic.slug ?: mangaData.comic.hid}/covers?tachiyomi=true" - val covers = client.newCall(GET(coversUrl)).execute() - .parseAs().mdCovers.reversed() - val firstVol = covers.filter { it.vol == "1" }.ifEmpty { covers } - val originalCovers = firstVol - .filter { mangaData.comic.isoLang.orEmpty().startsWith(it.locale.orEmpty()) } - val localCovers = firstVol - .filter { comickLang.startsWith(it.locale.orEmpty()) } - return mangaData.toSManga( - includeMuTags = preferences.includeMuTags, - scorePosition = preferences.scorePosition, - showAlternativeTitles = preferences.showAlternativeTitles, - covers = localCovers.ifEmpty { originalCovers }.ifEmpty { firstVol }, - groupTags = preferences.groupTags, - titleLang = preferences.localTitle, - coverQuality = coverQuality, - ) - } - return mangaData.toSManga( - includeMuTags = preferences.includeMuTags, - scorePosition = preferences.scorePosition, - showAlternativeTitles = preferences.showAlternativeTitles, - groupTags = preferences.groupTags, - titleLang = preferences.localTitle, - coverQuality = coverQuality, - ) - } - - override fun getMangaUrl(manga: SManga): String { - return "$baseUrl${manga.url.removeSuffix("#")}" - } - - /** Manga Chapter List **/ - override fun chapterListRequest(manga: SManga): Request { - // Migration from slug based urls to hid based ones - if (!manga.url.endsWith("#")) { - throw Exception("Migrate from Comick to Comick") - } - - val mangaUrl = manga.url.removeSuffix("#") - val url = "$apiUrl$mangaUrl".toHttpUrl().newBuilder().apply { - addPathSegment("chapters") - if (comickLang != "all") addQueryParameter("lang", comickLang) - addQueryParameter("tachiyomi", "true") - addQueryParameter("limit", "$CHAPTERS_LIMIT") - }.build() - - return GET(url, headers) - } - - override fun chapterListParse(response: Response): List { - val chapterListResponse = response.parseAs() - - val preferredGroups = preferences.preferredGroups - val ignoredGroupsLowercase = preferences.ignoredGroups.map { it.lowercase() } - - val mangaUrl = response.request.url.toString() - .substringBefore("/chapters") - .substringAfter(apiUrl) - - val currentTimestamp = System.currentTimeMillis() - - // First, apply the ignored groups filter to remove chapters from blocked groups - val filteredChapters = chapterListResponse.chapters - .filter { - val publishTime = try { - publishedDateFormat.parse(it.publishedAt)!!.time - } catch (_: ParseException) { - 0L - } - - val publishedChapter = publishTime <= currentTimestamp - - val noGroupBlock = it.groups.map { g -> g.lowercase() } - .intersect(ignoredGroupsLowercase) - .isEmpty() - - publishedChapter && noGroupBlock - } - - // Now apply the primary filtering logic based on user preferences - val finalChapters = if (preferredGroups.isEmpty()) { - // If preferredGroups is empty, fall back to the existing score filter - filteredChapters.filterOnScore(preferences.chapterScoreFiltering) - } else { - // If preferredGroups is not empty, use the list to grab chapters from those groups in order of preference - val chaptersByNumber = filteredChapters.groupBy { it.chap } - val preferredFilteredChapters = mutableListOf() - - // Iterate through each chapter number's group of chapters - chaptersByNumber.forEach { (_, chaptersForNumber) -> - // Find the chapter from the most preferred group - val preferredChapter = preferredGroups.firstNotNullOfOrNull { preferredGroup -> - chaptersForNumber.find { chapter -> - chapter.groups.any { group -> - group.lowercase() == preferredGroup.lowercase() - } - } - } - - if (preferredChapter != null) { - preferredFilteredChapters.add(preferredChapter) - } else { - // If no preferred group chapter was found, fall back to the score filter - val fallbackChapter = chaptersForNumber.filterOnScore(preferences.chapterScoreFiltering) - preferredFilteredChapters.addAll(fallbackChapter) - } - } - preferredFilteredChapters - } - - // Finally, map the filtered chapters to the SChapter model - return finalChapters.map { it.toSChapter(mangaUrl) } - } - - private fun List.filterOnScore(shouldFilter: Boolean): Collection { - if (shouldFilter) { - return groupBy { it.chap } - .map { (_, chapters) -> chapters.maxBy { it.score } } - } else { - return this - } - } - - private val publishedDateFormat = - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.ENGLISH).apply { - timeZone = TimeZone.getTimeZone("UTC") - } - - override fun getChapterUrl(chapter: SChapter): String { - return "$baseUrl${chapter.url}" - } - - /** Chapter Pages **/ - override fun pageListRequest(chapter: SChapter): Request { - val chapterHid = chapter.url.substringAfterLast("/").substringBefore("-") - return GET("$apiUrl/chapter/$chapterHid?tachiyomi=true", headers) - } - - override fun pageListParse(response: Response): List { - val result = response.parseAs() - val images = result.chapter.images.ifEmpty { - // cache busting - val url = response.request.url.newBuilder() - .addQueryParameter("_", System.currentTimeMillis().toString()) - .build() - - client.newCall(GET(url, headers)).execute() - .parseAs().chapter.images - } - return images.mapIndexedNotNull { index, data -> - if (data.url == null) null else Page(index = index, imageUrl = data.url) - } - } - - private inline fun Response.parseAs(): T { - return json.decodeFromString(body.string()) - } - - override fun imageUrlParse(response: Response): String { - throw UnsupportedOperationException() - } - - override fun getFilterList() = getFilters() - - private fun SharedPreferences.newLineIgnoredGroups() { - if (getBoolean(MIGRATED_IGNORED_GROUPS, false)) return - - val ignoredGroups = getString(IGNORED_GROUPS_PREF, "").orEmpty() - - edit() - .putString( - IGNORED_GROUPS_PREF, - ignoredGroups - .split(",") - .map(String::trim) - .filter(String::isNotEmpty) - .joinToString("\n"), - ) - .putBoolean(MIGRATED_IGNORED_GROUPS, true) - .apply() - } - - companion object { - const val SLUG_SEARCH_PREFIX = "id:" - private val SPACE_AND_SLASH_REGEX = Regex("[ /]") - private const val IGNORED_GROUPS_PREF = "IgnoredGroups" - private const val PREFERRED_GROUPS_PREF = "PreferredGroups" - private const val IGNORED_TAGS_PREF = "IgnoredTags" - private const val SHOW_ALTERNATIVE_TITLES_PREF = "ShowAlternativeTitles" - const val SHOW_ALTERNATIVE_TITLES_DEFAULT = false - private const val INCLUDE_MU_TAGS_PREF = "IncludeMangaUpdatesTags" - const val INCLUDE_MU_TAGS_DEFAULT = false - private const val GROUP_TAGS_PREF = "GroupTags" - const val GROUP_TAGS_DEFAULT = false - private const val MIGRATED_IGNORED_GROUPS = "MigratedIgnoredGroups" - private const val FIRST_COVER_PREF = "DefaultCover" - private const val FIRST_COVER_DEFAULT = true - private const val COVER_QUALITY_PREF = "CoverQuality" - const val COVER_QUALITY_DEFAULT = "WebDefault" - private const val SCORE_POSITION_PREF = "ScorePosition" - const val SCORE_POSITION_DEFAULT = "top" - private const val LOCAL_TITLE_PREF = "LocalTitle" - private const val LOCAL_TITLE_DEFAULT = false - private const val CHAPTER_SCORE_FILTERING_PREF = "ScoreAutoFiltering" - private const val CHAPTER_SCORE_FILTERING_DEFAULT = false - private const val LIMIT = 20 - private const val CHAPTERS_LIMIT = 99999 - } -} diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFactory.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFactory.kt deleted file mode 100644 index 79aae75d6..000000000 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickFactory.kt +++ /dev/null @@ -1,62 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.comickfun - -import eu.kanade.tachiyomi.source.Source -import eu.kanade.tachiyomi.source.SourceFactory - -// A legacy mapping of language codes to ensure that source IDs don't change -val legacyLanguageMappings = mapOf( - "pt-br" to "pt-BR", // Brazilian Portuguese - "zh-hk" to "zh-Hant", // Traditional Chinese, - "zh" to "zh-Hans", // Simplified Chinese -).withDefault { it } // country code matches language code - -class ComickFactory : SourceFactory { - private val idMap = listOf( - "all" to 982606170401027267, - "en" to 2971557565147974499, - "pt-br" to 8729626158695297897, - "ru" to 5846182885417171581, - "fr" to 9126078936214680667, - "es-419" to 3182432228546767958, - "pl" to 7005108854993254607, - "tr" to 7186425300860782365, - "it" to 8807318985460553537, - "es" to 9052019484488287695, - "id" to 5506707690027487154, - "hu" to 7838940669485160901, - "vi" to 9191587139933034493, - "zh-hk" to 3140511316190656180, - "ar" to 8266599095155001097, - "de" to 7552236568334706863, - "zh" to 1071494508319622063, - "ca" to 2159382907508433047, - "bg" to 8981320463367739957, - "th" to 4246541831082737053, - "fa" to 3146252372540608964, - "uk" to 3505068018066717349, - "mn" to 2147260678391898600, - "ro" to 6676949771764486043, - "he" to 5354540502202034685, - "ms" to 4731643595200952045, - "tl" to 8549617092958820123, - "ja" to 8288710818308434509, - "hi" to 5176570178081213805, - "my" to 9199495862098963317, - "ko" to 3493720175703105662, - "cs" to 2651978322082769022, - "pt" to 4153491877797434408, - "nl" to 6104206360977276112, - "sv" to 979314012722687145, - "bn" to 3598159956413889411, - "no" to 5932005504194733317, - "lt" to 1792260331167396074, - "el" to 6190162673651111756, - "sr" to 571668187470919545, - "da" to 7137437402245830147, - ).toMap() - override fun createSources(): List = idMap.keys.map { - object : Comick(legacyLanguageMappings.getValue(it), it) { - override val id: Long = idMap[it]!! - } - } -} diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickUrlActivity.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickUrlActivity.kt deleted file mode 100644 index fc7d912c0..000000000 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/ComickUrlActivity.kt +++ /dev/null @@ -1,34 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.comickfun - -import android.app.Activity -import android.content.ActivityNotFoundException -import android.content.Intent -import android.os.Bundle -import android.util.Log -import kotlin.system.exitProcess - -class ComickUrlActivity : Activity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - val pathSegments = intent?.data?.pathSegments - if (pathSegments != null && pathSegments.size > 1) { - val slug = pathSegments[1] - val mainIntent = Intent().apply { - action = "eu.kanade.tachiyomi.SEARCH" - putExtra("query", "${Comick.SLUG_SEARCH_PREFIX}$slug") - putExtra("filter", packageName) - } - - try { - startActivity(mainIntent) - } catch (e: ActivityNotFoundException) { - Log.e("ComickFunUrlActivity", e.toString()) - } - } else { - Log.e("ComickFunUrlActivity", "could not parse uri from intent $intent") - } - - finish() - exitProcess(0) - } -} diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Dto.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Dto.kt deleted file mode 100644 index 01ab8946e..000000000 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Dto.kt +++ /dev/null @@ -1,239 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.comickfun - -import eu.kanade.tachiyomi.extension.all.comickfun.Comick.Companion.GROUP_TAGS_DEFAULT -import eu.kanade.tachiyomi.extension.all.comickfun.Comick.Companion.INCLUDE_MU_TAGS_DEFAULT -import eu.kanade.tachiyomi.extension.all.comickfun.Comick.Companion.SCORE_POSITION_DEFAULT -import eu.kanade.tachiyomi.extension.all.comickfun.Comick.Companion.SHOW_ALTERNATIVE_TITLES_DEFAULT -import eu.kanade.tachiyomi.source.model.SChapter -import eu.kanade.tachiyomi.source.model.SManga -import kotlinx.serialization.SerialName -import kotlinx.serialization.Serializable -import java.math.BigDecimal -import java.math.RoundingMode - -@Serializable -class SearchManga( - private val hid: String, - private val title: String, - @SerialName("md_covers") val mdCovers: List = emptyList(), - @SerialName("cover_url") val cover: String? = null, -) { - fun toSManga() = SManga.create().apply { - // appending # at end as part of migration from slug to hid - url = "/comic/$hid#" - title = this@SearchManga.title - thumbnail_url = parseCover(cover, mdCovers) - } -} - -@Serializable -class Manga( - val comic: Comic, - private val artists: List = emptyList(), - private val authors: List = emptyList(), - private val genres: List = emptyList(), - private val demographic: String? = null, -) { - fun toSManga( - includeMuTags: Boolean = INCLUDE_MU_TAGS_DEFAULT, - scorePosition: String = SCORE_POSITION_DEFAULT, - showAlternativeTitles: Boolean = SHOW_ALTERNATIVE_TITLES_DEFAULT, - covers: List? = null, - groupTags: Boolean = GROUP_TAGS_DEFAULT, - titleLang: String, - coverQuality: CoverQuality = CoverQuality.Compressed, - ): SManga { - val entryTitle = comic.altTitles.firstOrNull { - titleLang != "all" && !it.lang.isNullOrBlank() && titleLang.startsWith(it.lang) - }?.title ?: comic.title - val titles = listOf(Title(title = comic.title)) + comic.altTitles - - return SManga.create().apply { - // appennding # at end as part of migration from slug to hid - url = "/comic/${comic.hid}#" - title = entryTitle - description = buildString { - if (scorePosition == "top") append(comic.fancyScore) - val desc = comic.desc?.beautifyDescription() - if (!desc.isNullOrEmpty()) { - if (this.isNotEmpty()) append("\n\n") - append(desc) - } - if (scorePosition == "middle") { - if (this.isNotEmpty()) append("\n\n") - append(comic.fancyScore) - } - if (showAlternativeTitles && comic.altTitles.isNotEmpty()) { - if (this.isNotEmpty()) append("\n\n") - append("Alternative Titles:\n") - append( - titles.distinctBy { it.title }.filter { it.title != entryTitle } - .mapNotNull { title -> - title.title?.let { "• $it" } - }.joinToString("\n"), - ) - } - if (scorePosition == "bottom") { - if (this.isNotEmpty()) append("\n\n") - append(comic.fancyScore) - } - } - - status = comic.status.parseStatus(comic.translationComplete) - thumbnail_url = parseCover( - comic.cover, - covers ?: comic.mdCovers, - coverQuality, - ) - artist = artists.joinToString { it.name.trim() } - author = authors.joinToString { it.name.trim() } - genre = buildList { - comic.origination?.let { add(Genre("Origination", it.name)) } - demographic?.let { add(Genre("Demographic", it)) } - addAll( - comic.mdGenres.mapNotNull { it.genre }.sortedBy { it.group } - .sortedBy { it.name }, - ) - addAll(genres.sortedBy { it.group }.sortedBy { it.name }) - if (includeMuTags) { - addAll( - comic.muGenres.categories.mapNotNull { it?.category?.title }.sorted() - .map { Genre("Category", it) }, - ) - } - } - .distinctBy { it.name } - .filterNot { it.name.isNullOrBlank() || it.group.isNullOrBlank() } - .joinToString { if (groupTags) "${it.group}:${it.name?.trim()}" else "${it.name?.trim()}" } - } - } -} - -@Serializable -class Comic( - val hid: String, - val title: String, - private val country: String? = null, - val slug: String? = null, - @SerialName("md_titles") val altTitles: List = emptyList(), - val desc: String? = null, - val status: Int? = 0, - @SerialName("translation_completed") val translationComplete: Boolean? = true, - @SerialName("md_covers") val mdCovers: List<MDcovers> = emptyList(), - @SerialName("cover_url") val cover: String? = null, - @SerialName("md_comic_md_genres") val mdGenres: List<MdGenres>, - @SerialName("mu_comics") val muGenres: MuComicCategories = MuComicCategories(emptyList()), - @SerialName("bayesian_rating") val score: String? = null, - @SerialName("iso639_1") val isoLang: String? = null, -) { - val origination = when (country) { - "jp" -> Name("Manga") - "kr" -> Name("Manhwa") - "cn" -> Name("Manhua") - else -> null - } - val fancyScore: String = if (score.isNullOrEmpty()) { - "" - } else { - val stars = score.toBigDecimal().div(BigDecimal(2)) - .setScale(0, RoundingMode.HALF_UP).toInt() - buildString { - append("★".repeat(stars)) - if (stars < 5) append("☆".repeat(5 - stars)) - append(" $score") - } - } -} - -@Serializable -class MdGenres( - @SerialName("md_genres") val genre: Genre? = null, -) - -@Serializable -class Genre( - val group: String? = null, - val name: String? = null, -) - -@Serializable -class MuComicCategories( - @SerialName("mu_comic_categories") val categories: List<MuCategories?> = emptyList(), -) - -@Serializable -class MuCategories( - @SerialName("mu_categories") val category: Title? = null, -) - -@Serializable -class Covers( - @SerialName("md_covers") val mdCovers: List<MDcovers> = emptyList(), -) - -@Serializable -class MDcovers( - val b2key: String?, - val vol: String? = null, - val locale: String? = null, -) - -@Serializable -class Title( - val title: String?, - val lang: String? = null, -) - -@Serializable -class Name( - val name: String, -) - -@Serializable -class ChapterList( - val chapters: List<Chapter>, -) - -@Serializable -class Chapter( - private val hid: String, - private val lang: String = "", - private val title: String = "", - @SerialName("created_at") private val createdAt: String = "", - @SerialName("publish_at") val publishedAt: String = "", - val chap: String = "", - private val vol: String = "", - @SerialName("group_name") val groups: List<String> = emptyList(), - @SerialName("up_count") private val upCount: Int, - @SerialName("down_count") private val downCount: Int, -) { - val score get() = upCount - downCount - - fun toSChapter(mangaUrl: String) = SChapter.create().apply { - url = "$mangaUrl/$hid-chapter-$chap-$lang" - name = beautifyChapterName(vol, chap, title) - date_upload = createdAt.parseDate() - scanlator = groups.joinToString().takeUnless { it.isBlank() } ?: "Unknown" - } -} - -@Serializable -class PageList( - val chapter: ChapterPageData, -) - -@Serializable -class ChapterPageData( - val images: List<Page>, -) - -@Serializable -class Page( - val url: String? = null, -) - -@Serializable -class Error( - val statusCode: Int, - val message: String, -) diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Filters.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Filters.kt deleted file mode 100644 index 5fdbfb537..000000000 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Filters.kt +++ /dev/null @@ -1,208 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.comickfun - -import eu.kanade.tachiyomi.source.model.Filter -import eu.kanade.tachiyomi.source.model.FilterList - -fun getFilters(): FilterList { - return FilterList( - Filter.Header(name = "The filter is ignored when using text search."), - GenreFilter("Genre", getGenresList), - DemographicFilter("Demographic", getDemographicList), - TypeFilter("Type", getTypeList), - SortFilter(), - StatusFilter("Status", getStatusList), - ContentRatingFilter("Content Rating", getContentRatingList), - CompletedFilter("Completely Scanlated?"), - CreatedAtFilter("Created at", getCreatedAtList), - MinimumFilter("Minimum Chapters"), - Filter.Header("From Year, ex: 2010"), - FromYearFilter("From"), - Filter.Header("To Year, ex: 2021"), - ToYearFilter("To"), - Filter.Header("Separate tags with commas"), - TagFilter("Tags"), - ExcludedTagFilter("Excluded Tags"), - ) -} - -/** Filters **/ -internal class GenreFilter(name: String, genreList: List<Pair<String, String>>) : - Filter.Group<TriFilter>(name, genreList.map { TriFilter(it.first, it.second) }) - -internal class TagFilter(name: String) : TextFilter(name) - -internal class ExcludedTagFilter(name: String) : TextFilter(name) - -internal class DemographicFilter(name: String, demographicList: List<Pair<String, String>>) : - Filter.Group<CheckBoxFilter>(name, demographicList.map { CheckBoxFilter(it.first, it.second) }) - -internal class TypeFilter(name: String, typeList: List<Pair<String, String>>) : - Filter.Group<CheckBoxFilter>(name, typeList.map { CheckBoxFilter(it.first, it.second) }) - -internal class CompletedFilter(name: String) : CheckBoxFilter(name) - -internal class CreatedAtFilter(name: String, createdAtList: List<Pair<String, String>>) : - SelectFilter(name, createdAtList) - -internal class MinimumFilter(name: String) : TextFilter(name) - -internal class FromYearFilter(name: String) : TextFilter(name) - -internal class ToYearFilter(name: String) : TextFilter(name) - -internal class SortFilter(defaultValue: String? = null, state: Int = 0) : - SelectFilter("Sort", getSortsList, state, defaultValue) - -internal class StatusFilter(name: String, statusList: List<Pair<String, String>>, state: Int = 0) : - SelectFilter(name, statusList, state) - -internal class ContentRatingFilter(name: String, statusList: List<Pair<String, String>>, state: Int = 0) : - SelectFilter(name, statusList, state) - -/** Generics **/ -internal open class TriFilter(name: String, val value: String) : Filter.TriState(name) - -internal open class TextFilter(name: String) : Filter.Text(name) - -internal open class CheckBoxFilter(name: String, val value: String = "") : Filter.CheckBox(name) - -internal open class SelectFilter(name: String, private val vals: List<Pair<String, String>>, state: Int = 0, defaultValue: String? = null) : - Filter.Select<String>(name, vals.map { it.first }.toTypedArray(), vals.indexOfFirst { it.second == defaultValue }.takeIf { it != -1 } ?: state) { - fun getValue() = vals[state].second -} - -/** Filters Data **/ -private val getGenresList: List<Pair<String, String>> = listOf( - Pair("4-Koma", "4-koma"), - Pair("Action", "action"), - Pair("Adaptation", "adaptation"), - Pair("Adult", "adult"), - Pair("Adventure", "adventure"), - Pair("Aliens", "aliens"), - Pair("Animals", "animals"), - Pair("Anthology", "anthology"), - Pair("Award Winning", "award-winning"), - Pair("Comedy", "comedy"), - Pair("Cooking", "cooking"), - Pair("Crime", "crime"), - Pair("Crossdressing", "crossdressing"), - Pair("Delinquents", "delinquents"), - Pair("Demons", "demons"), - Pair("Doujinshi", "doujinshi"), - Pair("Drama", "drama"), - Pair("Ecchi", "ecchi"), - Pair("Fan Colored", "fan-colored"), - Pair("Fantasy", "fantasy"), - Pair("Full Color", "full-color"), - Pair("Gender Bender", "gender-bender"), - Pair("Genderswap", "genderswap"), - Pair("Ghosts", "ghosts"), - Pair("Gore", "gore"), - Pair("Gyaru", "gyaru"), - Pair("Harem", "harem"), - Pair("Historical", "historical"), - Pair("Horror", "horror"), - Pair("Incest", "incest"), - Pair("Isekai", "isekai"), - Pair("Loli", "loli"), - Pair("Long Strip", "long-strip"), - Pair("Mafia", "mafia"), - Pair("Magic", "magic"), - Pair("Magical Girls", "magical-girls"), - Pair("Martial Arts", "martial-arts"), - Pair("Mature", "mature"), - Pair("Mecha", "mecha"), - Pair("Medical", "medical"), - Pair("Military", "military"), - Pair("Monster Girls", "monster-girls"), - Pair("Monsters", "monsters"), - Pair("Music", "music"), - Pair("Mystery", "mystery"), - Pair("Ninja", "ninja"), - Pair("Office Workers", "office-workers"), - Pair("Official Colored", "official-colored"), - Pair("Oneshot", "oneshot"), - Pair("Philosophical", "philosophical"), - Pair("Police", "police"), - Pair("Post-Apocalyptic", "post-apocalyptic"), - Pair("Psychological", "psychological"), - Pair("Reincarnation", "reincarnation"), - Pair("Reverse Harem", "reverse-harem"), - Pair("Romance", "romance"), - Pair("Samurai", "samurai"), - Pair("School Life", "school-life"), - Pair("Sci-Fi", "sci-fi"), - Pair("Sexual Violence", "sexual-violence"), - Pair("Shota", "shota"), - Pair("Shoujo Ai", "shoujo-ai"), - Pair("Shounen Ai", "shounen-ai"), - Pair("Slice of Life", "slice-of-life"), - Pair("Smut", "smut"), - Pair("Sports", "sports"), - Pair("Superhero", "superhero"), - Pair("Supernatural", "supernatural"), - Pair("Survival", "survival"), - Pair("Thriller", "thriller"), - Pair("Time Travel", "time-travel"), - Pair("Traditional Games", "traditional-games"), - Pair("Tragedy", "tragedy"), - Pair("User Created", "user-created"), - Pair("Vampires", "vampires"), - Pair("Video Games", "video-games"), - Pair("Villainess", "villainess"), - Pair("Virtual Reality", "virtual-reality"), - Pair("Web Comic", "web-comic"), - Pair("Wuxia", "wuxia"), - Pair("Yaoi", "yaoi"), - Pair("Yuri", "yuri"), - Pair("Zombies", "zombies"), -) - -private val getDemographicList: List<Pair<String, String>> = listOf( - Pair("Shounen", "1"), - Pair("Shoujo", "2"), - Pair("Seinen", "3"), - Pair("Josei", "4"), - Pair("None", "5"), -) - -private val getTypeList: List<Pair<String, String>> = listOf( - Pair("Manga", "jp"), - Pair("Manhwa", "kr"), - Pair("Manhua", "cn"), - Pair("Others", "others"), -) - -private val getCreatedAtList: List<Pair<String, String>> = listOf( - Pair("", ""), - Pair("3 days", "3"), - Pair("7 days", "7"), - Pair("30 days", "30"), - Pair("3 months", "90"), - Pair("6 months", "180"), - Pair("1 year", "365"), -) - -private val getSortsList: List<Pair<String, String>> = listOf( - Pair("Most popular", "follow"), - Pair("Most follows", "user_follow_count"), - Pair("Most views", "view"), - Pair("High rating", "rating"), - Pair("Last updated", "uploaded"), - Pair("Newest", "created_at"), -) - -private val getStatusList: List<Pair<String, String>> = listOf( - Pair("All", "0"), - Pair("Ongoing", "1"), - Pair("Completed", "2"), - Pair("Cancelled", "3"), - Pair("Hiatus", "4"), -) - -private val getContentRatingList: List<Pair<String, String>> = listOf( - Pair("All", ""), - Pair("Safe", "safe"), - Pair("Suggestive", "suggestive"), - Pair("Erotica", "erotica"), -) diff --git a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Helpers.kt b/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Helpers.kt deleted file mode 100644 index 1d55b2f94..000000000 --- a/src/all/comickfun/src/eu/kanade/tachiyomi/extension/all/comickfun/Helpers.kt +++ /dev/null @@ -1,85 +0,0 @@ -package eu.kanade.tachiyomi.extension.all.comickfun - -import eu.kanade.tachiyomi.source.model.SManga -import org.jsoup.parser.Parser -import java.text.SimpleDateFormat -import java.util.Locale -import java.util.TimeZone - -private val dateFormat by lazy { - SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssXXX", Locale.ENGLISH).apply { - timeZone = TimeZone.getTimeZone("UTC") - } -} -private val markdownLinksRegex = "\\[([^]]+)]\\(([^)]+)\\)".toRegex() -private val markdownItalicBoldRegex = "\\*+\\s*([^*]*)\\s*\\*+".toRegex() -private val markdownItalicRegex = "_+\\s*([^_]*)\\s*_+".toRegex() - -internal fun String.beautifyDescription(): String { - return Parser.unescapeEntities(this, false) - .substringBefore("---") - .replace(markdownLinksRegex, "") - .replace(markdownItalicBoldRegex, "") - .replace(markdownItalicRegex, "") - .trim() -} - -internal fun Int?.parseStatus(translationComplete: Boolean?): Int { - return when (this) { - 1 -> SManga.ONGOING - 2 -> { - if (translationComplete == true) { - SManga.COMPLETED - } else { - SManga.PUBLISHING_FINISHED - } - } - 3 -> SManga.CANCELLED - 4 -> SManga.ON_HIATUS - else -> SManga.UNKNOWN - } -} -enum class CoverQuality { - Original, // HQ original - Compressed, // HQ but compressed - WebDefault, // what comick serves in browser, usually compressed + downscaled -} - -internal fun parseCover(thumbnailUrl: String?, mdCovers: List<MDcovers>, coverQuality: CoverQuality = CoverQuality.WebDefault): String? { - fun addOrReplaceCoverQualitySuffix(url: String, qualitySuffix: String): String { - return url.substringBeforeLast('#').substringBeforeLast('.').replace(Regex("-(m|s)$"), "") + - "$qualitySuffix.jpg#${url.substringAfter('#', "")}" - } - - val mdCover = mdCovers.firstOrNull() - val coverUrl = if (mdCover != null) { - thumbnailUrl?.replaceAfterLast("/", "${mdCover.b2key}#${mdCover.vol.orEmpty()}") - } else { - thumbnailUrl - } ?: return null - - return when (coverQuality) { - CoverQuality.Original -> coverUrl - CoverQuality.Compressed -> addOrReplaceCoverQualitySuffix(coverUrl, "-m") - CoverQuality.WebDefault -> addOrReplaceCoverQualitySuffix(coverUrl, "-s") - } -} - -internal fun beautifyChapterName(vol: String, chap: String, title: String): String { - return buildString { - if (vol.isNotEmpty()) { - if (chap.isEmpty()) append("Volume $vol") else append("Vol. $vol") - } - if (chap.isNotEmpty()) { - if (vol.isEmpty()) append("Chapter $chap") else append(", Ch. $chap") - } - if (title.isNotEmpty()) { - if (chap.isEmpty()) append(title) else append(": $title") - } - } -} - -internal fun String.parseDate(): Long { - return runCatching { dateFormat.parse(this)?.time } - .getOrNull() ?: 0L -}