yt 0.7.0 → 0.7.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/HISTORY.md +4 -0
- data/README.md +75 -15
- data/lib/yt/collections/snippets.rb +8 -2
- data/lib/yt/collections/statuses.rb +7 -1
- data/lib/yt/models/account.rb +2 -3
- data/lib/yt/models/channel.rb +1 -2
- data/lib/yt/models/playlist.rb +1 -0
- data/lib/yt/models/playlist_item.rb +4 -28
- data/lib/yt/models/resource.rb +1 -1
- data/lib/yt/models/snippet.rb +92 -19
- data/lib/yt/models/statistics_set.rb +0 -1
- data/lib/yt/models/video.rb +5 -2
- data/lib/yt/version.rb +1 -1
- data/spec/associations/device_auth/channel_spec.rb +39 -70
- data/spec/associations/device_auth/playlist_item_spec.rb +30 -0
- data/spec/associations/device_auth/playlist_spec.rb +30 -46
- data/spec/associations/device_auth/video_spec.rb +14 -1
- data/spec/associations/server_auth/channel_spec.rb +24 -42
- data/spec/associations/server_auth/playlist_item_spec.rb +30 -0
- data/spec/associations/server_auth/playlist_spec.rb +18 -22
- data/spec/associations/server_auth/video_spec.rb +14 -1
- data/spec/models/playlist_item_spec.rb +6 -7
- data/spec/models/snippet_spec.rb +72 -15
- metadata +6 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a68ecd01c6ad97c1ba1e12d81d98585021ed0fbd
|
4
|
+
data.tar.gz: b2449d697020efacdaf22a0c4a4a618148ad7e9a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f45cdebb0d4794bf071049b3ad71970dfccefcf25fd9957db5b830a3391cc8074894324127f167f32542b53292b48a375806946370d8b3e5061bdac49c07c823
|
7
|
+
data.tar.gz: fbc25da3f1f339b26e5fc701eddd8043ccbbb978112d8f19318d2e705494bf16ac6f46ab967de57e71921ca5f9bdaaec55d7565a6e31429d9075b2baa3fe2cad
|
data/Gemfile.lock
CHANGED
data/HISTORY.md
CHANGED
@@ -4,6 +4,10 @@ v0.7 - 2014/06/18
|
|
4
4
|
* [breaking change] Rename DetailsSet to ContentDetail
|
5
5
|
* Add statistics_set to Video (views, likes, dislikes, favorites, comments)
|
6
6
|
* Add statistics_set to Channel (views, comments, videos, subscribers)
|
7
|
+
* More snippet methods for Video (channel_id, channel_title, category_id, live_broadcast_content)
|
8
|
+
* More snippet methods for Playlist (channel_id, channel_title)
|
9
|
+
* More snippet methods for PlaylistItem (channel_id, channel_title, playlist_id, video_id)
|
10
|
+
* More status methods for PlaylistItem (privacy_status, public?, private?, unlisted?)
|
7
11
|
|
8
12
|
v0.6 - 2014/06/05
|
9
13
|
-----------------
|
data/README.md
CHANGED
@@ -17,13 +17,8 @@ After [registering your app](#configuring-your-app), you can run commands like:
|
|
17
17
|
```ruby
|
18
18
|
channel = Yt::Channel.new id: 'UCxO1tY8h1AhOz0T4ENwmpow'
|
19
19
|
channel.title #=> "Fullscreen"
|
20
|
-
channel.description #=> "The first media company for the connected generation."
|
21
20
|
channel.public? #=> true
|
22
|
-
channel.view_count #=> 421619
|
23
21
|
channel.comment_count #=> 773
|
24
|
-
channel.video_count #=> 13
|
25
|
-
channel.subscriber_count #=> 136925
|
26
|
-
channel.subscriber_count_visible? #=> true
|
27
22
|
channel.videos.count #=> 13
|
28
23
|
```
|
29
24
|
|
@@ -63,7 +58,7 @@ Use [Yt::Account](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/Acco
|
|
63
58
|
account = Yt::Account.new access_token: 'ya29.1.ABCDEFGHIJ'
|
64
59
|
|
65
60
|
account.email #=> .. your e-mail address..
|
66
|
-
account.channel #=> #<Yt::Channel @id=...>
|
61
|
+
account.channel #=> #<Yt::Models::Channel @id=...>
|
67
62
|
```
|
68
63
|
|
69
64
|
*All the above methods require authentication (see below).*
|
@@ -81,7 +76,7 @@ Use [Yt::ContentOwner](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models
|
|
81
76
|
content_owner = Yt::ContentOwner.new owner_name: 'CMSname', access_token: 'ya29.1.ABCDEFGHIJ'
|
82
77
|
|
83
78
|
content_owner.partnered_channels.count #=> 12
|
84
|
-
content_owner.partnered_channels.first #=> #<Yt::Channel @id=...>
|
79
|
+
content_owner.partnered_channels.first #=> #<Yt::Models::Channel @id=...>
|
85
80
|
```
|
86
81
|
|
87
82
|
*All the above methods require authentication (see below).*
|
@@ -100,15 +95,25 @@ Use [Yt::Channel](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/Chan
|
|
100
95
|
|
101
96
|
```ruby
|
102
97
|
channel = Yt::Channel.new id: 'UCxO1tY8h1AhOz0T4ENwmpow'
|
98
|
+
|
103
99
|
channel.title #=> "Fullscreen"
|
100
|
+
channel.description #=> "The first media company for the connected generation."
|
104
101
|
channel.description.has_link_to_playlist? #=> false
|
102
|
+
channel.thumbnail_url #=> "https://yt3.ggpht.com/-KMnbKDBl60w/AAAAAAAAAAI/AAAAAAAAAAA/NjmFYOCVig4/s88-c-k-no/photo.jpg"
|
103
|
+
channel.published_at #=> 2006-03-23 06:13:25 UTC
|
105
104
|
channel.public? #=> true
|
106
105
|
|
106
|
+
channel.view_count #=> 421619
|
107
|
+
channel.comment_count #=> 773
|
108
|
+
channel.video_count #=> 13
|
109
|
+
channel.subscriber_count #=> 136925
|
110
|
+
channel.subscriber_count_visible? #=> true
|
111
|
+
|
107
112
|
channel.videos.count #=> 12
|
108
|
-
channel.videos.first #=> #<Yt::Video @id=...>
|
113
|
+
channel.videos.first #=> #<Yt::Models::Video @id=...>
|
109
114
|
|
110
115
|
channel.playlists.count #=> 2
|
111
|
-
channel.playlists.first #=> #<Yt::Playlist @id=...>
|
116
|
+
channel.playlists.first #=> #<Yt::Models::Playlist @id=...>
|
112
117
|
```
|
113
118
|
|
114
119
|
*The methods above do not require authentication.*
|
@@ -140,7 +145,6 @@ channel.views since: 3.days.ago, until: 2.days.ago #=> {Wed, 28 May 2014 => 12,
|
|
140
145
|
|
141
146
|
*The methods above require to be authenticated as the channel’s content owner (see below).*
|
142
147
|
|
143
|
-
|
144
148
|
Yt::Video
|
145
149
|
-----------
|
146
150
|
|
@@ -152,13 +156,27 @@ Use [Yt::Video](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/Video)
|
|
152
156
|
|
153
157
|
```ruby
|
154
158
|
video = Yt::Video.new id: 'MESycYJytkU'
|
159
|
+
|
155
160
|
video.title #=> "Fullscreen Creator Platform"
|
156
|
-
video.
|
157
|
-
video.description.
|
161
|
+
video.description #=> "The new Fullscreen Creator Platform gives creators and brands a suite of exclusive Fullscreen Apps to build, manage and monetize their YouTube audience..."
|
162
|
+
video.description.has_link_to_channel? #=> true
|
163
|
+
video.thumbnail_url #=> "https://i1.ytimg.com/vi/MESycYJytkU/default.jpg"
|
164
|
+
video.published_at #=> 2013-07-09 16:27:32 UTC
|
158
165
|
video.public? #=> true
|
166
|
+
video.tags #=> []
|
167
|
+
video.channel_id #=> "UCxO1tY8h1AhOz0T4ENwmpow"
|
168
|
+
video.channel_title #=> "Fullscreen"
|
169
|
+
video.category_id #=> "22"
|
170
|
+
video.live_broadcast_content #=> "none"
|
171
|
+
|
172
|
+
video.duration #=> 86
|
173
|
+
video.hd? #=> true
|
174
|
+
video.stereoscopic? #=> false
|
175
|
+
video.captioned? #=> true
|
176
|
+
video.licensed? #=> false
|
159
177
|
|
160
178
|
video.annotations.count #=> 1
|
161
|
-
video.annotations.first #=> #<Yt::Annotation @id=...>
|
179
|
+
video.annotations.first #=> #<Yt::Models::Annotation @id=...>
|
162
180
|
```
|
163
181
|
|
164
182
|
*The methods above do not require authentication.*
|
@@ -185,11 +203,19 @@ Use [Yt::Playlist](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/Pla
|
|
185
203
|
|
186
204
|
```ruby
|
187
205
|
playlist = Yt::Playlist.new id: 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc'
|
206
|
+
|
188
207
|
playlist.title #=> "Fullscreen Features"
|
208
|
+
playlist.description #=> "You send us your best videos and we feature our favorite ones throughout the week!"
|
209
|
+
playlist.description.has_link_to_subscribe? #=> false
|
210
|
+
playlist.thumbnail_url #=> "https://i1.ytimg.com/vi/36kL2alg7Jk/default.jpg"
|
211
|
+
playlist.published_at #=> 2012-11-27 21:23:38 UTC
|
189
212
|
playlist.public? #=> true
|
213
|
+
playlist.tags #=> []
|
214
|
+
playlist.channel_id #=> "UCxO1tY8h1AhOz0T4ENwmpow"
|
215
|
+
playlist.channel_title #=> "Fullscreen"
|
190
216
|
|
191
217
|
playlist.playlist_items.count #=> 1
|
192
|
-
playlist.playlist_items.first #=> #<Yt::PlaylistItem @id=...>
|
218
|
+
playlist.playlist_items.first #=> #<Yt::Models::PlaylistItem @id=...>
|
193
219
|
playlist.playlist_items.first.position #=> 0
|
194
220
|
playlist.playlist_items.first.video.title #=> "Fullscreen Creator Platform"
|
195
221
|
```
|
@@ -204,6 +230,40 @@ playlist.delete_playlist_items title: 'Fullscreen Creator Platform' #=> [true]
|
|
204
230
|
|
205
231
|
*The methods above require to be authenticated as the playlist’s owner (see below).*
|
206
232
|
|
233
|
+
Yt::PlaylistItem
|
234
|
+
----------------
|
235
|
+
|
236
|
+
Use [Yt::PlaylistItem](http://rubydoc.info/github/Fullscreen/yt/master/Yt/Models/PlaylistItem) to:
|
237
|
+
|
238
|
+
* read attributes of a playlist item
|
239
|
+
* delete a playlist item
|
240
|
+
|
241
|
+
```ruby
|
242
|
+
item = Yt::PlaylistItem.new id: 'PLjW_GNR5Ir0GWEP_oveGBNjTvKkYyZfsna1TZDCBP-Z8'
|
243
|
+
|
244
|
+
item.title #=> "Titanium - David Guetta - Space Among Many Cover ft. Evan Chan and Glenna Roberts"
|
245
|
+
item.description #=> "CLICK to tweet this video: [...]"
|
246
|
+
item.description.has_link_to_channel? #=> true
|
247
|
+
item.thumbnail_url #=> "https://i1.ytimg.com/vi/W4GhTprSsOY/default.jpg"
|
248
|
+
item.published_at #=> 2012-12-22 19:38:02 UTC
|
249
|
+
item.public? #=> true
|
250
|
+
item.channel_id #=> "UCxO1tY8h1AhOz0T4ENwmpow"
|
251
|
+
item.channel_title #=> "Fullscreen"
|
252
|
+
item.playlist_id #=> "PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc"
|
253
|
+
item.position #=> 0
|
254
|
+
item.video_id #=> "W4GhTprSsOY"
|
255
|
+
item.video #=> #<Yt::Models::Video @id=...>
|
256
|
+
|
257
|
+
```
|
258
|
+
|
259
|
+
*The methods above do not require authentication.*
|
260
|
+
|
261
|
+
```ruby
|
262
|
+
item.delete #=> true
|
263
|
+
```
|
264
|
+
|
265
|
+
*The methods above require to be authenticated as the playlist’s owner (see below).*
|
266
|
+
|
207
267
|
|
208
268
|
Yt::Annotation
|
209
269
|
--------------
|
@@ -346,7 +406,7 @@ To install on your system, run
|
|
346
406
|
|
347
407
|
To use inside a bundled Ruby project, add this line to the Gemfile:
|
348
408
|
|
349
|
-
gem 'yt', '~> 0.7.
|
409
|
+
gem 'yt', '~> 0.7.1'
|
350
410
|
|
351
411
|
Since the gem follows [Semantic Versioning](http://semver.org),
|
352
412
|
indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
|
@@ -10,7 +10,7 @@ module Yt
|
|
10
10
|
# @return [Yt::Models::Snippet] a new snippet initialized with
|
11
11
|
# one of the items returned by asking YouTube for a list of snippets.
|
12
12
|
def new_item(data)
|
13
|
-
Yt::Snippet.new data: data['snippet']
|
13
|
+
Yt::Snippet.new data: data['snippet'], auth: @auth
|
14
14
|
end
|
15
15
|
|
16
16
|
# @return [Hash] the parameters to submit to YouTube to get the
|
@@ -19,9 +19,15 @@ module Yt
|
|
19
19
|
def list_params
|
20
20
|
super.tap do |params|
|
21
21
|
params[:params] = {id: @parent.id, part: 'snippet'}
|
22
|
-
params[:path] = "/youtube/v3/#{@parent.kind.pluralize}"
|
23
22
|
end
|
24
23
|
end
|
24
|
+
|
25
|
+
# @private
|
26
|
+
# @note Snippets overrides +list_resources+ since the endpoint is not
|
27
|
+
# '/snippets' but the endpoint related to the snippet’s resource.
|
28
|
+
def list_resources
|
29
|
+
@parent.class.to_s.pluralize
|
30
|
+
end
|
25
31
|
end
|
26
32
|
end
|
27
33
|
end
|
@@ -21,9 +21,15 @@ module Yt
|
|
21
21
|
def list_params
|
22
22
|
super.tap do |params|
|
23
23
|
params[:params] = {id: @parent.id, part: 'status'}
|
24
|
-
params[:path] = "/youtube/v3/#{@parent.kind.pluralize}"
|
25
24
|
end
|
26
25
|
end
|
26
|
+
|
27
|
+
# @private
|
28
|
+
# @note Statuses overrides +list_resources+ since the endpoint is not
|
29
|
+
# '/statuses' but the endpoint related to the snippet’s resource.
|
30
|
+
def list_resources
|
31
|
+
@parent.class.to_s.pluralize
|
32
|
+
end
|
27
33
|
end
|
28
34
|
end
|
29
35
|
end
|
data/lib/yt/models/account.rb
CHANGED
@@ -16,9 +16,8 @@ module Yt
|
|
16
16
|
# @!attribute [r] user_info
|
17
17
|
# @return [Yt::Models::UserInfo] the account’s profile information.
|
18
18
|
has_one :user_info
|
19
|
-
delegate :id, :email, :has_verified_email?, :gender, :name,
|
20
|
-
|
21
|
-
:locale, :hd, to: :user_info
|
19
|
+
delegate :id, :email, :has_verified_email?, :gender, :name, :given_name,
|
20
|
+
:family_name, :profile_url, :avatar_url, :locale, :hd, to: :user_info
|
22
21
|
|
23
22
|
# @!attribute [r] videos
|
24
23
|
# @return [Yt::Collections::Videos] the videos owned by the account.
|
data/lib/yt/models/channel.rb
CHANGED
@@ -22,12 +22,11 @@ module Yt
|
|
22
22
|
# @return [Yt::Collections::Playlists] the channel’s playlists.
|
23
23
|
has_many :playlists
|
24
24
|
|
25
|
-
|
26
25
|
# @!attribute [r] statistics_set
|
27
26
|
# @return [Yt::Models::StatisticsSet] the statistics for the video.
|
28
27
|
has_one :statistics_set
|
29
28
|
delegate :view_count, :comment_count, :video_count, :subscriber_count,
|
30
|
-
|
29
|
+
:subscriber_count_visible?, to: :statistics_set
|
31
30
|
|
32
31
|
# Returns whether the authenticated account is subscribed to the channel.
|
33
32
|
#
|
data/lib/yt/models/playlist.rb
CHANGED
@@ -5,6 +5,7 @@ module Yt
|
|
5
5
|
# Provides methods to interact with YouTube playlists.
|
6
6
|
# @see https://developers.google.com/youtube/v3/docs/playlists
|
7
7
|
class Playlist < Resource
|
8
|
+
delegate :tags, :channel_id, :channel_title, to: :snippet
|
8
9
|
|
9
10
|
# @!attribute [r] playlist_items
|
10
11
|
# @return [Yt::Collections::PlaylistItems] the playlist’s items.
|
@@ -1,28 +1,12 @@
|
|
1
|
-
require 'yt/models/
|
1
|
+
require 'yt/models/resource'
|
2
2
|
|
3
3
|
module Yt
|
4
4
|
module Models
|
5
5
|
# Provides methods to interact with YouTube playlist items.
|
6
6
|
# @see https://developers.google.com/youtube/v3/docs/playlistItems
|
7
|
-
class PlaylistItem <
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# @return [String] the order in which the item appears in the playlist.
|
12
|
-
# The value uses a zero-based index, so the first item has a position
|
13
|
-
# of 0, the second item has a position of 1, and so forth.
|
14
|
-
attr_reader :position
|
15
|
-
|
16
|
-
attr_reader :video
|
17
|
-
|
18
|
-
def initialize(options = {})
|
19
|
-
@id = options[:id]
|
20
|
-
@auth = options[:auth]
|
21
|
-
if options[:snippet]
|
22
|
-
@position = options[:snippet]['position']
|
23
|
-
@video = Video.new video_params_for options
|
24
|
-
end
|
25
|
-
end
|
7
|
+
class PlaylistItem < Resource
|
8
|
+
delegate :channel_id, :channel_title, :playlist_id, :position, :video_id,
|
9
|
+
:video, to: :snippet
|
26
10
|
|
27
11
|
def delete(options = {})
|
28
12
|
do_delete {@id = nil}
|
@@ -43,14 +27,6 @@ module Yt
|
|
43
27
|
params[:params] = {id: @id}
|
44
28
|
end
|
45
29
|
end
|
46
|
-
|
47
|
-
def video_params_for(options = {})
|
48
|
-
{}.tap do |params|
|
49
|
-
params[:id] = options[:snippet].fetch('resourceId', {})['videoId']
|
50
|
-
params[:snippet] = options[:snippet].except 'resourceId'
|
51
|
-
params[:auth] = options[:auth]
|
52
|
-
end
|
53
|
-
end
|
54
30
|
end
|
55
31
|
end
|
56
32
|
end
|
data/lib/yt/models/resource.rb
CHANGED
data/lib/yt/models/snippet.rb
CHANGED
@@ -2,50 +2,123 @@ require 'yt/models/description'
|
|
2
2
|
|
3
3
|
module Yt
|
4
4
|
module Models
|
5
|
-
#
|
6
|
-
#
|
5
|
+
# Contains basic information about the resource. The details of the snippet
|
6
|
+
# are different for the different types of resources. Resources with a
|
7
|
+
# snippet are: channels, videos, playlists, playlist items.
|
7
8
|
# @see https://developers.google.com/youtube/v3/docs/channels#resource
|
9
|
+
# @see https://developers.google.com/youtube/v3/docs/videos#resource
|
8
10
|
class Snippet
|
9
11
|
def initialize(options = {})
|
10
12
|
@data = options[:data]
|
13
|
+
@auth = options[:auth]
|
11
14
|
end
|
12
15
|
|
13
16
|
# @return [String] the resource’s title.
|
17
|
+
# For channels: the channel’s title.
|
18
|
+
# For videos: the video’s title. Has a maximum of 100 characters and
|
19
|
+
# may contain all valid UTF-8 characters except < and >.
|
20
|
+
# For playlists: the playlist’s title.
|
21
|
+
# For playlist items: the item’s title.
|
14
22
|
def title
|
15
23
|
@title ||= @data.fetch 'title', ''
|
16
24
|
end
|
17
25
|
|
18
26
|
# @return [Yt::Models::Description] the resource’s description.
|
27
|
+
# For channels: the channel’s description. Has a maximum of 1000
|
28
|
+
# characters.
|
29
|
+
# For videos: the video’s description. Has a maximum of 5000 bytes and
|
30
|
+
# may contain all valid UTF-8 characters except < and >.
|
31
|
+
# For playlists: the playlist’s description.
|
32
|
+
# For playlist items: the item’s description.
|
19
33
|
def description
|
20
34
|
@description ||= Description.new @data.fetch('description', '')
|
21
35
|
end
|
22
36
|
|
23
|
-
# @return [Time
|
37
|
+
# @return [Time] the date and time that the resource was published.
|
38
|
+
# For channels: the date and time that the channel was created.
|
39
|
+
# For videos: the date and time that the video was published.
|
40
|
+
# For playlists: the date and time that the playlist was created.
|
41
|
+
# For playlist items: the date and time that the item was added to the
|
42
|
+
# playlist.
|
24
43
|
def published_at
|
25
44
|
@published_at ||= Time.parse @data['publishedAt']
|
26
45
|
end
|
27
46
|
|
28
|
-
# Return the tags of a YouTube resource.
|
29
|
-
#
|
30
|
-
# @return [Array] An array of Yt::Tag object, one for each tag of the resource.
|
31
|
-
#
|
32
|
-
# @note YouTube API only includes tags in a resource’s snippet if the
|
33
|
-
# resource is a video belonging to the authenticated account.
|
34
|
-
def tags
|
35
|
-
@tags ||= @data.fetch 'tags', []
|
36
|
-
end
|
37
|
-
|
38
|
-
# @return [String] a thumbnail image associated with the resource.
|
39
47
|
# @param [Symbol or String] size The size of the thumbnail to retrieve.
|
40
|
-
#
|
41
|
-
# For
|
42
|
-
#
|
43
|
-
# For
|
44
|
-
#
|
48
|
+
# @return [String or nil] a thumbnail image associated with the resource.
|
49
|
+
# For channels: sizes correspond to 88x88px (default), 240x240px
|
50
|
+
# (medium), and 800x800px (high).
|
51
|
+
# For videos, playlists, playlist items: sizes correspond to 120x90px
|
52
|
+
# (default), 320x180px (medium), and 480x360px (high).
|
45
53
|
def thumbnail_url(size = :default)
|
46
54
|
@thumbnails ||= @data.fetch 'thumbnails', {}
|
47
55
|
@thumbnails.fetch(size.to_s, {})['url']
|
48
56
|
end
|
57
|
+
|
58
|
+
# @return [String or nil] the ID of the related channel.
|
59
|
+
# For videos: the ID that YouTube uses to uniquely identify the
|
60
|
+
# channel that the video was uploaded to.
|
61
|
+
# For playlists: the ID that YouTube uses to uniquely identify the
|
62
|
+
# channel that published the playlist.
|
63
|
+
# For playlist items: the ID that YouTube uses to uniquely identify
|
64
|
+
# the user that added the item to the playlist.
|
65
|
+
def channel_id
|
66
|
+
@channel_id ||= @data['channelId']
|
67
|
+
end
|
68
|
+
|
69
|
+
# @return [String or nil] the title of the related channel.
|
70
|
+
# For videos: the title of the channel the video belongs to.
|
71
|
+
# For playlists: the title of the channel the playlist belongs to.
|
72
|
+
# For playlist items: the title of the channel the playlist item
|
73
|
+
# belongs to.
|
74
|
+
def channel_title
|
75
|
+
@channel_title ||= @data['channelTitle']
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [Array<Yt::Models::Tag>] A list of associated keyword tags.
|
79
|
+
# For videos: tags are only visible to the video’s uploader.
|
80
|
+
# For playlists: keyword tags associated with the playlist.
|
81
|
+
def tags
|
82
|
+
@tags ||= @data.fetch 'tags', []
|
83
|
+
end
|
84
|
+
|
85
|
+
# @return [String or nil] the YouTube video category associated with the
|
86
|
+
# video (for videos only).
|
87
|
+
# @see https://developers.google.com/youtube/v3/docs/videoCategories/list
|
88
|
+
def category_id
|
89
|
+
@category_id ||= @data['categoryId']
|
90
|
+
end
|
91
|
+
|
92
|
+
# @return [String or nil] whether the resource is an upcoming/active live
|
93
|
+
# broadcast. Valid values are: live, none, upcoming (for videos only).
|
94
|
+
def live_broadcast_content
|
95
|
+
@live_broadcast_content ||= @data['liveBroadcastContent']
|
96
|
+
end
|
97
|
+
|
98
|
+
# @return [String or nil] the ID that YouTube uses to uniquely identify
|
99
|
+
# the playlist that the item is in (for playlist items only).
|
100
|
+
def playlist_id
|
101
|
+
@playlist_id ||= @data['playlistId']
|
102
|
+
end
|
103
|
+
|
104
|
+
# @return [Integer or nil] the order in which the item appears in a
|
105
|
+
# playlist. The value is zero-based, so the first item has a position
|
106
|
+
# of 0, the second item of 1, and so forth (for playlist items only).
|
107
|
+
def position
|
108
|
+
@position ||= @data['position'].to_i
|
109
|
+
end
|
110
|
+
|
111
|
+
# @return [String or nil] the ID of the video the playlist item
|
112
|
+
# represents in the playlist (only for playlist items).
|
113
|
+
def video_id
|
114
|
+
@video_id ||= @data.fetch('resourceId', {})['videoId']
|
115
|
+
end
|
116
|
+
|
117
|
+
# @return [Yt::Models::Video or nil] the video the playlist item
|
118
|
+
# represents in the playlist (only for playlist items).
|
119
|
+
def video
|
120
|
+
@video ||= Video.new id: video_id, auth: @auth if video_id
|
121
|
+
end
|
49
122
|
end
|
50
123
|
end
|
51
124
|
end
|
data/lib/yt/models/video.rb
CHANGED
@@ -5,11 +5,14 @@ module Yt
|
|
5
5
|
# Provides methods to interact with YouTube videos.
|
6
6
|
# @see https://developers.google.com/youtube/v3/docs/videos
|
7
7
|
class Video < Resource
|
8
|
+
delegate :tags, :channel_id, :channel_title, :category_id,
|
9
|
+
:live_broadcast_content, to: :snippet
|
10
|
+
|
8
11
|
# @!attribute [r] content_detail
|
9
12
|
# @return [Yt::Models::ContentDetail] the video’s content details.
|
10
13
|
has_one :content_detail
|
11
14
|
delegate :duration, :hd?, :stereoscopic?, :captioned?, :licensed?,
|
12
|
-
|
15
|
+
to: :content_detail
|
13
16
|
|
14
17
|
# @!attribute [r] rating
|
15
18
|
# @return [Yt::Models::Rating] the video’s rating.
|
@@ -23,7 +26,7 @@ module Yt
|
|
23
26
|
# @return [Yt::Models::StatisticsSet] the statistics for the video.
|
24
27
|
has_one :statistics_set
|
25
28
|
delegate :view_count, :like_count, :dislike_count, :favorite_count,
|
26
|
-
|
29
|
+
:comment_count, to: :statistics_set
|
27
30
|
|
28
31
|
# Returns whether the authenticated account likes the video.
|
29
32
|
#
|
data/lib/yt/version.rb
CHANGED
@@ -4,88 +4,46 @@ require 'spec_helper'
|
|
4
4
|
require 'yt/models/channel'
|
5
5
|
|
6
6
|
describe Yt::Channel, :device_app do
|
7
|
-
|
7
|
+
subject(:channel) { Yt::Channel.new id: id, auth: $account }
|
8
8
|
|
9
|
-
|
9
|
+
context 'given someone else’s channel' do
|
10
10
|
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
11
|
-
it { expect(channel.snippet).to be_a Yt::Snippet }
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '.snippet of unknown channel' do
|
15
|
-
let(:id) { 'not-a-channel-id' }
|
16
|
-
it { expect{channel.snippet}.to raise_error Yt::Errors::NoItems }
|
17
|
-
end
|
18
|
-
|
19
|
-
describe '.statistics_set of existing channel' do
|
20
|
-
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
21
|
-
it { expect(channel.statistics_set).to be_a Yt::StatisticsSet }
|
22
|
-
end
|
23
11
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
12
|
+
it 'returns valid snippet data' do
|
13
|
+
expect(channel.snippet).to be_a Yt::Snippet
|
14
|
+
expect(channel.title).to be_a String
|
15
|
+
expect(channel.description).to be_a Yt::Description
|
16
|
+
expect(channel.thumbnail_url).to be_a String
|
17
|
+
expect(channel.published_at).to be_a Time
|
18
|
+
end
|
28
19
|
|
29
|
-
describe '.status of existing channel' do
|
30
|
-
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
31
20
|
it { expect(channel.status).to be_a Yt::Status }
|
32
|
-
|
33
|
-
|
34
|
-
describe '.status of unknown channel' do
|
35
|
-
let(:id) { 'not-a-channel-id' }
|
36
|
-
it { expect{channel.status}.to raise_error Yt::Errors::NoItems }
|
37
|
-
end
|
38
|
-
|
39
|
-
describe '.videos of existing channel' do
|
40
|
-
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
21
|
+
it { expect(channel.statistics_set).to be_a Yt::StatisticsSet }
|
41
22
|
it { expect(channel.videos).to be_a Yt::Collections::Videos }
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
# NOTE: This test is just a reflection of YouTube irrational behavior of
|
48
|
-
# returns 0 results if the name of an unknown channel starts with UC, but
|
49
|
-
# returning 100,000 results otherwise (ignoring the channel filter).
|
50
|
-
it { expect(channel.videos.count).to be 0 }
|
51
|
-
end
|
52
|
-
|
53
|
-
describe '.subscriptions to an existing channel' do
|
54
|
-
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
23
|
+
it { expect(channel.videos.first).to be_a Yt::Video }
|
24
|
+
it { expect(channel.playlists).to be_a Yt::Collections::Playlists }
|
25
|
+
it { expect(channel.playlists.first).to be_a Yt::Playlist }
|
26
|
+
it { expect{channel.create_playlist}.to raise_error Yt::Errors::RequestError }
|
27
|
+
it { expect{channel.delete_playlists}.to raise_error Yt::Errors::RequestError }
|
55
28
|
it { expect(channel.subscriptions).to be_a Yt::Collections::Subscriptions }
|
56
29
|
|
57
30
|
# NOTE: These tests are slow because we *must* wait some seconds between
|
58
31
|
# subscribing and unsubscribing to a channel, otherwise YouTube will show
|
59
32
|
# wrong (cached) data, such as a user is subscribed when he is not.
|
60
|
-
context '
|
33
|
+
context 'that I am not subscribed to', :slow do
|
61
34
|
before { channel.unsubscribe }
|
62
35
|
it { expect(channel.subscribed?).to be false }
|
63
36
|
it { expect(channel.subscribe!).to be_truthy }
|
64
37
|
end
|
65
38
|
|
66
|
-
context '
|
39
|
+
context 'that I am subscribed to', :slow do
|
67
40
|
before { channel.subscribe }
|
68
41
|
it { expect(channel.subscribed?).to be true }
|
69
42
|
it { expect(channel.unsubscribe!).to be_truthy }
|
70
43
|
end
|
71
44
|
end
|
72
45
|
|
73
|
-
|
74
|
-
let(:id) { 'not-a-channel-id' }
|
75
|
-
it { expect{channel.subscribe}.to raise_error Yt::Errors::RequestError }
|
76
|
-
end
|
77
|
-
|
78
|
-
describe '.subscriptions to my own channel' do
|
79
|
-
let(:id) { $account.channel.id }
|
80
|
-
|
81
|
-
# NOTE: This test is just a reflection of YouTube irrational behavior of
|
82
|
-
# raising a 500 error when you try to subscribe to your own channel, rather
|
83
|
-
# than a more logical 4xx error. Hopefully this will get fixed and this
|
84
|
-
# code (and test) removed.
|
85
|
-
it { expect{channel.subscribe}.to raise_error Yt::Errors::ServerError }
|
86
|
-
end
|
87
|
-
|
88
|
-
describe '.playlists of my own channel' do
|
46
|
+
context 'given my own channel' do
|
89
47
|
let(:id) { $account.channel.id }
|
90
48
|
let(:title) { 'Yt Test title' }
|
91
49
|
let(:description) { 'Yt Test description' }
|
@@ -93,15 +51,13 @@ describe Yt::Channel, :device_app do
|
|
93
51
|
let(:privacy_status) { 'unlisted' }
|
94
52
|
let(:params) { {title: title, description: description, tags: tags, privacy_status: privacy_status} }
|
95
53
|
|
96
|
-
|
97
|
-
|
98
|
-
describe 'can be created' do
|
54
|
+
describe 'playlists can be added' do
|
99
55
|
after { channel.delete_playlists params }
|
100
56
|
it { expect(channel.create_playlist params).to be_a Yt::Playlist }
|
101
57
|
it { expect{channel.create_playlist params}.to change{channel.playlists.count}.by(1) }
|
102
58
|
end
|
103
59
|
|
104
|
-
describe 'can be deleted' do
|
60
|
+
describe 'playlists can be deleted' do
|
105
61
|
let(:title) { "Yt Test Delete All Playlists #{rand}" }
|
106
62
|
before { channel.create_playlist params }
|
107
63
|
|
@@ -109,16 +65,29 @@ describe Yt::Channel, :device_app do
|
|
109
65
|
it { expect(channel.delete_playlists params).to eq [true] }
|
110
66
|
it { expect{channel.delete_playlists params}.to change{channel.playlists.count}.by(-1) }
|
111
67
|
end
|
68
|
+
|
69
|
+
# NOTE: This test is just a reflection of YouTube irrational behavior of
|
70
|
+
# raising a 500 error when you try to subscribe to your own channel,
|
71
|
+
# rather than a more logical 4xx error. Hopefully this will get fixed
|
72
|
+
# and this code (and test) removed.
|
73
|
+
it { expect{channel.subscribe}.to raise_error Yt::Errors::ServerError }
|
112
74
|
end
|
113
75
|
|
114
|
-
|
115
|
-
let(:id) { '
|
76
|
+
context 'given an unknown channel' do
|
77
|
+
let(:id) { 'not-a-channel-id' }
|
116
78
|
|
117
|
-
it { expect
|
79
|
+
it { expect{channel.snippet}.to raise_error Yt::Errors::NoItems }
|
80
|
+
it { expect{channel.status}.to raise_error Yt::Errors::NoItems }
|
81
|
+
it { expect{channel.statistics_set}.to raise_error Yt::Errors::NoItems }
|
82
|
+
it { expect{channel.subscribe}.to raise_error Yt::Errors::RequestError }
|
83
|
+
|
84
|
+
describe 'starting with UC' do
|
85
|
+
let(:id) { 'UC-not-a-channel-id' }
|
118
86
|
|
119
|
-
|
120
|
-
|
121
|
-
|
87
|
+
# NOTE: This test is just a reflection of YouTube irrational behavior of
|
88
|
+
# returns 0 results if the name of an unknown channel starts with UC, but
|
89
|
+
# returning 100,000 results otherwise (ignoring the channel filter).
|
90
|
+
it { expect(channel.videos.count).to be 0 }
|
122
91
|
end
|
123
92
|
end
|
124
93
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yt/models/playlist_item'
|
3
|
+
|
4
|
+
describe Yt::PlaylistItem, :device_app do
|
5
|
+
subject(:item) { Yt::PlaylistItem.new id: id, auth: $account }
|
6
|
+
|
7
|
+
context 'given an existing playlist item' do
|
8
|
+
let(:id) { 'PLjW_GNR5Ir0GWEP_oveGBNjTvKkYyZfsna1TZDCBP-Z8' }
|
9
|
+
|
10
|
+
it 'returns valid snippet data' do
|
11
|
+
expect(item.snippet).to be_a Yt::Snippet
|
12
|
+
expect(item.title).to be_a String
|
13
|
+
expect(item.description).to be_a Yt::Description
|
14
|
+
expect(item.thumbnail_url).to be_a String
|
15
|
+
expect(item.published_at).to be_a Time
|
16
|
+
expect(item.channel_id).to be_a String
|
17
|
+
expect(item.channel_title).to be_a String
|
18
|
+
expect(item.playlist_id).to be_a String
|
19
|
+
expect(item.position).to be_an Integer
|
20
|
+
expect(item.video_id).to be_a String
|
21
|
+
expect(item.video).to be_a Yt::Models::Video
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'given an unknown playlist item' do
|
26
|
+
let(:id) { 'not-a-playlist-item-id' }
|
27
|
+
|
28
|
+
it { expect{item.snippet}.to raise_error Yt::Errors::RequestError }
|
29
|
+
end
|
30
|
+
end
|
@@ -4,53 +4,63 @@ require 'spec_helper'
|
|
4
4
|
require 'yt/models/playlist'
|
5
5
|
|
6
6
|
describe Yt::Playlist, :device_app do
|
7
|
-
|
7
|
+
subject(:playlist) { Yt::Playlist.new id: id, auth: $account }
|
8
8
|
|
9
|
-
|
9
|
+
context 'given an existing playlist' do
|
10
10
|
let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
|
11
|
-
it { expect(playlist.snippet).to be_a Yt::Snippet }
|
12
|
-
end
|
13
11
|
|
14
|
-
|
15
|
-
|
12
|
+
it 'returns valid snippet data' do
|
13
|
+
expect(playlist.snippet).to be_a Yt::Snippet
|
14
|
+
expect(playlist.title).to be_a String
|
15
|
+
expect(playlist.description).to be_a Yt::Description
|
16
|
+
expect(playlist.thumbnail_url).to be_a String
|
17
|
+
expect(playlist.published_at).to be_a Time
|
18
|
+
expect(playlist.tags).to be_an Array
|
19
|
+
expect(playlist.channel_id).to be_a String
|
20
|
+
expect(playlist.channel_title).to be_a String
|
21
|
+
end
|
22
|
+
|
16
23
|
it { expect(playlist.status).to be_a Yt::Status }
|
24
|
+
it { expect(playlist.playlist_items).to be_a Yt::Collections::PlaylistItems }
|
25
|
+
it { expect(playlist.playlist_items.first).to be_a Yt::PlaylistItem }
|
17
26
|
end
|
18
27
|
|
19
|
-
|
28
|
+
context 'given an unknown playlist' do
|
20
29
|
let(:id) { 'not-a-playlist-id' }
|
30
|
+
|
21
31
|
it { expect{playlist.snippet}.to raise_error Yt::Errors::NoItems }
|
32
|
+
it { expect{playlist.status}.to raise_error Yt::Errors::NoItems }
|
22
33
|
end
|
23
34
|
|
24
|
-
|
25
|
-
let(:id) { '
|
26
|
-
|
35
|
+
context 'given someone else’s playlist' do
|
36
|
+
let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
|
37
|
+
let(:video_id) { 'MESycYJytkU' }
|
38
|
+
|
39
|
+
it { expect{playlist.delete}.to fail.with 'forbidden' }
|
40
|
+
it { expect{playlist.update}.to fail.with 'forbidden' }
|
41
|
+
it { expect{playlist.add_video! video_id}.to raise_error Yt::Errors::RequestError }
|
42
|
+
it { expect{playlist.delete_playlist_items}.to raise_error Yt::Errors::RequestError }
|
27
43
|
end
|
28
44
|
|
29
|
-
|
45
|
+
context 'given one of my own playlists that I want to delete' do
|
30
46
|
before(:all) { @my_playlist = $account.create_playlist title: "Yt Test Delete Playlist #{rand}" }
|
31
47
|
let(:id) { @my_playlist.id }
|
32
48
|
|
33
49
|
it { expect(playlist.delete).to be true }
|
34
50
|
end
|
35
51
|
|
36
|
-
|
37
|
-
let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
|
38
|
-
|
39
|
-
it { expect{playlist.delete}.to fail.with 'forbidden' }
|
40
|
-
end
|
41
|
-
|
42
|
-
describe '.update on my own playlist' do
|
52
|
+
context 'given one of my own playlists that I want to update' do
|
43
53
|
before(:all) { @my_playlist = $account.create_playlist title: "Yt Test Update Playlist #{rand}" }
|
44
54
|
after(:all) { @my_playlist.delete }
|
45
55
|
let(:id) { @my_playlist.id }
|
46
56
|
|
47
|
-
|
57
|
+
describe 'updates the attributes that I specify explicitly' do
|
48
58
|
let(:attrs) { {title: "Yt Test Update Playlist #{rand} - new title"} }
|
49
59
|
it { expect(playlist.update attrs).to eq true }
|
50
60
|
it { expect{playlist.update attrs}.to change{playlist.title} }
|
51
61
|
end
|
52
62
|
|
53
|
-
|
63
|
+
describe 'does not update the other attributes' do
|
54
64
|
let(:attrs) { {} }
|
55
65
|
it { expect(playlist.update attrs).to eq true }
|
56
66
|
it { expect{playlist.update attrs}.not_to change{playlist.title} }
|
@@ -58,20 +68,6 @@ describe Yt::Playlist, :device_app do
|
|
58
68
|
it { expect{playlist.update attrs}.not_to change{playlist.tags} }
|
59
69
|
it { expect{playlist.update attrs}.not_to change{playlist.privacy_status} }
|
60
70
|
end
|
61
|
-
end
|
62
|
-
|
63
|
-
describe '.update on someone else’s playlist' do
|
64
|
-
let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
|
65
|
-
|
66
|
-
it { expect{playlist.update}.to fail.with 'forbidden' }
|
67
|
-
end
|
68
|
-
|
69
|
-
describe '.playlist_items of my own playlist' do
|
70
|
-
before(:all) { @my_playlist = $account.create_playlist title: "Yt Test Items" }
|
71
|
-
after(:all) { @my_playlist.delete }
|
72
|
-
let(:id) { @my_playlist.id }
|
73
|
-
|
74
|
-
it { expect(playlist.playlist_items).to be_a Yt::Collections::PlaylistItems }
|
75
71
|
|
76
72
|
context 'given an existing video' do
|
77
73
|
let(:video_id) { 'MESycYJytkU' }
|
@@ -121,16 +117,4 @@ describe Yt::Playlist, :device_app do
|
|
121
117
|
end
|
122
118
|
end
|
123
119
|
end
|
124
|
-
|
125
|
-
describe '.playlist_items of someone else’s playlist' do
|
126
|
-
let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
|
127
|
-
let(:video_id) { 'MESycYJytkU' }
|
128
|
-
|
129
|
-
it { expect(playlist.playlist_items).to be_a Yt::Collections::PlaylistItems }
|
130
|
-
|
131
|
-
describe 'cannot be created or destroyed' do
|
132
|
-
it { expect{playlist.add_video! video_id}.to raise_error Yt::Errors::RequestError }
|
133
|
-
it { expect{playlist.delete_playlist_items}.to raise_error Yt::Errors::RequestError }
|
134
|
-
end
|
135
|
-
end
|
136
120
|
end
|
@@ -8,7 +8,20 @@ describe Yt::Video, :device_app do
|
|
8
8
|
let(:id) { 'MESycYJytkU' }
|
9
9
|
|
10
10
|
it { expect(video.content_detail).to be_a Yt::ContentDetail }
|
11
|
-
|
11
|
+
|
12
|
+
it 'returns valid snippet data' do
|
13
|
+
expect(video.snippet).to be_a Yt::Snippet
|
14
|
+
expect(video.title).to be_a String
|
15
|
+
expect(video.description).to be_a Yt::Description
|
16
|
+
expect(video.thumbnail_url).to be_a String
|
17
|
+
expect(video.published_at).to be_a Time
|
18
|
+
expect(video.tags).to be_an Array
|
19
|
+
expect(video.channel_id).to be_a String
|
20
|
+
expect(video.channel_title).to be_a String
|
21
|
+
expect(video.category_id).to be_a String
|
22
|
+
expect(video.live_broadcast_content).to be_a String
|
23
|
+
end
|
24
|
+
|
12
25
|
it { expect(video.rating).to be_a Yt::Rating }
|
13
26
|
it { expect(video.status).to be_a Yt::Status }
|
14
27
|
it { expect(video.statistics_set).to be_a Yt::StatisticsSet }
|
@@ -1,60 +1,42 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require 'spec_helper'
|
4
2
|
require 'yt/models/channel'
|
5
3
|
|
6
4
|
describe Yt::Channel, :server_app do
|
7
|
-
|
8
|
-
|
9
|
-
describe '.snippet of existing channel' do
|
10
|
-
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
11
|
-
it { expect(channel.snippet).to be_a Yt::Snippet }
|
12
|
-
end
|
13
|
-
|
14
|
-
describe '.snippet of unknown channel' do
|
15
|
-
let(:id) { 'not-a-channel-id' }
|
16
|
-
it { expect{channel.snippet}.to raise_error Yt::Errors::NoItems }
|
17
|
-
end
|
5
|
+
subject(:channel) { Yt::Channel.new id: id }
|
18
6
|
|
19
|
-
|
7
|
+
context 'given an existing channel' do
|
20
8
|
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
21
|
-
it { expect(channel.statistics_set).to be_a Yt::StatisticsSet }
|
22
|
-
end
|
23
9
|
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
10
|
+
it 'returns valid snippet data' do
|
11
|
+
expect(channel.snippet).to be_a Yt::Snippet
|
12
|
+
expect(channel.title).to be_a String
|
13
|
+
expect(channel.description).to be_a Yt::Description
|
14
|
+
expect(channel.thumbnail_url).to be_a String
|
15
|
+
expect(channel.published_at).to be_a Time
|
16
|
+
end
|
28
17
|
|
29
|
-
describe '.status of existing channel' do
|
30
|
-
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
31
18
|
it { expect(channel.status).to be_a Yt::Status }
|
32
|
-
|
33
|
-
|
34
|
-
describe '.status of unknown channel' do
|
35
|
-
let(:id) { 'not-a-channel-id' }
|
36
|
-
it { expect{channel.status}.to raise_error Yt::Errors::NoItems }
|
37
|
-
end
|
38
|
-
|
39
|
-
describe '.videos of existing channel' do
|
40
|
-
let(:id) { 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
19
|
+
it { expect(channel.statistics_set).to be_a Yt::StatisticsSet }
|
41
20
|
it { expect(channel.videos).to be_a Yt::Collections::Videos }
|
42
21
|
it { expect(channel.videos.first).to be_a Yt::Video }
|
22
|
+
it { expect(channel.playlists).to be_a Yt::Collections::Playlists }
|
23
|
+
it { expect(channel.playlists.first).to be_a Yt::Playlist }
|
43
24
|
end
|
44
25
|
|
45
|
-
|
46
|
-
let(:id) { '
|
26
|
+
context 'given an unknown channel' do
|
27
|
+
let(:id) { 'not-a-channel-id' }
|
47
28
|
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
it { expect(channel.videos.count).to be 0 }
|
52
|
-
end
|
29
|
+
it { expect{channel.snippet}.to raise_error Yt::Errors::NoItems }
|
30
|
+
it { expect{channel.status}.to raise_error Yt::Errors::NoItems }
|
31
|
+
it { expect{channel.statistics_set}.to raise_error Yt::Errors::NoItems }
|
53
32
|
|
54
|
-
|
55
|
-
|
33
|
+
describe 'starting with UC' do
|
34
|
+
let(:id) { 'UC-not-a-channel-id' }
|
56
35
|
|
57
|
-
|
58
|
-
|
36
|
+
# NOTE: This test is just a reflection of YouTube irrational behavior of
|
37
|
+
# returns 0 results if the name of an unknown channel starts with UC, but
|
38
|
+
# returning 100,000 results otherwise (ignoring the channel filter).
|
39
|
+
it { expect(channel.videos.count).to be 0 }
|
40
|
+
end
|
59
41
|
end
|
60
42
|
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'yt/models/playlist_item'
|
3
|
+
|
4
|
+
describe Yt::PlaylistItem, :server_app do
|
5
|
+
subject(:item) { Yt::PlaylistItem.new id: id }
|
6
|
+
|
7
|
+
context 'given an existing playlist item' do
|
8
|
+
let(:id) { 'PLjW_GNR5Ir0GWEP_oveGBNjTvKkYyZfsna1TZDCBP-Z8' }
|
9
|
+
|
10
|
+
it 'returns valid snippet data' do
|
11
|
+
expect(item.snippet).to be_a Yt::Snippet
|
12
|
+
expect(item.title).to be_a String
|
13
|
+
expect(item.description).to be_a Yt::Description
|
14
|
+
expect(item.thumbnail_url).to be_a String
|
15
|
+
expect(item.published_at).to be_a Time
|
16
|
+
expect(item.channel_id).to be_a String
|
17
|
+
expect(item.channel_title).to be_a String
|
18
|
+
expect(item.playlist_id).to be_a String
|
19
|
+
expect(item.position).to be_an Integer
|
20
|
+
expect(item.video_id).to be_a String
|
21
|
+
expect(item.video).to be_a Yt::Models::Video
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
context 'given an unknown playlist item' do
|
26
|
+
let(:id) { 'not-a-playlist-item-id' }
|
27
|
+
|
28
|
+
it { expect{item.snippet}.to raise_error Yt::Errors::RequestError }
|
29
|
+
end
|
30
|
+
end
|
@@ -1,36 +1,32 @@
|
|
1
|
-
# encoding: UTF-8
|
2
|
-
|
3
1
|
require 'spec_helper'
|
4
2
|
require 'yt/models/playlist'
|
5
3
|
|
6
4
|
describe Yt::Playlist, :server_app do
|
7
|
-
|
5
|
+
subject(:playlist) { Yt::Playlist.new id: id }
|
8
6
|
|
9
|
-
|
7
|
+
context 'given an existing playlist' do
|
10
8
|
let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
|
11
|
-
it { expect(playlist.snippet).to be_a Yt::Snippet }
|
12
|
-
end
|
13
9
|
|
14
|
-
|
15
|
-
|
10
|
+
it 'returns valid snippet data' do
|
11
|
+
expect(playlist.snippet).to be_a Yt::Snippet
|
12
|
+
expect(playlist.title).to be_a String
|
13
|
+
expect(playlist.description).to be_a Yt::Description
|
14
|
+
expect(playlist.thumbnail_url).to be_a String
|
15
|
+
expect(playlist.published_at).to be_a Time
|
16
|
+
expect(playlist.tags).to be_an Array
|
17
|
+
expect(playlist.channel_id).to be_a String
|
18
|
+
expect(playlist.channel_title).to be_a String
|
19
|
+
end
|
20
|
+
|
16
21
|
it { expect(playlist.status).to be_a Yt::Status }
|
22
|
+
it { expect(playlist.playlist_items).to be_a Yt::Collections::PlaylistItems }
|
23
|
+
it { expect(playlist.playlist_items.first).to be_a Yt::PlaylistItem }
|
17
24
|
end
|
18
25
|
|
19
|
-
|
26
|
+
context 'given an unknown playlist' do
|
20
27
|
let(:id) { 'not-a-playlist-id' }
|
21
|
-
it { expect{playlist.snippet}.to raise_error Yt::Errors::NoItems }
|
22
|
-
end
|
23
28
|
|
24
|
-
|
25
|
-
let(:id) { 'not-a-playlist-id' }
|
29
|
+
it { expect{playlist.snippet}.to raise_error Yt::Errors::NoItems }
|
26
30
|
it { expect{playlist.status}.to raise_error Yt::Errors::NoItems }
|
27
31
|
end
|
28
|
-
|
29
|
-
describe '.playlist_items of someone else’s playlist' do
|
30
|
-
let(:id) { 'PLSWYkYzOrPMRCK6j0UgryI8E0NHhoVdRc' }
|
31
|
-
let(:video_id) { 'MESycYJytkU' }
|
32
|
-
|
33
|
-
it { expect(playlist.playlist_items).to be_a Yt::Collections::PlaylistItems }
|
34
|
-
it { expect(playlist.playlist_items.first).to be_a Yt::PlaylistItem }
|
35
|
-
end
|
36
|
-
end
|
32
|
+
end
|
@@ -8,7 +8,20 @@ describe Yt::Video, :server_app do
|
|
8
8
|
let(:id) { 'MESycYJytkU' }
|
9
9
|
|
10
10
|
it { expect(video.content_detail).to be_a Yt::ContentDetail }
|
11
|
-
|
11
|
+
|
12
|
+
it 'returns valid snippet data' do
|
13
|
+
expect(video.snippet).to be_a Yt::Snippet
|
14
|
+
expect(video.title).to be_a String
|
15
|
+
expect(video.description).to be_a Yt::Description
|
16
|
+
expect(video.thumbnail_url).to be_a String
|
17
|
+
expect(video.published_at).to be_a Time
|
18
|
+
expect(video.tags).to be_an Array
|
19
|
+
expect(video.channel_id).to be_a String
|
20
|
+
expect(video.channel_title).to be_a String
|
21
|
+
expect(video.category_id).to be_a String
|
22
|
+
expect(video.live_broadcast_content).to be_a String
|
23
|
+
end
|
24
|
+
|
12
25
|
it { expect(video.status).to be_a Yt::Status }
|
13
26
|
it { expect(video.statistics_set).to be_a Yt::StatisticsSet }
|
14
27
|
end
|
@@ -4,18 +4,17 @@ require 'yt/models/playlist_item'
|
|
4
4
|
describe Yt::PlaylistItem do
|
5
5
|
subject(:playlist_item) { Yt::PlaylistItem.new attrs }
|
6
6
|
|
7
|
-
describe '#
|
7
|
+
describe '#snippet' do
|
8
8
|
context 'given fetching a playlist item returns a snippet' do
|
9
9
|
let(:attrs) { {snippet: {"position"=>0}} }
|
10
|
-
it { expect(playlist_item.
|
10
|
+
it { expect(playlist_item.snippet).to be_a Yt::Snippet }
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
describe '#
|
15
|
-
context 'given fetching a playlist item returns a
|
16
|
-
let(:attrs) { {
|
17
|
-
it { expect(playlist_item.
|
18
|
-
it { expect(playlist_item.video.title).to eq "Fullscreen" }
|
14
|
+
describe '#status' do
|
15
|
+
context 'given fetching a playlist item returns a status' do
|
16
|
+
let(:attrs) { {status: {"privacyStatus"=>"public"}} }
|
17
|
+
it { expect(playlist_item.status).to be_a Yt::Status }
|
19
18
|
end
|
20
19
|
end
|
21
20
|
|
data/spec/models/snippet_spec.rb
CHANGED
@@ -3,50 +3,50 @@ require 'yt/models/snippet'
|
|
3
3
|
|
4
4
|
describe Yt::Snippet do
|
5
5
|
subject(:snippet) { Yt::Snippet.new data: data }
|
6
|
+
let(:data) { {} }
|
6
7
|
|
7
8
|
describe '#title' do
|
8
|
-
context 'given
|
9
|
+
context 'given a snippet with a title' do
|
9
10
|
let(:data) { {"title"=>"Fullscreen"} }
|
10
11
|
it { expect(snippet.title).to eq 'Fullscreen' }
|
11
12
|
end
|
12
13
|
|
13
|
-
context 'given
|
14
|
-
let(:data) { {"description"=>"The first media company for the connected generation."} }
|
14
|
+
context 'given a snippet without a title' do
|
15
15
|
it { expect(snippet.title).to eq '' }
|
16
16
|
end
|
17
17
|
end
|
18
18
|
|
19
|
-
describe '#published_at' do
|
20
|
-
|
21
|
-
|
19
|
+
describe '#published_at' do
|
20
|
+
context 'given a snippet with a timestamp' do # always returned by YouTube
|
21
|
+
let(:data) { {"publishedAt"=>"2014-04-22T19:14:49.000Z"} }
|
22
|
+
it { expect(snippet.published_at.year).to be 2014 }
|
23
|
+
end
|
22
24
|
end
|
23
25
|
|
24
26
|
describe '#description' do
|
25
|
-
context 'given
|
27
|
+
context 'given a snippet with a description' do
|
26
28
|
let(:data) { {"description"=>"The first media company for the connected generation."} }
|
27
29
|
it { expect(snippet.description).to eq 'The first media company for the connected generation.' }
|
28
30
|
end
|
29
31
|
|
30
|
-
context 'given
|
31
|
-
let(:data) { {"title"=>"Fullscreen"} }
|
32
|
+
context 'given a snippet without a description' do
|
32
33
|
it { expect(snippet.description).to eq '' }
|
33
34
|
end
|
34
35
|
end
|
35
36
|
|
36
37
|
describe '#tags' do
|
37
|
-
context 'given
|
38
|
+
context 'given a snippet with tags' do
|
38
39
|
let(:data) { {"tags"=>["promotion", "new media"]} }
|
39
40
|
it { expect(snippet.tags).to eq ["promotion", "new media"] }
|
40
41
|
end
|
41
42
|
|
42
|
-
context 'given
|
43
|
-
let(:data) { {"title"=>"Fullscreen"} }
|
43
|
+
context 'given a snippet without tags' do
|
44
44
|
it { expect(snippet.tags).to eq [] }
|
45
45
|
end
|
46
46
|
end
|
47
47
|
|
48
48
|
describe '#thumbnail_url' do
|
49
|
-
context 'given
|
49
|
+
context 'given a snippet with thumbnails' do
|
50
50
|
let(:data) { {"thumbnails"=>{
|
51
51
|
"default"=>{"url"=> "http://example.com/88x88.jpg"},
|
52
52
|
"medium"=>{"url"=> "http://example.com/240x240.jpg"},
|
@@ -58,9 +58,66 @@ describe Yt::Snippet do
|
|
58
58
|
it { expect(snippet.thumbnail_url :large).to be_nil }
|
59
59
|
end
|
60
60
|
|
61
|
-
context 'given
|
62
|
-
let(:data) { {"title"=>"Fullscreen"} }
|
61
|
+
context 'given a snippet without thumbnails' do
|
63
62
|
it { expect(snippet.thumbnail_url).to be_nil }
|
64
63
|
end
|
65
64
|
end
|
65
|
+
|
66
|
+
describe '#channel_id' do
|
67
|
+
context 'given a snippet with a channel ID' do
|
68
|
+
let(:data) { {"channelId"=>"UCxO1tY8h1AhOz0T4ENwmpow"} }
|
69
|
+
it { expect(snippet.channel_id).to eq 'UCxO1tY8h1AhOz0T4ENwmpow' }
|
70
|
+
end
|
71
|
+
|
72
|
+
context 'given a snippet without a channel ID' do
|
73
|
+
it { expect(snippet.channel_id).to be_nil }
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe '#channel_title' do
|
78
|
+
context 'given a snippet with a channel title' do
|
79
|
+
let(:data) { {"channelTitle"=>"Fullscreen"} }
|
80
|
+
it { expect(snippet.channel_title).to eq 'Fullscreen' }
|
81
|
+
end
|
82
|
+
|
83
|
+
context 'given a snippet without a channel title' do
|
84
|
+
it { expect(snippet.channel_title).to be_nil }
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
describe '#category_id' do
|
89
|
+
context 'given a snippet with a category ID' do
|
90
|
+
let(:data) { {"categoryId"=>"22"} }
|
91
|
+
it { expect(snippet.category_id).to eq '22' }
|
92
|
+
end
|
93
|
+
|
94
|
+
context 'given a snippet without a category ID' do
|
95
|
+
it { expect(snippet.category_id).to be_nil }
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
describe '#live_broadcast_content' do
|
100
|
+
context 'given a snippet with live broadcast content' do
|
101
|
+
let(:data) { {"liveBroadcastContent"=>"live"} }
|
102
|
+
it { expect(snippet.live_broadcast_content).to eq 'live' }
|
103
|
+
end
|
104
|
+
|
105
|
+
context 'given a snippet without live broadcast content' do
|
106
|
+
it { expect(snippet.live_broadcast_content).to be_nil }
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
describe '#video_id and #video' do
|
111
|
+
context 'given a snippet with a video resource' do
|
112
|
+
let(:data) { {"resourceId"=>{"kind"=>"youtube#video","videoId"=>"W4GhTprSsOY"}} }
|
113
|
+
it { expect(snippet.video_id).to eq 'W4GhTprSsOY' }
|
114
|
+
it { expect(snippet.video).to be_a Yt::Models::Video }
|
115
|
+
it { expect(snippet.video.id).to eq 'W4GhTprSsOY' }
|
116
|
+
end
|
117
|
+
|
118
|
+
context 'given a snippet without a video resource' do
|
119
|
+
it { expect(snippet.video_id).to be_nil }
|
120
|
+
it { expect(snippet.video).to be_nil }
|
121
|
+
end
|
122
|
+
end
|
66
123
|
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.7.
|
4
|
+
version: 0.7.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Claudio Baccigalupo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-06-
|
11
|
+
date: 2014-06-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activesupport
|
@@ -178,12 +178,14 @@ files:
|
|
178
178
|
- spec/associations/device_auth/channel_spec.rb
|
179
179
|
- spec/associations/device_auth/content_owner_spec.rb
|
180
180
|
- spec/associations/device_auth/earnings_spec.rb
|
181
|
+
- spec/associations/device_auth/playlist_item_spec.rb
|
181
182
|
- spec/associations/device_auth/playlist_spec.rb
|
182
183
|
- spec/associations/device_auth/resource_spec.rb
|
183
184
|
- spec/associations/device_auth/video_spec.rb
|
184
185
|
- spec/associations/device_auth/views_spec.rb
|
185
186
|
- spec/associations/no_auth/video_spec.rb
|
186
187
|
- spec/associations/server_auth/channel_spec.rb
|
188
|
+
- spec/associations/server_auth/playlist_item_spec.rb
|
187
189
|
- spec/associations/server_auth/playlist_spec.rb
|
188
190
|
- spec/associations/server_auth/resource_spec.rb
|
189
191
|
- spec/associations/server_auth/video_spec.rb
|
@@ -254,12 +256,14 @@ test_files:
|
|
254
256
|
- spec/associations/device_auth/channel_spec.rb
|
255
257
|
- spec/associations/device_auth/content_owner_spec.rb
|
256
258
|
- spec/associations/device_auth/earnings_spec.rb
|
259
|
+
- spec/associations/device_auth/playlist_item_spec.rb
|
257
260
|
- spec/associations/device_auth/playlist_spec.rb
|
258
261
|
- spec/associations/device_auth/resource_spec.rb
|
259
262
|
- spec/associations/device_auth/video_spec.rb
|
260
263
|
- spec/associations/device_auth/views_spec.rb
|
261
264
|
- spec/associations/no_auth/video_spec.rb
|
262
265
|
- spec/associations/server_auth/channel_spec.rb
|
266
|
+
- spec/associations/server_auth/playlist_item_spec.rb
|
263
267
|
- spec/associations/server_auth/playlist_spec.rb
|
264
268
|
- spec/associations/server_auth/resource_spec.rb
|
265
269
|
- spec/associations/server_auth/video_spec.rb
|