Add Video Thumbnails in your apps

While a majority of the apps that are being made and will continue to be made are templates based on social media like features, the could be a requirement that it will have to display a video thumbnail. This is not difficult because at the end of the day they are simply images that represent the video. If you look at how WordPress manages this, each time an image is uploaded to WordPress, it generates a couple of images in certain resolutions as required. So if your app talks to your own server, and you have control over the media creation, you can create the thumbnails yourself and all done. However if the videos are hosted on YouTube and all you are required to do is display the thumbnail and then play the video when tapped, gets a bit interesting. One of my Android devs got his hands on a library and he would download the video and display the thumbnail generated from the video. Which according to me was absolutely NOT a good idea. Imagine a list of 10 videos of 2 MB each, you are downloading 20 MB of videos just to generate the thumbnails. Plus he wasn’t caching them, which if he did had its own set of issues and if he didn’t is another problem. Not to mention the speed issues with generating the thumbnails, the user could have changed their minds by the time the videos are downloaded and thumbnails generated. So, is there a better way? and II mentioned Android developer, what about the iOS/Swift?

Issue

First let us revisit the issue at hand to get some ideas on how to resolve them.
1. This was about 2 years ago, the client wanted to display the video in the app we were building for them.
2. The data was passed via a CMS system, so they could change the link and we could display the video accordingly
3. The client side contact had poor IT skills (as the case usually is) so they could just point to the YouTube video
4. YouTube has several links to the same video depending on how and where you are viewing the same

Quick and Dirty Solution

I say quick and dirty solution because the project came to my portfolio with a deadline that was already past the due dates. This had larger concerns where there could be legal proceedings. Having a record for delivery, this was one I did not want getting away from me and staining my reputation. While there were many moving parts to be managed at the same time, one of the parts was displaying video, which is not such a difficult thing. However, if there was nothing to start with, delivering something is equally difficult.

One critical issue here was the language of choice for this app was Swift, and you can imagine in 2015 when Swift was like a Tsunami, would have you literally wipe your code due to the changes every time there was an update. There were not many libraries that we wanted to integrate or use. So everything had to be done within the code and by us, AND no Objective-C code, using the Runtime and NSObjects was fine as Swift did not have good interoperability with the underlying Objective-C layer, but no bridges, etc.

So we decided to go with letting iOS play the video natively using a webView, wasn’t the best solution but it would work and save us the hassle of building a video player. Given when the resources were required to focus on other portions that required work to be done. This was easy, pass the YouTube link to webView and the video is managed by the app. However, there was another issue, the url passed would display the youTube page with the related videos instead of just the video. On a desktop you hardly notice and bother, but on a mobile, that is not the experience you want. Specially when the client wants to play the speech of their chairman/CEO and it shows related videos of things unrelated and at times embarrassing.

Thumbnails

The video was to be displayed via tapping a view or a tableViewCell, and these needed to have the thumbnail, otherwise the user would not know that it was a video and would provide no visual cognizance. Which brought us back tot he same issue, how to get a thumbnail, do we have to download the video, parse a frame and generate a thumbnail from it?

Luckily, YouTube is part of Google and they have a good API that you can explore. Given the timing and the impeding swords of doom hanging over our necks, spending time to understand how the Google API’s work was not an option. What we wanted was a quick way to get the video thumbnail. At this time I had to decide how to resolve this issue. I had used AVMediaPlayer and AVFoundation API’s to play video but you could not play YouTube videos, and there was an Objective-C library that could play the video from a given YouTube video. This to me was a faster way to manage things than reading Google’s API.

Learning

Amongst the many things that this library highlighted about YouTube, it shone a spotlight on the various URL’s that YouTube uses.

 youtu.be/ --> A URL shortner to make the links shorter
 www.youtube.com/embed/ --> A way to embed the video in your website
 youtube.googleapis.com --> The YouTube Google API's to access videos and related information
 www.youtube.com/watch?v= --> The normal YouTube link that has the video link

This helped resolve one more issue that I mentioned above, the embed scheme was useful to play full screen videos, it removed all the related videos and other UI fluff that you would not want.

Getting the Thumbnails

We have gone through a lot of things related to why I had to undergo this, but if you were here to just get that code on how to display a thumbnail, then let’s get to that, how to get the thumbnail.

YouTube in a similar fashion to WordPress generates thumbnails based on the size and resolution of the videos and offers all of these via a simple url. All you need to do is pass it the Video ID and it returns an image back for your use.

These thumbnails are accessible via the url http://i.ytimg.com/vi/ however just that url will not work for you as is, you have to choose one of the options from the following to get the image

 default.jpg       --> returns a default image of size (120x90)
 sddefault.jpg     --> returns a standard image of size (640x480)
 mqdefault.jpg     --> returns a medium sized image (320x180)
 hqdefault.jpg     --> returns an image with a higher resolution of size (480x360)
 maxresdefault.jpg --> returns a HD version image of size (1280x720) 
                       [If the video is low res, it might not provide this thumbnail]

Note: There are other aliases to the same URL which can be found at

img.youtube.com/vi/
i3.ytimg.com.vi/
i.ytimg.com/vi/

There is yet another aspect to the YouTube APIs, it also provides 4 images as follows

 0.jpg
 1.jpg
 2.jpg
 3.jpg

of which, 0.jpg is the thumbnail for that video (480×360) and the rest are small sized thumbnails (120×90) that you would have seen when you hover over the seek bar of the video.

Now you have several ways and several image sizes to choose from to create a thumbnail. Personally, since we are dealing with a retina device we settled on using the default size, which is returned by using the 0.jpg

Putting it all together

Now when we got a YouTube link, all we had to do was extract the Video_ID and that would then give us the image to the thumbnail and also allow us to create an embedded link to the video to pass to a webView.

The approach,
When this code was written, it was swift 2.1 (I think) and it was fun exploring what you could do with extensions. So the entire code was written as a String extension so the developer using it would simply create a string with the video id as

let videoID = "H3LLOqPMJZWw"

and from there get the thumbnail as

let imageURL = videoID.thumbnailImage.url
let videoURL = videoID.youtubeVideo.url
let isValid = videoID.isValidURL
let customURL = videoID.addBaseURL("SOME OTHER URL")

There were other extensions added to URL and UIImageView to automate the loading of the image and handling the URL parameters, etc.

In hindsight, this would make more sense as a structure that could store the VideoID and have the relevant functions to transform, load, display the image accordingly.

I am posting some of the code to highlight the technique,

Results

In case you are still reading and wanting closure on what happened to the project and how did it finally turn out? It was not very helpful that there was immense pressure of deadlines and the fact that my iOS developers were not very proficient with Swift. So it was a two way fight, one against time and the second to train and get my devs up to speed with Swift and the idea otherwise I would be bogged down to writing this and they would end up doing nothing. In addition to that, the timing of all this was such that I had to be away the week the deadline expired. Then it had to undergo a whole lot of other testing as required by the client. A 3rd party penetration test, etc.

This just provided us with a thumbnail, we still had the issue of adding the play button on top of this thumbnail to indicate a playable video. Though if we had time, we could have worked on custom overlay on the video but given that the client was very traditional in the sense that they did not want any deviation from the spec they signed on, it was not worth adding any functionality and allocating time and resources to that. So adding an image of a play button was overlaid on the thumbnail setting the center of the overlay image to the thumbnail image and done.

Was that the end of it? No, there were many other issues that reared their head and caused issues, the short of the long is we got the app completed and submitted to the client. My main dev on the project was slogging with barely able to make any progress as I believe he had frozen by the stress and the other dev was engaged in something that was unbelievable. He was changing the middle-ware API to his fancy thereby breaking the already written and tested code. Without going into the details, at one time it seemed that this was it, there was no way out of this predicament specially with the Project Manager and his non-technical decisions in consultation with this developer was messing the project further. I had some time-off pre-booked, and when I returned it was in a worse shape than it was when I left. They butchered the project further, this Project Manager was asked to leave and the other developer asked NOT to touch the backend or the code for this project. Had to liaise with the client, communicate get an extension and get the app released to the app store for all platforms, iPad, iPhone and android. Thereby averting some legalities that would not have been good for the business at that time.

In hindsight, the time the project took was enough to deliver a normal project, however it was the involvement of people with less than acceptable technical skills or knowing the impacts of their action on the overall project. The app when released worked well, looked quite polished and for a client that is a Tier 1 business in Australia.

The Code – Finally

The code has been fixed to work with Swift 3.0 and is to highlight what we discussed int eh article. Some other functions are not included like the dictionaryForQueryString, it simply iterated through the query parameters and splits them into key value pairs and returns this dictionary. So from the url http://www.youtube.com/watch?v=VIDEOID you get the returned dictionary of [“v”: VIDEOID] ( a dictionary object). I created two functions, one to get all of the query parameters passed, and the other to get a specific value as in code above. So simply create the one you are interested in. I am not including that code in here for now.

extension String {
    
    var url:URL?{
        return URL(string: self)
    }
    
    var thumbnailImage:String {
        return "http://img.youtube.com/vi/\(self)/0.jpg"
    }
    
    var youtubeVideo: String {
        return "http://www.youtube.com/embed/\(self)"
    }
    
    var getIDfromLink:String? {
        var result:String? = nil
        
        if let theURL = self.url {
            if theURL.host == "youtu.be" {
                result = theURL.pathComponents[1]
            } else if theURL.absoluteString.contains("www.youtube.com/embed") {
                result = theURL.pathComponents[2]
            } else if theURL.host == "youtube.googleapis.com" ||
                theURL.pathComponents.first == "www.youtube.com" {
                result = theURL.pathComponents[2]
            } else {
                let _res = theURL.dictionaryForQueryString("v")
                result = _res["v"] as? String
            }
        }
        
        return result
    }
    
    var isValidURL:Bool {
        if let _url = self.url {
            return _url.scheme != ""
        }
        return false
    }
    
    func addBaseURL(_ theURL:String) -> String {
    if self.isValidURL { return self }
    
    // No check for now, just prepending the base url as passed
    return theURL + self
    }
    
    var stringByDecodingURL:String {
        let result = self
            .replacingOccurrences(of: "+", with: " ")
            .removingPercentEncoding
        return result!
    }
}

Summary

This is not the best way to deal with the issue, however this solved the problem of getting a thumbnail and displaying it. Maybe when this is modeled into a structure to manage the video better, the structure and functions could be better. For another project, we had a list of video feeds that were displayed in the tableView, another article might cover how it was handled, the issues that came with loading these thumbnails asynchronously and how we got it to work for us including things that we could have done better (in hindsight).

Views All Time
Views All Time
5736
Views Today
Views Today
1
Posted in Article, Tip, Trench Notes.