Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse code

Basic playlist usage, no documentation yet. Still has some bugs (what…

… to do if videoID as well as playlist, handling implicit playlists...)
  • Loading branch information...
commit edc7c1b66cdd0d46a518ba2a962bff765ab3ef3f 1 parent 6c4e7ca
Chris Down authored

Showing 1 changed file with 63 additions and 22 deletions. Show diff stats Hide diff stats

  1. +63 22 yturl
85 yturl
@@ -8,22 +8,32 @@
8 8
9 9 import urllib
10 10 import urlparse
11   -import os.path
  11 +import os
12 12 import sys
  13 +import json
  14 +import tempfile
13 15
14 16 __author__ = 'Christopher Down'
15 17 __copyright__ = 'Copyright 2011-2012 %s' % __author__
16 18 __license__ = 'BSD'
17 19 __version__ = 1.01
18 20
  21 +class YouTubeAPIError(Exception):
  22 + pass
  23 +
19 24 class YTURL():
20 25 """Prints direct URLs to YouTube media."""
21 26 def __init__(self):
22   - self.youtubeQueryURL = 'http://youtube.com/get_video_info?&video_id=%s&el=detailpage&ps=default&eurl=&gl=US&hl=en'
  27 + self.youtubeQueryURLs = {
  28 + "video":'http://youtube.com/get_video_info?&video_id=%s&el=detailpage&ps=default&eurl=&gl=US&hl=en',
  29 + "playlist":"https://gdata.youtube.com/feeds/api/playlists/%s?v=2&alt=json"
  30 + }
23 31 self.videoURLListKey = 'url_encoded_fmt_stream_map'
24   - self.videoIDKeys = [ 'v', 'video_id' ]
  32 + self.queryStringKeys = {
  33 + "video":( "v", "video_id" ),
  34 + "playlist":( "p", "list" )
  35 + }
25 36 self.videoItagQualityOrder = self.getItagQualityOrder()
26   - self.allowedVideoIDCharacters = '-_abcdefghijklmnopqrstuvwxyz0123456789'
27 37
28 38 def getItagQualityOrder(self):
29 39 """Returns the itags in order of quality preference."""
@@ -102,7 +112,7 @@ class YTURL():
102 112 def getVideoItags(self, videoID):
103 113 """Returns the available itags and their associated URLs as a list."""
104 114 availableFormats = {}
105   - parsedResponse = urlparse.parse_qs(urllib.urlopen(self.youtubeQueryURL % videoID).read())
  115 + parsedResponse = urlparse.parse_qs(urllib.urlopen(self.youtubeQueryURLs["video"] % videoID).read())
106 116 if self.videoURLListKey in parsedResponse:
107 117 for videoFormat in parsedResponse[self.videoURLListKey][0].split(','):
108 118 videoFormat = urlparse.parse_qs(videoFormat)
@@ -133,16 +143,26 @@ class YTURL():
133 143 else:
134 144 return self.videoItagQualityOrder
135 145
136   - def stripYouTubeURL(self, url):
137   - """Strips a YouTube URL to the video ID."""
  146 + def parseQueryString(self, url):
  147 + """Parses a query string, returning both the content type and the ID."""
138 148 if '?' in url:
139 149 url = url[url.index('?') + 1:]
140 150 urlPost = urlparse.parse_qs(url)
141   - for key in self.videoIDKeys:
142   - if key in urlPost:
143   - return urlPost[key][0]
  151 + for keyType in self.queryStringKeys:
  152 + for key in keyType:
  153 + if key in urlPost:
  154 + return (keyType, urlPost[key][0])
  155 + bestGuess = url.split("/")[-1]
  156 + if len(bestGuess) == 11:
  157 + return ( "video", bestGuess )
144 158 else:
145   - return url.split("/")[-1][:11]
  159 + return ( "playlist", bestGuess )
  160 +
  161 + def getPlaylistVideoIDs(self, playlistID):
  162 + print playlistID
  163 + j = json.load(urllib.urlopen(self.youtubeQueryURLs["playlist"] % playlistID))
  164 + for entry in j["feed"]["entry"]:
  165 + yield entry["media$group"]["yt$videoid"]["$t"]
146 166
147 167 def main():
148 168 if len(sys.argv) == 1:
@@ -150,7 +170,8 @@ def main():
150 170 sys.exit(1)
151 171
152 172 y = YTURL()
153   - videoID = y.stripYouTubeURL(sys.argv[1])
  173 +
  174 + contentType, contentID = y.parseQueryString(sys.argv[1])
154 175
155 176 for itag in sys.argv[2:]:
156 177 if not itag.isdigit() or not y.isValidItag(int(itag)):
@@ -158,17 +179,37 @@ def main():
158 179 sys.exit(3)
159 180
160 181 preferredItags = map(int, sys.argv[2:])
161   - availableFormats = y.getVideoItags(videoID)
162   -
163   - if availableFormats is not False:
164   - for itag in y.getPreferredItagOrder(preferredItags):
165   - if itag in availableFormats:
166   - print >> sys.stderr, "Using itag %d." % itag
167   - print availableFormats[itag]
168   - break
  182 +
  183 + if contentType == "playlist":
  184 + videoIDs = [ x for x in y.getPlaylistVideoIDs(contentID) ]
  185 + else:
  186 + videoIDs = [ contentID ]
  187 +
  188 + uris = []
  189 + for videoID in videoIDs:
  190 + availableFormats = y.getVideoItags(videoID)
  191 +
  192 + if availableFormats is not False:
  193 + tempDir = tempfile.gettempdir()
  194 + for itag in y.getPreferredItagOrder(preferredItags):
  195 + if itag in availableFormats:
  196 + print >> sys.stderr, "Using itag %d for video ID %s." % (itag, videoID)
  197 + if contentType == "playlist":
  198 + uris.append(availableFormats[itag])
  199 + break
  200 + else:
  201 + uris.append(availableFormats[itag])
  202 + break
  203 + else:
  204 + raise YouTubeAPIError("The YouTube API returned data from which no media URL could be retrieved.")
  205 +
  206 + if contentType == "playlist":
  207 + filename = os.path.join(tempDir, "playlist-%s" % contentID)
  208 + with open(filename, "w+") as f:
  209 + f.write("\n".join(uris) + "\n")
  210 + print filename
169 211 else:
170   - print >> sys.stderr, "The YouTube API returned data from which no media URL could be retrieved."
171   - sys.exit(4)
  212 + print uris[0]
172 213
173 214 if __name__ == '__main__':
174 215 main()

0 comments on commit edc7c1b

Please sign in to comment.
Something went wrong with that request. Please try again.