yt 0.0.1 → 0.4.0

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 (106) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +24 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +10 -0
  5. data/.yardopts +1 -0
  6. data/Gemfile +9 -0
  7. data/Gemfile.lock +78 -0
  8. data/HISTORY.md +37 -0
  9. data/MIT-LICENSE +20 -0
  10. data/README.md +325 -0
  11. data/Rakefile +1 -0
  12. data/TODO.md +11 -0
  13. data/bin/yt +31 -0
  14. data/lib/yt.rb +2 -0
  15. data/lib/yt/actions/delete.rb +27 -0
  16. data/lib/yt/actions/delete_all.rb +28 -0
  17. data/lib/yt/actions/insert.rb +29 -0
  18. data/lib/yt/actions/list.rb +65 -0
  19. data/lib/yt/actions/update.rb +25 -0
  20. data/lib/yt/associations.rb +33 -0
  21. data/lib/yt/associations/annotations.rb +15 -0
  22. data/lib/yt/associations/channels.rb +20 -0
  23. data/lib/yt/associations/details_sets.rb +20 -0
  24. data/lib/yt/associations/playlist_items.rb +26 -0
  25. data/lib/yt/associations/playlists.rb +22 -0
  26. data/lib/yt/associations/ratings.rb +39 -0
  27. data/lib/yt/associations/snippets.rb +20 -0
  28. data/lib/yt/associations/statuses.rb +14 -0
  29. data/lib/yt/associations/subscriptions.rb +38 -0
  30. data/lib/yt/associations/user_infos.rb +21 -0
  31. data/lib/yt/associations/videos.rb +14 -0
  32. data/lib/yt/collections/annotations.rb +43 -0
  33. data/lib/yt/collections/base.rb +13 -0
  34. data/lib/yt/collections/channels.rb +32 -0
  35. data/lib/yt/collections/details_sets.rb +32 -0
  36. data/lib/yt/collections/playlist_items.rb +50 -0
  37. data/lib/yt/collections/playlists.rb +56 -0
  38. data/lib/yt/collections/ratings.rb +32 -0
  39. data/lib/yt/collections/snippets.rb +38 -0
  40. data/lib/yt/collections/subscriptions.rb +67 -0
  41. data/lib/yt/collections/user_infos.rb +41 -0
  42. data/lib/yt/collections/videos.rb +32 -0
  43. data/lib/yt/config.rb +55 -0
  44. data/lib/yt/models/account.rb +68 -0
  45. data/lib/yt/models/annotation.rb +137 -0
  46. data/lib/yt/models/base.rb +11 -0
  47. data/lib/yt/models/channel.rb +17 -0
  48. data/lib/yt/models/configuration.rb +29 -0
  49. data/lib/yt/models/description.rb +98 -0
  50. data/lib/yt/models/details_set.rb +31 -0
  51. data/lib/yt/models/playlist.rb +65 -0
  52. data/lib/yt/models/playlist_item.rb +42 -0
  53. data/lib/yt/models/rating.rb +28 -0
  54. data/lib/yt/models/snippet.rb +48 -0
  55. data/lib/yt/models/status.rb +26 -0
  56. data/lib/yt/models/subscription.rb +35 -0
  57. data/lib/yt/models/user_info.rb +66 -0
  58. data/lib/yt/models/video.rb +16 -0
  59. data/lib/yt/utils/request.rb +85 -0
  60. data/lib/yt/version.rb +3 -0
  61. data/spec/associations/device_auth/channels_spec.rb +10 -0
  62. data/spec/associations/device_auth/details_sets_spec.rb +19 -0
  63. data/spec/associations/device_auth/playlist_items_spec.rb +42 -0
  64. data/spec/associations/device_auth/playlists_spec.rb +42 -0
  65. data/spec/associations/device_auth/ratings_spec.rb +30 -0
  66. data/spec/associations/device_auth/snippets_spec.rb +30 -0
  67. data/spec/associations/device_auth/subscriptions_spec.rb +27 -0
  68. data/spec/associations/device_auth/user_infos_spec.rb +10 -0
  69. data/spec/associations/device_auth/videos_spec.rb +22 -0
  70. data/spec/associations/no_auth/annotations_spec.rb +15 -0
  71. data/spec/associations/server_auth/channels_spec.rb +2 -0
  72. data/spec/associations/server_auth/details_sets_spec.rb +18 -0
  73. data/spec/associations/server_auth/playlist_items_spec.rb +17 -0
  74. data/spec/associations/server_auth/playlists_spec.rb +17 -0
  75. data/spec/associations/server_auth/ratings_spec.rb +2 -0
  76. data/spec/associations/server_auth/snippets_spec.rb +28 -0
  77. data/spec/associations/server_auth/subscriptions_spec.rb +2 -0
  78. data/spec/associations/server_auth/user_infos_spec.rb +2 -0
  79. data/spec/associations/server_auth/videos_spec.rb +20 -0
  80. data/spec/collections/annotations_spec.rb +6 -0
  81. data/spec/collections/channels_spec.rb +6 -0
  82. data/spec/collections/details_sets_spec.rb +6 -0
  83. data/spec/collections/playlist_items_spec.rb +23 -0
  84. data/spec/collections/playlists_spec.rb +26 -0
  85. data/spec/collections/ratings_spec.rb +6 -0
  86. data/spec/collections/snippets_spec.rb +6 -0
  87. data/spec/collections/subscriptions_spec.rb +30 -0
  88. data/spec/collections/user_infos_spec.rb +6 -0
  89. data/spec/collections/videos_spec.rb +6 -0
  90. data/spec/models/annotation_spec.rb +131 -0
  91. data/spec/models/channel_spec.rb +13 -0
  92. data/spec/models/description_spec.rb +94 -0
  93. data/spec/models/details_set_spec.rb +23 -0
  94. data/spec/models/playlist_item_spec.rb +32 -0
  95. data/spec/models/playlist_spec.rb +52 -0
  96. data/spec/models/rating_spec.rb +13 -0
  97. data/spec/models/snippet_spec.rb +66 -0
  98. data/spec/models/status_spec.rb +42 -0
  99. data/spec/models/subscription_spec.rb +37 -0
  100. data/spec/models/user_info_spec.rb +69 -0
  101. data/spec/models/video_spec.rb +13 -0
  102. data/spec/spec_helper.rb +15 -0
  103. data/spec/support/device_app.rb +16 -0
  104. data/spec/support/server_app.rb +10 -0
  105. data/yt.gemspec +30 -0
  106. metadata +209 -17
@@ -0,0 +1,38 @@
1
+ require 'yt/collections/subscriptions'
2
+
3
+ module Yt
4
+ module Associations
5
+ # Provides the `has_one :subscription` method to YouTube resources, which
6
+ # allows to invoke subscription-related methods, such as .subscribe.
7
+ # YouTube resources with subscription are: channels.
8
+ module Subscriptions
9
+ def subscriptions
10
+ @subscriptions ||= Collections::Subscriptions.by_channel self
11
+ end
12
+
13
+ def subscribed?
14
+ subscriptions.any?{|s| s.exists?}
15
+ end
16
+
17
+ def subscribe
18
+ subscriptions.insert ignore_duplicates: true
19
+ subscribed?
20
+ end
21
+
22
+ def subscribe!
23
+ subscriptions.insert
24
+ subscribed?
25
+ end
26
+
27
+ def unsubscribe
28
+ subscriptions.delete_all({}, ignore_not_found: true)
29
+ !subscribed?
30
+ end
31
+
32
+ def unsubscribe!
33
+ subscriptions.delete_all
34
+ !subscribed?
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,21 @@
1
+ require 'yt/collections/user_infos'
2
+
3
+ module Yt
4
+ module Associations
5
+ # Provides the `has_one :user_info` method to YouTube resources, which
6
+ # allows to access to user_info-specific methods like `email`.
7
+ # YouTube resources with user infos are: accounts.
8
+ module UserInfos
9
+ def user_info
10
+ @user_info ||= user_infos.first
11
+ end
12
+
13
+ private
14
+
15
+ def user_infos
16
+ @user_infos ||= Collections::UserInfos.by_account self
17
+ end
18
+ end
19
+ end
20
+ end
21
+
@@ -0,0 +1,14 @@
1
+ require 'yt/collections/videos'
2
+
3
+ module Yt
4
+ module Associations
5
+ # Provides the `has_many :videos` method to YouTube resources, which
6
+ # allows to access only the videos that belong to a specific resource.
7
+ # YouTube resources with videos are: channels.
8
+ module Videos
9
+ def videos
10
+ @videos ||= Collections::Videos.by_channel self
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,43 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/annotation'
3
+
4
+ module Yt
5
+ module Collections
6
+ class Annotations < Base
7
+
8
+ def initialize(options = {})
9
+ @video = options[:video]
10
+ @auth = options[:auth]
11
+ end
12
+
13
+ def self.by_video(video)
14
+ new video: video, auth: video.auth
15
+ end
16
+
17
+ private
18
+
19
+ def new_item(data)
20
+ Yt::Annotation.new data: data
21
+ end
22
+
23
+ def list_params
24
+ super.tap do |params|
25
+ params[:format] = :xml
26
+ params[:host] = 'www.youtube.com'
27
+ params[:path] = '/annotations_invideo'
28
+ params[:params] = {video_id: @video.id}
29
+ end
30
+ end
31
+
32
+ def next_page
33
+ request = Request.new list_params
34
+ response = request.run
35
+ raise unless response.is_a? Net::HTTPOK
36
+ @page_token = nil
37
+
38
+ document = response.body.fetch('document', {})['annotations'] || {}
39
+ Array.wrap document.fetch 'annotation', []
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,13 @@
1
+ require 'yt/actions/delete_all'
2
+ require 'yt/actions/insert'
3
+ require 'yt/actions/list'
4
+
5
+ module Yt
6
+ module Collections
7
+ class Base
8
+ include Actions::DeleteAll
9
+ include Actions::Insert
10
+ include Actions::List
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,32 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/channel'
3
+
4
+ module Yt
5
+ module Collections
6
+ class Channels < Base
7
+
8
+ def initialize(options = {})
9
+ @account = options[:account]
10
+ @auth = options[:auth]
11
+ end
12
+
13
+ def self.by_account(account)
14
+ new account: account, auth: account.auth
15
+ end
16
+
17
+ private
18
+
19
+ def new_item(data)
20
+ Yt::Channel.new id: data['id'], snippet: data['snippet'], auth: @auth
21
+ end
22
+
23
+ def list_params
24
+ super.tap do |params|
25
+ params[:params] = {maxResults: 50, part: 'snippet', mine: true}
26
+ params[:scope] = 'https://www.googleapis.com/auth/youtube'
27
+ params[:path] = '/youtube/v3/channels'
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,32 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/details_set'
3
+
4
+ module Yt
5
+ module Collections
6
+ class DetailsSets < Base
7
+
8
+ def initialize(options = {})
9
+ @video = options[:video]
10
+ @auth = options[:auth]
11
+ end
12
+
13
+ def self.by_video(video)
14
+ new video: video, auth: video.auth
15
+ end
16
+
17
+ private
18
+
19
+ def new_item(data)
20
+ Yt::DetailsSet.new data: data['contentDetails']
21
+ end
22
+
23
+ def list_params
24
+ super.tap do |params|
25
+ params[:params] = {maxResults: 50, part: 'contentDetails', id: @video.id}
26
+ params[:scope] = 'https://www.googleapis.com/auth/youtube.readonly'
27
+ params[:path] = '/youtube/v3/videos'
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,50 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/playlist_item'
3
+
4
+ module Yt
5
+ module Collections
6
+ class PlaylistItems < Base
7
+
8
+ def initialize(options = {})
9
+ @playlist = options[:playlist]
10
+ @auth = options[:auth]
11
+ end
12
+
13
+ def self.by_playlist(playlist)
14
+ new playlist: playlist, auth: playlist.auth
15
+ end
16
+
17
+ # options are id and kind
18
+ def insert(options = {}) #
19
+ resource = {kind: "youtube##{options[:kind]}"}
20
+ resource["#{options[:kind]}Id"] = options[:id]
21
+ snippet = {playlistId: @playlist.id, resourceId: resource}
22
+ do_insert body: {snippet: snippet}, params: {part: 'snippet,status'}
23
+ end
24
+
25
+ def delete_all(params = {})
26
+ do_delete_all params
27
+ end
28
+
29
+ private
30
+
31
+ def new_item(data)
32
+ Yt::PlaylistItem.new id: data['id'], snippet: data['snippet'], status: data['status'], auth: @auth
33
+ end
34
+
35
+ def list_params
36
+ super.tap do |params|
37
+ params[:params] = {maxResults: 50, part: 'snippet,status', playlistId: @playlist.id}
38
+ params[:scope] = 'https://www.googleapis.com/auth/youtube.readonly'
39
+ params[:path] = '/youtube/v3/playlistItems'
40
+ end
41
+ end
42
+
43
+ def insert_params
44
+ super.tap do |params|
45
+ params[:path] = '/youtube/v3/playlistItems'
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,56 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/playlist'
3
+
4
+ module Yt
5
+ module Collections
6
+ class Playlists < Base
7
+
8
+ def initialize(options = {})
9
+ @channel = options[:channel]
10
+ @auth = options[:auth]
11
+ end
12
+
13
+ def self.by_channel(channel)
14
+ new channel: channel, auth: channel.auth
15
+ end
16
+
17
+ # Valid body (no defaults) are: title (string), description (string), privacy_status (string),
18
+ # tags (array of strings)
19
+ def insert(options = {})
20
+ body = {}
21
+
22
+ snippet = options.slice :title, :description, :tags
23
+ body[:snippet] = snippet if snippet.any?
24
+
25
+ status = options[:privacy_status]
26
+ body[:status] = {privacyStatus: status} if status
27
+
28
+ do_insert body: body, params: {part: 'snippet,status'}
29
+ end
30
+
31
+ def delete_all(params = {})
32
+ do_delete_all params
33
+ end
34
+
35
+ private
36
+
37
+ def new_item(data)
38
+ Yt::Playlist.new id: data['id'], snippet: data['snippet'], status: data['status'], auth: @auth
39
+ end
40
+
41
+ def list_params
42
+ super.tap do |params|
43
+ params[:params] = {maxResults: 50, part: 'snippet,status', channelId: @channel.id}
44
+ params[:scope] = 'https://www.googleapis.com/auth/youtube.readonly'
45
+ params[:path] = '/youtube/v3/playlists'
46
+ end
47
+ end
48
+
49
+ def insert_params
50
+ super.tap do |params|
51
+ params[:path] = '/youtube/v3/playlists'
52
+ end
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,32 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/rating'
3
+
4
+ module Yt
5
+ module Collections
6
+ class Ratings < Base
7
+
8
+ def initialize(options = {})
9
+ @video = options[:video]
10
+ @auth = options[:auth]
11
+ end
12
+
13
+ def self.by_video(video)
14
+ new video: video, auth: video.auth
15
+ end
16
+
17
+ private
18
+
19
+ def new_item(data)
20
+ Yt::Rating.new rating: data['rating'], video_id: @video.id, auth: @auth
21
+ end
22
+
23
+ def list_params
24
+ super.tap do |params|
25
+ params[:path] = '/youtube/v3/videos/getRating'
26
+ params[:params] = {id: @video.id}
27
+ params[:scope] = 'https://www.googleapis.com/auth/youtube'
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,38 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/snippet'
3
+
4
+ module Yt
5
+ module Collections
6
+ class Snippets < Base
7
+
8
+ def initialize(options = {})
9
+ @resource = options[:resource]
10
+ @auth = options[:auth]
11
+ end
12
+
13
+ # @note Google API must have some caching layer by which if we try to
14
+ # delete a snippet that we just created, we encounter an error.
15
+ # To overcome this, if we have just updated the snippet, we must
16
+ # wait some time before requesting it again.
17
+ #
18
+ def self.by_resource(resource)
19
+ new resource: resource, auth: resource.auth
20
+ end
21
+
22
+ private
23
+
24
+ def new_item(data)
25
+ Yt::Snippet.new data: data['snippet']
26
+ end
27
+
28
+ def list_params
29
+ resources_path = @resource.class.to_s.demodulize.underscore.pluralize
30
+ super.tap do |params|
31
+ params[:params] = {id: @resource.id, part: 'snippet'}
32
+ params[:scope] = 'https://www.googleapis.com/auth/youtube.readonly'
33
+ params[:path] = "/youtube/v3/#{resources_path}"
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,67 @@
1
+ require 'yt/collections/base'
2
+ require 'yt/models/subscription'
3
+
4
+ module Yt
5
+ module Collections
6
+ class Subscriptions < Base
7
+
8
+ def initialize(options = {})
9
+ @channel = options[:channel]
10
+ @auth = options[:auth]
11
+ end
12
+
13
+ def self.by_channel(channel)
14
+ new channel: channel, auth: channel.auth
15
+ end
16
+
17
+ def insert(options = {})
18
+ throttle
19
+ do_insert
20
+ rescue Yt::RequestError => error
21
+ raise error unless options[:ignore_duplicates] && error.reasons.include?('subscriptionDuplicate')
22
+ end
23
+
24
+ def delete_all(params = {}, options = {})
25
+ do_delete_all(params) {throttle}
26
+ end
27
+
28
+ private
29
+
30
+ def new_item(data)
31
+ Yt::Subscription.new id: data['id'], auth: @auth
32
+ end
33
+
34
+ # @note Google API must have some caching layer by which if we try to
35
+ # delete a subscription that we just created, we encounter an error.
36
+ # To overcome this, if we have just updated the subscription, we must
37
+ # wait some time before requesting it again.
38
+ #
39
+ def throttle(seconds = 8)
40
+ wait = [(@last_changed_at ||= Time.now) - Time.now + seconds, 0].max
41
+ sleep wait
42
+ @last_changed_at = Time.now
43
+ end
44
+
45
+ def fetch_page(params = {})
46
+ throttle
47
+ super params
48
+ end
49
+
50
+ def list_params
51
+ super.tap do |params|
52
+ params[:params] = {maxResults: 50, forChannelId: @channel.id, mine: true, part: 'snippet'}
53
+ params[:scope] = 'https://www.googleapis.com/auth/youtube'
54
+ params[:path] = '/youtube/v3/subscriptions'
55
+ end
56
+ end
57
+
58
+ def insert_params
59
+ super.tap do |params|
60
+ params[:path] = '/youtube/v3/subscriptions'
61
+ params[:params] = {part: 'snippet'}
62
+ params[:body] = {snippet: {resourceId: {channelId: @channel.id}}}
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end