From: Niki Roo Date: Fri, 12 Apr 2019 07:36:35 +0000 (+0200) Subject: Merge branch 'master' into search X-Git-Url: http://git.nikiroo.be/?p=fanfix.git;a=commitdiff_plain;h=7879b47fdbdcaf465327bcbee53a7e31331feb97;hp=dddfac1d036f22b694a00c029744ac0e92168a8f Merge branch 'master' into search --- diff --git a/src/be/nikiroo/fanfix/Main.java b/src/be/nikiroo/fanfix/Main.java index 953bc45..d5a5deb 100644 --- a/src/be/nikiroo/fanfix/Main.java +++ b/src/be/nikiroo/fanfix/Main.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; +import java.util.ArrayList; import java.util.List; import be.nikiroo.fanfix.bundles.StringId; @@ -20,6 +21,7 @@ import be.nikiroo.fanfix.output.BasicOutput.OutputType; import be.nikiroo.fanfix.reader.BasicReader; import be.nikiroo.fanfix.reader.Reader; import be.nikiroo.fanfix.reader.Reader.ReaderType; +import be.nikiroo.fanfix.searchable.BasicSearchable; import be.nikiroo.fanfix.supported.BasicSupport; import be.nikiroo.fanfix.supported.SupportType; import be.nikiroo.utils.Progress; @@ -33,7 +35,7 @@ import be.nikiroo.utils.serial.server.ServerObject; */ public class Main { private enum MainAction { - IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER, START, VERSION, SERVER, STOP_SERVER, REMOTE, SET_SOURCE, SET_TITLE, SET_AUTHOR + IMPORT, EXPORT, CONVERT, READ, READ_URL, LIST, HELP, SET_READER, START, VERSION, SERVER, STOP_SERVER, REMOTE, SET_SOURCE, SET_TITLE, SET_AUTHOR, SEARCH, SEARCH_TAG } /** @@ -59,6 +61,11 @@ public class Main { * *
  • --read-url [URL] ([chapter number]): convert on the fly and read the * story, without saving it
  • + *
  • --search WEBSITE [free text] ([page] ([item])): search for the given terms, + * show the given page (page 0 means "how many page do we have", starts at page 1)
  • + *
  • --search-tag WEBSITE ([tag 1] [tag2...] ([page] ([item]))): list the known + * tags or search the stories for the given tag(s), show the given page of results + * (page 0 means "how many page do we have", starts at page 1)
  • *
  • --list ([type]): list the stories present in the library
  • *
  • --set-source [id] [new source]: change the source of the given story
  • *
  • --set-title [id] [new title]: change the title of the given story
  • @@ -88,6 +95,11 @@ public class Main { Boolean plusInfo = null; String host = null; Integer port = null; + SupportType searchOn = null; + String search = null; + List tags = new ArrayList(); + Integer page = null; + Integer item = null; boolean noMoreActions = false; @@ -200,6 +212,64 @@ public class Main { exitCode = 255; } break; + case SEARCH: + if (searchOn == null) { + searchOn = SupportType.valueOfAllOkUC(args[i]); + + if (searchOn == null) { + Instance.getTraceHandler().error( + "Website not known: <" + args[i] + ">"); + exitCode = 255; + } + + if (BasicSearchable.getSearchable(searchOn) == null) { + Instance.getTraceHandler().error( + "Website not supported: " + searchOn); + exitCode = 255; + } + } else if (search == null) { + search = args[i]; + } else if (page == null) { + try { + page = Integer.parseInt(args[i]); + } catch (NumberFormatException e) { + Instance.getTraceHandler().error( + new Exception("Incorrect page number: <" + + args[i] + ">", e)); + exitCode = 255; + } + } else if (item == null) { + try { + item = Integer.parseInt(args[i]); + } catch (NumberFormatException e) { + Instance.getTraceHandler().error( + new Exception("Incorrect item number: <" + + args[i] + ">", e)); + exitCode = 255; + } + } else { + exitCode = 255; + } + break; + case SEARCH_TAG: + if (searchOn == null) { + searchOn = SupportType.valueOfAllOkUC(args[i]); + + if (searchOn == null) { + Instance.getTraceHandler().error( + "Website not known: <" + args[i] + ">"); + exitCode = 255; + } + + if (BasicSearchable.getSearchable(searchOn) == null) { + Instance.getTraceHandler().error( + "Website not supported: " + searchOn); + exitCode = 255; + } + } else { + tags.add(args[i]); + } + break; case HELP: exitCode = 255; break; @@ -282,8 +352,8 @@ public class Main { System.err.println("\tVersion " + v); System.err.println("\t-------------"); System.err.println(""); - for (String item : updates.getChanges().get(v)) { - System.err.println("\t- " + item); + for (String it : updates.getChanges().get(v)) { + System.err.println("\t- " + it); } System.err.println(""); } @@ -357,6 +427,73 @@ public class Main { break; } exitCode = read(urlString, chapString, false); + break; + case SEARCH: + if (searchOn == null || search == null) { + exitCode = 255; + break; + } + + if (page == null) { + page = 1; + } + if (item == null) { + item = 0; + } + + if (BasicReader.getReader() == null) { + Instance.getTraceHandler() + .error(new Exception( + "No reader type has been configured")); + exitCode = 10; + break; + } + + try { + BasicReader.getReader().search(searchOn, search, page, item); + } catch (IOException e1) { + Instance.getTraceHandler().error(e1); + } + + break; + case SEARCH_TAG: + if (searchOn == null) { + exitCode = 255; + break; + } + + item = 0; + page = 1; + + try { + page = Integer.parseInt(tags.get(tags.size()-1)); + tags.remove(tags.size() - 1); + + try { + int tmp = Integer.parseInt(tags.get(tags.size()-1)); + tags.remove(tags.size() - 1); + + item = page; + page = tmp; + } catch (Exception e) { + } + } catch (Exception e) { + } + + if (BasicReader.getReader() == null) { + Instance.getTraceHandler() + .error(new Exception( + "No reader type has been configured")); + exitCode = 10; + break; + } + + try { + BasicReader.getReader().searchTag(searchOn, page, item, tags.toArray(new String[]{})); + } catch (IOException e1) { + Instance.getTraceHandler().error(e1); + } + break; case HELP: syntax(true); diff --git a/src/be/nikiroo/fanfix/bundles/resources_core.properties b/src/be/nikiroo/fanfix/bundles/resources_core.properties index 62fd158..6a08973 100644 --- a/src/be/nikiroo/fanfix/bundles/resources_core.properties +++ b/src/be/nikiroo/fanfix/bundles/resources_core.properties @@ -16,6 +16,11 @@ HELP_SYNTAX = Valid options:\n\ \t--read [id] ([chapter number]): read the given story from the library\n\ \t--read-url [URL] ([chapter number]): convert on the fly and read the \n\ \t\tstory, without saving it\n\ +\t--search WEBSITE [free text] ([page] ([item])): search for the given terms, show the\n\ +\t\tgiven page (page 0 means "how many page do we have", starts at page 1)\n\ +\t--search-tag WEBSITE ([tag 1] [tag2...] ([page] ([item]))): list the known tags or \n\ +\t\tsearch the stories for the given tag(s), show the given page of results\n\ +\t\t(page 0 means "how many page do we have", starts at page 1)\n\ \t--list ([type]) : list the stories present in the library\n\ \t--set-source [id] [new source]: change the source of the given story\n\ \t--set-title [id] [new title]: change the title of the given story\n\ diff --git a/src/be/nikiroo/fanfix/bundles/resources_core_fr.properties b/src/be/nikiroo/fanfix/bundles/resources_core_fr.properties index 092bd33..149391e 100644 --- a/src/be/nikiroo/fanfix/bundles/resources_core_fr.properties +++ b/src/be/nikiroo/fanfix/bundles/resources_core_fr.properties @@ -15,6 +15,11 @@ HELP_SYNTAX = Options reconnues :\n\ \t--convert [URL] [output_type] [target] (+info): convertir l'histoire vers le fichier donné, et forcer l'ajout d'un fichier .info si +info est utilisé\n\ \t--read [id] ([chapter number]): afficher l'histoire "id"\n\ \t--read-url [URL] ([chapter number]): convertir l'histoire et la lire à la volée, sans la sauver\n\ +\t--search WEBSITE [texte libre] ([page] ([item])): cherche des histoires, affiche la\n\ +\t\tpage de résultats demandée (la page 0 affiche le nombre de pages, la première page est la page 1)\n\ +\t--search-tag WEBSITE ([tag 1] [tag2...] ([page] ([item]))): list the known tags or \n\ +\t\tcherche des histoires avec le/les tag(s) demandé(s), affiche la page de résultats demandée\n\ +\t\t(la page 0 affiche le nombre de pages, la première page est la page 1)\n\ \t--list ([type]): lister les histoires presentes dans la librairie et leurs IDs\n\ \t--set-source [id] [nouvelle source]: change la source de l'histoire\n\ \t--set-title [id] [nouveau titre]: change le titre de l'histoire\n\ diff --git a/src/be/nikiroo/fanfix/reader/BasicReader.java b/src/be/nikiroo/fanfix/reader/BasicReader.java index c2a650c..c749a59 100644 --- a/src/be/nikiroo/fanfix/reader/BasicReader.java +++ b/src/be/nikiroo/fanfix/reader/BasicReader.java @@ -351,7 +351,13 @@ public abstract class BasicReader implements Reader { } } + /** + * @deprecated use StringUtils when updated + */ + @Deprecated static private String format(long value) { + //TODO: use StringUtils + String display = ""; String suffix = ""; diff --git a/src/be/nikiroo/fanfix/reader/Reader.java b/src/be/nikiroo/fanfix/reader/Reader.java index b001e30..59ed024 100644 --- a/src/be/nikiroo/fanfix/reader/Reader.java +++ b/src/be/nikiroo/fanfix/reader/Reader.java @@ -6,6 +6,7 @@ import java.net.URL; import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.library.BasicLibrary; +import be.nikiroo.fanfix.supported.SupportType; import be.nikiroo.utils.Progress; /** @@ -169,6 +170,58 @@ public interface Reader { */ public void browse(String source); + /** + * Search for the given terms and find stories that correspond if possible. + * + * @param searchOn + * the website to search on + * @param keywords + * the words to search for (cannot be NULL) + * @param page + * the page of results to show (0 = request the maximum number of + * pages, pages start at 1) + * @param item + * the item to select (0 = do not select a specific item but show + * all the page, items start at 1) + * + * @throws IOException + * in case of I/O error + */ + public void search(SupportType searchOn, String keywords, int page, int item) + throws IOException; + + /** + * Search based upon a hierarchy of tags, or search for (sub)tags. + *

    + * We use the tags DisplayName. + *

    + * If no tag is given, the main tags will be shown. + *

    + * If a non-leaf tag is given, the subtags will be shown. + *

    + * If a leaf tag is given (or a full hierarchy ending with a leaf tag), + * stories will be shown. + *

    + * You can select the story you want with the item number. + * + * @param searchOn + * the website to search on + * @param page + * the page of results to show (0 = request the maximum number of + * pages, pages start at 1) + * @param item + * the item to select (0 = do not select a specific item but show + * all the page, items start at 1) + * @param tags + * the tags display names to search for (this is a tag + * hierarchy, NOT a multiple tags choice) + * + * @throws IOException + * in case of I/O error + */ + public void searchTag(SupportType searchOn, int page, int item, + String... tags) throws IOException; + /** * Open the {@link Story} with an external reader (the program should be * passed the main file associated with this {@link Story}). diff --git a/src/be/nikiroo/fanfix/reader/cli/CliReader.java b/src/be/nikiroo/fanfix/reader/cli/CliReader.java index 9ec37a5..89eb3ac 100644 --- a/src/be/nikiroo/fanfix/reader/cli/CliReader.java +++ b/src/be/nikiroo/fanfix/reader/cli/CliReader.java @@ -1,6 +1,7 @@ package be.nikiroo.fanfix.reader.cli; import java.io.IOException; +import java.util.ArrayList; import java.util.List; import be.nikiroo.fanfix.Instance; @@ -10,6 +11,10 @@ import be.nikiroo.fanfix.data.MetaData; import be.nikiroo.fanfix.data.Paragraph; import be.nikiroo.fanfix.data.Story; import be.nikiroo.fanfix.reader.BasicReader; +import be.nikiroo.fanfix.searchable.BasicSearchable; +import be.nikiroo.fanfix.searchable.SearchableTag; +import be.nikiroo.fanfix.supported.SupportType; +import be.nikiroo.utils.StringUtils; /** * Command line {@link Story} reader. @@ -95,4 +100,143 @@ class CliReader extends BasicReader { + author); } } + + @Override + public void search(SupportType searchOn, String keywords, int page, int item) + throws IOException { + + } + + @Override + public void searchTag(SupportType searchOn, int page, int item, + String... tags) throws IOException { + BasicSearchable search = BasicSearchable.getSearchable(searchOn); + List stags = search.getTags(); + + SearchableTag stag = null; + for (String tag : tags) { + stag = null; + for (int i = 0; i < stags.size(); i++) { + if (stags.get(i).getName().equalsIgnoreCase(tag)) { + stag = stags.get(i); + break; + } + } + + if (stag != null) { + search.fillTag(stag); + stags = stag.getChildren(); + } else { + stags = new ArrayList(); + break; + } + } + + if (stag != null) { + if (page <= 0) { + if (stag.isLeaf()) { + search.search(stag, 1); + System.out.println(stag.getPages()); + } else { + System.out.println(stag.getCount()); + } + } else { + List metas = null; + List subtags = null; + int count; + + if (stag.isLeaf()) { + metas = search.search(stag, page); + count = metas.size(); + } else { + subtags = stag.getChildren(); + count = subtags.size(); + } + + if (item > 0) { + if (item <= count) { + if (metas != null) { + MetaData meta = metas.get(item - 1); + System.out.println(page + "/" + item + ": " + + meta.getTitle()); + System.out.println(); + System.out.println(meta.getUrl()); + System.out.println(); + System.out.println("Tags: " + meta.getTags()); + System.out.println(); + for (Paragraph para : meta.getResume()) { + System.out.println(para.getContent()); + System.out.println(""); + } + } else { + SearchableTag subtag = subtags.get(item - 1); + + String sp = ""; + if (subtag.getParent() != null) { + List parents = new ArrayList(); + for (SearchableTag parent = subtag.getParent(); parent != null; parent = parent + .getParent()) { + parents.add(parent.getName()); + } + for (String parent : parents) { + if (!sp.isEmpty()) { + sp += " / "; + } + sp += parent; + } + } + + // TODO: i18n + if (sp.isEmpty()) { + System.out.println(String.format( + "%d/%d: %s, %d %s", page, item, + subtag.getName(), subtag.getCount(), + "stories")); + } else { + System.out.println(String.format( + "%d/%d: %s (%s), %d %s", page, item, + subtag.getName(), sp, + subtag.getCount(), "stories")); + } + } + } else { + System.out.println("Invalid item: only " + count + + " items found"); + } + } else { + if (metas != null) { + int i = 0; + for (MetaData meta : metas) { + System.out + .println((i + 1) + ": " + meta.getTitle()); + i++; + } + } else { + int i = 1; + for (SearchableTag subtag : subtags) { + String total = ""; + if (subtag.getCount() > 0) { + total = StringUtils.formatNumber(subtag + .getCount()); + } + + if (total.isEmpty()) { + System.out.println(String.format("%d: %s", i, + subtag.getName())); + } else { + System.out.println(String.format("%d: %s (%s)", + i, subtag.getName(), total)); + } + + i++; + } + } + } + } + } else { + for (SearchableTag s : stags) { + System.out.println(s.getName()); + } + } + } } diff --git a/src/be/nikiroo/fanfix/reader/tui/TuiReader.java b/src/be/nikiroo/fanfix/reader/tui/TuiReader.java index f94f783..cc39dcd 100644 --- a/src/be/nikiroo/fanfix/reader/tui/TuiReader.java +++ b/src/be/nikiroo/fanfix/reader/tui/TuiReader.java @@ -7,6 +7,7 @@ import jexer.TApplication.BackendType; import be.nikiroo.fanfix.Instance; import be.nikiroo.fanfix.reader.BasicReader; import be.nikiroo.fanfix.reader.Reader; +import be.nikiroo.fanfix.supported.SupportType; /** * This {@link Reader}is based upon the TUI widget library 'jexer' @@ -70,4 +71,16 @@ class TuiReader extends BasicReader { Instance.getTraceHandler().error(e); } } + + @Override + public void search(SupportType searchOn, String keywords, int page, int item) { + // TODO: !!! + throw new java.lang.IllegalStateException("Not implemented yet."); + } + + @Override + public void searchTag(SupportType searchOn, int page, int item, String... tags) { + // TODO: !!! + throw new java.lang.IllegalStateException("Not implemented yet."); + } } diff --git a/src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java b/src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java index f08b84c..177c12a 100644 --- a/src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java +++ b/src/be/nikiroo/fanfix/reader/tui/TuiReaderApplication.java @@ -25,6 +25,7 @@ import be.nikiroo.fanfix.library.BasicLibrary; import be.nikiroo.fanfix.reader.BasicReader; import be.nikiroo.fanfix.reader.Reader; import be.nikiroo.fanfix.reader.tui.TuiReaderMainWindow.Mode; +import be.nikiroo.fanfix.supported.SupportType; import be.nikiroo.utils.Progress; /** @@ -124,6 +125,16 @@ class TuiReaderApplication extends TApplication implements Reader { public void setChapter(int chapter) { reader.setChapter(chapter); } + + @Override + public void search(SupportType searchOn, String keywords, int page, int item) { + reader.search(searchOn, keywords, page,item); + } + + @Override + public void searchTag(SupportType searchOn, int page, int item, String... tags) { + reader.searchTag(searchOn, page, item, tags); + } /** * Open the given {@link Story} for reading. This may or may not start an diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReader.java b/src/be/nikiroo/fanfix/reader/ui/GuiReader.java index f4a932b..2ca6191 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReader.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReader.java @@ -25,6 +25,7 @@ import be.nikiroo.fanfix.library.BasicLibrary; import be.nikiroo.fanfix.library.CacheLibrary; import be.nikiroo.fanfix.reader.BasicReader; import be.nikiroo.fanfix.reader.Reader; +import be.nikiroo.fanfix.supported.SupportType; import be.nikiroo.utils.Progress; import be.nikiroo.utils.Version; import be.nikiroo.utils.ui.UIUtils; @@ -217,6 +218,18 @@ class GuiReader extends BasicReader { super.start(target, program, sync); } } + + @Override + public void search(SupportType searchOn, String keywords, int page, int item) { + // TODO: !!! + throw new java.lang.IllegalStateException("Not implemented yet."); + } + + @Override + public void searchTag(SupportType searchOn, int page, int item, String... tags) { + // TODO: !!! + throw new java.lang.IllegalStateException("Not implemented yet."); + } /** * Delete the {@link Story} from the cache if it is present, but NOT diff --git a/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java b/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java index bbd34ab..7afaacc 100644 --- a/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java +++ b/src/be/nikiroo/fanfix/reader/ui/GuiReaderFrame.java @@ -199,10 +199,7 @@ class GuiReaderFrame extends JFrame implements FrameHelper { } } - // TODO: enable when ready - if (false) { - bar.add(search); - } + bar.add(search); JMenu view = new JMenu(GuiReader.trans(StringIdGui.MENU_VIEW)); view.setMnemonic(KeyEvent.VK_V); diff --git a/src/be/nikiroo/fanfix/searchable/BasicSearchable.java b/src/be/nikiroo/fanfix/searchable/BasicSearchable.java index c639443..ebc5096 100644 --- a/src/be/nikiroo/fanfix/searchable/BasicSearchable.java +++ b/src/be/nikiroo/fanfix/searchable/BasicSearchable.java @@ -83,7 +83,7 @@ public abstract class BasicSearchable { * @throws IOException * in case of I/O error */ - abstract protected void fillTag(SearchableTag tag) throws IOException; + abstract public void fillTag(SearchableTag tag) throws IOException; /** * Search for the given term and return a list of stories satisfying this diff --git a/src/be/nikiroo/fanfix/searchable/Fanfiction.java b/src/be/nikiroo/fanfix/searchable/Fanfiction.java index bcc4759..28c0d1b 100644 --- a/src/be/nikiroo/fanfix/searchable/Fanfiction.java +++ b/src/be/nikiroo/fanfix/searchable/Fanfiction.java @@ -77,7 +77,7 @@ class Fanfiction extends BasicSearchable { if (storiesName != null) { SearchableTag tag = new SearchableTag(null, storiesName, false); for (String id : stories.keySet()) { - tag.add(new SearchableTag(id, stories.get(id), true, false)); + tag.add(new SearchableTag(id, stories.get(id), false, false)); } tags.add(tag); } @@ -94,7 +94,7 @@ class Fanfiction extends BasicSearchable { } @Override - protected void fillTag(SearchableTag tag) throws IOException { + public void fillTag(SearchableTag tag) throws IOException { if (tag.getId() == null || tag.isComplete()) { return; } @@ -126,7 +126,11 @@ class Fanfiction extends BasicSearchable { nr = nr.substring(0, nr.length() - 1); } nr = nr.trim(); - subtag.setCount(toNumber(nr)); + + // TODO: fix toNumber/fromNumber + nr = nr.replaceAll("\\.[0-9]*", ""); + + subtag.setCount(StringUtils.toNumber(nr)); } } } @@ -136,33 +140,6 @@ class Fanfiction extends BasicSearchable { tag.setComplete(true); } - /** - * @deprecated use {@link StringUtils} when updated - */ - @Deprecated - private static long toNumber(String value) { - // TODO: use StringUtils instead after update - long count = 0l; - if (value != null) { - try { - if (value.toLowerCase().endsWith("m")) { - count = Long.parseLong(value.substring(0, - value.length() - 1).trim()); - count *= 1000000; - } else if (value.toLowerCase().endsWith("k")) { - count = Long.parseLong(value.substring(0, - value.length() - 1).trim()); - count *= 1000; - } else { - count = Long.parseLong(value); - } - } catch (NumberFormatException pe) { - } - } - - return count; - } - @Override public List search(String search) throws IOException { String encoded = URLEncoder.encode(search.toLowerCase(), "utf-8"); @@ -190,7 +167,7 @@ class Fanfiction extends BasicSearchable { Document doc = load(url, false); // Update the pages number if needed - if (tag.getPages() < 0) { + if (tag.getPages() < 0 && tag.isLeaf()) { tag.setPages(getPages(doc)); }