yt 0.13.9 → 0.13.10

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: db12d589d5763d973797f580561eff7054db2643
4
- data.tar.gz: 2e73f17f11c03ae2b1e3805ba855fa7e84eb7610
3
+ metadata.gz: f38c80c9cc34b749849ed9934a037fe7707614ce
4
+ data.tar.gz: 2a9a06c9f14a9fabe19aa0d74e47e2a2fe2aaf6f
5
5
  SHA512:
6
- metadata.gz: 9cd65afd9ef4f2837604e2570fa3e404cec78fd183e9cb9cd0e7b0e1896c29cf53b50e2e210a1ed3d1688c747a9b12bffa93e14ed448c7efe35d7365beb34532
7
- data.tar.gz: e78d7b773dc49accb73b6e33c3285f5cde7dca2c49da55b071ccd9437df46d79a3c83df5c93ed211ba552d0a15c5a0b698c329860c8073eb6235bd113c89ae64
6
+ metadata.gz: d39da84ceb5bd9941ea7a90b32ca01e2c94d1aa101289ac7ff012728663f08279bf2d6e961761aeb3043f973fe9304109d4449b95c45e78126784bc7d278bdc4
7
+ data.tar.gz: fed816b3f0ef773b7b3f1050135c1a1e08b953ada0ba4aeafc69571c9e0f0408158190a20bcb4be0f4bca803a246254d2738502fed05ec4b4c61f512e09c585c
data/CHANGELOG.md CHANGED
@@ -6,6 +6,10 @@ For more information about changelogs, check
6
6
  [Keep a Changelog](http://keepachangelog.com) and
7
7
  [Vandamme](http://tech-angels.github.io/vandamme).
8
8
 
9
+ ## 0.13.9 - 2015-02-17
10
+
11
+ * [FEATURE] New `video.upload_thumbnail` to upload the thumbnail for a video.
12
+
9
13
  ## 0.13.9 - 2015-02-16
10
14
 
11
15
  * [ENHANCEMENT] Accept `force: true` in `authentication_url` to force approval prompt.
data/README.md CHANGED
@@ -236,6 +236,7 @@ Use [Yt::Video](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/Video)
236
236
 
237
237
  * read the attributes of a video
238
238
  * update the attributes of a video
239
+ * upload a thumbnail for a video
239
240
  * access the annotations of a video
240
241
  * delete a video
241
242
  * like and dislike a video
@@ -322,6 +323,9 @@ video = Yt::Video.new id: 'MESycYJytkU', auth: account
322
323
  video.update title: 'A title', description: 'A description <with angle brackets>'
323
324
  video.update tags: ['a tag'], categoryId: '21', license: 'creativeCommon'
324
325
 
326
+ video.upload_thumbnail 'my_thumbnail.jpg'
327
+ video.upload_thumbnail 'http://example.com/remote.png'
328
+
325
329
  video.views since: 7.days.ago #=> {Wed, 28 May 2014 => 12.0, Thu, 29 May 2014 => 3.0, …}
326
330
  video.comments until: 2.days.ago #=> {Wed, 28 May 2014 => 9.0, Thu, 29 May 2014 => 4.0, …}
327
331
  video.likes from: 8.days.ago #=> {Tue, 27 May 2014 => 7.0, Wed, 28 May 2014 => 0.0, …}
@@ -11,26 +11,12 @@ module Yt
11
11
  class ResumableSessions < Base
12
12
 
13
13
  # Starts a resumable session by sending to YouTube the metadata of the
14
- # video to upload. If the request succeeds, YouTube returns a unique
15
- # URL to upload the video file (and eventually resume the upload).
16
- # @param [Integer] content_length the size (bytes) of the video to upload.
17
- # @param [Hash] options the metadata to add to the uploaded video.
18
- # @option options [String] :title The video’s title.
19
- # @option options [String] :description The video’s description.
20
- # @option options [Array<String>] :title The video’s tags.
21
- # @option options [Integer] :category_id The video’s category ID.
22
- # @option options [String] :privacy_status The video’s privacy status.
23
- def insert(content_length, options = {})
14
+ # object to upload. If the request succeeds, YouTube returns a unique
15
+ # URL to upload the object file (and eventually resume the upload).
16
+ # @param [Integer] content_length the size (bytes) of the object to upload.
17
+ # @param [Hash] options the metadata to add to the uploaded object.
18
+ def insert(content_length, body = {})
24
19
  @headers = headers_for content_length
25
- body = {}
26
-
27
- snippet = options.slice :title, :description, :tags, :category_id
28
- snippet[:categoryId] = snippet.delete(:category_id) if snippet[:category_id]
29
- body[:snippet] = snippet if snippet.any?
30
-
31
- status = options[:privacy_status]
32
- body[:status] = {privacyStatus: status} if status
33
-
34
20
  do_insert body: body, headers: @headers
35
21
  end
36
22
 
@@ -43,15 +29,15 @@ module Yt
43
29
  def insert_params
44
30
  super.tap do |params|
45
31
  params[:response_format] = nil
46
- params[:path] = '/upload/youtube/v3/videos'
47
- params[:params] = {part: 'snippet,status', uploadType: 'resumable'}
32
+ params[:path] = @parent.upload_path
33
+ params[:params] = @parent.upload_params.merge uploadType: 'resumable'
48
34
  end
49
35
  end
50
36
 
51
37
  def headers_for(content_length)
52
38
  {}.tap do |headers|
53
39
  headers['x-upload-content-length'] = content_length
54
- headers['X-Upload-Content-Type'] = 'video/*'
40
+ headers['X-Upload-Content-Type'] = @parent.upload_content_type
55
41
  end
56
42
  end
57
43
 
@@ -46,7 +46,7 @@ module Yt
46
46
  # @return [Yt::Collections::Playlists] the account’s playlists.
47
47
  has_many :playlists
48
48
 
49
- # Uploads a video
49
+ # Uploads a video using resumable sessions
50
50
  # @param [String] path_or_url the video to upload. Can either be the
51
51
  # path of a local file or the URL of a remote file.
52
52
  # @param [Hash] params the metadata to add to the uploaded video.
@@ -57,8 +57,11 @@ module Yt
57
57
  # @return [Yt::Models::Video] the newly uploaded video.
58
58
  def upload_video(path_or_url, params = {})
59
59
  file = open path_or_url, 'rb'
60
- session = resumable_sessions.insert file.size, params
61
- session.upload_video file
60
+ session = resumable_sessions.insert file.size, upload_body(params)
61
+
62
+ session.update(body: file) do |data|
63
+ Yt::Video.new id: data['id'], snippet: data['snippet'], status: data['privacyStatus'], auth: self
64
+ end
62
65
  end
63
66
 
64
67
  def create_playlist(params = {})
@@ -71,6 +74,38 @@ module Yt
71
74
  def videos_params
72
75
  {for_mine: true}
73
76
  end
77
+
78
+ # @private
79
+ # Tells `has_many :resumable_sessions` what path to hit to upload a file.
80
+ def upload_path
81
+ '/upload/youtube/v3/videos'
82
+ end
83
+ # @private
84
+ # Tells `has_many :resumable_sessions` what params are set for the object
85
+ # associated to the uploaded file.
86
+ def upload_params
87
+ {part: 'snippet,status'}
88
+ end
89
+
90
+ # @private
91
+ # Tells `has_many :resumable_sessions` what metadata to set in the object
92
+ # associated to the uploaded file.
93
+ def upload_body(params = {})
94
+ {}.tap do |body|
95
+ snippet = params.slice :title, :description, :tags, :category_id
96
+ snippet[:categoryId] = snippet.delete(:category_id) if snippet[:category_id]
97
+ body[:snippet] = snippet if snippet.any?
98
+
99
+ status = params[:privacy_status]
100
+ body[:status] = {privacyStatus: status} if status
101
+ end
102
+ end
103
+
104
+ # @private
105
+ # Tells `has_many :resumable_sessions` what type of file can be uploaded.
106
+ def upload_content_type
107
+ 'video/*'
108
+ end
74
109
  end
75
110
  end
76
111
  end
@@ -13,14 +13,17 @@ module Yt
13
13
  @headers = options[:headers]
14
14
  end
15
15
 
16
- # Uploads a video using the current resumable session
17
- # @param [#read] file A binary object that contains the video content.
16
+ def update(params = {})
17
+ do_update(params) {|data| yield data}
18
+ end
19
+
20
+ # Uploads a thumbnail using the current resumable session
21
+ # @param [#read] file A binary object that contains the image content.
18
22
  # Can either be a File, a StringIO (for instance using open-uri), etc.
19
- # @return [Yt::Models::Video] the newly uploaded video.
20
- def upload_video(file)
21
- do_update(body: file) do |data|
22
- Yt::Video.new id: data['id'], snippet: data['snippet'], status: data['privacyStatus'], auth: @auth
23
- end
23
+ # @return the new thumbnail resource for the given image.
24
+ # @see https://developers.google.com/youtube/v3/docs/thumbnails#resource
25
+ def upload_thumbnail(file)
26
+ do_update(body: file) {|data| data['items'].first}
24
27
  end
25
28
 
26
29
  private
@@ -73,6 +73,11 @@ module Yt
73
73
  delegate :view_count, :like_count, :dislike_count, :favorite_count,
74
74
  :comment_count, to: :statistics_set
75
75
 
76
+ # @!attribute [r] resumable_sessions
77
+ # @return [Yt::Collections::ResumableSessions] the sessions used to
78
+ # upload thumbnails using the resumable upload protocol.
79
+ has_many :resumable_sessions
80
+
76
81
  # Override Resource's new to set statistics and content details as well
77
82
  # if the response includes them
78
83
  def initialize(options = {})
@@ -163,6 +168,20 @@ module Yt
163
168
  !liked?
164
169
  end
165
170
 
171
+ # Uploads a thumbnail
172
+ # @param [String] path_or_url the image to upload. Can either be the
173
+ # path of a local file or the URL of a remote file.
174
+ # @return the new thumbnail resource for the given image.
175
+ # @see https://developers.google.com/youtube/v3/docs/thumbnails#resource
176
+ def upload_thumbnail(path_or_url)
177
+ file = open path_or_url, 'rb'
178
+ session = resumable_sessions.insert file.size
179
+
180
+ session.update(body: file) do |data|
181
+ snippet.instance_variable_set :@thumbnails, data['items'].first
182
+ end
183
+ end
184
+
166
185
  # @private
167
186
  # Tells `has_reports` to retrieve the reports from YouTube Analytics API
168
187
  # either as a Channel or as a Content Owner.
@@ -178,6 +197,24 @@ module Yt
178
197
  end
179
198
  end
180
199
 
200
+ # @private
201
+ # Tells `has_many :resumable_sessions` what path to hit to upload a file.
202
+ def upload_path
203
+ '/upload/youtube/v3/thumbnails/set'
204
+ end
205
+ # @private
206
+ # Tells `has_many :resumable_sessions` what params are set for the object
207
+ # associated to the uploaded file.
208
+ def upload_params
209
+ {video_id: id}
210
+ end
211
+
212
+ # @private
213
+ # Tells `has_many :resumable_sessions` what type of file can be uploaded.
214
+ def upload_content_type
215
+ 'application/octet-stream'
216
+ end
217
+
181
218
  private
182
219
 
183
220
  # @see https://developers.google.com/youtube/v3/docs/videos/update
data/lib/yt/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Yt
2
- VERSION = '0.13.9'
2
+ VERSION = '0.13.10'
3
3
  end
@@ -109,15 +109,9 @@ describe Yt::Account, :device_app do
109
109
  it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
110
110
  end
111
111
 
112
- # @note: This test is a reflection of another irrational behavior of
113
- # YouTube API. When passing a wrong 'device_code', YouTube crashes and
114
- # raises 500, instead of an expected MissingAuth.
115
- # The day YouTube fixes it, then this test will finally fail and the
116
- # commented line will be restored.
117
112
  context 'and an invalid device code' do
118
113
  before { attrs[:device_code] = '--not-a-valid-device-code--' }
119
- # it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
120
- it { expect{account.authentication}.to raise_error Yt::Errors::ServerError }
114
+ it { expect{account.authentication}.to raise_error Yt::Errors::MissingAuth }
121
115
  end
122
116
  end
123
117
 
@@ -342,4 +342,26 @@ describe Yt::Video, :device_app do
342
342
  end
343
343
  end
344
344
  end
345
+
346
+ # @note: This should somehow test that the thumbnail *changes*. However,
347
+ # YouTube does not change the URL of the thumbnail even though the content
348
+ # changes. A full test would have to *download* the thumbnails before and
349
+ # after, and compare the files. For now, not raising error is enough.
350
+ # Eventually, change to `expect{update}.to change{video.thumbnail_url}`
351
+ context 'given one of my own videos for which I want to upload a thumbnail' do
352
+ let(:id) { $account.videos.where(order: 'viewCount').first.id }
353
+ let(:update) { video.upload_thumbnail path_or_url }
354
+
355
+ context 'given the path to a local JPG image file' do
356
+ let(:path_or_url) { File.expand_path '../thumbnail.jpg', __FILE__ }
357
+
358
+ it { expect{update}.not_to raise_error }
359
+ end
360
+
361
+ context 'given the path to a remote PNG image file' do
362
+ let(:path_or_url) { 'https://bit.ly/yt_thumbnail' }
363
+
364
+ it { expect{update}.not_to raise_error }
365
+ end
366
+ end
345
367
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.9
4
+ version: 0.13.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-02-16 00:00:00.000000000 Z
11
+ date: 2015-02-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -257,6 +257,7 @@ files:
257
257
  - spec/requests/as_account/playlist_item_spec.rb
258
258
  - spec/requests/as_account/playlist_spec.rb
259
259
  - spec/requests/as_account/resource_spec.rb
260
+ - spec/requests/as_account/thumbnail.jpg
260
261
  - spec/requests/as_account/video.mp4
261
262
  - spec/requests/as_account/video_spec.rb
262
263
  - spec/requests/as_content_owner/account_spec.rb
@@ -353,6 +354,7 @@ test_files:
353
354
  - spec/requests/as_account/playlist_item_spec.rb
354
355
  - spec/requests/as_account/playlist_spec.rb
355
356
  - spec/requests/as_account/resource_spec.rb
357
+ - spec/requests/as_account/thumbnail.jpg
356
358
  - spec/requests/as_account/video.mp4
357
359
  - spec/requests/as_account/video_spec.rb
358
360
  - spec/requests/as_content_owner/account_spec.rb