B4A Library YouTubeStreamFinder

YouTubeStreamFinder

Background
You Tube have a public Data API that allows you to pass a You Tube video id and get a streaming video URL that you can use to play a video in a VideoView.
Sounds Ok BUT they only make the URLs to low quality 3GP video streams avaialble.
You Tube have no official API to get URLs to the higher quality video streams.
Low quality 3GP dates back to the era of 2.5G connections and low power, low resolution handsets - it's 2012 and we have HSDPA, powerful handsets and high resolution displays - 3GP sucks big time!!

There exists an open source android-youtube-player which i was originally going to turn into a B4A library.
The android-youtube-player is actually an entire Activity that you'd add to your B4A (or native Android) project and call using an Intent.
The Intent would identify the video id to play.
android-youtube-player is a full screen landscape mode only Activity - you could not embed it within a B4A Activity as you can a VideoView.

android-youtube-player does however contain code which retrieves all available stream URLs for a video so what i have done is to to identify how the android-youtube-player gets these URLs and create the YouTubeStreamFinder library.

android-youtube-player is licensed under the Apache License, Version 2.0 so no problems there.
I think i'm obliged to make the library code publicly available so the library source code can be found here: Index of /b4a/youtubestreamfinder/src

Technical stuff!

YouTubeStreamFinder is not an Activity object, you should be able to use it as a Process Global with no problems.

The library adds three objects to the B4A IDE: YouTubeStreamFinder, YouTubeVideoInfo and YouTubeStreamInfo.

YouTubeStreamFinder is used to retrieve info about one or more videos using it's GetVideoInfo or GetVideoInfo2 methods.
Retrieval is done in a thread in the library - no blocking of the UI thread should occur.

When video info has been retrieved (or an error has occurred trying to retrieve video info) the YouTubeStreamFinder Ready event will be raised and passed the video id and a YouTubeVideoInfo object if retrieval was successful.

Version: 1.3
  • YouTubeStreamFinder
    Events:
    • Ready (VideoId1 As String, YouTubeVideoInfo1 As YouTubeVideoInfo)
    Methods:
    • GetLastError (VideoId As String) As String
      Get the last error associated with this VideoId.
    • GetVideoInfo (VideoId As String)
      Get VideoInfo for a single video.
      The Ready(VideoId1 As String, YouTubeVideoInfo1 As YouTubeVideoInfo) event
      will be raised when VideoInfo is available or an error occurs.
    • GetVideoInfo2 (VideoIds() As String)
      Get VideoInfo for more than one video.
      The Ready(VideoId1 As String, YouTubeVideoInfo1 As YouTubeVideoInfo) event
      will be raised for each video when VideoInfo is available or an error occurs.
    • Initialize (EventName As String)
      Initialize the YouTubeStreamFinder with an EventName.
    Permissions:
    • android.permission.INTERNET
  • YouTubeStreamInfo
    Properties:
    • FormatId As Int [read only]
    • Height As Int [read only]
    • StreamUrl As String [read only]
    • Width As Int [read only]
  • YouTubeVideoInfo
    Properties:
    • Author As String [read only]
    • Duration As Int [read only]
      Get the duration of the video in seconds.
    • Keywords() As String [read only]
    • Streams As Map [read only]
      Get a Map where the Map keys are Int FormatIds and
      the Map values are YouTubeStreamInfo objects.
    • ThumbnailUrl As String [read only]
    • Timestamp As Long [read only]
      Get the date this video was uploaded to You Tube as a PHP timestamp.
    • Title As String [read only]
    • VideoId As String [read only]

Some demo code:

B4X:
'Activity module
Sub Process_Globals
End Sub

Sub Globals
   Dim SelectedVideoTitle As String
   Dim Streams As Map
   Dim VideoStreamSpinner As Spinner
   Dim VideoTitleSpinner As Spinner
   Dim VideoView1 As VideoView
   Dim YouTubeStreamFinder1 As YouTubeStreamFinder
End Sub

Sub Activity_Create(FirstTime As Boolean)
   Activity.LoadLayout("Main")
   
   Streams.Initialize
   VideoView1.Initialize("VideoView1")
   VideoView1.MediaControllerEnabled=True
   Activity.AddView(VideoView1, 0, VideoTitleSpinner.Height, 100%x, 100%y-VideoTitleSpinner.Height)
   
   '   Initialize the YouTubeStreamFinder with an EventName
   YouTubeStreamFinder1.Initialize("YouTubeStreamFinder1")
   
   '   get video info for three videos, these Strings are the YouTube video ids
   YouTubeStreamFinder1.GetVideoInfo2(Array As String("t56cHZymmvs", "0ZFhjLw6f_k", "HfT7cPI_mRc"))
   
   ToastMessageShow("Waiting for YouTubeStreamFinder", False)
End Sub

Sub Activity_Resume
End Sub

Sub Activity_Pause (UserClosed As Boolean)
End Sub

Sub VideoTitleSpinner_ItemClick (Position As Int, Value As Object)
   '   the user has selected a video by it's title
   '   populate the VideoStreamSpinner with available streams for the selected video
   
   If VideoView1.IsPlaying Then
      VideoView1.Stop
   End If
   
   SelectedVideoTitle=Value
   Activity.Title=SelectedVideoTitle
   Dim SelectedVideoStreams As Map
   SelectedVideoStreams=Streams.Get(SelectedVideoTitle)
   
   Dim YouTubeStreamInfo1 As YouTubeStreamInfo
   
   VideoStreamSpinner.Clear
   
   Dim i As Int
   For i=0 To SelectedVideoStreams.Size-1
      YouTubeStreamInfo1=SelectedVideoStreams.GetValueAt(i)
      VideoStreamSpinner.Add(YouTubeStreamInfo1.Width&" x "&YouTubeStreamInfo1.Height&" ("&YouTubeStreamInfo1.FormatId&")")
   Next
   
   ToastMessageShow("Now select video stream, 18 is recommended", False)
End Sub

Sub VideoStreamSpinner_ItemClick (Position As Int, Value As Object)

   '   the user has selected a video quality stream - try to play the selected stream

   '   note that playback of the selected quality/format varies according to the device
   '   quality/format 18 seems to work on all devices

   Dim SelectedVideoStreams As Map
   SelectedVideoStreams=Streams.Get(SelectedVideoTitle)
   
   Dim YouTubeStreamInfo1 As YouTubeStreamInfo
   YouTubeStreamInfo1=SelectedVideoStreams.GetValueAt(Position)
   
'   uncomment if desired
'   VideoView1.Height=YouTubeStreamInfo1.Height*1dip
'   VideoView1.Width=YouTubeStreamInfo1.Width*1dip

   VideoView1.LoadVideo("http", YouTubeStreamInfo1.StreamUrl)
   VideoView1.Play
End Sub

Sub YouTubeStreamFinder1_Ready(VideoId1 As String, YouTubeVideoInfo1 As YouTubeVideoInfo)
   '   video info retrieval for a single video has completed
   If YouTubeVideoInfo1=Null Then
      '   YouTubeVideoInfo1 will be Null if an error has occurred
      ToastMessageShow("Failed to find streams for "&VideoId1, False)
      Log(YouTubeStreamFinder1.GetLastError(VideoId1))
   Else
      '   save the Streams property as a global so we can use it when a Spinner item is clicked
      Streams.Put(YouTubeVideoInfo1.Title, YouTubeVideoInfo1.Streams)
      '   add the video title to the VideoTitleSpinner
      VideoTitleSpinner.Add(YouTubeVideoInfo1.Title)
   End If
End Sub

I pass an Array of 3 video ids to the YouTubeStreamFinder GetVideoInfo2 method and the Sub YouTubeStreamFinder1_Ready is called each time YouTubeStreamFinder has finished requesting video info for each of these 3 videos.
VideoId1 passed to that Sub identifies which video the video info request has completed for and if the request was a success then YouTubeVideoInfo1 will contain the requested info.

Different videos will have different available streams and playback support for each available streams seems to vary widely depending on the device in use.
The stream with the FormatId of 18 seems to be available for all videos and also seems to be supported on all devices - but your mileage may vary lol!

This library makes use of an undocumented You Tube web service, i think this web service is used by the official Android You Tube application so it is unlikely to change or stop working as that would break all current versions of the Android You Tube application.
But the web service is an undocumented web service not intended for developer access so there is always a possibility that at some point in the future You Tube will make changes to the web service and the YouTubeStreamFinder library will then be broken.


Library and demo project attached.

Martin.
 

Attachments

  • YouTubeStreamFinder_v_1_30.zip
    15.4 KB · Views: 993
Last edited:

warwound

Expert
Licensed User
Longtime User
Hello Martin, I have been using your Youtubestreamfinder for a long time in combination with VideoView.
In order to avoid the "This video cannot be played" msgbox in vv, I used to check whether the actual Youtube stream contains the words "signature=null". If so, I would simply keep on searching until a playable video (i.e. without such signature) could be found. However, the last couple of days this trick does not work anymore.
Do you know of a way to avoid this msgbox? The users of my app are blind and thus cannot click on an OK button. Thanks!

No i can't think of any alternative way to detect whether a video is playable.
If You Tube have made changes to the web service that this library uses then that may be the end of being able to tell if a video is playable - the web service was never an official web service for developers to use, so You Tube have no obligations to maintain it or maintain any of it's functionality :(.

Martin.
 

itgirl

Active Member
Licensed User
Longtime User
Hello and thanks for this great Lib , but I'm guessing there's a problem with the timestamp of video , it doesnt return the actual timestamp of the video
 

sergiones

Member
Licensed User
Longtime User
Hi guys. I can't believe, I'm working on a robot that extract the links from youtube for a month. Basic4Android rocks, it's unbelievable. This guys has solution for everything. Ok, I want to share about what I learned from my robots. Some videos can be played even without signature. At first, signature was one of my required parameters, but I found that some videos works without a signature.

Please note that my intention is to play 3gp videos only. I'm from Brazil and the 3g connections big s#cks. Believe me, it's a dial up connection. So my intention is to parse and isolate the 3gp from youtube.com response, what I have done sucessfully.

I.M.O, the "sorry, this video cannot be played" is not a missing parameter from the stream request, but the codec used to create the video before the upload. I realized that the videos that shows "sorry, this video cannot be played", cannot be played even on youtube at 144p. They are the same, so sometimes Youtube display the 144p resolution option, but if you click on it nothing happens, the resolution keeps 240 or more. Because Youtube automatically tests the 144p and get empty, it changes to 240p or more. My robots cannot identify if the video is playable or not, so the "sorry, this video cannot be played" is inevitable.

I would say that it's not a missing parameter since I have test thousands of links and there is no co-relation between parameters. Hope this helps some way. If I can help in something just let me know.
 

Attachments

  • Caraca.jpg
    Caraca.jpg
    309 KB · Views: 215

warwound

Expert
Licensed User
Longtime User
warwound, why in most of searcher is coming: Failed to find streams for.... ???

To be honest i don't know!
I don't use this library - i created it some time ago for other forum members but have no experience of using it.
Hopefully another member can answer your question.

Martin.
 

itgirl

Active Member
Licensed User
Longtime User
To be honest i don't know!
I don't use this library - i created it some time ago for other forum members but have no experience of using it.
Hopefully another member can answer your question.

Martin.
Well it's a really nice Lib and can be upgraded to get alot more infos like :
publishedAt
description
channelTitle
liveBroadcastContent

i really hope you make an update for this nice Lib or maybe you can give us the Lib code which be much nicer lol :)
 

sergiones

Member
Licensed User
Longtime User
É Alberto, foi o primeiro nome que me apareceu na cabeça quando percebi o tamanho da minha área de trabalho, hehe. Esse pessoal do Basic4Android é muito bom. Não lembro de ver uma linguagem eficiente (até mesmo pelo tamanho do apk) e estável há décadas.
 

warwound

Expert
Licensed User
Longtime User
i really hope you make an update for this nice Lib or maybe you can give us the Lib code which be much nicer lol :)

I won't have time to look at updating the library for a week or more - got lots of work to get done!
If you want a copy of the Eclipse library project so you can look at updating it yourself just let me know.

Martin.
 

itgirl

Active Member
Licensed User
Longtime User
I won't have time to look at updating the library for a week or more - got lots of work to get done!
If you want a copy of the Eclipse library project so you can look at updating it yourself just let me know.

Martin.

That would be amazing and really helpful :) , I would love a copy of the Lib source... i really appreciate it and thanks million times
 

sergiones

Member
Licensed User
Longtime User
I won't have time to look at updating the library for a week or more - got lots of work to get done!
If you want a copy of the Eclipse library project so you can look at updating it yourself just let me know.

Martin.

Guys, I think it's not a lib issue. As I said early I did a robot for that and I'm having no response for some streams. I.M.O. the problem is the codec used to build the video file, before the upload. Take this example: Search youtube for

Relaxin' with the Miles Davis Quintet (Full Album)

I can get the HD stream, but cannot the 144p. Aparently the link for 144p exists, but it returns nothing (I mean, a clean http body response). If you send this link to a action_view you will receive the famous "Sorry, this video cannot be played". This lib may check the link before playing and gives you "Failed to find streams for", what is good because you can manage the error by yourself. If I'm wrong, please let me know.
 

Alberto Iglesias

Well-Known Member
Licensed User
Longtime User
Warwound,

I don´t know why some times the library can not find the stream.

Inside the library you use this GOOGLE API?

https://gdata.youtube.com/feeds/api/videos?q=android&orderby=published&alt=json&max-results=10

Look this example is not working:
YouTubeStreamFinder1.GetVideoInfo("ng9JuX2YuAk")


I think is the same, like this call: https : // www.youtube.com/watch?v=ng9JuX2YuAk

In the "media$content" you get the URL, like the sample below no?
"url": "rtsp://r1---sn-cg07luek.c.youtube.com/CiILENy73wIaGQliv9r4JpOXkRMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp"
 

warwound

Expert
Licensed User
Longtime User
@Alberto Iglesias

No the YouTueStreamFinder connects to an undocumented web service:

B4X:
private static final String YOUTUBE_VIDEO_INFORMATION_URL = "http://www.youtube.com/get_video_info?asv=3&el=detailpage&video_id=";

Try appending a 'video id' to that URL and paste it into a browser - what do you get?

Martin.
 

Alberto Iglesias

Well-Known Member
Licensed User
Longtime User
I get this file (attachment) but I see the almost same information, but scrambled

What the advantage in use this undocumented if the documented api I get the streaming url? like this

rtsp://r1---sn-cg07luek.c.youtube.com/CiILENy73wIaGQliv9r4JpOXkRMYDSANFEgGUgZ2aWRlb3MM/0/0/0/video.3gp

?
 

Attachments

  • get_video_info.zip
    4.1 KB · Views: 211

warwound

Expert
Licensed User
Longtime User
@Alberto Iglesias

When i first created the YouTubeStreamFinder, the official web service only provided URLs to low quality (think of small screen low bitrate .3GP) video.
Re-read post #1 in this thread:
Background
You Tube have a public Data API that allows you to pass a You Tube video id and get a streaming video URL that you can use to play a video in a VideoView.
Sounds Ok BUT they only make the URLs to low quality 3GP video streams avaialble.
You Tube have no official API to get URLs to the higher quality video streams.
So i'd guess that since that post (June 2012) the official You Tube web service has been updated and now supports high quality video - or does that link in your last post still only get you a low quality video?

Martin.
 

sergiones

Member
Licensed User
Longtime User
Sergiones, Utilizo a B4A faz tempo e é exepcional... Tanto é que estou trabalhando dentro da Citroen na Espanha por causa dela, e estou, pouco a pouco mostrando ao povo aqui o quão eficiente e tão melhor que programar em java diteramente.

Fantástico!

Se precisar de algo, dá um toque!

Abs.

Legal Alberto, parabéns pelo sucesso! Eu também tentei algumas outras linguagens como xamarim e sua antiga versão monodevelopment além do próprio java. Mas nada se comparou à facilidade, estabilidade e compactação do basic4android. Obrigado pela força, o que eu puder ajudar também pode contar comigo. Abraço!
 

KZero

Active Member
Licensed User
Longtime User
Just if anyone want to still use this great library

this library working with older android versions and always fail on the newer versions

to make it work with the newer versions you should decode the Stream url


B4X:
Dim su As StringUtils 'Require StringUtils library
VV.LoadVideo("http",su.DecodeUrl(YTSInfo.StreamUrl,"ISO-8859-1"))
 
Top