youtube-data 0.1.0 → 0.1.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 +4 -4
- data/Gemfile +8 -8
- data/LICENSE.txt +21 -21
- data/README.md +65 -61
- data/Rakefile +4 -4
- data/TODO.md +15 -12
- data/examples/basic_video_info.rb +12 -0
- data/examples/download_thumbnail.rb +12 -0
- data/lib/youtube/extractor.rb +133 -138
- data/lib/youtube/thumbnail.rb +102 -91
- data/lib/youtube/version.rb +5 -5
- data/lib/youtube/video.rb +89 -0
- data/lib/youtube-data.rb +25 -13
- data/sig/youtube.rbs +4 -4
- data/tests/data/get_data +12 -0
- data/tests/mocks.rb +29 -29
- data/tests/test_all.rb +10 -3
- data/tests/test_extractor.rb +45 -32
- data/tests/test_thumbnail.rb +41 -0
- data/tests/test_video.rb +74 -0
- metadata +15 -10
- data/tests/data/raw_video.html +0 -89
data/lib/youtube/thumbnail.rb
CHANGED
|
@@ -1,91 +1,102 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Youtube
|
|
4
|
-
|
|
5
|
-
# Used for extracting thumbnails/ images data and bytes stored on `i.ytimg.com`.
|
|
6
|
-
#
|
|
7
|
-
# @param extractor [Youtube::DataExtractor] The extractor to use when getting initial required video data.
|
|
8
|
-
#
|
|
9
|
-
class Thumbnail
|
|
10
|
-
BASEHOST = URI('https://i.ytimg.com/')
|
|
11
|
-
|
|
12
|
-
def initialize(extractor)
|
|
13
|
-
@extractor = extractor
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
# The
|
|
32
|
-
#
|
|
33
|
-
#
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
#
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
end
|
|
53
|
-
|
|
54
|
-
#
|
|
55
|
-
def
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
#
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Youtube
|
|
4
|
+
|
|
5
|
+
# Used for extracting thumbnails/ images data and bytes stored on `i.ytimg.com`.
|
|
6
|
+
#
|
|
7
|
+
# @param extractor [Youtube::DataExtractor] The extractor to use when getting initial required video data.
|
|
8
|
+
#
|
|
9
|
+
class Thumbnail
|
|
10
|
+
BASEHOST = URI('https://i.ytimg.com/')
|
|
11
|
+
|
|
12
|
+
def initialize(extractor, opts = {})
|
|
13
|
+
@extractor = extractor
|
|
14
|
+
|
|
15
|
+
# The session to use for the extractor allows information to persist during requests/session.
|
|
16
|
+
@session = Net::HTTP.start(BASEHOST.hostname, {'use_ssl': true})
|
|
17
|
+
if opts.include?(:mock_session)
|
|
18
|
+
@session = opts[:mock_session]
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
@thumb_json_array = Youtube::Video.new(@extractor).thumbnails
|
|
22
|
+
@thumb_default_json = @thumb_json_array[0]
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def inspect
|
|
26
|
+
return itself
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# An array of thumbnail(s) json data (stored as a hash).
|
|
30
|
+
#
|
|
31
|
+
# @return [Array] The array of hashes about the thumbnail(s).
|
|
32
|
+
# @yield [Array] The same as return, but yields to block if given.
|
|
33
|
+
#
|
|
34
|
+
def thumbnails_json
|
|
35
|
+
return @thumb_json_array unless block_given?
|
|
36
|
+
yield @thumb_json_array
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
# The json data for the default thumbnail (stored as a hash).
|
|
40
|
+
#
|
|
41
|
+
# @return [Hash] The hash form of the default thumbnail's json.
|
|
42
|
+
# @yield [Hash] The same as return, but yields to block if given.
|
|
43
|
+
#
|
|
44
|
+
def default_json
|
|
45
|
+
return @thumb_default_json unless block_given?
|
|
46
|
+
yield @thumb_default_json
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# @return [URI::HTTPS] Destination of the default thumbnail file on the server.
|
|
50
|
+
def default_url
|
|
51
|
+
return URI(@thumb_default_json['url'])
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# @return [String] Filename for the default thumbnail.
|
|
55
|
+
def default_filename
|
|
56
|
+
return default_url.path[/[A-Za-z0-9]+\.[A-Za-z0-9]+/]
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
# @return [String] The bytes from the default thumbnail file on the server. Sends one request.
|
|
60
|
+
def default_bytes
|
|
61
|
+
return get_raw(default_url.path).body
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Downloads default thumbnail to cwd with it's name from server unless a path/ file name is specified.
|
|
65
|
+
def download_default(file_path=nil)
|
|
66
|
+
download_thumbnail(default_bytes, if file_path.nil? then default_filename else file_path end)
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# Send a simple get request using the thumbnail session. This should be how the module sends all further requests
|
|
70
|
+
# to the `i.ytimg.com` hostname outside of this class also.
|
|
71
|
+
#
|
|
72
|
+
# @param path [String] Any valid path on the server, prefixed with `/`.
|
|
73
|
+
# @return [Net::HTTPResponse] The untouched response sent back from the request.
|
|
74
|
+
# @yield [Net::HTTPResponse] Same as return but yielded to block.
|
|
75
|
+
#
|
|
76
|
+
def get_raw(path)
|
|
77
|
+
res = get_request_path(path)
|
|
78
|
+
return res unless block_given?
|
|
79
|
+
yield res
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Downloads thumbnail bytes (writes to file in byte mode).
|
|
83
|
+
private def download_thumbnail(bytes, file_path=nil)
|
|
84
|
+
File.open(file_path, 'wb') do |file_stream|
|
|
85
|
+
file_stream.write(bytes)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Use `get_raw` method it's the same, but is shorter and can yield.
|
|
90
|
+
private def get_request_path(path)
|
|
91
|
+
if path.class != "".class
|
|
92
|
+
raise InvalidPathError, 'Path on server must be type `String\''
|
|
93
|
+
end
|
|
94
|
+
if path.empty? == false and path[0] != "/"
|
|
95
|
+
raise InvalidPathError, 'Path must be prefixed with `/\''
|
|
96
|
+
end
|
|
97
|
+
return @session.get(path)
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
end
|
|
101
|
+
|
|
102
|
+
end
|
data/lib/youtube/version.rb
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
module Youtube
|
|
4
|
-
VERSION = "0.1.
|
|
5
|
-
end
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Youtube
|
|
4
|
+
VERSION = "0.1.1"
|
|
5
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Youtube
|
|
4
|
+
|
|
5
|
+
class Video
|
|
6
|
+
|
|
7
|
+
def initialize(extractor, opts = {})
|
|
8
|
+
@extractor = extractor
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def inspect
|
|
12
|
+
return itself
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# @return [Hash] The section of scraped json that contains video information just as is.
|
|
16
|
+
def json
|
|
17
|
+
@video_info_json = @extractor.video_json_untouched['videoDetails']
|
|
18
|
+
return @video_info_json unless block_given?
|
|
19
|
+
yield @video_info_json
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# @return [Array] An array of different thumbnail image info hashes.
|
|
23
|
+
def thumbnails
|
|
24
|
+
return json['thumbnail']['thumbnails']
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# @return [String] ID of the channel that owns the video.
|
|
28
|
+
def channel_id
|
|
29
|
+
return json['channelId']
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
# @return [String] ID of the video.
|
|
33
|
+
def id
|
|
34
|
+
return json['videoId']
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# @return [String] The title of the video.
|
|
38
|
+
def title
|
|
39
|
+
return json['title']
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# @return [String] The video description.
|
|
43
|
+
def description
|
|
44
|
+
return json['shortDescription']
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
# @return [Integer] Number of views that the video has.
|
|
48
|
+
def views
|
|
49
|
+
return json['viewCount'].to_i
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# @return [String] The person who uploaded the video.
|
|
53
|
+
def author
|
|
54
|
+
return json['author']
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Same as `author` method.
|
|
58
|
+
def uploader
|
|
59
|
+
return author
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# @return [Array] An array of keywords relating to the video.
|
|
63
|
+
def keywords
|
|
64
|
+
return json['keywords']
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# @return [Integer] Length of the video in seconds.
|
|
68
|
+
def length_in_seconds
|
|
69
|
+
return json['lengthSeconds'].to_i
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# @return [TrueClass, FalseClass] Whether or not the video is crawlable in the player.
|
|
73
|
+
def is_crawlable?
|
|
74
|
+
return json['isCrawlable']
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
# @return [TrueClass, FalseClass] Whether or not the video displays likes.
|
|
78
|
+
def allow_ratings?
|
|
79
|
+
return json['allowRatings']
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# @return [TrueClass, FalseClass] Whether or not the video is a live stream.
|
|
83
|
+
def is_live?
|
|
84
|
+
return json['isLiveContent']
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
end
|
data/lib/youtube-data.rb
CHANGED
|
@@ -1,13 +1,25 @@
|
|
|
1
|
-
# frozen_string_literal: true
|
|
2
|
-
|
|
3
|
-
require 'uri'
|
|
4
|
-
require 'net/http'
|
|
5
|
-
require 'json'
|
|
6
|
-
|
|
7
|
-
require_relative 'youtube/version'
|
|
8
|
-
require_relative 'youtube/extractor'
|
|
9
|
-
require_relative 'youtube/thumbnail'
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'uri'
|
|
4
|
+
require 'net/http'
|
|
5
|
+
require 'json'
|
|
6
|
+
|
|
7
|
+
require_relative 'youtube/version'
|
|
8
|
+
require_relative 'youtube/extractor'
|
|
9
|
+
require_relative 'youtube/thumbnail'
|
|
10
|
+
require_relative 'youtube/video'
|
|
11
|
+
|
|
12
|
+
module Youtube
|
|
13
|
+
|
|
14
|
+
class InitExtractorError < RuntimeError
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class InvalidVideoIDError < StandardError
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class InvalidPathError < StandardError
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
class Error < StandardError; end
|
|
24
|
+
|
|
25
|
+
end
|
data/sig/youtube.rbs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
module Youtube
|
|
2
|
-
VERSION: String
|
|
3
|
-
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
|
|
4
|
-
end
|
|
1
|
+
module Youtube
|
|
2
|
+
VERSION: String
|
|
3
|
+
# See the writing guide of rbs: https://github.com/ruby/rbs#guides
|
|
4
|
+
end
|
data/tests/data/get_data
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
#!/usr/bin/bash
|
|
2
|
+
# Get the test html for mocked testing video id = FtutLA63Cp8.
|
|
3
|
+
|
|
4
|
+
cur=$(basename $(pwd))
|
|
5
|
+
|
|
6
|
+
if [ $cur != 'tests' ]
|
|
7
|
+
then
|
|
8
|
+
echo 'This script must be run from within the `tests/` sub-directory.'
|
|
9
|
+
exit -1
|
|
10
|
+
fi
|
|
11
|
+
|
|
12
|
+
curl 'https://www.youtube.com/watch?v=FtutLA63Cp8' -o 'data/raw_video.html'
|
data/tests/mocks.rb
CHANGED
|
@@ -1,29 +1,29 @@
|
|
|
1
|
-
require 'net/http/response'
|
|
2
|
-
|
|
3
|
-
# Mock types for testing the `Youtube` module.
|
|
4
|
-
module Mocks
|
|
5
|
-
|
|
6
|
-
class MockResponse < Net::HTTPResponse
|
|
7
|
-
|
|
8
|
-
def stream_check # Avoid: `stream_check': undefined method `closed?' for nil:NilClass (NoMethodError)
|
|
9
|
-
return
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
class MockSession
|
|
15
|
-
def initialize(url)
|
|
16
|
-
@res = MockResponse.new(1.0, 200, "OK")
|
|
17
|
-
end
|
|
18
|
-
|
|
19
|
-
def set_res_body(content)
|
|
20
|
-
@res.read_body # Update body content and set.
|
|
21
|
-
@res.body = content
|
|
22
|
-
end
|
|
23
|
-
|
|
24
|
-
def get(path)
|
|
25
|
-
return @res
|
|
26
|
-
end
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
end
|
|
1
|
+
require 'net/http/response'
|
|
2
|
+
|
|
3
|
+
# Mock types for testing the `Youtube` module.
|
|
4
|
+
module Mocks
|
|
5
|
+
|
|
6
|
+
class MockResponse < Net::HTTPResponse
|
|
7
|
+
|
|
8
|
+
def stream_check # Avoid: `stream_check': undefined method `closed?' for nil:NilClass (NoMethodError)
|
|
9
|
+
return
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
class MockSession
|
|
15
|
+
def initialize(url)
|
|
16
|
+
@res = MockResponse.new(1.0, 200, "OK")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def set_res_body(content)
|
|
20
|
+
@res.read_body # Update body content and set.
|
|
21
|
+
@res.body = content
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get(path)
|
|
25
|
+
return @res
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
end
|
data/tests/test_all.rb
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# frozen_string_literal: true
|
|
3
|
+
|
|
4
|
+
require 'test/unit'
|
|
5
|
+
|
|
6
|
+
require_relative 'test_extractor'
|
|
7
|
+
require_relative 'test_thumbnail'
|
|
8
|
+
require_relative 'test_video'
|
|
9
|
+
|
|
10
|
+
#https://ruby-doc.org/stdlib-3.0.2/libdoc/test-unit/rdoc/Test/Unit/Assertions.html
|
data/tests/test_extractor.rb
CHANGED
|
@@ -1,32 +1,45 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
|
|
5
|
+
require_relative '../lib/youtube-data'
|
|
6
|
+
require_relative 'mocks'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestExtractor < Test::Unit::TestCase
|
|
10
|
+
def setup
|
|
11
|
+
@mock_session = Mocks::MockSession.new(nil)
|
|
12
|
+
# Copy valid video html from file into the mock sessions `get` mock response which acts the same as normal session.
|
|
13
|
+
@mock_session.set_res_body(File.read('./data/raw_video.html'))
|
|
14
|
+
# Specify mock session to use and replace actual session with `opts[:mock_session]`.
|
|
15
|
+
@extractor = Youtube::DataExtractor.new('FtutLA63Cp8', {:mock_session => @mock_session})
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def test_video_uri
|
|
19
|
+
assert_equal(@extractor.video_uri, URI('https://www.youtube.com/watch?v=FtutLA63Cp8'))
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def test_player_uri
|
|
23
|
+
assert_instance_of(URI::HTTPS, @extractor.player_uri)
|
|
24
|
+
assert_true(@extractor.player_uri.to_s.end_with?('base.js'))
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def test_response_invalid_path
|
|
28
|
+
assert_raise(Youtube::InvalidPathError) do
|
|
29
|
+
@extractor.get_raw(nil)
|
|
30
|
+
end
|
|
31
|
+
assert_raise(Youtube::InvalidPathError) do
|
|
32
|
+
@extractor.get_raw("invalid/path")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_response_valid_mock_returned
|
|
37
|
+
res = @extractor.get_raw('/')
|
|
38
|
+
assert_instance_of(Mocks::MockResponse, res)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def test_not_raise_untouched_json_data
|
|
42
|
+
@extractor.video_json_untouched # Valid json hash returned.
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
|
|
5
|
+
require_relative '../lib/youtube-data'
|
|
6
|
+
require_relative 'mocks'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestThumbnail < Test::Unit::TestCase
|
|
10
|
+
def setup
|
|
11
|
+
@mock_session = Mocks::MockSession.new(nil)
|
|
12
|
+
# Copy valid video html from file into the mock sessions `get` mock response which acts the same as normal session.
|
|
13
|
+
@mock_session.set_res_body(File.read('./data/raw_video.html'))
|
|
14
|
+
# Specify mock session to use and replace actual session with `opts[:mock_session]`.
|
|
15
|
+
@extractor = Youtube::DataExtractor.new('FtutLA63Cp8', {:mock_session => @mock_session})
|
|
16
|
+
@thumbnail = Youtube::Thumbnail.new(@extractor, {:mock_session => @mock_session})
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_response_invalid_path
|
|
20
|
+
assert_raise(Youtube::InvalidPathError) do
|
|
21
|
+
@thumbnail.get_raw(nil)
|
|
22
|
+
end
|
|
23
|
+
assert_raise(Youtube::InvalidPathError) do
|
|
24
|
+
@thumbnail.get_raw("invalid/path")
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_response_valid_mock_returned
|
|
29
|
+
res = @thumbnail.get_raw('/')
|
|
30
|
+
assert_instance_of(Mocks::MockResponse, res)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def test_thumbnails_json
|
|
34
|
+
assert_instance_of(Array, @thumbnail.thumbnails_json)
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def test_default_json
|
|
38
|
+
assert_instance_of(Hash, @thumbnail.default_json)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
data/tests/test_video.rb
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test/unit'
|
|
4
|
+
|
|
5
|
+
require_relative '../lib/youtube-data'
|
|
6
|
+
require_relative 'mocks'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
class TestVideo < Test::Unit::TestCase
|
|
10
|
+
def setup
|
|
11
|
+
@mock_session = Mocks::MockSession.new(nil)
|
|
12
|
+
# Copy valid video html from file into the mock sessions `get` mock response which acts the same as normal session.
|
|
13
|
+
@mock_session.set_res_body(File.read('./data/raw_video.html'))
|
|
14
|
+
# Specify mock session to use and replace actual session with `opts[:mock_session]`.
|
|
15
|
+
@extractor = Youtube::DataExtractor.new('FtutLA63Cp8', {:mock_session => @mock_session})
|
|
16
|
+
@video = Youtube::Video.new(@extractor)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def test_json
|
|
20
|
+
assert_instance_of(Hash, @video.json)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def test_thumbnails_array
|
|
24
|
+
assert_instance_of(Array, @video.thumbnails)
|
|
25
|
+
assert_instance_of(Hash, @video.thumbnails[0])
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def test_channel_id
|
|
29
|
+
assert_equal(@video.channel_id, 'UCEJJbhF5Hod0zsupy-26n_g')
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def test_id
|
|
33
|
+
assert_equal(@video.id, 'FtutLA63Cp8')
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def test_title
|
|
37
|
+
assert_equal(@video.title, '【東方】Bad Apple!! PV【影絵】')
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def test_description
|
|
41
|
+
assert_equal(@video.description, 'sm8628149')
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def test_views
|
|
45
|
+
assert_instance_of(Integer, @video.views)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def test_author
|
|
49
|
+
assert_equal(@video.author, 'kasidid2')
|
|
50
|
+
assert_equal(@video.uploader, 'kasidid2')
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def test_keywords
|
|
54
|
+
assert_instance_of(Array, @video.keywords)
|
|
55
|
+
assert_equal(@video.keywords.length, 6)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def test_length_in_seconds
|
|
59
|
+
assert_equal(@video.length_in_seconds, 219)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def test_is_crawlable
|
|
63
|
+
assert_true(@video.is_crawlable?)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def test_allow_ratings
|
|
67
|
+
assert_true(@video.allow_ratings?)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def test_is_live
|
|
71
|
+
assert_false(@video.is_live?)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
end
|