Improve login. Add ignore book feature. Bump version to 1.1.6

This commit is contained in:
openaudible 2018-09-01 02:34:04 -07:00
parent 4d100cec3d
commit 2f89d5d6c6
12 changed files with 207 additions and 85 deletions

View file

@ -59,6 +59,9 @@ public class Audible implements IQueueListener<Book> {
private final HashMap<String, Book> books = new HashMap<>(); // Book.id(), Book
// AudibleRegion region = AudibleRegion.US;
private final HashSet<String> ignoreSet = new HashSet<>(); // book ID's to ignore.
public Audible() {
}
@ -155,19 +158,61 @@ public class Audible implements IQueueListener<Book> {
}
Book removeBook(Book b) {
Book out = null;
Book out;
synchronized (books) {
out = books.remove(b.getProduct_id());
out = books.remove(b.id());
}
assert (out != null);
return out;
}
public void addToIgnoreSet(Collection<Book> books)
{
for (Book b:books) {
removeBook(b);
ignoreSet.add(b.id());
}
saveIgnoreSet();
}
final static String ignoreSetFileName = "ignore.json";
private void loadIgnoreSet() {
try {
Gson gson = new GsonBuilder().create();
File prefsFile = Directories.META.getDir(ignoreSetFileName);
if (prefsFile.exists()) {
String content = HTMLUtil.readFile(prefsFile);
HashSet set = gson.fromJson(content, HashSet.class);
if (set!=null) {
ignoreSet.clear();
ignoreSet.addAll(set);
}
}
} catch (Throwable th) {
LOG.info("Error loadIgnoreSet", th);
}
}
private void saveIgnoreSet() {
if (!ignoreSet.isEmpty()) {
Gson gson = new GsonBuilder().create();
try {
HTMLUtil.writeFile(Directories.META.getDir(ignoreSetFileName), gson.toJson(ignoreSet));
}catch(Throwable th)
{
LOG.error("Error saving ignore list!",th);
}
}
}
protected boolean ignoreBook(Book b) {
String id = b.getProduct_id();
String id = b.id();
if (id.length() == 0)
return true;
return id.contains("FR_PRMO_");
return ignoreSet.contains(b);
}
public boolean ok(Book b) {
@ -218,8 +263,7 @@ public class Audible implements IQueueListener<Book> {
}
updateFileCache();
LookupKey.instance.load(Directories.BASE.getDir(keysFileName));
loadIgnoreSet();
}
public synchronized void save() throws IOException {
@ -807,5 +851,8 @@ public class Audible implements IQueueListener<Book> {
return book;
}
public boolean isIgnoredBook(Book b) {
return ignoreSet.contains(b.id());
}
}

View file

@ -3,9 +3,11 @@ package org.openaudible;
import java.security.InvalidParameterException;
public enum AudibleRegion {
US, UK, DE, FR, AU, IT, JP, CA;
US, UK, DE, FR, AU, /* IT, */ JP, CA;
// audible.de, audible.fr, audible.com.au, audible.it, audible.jp, audible.ca
// italy doesn't seem to have a way to list all books.
// all sites untested except US.
public String getBaseURL() {
return "https://" + this.getBaseDomain();
@ -23,8 +25,7 @@ public enum AudibleRegion {
return "audible.fr";
case AU:
return "audible.com.au";
case IT:
return "audible.it";
// case IT: return "audible.it";
case JP:
return "audible.co.jp";
case CA:

View file

@ -14,7 +14,7 @@ import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.Logger;
// WebClient to connect to audible.
// WebClient to connect to audible.com site
// Cookies are used to track logged in state and can be transfered from external web browser.
public class AudibleClient extends WebClient {
private static final Log LOG = LogFactory.getLog(AudibleClient.class);

View file

@ -120,7 +120,7 @@ public class AudibleScraper {
ConnectionNotifier.getInstance().signout();
try {
setURL("/signout");
setURL("/signout", "Signing out");
} catch(Throwable th)
{
LOG.info("signout error, ignorning...");
@ -319,7 +319,7 @@ public class AudibleScraper {
if (true)
getWebClient().setJavascriptEnabled(true);
try {
setURL(homeURL(), "Loading web page...");
setURL(homeURL());
if (checkLoggedIn())
return;
@ -380,11 +380,17 @@ public class AudibleScraper {
public void lib() throws Exception {
String browserURL = ConnectionNotifier.instance.getLastURL();
if (!browserURL.isEmpty())
if (browserURL.startsWith(getAudibleBase()))
{
// a bit of a hack.. try to log in using library URL in browser.
LOG.info("Using library location from browser: "+browserURL);
if (setURLAndLogIn(browserURL))
return;
try {
if (setURLAndLogIn(browserURL))
return;
} catch (IOException e)
{
LOG.error(e);
}
}
if (!setURLAndLogIn("/lib"))
@ -455,16 +461,23 @@ public class AudibleScraper {
}
public Page setURL(String u) throws FailingHttpStatusCodeException, IOException {
return setURL(u, u);
return setURL(u, "");
}
public Page setURL(String u, String task) throws FailingHttpStatusCodeException, IOException {
LOG.info("setURL:" + u);
getProgress().setSubTask(task);
EventTimer evt = new EventTimer();
if (u.startsWith("/"))
u = getAudibleBase() + u;
if (!task.isEmpty())
{
task += ": ";
}
task+=u;
getProgress().setSubTask(task);
LOG.info("setURL:" + u);
Page p = getWebClient().getPage(u);
if (p instanceof HtmlPage) {
@ -581,19 +594,21 @@ public class AudibleScraper {
ArrayList<Book> list = LibraryParser.instance.parseLibraryFragment(page);
int newBooks = 0;
int partialBooks = 0;
for (Book b : list) {
// LOG.info(b.toString());
if (isPartialBook(b))
{
LOG.error("ignore partial book: " + b);
continue;
}
if (results.contains(b)) {
LOG.error("duplicate book:" + b);
assert (false);
}
if (isPartialBook(b))
{
partialBooks++;
continue;
}
results.add(b);
if (existingBooks != null) {
if (!existingBooks.containsKey(b.getProduct_id()))
@ -602,6 +617,8 @@ public class AudibleScraper {
newBooks++;
}
}
LOG.info("pageNum="+pageNum+" total="+list.size()+" new="+newBooks+" partial="+partialBooks);
if (newBooks == 0) {

View file

@ -74,6 +74,10 @@ public class ConnectionNotifier extends EventNotifier<ConnectionListener> implem
public void setLastURL(String lastURL) {
this.lastURL = lastURL;
if (!lastURL.contains("audible"))
{
LOG.warn("expected audible");
}
LOG.info("Setting lastURL to:"+lastURL);
}

View file

@ -455,6 +455,28 @@ public class AudibleGUI implements BookListener, ConnectionListener {
downloadAAX(getSelected());
}
public void ignoreSelected() {
List<Book> sel = getSelected();
if (!sel.isEmpty())
{
Book first = sel.get(0);
String title = "Are you sure you want to ignore books";
String bodySingular = "Are you sure you want to ignore the following book? \n\t"+ first.getFullTitle()+"\n\nIt will be added to the ignore list and not shown in OpenAudible anymore.";
String bodyPlurel = "You selected "+sel.size()+" books. Are you sure you want to ignore them? \n\nThey will added to the ignore list and not be shown in OpenAudible anymore.";
String body = sel.size()==1 ? bodySingular:bodyPlurel;
boolean yn = MessageBoxFactory.showGeneralYesNo(getShell(), title,body);
if (yn)
{
audible.addToIgnoreSet(sel);
BookNotifier.getInstance().booksUpdated(); // redraw all.
}
}
}
public void convertSelected() {
convertMP3(getSelected());
}
@ -618,7 +640,7 @@ public class AudibleGUI implements BookListener, ConnectionListener {
ArrayList<Book> list = new ArrayList<>();
list.addAll(audible.getBooks());
Collections.sort(list);
// Collections.sort(list);
// sort by purchase date.
list.sort((b1, b2) -> -1 * b1.getPurchaseDate().compareTo(b2.getPurchaseDate()));
@ -736,18 +758,30 @@ public class AudibleGUI implements BookListener, ConnectionListener {
}
private boolean displayBook(Book b) {
if (audible.isIgnoredBook(b))
return false;
if (textFilter.isEmpty()) return true; // don't skip any books if no filter.
String text = textFilter.toLowerCase();
BookElement elems[] = {BookElement.fullTitle, BookElement.author, BookElement.narratedBy, BookElement.shortTitle};
for (BookElement e : elems) {
if (b.has(e) && b.get(e).toLowerCase().contains(text))
return true;
}
return false;
}
// if search text is filled, return books that match.
// otherwise, return all books (default)
public List<Book> getDisplayedBooks() {
ArrayList<Book> displayed = new ArrayList<>();
if (textFilter.isEmpty())
displayed.addAll(Audible.instance.getBooks());
else {
for (Book b : Audible.instance.getBooks()) {
if (bookContainsText(b, textFilter))
displayed.add(b);
}
for (Book b : Audible.instance.getBooks())
{
if (displayBook(b))
displayed.add(b);
}
return displayed;
}
@ -757,16 +791,6 @@ public class AudibleGUI implements BookListener, ConnectionListener {
bookNotifier.booksUpdated();
}
private boolean bookContainsText(Book b, String text) {
text = text.toLowerCase();
BookElement elems[] = {BookElement.fullTitle, BookElement.author, BookElement.narratedBy, BookElement.shortTitle};
for (BookElement e : elems) {
if (b.has(e) && b.get(e).toLowerCase().contains(text))
return true;
}
return false;
}
public void parseAAX() {
ProgressTask task = new ProgressTask("Parse AAX File") {
@ -1225,10 +1249,11 @@ public class AudibleGUI implements BookListener, ConnectionListener {
@Override
public void loginFailed(String url, String html) {
SWTAsync.slow(new SWTAsync("Login problem...") {
SWTAsync.slow(new SWTAsync("Need to open browser...") {
public void task() {
String message = "There was a problem logging in... Try to view the page in the OpenAudible Browser?";
boolean ok = MessageBoxFactory.showGeneralYesNo(null, "Trouble logging in", message);
String message = "Unable to automatically log in... \n\nPlease use the OpenAudible web browser to log onto your audible account and navigate to your library (list of books) and try to connect again." +
"\n\nOpen OpenAudible Browser Now?";
boolean ok = MessageBoxFactory.showGeneralYesNo(null, "Log in to your audible account", message);
if (ok)
browse(url);
}

View file

@ -3,7 +3,7 @@ package org.openaudible.desktop.swt.manager;
public interface Version {
String appName = "OpenAudible";
String appVersion = "1.1.5";
String appVersion = "1.1.6";
boolean appDebug = false;
String appLink = "http://openaudible.org";
String versionLink = "http://openaudible.org/swt_version.json";

View file

@ -31,7 +31,7 @@ public class AppMenu implements ITranslatable, SelectionListener {
private Menu aboutMenu;
private final Command[] actionCommands = {Command.ViewInAudible, Command.Show_MP3, Command.Play, Command.Download,
Command.Convert, Command.Refresh_Book_Info};
Command.Convert, Command.Refresh_Book_Info, Command.Ignore_Book};
private final Command[] controlCommands = {Command.Connect, Command.Quick_Refresh, Command.Rescan_Library, Command.Download_All, Command.Convert_All,
Command.MenuSeparator, Command.Browser, Command.Logout}; // , Command.MenuSeparator, Command.Logout};

View file

@ -31,7 +31,8 @@ public enum Command {
AppWebPage,
Logout,
Test1,
MenuSeparator;
MenuSeparator,
Ignore_Book;
public char getKeyEquiv() {

View file

@ -262,6 +262,9 @@ public class CommandCenter {
case Logout:
AudibleGUI.instance.logout();
break;
case Ignore_Book:
AudibleGUI.instance.ignoreSelected();
break;
default:
logger.info("Unknown cmd: " + c);
@ -314,7 +317,6 @@ public class CommandCenter {
return AudibleGUI.instance.canConvertAll();
case Logout:
return ConnectionNotifier.getInstance().isConnected();
case Preferences:
case Quit:
case About:
@ -327,6 +329,7 @@ public class CommandCenter {
case Cut:
case Paste:
return false;
case Ignore_Book:
case Refresh_Book_Info:
return AudibleGUI.instance.getSelected().size() > 0;
case Rescan_Library:

View file

@ -22,6 +22,7 @@ import org.openaudible.util.Platform;
import java.io.File;
import java.net.URI;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
@ -33,7 +34,6 @@ public class AudibleBrowser {
boolean title = false;
Composite parent;
Text locationBar;
private Browser browser;
ToolBar toolbar;
Canvas canvas;
ToolItem itemBack, itemForward;
@ -42,6 +42,7 @@ public class AudibleBrowser {
SWTError error = null;
Collection<Cookie> cookies;
String customHeader[];
private Browser browser;
public AudibleBrowser(Composite parent, String url) {
this.parent = parent;
@ -71,23 +72,6 @@ public class AudibleBrowser {
}
// Silence Windows SWT.browser widget from making awful clicks.
// For windows 32 and 64 bit SWT applications.
// Uses reflection to call OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, true);
// Without importing platform specific
// #import org.eclipse.swt.internal.win32.OS
private void silenceWindowsExplorer() {
try {
Class<?> c = Class.forName("org.eclipse.swt.internal.win32.OS");
java.lang.reflect.Method method = c.getDeclaredMethod("CoInternetSetFeatureEnabled", Integer.TYPE, Integer.TYPE, Boolean.TYPE);
method.invoke(null, new Object[]{21, 2, true});
} catch (Throwable th) {
// Might fail.. but probably will never do harm.
th.printStackTrace();
}
}
/**
* Gets a string from the resource bundle. We don't want to crash because of a missing String. Returns the key if not found.
*/
@ -139,16 +123,40 @@ public class AudibleBrowser {
display.dispose();
}
public static void showHelp(Display display) {
File dir = Directories.getHelpDirectory();
File index = new File(dir, "index.html");
if (index.exists()) {
URI uri = index.toURI();
String u = uri.toString();
newBrowserWindow(display, u);
} else {
MessageBoxFactory.showError(null, "Unable to open help. Expected at:" + index.getAbsolutePath());
}
}
// Silence Windows SWT.browser widget from making awful clicks.
// For windows 32 and 64 bit SWT applications.
// Uses reflection to call OS.CoInternetSetFeatureEnabled(OS.FEATURE_DISABLE_NAVIGATION_SOUNDS, OS.SET_FEATURE_ON_PROCESS, true);
// Without importing platform specific
// #import org.eclipse.swt.internal.win32.OS
private void silenceWindowsExplorer() {
try {
Class<?> c = Class.forName("org.eclipse.swt.internal.win32.OS");
java.lang.reflect.Method method = c.getDeclaredMethod("CoInternetSetFeatureEnabled", Integer.TYPE, Integer.TYPE, Boolean.TYPE);
method.invoke(null, new Object[]{21, 2, true});
} catch (Throwable th) {
// Might fail.. but probably will never do harm.
th.printStackTrace();
}
}
/**
* Disposes of all resources associated with a particular instance of the BrowserApplication.
*/
public void dispose() {
freeResources();
}
public SWTError getError() {
return error;
}
/*
public Browser getBrowser() {
@ -156,6 +164,10 @@ public class AudibleBrowser {
}
*/
public SWTError getError() {
return error;
}
public void setShellDecoration(Image icon, boolean title) {
this.icon = icon;
this.title = title;
@ -259,7 +271,25 @@ public class AudibleBrowser {
@Override
public void changed(LocationEvent locationEvent) {
ConnectionNotifier.instance.setLastURL(locationEvent.location);
try {
String u = locationEvent.location;
if (u.startsWith("http")) {
URL url = new URL(u);
String h = url.getHost();
if (h.contains("audible")) {
ConnectionNotifier.instance.setLastURL(locationEvent.location);
} else
{
System.err.println("ignore non-audible..."+h);
}
}
} catch (Throwable th) {
th.printStackTrace();
}
}
};
@ -437,18 +467,6 @@ public class AudibleBrowser {
return browser == null || browser.isDisposed();
}
public static void showHelp(Display display) {
File dir = Directories.getHelpDirectory();
File index = new File(dir, "index.html");
if (index.exists()) {
URI uri = index.toURI();
String u = uri.toString();
newBrowserWindow(display, u);
} else {
MessageBoxFactory.showError(null, "Unable to open help. Expected at:" + index.getAbsolutePath());
}
}
public void close() {
if (!isDisposed()) browser.close();
browser = null;

View file

@ -122,7 +122,13 @@ public class WebPage {
String mp3Name = fileName + ".mp3";
File mp3File = new File(mp3Dir, mp3Name);
progress.setTask("Copying book " + count + " of " + toCopy.size() + " to " + mp3File.getAbsolutePath());
CopyWithProgress.copyWithProgress(progress, mp3, mp3File);
try {
CopyWithProgress.copyWithProgress(progress, mp3, mp3File);
} catch(Throwable th)
{
LOG.error("error copying mp3:"+mp3.getAbsolutePath()+" to "+mp3File.getAbsolutePath()+" for book "+b);
}
count++;
}
}