yt_data_api 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/README.markdown CHANGED
@@ -1,6 +1,28 @@
1
1
  #Yt_Data_Api
2
2
 
3
- Yt_Data_Api adds functionality to access YouTube Data API.
3
+ yt_data_api adds functionality to access elements of the YouTube Data API. This was created as a learning tool. Check out youtube_it for more functionality, https://github.com/kylejginavan/youtube_it.
4
4
 
5
- # TODO
6
- Add more descriptions...
5
+ #Usage
6
+
7
+ First, create a new client object passing YouTube user credentials. Note you will have to acquire a developer key from YouTube, http://code.google.com/apis/youtube/2.0/developers_guide_protocol.html#Developer_Key.
8
+
9
+ `client = YtDataApi::YtDataApiClient.new(YOUTUBE_USER_ID, YOUTUBE_USER_PSWD, YOUTUBE_DEV_KEY)`
10
+
11
+ Then, call functions on the client object to access the YouTube Data API.
12
+
13
+ ##Create a playlist and get it's id
14
+ `client.create_playlist("test")`
15
+ `playlist_id = client.get_client_playlist_id("test")`
16
+
17
+ ##Delete a playlist
18
+ `client.delete_playlist(playlist_id)`
19
+
20
+ ##Get a video id given a query
21
+ Note spaces were replaced with '+'
22
+ `video_id = client.get_video_id("Otis+Kanye+West")`
23
+
24
+ ##Add video to client playlist
25
+ `client.add_video_to_playlist(video_id, playlist_id)`
26
+
27
+ #TODO
28
+ - Add more functionality
data/lib/yt_data_api.rb CHANGED
@@ -7,98 +7,78 @@ require 'xml'
7
7
  module YtDataApi
8
8
  class YtDataApiClient
9
9
  def initialize(user_id, user_pswd, dev_key)
10
- self.client_login(user_id, user_pswd, dev_key)
11
- end
12
-
13
- def client_login(user_id, user_pswd, dev_key)
14
- http = Net::HTTP.new("www.google.com", 443)
15
- http.use_ssl = true
16
- path = "/accounts/ClientLogin"
17
-
18
- data = "Email=#{user_id}&Passwd=#{user_pswd}&service=youtube&source=Test"
19
- headers = {"Content-Type" => "application/x-www-form-urlencoded"}
20
-
21
- response, data = http.post(path, data, headers)
22
-
23
- if(response.code != "200")
24
- raise "Error while authenticating YouTube user - #{response.code}"
10
+ post_response = client_login(user_id, user_pswd, dev_key)
11
+
12
+ if(post_response.code != "200")
13
+ raise "Error while authenticating #{user_id} - #{post_response.code}: #{post_response.message}"
25
14
  end
26
-
27
- @client_user_id = user_id
28
- @client_auth_key = data[/Auth=(.*)/, 1]
29
- @dev_auth_key = "key=" + dev_key
30
15
  end
31
16
 
32
- def get_video_id(search_query)
33
- query_uri = URI.parse("http://gdata.youtube.com/feeds/api/videos?q=#{search_query}&max-results=1")
34
- rss_query_result = parse_rss(query_uri)
17
+ def create_playlist(playlist_name)
18
+ headers = {"Content-Type" => "application/atom+xml",
19
+ "Authorization" => "GoogleLogin auth=#{@client_auth_key}",
20
+ "X-GData-Key" => @dev_auth_key,
21
+ "GData-Version" => "2"}
22
+
23
+ new_row = "<?xml version='1.0' encoding='UTF-8'?>" +
24
+ "<entry xmlns='http://www.w3.org/2005/Atom' " +
25
+ "xmlns:yt='http://gdata.youtube.com/schemas/2007'>" +
26
+ "<title type='text'>#{playlist_name}</title><summary></summary></entry>"
35
27
 
36
- rss_query_result.items.each do |item|
37
- return item.id.to_s[/<id>http:\/\/(.*\/)(.*)<\/id>/, 2]
38
- end
28
+ headers["Content-Length"] = new_row.bytesize.to_s
39
29
 
40
- return nil
30
+ uri = URI.parse("http://gdata.youtube.com/feeds/api/users/default/playlists")
31
+ http = Net::HTTP.new(uri.host, uri.port)
32
+
33
+ post_response = http.post(uri.path, new_row, headers)
34
+ post_response_content = parse_atom_xml(post_response)
35
+ post_response_entry = post_response_content.root.find_first('yt:playlistId')
36
+ playlist_id = post_response_entry.content
37
+
38
+ return post_response, playlist_id
41
39
  end
42
-
43
- def get_client_playlist_id(playlist_name)
40
+
41
+ def get_playlist_id(playlist_name)
44
42
  headers = {"Content-Type" => "application/x-www-form-urlencoded",
45
43
  "Authorization" => "GoogleLogin auth=#{@client_auth_key}",
46
44
  "X-GData-Key" => @dev_auth_key}
47
45
 
48
46
  uri = URI.parse("http://gdata.youtube.com/feeds/api/users/default/playlists?v=2")
49
47
 
50
- xml_client_playlists = Net::HTTP.start(uri.host, uri.port){|http|
48
+ playlists_feed = Net::HTTP.start(uri.host, uri.port){|http|
51
49
  http.get(uri.path, headers)
52
50
  }
51
+
52
+ playlists_content = parse_atom_xml(playlists_feed)
53
+ playlists_entries = playlists_content.root.find('atom:entry')
53
54
 
54
- #Parse xml of client's playlists
55
- source = XML::Parser.string(xml_client_playlists.body)
56
- content = source.parse
57
- content.root.namespaces.default_prefix = 'atom'
58
-
59
- entries = content.root.find('atom:entry')
60
-
61
- #Get playlist id
62
- entries.each do |entry|
55
+ playlists_entries.each do |entry|
63
56
  if(entry.find_first('atom:title').content.eql?(playlist_name))
64
- feedlink = entry.find_first('gd:feedLink').attributes["href"]
65
- return feedlink[/http:\/\/gdata.youtube.com\/feeds\/api\/playlists\/(.+)/, 1]
57
+ return entry.find_first('yt:playlistId').content
66
58
  end
67
59
  end
68
60
 
69
61
  return nil
70
62
  end
71
-
72
- def get_client_playlist_entries(playlist_id)
73
- headers = {"Content-Type" => "application/x-www-form-urlencoded",
74
- "Authorization" => "GoogleLogin auth=#{@client_auth_key}",
75
- "X-GData-Key" => @dev_auth_key}
76
-
77
- uri = URI.parse("http://gdata.youtube.com/feeds/api/playlists/#{playlist_id}?v=2")
78
-
79
- xml_client_playlist_videos = Net::HTTP.start(uri.host, uri.port){|http|
80
- http.get(uri.path, headers)
81
- }
82
-
83
- source = XML::Parser.string(xml_client_playlist_videos.body)
84
- content = source.parse
85
- content.root.namespaces.default_prefix = 'atom'
86
-
87
- entries = content.root.find('atom:entry')
88
-
89
- playlist_entries = []
90
-
91
- entries.each do |entry|
92
- entry_id = entry.find_first('atom:id')
93
- playlist_entry =
94
- entry_id.content[/http:\/\/.*\/(.+)/, 1]
95
- playlist_entries << playlist_entry
63
+
64
+ def get_video_ids(search_query, count = 1)
65
+ query_uri = URI.parse("http://gdata.youtube.com/feeds/api/videos?q=#{search_query}&max-results=#{count}")
66
+ rss_query_result = parse_rss(query_uri)
67
+ video_ids = []
68
+
69
+ rss_query_result.items.each do |item|
70
+ video_id = item.id.to_s[/<id>http:\/\/(.*\/)(.*)<\/id>/, 2]
71
+ return video_id if count == 1
72
+ video_ids << video_id
96
73
  end
97
-
98
- playlist_entries
99
74
 
75
+ if(video_ids.empty?)
76
+ nil
77
+ else
78
+ video_ids
79
+ end
100
80
  end
101
-
81
+
102
82
  def add_video_to_playlist(video_id, playlist_id)
103
83
  headers = {"Content-Type" => "application/atom+xml",
104
84
  "Authorization" => "GoogleLogin auth=#{@client_auth_key}",
@@ -116,37 +96,56 @@ module YtDataApi
116
96
  http = Net::HTTP.new(uri.host, uri.port)
117
97
 
118
98
  post_response = http.post(uri.path, new_row, headers)
119
-
120
- if(post_response.code != "201")
121
- raise "Error while adding #{video_id} to #{playlist_id} - #{post_response.code}"
99
+ end
100
+
101
+ def add_videos_to_playlist(video_ids, playlist_id)
102
+ failed_to_add = []
103
+
104
+ video_ids.each_with_index do |video_id, index|
105
+ post_response = add_video_to_playlist(video_id, playlist_id)
106
+
107
+ if(post_response.code == "403")
108
+ sleep(30)
109
+ post_repsonse = add_video_to_playlist(video_id, playlist_id)
110
+ end
111
+
112
+ if(post_response.code != "201")
113
+ failed_to_add << "#{index + 1},#{video_id},#{post_response.code}"
114
+ end
115
+ end
116
+
117
+ if(failed_to_add.empty?)
118
+ "201"
119
+ else
120
+ failed_to_add
122
121
  end
123
-
124
- post_response.code
125
122
  end
126
123
 
127
- def create_playlist(playlist_name)
128
- headers = {"Content-Type" => "application/atom+xml",
124
+ def get_client_playlist_entries(playlist_id)
125
+ headers = {"Content-Type" => "application/x-www-form-urlencoded",
129
126
  "Authorization" => "GoogleLogin auth=#{@client_auth_key}",
130
- "X-GData-Key" => @dev_auth_key,
131
- "GData-Version" => "2"}
127
+ "X-GData-Key" => @dev_auth_key}
132
128
 
133
- new_row = "<?xml version='1.0' encoding='UTF-8'?>" +
134
- "<entry xmlns='http://www.w3.org/2005/Atom' " +
135
- "xmlns:yt='http://gdata.youtube.com/schemas/2007'>" +
136
- "<title type='text'>#{playlist_name}</title><summary></summary></entry>"
129
+ uri = URI.parse("http://gdata.youtube.com/feeds/api/playlists/#{playlist_id}?v=2")
137
130
 
138
- headers["Content-Length"] = new_row.bytesize.to_s
131
+ playlist_entries_feed = Net::HTTP.start(uri.host, uri.port){|http|
132
+ http.get(uri.path, headers)
133
+ }
139
134
 
140
- uri = URI.parse("http://gdata.youtube.com/feeds/api/users/default/playlists")
141
- http = Net::HTTP.new(uri.host, uri.port)
135
+ playlist_entries_content = parse_atom_xml(playlist_entries_feed)
136
+ playlist_entries = playlist_entries_content.root.find('atom:entry')
142
137
 
143
- post_response = http.post(uri.path, new_row, headers)
138
+ playlist_entries_ids = []
144
139
 
145
- if(post_response.code != "201")
146
- raise "Error while creating #{playlist_name} - #{post_response.code}"
140
+ playlist_entries.each do |entry|
141
+ entry_id = entry.find_first('atom:id')
142
+ playlist_entry =
143
+ entry_id.content[/http:\/\/.*\/(.+)/, 1]
144
+ playlist_entries_ids << playlist_entry
147
145
  end
146
+
147
+ playlist_entries_ids
148
148
 
149
- post_response.code
150
149
  end
151
150
 
152
151
  def empty_playlist(playlist_id)
@@ -188,7 +187,7 @@ module YtDataApi
188
187
 
189
188
  delete_response.code
190
189
  end
191
-
190
+
192
191
  private
193
192
  #Use Ruby RSS Parser to return parseable info from RSS feed
194
193
  def parse_rss(rss_feed)
@@ -200,6 +199,38 @@ module YtDataApi
200
199
  end
201
200
 
202
201
  RSS::Parser.parse(rss_content, false)
203
- end
202
+ end
203
+
204
+ def parse_atom_xml(xml)
205
+ source = XML::Parser.string(xml.body)
206
+ content = source.parse
207
+ content.root.namespaces.default_prefix = 'atom'
208
+ content
209
+ end
210
+
211
+ def http_post_ssl(url, data, headers)
212
+ uri = URI.parse(url)
213
+ http = Net::HTTP.new(uri.host, uri.port)
214
+ http.use_ssl = true
215
+
216
+ http.post(uri.path, data, headers)
217
+ end
218
+
219
+ def client_login(user_id, user_pswd, dev_key)
220
+ post_url = "https://www.google.com/accounts/ClientLogin"
221
+ data = "Email=#{user_id}&Passwd=#{user_pswd}&service=youtube&source=YtDataApi"
222
+ headers = {"Content-Type" => "application/x-www-form-urlencoded"}
223
+
224
+ post_response, data = http_post_ssl(post_url, data, headers)
225
+
226
+ if(post_response.code != "200")
227
+ post_response
228
+ else
229
+ @client_user_id = user_id
230
+ @client_auth_key = data[/Auth=(.*)/, 1]
231
+ @dev_auth_key = "key=" + dev_key
232
+ post_response
233
+ end
234
+ end
204
235
  end
205
236
  end
@@ -1,3 +1,3 @@
1
1
  module YtDataApi
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -5,10 +5,6 @@ describe "YtDataApi::YtDataApiClient" do
5
5
  YtDataApi::YtDataApiClient.new(ENV['YT_USER'], ENV['YT_USER_PSWD'], ENV['YT_DEV_AUTH_KEY'])
6
6
  end
7
7
 
8
- it "should not create a new instance without credentials for ClientLogin Authentication" do
9
- lambda{ YtDataApi::YtDataApiClient.new }.should raise_error ArgumentError
10
- end
11
-
12
8
  it "should not create a new instance without valid credentials for ClientLogin Authentication" do
13
9
  lambda{ YtDataApi::YtDataApiClient.new("trash", "trash", "trash") }.should raise_error
14
10
  end
@@ -16,27 +12,72 @@ describe "YtDataApi::YtDataApiClient" do
16
12
  describe "authenticated" do
17
13
  before(:each) do
18
14
  @client = YtDataApi::YtDataApiClient.new(ENV['YT_USER'], ENV['YT_USER_PSWD'], ENV['YT_DEV_AUTH_KEY'])
19
- @create_response = @client.create_playlist("test_one")
20
- @playlist_id = @client.get_client_playlist_id("test_one")
15
+ @create_response, @playlist_id = @client.create_playlist("test_one")
21
16
  end
22
17
 
23
18
  it "should create a client's playlist" do
24
- @create_response.should == "201"
19
+ @create_response.code.should == "201"
25
20
  end
26
21
 
27
22
  it "should get a client's playlist id given the playlist's name" do
28
- @playlist_id.should_not be nil
23
+ playlist_id = @client.get_playlist_id("test_one")
24
+ playlist_id.should == @playlist_id
25
+ end
26
+
27
+ it "should return nil if the playlist id cannot be found given playlist name" do
28
+ playlist_id = @client.get_playlist_id("trash")
29
+ playlist_id.should == nil
29
30
  end
30
31
 
31
- it "should get a video id given a query string" do
32
+ it "should get one video id given a query string" do
32
33
  query = "vampire-weekend-giving-up-the-gun"
33
- video_id = @client.get_video_id(query)
34
+ video_id = @client.get_video_ids(query)
34
35
  video_id.should == "bccKotFwzoY"
35
36
  end
37
+
38
+ it "should get multiple video ids given a query string and count" do
39
+ query = "vampire-weekend-giving-up-the-gun"
40
+ video_ids = @client.get_video_ids(query, 10)
41
+ video_ids.size.should == 10
42
+ end
43
+
44
+ it "should return nil if no videos are found for given query" do
45
+ query = ","
46
+ video_id = @client.get_video_ids(query)
47
+ video_id.should == nil
48
+ end
36
49
 
37
50
  it "should add a video to a client's playlist" do
38
51
  video_id = "bccKotFwzoY"
39
52
  post_response = @client.add_video_to_playlist(video_id, @playlist_id)
53
+ post_response.code.should == "201"
54
+ end
55
+
56
+ it "should add multiple videos (less than 50) to a client's playlist" do
57
+ video_ids = []
58
+ 5.times do
59
+ video_ids << "bccKotFwzoY"
60
+ end
61
+
62
+ post_response = @client.add_videos_to_playlist(video_ids, @playlist_id)
63
+ post_response.should == "201"
64
+ end
65
+
66
+ it "should return an array of videos that could not be added to the client's playlist" do
67
+ video_ids = %w{ bccKotFwzoY trash trash bccKotFwzoY trash }
68
+
69
+ post_response = @client.add_videos_to_playlist(video_ids, @playlist_id)
70
+ array = post_response.map{|entry| entry.split(",")[1] }
71
+ array.should == %w{ trash trash trash }
72
+ end
73
+
74
+ it "should add multiple videos (more than 50) to a playlist" do
75
+ video_ids = []
76
+ 100.times do
77
+ video_ids << "bccKotFwzoY"
78
+ end
79
+
80
+ post_response = @client.add_videos_to_playlist(video_ids, @playlist_id)
40
81
  post_response.should == "201"
41
82
  end
42
83
 
@@ -55,7 +96,7 @@ describe "YtDataApi::YtDataApiClient" do
55
96
  end
56
97
 
57
98
  after(:each) do
58
- playlist = @client.get_client_playlist_id("test_one")
99
+ playlist = @client.get_playlist_id("test_one")
59
100
  @client.delete_playlist(@playlist_id) unless playlist.nil?
60
101
  end
61
102
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yt_data_api
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2011-08-24 00:00:00.000000000Z
12
+ date: 2011-11-15 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: libxml-ruby
16
- requirement: &70195100472000 !ruby/object:Gem::Requirement
16
+ requirement: &70157707836100 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: 1.1.3
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *70195100472000
24
+ version_requirements: *70157707836100
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: rspec
27
- requirement: &70195100474040 !ruby/object:Gem::Requirement
27
+ requirement: &70157707835600 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,7 +32,7 @@ dependencies:
32
32
  version: 2.4.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *70195100474040
35
+ version_requirements: *70157707835600
36
36
  description: ! "Create a new instance of YtDataApi::YtDataApiClient passing \n user
37
37
  credentials (username, password) and YouTube developer key\n to
38
38
  access YouTube Data API using ClientLogin authentication."