md.MangaDownloader.java Source code

Java tutorial

Introduction

Here is the source code for md.MangaDownloader.java

Source

/* MangaDownloader can help with Comics too
   Copyright (C) 2012 Hernn Parra
    
   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 2 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, write to the Free Software
   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/
package md;

import java.io.*;
import java.net.*;
import java.util.*;
import md.model.*;
import org.apache.commons.io.*;

/**
 *
 * @author Hernan
 */
//API Submanga.com backups & ripping
//API Mangareader.net backups & ripping
//API Support for zip, cbz, rar, cbr compression & packing
//API Automatic chapter naming
//API Multichapter (volume) download & packing
//API Inteligent Automatic volume naming
//API Retry on download timeout or errors
//TODO API Soporte a parsers implementados en Groovy
//TODO API Resumir si existe el directorio de download
//TODO API Resumir si existe el archivo el download de un capitulo - Descomprimir archivo y continuar como caso de arriba.
//TODO API Soporte a resume desde el archivo de pendings.txt?
//TODO API Descubrimiento automtico de parsers
//TODO API Watchdog de nuevos capitulos
public class MangaDownloader {
    private final static ResourceBundle STRINGS = java.util.ResourceBundle.getBundle("md/strings");
    private final static String PENDINGS_FILE_NAME = STRINGS.getString("NAME_PENDINGS");
    private final static int CONNECTION_TIMEOUT = 20000;
    private final static int READ_TIMEOUT = 60000;
    private SiteParser site;
    private Archiver archiver;
    private EventsHandler handler;
    private File tempDirectory = null;
    private String savePath = new File(".").getAbsolutePath() + File.separator;

    public MangaDownloader(SiteParser site, Archiver archiver, EventsHandler eh) {
        this.site = site;
        this.archiver = archiver;
        this.handler = eh;
    }

    public File volumeDownload(String volumeName, String[] urls) throws IOException {
        for (int i = 0; i < urls.length; i++) {
            String downloadedChapter = download(urls[i]);
            handler.event(EventsHandler.EventTypes.HighLevelEvent,
                    String.format(STRINGS.getString("MSG_DOWNLOADEDCHAPTER"), downloadedChapter));
        }
        return pack(volumeName);
    }

    public File volumeDownload(String volumeName, List<MangaChaptersInfo> chapters) throws IOException {
        List<String> urls = new ArrayList<String>();
        for (MangaChaptersInfo chapter : chapters) {
            urls.add(chapter.url);
        }
        String[] urlsArray = {};
        urlsArray = urls.toArray(urlsArray);
        return volumeDownload(volumeName, urlsArray);
    }

    public File chapterDownload(String url) throws IOException {
        return pack(download(url));
    }

    private String download(String url) throws IOException {
        MangaChapterInfo downloadInfo = site.retrieveMangaPagesList(url, handler);
        handler.event(EventsHandler.EventTypes.HighLevelEvent, STRINGS.getString("MSG_GOTCHAPTERDOWNLOADLIST"));

        String filename = String.format("%s %s", makeValidFileName(downloadInfo.mangaName),
                makeValidFileName(downloadInfo.chapterNumber));

        if (tempDirectory == null) {
            tempDirectory = createTempDirectory(filename);
        }

        downloadList(downloadInfo.images, filename, tempDirectory);
        handler.event(EventsHandler.EventTypes.HighLevelEvent,
                String.format(STRINGS.getString("MSG_ARCHIVEDFILE"), filename));
        return filename;
    }

    private File pack(String filename) throws IOException {
        filename = makeValidFileName(filename);
        try {
            return archiver.archive(getSavePath(), filename, tempDirectory);
        } finally {
            if (tempDirectory != null) {
                FileUtils.deleteDirectory(tempDirectory);
            }
        }
    }

    /**
     * The urlList must be ordered for renumbering
     *
     * @param urlList List of ordered url to be downloaded and renamed
     * @throws MalformedURLException
     */
    private boolean downloadList(List<MangaImageInfo> urlList, String namePrefix, File workDirectory)
            throws IOException {
        String baseName = namePrefix + " - ";
        File baseDirectory = new File(workDirectory, namePrefix);
        boolean allDownloaded = true;

        int counter = 1;
        for (MangaImageInfo page : urlList) {
            File outputFile = new File(baseDirectory,
                    String.format("%s%04d%s", baseName, counter++, page.imgExtension));
            if (outputFile.exists() && (outputFile.length() > 0)) {
                handler.event(EventsHandler.EventTypes.LowLevelEvent,
                        String.format(STRINGS.getString("MSG_ALREADYDOWNLOADEDFILE"), page.url));
            } else {
                try {
                    FileUtils.copyURLToFile(new URL(page.url), outputFile, CONNECTION_TIMEOUT, READ_TIMEOUT);
                } catch (IOException ioe) {
                    outputFile.delete();
                    handler.event(EventsHandler.EventTypes.LowLevelEvent,
                            String.format(STRINGS.getString("MSG_RETRYINGFILE"), page.url, ioe.getMessage()));
                    try {
                        FileUtils.copyURLToFile(new URL(page.url), outputFile, CONNECTION_TIMEOUT, READ_TIMEOUT);
                    } catch (IOException ex) {
                        handler.event(EventsHandler.EventTypes.LowLevelEvent,
                                String.format(STRINGS.getString("MSG_SKIPPINGFILE"), page.url, ex.getMessage()));
                        allDownloaded = false;
                        FileUtils.writeStringToFile(new File(baseDirectory, PENDINGS_FILE_NAME), page.url);
                    }
                }
                handler.event(EventsHandler.EventTypes.LowLevelEvent,
                        String.format(STRINGS.getString("MSG_DOWNLOADEDFILE"), page.url));
            }
        }
        return allDownloaded;
    }

    private File createTempDirectory(String filename) throws IOException {
        final File tempDir = new File(getSavePath() + "md." + filename);
        if (!tempDir.exists()) {
            if (!(tempDir.mkdir())) {
                throw new IOException(String.format(STRINGS.getString("MSG_COULDNOTCREATETEMPORALDIRECTORY"),
                        tempDir.getAbsolutePath()));
            }
        }
        return tempDir;
    }

    //    private File createTempDirectory() throws IOException {
    //        final File temp = File.createTempFile(STRINGS.getString("NAME_TEMP"), Long.toBinaryString(System.nanoTime()));
    //
    //        if (!(temp.delete())) {
    //            throw new IOException(String.format(STRINGS.getString("MSG_COULDNOTDELETETEMPORALFILE"), temp.getAbsolutePath()));
    //        }
    //
    //        if (!(temp.mkdir())) {
    //            throw new IOException(String.format(STRINGS.getString("MSG_COULDNOTCREATETEMPORALDIRECTORY"), temp.getAbsolutePath()));
    //        }
    //
    //        return temp;
    //    }

    private String makeValidFileName(String mangaName) {
        mangaName = mangaName.replaceAll("(\\\\|/|\\*|\\?)*", "");
        return mangaName;
    }

    public void setSavePath(String saveTo) {
        if (!saveTo.endsWith(File.separator)) {
            saveTo = saveTo + File.separator;
        }
        this.savePath = saveTo;
    }

    public String getSavePath() {
        return savePath;
    }

    public static String automaticVolumeName(List<MangaChaptersInfo> selectedChapters) {
        TreeSet<Integer> chapterNumbers = new TreeSet<Integer>();
        String alternative = "";
        for (MangaChaptersInfo m : selectedChapters) {
            try {
                if (!m.chapterNumber.equals("")) {
                    int test = Integer.parseInt(m.chapterNumber);
                    chapterNumbers.add(test);
                }
            } catch (NumberFormatException e) {
                //If more than one alternative chapter name then all renamed to "Extras"
                if (alternative.equals("")) {
                    alternative = m.chapterNumber;
                } else {
                    alternative = STRINGS.getString("NAME_EXTRAS");
                }
            }
        }

        String result = "";
        if (!chapterNumbers.isEmpty()) {
            int firstInSequence = Integer.MIN_VALUE;
            int lastInSequence;

            for (Iterator<Integer> i = chapterNumbers.iterator(); i.hasNext();) {
                int actualNumber = i.next();
                if (firstInSequence == Integer.MIN_VALUE)
                    firstInSequence = actualNumber;
                lastInSequence = actualNumber;
                int next;
                if (i.hasNext()) {
                    next = chapterNumbers.higher(actualNumber);
                } else {
                    next = Integer.MIN_VALUE;
                }
                if ((actualNumber + 1 != next) || (next == Integer.MIN_VALUE)) {
                    //FIN DE SECUENCIA
                    if (!result.equals("")) {
                        result += ", ";
                    }
                    if (firstInSequence == lastInSequence) {
                        result += String.format("%04d", firstInSequence);
                    } else {
                        result += String.format("%04d - %04d", firstInSequence, lastInSequence);
                    }
                    firstInSequence = Integer.MIN_VALUE;
                }
            }

        }

        if (!alternative.equals("")) {
            if (!result.equals(""))
                result += " & ";
            result += alternative;
        }

        return result;
    }

}