yt 0.9.5 → 0.9.6
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 +1 -0
- data/README.md +3 -3
- data/lib/yt/actions/delete_all.rb +3 -1
- data/lib/yt/collections/playlist_items.rb +8 -8
- data/lib/yt/collections/playlists.rb +6 -14
- data/lib/yt/collections/resources.rb +56 -0
- data/lib/yt/models/playlist.rb +5 -2
- data/lib/yt/models/resource.rb +28 -6
- data/lib/yt/models/video.rb +2 -1
- data/lib/yt/version.rb +1 -1
- data/spec/requests/as_account/channel_spec.rb +3 -3
- data/spec/requests/as_account/playlist_spec.rb +11 -0
- data/spec/requests/as_account/video_spec.rb +11 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f2401a0d9b81d6da08424943a09b392870081fc6
|
4
|
+
data.tar.gz: b1fd60bb4f1426f34182730f73bdf4641e6025d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 74acf5be09a43cdbb3f7da60d275c57b5db7fb9336c4f236c0db48257b0120ebb95619a4e5a5ae8eb77a5b6450c600d0cbed14bb015a43e1d2b474c813020caf
|
7
|
+
data.tar.gz: 7daada1a1bc24ddd581cacbb6f2171a5947f9c387b608222dae391b664bb944d1c22a8e6d6cbcfd7df3531ae90622e200eae61eaebfbe8bf00c8e333c0475523
|
data/Gemfile.lock
CHANGED
data/HISTORY.md
CHANGED
@@ -8,6 +8,7 @@ v0.9 - 2014/07/28
|
|
8
8
|
* Add claim.third_party?
|
9
9
|
* Add actual_start_time, actual_end_time, scheduled_start_time, scheduled_end_time for live-streaming videos
|
10
10
|
* Add privacy_status, public_stats_viewable, publish_at to the options that Video#update accepts
|
11
|
+
* Allow angle brackets when editing title, description, tags and replace them with similar characters allowed by YouTube
|
11
12
|
|
12
13
|
v0.8 - 2014/07/18
|
13
14
|
-----------------
|
data/README.md
CHANGED
@@ -41,7 +41,7 @@ To install on your system, run
|
|
41
41
|
|
42
42
|
To use inside a bundled Ruby project, add this line to the Gemfile:
|
43
43
|
|
44
|
-
gem 'yt', '~> 0.9.
|
44
|
+
gem 'yt', '~> 0.9.6'
|
45
45
|
|
46
46
|
Since the gem follows [Semantic Versioning](http://semver.org),
|
47
47
|
indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
|
@@ -287,7 +287,7 @@ video.like #=> true
|
|
287
287
|
account = Yt::Account.new access_token: 'ya29.1.ABCDEFGHIJ'
|
288
288
|
video = Yt::Video.new id: 'MESycYJytkU', auth: account
|
289
289
|
|
290
|
-
video.update title: 'A title', description: 'A description', tags: ['a tag'], categoryId: '21'
|
290
|
+
video.update title: 'A title', description: 'A description <with angle brackets>', tags: ['a tag'], categoryId: '21'
|
291
291
|
|
292
292
|
video.views since: 7.days.ago #=> {Wed, 28 May 2014 => 12.0, Thu, 29 May 2014 => 3.0, …}
|
293
293
|
video.comments until: 2.days.ago #=> {Wed, 28 May 2014 => 9.0, Thu, 29 May 2014 => 4.0, …}
|
@@ -353,7 +353,7 @@ playlist.playlist_items.first #=> #<Yt::Models::PlaylistItem @id=...>
|
|
353
353
|
*The methods above do not require authentication.*
|
354
354
|
|
355
355
|
```ruby
|
356
|
-
playlist.update title: 'title', description: 'desc', tags: ['new tag'], privacy_status: 'private'
|
356
|
+
playlist.update title: 'A <title> with angle brackets', description: 'desc', tags: ['new tag'], privacy_status: 'private'
|
357
357
|
playlist.add_video 'MESycYJytkU'
|
358
358
|
playlist.add_videos ['MESycYJytkU', 'MESycYJytkU']
|
359
359
|
playlist.delete_playlist_items title: 'Fullscreen Creator Platform' #=> [true]
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'yt/actions/list'
|
2
3
|
require 'yt/models/request'
|
3
4
|
|
@@ -20,7 +21,8 @@ module Yt
|
|
20
21
|
# TODO: could be symbol etc...
|
21
22
|
item.respond_to?(method) && case value
|
22
23
|
when Regexp then item.send(method) =~ value
|
23
|
-
|
24
|
+
when Array then item.send(method) == value.map{|item| item.to_s.gsub('<', '‹').gsub('>', '›')}
|
25
|
+
else item.send(method) == value.to_s.gsub('<', '‹').gsub('>', '›')
|
24
26
|
end
|
25
27
|
end
|
26
28
|
end
|
@@ -4,12 +4,8 @@ module Yt
|
|
4
4
|
module Collections
|
5
5
|
class PlaylistItems < Resources
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
resource = {kind: "youtube##{attrs[:kind]}"}
|
10
|
-
resource["#{attrs[:kind]}Id"] = attrs[:id]
|
11
|
-
snippet = {playlistId: @parent.id, resourceId: resource}
|
12
|
-
do_insert body: {snippet: snippet}, params: {part: 'snippet,status'}
|
7
|
+
def insert(attributes = {}, options = {})
|
8
|
+
super attributes.merge(playlist_id: @parent.id), options
|
13
9
|
rescue Yt::Error => error
|
14
10
|
ignorable_errors = error.reasons & ['videoNotFound', 'forbidden']
|
15
11
|
raise error unless options[:ignore_errors] && ignorable_errors.any?
|
@@ -17,8 +13,8 @@ module Yt
|
|
17
13
|
|
18
14
|
private
|
19
15
|
|
20
|
-
# @return [Hash] the parameters to submit to YouTube to list
|
21
|
-
# @see https://developers.google.com/youtube/v3/docs/
|
16
|
+
# @return [Hash] the parameters to submit to YouTube to list playlist items.
|
17
|
+
# @see https://developers.google.com/youtube/v3/docs/playlistItems/list
|
22
18
|
def list_params
|
23
19
|
super.tap{|params| params[:params] = playlist_items_params}
|
24
20
|
end
|
@@ -26,6 +22,10 @@ module Yt
|
|
26
22
|
def playlist_items_params
|
27
23
|
resources_params.merge playlist_id: @parent.id
|
28
24
|
end
|
25
|
+
|
26
|
+
def insert_parts
|
27
|
+
{snippet: {keys: [:playlist_id, :resource_id]}}
|
28
|
+
end
|
29
29
|
end
|
30
30
|
end
|
31
31
|
end
|
@@ -4,20 +4,6 @@ module Yt
|
|
4
4
|
module Collections
|
5
5
|
class Playlists < Resources
|
6
6
|
|
7
|
-
# Valid body (no defaults) are: title (string), description (string), privacy_status (string),
|
8
|
-
# tags (array of strings)
|
9
|
-
def insert(options = {})
|
10
|
-
body = {}
|
11
|
-
|
12
|
-
snippet = options.slice :title, :description, :tags
|
13
|
-
body[:snippet] = snippet if snippet.any?
|
14
|
-
|
15
|
-
status = options[:privacy_status]
|
16
|
-
body[:status] = {privacyStatus: status} if status
|
17
|
-
|
18
|
-
do_insert body: body, params: {part: 'snippet,status'}
|
19
|
-
end
|
20
|
-
|
21
7
|
private
|
22
8
|
|
23
9
|
# @return [Hash] the parameters to submit to YouTube to list channels.
|
@@ -29,6 +15,12 @@ module Yt
|
|
29
15
|
def playlists_params
|
30
16
|
resources_params.merge channel_id: @parent.id
|
31
17
|
end
|
18
|
+
|
19
|
+
def insert_parts
|
20
|
+
snippet = {keys: [:title, :description, :tags], sanitize_brackets: true}
|
21
|
+
status = {keys: [:privacy_status]}
|
22
|
+
{snippet: snippet, status: status}
|
23
|
+
end
|
32
24
|
end
|
33
25
|
end
|
34
26
|
end
|
@@ -1,3 +1,4 @@
|
|
1
|
+
# encoding: UTF-8
|
1
2
|
require 'yt/collections/base'
|
2
3
|
|
3
4
|
module Yt
|
@@ -7,6 +8,13 @@ module Yt
|
|
7
8
|
do_delete_all params
|
8
9
|
end
|
9
10
|
|
11
|
+
def insert(attributes = {}, options = {}) #
|
12
|
+
underscore_keys! attributes
|
13
|
+
body = build_insert_body attributes
|
14
|
+
params = {part: body.keys.join(',')}
|
15
|
+
do_insert(params: params, body: body)
|
16
|
+
end
|
17
|
+
|
10
18
|
private
|
11
19
|
|
12
20
|
# @return [resource_class] a new resource item initialized with
|
@@ -27,6 +35,54 @@ module Yt
|
|
27
35
|
require "yt/models/#{resource_name.underscore}"
|
28
36
|
"Yt::Models::#{resource_name}".constantize
|
29
37
|
end
|
38
|
+
|
39
|
+
def build_insert_body(attributes = {})
|
40
|
+
{}.tap do |body|
|
41
|
+
insert_parts.each do |name, part|
|
42
|
+
if should_include_part_in_insert? part, attributes
|
43
|
+
body[name] = build_insert_body_part part, attributes
|
44
|
+
sanitize_brackets! body[name] if part[:sanitize_brackets]
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
def build_insert_body_part(part, attributes = {})
|
51
|
+
{}.tap do |body_part|
|
52
|
+
part[:keys].map do |key|
|
53
|
+
body_part[camelize key] = attributes[key]
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def should_include_part_in_insert?(part, attributes = {})
|
59
|
+
(part[:keys] & attributes.keys).any?
|
60
|
+
end
|
61
|
+
|
62
|
+
# If we dropped support for ActiveSupport 3, then we could simply
|
63
|
+
# invoke transform_keys!{|key| key.to_s.underscore.to_sym}
|
64
|
+
def underscore_keys!(hash)
|
65
|
+
hash.dup.each_key{|key| hash[underscore key] = hash.delete key}
|
66
|
+
end
|
67
|
+
|
68
|
+
# @return [Hash] the original hash with angle brackets characters in its
|
69
|
+
# values replaced with similar Unicode characters accepted by Youtube.
|
70
|
+
# @see https://support.google.com/youtube/answer/57404?hl=en
|
71
|
+
def sanitize_brackets!(source)
|
72
|
+
case source
|
73
|
+
when String then source.gsub('<', '‹').gsub('>', '›')
|
74
|
+
when Array then source.map{|string| sanitize_brackets! string}
|
75
|
+
when Hash then source.each{|k,v| source[k] = sanitize_brackets! v}
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def camelize(value)
|
80
|
+
value.to_s.camelize(:lower).to_sym
|
81
|
+
end
|
82
|
+
|
83
|
+
def underscore(value)
|
84
|
+
value.to_s.underscore.to_sym
|
85
|
+
end
|
30
86
|
end
|
31
87
|
end
|
32
88
|
end
|
data/lib/yt/models/playlist.rb
CHANGED
@@ -68,13 +68,16 @@ module Yt
|
|
68
68
|
|
69
69
|
# @see https://developers.google.com/youtube/v3/docs/playlists/update
|
70
70
|
def update_parts
|
71
|
-
|
71
|
+
keys = [:title, :description, :tags]
|
72
|
+
snippet = {keys: keys, required: true, sanitize_brackets: true}
|
72
73
|
status = {keys: [:privacy_status]}
|
73
74
|
{snippet: snippet, status: status}
|
74
75
|
end
|
75
76
|
|
77
|
+
# @todo: extend camelize to also camelize the nested hashes, so we
|
78
|
+
# don’t have to write videoId
|
76
79
|
def video_params(video_id)
|
77
|
-
{
|
80
|
+
{resource_id: {kind: 'youtube#video', videoId: video_id}}
|
78
81
|
end
|
79
82
|
end
|
80
83
|
end
|
data/lib/yt/models/resource.rb
CHANGED
@@ -1,3 +1,5 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
1
3
|
require 'yt/models/base'
|
2
4
|
require 'yt/models/url'
|
3
5
|
|
@@ -56,13 +58,22 @@ module Yt
|
|
56
58
|
end
|
57
59
|
|
58
60
|
def build_update_body(attributes = {})
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
61
|
+
{}.tap do |body|
|
62
|
+
update_parts.each do |name, part|
|
63
|
+
if should_include_part_in_update? part, attributes
|
64
|
+
body[name] = build_update_body_part part, attributes
|
65
|
+
sanitize_brackets! body[name] if part[:sanitize_brackets]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def build_update_body_part(part, attributes = {})
|
72
|
+
{}.tap do |body_part|
|
73
|
+
part[:keys].map do |key|
|
74
|
+
body_part[camelize key] = attributes.fetch key, send(key)
|
75
|
+
end
|
64
76
|
end
|
65
|
-
body
|
66
77
|
end
|
67
78
|
|
68
79
|
def should_include_part_in_update?(part, attributes = {})
|
@@ -75,6 +86,17 @@ module Yt
|
|
75
86
|
hash.dup.each_key{|key| hash[underscore key] = hash.delete key}
|
76
87
|
end
|
77
88
|
|
89
|
+
# @return [Hash] the original hash with angle brackets characters in its
|
90
|
+
# values replaced with similar Unicode characters accepted by Youtube.
|
91
|
+
# @see https://support.google.com/youtube/answer/57404?hl=en
|
92
|
+
def sanitize_brackets!(source)
|
93
|
+
case source
|
94
|
+
when String then source.gsub('<', '‹').gsub('>', '›')
|
95
|
+
when Array then source.map{|string| sanitize_brackets! string}
|
96
|
+
when Hash then source.each{|k,v| source[k] = sanitize_brackets! v}
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
78
100
|
def camelize(value)
|
79
101
|
value.to_s.camelize(:lower).to_sym
|
80
102
|
end
|
data/lib/yt/models/video.rb
CHANGED
@@ -161,9 +161,10 @@ module Yt
|
|
161
161
|
# @todo: Add recording details keys
|
162
162
|
def update_parts
|
163
163
|
snippet_keys = [:title, :description, :tags, :category_id]
|
164
|
+
snippet = {keys: snippet_keys, sanitize_brackets: true}
|
164
165
|
status_keys = [:privacy_status, :embeddable, :license,
|
165
166
|
:public_stats_viewable, :publish_at]
|
166
|
-
{snippet:
|
167
|
+
{snippet: snippet, status: {keys: status_keys}}
|
167
168
|
end
|
168
169
|
end
|
169
170
|
end
|
data/lib/yt/version.rb
CHANGED
@@ -54,9 +54,9 @@ describe Yt::Channel, :device_app do
|
|
54
54
|
|
55
55
|
context 'given my own channel' do
|
56
56
|
let(:id) { $account.channel.id }
|
57
|
-
let(:title) { 'Yt Test title' }
|
58
|
-
let(:description) { 'Yt Test description' }
|
59
|
-
let(:tags) { ['Yt Test Tag 1', 'Yt Test Tag 2'] }
|
57
|
+
let(:title) { 'Yt Test <title>' }
|
58
|
+
let(:description) { 'Yt Test <description>' }
|
59
|
+
let(:tags) { ['Yt Test Tag 1', 'Yt Test <Tag> 2'] }
|
60
60
|
let(:privacy_status) { 'unlisted' }
|
61
61
|
let(:params) { {title: title, description: description, tags: tags, privacy_status: privacy_status} }
|
62
62
|
|
@@ -91,6 +91,17 @@ describe Yt::Playlist, :device_app do
|
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
|
+
context 'given I update title, description and/or tags using angle brackets' do
|
95
|
+
let(:attrs) { {title: "Yt Test < >", description: '< >', tags: ['<tag>']} }
|
96
|
+
|
97
|
+
specify 'updates them replacing angle brackets with similar unicode characters accepted by YouTube' do
|
98
|
+
expect(update).to be true
|
99
|
+
expect(playlist.title).to eq 'Yt Test ‹ ›'
|
100
|
+
expect(playlist.description).to eq '‹ ›'
|
101
|
+
expect(playlist.tags).to eq ['‹tag›']
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
94
105
|
context 'given I update the privacy status' do
|
95
106
|
let!(:new_privacy_status) { old_privacy_status == 'private' ? 'unlisted' : 'private' }
|
96
107
|
|
@@ -194,6 +194,17 @@ describe Yt::Video, :device_app do
|
|
194
194
|
end
|
195
195
|
end
|
196
196
|
|
197
|
+
context 'given I update title, description and/or tags using angle brackets' do
|
198
|
+
let(:attrs) { {title: "Example Yt Test < >", description: '< >', tags: ['<tag>']} }
|
199
|
+
|
200
|
+
specify 'updates them replacing angle brackets with similar unicode characters accepted by YouTube' do
|
201
|
+
expect(update).to be true
|
202
|
+
expect(video.title).to eq 'Example Yt Test ‹ ›'
|
203
|
+
expect(video.description).to eq '‹ ›'
|
204
|
+
expect(video.tags).to eq ['‹tag›']
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
197
208
|
# note: 'scheduled' videos cannot be set to 'unlisted'
|
198
209
|
context 'given I update the privacy status' do
|
199
210
|
before { video.update publish_at: nil if video.scheduled? }
|
@@ -347,5 +358,4 @@ describe Yt::Video, :device_app do
|
|
347
358
|
end
|
348
359
|
end
|
349
360
|
end
|
350
|
-
|
351
361
|
end
|