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: 1,082
Last edited:

johnmie

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"))

Sounds great but how do I get the YTSInfo for a single video?
Would you, by chance, have a working example or a more complete code snippet available?
Thanks and best regards,
john m.
 

sentechnologies

Member
Licensed User
i used the following youtube videoid of this link to extract full streaming url
but the following error is raised
B4X:
errorcode=180&status=fail&reason=HTTP+is+not+supported.
 

drgottjr

Expert
Licensed User
Longtime User
you need https, not http
 

sentechnologies

Member
Licensed User
you need https, not http

i used https link only. and i added the code below

B4X:
sub CLV1_ItemClick(Index as Int, Value as object)
    yt.GetVideoInfo("EHUDBa4wPio")
   ' yt.GetVideoInfo("https://youtube.com/watch?v=EHUDBa4wPio")
end sub

Sub yt_ready(VideoId1 As String,YoutubeVideoInfo1 As YouTubeVideoInfo)
    Dim streams As Map
    Dim ytsi As YouTubeStreamInfo
    Dim su As StringUtils 'Require StringUtils library
    If YoutubeVideoInfo1 = Null Then
        ToastMessageShow("Failed to fetch video...",True)
        Log(yt.GetLastError(VideoId1))
    Else
        streams.Put(YoutubeVideoInfo1.Title,YoutubeVideoInfo1.Streams)
        ytsi = streams.GetValueAt(1)
        Log("Stream Link:" & su.DecodeUrl(ytsi.StreamUrl,"ISO-8859-1"))
        ExoPlay(su.DecodeUrl(ytsi.StreamUrl,"ISO-8859-1"))
    End If
End Sub

error received
B4X:
reason=HTTP+is+not+supported.&errorcode=180&status=fail
 
Last edited:

drgottjr

Expert
Licensed User
Longtime User
it would seem that somewhere in the pipeline someone is using http. (it is also possible - but unlikely - that the reason is bogus.)
do you know exactly what is in YoutubeVideoInfo? could there be a reference to http?
 

DonManfred

Expert
Licensed User
Longtime User
This is a 8 Years old thread.

I don´t think it can be used today. Youtube has changed the system. Search the forum for similar issues. I can remember seeing such threads in the past.
 
Top