yt 0.7.0 → 0.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|