/*******************************************************************************
* HelloNzb -- The Binary Usenet Tool
* Copyright (C) 2010-2013 Matthias F. Brandstetter
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package me.mabra.hellonzb;
import me.mabra.hellonzb.listener.MyWindowListener;
import me.mabra.hellonzb.listener.actions.NzbFileListPopupMoveRowAction;
import me.mabra.hellonzb.listener.actions.QuitAction;
import me.mabra.hellonzb.nntpclient.BackupFileDownloader;
import me.mabra.hellonzb.nntpclient.NntpFileDownloader;
import me.mabra.hellonzb.nntpclient.nioengine.NettyNioClient;
import me.mabra.hellonzb.parser.DownloadFile;
import me.mabra.hellonzb.parser.NzbParser;
import me.mabra.hellonzb.parser.SegmentQueue;
import me.mabra.hellonzb.preferences.HelloNzbPreferences;
import me.mabra.hellonzb.statistics.HelloNzbUsageStats;
import me.mabra.hellonzb.unrar.CompleteRarExtractor;
import me.mabra.hellonzb.util.*;
import javax.swing.*;
import java.awt.*;
import java.awt.TrayIcon.MessageType;
import java.awt.dnd.DropTarget;
import java.io.File;
import java.io.IOException;
import java.net.InetAddress;
import java.net.ServerSocket;
import java.net.UnknownHostException;
import java.util.ArrayList;
import java.util.List;
/**
* This is the main class of the JavaNzb application.
*
* @author Matthias F. Brandstetter
*/
public class HelloNzb extends HelloNzbCradle
{
/** Default window title string */
private String winTitle;
/** vars for one instance check */
private static ServerSocket oneInstanceSocket;
/** set to true if the user has stopped download before it was finished */
private boolean downloadStopped;
/**
* This is the main method of the HelloNzb application.
*
* @param args String array of command line arguments
*/
public static void main(String [] args)
{
try
{
final String nzbFileToLoad = HelloNzbToolkit.parseCmdLineArgs(args);
// only one instance of the application is allowed to run simultaneously
if(ONLY_ONE_INSTANCE)
{
// try to open the socket
try
{
oneInstanceSocket = new ServerSocket(SERVER_SOCKET, 0, InetAddress.getByAddress(new byte[] {127,0,0,1}));
}
catch(IOException e)
{
if(nzbFileToLoad != null)
HelloNzbToolkit.writeToMappedBuffer("NZB " + nzbFileToLoad);
else
HelloNzbToolkit.writeToMappedBuffer("SHOW");
System.exit(0);
}
catch(Exception e)
{
e.printStackTrace();
System.exit(1);
}
// create runtime shutdown hook
ShutdownHook shutdownHook = new ShutdownHook();
Runtime.getRuntime().addShutdownHook(shutdownHook);
}
// init GUI look&feel
HelloNzbToolkit.initializeLookAndFeel();
// start application
EventQueue.invokeLater(new Runnable()
{
public void run()
{
JFrame frame = new JFrame();
new HelloNzb(frame, "HelloNzb - The Binary Usenet Tool", nzbFileToLoad);
}
});
}
catch(Throwable t)
{
t.printStackTrace();
System.exit(1);
}
}
/**
* This is the constructor of the class.
*
* @param frame The JFrame object to use as window
* @param title The string used as window title
*/
public HelloNzb(JFrame frame, String title, String nzbFileToLoad)
{
super(frame, title);
winTitle = title + " - v" + VERSION;
jframe.setTitle(winTitle);
HelloNzbToolkit.setMainApp(this);
// create new object of the preferences class
prefContainer = new HelloNzbPreferences(this);
// HelloNzb usage statistics
HelloNzbUsageStats.main = this;
activateUsageStats();
HelloNzbUsageStats.registerStartup(VERSION, localer.getLocale().getDisplayLanguage());
// set console output mode
if(DEBUG || prefContainer.getBooleanPrefValue("ExtendedSettingsConsoleOutput"))
logger.setPrintLevel(MyLogger.SEV_DEBUG);
else
logger.setPrintLevel(MyLogger.SEV_INFO);
// restore window size
String savedWinWidth = this.prefContainer.getPrefValue("AutoSettingsWindowWidth");
String savedWinHeight = this.prefContainer.getPrefValue("AutoSettingsWindowHeight");
if(!savedWinWidth.isEmpty() && !savedWinHeight.isEmpty())
{
jframe.setSize(Integer.parseInt(savedWinWidth), Integer.parseInt(savedWinHeight));
jframe.setLocationRelativeTo(null);
}
// create the background task manager
taskMgr = new TaskManager(this);
JProgressBar progBar = taskMgr.getProgressBar();
taskMgr.start();
// create a shared memory mapping so that a second instance of this
// program can later on pass new nzb files to this first instance here;
// start background worker execution
startBgWorker();
// add content panes to the main window
QuitAction quitAction = addContentPanes(progBar);
// check for new program version
if(!BETA_VERSION)
checkProgramUpdate();
// initialise system tray icon
initSystemTray(quitAction);
// add window listener
MyWindowListener wListener = new MyWindowListener(this);
jframe.addWindowListener(wListener);
wListener.setQuitAction(quitAction);
// drag'n'drop support
DropTarget target = new DropTarget(this.jframe, new MyDropTargetAdapter(this));
this.jframe.setDropTarget(target);
// repaint window
this.jframe.setVisible(true);
this.jframe.validate();
this.jframe.repaint();
// update speed graph panel
this.speedGraph.setSize(toolBar.getHeight());
this.jframe.validate();
this.jframe.repaint();
// load nzb file from command line
loadFileFromCmdLine(nzbFileToLoad);
// load last session (NZB files that were open)
loadLastSession();
// preferences set?
String hostname = prefContainer.getPrefValue("ServerSettingsHost");
if(hostname.isEmpty())
showPreferences();
else
saveOpenParserData(false);
// init. AppConnector
AppConnector._setMainApp(this);
// default values
this.downloadStopped = false;
}
/**
* Check online whether a new HelloNzb version is available.
* Called from constructor.
*/
private void checkProgramUpdate()
{
// set background status bar
taskMgr.updateCheck(true);
if(HelloNzbToolkit.isUpdateAvailable(VERSION))
{
logger.msg("New program version available", MyLogger.SEV_INFO);
MyFuture<Boolean> future = new MyFuture<Boolean>(false);
HelloNzbUpdateNotifier notifier = new HelloNzbUpdateNotifier(jframe, localer, logger);
notifier.show(future);
if(future.getPayload())
{
// the user wants to update the application, start auto-updater
notifier.startUpdater(this);
// at this point the startUpdater() method should have spawned the updater
// in a new process and quit itself. we thus should usually not come to this
// point. but if so, then try traditional/manual update now.
notifier.downloadLatestDirectly(this);
}
}
// unset background status bar
taskMgr.updateCheck(false);
}
/**
* Called at app shutdown to delete lock file
*/
private static void unlockFile()
{
// unbind server socket
try
{
oneInstanceSocket.close();
}
catch(IOException e)
{
e.printStackTrace();
}
}
/**
* Open the preferences window.
*/
public void showPreferences()
{
prefContainer = new HelloNzbPreferences(this);
prefContainer.getPrefDialog().setVisible(true);
}
/**
* This method starts to download the first file in the queue.
*/
public void startDownload()
{
String rootDir = prefContainer.getPrefValue("GeneralSettingsDownloadDir");
String msg, title;
// check if we are running already
if(currentFileDownloader != null)
{
stopCurrDownload(true);
if(trayIcon != null)
trayIcon.setAnimation(false);
return;
}
else if(nzbFileQueueTabModel.getRowCount() == 0)
return; // nothing to download in the queue
else if(prefContainer != null && prefContainer.getPrefDialog().isVisible())
return; // don't start download if preferences dialog is visible
else if(trayIcon != null)
trayIcon.setAnimation(true);
// check if the root download directory is set
if(rootDir.equals(""))
{
msg = localer.getBundleText("PopupCreateDirError");
title = localer.getBundleText("PopupErrorTitle");
JOptionPane.showMessageDialog(jframe, msg, title, JOptionPane.ERROR_MESSAGE);
return;
}
// create download directory if necessary
File downloadDir = createDownloadDirectory(rootDir);
if(downloadDir == null)
return;
// (try to) start NIO clients (if not already started)
if(!startNioClient(false) || !startNioClient(true))
return;
// start backup downloader thread
BackupFileDownloader backupDownloader = null;
if(backupNioClient != null)
{
backupDownloader = new BackupFileDownloader(this, backupNioClient);
backupDownloader.setPaused(pauseToggleButton.isSelected());
Thread thread = new Thread(backupDownloader);
thread.setDaemon(true);
thread.start();
}
// start file downloader thread
SegmentQueue segs = new SegmentQueue(filesToDownloadTabModel);
NntpFileDownloader downloader = new NntpFileDownloader(this, nioClient, backupDownloader, segs, downloadDir);
downloader.setPaused(pauseToggleButton.isSelected());
Thread thread = new Thread(downloader);
thread.setDaemon(true);
thread.start();
// send usage stats
statsNewDownload(segs.remainingBytes());
// enable/disable toolbar and menu action
AbstractAction action = actions.get("MenuServerStartDownload");
action.setEnabled(true);
action = actions.get("MenuServerPauseDownload");
action.setEnabled(true);
setStartStopPauseToggleButtons(false);
currentFileDownloader = downloader;
backupFileDownloader = backupDownloader;
// reset data counter?
if(downloadStopped)
downloadStopped = false;
else
totalBytesLoaded = currentNzbParser.getDownloadedBytes();
}
// create download directory, if necessary
private File createDownloadDirectory(String rootDir)
{
String nzbFilename = nzbFileQueueTabModel.getNzbParser(0).getName();
nzbFilename = HelloNzbToolkit.getLastFilename(nzbFilename);
File downloadDir = new File(rootDir + File.separator + nzbFilename);
if(!downloadDir.isDirectory() && !downloadDir.mkdirs())
{
String msg = localer.getBundleText("PopupCreateDirError");
String title = localer.getBundleText("PopupErrorTitle");
JOptionPane.showMessageDialog(jframe, msg, title, JOptionPane.ERROR_MESSAGE);
return null;
}
return downloadDir;
}
// immediately stop currently active download
private void stopCurrDownload(boolean wait)
{
if(currentFileDownloader == null)
return;
// disconnect
globalDisconnect(wait);
filesToDownloadTabModel.resetAllSegCounts(false);
List<DownloadFile> dlFiles = currentNzbParser.getFiles();
for(DownloadFile file : dlFiles)
file.resetSegments();
dlFiles = filesToDownloadTabModel.getDownloadFileVector();
for(DownloadFile file : dlFiles)
updateDownloadQueue(file.getFilename(), 0);
// and reset actions (menus and actions)
updStatusBar(0);
setStartStopPauseToggleButtons(true);
AbstractAction action = actions.get("MenuServerPauseDownload");
action.setEnabled(false);
if(trayIcon != null)
trayIcon.setAnimation(false);
downloadStopped = true;
}
// (try to) start NIO client, if not already started
private boolean startNioClient(boolean backup)
{
try
{
if(backup)
{
String host = getPrefValue("ServerSettingsBackupHost");
String port = getPrefValue("ServerSettingsBackupPort");
if(host.isEmpty() || port.isEmpty())
{
backupNioClient = null;
return true; // no backup server settings set
}
}
// start NIO client
NettyNioClient client = backup ? backupNioClient : nioClient;
if(client == null)
{
client = new NettyNioClient(this, backup);
if(backup)
backupNioClient = client;
else
nioClient = client;
Thread t = new Thread(client);
t.setDaemon(true);
t.start();
}
return true;
}
catch(UnknownHostException e)
{
logger.printStackTrace(e);
String msg = localer.getBundleText("PopupUnknownServer");
String title = localer.getBundleText("PopupErrorTitle");
JOptionPane.showMessageDialog(jframe, msg, title, JOptionPane.ERROR_MESSAGE);
setStartStopPauseToggleButtons(true);
if(trayIcon != null)
trayIcon.setAnimation(false);
}
catch(IOException e)
{
logger.printStackTrace(e);
setStartStopPauseToggleButtons(true);
if(trayIcon != null)
trayIcon.setAnimation(false);
}
return false;
}
// register new download in usage statistics (call web service)
private void statsNewDownload(long bytes)
{
int threadcount = Integer.valueOf(prefContainer.getPrefValue("ServerSettingsThreadCount"));
boolean ssl = prefContainer.getBooleanPrefValue("ServerSettingsUseSSL");
String speedPref = prefContainer.getPrefValue("DownloadSettingsMaxConnectionSpeed");
boolean speed = false;
if(!speedPref.equals("0")) speed = true;
boolean par2 = prefContainer.getBooleanPrefValue("DownloadSettingsPar2Check");
boolean unrar = prefContainer.getBooleanPrefValue("DownloadSettingsExtractRARArchives");
HelloNzbUsageStats.registerDownload(bytes, threadcount, ssl, speed, par2, unrar);
}
/**
* Called from action listener when download should be paused.
*/
public void pauseDownload()
{
if(currentFileDownloader == null)
return;
// check if we are currently in paused state
if(!currentFileDownloader.isPaused())
{
pauseToggleButton.setSelected(true);
currentFileDownloader.setPaused(true);
if(backupFileDownloader != null)
backupFileDownloader.setPaused(true);
SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
statusBarText.setText(localer.getBundleText("StatusBarDownloadPaused"));
}
} );
}
else
{
pauseToggleButton.setSelected(false);
currentFileDownloader.setPaused(false);
if(backupFileDownloader != null)
backupFileDownloader.setPaused(false);
}
}
/**
* Returns whether or not the download is paused.
*
* @return true/false
*/
public boolean isDownloadPaused()
{
return pauseToggleButton.isSelected();
}
/**
* This method is called by a NntpFileDownloader object when it has finished
* downloading the file's data.
*/
public void fileDownloadFinished(String filename)
{
// update total progress bar in the nzb file queue table, also the application window title
synchronized(filesToDownloadTabModel)
{
DownloadFile file = filesToDownloadTabModel.getDownloadFile(filename);
if(file != null)
totalBytesLoaded += file.getTotalFileSize();
else
return;
}
currentNzbParser.setDownloadedBytes(totalBytesLoaded);
int p = (int) (totalBytesLoaded * 100 / currentNzbParser.getOrigTotalSize());
jframe.setTitle("(" + p + "%) " + winTitle);
synchronized(nzbFileQueueTabModel)
{
nzbFileQueueTabModel.setRowProgress(currentNzbParser, p);
}
}
/**
* This method is called by a NntpFileDownloader object when it has finished
* decoding the file's data. It then updates the download queues and menu
* items if necessary.
*/
public void fileDecodingFinished(String filename)
{
// remove according row from file download queue table
boolean emptyDownloadFileQueue = false;
synchronized(filesToDownloadTabModel)
{
for(int i = 0; i < filesToDownloadTabModel.getRowCount(); i++)
{
DownloadFile file = filesToDownloadTabModel.getDownloadFile(i);
// check if an error occurred during file download
// remove line from table if no error occurred
if(file.getFilename().equals(filename) && !file.downloadError())
{
// start running RAR extractor
if(!currentNzbParser.hasUnrarStarted())
taskMgr.startRunningRarExtract(currentNzbParser, filename);
else
taskMgr.downloadFinished(currentNzbParser, filename);
// remove from table
disposeRightPopup();
filesToDownloadTabModel.removeRow(filename);
currentNzbParser.removeFile(filename);
break;
}
}
// if running RAR extractor succeeded, we don't need to download any more files
if(currentNzbParser.hasUnrarStarted() && currentNzbParser.hasUnrarSucceeded())
{
removeInactivePar2FilesInQueue();
String prefix = HelloNzbToolkit.getRarFilePrefix(currentNzbParser.getSucceededFirstRarFile());
if(prefix != null && filename.startsWith(prefix))
currentNzbParser.saveToDelete(filename);
}
if(filesToDownloadTabModel.getRowCount() == 0)
emptyDownloadFileQueue = true;
}
synchronized(nzbFileQueueTabModel)
{
// remove first entry from nzb file list, if last seg. was downloaded
if(emptyDownloadFileQueue)
{
// notify running unrar extractor via task manager
taskMgr.allFilesDownloaded(currentNzbParser); // blocks until bg job is done
// par2 check?
String pref = prefContainer.getPrefValue("DownloadSettingsPar2Check");
if(pref.equals("PAR2") && (!currentNzbParser.hasUnrarStarted() || currentNzbParser.hasUnrarFailed()))
par2Check(currentNzbParser);
else if(pref.contains("MultiPAR") && (!currentNzbParser.hasUnrarStarted() || currentNzbParser.hasUnrarFailed()))
par2jCheck(currentNzbParser);
else if(trayIcon != null)
{
// show tray icon message
String msg = localer.getBundleText("SystemTrayMsgDownloadFinished");
String name = "\"" + HelloNzbToolkit.getLastFilename(currentNzbParser.getName()) + "\"";
msg = msg.replaceAll("_", name);
trayIcon.displayMessage(null, msg, MessageType.INFO);
}
// delete unwanted files (as specified in preferences)
HelloNzbToolkit.deleteFilesByType(currentNzbParser);
HelloNzbToolkit.deleteRarAndPar2Files(currentNzbParser);
// move extracted files to a user-specified destination folder
HelloNzbToolkit.moveFilesAfterDownload(currentNzbParser);
// begin next download or disconnect globally
disposeLeftPopup();
nzbFileQueueTabModel.removeRow(currentNzbParser);
startNextNzbDownload();
}
// shutdown computer after all downloads have finished
if(shutdownToggleButton.isSelected() && nzbFileQueueTabModel.getRowCount() == 0)
{
if(!taskMgr.activeTask())
shutdownNow();
}
}
saveOpenParserData(false);
}
/**
* Remove all par2 files from the download queue that are not currently being downloaded.
*/
private void removeInactivePar2FilesInQueue()
{
if(filesToDownloadTabModel.getRowCount() > 0)
{
pauseDownload();
disposeRightPopup();
List<String> toRemove = new ArrayList<>();
for(int i = 0; i < filesToDownloadTabModel.getRowCount(); i++)
{
DownloadFile file = filesToDownloadTabModel.getDownloadFile(i);
if(!file.getFilename().toLowerCase().endsWith(".par2"))
continue;
String prefix = HelloNzbToolkit.getRarFilePrefix(currentNzbParser.getSucceededFirstRarFile());
if(prefix != null && !file.getFilename().startsWith(prefix))
continue;
if(!currentFileDownloader.isActivelyDownloaded(file.getFilename()))
toRemove.add(file.getFilename());
else
currentNzbParser.saveToDelete(file.getFilename());
}
for(String f : toRemove)
{
logger.msg("Removing inactive file from download queue: " + f, MyLogger.SEV_DEBUG);
filesToDownloadTabModel.removeRow(f);
currentNzbParser.removeFile(f);
currentNzbParser.saveToDelete(f);
currentFileDownloader.removeFileSegmentsFromQueue(f);
if(backupFileDownloader != null)
backupFileDownloader.removeFileSegmentsFromQueue(f);
}
pauseDownload();
}
}
/**
* Called from background thread when par2 check is done.
*
* @param parser The parser object that is finished being checked
* @param success Whether or not PAR2 check was successful
*/
public void par2CheckDone(NzbParser parser, boolean success)
{
// update task manager
taskMgr.par2Done(parser);
// get RAR extraction setting
String pref = prefContainer.getPrefValue("DownloadSettingsExtractRARArchives");
if(success && pref.equals("true"))
rarExtract(parser); // start RAR archive extraction
else if(trayIcon != null)
{
// show tray icon message
String msg = localer.getBundleText("SystemTrayMsgDownloadFinished");
String name = "\"" + HelloNzbToolkit.getLastFilename(parser.getName()) + "\"";
msg = msg.replaceAll("_", name);
trayIcon.displayMessage(null, msg, MessageType.INFO);
}
}
/**
* Called from background thread when RAR extract is done.
*
* @param parser The parser object that is finished being checked
*/
public void rarExtractDone(NzbParser parser)
{
// update task manager
taskMgr.rarDone(parser);
HelloNzbToolkit.deleteRarAndPar2Files(parser);
HelloNzbToolkit.moveFilesAfterDownload(parser);
if(trayIcon != null)
{
// show tray icon message
String msg = localer.getBundleText("SystemTrayMsgDownloadFinished");
String name = "\"" + HelloNzbToolkit.getLastFilename(parser.getName()) + "\"";
msg = msg.replaceAll("_", name);
trayIcon.displayMessage(null, msg, MessageType.INFO);
}
}
/**
* Check whether or not we should shutdown the computer.
* Depends on the toggle button and an empty NZB file queue.
*
* @return either true or false
*/
public boolean shouldShutdown()
{
if(shutdownToggleButton == null || nzbFileQueueTabModel == null)
return false;
return (shutdownToggleButton.isSelected() && nzbFileQueueTabModel.getRowCount() == 0);
}
/**
* Called when the next nzb file in queue should be downloaded.
*/
public void startNextNzbDownload()
{
if(filesToDownloadTabModel.getRowCount() > 0)
return;
// is there at least one more nzb file to download?
currentFileDownloader = null;
backupFileDownloader = null;
if(nzbFileQueueTabModel.getRowCount() > 0)
{
loadNextNzbFile();
startDownload();
}
else
{
globalDisconnect(false);
updStatusBar(0);
AbstractAction action = actions.get("MenuServerStartDownload");
action.setEnabled(false);
action = actions.get("MenuServerPauseDownload");
action.setEnabled(false);
setStartStopPauseToggleButtons(true);
if(trayIcon != null)
trayIcon.setAnimation(false);
jframe.setTitle(winTitle);
// call garbage collector
Runtime.getRuntime().gc();
}
}
/**
* Remove the specified row from the nzb queue.
*
* @param parser The parser object of the row to remove
*/
public void removeRowFromNzbQueue(NzbParser parser)
{
nzbFileQueueTabModel.removeRow(parser);
saveOpenParserData(true);
}
/**
* This method is called when a background file downloader thread
* has finished file decoding and started writing file data to hard
* disk.
*/
public void fileWritingStarted(String filename)
{
synchronized(filesToDownloadTabModel)
{
filesToDownloadTabModel.setRowToWriting(filename);
}
}
/**
* Update the download file (for progress bar redrawing).
*
* @param filename The name of the download file row to update
* @param value The absolute value of the progress bar to set
*/
public void updateDownloadQueue(String filename, int value)
{
// update download file progress bar
synchronized(filesToDownloadTabModel)
{
filesToDownloadTabModel.setValueAt(value, filename, 3);
}
}
/**
* Decrease the segment counter of this file by one.
*
* @param filename The name of the download file row to update
*/
public void decrSegCount(String filename)
{
synchronized(filesToDownloadTabModel)
{
filesToDownloadTabModel.decrSegCount(filename);
}
}
/**
* This method sets the progress bar of the row identified by its filename
* to the "decoding" status, including the maximum value of the progress bar.
*
* @param filename The name of the download file row to update
* @param value The new maximum value of the progress bar
*/
public void setProgBarToDecoding(String filename, int value)
{
synchronized(filesToDownloadTabModel)
{
filesToDownloadTabModel.setRowToDecoding(filename, value);
}
}
/**
* Called when a NNTP client thread catches an IO exception.
*/
public void nntpConnectIoException()
{
// show error popup
String msg = localer.getBundleText("PopupSocketError");
String title = localer.getBundleText("PopupErrorTitle");
JOptionPane.showMessageDialog(jframe, msg, title, JOptionPane.ERROR_MESSAGE);
}
/**
* Called when a file that has been downloaded is corrupted.
*/
public void downloadDataCorrupted()
{
// show error popup
String msg = localer.getBundleText("PopupDownloadDataCorrupted");
String title = localer.getBundleText("PopupErrorTitle");
JOptionPane.showMessageDialog(jframe, msg, title, JOptionPane.ERROR_MESSAGE);
}
/**
* Called when we can't write downloaded data to disk (ie. due to disk is full error).
*/
public void IoErrorWhileSavingData(IOException ex)
{
// stop download if currently active, don't try to load any more data after this error
if(currentFileDownloader != null)
{
stopCurrDownload(true);
if(trayIcon != null)
trayIcon.setAnimation(false);
currentFileDownloader = null;
backupFileDownloader = null;
}
// show error popup
String msg = ex.getLocalizedMessage();
String title = localer.getBundleText("PopupErrorTitle");
JOptionPane.showMessageDialog(jframe, msg, title, JOptionPane.ERROR_MESSAGE);
}
/**
* Called when we can't write downloaded data to disk (ie. due to disk is full error).
*/
public void tooMany430Errors()
{
// show non-modal error popup
String title = localer.getBundleText("PopupErrorTitle");
String msg = localer.getBundleText("PopupDownloadFilesNotFound");
if(currentNzbParser != null)
msg += ":\n" + currentNzbParser.getName();
HelloNzbToolkit.showNonModalDialog(msg, title, JOptionPane.ERROR_MESSAGE);
}
/**
* Clear all selections on the NZB file queue table.
*/
public void clearNzbQueueSelection()
{
nzbListTab.clearSelection();
}
/**
* This method is called when the user wants to move one or more row(s)
* in the NZB file queue table up or down, via the according context menu.
*
* @param selectedRows The row(s) to move
* @param direction The direction to move the row(s), up or down
*/
public void moveRowsInNzbQueue(int [] selectedRows, NzbFileListPopupMoveRowAction.MoveDirection direction)
{
if(selectedRows.length == 0)
return;
// now move row(s) and compare first row before and after operation
NzbParser pBefore = nzbFileQueueTabModel.getNzbParser(0);
nzbFileQueueTabModel.moveRows(selectedRows, direction);
NzbParser pAfter = nzbFileQueueTabModel.getNzbParser(0);
if(pBefore != pAfter)
nzbQueueReordered(pAfter);
}
/**
* (Re-)start downloading with the given parser object.
*/
public void nzbQueueReordered(NzbParser parser)
{
filesToDownloadTabModel.clearTableData();
filesToDownloadTabModel.addNzbParserContents(parser);
currentNzbParser = parser;
totalBytesLoaded = currentNzbParser.getDownloadedBytes();
super.jframe.setTitle(winTitle);
}
/**
* Called when the user has clicked on the "remove row" menu item
* in context popup menu of the nzb files table.
*
* @param row The row number to delete (zero-based)
* @param delLocalData When true then also delete local data on disk
*/
public void removeNzbFileQueueRow(int row, boolean delLocalData)
{
if(row < 0 || row > (nzbFileQueueTabModel.getRowCount() - 1))
return;
if(row == 0)
jframe.setTitle(winTitle);
String dirname = nzbFileQueueTabModel.getNzbParser(row).getName();
synchronized(filesToDownloadTabModel)
{
// remove row from nzb file queue table
nzbFileQueueTabModel.removeRow(row);
if(row == 0)
{
// also remove according download files from right table
filesToDownloadTabModel.clearTableData();
loadNextNzbFile();
}
}
// delete local content
if(delLocalData)
{
String rootDir = prefContainer.getPrefValue("GeneralSettingsDownloadDir");
dirname = HelloNzbToolkit.getLastFilename(dirname);
File downloadDir = new File(rootDir + File.separator + dirname);
if(downloadDir.isDirectory())
HelloNzbToolkit.deleteNonEmptyDir(downloadDir);
}
// disable toolbar and menu action
if(nzbFileQueueTabModel.getRowCount() == 0)
{
AbstractAction action = actions.get("MenuServerStartDownload");
action.setEnabled(false);
action = actions.get("MenuServerPauseDownload");
action.setEnabled(false);
}
else
saveOpenParserData(true);
}
/**
* Called when the user has clicked on the "remove row" menu item
* in context popup menu of the files to download table.
*/
public void removeDownloadFileQueueRow(String name)
{
synchronized(filesToDownloadTabModel)
{
int row = -1;
if(!filesToDownloadTabModel.containsFilename(name))
return;
else
row = filesToDownloadTabModel.getRowByFilename(name);
// subtract the amount of bytes of the removed file from
// the total nzb file size progress bar on the left side
totalBytesLoaded += filesToDownloadTabModel.getDownloadFile(row).getTotalFileSize();
int p = (int) (totalBytesLoaded * 100 / currentNzbParser.getOrigTotalSize());
nzbFileQueueTabModel.setRowProgress(currentNzbParser, p);
// remove item from download file queue
filesToDownloadTabModel.removeRow(row);
currentNzbParser.removeFile(name);
// if this was the last item in download queue, then load the next nzb file
if(filesToDownloadTabModel.getRowCount() == 0)
removeNzbFileQueueRow(0, false);
}
}
/**
* Returns the file name of the given row in the download file queue.
*
* @param row Get the name of this row (zero-based)
* @return The file name of the given row, or null if this row was not found
*/
public String getDownloadFileName(int row)
{
String name = null;
synchronized(filesToDownloadTabModel)
{
if(row < 0 || row >= filesToDownloadTabModel.getRowCount())
return null;
name = filesToDownloadTabModel.getDownloadFile(row).getFilename();
}
return name;
}
/**
* Returns the error status of a download file in a specific row.
*
* @param row The row in the table
* @return error yes/no
*/
public boolean errorAtDownloadFile(int row)
{
return filesToDownloadTabModel.getDownloadFile(row).downloadError();
}
/**
* If set via program config, do a par2 check and automatically
* repair if necessary.
*
* @param parser The parser object to check
*/
private void par2Check(NzbParser parser)
{
// wait until all download files are finished
if(filesToDownloadTabModel.getRowCount() > 1)
return;
// do par2 check
taskMgr.par2Check(parser);
Par2Check par2 = new Par2Check(this, parser);
Thread t = new Thread(par2);
t.start();
}
/**
* If set via program config, do a par2j check and automatically
* repair if necessary.
*
* @param parser The parser object to check
*/
private void par2jCheck(NzbParser parser)
{
// wait until all download files are finished
if(filesToDownloadTabModel.getRowCount() > 1)
return;
// do par2j check
taskMgr.par2Check(parser);
Par2jCheck par2 = new Par2jCheck(this, parser);
Thread t = new Thread(par2);
t.start();
}
/**
* If set via program config, do a RAR archive extraction after download.
*
* @param parser The parser object to use
*/
private void rarExtract(NzbParser parser)
{
// do rar archive extracr
taskMgr.rarExtract(parser);
CompleteRarExtractor unrar = new CompleteRarExtractor(this, parser);
Thread t = new Thread(unrar);
t.start();
}
/**
* Reset the thread view and the status bar.
*/
public void resetThreadView()
{
super.resetThreadView();
updStatusBar(0);
}
/**
* Returns the currently set bps value (on status bar).
*
* @return Last bps value
*/
public long lastBpsValue()
{
if(nioClient == null && backupNioClient == null) // no NIO client active
return 0;
else if(nioClient != null && backupNioClient != null) // both NIO clients active
return nioClient.getDlTraffic() + backupNioClient.getDlTraffic();
else if(nioClient != null) // only the normal NIO client active
return nioClient.getDlTraffic();
else // only the backup NIO client active
return backupNioClient.getDlTraffic();
}
public void activateUsageStats()
{
boolean flag = prefContainer.getBooleanPrefValue("UsageStatsSettingsActivate");
HelloNzbUsageStats.setActive(flag);
}
// used at application shutdown
static class ShutdownHook extends Thread
{
public void run()
{
unlockFile();
}
}
}