youtube_it 1.4.3 → 2.0.0
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/Manifest.txt +4 -0
- data/README.rdoc +37 -6
- data/Rakefile +2 -2
- data/VERSION +1 -1
- data/lib/youtube_it.rb +6 -0
- data/lib/youtube_it/client.rb +47 -12
- data/lib/youtube_it/middleware/faraday_authheader.rb +13 -0
- data/lib/youtube_it/middleware/faraday_oauth.rb +21 -0
- data/lib/youtube_it/middleware/faraday_youtubeit.rb +30 -0
- data/lib/youtube_it/model/subscription.rb +7 -0
- data/lib/youtube_it/model/video.rb +3 -0
- data/lib/youtube_it/parser.rb +97 -58
- data/lib/youtube_it/request/base_search.rb +45 -0
- data/lib/youtube_it/request/error.rb +15 -0
- data/lib/youtube_it/request/user_search.rb +2 -0
- data/lib/youtube_it/request/video_search.rb +4 -1
- data/lib/youtube_it/request/video_upload.rb +146 -277
- data/lib/youtube_it/version.rb +1 -1
- data/test/files/recorded_response.xml +1 -0
- data/test/helper.rb +1 -0
- data/test/test_chain_io.rb +1 -1
- data/test/test_client.rb +77 -29
- data/test/test_field_search.rb +48 -0
- data/test/test_video.rb +2 -2
- data/test/test_video_feed_parser.rb +1 -1
- data/test/test_video_search.rb +2 -1
- data/youtube_it.gemspec +16 -4
- metadata +34 -6
- data/History.txt +0 -16
- data/TODO.txt +0 -16
@@ -22,6 +22,51 @@ class YouTubeIt
|
|
22
22
|
end
|
23
23
|
end
|
24
24
|
|
25
|
+
|
26
|
+
module FieldSearch
|
27
|
+
def default_fields
|
28
|
+
"id,updated,openSearch:totalResults,openSearch:startIndex,openSearch:itemsPerPage"
|
29
|
+
end
|
30
|
+
|
31
|
+
def fields_to_params(fields)
|
32
|
+
return "" unless fields
|
33
|
+
|
34
|
+
fields_param = [default_fields]
|
35
|
+
|
36
|
+
if fields[:recorded]
|
37
|
+
if fields[:recorded].is_a? Range
|
38
|
+
fields_param << "entry[xs:date(yt:recorded) > xs:date('#{formatted_date(fields[:recorded].first)}') and xs:date(yt:recorded) < xs:date('#{formatted_date(fields[:recorded].last)}')]"
|
39
|
+
else
|
40
|
+
fields_param << "entry[xs:date(yt:recorded) = xs:date('#{formatted_date(fields[:recorded])}')]"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
if fields[:published]
|
45
|
+
if fields[:published].is_a? Range
|
46
|
+
fields_param << "entry[xs:dateTime(published) > xs:dateTime('#{formatted_date(fields[:published].first)}T00:00:00') and xs:dateTime(published) < xs:dateTime('#{formatted_date(fields[:published].last)}T00:00:00')]"
|
47
|
+
else
|
48
|
+
fields_param << "entry[xs:date(published) = xs:date('#{formatted_date(fields[:published])}')]"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
if fields[:view_count]
|
53
|
+
fields_param << "entry[yt:statistics/@viewCount > #{fields[:view_count]}]"
|
54
|
+
end
|
55
|
+
|
56
|
+
|
57
|
+
return "&fields=#{URI.escape(fields_param.join(","))}"
|
58
|
+
end
|
59
|
+
|
60
|
+
#youtube taked dates that look like 'YYYY-MM-DD'
|
61
|
+
def formatted_date(date)
|
62
|
+
return date if date.is_a? String
|
63
|
+
if date.respond_to? :strftime
|
64
|
+
date.strftime("%Y-%m-%d")
|
65
|
+
else
|
66
|
+
""
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
25
70
|
end
|
26
71
|
end
|
27
72
|
|
@@ -0,0 +1,15 @@
|
|
1
|
+
class UploadError < YouTubeIt::Error
|
2
|
+
attr_reader :code
|
3
|
+
def initialize(msg, code = 0)
|
4
|
+
super(msg)
|
5
|
+
@code = code
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class AuthenticationError < YouTubeIt::Error
|
10
|
+
attr_reader :code
|
11
|
+
def initialize(msg, code = 0)
|
12
|
+
super(msg)
|
13
|
+
@code = code
|
14
|
+
end
|
15
|
+
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
class YouTubeIt
|
2
2
|
module Request #:nodoc:
|
3
3
|
class UserSearch < BaseSearch #:nodoc:
|
4
|
+
include FieldSearch
|
4
5
|
attr_reader :max_results # max_results
|
5
6
|
attr_reader :order_by # orderby, ([relevance], viewCount, published, rating)
|
6
7
|
attr_reader :offset # start-index
|
@@ -22,6 +23,7 @@ class YouTubeIt
|
|
22
23
|
end
|
23
24
|
|
24
25
|
@url << build_query_params(to_youtube_params)
|
26
|
+
@url << fields_to_params(params.delete(:fields)) if params != :favorites && params[:fields]
|
25
27
|
end
|
26
28
|
|
27
29
|
private
|
@@ -1,6 +1,8 @@
|
|
1
1
|
class YouTubeIt
|
2
2
|
module Request #:nodoc:
|
3
3
|
class VideoSearch < BaseSearch #:nodoc:
|
4
|
+
include FieldSearch
|
5
|
+
|
4
6
|
# From here: http://code.google.com/apis/youtube/reference.html#yt_format
|
5
7
|
ONLY_EMBEDDABLE = 5
|
6
8
|
|
@@ -16,7 +18,6 @@ class YouTubeIt
|
|
16
18
|
attr_reader :author
|
17
19
|
attr_reader :lang # lt
|
18
20
|
|
19
|
-
|
20
21
|
def initialize(params={})
|
21
22
|
# Initialize our various member data to avoid warnings and so we'll
|
22
23
|
# automatically fall back to the youtube api defaults
|
@@ -41,6 +42,7 @@ class YouTubeIt
|
|
41
42
|
end
|
42
43
|
|
43
44
|
@url << build_query_params(to_youtube_params)
|
45
|
+
@url << fields_to_params(params.delete(:fields)) if params[:fields]
|
44
46
|
end
|
45
47
|
|
46
48
|
private
|
@@ -64,6 +66,7 @@ class YouTubeIt
|
|
64
66
|
}
|
65
67
|
end
|
66
68
|
|
69
|
+
|
67
70
|
# Convert category symbols into strings and build the URL. GData requires categories to be capitalized.
|
68
71
|
# Categories defined like: categories => { :include => [:news], :exclude => [:sports], :either => [..] }
|
69
72
|
# or like: categories => [:news, :sports]
|
@@ -1,25 +1,7 @@
|
|
1
1
|
class YouTubeIt
|
2
|
-
|
3
2
|
module Upload
|
4
|
-
class UploadError < YouTubeIt::Error; end
|
5
|
-
class AuthenticationError < YouTubeIt::Error; end
|
6
|
-
|
7
|
-
# Implements video uploads/updates/deletions
|
8
|
-
#
|
9
|
-
# require 'youtube_it'
|
10
|
-
#
|
11
|
-
# uploader = YouTubeIt::Upload::VideoUpload.new("user", "pass", "dev-key")
|
12
|
-
# uploader.upload File.open("test.m4v"), :title => 'test',
|
13
|
-
# :description => 'cool vid d00d',
|
14
|
-
# :category => 'People',
|
15
|
-
# :keywords => %w[cool blah test]
|
16
|
-
#
|
17
3
|
class VideoUpload
|
18
4
|
include YouTubeIt::Logging
|
19
|
-
def initialize user, pass, dev_key, access_token = nil, authsub_token = nil, client_id = 'youtube_it'
|
20
|
-
@user, @password, @dev_key, @access_token, @authsub_token, @client_id = user, pass, dev_key, access_token, authsub_token, client_id
|
21
|
-
@http_debugging = false
|
22
|
-
end
|
23
5
|
|
24
6
|
def initialize *params
|
25
7
|
if params.first.is_a?(Hash)
|
@@ -30,6 +12,7 @@ class YouTubeIt
|
|
30
12
|
@access_token = hash_options[:access_token]
|
31
13
|
@authsub_token = hash_options[:authsub_token]
|
32
14
|
@client_id = hash_options[:client_id] || "youtube_it"
|
15
|
+
@config_token = hash_options[:config_token]
|
33
16
|
else
|
34
17
|
puts "* warning: the method YouTubeIt::Upload::VideoUpload.new(username, password, dev_key) is depricated, use YouTubeIt::Upload::VideoUpload.new(:username => 'user', :password => 'passwd', :dev_key => 'dev_key')"
|
35
18
|
@user = params.shift
|
@@ -38,6 +21,7 @@ class YouTubeIt
|
|
38
21
|
@access_token = params.shift
|
39
22
|
@authsub_token = params.shift
|
40
23
|
@client_id = params.shift || "youtube_it"
|
24
|
+
@config_token = params.shift
|
41
25
|
end
|
42
26
|
end
|
43
27
|
|
@@ -73,7 +57,6 @@ class YouTubeIt
|
|
73
57
|
#
|
74
58
|
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
75
59
|
def upload(data, opts = {})
|
76
|
-
response = nil
|
77
60
|
@opts = { :mime_type => 'video/mp4',
|
78
61
|
:title => '',
|
79
62
|
:description => '',
|
@@ -90,22 +73,9 @@ class YouTubeIt
|
|
90
73
|
"Content-Length" => "#{post_body_io.expected_length}",
|
91
74
|
}
|
92
75
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
http.set_debug_output(logger) if @http_debugging
|
97
|
-
path = '/feeds/api/users/default/uploads'
|
98
|
-
http.start do | session |
|
99
|
-
post = Net::HTTP::Post.new(path, upload_header)
|
100
|
-
post.body_stream = post_body_io
|
101
|
-
response = session.request(post)
|
102
|
-
end
|
103
|
-
else
|
104
|
-
upload_header.merge!(authorization_headers_for_oauth).delete("GData-Version")
|
105
|
-
url = 'http://%s/feeds/api/users/default/uploads' % uploads_url
|
106
|
-
response = @access_token.post(url, post_body_io, upload_header)
|
107
|
-
end
|
108
|
-
raise_on_faulty_response(response)
|
76
|
+
upload_url = "/feeds/api/users/default/uploads"
|
77
|
+
response = yt_session(uploads_url).post(upload_url, post_body_io, upload_header)
|
78
|
+
|
109
79
|
return YouTubeIt::Parser::VideoFeedParser.new(response.body).parse
|
110
80
|
end
|
111
81
|
|
@@ -118,53 +88,59 @@ class YouTubeIt
|
|
118
88
|
# :private
|
119
89
|
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
120
90
|
def update(video_id, options)
|
121
|
-
|
122
|
-
|
123
|
-
update_body = video_xml
|
91
|
+
@opts = options
|
92
|
+
update_body = video_xml
|
124
93
|
update_header = {
|
125
94
|
"Content-Type" => "application/atom+xml",
|
126
95
|
"Content-Length" => "#{update_body.length}",
|
127
96
|
}
|
128
97
|
update_url = "/feeds/api/users/default/uploads/%s" % video_id
|
98
|
+
response = yt_session.put(update_url, update_body, update_header)
|
99
|
+
|
100
|
+
return YouTubeIt::Parser::VideoFeedParser.new(response.body).parse
|
101
|
+
end
|
129
102
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
raise_on_faulty_response(response)
|
103
|
+
# Fetches the data of a video, which may be private. The video must be owned by this user.
|
104
|
+
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
105
|
+
def get_my_video(video_id)
|
106
|
+
get_header = {
|
107
|
+
"Content-Type" => "application/atom+xml",
|
108
|
+
"Content-Length" => "0",
|
109
|
+
}
|
110
|
+
get_url = "/feeds/api/users/default/uploads/%s" % video_id
|
111
|
+
response = yt_session.get(get_url, get_header)
|
112
|
+
|
141
113
|
return YouTubeIt::Parser::VideoFeedParser.new(response.body).parse
|
142
114
|
end
|
143
115
|
|
116
|
+
# Fetches the data of the videos of the current user, which may be private.
|
117
|
+
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
118
|
+
def get_my_videos(opts)
|
119
|
+
max_results = opts[:per_page] || 50
|
120
|
+
start_index = ((opts[:page] || 1) -1) * max_results +1
|
121
|
+
get_header = {
|
122
|
+
"Content-Type" => "application/atom+xml",
|
123
|
+
"Content-Length" => "0",
|
124
|
+
}
|
125
|
+
get_url = "/feeds/api/users/default/uploads?max-results=#{max_results}&start-index=#{start_index}"
|
126
|
+
response = yt_session.get(get_url, get_header)
|
127
|
+
|
128
|
+
return YouTubeIt::Parser::VideosFeedParser.new(response.body).parse
|
129
|
+
end
|
130
|
+
|
144
131
|
# Delete a video on YouTube
|
145
132
|
def delete(video_id)
|
146
|
-
response = nil
|
147
133
|
delete_header = {
|
148
134
|
"Content-Type" => "application/atom+xml; charset=UTF-8",
|
149
135
|
"Content-Length" => "0",
|
150
136
|
}
|
151
137
|
delete_url = "/feeds/api/users/default/uploads/%s" % video_id
|
138
|
+
response = yt_session.delete(delete_url, delete_header)
|
152
139
|
|
153
|
-
if @access_token.nil?
|
154
|
-
delete_header.merge!(authorization_headers)
|
155
|
-
http_connection do |session|
|
156
|
-
response = session.delete(delete_url, delete_header)
|
157
|
-
end
|
158
|
-
else
|
159
|
-
delete_header.merge!(authorization_headers_for_oauth)
|
160
|
-
response = @access_token.delete("http://%s%s" % [base_url,delete_url], delete_header)
|
161
|
-
end
|
162
|
-
raise_on_faulty_response(response)
|
163
140
|
return true
|
164
141
|
end
|
165
142
|
|
166
143
|
def get_upload_token(options, nexturl)
|
167
|
-
response = nil
|
168
144
|
@opts = options
|
169
145
|
token_body = video_xml
|
170
146
|
token_header = {
|
@@ -172,23 +148,13 @@ class YouTubeIt
|
|
172
148
|
"Content-Length" => "#{token_body.length}",
|
173
149
|
}
|
174
150
|
token_url = "/action/GetUploadToken"
|
175
|
-
|
176
|
-
|
177
|
-
token_header.merge!(authorization_headers)
|
178
|
-
http_connection do |session|
|
179
|
-
response = session.post(token_url, token_body, token_header)
|
180
|
-
end
|
181
|
-
else
|
182
|
-
token_header.merge!(authorization_headers_for_oauth)
|
183
|
-
response = @access_token.post("http://%s%s" % [base_url, token_url], token_body, token_header)
|
184
|
-
end
|
185
|
-
raise_on_faulty_response(response)
|
151
|
+
response = yt_session.post(token_url, token_body, token_header)
|
152
|
+
|
186
153
|
return {:url => "#{response.body[/<url>(.+)<\/url>/, 1]}?nexturl=#{nexturl}",
|
187
154
|
:token => response.body[/<token>(.+)<\/token>/, 1]}
|
188
155
|
end
|
189
156
|
|
190
157
|
def add_comment(video_id, comment)
|
191
|
-
response = nil
|
192
158
|
comment_body = video_xml_for(:comment => comment)
|
193
159
|
comment_header = {
|
194
160
|
"Content-Type" => "application/atom+xml",
|
@@ -196,265 +162,193 @@ class YouTubeIt
|
|
196
162
|
}
|
197
163
|
comment_url = "/feeds/api/videos/%s/comments" % video_id
|
198
164
|
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
response = session.post(comment_url, comment_body, comment_header)
|
203
|
-
end
|
204
|
-
else
|
205
|
-
comment_header.merge!(authorization_headers_for_oauth)
|
206
|
-
response = @access_token.post("http://%s%s" % [base_url, comment_url], comment_body, comment_header)
|
207
|
-
end
|
208
|
-
raise_on_faulty_response(response)
|
209
|
-
{:code => response.code, :body => response.body}
|
165
|
+
response = yt_session.post(comment_url, comment_body, comment_header)
|
166
|
+
|
167
|
+
return {:code => response.status, :body => response.body}
|
210
168
|
end
|
211
169
|
|
212
170
|
def comments(video_id, opts = {})
|
213
171
|
comment_url = "/feeds/api/videos/%s/comments?" % video_id
|
214
172
|
comment_url << opts.collect { |k,p| [k,p].join '=' }.join('&')
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
{:code => response.code, :body => response.body}
|
219
|
-
return YouTubeIt::Parser::CommentsFeedParser.new(response).parse
|
220
|
-
end
|
173
|
+
response = yt_session.get(comment_url)
|
174
|
+
|
175
|
+
return YouTubeIt::Parser::CommentsFeedParser.new(response).parse
|
221
176
|
end
|
222
177
|
|
223
178
|
def add_favorite(video_id)
|
224
|
-
response = nil
|
225
179
|
favorite_body = video_xml_for(:favorite => video_id)
|
226
180
|
favorite_header = {
|
227
181
|
"Content-Type" => "application/atom+xml",
|
228
182
|
"Content-Length" => "#{favorite_body.length}",
|
229
183
|
}
|
230
184
|
favorite_url = "/feeds/api/users/default/favorites"
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
http_connection do |session|
|
235
|
-
response = session.post(favorite_url, favorite_body, favorite_header)
|
236
|
-
end
|
237
|
-
else
|
238
|
-
favorite_header.merge!(authorization_headers_for_oauth)
|
239
|
-
response = @access_token.post("http://%s%s" % [base_url, favorite_url], favorite_body, favorite_header)
|
240
|
-
end
|
241
|
-
raise_on_faulty_response(response)
|
242
|
-
{:code => response.code, :body => response.body}
|
185
|
+
response = yt_session.post(favorite_url, favorite_body, favorite_header)
|
186
|
+
|
187
|
+
return {:code => response.status, :body => response.body}
|
243
188
|
end
|
244
189
|
|
245
190
|
def delete_favorite(video_id)
|
246
|
-
response = nil
|
247
191
|
favorite_header = {
|
248
192
|
"Content-Type" => "application/atom+xml; charset=UTF-8",
|
249
193
|
"Content-Length" => "0",
|
250
194
|
}
|
251
195
|
favorite_url = "/feeds/api/users/default/favorites/%s" % video_id
|
252
|
-
|
253
|
-
|
254
|
-
favorite_header.merge!(authorization_headers).delete("GData-Version")
|
255
|
-
http_connection do |session|
|
256
|
-
response = session.delete(favorite_url, favorite_header)
|
257
|
-
end
|
258
|
-
else
|
259
|
-
favorite_header.merge!(authorization_headers_for_oauth).delete("GData-Version")
|
260
|
-
response = @access_token.delete("http://%s%s" % [base_url, favorite_url], favorite_header)
|
261
|
-
end
|
262
|
-
raise_on_faulty_response(response)
|
196
|
+
response = yt_session.delete(favorite_url, favorite_header)
|
197
|
+
|
263
198
|
return true
|
264
199
|
end
|
265
200
|
|
266
|
-
def profile(
|
267
|
-
profile_url
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
201
|
+
def profile(user)
|
202
|
+
profile_url = "/feeds/api/users/%s?v=2" % (user ? user : "default")
|
203
|
+
profile_header = {
|
204
|
+
"Content-Type" => "application/atom+xml; charset=UTF-8"
|
205
|
+
}
|
206
|
+
response = yt_session.get(profile_url, profile_header)
|
207
|
+
|
208
|
+
return YouTubeIt::Parser::ProfileFeedParser.new(response).parse
|
273
209
|
end
|
274
210
|
|
275
211
|
def playlist(playlist_id)
|
276
212
|
playlist_url = "/feeds/api/playlists/%s?v=2" % playlist_id
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
|
281
|
-
end
|
282
|
-
end
|
283
|
-
|
284
|
-
def playlists
|
285
|
-
playlist_url = "/feeds/api/users/default/playlists?v=2"
|
286
|
-
http_connection do |session|
|
287
|
-
response = session.get(playlist_url)
|
288
|
-
raise_on_faulty_response(response)
|
289
|
-
return response.body
|
290
|
-
end
|
213
|
+
response = yt_session.get(playlist_url)
|
214
|
+
|
215
|
+
return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
|
291
216
|
end
|
292
217
|
|
293
|
-
def
|
294
|
-
playlist_url = "/feeds/api/users
|
295
|
-
|
296
|
-
|
297
|
-
|
298
|
-
return YouTubeIt::Parser::PlaylistsFeedParser.new(response).parse #return response.body
|
299
|
-
end
|
218
|
+
def playlists(user)
|
219
|
+
playlist_url = "/feeds/api/users/%s/playlists?v=2" % (user ? user : "default")
|
220
|
+
response = yt_session.get(playlist_url)
|
221
|
+
|
222
|
+
return YouTubeIt::Parser::PlaylistsFeedParser.new(response).parse
|
300
223
|
end
|
301
|
-
|
224
|
+
|
302
225
|
def add_playlist(options)
|
303
|
-
response = nil
|
304
226
|
playlist_body = video_xml_for_playlist(options)
|
305
227
|
playlist_header = {
|
306
228
|
"Content-Type" => "application/atom+xml",
|
307
229
|
"Content-Length" => "#{playlist_body.length}",
|
308
230
|
}
|
309
231
|
playlist_url = "/feeds/api/users/default/playlists"
|
310
|
-
|
311
|
-
|
312
|
-
playlist_header.merge!(authorization_headers)
|
313
|
-
http_connection do |session|
|
314
|
-
response = session.post(playlist_url, playlist_body, playlist_header)
|
315
|
-
end
|
316
|
-
else
|
317
|
-
playlist_header.merge!(authorization_headers_for_oauth)
|
318
|
-
response = @access_token.post("http://%s%s" % [base_url, playlist_url], playlist_body, playlist_header)
|
319
|
-
end
|
320
|
-
raise_on_faulty_response(response)
|
232
|
+
response = yt_session.post(playlist_url, playlist_body, playlist_header)
|
233
|
+
|
321
234
|
return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
|
322
235
|
end
|
323
236
|
|
324
237
|
def add_video_to_playlist(playlist_id, video_id)
|
325
|
-
response = nil
|
326
238
|
playlist_body = video_xml_for(:playlist => video_id)
|
327
239
|
playlist_header = {
|
328
240
|
"Content-Type" => "application/atom+xml",
|
329
241
|
"Content-Length" => "#{playlist_body.length}",
|
330
242
|
}
|
331
243
|
playlist_url = "/feeds/api/playlists/%s" % playlist_id
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
http_connection do |session|
|
336
|
-
response = session.post(playlist_url, playlist_body, playlist_header)
|
337
|
-
end
|
338
|
-
else
|
339
|
-
playlist_header.merge!(authorization_headers_for_oauth)
|
340
|
-
response = @access_token.post("http://%s%s" % [base_url, playlist_url], playlist_body, playlist_header)
|
341
|
-
end
|
342
|
-
raise_on_faulty_response(response)
|
343
|
-
{:code => response.code, :body => response.body, :playlist_entry_id => playlist_entry_id_from_playlist(response.body)}
|
244
|
+
response = yt_session.post(playlist_url, playlist_body, playlist_header)
|
245
|
+
|
246
|
+
return {:code => response.status, :body => response.body, :playlist_entry_id => playlist_entry_id_from_playlist(response.body)}
|
344
247
|
end
|
345
248
|
|
346
249
|
def update_playlist(playlist_id, options)
|
347
|
-
response = nil
|
348
250
|
playlist_body = video_xml_for_playlist(options)
|
349
251
|
playlist_header = {
|
350
252
|
"Content-Type" => "application/atom+xml",
|
351
253
|
"Content-Length" => "#{playlist_body.length}",
|
352
254
|
}
|
353
255
|
playlist_url = "/feeds/api/users/default/playlists/%s" % playlist_id
|
354
|
-
|
355
|
-
|
356
|
-
playlist_header.merge!(authorization_headers)
|
357
|
-
http_connection do |session|
|
358
|
-
response = session.put(playlist_url, playlist_body, playlist_header)
|
359
|
-
end
|
360
|
-
else
|
361
|
-
playlist_header.merge!(authorization_headers_for_oauth)
|
362
|
-
response = @access_token.put("http://%s%s" % [base_url, playlist_url], playlist_body, playlist_header)
|
363
|
-
end
|
364
|
-
raise_on_faulty_response(response)
|
256
|
+
response = yt_session.put(playlist_url, playlist_body, playlist_header)
|
257
|
+
|
365
258
|
return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
|
366
259
|
end
|
367
260
|
|
368
261
|
def delete_video_from_playlist(playlist_id, playlist_entry_id)
|
369
|
-
response = nil
|
370
262
|
playlist_header = {
|
371
263
|
"Content-Type" => "application/atom+xml",
|
372
264
|
}
|
373
265
|
playlist_url = "/feeds/api/playlists/%s/%s" % [playlist_id, playlist_entry_id]
|
374
|
-
|
375
|
-
|
376
|
-
playlist_header.merge!(authorization_headers)
|
377
|
-
http_connection do |session|
|
378
|
-
response = session.delete(playlist_url, playlist_header)
|
379
|
-
end
|
380
|
-
else
|
381
|
-
playlist_header.merge!(authorization_headers_for_oauth)
|
382
|
-
response = @access_token.delete("http://%s%s" % [base_url, playlist_url], playlist_header)
|
383
|
-
end
|
384
|
-
raise_on_faulty_response(response)
|
266
|
+
response = yt_session.delete(playlist_url, playlist_header)
|
267
|
+
|
385
268
|
return true
|
386
269
|
end
|
387
270
|
|
388
271
|
def delete_playlist(playlist_id)
|
389
|
-
|
390
|
-
|
391
|
-
"Content-Type" => "application/atom+xml; charset=UTF-8",
|
272
|
+
playlist_header = {
|
273
|
+
"Content-Type" => "application/atom+xml; charset=UTF-8",
|
392
274
|
}
|
393
|
-
playlist_url
|
394
|
-
|
395
|
-
|
396
|
-
playlist_header.merge!(authorization_headers)
|
397
|
-
http_connection do |session|
|
398
|
-
response = session.delete(playlist_url, playlist_header)
|
399
|
-
end
|
400
|
-
else
|
401
|
-
playlist_header.merge!(authorization_headers_for_oauth)
|
402
|
-
response = @access_token.delete("http://%s%s" % [base_url, playlist_url], playlist_header)
|
403
|
-
end
|
404
|
-
raise_on_faulty_response(response)
|
275
|
+
playlist_url = "/feeds/api/users/default/playlists/%s" % playlist_id
|
276
|
+
response = yt_session.delete(playlist_url, playlist_header)
|
277
|
+
|
405
278
|
return true
|
406
279
|
end
|
407
280
|
|
408
|
-
def
|
409
|
-
|
410
|
-
|
411
|
-
|
412
|
-
|
413
|
-
|
414
|
-
|
415
|
-
|
416
|
-
|
417
|
-
|
281
|
+
def rate_video(video_id, rating)
|
282
|
+
rating_body = video_xml_for(:rating => rating)
|
283
|
+
rating_header = {
|
284
|
+
"Content-Type" => "application/atom+xml",
|
285
|
+
"Content-Length" => "#{rating_body.length}",
|
286
|
+
}
|
287
|
+
rating_url = "/feeds/api/videos/#{video_id}/ratings"
|
288
|
+
response = yt_session.post(rating_url, rating_body, rating_header)
|
289
|
+
|
290
|
+
return {:code => response.status, :body => response.body}
|
291
|
+
end
|
292
|
+
|
293
|
+
def subscriptions(user)
|
294
|
+
subscription_url = "/feeds/api/users/%s/subscriptions?v=2" % (user ? user : "default")
|
295
|
+
subscription_header = {
|
296
|
+
"Content-Type" => "application/atom+xml",
|
297
|
+
}
|
298
|
+
response = yt_session.get(subscription_url, subscription_header)
|
299
|
+
|
300
|
+
return YouTubeIt::Parser::SubscriptionFeedParser.new(response).parse
|
301
|
+
end
|
302
|
+
|
303
|
+
def subscribe_channel(channel_name)
|
304
|
+
subscribe_body = video_xml_for(:subscribe => channel_name)
|
305
|
+
subscribe_header = {
|
306
|
+
"Content-Type" => "application/atom+xml",
|
307
|
+
"Content-Length" => "#{subscribe_body.length}",
|
308
|
+
}
|
309
|
+
subscribe_url = "/feeds/api/users/default/subscriptions"
|
310
|
+
response = yt_session.post(subscribe_url, subscribe_body, subscribe_header)
|
311
|
+
|
312
|
+
return {:code => response.status, :body => response.body}
|
313
|
+
end
|
314
|
+
|
315
|
+
def unsubscribe_channel(subscription_id)
|
316
|
+
unsubscribe_header = {
|
317
|
+
"Content-Type" => "application/atom+xml"
|
318
|
+
}
|
319
|
+
unsubscribe_url = "/feeds/api/users/default/subscriptions/%s" % subscription_id
|
320
|
+
response = yt_session.delete(unsubscribe_url, unsubscribe_header)
|
321
|
+
|
322
|
+
return {:code => response.status, :body => response.body}
|
323
|
+
end
|
324
|
+
|
325
|
+
def favorites(user, opts = {})
|
326
|
+
favorite_url = "/feeds/api/users/%s/favorites#{opts.empty? ? '' : '?#{opts.to_param}'}" % (user ? user : "default")
|
327
|
+
response = yt_session.get(favorite_url)
|
418
328
|
return YouTubeIt::Parser::VideosFeedParser.new(response.body).parse
|
419
329
|
end
|
420
330
|
|
421
331
|
def get_current_user
|
422
332
|
current_user_url = "/feeds/api/users/default"
|
423
|
-
response
|
424
|
-
if @access_token.nil?
|
425
|
-
http_connection do |session|
|
426
|
-
response = session.get2(current_user_url, authorization_headers)
|
427
|
-
end
|
428
|
-
else
|
429
|
-
response = @access_token.get("http://gdata.youtube.com/feeds/api/users/default")
|
430
|
-
end
|
333
|
+
response = yt_session.get(current_user_url, authorization_headers)
|
431
334
|
|
432
|
-
|
433
|
-
REXML::Document.new(response.body).elements["entry"].elements['author'].elements['name'].text
|
335
|
+
return REXML::Document.new(response.body).elements["entry"].elements['author'].elements['name'].text
|
434
336
|
end
|
435
337
|
|
436
338
|
private
|
437
339
|
|
438
340
|
def uploads_url
|
439
|
-
["uploads", base_url].join('.')
|
341
|
+
["http://uploads", base_url.sub("http://","")].join('.')
|
440
342
|
end
|
441
343
|
|
442
344
|
def base_url
|
443
|
-
"gdata.youtube.com"
|
345
|
+
"http://gdata.youtube.com"
|
444
346
|
end
|
445
347
|
|
446
348
|
def boundary
|
447
349
|
"An43094fu"
|
448
350
|
end
|
449
351
|
|
450
|
-
def authorization_headers_for_oauth
|
451
|
-
{
|
452
|
-
"X-GData-Client" => "#{@client_id}",
|
453
|
-
"X-GData-Key" => "key=#{@dev_key}",
|
454
|
-
"GData-Version" => "2",
|
455
|
-
}
|
456
|
-
end
|
457
|
-
|
458
352
|
def authorization_headers
|
459
353
|
header = {
|
460
354
|
"X-GData-Client" => "#{@client_id}",
|
@@ -463,42 +357,12 @@ class YouTubeIt
|
|
463
357
|
}
|
464
358
|
if @authsub_token
|
465
359
|
header.merge!("Authorization" => "AuthSub token=#{@authsub_token}")
|
466
|
-
|
360
|
+
elsif @access_token.nil? && @authsub_token.nil?
|
467
361
|
header.merge!("Authorization" => "GoogleLogin auth=#{auth_token}")
|
468
362
|
end
|
469
363
|
header
|
470
364
|
end
|
471
365
|
|
472
|
-
def parse_upload_error_from(string)
|
473
|
-
begin
|
474
|
-
REXML::Document.new(string).elements["//errors"].inject('') do | all_faults, error|
|
475
|
-
if error.elements["internalReason"]
|
476
|
-
msg_error = error.elements["internalReason"].text
|
477
|
-
elsif error.elements["location"]
|
478
|
-
msg_error = error.elements["location"].text[/media:group\/media:(.*)\/text\(\)/,1]
|
479
|
-
else
|
480
|
-
msg_error = "Unspecified error"
|
481
|
-
end
|
482
|
-
code = error.elements["code"].text if error.elements["code"]
|
483
|
-
all_faults + sprintf("%s: %s\n", msg_error, code)
|
484
|
-
end
|
485
|
-
rescue
|
486
|
-
string[/<TITLE>(.+)<\/TITLE>/, 1] || string
|
487
|
-
end
|
488
|
-
end
|
489
|
-
|
490
|
-
def raise_on_faulty_response(response)
|
491
|
-
response_code = response.code.to_i
|
492
|
-
msg = parse_upload_error_from(response.body.gsub(/\n/, ''))
|
493
|
-
|
494
|
-
if response_code == 403 || response_code == 401
|
495
|
-
#if response_code / 10 == 40
|
496
|
-
raise AuthenticationError, msg
|
497
|
-
elsif response_code / 10 != 20 # Response in 20x means success
|
498
|
-
raise UploadError, msg
|
499
|
-
end
|
500
|
-
end
|
501
|
-
|
502
366
|
def uploaded_video_id_from(string)
|
503
367
|
xml = REXML::Document.new(string)
|
504
368
|
xml.elements["//id"].text[/videos\/(.+)/, 1]
|
@@ -526,11 +390,10 @@ class YouTubeIt
|
|
526
390
|
|
527
391
|
def auth_token
|
528
392
|
@auth_token ||= begin
|
529
|
-
http
|
530
|
-
http.use_ssl = true
|
393
|
+
http = Faraday.new("https://www.google.com")
|
531
394
|
body = "Email=#{YouTubeIt.esc @user}&Passwd=#{YouTubeIt.esc @password}&service=youtube&source=#{YouTubeIt.esc @client_id}"
|
532
395
|
response = http.post("/youtube/accounts/ClientLogin", body, "Content-Type" => "application/x-www-form-urlencoded")
|
533
|
-
raise
|
396
|
+
raise ::AuthenticationError.new(response.body[/Error=(.+)/,1], response.status.to_i) if response.status.to_i != 200
|
534
397
|
@auth_token = response.body[/Auth=(.+)/, 1]
|
535
398
|
end
|
536
399
|
end
|
@@ -564,6 +427,11 @@ class YouTubeIt
|
|
564
427
|
b.entry(:xmlns => "http://www.w3.org/2005/Atom", 'xmlns:yt' => "http://gdata.youtube.com/schemas/2007") do | m |
|
565
428
|
m.content(data[:comment]) if data[:comment]
|
566
429
|
m.id(data[:favorite] || data[:playlist]) if data[:favorite] || data[:playlist]
|
430
|
+
m.tag!("yt:rating", :value => data[:rating]) if data[:rating]
|
431
|
+
if(data[:subscribe])
|
432
|
+
m.category(:scheme => "http://gdata.youtube.com/schemas/2007/subscriptiontypes.cat", :term => "channel")
|
433
|
+
m.tag!("yt:username", data[:subscribe])
|
434
|
+
end
|
567
435
|
end.to_s
|
568
436
|
end
|
569
437
|
|
@@ -599,11 +467,12 @@ class YouTubeIt
|
|
599
467
|
end
|
600
468
|
end
|
601
469
|
|
602
|
-
def
|
603
|
-
|
604
|
-
|
605
|
-
|
606
|
-
|
470
|
+
def yt_session(url = nil)
|
471
|
+
Faraday.new(:url => url ? url : base_url) do |builder|
|
472
|
+
builder.use Faraday::Request::OAuth, @config_token if @config_token
|
473
|
+
builder.use Faraday::Request::AuthHeader, authorization_headers
|
474
|
+
builder.use Faraday::Response::YouTubeIt
|
475
|
+
builder.adapter Faraday.default_adapter
|
607
476
|
end
|
608
477
|
end
|
609
478
|
end
|