yt 0.25.13 → 0.32.2

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.
Files changed (96) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.md +305 -1
  3. data/MIT-LICENSE +1 -1
  4. data/README.md +86 -5
  5. data/YOUTUBE_IT.md +3 -3
  6. data/lib/yt.rb +5 -2
  7. data/lib/yt/actions/list.rb +3 -3
  8. data/lib/yt/associations/has_authentication.rb +33 -1
  9. data/lib/yt/associations/has_reports.rb +13 -18
  10. data/lib/yt/collections/assets.rb +2 -2
  11. data/lib/yt/collections/authentications.rb +9 -2
  12. data/lib/yt/collections/base.rb +3 -3
  13. data/lib/yt/collections/bulk_report_jobs.rb +28 -0
  14. data/lib/yt/collections/bulk_reports.rb +24 -0
  15. data/lib/yt/collections/claims.rb +22 -1
  16. data/lib/yt/collections/comment_threads.rb +41 -0
  17. data/lib/yt/collections/content_owners.rb +1 -1
  18. data/lib/yt/collections/group_infos.rb +27 -0
  19. data/lib/yt/collections/group_items.rb +45 -0
  20. data/lib/yt/collections/reports.rb +75 -13
  21. data/lib/yt/collections/revocations.rb +30 -0
  22. data/lib/yt/collections/video_groups.rb +29 -0
  23. data/lib/yt/collections/videos.rb +34 -9
  24. data/lib/yt/constants/geography.rb +326 -0
  25. data/lib/yt/errors/forbidden.rb +1 -3
  26. data/lib/yt/errors/no_items.rb +1 -3
  27. data/lib/yt/errors/request_error.rb +10 -7
  28. data/lib/yt/errors/server_error.rb +1 -3
  29. data/lib/yt/errors/unauthorized.rb +3 -3
  30. data/lib/yt/models/account.rb +12 -0
  31. data/lib/yt/models/advertising_options_set.rb +4 -4
  32. data/lib/yt/models/bulk_report.rb +23 -0
  33. data/lib/yt/models/bulk_report_job.rb +23 -0
  34. data/lib/yt/models/channel.rb +21 -12
  35. data/lib/yt/models/claim.rb +13 -2
  36. data/lib/yt/models/comment.rb +37 -0
  37. data/lib/yt/models/comment_thread.rb +50 -0
  38. data/lib/yt/models/content_detail.rb +6 -0
  39. data/lib/yt/models/content_owner.rb +31 -1
  40. data/lib/yt/models/group_info.rb +16 -0
  41. data/lib/yt/models/group_item.rb +15 -0
  42. data/lib/yt/models/resource.rb +3 -10
  43. data/lib/yt/models/revocation.rb +12 -0
  44. data/lib/yt/models/right_owner.rb +0 -2
  45. data/lib/yt/models/snippet.rb +24 -3
  46. data/lib/yt/models/video.rb +42 -11
  47. data/lib/yt/models/video_group.rb +186 -0
  48. data/lib/yt/request.rb +5 -3
  49. data/lib/yt/version.rb +2 -2
  50. data/spec/collections/comment_threads_spec.rb +46 -0
  51. data/spec/collections/playlist_items_spec.rb +1 -1
  52. data/spec/collections/reports_spec.rb +2 -2
  53. data/spec/constants/geography_spec.rb +16 -0
  54. data/spec/models/annotation_spec.rb +1 -1
  55. data/spec/models/claim_spec.rb +15 -3
  56. data/spec/models/comment_spec.rb +40 -0
  57. data/spec/models/comment_thread_spec.rb +93 -0
  58. data/spec/models/content_detail_spec.rb +7 -0
  59. data/spec/models/reference_spec.rb +2 -2
  60. data/spec/models/request_spec.rb +21 -0
  61. data/spec/models/resource_spec.rb +0 -15
  62. data/spec/models/video_spec.rb +1 -1
  63. data/spec/requests/as_account/account_spec.rb +16 -4
  64. data/spec/requests/as_account/authentications_spec.rb +1 -13
  65. data/spec/requests/as_account/channel_spec.rb +15 -45
  66. data/spec/requests/as_account/playlist_item_spec.rb +3 -3
  67. data/spec/requests/as_account/playlist_spec.rb +5 -32
  68. data/spec/requests/as_account/video_spec.rb +2022 -21
  69. data/spec/requests/as_content_owner/account_spec.rb +4 -0
  70. data/spec/requests/as_content_owner/bulk_report_job_spec.rb +19 -0
  71. data/spec/requests/as_content_owner/channel_spec.rb +59 -270
  72. data/spec/requests/as_content_owner/content_owner_spec.rb +89 -1
  73. data/spec/requests/as_content_owner/playlist_spec.rb +0 -15
  74. data/spec/requests/as_content_owner/video_group_spec.rb +112 -0
  75. data/spec/requests/as_content_owner/video_spec.rb +72 -146
  76. data/spec/requests/as_server_app/channel_spec.rb +1 -21
  77. data/spec/requests/as_server_app/comment_spec.rb +22 -0
  78. data/spec/requests/as_server_app/comment_thread_spec.rb +27 -0
  79. data/spec/requests/as_server_app/comment_threads_spec.rb +41 -0
  80. data/spec/requests/as_server_app/playlist_item_spec.rb +2 -2
  81. data/spec/requests/as_server_app/playlist_spec.rb +1 -22
  82. data/spec/requests/as_server_app/video_spec.rb +21 -19
  83. data/spec/requests/as_server_app/videos_spec.rb +5 -5
  84. data/spec/requests/unauthenticated/video_spec.rb +1 -9
  85. data/spec/spec_helper.rb +1 -1
  86. data/yt.gemspec +2 -1
  87. metadata +51 -17
  88. data/lib/yt/collections/ids.rb +0 -27
  89. data/lib/yt/config.rb +0 -54
  90. data/lib/yt/models/configuration.rb +0 -70
  91. data/lib/yt/models/description.rb +0 -58
  92. data/lib/yt/models/url.rb +0 -91
  93. data/spec/models/configuration_spec.rb +0 -44
  94. data/spec/models/description_spec.rb +0 -94
  95. data/spec/models/url_spec.rb +0 -84
  96. data/spec/requests/as_account/resource_spec.rb +0 -18
@@ -0,0 +1,16 @@
1
+ module Yt
2
+ module Models
3
+ class GroupInfo < Base
4
+ attr_reader :data
5
+
6
+ def initialize(options = {})
7
+ @data = options[:data]['snippet'].merge options[:data]['contentDetails']
8
+ @auth = options[:auth]
9
+ end
10
+
11
+ has_attribute :title, default: ''
12
+ has_attribute :item_count, type: Integer
13
+ has_attribute :published_at, type: Time
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,15 @@
1
+ require 'yt/models/video'
2
+
3
+ module Yt
4
+ module Models
5
+ class GroupItem < Base
6
+ attr_reader :auth, :data, :video
7
+
8
+ def initialize(options = {})
9
+ @data = options[:data]
10
+ @auth = options[:auth]
11
+ @video = options[:video] if options[:video]
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,7 +1,6 @@
1
1
  # encoding: UTF-8
2
2
 
3
3
  require 'yt/models/base'
4
- require 'yt/models/url'
5
4
 
6
5
  module Yt
7
6
  module Models
@@ -13,7 +12,7 @@ module Yt
13
12
 
14
13
  # @!attribute [r] id
15
14
  # @return [String] the ID that YouTube uses to identify each resource.
16
- has_one :id
15
+ attr_reader :id
17
16
 
18
17
  ### STATUS ###
19
18
 
@@ -43,8 +42,7 @@ module Yt
43
42
 
44
43
  # @private
45
44
  def initialize(options = {})
46
- @url = URL.new(options[:url]) if options[:url]
47
- @id = options[:id] || (@url.id if @url)
45
+ @id = options[:id]
48
46
  @auth = options[:auth]
49
47
  @snippet = Snippet.new(data: options[:snippet]) if options[:snippet]
50
48
  @status = Status.new(data: options[:status]) if options[:status]
@@ -52,12 +50,7 @@ module Yt
52
50
 
53
51
  # @private
54
52
  def kind
55
- @url ? @url.kind.to_s : self.class.to_s.demodulize.underscore
56
- end
57
-
58
- # @private
59
- def username
60
- @url.username if @url
53
+ self.class.to_s.demodulize.underscore
61
54
  end
62
55
 
63
56
  # @private
@@ -0,0 +1,12 @@
1
+ require 'yt/models/base'
2
+
3
+ module Yt
4
+ module Models
5
+ # @private
6
+ class Revocation < Base
7
+ def initialize(options = {})
8
+ @data = options[:data]
9
+ end
10
+ end
11
+ end
12
+ end
@@ -1,5 +1,3 @@
1
- require 'yt/models/description'
2
-
3
1
  module Yt
4
2
  module Models
5
3
  # Encapsulates information about the various types of owners of an asset.
@@ -1,4 +1,4 @@
1
- require 'yt/models/description'
1
+ require 'yt/models/comment'
2
2
 
3
3
  module Yt
4
4
  module Models
@@ -8,6 +8,8 @@ module Yt
8
8
  # @see https://developers.google.com/youtube/v3/docs/videos#resource
9
9
  # @see https://developers.google.com/youtube/v3/docs/playlists#resource
10
10
  # @see https://developers.google.com/youtube/v3/docs/playlistItems#resource
11
+ # @see https://developers.google.com/youtube/v3/docs/commentThreads#resource
12
+ # @see https://developers.google.com/youtube/v3/docs/comments#resource
11
13
  class Snippet < Base
12
14
  attr_reader :data
13
15
 
@@ -17,7 +19,7 @@ module Yt
17
19
  end
18
20
 
19
21
  has_attribute :title, default: ''
20
- has_attribute(:description, default: '') {|text| Description.new text}
22
+ has_attribute :description, default: ''
21
23
  has_attribute :published_at, type: Time
22
24
  has_attribute :channel_id
23
25
  has_attribute :channel_title
@@ -28,11 +30,30 @@ module Yt
28
30
  has_attribute :position, type: Integer
29
31
  has_attribute :resource_id, default: {}
30
32
  has_attribute :thumbnails, default: {}
33
+ has_attribute :video_id
34
+ has_attribute :total_reply_count, type: Integer
35
+ has_attribute :author_display_name
36
+ has_attribute :text_display
37
+ has_attribute :parent_id
38
+ has_attribute :like_count, type: Integer
39
+ has_attribute :updated_at, type: Time
31
40
 
32
41
  def thumbnail_url(size = :default)
33
42
  thumbnails.fetch(size.to_s, {})['url']
34
43
  end
35
44
 
45
+ def public?
46
+ @public ||= data.fetch 'isPublic', false
47
+ end
48
+
49
+ def can_reply?
50
+ @can_reply ||= data.fetch 'canReply', false
51
+ end
52
+
53
+ def top_level_comment
54
+ @top_level_comment ||= Yt::Comment.new data['topLevelComment'].symbolize_keys
55
+ end
56
+
36
57
  # Returns whether YouTube API includes all attributes in this snippet.
37
58
  # For instance, YouTube API only returns tags and categoryId on
38
59
  # Videos#list, not on Videos#search. And returns position on
@@ -47,4 +68,4 @@ module Yt
47
68
  end
48
69
  end
49
70
  end
50
- end
71
+ end
@@ -37,6 +37,11 @@ module Yt
37
37
  # @return [String] the title of the channel that the video belongs to.
38
38
  delegate :channel_title, to: :snippet
39
39
 
40
+ # @return [<String>] the URL of the channel that the video belongs to.
41
+ def channel_url
42
+ "https://www.youtube.com/channel/#{channel_id}"
43
+ end
44
+
40
45
  # @!attribute [r] live_broadcast_content
41
46
  # @return [String] the type of live broadcast that the video contains.
42
47
  # Possible values are: +'live'+, +'none'+, +'upcoming'+.
@@ -226,6 +231,10 @@ module Yt
226
231
  # @return [Integer] the duration of the video (in seconds).
227
232
  delegate :duration, to: :content_detail
228
233
 
234
+ # @!attribute [r] duration
235
+ # @return [String] the length of the video as an ISO 8601 time, HH:MM:SS.
236
+ delegate :length, to: :content_detail
237
+
229
238
  # @return [Boolean] whether the video is available in 3D.
230
239
  def stereoscopic?
231
240
  content_detail.dimension == '3d'
@@ -369,6 +378,9 @@ module Yt
369
378
  delegate :concurrent_viewers, to: :live_streaming_detail
370
379
 
371
380
  ### ASSOCIATIONS ###
381
+ # @!attribute [r] comments
382
+ # @return [Yt::Collections::Comments] the video’s comments.
383
+ has_many :comment_threads
372
384
 
373
385
  # @!attribute [r] annotations
374
386
  # @return [Yt::Collections::Annotations] the video’s annotations.
@@ -376,6 +388,13 @@ module Yt
376
388
 
377
389
  has_many :resumable_sessions
378
390
 
391
+ # @!attribute [r] channel
392
+ # @return [Yt::Models::Claim, nil] the first claim on the video by
393
+ # the content owner of the video, if eagerly loaded.
394
+ def claim
395
+ @claim
396
+ end
397
+
379
398
  ### ANALYTICS ###
380
399
 
381
400
  # @macro reports
@@ -383,9 +402,6 @@ module Yt
383
402
  # @macro report_by_video_dimensions
384
403
  has_report :views, Integer
385
404
 
386
- # @macro report_by_day
387
- has_report :uniques, Integer
388
-
389
405
  # @macro report_by_video_dimensions
390
406
  has_report :estimated_minutes_watched, Integer
391
407
 
@@ -414,12 +430,6 @@ module Yt
414
430
  # @macro report_by_day_and_country
415
431
  has_report :subscribers_lost, Integer
416
432
 
417
- # @macro report_by_day_and_country
418
- has_report :favorites_added, Integer
419
-
420
- # @macro report_by_day_and_country
421
- has_report :favorites_removed, Integer
422
-
423
433
  # @macro report_by_day_and_country
424
434
  has_report :videos_added_to_playlists, Integer
425
435
 
@@ -441,11 +451,29 @@ module Yt
441
451
  # @macro report_by_day_and_state
442
452
  has_report :annotation_close_rate, Float
443
453
 
454
+ # @macro report_by_day_and_state
455
+ has_report :card_impressions, Integer
456
+
457
+ # @macro report_by_day_and_state
458
+ has_report :card_clicks, Integer
459
+
460
+ # @macro report_by_day_and_state
461
+ has_report :card_click_rate, Float
462
+
463
+ # @macro report_by_day_and_state
464
+ has_report :card_teaser_impressions, Integer
465
+
466
+ # @macro report_by_day_and_state
467
+ has_report :card_teaser_clicks, Integer
468
+
469
+ # @macro report_by_day_and_state
470
+ has_report :card_teaser_click_rate, Float
471
+
444
472
  # @macro report_by_day_and_country
445
- has_report :earnings, Float
473
+ has_report :estimated_revenue, Float
446
474
 
447
475
  # @macro report_by_day_and_country
448
- has_report :impressions, Integer
476
+ has_report :ad_impressions, Integer
449
477
 
450
478
  # @macro report_by_day_and_country
451
479
  has_report :monetized_playbacks, Integer
@@ -576,6 +604,9 @@ module Yt
576
604
  if options[:player]
577
605
  @player = Player.new data: options[:player]
578
606
  end
607
+ if options[:claim]
608
+ @claim = options[:claim]
609
+ end
579
610
  end
580
611
 
581
612
  # @private
@@ -0,0 +1,186 @@
1
+ require 'yt/models/base'
2
+
3
+ module Yt
4
+ module Models
5
+ # Provides methods to interact with YouTube Analytics video-groups.
6
+ # @see https://developers.google.com/youtube/analytics/v1/reference/groups
7
+ class VideoGroup < Base
8
+ # @private
9
+ attr_reader :id, :auth
10
+
11
+ ### GROUP INFO ###
12
+
13
+ has_one :group_info
14
+
15
+ # @!attribute [r] title
16
+ # @return [String] the title of the group.
17
+ delegate :title, to: :group_info
18
+
19
+ # @!attribute [r] item_count
20
+ # @return [Integer] the number of resources in the group.
21
+ delegate :item_count, to: :group_info
22
+
23
+ # @!attribute [r] published_at
24
+ # @return [Time] the date and time when the group was created.
25
+ delegate :published_at, to: :group_info
26
+
27
+ ### ASSOCIATIONS ###
28
+
29
+ # @!attribute [r] group_items
30
+ # @return [Yt::Collections::GroupItems] the group’s items.
31
+ has_many :group_items
32
+
33
+ ### ANALYTICS ###
34
+
35
+ # @macro reports
36
+
37
+ # @macro report_by_video_dimensions
38
+ has_report :views, Integer
39
+
40
+ # @macro report_by_video_dimensions
41
+ has_report :estimated_minutes_watched, Integer
42
+
43
+ # @macro report_by_gender_and_age_group
44
+ has_report :viewer_percentage, Float
45
+
46
+ # @macro report_by_day_and_country
47
+ has_report :comments, Integer
48
+
49
+ # @macro report_by_day_and_country
50
+ has_report :likes, Integer
51
+
52
+ # @macro report_by_day_and_country
53
+ has_report :dislikes, Integer
54
+
55
+ # @macro report_by_day_and_country
56
+ has_report :shares, Integer
57
+
58
+ # @note This is not the total number of subscribers gained by the video’s
59
+ # channel, but the subscribers gained *from* the video’s page.
60
+ # @macro report_by_day_and_country
61
+ has_report :subscribers_gained, Integer
62
+
63
+ # @note This is not the total number of subscribers lost by the video’s
64
+ # channel, but the subscribers lost *from* the video’s page.
65
+ # @macro report_by_day_and_country
66
+ has_report :subscribers_lost, Integer
67
+
68
+ # @macro report_by_day_and_country
69
+ has_report :videos_added_to_playlists, Integer
70
+
71
+ # @macro report_by_day_and_country
72
+ has_report :videos_removed_from_playlists, Integer
73
+
74
+ # @macro report_by_day_and_state
75
+ has_report :average_view_duration, Integer
76
+
77
+ # @macro report_by_day_and_state
78
+ has_report :average_view_percentage, Float
79
+
80
+ # @macro report_by_day_and_state
81
+ has_report :annotation_clicks, Integer
82
+
83
+ # @macro report_by_day_and_state
84
+ has_report :annotation_click_through_rate, Float
85
+
86
+ # @macro report_by_day_and_state
87
+ has_report :annotation_close_rate, Float
88
+
89
+ # @macro report_by_day_and_state
90
+ has_report :card_impressions, Integer
91
+
92
+ # @macro report_by_day_and_state
93
+ has_report :card_clicks, Integer
94
+
95
+ # @macro report_by_day_and_state
96
+ has_report :card_click_rate, Float
97
+
98
+ # @macro report_by_day_and_state
99
+ has_report :card_teaser_impressions, Integer
100
+
101
+ # @macro report_by_day_and_state
102
+ has_report :card_teaser_clicks, Integer
103
+
104
+ # @macro report_by_day_and_state
105
+ has_report :card_teaser_click_rate, Float
106
+
107
+ # @macro report_by_day_and_country
108
+ has_report :estimated_revenue, Float
109
+
110
+ # @macro report_by_day_and_country
111
+ has_report :ad_impressions, Integer
112
+
113
+ # @macro report_by_day_and_country
114
+ has_report :monetized_playbacks, Integer
115
+
116
+ # @macro report_by_day_and_country
117
+ has_report :playback_based_cpm, Float
118
+
119
+ ### PRIVATE API ###
120
+
121
+ # @private
122
+ def initialize(options = {})
123
+ @id = options[:id]
124
+ @auth = options[:auth]
125
+ @group_info = options[:group_info] if options[:group_info]
126
+ end
127
+
128
+ # @private
129
+ # Tells `has_reports` to retrieve group reports from the Analytics API.
130
+ def reports_params
131
+ {}.tap do |params|
132
+ if auth.owner_name
133
+ params[:ids] = "contentOwner==#{auth.owner_name}"
134
+ else
135
+ params[:ids] = "channel==mine"
136
+ end
137
+ params[:filters] = "group==#{id}"
138
+ end
139
+ end
140
+
141
+ def all_video_ids
142
+ resource_ids = group_items.map{|item| item.data['resource']['id']}.uniq
143
+ case group_info.data["itemType"]
144
+ when "youtube#video"
145
+ resource_ids
146
+ when "youtube#channel"
147
+ resource_ids.flat_map do |channel_id|
148
+ Yt::Channel.new(id: channel_id, auth: @auth).videos.map(&:id)
149
+ end
150
+ else
151
+ []
152
+ end
153
+ end
154
+
155
+ def videos
156
+ all_video_ids.each_slice(50).flat_map do |video_ids|
157
+ conditions = {id: video_ids.join(',')}
158
+ conditions[:part] = 'snippet,status,statistics,contentDetails'
159
+ Collections::Videos.new(auth: @auth).where(conditions).map(&:itself)
160
+ end
161
+ end
162
+
163
+ def all_channel_ids
164
+ resource_ids = group_items.map {|item| item.data['resource']['id']}.uniq
165
+ case group_info.data['itemType']
166
+ when "youtube#video"
167
+ resource_ids.flat_map do |video_id|
168
+ Yt::Video.new(id: video_id, auth: @auth).channel_id
169
+ end.uniq
170
+ when "youtube#channel"
171
+ resource_ids
172
+ else
173
+ []
174
+ end
175
+ end
176
+
177
+ def channels
178
+ all_channel_ids.each_slice(50).flat_map do |channel_ids|
179
+ conditions = {id: channel_ids.join(',')}
180
+ conditions[:part] = 'snippet'
181
+ Collections::Channels.new(auth: @auth).where(conditions).map(&:itself)
182
+ end
183
+ end
184
+ end
185
+ end
186
+ end