mirror of
https://github.com/mitb-archive/filebot
synced 2024-12-23 16:28:51 -05:00
* properly lock history.xml file for reading and writing
* revise donation reminders
This commit is contained in:
parent
3d6578cbaf
commit
1cbdc1f32b
@ -6,6 +6,8 @@ import static java.util.Collections.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
@ -204,23 +206,21 @@ public class History {
|
||||
}
|
||||
|
||||
|
||||
public static void exportHistory(History history, File file) throws IOException {
|
||||
public static void exportHistory(History history, OutputStream output) throws IOException {
|
||||
try {
|
||||
Marshaller marshaller = JAXBContext.newInstance(History.class).createMarshaller();
|
||||
marshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
|
||||
|
||||
marshaller.marshal(history, file);
|
||||
marshaller.marshal(history, output);
|
||||
} catch (JAXBException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public static History importHistory(File file) throws IOException {
|
||||
public static History importHistory(InputStream stream) throws IOException {
|
||||
try {
|
||||
Unmarshaller unmarshaller = JAXBContext.newInstance(History.class).createUnmarshaller();
|
||||
|
||||
return ((History) unmarshaller.unmarshal(file));
|
||||
return ((History) unmarshaller.unmarshal(stream));
|
||||
} catch (JAXBException e) {
|
||||
throw new IOException(e);
|
||||
}
|
||||
|
@ -2,10 +2,13 @@
|
||||
package net.sourceforge.filebot;
|
||||
|
||||
|
||||
import static net.sourceforge.filebot.History.*;
|
||||
import static net.sourceforge.filebot.Settings.*;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.nio.channels.FileLock;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map.Entry;
|
||||
@ -13,6 +16,8 @@ import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import net.sourceforge.filebot.History.Element;
|
||||
import net.sourceforge.tuned.ByteBufferInputStream;
|
||||
import net.sourceforge.tuned.ByteBufferOutputStream;
|
||||
|
||||
|
||||
public final class HistorySpooler {
|
||||
@ -24,31 +29,72 @@ public final class HistorySpooler {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private HistoryStorage persistentHistory = null;
|
||||
private File persistentHistoryFile = new File(getApplicationFolder(), "history.xml");
|
||||
private int persistentHistoryTotalSize = -1;
|
||||
|
||||
private History sessionHistory = new History();
|
||||
|
||||
|
||||
public synchronized History getCompleteHistory() {
|
||||
History history = new History();
|
||||
|
||||
// add persistent history
|
||||
try {
|
||||
if (getPersistentHistory() != null) {
|
||||
history.addAll(getPersistentHistory().read().sequences());
|
||||
}
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Failed to load history", e);
|
||||
public synchronized History getCompleteHistory() throws IOException {
|
||||
if (persistentHistoryFile.length() <= 0) {
|
||||
return new History();
|
||||
}
|
||||
|
||||
// add session history
|
||||
history.addAll(sessionHistory.sequences());
|
||||
|
||||
return history;
|
||||
RandomAccessFile f = new RandomAccessFile(persistentHistoryFile, "rw");
|
||||
FileChannel channel = f.getChannel();
|
||||
FileLock lock = channel.lock();
|
||||
try {
|
||||
ByteBufferOutputStream data = new ByteBufferOutputStream(f.length());
|
||||
data.transferFully(channel);
|
||||
|
||||
History history = History.importHistory(new ByteBufferInputStream(data.getByteBuffer()));
|
||||
history.addAll(sessionHistory.sequences());
|
||||
return history;
|
||||
} finally {
|
||||
lock.release();
|
||||
channel.close();
|
||||
f.close();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public History getSessionHistory() {
|
||||
return sessionHistory;
|
||||
public synchronized void commit() {
|
||||
if (sessionHistory.sequences().isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
if (persistentHistoryFile.length() <= 0) {
|
||||
persistentHistoryFile.createNewFile();
|
||||
}
|
||||
RandomAccessFile f = new RandomAccessFile(persistentHistoryFile, "rw");
|
||||
FileChannel channel = f.getChannel();
|
||||
FileLock lock = channel.lock();
|
||||
try {
|
||||
ByteBufferOutputStream data = new ByteBufferOutputStream(f.length());
|
||||
int read = data.transferFully(channel);
|
||||
|
||||
History history = read > 0 ? History.importHistory(new ByteBufferInputStream(data.getByteBuffer())) : new History();
|
||||
history.addAll(sessionHistory.sequences());
|
||||
|
||||
data.rewind();
|
||||
History.exportHistory(history, data);
|
||||
|
||||
channel.position(0);
|
||||
channel.write(data.getByteBuffer());
|
||||
|
||||
sessionHistory.clear();
|
||||
persistentHistoryTotalSize = history.totalSize();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
} finally {
|
||||
lock.release();
|
||||
channel.close();
|
||||
f.close();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Failed to write rename history.", e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -66,71 +112,13 @@ public final class HistorySpooler {
|
||||
}
|
||||
|
||||
|
||||
public synchronized void commit(History history) {
|
||||
try {
|
||||
if (getPersistentHistory() != null) {
|
||||
getPersistentHistory().write(history);
|
||||
}
|
||||
|
||||
// clear session history
|
||||
sessionHistory.clear();
|
||||
} catch (IOException e) {
|
||||
Logger.getLogger(getClass().getName()).log(Level.SEVERE, "Failed to store history", e);
|
||||
}
|
||||
public History getSessionHistory() {
|
||||
return sessionHistory;
|
||||
}
|
||||
|
||||
|
||||
public synchronized void commit() {
|
||||
// check if session history is not empty
|
||||
if (sessionHistory.sequences().size() > 0) {
|
||||
commit(getCompleteHistory());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public synchronized void setPersistentHistory(HistoryStorage persistentHistory) {
|
||||
this.persistentHistory = persistentHistory;
|
||||
}
|
||||
|
||||
|
||||
public synchronized HistoryStorage getPersistentHistory() {
|
||||
return persistentHistory;
|
||||
}
|
||||
|
||||
|
||||
public static interface HistoryStorage {
|
||||
|
||||
History read() throws IOException;
|
||||
|
||||
|
||||
void write(History history) throws IOException;
|
||||
}
|
||||
|
||||
|
||||
public static class HistoryFileStorage implements HistoryStorage {
|
||||
|
||||
private final File file;
|
||||
|
||||
|
||||
public HistoryFileStorage(File file) {
|
||||
this.file = file;
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public History read() throws IOException {
|
||||
if (file.exists()) {
|
||||
return importHistory(file);
|
||||
} else {
|
||||
return new History();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void write(History history) throws IOException {
|
||||
exportHistory(history, file);
|
||||
}
|
||||
public int getPersistentHistoryTotalSize() {
|
||||
return persistentHistoryTotalSize;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -32,7 +32,6 @@ import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.Random;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
@ -49,8 +48,6 @@ import javax.xml.parsers.DocumentBuilderFactory;
|
||||
|
||||
import net.miginfocom.swing.MigLayout;
|
||||
import net.sf.ehcache.CacheManager;
|
||||
import net.sourceforge.filebot.HistorySpooler.HistoryFileStorage;
|
||||
import net.sourceforge.filebot.HistorySpooler.HistoryStorage;
|
||||
import net.sourceforge.filebot.cli.ArgumentBean;
|
||||
import net.sourceforge.filebot.cli.ArgumentProcessor;
|
||||
import net.sourceforge.filebot.cli.CmdlineOperations;
|
||||
@ -98,7 +95,6 @@ public class Main {
|
||||
// initialize this stuff before anything else
|
||||
initializeCache();
|
||||
initializeSecurityManager();
|
||||
HistorySpooler.getInstance().setPersistentHistory(new HistoryFileStorage(new File(getApplicationFolder(), "history.xml")));
|
||||
|
||||
if (args.clearUserData()) {
|
||||
System.out.println("Reset preferences");
|
||||
@ -207,48 +203,6 @@ public class Main {
|
||||
Logger.getLogger(Main.class.getName()).log(Level.WARNING, "Failed to check for updates", e);
|
||||
}
|
||||
}
|
||||
|
||||
// hook donation reminder into rename history
|
||||
if (useDonationReminder()) {
|
||||
final HistoryStorage fileStorage = HistorySpooler.getInstance().getPersistentHistory();
|
||||
HistorySpooler.getInstance().setPersistentHistory(new HistoryStorage() {
|
||||
|
||||
@Override
|
||||
public void write(History history) throws IOException {
|
||||
// store history
|
||||
fileStorage.write(history);
|
||||
|
||||
// display donation reminder
|
||||
try {
|
||||
PreferencesEntry<String> persistentDonateLv = Settings.forPackage(Main.class).entry("donate.lv").defaultValue("0");
|
||||
int donateLv = Integer.parseInt(persistentDonateLv.getValue());
|
||||
int donateStep = 1000;
|
||||
int usage = history.totalSize();
|
||||
|
||||
if (usage / donateStep > donateLv || new Random().nextInt(100) == 42) {
|
||||
persistentDonateLv.setValue(String.valueOf(Math.max(donateLv + 1, usage / donateStep)));
|
||||
|
||||
String message = String.format(Locale.ROOT, "<html><p style='font-size:16pt; font-weight:bold'>Thank you for using FileBot!</p><br><p>It has taken many nights to develop this application. If you enjoy using it,<br>please consider a donation to the author of this software. It will help to<br>make FileBot even better!<p><p style='font-size:14pt; font-weight:bold'>You've renamed %,d files.</p><br><html>", history.totalSize());
|
||||
String[] actions = new String[] { "Donate!", "Later" };
|
||||
JOptionPane pane = new JOptionPane(message, INFORMATION_MESSAGE, YES_NO_OPTION, ResourceManager.getIcon("message.donate"), actions, actions[0]);
|
||||
pane.createDialog(null, "Please Donate").setVisible(true);
|
||||
if (pane.getValue() == actions[0]) {
|
||||
Desktop.getDesktop().browse(URI.create(getApplicationProperty("donate.url")));
|
||||
}
|
||||
Analytics.trackEvent("GUI", "Donate", "Lv " + donateLv, pane.getValue() == actions[0] ? 1 : 0);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Main.class.getName()).log(Level.WARNING, e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public History read() throws IOException {
|
||||
return fileStorage.read();
|
||||
}
|
||||
});
|
||||
}
|
||||
} catch (CmdLineException e) {
|
||||
// illegal arguments => just print CLI error message and stop
|
||||
System.err.println(e.getMessage());
|
||||
@ -276,6 +230,11 @@ public class Main {
|
||||
public void windowClosing(WindowEvent e) {
|
||||
e.getWindow().setVisible(false);
|
||||
HistorySpooler.getInstance().commit();
|
||||
|
||||
if (useDonationReminder()) {
|
||||
showDonationReminder();
|
||||
}
|
||||
|
||||
System.exit(0);
|
||||
}
|
||||
});
|
||||
@ -371,6 +330,38 @@ public class Main {
|
||||
}
|
||||
|
||||
|
||||
private static void showDonationReminder() {
|
||||
int renameCount = HistorySpooler.getInstance().getPersistentHistoryTotalSize();
|
||||
if (renameCount <= 0)
|
||||
return;
|
||||
|
||||
PreferencesEntry<String> donation = Settings.forPackage(Main.class).entry("donation").defaultValue("0");
|
||||
int donationRev = Integer.parseInt(donation.getValue());
|
||||
int currentRev = getApplicationRevisionNumber();
|
||||
if (donationRev >= currentRev) {
|
||||
return;
|
||||
}
|
||||
|
||||
String message = String.format(Locale.ROOT, "<html><p style='font-size:16pt; font-weight:bold'>Thank you for using FileBot!</p><br><p>It has taken many nights to develop this application. If you enjoy using it,<br>please consider a donation to the author of this software. It will help to<br>make FileBot even better!<p><p style='font-size:14pt; font-weight:bold'>You've renamed %,d files.</p><br><html>", renameCount);
|
||||
String[] actions = new String[] { "Donate!", "Later" };
|
||||
JOptionPane pane = new JOptionPane(message, INFORMATION_MESSAGE, YES_NO_OPTION, ResourceManager.getIcon("message.donate"), actions, actions[0]);
|
||||
pane.createDialog(null, "Please Donate").setVisible(true);
|
||||
if (pane.getValue() == actions[0]) {
|
||||
try {
|
||||
Desktop.getDesktop().browse(URI.create(getApplicationProperty("donate.url")));
|
||||
donation.setValue(String.valueOf(currentRev));
|
||||
} catch (Exception e) {
|
||||
Logger.getLogger(Main.class.getName()).log(Level.SEVERE, "Failed to open URL.", e);
|
||||
}
|
||||
} else {
|
||||
if (donationRev > 0 && donationRev < currentRev) {
|
||||
donation.setValue(String.valueOf(currentRev));
|
||||
}
|
||||
}
|
||||
Analytics.trackEvent("GUI", "Donate", "r" + currentRev, pane.getValue() == actions[0] ? 1 : 0);
|
||||
}
|
||||
|
||||
|
||||
private static void warmupCachedResources() {
|
||||
Thread warmup = new Thread("warmupCachedResources") {
|
||||
|
||||
|
@ -17,6 +17,8 @@ import java.awt.event.MouseAdapter;
|
||||
import java.awt.event.MouseEvent;
|
||||
import java.awt.event.MouseListener;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.DateFormat;
|
||||
import java.util.ArrayList;
|
||||
@ -635,7 +637,7 @@ class HistoryDialog extends JDialog {
|
||||
|
||||
try {
|
||||
for (File file : files) {
|
||||
history.merge(History.importHistory(file));
|
||||
history.merge(History.importHistory(new FileInputStream(file)));
|
||||
}
|
||||
} finally {
|
||||
// update view
|
||||
@ -661,7 +663,7 @@ class HistoryDialog extends JDialog {
|
||||
|
||||
@Override
|
||||
public void export(File file) throws IOException {
|
||||
History.exportHistory(getModel(), file);
|
||||
History.exportHistory(getModel(), new FileOutputStream(file));
|
||||
}
|
||||
|
||||
|
||||
|
@ -407,19 +407,18 @@ public class RenamePanel extends JComponent {
|
||||
protected final Action openHistoryAction = new AbstractAction("Open History", ResourceManager.getIcon("action.report")) {
|
||||
|
||||
@Override
|
||||
public void actionPerformed(ActionEvent e) {
|
||||
History model = HistorySpooler.getInstance().getCompleteHistory();
|
||||
|
||||
HistoryDialog dialog = new HistoryDialog(getWindow(RenamePanel.this));
|
||||
dialog.setLocationRelativeTo(RenamePanel.this);
|
||||
dialog.setModel(model);
|
||||
|
||||
// show and block
|
||||
dialog.setVisible(true);
|
||||
|
||||
if (!model.equals(dialog.getModel())) {
|
||||
// model was changed, switch to the new model
|
||||
HistorySpooler.getInstance().commit(dialog.getModel());
|
||||
public void actionPerformed(ActionEvent evt) {
|
||||
try {
|
||||
History model = HistorySpooler.getInstance().getCompleteHistory();
|
||||
|
||||
HistoryDialog dialog = new HistoryDialog(getWindow(RenamePanel.this));
|
||||
dialog.setLocationRelativeTo(RenamePanel.this);
|
||||
dialog.setModel(model);
|
||||
|
||||
// show and block
|
||||
dialog.setVisible(true);
|
||||
} catch (Exception e) {
|
||||
UILogger.log(Level.WARNING, String.format("%s: %s", getRootCause(e).getClass().getSimpleName(), getRootCauseMessage(e)), e);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user