Package com.github.axet.vget

Source Code of com.github.axet.vget.VGet

package com.github.axet.vget;

import java.io.File;
import java.io.FileNotFoundException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.concurrent.atomic.AtomicBoolean;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

import com.github.axet.vget.info.VideoInfo;
import com.github.axet.vget.info.VideoInfo.States;
import com.github.axet.vget.info.VideoInfoUser;
import com.github.axet.wget.Direct;
import com.github.axet.wget.DirectMultipart;
import com.github.axet.wget.DirectRange;
import com.github.axet.wget.DirectSingle;
import com.github.axet.wget.RetryWrap;
import com.github.axet.wget.info.DownloadInfo;
import com.github.axet.wget.info.DownloadInfo.Part;
import com.github.axet.wget.info.ex.DownloadError;
import com.github.axet.wget.info.ex.DownloadIOCodeError;
import com.github.axet.wget.info.ex.DownloadIOError;
import com.github.axet.wget.info.ex.DownloadInterruptedError;
import com.github.axet.wget.info.ex.DownloadMultipartError;
import com.github.axet.wget.info.ex.DownloadRetry;

public class VGet {

    VideoInfo info;
    File targetDir;

    File targetForce = null;

    File targetFile = null;

    /**
     * extract video information constructor
     *
     * @param source
     */
    public VGet(URL source) {
        this(new VideoInfo(source), null);
    }

    public VGet(URL source, File targetDir) {
        this(new VideoInfo(source), targetDir);
    }

    public VGet(VideoInfo info, File targetDir) {
        this.info = info;
        this.targetDir = targetDir;
    }

    public void setTarget(File file) {
        targetForce = file;
    }

    public void setTargetDir(File targetDir) {
        this.targetDir = targetDir;
    }

    /**
     * get output file on local file system
     *
     * @return
     */
    public File getTarget() {
        return targetFile;
    }

    public VideoInfo getVideo() {
        return info;
    }

    public void download() {
        download(new VideoInfoUser(), new AtomicBoolean(false), new Runnable() {
            @Override
            public void run() {
            }
        });
    }

    public void download(VideoInfoUser user) {
        download(user, new AtomicBoolean(false), new Runnable() {
            @Override
            public void run() {
            }
        });
    }

    /**
     * Drop all foribiden characters from filename
     *
     * @param f
     *            input file name
     * @return normalized file name
     */
    static String replaceBadChars(String f) {
        String replace = " ";
        f = f.replaceAll("/", replace);
        f = f.replaceAll("\\\\", replace);
        f = f.replaceAll(":", replace);
        f = f.replaceAll("\\?", replace);
        f = f.replaceAll("\\\"", replace);
        f = f.replaceAll("\\*", replace);
        f = f.replaceAll("<", replace);
        f = f.replaceAll(">", replace);
        f = f.replaceAll("\\|", replace);
        f = f.trim();
        f = StringUtils.removeEnd(f, ".");
        f = f.trim();

        String ff;
        while (!(ff = f.replaceAll("  ", " ")).equals(f)) {
            f = ff;
        }

        return f;
    }

    static String maxFileNameLength(String str) {
        int max = 255;
        if (str.length() > max)
            str = str.substring(0, max);
        return str;
    }

    boolean done(AtomicBoolean stop) {
        if (stop.get())
            throw new DownloadInterruptedError("stop");
        if (Thread.currentThread().isInterrupted())
            throw new DownloadInterruptedError("interrupted");

        return false;
    }

    void retry(VideoInfoUser user, AtomicBoolean stop, Runnable notify, Throwable e) {
        boolean retracted = false;

        while (!retracted) {
            for (int i = RetryWrap.RETRY_DELAY; i >= 0; i--) {
                if (stop.get())
                    throw new DownloadInterruptedError("stop");
                if (Thread.currentThread().isInterrupted())
                    throw new DownloadInterruptedError("interrupted");

                info.setDelay(i, e);
                notify.run();

                try {
                    Thread.sleep(1000);
                } catch (InterruptedException ee) {
                    throw new DownloadInterruptedError(ee);
                }
            }

            try {
                // if we continue to download from old source, and this proxy
                // server is
                // down we have to try to extract new info and try to resume
                // download

                DownloadInfo infoOld = info.getInfo();
                info.extract(user, stop, notify);
                DownloadInfo infoNew = info.getInfo();

                if (infoOld != null && infoOld.resume(infoNew)) {
                    infoNew.copy(infoOld);
                } else {
                    if (targetFile != null) {
                        FileUtils.deleteQuietly(targetFile);
                        targetFile = null;
                    }
                }

                retracted = true;
            } catch (DownloadIOCodeError ee) {
                if (retry(ee)) {
                    info.setState(States.RETRYING, ee);
                    notify.run();
                } else {
                    throw ee;
                }
            } catch (DownloadRetry ee) {
                info.setState(States.RETRYING, ee);
                notify.run();
            }
        }
    }

    void target(DownloadInfo dinfo) {
        if (targetForce != null) {
            targetFile = targetForce;

            if (dinfo.multipart()) {
                if (!DirectMultipart.canResume(dinfo, targetFile))
                    targetFile = null;
            } else if (dinfo.getRange()) {
                if (!DirectRange.canResume(dinfo, targetFile))
                    targetFile = null;
            } else {
                if (!DirectSingle.canResume(dinfo, targetFile))
                    targetFile = null;
            }
        }

        if (targetFile == null) {
            File f;

            Integer idupcount = 0;

            String sfilename = replaceBadChars(info.getTitle());

            sfilename = maxFileNameLength(sfilename);

            String ct = dinfo.getContentType();
            if (ct == null)
                throw new DownloadRetry("null content type");

            String ext = ct.replaceFirst("video/", "").replaceAll("x-", "");

            do {
                String add = idupcount > 0 ? " (".concat(idupcount.toString()).concat(")") : "";

                f = new File(targetDir, sfilename + add + "." + ext);
                idupcount += 1;
            } while (f.exists());

            targetFile = f;

            // if we dont have resume file (targetForce==null) then we shall
            // start over.
            dinfo.reset();
        }
    }

    boolean retry(Throwable e) {
        if (e == null)
            return true;

        if (e instanceof DownloadIOCodeError) {
            DownloadIOCodeError c = (DownloadIOCodeError) e;
            switch (c.getCode()) {
            case HttpURLConnection.HTTP_FORBIDDEN:
            case 416:
                return true;
            default:
                return false;
            }
        }

        return false;
    }

    /**
     * return status of download information. subclassing for VideoInfo.empty();
     *
     * @return
     */
    public boolean empty() {
        return getVideo().empty();
    }

    public void extract() {
        extract(new AtomicBoolean(false), new Runnable() {
            @Override
            public void run() {
            }
        });
    }

    public void extract(AtomicBoolean stop, Runnable notify) {
        extract(new VideoInfoUser(), stop, notify);
    }

    /**
     * extract video information, retry until success
     *
     * @param stop
     * @param notify
     */
    public void extract(VideoInfoUser user, AtomicBoolean stop, Runnable notify) {
        while (!done(stop)) {
            try {
                if (info.empty()) {
                    info.setState(States.EXTRACTING);
                    info.extract(user, stop, notify);
                    info.setState(States.EXTRACTING_DONE);
                    notify.run();
                }
                return;
            } catch (DownloadRetry e) {
                retry(user, stop, notify, e);
            } catch (DownloadMultipartError e) {
                checkFileNotFound(e);
                checkRetry(e);
                retry(user, stop, notify, e);
            } catch (DownloadIOCodeError e) {
                if (retry(e))
                    retry(user, stop, notify, e);
                else
                    throw e;
            } catch (DownloadIOError e) {
                retry(user, stop, notify, e);
            }
        }
    }

    void checkRetry(DownloadMultipartError e) {
        for (Part ee : e.getInfo().getParts()) {
            if (!retry(ee.getException())) {
                throw e;
            }
        }
    }

    /**
     * check if all parts has the same filenotfound exception. if so throw
     * DownloadError.FilenotFoundexcepiton
     *
     * @param e
     */
    void checkFileNotFound(DownloadMultipartError e) {
        FileNotFoundException f = null;
        for (Part ee : e.getInfo().getParts()) {
            // no error for this part? skip it
            if (ee.getException() == null)
                continue;
            // this exception has no cause? then it is not FileNotFound
            // excpetion. then do noting. this is checking function. do not
            // rethrow
            if (ee.getException().getCause() == null)
                return;
            if (ee.getException().getCause() instanceof FileNotFoundException) {
                // our first filenotfoundexception?
                if (f == null) {
                    // save it for later checks
                    f = (FileNotFoundException) ee.getException().getCause();
                } else {
                    // check filenotfound error message is it the same?
                    FileNotFoundException ff = (FileNotFoundException) ee.getException().getCause();
                    if (!ff.getMessage().equals(f.getMessage())) {
                        // if the filenotfound exception message is not the
                        // same. then we cannot retrhow filenotfound exception.
                        // return and continue checks
                        return;
                    }
                }
            } else {
                break;
            }
        }
        if (f != null)
            throw new DownloadError(f);
    }

    public void download(final AtomicBoolean stop, final Runnable notify) {
        download(new VideoInfoUser(), stop, notify);
    }

    public void download(VideoInfoUser user, final AtomicBoolean stop, final Runnable notify) {
        if (targetFile == null && targetForce == null && targetDir == null) {
            throw new RuntimeException("Set download file or directory first");
        }

        try {
            if (empty()) {
                extract(user, stop, notify);
            }

            while (!done(stop)) {
                try {
                    final DownloadInfo dinfo = info.getInfo();

                    if (dinfo.getContentType() == null || !dinfo.getContentType().contains("video/")) {
                        throw new DownloadRetry("unable to download video, bad content");
                    }

                    target(dinfo);

                    Direct direct;

                    if (dinfo.multipart()) {
                        // multi part? overwrite.
                        direct = new DirectMultipart(dinfo, targetFile);
                    } else if (dinfo.getRange()) {
                        // range download? try to resume download from last
                        // position
                        if (targetFile.exists() && targetFile.length() != dinfo.getCount())
                            targetFile = null;
                        direct = new DirectRange(dinfo, targetFile);
                    } else {
                        // single download? overwrite file
                        direct = new DirectSingle(dinfo, targetFile);
                    }

                    direct.download(stop, new Runnable() {
                        @Override
                        public void run() {
                            switch (dinfo.getState()) {
                            case DOWNLOADING:
                                info.setState(States.DOWNLOADING);
                                notify.run();
                                break;
                            case RETRYING:
                                info.setDelay(dinfo.getDelay(), dinfo.getException());
                                notify.run();
                                break;
                            default:
                                // we can safely skip all statues. (extracting -
                                // already
                                // pased, STOP / ERROR / DONE i will catch up
                                // here
                            }
                        }
                    });

                    info.setState(States.DONE);
                    notify.run();

                    // break while()
                    return;
                } catch (DownloadRetry e) {
                    retry(user, stop, notify, e);
                } catch (DownloadMultipartError e) {
                    checkFileNotFound(e);
                    checkRetry(e);
                    retry(user, stop, notify, e);
                } catch (DownloadIOCodeError e) {
                    if (retry(e))
                        retry(user, stop, notify, e);
                    else
                        throw e;
                } catch (DownloadIOError e) {
                    retry(user, stop, notify, e);
                }
            }
        } catch (DownloadInterruptedError e) {
            info.setState(VideoInfo.States.STOP, e);
            notify.run();

            throw e;
        } catch (RuntimeException e) {
            info.setState(VideoInfo.States.ERROR, e);
            notify.run();

            throw e;
        }
    }
}
TOP

Related Classes of com.github.axet.vget.VGet

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.