yt 0.15.2 → 0.15.3

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: fe92325f41cffdb8e81da539742c539cc3280fd0
4
- data.tar.gz: 40ccb712b549661f2cd905dfed5870cf14d77f4e
3
+ metadata.gz: 955b88eeaa4654c03880228bf322546eb0fba3c3
4
+ data.tar.gz: 8b5bbaf09793909b2230180a49107a58f0f69ed7
5
5
  SHA512:
6
- metadata.gz: 2567686bcaae799d503506e063ec171e16fa7ed975e9fb78d053c971c1aad144c0d3bdabc10daf7ad69b30045ef8c8edb945c37d0b19ed007cb5d664b774e9c4
7
- data.tar.gz: 6283dfcb5b3288c2bf1f66bd15833a5bd073982f253b759c3fceb665c6c29ce7e373073aa0a9e1243b82167e5b58f9e7d1ae8f02f90a2764ac77289279398e0c
6
+ metadata.gz: 6715a5ff1ec202aa3f1d678aea12baa21f87daf15f1234c3b24e4e0be298b62db322ea1c6ea8e948fd4345a49ca15d27aa3b03237f0f8249d0fea67f371cbcc8
7
+ data.tar.gz: eac8f4472b3c814986f3c41469357043019648e523edfef4aafdf4ef3003a4d4700e5c3b2d6eb17df799ed750ebd0372057ecfa2946b3196e0dc43e1df944b60
@@ -7,6 +7,12 @@ For more information about changelogs, check
7
7
  [Vandamme](http://tech-angels.github.io/vandamme).
8
8
 
9
9
 
10
+ ## 0.15.3 - 2015-04-27
11
+
12
+ * [FEATURE] New `file_size`, `file_type`, `container` methods for Yt::Video.
13
+ * [BUGFIX] Retrieve `category_id` also for videos obtained through a search.
14
+ * [FEATURE] Add .includes(:category) to .videos in order to eager-load category title and ID of a collection of videos
15
+
10
16
  ## 0.15.2 - 2015-04-27
11
17
 
12
18
  * [FEATURE] New `embed_html` method for Yt::Video.
data/README.md CHANGED
@@ -41,7 +41,7 @@ To install on your system, run
41
41
 
42
42
  To use inside a bundled Ruby project, add this line to the Gemfile:
43
43
 
44
- gem 'yt', '~> 0.15.2'
44
+ gem 'yt', '~> 0.15.3'
45
45
 
46
46
  Since the gem follows [Semantic Versioning](http://semver.org),
47
47
  indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
@@ -310,6 +310,8 @@ Use [Yt::Video](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/Video)
310
310
  * retrieve data about live-streaming videos
311
311
  * retrieve the advertising options of a video
312
312
  * retrieve the HTML code of the video player
313
+ * retrieve the file details of a video
314
+ * retrieve the category title of a video
313
315
 
314
316
  ```ruby
315
317
  # Videos can be initialized with ID or URL
@@ -368,6 +370,12 @@ video.concurrent_viewers #=> 0
368
370
 
369
371
  video.embed_html #=> "<iframe type='text/html' src='http://www.youtube.com/embed/BPNYv0vd78A' width='640' height='360' frameborder='0' allowfullscreen='true'/>"
370
372
 
373
+ video.file_size #=> 8000000
374
+ video.file_type #=> "video"
375
+ video.container #=> "mov"
376
+
377
+ video.category_title #=> "People & Blogs"
378
+
371
379
  video.annotations.count #=> 1
372
380
  video.annotations.first #=> #<Yt::Models::Annotation @id=...>
373
381
  ```
@@ -913,9 +921,3 @@ the [YouTube Analytics API](https://developers.google.com/youtube/analytics).
913
921
  If you find that a method is missing, fork the project, add the missing code,
914
922
  write the appropriate tests, then submit a pull request, and it will gladly
915
923
  be merged!
916
-
917
- Don’t hesitate to send code comments, issues or pull requests through GitHub
918
- and to spread the love on Twitter by following [@ytgem](https://twitter.com/intent/user?screen_name=ytgem) – 
919
- all feedback is appreciated.
920
-
921
- ![yt4](https://cloud.githubusercontent.com/assets/7408595/3638115/0ef9d6bc-1037-11e4-831a-787204e9b979.png)
@@ -0,0 +1,29 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/file_detail'
3
+
4
+ module Yt
5
+ module Collections
6
+ class FileDetails < Base
7
+
8
+ private
9
+
10
+ def attributes_for_new_item(data)
11
+ {data: data['fileDetails']}
12
+ end
13
+
14
+ # @return [Hash] the parameters to submit to YouTube to get the
15
+ # file detail of a video.
16
+ # @see https://developers.google.com/youtube/v3/docs/videos#fileDetails
17
+ def list_params
18
+ super.tap do |params|
19
+ params[:params] = file_details_params
20
+ params[:path] = '/youtube/v3/videos'
21
+ end
22
+ end
23
+
24
+ def file_details_params
25
+ {max_results: 50, part: 'fileDetails', id: @parent.id}
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,35 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/video_category'
3
+
4
+ module Yt
5
+ module Collections
6
+ class VideoCategories < Base
7
+
8
+ private
9
+
10
+ def attributes_for_new_item(data)
11
+ {}.tap do |attributes|
12
+ attributes[:id] = data['id']
13
+ attributes[:snippet] = data['snippet']
14
+ attributes[:auth] = @auth
15
+ end
16
+ end
17
+
18
+ # @return [Hash] the parameters to submit to YouTube to list video categories.
19
+ # @see https://developers.google.com/youtube/v3/docs/videoCategories/list
20
+ def list_params
21
+ super.tap do |params|
22
+ params[:params] = video_categories_params
23
+ end
24
+ end
25
+
26
+ def video_categories_params
27
+ {}.tap do |params|
28
+ params[:part] = 'snippet'
29
+ params[:id] = @parent.category_id if @parent
30
+ apply_where_params! params
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -24,12 +24,16 @@ module Yt
24
24
  attributes[:status] = data['status']
25
25
  attributes[:content_details] = data['contentDetails']
26
26
  attributes[:statistics] = data['statistics']
27
+ attributes[:video_category] = data['videoCategory']
27
28
  attributes[:auth] = @auth
28
29
  end
29
30
  end
30
31
 
31
32
  def eager_load_items_from(items)
32
33
  if included_relationships.any?
34
+ include_category = included_relationships.delete(:category)
35
+ included_relationships.append(:snippet).uniq! if include_category
36
+
33
37
  ids = items.map{|item| item['id']['videoId']}
34
38
  parts = included_relationships.map{|r| r.to_s.camelize(:lower)}
35
39
  conditions = {id: ids.join(','), part: parts.join(',')}
@@ -46,6 +50,17 @@ module Yt
46
50
  end
47
51
  end
48
52
  end
53
+
54
+ if include_category
55
+ category_ids = items.map{|item| item['snippet']['categoryId']}.uniq
56
+ conditions = {id: category_ids.join(',')}
57
+ video_categories = Collections::VideoCategories.new(auth: @auth).where conditions
58
+
59
+ items.each do |item|
60
+ video_category = video_categories.find{|v| v.id == item['snippet']['categoryId']}
61
+ item['videoCategory'] = video_category.data
62
+ end
63
+ end
49
64
  end
50
65
  super
51
66
  end
@@ -0,0 +1,26 @@
1
+ require 'yt/models/base'
2
+
3
+ module Yt
4
+ module Models
5
+ # Encapsulates basic information about the video file itself,
6
+ # including the file name, size, type, and container.
7
+ # @see https://developers.google.com/youtube/v3/docs/videos#fileDetails
8
+ class FileDetail < Base
9
+ attr_reader :data
10
+
11
+ def initialize(options = {})
12
+ @data = options[:data] || {}
13
+ end
14
+
15
+ # @return [Integer] the file size of the video (in bytes).
16
+ has_attribute :file_size, type: Integer
17
+
18
+ # @return [String] the file type of the file. May be one of:
19
+ # archive, audio, document, image, other, project, or video.
20
+ has_attribute :file_type
21
+
22
+ # @return [String] the container of the video (e.g. 'mov').
23
+ has_attribute :container
24
+ end
25
+ end
26
+ end
@@ -147,9 +147,9 @@ module Yt
147
147
  end
148
148
 
149
149
  # Returns whether YouTube API includes all attributes in this snippet.
150
- # For instance, YouTube API only returns tags on Videos#list, not on
151
- # Videos#search. And returns position on PlaylistItems#list, not on
152
- # PlaylistItems#insert.
150
+ # For instance, YouTube API only returns tags and categoryId on
151
+ # Videos#list, not on Videos#search. And returns position on
152
+ # PlaylistItems#list, not on PlaylistItems#insert.
153
153
  # This method is used to guarantee that, when a video was instantiated
154
154
  # by one of the methods above, an additional call to is made to retrieve
155
155
  # the full snippet in case an attribute is missing.
@@ -23,6 +23,11 @@ module Yt
23
23
  delegate :duration, :hd?, :stereoscopic?, :captioned?, :licensed?,
24
24
  to: :content_detail
25
25
 
26
+ # @!attribute [r] file_detail
27
+ # @return [Yt::Models::FileDetail] the video’s file details.
28
+ has_one :file_detail
29
+ delegate :file_size, :file_type, :container, to: :file_detail
30
+
26
31
  has_one :advertising_options_set
27
32
  delegate :ad_formats, to: :advertising_options_set
28
33
 
@@ -30,6 +35,11 @@ module Yt
30
35
  # @return [Yt::Models::Rating] the video’s rating.
31
36
  has_one :rating
32
37
 
38
+ # @!attribute [r] video_category
39
+ # @return [Yt::Models::VideoCategory] the video’s category.
40
+ has_one :video_category
41
+ delegate :title, to: :video_category, prefix: :category
42
+
33
43
  # @!attribute [r] live_streaming_detail
34
44
  # @return [Yt::Models::LiveStreamingDetail] live streaming detail.
35
45
  has_one :live_streaming_detail
@@ -131,6 +141,12 @@ module Yt
131
141
  if options[:content_details]
132
142
  @content_detail = ContentDetail.new data: options[:content_details]
133
143
  end
144
+ if options[:file_details]
145
+ @file_detail = FileDetail.new data: options[:file_details]
146
+ end
147
+ if options[:video_category]
148
+ @video_category = VideoCategory.new data: options[:video_category]
149
+ end
134
150
  end
135
151
 
136
152
  # Returns the list of keyword tags associated with the video.
@@ -148,6 +164,20 @@ module Yt
148
164
  snippet.tags
149
165
  end
150
166
 
167
+ # Returns the category ID associated with the video.
168
+ # Since YouTube API only returns categoryID on Videos#list, the memoized
169
+ # @snippet is erased if the video was instantiated through Video#search
170
+ # (e.g., by calling account.videos or channel.videos), so that the full
171
+ # snippet (with categoryID) is loaded, rather than the partial one.
172
+ # @see https://developers.google.com/youtube/v3/docs/videos
173
+ # @return [String] ID of the YouTube category associated with the video.
174
+ def category_id
175
+ unless snippet.category_id.present? || snippet.complete?
176
+ @snippet = nil
177
+ end
178
+ snippet.category_id
179
+ end
180
+
151
181
  # Deletes the video.
152
182
  #
153
183
  # This method requires {Resource#auth auth} to return an authenticated
@@ -273,4 +303,4 @@ module Yt
273
303
  end
274
304
  end
275
305
  end
276
- end
306
+ end
@@ -0,0 +1,11 @@
1
+ require 'yt/models/resource'
2
+
3
+ module Yt
4
+ module Models
5
+ # Provides methods to interact with YouTube video categories.
6
+ # @see https://developers.google.com/youtube/v3/docs/videoCategories
7
+ class VideoCategory < Resource
8
+ delegate :data, :title, to: :snippet
9
+ end
10
+ end
11
+ end
@@ -1,3 +1,3 @@
1
1
  module Yt
2
- VERSION = '0.15.2'
2
+ VERSION = '0.15.3'
3
3
  end
@@ -0,0 +1,34 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/file_detail'
3
+
4
+ describe Yt::FileDetail do
5
+ subject(:file_detail) { Yt::FileDetail.new data: data }
6
+
7
+ describe '#data' do
8
+ let(:data) { {"key"=>"value"} }
9
+ specify 'returns the data the file detail was initialized with' do
10
+ expect(file_detail.data).to eq data
11
+ end
12
+ end
13
+
14
+ describe '#file_size' do
15
+ context 'given a video with fileSize' do
16
+ let(:data) { {"fileSize"=>"8000000"} }
17
+ it { expect(file_detail.file_size).to be 8_000_000 }
18
+ end
19
+ end
20
+
21
+ describe '#file_type' do
22
+ context 'given a video with fileType' do
23
+ let(:data) { {"fileType"=>"video"} }
24
+ it { expect(file_detail.file_type).to eq 'video' }
25
+ end
26
+ end
27
+
28
+ describe '#container' do
29
+ context 'given a video with container' do
30
+ let(:data) { {"container"=>"mov"} }
31
+ it { expect(file_detail.container).to eq 'mov' }
32
+ end
33
+ end
34
+ end
@@ -7,7 +7,7 @@ describe Yt::Player do
7
7
 
8
8
  describe '#data' do
9
9
  let(:data) { {"key"=>"value"} }
10
- specify 'returns the data the statistics set was initialized with' do
10
+ specify 'returns the data the player was initialized with' do
11
11
  expect(player.data).to eq data
12
12
  end
13
13
  end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+ require 'yt/models/video_category'
3
+
4
+ describe Yt::VideoCategory do
5
+ subject(:video_category) { Yt::VideoCategory.new attrs }
6
+
7
+ describe '#id' do
8
+ context 'given fetching a video category returns an id' do
9
+ let(:attrs) { {id: "22"} }
10
+ it { expect(video_category.id).to eq '22' }
11
+ end
12
+ end
13
+
14
+ describe '#snippet' do
15
+ context 'given fetching a video category returns a snippet' do
16
+ let(:attrs) { {snippet: {"title": "People & Blogs", "assignable": true}} }
17
+
18
+ it { expect(video_category.snippet).to be_a Yt::Snippet }
19
+ end
20
+ end
21
+ end
@@ -21,9 +21,10 @@ describe Yt::Account, :device_app do
21
21
  describe '.videos' do
22
22
  let(:video) { $account.videos.where(order: 'viewCount').first }
23
23
 
24
- specify 'returns the videos uploaded by the account with their tags' do
24
+ specify 'returns the videos uploaded by the account with their tags and category ID' do
25
25
  expect(video).to be_a Yt::Video
26
26
  expect(video.tags).not_to be_empty
27
+ expect(video.category_id).not_to be_nil
27
28
  end
28
29
 
29
30
  describe '.where(q: query_string)' do
@@ -24,7 +24,7 @@ describe Yt::Channel, :device_app do
24
24
  describe '.videos' do
25
25
  let(:video) { channel.videos.first }
26
26
 
27
- specify 'returns the videos in the channel without their tags' do
27
+ specify 'returns the videos in the channel without tags or category ID' do
28
28
  expect(video).to be_a Yt::Video
29
29
  expect(video.snippet).not_to be_complete
30
30
  end
@@ -62,6 +62,14 @@ describe Yt::Channel, :device_app do
62
62
  end
63
63
  end
64
64
 
65
+ describe '.includes(:category)' do
66
+ let(:video) { channel.videos.includes(:category, :status).first }
67
+
68
+ specify 'eager-loads the category (id and title) of each video' do
69
+ expect(video.instance_variable_defined? :@snippet).to be true
70
+ expect(video.instance_variable_defined? :@video_category).to be true
71
+ end
72
+ end
65
73
 
66
74
  describe 'when the channel has more than 500 videos' do
67
75
  let(:id) { 'UCsmvakQZlvGsyjyOhmhvOsw' }
@@ -64,6 +64,7 @@ describe Yt::Video, :device_app do
64
64
  expect(video.scheduled_end_time).to be_nil
65
65
  expect(video.concurrent_viewers).to be_nil
66
66
  expect(video.embed_html).to be_a String
67
+ expect(video.category_title).to be_a String
67
68
  end
68
69
 
69
70
  it { expect{video.update}.to fail }
@@ -119,6 +120,7 @@ describe Yt::Video, :device_app do
119
120
  it { expect{video.rating}.to raise_error Yt::Errors::NoItems }
120
121
  it { expect{video.status}.to raise_error Yt::Errors::NoItems }
121
122
  it { expect{video.statistics_set}.to raise_error Yt::Errors::NoItems }
123
+ it { expect{video.file_detail}.to raise_error Yt::Errors::NoItems }
122
124
  end
123
125
 
124
126
  context 'given one of my own videos that I want to delete' do
@@ -280,7 +282,7 @@ describe Yt::Video, :device_app do
280
282
 
281
283
  it 'returns valid reports for video-related metrics' do
282
284
  # Some reports are only available to Content Owners.
283
- # See content ownere test for more details about what the methods return.
285
+ # See content owner test for more details about what the methods return.
284
286
  expect{video.views}.not_to raise_error
285
287
  expect{video.comments}.not_to raise_error
286
288
  expect{video.likes}.not_to raise_error
@@ -389,4 +391,22 @@ describe Yt::Video, :device_app do
389
391
  it { expect{update}.to raise_error Yt::Errors::RequestError }
390
392
  end
391
393
  end
392
- end
394
+
395
+ # @note: This test is separated from the block above because YouTube only
396
+ # returns file details for *some videos*: "The fileDetails object will
397
+ # only be returned if the processingDetails.fileAvailability property
398
+ # has a value of available.". Therefore, just to test fileDetails, we use a
399
+ # different video that (for some unknown reason) is marked as 'available'.
400
+ # Also note that I was not able to find a single video returning fileName,
401
+ # therefore video.file_name is not returned by Yt, until it can be tested.
402
+ # @see https://developers.google.com/youtube/v3/docs/videos#processingDetails.fileDetailsAvailability
403
+ context 'given one of my own *available* videos' do
404
+ let(:id) { 'yCmaOvUFhlI' }
405
+
406
+ it 'returns valid file details' do
407
+ expect(video.file_size).to be_an Integer
408
+ expect(video.file_type).to be_a String
409
+ expect(video.container).to be_a String
410
+ end
411
+ end
412
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.15.2
4
+ version: 0.15.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo
@@ -142,6 +142,7 @@ files:
142
142
  - lib/yt/collections/content_owner_details.rb
143
143
  - lib/yt/collections/content_owners.rb
144
144
  - lib/yt/collections/device_flows.rb
145
+ - lib/yt/collections/file_details.rb
145
146
  - lib/yt/collections/ids.rb
146
147
  - lib/yt/collections/live_streaming_details.rb
147
148
  - lib/yt/collections/ownerships.rb
@@ -162,6 +163,7 @@ files:
162
163
  - lib/yt/collections/subscribers.rb
163
164
  - lib/yt/collections/subscriptions.rb
164
165
  - lib/yt/collections/user_infos.rb
166
+ - lib/yt/collections/video_categories.rb
165
167
  - lib/yt/collections/videos.rb
166
168
  - lib/yt/collections/viewer_percentages.rb
167
169
  - lib/yt/config.rb
@@ -189,6 +191,7 @@ files:
189
191
  - lib/yt/models/content_owner_detail.rb
190
192
  - lib/yt/models/description.rb
191
193
  - lib/yt/models/device_flow.rb
194
+ - lib/yt/models/file_detail.rb
192
195
  - lib/yt/models/id.rb
193
196
  - lib/yt/models/iterator.rb
194
197
  - lib/yt/models/live_streaming_detail.rb
@@ -212,6 +215,7 @@ files:
212
215
  - lib/yt/models/url.rb
213
216
  - lib/yt/models/user_info.rb
214
217
  - lib/yt/models/video.rb
218
+ - lib/yt/models/video_category.rb
215
219
  - lib/yt/request.rb
216
220
  - lib/yt/version.rb
217
221
  - spec/collections/claims_spec.rb
@@ -239,6 +243,7 @@ files:
239
243
  - spec/models/content_detail_spec.rb
240
244
  - spec/models/content_owner_detail_spec.rb
241
245
  - spec/models/description_spec.rb
246
+ - spec/models/file_detail_spec.rb
242
247
  - spec/models/live_streaming_detail_spec.rb
243
248
  - spec/models/ownership_spec.rb
244
249
  - spec/models/player_spec.rb
@@ -257,6 +262,7 @@ files:
257
262
  - spec/models/subscription_spec.rb
258
263
  - spec/models/url_spec.rb
259
264
  - spec/models/user_info_spec.rb
265
+ - spec/models/video_category_spec.rb
260
266
  - spec/models/video_spec.rb
261
267
  - spec/requests/as_account/account_spec.rb
262
268
  - spec/requests/as_account/authentications_spec.rb
@@ -341,6 +347,7 @@ test_files:
341
347
  - spec/models/content_detail_spec.rb
342
348
  - spec/models/content_owner_detail_spec.rb
343
349
  - spec/models/description_spec.rb
350
+ - spec/models/file_detail_spec.rb
344
351
  - spec/models/live_streaming_detail_spec.rb
345
352
  - spec/models/ownership_spec.rb
346
353
  - spec/models/player_spec.rb
@@ -359,6 +366,7 @@ test_files:
359
366
  - spec/models/subscription_spec.rb
360
367
  - spec/models/url_spec.rb
361
368
  - spec/models/user_info_spec.rb
369
+ - spec/models/video_category_spec.rb
362
370
  - spec/models/video_spec.rb
363
371
  - spec/requests/as_account/account_spec.rb
364
372
  - spec/requests/as_account/authentications_spec.rb