package com.dar.nclientv2.api; import android.content.Context; import android.net.Uri; import android.os.Parcel; import android.os.Parcelable; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import com.dar.nclientv2.api.components.Gallery; import com.dar.nclientv2.api.components.GenericGallery; import com.dar.nclientv2.api.components.Tag; import com.dar.nclientv2.api.enums.ApiRequestType; import com.dar.nclientv2.api.enums.Language; import com.dar.nclientv2.api.enums.SortType; import com.dar.nclientv2.api.enums.SpecialTagIds; import com.dar.nclientv2.api.enums.TagStatus; import com.dar.nclientv2.api.enums.TagType; import com.dar.nclientv2.api.local.LocalGallery; import com.dar.nclientv2.async.database.Queries; import com.dar.nclientv2.settings.Global; import com.dar.nclientv2.settings.Login; import com.dar.nclientv2.utility.LogUtility; import com.dar.nclientv2.utility.Utility; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import org.jsoup.select.Elements; import java.io.IOException; import java.lang.ref.WeakReference; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import okhttp3.Request; import okhttp3.Response; public class InspectorV3 extends Thread implements Parcelable { private SortType sortType; private boolean custom,forceStart=false; private int page,pageCount=-1,id; private String query,url; private ApiRequestType requestType; private Set<Tag> tags; private List<GenericGallery> galleries=null; private InspectorResponse response; private WeakReference<Context> context; private Document htmlDocument; protected InspectorV3(Parcel in) { sortType = SortType.values()[in.readByte()]; custom = in.readByte() != 0; page = in.readInt(); pageCount = in.readInt(); id = in.readInt(); query = in.readString(); url = in.readString(); requestType=ApiRequestType.values[in.readByte()]; ArrayList x=null; switch (GenericGallery.Type.values()[in.readByte()]){ case LOCAL:x = in.createTypedArrayList(LocalGallery.CREATOR); break; case SIMPLE:x = in.createTypedArrayList(SimpleGallery.CREATOR);break; case COMPLETE:x = in.createTypedArrayList(Gallery.CREATOR);break; } galleries=(ArrayList<GenericGallery>)x; tags =new HashSet<>(in.createTypedArrayList(Tag.CREATOR)); } public static final Creator<InspectorV3> CREATOR = new Creator<InspectorV3>() { @Override public InspectorV3 createFromParcel(Parcel in) { return new InspectorV3(in); } @Override public InspectorV3[] newArray(int size) { return new InspectorV3[size]; } }; @Override public int describeContents() { return 0; } @Override public void writeToParcel(Parcel dest, int flags) { dest.writeByte((byte) (sortType.ordinal())); dest.writeByte((byte) (custom ? 1 : 0)); dest.writeInt(page); dest.writeInt(pageCount); dest.writeInt(id); dest.writeString(query); dest.writeString(url); dest.writeByte(requestType.ordinal()); if(galleries==null||galleries.size()==0)dest.writeByte((byte)GenericGallery.Type.SIMPLE.ordinal()); else dest.writeByte((byte)galleries.get(0).getType().ordinal()); dest.writeTypedList(galleries); dest.writeTypedList(new ArrayList<>(tags)); } public String getSearchTitle() { //triggered only when in searchMode if(query.length()>0)return query; return url.replace(Utility.getBaseUrl()+"search/?q=","").replace('+',' '); } public interface InspectorResponse{ boolean shouldStart(InspectorV3 inspector); void onSuccess(List<GenericGallery>galleries); void onFailure(Exception e); void onStart(); void onEnd(); } public static abstract class DefaultInspectorResponse implements InspectorResponse{ @Override public boolean shouldStart(InspectorV3 inspector) { return true; } @Override public void onStart() {} @Override public void onEnd() {} @Override public void onSuccess(List<GenericGallery> galleries) {} @Override public void onFailure(Exception e) { LogUtility.e(e.getLocalizedMessage(),e); } } private InspectorV3(Context context,InspectorResponse response){ initialize(context, response); } public void initialize(Context context,InspectorResponse response){ this.response=response; this.context=new WeakReference<>(context); } public InspectorResponse getResponse() { return response; } public InspectorV3 cloneInspector(Context context, InspectorResponse response){ InspectorV3 inspectorV3=new InspectorV3(context,response); inspectorV3.query=query; inspectorV3.url=url; inspectorV3.tags=tags; inspectorV3.requestType=requestType; inspectorV3.sortType = sortType; inspectorV3.pageCount=pageCount; inspectorV3.page=page; inspectorV3.id=id; inspectorV3.custom=custom; return inspectorV3; } /** * This method will not run, but a WebView inside MainActivity will do it in its place * */ public static InspectorV3 favoriteInspector(Context context,String query, int page, InspectorResponse response){ InspectorV3 inspector=new InspectorV3(context,response); inspector.page=page; inspector.pageCount=0; inspector.query=query==null?"":query; inspector.requestType=ApiRequestType.FAVORITE; inspector.tags=new HashSet<>(1); inspector.createUrl(); return inspector; } /** * @param favorite true if random online favorite, false for general random manga * */ public static InspectorV3 randomInspector(Context context,InspectorResponse response,boolean favorite){ if(!favorite&&Global.useAlternativeSite()) return InspectorV3.galleryInspector(context,Utility.RANDOM.nextInt(Global.getMaxId()),response); InspectorV3 inspector=new InspectorV3(context,response); inspector.requestType=favorite?ApiRequestType.RANDOM_FAVORITE:ApiRequestType.RANDOM; inspector.createUrl(); return inspector; } public static InspectorV3 galleryInspector(Context context,int id,InspectorResponse response){ InspectorV3 inspector=new InspectorV3(context,response); inspector.id=id; inspector.requestType=ApiRequestType.BYSINGLE; inspector.createUrl(); return inspector; } public static InspectorV3 basicInspector(Context context,int page,InspectorResponse response){ return searchInspector(context,null,null,page,Global.getSortType(),response); } public static InspectorV3 tagInspector(Context context,Tag tag,int page,SortType sortType,InspectorResponse response){ Collection<Tag>tags; if(!Global.isOnlyTag()){ tags=getDefaultTags(); tags.add(tag); }else { tags = Collections.singleton(tag); } return searchInspector(context,null,tags,page,sortType,response); } public static InspectorV3 searchInspector(Context context, String query, Collection<Tag> tags, int page, SortType sortType, InspectorResponse response){ InspectorV3 inspector=new InspectorV3(context,response); inspector.custom=tags!=null; inspector.tags=inspector.custom?new HashSet<>(tags):getDefaultTags(); inspector.tags.addAll(getLanguageTags(Global.getOnlyLanguage())); inspector.page=page; inspector.pageCount=0; inspector.query=query==null?"":query; inspector.sortType =sortType; if(inspector.query.isEmpty()) { switch (inspector.tags.size()) { case 0: inspector.requestType = ApiRequestType.BYALL; inspector.tryByAllPopular(); break; case 1: inspector.requestType = ApiRequestType.BYTAG; //else by search for the negative tag if(inspector.getTag().getStatus()!=TagStatus.AVOIDED) break; default: inspector.requestType = ApiRequestType.BYSEARCH; break; } }else inspector.requestType=ApiRequestType.BYSEARCH; inspector.createUrl(); return inspector; } private void tryByAllPopular() { if(sortType !=SortType.RECENT_ALL_TIME){ requestType=ApiRequestType.BYSEARCH; query="-nclientv2"; } } private void createUrl() { StringBuilder builder=new StringBuilder(Utility.getBaseUrl()); if(requestType==ApiRequestType.BYALL)builder.append("?page=").append(page); else if(requestType==ApiRequestType.RANDOM)builder.append("random/"); else if(requestType==ApiRequestType.RANDOM_FAVORITE)builder.append("favorites/random"); else if(requestType==ApiRequestType.BYSINGLE)builder.append("g/").append(id); else if(requestType==ApiRequestType.FAVORITE){ builder.append("favorites/"); if(query!=null&&query.length()>0)builder.append("?q=").append(query).append('&'); else builder.append('?'); builder.append("page=").append(page); }/*else if(requestType==ApiRequestType.BYTAG){ for(Tag tt:tags)t=tt; assert t!=null; builder.append(t.getTypeSingleName()).append('/') .append(t.getName().replace(' ','-').replace(".","")); if(byPopular)builder.append("/popular"); else builder.append('/'); builder.append("?page=").append(page); }*/else if(requestType==ApiRequestType.BYSEARCH||requestType==ApiRequestType.BYTAG){ builder.append("search/?q=").append(query); for(Tag tt:tags){ if(builder.toString().contains(tt.toQueryTag(TagStatus.ACCEPTED)))continue; builder.append('+').append(tt.toQueryTag()); } builder.append("&page=").append(page); if(sortType.getUrlAddition()!=null){ builder.append("&sort=").append(sortType.getUrlAddition()); } } url=builder.toString().replace(' ','+'); LogUtility.d("WWW: "+getBookmarkURL()); } public void forceStart(){ forceStart=true; start(); } private String getBookmarkURL(){ if(page<2)return url; else return url.substring(0,url.lastIndexOf('=')+1); } @NonNull private static HashSet<Tag> getDefaultTags(){ HashSet<Tag> tags = new HashSet<>(Queries.TagTable.getAllStatus( TagStatus.ACCEPTED)); tags.addAll(getLanguageTags(Global.getOnlyLanguage())); if(Global.removeAvoidedGalleries()) tags.addAll(Queries.TagTable.getAllStatus(TagStatus.AVOIDED)); if(Login.isLogged())tags.addAll(Queries.TagTable.getAllOnlineBlacklisted()); return tags; } private static Set<Tag> getLanguageTags(Language onlyLanguage) { Set<Tag>tags=new HashSet<>(); if(onlyLanguage==null)return tags; switch (onlyLanguage){ case ENGLISH:tags.add(Queries.TagTable.getTagById(SpecialTagIds.LANGUAGE_ENGLISH));break; case JAPANESE:tags.add(Queries.TagTable.getTagById(SpecialTagIds.LANGUAGE_JAPANESE));break; case CHINESE:tags.add(Queries.TagTable.getTagById(SpecialTagIds.LANGUAGE_CHINESE));break; } return tags; } public void createDocument()throws IOException{ if(htmlDocument!=null)return; Response response = Global.getClient(context.get()).newCall(new Request.Builder().url(url).build()).execute(); setHtmlDocument(Jsoup.parse(response.body().byteStream(), "UTF-8", Utility.getBaseUrl())); response.close(); } private void parseDocument() throws IOException{ if(requestType.isSingle()) doSingle(htmlDocument.body()); else doSearch(htmlDocument.body()); htmlDocument = null; } public void setHtmlDocument(Document htmlDocument){ this.htmlDocument = htmlDocument; } public boolean canParseDocument(){ return this.htmlDocument!=null; } @Override public synchronized void start() { if(forceStart||response.shouldStart(this)) super.start(); } @Override public void run() { LogUtility.d("Starting download: "+url); if(response!=null)response.onStart(); try { createDocument(); parseDocument(); if(response!=null)response.onSuccess(galleries); } catch (IOException e) { if(response!=null)response.onFailure(e); } if(response!=null)response.onEnd(); LogUtility.d("Finished download: "+url); } private void doSingle(Element document) throws IOException { galleries=new ArrayList<>(1); Elements scripts=document.getElementsByTag("script"); if(scripts.size()==0 )return; String json=trimScriptTag(scripts.last().html()); if(json==null)return; Elements com=document.getElementById("comments").getElementsByClass("comment"); Elements rel=document.getElementById("related-container").getElementsByClass("gallery"); boolean isFavorite; try { isFavorite = document.getElementById("favorite").getElementsByTag("span").get(0).text().equals("Unfavorite"); }catch (Exception e){ isFavorite=false; } LogUtility.d("is favorite? "+isFavorite); galleries.add(new Gallery(context.get(), json, rel,isFavorite)); } @Nullable private String trimScriptTag(String scriptHtml) { int s=scriptHtml.indexOf("parse"); if(s<0)return null; s+=7; scriptHtml=scriptHtml.substring(s,scriptHtml.lastIndexOf(");")-1); scriptHtml=Utility.unescapeUnicodeString(scriptHtml); if(scriptHtml.isEmpty())return null; return scriptHtml; } private void doSearch(Element document) { Elements gal=document.getElementsByClass("gallery"); galleries=new ArrayList<>(gal.size()); for(Element e:gal)galleries.add(new SimpleGallery(context.get(),e)); gal=document.getElementsByClass("last"); pageCount=gal.size()==0?Math.max(1,page):findTotal(gal.last()); } private int findTotal(Element e){ String temp=e.attr("href"); try { return Integer.parseInt(Uri.parse(temp).getQueryParameter("page")); }catch (Exception ignore){return 1;} } public void setSortType(SortType sortType) { this.sortType = sortType; createUrl(); } public void setPage(int page) { this.page = page; createUrl(); } public int getPage() { return page; } public List<GenericGallery> getGalleries() { return galleries; } public String getUrl() { return url; } public ApiRequestType getRequestType() { return requestType; } public int getPageCount() { return pageCount; } public boolean isCustom() { return custom; } public String getQuery() { return query; } public Tag getTag(){ Tag t=null; for(Tag tt:tags){ if(tt.getType()!= TagType.LANGUAGE) return tt; t=tt; } return t; } }