yt_meta 0.0.1
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 +7 -0
- data/.gitignore +16 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +66 -0
- data/Rakefile +1 -0
- data/lib/yt_meta.rb +12 -0
- data/lib/yt_meta/api_uri.rb +48 -0
- data/lib/yt_meta/configuration.rb +18 -0
- data/lib/yt_meta/fetcher.rb +29 -0
- data/lib/yt_meta/version.rb +3 -0
- data/lib/yt_meta/youtube_fetch_error.rb +4 -0
- data/lib/yt_meta/youtube_id.rb +29 -0
- data/spec/api_uri_spec.rb +40 -0
- data/spec/data_fetcher_spec.rb +30 -0
- data/spec/fixtures/vcr_cassettes/no_video.yml +57 -0
- data/spec/fixtures/vcr_cassettes/valid_id.yml +88 -0
- data/spec/spec_helper.rb +6 -0
- data/spec/youtube_id_spec.rb +26 -0
- data/spec/yt_meta_spec.rb +36 -0
- data/yt_meta.gemspec +26 -0
- metadata +143 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 5851b62a176ca5e3c8044a1cd64c95b5c67ef35a
|
|
4
|
+
data.tar.gz: a321bd5e172e5ccecfdaf8afcc000128da57dc86
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: a6f23792f41917d0a0cefbb083b92ed7c1332dcbfedb9d61b0c7f31fd44ea68ff343dd2ef37150a29fbbf4b3ec0f5bda80161dc98f4bd73aa076f41848acd228
|
|
7
|
+
data.tar.gz: 70c9bcef7f77148d82b86de3a2a0215ec6ebc4e486e777238aef8958f98f028a9c4d266ceb1db72b3041bd7a9f8747cc0843ad5f4e419ee1de7e9e6e40a852f1
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 Brent Yoder
|
|
2
|
+
|
|
3
|
+
MIT License
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# YtMeta
|
|
2
|
+
|
|
3
|
+
This gem takes a Youtube URL or ID and optional data attributes and returns a
|
|
4
|
+
hash of metadata about the video.
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Add this line to your application's Gemfile:
|
|
9
|
+
|
|
10
|
+
gem 'yt_meta'
|
|
11
|
+
|
|
12
|
+
And then execute:
|
|
13
|
+
|
|
14
|
+
$ bundle
|
|
15
|
+
|
|
16
|
+
Or install it yourself as:
|
|
17
|
+
|
|
18
|
+
$ gem install yt_meta
|
|
19
|
+
|
|
20
|
+
## Usage
|
|
21
|
+
|
|
22
|
+
Google requires an API key to use the Youtube API. Follow these instructions to
|
|
23
|
+
create an app: [Generate Key](https://developers.google.com/youtube/registering_an_application).
|
|
24
|
+
Use the server key Google generates for you as your API key. Then create an
|
|
25
|
+
initializer with your API key and an optional default parts array so you don't
|
|
26
|
+
have to specify the parts you want every time.
|
|
27
|
+
|
|
28
|
+
```ruby
|
|
29
|
+
YtMeta.configure do |config|
|
|
30
|
+
config.api_key = "API_KEY"
|
|
31
|
+
config.parts = %w[snippet player] #optional
|
|
32
|
+
end
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
To retrieve all public data about a video (or your default parts),
|
|
36
|
+
`require "yt_meta"` and then call `YtMeta.fetch_data(youtube_id: "some_id")`.
|
|
37
|
+
|
|
38
|
+
If you wish to fetch only specific parts, call
|
|
39
|
+
`YtMeta.fetch_data(youtube_id: "some_id", parts: ["snippet", "player"])`,
|
|
40
|
+
replacing the parts in the array with the ones you want.
|
|
41
|
+
|
|
42
|
+
You can find a full list of available parts here:
|
|
43
|
+
[API Docs](https://developers.google.com/youtube/v3/docs/videos/list).
|
|
44
|
+
Be aware that `fileDetails`, `processingDetails`, and `suggestions` require
|
|
45
|
+
client OAuth2 login and won't work with this gem.
|
|
46
|
+
|
|
47
|
+
## Contributing
|
|
48
|
+
|
|
49
|
+
1. Fork!
|
|
50
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
51
|
+
3. Add a configuration file with your API key at `spec/support/api_config.rb`.
|
|
52
|
+
It's `.gitignore`d, so your credentials won't be saved to the repo.
|
|
53
|
+
|
|
54
|
+
```ruby
|
|
55
|
+
YtMeta.configure do |config|
|
|
56
|
+
config.api_key = "TEST_API_KEY"
|
|
57
|
+
end
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
4. Make your changes!
|
|
61
|
+
5. Make sure all tests pass! If you get SSL certificate verification errors
|
|
62
|
+
when running the specs, try
|
|
63
|
+
[these fixes](http://railsapps.github.io/openssl-certificate-verify-failed.html)
|
|
64
|
+
6. Commit your changes (`git commit -am 'Add some feature'`)
|
|
65
|
+
7. Push to the branch (`git push origin my-new-feature`)
|
|
66
|
+
8. Create new Pull Request
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
data/lib/yt_meta.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require "yt_meta/version"
|
|
2
|
+
require "yt_meta/fetcher"
|
|
3
|
+
require "yt_meta/youtube_id"
|
|
4
|
+
require "yt_meta/api_uri"
|
|
5
|
+
|
|
6
|
+
module YtMeta
|
|
7
|
+
def self.fetch_data(youtube_id: "", parts: nil)
|
|
8
|
+
youtube_id = YoutubeId.parse_url(youtube_id)
|
|
9
|
+
api_uri = ApiUri.new(youtube_id, parts)
|
|
10
|
+
Fetcher.new(youtube_id, api_uri).fetch
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
require "open-uri"
|
|
2
|
+
require "yt_meta/configuration"
|
|
3
|
+
|
|
4
|
+
module YtMeta
|
|
5
|
+
class ApiUri
|
|
6
|
+
def initialize(youtube_id, parts=nil)
|
|
7
|
+
@youtube_id = youtube_id
|
|
8
|
+
@parts = Array(parts || default_parts)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def read
|
|
12
|
+
api_uri.read
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_s
|
|
16
|
+
api_uri.to_s
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
private
|
|
20
|
+
def config
|
|
21
|
+
@config ||= YtMeta.configuration
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def api_key
|
|
25
|
+
config.api_key
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def default_parts
|
|
29
|
+
config.parts
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def api_base_url
|
|
33
|
+
'https://www.googleapis.com/youtube/v3/videos'
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def api_uri
|
|
37
|
+
uri = URI.parse(api_base_url)
|
|
38
|
+
uri.query = query_string
|
|
39
|
+
uri
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def query_string
|
|
43
|
+
query_hash = {id: @youtube_id, key: api_key}
|
|
44
|
+
query_hash[:part] = @parts.join(',') unless @parts.empty?
|
|
45
|
+
query_hash.map {|key, val| "#{key}=#{val}"}.join("&")
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module YtMeta
|
|
2
|
+
class << self
|
|
3
|
+
attr_accessor :configuration
|
|
4
|
+
|
|
5
|
+
def configure
|
|
6
|
+
self.configuration ||= Configuration.new
|
|
7
|
+
yield(configuration)
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class Configuration
|
|
12
|
+
attr_accessor :api_key, :parts
|
|
13
|
+
|
|
14
|
+
def initialize
|
|
15
|
+
@parts = %w[id snippet player contentDetails recordingDetails statistics status topicDetails]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "yt_meta/youtube_fetch_error"
|
|
2
|
+
require "json"
|
|
3
|
+
|
|
4
|
+
module YtMeta
|
|
5
|
+
class Fetcher
|
|
6
|
+
def initialize(youtube_id, api_uri)
|
|
7
|
+
@youtube_id = youtube_id
|
|
8
|
+
@api_uri = api_uri
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def fetch
|
|
12
|
+
response = JSON.parse(@api_uri.read)
|
|
13
|
+
|
|
14
|
+
raise_fetch_error response["message"] if response["error"]
|
|
15
|
+
raise_not_found if response["items"].empty?
|
|
16
|
+
|
|
17
|
+
response["items"].first
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
private
|
|
21
|
+
def raise_fetch_error(message)
|
|
22
|
+
raise YoutubeFetchError, message
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def raise_not_found
|
|
26
|
+
raise YoutubeFetchError, "Video with ID #{@youtube_id} was not found."
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
require "yt_meta/youtube_fetch_error"
|
|
2
|
+
|
|
3
|
+
module YtMeta
|
|
4
|
+
class YoutubeId
|
|
5
|
+
class << self
|
|
6
|
+
def parse_url(input)
|
|
7
|
+
input = input.to_s.strip
|
|
8
|
+
raise_invalid_input_error if input.empty?
|
|
9
|
+
|
|
10
|
+
extract_id(input) || input
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
private
|
|
14
|
+
def extract_id(input)
|
|
15
|
+
if matches = input.match(youtube_id_regex)
|
|
16
|
+
matches[:id]
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def youtube_id_regex
|
|
21
|
+
/(youtu)(be\.com|\.be)\/(watch\?.*v=)?(?<id>[a-zA-Z0-9\-\_]*)/i
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def raise_invalid_input_error
|
|
25
|
+
raise YoutubeFetchError, "You must provide a valid video ID or URL"
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "yt_meta/api_uri"
|
|
3
|
+
require "support/api_config"
|
|
4
|
+
|
|
5
|
+
describe YtMeta::ApiUri do
|
|
6
|
+
describe "#to_s" do
|
|
7
|
+
it "returns a proper Youtube API URL" do
|
|
8
|
+
api_url = described_class.new("C93umZAYYII")
|
|
9
|
+
api_key = YtMeta.configuration.api_key
|
|
10
|
+
expect(api_url.to_s).to eq "https://www.googleapis.com/youtube/v3/videos?id=C93umZAYYII&key=#{api_key}&part=#{all_parts.join(',')}"
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
context "specifying parts" do
|
|
15
|
+
it "includes all parts if none are given" do
|
|
16
|
+
api_uri = described_class.new("C93umZAYYII")
|
|
17
|
+
all_parts.each {|part_name|
|
|
18
|
+
expect(api_uri.to_s).to match(/#{part_name}/)
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "includes only the given part" do
|
|
23
|
+
api_uri = described_class.new("C93umZAYYII", "snippet")
|
|
24
|
+
expect(api_uri.to_s).to match(/snippet/)
|
|
25
|
+
expect(api_uri.to_s).to_not match(/player/)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "includes only the given parts" do
|
|
29
|
+
api_uri = described_class.new("C93umZAYYII", %w[snippet player])
|
|
30
|
+
expect(api_uri.to_s).to match(/snippet/)
|
|
31
|
+
expect(api_uri.to_s).to match(/player/)
|
|
32
|
+
expect(api_uri.to_s).to_not match(/contentDetails/)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
private
|
|
37
|
+
def all_parts
|
|
38
|
+
YtMeta.configuration.parts
|
|
39
|
+
end
|
|
40
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "yt_meta/fetcher"
|
|
3
|
+
|
|
4
|
+
describe YtMeta::Fetcher do
|
|
5
|
+
it "raises an import error when initialized with a non-existent ID" do
|
|
6
|
+
api_uri = double(:api_uri, read: error_json)
|
|
7
|
+
|
|
8
|
+
expect { described_class.new("novideo", api_uri).fetch }
|
|
9
|
+
.to raise_error YtMeta::YoutubeFetchError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
context "with a valid Youtube ID" do
|
|
13
|
+
it "fetches correct video metadata" do
|
|
14
|
+
api_uri = double(:api_uri, read: video_json)
|
|
15
|
+
|
|
16
|
+
data = described_class.new("C93umZAYYII", api_uri).fetch
|
|
17
|
+
expect(data).to be_a Hash
|
|
18
|
+
expect(data["snippet"]["title"]).to eq("Title!")
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
private
|
|
23
|
+
def error_json
|
|
24
|
+
JSON.generate(error: "error", message: "No video")
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def video_json
|
|
28
|
+
JSON.generate(items: [{snippet: {title: "Title!"}}])
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
http_interactions:
|
|
3
|
+
- request:
|
|
4
|
+
method: get
|
|
5
|
+
uri: https://www.googleapis.com/youtube/v3/videos?id=novideo&key=AIzaSyDqGdVzllC-lXsGCgqIPjg15mI2AVolW-M&part=snippet
|
|
6
|
+
body:
|
|
7
|
+
encoding: US-ASCII
|
|
8
|
+
string: ''
|
|
9
|
+
headers:
|
|
10
|
+
Accept-Encoding:
|
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
|
12
|
+
Accept:
|
|
13
|
+
- '*/*'
|
|
14
|
+
User-Agent:
|
|
15
|
+
- Ruby
|
|
16
|
+
response:
|
|
17
|
+
status:
|
|
18
|
+
code: 200
|
|
19
|
+
message: OK
|
|
20
|
+
headers:
|
|
21
|
+
Expires:
|
|
22
|
+
- Mon, 30 Sep 2013 21:48:18 GMT
|
|
23
|
+
Date:
|
|
24
|
+
- Mon, 30 Sep 2013 21:48:18 GMT
|
|
25
|
+
Cache-Control:
|
|
26
|
+
- private, max-age=300, must-revalidate, no-transform
|
|
27
|
+
Etag:
|
|
28
|
+
- '"Agg_AJE1TyhX8kUKlYNIvjtfysI/FoCsU7yzN8iy5FBrKm0m_B4IVLI"'
|
|
29
|
+
Content-Type:
|
|
30
|
+
- application/json; charset=UTF-8
|
|
31
|
+
X-Content-Type-Options:
|
|
32
|
+
- nosniff
|
|
33
|
+
X-Frame-Options:
|
|
34
|
+
- SAMEORIGIN
|
|
35
|
+
X-Xss-Protection:
|
|
36
|
+
- 1; mode=block
|
|
37
|
+
Server:
|
|
38
|
+
- GSE
|
|
39
|
+
Alternate-Protocol:
|
|
40
|
+
- 443:quic
|
|
41
|
+
Transfer-Encoding:
|
|
42
|
+
- chunked
|
|
43
|
+
body:
|
|
44
|
+
encoding: UTF-8
|
|
45
|
+
string: |
|
|
46
|
+
{
|
|
47
|
+
"kind": "youtube#videoListResponse",
|
|
48
|
+
"etag": "\"Agg_AJE1TyhX8kUKlYNIvjtfysI/FoCsU7yzN8iy5FBrKm0m_B4IVLI\"",
|
|
49
|
+
"pageInfo": {
|
|
50
|
+
"totalResults": 0,
|
|
51
|
+
"resultsPerPage": 0
|
|
52
|
+
},
|
|
53
|
+
"items": []
|
|
54
|
+
}
|
|
55
|
+
http_version:
|
|
56
|
+
recorded_at: Mon, 30 Sep 2013 21:48:18 GMT
|
|
57
|
+
recorded_with: VCR 2.6.0
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
---
|
|
2
|
+
http_interactions:
|
|
3
|
+
- request:
|
|
4
|
+
method: get
|
|
5
|
+
uri: https://www.googleapis.com/youtube/v3/videos?id=C93umZAYYII&key=AIzaSyDqGdVzllC-lXsGCgqIPjg15mI2AVolW-M&part=snippet
|
|
6
|
+
body:
|
|
7
|
+
encoding: US-ASCII
|
|
8
|
+
string: ''
|
|
9
|
+
headers:
|
|
10
|
+
Accept-Encoding:
|
|
11
|
+
- gzip;q=1.0,deflate;q=0.6,identity;q=0.3
|
|
12
|
+
Accept:
|
|
13
|
+
- '*/*'
|
|
14
|
+
User-Agent:
|
|
15
|
+
- Ruby
|
|
16
|
+
response:
|
|
17
|
+
status:
|
|
18
|
+
code: 200
|
|
19
|
+
message: OK
|
|
20
|
+
headers:
|
|
21
|
+
Expires:
|
|
22
|
+
- Mon, 30 Sep 2013 21:48:17 GMT
|
|
23
|
+
Date:
|
|
24
|
+
- Mon, 30 Sep 2013 21:48:17 GMT
|
|
25
|
+
Cache-Control:
|
|
26
|
+
- private, max-age=300, must-revalidate, no-transform
|
|
27
|
+
Etag:
|
|
28
|
+
- '"Agg_AJE1TyhX8kUKlYNIvjtfysI/MAq3QnA5iIbZYC1KI_XNMxPCGFg"'
|
|
29
|
+
Content-Type:
|
|
30
|
+
- application/json; charset=UTF-8
|
|
31
|
+
X-Content-Type-Options:
|
|
32
|
+
- nosniff
|
|
33
|
+
X-Frame-Options:
|
|
34
|
+
- SAMEORIGIN
|
|
35
|
+
X-Xss-Protection:
|
|
36
|
+
- 1; mode=block
|
|
37
|
+
Server:
|
|
38
|
+
- GSE
|
|
39
|
+
Alternate-Protocol:
|
|
40
|
+
- 443:quic
|
|
41
|
+
Transfer-Encoding:
|
|
42
|
+
- chunked
|
|
43
|
+
body:
|
|
44
|
+
encoding: UTF-8
|
|
45
|
+
string: |
|
|
46
|
+
{
|
|
47
|
+
"kind": "youtube#videoListResponse",
|
|
48
|
+
"etag": "\"Agg_AJE1TyhX8kUKlYNIvjtfysI/MAq3QnA5iIbZYC1KI_XNMxPCGFg\"",
|
|
49
|
+
"pageInfo": {
|
|
50
|
+
"totalResults": 1,
|
|
51
|
+
"resultsPerPage": 1
|
|
52
|
+
},
|
|
53
|
+
"items": [
|
|
54
|
+
{
|
|
55
|
+
"kind": "youtube#video",
|
|
56
|
+
"etag": "\"Agg_AJE1TyhX8kUKlYNIvjtfysI/ObaPgqXgsNSRHsynHRCVsL4Ynpk\"",
|
|
57
|
+
"id": "C93umZAYYII",
|
|
58
|
+
"snippet": {
|
|
59
|
+
"publishedAt": "2013-02-11T18:30:23.000Z",
|
|
60
|
+
"channelId": "UCTAgbu2l6_rBKdbTvEodEDw",
|
|
61
|
+
"title": "REAL WORLD: Whiterun - Episode 4 (Skyrim machinima)",
|
|
62
|
+
"description": "Subscribe for More! http://goo.gl/dy9jl\nFrom creator @bryan_basham, producer of the viral \"Death and Return of Superman\" and Nerdist Channel's COPS: Skyrim, comes the next level of \"reality\" machinima set in Elder Scroll's Tamriel - The REAL WORLD: Whiterun.\n\nFeaturing Margaret Cho as the voice Lorandia.\nMusic by Ursula 1000: http://www.ursula1000.com/\n\nWatch Episode 1: http://youtu.be/lOWYUaX9jgU\nWatch Episode 2: http://youtu.be/SdWVSGHGoM0\nWatch Episode 3: http://youtu.be/wdH5FKW3Isg\n\nSee more from @bryan_basham and Adjacent Studios at http://www.adjacentla.com. Like us on facebook. http://www.facebook.com/adjacentla\n\nCheck out Bryan Basham's soon to come YouTube Channel on the Machinima network! http://www.youtube.com/adjacentgames\n\nCheck out the video's Animator! @ http://www.youtube.com/tyrannicon",
|
|
63
|
+
"thumbnails": {
|
|
64
|
+
"default": {
|
|
65
|
+
"url": "https://i1.ytimg.com/vi/C93umZAYYII/default.jpg"
|
|
66
|
+
},
|
|
67
|
+
"medium": {
|
|
68
|
+
"url": "https://i1.ytimg.com/vi/C93umZAYYII/mqdefault.jpg"
|
|
69
|
+
},
|
|
70
|
+
"high": {
|
|
71
|
+
"url": "https://i1.ytimg.com/vi/C93umZAYYII/hqdefault.jpg"
|
|
72
|
+
},
|
|
73
|
+
"standard": {
|
|
74
|
+
"url": "https://i1.ytimg.com/vi/C93umZAYYII/sddefault.jpg"
|
|
75
|
+
},
|
|
76
|
+
"maxres": {
|
|
77
|
+
"url": "https://i1.ytimg.com/vi/C93umZAYYII/maxresdefault.jpg"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
"channelTitle": "Nerdist",
|
|
81
|
+
"categoryId": "20"
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
]
|
|
85
|
+
}
|
|
86
|
+
http_version:
|
|
87
|
+
recorded_at: Mon, 30 Sep 2013 21:48:17 GMT
|
|
88
|
+
recorded_with: VCR 2.6.0
|
data/spec/spec_helper.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "yt_meta/youtube_id"
|
|
3
|
+
|
|
4
|
+
describe YtMeta::YoutubeId do
|
|
5
|
+
it "returns the input when not given a URL" do
|
|
6
|
+
described_class.parse_url("C93umZAYYII").should == "C93umZAYYII"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
it "returns the ID when given a valid full URL" do
|
|
10
|
+
id = described_class.parse_url("http://www.youtube.com/watch?v=1eCbGrMfQ-g")
|
|
11
|
+
expect(id).to eq "1eCbGrMfQ-g"
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
it "returns the ID when given a valid short URL" do
|
|
15
|
+
id = described_class.parse_url("http://www.youtu.be/1eCbGrMfQ-g")
|
|
16
|
+
expect(id).to eq "1eCbGrMfQ-g"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it "raises an error when given an empty string" do
|
|
20
|
+
expect { described_class.parse_url("") }.to raise_error YtMeta::YoutubeFetchError
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
it "raises an error when given a string made up only of spaces" do
|
|
24
|
+
expect { described_class.parse_url(" ") }.to raise_error YtMeta::YoutubeFetchError
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
require "spec_helper"
|
|
2
|
+
require "yt_meta"
|
|
3
|
+
require "support/api_config"
|
|
4
|
+
|
|
5
|
+
describe YtMeta do
|
|
6
|
+
uri_without_api_key = VCR.request_matchers.uri_without_param(:key)
|
|
7
|
+
|
|
8
|
+
context "#fetch_data" do
|
|
9
|
+
it "returns a hash of video data" do
|
|
10
|
+
VCR.use_cassette("valid_id", match_requests_on: [uri_without_api_key]) do
|
|
11
|
+
data = described_class.fetch_data(
|
|
12
|
+
youtube_id: "C93umZAYYII", parts: "snippet"
|
|
13
|
+
)
|
|
14
|
+
|
|
15
|
+
expect(data).to have_key "snippet"
|
|
16
|
+
expect(data["snippet"]["title"]).to eq(
|
|
17
|
+
"REAL WORLD: Whiterun - Episode 4 (Skyrim machinima)"
|
|
18
|
+
)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
it "raises an error when no id or url is given" do
|
|
23
|
+
expect {
|
|
24
|
+
described_class.fetch_data
|
|
25
|
+
}.to raise_error YtMeta::YoutubeFetchError
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
it "raises an error when an invalid ID is given" do
|
|
29
|
+
VCR.use_cassette("no_video", match_requests_on: [uri_without_api_key]) do
|
|
30
|
+
expect {
|
|
31
|
+
described_class.fetch_data(youtube_id: "novideo", parts: "snippet")
|
|
32
|
+
}.to raise_error YtMeta::YoutubeFetchError
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
end
|
data/yt_meta.gemspec
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'yt_meta/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = "yt_meta"
|
|
8
|
+
spec.version = YtMeta::VERSION
|
|
9
|
+
spec.authors = ["Brent Yoder"]
|
|
10
|
+
spec.email = ["brent@bpyoder.com"]
|
|
11
|
+
spec.description = "A tiny gem that pulls metadata for a specified video from Google's Youtube API"
|
|
12
|
+
spec.summary = "A tiny gem that pulls metadata for a specified video from Google's Youtube API"
|
|
13
|
+
spec.homepage = "https://github.com/InAbsentia/yt_meta"
|
|
14
|
+
spec.license = "MIT"
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
|
19
|
+
spec.require_paths = ["lib"]
|
|
20
|
+
|
|
21
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
|
22
|
+
spec.add_development_dependency "rake"
|
|
23
|
+
spec.add_development_dependency "vcr", "~> 2.6.0"
|
|
24
|
+
spec.add_development_dependency "rspec", "~> 2.14.1"
|
|
25
|
+
spec.add_development_dependency "webmock", "~> 1.13.0"
|
|
26
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: yt_meta
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Brent Yoder
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2013-09-30 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: bundler
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - ~>
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '1.3'
|
|
20
|
+
type: :development
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - ~>
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '1.3'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: rake
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :development
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: vcr
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ~>
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: 2.6.0
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: 2.6.0
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rspec
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - ~>
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: 2.14.1
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - ~>
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: 2.14.1
|
|
69
|
+
- !ruby/object:Gem::Dependency
|
|
70
|
+
name: webmock
|
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
|
72
|
+
requirements:
|
|
73
|
+
- - ~>
|
|
74
|
+
- !ruby/object:Gem::Version
|
|
75
|
+
version: 1.13.0
|
|
76
|
+
type: :development
|
|
77
|
+
prerelease: false
|
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
79
|
+
requirements:
|
|
80
|
+
- - ~>
|
|
81
|
+
- !ruby/object:Gem::Version
|
|
82
|
+
version: 1.13.0
|
|
83
|
+
description: A tiny gem that pulls metadata for a specified video from Google's Youtube
|
|
84
|
+
API
|
|
85
|
+
email:
|
|
86
|
+
- brent@bpyoder.com
|
|
87
|
+
executables: []
|
|
88
|
+
extensions: []
|
|
89
|
+
extra_rdoc_files: []
|
|
90
|
+
files:
|
|
91
|
+
- .gitignore
|
|
92
|
+
- Gemfile
|
|
93
|
+
- LICENSE.txt
|
|
94
|
+
- README.md
|
|
95
|
+
- Rakefile
|
|
96
|
+
- lib/yt_meta.rb
|
|
97
|
+
- lib/yt_meta/api_uri.rb
|
|
98
|
+
- lib/yt_meta/configuration.rb
|
|
99
|
+
- lib/yt_meta/fetcher.rb
|
|
100
|
+
- lib/yt_meta/version.rb
|
|
101
|
+
- lib/yt_meta/youtube_fetch_error.rb
|
|
102
|
+
- lib/yt_meta/youtube_id.rb
|
|
103
|
+
- spec/api_uri_spec.rb
|
|
104
|
+
- spec/data_fetcher_spec.rb
|
|
105
|
+
- spec/fixtures/vcr_cassettes/no_video.yml
|
|
106
|
+
- spec/fixtures/vcr_cassettes/valid_id.yml
|
|
107
|
+
- spec/spec_helper.rb
|
|
108
|
+
- spec/youtube_id_spec.rb
|
|
109
|
+
- spec/yt_meta_spec.rb
|
|
110
|
+
- yt_meta.gemspec
|
|
111
|
+
homepage: https://github.com/InAbsentia/yt_meta
|
|
112
|
+
licenses:
|
|
113
|
+
- MIT
|
|
114
|
+
metadata: {}
|
|
115
|
+
post_install_message:
|
|
116
|
+
rdoc_options: []
|
|
117
|
+
require_paths:
|
|
118
|
+
- lib
|
|
119
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
120
|
+
requirements:
|
|
121
|
+
- - '>='
|
|
122
|
+
- !ruby/object:Gem::Version
|
|
123
|
+
version: '0'
|
|
124
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
|
+
requirements:
|
|
126
|
+
- - '>='
|
|
127
|
+
- !ruby/object:Gem::Version
|
|
128
|
+
version: '0'
|
|
129
|
+
requirements: []
|
|
130
|
+
rubyforge_project:
|
|
131
|
+
rubygems_version: 2.1.5
|
|
132
|
+
signing_key:
|
|
133
|
+
specification_version: 4
|
|
134
|
+
summary: A tiny gem that pulls metadata for a specified video from Google's Youtube
|
|
135
|
+
API
|
|
136
|
+
test_files:
|
|
137
|
+
- spec/api_uri_spec.rb
|
|
138
|
+
- spec/data_fetcher_spec.rb
|
|
139
|
+
- spec/fixtures/vcr_cassettes/no_video.yml
|
|
140
|
+
- spec/fixtures/vcr_cassettes/valid_id.yml
|
|
141
|
+
- spec/spec_helper.rb
|
|
142
|
+
- spec/youtube_id_spec.rb
|
|
143
|
+
- spec/yt_meta_spec.rb
|