/* * [BStore.java] * * Summary: Facts about various online bookstores. * * Copyright: (c) 2011-2017 Roedy Green, Canadian Mind Products, http://mindprod.com * * Licence: This software may be copied and used freely for any purpose but military. * http://mindprod.com/contact/nonmil.html * * Requires: JDK 1.8+ * * Created with: JetBrains IntelliJ IDEA IDE http://www.jetbrains.com/idea/ * * Version History: * 1.0 2011-09-28 initial version */ package com.mindprod.stores; import com.mindprod.aws.jax.AWSECommerceService; import com.mindprod.aws.jax.AWSECommerceServicePortType; import com.mindprod.common18.Misc; import com.mindprod.common18.ST; import com.mindprod.entities.DeEntifyStrings; import com.mindprod.fastcat.FastCat; import com.mindprod.htmlmacros.macro.CountryFlag; import com.mindprod.http.Get; import com.mindprod.isbn.ISBNValidate; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.Arrays; import java.util.EnumSet; import java.util.HashSet; import java.util.Set; import static com.mindprod.stores.BStoreConstants.*; import static com.mindprod.stores.Hint.*; import static com.mindprod.stores.StockStatus.NOTCARRIED; import static java.lang.System.*; /** * Facts about various online bookstores. *
* Amazon, except Amazon.es gets info via AWS SOAP interface. * Others get in by screenscraping. * BooksInStock.fireup triggers the check of bookstores to find out which books are in stock. * To add bookstore: * Book -- display link to store in HTML * BStore -- detail of links and probes, analysis of probe results. * BulkProber -- extra SET parms to control which stores probed. * Stock -- file to track in stock for this store. * e:\mindprod\embellishment\xxxxstock.dat track what stores have in inventory * * @author Roedy Green, Canadian Mind Products * @version 1.0 2011-09-28 initial version * @see Book * @see BulkProber for checkxxxx defininitions * @see Stock#reportUnusedClues * @since 2011-09-28 */ public enum BStore implements OnlineStore { ABEANZ( true /* alive */, "abe books anz", "Abe Books Australia and New Zealand", "https://", false, "www.abebooks.com", false /* pp */, false /* not for ebooks */, CountryFlag.Australia, null /* account */, INTERPROBE_DELAY_NONE, /* dummy probes need no delay */ /* no clues, use abe.com */ 0, null, 0, 24_000 ) { public String buildProductLink( final String isbn13 ) { validateIsbn13( isbn13 ); return buildAbeProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAbeQueryLink( title, author ); } /** * is a given product in stock? * * @param isbn13 product number * @param probe * @return true if the product is in stock. */ public StockStatus getProductStatus( String isbn13, final boolean probe ) { validateIsbn13( isbn13 ); // we don't maintain a separate inventory, we borrow AEBCOM's return ABECOM.getProductStatus( isbn13, true ); } /** * Probe this bookstore to see if it carries the given isbn/product. * * @param isbn13 ISBN of book to check * * @return does bookstore carry this book 0=no 1=yes 2=don't know 3=store is refusing probes. */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); out.println( " dummy probe" ); return ABECOM.getProductStatus( isbn13, true ); } }, ABECA( true, "abe books.ca", "Abe Books Canada", "https://", false, "www.abebooks.com", false, false /* not for ebooks */, CountryFlag.Canada, null, INTERPROBE_DELAY_NONE, /* dummy probes need no delay */ 0, null, /* no clues, use USA status */ 0, 24_000 ) { public String buildProductLink( final String isbn13 ) { return buildAbeProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAbeQueryLink( title, author ); } /** * is a given product in stock? * * @param isbn13 product number * * @param probe * @return status of the product stock */ public StockStatus getProductStatus( String isbn13, final boolean probe ) { // we don't maintain a separate inventory, we borrow AEBCOM's return ABECOM.getProductStatus( isbn13, true ); } /** * Probe this bookstore to see if it carries the given isbn/product. * * @param isbn13 ISBN of book to check * * @return does bookstore carry this book 0=no 1=yes 2=don't know 3=store is refusing probes. */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); out.println( " dummy probe" ); return ABECOM.getProductStatus( isbn13, true ); } }, ABECOM( true, "abe books.com", "Abe Books USA", "https://", false, "www.abebooks.com", false, false /* not for ebooks */, CountryFlag.USA, null, INTERPROBE_DELAY_FOR_ABE, 0, new Clue[] { new Clue( MUST, "\"price\">US$ " ), // >Quantity Available: > 20< >Quantity Available: 10< new Clue( MUST, "regex:>Quantity Available:\\s*(?:>)?\\s*([1-9]|[1-9][0-9])<" ), new Clue( UNLISTED, ">We're sorry, no results were found for the search terms.<" ), }, 0, 24_000 ) { public String buildProductLink( final String isbn13 ) { return buildAbeProductLink( isbn13 ); } public String buildProbeLink( final String isbn13 ) { return buildAbeProbeLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAbeQueryLink( title, author ); } /** Probe abe store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, ABEDE( true, "abe books.de", "Abe Books Germany", "https://", false, "www.abebooks.de", false, false /* not for ebooks */, CountryFlag.Germany, null, INTERPROBE_DELAY_FOR_ABE_DE, 0, new Clue[] { new Clue( IN, "regex:>Anzahl: [1-9][0-9]*<" ), new Clue( MUST, "\"price\">EUR " ), new Clue( UNLISTED, ">Keine Suchergebnisse<" ), new Clue( UNLISTED, "Leider konnten keine Treffer f\u00fcr folgende Suchbegriffe gefunden werden" ), }, 0, 23_000 ) { public String buildProductLink( final String isbn13 ) { return buildAbeProductLink( isbn13 ); } public String buildProbeLink( final String isbn13 ) { return buildAbeProbeLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAbeQueryLink( title, author ); } /** Probe abede store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, ABEES( true, "iberlibro.com", "Iberlibro, Abe Books Spain", "https://", false, "www.iberlibro.com", false, false /* not for ebooks */, CountryFlag.Spain, null, INTERPROBE_DELAY_FOR_ABE, 0, new Clue[] { new Clue( IN, "ISBN 13: EUR " ), }, 0, 23_000 )// zanox rejected us { public String buildProductLink( final String isbn13 ) { return buildAbeProductLink( isbn13 ); } public String buildProbeLink( final String isbn13 ) { return buildAbeProbeLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAbeQueryLink( title, author ); } /** Probe abees store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, ABEFR( true, "abe books.fr", "Abe Books France", "https://", false, "www.abebooks.fr", false, false /* not for ebooks */, CountryFlag.France, null, INTERPROBE_DELAY_FOR_ABE, 0, new Clue[] { new Clue( MUST, "\"price\">EUR " ), new Clue( UNLISTED, "Nous sommes d\u00e9sol\u00e9s ; aucun r\u00e9sultat n'a " + "\u00e9t\u00e9 trouv\u00e9 pour les termes que vous avez saisis" ), }, 0, 23_0000 ) // zanox rejected us { public String buildProductLink( final String isbn13 ) { return buildAbeProductLink( isbn13 ); } public String buildProbeLink( final String isbn13 ) { return buildAbeProbeLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAbeQueryLink( title, author ); } /** Probe abefr store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, ABEIT( true, "abe books.it", "Abe Books Italy", "https://", false, "www.abebooks.it", false, false /* not for ebooks */, CountryFlag.Italy, null, INTERPROBE_DELAY_FOR_ABE, 0, new Clue[] { new Clue( MUST, "\"price\">EUR " ), new Clue( UNLISTED, "Siamo spiacenti, non ci sono risultati per i criteri di ricerca " + "inseriti" ), }, 0, 23_000 ) // zanox rejected us { public String buildProductLink( final String isbn13 ) { return buildAbeProductLink( isbn13 ); } public String buildProbeLink( final String isbn13 ) { return buildAbeProbeLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAbeQueryLink( title, author ); } public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, ABEUK( true, "abe books.co.uk", "Abe Books UK", "https://", false, "www.abebooks.co.uk", false, false /* not for ebooks */, CountryFlag.UK, null, INTERPROBE_DELAY_FOR_ABE, 0, new Clue[] { new Clue( IN, "Book Description:" ), new Clue( MUST, "price\">\u00a3 " ), new Clue( UNLISTED, "no results were found" ), }, 0, 23_000 ) { public String buildProductLink( final String isbn13 ) { return buildAbeProductLink( isbn13 ); } public String buildProbeLink( final String isbn13 ) { return buildAbeProbeLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAbeQueryLink( title, author ); } /** Probe abeuk store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, AMAZONCA( true, "amazon.ca", "Amazon Canada", "https://", true, "www.amazon.ca", false, false /* not for ebooks */, CountryFlag.Canada, CONFIGURATION.getAccountForAmazonCa(), INTERPROBE_DELAY_FOR_AMAZON_CA, 330641, null, 0, 15121 ) { public String buildProductLink( final String isbn13 ) { return buildAmazonProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAmazonQueryLink( title, author ); } public AWSECommerceServicePortType getPort( AWSECommerceService service ) { return service.getAWSECommerceServicePortCA(); } /** * Is product in stock? * @param product isbn or asin * * @param probe * @return true if in stock. */ public StockStatus getProductStatus( final String product, final boolean probe ) { return getAmazonProductStatus( product ); } public StockStatus probe( final String asin ) { validateAsin( asin ); return BulkProber.awsProbe( this, asin ); } }, AMAZONCOM( true, "amazon.com", "Amazon USA", "https://", true, "www.amazon.com", false, false /* not for ebooks */, CountryFlag.USA, CONFIGURATION.getAccountForAmazonCom(), BStoreConstants.INTERPROBE_DELAY_FOR_AMAZON_COM, 9325, null, 0, 1789 ) { public String buildProductLink( final String isbn13 ) { return buildAmazonProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAmazonQueryLink( title, author ); } public AWSECommerceServicePortType getPort( AWSECommerceService service ) { return service.getAWSECommerceServicePort(); } /** * Is product in stock? * @param product isbn or asin * * @param probe * @return true if in stock. */ public StockStatus getProductStatus( final String product, final boolean probe ) { return getAmazonProductStatus( product ); } public StockStatus probe( final String asin ) { validateAsin( asin ); return BulkProber.awsProbe( this, asin ); } }, AMAZONDE( true, "amazon.de", "Amazon Germany", "https://", true, "www.amazon.de", false, false /* not for ebooks */, CountryFlag.Germany, CONFIGURATION.getAccountForAmazonDe(), BStoreConstants.INTERPROBE_DELAY_FOR_AMAZON_GENERIC, 6742, null, 0, 1638 ) { public String buildProductLink( final String isbn13 ) { return buildAmazonProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAmazonQueryLink( title, author ); } public AWSECommerceServicePortType getPort( AWSECommerceService service ) { return service.getAWSECommerceServicePortDE(); } /** * Is product in stock? * @param product isbn or asin * * @param probe * @return true if in stock. */ public StockStatus getProductStatus( final String product, final boolean probe ) { return getAmazonProductStatus( product ); } public StockStatus probe( final String asin ) { validateAsin( asin ); return BulkProber.awsProbe( this, asin ); } }, AMAZONES( true, "amazon.es", "Amazon Spain", "https://", true, "www.amazon.es", false, false /* not for ebooks */, CountryFlag.Spain, CONFIGURATION.getAccountForAmazonEs(), BStoreConstants.INTERPROBE_DELAY_FOR_AMAZON_GENERIC, 6746, null, 0, 1624 ) { public String buildProductLink( final String isbn13 ) { return buildAmazonProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAmazonQueryLink( title, author ); } public AWSECommerceServicePortType getPort( AWSECommerceService service ) { return service.getAWSECommerceServicePortES(); } /** * Is product in stock? * @param product isbn or asin * * @param probe * @return true if in stock. */ public StockStatus getProductStatus( final String product, final boolean probe ) { return getAmazonProductStatus( product ); } public StockStatus probe( final String asin ) { validateAsin( asin ); return BulkProber.awsProbe( this, asin ); } }, AMAZONFR( true, "amazon.fr", "Amazon France", "https://", true, "www.amazon.fr", false, false /* not for ebooks */, CountryFlag.France, CONFIGURATION.getAccountForAmazonFr(), BStoreConstants.INTERPROBE_DELAY_FOR_AMAZON_GENERIC, 6746, null, 0, 1642 ) { public String buildProductLink( final String isbn13 ) { return buildAmazonProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAmazonQueryLink( title, author ); } public AWSECommerceServicePortType getPort( AWSECommerceService service ) { return service.getAWSECommerceServicePortFR(); } /** * Is product in stock? * @param product isbn or asin * * @param probe * @return true if in stock. */ public StockStatus getProductStatus( final String product, final boolean probe ) { return getAmazonProductStatus( product ); } public StockStatus probe( final String asin ) { validateAsin( asin ); return BulkProber.awsProbe( this, asin ); } }, AMAZONIT( true, "amazon.it", "Amazon Italy", "https://", true, "www.amazon.it", false, false /* not for ebooks */, CountryFlag.Italy, CONFIGURATION.getAccountForAmazonIt(), BStoreConstants.INTERPROBE_DELAY_FOR_AMAZON_GENERIC, 23322, null, 0, 3370 ) { public String buildProductLink( final String isbn13 ) { return buildAmazonProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAmazonQueryLink( title, author ); } public AWSECommerceServicePortType getPort( AWSECommerceService service ) { return service.getAWSECommerceServicePortIT(); } /** * Is product in stock? * @param product isbn or asin * * @param probe * @return true if in stock. */ public StockStatus getProductStatus( final String product, final boolean probe ) { return getAmazonProductStatus( product ); } public StockStatus probe( final String asin ) { validateAsin( asin ); return BulkProber.awsProbe( this, asin ); } }, AMAZONUK( true, "amazon.co.uk", "Amazon UK", "https://", true, "www.amazon.co.uk", false, false /* not for ebooks */, CountryFlag.UK, CONFIGURATION.getAccountForAmazonUk(), BStoreConstants.INTERPROBE_DELAY_FOR_AMAZON_GENERIC, 6738, null, 0, 1634 ) { public String buildProductLink( final String isbn13 ) { return buildAmazonProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAmazonQueryLink( title, author ); } public AWSECommerceServicePortType getPort( AWSECommerceService service ) { return service.getAWSECommerceServicePortUK(); } /** * Is product in stock? * @param product isbn or asin * * @param probe * @return true if in stock. */ public StockStatus getProductStatus( final String product, final boolean probe ) { return getAmazonProductStatus( product ); } public StockStatus probe( final String asin ) { validateAsin( asin ); return BulkProber.awsProbe( this, asin ); } }, JUNGLEE( /* amazon india, affiliates for Indians only */ true, "junglee.com", "Junglee (Amazon in India)", "https://", false, "www.junglee.com", false, false /* not for ebooks */, CountryFlag.India, CONFIGURATION.getAccountForJungleeCom(), BStoreConstants.INTERPROBE_DELAY_FOR_JUNGLEE, /* Theoretically supports SOAP AWS, but cannot get it to work */ /* shares stock with other Amazon stores. Stock in ASIN not ISBN */ 0, new Clue[] { new Clue( CHOP_TAIL, "Explore More Items Similar" ), new Clue( IN, "class=\"formatHeader\"" ), new Clue( IN, "class=\"offer-price-lowest-price mrs\"" ), new Clue( IN, "class=\"offerPrice\"" ), new Clue( MUST, "class=\"whole-price\"" ), new Clue( OUT5, "This product is not available from any of our online sellers" ), }, 55_000, 0 ) { public String buildProbeLink( final String asin ) { return buildAmazonManualProbeLink( asin ); } public String buildProductLink( final String isbn13 ) { return buildAmazonProductLink( isbn13 ); } public String buildQueryLink( final String title, final String author ) { return buildAmazonQueryLink( title, author ); } /** not used. Will if India AWS starts working */ public AWSECommerceServicePortType getPort( AWSECommerceService service ) { return service.getAWSECommerceServicePortIN(); } /** * Is product in stock? * @param product isbn or asin * * @param probe * @return true if in stock. Internally stored as asin. */ public StockStatus getProductStatus( final String product, final boolean probe ) { return getAmazonProductStatus( product ); } /** Probe junglee store to see if product in stock, don't use AWS * * @param asin of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String asin ) { validateAsin( asin ); return BulkProber.screenScrapeProbe( this, asin, buildProbeLink( asin ) ); } }, BN( true, "Barnes & Noble", "Barnes & Noble", "https://", true, "www.barnesandnoble.com", true /* paypal */, false, CountryFlag.USA, CONFIGURATION.getAccountForBarnesAndNoble(), BStoreConstants.INTERPROBE_DELAY_FOR_BN, 0, new Clue[] { // partly duplicated in NOOK new Clue( CHOP_LEAD, "" ), new Clue( CHOP_TAIL, "Customers Who Bought This Item Also Bought" ), new Clue( FREEZE, "Yikes! We’re having technical difficulties. Please try again shortly." ), new Clue( IN, ">Item is available through our marketplace sellers and in stores.<" ), new Clue( IN, ">Item is available through our marketplace sellers.<" ), new Clue( IN, "Add to Bag" ), new Clue( IN, "\"outOfStock\":false" ), new Clue( IN, "regex:\\s*" + DOLLAR_PRICE + "" ), new Clue( IN, "regex:[1-9][0-9]? New & Used from\\s*" + DOLLAR_PRICE + "
" ), new Clue( LISTED, "ean=xxx\">Audiobook" ), new Clue( LISTED, "ean=xxx\">Hardcover" ), new Clue( LISTED, "ean=xxx\">Paperback" ), new Clue( LISTED, "regex:itemprop=\"price\">\\s*" + DOLLAR_PRICE + "" ), new Clue( LISTED, "regex:itemscope itemtype=\"http://schema.org/Offer\">\\s*" + DOLLAR_PRICE + "" ), new Clue( OUT5, "Temporarily Out of Stock Online" ), new Clue( UNLISTED2, "0 results" ), // has schema, but not Instock/OutOfStock }, 0, 93_000 ) { /** Barnes & Noble quick probe * * @param isbn13 Isbn * @return URL to probe with */ public String buildProbeLink( final String isbn13 ) { validateIsbn13( isbn13 ); final FastCat sb = new FastCat( 4 ); sb.append( getProtocol(), getHost() ); sb.append( "/w/?ean=", isbn13 ); return sb.toString(); } /** * linksynergy stopped working * @param isbn13 isbn of book * @return link for book macro */ public String buildProductLink( String isbn13 ) { return buildProbeLink( isbn13 ); } /** * query link, for general query for Barnes & Noble * @param title title of book * @param author author of this book * @return HTML for URL to link. */ public String buildQueryLink( final String title, final String author ) { final FastCat sb = new FastCat( 4 ); sb.append( getProtocol(), getHost() ); sb.append( "/s/" ); sb.append( prepareQueryField( title + " by " + author ) ); return sb.toString(); } /** Probe bn store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, CHAPTERS( true, "Chapters Indigo", "Chapters Indigo", "https://", true, "www.chapters.indigo.ca", true, false /* not for ebooks */, CountryFlag.Canada, CONFIGURATION.getAccountForChaptersIndigo(), BStoreConstants.INTERPROBE_DELAY_NONE, 0, new Clue[] { // clues are case-sensitive, unless the regex specifies otherwise. new Clue( IN, "Available for download" ), new Clue( IN, "Ships within 1-2 weeks" ), new Clue( IN, "http://schema.org/LimitedAvailability" ), new Clue( IN2, "In stock online" ), new Clue( IN2, "regex:We found (?:[1-9]|[1-9][0-9]) results? for “[\\d]+”" ), new Clue( OUT2, ">Sold Out<" ), new Clue( OUT2, "Pre-order online" ), new Clue( OUT2, "http://schema.org/OutOfStock" ), new Clue( OUT2, "http://schema.org/PreOrder" ), new Clue( OUT5, "Out of stock online" ), }, 0, 290_000 ) { /** * probe for chapters * * @param isbn13 INbn * @return code for link */ public String buildProbeLink( final String isbn13 ) { validateIsbn13( isbn13 ); final FastCat sb = new FastCat( 5 ); sb.append( getProtocol(), getHost() ); sb.append( "/en-ca/books/search/?keywords=" ); sb.append( isbn13 ); sb.append( "&os=Books&isFallback=true" ); return sb.toString(); } /** build link to product from web page * * @param isbn13 book number * @return URL tc go to that page of Chapters site */ public String buildProductLink( String isbn13 ) { return buildProbeLink( isbn13 ); } /** * query link, for general query for Chapters Indigo * @param title title of book * @param author author of this book * @return HTML for URL to link. */ public String buildQueryLink( final String title, final String author ) { final FastCat sb = new FastCat( 4 ); sb.append( getProtocol(), getHost() ); sb.append( "/en-ca/home/search?SearchBoxKeywords=" ); sb.append( prepareQueryField( title + " by " + author ) ); return sb.toString(); } /** Probe chapters store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, CHAPTERSEBOOKS( true, "Chapters Indigo eBooks", "Chapters Indigo eBooks", "https://", true, "www.chapters.indigo.ca", true /* takes paypal */, true /* for ebooks */, CountryFlag.Canada, CONFIGURATION.getAccountForChaptersIndigo(), BStoreConstants.INTERPROBE_DELAY_NONE, 0, new Clue[] { // clues are case-sensitive, unless the regex specifies otherwise. new Clue( IN, "Available for download" ), new Clue( IN, "Ships within 1-2 weeks" ), new Clue( IN, "http://schema.org/LimitedAvailability" ), new Clue( IN2, "In stock online" ), new Clue( IN2, "regex:We found (?:[1-9]|[1-9][0-9]) results? for “[\\d]+”" ), new Clue( LISTED, "buy eBook" ), new Clue( MUST3, ">Kobo ebook<" ), new Clue( MUST3, ">buy ebook<" ), new Clue( OUT2, ">Sold Out<" ), new Clue( OUT2, "Pre-order online" ), new Clue( OUT2, "http://schema.org/OutOfStock" ), new Clue( OUT2, "http://schema.org/PreOrder" ), new Clue( OUT5, "Out of stock online" ), }, 0, 290_000 ) { /** * probe for chapters * * @param isbn13 INbn * @return code for link */ public String buildProbeLink( final String isbn13 ) { validateIsbn13( isbn13 ); final FastCat sb = new FastCat( 5 ); sb.append( getProtocol(), getHost() ); sb.append( "/en-ca/home/search/?keywords=" ); sb.append( isbn13 ); sb.append( "&os=Books&isFallback=true" ); return sb.toString(); } /** build link to product from web page * * @param isbn13 book number * @return URL tc go to that page of Chapters site */ public String buildProductLink( String isbn13 ) { return buildProbeLink( isbn13 ); } /** * query link, for general query for Chapters Indigo * @param title title of book * @param author author of this book * @return HTML for URL to link. */ public String buildQueryLink( final String title, final String author ) { final FastCat sb = new FastCat( 4 ); sb.append( getProtocol(), getHost() ); sb.append( "en-ca/home/search?SearchBoxKeywords=" ); sb.append( prepareQueryField( title + " by " + author ) ); return sb.toString(); } /** Probe chapters store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, // used to be books.google.com. Now play.google.com // sells ebooks listed by paperback ISBN. GOOGLE( true, "Google play", "Google Play", "https://", false, "play.google.com", false /* no paypal */, true /* for ebooks */, CountryFlag.USA, null /* account */, BStoreConstants.INTERPROBE_DELAY_FOR_GOOGLE, 0, new Clue[] { new Clue( CHOP_LEAD, "" + DOLLAR_PRICE + "" ), new Clue( UNLISTED, "We couldn't find anything for your search" ), }, 0, 1_300 ) { /** * fast probe link to google online book * * @param isbn13 book number * @return url */ public String buildProbeLink( final String isbn13 ) { validateIsbn13( isbn13 ); final FastCat sb = new FastCat( 5 ); sb.append( getProtocol(), getHost() ); sb.append( "/store/search?q=", isbn13 ); sb.append( "&c=books" ); return sb.toString(); } /** * monetised link to Google book * * @param isbn13 book numer * @return url */ public String buildProductLink( final String isbn13 ) { validateIsbn13( isbn13 ); final FastCat sb = new FastCat( 5 ); sb.append( getProtocol(), getHost() ); sb.append( "/store/search?q=", isbn13 ); sb.append( "&c=books" ); return sb.toString(); } /** * query link, for general query for Google play * @param title title of book * @param author author of this book * @return HTML for URL to link. */ public String buildQueryLink( final String title, final String author ) { final FastCat sb = new FastCat( 4 ); // https://play.google.com/store/search?q=The+Secret+Life+of+Plants+by+Peter+Tompkins+and+Christopher+Bird sb.append( getProtocol(), getHost() ); sb.append( "/store/search?q=" ); sb.append( prepareQueryField( title + " by " + author ) ); return sb.toString(); } /** Probe google store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book 0=no 1=yes 2=don't know */ public StockStatus probe( final String isbn13 ) { validateIsbn13( isbn13 ); return BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); } }, KOBO( true, "Kobo", "Kobo (eBooks only)", "https://", true, "store.kobobooks.com", true, true /* for ebooks */, CountryFlag.USA, CONFIGURATION.getAccountForKobo(), BStoreConstants.INTERPROBE_DELAY_FOR_KOBO, 0, new Clue[] { new Clue( IN, "regex:\\s*" + DOLLAR_PRICE + "\\s*
" ), new Clue( IN, "\\s*" + DOLLAR_PRICE + "" ), new Clue( IN, "regex:itemprop=\"price\">\\s*" + DOLLAR_PRICE + "" ), new Clue( IN2, "ean=xxx\">NOOK Book" ), new Clue( UNLISTED2, "0 results" ), }, 0, 93_000 ) { /** nook quick probe * * @param isbn13 Isbn * @return URL to probe with */ public String buildProbeLink( final String isbn13 ) { validateIsbn13( isbn13 ); final FastCat sb = new FastCat( 4 ); sb.append( getProtocol(), getHost() ); sb.append( "/w/?ean=", isbn13 ); return sb.toString(); } /** * linksynergy stopped working * @param isbn13 isbn of book * @return link for book macro */ public String buildProductLink( String isbn13 ) { return buildProbeLink( isbn13 ); } /** * query link, for general query for Barnes & Noble * @param title title of book * @param author author of this book * @return HTML for URL to link. */ public String buildQueryLink( final String title, final String author ) { final FastCat sb = new FastCat( 4 ); sb.append( getProtocol(), getHost() ); sb.append( "/s/" ); sb.append( prepareQueryField( title + " by " + author ) ); return sb.toString(); } /** Probe bn store to see if product in stock * * @param isbn13 of product * * @return does bookstore carry this book */ public StockStatus probe( final String isbn13 ) { // this is an ebook. It can't be outofstock final StockStatus stockStatus = BulkProber.screenScrapeProbe( this, isbn13, buildProbeLink( isbn13 ) ); return ( stockStatus == StockStatus.OUTOFSTOCK ) ? StockStatus.INSTOCK : stockStatus; } }, POWELLS( true, "Powells", "Powells", "http://", false, "www.powells.com", true, false /* not for ebooks */, CountryFlag.USA, CONFIGURATION.getAccountForPowells(), BStoreConstants.INTERPROBE_DELAY_FOR_POWELLS, 0, new Clue[] { new Clue( LISTED, "regex:Ships in [\\p{Digit}]+ to [\\p{Digit}]+ days" ), new Clue( MUST, "regex: