vibedeck-youtube_it 0.0.1
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.rdoc +234 -0
- data/Rakefile +35 -0
- data/lib/youtube_it/chain_io.rb +76 -0
- data/lib/youtube_it/client.rb +367 -0
- data/lib/youtube_it/middleware/faraday_authheader.rb +24 -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/author.rb +13 -0
- data/lib/youtube_it/model/category.rb +11 -0
- data/lib/youtube_it/model/comment.rb +16 -0
- data/lib/youtube_it/model/contact.rb +16 -0
- data/lib/youtube_it/model/content.rb +18 -0
- data/lib/youtube_it/model/playlist.rb +11 -0
- data/lib/youtube_it/model/rating.rb +23 -0
- data/lib/youtube_it/model/subscription.rb +7 -0
- data/lib/youtube_it/model/thumbnail.rb +17 -0
- data/lib/youtube_it/model/user.rb +26 -0
- data/lib/youtube_it/model/video.rb +225 -0
- data/lib/youtube_it/parser.rb +357 -0
- data/lib/youtube_it/record.rb +12 -0
- data/lib/youtube_it/request/base_search.rb +72 -0
- data/lib/youtube_it/request/error.rb +15 -0
- data/lib/youtube_it/request/standard_search.rb +43 -0
- data/lib/youtube_it/request/user_search.rb +47 -0
- data/lib/youtube_it/request/video_search.rb +102 -0
- data/lib/youtube_it/request/video_upload.rb +415 -0
- data/lib/youtube_it/response/video_search.rb +41 -0
- data/lib/youtube_it/version.rb +4 -0
- data/lib/youtube_it.rb +75 -0
- data/test/helper.rb +10 -0
- data/test/test_chain_io.rb +63 -0
- data/test/test_client.rb +418 -0
- data/test/test_field_search.rb +48 -0
- data/test/test_video.rb +43 -0
- data/test/test_video_feed_parser.rb +271 -0
- data/test/test_video_search.rb +141 -0
- metadata +150 -0
@@ -0,0 +1,43 @@
|
|
1
|
+
class YouTubeIt
|
2
|
+
module Request #:nodoc:
|
3
|
+
class StandardSearch < BaseSearch #:nodoc:
|
4
|
+
attr_reader :max_results # max_results
|
5
|
+
attr_reader :order_by # orderby, ([relevance], viewCount, published, rating)
|
6
|
+
attr_reader :offset # start-index
|
7
|
+
attr_reader :time # time
|
8
|
+
|
9
|
+
TYPES = [ :top_rated, :top_favorites, :most_viewed, :most_popular,
|
10
|
+
:most_recent, :most_discussed, :most_linked, :most_responded,
|
11
|
+
:recently_featured, :watch_on_mobile ]
|
12
|
+
|
13
|
+
def initialize(type, options={})
|
14
|
+
@dev_key = options[:dev_key] if options[:dev_key]
|
15
|
+
if TYPES.include?(type)
|
16
|
+
@max_results, @order_by, @offset, @time = nil
|
17
|
+
set_instance_variables(options)
|
18
|
+
@url = base_url + type.to_s << build_query_params(to_youtube_params)
|
19
|
+
else
|
20
|
+
raise "Invalid type, must be one of: #{ TYPES.map { |t| t.to_s }.join(", ") }"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def base_url
|
27
|
+
super << "standardfeeds/"
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_youtube_params
|
31
|
+
{
|
32
|
+
'max-results' => @max_results,
|
33
|
+
'orderby' => @order_by,
|
34
|
+
'start-index' => @offset,
|
35
|
+
'time' => @time,
|
36
|
+
'v' => 2
|
37
|
+
}
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
class YouTubeIt
|
2
|
+
module Request #:nodoc:
|
3
|
+
class UserSearch < BaseSearch #:nodoc:
|
4
|
+
include FieldSearch
|
5
|
+
attr_reader :max_results # max_results
|
6
|
+
attr_reader :order_by # orderby, ([relevance], viewCount, published, rating)
|
7
|
+
attr_reader :offset # start-index
|
8
|
+
|
9
|
+
def initialize(params, options={})
|
10
|
+
@max_results, @order_by, @offset = nil
|
11
|
+
@url = base_url
|
12
|
+
@dev_key = options[:dev_key] if options[:dev_key]
|
13
|
+
if params == :favorites
|
14
|
+
@url << "#{options[:user]}/favorites"
|
15
|
+
set_instance_variables(options)
|
16
|
+
elsif params[:user] && options[:favorites]
|
17
|
+
@url << "#{params[:user]}/favorites"
|
18
|
+
set_instance_variables(params)
|
19
|
+
return
|
20
|
+
elsif params[:user]
|
21
|
+
@url << "#{params[:user]}/uploads"
|
22
|
+
set_instance_variables(params)
|
23
|
+
end
|
24
|
+
|
25
|
+
@url << build_query_params(to_youtube_params)
|
26
|
+
@url << fields_to_params(params.delete(:fields)) if params != :favorites && params[:fields]
|
27
|
+
end
|
28
|
+
|
29
|
+
private
|
30
|
+
|
31
|
+
def base_url
|
32
|
+
super << "users/"
|
33
|
+
end
|
34
|
+
|
35
|
+
def to_youtube_params
|
36
|
+
{
|
37
|
+
'max-results' => @max_results,
|
38
|
+
'orderby' => @order_by,
|
39
|
+
'start-index' => @offset,
|
40
|
+
'v' => 2
|
41
|
+
}
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
@@ -0,0 +1,102 @@
|
|
1
|
+
class YouTubeIt
|
2
|
+
module Request #:nodoc:
|
3
|
+
class VideoSearch < BaseSearch #:nodoc:
|
4
|
+
include FieldSearch
|
5
|
+
|
6
|
+
# From here: http://code.google.com/apis/youtube/reference.html#yt_format
|
7
|
+
ONLY_EMBEDDABLE = 5
|
8
|
+
|
9
|
+
attr_reader :max_results # max_results
|
10
|
+
attr_reader :order_by # orderby, ([relevance], viewCount, published, rating)
|
11
|
+
attr_reader :offset # start-index
|
12
|
+
attr_reader :query # vq
|
13
|
+
attr_reader :response_format # alt, ([atom], rss, json)
|
14
|
+
attr_reader :tags # /-/tag1/tag2
|
15
|
+
attr_reader :categories # /-/Category1/Category2
|
16
|
+
attr_reader :video_format # format (1=mobile devices)
|
17
|
+
attr_reader :racy # racy ([exclude], include)
|
18
|
+
attr_reader :author
|
19
|
+
attr_reader :lang # lt
|
20
|
+
|
21
|
+
def initialize(params={})
|
22
|
+
# Initialize our various member data to avoid warnings and so we'll
|
23
|
+
# automatically fall back to the youtube api defaults
|
24
|
+
@max_results, @order_by,
|
25
|
+
@offset, @query,
|
26
|
+
@response_format, @video_format,
|
27
|
+
@racy, @author, @lang = nil
|
28
|
+
@url = base_url
|
29
|
+
@dev_key = params[:dev_key] if params[:dev_key]
|
30
|
+
|
31
|
+
# Return a single video (base_url + /T7YazwP8GtY)
|
32
|
+
return @url << "/" << params[:video_id] << "?v=2" if params[:video_id]
|
33
|
+
|
34
|
+
@url << "/-/" if (params[:categories] || params[:tags])
|
35
|
+
@url << categories_to_params(params.delete(:categories)) if params[:categories]
|
36
|
+
@url << tags_to_params(params.delete(:tags)) if params[:tags]
|
37
|
+
|
38
|
+
set_instance_variables(params)
|
39
|
+
|
40
|
+
if( params[ :only_embeddable ] )
|
41
|
+
@video_format = ONLY_EMBEDDABLE
|
42
|
+
end
|
43
|
+
|
44
|
+
@url << build_query_params(to_youtube_params)
|
45
|
+
@url << fields_to_params(params.delete(:fields)) if params[:fields]
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def base_url
|
51
|
+
super << "videos"
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_youtube_params
|
55
|
+
{
|
56
|
+
'max-results' => @max_results,
|
57
|
+
'orderby' => @order_by,
|
58
|
+
'start-index' => @offset,
|
59
|
+
'v' => 2,
|
60
|
+
'q' => @query,
|
61
|
+
'alt' => @response_format,
|
62
|
+
'format' => @video_format,
|
63
|
+
'racy' => @racy,
|
64
|
+
'author' => @author,
|
65
|
+
'lr' => @lang
|
66
|
+
}
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
# Convert category symbols into strings and build the URL. GData requires categories to be capitalized.
|
71
|
+
# Categories defined like: categories => { :include => [:news], :exclude => [:sports], :either => [..] }
|
72
|
+
# or like: categories => [:news, :sports]
|
73
|
+
def categories_to_params(categories)
|
74
|
+
if categories.respond_to?(:keys) and categories.respond_to?(:[])
|
75
|
+
s = ""
|
76
|
+
s << categories[:either].map { |c| c.to_s.capitalize }.join("%7C") << '/' if categories[:either]
|
77
|
+
s << categories[:include].map { |c| c.to_s.capitalize }.join("/") << '/' if categories[:include]
|
78
|
+
s << ("-" << categories[:exclude].map { |c| c.to_s.capitalize }.join("/-")) << '/' if categories[:exclude]
|
79
|
+
s
|
80
|
+
else
|
81
|
+
categories.map { |c| c.to_s.capitalize }.join("/") << '/'
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Tags defined like: tags => { :include => [:football], :exclude => [:soccer], :either => [:polo, :tennis] }
|
86
|
+
# or tags => [:football, :soccer]
|
87
|
+
def tags_to_params(tags)
|
88
|
+
if tags.respond_to?(:keys) and tags.respond_to?(:[])
|
89
|
+
s = ""
|
90
|
+
s << tags[:either].map { |t| YouTubeIt.esc(t.to_s) }.join("%7C") << '/' if tags[:either]
|
91
|
+
s << tags[:include].map { |t| YouTubeIt.esc(t.to_s) }.join("/") << '/' if tags[:include]
|
92
|
+
s << ("-" << tags[:exclude].map { |t| YouTubeIt.esc(t.to_s) }.join("/-")) << '/' if tags[:exclude]
|
93
|
+
s
|
94
|
+
else
|
95
|
+
tags.map { |t| YouTubeIt.esc(t.to_s) }.join("/") << '/'
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
@@ -0,0 +1,415 @@
|
|
1
|
+
class YouTubeIt
|
2
|
+
module Upload
|
3
|
+
class VideoUpload
|
4
|
+
include YouTubeIt::Logging
|
5
|
+
|
6
|
+
def initialize *params
|
7
|
+
if params.first.is_a?(Hash)
|
8
|
+
hash_options = params.first
|
9
|
+
@user = hash_options[:username]
|
10
|
+
@password = hash_options[:password]
|
11
|
+
@dev_key = hash_options[:dev_key]
|
12
|
+
@access_token = hash_options[:access_token]
|
13
|
+
@authsub_token = hash_options[:authsub_token]
|
14
|
+
@client_id = hash_options[:client_id] || "youtube_it"
|
15
|
+
@config_token = hash_options[:config_token]
|
16
|
+
else
|
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')"
|
18
|
+
@user = params.shift
|
19
|
+
@password = params.shift
|
20
|
+
@dev_key = params.shift
|
21
|
+
@access_token = params.shift
|
22
|
+
@authsub_token = params.shift
|
23
|
+
@client_id = params.shift || "youtube_it"
|
24
|
+
@config_token = params.shift
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
|
29
|
+
def enable_http_debugging
|
30
|
+
@http_debugging = true
|
31
|
+
end
|
32
|
+
|
33
|
+
#
|
34
|
+
# Upload "data" to youtube, where data is either an IO object or
|
35
|
+
# raw file data.
|
36
|
+
# The hash keys for opts (which specify video info) are as follows:
|
37
|
+
# :mime_type
|
38
|
+
# :filename
|
39
|
+
# :title
|
40
|
+
# :description
|
41
|
+
# :category
|
42
|
+
# :keywords
|
43
|
+
# :private
|
44
|
+
# New V2 api hash keys for accessControl:
|
45
|
+
# :rate
|
46
|
+
# :comment
|
47
|
+
# :commentVote
|
48
|
+
# :videoRespond
|
49
|
+
# :list
|
50
|
+
# :embed
|
51
|
+
# :syndicate
|
52
|
+
# Specifying :private will make the video private, otherwise it will be public.
|
53
|
+
#
|
54
|
+
# When one of the fields is invalid according to YouTube,
|
55
|
+
# an UploadError will be raised. Its message contains a list of newline separated
|
56
|
+
# errors, containing the key and its error code.
|
57
|
+
#
|
58
|
+
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
59
|
+
def upload(data, opts = {})
|
60
|
+
@opts = { :mime_type => 'video/mp4',
|
61
|
+
:title => '',
|
62
|
+
:description => '',
|
63
|
+
:category => '',
|
64
|
+
:keywords => [] }.merge(opts)
|
65
|
+
|
66
|
+
@opts[:filename] ||= generate_uniq_filename_from(data)
|
67
|
+
|
68
|
+
post_body_io = generate_upload_io(video_xml, data)
|
69
|
+
|
70
|
+
upload_header = {
|
71
|
+
"Slug" => "#{@opts[:filename]}",
|
72
|
+
"Content-Type" => "multipart/related; boundary=#{boundary}",
|
73
|
+
"Content-Length" => "#{post_body_io.expected_length}",
|
74
|
+
}
|
75
|
+
|
76
|
+
upload_url = "/feeds/api/users/default/uploads"
|
77
|
+
response = yt_session(uploads_url).post(upload_url, post_body_io, upload_header)
|
78
|
+
|
79
|
+
return YouTubeIt::Parser::VideoFeedParser.new(response.body).parse
|
80
|
+
end
|
81
|
+
|
82
|
+
# Updates a video in YouTube. Requires:
|
83
|
+
# :title
|
84
|
+
# :description
|
85
|
+
# :category
|
86
|
+
# :keywords
|
87
|
+
# The following are optional attributes:
|
88
|
+
# :private
|
89
|
+
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
90
|
+
def update(video_id, options)
|
91
|
+
@opts = options
|
92
|
+
update_body = video_xml
|
93
|
+
update_url = "/feeds/api/users/default/uploads/%s" % video_id
|
94
|
+
response = yt_session.put(update_url, update_body)
|
95
|
+
|
96
|
+
return YouTubeIt::Parser::VideoFeedParser.new(response.body).parse
|
97
|
+
end
|
98
|
+
|
99
|
+
# Fetches the data of a video, which may be private. The video must be owned by this user.
|
100
|
+
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
101
|
+
def get_my_video(video_id)
|
102
|
+
get_url = "/feeds/api/users/default/uploads/%s" % video_id
|
103
|
+
response = yt_session.get(get_url)
|
104
|
+
|
105
|
+
return YouTubeIt::Parser::VideoFeedParser.new(response.body).parse
|
106
|
+
end
|
107
|
+
|
108
|
+
# Fetches the data of the videos of the current user, which may be private.
|
109
|
+
# When the authentication credentials are incorrect, an AuthenticationError will be raised.
|
110
|
+
def get_my_videos(opts)
|
111
|
+
max_results = opts[:per_page] || 50
|
112
|
+
start_index = ((opts[:page] || 1) -1) * max_results +1
|
113
|
+
get_url = "/feeds/api/users/default/uploads?max-results=#{max_results}&start-index=#{start_index}"
|
114
|
+
response = yt_session.get(get_url)
|
115
|
+
|
116
|
+
return YouTubeIt::Parser::VideosFeedParser.new(response.body).parse
|
117
|
+
end
|
118
|
+
|
119
|
+
# Delete a video on YouTube
|
120
|
+
def delete(video_id)
|
121
|
+
delete_url = "/feeds/api/users/default/uploads/%s" % video_id
|
122
|
+
response = yt_session.delete(delete_url)
|
123
|
+
|
124
|
+
return true
|
125
|
+
end
|
126
|
+
|
127
|
+
def get_upload_token(options, nexturl)
|
128
|
+
@opts = options
|
129
|
+
token_body = video_xml
|
130
|
+
token_url = "/action/GetUploadToken"
|
131
|
+
response = yt_session.post(token_url, token_body)
|
132
|
+
|
133
|
+
return {:url => "#{response.body[/<url>(.+)<\/url>/, 1]}?nexturl=#{nexturl}",
|
134
|
+
:token => response.body[/<token>(.+)<\/token>/, 1]}
|
135
|
+
end
|
136
|
+
|
137
|
+
def add_comment(video_id, comment)
|
138
|
+
comment_body = video_xml_for(:comment => comment)
|
139
|
+
comment_url = "/feeds/api/videos/%s/comments" % video_id
|
140
|
+
response = yt_session.post(comment_url, comment_body)
|
141
|
+
|
142
|
+
return {:code => response.status, :body => response.body}
|
143
|
+
end
|
144
|
+
|
145
|
+
def comments(video_id, opts = {})
|
146
|
+
comment_url = "/feeds/api/videos/%s/comments?" % video_id
|
147
|
+
comment_url << opts.collect { |k,p| [k,p].join '=' }.join('&')
|
148
|
+
response = yt_session.get(comment_url)
|
149
|
+
|
150
|
+
return YouTubeIt::Parser::CommentsFeedParser.new(response).parse
|
151
|
+
end
|
152
|
+
|
153
|
+
def add_favorite(video_id)
|
154
|
+
favorite_body = video_xml_for(:favorite => video_id)
|
155
|
+
favorite_url = "/feeds/api/users/default/favorites"
|
156
|
+
response = yt_session.post(favorite_url, favorite_body)
|
157
|
+
|
158
|
+
return {:code => response.status, :body => response.body}
|
159
|
+
end
|
160
|
+
|
161
|
+
def delete_favorite(video_id)
|
162
|
+
favorite_header = {
|
163
|
+
"GData-Version" => "1",
|
164
|
+
}
|
165
|
+
favorite_url = "/feeds/api/users/default/favorites/%s" % video_id
|
166
|
+
response = yt_session.delete(favorite_url, favorite_header)
|
167
|
+
|
168
|
+
return true
|
169
|
+
end
|
170
|
+
|
171
|
+
def profile(user)
|
172
|
+
profile_url = "/feeds/api/users/%s?v=2" % (user ? user : "default")
|
173
|
+
response = yt_session.get(profile_url)
|
174
|
+
|
175
|
+
return YouTubeIt::Parser::ProfileFeedParser.new(response).parse
|
176
|
+
end
|
177
|
+
|
178
|
+
def playlist(playlist_id)
|
179
|
+
playlist_url = "/feeds/api/playlists/%s?v=2" % playlist_id
|
180
|
+
response = yt_session.get(playlist_url)
|
181
|
+
|
182
|
+
return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
|
183
|
+
end
|
184
|
+
|
185
|
+
def playlists(user)
|
186
|
+
playlist_url = "/feeds/api/users/%s/playlists?v=2" % (user ? user : "default")
|
187
|
+
response = yt_session.get(playlist_url)
|
188
|
+
|
189
|
+
return YouTubeIt::Parser::PlaylistsFeedParser.new(response).parse
|
190
|
+
end
|
191
|
+
|
192
|
+
def add_playlist(options)
|
193
|
+
playlist_body = video_xml_for_playlist(options)
|
194
|
+
playlist_url = "/feeds/api/users/default/playlists"
|
195
|
+
response = yt_session.post(playlist_url, playlist_body)
|
196
|
+
|
197
|
+
return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
|
198
|
+
end
|
199
|
+
|
200
|
+
def add_video_to_playlist(playlist_id, video_id)
|
201
|
+
playlist_body = video_xml_for(:playlist => video_id)
|
202
|
+
playlist_url = "/feeds/api/playlists/%s" % playlist_id
|
203
|
+
response = yt_session.post(playlist_url, playlist_body)
|
204
|
+
|
205
|
+
return {:code => response.status, :body => response.body, :playlist_entry_id => playlist_entry_id_from_playlist(response.body)}
|
206
|
+
end
|
207
|
+
|
208
|
+
def update_playlist(playlist_id, options)
|
209
|
+
playlist_body = video_xml_for_playlist(options)
|
210
|
+
playlist_url = "/feeds/api/users/default/playlists/%s" % playlist_id
|
211
|
+
response = yt_session.put(playlist_url, playlist_body)
|
212
|
+
|
213
|
+
return YouTubeIt::Parser::PlaylistFeedParser.new(response).parse
|
214
|
+
end
|
215
|
+
|
216
|
+
def delete_video_from_playlist(playlist_id, playlist_entry_id)
|
217
|
+
playlist_url = "/feeds/api/playlists/%s/%s" % [playlist_id, playlist_entry_id]
|
218
|
+
response = yt_session.delete(playlist_url)
|
219
|
+
|
220
|
+
return true
|
221
|
+
end
|
222
|
+
|
223
|
+
def delete_playlist(playlist_id)
|
224
|
+
playlist_url = "/feeds/api/users/default/playlists/%s" % playlist_id
|
225
|
+
response = yt_session.delete(playlist_url)
|
226
|
+
|
227
|
+
return true
|
228
|
+
end
|
229
|
+
|
230
|
+
def rate_video(video_id, rating)
|
231
|
+
rating_body = video_xml_for(:rating => rating)
|
232
|
+
rating_url = "/feeds/api/videos/#{video_id}/ratings"
|
233
|
+
response = yt_session.post(rating_url, rating_body)
|
234
|
+
|
235
|
+
return {:code => response.status, :body => response.body}
|
236
|
+
end
|
237
|
+
|
238
|
+
def subscriptions(user)
|
239
|
+
subscription_url = "/feeds/api/users/%s/subscriptions?v=2" % (user ? user : "default")
|
240
|
+
response = yt_session.get(subscription_url)
|
241
|
+
|
242
|
+
return YouTubeIt::Parser::SubscriptionFeedParser.new(response).parse
|
243
|
+
end
|
244
|
+
|
245
|
+
def subscribe_channel(channel_name)
|
246
|
+
subscribe_body = video_xml_for(:subscribe => channel_name)
|
247
|
+
subscribe_url = "/feeds/api/users/default/subscriptions"
|
248
|
+
response = yt_session.post(subscribe_url, subscribe_body)
|
249
|
+
|
250
|
+
return {:code => response.status, :body => response.body}
|
251
|
+
end
|
252
|
+
|
253
|
+
def unsubscribe_channel(subscription_id)
|
254
|
+
unsubscribe_url = "/feeds/api/users/default/subscriptions/%s" % subscription_id
|
255
|
+
response = yt_session.delete(unsubscribe_url)
|
256
|
+
|
257
|
+
return {:code => response.status, :body => response.body}
|
258
|
+
end
|
259
|
+
|
260
|
+
def favorites(user, opts = {})
|
261
|
+
favorite_url = "/feeds/api/users/%s/favorites#{opts.empty? ? '' : '?#{opts.to_param}'}" % (user ? user : "default")
|
262
|
+
response = yt_session.get(favorite_url)
|
263
|
+
|
264
|
+
return YouTubeIt::Parser::VideosFeedParser.new(response.body).parse
|
265
|
+
end
|
266
|
+
|
267
|
+
def get_current_user
|
268
|
+
current_user_url = "/feeds/api/users/default"
|
269
|
+
response = yt_session.get(current_user_url, authorization_headers)
|
270
|
+
|
271
|
+
return REXML::Document.new(response.body).elements["entry"].elements['author'].elements['name'].text
|
272
|
+
end
|
273
|
+
|
274
|
+
private
|
275
|
+
|
276
|
+
def uploads_url
|
277
|
+
["http://uploads", base_url.sub("http://","")].join('.')
|
278
|
+
end
|
279
|
+
|
280
|
+
def base_url
|
281
|
+
"http://gdata.youtube.com"
|
282
|
+
end
|
283
|
+
|
284
|
+
def boundary
|
285
|
+
"An43094fu"
|
286
|
+
end
|
287
|
+
|
288
|
+
def authorization_headers
|
289
|
+
header = {
|
290
|
+
"X-GData-Client" => "#{@client_id}",
|
291
|
+
"X-GData-Key" => "key=#{@dev_key}",
|
292
|
+
}
|
293
|
+
if @authsub_token
|
294
|
+
header.merge!("Authorization" => "AuthSub token=#{@authsub_token}")
|
295
|
+
elsif @access_token.nil? && @authsub_token.nil?
|
296
|
+
header.merge!("Authorization" => "GoogleLogin auth=#{auth_token}")
|
297
|
+
end
|
298
|
+
header
|
299
|
+
end
|
300
|
+
|
301
|
+
def uploaded_video_id_from(string)
|
302
|
+
xml = REXML::Document.new(string)
|
303
|
+
xml.elements["//id"].text[/videos\/(.+)/, 1]
|
304
|
+
end
|
305
|
+
|
306
|
+
def playlist_id_from(string)
|
307
|
+
xml = REXML::Document.new(string)
|
308
|
+
entry = xml.elements["entry"]
|
309
|
+
entry.elements["id"].text[/playlist([^<]+)/, 1].sub(':','')
|
310
|
+
end
|
311
|
+
|
312
|
+
# If data can be read, use the first 1024 bytes as filename. If data
|
313
|
+
# is a file, use path. If data is a string, checksum it
|
314
|
+
def generate_uniq_filename_from(data)
|
315
|
+
if data.respond_to?(:path)
|
316
|
+
Digest::MD5.hexdigest(data.path)
|
317
|
+
elsif data.respond_to?(:read)
|
318
|
+
chunk = data.read(1024)
|
319
|
+
data.rewind
|
320
|
+
Digest::MD5.hexdigest(chunk)
|
321
|
+
else
|
322
|
+
Digest::MD5.hexdigest(data)
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def auth_token
|
327
|
+
@auth_token ||= begin
|
328
|
+
http = Faraday.new("https://www.google.com")
|
329
|
+
body = "Email=#{YouTubeIt.esc @user}&Passwd=#{YouTubeIt.esc @password}&service=youtube&source=#{YouTubeIt.esc @client_id}"
|
330
|
+
response = http.post("/youtube/accounts/ClientLogin", body, "Content-Type" => "application/x-www-form-urlencoded")
|
331
|
+
raise ::AuthenticationError.new(response.body[/Error=(.+)/,1], response.status.to_i) if response.status.to_i != 200
|
332
|
+
@auth_token = response.body[/Auth=(.+)/, 1]
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
# TODO: isn't there a cleaner way to output top-notch XML without requiring stuff all over the place?
|
337
|
+
def video_xml
|
338
|
+
b = Builder::XmlMarkup.new
|
339
|
+
b.instruct!
|
340
|
+
b.entry(:xmlns => "http://www.w3.org/2005/Atom", 'xmlns:media' => "http://search.yahoo.com/mrss/", 'xmlns:yt' => "http://gdata.youtube.com/schemas/2007") do | m |
|
341
|
+
m.tag!("media:group") do | mg |
|
342
|
+
mg.tag!("media:title", @opts[:title], :type => "plain")
|
343
|
+
mg.tag!("media:description", @opts[:description], :type => "plain")
|
344
|
+
mg.tag!("media:keywords", @opts[:keywords].join(","))
|
345
|
+
mg.tag!('media:category', @opts[:category], :scheme => "http://gdata.youtube.com/schemas/2007/categories.cat")
|
346
|
+
mg.tag!('yt:private') if @opts[:private]
|
347
|
+
mg.tag!('media:category', @opts[:dev_tag], :scheme => "http://gdata.youtube.com/schemas/2007/developertags.cat") if @opts[:dev_tag]
|
348
|
+
end
|
349
|
+
m.tag!("yt:accessControl", :action => "rate", :permission => @opts[:rate]) if @opts[:rate]
|
350
|
+
m.tag!("yt:accessControl", :action => "comment", :permission => @opts[:comment]) if @opts[:comment]
|
351
|
+
m.tag!("yt:accessControl", :action => "commentVote", :permission => @opts[:commentVote]) if @opts[:commentVote]
|
352
|
+
m.tag!("yt:accessControl", :action => "videoRespond", :permission => @opts[:videoRespond]) if @opts[:videoRespond]
|
353
|
+
m.tag!("yt:accessControl", :action => "list", :permission => @opts[:list]) if @opts[:list]
|
354
|
+
m.tag!("yt:accessControl", :action => "embed", :permission => @opts[:embed]) if @opts[:embed]
|
355
|
+
m.tag!("yt:accessControl", :action => "syndicate", :permission => @opts[:syndicate]) if @opts[:syndicate]
|
356
|
+
end.to_s
|
357
|
+
end
|
358
|
+
|
359
|
+
def video_xml_for(data)
|
360
|
+
b = Builder::XmlMarkup.new
|
361
|
+
b.instruct!
|
362
|
+
b.entry(:xmlns => "http://www.w3.org/2005/Atom", 'xmlns:yt' => "http://gdata.youtube.com/schemas/2007") do | m |
|
363
|
+
m.content(data[:comment]) if data[:comment]
|
364
|
+
m.id(data[:favorite] || data[:playlist]) if data[:favorite] || data[:playlist]
|
365
|
+
m.tag!("yt:rating", :value => data[:rating]) if data[:rating]
|
366
|
+
if(data[:subscribe])
|
367
|
+
m.category(:scheme => "http://gdata.youtube.com/schemas/2007/subscriptiontypes.cat", :term => "channel")
|
368
|
+
m.tag!("yt:username", data[:subscribe])
|
369
|
+
end
|
370
|
+
end.to_s
|
371
|
+
end
|
372
|
+
|
373
|
+
def video_xml_for_playlist(data)
|
374
|
+
b = Builder::XmlMarkup.new
|
375
|
+
b.instruct!
|
376
|
+
b.entry(:xmlns => "http://www.w3.org/2005/Atom", 'xmlns:yt' => "http://gdata.youtube.com/schemas/2007") do | m |
|
377
|
+
m.title(data[:title]) if data[:title]
|
378
|
+
m.summary(data[:description] || data[:summary]) if data[:description] || data[:summary]
|
379
|
+
m.tag!('yt:private') if data[:private]
|
380
|
+
end.to_s
|
381
|
+
end
|
382
|
+
|
383
|
+
def generate_upload_io(video_xml, data)
|
384
|
+
post_body = [
|
385
|
+
"--#{boundary}\r\n",
|
386
|
+
"Content-Type: application/atom+xml; charset=UTF-8\r\n\r\n",
|
387
|
+
video_xml,
|
388
|
+
"\r\n--#{boundary}\r\n",
|
389
|
+
"Content-Type: #{@opts[:mime_type]}\r\nContent-Transfer-Encoding: binary\r\n\r\n",
|
390
|
+
data,
|
391
|
+
"\r\n--#{boundary}--\r\n",
|
392
|
+
]
|
393
|
+
|
394
|
+
# Use Greedy IO to not be limited by 1K chunks
|
395
|
+
YouTubeIt::GreedyChainIO.new(post_body)
|
396
|
+
end
|
397
|
+
|
398
|
+
def playlist_entry_id_from_playlist(string)
|
399
|
+
playlist_xml = REXML::Document.new(string)
|
400
|
+
playlist_xml.elements.each("/entry") do |item|
|
401
|
+
return item.elements["id"].text[/^.*:([^:]+)$/,1]
|
402
|
+
end
|
403
|
+
end
|
404
|
+
|
405
|
+
def yt_session(url = nil)
|
406
|
+
Faraday.new(:url => url ? url : base_url) do |builder|
|
407
|
+
builder.use Faraday::Request::OAuth, @config_token if @config_token
|
408
|
+
builder.use Faraday::Request::AuthHeader, authorization_headers
|
409
|
+
builder.use Faraday::Response::YouTubeIt
|
410
|
+
builder.adapter Faraday.default_adapter
|
411
|
+
end
|
412
|
+
end
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class YouTubeIt
|
2
|
+
module Response
|
3
|
+
class VideoSearch < YouTubeIt::Record
|
4
|
+
# *String*:: Unique feed identifying url.
|
5
|
+
attr_reader :feed_id
|
6
|
+
|
7
|
+
# *Fixnum*:: Number of results per page.
|
8
|
+
attr_reader :max_result_count
|
9
|
+
|
10
|
+
# *Fixnum*:: 1-based offset index into the full result set.
|
11
|
+
attr_reader :offset
|
12
|
+
|
13
|
+
# *Fixnum*:: Total number of results available for the original request.
|
14
|
+
attr_reader :total_result_count
|
15
|
+
|
16
|
+
# *Time*:: Date and time at which the feed was last updated
|
17
|
+
attr_reader :updated_at
|
18
|
+
|
19
|
+
# *Array*:: Array of YouTubeIt::Model::Video records
|
20
|
+
attr_reader :videos
|
21
|
+
|
22
|
+
def current_page
|
23
|
+
((offset - 1) / max_result_count) + 1
|
24
|
+
end
|
25
|
+
|
26
|
+
# current_page + 1 or nil if there is no next page
|
27
|
+
def next_page
|
28
|
+
current_page < total_pages ? (current_page + 1) : nil
|
29
|
+
end
|
30
|
+
|
31
|
+
# current_page - 1 or nil if there is no previous page
|
32
|
+
def previous_page
|
33
|
+
current_page > 1 ? (current_page - 1) : nil
|
34
|
+
end
|
35
|
+
|
36
|
+
def total_pages
|
37
|
+
(total_result_count / max_result_count.to_f).ceil
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|