Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to stream audio from a Youtube URL in Python (without download)?

I have been trying to create a way to stream a youtube url (preferably audio only, although this doesn't matter too much) right from python code. I have tried numerous things but none really seem to work. So far, I am able to search for videos or playlists using youtube data api, grab the first video or playlist and pass it into pafy to get different streaming urls. Does anyone know of a way to play youtube audio/video through python without downloading the video first? I would think it is possible with a cmd line tool such as mplayer or vlc using the sub process to pop open a cmd for the cmd line and pass in the url, but I am stuck. Any help is needed. Please! Here is my following code:

import argparse
import pafy
from googleapiclient.discovery import build
from googleapiclient.errors import HttpError

DEVELOPER_KEY = 'DEVELOPER KEY'
YOUTUBE_API_SERVICE_NAME = 'youtube'
YOUTUBE_API_VERSION = 'v3'

def pafy_video(video_id):
    url = 'https://www.youtube.com/watch?v={0}'.format(video_id)
    vid = pafy.new(url)

def pafy_playlist(playlist_id)
    url = "https://www.youtube.com/playlist?list={0}".format(playlist_id)
    playlist = pafy.get_playlist(url)


def youtube_search(options):
  youtube = build(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION, developerKey=DEVELOPER_KEY)

  search_response = youtube.search().list(
    q='Hello world',
    part='id,snippet',
    maxResults=options.max_results
  ).execute()

  videos = []
  playlists = []
  channels = []
  for search_result in search_response.get('items', []):
    if search_result['id']['kind'] == 'youtube#video':
      videos.append('%s' % (search_result['id']['videoId']))
    elif search_result['id']['kind'] == 'youtube#channel':
      channels.append('%s' % (search_result['id']['channelId']))
    elif search_result['id']['kind'] == 'youtube#playlist':
      playlists.append('%s' % (search_result['id']['playlistId']))


  if videos:
    print('Videos:{0}'.format(videos))
    pafy_video(videos[0])
  elif playlists:
    print('Playlists:{0}'.format(playlists))
    pafy_video(playlists[0])

  #https://www.youtube.com/watch?v=rOU4YiuaxAM
  #url = 'https://www.youtube.com/watch?v={0}'.format(videos[0])
  #print(url)

if __name__ == '__main__':
  parser = argparse.ArgumentParser()
  parser.add_argument('--q', help='Search term', default='Google')
  parser.add_argument('--max-results', help='Max results', default=3)
  args = parser.parse_args()
  youtube_search(args)

Tldr; I would like to stream a youtube video (using the url or id) straight from python code without downloading the video first

Thank you!

like image 405
Nick D Avatar asked Mar 19 '18 00:03

Nick D


2 Answers

pafy according to its documentation do not list playing media directly (at least I didn't find any).

However we can use it to get correct url, and then use player such as vlc to play directly without downloading.

You can download vlc from here

First we get correct / best URL from youtube using pafy

import pafy
import vlc

url = "https://www.youtube.com/watch?v=bMt47wvK6u0"
video = pafy.new(url)
best = video.getbest()
playurl = best.url

Over here playurl is best URL to play. Then we use VLC to play it.

Instance = vlc.Instance()
player = Instance.media_player_new()
Media = Instance.media_new(playurl)
Media.get_mrl()
player.set_media(Media)
player.play()

This will open a window with no controls (play/pause/stop etc). You can run these command on the repr window or at python prompt (depending upon how you are using it)
You will need to build one accordingly using vlc commands such as

>>> player.pause() #-- to pause video
>>> player.play()  #-- resume paused video. On older versions, 
                   #     this function was called resume
>>> player.stop()  #-- to stop/end video
like image 61
Anil_M Avatar answered Oct 02 '22 15:10

Anil_M


The best way to accomplish this is to use mpv with youtube-dl (a light weight media player that comes with a command line console which allows you to stream any type of video/audio codecs via copy and pasting url to mpv console (works for cmd and terminal). Usage: path\to\mpv https://your/video/audioURL.com/

First import these modules - install via pip install bs4 requests

import re, requests, subprocess, urllib.parse, urllib.request
from bs4 import BeautifulSoup

Then create a variable to store music title of your choice

music_name = "Linkin Park Numb"
query_string = urllib.parse.urlencode({"search_query": music_name})

The goal here to use this search query to extract the output title and link of the first video result. We can then endcode the youtube url "https://www.youtube.com/results?search_query=" with the identifier using urllib (eg., "https://www.youtube.com/results?search_query=linkin+park+numb")

formatUrl = urllib.request.urlopen("https://www.youtube.com/results?" + query_string)

This right here re.findall(r"watch\?v=(\S{11})" views the 11 character identifier of all the video results from our query

search_results = re.findall(r"watch\?v=(\S{11})", formatUrl.read().decode())

After decoding the content, we can extract the full url by concatenating the main youtube url with the 11 character identifier

clip = requests.get("https://www.youtube.com/watch?v=" + "{}".format(search_results[0]))
clip2 = "https://www.youtube.com/watch?v=" + "{}".format(search_results[0])

print(clip2)
  • Output ==> https://www.youtube.com/watch?v=kXYiU_JCYtU

To further inspect the content, we can use beautifulsoup to scrape the extact title of the video

inspect = BeautifulSoup(clip.content, "html.parser")
yt_title = inspect.find_all("meta", property="og:title")

for concatMusic1 in yt_title:
    pass

print(concatMusic1['content'])
  • Output ==> Numb (Official Video) - Linkin Park

Finally, to play our music we input this to the command line

subprocess.Popen(
"start /b " + "path\\to\\mpv.exe" + clip2 + " --no-video --loop=inf --input-ipc-server=\\\\.\\pipe\\mpv-pipe > output.txt",
shell=True)

Full script

===================================================================

import re, requests, subprocess, urllib.parse, urllib.request
from bs4 import BeautifulSoup

music_name = "Linkin Park Numb"
query_string = urllib.parse.urlencode({"search_query": music_name})
formatUrl = urllib.request.urlopen("https://www.youtube.com/results?" + query_string)

search_results = re.findall(r"watch\?v=(\S{11})", formatUrl.read().decode())
clip = requests.get("https://www.youtube.com/watch?v=" + "{}".format(search_results[0]))
clip2 = "https://www.youtube.com/watch?v=" + "{}".format(search_results[0])

inspect = BeautifulSoup(clip.content, "html.parser")
yt_title = inspect.find_all("meta", property="og:title")

for concatMusic1 in yt_title:
    pass

print(concatMusic1['content'])

subprocess.Popen(
"start /b " + "path\\to\\mpv.exe " + clip2 + " --no-video --loop=inf --input-ipc-server=\\\\.\\pipe\\mpv-pipe > output.txt",
shell=True)


# Alternatively, you can do this for simplicity sake:
# subprocess.Popen("start /b " + "path\\to\\mpv.exe " + clip2 + "--no-video", shell=True)
like image 26
Rook Crossfield Avatar answered Oct 02 '22 15:10

Rook Crossfield