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 +25 -3
- data/lib/yt_data_api.rb +121 -90
- data/lib/yt_data_api/version.rb +1 -1
- data/spec/yt_data_api_spec.rb +52 -11
- metadata +6 -6
data/README.markdown
CHANGED
@@ -1,6 +1,28 @@
|
|
1
1
|
#Yt_Data_Api
|
2
2
|
|
3
|
-
|
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
|
-
#
|
6
|
-
|
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
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
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
|
33
|
-
|
34
|
-
|
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
|
-
|
37
|
-
return item.id.to_s[/<id>http:\/\/(.*\/)(.*)<\/id>/, 2]
|
38
|
-
end
|
28
|
+
headers["Content-Length"] = new_row.bytesize.to_s
|
39
29
|
|
40
|
-
|
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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
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
|
-
|
121
|
-
|
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
|
128
|
-
headers = {"Content-Type" => "application/
|
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
|
-
|
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
|
-
|
131
|
+
playlist_entries_feed = Net::HTTP.start(uri.host, uri.port){|http|
|
132
|
+
http.get(uri.path, headers)
|
133
|
+
}
|
139
134
|
|
140
|
-
|
141
|
-
|
135
|
+
playlist_entries_content = parse_atom_xml(playlist_entries_feed)
|
136
|
+
playlist_entries = playlist_entries_content.root.find('atom:entry')
|
142
137
|
|
143
|
-
|
138
|
+
playlist_entries_ids = []
|
144
139
|
|
145
|
-
|
146
|
-
|
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
|
-
|
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
|
data/lib/yt_data_api/version.rb
CHANGED
data/spec/yt_data_api_spec.rb
CHANGED
@@ -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
|
-
|
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
|
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.
|
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.
|
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.
|
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-
|
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: &
|
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: *
|
24
|
+
version_requirements: *70157707836100
|
25
25
|
- !ruby/object:Gem::Dependency
|
26
26
|
name: rspec
|
27
|
-
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: *
|
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."
|