keep publisher on re-import
[fanfix.git] / src / be / nikiroo / fanfix / supported / BasicSupport.java
index 527f4de8310a09fe6e429b7b3bbca39d7cbc767e..ff9ecaf9be3cc80666d964ee48da375d1307bae6 100644 (file)
@@ -2,14 +2,18 @@ package be.nikiroo.fanfix.supported;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.net.MalformedURLException;
 import java.net.URL;
 import java.util.ArrayList;
 import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.Scanner;
 import java.util.Map.Entry;
 
+import org.json.JSONException;
+import org.json.JSONObject;
 import org.jsoup.helper.DataUtil;
 import org.jsoup.nodes.Document;
 import org.jsoup.nodes.Element;
@@ -37,13 +41,10 @@ public abstract class BasicSupport {
        private URL source;
        private SupportType type;
        private URL currentReferer; // with only one 'r', as in 'HTTP'...
-
-       /**
-        * The name of this support class.
-        * 
-        * @return the name
-        */
-       protected abstract String getSourceName();
+       
+       static protected BasicSupportHelper bsHelper = new BasicSupportHelper();
+       static protected BasicSupportImages bsImages = new BasicSupportImages();
+       static protected BasicSupportPara bsPara = new BasicSupportPara(new BasicSupportHelper(), new BasicSupportImages());
 
        /**
         * Check if the given resource is supported by this {@link BasicSupport}.
@@ -84,7 +85,7 @@ public abstract class BasicSupport {
        protected abstract String getDesc() throws IOException;
 
        /**
-        * Return the list of chapters (name and resource). *
+        * Return the list of chapters (name and resource).
         * <p>
         * Can be NULL if this {@link BasicSupport} do no use chapters.
         * 
@@ -227,8 +228,7 @@ public abstract class BasicSupport {
         */
        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());
+               return DataUtil.load(Instance.getInstance().getCache().open(source, this, false), "UTF-8", url.toString());
        }
 
        /**
@@ -247,27 +247,6 @@ public abstract class BasicSupport {
                setCurrentReferer(null);
        }
 
-       /**
-        * Process the given story resource into a partially filled {@link Story}
-        * object containing the name and metadata, except for the description.
-        * 
-        * @return the {@link Story}
-        * 
-        * @throws IOException
-        *             in case of I/O error
-        */
-       public final Story processMeta() throws IOException {
-               Story story = null;
-
-               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.
@@ -293,37 +272,102 @@ public abstract class BasicSupport {
                pg.setProgress(30);
 
                Story story = new Story();
+               
                MetaData meta = getMeta();
-               if (meta.getCreationDate() == null || meta.getCreationDate().isEmpty()) {
-                       meta.setCreationDate(StringUtils.fromTime(new Date().getTime()));
+               meta.setType(getType().toString());
+               meta.setSource(getType().getSourceName());
+               if (meta.getPublisher() == null) {
+                       meta.setPublisher(getType().getSourceName());
+               }
+
+               if (meta.getCreationDate() == null
+                               || meta.getCreationDate().trim().isEmpty()) {
+                       meta.setCreationDate(bsHelper
+                                       .formatDate(StringUtils.fromTime(new Date().getTime())));
                }
                story.setMeta(meta);
+               pg.put("meta", meta);
 
                pg.setProgress(50);
 
                if (meta.getCover() == null) {
-                       meta.setCover(BasicSupportHelper.getDefaultCover(meta.getSubject()));
+                       meta.setCover(bsHelper.getDefaultCover(meta.getSubject()));
                }
 
                pg.setProgress(60);
 
                if (getDesc) {
-                       String descChapterName = Instance.getTrans().getString(
-                                       StringId.DESCRIPTION);
-                       story.getMeta().setResume(
-                                       BasicSupportPara.makeChapter(this, source, 0,
-                                                       descChapterName, //
-                                                       getDesc(), isHtml(), null));
+                       String descChapterName = Instance.getInstance().getTrans().getString(StringId.DESCRIPTION);
+                       story.getMeta().setResume(bsPara.makeChapter(this, source, 0, descChapterName, //
+                                       getDesc(), isHtml(), null));
                }
 
-               pg.setProgress(100);
+               pg.done();
                return story;
        }
 
        /**
-        * Actual processing step, without the calls to other methods.
+        * Utility method to convert the given URL into a JSON object.
         * <p>
-        * Will convert the story resource into a fully filled {@link Story} object.
+        * Note that this method expects small JSON files (everything is copied into
+        * memory at least twice).
+        * 
+        * @param url
+        *            the URL to parse
+        * @param stable
+        *            TRUE for more stable resources, FALSE when they often change
+        * 
+        * @return the JSON object
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       protected JSONObject getJson(String url, boolean stable)
+                       throws IOException {
+               try {
+                       return getJson(new URL(url), stable);
+               } catch (MalformedURLException e) {
+                       throw new IOException("Malformed URL: " + url, e);
+               }
+       }
+
+       /**
+        * Utility method to convert the given URL into a JSON object.
+        * <p>
+        * Note that this method expects small JSON files (everything is copied into
+        * memory at least twice).
+        * 
+        * @param url
+        *            the URL to parse
+        * @param stable
+        *            TRUE for more stable resources, FALSE when they often change
+        * 
+        * @return the JSON object
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       protected JSONObject getJson(URL url, boolean stable) throws IOException {
+               InputStream in = Instance.getInstance().getCache().open(url, null,
+                               stable);
+               try {
+                       Scanner scan = new Scanner(in);
+                       scan.useDelimiter("\0");
+                       try {
+                               return new JSONObject(scan.next());
+                       } catch (JSONException e) {
+                               throw new IOException(e);
+                       } finally {
+                               scan.close();
+                       }
+               } finally {
+                       in.close();
+               }
+       }
+
+       /**
+        * Process the given story resource into a fully filled {@link Story}
+        * object.
         * 
         * @param pg
         *            the optional progress reporter
@@ -333,22 +377,41 @@ public abstract class BasicSupport {
         * @throws IOException
         *             in case of I/O error
         */
-       // TODO: add final
+       // TODO: ADD final when BasicSupport_Deprecated is gone
        public Story process(Progress pg) throws IOException {
                setCurrentReferer(source);
                login();
                sourceNode = loadDocument(source);
 
                try {
-                       return doProcess(pg);
+                       Story story = doProcess(pg);
+                       
+                       // Check for "no chapters" stories
+                       if (story.getChapters().isEmpty()
+                                       && story.getMeta().getResume() != null
+                                       && !story.getMeta().getResume().getParagraphs().isEmpty()) {
+                               Chapter resume = story.getMeta().getResume();
+                               resume.setName("");
+                               resume.setNumber(1);
+                               story.getChapters().add(resume);
+                               story.getMeta().setWords(resume.getWords());
+
+                               String descChapterName = Instance.getInstance().getTrans()
+                                               .getString(StringId.DESCRIPTION);
+                               resume = new Chapter(0, descChapterName);
+                               story.getMeta().setResume(resume);
+                       }
+                       
+                       return story;
                } finally {
                        close();
                }
        }
 
        /**
-        * Process the given story resource into a fully filled {@link Story}
-        * object.
+        * Actual processing step, without the calls to other methods.
+        * <p>
+        * Will convert the story resource into a fully filled {@link Story} object.
         * 
         * @param pg
         *            the optional progress reporter
@@ -358,31 +421,28 @@ public abstract class BasicSupport {
         * @throws IOException
         *             in case of I/O error
         */
-       public Story doProcess(Progress pg) throws IOException {
+       protected Story doProcess(Progress pg) throws IOException {
                if (pg == null) {
                        pg = new Progress();
                } else {
                        pg.setMinMax(0, 100);
                }
+               
+               pg.setName("Initialising");
 
                pg.setProgress(1);
                Progress pgMeta = new Progress();
                pg.addProgress(pgMeta, 10);
                Story story = processMeta(true, pgMeta);
-               if (!pgMeta.isDone()) {
-                       pgMeta.setProgress(pgMeta.getMax()); // 10%
-               }
-
-               pg.setName("Retrieving " + story.getMeta().getTitle());
+               pgMeta.done(); // 10%
+               pg.put("meta", story.getMeta());
 
                Progress pgGetChapters = new Progress();
                pg.addProgress(pgGetChapters, 10);
                story.setChapters(new ArrayList<Chapter>());
                List<Entry<String, URL>> chapters = getChapters(pgGetChapters);
-               if (!pgGetChapters.isDone()) {
-                       pgGetChapters.setProgress(pgGetChapters.getMax()); // 20%
-               }
-
+               pgGetChapters.done(); // 20%
+               
                if (chapters != null) {
                        Progress pgChaps = new Progress("Extracting chapters", 0,
                                        chapters.size() * 300);
@@ -406,32 +466,52 @@ public abstract class BasicSupport {
 
                                String content = getChapterContent(chapUrl, i,
                                                pgGetChapterContent);
-                               if (!pgGetChapterContent.isDone()) {
-                                       pgGetChapterContent.setProgress(pgGetChapterContent
-                                                       .getMax());
-                               }
-
-                               Chapter cc = BasicSupportPara.makeChapter(this, chapUrl, i,
+                               pgGetChapterContent.done();
+                               Chapter cc = bsPara.makeChapter(this, chapUrl, i,
                                                chapName, content, isHtml(), pgMakeChapter);
-                               if (!pgMakeChapter.isDone()) {
-                                       pgMakeChapter.setProgress(pgMakeChapter.getMax());
-                               }
+                               pgMakeChapter.done();
 
                                words += cc.getWords();
                                story.getChapters().add(cc);
-                               story.getMeta().setWords(words);
 
                                i++;
                        }
+                       
+                       story.getMeta().setWords(words);
 
                        pgChaps.setName("Extracting chapters");
-               } else {
-                       pg.setProgress(80);
+                       pgChaps.done();
                }
 
+               pg.done();
+
                return story;
        }
 
+       /**
+        * Create a chapter from the given data.
+        * 
+        * @param source
+        *            the source URL for this content, which can be used to try and
+        *            find images if images are present in the format [image-url]
+        * @param number
+        *            the chapter number (0 = description)
+        * @param name
+        *            the chapter name
+        * @param content
+        *            the content of the chapter
+        *            
+        * @return the {@link Chapter}, never NULL
+        * 
+        * @throws IOException
+        *             in case of I/O error
+        */
+       public Chapter makeChapter(URL source, int number, String name,
+                       String content) throws IOException {
+               return bsPara.makeChapter(this, source, number, name,
+                               content, isHtml(), null);
+       }
+
        /**
         * Return a {@link BasicSupport} implementation supporting the given
         * resource if possible.
@@ -471,10 +551,11 @@ public abstract class BasicSupport {
         * Return a {@link BasicSupport} implementation supporting the given type.
         * 
         * @param type
-        *            the type
+        *            the type, must not be NULL
         * @param url
         *            the {@link URL} to support (can be NULL to get an
-        *            "abstract support")
+        *            "abstract support"; if not NULL, will be used as the source
+        *            URL)
         * 
         * @return an implementation that supports it, or NULL
         */
@@ -502,8 +583,8 @@ public abstract class BasicSupport {
                case TEXT:
                        support = new Text();
                        break;
-               case MANGAFOX:
-                       support = new MangaFox();
+               case MANGAHUB:
+                       support = new MangaHub();
                        break;
                case E621:
                        support = new E621();