Skip to content

Commit

Permalink
Allow angle brackets when editing videos/playlists
Browse files Browse the repository at this point in the history
Angle brackets are not characters allowed by YouTube, but instead
of failing, Yt replaces them with Unicode characters that look very
similar and are accepted by YouTube.
  • Loading branch information
claudiofullscreen committed Aug 1, 2014
1 parent 4539fa2 commit 1c6741c
Show file tree
Hide file tree
Showing 9 changed files with 59 additions and 13 deletions.
2 changes: 1 addition & 1 deletion Gemfile.lock
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
yt (0.9.3)
yt (0.9.4)
activesupport

GEM
Expand Down
1 change: 1 addition & 0 deletions HISTORY.md
Expand Up @@ -6,6 +6,7 @@ v0.9 - 2014/07/28
* Add content_owner.policies to list ContentID policies
* Let 'update' methods understand both under_score and camelCased parameters
* Add claim.third_party?
* Allow angle brackets when editing title, description, tags and replace them with similar characters allowed by YouTube


v0.8 - 2014/07/18
Expand Down
6 changes: 3 additions & 3 deletions README.md
Expand Up @@ -41,7 +41,7 @@ To install on your system, run

To use inside a bundled Ruby project, add this line to the Gemfile:

gem 'yt', '~> 0.9.3'
gem 'yt', '~> 0.9.4'

Since the gem follows [Semantic Versioning](http://semver.org),
indicating the full version in your Gemfile (~> *major*.*minor*.*patch*)
Expand Down Expand Up @@ -280,7 +280,7 @@ video.like #=> true
account = Yt::Account.new access_token: 'ya29.1.ABCDEFGHIJ'
video = Yt::Video.new id: 'MESycYJytkU', auth: account

video.update title: 'A title', description: 'A description', tags: ['a tag'], categoryId: '21'
video.update title: 'A title', description: 'A description <with angle brackets>', tags: ['a tag'], categoryId: '21'

video.views since: 7.days.ago #=> {Wed, 28 May 2014 => 12.0, Thu, 29 May 2014 => 3.0, …}
video.comments until: 2.days.ago #=> {Wed, 28 May 2014 => 9.0, Thu, 29 May 2014 => 4.0, …}
Expand Down Expand Up @@ -346,7 +346,7 @@ playlist.playlist_items.first #=> #<Yt::Models::PlaylistItem @id=...>
*The methods above do not require authentication.*

```ruby
playlist.update title: 'title', description: 'desc', tags: ['new tag'], privacy_status: 'private'
playlist.update title: 'A <title> with angle brackets', description: 'desc', tags: ['new tag'], privacy_status: 'private'
playlist.add_video 'MESycYJytkU'
playlist.add_videos ['MESycYJytkU', 'MESycYJytkU']
playlist.delete_playlist_items title: 'Fullscreen Creator Platform' #=> [true]
Expand Down
3 changes: 2 additions & 1 deletion lib/yt/models/playlist.rb
Expand Up @@ -68,7 +68,8 @@ def delete_playlist_items(attrs = {})

# @see https://developers.google.com/youtube/v3/docs/playlists/update
def update_parts
snippet = {keys: [:title, :description, :tags], required: true}
keys = [:title, :description, :tags]
snippet = {keys: keys, required: true, sanitize_brackets: true}
status = {keys: [:privacy_status]}
{snippet: snippet, status: status}
end
Expand Down
34 changes: 28 additions & 6 deletions lib/yt/models/resource.rb
@@ -1,3 +1,5 @@
# encoding: UTF-8

require 'yt/models/base'
require 'yt/models/url'

Expand Down Expand Up @@ -56,13 +58,22 @@ def delete_params
end

def build_update_body(attributes = {})
body = {}
update_parts.each do |name, part|
body[name] = {}.tap do |hash|
part[:keys].map{|k| hash[camelize k] = attributes.fetch k, send(k)}
end if should_include_part_in_update?(part, attributes)
{}.tap do |body|
update_parts.each do |name, part|
if should_include_part_in_update? part, attributes
body[name] = build_update_body_part part, attributes
sanitize_brackets! body[name] if part[:sanitize_brackets]
end
end
end
end

def build_update_body_part(part, attributes = {})
{}.tap do |body_part|
part[:keys].map do |key|
body_part[camelize key] = attributes.fetch key, send(key)
end
end
body
end

def should_include_part_in_update?(part, attributes = {})
Expand All @@ -75,6 +86,17 @@ def underscore_keys!(hash)
hash.dup.each_key{|key| hash[underscore key] = hash.delete key}
end

# @return [Hash] the original hash with angle brackets characters in its
# values replaced with similar Unicode characters accepted by Youtube.
# @see https://support.google.com/youtube/answer/57404?hl=en
def sanitize_brackets!(source)
case source
when String then source.gsub('<', '‹').gsub('>', '›')
when Array then source.map{|string| sanitize_brackets! string}
when Hash then source.each{|k,v| source[k] = sanitize_brackets! v}
end
end

def camelize(value)
value.to_s.camelize(:lower).to_sym
end
Expand Down
2 changes: 1 addition & 1 deletion lib/yt/models/video.rb
Expand Up @@ -158,7 +158,7 @@ def reports_params
# @todo: Add status, recording details keys
def update_parts
snippet_keys = [:title, :description, :tags, :category_id]
{snippet: {keys: snippet_keys}}
{snippet: {keys: snippet_keys, sanitize_brackets: true}}
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/yt/version.rb
@@ -1,3 +1,3 @@
module Yt
VERSION = '0.9.3'
VERSION = '0.9.4'
end
11 changes: 11 additions & 0 deletions spec/requests/as_account/playlist_spec.rb
Expand Up @@ -91,6 +91,17 @@
end
end

context 'given I update title, description and/or tags using angle brackets' do
let(:attrs) { {title: "Yt Test < >", description: '< >', tags: ['<tag>']} }

specify 'updates them replacing angle brackets with similar unicode characters accepted by YouTube' do
expect(update).to be true
expect(playlist.title).to eq 'Yt Test ‹ ›'
expect(playlist.description).to eq '‹ ›'
expect(playlist.tags).to eq ['‹tag›']
end
end

context 'given I update the privacy status' do
let!(:new_privacy_status) { old_privacy_status == 'private' ? 'unlisted' : 'private' }

Expand Down
11 changes: 11 additions & 0 deletions spec/requests/as_account/video_spec.rb
Expand Up @@ -166,6 +166,17 @@
end
end

context 'given I update title, description and/or tags using angle brackets' do
let(:attrs) { {title: "Yt Test < >", description: '< >', tags: ['<tag>']} }

specify 'updates them replacing angle brackets with similar unicode characters accepted by YouTube' do
expect(update).to be true
expect(video.title).to eq 'Yt Test ‹ ›'
expect(video.description).to eq '‹ ›'
expect(video.tags).to eq ['‹tag›']
end
end

it 'returns valid reports for video-related metrics' do
# Some reports are only available to Content Owners.
# See content ownere test for more details about what the methods return.
Expand Down

0 comments on commit 1c6741c

Please sign in to comment.