msp-youtube-g 0.4.8.1 → 0.4.8.3

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.
@@ -1,4 +1,5 @@
1
- == trunk
1
+ == trunk
2
+ * Create a method to check the status of the uploaded video by status URL [msp]
2
3
  * Move network dependent tests to integration-test [msp]
3
4
  * Create a parser and a model for upload errors [msp]
4
5
  * Stub driven tests for VideoFeedParser, VideosFeedParser, UploadErrorParser [msp]
@@ -26,5 +26,5 @@ require File.dirname(__FILE__) + '/youtube_g/request/video_search'
26
26
  require File.dirname(__FILE__) + '/youtube_g/response/video_search'
27
27
 
28
28
  class YouTubeG #:nodoc:
29
- VERSION = '0.4.8.1'
29
+ VERSION = '0.4.8.3'
30
30
  end
@@ -61,16 +61,34 @@ class YouTubeG
61
61
  # Retrieves a single YouTube video.
62
62
  #
63
63
  # === Parameters
64
- # vid<String>:: The ID or URL of the video that you'd like to retrieve.
64
+ # vid<String>:: The ID or URL of the video that you'd like to retrieve. If your uncertain
65
+ # of the processed state use video_processed_by_youtube? first else this will fail.
65
66
  #
66
67
  # === Returns
67
68
  # YouTubeG::Model::Video
68
69
  def video_by(vid)
69
- video_id = vid =~ /^http/ ? vid : "http://gdata.youtube.com/feeds/videos/#{vid}"
70
+ video_id = vid =~ /^http/ ? vid : "http://gdata.youtube.com/feeds/api/videos/#{vid}"
70
71
  parser = YouTubeG::Parser::VideoFeedParser.new(video_id)
71
- parser.parse
72
- end
73
-
72
+ parser.parse
73
+ end
74
+
75
+ # Checks the status of a users uploaded video.
76
+ #
77
+ # === Parameters
78
+ # status_url<String>:: The URL returned by YT when the video was uploaded e.g.
79
+ #
80
+ # http://gdata.youtube.com/feeds/api/users/speechboxmatt/uploads/QR1sWOVqxoM
81
+ #
82
+ # This is stored on upload as Video.status_url
83
+ # === Returns
84
+ # Boolean
85
+ def video_by_status_url(status_url)
86
+ parser = YouTubeG::Parser::VideoFeedParser.new(status_url)
87
+ video = parser.parse
88
+ return video
89
+ end
90
+
91
+
74
92
  private
75
93
 
76
94
  def calculate_offset(page, per_page)
@@ -59,7 +59,10 @@ class YouTubeG
59
59
  # Used in video uploads only to check the state of the upload
60
60
  class AppControl < YouTubeG::Record
61
61
  attr_reader :draft
62
- attr_reader :state
62
+ attr_reader :state
63
+ attr_reader :reason
64
+ attr_reader :help_url
65
+ attr_reader :description
63
66
  end
64
67
 
65
68
  # *Fixnum*:: Duration of a video in seconds.
@@ -113,6 +116,9 @@ class YouTubeG
113
116
  # *String*:: The link to watch the URL on YouTubes website.
114
117
  attr_reader :player_url
115
118
 
119
+ # *String*:: The URL to check the processing status of the video at YT.
120
+ attr_reader :status_url
121
+
116
122
  # YouTubeG::Model::Rating:: Information about the videos rating.
117
123
  attr_reader :rating
118
124
 
@@ -187,6 +193,15 @@ EDOC
187
193
  # String: Absolute URL for embedding video
188
194
  def embed_url
189
195
  @player_url.sub('watch?', '').sub('=', '/')
196
+ end
197
+
198
+ # Has this vid been processed by YT?
199
+ #
200
+ # === Returns
201
+ # Boolean
202
+ def processed?
203
+ return true if self.app_control == nil
204
+ return false if self.app_control.state.upcase == 'PROCESSING' || self.app_control.state.upcase == 'REJECTED' || self.app_control.state.upcase == 'FAILED'
190
205
  end
191
206
 
192
207
  end
@@ -68,7 +68,10 @@ class YouTubeG
68
68
  if app_control_element
69
69
  app_control = YouTubeG::Model::Video::AppControl.new(
70
70
  :draft => app_control_element.elements["app:draft"].text,
71
- :state => app_control_element.elements["yt:state"].attributes["name"])
71
+ :state => app_control_element.elements["yt:state"].attributes["name"],
72
+ :reason => app_control_element.elements["yt:state"].attributes["reasonCode"],
73
+ :help_url => app_control_element.elements["yt:state"].attributes["helpUrl"],
74
+ :description => app_control_element.elements["yt:state"].text)
72
75
  end
73
76
 
74
77
  # parse the category and keyword lists
@@ -136,7 +139,12 @@ class YouTubeG
136
139
  view_count = (el = entry.elements["yt:statistics"]) ? el.attributes["viewCount"].to_i : 0
137
140
 
138
141
  noembed = entry.elements["yt:noembed"] ? true : false
139
- racy = entry.elements["media:rating"] ? true : false
142
+ racy = entry.elements["media:rating"] ? true : false
143
+
144
+ status_url = nil
145
+ entry.elements.each("link") do |el|
146
+ status_url = el.attributes["href"] if el.attributes["rel"] == "self"
147
+ end
140
148
 
141
149
  YouTubeG::Model::Video.new(
142
150
  :video_id => video_id,
@@ -156,7 +164,8 @@ class YouTubeG
156
164
  :rating => rating,
157
165
  :view_count => view_count,
158
166
  :noembed => noembed,
159
- :racy => racy)
167
+ :racy => racy,
168
+ :status_url => status_url)
160
169
  end
161
170
 
162
171
  def parse_media_content (media_content_element)
@@ -79,9 +79,13 @@ class YouTubeG
79
79
  response = upload.post(direct_upload_url, upload_body, upload_header)
80
80
  # todo parse this out also
81
81
  if response.code.to_i == 403
82
+ logger.error("ERROR: #{response.code}")
82
83
  raise AuthenticationError, response.body[/<TITLE>(.+)<\/TITLE>/, 1]
83
- elsif response.code.to_i != 201
84
+ elsif response.code.to_i != 201
85
+ logger.error("ERROR: #{response.code}")
86
+ logger.debug("response: #{response.body}")
84
87
  upload_errors = YouTubeG::Parser::UploadErrorParser.new(response.body).parse
88
+ logger.debug("upload_errors: #{upload_errors}")
85
89
  raise UploadError, upload_errors.inspect
86
90
  end
87
91
  return YouTubeG::Parser::VideoFeedParser.new(response.body).parse
@@ -0,0 +1,47 @@
1
+ <?xml version='1.0' encoding='UTF-8'?>
2
+ <entry xmlns='http://www.w3.org/2005/Atom' xmlns:gml='http://www.opengis.net/gml'
3
+ xmlns:georss='http://www.georss.org/georss' xmlns:media='http://search.yahoo.com/mrss/'
4
+ xmlns:batch='http://schemas.google.com/gdata/batch' xmlns:yt='http://gdata.youtube.com/schemas/2007'
5
+ xmlns:gd='http://schemas.google.com/g/2005'>
6
+ <id>http://gdata.youtube.com/feeds/api/videos/QR1sWOVqxoM</id>
7
+ <published>2008-08-22T04:07:16.000-07:00</published>
8
+ <updated>2008-08-22T04:07:16.000-07:00</updated>
9
+ <app:control xmlns:app='http://purl.org/atom/app#'>
10
+ <app:draft>yes</app:draft>
11
+ <yt:state name='rejected'
12
+ reasonCode='tooLong'
13
+ helpUrl='http://www.youtube.com/t/community_guidelines'>Video is too long.</yt:state>
14
+ </app:control>
15
+ <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='speechbox'/>
16
+ <category scheme='http://gdata.youtube.com/schemas/2007/categories.cat' term='Film' label='Film &amp; Animation'/>
17
+ <category scheme='http://schemas.google.com/g/2005#kind' term='http://gdata.youtube.com/schemas/2007#video'/>
18
+ <category scheme='http://gdata.youtube.com/schemas/2007/keywords.cat' term='wordia'/>
19
+ <title type='text'>discombobulate [08/22/2008 at 12:06PM]</title>
20
+ <content type='text'>To throw into a state of confusion; to befuddle or perplex</content>
21
+ <link rel='alternate' type='text/html' href='http://www.youtube.com/my_videos_edit?ns=1&amp;video_id=QR1sWOVqxoM'/>
22
+ <link rel='http://gdata.youtube.com/schemas/2007#video.responses' type='application/atom+xml'
23
+ href='http://gdata.youtube.com/feeds/api/videos/QR1sWOVqxoM/responses'/>
24
+ <link rel='http://gdata.youtube.com/schemas/2007#video.related' type='application/atom+xml'
25
+ href='http://gdata.youtube.com/feeds/api/videos/QR1sWOVqxoM/related'/>
26
+ <link rel='self' type='application/atom+xml'
27
+ href='http://gdata.youtube.com/feeds/api/users/speechboxmatt/uploads/QR1sWOVqxoM'/>
28
+ <author>
29
+ <name>speechboxmatt</name>
30
+ <uri>http://gdata.youtube.com/feeds/api/users/speechboxmatt</uri>
31
+ </author>
32
+ <media:group>
33
+ <media:title type='plain'>discombobulate [08/22/2008 at 12:06PM]</media:title>
34
+ <media:description type='plain'>To throw into a state of confusion; to befuddle or perplex</media:description>
35
+ <media:keywords>speechbox, wordia</media:keywords>
36
+ <yt:duration seconds='0'/>
37
+ <media:category label='Film &amp; Animation' scheme='http://gdata.youtube.com/schemas/2007/categories.cat'>
38
+ Film
39
+ </media:category>
40
+ <media:content url='http://www.youtube.com/v/QR1sWOVqxoM&amp;f=gdata_user_uploads'
41
+ type='application/x-shockwave-flash' medium='video' isDefault='true' expression='full'
42
+ yt:format='5'/>
43
+ </media:group>
44
+ <gd:comments>
45
+ <gd:feedLink href='http://gdata.youtube.com/feeds/api/videos/QR1sWOVqxoM/comments' countHint='0'/>
46
+ </gd:comments>
47
+ </entry>
@@ -4,7 +4,8 @@ require File.dirname(__FILE__) + '/../lib/youtube_g'
4
4
  class TestParser < Test::Unit::TestCase
5
5
 
6
6
  UPLOAD_XML = File.new(File.dirname(__FILE__) +"/upload.xml")
7
- SEARCH_XML = File.new(File.dirname(__FILE__) +"/search.xml")
7
+ SEARCH_XML = File.new(File.dirname(__FILE__) +"/search.xml")
8
+ YOUTUBE_STATUS_XML = File.new(File.dirname(__FILE__) +"/status.xml")
8
9
 
9
10
  YT_VALIDATION = "yt:validation"
10
11
  REQUIRED = "required"
@@ -18,6 +19,10 @@ class TestParser < Test::Unit::TestCase
18
19
  @vf_parser = YouTubeG::Parser::VideoFeedParser.new(UPLOAD_XML.path)
19
20
  @vf_parser_result = @vf_parser.parse
20
21
 
22
+ # video feed error
23
+ @vfe_parser = YouTubeG::Parser::VideoFeedParser.new(YOUTUBE_STATUS_XML.path)
24
+ @vfe_parser_result = @vfe_parser.parse
25
+
21
26
  @vfs_parser = YouTubeG::Parser::VideosFeedParser.new(SEARCH_XML.path)
22
27
  @vfs_parser_result = @vfs_parser.parse
23
28
  end
@@ -93,7 +98,9 @@ class TestParser < Test::Unit::TestCase
93
98
  assert_equal(320,video.thumbnails[3].width)
94
99
  assert_equal("00:00:07.500",video.thumbnails[3].time)
95
100
 
96
- assert_equal("http://www.youtube.com/watch?v=32_mQ0PRT9I",video.player_url)
101
+ assert_equal("http://www.youtube.com/watch?v=32_mQ0PRT9I",video.player_url)
102
+
103
+ assert_equal("http://gdata.youtube.com/feeds/api/users/speechboxmatt/uploads/32_mQ0PRT9I",video.status_url);
97
104
  end
98
105
  end
99
106
 
@@ -143,6 +150,21 @@ class TestParser < Test::Unit::TestCase
143
150
  assert_equal("application/x-shockwave-flash",video.media_content[0].mime_type)
144
151
  end
145
152
 
153
+ def test_video_feed_parser_with_error_status
154
+ video = @vfe_parser_result
155
+ assert_instance_of(YouTubeG::Model::Video, video)
156
+
157
+ assert_equal("http://gdata.youtube.com/feeds/api/videos/QR1sWOVqxoM",video.video_id)
158
+
159
+ # App control - state of the upload
160
+ assert_equal("yes", video.app_control.draft)
161
+ assert_equal("rejected", video.app_control.state)
162
+ # these populated only if errored
163
+ assert_equal("tooLong", video.app_control.reason)
164
+ assert_equal("http://www.youtube.com/t/community_guidelines", video.app_control.help_url)
165
+ assert_equal("Video is too long.", video.app_control.description)
166
+ end
167
+
146
168
  def well_formed_errors_xml
147
169
  errors_xml = ''
148
170
  errors_xml << "<?xml version='1.0' encoding='UTF-8'?>"
@@ -169,4 +191,4 @@ class TestParser < Test::Unit::TestCase
169
191
  errors_xml << " <location type='xpath'>media:group/media:title/text()</location>"
170
192
  errors_xml << "</errors>"
171
193
  end
172
- end
194
+ end
@@ -0,0 +1,26 @@
1
+ require 'test/unit'
2
+ require File.dirname(__FILE__) + '/../lib/youtube_g'
3
+
4
+ class TestVideo < Test::Unit::TestCase
5
+
6
+ def test_processed_method_for_nil_state
7
+ # we assume no app_control from YT means processed.
8
+ video = YouTubeG::Model::Video.new({})
9
+ assert_equal(true, video.processed?)
10
+ end
11
+
12
+ def test_processed_method_for_processing_state
13
+ video = YouTubeG::Model::Video.new({:app_control => YouTubeG::Model::Video::AppControl.new({:state => 'processing'})})
14
+ assert_equal(false, video.processed?)
15
+ end
16
+
17
+ def test_processed_method_for_rejected_state
18
+ video = YouTubeG::Model::Video.new({:app_control => YouTubeG::Model::Video::AppControl.new({:state => 'rejected'})})
19
+ assert_equal(false, video.processed?)
20
+ end
21
+
22
+ def test_processed_method_for_failed_state
23
+ video = YouTubeG::Model::Video.new({:app_control => YouTubeG::Model::Video::AppControl.new({:state => 'failed'})})
24
+ assert_equal(false, video.processed?)
25
+ end
26
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: msp-youtube-g
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.8.1
4
+ version: 0.4.8.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Shane Vitarana
@@ -58,9 +58,11 @@ files:
58
58
  - integration-test/test_video.rb
59
59
  - integration-test/test_video_search.rb
60
60
  - test/test_parser.rb
61
+ - test/test_video.rb
61
62
  - test/test_upload.rb
62
63
  - test/search.xml
63
64
  - test/upload.xml
65
+ - test/status.xml
64
66
  has_rdoc: true
65
67
  homepage: http://youtube-g.rubyforge.org/
66
68
  post_install_message: