package IO;
import callback.ICallback;
import callback.IDownloadCallback;
import configuration.Configuration;
import configuration.objects.Dropbox;
import configuration.objects.URLPattern;
import exception.CantLoadPluginException;
import exception.FailedToDownloadException;
import exception.FailedToRenameException;
import exception.PluginNotFoundException;
import interfaces.IPlugin;
import main.PluginLoader;
import java.io.*;
import java.net.URL;
import java.nio.file.Files;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by tama on 6/27/14.
*
* Try to download an image from an url
*/
public class Downloader implements Runnable
{
private static String JPEG_TYPE = "image/jpeg";
private static String PNG_TYPE = "image/png";
private static String GIF_TYPE = "image/gif";
private static Map<String, String> extensions;
private String url;
private String title;
private Configuration conf;
private PluginLoader loader;
private String localFolder;
private IDownloadCallback callback;
private String getThread()
{
return "[" + new Date().toString() + "]";
}
public Downloader(Configuration conf, PluginLoader loader, String url, String title)
{
init(conf, loader, url, title, null);
}
public Downloader(Configuration conf, PluginLoader loader, String url, String title, IDownloadCallback callback)
{
init(conf, loader, url, title, callback);
}
private void init(Configuration conf, PluginLoader loader, String url, String title, IDownloadCallback callback)
{
this.url = url;
this.title = title;
this.conf = conf;
this.loader = loader;
this.callback = callback;
extensions = new HashMap<String, String>();
extensions.put(JPEG_TYPE, "jpeg");
extensions.put(PNG_TYPE, "png");
extensions.put(GIF_TYPE, "gif");
System.out.println(getThread() + "Downloading [" + title + "] (" + url + ")");
}
/** Main method */
public void run()
{
/* Get the different existing patterns */
List<URLPattern> patterns = (List<URLPattern>)conf.get(Configuration.PATTERNS_TAG);
/* Get the local folder */
localFolder = (String)conf.get(Configuration.LOCAL_FOLDER_TAG);
if (patterns == null)
return;
boolean found = false;
int i = 0;
try
{
for (; !found && i < patterns.size(); ++i)
{
URLPattern pattern = patterns.get(i);
// The pattern is not active, skip it
if (!pattern.active)
{
continue;
}
// If the url pattern matches the pattern defined in the configuration ...
Pattern urlPattern = pattern.pattern;
if (urlPattern == null)
continue;
Matcher matcher = urlPattern.matcher(url);
if (matcher.find())
{
//Load the corresponding pattern and download it !
IPlugin plugin = loader.getPlugin(pattern.pluginName);
//We found and loaded a plugin able to handle the link
found = true;
if (plugin != null)
{
//System.out.println(getThread() + "Using plugin " + pattern.pluginName);
List<String> urls = plugin.getLinks(url);
for (String url : urls)
{
download(url, localFolder, title, new ICallback() {
@Override
public void onDownloadFinished(File downloaded) {
//Check if we should upload to Dropbox
Dropbox dropbox = (Dropbox) conf.get(Configuration.DROPBOX_TAG);
if (dropbox != null)
{
DropboxUploader.upload(downloaded, dropbox.access_token, dropbox.upload_folder);
}
if (callback != null)
{
callback.onSuccess(title);
}
}
});
}
}
else
{
callback.onError(title, new CantLoadPluginException(pattern.pluginName));
}
}
}
if (!found)
{
callback.onError(title, new PluginNotFoundException(url));
}
}
catch (Exception e)
{
e.printStackTrace();
if (callback != null)
{
callback.onError(title, e);
}
}
}
/**
* Download a file, with a callback called once the download is successful
* @param url The URL to the file to download
* @param localFolder the local folder in which the file will be downloaded
* @param title the title of the link
* @param downloadCallback the callback once the download is successful
*/
private void download(String url, String localFolder, String title, ICallback downloadCallback)
{
InputStream stream = null;
OutputStream out = null;
try
{
stream = new URL(url).openStream();
//Write to temporary file
File tmp = File.createTempFile("img", "");
out = new FileOutputStream(tmp);
int read = 0;
byte[] buffer = new byte[1024];
while ((read = stream.read(buffer)) != -1)
{
out.write(buffer, 0, read);
}
File downloadedFile = rename(tmp, localFolder, title);
if (downloadedFile == null)
{
/* Some error occured or the file is not valid, delete the temporary file */
tmp.delete();
callback.onError(title, new FailedToRenameException());
}
else
{
if (downloadCallback != null)
{
downloadCallback.onDownloadFinished(downloadedFile);
}
}
}
catch (Exception e)
{
callback.onError(title, new FailedToDownloadException(e));
}
finally
{
try
{
if (stream != null)
stream.close();
}
catch (IOException e) {} //TODO : exception handling ?
try
{
if (out != null)
out.close();
}
catch (IOException e) {} //TODO : exception handling ?
}
}
/**
* Rename a temporary file to the correct name according to the title of the link and the existing files
* @param tmp the temporary file
* @param localFolder the folder where the file will be moved
* @param title the title of the link
* @throws IOException IO error (file not found...)
*/
private File rename(File tmp, String localFolder, String title) throws IOException
{
String fileType = Files.probeContentType(tmp.toPath());
String extension = extensions.get(fileType);
if (extension == null)
{
//System.out.println(getThread() + "File type unknown (" + fileType + "), defaulting to jpeg");
//Default extension = jpeg
extension = extensions.get(JPEG_TYPE);
}
/* Extension not authorized */
List<String> authorizedExtensions = (List<String>) conf.get(Configuration.EXTENSIONS_ALLOWED_TAG);
if (authorizedExtensions == null || !authorizedExtensions.contains(extension))
{
return null;
}
int extensionSize = extension.length() + 1;
//System.out.println(getThread() + "File : " + extension + " [" + extensionSize + "]");
//Check if the destination file exists, add random suffix if already existant
StringBuilder sb = new StringBuilder();
sb.append(localFolder);
if (!localFolder.endsWith("/"))
{
sb.append("/");
}
sb.append(title).append(".").append(extension);
boolean ok = false;
File finalFile = null;
int count = 1;
String tmpName = sb.toString();
while (!ok && count <= 25)
{
finalFile = new File(tmpName);
//System.out.println(getThread() + ">> Trying " + tmpName);
if (!finalFile.exists())
{
ok = true;
}
else
{
if (count == 1)
{
//First try, replace extension with (1).extension
//e.g file_name (1).jpg
tmpName = tmpName.replace("." + extension, "(1)." + extension);
}
else
{
//Replace file_name (n).jpg with file_name(n+1).jpg
//e.g file_name (1).jpg -> file_name (2).jpg
String toReplace = "(" + String.valueOf(count - 1) + ")." + extension;
String newName = "(" + String.valueOf(count) + ")." + extension;
tmpName = tmpName.replace(toReplace, newName);
}
count++;
}
}
//Move the file !
if (ok)
{
tmp.renameTo(finalFile);
return finalFile;
}
return null;
}
}