Skip to content

Commit

Permalink
Add PlaylistItem#insert and .delete
Browse files Browse the repository at this point in the history
  • Loading branch information
claudiob committed Aug 24, 2017
1 parent 7c0d411 commit fe16f8d
Show file tree
Hide file tree
Showing 6 changed files with 81 additions and 3 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ For more information about changelogs, check

## 0.1.5 - 2017-08-24

* [FEATURE] Add `Yt::PlaylistItem.insert` and `Yt::PlaylistItem#delete`
* [FEATURE] Add `Channel#related_playlists` and `Channel#like_playlists`
* [FEATURE] Add Channel.mine

Expand Down
21 changes: 21 additions & 0 deletions docs/playlist_items.html
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,20 @@ <h4>Authentication</h4>
item.position # => 0
{% endhighlight %}

<p>
Other methods <strong>acts on behalf of YouTube accounts</strong> (e.g.: add an item to a playlist).<br />
To use these methods (marked with <span class="label label-warning">&nbsp;</span> below), you need to <a href="{{ site.baseurl }}/#api_client">get an API Client ID/Secret from Google</a>, then <a href="{{ site.baseurl }}/#tokens">obtain a refresh token</a> from the account you want to act as, and finally configure the values:
</p>

{% highlight ruby %}
Yt.configuration.client_id = "<your ID>" ## replace with your client ID
Yt.configuration.client_secret = "<your secret>" ## replace with your client secret
Yt.configuration.refresh_token = "<token>" ## use the account’s refresh token

Yt::PlaylistItem.insert playlist_id: 'PL-LeTutc9GRKD3DhnRF_y', video_id: 'gknzFj_0vvY'
# => #<Yt::PlaylistItem:0x... @id=UEwtTGVUdXRjOUdSS0Qze>
{% endhighlight %}

<hr />
<h4>List of <code>Yt::PlaylistItem</code> data methods</h4>
<dl>
Expand Down Expand Up @@ -63,3 +77,10 @@ <h4>List of <code>Yt::PlaylistItem</code> data methods</h4>
{% include example.html object='fast' method='privacy_status' result='=> no extra HTTP requests' %}</pre>
</div></dd>
</dl>
<dl>
{% include dt.html title="Adding and removing a playlist item" label="warning" auth="must authenticate as the channel’s account" %}
<dd><a class="anchor" id="insert_remove"></a><div class="highlight"><pre>
{% include doc.html class="PlaylistItem#insert" %}{% include example.html object='item = <span class="no">Yt</span><span class="o">::</span><span class="no">PlaylistItem</span>' method='insert' params=' <span class="ss">playlist_id:</span> <span class="s1">"PL-..."</span>, <span class="ss">video_id:</span> <span class="s1">"gknzFj_0vvY"</span>' %}
{% include doc.html instance="PlaylistItem#delete" %}{% include example.html object='item' method='delete' result='true' %}</pre>
</div></dd>
</dl>
22 changes: 22 additions & 0 deletions lib/yt/playlist_item.rb
Original file line number Diff line number Diff line change
Expand Up @@ -55,5 +55,27 @@ class PlaylistItem < Resource
def thumbnail_url(size = :default)
thumbnails.fetch(size.to_s, {})['url']
end

# @return [Yt::PlaylistItem] the item created by appending the given
# video to the given playlist.
def self.insert(playlist_id:, video_id:)
parts = %i(id snippet)
items = -> (body) { [body] } # the response body only includes one item
resource_id = {kind: 'youtube#video', videoId: video_id}
snippet = {playlistId: playlist_id, resourceId: resource_id}

Relation.new(self, parts: parts, extract_items: items) do |options|
post '/youtube/v3/playlistItems', {part: 'snippet'}, {snippet: snippet}
end.first
end

# @return [Boolean] whether the item was removed from the playlist.
def delete
items = -> (body) { [{}] } # the response body is empty

Relation.new(PlaylistItem, id: id, extract_items: items) do |options|
delete '/youtube/v3/playlistItems', id: options[:id]
end.any?
end
end
end
6 changes: 3 additions & 3 deletions lib/yt/relation.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ class Relation
# @yield [Hash] the options to change which items to iterate through.
def initialize(item_class, options = {}, &item_block)
@options = {parts: %i(id), limit: Float::INFINITY, item_class: item_class,
initial_items: -> {[]}}
initial_items: -> {[]}, extract_items: -> (body) {body['items']}}
@options.merge! options
@item_block = item_block
end
Expand All @@ -27,10 +27,10 @@ def find_next
@items ||= initial_items.dup
if @items[@last_index].nil? && more_pages?
response = Response.new(@options, &@item_block).run
more_items = response.body['items'].map do |item|
more_items = @options[:extract_items].call(response.body).map do |item|
@options[:item_class].new attributes_for_new_item(item)
end
@options.merge! offset: response.body['nextPageToken']
@options.merge! offset: response.body['nextPageToken'] if response.body
@items.concat more_items
end
@items[(@last_index +=1) -1]
Expand Down
8 changes: 8 additions & 0 deletions lib/yt/response.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,14 @@ def get(path, params = {})
request :get, path: path, params: params
end

def post(path, params = {}, body = {})
request :post, path: path, params: params, body: body
end

def delete(path, params = {})
request :delete, path: path, params: params
end

def request(method, options = {})
HTTPRequest.new(request_options options.merge method: method).run
rescue HTTPError => error
Expand Down
26 changes: 26 additions & 0 deletions spec/playlist_item/insert_delete_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
require 'spec_helper'

describe 'Yt::PlaylistItem.insert and .delete', :account do
subject(:item) { Yt::PlaylistItem.insert attrs }
let(:attrs) { {playlist_id: playlist_id, video_id: video_id} }

context 'given my own playlist and an existing video' do
# ADD TO README: requires your own account to have at least one playlist
let(:playlist_id) { $own_channel.playlists.first.id }
let(:video_id) { $existing_video_id }

specify 'add and remove the video from the playlist', requests: 3 do
expect(item).to be_a Yt::PlaylistItem
expect(item.title).to be_a String
expect(item.description).to be_a String
expect(item.published_at).to be_a Time
expect(item.thumbnail_url).to be_a String
expect(item.channel_id).to be_a String
expect(item.channel_title).to be_a String
expect(item.playlist_id).to eq playlist_id
expect(item.video_id).to eq video_id

expect(item.delete).to be true
end
end
end

0 comments on commit fe16f8d

Please sign in to comment.