JetCracker

Life-time learner's blog

[Java] [Vimeo] Generating a direct download link to Vimeo clip

Recently, I’ve been looking into ways of playing video streams on Android. Fortunately, with it’s VideoView you can play any video clip easily no matter where the actual file is. All you need is to provide a URL to the video file. With VideoView I managed to play the video even on my friend’s old HTC Desire S with its antient Android 2.3.5! At the end of the article you will find a link to a good article on how to play video in Android using VideoView component.

But soon I found out that I couldn’t play a youtube or vimeo video clip simply because I didn’t have a direct link to the video stream. Unfortunately, for some security reasons Vimeo.com doesn’t provide a download links. But if the video can be shown on the website, it can be downloaded. There’s always a way to do it.

On the internet, there were a lot of discussions about this and I found a solution (for PHP language) to my problem on StackOverflow. I implemented it, but a couple of days ago it suddenly stopped working. Turned out that Vimeo changed the markup of the page and old solutions became not applicable anymore.

I did some investigation into the new Vimeo.com markup and came up with the following solution.

Generating a vimeo download link

The algorithm looks like this:

Input data: vimeoID.

  1. content = getContentByURL("http://vimeo.com/m/" + vimeoID)
  2. Parse content to find and extract the value from data-config-url attribute:
    1. rawDataConfig = extractDataConfigURL(content);
    2. dataConfigURL = rawDataConfig.replace("&", "&");
  3. Navigate to dataConfigURL and load the content as JSON Object:
    1. json = getContentByURL(dataConfigURL);
    2. DataConfig config = new Gson().fromJson(json, DataConfig.class);
  4. Return config.request.files.h264.sd.url — this will return a download link for SD quality

Remarks

  • In order to make it work, you must use the same User-Agent in all our requests. I recommend using some mobile user-agent code to reduce the traffic.
  • DataConfig is a structure that contains all information about session, user and video.
  • In my implementation, I used Google Gson library to convert a string representation of JSON object to a Java object.

Implementation

import com.google.gson.Gson;

import java.text.ParseException;
import java.util.ArrayList;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class VimeoDownloadLinkProvider {

    public static final String DEFAULT_USER_AGENT_STRING
            = "Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/30.0.1599.101 Safari/537.36";
    public static final String DEFAULT_MOBILE_USER_AGENT_STRING
            = "Mozilla/5.0 (Linux; U; Android 2.3.3; ko-kr; LG-L160L Build/IML74K) AppleWebkit/534.30 (KHTML, like Gecko) Version/4.0 Mobile Safari/534.30";

    public static final String VIDEO_LINK_PREFIX = "http://vimeo.com/m/";
    public static final String MOBILE_QUALITY = "mobile";
    public static final String SD_QUALITY = "sd";

    private String source;
    private String userAgent = DEFAULT_MOBILE_USER_AGENT_STRING;
    private String vimeoId;
    private String videoLink;
    private String quality = SD_QUALITY;

    public VimeoDownloadLinkProvider(String source) throws ParseException {
        this.source = source;
        vimeoId = extractVimeoId(source);
        videoLink = VIDEO_LINK_PREFIX + vimeoId;
    }

    public String generateDownloadLink() {
        try {
            String videoPage = getText(videoLink);
            Pattern p = Pattern.compile("data-config-url=\"([^\"]+)\"");
            Matcher m = p.matcher(videoPage);
            if (m.find()) {
                String playerConfigLink = m.group(1);
                if (playerConfigLink != null) {
                    playerConfigLink = playerConfigLink.replace("&", "&");
                    VimeoPlayerData data = new Gson().fromJson(getText(playerConfigLink), VimeoPlayerData.class);
                    if (SD_QUALITY.equalsIgnoreCase(quality)) {
                        return data.getRequest().getFiles().getH264().getSd().getUrl();
                    } else
                        return data.getRequest().getFiles().getH264().getMobile().getUrl();
                }

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }

        return null;
    }

    public String getQuality() {
        return quality;
    }

    public void setQuality(String quality) {
        this.quality = quality;
    }

    public String getVimeoId() {
        return vimeoId;
    }

    public String getVideoLink() {
        return videoLink;
    }

    public String getSource() {
        return source;
    }

    public String getUserAgent() {
        return userAgent;
    }

    public void setUserAgent(String userAgent) {
        this.userAgent = userAgent;
    }

    protected String getText(String url) throws Exception {
        URL website = new URL(url);
        URLConnection connection = website.openConnection();
        connection.setRequestProperty("User-Agent", userAgent);
        connection.setRequestProperty("Connection", "Keep-Alive");
        connection.setRequestProperty("Referrer", url);
        BufferedReader in = new BufferedReader(
                new InputStreamReader(
                        connection.getInputStream()));

        StringBuilder response = new StringBuilder();
        String inputLine;

        while ((inputLine = in.readLine()) != null)
            response.append(inputLine);

        in.close();

        return response.toString();
    }

    private String extractVimeoId(String src) throws ParseException {
        Pattern p = Pattern.compile("((http://|https://){0,1}vimeo.com/(m/){0,1}){0,1}(\\d+)[^0-9]*");
        Matcher m = p.matcher(src);
        if (m.find()) {
            return m.group(4);
        } else {
            throw new ParseException(src, -1);
        }
    }

    private static class VimeoPlayerData {
        private Request request;

        private Request getRequest() {
            return request;
        }

        private void setRequest(Request request) {
            this.request = request;
        }
    }

    private static class Request {
        private String signature;
        private String session;
        private long timestamp;
        private long expires;
        private FilesStructure files;

        private String getSignature() {
            return signature;
        }

        private void setSignature(String signature) {
            this.signature = signature;
        }

        private String getSession() {
            return session;
        }

        private void setSession(String session) {
            this.session = session;
        }

        private long getTimestamp() {
            return timestamp;
        }

        private void setTimestamp(long timestamp) {
            this.timestamp = timestamp;
        }

        private long getExpires() {
            return expires;
        }

        private void setExpires(long expires) {
            this.expires = expires;
        }

        private FilesStructure getFiles() {
            return files;
        }

        private void setFiles(FilesStructure files) {
            this.files = files;
        }
    }

    private static class FilesStructure {
        private ArrayList<String> codecs;
        private CodecStructure h264;

        private ArrayList<String> getCodecs() {
            return codecs;
        }

        private void setCodecs(ArrayList<String> codecs) {
            this.codecs = codecs;
        }

        private CodecStructure getH264() {
            return h264;
        }

        private void setH264(CodecStructure h264) {
            this.h264 = h264;
        }
    }

    private static class CodecStructure {
        private VideoInfo hd;
        private VideoInfo sd;
        private VideoInfo mobile;

        private VideoInfo getHd() {
            return hd;
        }

        private void setHd(VideoInfo hd) {
            this.hd = hd;
        }

        private VideoInfo getMobile() {
            return mobile;
        }

        private void setMobile(VideoInfo mobile) {
            this.mobile = mobile;
        }

        private VideoInfo getSd() {
            return sd;
        }

        private void setSd(VideoInfo sd) {
            this.sd = sd;
        }
    }

    public static class VideoInfo {
        private String url;
        private int height;
        private int width;
        private String id;
        private int bitrate;

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

        public int getHeight() {
            return height;
        }

        public void setHeight(int height) {
            this.height = height;
        }

        public int getWidth() {
            return width;
        }

        public void setWidth(int width) {
            this.width = width;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public int getBitrate() {
            return bitrate;
        }

        public void setBitrate(int bitrate) {
            this.bitrate = bitrate;
        }
    }
}

 

Usage

public class VimeoDownloadLinkExample {

    public static void main(String[] args) throws Exception {
        VimeoDownloadLinkProvider provider = new VimeoDownloadLinkProvider("76978172");
        System.out.println(provider.getVimeoId());
        System.out.println(provider.generateDownloadLink());
    }

}
Advertisements

8 responses to “[Java] [Vimeo] Generating a direct download link to Vimeo clip

  1. Pingback: Vimeo Download link | sandiponduet

  2. lopaulo182 January 8, 2014 at 08:38

    This code has found?

    • lopaulo182 January 8, 2014 at 08:39

      This code has working?

  3. Larry Mustaine February 4, 2014 at 23:58

    Awesome, save my day!

  4. Maria February 6, 2014 at 19:02

    Bսeno, no es que est del todo deacuerdo con lo comentado, pero еst correcto lo que ѕe dice.Saludos

  5. Sebastian March 25, 2014 at 17:25

    Awesome! It works! You saved my a**, haha!

  6. neymar jr June 2, 2014 at 12:40

    You’ve made some decent points there. I checked on the internet to find out more about the issue and found most individuals will go along with your views on this website.

  7. Clawe July 22, 2014 at 13:17

    Thank you for this. I am implementing C# application for viewing videos from all major sites and this helped me with the last one.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: