mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-18 15:07:30 +01:00
Reorganize data package
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.caches;
|
||||
package eu.kanade.mangafeed.data.cache;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -15,7 +15,7 @@ import java.io.OutputStream;
|
||||
import java.lang.reflect.Type;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.data.models.Page;
|
||||
import eu.kanade.mangafeed.data.source.model.Page;
|
||||
import eu.kanade.mangafeed.util.DiskUtils;
|
||||
import okio.BufferedSink;
|
||||
import okio.Okio;
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.helpers;
|
||||
package eu.kanade.mangafeed.data.database;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -16,16 +16,16 @@ import com.pushtorefresh.storio.sqlite.queries.RawQuery;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.data.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.models.ChapterStorIOSQLiteDeleteResolver;
|
||||
import eu.kanade.mangafeed.data.models.ChapterStorIOSQLiteGetResolver;
|
||||
import eu.kanade.mangafeed.data.models.ChapterStorIOSQLitePutResolver;
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.data.models.MangaStorIOSQLiteDeleteResolver;
|
||||
import eu.kanade.mangafeed.data.models.MangaStorIOSQLitePutResolver;
|
||||
import eu.kanade.mangafeed.data.resolvers.MangaWithUnreadGetResolver;
|
||||
import eu.kanade.mangafeed.data.tables.ChaptersTable;
|
||||
import eu.kanade.mangafeed.data.tables.MangasTable;
|
||||
import eu.kanade.mangafeed.data.database.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLiteDeleteResolver;
|
||||
import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLiteGetResolver;
|
||||
import eu.kanade.mangafeed.data.database.models.ChapterStorIOSQLitePutResolver;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLiteDeleteResolver;
|
||||
import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLitePutResolver;
|
||||
import eu.kanade.mangafeed.data.database.resolvers.MangaWithUnreadGetResolver;
|
||||
import eu.kanade.mangafeed.data.database.tables.ChaptersTable;
|
||||
import eu.kanade.mangafeed.data.database.tables.MangasTable;
|
||||
import eu.kanade.mangafeed.util.ChapterRecognition;
|
||||
import eu.kanade.mangafeed.util.PostResult;
|
||||
import rx.Observable;
|
||||
@@ -1,12 +1,12 @@
|
||||
package eu.kanade.mangafeed.data.helpers;
|
||||
package eu.kanade.mangafeed.data.database;
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import eu.kanade.mangafeed.data.tables.ChaptersTable;
|
||||
import eu.kanade.mangafeed.data.tables.MangasTable;
|
||||
import eu.kanade.mangafeed.data.database.tables.ChaptersTable;
|
||||
import eu.kanade.mangafeed.data.database.tables.MangasTable;
|
||||
|
||||
public class DbOpenHelper extends SQLiteOpenHelper {
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package eu.kanade.mangafeed.data.models;
|
||||
package eu.kanade.mangafeed.data.database.models;
|
||||
|
||||
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteColumn;
|
||||
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType;
|
||||
|
||||
import eu.kanade.mangafeed.data.tables.ChaptersTable;
|
||||
import eu.kanade.mangafeed.data.database.tables.ChaptersTable;
|
||||
|
||||
@StorIOSQLiteType(table = ChaptersTable.TABLE)
|
||||
public class Chapter {
|
||||
@@ -1,9 +1,9 @@
|
||||
package eu.kanade.mangafeed.data.models;
|
||||
package eu.kanade.mangafeed.data.database.models;
|
||||
|
||||
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteColumn;
|
||||
import com.pushtorefresh.storio.sqlite.annotations.StorIOSQLiteType;
|
||||
|
||||
import eu.kanade.mangafeed.data.tables.MangasTable;
|
||||
import eu.kanade.mangafeed.data.database.tables.MangasTable;
|
||||
|
||||
@StorIOSQLiteType(table = MangasTable.TABLE)
|
||||
public class Manga {
|
||||
@@ -1,11 +1,11 @@
|
||||
package eu.kanade.mangafeed.data.resolvers;
|
||||
package eu.kanade.mangafeed.data.database.resolvers;
|
||||
|
||||
import android.database.Cursor;
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.data.models.MangaStorIOSQLiteGetResolver;
|
||||
import eu.kanade.mangafeed.data.tables.MangasTable;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.data.database.models.MangaStorIOSQLiteGetResolver;
|
||||
import eu.kanade.mangafeed.data.database.tables.MangasTable;
|
||||
|
||||
|
||||
public class MangaWithUnreadGetResolver extends MangaStorIOSQLiteGetResolver {
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.tables;
|
||||
package eu.kanade.mangafeed.data.database.tables;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.tables;
|
||||
package eu.kanade.mangafeed.data.database.tables;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.tables;
|
||||
package eu.kanade.mangafeed.data.database.tables;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.tables;
|
||||
package eu.kanade.mangafeed.data.database.tables;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.helpers;
|
||||
package eu.kanade.mangafeed.data.download;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -16,14 +16,15 @@ import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.data.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.models.Download;
|
||||
import eu.kanade.mangafeed.data.models.DownloadQueue;
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.data.models.Page;
|
||||
import eu.kanade.mangafeed.data.services.DownloadService;
|
||||
import eu.kanade.mangafeed.events.DownloadChaptersEvent;
|
||||
import eu.kanade.mangafeed.sources.base.Source;
|
||||
import eu.kanade.mangafeed.data.database.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.data.download.model.Download;
|
||||
import eu.kanade.mangafeed.data.download.model.DownloadQueue;
|
||||
import eu.kanade.mangafeed.data.source.model.Page;
|
||||
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
|
||||
import eu.kanade.mangafeed.data.source.SourceManager;
|
||||
import eu.kanade.mangafeed.event.DownloadChaptersEvent;
|
||||
import eu.kanade.mangafeed.data.source.base.Source;
|
||||
import eu.kanade.mangafeed.util.DiskUtils;
|
||||
import eu.kanade.mangafeed.util.DynamicConcurrentMergeOperator;
|
||||
import rx.Observable;
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.services;
|
||||
package eu.kanade.mangafeed.data.download;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.Context;
|
||||
@@ -12,8 +12,7 @@ import javax.inject.Inject;
|
||||
|
||||
import de.greenrobot.event.EventBus;
|
||||
import eu.kanade.mangafeed.App;
|
||||
import eu.kanade.mangafeed.data.helpers.DownloadManager;
|
||||
import eu.kanade.mangafeed.events.DownloadChaptersEvent;
|
||||
import eu.kanade.mangafeed.event.DownloadChaptersEvent;
|
||||
import eu.kanade.mangafeed.util.ContentObservable;
|
||||
import eu.kanade.mangafeed.util.EventBusHook;
|
||||
import eu.kanade.mangafeed.util.NetworkUtil;
|
||||
@@ -1,9 +1,12 @@
|
||||
package eu.kanade.mangafeed.data.models;
|
||||
package eu.kanade.mangafeed.data.download.model;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.sources.base.Source;
|
||||
import eu.kanade.mangafeed.data.database.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.data.source.model.Page;
|
||||
import eu.kanade.mangafeed.data.source.base.Source;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
public class Download {
|
||||
@@ -1,8 +1,9 @@
|
||||
package eu.kanade.mangafeed.data.models;
|
||||
package eu.kanade.mangafeed.data.download.model;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.data.download.model.Download;
|
||||
import rx.Observable;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
@@ -1,25 +1,17 @@
|
||||
package eu.kanade.mangafeed.data.helpers;
|
||||
package eu.kanade.mangafeed.data.network;
|
||||
|
||||
|
||||
import com.squareup.okhttp.CacheControl;
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.OkHttpClient;
|
||||
import com.squareup.okhttp.Request;
|
||||
import com.squareup.okhttp.RequestBody;
|
||||
import com.squareup.okhttp.Response;
|
||||
import com.squareup.okhttp.ResponseBody;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.CookieManager;
|
||||
import java.net.CookiePolicy;
|
||||
import java.net.CookieStore;
|
||||
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSource;
|
||||
import okio.ForwardingSource;
|
||||
import okio.Okio;
|
||||
import okio.Source;
|
||||
import rx.Observable;
|
||||
|
||||
public final class NetworkHelper {
|
||||
@@ -121,48 +113,4 @@ public final class NetworkHelper {
|
||||
return cookieManager.getCookieStore();
|
||||
}
|
||||
|
||||
private static class ProgressResponseBody extends ResponseBody {
|
||||
|
||||
private final ResponseBody responseBody;
|
||||
private final ProgressListener progressListener;
|
||||
private BufferedSource bufferedSource;
|
||||
|
||||
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
|
||||
this.responseBody = responseBody;
|
||||
this.progressListener = progressListener;
|
||||
}
|
||||
|
||||
@Override public MediaType contentType() {
|
||||
return responseBody.contentType();
|
||||
}
|
||||
|
||||
@Override public long contentLength() throws IOException {
|
||||
return responseBody.contentLength();
|
||||
}
|
||||
|
||||
@Override public BufferedSource source() throws IOException {
|
||||
if (bufferedSource == null) {
|
||||
bufferedSource = Okio.buffer(source(responseBody.source()));
|
||||
}
|
||||
return bufferedSource;
|
||||
}
|
||||
|
||||
private Source source(Source source) {
|
||||
return new ForwardingSource(source) {
|
||||
long totalBytesRead = 0L;
|
||||
@Override public long read(Buffer sink, long byteCount) throws IOException {
|
||||
long bytesRead = super.read(sink, byteCount);
|
||||
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
||||
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
|
||||
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
|
||||
return bytesRead;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public interface ProgressListener {
|
||||
void update(long bytesRead, long contentLength, boolean done);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
package eu.kanade.mangafeed.data.network;
|
||||
|
||||
public interface ProgressListener {
|
||||
void update(long bytesRead, long contentLength, boolean done);
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package eu.kanade.mangafeed.data.network;
|
||||
|
||||
import com.squareup.okhttp.MediaType;
|
||||
import com.squareup.okhttp.ResponseBody;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import okio.Buffer;
|
||||
import okio.BufferedSource;
|
||||
import okio.ForwardingSource;
|
||||
import okio.Okio;
|
||||
import okio.Source;
|
||||
|
||||
public class ProgressResponseBody extends ResponseBody {
|
||||
|
||||
private final ResponseBody responseBody;
|
||||
private final ProgressListener progressListener;
|
||||
private BufferedSource bufferedSource;
|
||||
|
||||
public ProgressResponseBody(ResponseBody responseBody, ProgressListener progressListener) {
|
||||
this.responseBody = responseBody;
|
||||
this.progressListener = progressListener;
|
||||
}
|
||||
|
||||
@Override public MediaType contentType() {
|
||||
return responseBody.contentType();
|
||||
}
|
||||
|
||||
@Override public long contentLength() throws IOException {
|
||||
return responseBody.contentLength();
|
||||
}
|
||||
|
||||
@Override public BufferedSource source() throws IOException {
|
||||
if (bufferedSource == null) {
|
||||
bufferedSource = Okio.buffer(source(responseBody.source()));
|
||||
}
|
||||
return bufferedSource;
|
||||
}
|
||||
|
||||
private Source source(Source source) {
|
||||
return new ForwardingSource(source) {
|
||||
long totalBytesRead = 0L;
|
||||
@Override public long read(Buffer sink, long byteCount) throws IOException {
|
||||
long bytesRead = super.read(sink, byteCount);
|
||||
// read() returns the number of bytes read, or -1 if this source is exhausted.
|
||||
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
|
||||
progressListener.update(totalBytesRead, responseBody.contentLength(), bytesRead == -1);
|
||||
return bytesRead;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.helpers;
|
||||
package eu.kanade.mangafeed.data.preference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
@@ -7,7 +7,7 @@ import android.preference.PreferenceManager;
|
||||
import com.f2prateek.rx.preferences.RxSharedPreferences;
|
||||
|
||||
import eu.kanade.mangafeed.R;
|
||||
import eu.kanade.mangafeed.sources.base.Source;
|
||||
import eu.kanade.mangafeed.data.source.base.Source;
|
||||
import eu.kanade.mangafeed.util.DiskUtils;
|
||||
import rx.Observable;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.helpers;
|
||||
package eu.kanade.mangafeed.data.source;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
@@ -6,10 +6,10 @@ import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.sources.Batoto;
|
||||
import eu.kanade.mangafeed.sources.Mangahere;
|
||||
import eu.kanade.mangafeed.sources.Mangafox;
|
||||
import eu.kanade.mangafeed.sources.base.Source;
|
||||
import eu.kanade.mangafeed.data.source.online.english.Batoto;
|
||||
import eu.kanade.mangafeed.data.source.online.english.Mangahere;
|
||||
import eu.kanade.mangafeed.data.source.online.english.Mangafox;
|
||||
import eu.kanade.mangafeed.data.source.base.Source;
|
||||
|
||||
public class SourceManager {
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
package eu.kanade.mangafeed.data.source.base;
|
||||
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.data.database.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import rx.Observable;
|
||||
|
||||
public abstract class BaseSource {
|
||||
|
||||
// Name of the source to display
|
||||
public abstract String getName();
|
||||
|
||||
// Id of the source (must be declared and obtained from SourceManager to avoid conflicts)
|
||||
public abstract int getSourceId();
|
||||
|
||||
// True if the source requires a login
|
||||
public abstract boolean isLoginRequired();
|
||||
|
||||
// Given a page number, it should return the URL of the page where the manga list is found
|
||||
protected abstract String getUrlFromPageNumber(int page);
|
||||
|
||||
// From the URL obtained before, this method must return a list of mangas
|
||||
protected abstract List<Manga> parsePopularMangasFromHtml(String unparsedHtml);
|
||||
|
||||
// Given a query and a page number, return the URL of the results
|
||||
protected abstract String getSearchUrl(String query, int page);
|
||||
|
||||
// From the URL obtained before, this method must return a list of mangas
|
||||
protected abstract List<Manga> parseSearchFromHtml(String unparsedHtml);
|
||||
|
||||
// Given the URL of a manga and the result of the request, return the details of the manga
|
||||
protected abstract Manga parseHtmlToManga(String mangaUrl, String unparsedHtml);
|
||||
|
||||
// Given the result of the request to mangas' chapters, return a list of chapters
|
||||
protected abstract List<Chapter> parseHtmlToChapters(String unparsedHtml);
|
||||
|
||||
// Given the result of the request to a chapter, return the list of URLs of the chapter
|
||||
protected abstract List<String> parseHtmlToPageUrls(String unparsedHtml);
|
||||
|
||||
// Given the result of the request to a chapter's page, return the URL of the image of the page
|
||||
protected abstract String parseHtmlToImageUrl(String unparsedHtml);
|
||||
|
||||
|
||||
// Login related methods, shouldn't be overriden if the source doesn't require it
|
||||
public Observable<Boolean> login(String username, String password) {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
|
||||
public boolean isLogged() {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
|
||||
protected boolean isAuthenticationSuccessful(Response response) {
|
||||
throw new UnsupportedOperationException("Not implemented");
|
||||
}
|
||||
|
||||
|
||||
// Default fields, they can be overriden by sources' implementation
|
||||
|
||||
// Get the URL to the details of a manga, useful if the source provides some kind of API or fast calls
|
||||
protected String overrideMangaUrl(String defaultMangaUrl) {
|
||||
return defaultMangaUrl;
|
||||
}
|
||||
|
||||
// Get the URL of the first page that contains a source image and the page list
|
||||
protected String overrideChapterPageUrl(String defaultPageUrl) {
|
||||
return defaultPageUrl;
|
||||
}
|
||||
|
||||
// Get the URL of the remaining pages that contains source images
|
||||
protected String overrideRemainingPagesUrl(String defaultPageUrl) {
|
||||
return defaultPageUrl;
|
||||
}
|
||||
|
||||
// Default headers, it can be overriden by children or just add new keys
|
||||
protected Headers.Builder headersBuilder() {
|
||||
Headers.Builder builder = new Headers.Builder();
|
||||
builder.add("User-Agent", "Mozilla/5.0 (Windows NT 6.3; WOW64)");
|
||||
return builder;
|
||||
}
|
||||
|
||||
// Number of images to download at the same time. 3 by default
|
||||
protected int overrideNumberOfConcurrentPageDownloads() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
package eu.kanade.mangafeed.data.source.base;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import eu.kanade.mangafeed.App;
|
||||
import eu.kanade.mangafeed.data.cache.CacheManager;
|
||||
import eu.kanade.mangafeed.data.network.NetworkHelper;
|
||||
import eu.kanade.mangafeed.data.preference.PreferencesHelper;
|
||||
import eu.kanade.mangafeed.data.database.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.data.source.model.Page;
|
||||
import rx.Observable;
|
||||
import rx.schedulers.Schedulers;
|
||||
|
||||
public abstract class Source extends BaseSource {
|
||||
|
||||
@Inject protected NetworkHelper mNetworkService;
|
||||
@Inject protected CacheManager mCacheManager;
|
||||
@Inject protected PreferencesHelper prefs;
|
||||
protected Headers mRequestHeaders;
|
||||
|
||||
public Source(Context context) {
|
||||
App.get(context).getComponent().inject(this);
|
||||
mRequestHeaders = headersBuilder().build();
|
||||
}
|
||||
|
||||
// Get the most popular mangas from the source
|
||||
public Observable<List<Manga>> pullPopularMangasFromNetwork(int page) {
|
||||
String url = getUrlFromPageNumber(page);
|
||||
return mNetworkService
|
||||
.getStringResponse(url, mRequestHeaders, null)
|
||||
.flatMap(response -> Observable.just(parsePopularMangasFromHtml(response)));
|
||||
}
|
||||
|
||||
// Get mangas from the source with a query
|
||||
public Observable<List<Manga>> searchMangasFromNetwork(String query, int page) {
|
||||
return mNetworkService
|
||||
.getStringResponse(getSearchUrl(query, page), mRequestHeaders, null)
|
||||
.flatMap(response -> Observable.just(parseSearchFromHtml(response)));
|
||||
}
|
||||
|
||||
// Get manga details from the source
|
||||
public Observable<Manga> pullMangaFromNetwork(final String mangaUrl) {
|
||||
return mNetworkService
|
||||
.getStringResponse(overrideMangaUrl(mangaUrl), mRequestHeaders, null)
|
||||
.flatMap(unparsedHtml -> Observable.just(parseHtmlToManga(mangaUrl, unparsedHtml)));
|
||||
}
|
||||
|
||||
// Get chapter list of a manga from the source
|
||||
public Observable<List<Chapter>> pullChaptersFromNetwork(String mangaUrl) {
|
||||
return mNetworkService
|
||||
.getStringResponse(mangaUrl, mRequestHeaders, null)
|
||||
.flatMap(unparsedHtml ->
|
||||
Observable.just(parseHtmlToChapters(unparsedHtml)));
|
||||
}
|
||||
|
||||
public Observable<List<Page>> getCachedPageListOrPullFromNetwork(final String chapterUrl) {
|
||||
return mCacheManager.getPageUrlsFromDiskCache(chapterUrl)
|
||||
.onErrorResumeNext(throwable -> {
|
||||
return pullPageListFromNetwork(chapterUrl);
|
||||
})
|
||||
.onBackpressureBuffer();
|
||||
}
|
||||
|
||||
public Observable<List<Page>> pullPageListFromNetwork(final String chapterUrl) {
|
||||
return mNetworkService
|
||||
.getStringResponse(overrideChapterPageUrl(chapterUrl), mRequestHeaders, null)
|
||||
.flatMap(unparsedHtml -> {
|
||||
List<String> pageUrls = parseHtmlToPageUrls(unparsedHtml);
|
||||
return Observable.just(getFirstImageFromPageUrls(pageUrls, unparsedHtml));
|
||||
});
|
||||
}
|
||||
|
||||
// Get the URLs of the images of a chapter
|
||||
public Observable<Page> getRemainingImageUrlsFromPageList(final List<Page> pages) {
|
||||
return Observable.from(pages)
|
||||
.filter(page -> page.getImageUrl() == null)
|
||||
.window(overrideNumberOfConcurrentPageDownloads())
|
||||
.concatMap(batchedPages -> batchedPages.concatMap(this::getImageUrlFromPage));
|
||||
}
|
||||
|
||||
private Observable<Page> getImageUrlFromPage(final Page page) {
|
||||
page.setStatus(Page.LOAD_PAGE);
|
||||
return mNetworkService
|
||||
.getStringResponse(overrideRemainingPagesUrl(page.getUrl()), mRequestHeaders, null)
|
||||
.flatMap(unparsedHtml -> Observable.just(parseHtmlToImageUrl(unparsedHtml)))
|
||||
.onErrorResumeNext(e -> {
|
||||
page.setStatus(Page.ERROR);
|
||||
return Observable.just(null);
|
||||
})
|
||||
.flatMap(imageUrl -> {
|
||||
page.setImageUrl(imageUrl);
|
||||
return Observable.just(page);
|
||||
})
|
||||
.subscribeOn(Schedulers.io());
|
||||
}
|
||||
|
||||
public Observable<Page> getCachedImage(final Page page) {
|
||||
Observable<Page> obs = Observable.just(page);
|
||||
if (page.getImageUrl() == null)
|
||||
return obs;
|
||||
|
||||
if (!mCacheManager.isImageInCache(page.getImageUrl())) {
|
||||
page.setStatus(Page.DOWNLOAD_IMAGE);
|
||||
obs = cacheImage(page);
|
||||
}
|
||||
|
||||
return obs.flatMap(p -> {
|
||||
page.setImagePath(mCacheManager.getImagePath(page.getImageUrl()));
|
||||
page.setStatus(Page.READY);
|
||||
return Observable.just(page);
|
||||
}).onErrorResumeNext(e -> {
|
||||
page.setStatus(Page.ERROR);
|
||||
return Observable.just(page);
|
||||
});
|
||||
}
|
||||
|
||||
private Observable<Page> cacheImage(final Page page) {
|
||||
return getImageProgressResponse(page)
|
||||
.flatMap(resp -> {
|
||||
if (!mCacheManager.putImageToDiskCache(page.getImageUrl(), resp)) {
|
||||
throw new IllegalStateException("Unable to save image");
|
||||
}
|
||||
return Observable.just(page);
|
||||
});
|
||||
}
|
||||
|
||||
public Observable<Response> getImageProgressResponse(final Page page) {
|
||||
return mNetworkService.getProgressResponse(page.getImageUrl(), mRequestHeaders, page);
|
||||
}
|
||||
|
||||
public void savePageList(String chapterUrl, List<Page> pages) {
|
||||
if (pages != null)
|
||||
mCacheManager.putPageUrlsToDiskCache(chapterUrl, pages);
|
||||
}
|
||||
|
||||
private List<Page> convertToPages(List<String> pageUrls) {
|
||||
List<Page> pages = new ArrayList<>();
|
||||
for (int i = 0; i < pageUrls.size(); i++) {
|
||||
pages.add(new Page(i, pageUrls.get(i)));
|
||||
}
|
||||
return pages;
|
||||
}
|
||||
|
||||
private List<Page> getFirstImageFromPageUrls(List<String> pageUrls, String unparsedHtml) {
|
||||
List<Page> pages = convertToPages(pageUrls);
|
||||
String firstImage = parseHtmlToImageUrl(unparsedHtml);
|
||||
pages.get(0).setImageUrl(firstImage);
|
||||
return pages;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,9 +1,9 @@
|
||||
package eu.kanade.mangafeed.data.models;
|
||||
package eu.kanade.mangafeed.data.source.model;
|
||||
|
||||
import eu.kanade.mangafeed.data.helpers.NetworkHelper;
|
||||
import eu.kanade.mangafeed.data.network.ProgressListener;
|
||||
import rx.subjects.PublishSubject;
|
||||
|
||||
public class Page implements NetworkHelper.ProgressListener {
|
||||
public class Page implements ProgressListener {
|
||||
|
||||
private int pageNumber;
|
||||
private String url;
|
||||
@@ -0,0 +1,402 @@
|
||||
package eu.kanade.mangafeed.data.source.online.english;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import com.squareup.okhttp.FormEncodingBuilder;
|
||||
import com.squareup.okhttp.Headers;
|
||||
import com.squareup.okhttp.Response;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.net.HttpCookie;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import eu.kanade.mangafeed.data.source.SourceManager;
|
||||
import eu.kanade.mangafeed.data.database.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.data.source.base.Source;
|
||||
import rx.Observable;
|
||||
|
||||
public class Batoto extends Source {
|
||||
|
||||
public static final String NAME = "Batoto (EN)";
|
||||
public static final String BASE_URL = "http://bato.to";
|
||||
public static final String INITIAL_UPDATE_URL =
|
||||
"http://bato.to/search_ajax?order_cond=views&order=desc&p=";
|
||||
public static final String INITIAL_SEARCH_URL = "http://bato.to/search_ajax?";
|
||||
public static final String INITIAL_PAGE_URL = "http://bato.to/areader?";
|
||||
public static final String LOGIN_URL =
|
||||
"https://bato.to/forums/index.php?app=core&module=global§ion=login";
|
||||
|
||||
|
||||
public Batoto(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Headers.Builder headersBuilder() {
|
||||
Headers.Builder builder = super.headersBuilder();
|
||||
builder.add("Cookie", "lang_option=English");
|
||||
builder.add("Referer", "http://bato.to/reader");
|
||||
return builder;
|
||||
}
|
||||
|
||||
public Observable<List<String>> getGenres() {
|
||||
List<String> genres = new ArrayList<>(38);
|
||||
|
||||
genres.add("4-Koma");
|
||||
genres.add("Action");
|
||||
genres.add("Adventure");
|
||||
genres.add("Award Winning");
|
||||
genres.add("Comedy");
|
||||
genres.add("Cooking");
|
||||
genres.add("Doujinshi");
|
||||
genres.add("Drama");
|
||||
genres.add("Ecchi");
|
||||
genres.add("Fantasy");
|
||||
genres.add("Gender Bender");
|
||||
genres.add("Harem");
|
||||
genres.add("Historical");
|
||||
genres.add("Horror");
|
||||
genres.add("Josei");
|
||||
genres.add("Martial Arts");
|
||||
genres.add("Mecha");
|
||||
genres.add("Medical");
|
||||
genres.add("Music");
|
||||
genres.add("Mystery");
|
||||
genres.add("One Shot");
|
||||
genres.add("Psychological");
|
||||
genres.add("Romance");
|
||||
genres.add("School Life");
|
||||
genres.add("Sci-fi");
|
||||
genres.add("Seinen");
|
||||
genres.add("Shoujo");
|
||||
genres.add("Shoujo Ai");
|
||||
genres.add("Shounen");
|
||||
genres.add("Shounen Ai");
|
||||
genres.add("Slice of Life");
|
||||
genres.add("Smut");
|
||||
genres.add("Sports");
|
||||
genres.add("Supernatural");
|
||||
genres.add("Tragedy");
|
||||
genres.add("Webtoon");
|
||||
genres.add("Yaoi");
|
||||
genres.add("Yuri");
|
||||
|
||||
return Observable.just(genres);
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceId() {
|
||||
return SourceManager.BATOTO;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoginRequired() {
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUrlFromPageNumber(int page) {
|
||||
return INITIAL_UPDATE_URL + page;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSearchUrl(String query, int page) {
|
||||
return INITIAL_SEARCH_URL + "name=" + query + "&p=" + page;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String overrideMangaUrl(String defaultMangaUrl) {
|
||||
String mangaId = defaultMangaUrl.substring(defaultMangaUrl.lastIndexOf("r") + 1);
|
||||
return "http://bato.to/comic_pop?id=" + mangaId;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String overrideChapterPageUrl(String defaultPageUrl) {
|
||||
String id = defaultPageUrl.substring(defaultPageUrl.indexOf("#") + 1);
|
||||
return INITIAL_PAGE_URL + "id=" + id + "&p=1";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String overrideRemainingPagesUrl(String defaultPageUrl) {
|
||||
int start = defaultPageUrl.indexOf("#") + 1;
|
||||
int end = defaultPageUrl.indexOf("_", start);
|
||||
String id = defaultPageUrl.substring(start, end);
|
||||
return INITIAL_PAGE_URL + "id=" + id + "&p=" + defaultPageUrl.substring(end+1);
|
||||
}
|
||||
|
||||
private List<Manga> parseMangasFromHtml(String unparsedHtml) {
|
||||
if (unparsedHtml.contains("No (more) comics found!")) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
List<Manga> updatedMangaList = new ArrayList<>();
|
||||
|
||||
Elements updatedHtmlBlocks = parsedDocument.select("tr:not([id]):not([class])");
|
||||
for (Element currentHtmlBlock : updatedHtmlBlocks) {
|
||||
Manga currentlyUpdatedManga = constructMangaFromHtmlBlock(currentHtmlBlock);
|
||||
|
||||
updatedMangaList.add(currentlyUpdatedManga);
|
||||
}
|
||||
|
||||
return updatedMangaList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Manga> parsePopularMangasFromHtml(String unparsedHtml) {
|
||||
return parseMangasFromHtml(unparsedHtml);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Manga> parseSearchFromHtml(String unparsedHtml) {
|
||||
return parseMangasFromHtml(unparsedHtml);
|
||||
}
|
||||
|
||||
private Manga constructMangaFromHtmlBlock(Element htmlBlock) {
|
||||
Manga mangaFromHtmlBlock = new Manga();
|
||||
|
||||
Element urlElement = htmlBlock.select("a[href^=http://bato.to]").first();
|
||||
Element nameElement = urlElement;
|
||||
Element updateElement = htmlBlock.select("td").get(5);
|
||||
|
||||
mangaFromHtmlBlock.source = getSourceId();
|
||||
|
||||
if (urlElement != null) {
|
||||
String fieldUrl = urlElement.attr("href");
|
||||
mangaFromHtmlBlock.url = fieldUrl;
|
||||
}
|
||||
if (nameElement != null) {
|
||||
String fieldName = nameElement.text().trim();
|
||||
mangaFromHtmlBlock.title = fieldName;
|
||||
}
|
||||
if (updateElement != null) {
|
||||
long fieldUpdate = parseUpdateFromElement(updateElement);
|
||||
mangaFromHtmlBlock.last_update = fieldUpdate;
|
||||
}
|
||||
|
||||
return mangaFromHtmlBlock;
|
||||
}
|
||||
|
||||
private long parseUpdateFromElement(Element updateElement) {
|
||||
String updatedDateAsString = updateElement.text();
|
||||
|
||||
try {
|
||||
Date specificDate = new SimpleDateFormat("dd MMMMM yyyy - hh:mm a", Locale.ENGLISH).parse(updatedDateAsString);
|
||||
|
||||
return specificDate.getTime();
|
||||
} catch (ParseException e) {
|
||||
// Do Nothing.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Manga parseHtmlToManga(String mangaUrl, String unparsedHtml) {
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
Elements artistElements = parsedDocument.select("a[href^=http://bato.to/search?artist_name]");
|
||||
Element descriptionElement = parsedDocument.select("tr").get(5);
|
||||
Elements genreElements = parsedDocument.select("img[src=http://bato.to/forums/public/style_images/master/bullet_black.png]");
|
||||
Element thumbnailUrlElement = parsedDocument.select("img[src^=http://img.bato.to/forums/uploads/]").first();
|
||||
|
||||
Manga newManga = new Manga();
|
||||
newManga.url = mangaUrl;
|
||||
|
||||
if (artistElements != null) {
|
||||
newManga.author = artistElements.get(0).text();
|
||||
if (artistElements.size() > 1) {
|
||||
newManga.artist = artistElements.get(1).text();
|
||||
} else {
|
||||
newManga.artist = newManga.author;
|
||||
}
|
||||
}
|
||||
if (descriptionElement != null) {
|
||||
String fieldDescription = descriptionElement.text().substring("Description:".length()).trim();
|
||||
newManga.description = fieldDescription;
|
||||
}
|
||||
if (genreElements != null) {
|
||||
String fieldGenres = "";
|
||||
for (int index = 0; index < genreElements.size(); index++) {
|
||||
String currentGenre = genreElements.get(index).attr("alt");
|
||||
|
||||
if (index < genreElements.size() - 1) {
|
||||
fieldGenres += currentGenre + ", ";
|
||||
} else {
|
||||
fieldGenres += currentGenre;
|
||||
}
|
||||
}
|
||||
newManga.genre = fieldGenres;
|
||||
}
|
||||
if (thumbnailUrlElement != null) {
|
||||
String fieldThumbnailUrl = thumbnailUrlElement.attr("src");
|
||||
newManga.thumbnail_url = fieldThumbnailUrl;
|
||||
}
|
||||
|
||||
boolean fieldCompleted = unparsedHtml.contains("<td>Complete</td>");
|
||||
//TODO fix
|
||||
newManga.status = fieldCompleted + "";
|
||||
|
||||
newManga.initialized = true;
|
||||
|
||||
return newManga;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Chapter> parseHtmlToChapters(String unparsedHtml) {
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
List<Chapter> chapterList = new ArrayList<>();
|
||||
|
||||
Elements chapterElements = parsedDocument.select("tr.row.lang_English.chapter_row");
|
||||
for (Element chapterElement : chapterElements) {
|
||||
Chapter currentChapter = constructChapterFromHtmlBlock(chapterElement);
|
||||
chapterList.add(currentChapter);
|
||||
}
|
||||
|
||||
//saveChaptersToDatabase(chapterList, mangaUrl);
|
||||
|
||||
return chapterList;
|
||||
|
||||
}
|
||||
|
||||
private Chapter constructChapterFromHtmlBlock(Element chapterElement) {
|
||||
Chapter newChapter = Chapter.newChapter();
|
||||
|
||||
Element urlElement = chapterElement.select("a[href^=http://bato.to/reader").first();
|
||||
Element nameElement = urlElement;
|
||||
Element dateElement = chapterElement.select("td").get(4);
|
||||
|
||||
if (urlElement != null) {
|
||||
String fieldUrl = urlElement.attr("href");
|
||||
newChapter.url = fieldUrl;
|
||||
}
|
||||
if (nameElement != null) {
|
||||
String fieldName = nameElement.text().trim();
|
||||
newChapter.name = fieldName;
|
||||
}
|
||||
if (dateElement != null) {
|
||||
long fieldDate = parseDateFromElement(dateElement);
|
||||
newChapter.date_upload = fieldDate;
|
||||
}
|
||||
newChapter.date_fetch = new Date().getTime();
|
||||
|
||||
return newChapter;
|
||||
}
|
||||
|
||||
private long parseDateFromElement(Element dateElement) {
|
||||
String dateAsString = dateElement.text();
|
||||
|
||||
try {
|
||||
Date specificDate = new SimpleDateFormat("dd MMMMM yyyy - hh:mm a", Locale.ENGLISH).parse(dateAsString);
|
||||
|
||||
return specificDate.getTime();
|
||||
} catch (ParseException e) {
|
||||
// Do Nothing.
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> parseHtmlToPageUrls(String unparsedHtml) {
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
List<String> pageUrlList = new ArrayList<>();
|
||||
|
||||
Elements pageUrlElements = parsedDocument.getElementById("page_select").getElementsByTag("option");
|
||||
for (Element pageUrlElement : pageUrlElements) {
|
||||
pageUrlList.add(pageUrlElement.attr("value"));
|
||||
}
|
||||
|
||||
return pageUrlList;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseHtmlToImageUrl(String unparsedHtml) {
|
||||
int beginIndex = unparsedHtml.indexOf("<img id=\"comic_page\"");
|
||||
int endIndex = unparsedHtml.indexOf("</a>", beginIndex);
|
||||
String trimmedHtml = unparsedHtml.substring(beginIndex, endIndex);
|
||||
|
||||
Document parsedDocument = Jsoup.parse(trimmedHtml);
|
||||
|
||||
Element imageElement = parsedDocument.getElementById("comic_page");
|
||||
|
||||
return imageElement.attr("src");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<Boolean> login(String username, String password) {
|
||||
return mNetworkService.getStringResponse(LOGIN_URL, mRequestHeaders, null)
|
||||
.flatMap(response -> doLogin(response, username, password))
|
||||
.map(this::isAuthenticationSuccessful);
|
||||
}
|
||||
|
||||
private Observable<Response> doLogin(String response, String username, String password) {
|
||||
Document doc = Jsoup.parse(response);
|
||||
Element form = doc.select("#login").first();
|
||||
String postUrl = form.attr("action");
|
||||
|
||||
FormEncodingBuilder formBody = new FormEncodingBuilder();
|
||||
Element authKey = form.select("input[name=auth_key").first();
|
||||
|
||||
formBody.add(authKey.attr("name"), authKey.attr("value"));
|
||||
formBody.add("ips_username", username);
|
||||
formBody.add("ips_password", password);
|
||||
formBody.add("invisible", "1");
|
||||
formBody.add("rememberMe", "1");
|
||||
|
||||
return mNetworkService.postData(postUrl, formBody.build(), mRequestHeaders);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected boolean isAuthenticationSuccessful(Response response) {
|
||||
return response.priorResponse() != null && response.priorResponse().code() == 302;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLogged() {
|
||||
try {
|
||||
for ( HttpCookie cookie : mNetworkService.getCookies().get(new URI(BASE_URL)) ) {
|
||||
if (cookie.getName().equals("pass_hash"))
|
||||
return true;
|
||||
}
|
||||
|
||||
} catch (URISyntaxException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Observable<List<Chapter>> pullChaptersFromNetwork(String mangaUrl) {
|
||||
Observable<List<Chapter>> observable;
|
||||
if (!isLogged()) {
|
||||
observable = login(prefs.getSourceUsername(this), prefs.getSourcePassword(this))
|
||||
.flatMap(result -> super.pullChaptersFromNetwork(mangaUrl));
|
||||
}
|
||||
else {
|
||||
observable = super.pullChaptersFromNetwork(mangaUrl);
|
||||
}
|
||||
return observable;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package eu.kanade.mangafeed.data.source.online.english;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import eu.kanade.mangafeed.data.source.SourceManager;
|
||||
import eu.kanade.mangafeed.data.database.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.data.source.base.Source;
|
||||
|
||||
public class Mangafox extends Source {
|
||||
|
||||
public static final String NAME = "Mangafox (EN)";
|
||||
|
||||
private static final String INITIAL_UPDATE_URL = "http://mangafox.me/directory/";
|
||||
private static final String INITIAL_SEARCH_URL =
|
||||
"http://mangafox.me/search.php?name_method=cw&advopts=1&order=az&sort=name";
|
||||
|
||||
public Mangafox(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceId() {
|
||||
return SourceManager.MANGAFOX;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLoginRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUrlFromPageNumber(int page) {
|
||||
return INITIAL_UPDATE_URL + page + ".htm";
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Manga> parsePopularMangasFromHtml(String unparsedHtml) {
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
List<Manga> mangaList = new ArrayList<>();
|
||||
|
||||
Elements mangaHtmlBlocks = parsedDocument.select("div#mangalist > ul.list > li");
|
||||
for (Element currentHtmlBlock : mangaHtmlBlocks) {
|
||||
Manga currentManga = constructPopularMangaFromHtmlBlock(currentHtmlBlock);
|
||||
mangaList.add(currentManga);
|
||||
}
|
||||
|
||||
return mangaList;
|
||||
}
|
||||
|
||||
private Manga constructPopularMangaFromHtmlBlock(Element htmlBlock) {
|
||||
Manga mangaFromHtmlBlock = new Manga();
|
||||
mangaFromHtmlBlock.source = getSourceId();
|
||||
|
||||
Element urlElement = htmlBlock.select("a.title").first();
|
||||
|
||||
if (urlElement != null) {
|
||||
mangaFromHtmlBlock.url = urlElement.attr("href");
|
||||
mangaFromHtmlBlock.title = urlElement.text();
|
||||
}
|
||||
|
||||
return mangaFromHtmlBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSearchUrl(String query, int page) {
|
||||
return INITIAL_SEARCH_URL + "&name=" + query + "&page=" + page;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Manga> parseSearchFromHtml(String unparsedHtml) {
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
List<Manga> mangaList = new ArrayList<>();
|
||||
|
||||
Elements mangaHtmlBlocks = parsedDocument.select("table#listing > tbody > tr:gt(0)");
|
||||
for (Element currentHtmlBlock : mangaHtmlBlocks) {
|
||||
Manga currentManga = constructSearchMangaFromHtmlBlock(currentHtmlBlock);
|
||||
mangaList.add(currentManga);
|
||||
}
|
||||
|
||||
return mangaList;
|
||||
}
|
||||
|
||||
private Manga constructSearchMangaFromHtmlBlock(Element htmlBlock) {
|
||||
Manga mangaFromHtmlBlock = new Manga();
|
||||
mangaFromHtmlBlock.source = getSourceId();
|
||||
|
||||
Element urlElement = htmlBlock.select("a.series_preview").first();
|
||||
|
||||
if (urlElement != null) {
|
||||
mangaFromHtmlBlock.url = urlElement.attr("href");
|
||||
mangaFromHtmlBlock.title = urlElement.text();
|
||||
}
|
||||
|
||||
return mangaFromHtmlBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Manga parseHtmlToManga(String mangaUrl, String unparsedHtml) {
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
Element infoElement = parsedDocument.select("div#title").first();
|
||||
Element titleElement = infoElement.select("h2 > a").first();
|
||||
Element rowElement = infoElement.select("table > tbody > tr:eq(1)").first();
|
||||
Element authorElement = rowElement.select("td:eq(1)").first();
|
||||
Element artistElement = rowElement.select("td:eq(2)").first();
|
||||
Element genreElement = rowElement.select("td:eq(3)").first();
|
||||
Element descriptionElement = infoElement.select("p.summary").first();
|
||||
Element thumbnailUrlElement = parsedDocument.select("div.cover > img").first();
|
||||
|
||||
Manga newManga = new Manga();
|
||||
newManga.url = mangaUrl;
|
||||
|
||||
if (titleElement != null) {
|
||||
String title = titleElement.text();
|
||||
// Strip the last word
|
||||
title = title.substring(0, title.lastIndexOf(" "));
|
||||
newManga.title = title;
|
||||
}
|
||||
if (artistElement != null) {
|
||||
String fieldArtist = artistElement.text();
|
||||
newManga.artist = fieldArtist;
|
||||
}
|
||||
if (authorElement != null) {
|
||||
String fieldAuthor = authorElement.text();
|
||||
newManga.author = fieldAuthor;
|
||||
}
|
||||
if (descriptionElement != null) {
|
||||
String fieldDescription = descriptionElement.text();
|
||||
newManga.description = fieldDescription;
|
||||
}
|
||||
if (genreElement != null) {
|
||||
String fieldGenre = genreElement.text();
|
||||
newManga.genre = fieldGenre;
|
||||
}
|
||||
if (thumbnailUrlElement != null) {
|
||||
String fieldThumbnailUrl = thumbnailUrlElement.attr("src");
|
||||
newManga.thumbnail_url = fieldThumbnailUrl;
|
||||
}
|
||||
// if (statusElement != null) {
|
||||
// boolean fieldCompleted = statusElement.text().contains("Completed");
|
||||
// newManga.status = fieldCompleted + "";
|
||||
// }
|
||||
|
||||
newManga.initialized = true;
|
||||
|
||||
return newManga;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Chapter> parseHtmlToChapters(String unparsedHtml) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<String> parseHtmlToPageUrls(String unparsedHtml) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String parseHtmlToImageUrl(String unparsedHtml) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,374 @@
|
||||
package eu.kanade.mangafeed.data.source.online.english;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.nodes.Element;
|
||||
import org.jsoup.select.Elements;
|
||||
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
|
||||
import eu.kanade.mangafeed.data.source.SourceManager;
|
||||
import eu.kanade.mangafeed.data.database.models.Chapter;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.data.source.base.Source;
|
||||
import rx.Observable;
|
||||
|
||||
public class Mangahere extends Source {
|
||||
|
||||
public static final String NAME = "Mangahere (EN)";
|
||||
public static final String BASE_URL = "www.mangahere.co";
|
||||
|
||||
private static final String INITIAL_UPDATE_URL = "http://www.mangahere.co/directory/";
|
||||
private static final String INITIAL_SEARCH_URL = "http://www.mangahere.co/search.php?";
|
||||
|
||||
public Mangahere(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getName() {
|
||||
return NAME;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getSourceId() {
|
||||
return SourceManager.MANGAHERE;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getUrlFromPageNumber(int page) {
|
||||
return INITIAL_UPDATE_URL + page + ".htm?views.za";
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getSearchUrl(String query, int page) {
|
||||
return INITIAL_SEARCH_URL + "name=" + query + "&page=" + page;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isLoginRequired() {
|
||||
return false;
|
||||
}
|
||||
|
||||
public Observable<List<String>> getGenres() {
|
||||
List<String> genres = new ArrayList<>(30);
|
||||
|
||||
genres.add("Action");
|
||||
genres.add("Adventure");
|
||||
genres.add("Comedy");
|
||||
genres.add("Drama");
|
||||
genres.add("Ecchi");
|
||||
genres.add("Fantasy");
|
||||
genres.add("Gender Bender");
|
||||
genres.add("Harem");
|
||||
genres.add("Historical");
|
||||
genres.add("Horror");
|
||||
genres.add("Josei");
|
||||
genres.add("Martial Arts");
|
||||
genres.add("Mature");
|
||||
genres.add("Mecha");
|
||||
genres.add("Mystery");
|
||||
genres.add("One Shot");
|
||||
genres.add("Psychological");
|
||||
genres.add("Romance");
|
||||
genres.add("School Life");
|
||||
genres.add("Sci-fi");
|
||||
genres.add("Seinen");
|
||||
genres.add("Shoujo");
|
||||
genres.add("Shoujo Ai");
|
||||
genres.add("Shounen");
|
||||
genres.add("Shounen Ai");
|
||||
genres.add("Slice of Life");
|
||||
genres.add("Sports");
|
||||
genres.add("Supernatural");
|
||||
genres.add("Tragedy");
|
||||
genres.add("Yaoi");
|
||||
genres.add("Yuri");
|
||||
|
||||
return Observable.just(genres);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Manga> parsePopularMangasFromHtml(String unparsedHtml) {
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
List<Manga> mangaList = new ArrayList<>();
|
||||
|
||||
Elements mangaHtmlBlocks = parsedDocument.select("div.directory_list > ul > li");
|
||||
for (Element currentHtmlBlock : mangaHtmlBlocks) {
|
||||
Manga currentManga = constructPopularMangaFromHtmlBlock(currentHtmlBlock);
|
||||
mangaList.add(currentManga);
|
||||
}
|
||||
|
||||
return mangaList;
|
||||
}
|
||||
|
||||
private Manga constructPopularMangaFromHtmlBlock(Element htmlBlock) {
|
||||
Manga mangaFromHtmlBlock = new Manga();
|
||||
mangaFromHtmlBlock.source = getSourceId();
|
||||
|
||||
Element urlElement = htmlBlock.select("div.title > a").first();
|
||||
|
||||
if (urlElement != null) {
|
||||
mangaFromHtmlBlock.url = urlElement.attr("href");
|
||||
mangaFromHtmlBlock.title = urlElement.attr("title");
|
||||
}
|
||||
|
||||
return mangaFromHtmlBlock;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected List<Manga> parseSearchFromHtml(String unparsedHtml) {
|
||||
Document parsedDocument = Jsoup.parse(unparsedHtml);
|
||||
|
||||
List<Manga> mangaList = new ArrayList<>();
|
||||
|
||||
Elements mangaHtmlBlocks = parsedDocument.select("div.result_search > dl");
|
||||
for (Element currentHtmlBlock : mangaHtmlBlocks) {
|
||||
Manga currentManga = constructSearchMangaFromHtmlBlock(currentHtmlBlock);
|
||||
mangaList.add(currentManga);
|
||||
}
|
||||
|
||||
return mangaList;
|
||||
}
|
||||
|
||||
private Manga constructSearchMangaFromHtmlBlock(Element htmlBlock) {
|
||||
Manga mangaFromHtmlBlock = new Manga();
|
||||
mangaFromHtmlBlock.source = getSourceId();
|
||||
|
||||
Element urlElement = htmlBlock.select("a.manga_info").first();
|
||||
|
||||
if (urlElement != null) {
|
||||
mangaFromHtmlBlock.url = urlElement.attr("href");
|
||||
mangaFromHtmlBlock.title = urlElement.text();
|
||||
}
|
||||
|
||||
return mangaFromHtmlBlock;
|
||||
}
|
||||
|
||||
private long parseUpdateFromElement(Element updateElement) {
|
||||
String updatedDateAsString = updateElement.text();
|
||||
|
||||
if (updatedDateAsString.contains("Today")) {
|
||||
Calendar today = Calendar.getInstance();
|
||||
today.set(Calendar.HOUR_OF_DAY, 0);
|
||||
today.set(Calendar.MINUTE, 0);
|
||||
today.set(Calendar.SECOND, 0);
|
||||
today.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
try {
|
||||
Date withoutDay = new SimpleDateFormat("MMM d, yyyy h:mma", Locale.ENGLISH).parse(updatedDateAsString.replace("Today", ""));
|
||||
return today.getTimeInMillis() + withoutDay.getTime();
|
||||
} catch (ParseException e) {
|
||||
return today.getTimeInMillis();
|
||||
}
|
||||
} else if (updatedDateAsString.contains("Yesterday")) {
|
||||
Calendar yesterday = Calendar.getInstance();
|
||||
yesterday.add(Calendar.DATE, -1);
|
||||
yesterday.set(Calendar.HOUR_OF_DAY, 0);
|
||||
yesterday.set(Calendar.MINUTE, 0);
|
||||
yesterday.set(Calendar.SECOND, 0);
|
||||
yesterday.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
try {
|
||||
Date withoutDay = new SimpleDateFormat("MMM d, yyyy h:mma", Locale.ENGLISH).parse(updatedDateAsString.replace("Yesterday", ""));
|
||||
return yesterday.getTimeInMillis() + withoutDay.getTime();
|
||||
} catch (ParseException e) {
|
||||
return yesterday.getTimeInMillis();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Date specificDate = new SimpleDateFormat("MMM d, yyyy h:mma", Locale.ENGLISH).parse(updatedDateAsString);
|
||||
|
||||
return specificDate.getTime();
|
||||
} catch (ParseException e) {
|
||||
// Do Nothing.
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public Manga parseHtmlToManga(String mangaUrl, String unparsedHtml) {
|
||||
int beginIndex = unparsedHtml.indexOf("<ul class=\"detail_topText\">");
|
||||
int endIndex = unparsedHtml.indexOf("</ul>", beginIndex);
|
||||
String trimmedHtml = unparsedHtml.substring(beginIndex, endIndex);
|
||||
|
||||
Document parsedDocument = Jsoup.parse(trimmedHtml);
|
||||
|
||||
Elements detailElements = parsedDocument.select("ul.detail_topText li");
|
||||
|
||||
Element artistElement = parsedDocument.select("a[href^=http://www.mangahere.co/artist/]").first();
|
||||
Element authorElement = parsedDocument.select("a[href^=http://www.mangahere.co/author/]").first();
|
||||
Element descriptionElement = detailElements.select("#show").first();
|
||||
Element genreElement = detailElements.get(3);
|
||||
Element statusElement = detailElements.get(6);
|
||||
|
||||
Manga newManga = new Manga();
|
||||
newManga.url = mangaUrl;
|
||||
|
||||
if (artistElement != null) {
|
||||
String fieldArtist = artistElement.text();
|
||||
newManga.artist = fieldArtist;
|
||||
}
|
||||
if (authorElement != null) {
|
||||
String fieldAuthor = authorElement.text();
|
||||
newManga.author = fieldAuthor;
|
||||
}
|
||||
if (descriptionElement != null) {
|
||||
String fieldDescription = descriptionElement.text().substring(0, descriptionElement.text().length() - "Show less".length());
|
||||
newManga.description = fieldDescription;
|
||||
}
|
||||
if (genreElement != null) {
|
||||
String fieldGenre = genreElement.text().substring("Genre(s):".length());
|
||||
newManga.genre = fieldGenre;
|
||||
}
|
||||
if (statusElement != null) {
|
||||
boolean fieldCompleted = statusElement.text().contains("Completed");
|
||||
newManga.status = fieldCompleted + "";
|
||||
}
|
||||
|
||||
beginIndex = unparsedHtml.indexOf("<img");
|
||||
endIndex = unparsedHtml.indexOf("/>", beginIndex);
|
||||
trimmedHtml = unparsedHtml.substring(beginIndex, endIndex + 2);
|
||||
parsedDocument = Jsoup.parse(trimmedHtml);
|
||||
Element thumbnailUrlElement = parsedDocument.select("img").first();
|
||||
|
||||
if (thumbnailUrlElement != null) {
|
||||
String fieldThumbnailUrl = thumbnailUrlElement.attr("src");
|
||||
newManga.thumbnail_url = fieldThumbnailUrl;
|
||||
}
|
||||
|
||||
newManga.initialized = true;
|
||||
|
||||
return newManga;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Chapter> parseHtmlToChapters(String unparsedHtml) {
|
||||
int beginIndex = unparsedHtml.indexOf("<ul>");
|
||||
int endIndex = unparsedHtml.indexOf("</ul>", beginIndex);
|
||||
String trimmedHtml = unparsedHtml.substring(beginIndex, endIndex);
|
||||
|
||||
Document parsedDocument = Jsoup.parse(trimmedHtml);
|
||||
|
||||
List<Chapter> chapterList = new ArrayList<Chapter>();
|
||||
|
||||
Elements chapterElements = parsedDocument.getElementsByTag("li");
|
||||
for (Element chapterElement : chapterElements) {
|
||||
Chapter currentChapter = constructChapterFromHtmlBlock(chapterElement);
|
||||
|
||||
chapterList.add(currentChapter);
|
||||
}
|
||||
|
||||
return chapterList;
|
||||
}
|
||||
|
||||
private Chapter constructChapterFromHtmlBlock(Element chapterElement) {
|
||||
Chapter newChapter = Chapter.newChapter();
|
||||
|
||||
Element urlElement = chapterElement.select("a").first();
|
||||
Element nameElement = chapterElement.select("a").first();
|
||||
Element dateElement = chapterElement.select("span.right").first();
|
||||
|
||||
if (urlElement != null) {
|
||||
String fieldUrl = urlElement.attr("href");
|
||||
newChapter.url = fieldUrl;
|
||||
}
|
||||
if (nameElement != null) {
|
||||
String fieldName = nameElement.text();
|
||||
newChapter.name = fieldName;
|
||||
}
|
||||
if (dateElement != null) {
|
||||
long fieldDate = parseDateFromElement(dateElement);
|
||||
newChapter.date_upload = fieldDate;
|
||||
}
|
||||
newChapter.date_fetch = new Date().getTime();
|
||||
|
||||
return newChapter;
|
||||
}
|
||||
|
||||
private long parseDateFromElement(Element dateElement) {
|
||||
String dateAsString = dateElement.text();
|
||||
|
||||
if (dateAsString.contains("Today")) {
|
||||
Calendar today = Calendar.getInstance();
|
||||
today.set(Calendar.HOUR_OF_DAY, 0);
|
||||
today.set(Calendar.MINUTE, 0);
|
||||
today.set(Calendar.SECOND, 0);
|
||||
today.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
try {
|
||||
Date withoutDay = new SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(dateAsString.replace("Today", ""));
|
||||
return today.getTimeInMillis() + withoutDay.getTime();
|
||||
} catch (ParseException e) {
|
||||
return today.getTimeInMillis();
|
||||
}
|
||||
} else if (dateAsString.contains("Yesterday")) {
|
||||
Calendar yesterday = Calendar.getInstance();
|
||||
yesterday.add(Calendar.DATE, -1);
|
||||
yesterday.set(Calendar.HOUR_OF_DAY, 0);
|
||||
yesterday.set(Calendar.MINUTE, 0);
|
||||
yesterday.set(Calendar.SECOND, 0);
|
||||
yesterday.set(Calendar.MILLISECOND, 0);
|
||||
|
||||
try {
|
||||
Date withoutDay = new SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(dateAsString.replace("Yesterday", ""));
|
||||
return yesterday.getTimeInMillis() + withoutDay.getTime();
|
||||
} catch (ParseException e) {
|
||||
return yesterday.getTimeInMillis();
|
||||
}
|
||||
} else {
|
||||
try {
|
||||
Date date = new SimpleDateFormat("MMM d, yyyy", Locale.ENGLISH).parse(dateAsString);
|
||||
|
||||
return date.getTime();
|
||||
} catch (ParseException e) {
|
||||
// Do Nothing.
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> parseHtmlToPageUrls(String unparsedHtml) {
|
||||
int beginIndex = unparsedHtml.indexOf("<div class=\"go_page clearfix\">");
|
||||
int endIndex = unparsedHtml.indexOf("</div>", beginIndex);
|
||||
String trimmedHtml = unparsedHtml.substring(beginIndex, endIndex);
|
||||
|
||||
Document parsedDocument = Jsoup.parse(trimmedHtml);
|
||||
|
||||
List<String> pageUrlList = new ArrayList<String>();
|
||||
|
||||
Elements pageUrlElements = parsedDocument.select("select.wid60").first().getElementsByTag("option");
|
||||
for (Element pageUrlElement : pageUrlElements) {
|
||||
pageUrlList.add(pageUrlElement.attr("value"));
|
||||
}
|
||||
|
||||
return pageUrlList;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String parseHtmlToImageUrl(String unparsedHtml) {
|
||||
int beginIndex = unparsedHtml.indexOf("<section class=\"read_img\" id=\"viewer\">");
|
||||
int endIndex = unparsedHtml.indexOf("</section>", beginIndex);
|
||||
String trimmedHtml = unparsedHtml.substring(beginIndex, endIndex);
|
||||
|
||||
Document parsedDocument = Jsoup.parse(trimmedHtml);
|
||||
|
||||
Element imageElement = parsedDocument.getElementById("image");
|
||||
|
||||
return imageElement.attr("src");
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package eu.kanade.mangafeed.data.services;
|
||||
package eu.kanade.mangafeed.data.sync;
|
||||
|
||||
import android.app.Service;
|
||||
import android.content.BroadcastReceiver;
|
||||
@@ -15,9 +15,9 @@ import javax.inject.Inject;
|
||||
import eu.kanade.mangafeed.App;
|
||||
import eu.kanade.mangafeed.BuildConfig;
|
||||
import eu.kanade.mangafeed.R;
|
||||
import eu.kanade.mangafeed.data.helpers.DatabaseHelper;
|
||||
import eu.kanade.mangafeed.data.helpers.SourceManager;
|
||||
import eu.kanade.mangafeed.data.models.Manga;
|
||||
import eu.kanade.mangafeed.data.database.DatabaseHelper;
|
||||
import eu.kanade.mangafeed.data.source.SourceManager;
|
||||
import eu.kanade.mangafeed.data.database.models.Manga;
|
||||
import eu.kanade.mangafeed.util.AndroidComponentUtil;
|
||||
import eu.kanade.mangafeed.util.NetworkUtil;
|
||||
import eu.kanade.mangafeed.util.NotificationUtil;
|
||||
Reference in New Issue
Block a user