Skip to content

Commit

Permalink
Merge pull request #73 from vheuken/youtubeplaylist-fix
Browse files Browse the repository at this point in the history
fixed YoutubePlaylist spec
  • Loading branch information
thibaudgg committed Jun 1, 2015
2 parents 9658088 + 70d6cdd commit 002bcc4
Show file tree
Hide file tree
Showing 16 changed files with 2,311 additions and 35 deletions.
4 changes: 2 additions & 2 deletions lib/video_info/provider.rb
Expand Up @@ -56,8 +56,8 @@ def _clean_options(options)
options
end

def _set_data_from_api
uri = open(_api_url, options)
def _set_data_from_api(api_url = _api_url)
uri = open(api_url, options)
MultiJson.load(uri.read)
end

Expand Down
45 changes: 14 additions & 31 deletions lib/video_info/providers/youtubeplaylist.rb
@@ -1,56 +1,39 @@
require_relative 'youtubeplaylist_api'
require_relative 'youtubeplaylist_scraper'

class VideoInfo
module Providers
class YoutubePlaylist < Youtube

alias_method :playlist_id, :video_id
attr_accessor :playlist_items_data

def self.usable?(url)
url =~ /((youtube\.com)\/playlist)|((youtube\.com)\/embed\/videoseries)/
def initialize(url, options = {})
if VideoInfo.provider_api_keys[:youtube].nil?
extend YoutubePlaylistScraper
else
extend YoutubePlaylistAPI
end

super(url, options)
end

def videos
_playlist_video_ids.map do |entry_id|
VideoInfo.new("http://www.youtube.com/watch?v=#{entry_id}")
end
def self.usable?(url)
url =~ /((youtube\.com)\/playlist)|((youtube\.com)\/embed\/videoseries)/
end

def embed_url
"//www.youtube.com/embed/videoseries?list=#{playlist_id}"
end

def description
_playlist_entry['subtitle']['$t']
end

%w[date keywords duration view_count].each do |method|
define_method(method) { nil }
end

private

def _playlist_entry
data['feed']
end

def _video_entry
_playlist_entry
end

def _url_regex
/youtube.com\/playlist\?p=(\S*)|youtube.com\/embed\/videoseries\?list=([a-zA-Z0-9-]*)/
end

def _api_path
"/feeds/api/playlists/#{playlist_id}?v=2&alt=json"
end

def _playlist_video_ids
return [] unless _playlist_entry['entry']
_playlist_entry['entry'].map do |entry|
entry['media$group']['yt$videoid']['$t']
end
end

end
end
end
45 changes: 45 additions & 0 deletions lib/video_info/providers/youtubeplaylist_api.rb
@@ -0,0 +1,45 @@
class VideoInfo
module YoutubePlaylistAPI
def description
data['items'][0]['snippet']['description']
end

def videos
_playlist_video_ids.map do |entry_id|
VideoInfo.new("http://www.youtube.com/watch?v=#{entry_id}")
end
end

private

def _playlist_entry
data['items']
end

def _playlist_items
data['items']
end

def _api_path
"/youtube/v3/playlists?part=snippet&id=#{playlist_id}&key=#{api_key}"
end

def _playlist_items_api_path
"/youtube/v3/playlistItems?part=snippet&playlistId=#{playlist_id}&fields=items&key=#{api_key}"
end

def _playlist_items_api_url
"https://#{_api_base}#{_playlist_items_api_path}"
end

def _playlist_items_data
@playlist_items_data ||= _set_data_from_api(_playlist_items_api_url)
end

def _playlist_video_ids
_playlist_items_data['items'].map do |item|
item['snippet']['resourceId']['videoId']
end
end
end
end
59 changes: 59 additions & 0 deletions lib/video_info/providers/youtubeplaylist_scraper.rb
@@ -0,0 +1,59 @@
require 'oga'
require 'open-uri'
require 'open_uri_redirections'

class VideoInfo
module Providers
module YoutubePlaylistScraper
def description
meta_nodes = data.css('meta')

description_node = meta_nodes.detect do |m|
m.attr('name').value == 'description'
end

description_node.attr('content').value
end

def title
meta_nodes = data.css('meta')

title_node = meta_nodes.detect do |m|
m.attr('name').value == 'title'
end

title_node.attr('content').value
end

def videos
raise(NotImplementedError, 'To access videos, you must provide an API key to VideoInfo.provider_api_keys')
end

def thumbnail_small
thumbnail_medium.sub('mqdefault.jpg', 'default.jpg')
end

def thumbnail_medium
'https:' + data.css('div.pl-header-thumb img').attr('src')[0].value
end

def thumbnail_large
thumbnail_medium.sub('mqdefault.jpg', 'hqdefault.jpg')
end

private

def available?
!data.css('div#page').attr('class')[0].value.include?('oops-content')
end

def _set_data_from_api(api_url = _api_url)
Oga.parse_html(open(api_url, :allow_redirections => :safe))
end

def _api_url
@url
end
end
end
end
Expand Up @@ -43,4 +43,61 @@ http_interactions:
string: ''
http_version:
recorded_at: Mon, 09 Mar 2015 14:06:54 GMT
- request:
method: get
uri: https://www.googleapis.com/youtube/v3/playlists?id=PLA575C81A1FBC04CF_invalid&key=AIzaSyA6PYwSr1EnLFUFy1cZDk3Ifb0rxeJaeZ0&part=snippet
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- VideoInfo/2.4.2
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
response:
status:
code: 200
message: OK
headers:
Expires:
- Sun, 17 May 2015 10:27:44 GMT
Date:
- Sun, 17 May 2015 10:27:44 GMT
Cache-Control:
- private, max-age=0, must-revalidate, no-transform
Etag:
- '"NO6QTeg0-3ShswIeqLchQ_mzWJs/ZLdVv1yknYCGRyTYfR9cqqqg-3c"'
Vary:
- Origin
- X-Origin
Content-Type:
- application/json; charset=UTF-8
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- SAMEORIGIN
X-Xss-Protection:
- 1; mode=block
Content-Length:
- '192'
Server:
- GSE
Alternate-Protocol:
- 443:quic,p=1
body:
encoding: UTF-8
string: |
{
"kind": "youtube#playlistListResponse",
"etag": "\"NO6QTeg0-3ShswIeqLchQ_mzWJs/ZLdVv1yknYCGRyTYfR9cqqqg-3c\"",
"pageInfo": {
"totalResults": 0,
"resultsPerPage": 0
},
"items": []
}
http_version:
recorded_at: Sun, 17 May 2015 10:25:36 GMT
recorded_with: VCR 2.9.3
Expand Up @@ -53,4 +53,89 @@ http_interactions:
string: ''
http_version:
recorded_at: Mon, 09 Mar 2015 14:06:53 GMT
- request:
method: get
uri: https://www.googleapis.com/youtube/v3/playlists?id=PLA575C81A1FBC04CF&key=AIzaSyA6PYwSr1EnLFUFy1cZDk3Ifb0rxeJaeZ0&part=snippet
body:
encoding: US-ASCII
string: ''
headers:
User-Agent:
- VideoInfo/2.4.2
Accept-Encoding:
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
Accept:
- "*/*"
response:
status:
code: 200
message: OK
headers:
Expires:
- Sun, 17 May 2015 10:27:44 GMT
Date:
- Sun, 17 May 2015 10:27:44 GMT
Cache-Control:
- private, max-age=0, must-revalidate, no-transform
Etag:
- '"NO6QTeg0-3ShswIeqLchQ_mzWJs/775uqayGKfvufZcffd_1ekvmfO0"'
Vary:
- Origin
- X-Origin
Content-Type:
- application/json; charset=UTF-8
X-Content-Type-Options:
- nosniff
X-Frame-Options:
- SAMEORIGIN
X-Xss-Protection:
- 1; mode=block
Content-Length:
- '881'
Server:
- GSE
Alternate-Protocol:
- 443:quic,p=1
body:
encoding: UTF-8
string: |
{
"kind": "youtube#playlistListResponse",
"etag": "\"NO6QTeg0-3ShswIeqLchQ_mzWJs/775uqayGKfvufZcffd_1ekvmfO0\"",
"pageInfo": {
"totalResults": 1,
"resultsPerPage": 1
},
"items": [
{
"kind": "youtube#playlist",
"etag": "\"NO6QTeg0-3ShswIeqLchQ_mzWJs/LWX5mzkRf8mBLPLSjS1m-HQOnbo\"",
"id": "PLA575C81A1FBC04CF",
"snippet": {
"publishedAt": "2010-01-07T22:20:45.000Z",
"channelId": "UC2y5oKUfkQJDKO7cM_OIc3w",
"title": "the century of self",
"description": "",
"thumbnails": {
"default": {
"url": "https://i.ytimg.com/vi/default.jpg"
},
"medium": {
"url": "https://i.ytimg.com/vi/default.jpg"
},
"high": {
"url": "https://i.ytimg.com/vi/default.jpg"
}
},
"channelTitle": "Chris Szanto",
"localized": {
"title": "the century of self",
"description": ""
}
}
}
]
}
http_version:
recorded_at: Sun, 17 May 2015 10:25:35 GMT
recorded_with: VCR 2.9.3

0 comments on commit 002bcc4

Please sign in to comment.