From 0ffa47548f474c1330d8d723300d9aa7a4894736 Mon Sep 17 00:00:00 2001 From: Niki Roo Date: Sun, 18 Mar 2018 20:16:19 +0100 Subject: [PATCH] Change BasicSupport to use jsoup --- README-fr.md | 1 + README.md | 1 + libs/jsoup-1.10.3-sources.jar | Bin 0 -> 172299 bytes src/be/nikiroo/fanfix/Main.java | 4 +- .../fanfix/bundles/StringIdBundle.java | 1 - .../nikiroo/fanfix/library/BasicLibrary.java | 8 +- src/be/nikiroo/fanfix/reader/BasicReader.java | 2 +- .../fanfix/supported/BasicSupport.java | 1636 +++-------------- .../fanfix/supported/BasicSupportHelper.java | 225 +++ .../fanfix/supported/BasicSupportPara.java | 571 ++++++ .../supported/BasicSupport_Deprecated.java | 1325 +++++++++++++ src/be/nikiroo/fanfix/supported/E621.java | 2 +- src/be/nikiroo/fanfix/supported/EHentai.java | 2 +- src/be/nikiroo/fanfix/supported/Epub.java | 8 +- .../nikiroo/fanfix/supported/Fanfiction.java | 4 +- .../nikiroo/fanfix/supported/Fimfiction.java | 2 +- .../fanfix/supported/FimfictionApi.java | 2 +- src/be/nikiroo/fanfix/supported/Html.java | 12 +- .../nikiroo/fanfix/supported/InfoReader.java | 10 +- src/be/nikiroo/fanfix/supported/MangaFox.java | 5 +- .../nikiroo/fanfix/supported/SupportType.java | 117 ++ src/be/nikiroo/fanfix/supported/Text.java | 4 +- src/be/nikiroo/fanfix/supported/YiffStar.java | 28 +- .../nikiroo/fanfix/test/BasicSupportTest.java | 19 +- 24 files changed, 2557 insertions(+), 1432 deletions(-) create mode 100644 libs/jsoup-1.10.3-sources.jar create mode 100644 src/be/nikiroo/fanfix/supported/BasicSupportHelper.java create mode 100644 src/be/nikiroo/fanfix/supported/BasicSupportPara.java create mode 100644 src/be/nikiroo/fanfix/supported/BasicSupport_Deprecated.java create mode 100644 src/be/nikiroo/fanfix/supported/SupportType.java diff --git a/README-fr.md b/README-fr.md index d45f6f3..59837e4 100644 --- a/README-fr.md +++ b/README-fr.md @@ -94,6 +94,7 @@ Quelques tests unitaires sont disponibles : Nécessaires : - libs/nikiroo-utils-sources.jar: quelques utilitaires partagés - [libs/unbescape-sources.jar](https://github.com/unbescape/unbescape): une librairie sympathique pour convertir du texte depuis/vers beaucoup de formats ; utilisée ici pour la partie HTML +- [libs/jsoup-sources.jar](https://jsoup.org/): une libraririe pour parser du HTML Optionnelles : - [libs/jexer-sources.jar](https://github.com/klamonte/jexer): une petite librairie qui offre des widgets en mode TUI diff --git a/README.md b/README.md index eeb1a87..e231268 100644 --- a/README.md +++ b/README.md @@ -94,6 +94,7 @@ There are some unit tests you can run, too: - libs/nikiroo-utils-sources.jar: some shared utility functions - [libs/unbescape-sources.jar](https://github.com/unbescape/unbescape): a nice library to escape/unescape a lot of text formats; used here for HTML - [libs/jexer-sources.jar](https://github.com/klamonte/jexer): a small library that offers TUI widgets +- [libs/jsoup-sources.jar](https://jsoup.org/): a library to parse HTML Nothing else but Java 1.6+. diff --git a/libs/jsoup-1.10.3-sources.jar b/libs/jsoup-1.10.3-sources.jar new file mode 100644 index 0000000000000000000000000000000000000000..1fe0db4e2e12fc9f240b7eb58a5baf30b6e78120 GIT binary patch literal 172299 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 z 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 { */ public Locale getLanguage() { return getLocaleFor(lang); - } /** diff --git a/src/be/nikiroo/fanfix/library/BasicLibrary.java b/src/be/nikiroo/fanfix/library/BasicLibrary.java index 8c86fc9..350a8ca 100644 --- a/src/be/nikiroo/fanfix/library/BasicLibrary.java +++ b/src/be/nikiroo/fanfix/library/BasicLibrary.java @@ -14,7 +14,7 @@ import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.output.BasicOutput; import be.nikiroo.fanfix.output.BasicOutput.OutputType; import be.nikiroo.fanfix.supported.BasicSupport; -import be.nikiroo.fanfix.supported.BasicSupport.SupportType; +import be.nikiroo.fanfix.supported.SupportType; import be.nikiroo.utils.Image; import be.nikiroo.utils.Progress; @@ -343,8 +343,8 @@ abstract public class BasicLibrary { .getType()); URL url = file.toURI().toURL(); if (type != null) { - story = BasicSupport.getSupport(type).process(url, - pgProcess); + story = BasicSupport.getSupport(type, url) // + .process(pgProcess); // Because we do not want to clear the meta cache: meta.setCover(story.getMeta().getCover()); story.setMeta(meta); @@ -392,7 +392,7 @@ abstract public class BasicLibrary { throw new UnknownHostException("" + url); } - return save(support.process(url, pg), null); + return save(support.process(pg), null); } /** diff --git a/src/be/nikiroo/fanfix/reader/BasicReader.java b/src/be/nikiroo/fanfix/reader/BasicReader.java index 74a99c4..9d5d5bb 100644 --- a/src/be/nikiroo/fanfix/reader/BasicReader.java +++ b/src/be/nikiroo/fanfix/reader/BasicReader.java @@ -98,7 +98,7 @@ public abstract class BasicReader implements Reader { throw new IOException("URL not supported: " + source.toString()); } - story = support.process(source, pg); + story = support.process(pg); if (story == null) { throw new IOException( "Cannot retrieve story from external source: " diff --git a/src/be/nikiroo/fanfix/supported/BasicSupport.java b/src/be/nikiroo/fanfix/supported/BasicSupport.java index 0c127aa..4314b99 100644 --- a/src/be/nikiroo/fanfix/supported/BasicSupport.java +++ b/src/be/nikiroo/fanfix/supported/BasicSupport.java @@ -1,12 +1,7 @@ package be.nikiroo.fanfix.supported; -import java.io.BufferedReader; -import java.io.ByteArrayInputStream; -import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.InputStreamReader; -import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Date; @@ -14,17 +9,17 @@ import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Scanner; + +import org.jsoup.helper.DataUtil; +import org.jsoup.nodes.Document; +import org.jsoup.nodes.Element; +import org.jsoup.nodes.Node; import be.nikiroo.fanfix.Instance; -import be.nikiroo.fanfix.bundles.Config; import be.nikiroo.fanfix.bundles.StringId; import be.nikiroo.fanfix.data.Chapter; import be.nikiroo.fanfix.data.MetaData; -import be.nikiroo.fanfix.data.Paragraph; -import be.nikiroo.fanfix.data.Paragraph.ParagraphType; import be.nikiroo.fanfix.data.Story; -import be.nikiroo.utils.Image; import be.nikiroo.utils.Progress; import be.nikiroo.utils.StringUtils; @@ -38,136 +33,11 @@ import be.nikiroo.utils.StringUtils; * @author niki */ public abstract class BasicSupport { - /** - * The supported input types for which we can get a {@link BasicSupport} - * object. - * - * @author niki - */ - public enum SupportType { - /** EPUB files created with this program */ - EPUB, - /** Pure text file with some rules */ - TEXT, - /** TEXT but with associated .info file */ - INFO_TEXT, - /** My Little Pony fanfictions */ - FIMFICTION, - /** Fanfictions from a lot of different universes */ - FANFICTION, - /** Website with lots of Mangas */ - MANGAFOX, - /** Furry website with comics support */ - E621, - /** Furry website with stories */ - YIFFSTAR, - /** Comics and images groups, mostly but not only NSFW */ - E_HENTAI, - /** CBZ files */ - CBZ, - /** HTML files */ - HTML; - - /** - * A description of this support type (more information than the - * {@link BasicSupport#getSourceName()}). - * - * @return the description - */ - public String getDesc() { - String desc = Instance.getTrans().getStringX(StringId.INPUT_DESC, - this.name()); - - if (desc == null) { - desc = Instance.getTrans().getString(StringId.INPUT_DESC, this); - } - - return desc; - } - - /** - * The name of this support type (a short version). - * - * @return the name - */ - public String getSourceName() { - BasicSupport support = BasicSupport.getSupport(this); - if (support != null) { - return support.getSourceName(); - } - - return null; - } - - @Override - public String toString() { - return super.toString().toLowerCase(); - } - - /** - * Call {@link SupportType#valueOf(String)} after conversion to upper - * case. - * - * @param typeName - * the possible type name - * - * @return NULL or the type - */ - public static SupportType valueOfUC(String typeName) { - return SupportType.valueOf(typeName == null ? null : typeName - .toUpperCase()); - } - - /** - * Call {@link SupportType#valueOf(String)} after conversion to upper - * case but return NULL for NULL instead of raising exception. - * - * @param typeName - * the possible type name - * - * @return NULL or the type - */ - public static SupportType valueOfNullOkUC(String typeName) { - if (typeName == null) { - return null; - } - - return SupportType.valueOfUC(typeName); - } - - /** - * Call {@link SupportType#valueOf(String)} after conversion to upper - * case but return NULL in case of error instead of raising an - * exception. - * - * @param typeName - * the possible type name - * - * @return NULL or the type - */ - public static SupportType valueOfAllOkUC(String typeName) { - try { - return SupportType.valueOfUC(typeName); - } catch (Exception e) { - return null; - } - } - } - - private InputStream in; + private Document sourceNode; + private URL source; private SupportType type; private URL currentReferer; // with only one 'r', as in 'HTTP'... - // quote chars - private char openQuote = Instance.getTrans().getCharacter( - StringId.OPEN_SINGLE_QUOTE); - private char closeQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_SINGLE_QUOTE); - private char openDoubleQuote = Instance.getTrans().getCharacter( - StringId.OPEN_DOUBLE_QUOTE); - private char closeDoubleQuote = Instance.getTrans().getCharacter( - StringId.CLOSE_DOUBLE_QUOTE); - /** * The name of this support class. * @@ -196,61 +66,45 @@ public abstract class BasicSupport { /** * Return the {@link MetaData} of this story. * - * @param source - * the source of the story - * @param in - * the input (the main resource) - * * @return the associated {@link MetaData}, never NULL * * @throws IOException * in case of I/O error */ - protected abstract MetaData getMeta(URL source, InputStream in) - throws IOException; + protected abstract MetaData getMeta() throws IOException; /** * Return the story description. * - * @param source - * the source of the story - * @param in - * the input (the main resource) - * * @return the description * * @throws IOException * in case of I/O error */ - protected abstract String getDesc(URL source, InputStream in) - throws IOException; + protected abstract String getDesc() throws IOException; /** - * Return the list of chapters (name and resource). + * Return the list of chapters (name and resource). * + *

+ * Can be NULL if this {@link BasicSupport} do no use chapters. * - * @param source - * the source of the story - * @param in - * the input (the main resource) * @param pg * the optional progress reporter * - * @return the chapters + * @return the chapters or NULL * * @throws IOException * in case of I/O error */ - protected abstract List> getChapters(URL source, - InputStream in, Progress pg) throws IOException; + protected abstract List> getChapters(Progress pg) + throws IOException; /** * Return the content of the chapter (possibly HTML encoded, if * {@link BasicSupport#isHtml()} is TRUE). * - * @param source - * the source of the story - * @param in - * the input (the main resource) + * @param chapUrl + * the chapter {@link URL} * @param number * the chapter number * @param pg @@ -261,18 +115,8 @@ public abstract class BasicSupport { * @throws IOException * in case of I/O error */ - protected abstract String getChapterContent(URL source, InputStream in, - int number, Progress pg) throws IOException; - - /** - * Log into the support (can be a no-op depending upon the support). - * - * @throws IOException - * in case of I/O error - */ - @SuppressWarnings("unused") - public void login() throws IOException { - } + protected abstract String getChapterContent(URL chapUrl, int number, + Progress pg) throws IOException; /** * Return the list of cookies (values included) that must be used to @@ -300,42 +144,146 @@ public abstract class BasicSupport { * Return the canonical form of the main {@link URL}. * * @param source + * the source {@link URL}, which can be NULL + * + * @return the canonical form of this {@link URL} or NULL if the source was + * NULL + */ + protected URL getCanonicalUrl(URL source) { + return source; + } + + /** + * The main {@link Node} for this {@link Story}. + * + * @return the node + */ + protected Element getSourceNode() { + return sourceNode; + } + + /** + * The main {@link URL} for this {@link Story}. + * + * @return the URL + */ + protected URL getSource() { + return source; + } + + /** + * The current referer {@link URL} (only one 'r', as in 'HTML'...), i.e., + * the current {@link URL} we work on. + * + * @return the referer + */ + public URL getCurrentReferer() { + return currentReferer; + } + + /** + * The current referer {@link URL} (only one 'r', as in 'HTML'...), i.e., + * the current {@link URL} we work on. + * + * @param currentReferer + * the new referer + */ + protected void setCurrentReferer(URL currentReferer) { + this.currentReferer = currentReferer; + } + + /** + * The support type. + * + * @return the type + */ + public SupportType getType() { + return type; + } + + /** + * The support type. + * + * @param type + * the new type + */ + protected void setType(SupportType type) { + this.type = type; + } + + /** + * Open an input link that will be used for the support. + *

+ * Can return NULL, in which case you are supposed to work without an + * {@link InputStream}. + * + * @param source * the source {@link URL} * - * @return the canonical form of this {@link URL} + * @return the {@link InputStream} + * + * @throws IOException + * in case of I/O error + */ + protected Document loadDocument(URL source) throws IOException { + String url = getCanonicalUrl(source).toString(); + return DataUtil.load(Instance.getCache().open(source, this, false), + "UTF-8", url.toString()); + } + + /** + * Log into the support (can be a no-op depending upon the support). * * @throws IOException * in case of I/O error */ @SuppressWarnings("unused") - public URL getCanonicalUrl(URL source) throws IOException { - return source; + protected void login() throws IOException { + } + + /** + * Prepare the support if needed before processing. + * + * @throws IOException + * on I/O error + */ + @SuppressWarnings("unused") + protected void preprocess() throws IOException { + } + + /** + * Now that we have processed the {@link Story}, close the resources if any. + */ + protected void close() { + setCurrentReferer(null); } /** * Process the given story resource into a partially filled {@link Story} * object containing the name and metadata, except for the description. * - * @param url - * the story resource - * * @return the {@link Story} * * @throws IOException * in case of I/O error */ - public Story processMeta(URL url) throws IOException { - return processMeta(url, true, false, null); + public Story processMeta() throws IOException { + Story story = null; + + preprocess(); + try { + story = processMeta(false, null); + } finally { + close(); + } + + return story; } /** * Process the given story resource into a partially filled {@link Story} * object containing the name and metadata. * - * @param url - * the story resource - * @param close - * close "this" and "in" when done * @param getDesc * retrieve the description of the story, or not * @param pg @@ -346,75 +294,48 @@ public abstract class BasicSupport { * @throws IOException * in case of I/O error */ - protected Story processMeta(URL url, boolean close, boolean getDesc, - Progress pg) throws IOException { + protected Story processMeta(boolean getDesc, Progress pg) + throws IOException { if (pg == null) { pg = new Progress(); } else { pg.setMinMax(0, 100); } - login(); - pg.setProgress(10); - - url = getCanonicalUrl(url); - - setCurrentReferer(url); - - in = openInput(url); // NULL allowed here - try { - preprocess(url, getInput()); - pg.setProgress(30); - - Story story = new Story(); - MetaData meta = getMeta(url, getInput()); - if (meta.getCreationDate() == null - || meta.getCreationDate().isEmpty()) { - meta.setCreationDate(StringUtils.fromTime(new Date().getTime())); - } - story.setMeta(meta); - - pg.setProgress(50); + pg.setProgress(30); - if (meta.getCover() == null) { - meta.setCover(getDefaultCover(meta.getSubject())); - } - - pg.setProgress(60); + Story story = new Story(); + MetaData meta = getMeta(); + if (meta.getCreationDate() == null || meta.getCreationDate().isEmpty()) { + meta.setCreationDate(StringUtils.fromTime(new Date().getTime())); + } + story.setMeta(meta); - if (getDesc) { - String descChapterName = Instance.getTrans().getString( - StringId.DESCRIPTION); - story.getMeta().setResume( - makeChapter(url, 0, descChapterName, - getDesc(url, getInput()), null)); - } + pg.setProgress(50); - pg.setProgress(100); - return story; - } finally { - if (close) { - try { - close(); - } catch (IOException e) { - Instance.getTraceHandler().error(e); - } + if (meta.getCover() == null) { + meta.setCover(BasicSupportHelper.getDefaultCover(meta.getSubject())); + } - if (in != null) { - in.close(); - } - } + pg.setProgress(60); - setCurrentReferer(null); + if (getDesc) { + String descChapterName = Instance.getTrans().getString( + StringId.DESCRIPTION); + story.getMeta().setResume( + BasicSupportPara.makeChapter(this, source, 0, + descChapterName, // + getDesc(), isHtml(), null)); } + + pg.setProgress(100); + return story; } /** * Process the given story resource into a fully filled {@link Story} * object. * - * @param url - * the story resource * @param pg * the optional progress reporter * @@ -423,32 +344,33 @@ public abstract class BasicSupport { * @throws IOException * in case of I/O error */ - public Story process(URL url, Progress pg) throws IOException { + public Story process(Progress pg) throws IOException { if (pg == null) { pg = new Progress(); } else { pg.setMinMax(0, 100); } - url = getCanonicalUrl(url); + setCurrentReferer(source); + login(); + sourceNode = loadDocument(source); + pg.setProgress(1); try { Progress pgMeta = new Progress(); pg.addProgress(pgMeta, 10); - Story story = processMeta(url, false, true, pgMeta); + preprocess(); + Story story = processMeta(true, pgMeta); if (!pgMeta.isDone()) { pgMeta.setProgress(pgMeta.getMax()); // 10% } pg.setName("Retrieving " + story.getMeta().getTitle()); - setCurrentReferer(url); - Progress pgGetChapters = new Progress(); pg.addProgress(pgGetChapters, 10); story.setChapters(new ArrayList()); - List> chapters = getChapters(url, getInput(), - pgGetChapters); + List> chapters = getChapters(pgGetChapters); if (!pgGetChapters.isDone()) { pgGetChapters.setProgress(pgGetChapters.getMax()); // 20% } @@ -462,41 +384,35 @@ public abstract class BasicSupport { int i = 1; for (Entry chap : chapters) { pgChaps.setName("Extracting chapter " + i); - InputStream chapIn = null; - if (chap.getValue() != null) { - setCurrentReferer(chap.getValue()); - chapIn = Instance.getCache().open(chap.getValue(), - this, false); + URL chapUrl = chap.getValue(); + String chapName = chap.getKey(); + if (chapUrl != null) { + setCurrentReferer(chapUrl); } - pgChaps.setProgress(i * 100); - try { - Progress pgGetChapterContent = new Progress(); - Progress pgMakeChapter = new Progress(); - pgChaps.addProgress(pgGetChapterContent, 100); - pgChaps.addProgress(pgMakeChapter, 100); - - String content = getChapterContent(url, chapIn, i, - pgGetChapterContent); - if (!pgGetChapterContent.isDone()) { - pgGetChapterContent.setProgress(pgGetChapterContent - .getMax()); - } - Chapter cc = makeChapter(url, i, chap.getKey(), - content, pgMakeChapter); - if (!pgMakeChapter.isDone()) { - pgMakeChapter.setProgress(pgMakeChapter.getMax()); - } + pgChaps.setProgress(i * 100); + Progress pgGetChapterContent = new Progress(); + Progress pgMakeChapter = new Progress(); + pgChaps.addProgress(pgGetChapterContent, 100); + pgChaps.addProgress(pgMakeChapter, 100); + + String content = getChapterContent(chapUrl, i, + pgGetChapterContent); + if (!pgGetChapterContent.isDone()) { + pgGetChapterContent.setProgress(pgGetChapterContent + .getMax()); + } - words += cc.getWords(); - story.getChapters().add(cc); - story.getMeta().setWords(words); - } finally { - if (chapIn != null) { - chapIn.close(); - } + Chapter cc = BasicSupportPara.makeChapter(this, chapUrl, i, + chapName, content, isHtml(), pgMakeChapter); + if (!pgMakeChapter.isDone()) { + pgMakeChapter.setProgress(pgMakeChapter.getMax()); } + words += cc.getWords(); + story.getChapters().add(cc); + story.getMeta().setWords(words); + i++; } @@ -506,1148 +422,106 @@ public abstract class BasicSupport { } return story; - } finally { - try { - close(); - } catch (IOException e) { - Instance.getTraceHandler().error(e); - } - - if (in != null) { - in.close(); - } - - setCurrentReferer(null); + close(); } } /** - * The support type. + * Return a {@link BasicSupport} implementation supporting the given + * resource if possible. * - * @return the type - */ - public SupportType getType() { - return type; - } - - /** - * The current referer {@link URL} (only one 'r', as in 'HTML'...), i.e., - * the current {@link URL} we work on. + * @param url + * the story resource * - * @return the referer + * @return an implementation that supports it, or NULL */ - public URL getCurrentReferer() { - return currentReferer; - } + public static BasicSupport getSupport(URL url) { + if (url == null) { + return null; + } - /** - * The current referer {@link URL} (only one 'r', as in 'HTML'...), i.e., - * the current {@link URL} we work on. - * - * @param currentReferer - * the new referer - */ - protected void setCurrentReferer(URL currentReferer) { - this.currentReferer = currentReferer; - } + // TEXT and INFO_TEXT always support files (not URLs though) + for (SupportType type : SupportType.values()) { + if (type != SupportType.TEXT && type != SupportType.INFO_TEXT) { + BasicSupport support = getSupport(type, url); + if (support != null && support.supports(url)) { + return support; + } + } + } - /** - * The support type. - * - * @param type - * the new type - * - * @return this - */ - protected BasicSupport setType(SupportType type) { - this.type = type; - return this; + for (SupportType type : new SupportType[] { SupportType.INFO_TEXT, + SupportType.TEXT }) { + BasicSupport support = getSupport(type, url); + if (support != null && support.supports(url)) { + return support; + } + } + + return null; } /** - * Prepare the support if needed before processing. + * Return a {@link BasicSupport} implementation supporting the given type. * - * @param source - * the source of the story - * @param in - * the input (the main resource) + * @param type + * the type + * @param url + * the {@link URL} to support (can be NULL to get an + * "abstract support") * - * @throws IOException - * on I/O error + * @return an implementation that supports it, or NULL */ - @SuppressWarnings("unused") - protected void preprocess(URL source, InputStream in) throws IOException { - } + public static BasicSupport getSupport(SupportType type, URL url) { + BasicSupport support = null; - /** - * Now that we have processed the {@link Story}, close the resources if any. - * - * @throws IOException - * on I/O error - */ - @SuppressWarnings("unused") - protected void close() throws IOException { - } - - /** - * Create a {@link Chapter} object from the given information, formatting - * the content as it should be. - * - * @param source - * the source of the story - * @param number - * the chapter number - * @param name - * the chapter name - * @param content - * the chapter content - * @param pg - * the optional progress reporter - * - * @return the {@link Chapter} - * - * @throws IOException - * in case of I/O error - */ - protected Chapter makeChapter(URL source, int number, String name, - String content, Progress pg) throws IOException { - // Chapter name: process it correctly, then remove the possible - // redundant "Chapter x: " in front of it, or "-" (as in - // "Chapter 5: - Fun!" after the ": " was automatically added) - String chapterName = processPara(name).getContent().trim(); - for (String lang : Instance.getConfig().getString(Config.CHAPTER) - .split(",")) { - String chapterWord = Instance.getConfig().getStringX( - Config.CHAPTER, lang); - if (chapterName.startsWith(chapterWord)) { - chapterName = chapterName.substring(chapterWord.length()) - .trim(); - break; - } - } - - if (chapterName.startsWith(Integer.toString(number))) { - chapterName = chapterName.substring( - Integer.toString(number).length()).trim(); - } - - while (chapterName.startsWith(":") || chapterName.startsWith("-")) { - chapterName = chapterName.substring(1).trim(); - } - // - - Chapter chap = new Chapter(number, chapterName); - - if (content != null) { - List paras = makeParagraphs(source, content, pg); - long words = 0; - for (Paragraph para : paras) { - words += para.getWords(); - } - chap.setParagraphs(paras); - chap.setWords(words); - } - - return chap; - - } - - /** - * Convert the given content into {@link Paragraph}s. - * - * @param source - * the source URL of the story - * @param content - * the textual content - * @param pg - * the optional progress reporter - * - * @return the {@link Paragraph}s - * - * @throws IOException - * in case of I/O error - */ - protected List makeParagraphs(URL source, String content, - Progress pg) throws IOException { - if (pg == null) { - pg = new Progress(); - } - - if (isHtml()) { - // Special


processing: - content = content.replaceAll("(
]*>)|(
)|(
)", - "
* * *
"); - } - - List paras = new ArrayList(); - - if (content != null && !content.trim().isEmpty()) { - if (isHtml()) { - String[] tab = content.split("(

|

|
|
)"); - pg.setMinMax(0, tab.length); - int i = 1; - for (String line : tab) { - if (line.startsWith("[") && line.endsWith("]")) { - pg.setName("Extracting image " + i); - } - paras.add(makeParagraph(source, line.trim())); - pg.setProgress(i++); - } - pg.setName(null); - } else { - List lines = new ArrayList(); - BufferedReader buff = null; - try { - buff = new BufferedReader( - new InputStreamReader(new ByteArrayInputStream( - content.getBytes("UTF-8")), "UTF-8")); - for (String line = buff.readLine(); line != null; line = buff - .readLine()) { - lines.add(line.trim()); - } - } finally { - if (buff != null) { - buff.close(); - } - } - - pg.setMinMax(0, lines.size()); - int i = 0; - for (String line : lines) { - if (line.startsWith("[") && line.endsWith("]")) { - pg.setName("Extracting image " + i); - } - paras.add(makeParagraph(source, line)); - pg.setProgress(i++); - } - pg.setName(null); - } - - // Check quotes for "bad" format - List newParas = new ArrayList(); - for (Paragraph para : paras) { - newParas.addAll(requotify(para)); - } - paras = newParas; - - // Remove double blanks/brks - fixBlanksBreaks(paras); - } - - return paras; - } - - /** - * Convert the given line into a single {@link Paragraph}. - * - * @param source - * the source URL of the story - * @param line - * the textual content of the paragraph - * - * @return the {@link Paragraph} - */ - private Paragraph makeParagraph(URL source, String line) { - Image image = null; - if (line.startsWith("[") && line.endsWith("]")) { - image = getImage(this, source, line.substring(1, line.length() - 1) - .trim()); - } - - if (image != null) { - return new Paragraph(image); - } - - return processPara(line); - } - - /** - * Fix the {@link ParagraphType#BLANK}s and {@link ParagraphType#BREAK}s of - * those {@link Paragraph}s. - *

- * The resulting list will not contain a starting or trailing blank/break - * nor 2 blanks or breaks following each other. - * - * @param paras - * the list of {@link Paragraph}s to fix - */ - protected void fixBlanksBreaks(List paras) { - boolean space = false; - boolean brk = true; - for (int i = 0; i < paras.size(); i++) { - Paragraph para = paras.get(i); - boolean thisSpace = para.getType() == ParagraphType.BLANK; - boolean thisBrk = para.getType() == ParagraphType.BREAK; - - if (i > 0 && space && thisBrk) { - paras.remove(i - 1); - i--; - } else if ((space || brk) && (thisSpace || thisBrk)) { - paras.remove(i); - i--; - } - - space = thisSpace; - brk = thisBrk; - } - - // Remove blank/brk at start - if (paras.size() > 0 - && (paras.get(0).getType() == ParagraphType.BLANK || paras.get( - 0).getType() == ParagraphType.BREAK)) { - paras.remove(0); - } - - // Remove blank/brk at end - int last = paras.size() - 1; - if (paras.size() > 0 - && (paras.get(last).getType() == ParagraphType.BLANK || paras - .get(last).getType() == ParagraphType.BREAK)) { - paras.remove(last); - } - } - - /** - * Get the default cover related to this subject (see .info files). - * - * @param subject - * the subject - * - * @return the cover if any, or NULL - */ - static Image getDefaultCover(String subject) { - if (subject != null && !subject.isEmpty() - && Instance.getCoverDir() != null) { - try { - File fileCover = new File(Instance.getCoverDir(), subject); - return getImage(null, fileCover.toURI().toURL(), subject); - } catch (MalformedURLException e) { - } - } - - return null; - } - - /** - * Return the list of supported image extensions. - * - * @param emptyAllowed - * TRUE to allow an empty extension on first place, which can be - * used when you may already have an extension in your input but - * are not sure about it - * - * @return the extensions - */ - static String[] getImageExt(boolean emptyAllowed) { - if (emptyAllowed) { - return new String[] { "", ".png", ".jpg", ".jpeg", ".gif", ".bmp" }; - } - - return new String[] { ".png", ".jpg", ".jpeg", ".gif", ".bmp" }; - } - - /** - * Check if the given resource can be a local image or a remote image, then - * refresh the cache with it if it is. - * - * @param source - * the story source - * @param line - * the resource to check - * - * @return the image if found, or NULL - * - */ - static Image getImage(BasicSupport support, URL source, String line) { - URL url = getImageUrl(support, source, line); - if (url != null) { - if ("file".equals(url.getProtocol())) { - if (new File(url.getPath()).isDirectory()) { - return null; - } - } - InputStream in = null; - try { - in = Instance.getCache().open(url, getSupport(url), true); - return new Image(in); - } catch (IOException e) { - } finally { - if (in != null) { - try { - in.close(); - } catch (IOException e) { - } - } - } - } - - return null; - } - - /** - * Check if the given resource can be a local image or a remote image, then - * refresh the cache with it if it is. - * - * @param source - * the story source - * @param line - * the resource to check - * - * @return the image URL if found, or NULL - * - */ - static URL getImageUrl(BasicSupport support, URL source, String line) { - URL url = null; - - if (line != null) { - // try for files - if (source != null) { - try { - - String relPath = null; - String absPath = null; - try { - String path = new File(source.getFile()).getParent(); - relPath = new File(new File(path), line.trim()) - .getAbsolutePath(); - } catch (Exception e) { - // Cannot be converted to path (one possibility to take - // into account: absolute path on Windows) - } - try { - absPath = new File(line.trim()).getAbsolutePath(); - } catch (Exception e) { - // Cannot be converted to path (at all) - } - - for (String ext : getImageExt(true)) { - File absFile = new File(absPath + ext); - File relFile = new File(relPath + ext); - if (absPath != null && absFile.exists() - && absFile.isFile()) { - url = absFile.toURI().toURL(); - } else if (relPath != null && relFile.exists() - && relFile.isFile()) { - url = relFile.toURI().toURL(); - } - } - } catch (Exception e) { - // Should not happen since we control the correct arguments - } - } - - if (url == null) { - // try for URLs - try { - for (String ext : getImageExt(true)) { - if (Instance.getCache() - .check(new URL(line + ext), true)) { - url = new URL(line + ext); - break; - } - } - - // try out of cache - if (url == null) { - for (String ext : getImageExt(true)) { - try { - url = new URL(line + ext); - Instance.getCache().refresh(url, support, true); - break; - } catch (IOException e) { - // no image with this ext - url = null; - } - } - } - } catch (MalformedURLException e) { - // Not an url - } - } - - // refresh the cached file - if (url != null) { - try { - Instance.getCache().refresh(url, support, true); - } catch (IOException e) { - // woops, broken image - url = null; - } - } - } - - return url; - } - - /** - * Open the input file that will be used through the support. - *

- * Can return NULL, in which case you are supposed to work without an - * {@link InputStream}. - * - * @param source - * the source {@link URL} - * - * @return the {@link InputStream} - * - * @throws IOException - * in case of I/O error - */ - protected InputStream openInput(URL source) throws IOException { - return Instance.getCache().open(source, this, false); - } - - /** - * Reset then return {@link BasicSupport#in}. - * - * @return {@link BasicSupport#in} - */ - protected InputStream getInput() { - return reset(in); - } - - /** - * Fix the author name if it is prefixed with some "by" {@link String}. - * - * @param author - * the author with a possible prefix - * - * @return the author without prefixes - */ - protected String fixAuthor(String author) { - if (author != null) { - for (String suffix : new String[] { " ", ":" }) { - for (String byString : Instance.getConfig() - .getString(Config.BYS).split(",")) { - byString += suffix; - if (author.toUpperCase().startsWith(byString.toUpperCase())) { - author = author.substring(byString.length()).trim(); - } - } - } - - // Special case (without suffix): - if (author.startsWith("©")) { - author = author.substring(1); - } - } - - return author; - } - - /** - * Check quotes for bad format (i.e., quotes with normal paragraphs inside) - * and requotify them (i.e., separate them into QUOTE paragraphs and other - * paragraphs (quotes or not)). - * - * @param para - * the paragraph to requotify (not necessarily a quote) - * - * @return the correctly (or so we hope) quotified paragraphs - */ - protected List requotify(Paragraph para) { - List newParas = new ArrayList(); - - if (para.getType() == ParagraphType.QUOTE - && para.getContent().length() > 2) { - String line = para.getContent(); - boolean singleQ = line.startsWith("" + openQuote); - boolean doubleQ = line.startsWith("" + openDoubleQuote); - - // Do not try when more than one quote at a time - // (some stories are not easily readable if we do) - if (singleQ - && line.indexOf(closeQuote, 1) < line - .lastIndexOf(closeQuote)) { - newParas.add(para); - return newParas; - } - if (doubleQ - && line.indexOf(closeDoubleQuote, 1) < line - .lastIndexOf(closeDoubleQuote)) { - newParas.add(para); - return newParas; - } - // - - if (!singleQ && !doubleQ) { - line = openDoubleQuote + line + closeDoubleQuote; - newParas.add(new Paragraph(ParagraphType.QUOTE, line, para - .getWords())); - } else { - char open = singleQ ? openQuote : openDoubleQuote; - char close = singleQ ? closeQuote : closeDoubleQuote; - - int posDot = -1; - boolean inQuote = false; - int i = 0; - for (char car : line.toCharArray()) { - if (car == open) { - inQuote = true; - } else if (car == close) { - inQuote = false; - } else if (car == '.' && !inQuote) { - posDot = i; - break; - } - i++; - } - - if (posDot >= 0) { - String rest = line.substring(posDot + 1).trim(); - line = line.substring(0, posDot + 1).trim(); - long words = 1; - for (char car : line.toCharArray()) { - if (car == ' ') { - words++; - } - } - newParas.add(new Paragraph(ParagraphType.QUOTE, line, words)); - if (!rest.isEmpty()) { - newParas.addAll(requotify(processPara(rest))); - } - } else { - newParas.add(para); - } - } - } else { - newParas.add(para); - } - - return newParas; - } - - /** - * Process a {@link Paragraph} from a raw line of text. - *

- * Will also fix quotes and HTML encoding if needed. - * - * @param line - * the raw line - * - * @return the processed {@link Paragraph} - */ - protected Paragraph processPara(String line) { - line = ifUnhtml(line).trim(); - - boolean space = true; - boolean brk = true; - boolean quote = false; - boolean tentativeCloseQuote = false; - char prev = '\0'; - int dashCount = 0; - long words = 1; - - StringBuilder builder = new StringBuilder(); - for (char car : line.toCharArray()) { - if (car != '-') { - if (dashCount > 0) { - // dash, ndash and mdash: - – — - // currently: always use mdash - builder.append(dashCount == 1 ? '-' : '—'); - } - dashCount = 0; - } - - if (tentativeCloseQuote) { - tentativeCloseQuote = false; - if (Character.isLetterOrDigit(car)) { - builder.append("'"); - } else { - // handle double-single quotes as double quotes - if (prev == car) { - builder.append(closeDoubleQuote); - continue; - } - - builder.append(closeQuote); - } - } - - switch (car) { - case ' ': // note: unbreakable space - case ' ': - case '\t': - case '\n': // just in case - case '\r': // just in case - if (builder.length() > 0 - && builder.charAt(builder.length() - 1) != ' ') { - words++; - } - builder.append(' '); - break; - - case '\'': - if (space || (brk && quote)) { - quote = true; - // handle double-single quotes as double quotes - if (prev == car) { - builder.deleteCharAt(builder.length() - 1); - builder.append(openDoubleQuote); - } else { - builder.append(openQuote); - } - } else if (prev == ' ' || prev == car) { - // handle double-single quotes as double quotes - if (prev == car) { - builder.deleteCharAt(builder.length() - 1); - builder.append(openDoubleQuote); - } else { - builder.append(openQuote); - } - } else { - // it is a quote ("I'm off") or a 'quote' ("This - // 'good' restaurant"...) - tentativeCloseQuote = true; - } - break; - - case '"': - if (space || (brk && quote)) { - quote = true; - builder.append(openDoubleQuote); - } else if (prev == ' ') { - builder.append(openDoubleQuote); - } else { - builder.append(closeDoubleQuote); - } - break; - - case '-': - if (space) { - quote = true; - } else { - dashCount++; - } - space = false; - break; - - case '*': - case '~': - case '/': - case '\\': - case '<': - case '>': - case '=': - case '+': - case '_': - case '–': - case '—': - space = false; - builder.append(car); - break; - - case '‘': - case '`': - case '‹': - case '﹁': - case '〈': - case '「': - if (space || (brk && quote)) { - quote = true; - builder.append(openQuote); - } else { - // handle double-single quotes as double quotes - if (prev == car) { - builder.deleteCharAt(builder.length() - 1); - builder.append(openDoubleQuote); - } else { - builder.append(openQuote); - } - } - space = false; - brk = false; - break; - - case '’': - case '›': - case '﹂': - case '〉': - case '」': - space = false; - brk = false; - // handle double-single quotes as double quotes - if (prev == car) { - builder.deleteCharAt(builder.length() - 1); - builder.append(closeDoubleQuote); - } else { - builder.append(closeQuote); - } - break; - - case '«': - case '“': - case '﹃': - case '《': - case '『': - if (space || (brk && quote)) { - quote = true; - builder.append(openDoubleQuote); - } else { - builder.append(openDoubleQuote); - } - space = false; - brk = false; - break; - - case '»': - case '”': - case '﹄': - case '》': - case '』': - space = false; - brk = false; - builder.append(closeDoubleQuote); - break; - - default: - space = false; - brk = false; - builder.append(car); - break; - } - - prev = car; - } - - if (tentativeCloseQuote) { - tentativeCloseQuote = false; - builder.append(closeQuote); - } - - line = builder.toString().trim(); - - ParagraphType type = ParagraphType.NORMAL; - if (space) { - type = ParagraphType.BLANK; - } else if (brk) { - type = ParagraphType.BREAK; - } else if (quote) { - type = ParagraphType.QUOTE; - } - - return new Paragraph(type, line, words); - } - - /** - * Remove the HTML from the input if {@link BasicSupport#isHtml()} is - * true. - * - * @param input - * the input - * - * @return the no html version if needed - */ - private String ifUnhtml(String input) { - if (isHtml() && input != null) { - return StringUtils.unhtml(input); - } - - return input; - } - - /** - * Return a {@link BasicSupport} implementation supporting the given - * resource if possible. - * - * @param url - * the story resource - * - * @return an implementation that supports it, or NULL - */ - public static BasicSupport getSupport(URL url) { - if (url == null) { - return null; - } - - // TEXT and INFO_TEXT always support files (not URLs though) - for (SupportType type : SupportType.values()) { - if (type != SupportType.TEXT && type != SupportType.INFO_TEXT) { - BasicSupport support = getSupport(type); - if (support != null && support.supports(url)) { - return support; - } - } - } - - for (SupportType type : new SupportType[] { SupportType.INFO_TEXT, - SupportType.TEXT }) { - BasicSupport support = getSupport(type); - if (support != null && support.supports(url)) { - return support; - } - } - - return null; - } - - /** - * Return a {@link BasicSupport} implementation supporting the given type. - * - * @param type - * the type - * - * @return an implementation that supports it, or NULL - */ - public static BasicSupport getSupport(SupportType type) { switch (type) { case EPUB: - return new Epub().setType(type); + support = new Epub(); + break; case INFO_TEXT: - return new InfoText().setType(type); + support = new InfoText(); + break; case FIMFICTION: try { // Can fail if no client key or NO in options - return new FimfictionApi().setType(type); + support = new FimfictionApi(); } catch (IOException e) { - return new Fimfiction().setType(type); + support = new Fimfiction(); } + break; case FANFICTION: - return new Fanfiction().setType(type); + support = new Fanfiction(); + break; case TEXT: - return new Text().setType(type); + support = new Text(); + break; case MANGAFOX: - return new MangaFox().setType(type); + support = new MangaFox(); + break; case E621: - return new E621().setType(type); + support = new E621(); + break; case YIFFSTAR: - return new YiffStar().setType(type); + support = new YiffStar(); + break; case E_HENTAI: - return new EHentai().setType(type); + support = new EHentai(); + break; case CBZ: - return new Cbz().setType(type); + support = new Cbz(); + break; case HTML: - return new Html().setType(type); - } - - return null; - } - - /** - * Reset the given {@link InputStream} and return it. - * - * @param in - * the {@link InputStream} to reset - * - * @return the same {@link InputStream} after reset - */ - static protected InputStream reset(InputStream in) { - try { - if (in != null) { - in.reset(); - } - } catch (IOException e) { - } - - return in; - } - - /** - * Return the first line from the given input which correspond to the given - * selectors. - * - * @param in - * the input - * @param needle - * a string that must be found inside the target line (also - * supports "^" at start to say "only if it starts with" the - * needle) - * @param relativeLine - * the line to return based upon the target line position (-1 = - * the line before, 0 = the target line...) - * - * @return the line - */ - static protected String getLine(InputStream in, String needle, - int relativeLine) { - return getLine(in, needle, relativeLine, true); - } - - /** - * Return a line from the given input which correspond to the given - * selectors. - * - * @param in - * the input - * @param needle - * a string that must be found inside the target line (also - * supports "^" at start to say "only if it starts with" the - * needle) - * @param relativeLine - * the line to return based upon the target line position (-1 = - * the line before, 0 = the target line...) - * @param first - * takes the first result (as opposed to the last one, which will - * also always spend the input) - * - * @return the line - */ - static protected String getLine(InputStream in, String needle, - int relativeLine, boolean first) { - String rep = null; - - reset(in); - - List lines = new ArrayList(); - @SuppressWarnings("resource") - Scanner scan = new Scanner(in, "UTF-8"); - int index = -1; - scan.useDelimiter("\\n"); - while (scan.hasNext()) { - lines.add(scan.next()); - - if (index == -1) { - if (needle.startsWith("^")) { - if (lines.get(lines.size() - 1).startsWith( - needle.substring(1))) { - index = lines.size() - 1; - } - - } else { - if (lines.get(lines.size() - 1).contains(needle)) { - index = lines.size() - 1; - } - } - } - - if (index >= 0 && index + relativeLine < lines.size()) { - rep = lines.get(index + relativeLine); - if (first) { - break; - } - } + support = new Html(); + break; } - return rep; - } - - /** - * Return the text between the key and the endKey (and optional subKey can - * be passed, in this case we will look for the key first, then take the - * text between the subKey and the endKey). - *

- * Will only match the first line with the given key if more than one are - * possible. Which also means that if the subKey or endKey is not found on - * that line, NULL will be returned. - * - * @param in - * the input - * @param key - * the key to match (also supports "^" at start to say - * "only if it starts with" the key) - * @param subKey - * the sub key or NULL if none - * @param endKey - * the end key or NULL for "up to the end" - * @return the text or NULL if not found - */ - static protected String getKeyLine(InputStream in, String key, - String subKey, String endKey) { - return getKeyText(getLine(in, key, 0), key, subKey, endKey); - } - - /** - * Return the text between the key and the endKey (and optional subKey can - * be passed, in this case we will look for the key first, then take the - * text between the subKey and the endKey). - * - * @param in - * the input - * @param key - * the key to match (also supports "^" at start to say - * "only if it starts with" the key) - * @param subKey - * the sub key or NULL if none - * @param endKey - * the end key or NULL for "up to the end" - * @return the text or NULL if not found - */ - static protected String getKeyText(String in, String key, String subKey, - String endKey) { - String result = null; - - String line = in; - if (line != null && line.contains(key)) { - line = line.substring(line.indexOf(key) + key.length()); - if (subKey == null || subKey.isEmpty() || line.contains(subKey)) { - if (subKey != null) { - line = line.substring(line.indexOf(subKey) - + subKey.length()); - } - if (endKey == null || line.contains(endKey)) { - if (endKey != null) { - line = line.substring(0, line.indexOf(endKey)); - result = line; - } - } - } - } - - return result; - } - - /** - * Return the text between the key and the endKey (optional subKeys can be - * passed, in this case we will look for the subKeys first, then take the - * text between the key and the endKey). - * - * @param in - * the input - * @param key - * the key to match - * @param endKey - * the end key or NULL for "up to the end" - * @param afters - * the sub-keys to find before checking for key/endKey - * - * @return the text or NULL if not found - */ - static protected String getKeyTextAfter(String in, String key, - String endKey, String... afters) { - - if (in != null && !in.isEmpty()) { - int pos = indexOfAfter(in, 0, afters); - if (pos < 0) { - return null; - } - - in = in.substring(pos); - } - - return getKeyText(in, key, null, endKey); - } - - /** - * Return the first index after all the given "afters" have been found in - * the {@link String}, or -1 if it was not possible. - * - * @param in - * the input - * @param startAt - * start at this position in the string - * @param afters - * the sub-keys to find before checking for key/endKey - * - * @return the text or NULL if not found - */ - static protected int indexOfAfter(String in, int startAt, String... afters) { - int pos = -1; - if (in != null && !in.isEmpty()) { - pos = startAt; - if (afters != null) { - for (int i = 0; pos >= 0 && i < afters.length; i++) { - String subKey = afters[i]; - if (!subKey.isEmpty()) { - pos = in.indexOf(subKey, pos); - if (pos >= 0) { - pos += subKey.length(); - } - } - } - } + if (support != null) { + support.setType(type); + support.source = support.getCanonicalUrl(url); } - return pos; + return support; } } diff --git a/src/be/nikiroo/fanfix/supported/BasicSupportHelper.java b/src/be/nikiroo/fanfix/supported/BasicSupportHelper.java new file mode 100644 index 0000000..0c9e199 --- /dev/null +++ b/src/be/nikiroo/fanfix/supported/BasicSupportHelper.java @@ -0,0 +1,225 @@ +package be.nikiroo.fanfix.supported; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; + +import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.bundles.Config; +import be.nikiroo.utils.Image; + +/** + * Helper class for {@link BasicSupport}, mostly dedicated to text formating for + * the classes that implement {@link BasicSupport}. + * + * @author niki + */ +class BasicSupportHelper { + /** + * Get the default cover related to this subject (see .info files). + * + * @param subject + * the subject + * + * @return the cover if any, or NULL + */ + public static Image getDefaultCover(String subject) { + if (subject != null && !subject.isEmpty() + && Instance.getCoverDir() != null) { + try { + File fileCover = new File(Instance.getCoverDir(), subject); + return getImage(null, fileCover.toURI().toURL(), subject); + } catch (MalformedURLException e) { + } + } + + return null; + } + + /** + * Return the list of supported image extensions. + * + * @param emptyAllowed + * TRUE to allow an empty extension on first place, which can be + * used when you may already have an extension in your input but + * are not sure about it + * + * @return the extensions + */ + public static String[] getImageExt(boolean emptyAllowed) { + if (emptyAllowed) { + return new String[] { "", ".png", ".jpg", ".jpeg", ".gif", ".bmp" }; + } + + return new String[] { ".png", ".jpg", ".jpeg", ".gif", ".bmp" }; + } + + /** + * Check if the given resource can be a local image or a remote image, then + * refresh the cache with it if it is. + * + * @param support + * the linked {@link BasicSupport} + * @param source + * the story source + * @param line + * the resource to check + * + * @return the image if found, or NULL + * + */ + public static Image getImage(BasicSupport support, URL source, String line) { + URL url = getImageUrl(support, source, line); + if (url != null) { + if ("file".equals(url.getProtocol())) { + if (new File(url.getPath()).isDirectory()) { + return null; + } + } + InputStream in = null; + try { + in = Instance.getCache().open(url, + BasicSupport.getSupport(url), true); + return new Image(in); + } catch (IOException e) { + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + } + + return null; + } + + /** + * Check if the given resource can be a local image or a remote image, then + * refresh the cache with it if it is. + * + * @param support + * the linked {@link BasicSupport} + * @param source + * the story source + * @param line + * the resource to check + * + * @return the image URL if found, or NULL + * + */ + public static URL getImageUrl(BasicSupport support, URL source, String line) { + URL url = null; + + if (line != null) { + // try for files + if (source != null) { + try { + + String relPath = null; + String absPath = null; + try { + String path = new File(source.getFile()).getParent(); + relPath = new File(new File(path), line.trim()) + .getAbsolutePath(); + } catch (Exception e) { + // Cannot be converted to path (one possibility to take + // into account: absolute path on Windows) + } + try { + absPath = new File(line.trim()).getAbsolutePath(); + } catch (Exception e) { + // Cannot be converted to path (at all) + } + + for (String ext : getImageExt(true)) { + File absFile = new File(absPath + ext); + File relFile = new File(relPath + ext); + if (absPath != null && absFile.exists() + && absFile.isFile()) { + url = absFile.toURI().toURL(); + } else if (relPath != null && relFile.exists() + && relFile.isFile()) { + url = relFile.toURI().toURL(); + } + } + } catch (Exception e) { + // Should not happen since we control the correct arguments + } + } + + if (url == null) { + // try for URLs + try { + for (String ext : getImageExt(true)) { + if (Instance.getCache() + .check(new URL(line + ext), true)) { + url = new URL(line + ext); + break; + } + } + + // try out of cache + if (url == null) { + for (String ext : getImageExt(true)) { + try { + url = new URL(line + ext); + Instance.getCache().refresh(url, support, true); + break; + } catch (IOException e) { + // no image with this ext + url = null; + } + } + } + } catch (MalformedURLException e) { + // Not an url + } + } + + // refresh the cached file + if (url != null) { + try { + Instance.getCache().refresh(url, support, true); + } catch (IOException e) { + // woops, broken image + url = null; + } + } + } + + return url; + } + + /** + * Fix the author name if it is prefixed with some "by" {@link String}. + * + * @param author + * the author with a possible prefix + * + * @return the author without prefixes + */ + public static String fixAuthor(String author) { + if (author != null) { + for (String suffix : new String[] { " ", ":" }) { + for (String byString : Instance.getConfig() + .getString(Config.BYS).split(",")) { + byString += suffix; + if (author.toUpperCase().startsWith(byString.toUpperCase())) { + author = author.substring(byString.length()).trim(); + } + } + } + + // Special case (without suffix): + if (author.startsWith("©")) { + author = author.substring(1); + } + } + + return author; + } +} diff --git a/src/be/nikiroo/fanfix/supported/BasicSupportPara.java b/src/be/nikiroo/fanfix/supported/BasicSupportPara.java new file mode 100644 index 0000000..c098d11 --- /dev/null +++ b/src/be/nikiroo/fanfix/supported/BasicSupportPara.java @@ -0,0 +1,571 @@ +package be.nikiroo.fanfix.supported; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; +import java.util.ArrayList; +import java.util.List; + +import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.bundles.Config; +import be.nikiroo.fanfix.bundles.StringId; +import be.nikiroo.fanfix.data.Chapter; +import be.nikiroo.fanfix.data.Paragraph; +import be.nikiroo.fanfix.data.Paragraph.ParagraphType; +import be.nikiroo.utils.Image; +import be.nikiroo.utils.Progress; +import be.nikiroo.utils.StringUtils; + +/** + * Helper class for {@link BasicSupport}, mostly dedicated to {@link Paragraph} + * and text formating for the {@link BasicSupport} class itself (not its + * children). + * + * @author niki + */ +class BasicSupportPara { + // quote chars + private static char openQuote = Instance.getTrans().getCharacter( + StringId.OPEN_SINGLE_QUOTE); + private static char closeQuote = Instance.getTrans().getCharacter( + StringId.CLOSE_SINGLE_QUOTE); + private static char openDoubleQuote = Instance.getTrans().getCharacter( + StringId.OPEN_DOUBLE_QUOTE); + private static char closeDoubleQuote = Instance.getTrans().getCharacter( + StringId.CLOSE_DOUBLE_QUOTE); + + /** + * Create a {@link Chapter} object from the given information, formatting + * the content as it should be. + * + * @param support + * the linked {@link BasicSupport} + * @param source + * the source of the story + * @param number + * the chapter number + * @param name + * the chapter name + * @param content + * the chapter content + * @param pg + * the optional progress reporter + * @param html + * TRUE if the input content is in HTML mode + * + * @return the {@link Chapter} + * + * @throws IOException + * in case of I/O error + */ + public static Chapter makeChapter(BasicSupport support, URL source, + int number, String name, String content, boolean html, Progress pg) + throws IOException { + // Chapter name: process it correctly, then remove the possible + // redundant "Chapter x: " in front of it, or "-" (as in + // "Chapter 5: - Fun!" after the ": " was automatically added) + String chapterName = BasicSupportPara.processPara(name, false) + .getContent().trim(); + for (String lang : Instance.getConfig().getString(Config.CHAPTER) + .split(",")) { + String chapterWord = Instance.getConfig().getStringX( + Config.CHAPTER, lang); + if (chapterName.startsWith(chapterWord)) { + chapterName = chapterName.substring(chapterWord.length()) + .trim(); + break; + } + } + + if (chapterName.startsWith(Integer.toString(number))) { + chapterName = chapterName.substring( + Integer.toString(number).length()).trim(); + } + + while (chapterName.startsWith(":") || chapterName.startsWith("-")) { + chapterName = chapterName.substring(1).trim(); + } + // + + Chapter chap = new Chapter(number, chapterName); + + if (content != null) { + List paras = makeParagraphs(support, source, content, + html, pg); + long words = 0; + for (Paragraph para : paras) { + words += para.getWords(); + } + chap.setParagraphs(paras); + chap.setWords(words); + } + + return chap; + } + + /** + * Check quotes for bad format (i.e., quotes with normal paragraphs inside) + * and requotify them (i.e., separate them into QUOTE paragraphs and other + * paragraphs (quotes or not)). + * + * @param para + * the paragraph to requotify (not necessarily a quote) + * @param html + * TRUE if the input content is in HTML mode + * + * @return the correctly (or so we hope) quotified paragraphs + */ + private static List requotify(Paragraph para, boolean html) { + List newParas = new ArrayList(); + + if (para.getType() == ParagraphType.QUOTE + && para.getContent().length() > 2) { + String line = para.getContent(); + boolean singleQ = line.startsWith("" + openQuote); + boolean doubleQ = line.startsWith("" + openDoubleQuote); + + // Do not try when more than one quote at a time + // (some stories are not easily readable if we do) + if (singleQ + && line.indexOf(closeQuote, 1) < line + .lastIndexOf(closeQuote)) { + newParas.add(para); + return newParas; + } + if (doubleQ + && line.indexOf(closeDoubleQuote, 1) < line + .lastIndexOf(closeDoubleQuote)) { + newParas.add(para); + return newParas; + } + // + + if (!singleQ && !doubleQ) { + line = openDoubleQuote + line + closeDoubleQuote; + newParas.add(new Paragraph(ParagraphType.QUOTE, line, para + .getWords())); + } else { + char open = singleQ ? openQuote : openDoubleQuote; + char close = singleQ ? closeQuote : closeDoubleQuote; + + int posDot = -1; + boolean inQuote = false; + int i = 0; + for (char car : line.toCharArray()) { + if (car == open) { + inQuote = true; + } else if (car == close) { + inQuote = false; + } else if (car == '.' && !inQuote) { + posDot = i; + break; + } + i++; + } + + if (posDot >= 0) { + String rest = line.substring(posDot + 1).trim(); + line = line.substring(0, posDot + 1).trim(); + long words = 1; + for (char car : line.toCharArray()) { + if (car == ' ') { + words++; + } + } + newParas.add(new Paragraph(ParagraphType.QUOTE, line, words)); + if (!rest.isEmpty()) { + newParas.addAll(requotify(processPara(rest, html), html)); + } + } else { + newParas.add(para); + } + } + } else { + newParas.add(para); + } + + return newParas; + } + + /** + * Process a {@link Paragraph} from a raw line of text. + *

+ * Will also fix quotes and HTML encoding if needed. + * + * @param line + * the raw line + * @param html + * TRUE if the input content is in HTML mode + * + * @return the processed {@link Paragraph} + */ + private static Paragraph processPara(String line, boolean html) { + if (html) { + line = StringUtils.unhtml(line).trim(); + } + boolean space = true; + boolean brk = true; + boolean quote = false; + boolean tentativeCloseQuote = false; + char prev = '\0'; + int dashCount = 0; + long words = 1; + + StringBuilder builder = new StringBuilder(); + for (char car : line.toCharArray()) { + if (car != '-') { + if (dashCount > 0) { + // dash, ndash and mdash: - – — + // currently: always use mdash + builder.append(dashCount == 1 ? '-' : '—'); + } + dashCount = 0; + } + + if (tentativeCloseQuote) { + tentativeCloseQuote = false; + if (Character.isLetterOrDigit(car)) { + builder.append("'"); + } else { + // handle double-single quotes as double quotes + if (prev == car) { + builder.append(closeDoubleQuote); + continue; + } + + builder.append(closeQuote); + } + } + + switch (car) { + case ' ': // note: unbreakable space + case ' ': + case '\t': + case '\n': // just in case + case '\r': // just in case + if (builder.length() > 0 + && builder.charAt(builder.length() - 1) != ' ') { + words++; + } + builder.append(' '); + break; + + case '\'': + if (space || (brk && quote)) { + quote = true; + // handle double-single quotes as double quotes + if (prev == car) { + builder.deleteCharAt(builder.length() - 1); + builder.append(openDoubleQuote); + } else { + builder.append(openQuote); + } + } else if (prev == ' ' || prev == car) { + // handle double-single quotes as double quotes + if (prev == car) { + builder.deleteCharAt(builder.length() - 1); + builder.append(openDoubleQuote); + } else { + builder.append(openQuote); + } + } else { + // it is a quote ("I'm off") or a 'quote' ("This + // 'good' restaurant"...) + tentativeCloseQuote = true; + } + break; + + case '"': + if (space || (brk && quote)) { + quote = true; + builder.append(openDoubleQuote); + } else if (prev == ' ') { + builder.append(openDoubleQuote); + } else { + builder.append(closeDoubleQuote); + } + break; + + case '-': + if (space) { + quote = true; + } else { + dashCount++; + } + space = false; + break; + + case '*': + case '~': + case '/': + case '\\': + case '<': + case '>': + case '=': + case '+': + case '_': + case '–': + case '—': + space = false; + builder.append(car); + break; + + case '‘': + case '`': + case '‹': + case '﹁': + case '〈': + case '「': + if (space || (brk && quote)) { + quote = true; + builder.append(openQuote); + } else { + // handle double-single quotes as double quotes + if (prev == car) { + builder.deleteCharAt(builder.length() - 1); + builder.append(openDoubleQuote); + } else { + builder.append(openQuote); + } + } + space = false; + brk = false; + break; + + case '’': + case '›': + case '﹂': + case '〉': + case '」': + space = false; + brk = false; + // handle double-single quotes as double quotes + if (prev == car) { + builder.deleteCharAt(builder.length() - 1); + builder.append(closeDoubleQuote); + } else { + builder.append(closeQuote); + } + break; + + case '«': + case '“': + case '﹃': + case '《': + case '『': + if (space || (brk && quote)) { + quote = true; + builder.append(openDoubleQuote); + } else { + builder.append(openDoubleQuote); + } + space = false; + brk = false; + break; + + case '»': + case '”': + case '﹄': + case '》': + case '』': + space = false; + brk = false; + builder.append(closeDoubleQuote); + break; + + default: + space = false; + brk = false; + builder.append(car); + break; + } + + prev = car; + } + + if (tentativeCloseQuote) { + tentativeCloseQuote = false; + builder.append(closeQuote); + } + + line = builder.toString().trim(); + + ParagraphType type = ParagraphType.NORMAL; + if (space) { + type = ParagraphType.BLANK; + } else if (brk) { + type = ParagraphType.BREAK; + } else if (quote) { + type = ParagraphType.QUOTE; + } + + return new Paragraph(type, line, words); + } + + /** + * Convert the given content into {@link Paragraph}s. + * + * @param support + * the linked {@link BasicSupport} + * @param source + * the source URL of the story + * @param content + * the textual content + * @param html + * TRUE if the input content is in HTML mode + * @param pg + * the optional progress reporter + * + * @return the {@link Paragraph}s + * + * @throws IOException + * in case of I/O error + */ + private static List makeParagraphs(BasicSupport support, + URL source, String content, boolean html, Progress pg) + throws IOException { + if (pg == null) { + pg = new Progress(); + } + + if (html) { + // Special


processing: + content = content.replaceAll("(
]*>)|(
)|(
)", + "
* * *
"); + } + + List paras = new ArrayList(); + + if (content != null && !content.trim().isEmpty()) { + if (html) { + String[] tab = content.split("(

|

|
|
)"); + pg.setMinMax(0, tab.length); + int i = 1; + for (String line : tab) { + if (line.startsWith("[") && line.endsWith("]")) { + pg.setName("Extracting image " + i); + } + paras.add(makeParagraph(support, source, line.trim(), html)); + pg.setProgress(i++); + } + pg.setName(null); + } else { + List lines = new ArrayList(); + BufferedReader buff = null; + try { + buff = new BufferedReader( + new InputStreamReader(new ByteArrayInputStream( + content.getBytes("UTF-8")), "UTF-8")); + for (String line = buff.readLine(); line != null; line = buff + .readLine()) { + lines.add(line.trim()); + } + } finally { + if (buff != null) { + buff.close(); + } + } + + pg.setMinMax(0, lines.size()); + int i = 0; + for (String line : lines) { + if (line.startsWith("[") && line.endsWith("]")) { + pg.setName("Extracting image " + i); + } + paras.add(makeParagraph(support, source, line, html)); + pg.setProgress(i++); + } + pg.setName(null); + } + + // Check quotes for "bad" format + List newParas = new ArrayList(); + for (Paragraph para : paras) { + newParas.addAll(BasicSupportPara.requotify(para, html)); + } + paras = newParas; + + // Remove double blanks/brks + fixBlanksBreaks(paras); + } + + return paras; + } + + /** + * Convert the given line into a single {@link Paragraph}. + * + * @param support + * the linked {@link BasicSupport} + * @param source + * the source URL of the story + * @param line + * the textual content of the paragraph + * @param html + * TRUE if the input content is in HTML mode + * + * @return the {@link Paragraph} + */ + private static Paragraph makeParagraph(BasicSupport support, URL source, + String line, boolean html) { + Image image = null; + if (line.startsWith("[") && line.endsWith("]")) { + image = BasicSupportHelper.getImage(support, source, line + .substring(1, line.length() - 1).trim()); + } + + if (image != null) { + return new Paragraph(image); + } + + return BasicSupportPara.processPara(line, html); + } + + /** + * Fix the {@link ParagraphType#BLANK}s and {@link ParagraphType#BREAK}s of + * those {@link Paragraph}s. + *

+ * The resulting list will not contain a starting or trailing blank/break + * nor 2 blanks or breaks following each other. + * + * @param paras + * the list of {@link Paragraph}s to fix + */ + private static void fixBlanksBreaks(List paras) { + boolean space = false; + boolean brk = true; + for (int i = 0; i < paras.size(); i++) { + Paragraph para = paras.get(i); + boolean thisSpace = para.getType() == ParagraphType.BLANK; + boolean thisBrk = para.getType() == ParagraphType.BREAK; + + if (i > 0 && space && thisBrk) { + paras.remove(i - 1); + i--; + } else if ((space || brk) && (thisSpace || thisBrk)) { + paras.remove(i); + i--; + } + + space = thisSpace; + brk = thisBrk; + } + + // Remove blank/brk at start + if (paras.size() > 0 + && (paras.get(0).getType() == ParagraphType.BLANK || paras.get( + 0).getType() == ParagraphType.BREAK)) { + paras.remove(0); + } + + // Remove blank/brk at end + int last = paras.size() - 1; + if (paras.size() > 0 + && (paras.get(last).getType() == ParagraphType.BLANK || paras + .get(last).getType() == ParagraphType.BREAK)) { + paras.remove(last); + } + } +} diff --git a/src/be/nikiroo/fanfix/supported/BasicSupport_Deprecated.java b/src/be/nikiroo/fanfix/supported/BasicSupport_Deprecated.java new file mode 100644 index 0000000..591ba58 --- /dev/null +++ b/src/be/nikiroo/fanfix/supported/BasicSupport_Deprecated.java @@ -0,0 +1,1325 @@ +package be.nikiroo.fanfix.supported; + +import java.io.BufferedReader; +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.net.MalformedURLException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.Map.Entry; +import java.util.Scanner; + +import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.bundles.Config; +import be.nikiroo.fanfix.bundles.StringId; +import be.nikiroo.fanfix.data.Chapter; +import be.nikiroo.fanfix.data.MetaData; +import be.nikiroo.fanfix.data.Paragraph; +import be.nikiroo.fanfix.data.Paragraph.ParagraphType; +import be.nikiroo.fanfix.data.Story; +import be.nikiroo.utils.Image; +import be.nikiroo.utils.Progress; +import be.nikiroo.utils.StringUtils; + +/** + * DEPRECATED: use the new Jsoup 'Node' system. + *

+ * This class is the base class used by the other support classes. It can be + * used outside of this package, and have static method that you can use to get + * access to the correct support class. + *

+ * It will be used with 'resources' (usually web pages or files). + * + * @author niki + */ +@Deprecated +public abstract class BasicSupport_Deprecated extends BasicSupport { + private InputStream in; + private URL currentReferer; // with only one 'r', as in 'HTTP'... + + // quote chars + private char openQuote = Instance.getTrans().getCharacter( + StringId.OPEN_SINGLE_QUOTE); + private char closeQuote = Instance.getTrans().getCharacter( + StringId.CLOSE_SINGLE_QUOTE); + private char openDoubleQuote = Instance.getTrans().getCharacter( + StringId.OPEN_DOUBLE_QUOTE); + private char closeDoubleQuote = Instance.getTrans().getCharacter( + StringId.CLOSE_DOUBLE_QUOTE); + + // New methods not used in Deprecated mode + @Override + protected String getDesc() throws IOException { + throw new RuntimeException("should not be used by legacy code"); + } + + @Override + protected MetaData getMeta() throws IOException { + throw new RuntimeException("should not be used by legacy code"); + } + + @Override + protected List> getChapters(Progress pg) + throws IOException { + throw new RuntimeException("should not be used by legacy code"); + } + + @Override + protected String getChapterContent(URL chapUrl, int number, Progress pg) + throws IOException { + throw new RuntimeException("should not be used by legacy code"); + } + + @Override + public Story process(Progress pg) throws IOException { + return process(getSource(), pg); + } + + // + + /** + * Return the {@link MetaData} of this story. + * + * @param source + * the source of the story + * @param in + * the input (the main resource) + * + * @return the associated {@link MetaData}, never NULL + * + * @throws IOException + * in case of I/O error + */ + protected abstract MetaData getMeta(URL source, InputStream in) + throws IOException; + + /** + * Return the story description. + * + * @param source + * the source of the story + * @param in + * the input (the main resource) + * + * @return the description + * + * @throws IOException + * in case of I/O error + */ + protected abstract String getDesc(URL source, InputStream in) + throws IOException; + + /** + * Return the list of chapters (name and resource). + * + * @param source + * the source of the story + * @param in + * the input (the main resource) + * @param pg + * the optional progress reporter + * + * @return the chapters + * + * @throws IOException + * in case of I/O error + */ + protected abstract List> getChapters(URL source, + InputStream in, Progress pg) throws IOException; + + /** + * Return the content of the chapter (possibly HTML encoded, if + * {@link BasicSupport_Deprecated#isHtml()} is TRUE). + * + * @param source + * the source of the story + * @param in + * the input (the main resource) + * @param number + * the chapter number + * @param pg + * the optional progress reporter + * + * @return the content + * + * @throws IOException + * in case of I/O error + */ + protected abstract String getChapterContent(URL source, InputStream in, + int number, Progress pg) throws IOException; + + /** + * Process the given story resource into a partially filled {@link Story} + * object containing the name and metadata, except for the description. + * + * @param url + * the story resource + * + * @return the {@link Story} + * + * @throws IOException + * in case of I/O error + */ + public Story processMeta(URL url) throws IOException { + return processMeta(url, true, false, null); + } + + /** + * Process the given story resource into a partially filled {@link Story} + * object containing the name and metadata. + * + * @param url + * the story resource + * @param close + * close "this" and "in" when done + * @param getDesc + * retrieve the description of the story, or not + * @param pg + * the optional progress reporter + * + * @return the {@link Story}, never NULL + * + * @throws IOException + * in case of I/O error + */ + protected Story processMeta(URL url, boolean close, boolean getDesc, + Progress pg) throws IOException { + if (pg == null) { + pg = new Progress(); + } else { + pg.setMinMax(0, 100); + } + + login(); + pg.setProgress(10); + + url = getCanonicalUrl(url); + + setCurrentReferer(url); + + in = openInput(url); // NULL allowed here + try { + preprocess(url, getInput()); + pg.setProgress(30); + + Story story = new Story(); + MetaData meta = getMeta(url, getInput()); + if (meta.getCreationDate() == null + || meta.getCreationDate().isEmpty()) { + meta.setCreationDate(StringUtils.fromTime(new Date().getTime())); + } + story.setMeta(meta); + + pg.setProgress(50); + + if (meta.getCover() == null) { + meta.setCover(getDefaultCover(meta.getSubject())); + } + + pg.setProgress(60); + + if (getDesc) { + String descChapterName = Instance.getTrans().getString( + StringId.DESCRIPTION); + story.getMeta().setResume( + makeChapter(url, 0, descChapterName, + getDesc(url, getInput()), null)); + } + + pg.setProgress(100); + return story; + } finally { + if (close) { + close(); + + if (in != null) { + in.close(); + } + } + } + } + + /** + * Process the given story resource into a fully filled {@link Story} + * object. + * + * @param url + * the story resource + * @param pg + * the optional progress reporter + * + * @return the {@link Story}, never NULL + * + * @throws IOException + * in case of I/O error + */ + protected Story process(URL url, Progress pg) throws IOException { + if (pg == null) { + pg = new Progress(); + } else { + pg.setMinMax(0, 100); + } + + url = getCanonicalUrl(url); + pg.setProgress(1); + try { + Progress pgMeta = new Progress(); + pg.addProgress(pgMeta, 10); + Story story = processMeta(url, false, true, pgMeta); + if (!pgMeta.isDone()) { + pgMeta.setProgress(pgMeta.getMax()); // 10% + } + + pg.setName("Retrieving " + story.getMeta().getTitle()); + + setCurrentReferer(url); + + Progress pgGetChapters = new Progress(); + pg.addProgress(pgGetChapters, 10); + story.setChapters(new ArrayList()); + List> chapters = getChapters(url, getInput(), + pgGetChapters); + if (!pgGetChapters.isDone()) { + pgGetChapters.setProgress(pgGetChapters.getMax()); // 20% + } + + if (chapters != null) { + Progress pgChaps = new Progress("Extracting chapters", 0, + chapters.size() * 300); + pg.addProgress(pgChaps, 80); + + long words = 0; + int i = 1; + for (Entry chap : chapters) { + pgChaps.setName("Extracting chapter " + i); + InputStream chapIn = null; + if (chap.getValue() != null) { + setCurrentReferer(chap.getValue()); + chapIn = Instance.getCache().open(chap.getValue(), + this, false); + } + pgChaps.setProgress(i * 100); + try { + Progress pgGetChapterContent = new Progress(); + Progress pgMakeChapter = new Progress(); + pgChaps.addProgress(pgGetChapterContent, 100); + pgChaps.addProgress(pgMakeChapter, 100); + + String content = getChapterContent(url, chapIn, i, + pgGetChapterContent); + if (!pgGetChapterContent.isDone()) { + pgGetChapterContent.setProgress(pgGetChapterContent + .getMax()); + } + + Chapter cc = makeChapter(url, i, chap.getKey(), + content, pgMakeChapter); + if (!pgMakeChapter.isDone()) { + pgMakeChapter.setProgress(pgMakeChapter.getMax()); + } + + words += cc.getWords(); + story.getChapters().add(cc); + story.getMeta().setWords(words); + } finally { + if (chapIn != null) { + chapIn.close(); + } + } + + i++; + } + + pgChaps.setName("Extracting chapters"); + } else { + pg.setProgress(80); + } + + return story; + + } finally { + close(); + + if (in != null) { + in.close(); + } + } + } + + /** + * Prepare the support if needed before processing. + * + * @param source + * the source of the story + * @param in + * the input (the main resource) + * + * @throws IOException + * on I/O error + */ + @SuppressWarnings("unused") + protected void preprocess(URL source, InputStream in) throws IOException { + } + + /** + * Create a {@link Chapter} object from the given information, formatting + * the content as it should be. + * + * @param source + * the source of the story + * @param number + * the chapter number + * @param name + * the chapter name + * @param content + * the chapter content + * @param pg + * the optional progress reporter + * + * @return the {@link Chapter} + * + * @throws IOException + * in case of I/O error + */ + protected Chapter makeChapter(URL source, int number, String name, + String content, Progress pg) throws IOException { + // Chapter name: process it correctly, then remove the possible + // redundant "Chapter x: " in front of it, or "-" (as in + // "Chapter 5: - Fun!" after the ": " was automatically added) + String chapterName = processPara(name).getContent().trim(); + for (String lang : Instance.getConfig().getString(Config.CHAPTER) + .split(",")) { + String chapterWord = Instance.getConfig().getStringX( + Config.CHAPTER, lang); + if (chapterName.startsWith(chapterWord)) { + chapterName = chapterName.substring(chapterWord.length()) + .trim(); + break; + } + } + + if (chapterName.startsWith(Integer.toString(number))) { + chapterName = chapterName.substring( + Integer.toString(number).length()).trim(); + } + + while (chapterName.startsWith(":") || chapterName.startsWith("-")) { + chapterName = chapterName.substring(1).trim(); + } + // + + Chapter chap = new Chapter(number, chapterName); + + if (content != null) { + List paras = makeParagraphs(source, content, pg); + long words = 0; + for (Paragraph para : paras) { + words += para.getWords(); + } + chap.setParagraphs(paras); + chap.setWords(words); + } + + return chap; + + } + + /** + * Convert the given content into {@link Paragraph}s. + * + * @param source + * the source URL of the story + * @param content + * the textual content + * @param pg + * the optional progress reporter + * + * @return the {@link Paragraph}s + * + * @throws IOException + * in case of I/O error + */ + protected List makeParagraphs(URL source, String content, + Progress pg) throws IOException { + if (pg == null) { + pg = new Progress(); + } + + if (isHtml()) { + // Special


processing: + content = content.replaceAll("(
]*>)|(
)|(
)", + "
* * *
"); + } + + List paras = new ArrayList(); + + if (content != null && !content.trim().isEmpty()) { + if (isHtml()) { + String[] tab = content.split("(

|

|
|
)"); + pg.setMinMax(0, tab.length); + int i = 1; + for (String line : tab) { + if (line.startsWith("[") && line.endsWith("]")) { + pg.setName("Extracting image " + i); + } + paras.add(makeParagraph(source, line.trim())); + pg.setProgress(i++); + } + pg.setName(null); + } else { + List lines = new ArrayList(); + BufferedReader buff = null; + try { + buff = new BufferedReader( + new InputStreamReader(new ByteArrayInputStream( + content.getBytes("UTF-8")), "UTF-8")); + for (String line = buff.readLine(); line != null; line = buff + .readLine()) { + lines.add(line.trim()); + } + } finally { + if (buff != null) { + buff.close(); + } + } + + pg.setMinMax(0, lines.size()); + int i = 0; + for (String line : lines) { + if (line.startsWith("[") && line.endsWith("]")) { + pg.setName("Extracting image " + i); + } + paras.add(makeParagraph(source, line)); + pg.setProgress(i++); + } + pg.setName(null); + } + + // Check quotes for "bad" format + List newParas = new ArrayList(); + for (Paragraph para : paras) { + newParas.addAll(requotify(para)); + } + paras = newParas; + + // Remove double blanks/brks + fixBlanksBreaks(paras); + } + + return paras; + } + + /** + * Convert the given line into a single {@link Paragraph}. + * + * @param source + * the source URL of the story + * @param line + * the textual content of the paragraph + * + * @return the {@link Paragraph} + */ + private Paragraph makeParagraph(URL source, String line) { + Image image = null; + if (line.startsWith("[") && line.endsWith("]")) { + image = getImage(this, source, line.substring(1, line.length() - 1) + .trim()); + } + + if (image != null) { + return new Paragraph(image); + } + + return processPara(line); + } + + /** + * Fix the {@link ParagraphType#BLANK}s and {@link ParagraphType#BREAK}s of + * those {@link Paragraph}s. + *

+ * The resulting list will not contain a starting or trailing blank/break + * nor 2 blanks or breaks following each other. + * + * @param paras + * the list of {@link Paragraph}s to fix + */ + protected void fixBlanksBreaks(List paras) { + boolean space = false; + boolean brk = true; + for (int i = 0; i < paras.size(); i++) { + Paragraph para = paras.get(i); + boolean thisSpace = para.getType() == ParagraphType.BLANK; + boolean thisBrk = para.getType() == ParagraphType.BREAK; + + if (i > 0 && space && thisBrk) { + paras.remove(i - 1); + i--; + } else if ((space || brk) && (thisSpace || thisBrk)) { + paras.remove(i); + i--; + } + + space = thisSpace; + brk = thisBrk; + } + + // Remove blank/brk at start + if (paras.size() > 0 + && (paras.get(0).getType() == ParagraphType.BLANK || paras.get( + 0).getType() == ParagraphType.BREAK)) { + paras.remove(0); + } + + // Remove blank/brk at end + int last = paras.size() - 1; + if (paras.size() > 0 + && (paras.get(last).getType() == ParagraphType.BLANK || paras + .get(last).getType() == ParagraphType.BREAK)) { + paras.remove(last); + } + } + + /** + * Get the default cover related to this subject (see .info files). + * + * @param subject + * the subject + * + * @return the cover if any, or NULL + */ + static Image getDefaultCover(String subject) { + if (subject != null && !subject.isEmpty() + && Instance.getCoverDir() != null) { + try { + File fileCover = new File(Instance.getCoverDir(), subject); + return getImage(null, fileCover.toURI().toURL(), subject); + } catch (MalformedURLException e) { + } + } + + return null; + } + + /** + * Return the list of supported image extensions. + * + * @param emptyAllowed + * TRUE to allow an empty extension on first place, which can be + * used when you may already have an extension in your input but + * are not sure about it + * + * @return the extensions + */ + static String[] getImageExt(boolean emptyAllowed) { + if (emptyAllowed) { + return new String[] { "", ".png", ".jpg", ".jpeg", ".gif", ".bmp" }; + } + + return new String[] { ".png", ".jpg", ".jpeg", ".gif", ".bmp" }; + } + + /** + * Check if the given resource can be a local image or a remote image, then + * refresh the cache with it if it is. + * + * @param source + * the story source + * @param line + * the resource to check + * + * @return the image if found, or NULL + * + */ + static Image getImage(BasicSupport_Deprecated support, URL source, + String line) { + URL url = getImageUrl(support, source, line); + if (url != null) { + if ("file".equals(url.getProtocol())) { + if (new File(url.getPath()).isDirectory()) { + return null; + } + } + InputStream in = null; + try { + in = Instance.getCache().open(url, getSupport(url), true); + return new Image(in); + } catch (IOException e) { + } finally { + if (in != null) { + try { + in.close(); + } catch (IOException e) { + } + } + } + } + + return null; + } + + /** + * Check if the given resource can be a local image or a remote image, then + * refresh the cache with it if it is. + * + * @param source + * the story source + * @param line + * the resource to check + * + * @return the image URL if found, or NULL + * + */ + static URL getImageUrl(BasicSupport_Deprecated support, URL source, + String line) { + URL url = null; + + if (line != null) { + // try for files + if (source != null) { + try { + + String relPath = null; + String absPath = null; + try { + String path = new File(source.getFile()).getParent(); + relPath = new File(new File(path), line.trim()) + .getAbsolutePath(); + } catch (Exception e) { + // Cannot be converted to path (one possibility to take + // into account: absolute path on Windows) + } + try { + absPath = new File(line.trim()).getAbsolutePath(); + } catch (Exception e) { + // Cannot be converted to path (at all) + } + + for (String ext : getImageExt(true)) { + File absFile = new File(absPath + ext); + File relFile = new File(relPath + ext); + if (absPath != null && absFile.exists() + && absFile.isFile()) { + url = absFile.toURI().toURL(); + } else if (relPath != null && relFile.exists() + && relFile.isFile()) { + url = relFile.toURI().toURL(); + } + } + } catch (Exception e) { + // Should not happen since we control the correct arguments + } + } + + if (url == null) { + // try for URLs + try { + for (String ext : getImageExt(true)) { + if (Instance.getCache() + .check(new URL(line + ext), true)) { + url = new URL(line + ext); + break; + } + } + + // try out of cache + if (url == null) { + for (String ext : getImageExt(true)) { + try { + url = new URL(line + ext); + Instance.getCache().refresh(url, support, true); + break; + } catch (IOException e) { + // no image with this ext + url = null; + } + } + } + } catch (MalformedURLException e) { + // Not an url + } + } + + // refresh the cached file + if (url != null) { + try { + Instance.getCache().refresh(url, support, true); + } catch (IOException e) { + // woops, broken image + url = null; + } + } + } + + return url; + } + + /** + * Open the input file that will be used through the support. + *

+ * Can return NULL, in which case you are supposed to work without an + * {@link InputStream}. + * + * @param source + * the source {@link URL} + * + * @return the {@link InputStream} + * + * @throws IOException + * in case of I/O error + */ + protected InputStream openInput(URL source) throws IOException { + return Instance.getCache().open(source, this, false); + } + + /** + * Reset then return {@link BasicSupport_Deprecated#in}. + * + * @return {@link BasicSupport_Deprecated#in} + */ + protected InputStream getInput() { + return reset(in); + } + + /** + * Check quotes for bad format (i.e., quotes with normal paragraphs inside) + * and requotify them (i.e., separate them into QUOTE paragraphs and other + * paragraphs (quotes or not)). + * + * @param para + * the paragraph to requotify (not necessarily a quote) + * + * @return the correctly (or so we hope) quotified paragraphs + */ + protected List requotify(Paragraph para) { + List newParas = new ArrayList(); + + if (para.getType() == ParagraphType.QUOTE + && para.getContent().length() > 2) { + String line = para.getContent(); + boolean singleQ = line.startsWith("" + openQuote); + boolean doubleQ = line.startsWith("" + openDoubleQuote); + + // Do not try when more than one quote at a time + // (some stories are not easily readable if we do) + if (singleQ + && line.indexOf(closeQuote, 1) < line + .lastIndexOf(closeQuote)) { + newParas.add(para); + return newParas; + } + if (doubleQ + && line.indexOf(closeDoubleQuote, 1) < line + .lastIndexOf(closeDoubleQuote)) { + newParas.add(para); + return newParas; + } + // + + if (!singleQ && !doubleQ) { + line = openDoubleQuote + line + closeDoubleQuote; + newParas.add(new Paragraph(ParagraphType.QUOTE, line, para + .getWords())); + } else { + char open = singleQ ? openQuote : openDoubleQuote; + char close = singleQ ? closeQuote : closeDoubleQuote; + + int posDot = -1; + boolean inQuote = false; + int i = 0; + for (char car : line.toCharArray()) { + if (car == open) { + inQuote = true; + } else if (car == close) { + inQuote = false; + } else if (car == '.' && !inQuote) { + posDot = i; + break; + } + i++; + } + + if (posDot >= 0) { + String rest = line.substring(posDot + 1).trim(); + line = line.substring(0, posDot + 1).trim(); + long words = 1; + for (char car : line.toCharArray()) { + if (car == ' ') { + words++; + } + } + newParas.add(new Paragraph(ParagraphType.QUOTE, line, words)); + if (!rest.isEmpty()) { + newParas.addAll(requotify(processPara(rest))); + } + } else { + newParas.add(para); + } + } + } else { + newParas.add(para); + } + + return newParas; + } + + /** + * Process a {@link Paragraph} from a raw line of text. + *

+ * Will also fix quotes and HTML encoding if needed. + * + * @param line + * the raw line + * + * @return the processed {@link Paragraph} + */ + protected Paragraph processPara(String line) { + line = ifUnhtml(line).trim(); + + boolean space = true; + boolean brk = true; + boolean quote = false; + boolean tentativeCloseQuote = false; + char prev = '\0'; + int dashCount = 0; + long words = 1; + + StringBuilder builder = new StringBuilder(); + for (char car : line.toCharArray()) { + if (car != '-') { + if (dashCount > 0) { + // dash, ndash and mdash: - – — + // currently: always use mdash + builder.append(dashCount == 1 ? '-' : '—'); + } + dashCount = 0; + } + + if (tentativeCloseQuote) { + tentativeCloseQuote = false; + if (Character.isLetterOrDigit(car)) { + builder.append("'"); + } else { + // handle double-single quotes as double quotes + if (prev == car) { + builder.append(closeDoubleQuote); + continue; + } + + builder.append(closeQuote); + } + } + + switch (car) { + case ' ': // note: unbreakable space + case ' ': + case '\t': + case '\n': // just in case + case '\r': // just in case + if (builder.length() > 0 + && builder.charAt(builder.length() - 1) != ' ') { + words++; + } + builder.append(' '); + break; + + case '\'': + if (space || (brk && quote)) { + quote = true; + // handle double-single quotes as double quotes + if (prev == car) { + builder.deleteCharAt(builder.length() - 1); + builder.append(openDoubleQuote); + } else { + builder.append(openQuote); + } + } else if (prev == ' ' || prev == car) { + // handle double-single quotes as double quotes + if (prev == car) { + builder.deleteCharAt(builder.length() - 1); + builder.append(openDoubleQuote); + } else { + builder.append(openQuote); + } + } else { + // it is a quote ("I'm off") or a 'quote' ("This + // 'good' restaurant"...) + tentativeCloseQuote = true; + } + break; + + case '"': + if (space || (brk && quote)) { + quote = true; + builder.append(openDoubleQuote); + } else if (prev == ' ') { + builder.append(openDoubleQuote); + } else { + builder.append(closeDoubleQuote); + } + break; + + case '-': + if (space) { + quote = true; + } else { + dashCount++; + } + space = false; + break; + + case '*': + case '~': + case '/': + case '\\': + case '<': + case '>': + case '=': + case '+': + case '_': + case '–': + case '—': + space = false; + builder.append(car); + break; + + case '‘': + case '`': + case '‹': + case '﹁': + case '〈': + case '「': + if (space || (brk && quote)) { + quote = true; + builder.append(openQuote); + } else { + // handle double-single quotes as double quotes + if (prev == car) { + builder.deleteCharAt(builder.length() - 1); + builder.append(openDoubleQuote); + } else { + builder.append(openQuote); + } + } + space = false; + brk = false; + break; + + case '’': + case '›': + case '﹂': + case '〉': + case '」': + space = false; + brk = false; + // handle double-single quotes as double quotes + if (prev == car) { + builder.deleteCharAt(builder.length() - 1); + builder.append(closeDoubleQuote); + } else { + builder.append(closeQuote); + } + break; + + case '«': + case '“': + case '﹃': + case '《': + case '『': + if (space || (brk && quote)) { + quote = true; + builder.append(openDoubleQuote); + } else { + builder.append(openDoubleQuote); + } + space = false; + brk = false; + break; + + case '»': + case '”': + case '﹄': + case '》': + case '』': + space = false; + brk = false; + builder.append(closeDoubleQuote); + break; + + default: + space = false; + brk = false; + builder.append(car); + break; + } + + prev = car; + } + + if (tentativeCloseQuote) { + tentativeCloseQuote = false; + builder.append(closeQuote); + } + + line = builder.toString().trim(); + + ParagraphType type = ParagraphType.NORMAL; + if (space) { + type = ParagraphType.BLANK; + } else if (brk) { + type = ParagraphType.BREAK; + } else if (quote) { + type = ParagraphType.QUOTE; + } + + return new Paragraph(type, line, words); + } + + /** + * Remove the HTML from the input if + * {@link BasicSupport_Deprecated#isHtml()} is true. + * + * @param input + * the input + * + * @return the no html version if needed + */ + private String ifUnhtml(String input) { + if (isHtml() && input != null) { + return StringUtils.unhtml(input); + } + + return input; + } + + /** + * Reset the given {@link InputStream} and return it. + * + * @param in + * the {@link InputStream} to reset + * + * @return the same {@link InputStream} after reset + */ + static protected InputStream reset(InputStream in) { + try { + if (in != null) { + in.reset(); + } + } catch (IOException e) { + } + + return in; + } + + /** + * Return the first line from the given input which correspond to the given + * selectors. + * + * @param in + * the input + * @param needle + * a string that must be found inside the target line (also + * supports "^" at start to say "only if it starts with" the + * needle) + * @param relativeLine + * the line to return based upon the target line position (-1 = + * the line before, 0 = the target line...) + * + * @return the line + */ + static protected String getLine(InputStream in, String needle, + int relativeLine) { + return getLine(in, needle, relativeLine, true); + } + + /** + * Return a line from the given input which correspond to the given + * selectors. + * + * @param in + * the input + * @param needle + * a string that must be found inside the target line (also + * supports "^" at start to say "only if it starts with" the + * needle) + * @param relativeLine + * the line to return based upon the target line position (-1 = + * the line before, 0 = the target line...) + * @param first + * takes the first result (as opposed to the last one, which will + * also always spend the input) + * + * @return the line + */ + static protected String getLine(InputStream in, String needle, + int relativeLine, boolean first) { + String rep = null; + + reset(in); + + List lines = new ArrayList(); + @SuppressWarnings("resource") + Scanner scan = new Scanner(in, "UTF-8"); + int index = -1; + scan.useDelimiter("\\n"); + while (scan.hasNext()) { + lines.add(scan.next()); + + if (index == -1) { + if (needle.startsWith("^")) { + if (lines.get(lines.size() - 1).startsWith( + needle.substring(1))) { + index = lines.size() - 1; + } + + } else { + if (lines.get(lines.size() - 1).contains(needle)) { + index = lines.size() - 1; + } + } + } + + if (index >= 0 && index + relativeLine < lines.size()) { + rep = lines.get(index + relativeLine); + if (first) { + break; + } + } + } + + return rep; + } + + /** + * Return the text between the key and the endKey (and optional subKey can + * be passed, in this case we will look for the key first, then take the + * text between the subKey and the endKey). + *

+ * Will only match the first line with the given key if more than one are + * possible. Which also means that if the subKey or endKey is not found on + * that line, NULL will be returned. + * + * @param in + * the input + * @param key + * the key to match (also supports "^" at start to say + * "only if it starts with" the key) + * @param subKey + * the sub key or NULL if none + * @param endKey + * the end key or NULL for "up to the end" + * @return the text or NULL if not found + */ + static protected String getKeyLine(InputStream in, String key, + String subKey, String endKey) { + return getKeyText(getLine(in, key, 0), key, subKey, endKey); + } + + /** + * Return the text between the key and the endKey (and optional subKey can + * be passed, in this case we will look for the key first, then take the + * text between the subKey and the endKey). + * + * @param in + * the input + * @param key + * the key to match (also supports "^" at start to say + * "only if it starts with" the key) + * @param subKey + * the sub key or NULL if none + * @param endKey + * the end key or NULL for "up to the end" + * @return the text or NULL if not found + */ + static protected String getKeyText(String in, String key, String subKey, + String endKey) { + String result = null; + + String line = in; + if (line != null && line.contains(key)) { + line = line.substring(line.indexOf(key) + key.length()); + if (subKey == null || subKey.isEmpty() || line.contains(subKey)) { + if (subKey != null) { + line = line.substring(line.indexOf(subKey) + + subKey.length()); + } + if (endKey == null || line.contains(endKey)) { + if (endKey != null) { + line = line.substring(0, line.indexOf(endKey)); + result = line; + } + } + } + } + + return result; + } + + /** + * Return the text between the key and the endKey (optional subKeys can be + * passed, in this case we will look for the subKeys first, then take the + * text between the key and the endKey). + * + * @param in + * the input + * @param key + * the key to match + * @param endKey + * the end key or NULL for "up to the end" + * @param afters + * the sub-keys to find before checking for key/endKey + * + * @return the text or NULL if not found + */ + static protected String getKeyTextAfter(String in, String key, + String endKey, String... afters) { + + if (in != null && !in.isEmpty()) { + int pos = indexOfAfter(in, 0, afters); + if (pos < 0) { + return null; + } + + in = in.substring(pos); + } + + return getKeyText(in, key, null, endKey); + } + + /** + * Return the first index after all the given "afters" have been found in + * the {@link String}, or -1 if it was not possible. + * + * @param in + * the input + * @param startAt + * start at this position in the string + * @param afters + * the sub-keys to find before checking for key/endKey + * + * @return the text or NULL if not found + */ + static protected int indexOfAfter(String in, int startAt, String... afters) { + int pos = -1; + if (in != null && !in.isEmpty()) { + pos = startAt; + if (afters != null) { + for (int i = 0; pos >= 0 && i < afters.length; i++) { + String subKey = afters[i]; + if (!subKey.isEmpty()) { + pos = in.indexOf(subKey, pos); + if (pos >= 0) { + pos += subKey.length(); + } + } + } + } + } + + return pos; + } +} diff --git a/src/be/nikiroo/fanfix/supported/E621.java b/src/be/nikiroo/fanfix/supported/E621.java index 72c68b1..5fe99a8 100644 --- a/src/be/nikiroo/fanfix/supported/E621.java +++ b/src/be/nikiroo/fanfix/supported/E621.java @@ -26,7 +26,7 @@ import be.nikiroo.utils.StringUtils; * * @author niki */ -class E621 extends BasicSupport { +class E621 extends BasicSupport_Deprecated { @Override public String getSourceName() { return "e621.net"; diff --git a/src/be/nikiroo/fanfix/supported/EHentai.java b/src/be/nikiroo/fanfix/supported/EHentai.java index c082b91..574ea06 100644 --- a/src/be/nikiroo/fanfix/supported/EHentai.java +++ b/src/be/nikiroo/fanfix/supported/EHentai.java @@ -23,7 +23,7 @@ import be.nikiroo.utils.StringUtils; * * @author niki */ -class EHentai extends BasicSupport { +class EHentai extends BasicSupport_Deprecated { @Override public String getSourceName() { return "e-hentai.org"; diff --git a/src/be/nikiroo/fanfix/supported/Epub.java b/src/be/nikiroo/fanfix/supported/Epub.java index ba56cfb..9b06f20 100644 --- a/src/be/nikiroo/fanfix/supported/Epub.java +++ b/src/be/nikiroo/fanfix/supported/Epub.java @@ -200,7 +200,7 @@ class Epub extends InfoText { } @Override - protected void close() throws IOException { + protected void close() { if (tmp != null && tmp.exists()) { if (!tmp.delete()) { tmp.deleteOnExit(); @@ -210,7 +210,11 @@ class Epub extends InfoText { tmp = null; if (fakeIn != null) { - fakeIn.close(); + try { + fakeIn.close(); + } catch (Exception e) { + Instance.getTraceHandler().error(e); + } } super.close(); diff --git a/src/be/nikiroo/fanfix/supported/Fanfiction.java b/src/be/nikiroo/fanfix/supported/Fanfiction.java index b80156d..0feb964 100644 --- a/src/be/nikiroo/fanfix/supported/Fanfiction.java +++ b/src/be/nikiroo/fanfix/supported/Fanfiction.java @@ -25,7 +25,7 @@ import be.nikiroo.utils.StringUtils; * * @author niki */ -class Fanfiction extends BasicSupport { +class Fanfiction extends BasicSupport_Deprecated { @Override protected boolean isHtml() { return true; @@ -142,7 +142,7 @@ class Fanfiction extends BasicSupport { } } - return fixAuthor(author); + return BasicSupportHelper.fixAuthor(author); } private String getDate(InputStream in) { diff --git a/src/be/nikiroo/fanfix/supported/Fimfiction.java b/src/be/nikiroo/fanfix/supported/Fimfiction.java index 7d2eec4..ed9c9ad 100644 --- a/src/be/nikiroo/fanfix/supported/Fimfiction.java +++ b/src/be/nikiroo/fanfix/supported/Fimfiction.java @@ -23,7 +23,7 @@ import be.nikiroo.utils.StringUtils; * * @author niki */ -class Fimfiction extends BasicSupport { +class Fimfiction extends BasicSupport_Deprecated { @Override protected boolean isHtml() { return true; diff --git a/src/be/nikiroo/fanfix/supported/FimfictionApi.java b/src/be/nikiroo/fanfix/supported/FimfictionApi.java index ee436f1..231405b 100644 --- a/src/be/nikiroo/fanfix/supported/FimfictionApi.java +++ b/src/be/nikiroo/fanfix/supported/FimfictionApi.java @@ -23,7 +23,7 @@ import be.nikiroo.utils.Progress; * * @author niki */ -class FimfictionApi extends BasicSupport { +class FimfictionApi extends BasicSupport_Deprecated { private String oauth; private String storyId; private String json; diff --git a/src/be/nikiroo/fanfix/supported/Html.java b/src/be/nikiroo/fanfix/supported/Html.java index fffbcd7..8dec5f7 100644 --- a/src/be/nikiroo/fanfix/supported/Html.java +++ b/src/be/nikiroo/fanfix/supported/Html.java @@ -6,6 +6,8 @@ import java.net.MalformedURLException; import java.net.URISyntaxException; import java.net.URL; +import be.nikiroo.fanfix.Instance; + /** * Support class for HTML files created with this program (as we need some * metadata available in those we create). @@ -34,17 +36,17 @@ class Html extends InfoText { } @Override - public URL getCanonicalUrl(URL source) throws IOException { + public URL getCanonicalUrl(URL source) { if (source.toString().endsWith(File.separator + "index.html")) { try { File fakeFile = new File(source.toURI()); // "story/index.html" fakeFile = new File(fakeFile.getParent()); // "story" fakeFile = new File(fakeFile, fakeFile.getName()); // "story/story" return fakeFile.toURI().toURL(); - } catch (URISyntaxException e) { - throw new IOException( - "file not supported (maybe not created with this program or corrupt)", - e); + } catch (Exception e) { + Instance.getTraceHandler().error( + new IOException("Cannot find the right URL for " + + source, e)); } } diff --git a/src/be/nikiroo/fanfix/supported/InfoReader.java b/src/be/nikiroo/fanfix/supported/InfoReader.java index b65256c..5203cc8 100644 --- a/src/be/nikiroo/fanfix/supported/InfoReader.java +++ b/src/be/nikiroo/fanfix/supported/InfoReader.java @@ -58,7 +58,7 @@ public class InfoReader { if (withCover) { String infoTag = getInfoTag(in, "COVER"); if (infoTag != null && !infoTag.trim().isEmpty()) { - meta.setCover(BasicSupport.getImage(null, sourceInfoFile, + meta.setCover(BasicSupportHelper.getImage(null, sourceInfoFile, infoTag)); } // Second chance: try to check for a cover next to the info file @@ -70,8 +70,8 @@ public class InfoReader { + Instance.getConfig() .getString(Config.IMAGE_FORMAT_COVER) .toLowerCase(); - meta.setCover(BasicSupport.getImage(null, sourceInfoFile, - info + ext)); + meta.setCover(BasicSupportHelper.getImage(null, + sourceInfoFile, info + ext)); } } } @@ -84,7 +84,7 @@ public class InfoReader { meta.setFakeCover(Boolean.parseBoolean(getInfoTag(in, "FAKE_COVER"))); if (withCover && meta.getCover() == null) { - meta.setCover(BasicSupport.getDefaultCover(meta.getSubject())); + meta.setCover(BasicSupportHelper.getDefaultCover(meta.getSubject())); } return meta; @@ -138,7 +138,7 @@ public class InfoReader { if (in != null) { in.reset(); - String value = BasicSupport.getLine(in, key, 0); + String value = BasicSupport_Deprecated.getLine(in, key, 0); if (value != null && !value.isEmpty()) { value = value.trim().substring(key.length() - 1).trim(); if (value.startsWith("'") && value.endsWith("'") diff --git a/src/be/nikiroo/fanfix/supported/MangaFox.java b/src/be/nikiroo/fanfix/supported/MangaFox.java index 8d31cb8..8fc1965 100644 --- a/src/be/nikiroo/fanfix/supported/MangaFox.java +++ b/src/be/nikiroo/fanfix/supported/MangaFox.java @@ -16,7 +16,7 @@ import be.nikiroo.utils.Image; import be.nikiroo.utils.Progress; import be.nikiroo.utils.StringUtils; -class MangaFox extends BasicSupport { +class MangaFox extends BasicSupport_Deprecated { @Override protected boolean isHtml() { return true; @@ -376,6 +376,9 @@ class MangaFox extends BasicSupport { @Override protected boolean supports(URL url) { + // Broken code (see MangaFoxNew) + if (true) + return false; return "mangafox.me".equals(url.getHost()) || "www.mangafox.me".equals(url.getHost()); } diff --git a/src/be/nikiroo/fanfix/supported/SupportType.java b/src/be/nikiroo/fanfix/supported/SupportType.java new file mode 100644 index 0000000..2c92562 --- /dev/null +++ b/src/be/nikiroo/fanfix/supported/SupportType.java @@ -0,0 +1,117 @@ +package be.nikiroo.fanfix.supported; + +import be.nikiroo.fanfix.Instance; +import be.nikiroo.fanfix.bundles.StringId; + +/** + * The supported input types for which we can get a {@link BasicSupport} object. + * + * @author niki + */ +public enum SupportType { + /** EPUB files created with this program */ + EPUB, + /** Pure text file with some rules */ + TEXT, + /** TEXT but with associated .info file */ + INFO_TEXT, + /** My Little Pony fanfictions */ + FIMFICTION, + /** Fanfictions from a lot of different universes */ + FANFICTION, + /** Website with lots of Mangas */ + MANGAFOX, + /** Furry website with comics support */ + E621, + /** Furry website with stories */ + YIFFSTAR, + /** Comics and images groups, mostly but not only NSFW */ + E_HENTAI, + /** CBZ files */ + CBZ, + /** HTML files */ + HTML; + + /** + * A description of this support type (more information than the + * {@link BasicSupport#getSourceName()}). + * + * @return the description + */ + public String getDesc() { + String desc = Instance.getTrans().getStringX(StringId.INPUT_DESC, + this.name()); + + if (desc == null) { + desc = Instance.getTrans().getString(StringId.INPUT_DESC, this); + } + + return desc; + } + + /** + * The name of this support type (a short version). + * + * @return the name + */ + public String getSourceName() { + BasicSupport support = BasicSupport.getSupport(this, null); + if (support != null) { + return support.getSourceName(); + } + + return null; + } + + @Override + public String toString() { + return super.toString().toLowerCase(); + } + + /** + * Call {@link SupportType#valueOf(String)} after conversion to upper case. + * + * @param typeName + * the possible type name + * + * @return NULL or the type + */ + public static SupportType valueOfUC(String typeName) { + return SupportType.valueOf(typeName == null ? null : typeName + .toUpperCase()); + } + + /** + * Call {@link SupportType#valueOf(String)} after conversion to upper case + * but return NULL for NULL instead of raising exception. + * + * @param typeName + * the possible type name + * + * @return NULL or the type + */ + public static SupportType valueOfNullOkUC(String typeName) { + if (typeName == null) { + return null; + } + + return SupportType.valueOfUC(typeName); + } + + /** + * Call {@link SupportType#valueOf(String)} after conversion to upper case + * but return NULL in case of error instead of raising an exception. + * + * @param typeName + * the possible type name + * + * @return NULL or the type + */ + public static SupportType valueOfAllOkUC(String typeName) { + try { + return SupportType.valueOfUC(typeName); + } catch (Exception e) { + return null; + } + } +} \ No newline at end of file diff --git a/src/be/nikiroo/fanfix/supported/Text.java b/src/be/nikiroo/fanfix/supported/Text.java index 79ee851..1437911 100644 --- a/src/be/nikiroo/fanfix/supported/Text.java +++ b/src/be/nikiroo/fanfix/supported/Text.java @@ -33,7 +33,7 @@ import be.nikiroo.utils.Progress; * * @author niki */ -class Text extends BasicSupport { +class Text extends BasicSupport_Deprecated { @Override protected boolean isHtml() { return false; @@ -123,7 +123,7 @@ class Text extends BasicSupport { author = authorDate.substring(0, pos); } - return fixAuthor(author); + return BasicSupportHelper.fixAuthor(author); } private String getDate(InputStream in) { diff --git a/src/be/nikiroo/fanfix/supported/YiffStar.java b/src/be/nikiroo/fanfix/supported/YiffStar.java index a612d4f..ca331e5 100644 --- a/src/be/nikiroo/fanfix/supported/YiffStar.java +++ b/src/be/nikiroo/fanfix/supported/YiffStar.java @@ -24,7 +24,7 @@ import be.nikiroo.utils.StringUtils; * * @author niki */ -class YiffStar extends BasicSupport { +class YiffStar extends BasicSupport_Deprecated { @Override public String getSourceName() { @@ -93,19 +93,23 @@ class YiffStar extends BasicSupport { } @Override - public URL getCanonicalUrl(URL source) throws IOException { - if (source.getPath().startsWith("/view")) { - source = new URL(source.toString() + "/guest"); - InputStream in = Instance.getCache().open(source, this, false); - String line = getLine(in, "/browse/folder/", 0); - if (line != null) { - String[] tab = line.split("\""); - if (tab.length > 1) { - String groupUrl = source.getProtocol() + "://" - + source.getHost() + tab[1]; - return guest(groupUrl); + public URL getCanonicalUrl(URL source) { + try { + if (source.getPath().startsWith("/view")) { + source = new URL(source.toString() + "/guest"); + InputStream in = Instance.getCache().open(source, this, false); + String line = getLine(in, "/browse/folder/", 0); + if (line != null) { + String[] tab = line.split("\""); + if (tab.length > 1) { + String groupUrl = source.getProtocol() + "://" + + source.getHost() + tab[1]; + return guest(groupUrl); + } } } + } catch (Exception e) { + Instance.getTraceHandler().error(e); } return super.getCanonicalUrl(source); diff --git a/src/be/nikiroo/fanfix/test/BasicSupportTest.java b/src/be/nikiroo/fanfix/test/BasicSupportTest.java index dba0ef0..a3f5221 100644 --- a/src/be/nikiroo/fanfix/test/BasicSupportTest.java +++ b/src/be/nikiroo/fanfix/test/BasicSupportTest.java @@ -15,7 +15,8 @@ import be.nikiroo.fanfix.data.Paragraph; import be.nikiroo.fanfix.data.Paragraph.ParagraphType; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.supported.BasicSupport; -import be.nikiroo.fanfix.supported.BasicSupport.SupportType; +import be.nikiroo.fanfix.supported.BasicSupport_Deprecated; +import be.nikiroo.fanfix.supported.SupportType; import be.nikiroo.utils.IOUtils; import be.nikiroo.utils.Progress; import be.nikiroo.utils.test.TestCase; @@ -334,11 +335,10 @@ class BasicSupportTest extends TestLauncher { @Override public void test() throws Exception { - BasicSupport support = BasicSupport - .getSupport(SupportType.TEXT); + BasicSupport support = BasicSupport.getSupport( + SupportType.TEXT, tmp.toURI().toURL()); - Story story = support - .process(tmp.toURI().toURL(), null); + Story story = support.process(null); assertEquals(2, story.getChapters().size()); assertEquals(1, story.getChapters().get(1) @@ -377,11 +377,10 @@ class BasicSupportTest extends TestLauncher { @Override public void test() throws Exception { - BasicSupport support = BasicSupport - .getSupport(SupportType.TEXT); + BasicSupport support = BasicSupport.getSupport( + SupportType.TEXT, tmp.toURI().toURL()); - Story story = support - .process(tmp.toURI().toURL(), null); + Story story = support.process(null); assertEquals(2, story.getChapters().size()); assertEquals(1, story.getChapters().get(1) @@ -394,7 +393,7 @@ class BasicSupportTest extends TestLauncher { }); } - private class BasicSupportEmpty extends BasicSupport { + private class BasicSupportEmpty extends BasicSupport_Deprecated { @Override protected String getSourceName() { return null; -- 2.27.0