yt 0.9.5 → 0.9.6

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a618a834dfa7b3cecfc38f24ea9d7c7f42db9ca3
4
- data.tar.gz: 8136067b0f3568ac059e4eacfe0eb812c3c9c4e2
3
+ metadata.gz: f2401a0d9b81d6da08424943a09b392870081fc6
4
+ data.tar.gz: b1fd60bb4f1426f34182730f73bdf4641e6025d0
5
5
  SHA512:
6
- metadata.gz: adb13cd19177496972a8f43bf663716ce250ca8e0d53d8aa570efe82ae4b5b97371d9697bb3f90bdec1a1aa6d082f13a79096c9af777a70fe6f27e2a577819f9
7
- data.tar.gz: 212188da7ddfd9dd7c17b62c6568ff51e1fcdc55cc63da4b13552bae96140338723a2fce7b82f025273bd3e538405fbb4da81fcf1d678f621feb4fc58da573e3
6
+ metadata.gz: 74acf5be09a43cdbb3f7da60d275c57b5db7fb9336c4f236c0db48257b0120ebb95619a4e5a5ae8eb77a5b6450c600d0cbed14bb015a43e1d2b474c813020caf
7
+ data.tar.gz: 7daada1a1bc24ddd581cacbb6f2171a5947f9c387b608222dae391b664bb944d1c22a8e6d6cbcfd7df3531ae90622e200eae61eaebfbe8bf00c8e333c0475523
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- yt (0.9.5)
4
+ yt (0.9.6)
5
5
  activesupport
6
6
 
7
7
  GEM
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.5'
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
- else item.send(method) == value
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
- # attrs are id and kind
8
- def insert(attrs = {}, options = {}) #
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 channels.
21
- # @see https://developers.google.com/youtube/v3/docs/channels/list
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
@@ -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
- snippet = {keys: [:title, :description, :tags], required: true}
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
- {id: video_id, kind: :video}
80
+ {resource_id: {kind: 'youtube#video', videoId: video_id}}
78
81
  end
79
82
  end
80
83
  end
@@ -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
- body = {}
60
- update_parts.each do |name, part|
61
- body[name] = {}.tap do |hash|
62
- part[:keys].map{|k| hash[camelize k] = attributes.fetch k, send(k)}
63
- end if should_include_part_in_update?(part, attributes)
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
@@ -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: {keys: snippet_keys}, status: {keys: status_keys}}
167
+ {snippet: snippet, status: {keys: status_keys}}
167
168
  end
168
169
  end
169
170
  end
@@ -1,3 +1,3 @@
1
1
  module Yt
2
- VERSION = '0.9.5'
2
+ VERSION = '0.9.6'
3
3
  end
@@ -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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: yt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.9.5
4
+ version: 0.9.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Claudio Baccigalupo