Discovering the Vine API - CodeProject

:

: 7

Background

I've titled this article 'Discovering the Vine API', it could also have been titled 'Playing with Vine API',  because there's no official API available for Vine so I've written a simple tutorial on how to use this API through an example of how to upload a video to this platform using the API. 

What is Vine?

Vine is short form video sharing website. It allows users to share videos having duration upto 6 seconds. It's available for Android, iOS and Windows Phone. The website of it shows various such short videos. Users can sign up using Google or Facebook account. 

Information about Vine API

Vine is a product from Twitter. They don't provide any public API like Twitter. So some folks out there has reverse engineered the app and found API endpoints. The unofficial API includes a lot of endpoints, but here we will see how to share a video to Vine.

The Vine app doesn't allow to pick video from storage location. One has to record video from in-app camera, though it provides stop motion video recording. The app records video in MP4 format having resolution 480x480. Let’s go through one by one API.

 

Discovering the API

I've used these two links as reference while implementing code examples to show how to upload a video with category and caption. Both are documents/github projects about the private Vine API: VineAPI and Vine.app API Reference

Login to Vine

As we know Vine provides social network login. So the API takes those social credentials as username and password. To login to Vine and obtain session ID, you have to send username and password as multipart form data request using post method. The endpoint, code and response format is given below.

POST https://api.vineapp.com/users/authenticate

Post data
username=xxx@example.com
password=xxx

/// <summary>
/// Logs in to Vine and returns session ID.
/// </summary>
public static async Task<string> VineLoginAsync(string Username, string Password)
{
    try
    {
        string _VineSessionId = string.Empty;
        var multiPartData = new HttpMultipartFormDataContent();
        multiPartData.Add(new HttpStringContent(Username), "username");
        multiPartData.Add(new HttpStringContent(Password), "password");
        HttpClient httpClient = new HttpClient();
        var HttpReq = await httpClient.PostAsync(new Uri("https://api.vineapp.com/users/authenticate"), multiPartData);

        if (HttpReq.IsSuccessStatusCode)
        {
            var JsonResponse = await HttpReq.Content.ReadAsStringAsync();
            var obj = JsonObject.Parse(JsonResponse);
            _VineSessionId = obj.GetNamedObject("data").GetNamedString("key");
            ApplicationData.Current.LocalSettings.Values["VINE_SESSION_ID_KEY"] = _VineSessionId;
        }

        return _VineSessionId; 
    }
    
    catch (Exception ex)
    {

    }
}

 

Getting Channels

Channels are kind of category which is used while uploading video. By setting channel ID while uploading, it puts video in specified channel. To get channel name and its respective ID, you need to send a get request to below given endpoint. It's parameter less service call. The code and JSON output format is given below.

GET https://api.vineapp.com/channels/featured

/// <summary>
/// Returns Vine channel name with its ID.
/// </summary>
public static async Task<List<KeyValuePair<string, string>>> GetChannelsAsync()
{
    try
    {
        var HttpReq = await httpClient.GetAsync(new Uri("https://api.vineapp.com/channels/featured"));

        if (HttpReq.IsSuccessStatusCode)
        {
            var ResponseStr = await HttpReq.Content.ReadAsStringAsync();
            var JsonObj = JsonObject.Parse(ResponseStr);

            var array = JsonObj["data"].GetObject()["records"].GetArray();

            var ChannelDictionary = new List<KeyValuePair<string, string>>();

            foreach (var item in array)
            {
                ChannelDictionary.Add(new KeyValuePair<string, string>(item.GetObject()["channelId"].GetNumber().ToFloatingPointString(), item.GetObject()["channel"].GetString()));
            }
            
            return ChannelDictionary;
        }
    }
    
    catch (Exception ex)
    {

    }
}

Here you have to use an extension method to convert large integer number into string. If you just use ToString() method, it will return the number in scientific notation. For example, if channel ID is 1070175184667013120, you will get “1.07017518466701E+18” in return if you use ToString(). Hence you should use ToFloatingPointString() extension method, which is given below.

private static readonly Regex rxScientific = new Regex(@"^(?<sign>-?)(?<head>\d+)(\.(?<tail>\d*?)0*)?E(?<exponent>[+\-]\d+)$", RegexOptions.IgnoreCase|RegexOptions.ExplicitCapture|RegexOptions.CultureInvariant);

public static string ToFloatingPointString(this double value) 
{
    return ToFloatingPointString(value, NumberFormatInfo.CurrentInfo);
}

public static string ToFloatingPointString(double value, NumberFormatInfo formatInfo) 
{
    string result = value.ToString("r", NumberFormatInfo.InvariantInfo);
    Match match = rxScientific.Match(result);
    
    if (match.Success) 
    {
        Debug.WriteLine("Found scientific format: {0} => [{1}] [{2}] [{3}] [{4}]", result, match.Groups["sign"], match.Groups["head"], match.Groups["tail"], match.Groups["exponent"]);
        
        int exponent = int.Parse(match.Groups["exponent"].Value, NumberStyles.Integer, NumberFormatInfo.InvariantInfo);
        StringBuilder builder = new StringBuilder(result.Length + Math.Abs(exponent));
        builder.Append(match.Groups["sign"].Value);
        
        if (exponent >= 0) 
        {
            builder.Append(match.Groups["head"].Value);
            string tail = match.Groups["tail"].Value;
            
            if (exponent < tail.Length) 
            {
                builder.Append(tail, 0, exponent);
                builder.Append(formatInfo.NumberDecimalSeparator);
                builder.Append(tail, exponent, tail.Length-exponent);
            } 
            else 
            {
                builder.Append(tail);
                builder.Append('0', exponent-tail.Length);
            }
        } 
        else 
        {
            builder.Append('0');
            builder.Append(formatInfo.NumberDecimalSeparator);
            builder.Append('0', (-exponent)-1);
            builder.Append(match.Groups["head"].Value);
            builder.Append(match.Groups["tail"].Value);
        }
        
        result = builder.ToString();
    }
    
    return result;
}

 

Uploading a video

As you know Vine allows video only with resolution 480x480 and it must not exceed 6 seconds duration. So first you have to modify video in such specification if it's not. Uploading process happens in three service call.

First you have to upload the video. You have to send PUT request. The request's header will have Vine session ID. Request's content will be the video in binary format. Content type of requests would be video/mp4. The response header with key "X-Upload-Key" of PUT request will have URL of the uploaded video which you need to extract since we will need that URL in 3rd step. The endpoint, JSON response and code is given below. 

PUT https://media.vineapp.com/upload/videos/1.3.1.mp4

/// <summary>
/// Uploads video to Vine and returns Vine URL for video.
/// </summary>
public static async Task<string> UploadVineVideoAsync(StorageFile Video, string VineSessionId)
{
    try
    {
        string _VideoUrl = string.Empty;

        var binaryContent = new HttpBufferContent(await FileIO.ReadBufferAsync(Video));
        binaryContent.Headers.Add("Content-Type", "video/mp4");

        var request = new HttpRequestMessage(HttpMethod.Put, new Uri("https://media.vineapp.com/upload/videos/1.3.1.mp4"));
        request.Headers.Add("vine-session-id", VineSessionId);
        request.Content = binaryContent;

        var HttpReq = await httpClient.SendRequestAsync(request);

        if (HttpReq.IsSuccessStatusCode)
        {
            var ResonseHeader = HttpReq.Headers;
            ResonseHeader.TryGetValue("X-Upload-Key", out _VideoUrl);
        }

        return _VideoUrl; 
    }
    catch (Exception ex)
    {
        return string.Empty;
    }
}

Our second step will be to upload the thumbnail of video. Thumbnail should be also of 480x480 resolution. Its content type should be image/jpeg. The service call pattern is same as uploading video. You have to send a PUT request by setting its header with Vine session ID and content of it will have image as binary format. Success service call will return thumbnail URL in response header with key "X-Upload-Key". Extract it, which will be used in 3rd step.

PUT https://media.vineapp.com/upload/thumbs/1.3.1.mp4.jpg

/// <summary>
/// Uploads video thumbnail image to Vine and returns Vine URL for thumbnail image.
/// </summary>
public static async Task<string> UploadVineThumbAsync(StorageFile Video, string VineSessionId)
{
    try
    {
        var VideoThumb = await Video.GetThumbnailAsync(Windows.Storage.FileProperties.ThumbnailMode.SingleItem);
        Byte[] VideoThumbBytes = new Byte[VideoThumb.Size];
        var VideoThumbBuffer = VideoThumbBytes.AsBuffer();

        string _ThumbUrl = string.Empty;

        var binaryContent = new HttpBufferContent(VideoThumbBuffer);
        binaryContent.Headers.Add("Content-Type", "image/jpeg");

        var request = new HttpRequestMessage(HttpMethod.Put, new Uri("https://media.vineapp.com/upload/thumbs/1.3.1.mp4.jpg"));
        request.Headers.Add("vine-session-id", VineSessionId);
        request.Content = binaryContent;

        var HttpReq = await httpClient.SendRequestAsync(request);

        if (HttpReq.IsSuccessStatusCode)
        {
            var ResonseHeader = HttpReq.Headers;
            ResonseHeader.TryGetValue("X-Upload-Key", out _ThumbUrl);
            var ResonseStr = await HttpReq.Content.ReadAsStringAsync();
        }
        
        return _ThumbUrl; 
    }
    
    catch (Exception ex)
    {
        return string.Empty;
    }
}

Now the last service call is a POST service call which takes parameters as Video Url, Thumbnail Url, Channel ID, Vine description and entities. The POST request header will have Vine session key. The code and endpoint is given below. Please note the purpose of “entities” parameter is not yet discovered, so empty string will work with that because it’s mandatory parameter.

/// <summary>
/// Uploads Vine.
/// </summary>
public static async Task PostVineAsync(string VideoUrl, string ThumbUrl, string ChannelId, string Description, string Entities)
{
    try
    {
        httpClient.DefaultRequestHeaders.Add("vine-session-id", VineSessionId);

        var multiPartData = new HttpMultipartFormDataContent();
        multiPartData.Add(new HttpStringContent(VideoUrl), "videoUrl");
        multiPartData.Add(new HttpStringContent(ThumbUrl), "thumbnailUrl");
        multiPartData.Add(new HttpStringContent(ChannelId), "channelId");
        multiPartData.Add(new HttpStringContent(string.IsNullOrWhiteSpace(Description) ? "Default Description" : Description), "description");
        multiPartData.Add(new HttpStringContent(Entities), "entities");

        var HttpReq = await httpClient.PostAsync(new Uri("https://api.vineapp.com/posts"), multiPartData);

        if (HttpReq.IsSuccessStatusCode)
        {
            var ResonseHeader = HttpReq.Headers;
            var ResonseStr = await HttpReq.Content.ReadAsStringAsync();
            await new MessageDialog("Video uploaded successfully on Vine", "App name").ShowAsync();
        }
    }
    
    catch (Exception ex)
    {

    }
}

 

References

As I said before, I've used these two links as reference while writing the article and implementing code examples: VineAPI and Vine.app API Reference