49

I am trying to play youtube video in exoplayer but here is some confusion I don't know what is DASH url, I have only real youtube url like "https://www.youtube.com/watch?v=v1uyQZNg2vE" , I have no idea how to generate dash url form real url.

Dash Url:

new Sample("Google Glass",
        "http://www.youtube.com/api/manifest/dash/id/bf5bb2419360daf1/source/youtube?"
        + "as=fmp4_audio_clear,fmp4_sd_hd_clear&sparams=ip,ipbits,expire,as&ip=0.0.0.0&"
        + "ipbits=0&expire=19000000000&signature=255F6B3C07C753C88708C07EA31B7A1A10703C8D."
        + "2D6A28B21F921D0B245CDCF36F7EB54A2B5ABFC2&key=ik0", DemoUtil.TYPE_DASH),

Real Url :

 https://www.youtube.com/watch?v=v1uyQZNg2vE
ppreetikaa
  • 1,149
  • 2
  • 15
  • 22
Amit Prajapati
  • 13,525
  • 8
  • 62
  • 84

5 Answers5

14

I've written a class that retrieves actual YouTube video streaming URL for format like DASH and HLS using http://www.youtube.com/get_video_info?&video_id=[video_id]&el=info&ps=default&eurl=&gl=US&hl=en url with video ID as described by Karim Abdell Salam. I have also tested the URL in an app that uses ExoPlayer and it works:

import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.ProtocolException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Map;
import java.util.TreeMap;

/**
 * Represents youtube video information retriever.
 */
public class YouTubeVideoInfoRetriever
{
    private static final String URL_YOUTUBE_GET_VIDEO_INFO = "http://www.youtube.com/get_video_info?&video_id=";

    public static final String KEY_DASH_VIDEO = "dashmpd";
    public static final String KEY_HLS_VIDEO = "hlsvp";

    private TreeMap<String, String> kvpList = new TreeMap<>();

    public void retrieve(String videoId) throws IOException
    {
        String targetUrl = URL_YOUTUBE_GET_VIDEO_INFO + videoId+"&el=info&ps=default&eurl=&gl=US&hl=en";
        SimpleHttpClient client = new SimpleHttpClient();
        String output = client.execute(targetUrl, SimpleHttpClient.HTTP_GET, SimpleHttpClient.DEFAULT_TIMEOUT);
        parse(output);
    }

    public String getInfo(String key)
    {
        return kvpList.get(key);
    }

    public void printAll()
    {
        System.out.println("TOTAL VARIABLES=" + kvpList.size());

        for(Map.Entry<String, String> entry : kvpList.entrySet())
        {
            System.out.print( "" + entry.getKey() + "=");
            System.out.println("" + entry.getValue() + "");
        }
    }

    private void parse(String data) throws UnsupportedEncodingException
    {
        String[] splits = data.split("&");
        String kvpStr = "";

        if(splits.length < 1)
        {
            return;
        }

        kvpList.clear();

        for(int i = 0; i < splits.length; ++i)
        {
            kvpStr = splits[i];

            try
            {
                // Data is encoded multiple times
                kvpStr = URLDecoder.decode(kvpStr, SimpleHttpClient.ENCODING_UTF_8);
                kvpStr = URLDecoder.decode(kvpStr, SimpleHttpClient.ENCODING_UTF_8);

                String[] kvpSplits = kvpStr.split("=", 2);

                if(kvpSplits.length == 2)
                {
                    kvpList.put(kvpSplits[0], kvpSplits[1]);
                }
                else if(kvpSplits.length == 1)
                {
                    kvpList.put(kvpSplits[0], "");
                }
            }
            catch (UnsupportedEncodingException ex)
            {
                throw ex;
            }
        }
    }

    public static class SimpleHttpClient
    {
        public static final String ENCODING_UTF_8 = "UTF-8";
        public static final int DEFAULT_TIMEOUT = 10000;

        public static final String HTTP_GET = "GET";

        public String execute(String urlStr, String httpMethod, int timeout) throws IOException
        {
            URL url = null;
            HttpURLConnection conn = null;
            InputStream inStream = null;
            OutputStream outStream = null;
            String response = null;

            try
            {
                url = new URL(urlStr);
                conn = (HttpURLConnection) url.openConnection();
                conn.setConnectTimeout(timeout);
                conn.setRequestMethod(httpMethod);

                inStream = new BufferedInputStream(conn.getInputStream());
                response = getInput(inStream);
            }
            finally
            {
                if(conn != null && conn.getErrorStream() != null)
                {
                    String errorResponse = " : ";
                    errorResponse = errorResponse + getInput(conn.getErrorStream());
                    response = response + errorResponse;
                }

                if (conn != null)
                {
                    conn.disconnect();
                }
            }

            return response;
        }

        private String getInput(InputStream in) throws IOException
        {
            StringBuilder sb = new StringBuilder(8192);
            byte[] b = new byte[1024];
            int bytesRead = 0;

            while (true)
            {
                bytesRead = in.read(b);
                if (bytesRead < 0)
                {
                    break;
                }
                String s = new String(b, 0, bytesRead, ENCODING_UTF_8);
                sb.append(s);
            }

            return sb.toString();
        }

    }
}

Here is the test code:

public static void main(String[] args)
{
    String youTubeVideoID = "v1uyQZNg2vE";

    YouTubeVideoInfoRetriever retriever = new YouTubeVideoInfoRetriever();

    try
    {
        retriever.retrieve(youTubeVideoID);
        System.out.println(retriever.getInfo(YouTubeVideoInfoRetriever.KEY_DASH_VIDEO));
    }
    catch (IOException e)
    {
        e.printStackTrace();
    }
}
Karim Abdell Salam
  • 706
  • 1
  • 6
  • 15
MARK002-MAB
  • 772
  • 8
  • 18
  • Great answer! Thank you! – user486312 Mar 13 '17 at 18:57
  • 1
    @MARK002-MAB, i tried above code its work only temporary.its fail after sometime – Magesh Pandian Apr 19 '17 at 13:27
  • @MageshPandian Yes. Google changed something from my last test. Url now client IP bound (you cannot use the retrieved url from another IP address) and other things are set that makes video to run only few seconds and then stop. Since, these URLs are private, they are not consistent. URLs are now different than when I created an app using ExoPlayer lib. – MARK002-MAB Apr 24 '17 at 09:21
  • 2
    @MARK002-MAB Thank you for your reply, i have youtube url , how can i play in exoplayer – Magesh Pandian Apr 25 '17 at 03:16
  • @MageshPandian Use it in ExoPlayer sample app. They have hardcoded links last I checked. Just add yours. – MARK002-MAB Mar 23 '18 at 14:45
  • 2
    Would this violate youtube terms of service and cause my app to be pulled from the play store if i used this method? – MichaelStoddart Apr 27 '19 at 10:25
  • 2
    @MichaelStoddart Yes. It is a possibility. I read somewhere that using methods other than what was provided can be a violation. It applies to all Google service and APIs. – MARK002-MAB Apr 27 '19 at 10:29
11

I had the same issue but i finally found the simplest solution and its working so good

  1. First you need to call this url..

    HTTP GET: https://www.youtube.com/get_video_info?&video_id=[video_id]&el=info&ps=default&eurl=&gl=US&hl=en

and don't forget to change the last id with the target one.

  1. now you will get notice to download a file called get_video_info with no extesion.
  2. try to open this file using notepad and so.
  3. Now you have the right data but you can't read it because its encoded You need HTML decoder to reed this data use this one: http://meyerweb.com/eric/tools/dencoder/

-just paste your data and press decode a several times to ensure it decoded well

finally search for a key called dashmpd

and enjoy ur URL

Or use this Simple Solution

private void extractYoutubeUrl() {
    @SuppressLint("StaticFieldLeak") YouTubeExtractor mExtractor = new YouTubeExtractor(this) {
        @Override
        protected void onExtractionComplete(SparseArray<YtFile> sparseArray, VideoMeta videoMeta) {
            if (sparseArray != null) {
                playVideo(sparseArray.get(17).getUrl());
            }
        }
    };
    mExtractor.extract(mYoutubeLink, true, true);

implementation 'com.github.HaarigerHarald:android-youtubeExtractor:v1.7.0'
Karim Abdell Salam
  • 706
  • 1
  • 6
  • 15
6

You will have to get the HTTP response from the youtube URL (in your case real URL) and then search for the section "url_encoded_fmt_stream_map". In that section you will get a URI which needs to be decoded twice to get the DASH URL you are looking for.

josliber
  • 43,891
  • 12
  • 98
  • 133
GAG'sB
  • 109
  • 1
  • 6
5

to play youtube video in exoplayer we can use this library

https://github.com/HaarigerHarald/android-youtubeExtractor

and simply get the url like this and then play in exoplyer

String youtubeLink = "http://youtube.com/watch?v=xxxx";

new YouTubeExtractor(this) {
    @Override
    public void onExtractionComplete(SparseArray<YtFile> ytFiles, VideoMeta vMeta) {
        if (ytFiles != null) {
            int itag = 22;
        String downloadUrl = ytFiles.get(itag).getUrl();
        }
    }
}.extract(youtubeLink, true, true);
sajad abbasi
  • 1,988
  • 2
  • 22
  • 43
5

That is the library that was used

implementation 'com.github.HaarigerHarald:android-youtubeExtractor:master-SNAPSHOT'

That is base url add to them video id

val YouTubeBase="https://www.youtube.com/watch?v="

that is library used I will use a loop to get all video quality (420,720,180) because if the video doesn't contain the 1080 not work with u so that I will loop for all quality but if it found more quality it will choose best quality, u can edit this by rverse array called iTags if u want to get low quality

    object : YouTubeExtractor(requireContext()) {
        override fun onExtractionComplete(
            ytFiles: SparseArray<YtFile>?,
            videoMeta: VideoMeta?
        ) {
            if (ytFiles != null) {

                val iTag = 137//tag of video 1080
                val audioTag = 140 //tag m4a audio
                // 720, 1080, 480
                var videoUrl = ""
                val iTags: List<Int> = listOf(22, 137, 18)
                for (i in iTags) {
                    val ytFile = ytFiles.get(i)
                    if (ytFile != null) {
                        val downloadUrl = ytFile.url
                        if (downloadUrl != null && downloadUrl.isNotEmpty()) {
                            videoUrl = downloadUrl
                        }
                    }
                }
                if (videoUrl == "")
                    videoUrl = ytFiles[iTag].url
                val audioUrl = ytFiles[audioTag].url
                val audioSource: MediaSource = ProgressiveMediaSource
                    .Factory(DefaultHttpDataSource.Factory())
                    .createMediaSource(MediaItem.fromUri(audioUrl))
                val videoSource: MediaSource = ProgressiveMediaSource
                    .Factory(DefaultHttpDataSource.Factory())
                    .createMediaSource(MediaItem.fromUri(videoUrl))
                player?.setMediaSource(
                    MergingMediaSource(true, videoSource, audioSource), true
                )
            }
        }

    }.extract(youtubeLink)

That is my code in the last project i hope to help u in used library with Exo player

package com.hamdy.showtime.ui.ui.video_player

import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.exoplayer2.ExoPlayer
import com.hamdy.showtime.databinding.FragmentVideoPlayerBinding
import at.huber.youtubeExtractor.VideoMeta
import at.huber.youtubeExtractor.YtFile
import android.util.SparseArray
import at.huber.youtubeExtractor.YouTubeExtractor
import com.google.android.exoplayer2.MediaItem
import com.google.android.exoplayer2.Player
import com.google.android.exoplayer2.source.MediaSource
import com.google.android.exoplayer2.source.MergingMediaSource
import com.google.android.exoplayer2.source.ProgressiveMediaSource
import com.google.android.exoplayer2.upstream.DefaultHttpDataSource
import com.google.android.exoplayer2.util.Util
import com.hamdy.showtime.ui.util.YouTubeBase


class VideoPlayerFragment : Fragment(), Player.Listener {
    private lateinit var youtubeLink: String
    private lateinit var binding: FragmentVideoPlayerBinding
    var player: ExoPlayer? = null
    private var playWhenReady = true
    private var currentWindow = 0
    private var playbackPosition = 0L

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        binding = FragmentVideoPlayerBinding.inflate(inflater, container, false)
        return binding.root
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        youtubeLink = YouTubeBase + arguments?.get("url").toString()

        initializePlayer()

    }

    private fun initializePlayer() {
        player = ExoPlayer.Builder(requireContext()).build()
        binding.videoView.player = player

        object : YouTubeExtractor(requireContext()) {
            override fun onExtractionComplete(
                ytFiles: SparseArray<YtFile>?,
                videoMeta: VideoMeta?
            ) {
                if (ytFiles != null) {

                    val iTag = 137//tag of video 1080
                    val audioTag = 140 //tag m4a audio
                    // 720, 1080, 480
                    var videoUrl = ""
                    val iTags: List<Int> = listOf(22, 137, 18)
                    for (i in iTags) {
                        val ytFile = ytFiles.get(i)
                        if (ytFile != null) {
                            val downloadUrl = ytFile.url
                            if (downloadUrl != null && downloadUrl.isNotEmpty()) {
                                videoUrl = downloadUrl
                            }
                        }
                    }
                    if (videoUrl == "")
                        videoUrl = ytFiles[iTag].url
                    val audioUrl = ytFiles[audioTag].url
                    val audioSource: MediaSource = ProgressiveMediaSource
                        .Factory(DefaultHttpDataSource.Factory())
                        .createMediaSource(MediaItem.fromUri(audioUrl))
                    val videoSource: MediaSource = ProgressiveMediaSource
                        .Factory(DefaultHttpDataSource.Factory())
                        .createMediaSource(MediaItem.fromUri(videoUrl))
                    player?.setMediaSource(
                        MergingMediaSource(true, videoSource, audioSource), true
                    )
                    player?.prepare()
                    player?.playWhenReady = playWhenReady
                    player?.seekTo(currentWindow, playbackPosition)
                    player?.addListener(this@VideoPlayerFragment)
                }
            }

        }.extract(youtubeLink)

    }

    override fun onPlayerStateChanged(playWhenReady: Boolean, playbackState: Int) {
        if (playbackState == Player.STATE_READY) {
            binding.progressBar.visibility = View.INVISIBLE
        } else {
            binding.progressBar.visibility = View.VISIBLE
        }
    }


    override fun onStart() {
        super.onStart()
        if (Util.SDK_INT >= 24 || player == null) {
            initializePlayer()

        }
    }

    override fun onResume() {
        super.onResume()
        if (Util.SDK_INT < 24 || player == null) {
            initializePlayer()

        }
    }

    override fun onPause() {
        if (Util.SDK_INT < 24) releasePlayer()
        super.onPause()
    }

    private fun releasePlayer() {
        if (player != null) {
            playWhenReady = player!!.playWhenReady
            playbackPosition = player!!.currentPosition
            currentWindow = player!!.currentMediaItemIndex
            player?.release()
            player = null
        }

    }
}
Hamdy Abd El Fattah
  • 1,405
  • 1
  • 16
  • 16
  • 1
    Important! jitpack can no longer resovle the library. to get this once, try the solution in this PR: https://github.com/HaarigerHarald/android-youtubeExtractor/pull/223 Nice work! – hannunehg Jul 04 '23 at 07:14