YPBT 0.2.9 → 0.2.10

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.
@@ -1,37 +1,38 @@
1
- # frozen_string_literal: true
2
-
3
- module YoutubeVideo
4
- # Executable code for file(s) in bin/ folder
5
- class Runner
6
- def self.run!(args)
7
- video_id = args[0] || ENV['YT_VIDEO_ID']
8
- unless video_id
9
- puts 'USAGE: YPBT [video_id]'
10
- exit(1)
11
- end
12
-
13
- video = YoutubeVideo::Video.find(video_id: video_id)
14
-
15
- output_info(video)
16
- end
17
-
18
- def self.output_info(video)
19
- title = video.title
20
- separator = Array.new(video.title.length) { '-' }.join
21
- video_info =
22
- video.comments.map.with_index do |comment, index|
23
- comment_info(comment, index)
24
- end.join
25
-
26
- [title, separator, video_info].join("\n")
27
- end
28
-
29
- def self.comment_info(comment, index)
30
- "#{index + 1}:\n"\
31
- " Author: #{comment.author.author_name}\n"\
32
- " Comment: #{comment.text_display}\n"\
33
- " LIKE: #{comment.author.like_count}\n"\
34
- " AuthorChannelUrl: #{comment.author.author_channel_url}\n"
35
- end
36
- end
37
- end
1
+ # frozen_string_literal: true
2
+
3
+ module YoutubeVideo
4
+ # Executable code for file(s) in bin/ folder
5
+ class Runner
6
+ def self.run!(args)
7
+ video_id = args[0] || ENV['YT_VIDEO_ID']
8
+ unless video_id
9
+ puts 'USAGE: YPBT [video_id]'
10
+ exit(1)
11
+ end
12
+
13
+ video = YoutubeVideo::Video.find(video_id: video_id)
14
+
15
+ output_info(video)
16
+ end
17
+
18
+ def self.output_info(video)
19
+ return 'Nothing found. (Invalid video id or api-key)' if video.nil?
20
+ title = video.title
21
+ separator = Array.new(video.title.length) { '-' }.join
22
+ video_info =
23
+ video.comments.map.with_index do |comment, index|
24
+ comment_info(comment, index)
25
+ end.join
26
+
27
+ [title, separator, video_info].join("\n")
28
+ end
29
+
30
+ def self.comment_info(comment, index)
31
+ "#{index + 1}:\n"\
32
+ " Author: #{comment.author.author_name}\n"\
33
+ " Comment: #{comment.text_display}\n"\
34
+ " LIKE: #{comment.author.like_count}\n"\
35
+ " AuthorChannelUrl: #{comment.author.author_channel_url}\n"
36
+ end
37
+ end
38
+ end
@@ -1,55 +1,55 @@
1
- require 'ruby-duration'
2
-
3
- module YoutubeVideo
4
- TAG_TYPES = { MUSIC: 'music', VIDEO: 'video' }.freeze
5
- # comment's time tag infomation
6
- class Timetag
7
- attr_reader :start_time, :end_time, :tag_type, :duration, :comment,
8
- :like_count
9
- def initialize(start_time:, comment:, end_time: nil, like_count: nil,
10
- tag_type: nil)
11
- @start_time = string_to_time start_time
12
- @end_time = end_time
13
- @like_count = like_count ? like_count : comment.like_count
14
- @tag_type = tag_type
15
- end
16
-
17
- def start_time
18
- @start_time&.iso8601
19
- end
20
-
21
- def end_time=(end_time)
22
- @end_time = string_to_time end_time if end_time
23
- end
24
-
25
- def end_time
26
- @end_time&.iso8601 if @end_time
27
- end
28
-
29
- def duration
30
- @duration = @end_time - @start_time if @end_time && @start_time
31
- @duration&.iso8601 if @duration
32
- end
33
-
34
- def tag_type=(tag_type)
35
- @tag_type = TAG_TYPES[tag_type.to_sym] if tag_type
36
- end
37
-
38
- def self.find(comment:)
39
- time_tag_pattern = /http.+?youtube.+?\?.+?t=.+?\>([0-9:]+)<\/a>/
40
- start_times_string = comment.text_display.scan time_tag_pattern
41
- tags = start_times_string.map do |match_parts|
42
- Timetag.new(start_time: match_parts[0], comment: comment)
43
- end
44
- tags
45
- end
46
-
47
- private
48
-
49
- def string_to_time(time_string)
50
- time_unit = [:seconds, :minutes, :hours, :day, :weeks]
51
- time_array = time_string.scan(/[0-9]+/).map(&:to_i).reverse
52
- Duration.new(Hash[time_unit.zip(time_array)])
53
- end
54
- end
55
- end
1
+ require 'ruby-duration'
2
+
3
+ module YoutubeVideo
4
+ TAG_TYPES = { MUSIC: 'music', VIDEO: 'video' }.freeze
5
+ # comment's time tag infomation
6
+ class Timetag
7
+ attr_reader :start_time, :end_time, :tag_type, :duration, :comment,
8
+ :like_count
9
+ def initialize(start_time:, comment:, end_time: nil, like_count: nil,
10
+ tag_type: nil)
11
+ @start_time = string_to_time start_time
12
+ @end_time = end_time
13
+ @like_count = like_count ? like_count : comment.like_count
14
+ @tag_type = tag_type
15
+ end
16
+
17
+ def start_time
18
+ @start_time&.iso8601
19
+ end
20
+
21
+ def end_time=(end_time)
22
+ @end_time = string_to_time end_time if end_time
23
+ end
24
+
25
+ def end_time
26
+ @end_time&.iso8601 if @end_time
27
+ end
28
+
29
+ def duration
30
+ @duration = @end_time - @start_time if @end_time && @start_time
31
+ @duration&.iso8601 if @duration
32
+ end
33
+
34
+ def tag_type=(tag_type)
35
+ @tag_type = TAG_TYPES[tag_type.to_sym] if tag_type
36
+ end
37
+
38
+ def self.find(comment:)
39
+ time_tag_pattern = /http.+?youtube.+?\?.+?t=.+?\>([0-9:]+)<\/a>/
40
+ start_times_string = comment.text_display.scan time_tag_pattern
41
+ tags = start_times_string.map do |match_parts|
42
+ Timetag.new(start_time: match_parts[0], comment: comment)
43
+ end
44
+ tags
45
+ end
46
+
47
+ private
48
+
49
+ def string_to_time(time_string)
50
+ time_unit = [:seconds, :minutes, :hours, :day, :weeks]
51
+ time_array = time_string.scan(/[0-9]+/).map(&:to_i).reverse
52
+ Duration.new(Hash[time_unit.zip(time_array)])
53
+ end
54
+ end
55
+ end
@@ -1,5 +1,5 @@
1
- # frozen_string_literal: true
2
-
3
- module YoutubeVideo
4
- VERSION = '0.2.9'
5
- end
1
+ # frozen_string_literal: true
2
+
3
+ module YoutubeVideo
4
+ VERSION = '0.2.10'
5
+ end
@@ -1,73 +1,73 @@
1
- # frozen_string_literal: true
2
- require_relative 'comment'
3
- require_relative 'youtube_api'
4
-
5
- module YoutubeVideo
6
- # Main class to setup a Video
7
- class Video
8
- attr_reader :title, :description, :dislike_count, :like_count,
9
- :comment_count, :view_count, :duration, :id, :channel_id,
10
- :thumbnail_url, :category_id
11
-
12
- def initialize(data:)
13
- @id = data['id']
14
- @title = data['snippet']['title']
15
- @channel_id = data['snippet']['channelId']
16
- @description = data['snippet']['description']
17
- @category_id = data['snippet']['categoryId']
18
- @thumbnail_url = data['snippet']['thumbnails']['medium']['url']
19
- @dislike_count = data['statistics']['dislikeCount'].to_i
20
- @like_count = data['statistics']['likeCount'].to_i
21
- @view_count = data['statistics']['viewCount'].to_i
22
- @duration = data['contentDetails']['duration']
23
- @is_channel = false
24
- end
25
-
26
- def comments
27
- # contain only the comments which have time tag.
28
- return @comments if @comments
29
- raw_comments = YtApi.time_tags_info(@id)
30
- @comments = raw_comments.map { |comment| Comment.new(data: comment) }
31
- end
32
-
33
- def channel_title
34
- load_channel_info unless @is_channel
35
- @channel_title
36
- end
37
-
38
- def channel_image_url
39
- load_channel_info unless @is_channel
40
- @channel_image_url
41
- end
42
-
43
- def channel_description
44
- load_channel_info unless @is_channel
45
- @channel_description
46
- end
47
-
48
- def embed_url
49
- return @embed_url if @embed_url
50
- @embed_url = "https://www.youtube.com/embed/#{@id}"
51
- end
52
-
53
- def self.find(video_id:)
54
- video_data = YtApi.video_info(video_id)
55
- new(data: video_data) if video_data
56
- end
57
-
58
- def self.find_popular(max_results: 25)
59
- videos_data = YtApi.popular_videos_info(max_results)
60
- videos_data.map { |data| new(data: data) } unless videos_data.empty?
61
- end
62
-
63
- private
64
-
65
- def load_channel_info
66
- channel_data = YtApi.channel_info @channel_id
67
- @channel_title = channel_data['title'] if channel_data
68
- @channel_image_url = channel_data['image_url'] if channel_data
69
- @channel_description = channel_data['description'] if channel_data
70
- @is_channel = true
71
- end
72
- end
73
- end
1
+ # frozen_string_literal: true
2
+ require_relative 'comment'
3
+ require_relative 'youtube_api'
4
+
5
+ module YoutubeVideo
6
+ # Main class to setup a Video
7
+ class Video
8
+ attr_reader :title, :description, :dislike_count, :like_count,
9
+ :comment_count, :view_count, :duration, :id, :channel_id,
10
+ :thumbnail_url, :category_id
11
+
12
+ def initialize(data:)
13
+ @id = data['id']
14
+ @title = data['snippet']['title']
15
+ @channel_id = data['snippet']['channelId']
16
+ @description = data['snippet']['description']
17
+ @category_id = data['snippet']['categoryId']
18
+ @thumbnail_url = data['snippet']['thumbnails']['medium']['url']
19
+ @dislike_count = data['statistics']['dislikeCount'].to_i
20
+ @like_count = data['statistics']['likeCount'].to_i
21
+ @view_count = data['statistics']['viewCount'].to_i
22
+ @duration = data['contentDetails']['duration']
23
+ @is_channel = false
24
+ end
25
+
26
+ def comments
27
+ # contain only the comments which have time tag.
28
+ return @comments if @comments
29
+ raw_comments = YtApi.time_tags_info(@id)
30
+ @comments = raw_comments.map { |comment| Comment.new(data: comment) }
31
+ end
32
+
33
+ def channel_title
34
+ load_channel_info unless @is_channel
35
+ @channel_title
36
+ end
37
+
38
+ def channel_image_url
39
+ load_channel_info unless @is_channel
40
+ @channel_image_url
41
+ end
42
+
43
+ def channel_description
44
+ load_channel_info unless @is_channel
45
+ @channel_description
46
+ end
47
+
48
+ def embed_url
49
+ return @embed_url if @embed_url
50
+ @embed_url = "https://www.youtube.com/embed/#{@id}"
51
+ end
52
+
53
+ def self.find(video_id:)
54
+ video_data = YtApi.video_info(video_id)
55
+ new(data: video_data) if video_data
56
+ end
57
+
58
+ def self.find_popular(max_results: 25)
59
+ videos_data = YtApi.popular_videos_info(max_results)
60
+ videos_data.map { |data| new(data: data) } unless videos_data.empty?
61
+ end
62
+
63
+ private
64
+
65
+ def load_channel_info
66
+ channel_data = YtApi.channel_info @channel_id
67
+ @channel_title = channel_data['title'] if channel_data
68
+ @channel_image_url = channel_data['image_url'] if channel_data
69
+ @channel_description = channel_data['description'] if channel_data
70
+ @is_channel = true
71
+ end
72
+ end
73
+ end
@@ -1,127 +1,127 @@
1
- # frozen_string_literal: true
2
- require 'http'
3
- require 'json'
4
-
5
- module YoutubeVideo
6
- # Service for all Youtube API calls
7
- class YtApi
8
- YT_URL = 'https://www.googleapis.com'
9
- YT_COMPANY = 'youtube'
10
- YT_COMPANY_URL = URI.join(YT_URL, "#{YT_COMPANY}/")
11
- API_VER = 'v3'
12
- YT_API_URL = URI.join(YT_COMPANY_URL, "#{API_VER}/")
13
- TIME_TAG_PATTERN = /http.+?youtube.+?\?.+?t=.+?\>([0-9:]+)<\/a>/
14
- def self.api_key
15
- return @api_key if @api_key
16
- @api_key = ENV['YOUTUBE_API_KEY']
17
- end
18
-
19
- def self.config=(credentials)
20
- @config ? @config.update(credentials) : @config = credentials
21
- end
22
-
23
- def self.video_info(video_id)
24
- field = 'items(id,'\
25
- 'snippet(thumbnails(medium),channelId,description,'\
26
- 'publishedAt,title,categoryId),'\
27
- 'statistics(likeCount,dislikeCount,viewCount),'\
28
- 'contentDetails(duration))'
29
- video_response = HTTP.get(yt_resource_url('videos'),
30
- params: { id: video_id,
31
- key: api_key,
32
- part: 'snippet,statistics,
33
- contentDetails',
34
- fields: field })
35
- JSON.parse(video_response.to_s)['items']&.first
36
- end
37
-
38
- def self.popular_videos_info(max_results = 25)
39
- field = 'items(id,'\
40
- 'snippet(thumbnails(medium),channelId,description,'\
41
- 'publishedAt,title,categoryId),'\
42
- 'statistics(likeCount,dislikeCount,viewCount),'\
43
- 'contentDetails(duration))'
44
- video_response = HTTP.get(yt_resource_url('videos'),
45
- params: { chart: 'mostpopular',
46
- key: api_key,
47
- maxResults: max_results,
48
- part: 'snippet,statistics,
49
- contentDetails',
50
- fields: field })
51
- JSON.parse(video_response.to_s)['items']
52
- end
53
-
54
- def self.comment_info(comment_id)
55
- comment_response = HTTP.get(yt_resource_url('comments'),
56
- params: { id: comment_id,
57
- key: api_key,
58
- part: 'snippet' })
59
- item = JSON.parse(comment_response.to_s)['items'].first
60
- comment = item['snippet']
61
- comment['id'] = comment_id
62
- comment
63
- end
64
-
65
- def self.video_comments_info(video_id, page_token = '', max_results = 100)
66
- comment_threads_response = HTTP.get(yt_resource_url('commentThreads'),
67
- params: { videoId: video_id,
68
- key: api_key,
69
- order: 'relevance',
70
- part: 'snippet',
71
- maxResults: max_results,
72
- pageToken: page_token })
73
- comment_threads = JSON.parse(comment_threads_response.to_s)
74
- comments = extract_comment(comment_threads)
75
- next_page_token = comment_threads['nextPageToken']
76
- [next_page_token, comments]
77
- end
78
-
79
- def self.channel_info(channel_id)
80
- fields = 'items(id,snippet(title,description,thumbnails(default(url))))'
81
- channel_response = HTTP.get(yt_resource_url('channels'),
82
- params: { id: channel_id,
83
- key: api_key,
84
- part: 'snippet',
85
- fields: fields })
86
- channel_data = JSON.parse(channel_response.to_s)['items'].first
87
- if channel_data
88
- {
89
- 'title' => channel_data['snippet']['title'],
90
- 'description' => channel_data['snippet']['description'],
91
- 'image_url' => channel_data['snippet']['thumbnails']['default']['url']
92
- }
93
- end
94
- end
95
-
96
- def self.extract_comment(comment_threads)
97
- comments = comment_threads['items'].map do |item|
98
- comment = item['snippet']['topLevelComment']['snippet']
99
- comment['id'] = item['id']
100
- comment
101
- end
102
- comments
103
- end
104
-
105
- def self.time_tags_info(video_id, max_search_time = 5)
106
- next_page = ''
107
- comments_with_tags = []
108
- max_search_time.times do
109
- next_page, tmp_comments = video_comments_info(video_id, next_page)
110
- tmp_comments.each do |comment|
111
- comments_with_tags.push(comment) if time_tag? comment
112
- end
113
- break unless next_page
114
- end
115
- comments_with_tags
116
- end
117
-
118
- def self.time_tag?(comment)
119
- !(comment['textDisplay'] =~ TIME_TAG_PATTERN).nil?
120
- end
121
-
122
- private_class_method
123
- def self.yt_resource_url(resouce_name)
124
- URI.join(YT_API_URL, resouce_name.to_s)
125
- end
126
- end
127
- end
1
+ # frozen_string_literal: true
2
+ require 'http'
3
+ require 'json'
4
+
5
+ module YoutubeVideo
6
+ # Service for all Youtube API calls
7
+ class YtApi
8
+ YT_URL = 'https://youtube.googleapis.com'
9
+ YT_COMPANY = 'youtube'
10
+ YT_COMPANY_URL = URI.join(YT_URL, "#{YT_COMPANY}/")
11
+ API_VER = 'v3'
12
+ YT_API_URL = URI.join(YT_COMPANY_URL, "#{API_VER}/")
13
+ TIME_TAG_PATTERN = /http.+?youtube.+?\?.+?t=.+?\>([0-9:]+)<\/a>/
14
+ def self.api_key
15
+ return @api_key if @api_key
16
+ @api_key = ENV['YOUTUBE_API_KEY']
17
+ end
18
+
19
+ def self.config=(credentials)
20
+ @config ? @config.update(credentials) : @config = credentials
21
+ end
22
+
23
+ def self.video_info(video_id)
24
+ field = 'items(id,'\
25
+ 'snippet(thumbnails(medium),channelId,description,'\
26
+ 'publishedAt,title,categoryId),'\
27
+ 'statistics(likeCount,dislikeCount,viewCount),'\
28
+ 'contentDetails(duration))'
29
+ video_response = HTTP.get(yt_resource_url('videos'),
30
+ params: { id: video_id,
31
+ key: api_key,
32
+ part: 'snippet,statistics,'\
33
+ 'contentDetails',
34
+ fields: field })
35
+ JSON.parse(video_response.to_s)['items']&.first
36
+ end
37
+
38
+ def self.popular_videos_info(max_results = 25)
39
+ field = 'items(id,'\
40
+ 'snippet(thumbnails(medium),channelId,description,'\
41
+ 'publishedAt,title,categoryId),'\
42
+ 'statistics(likeCount,dislikeCount,viewCount),'\
43
+ 'contentDetails(duration))'
44
+ video_response = HTTP.get(yt_resource_url('videos'),
45
+ params: { chart: 'mostpopular',
46
+ key: api_key,
47
+ maxResults: max_results,
48
+ part: 'snippet,statistics,'\
49
+ 'contentDetails',
50
+ fields: field })
51
+ JSON.parse(video_response.to_s)['items']
52
+ end
53
+
54
+ def self.comment_info(comment_id)
55
+ comment_response = HTTP.get(yt_resource_url('comments'),
56
+ params: { id: comment_id,
57
+ key: api_key,
58
+ part: 'snippet' })
59
+ item = JSON.parse(comment_response.to_s)['items'].first
60
+ comment = item['snippet']
61
+ comment['id'] = comment_id
62
+ comment
63
+ end
64
+
65
+ def self.video_comments_info(video_id, page_token = '', max_results = 100)
66
+ comment_threads_response = HTTP.get(yt_resource_url('commentThreads'),
67
+ params: { videoId: video_id,
68
+ key: api_key,
69
+ order: 'relevance',
70
+ part: 'snippet',
71
+ maxResults: max_results,
72
+ pageToken: page_token })
73
+ comment_threads = JSON.parse(comment_threads_response.to_s)
74
+ comments = extract_comment(comment_threads)
75
+ next_page_token = comment_threads['nextPageToken']
76
+ [next_page_token, comments]
77
+ end
78
+
79
+ def self.channel_info(channel_id)
80
+ fields = 'items(id,snippet(title,description,thumbnails(default(url))))'
81
+ channel_response = HTTP.get(yt_resource_url('channels'),
82
+ params: { id: channel_id,
83
+ key: api_key,
84
+ part: 'snippet',
85
+ fields: fields })
86
+ channel_data = JSON.parse(channel_response.to_s)['items'].first
87
+ if channel_data
88
+ {
89
+ 'title' => channel_data['snippet']['title'],
90
+ 'description' => channel_data['snippet']['description'],
91
+ 'image_url' => channel_data['snippet']['thumbnails']['default']['url']
92
+ }
93
+ end
94
+ end
95
+
96
+ def self.extract_comment(comment_threads)
97
+ comments = comment_threads['items'].map do |item|
98
+ comment = item['snippet']['topLevelComment']['snippet']
99
+ comment['id'] = item['id']
100
+ comment
101
+ end
102
+ comments
103
+ end
104
+
105
+ def self.time_tags_info(video_id, max_search_time = 5)
106
+ next_page = ''
107
+ comments_with_tags = []
108
+ max_search_time.times do
109
+ next_page, tmp_comments = video_comments_info(video_id, next_page)
110
+ tmp_comments.each do |comment|
111
+ comments_with_tags.push(comment) if time_tag? comment
112
+ end
113
+ break unless next_page
114
+ end
115
+ comments_with_tags
116
+ end
117
+
118
+ def self.time_tag?(comment)
119
+ !(comment['textDisplay'] =~ TIME_TAG_PATTERN).nil?
120
+ end
121
+
122
+ private_class_method
123
+ def self.yt_resource_url(resouce_name)
124
+ URI.join(YT_API_URL, resouce_name.to_s)
125
+ end
126
+ end
127
+ end