shrine-storage-you_tube 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 294ce939d884122234328a9458cbaceda8c7c53d
4
+ data.tar.gz: 5688b2ca8885b1cc6075485bf7ab4d52b0564456
5
+ SHA512:
6
+ metadata.gz: 7892977f4c52e090bd56f015a6c3835006547923f2164df2648fecaced615c5499092577acdf3bf0cbbb73e33279286661ca5906c5c5160f8c8a020b99847a58
7
+ data.tar.gz: c3240f13832aabbb0662b19d83304c1112a807ff582022c745956de40cc4895e7e905ca4050466e3dc37965854c1d8b63a81f95c764f2664417c0dc4957b4b0c
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ .env
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.travis.yml ADDED
@@ -0,0 +1,4 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.2.3
4
+ before_install: gem install bundler -v 1.10.6
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in shrine-storage-you_tube.gemspec
4
+ gemspec
5
+
6
+ gem 'webmock', git: "https://github.com/bblimke/webmock.git"
7
+
8
+ gem 'pry'
data/README.md ADDED
@@ -0,0 +1,40 @@
1
+ # Shrine::YouTube
2
+
3
+ Provides [YouTube](https://youtube.com) storage for [Shrine](http://shrinerb.com).
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'shrine-storage-you_tube'
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ Since YouTube's API doesn't allow reading of uploaded data, the YouTube storage must be paired with another storage provider to hold the original videos. Videos are first uploaded to YouTube and then to the specified storage provider using the ID returned from the YouTube upload.
16
+
17
+ ```ruby
18
+ require "shrine/storage/you_tube"
19
+ require "shrine/storage/s3"
20
+
21
+ s3 = Shrine::Storage::S3.new(**s3_options)
22
+ youtube = Shrine::Storage::YouTube.new(
23
+ original_storage: s3,
24
+ client_id: "abc123", # Google OAuth2 Client ID
25
+ client_secret: "def456", # Google OAuth2 Client Secret
26
+ refresh_token: "ghi789", # Refresh token for YouTube user
27
+ # optional parameters
28
+ channel_id: nil, # Channel ID for uploads, defaults to the user's channel
29
+ default_privacy: :private, # one of :private, :public, or :unlisted
30
+ upload_options: {} # may be used to set YouTube's snippet
31
+ )
32
+
33
+ Shrine.storages[:store] = youtube
34
+ ```
35
+
36
+ ## Development
37
+
38
+ After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
39
+
40
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "shrine/storage/you_tube"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,7 @@
1
+ class Shrine
2
+ module Storage
3
+ class YouTube
4
+ VERSION = "0.2.0"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,153 @@
1
+ require 'shrine/storage/you_tube/version'
2
+ require 'google/apis/youtube_v3'
3
+
4
+ class Shrine
5
+ module Storage
6
+ class YouTube
7
+ class UserChannelNotFound < StandardError
8
+ def initialize(found_channel_count)
9
+ super("Could not determine the user's channel (found #{found_channel_count} channels). Create a channel at https://www.youtube.com/create_channel or set channel_id.")
10
+ end
11
+ end
12
+
13
+ class VideoNotFound < StandardError; end
14
+
15
+ attr_reader :original_storage, :youtube, :default_privacy, :upload_options
16
+
17
+ def initialize(
18
+ original_storage:,
19
+ client_id:,
20
+ client_secret:,
21
+ refresh_token:,
22
+ channel_id: nil,
23
+ default_privacy: :private,
24
+ upload_options: {}
25
+ )
26
+ @youtube = youtube_service(
27
+ client_id: client_id,
28
+ client_secret: client_secret,
29
+ refresh_token: refresh_token
30
+ )
31
+
32
+ @channel_id = channel_id
33
+ @default_privacy = default_privacy
34
+ @upload_options = upload_options
35
+ @original_storage = original_storage
36
+ end
37
+
38
+ extend Forwardable
39
+ delegate [:download, :open, :read, :stream] => :original_storage
40
+
41
+ def channel_id
42
+ @channel_id ||= find_user_channel
43
+ end
44
+
45
+ def upload(io, id, metadata = {}, **upload_options)
46
+ snippet = { title: metadata['filename'], channel_id: channel_id }
47
+ snippet.update(upload_options)
48
+ snippet.update(metadata.delete('youtube') || {})
49
+
50
+ video_data = Google::Apis::YoutubeV3::Video.new(snippet: snippet)
51
+ uploaded_video = upload_video(io, video_data)
52
+
53
+ id.replace(uploaded_video.id)
54
+ original_storage.upload(io, id, metadata)
55
+
56
+ uploaded_video.to_h
57
+ end
58
+
59
+ def exists?(id)
60
+ youtube.list_videos('id', id: id).page_info.total_results == 1
61
+ end
62
+
63
+ def delete(id)
64
+ return unless exists?(id)
65
+ youtube.delete_video(id)
66
+ original_storage.delete(id)
67
+ end
68
+
69
+ def url(id, type: nil, **options)
70
+ case type && type.to_sym
71
+ when :original
72
+ original_storage.url(id, **options)
73
+ when :embed
74
+ "https://youtube.com/embed/#{id}"
75
+ when :short
76
+ "https://youtu.be/#{id}"
77
+ else
78
+ "https://youtube.com/watch?v=#{id}"
79
+ end
80
+ end
81
+
82
+ def clear!
83
+ while uploads_playlist_count > 0
84
+ uploads_playlist_items.items.each do |item|
85
+ video_id = item.snippet.resource_id.video_id
86
+ youtube.delete_video(video_id)
87
+ end
88
+ end
89
+
90
+ original_storage.clear!
91
+ end
92
+
93
+ def update(id, metadata: {})
94
+ snippet = metadata.delete('youtube')
95
+ return unless snippet
96
+
97
+ video_query = youtube.list_videos('snippet', id: id)
98
+ raise VideoNotFound unless video_query.page_info.total_results == 1
99
+
100
+ existing_snippet = video_query.items.first.snippet.to_h
101
+
102
+ video_data = Google::Apis::YoutubeV3::Video.new(id: id, snippet: existing_snippet.merge(snippet))
103
+ updated_video = youtube.update_video('snippet', video_data)
104
+ updated_video.to_h
105
+ end
106
+
107
+ protected
108
+
109
+ def youtube_service(credentials)
110
+ service = Google::Apis::YoutubeV3::YouTubeService.new
111
+ service.authorization = Google::Auth::UserRefreshCredentials.new(credentials)
112
+ service
113
+ end
114
+
115
+ def find_user_channel
116
+ user_channels = youtube.list_channels('id', mine: true)
117
+ channel_count = user_channels.items.count
118
+
119
+ raise(UserChannelNotFound, channel_count) unless channel_count == 1
120
+ user_channels.items.first.id
121
+ end
122
+
123
+ def uploads_playlist_id
124
+ @uploads_playlist_id ||= begin
125
+ channel_response = youtube.list_channels('contentDetails', id: channel_id)
126
+ channel = channel_response.items.first
127
+ channel.content_details.related_playlists.uploads
128
+ end
129
+ end
130
+
131
+ def uploads_playlist_items
132
+ youtube.list_playlist_items('snippet', playlist_id: uploads_playlist_id)
133
+ end
134
+
135
+ def uploads_playlist_count
136
+ uploads_playlist_items.page_info.total_results
137
+ end
138
+
139
+ def upload_video(io, video_data)
140
+ youtube.insert_video('snippet', video_data, upload_source: io)
141
+ rescue Google::Apis::ClientError => e
142
+ raise e unless (
143
+ e.message.include?('mediaBodyRequired') ||
144
+ e.message.include?('invalidFilename')
145
+ ) && io.respond_to?(:download)
146
+
147
+ youtube.insert_video('snippet', video_data, upload_source: io.download)
148
+ ensure
149
+ io.rewind
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'shrine/storage/you_tube/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "shrine-storage-you_tube"
8
+ spec.version = Shrine::Storage::YouTube::VERSION
9
+ spec.authors = ["Reid Beels", "The Dyrt"]
10
+ spec.email = ["reid@thedyrt.com"]
11
+
12
+ spec.summary = %q{Provides YouTube storage for the Shrine file upload framework.}
13
+ spec.homepage = "https://github.com/thedyrt/shrine-storage-you_tube"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
16
+ spec.bindir = "exe"
17
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency 'shrine', '~> 2.0'
21
+ spec.add_dependency 'google-api-client', '~> 0.10.1'
22
+
23
+ spec.add_development_dependency "bundler", "~> 1.10"
24
+ spec.add_development_dependency "rake", "~> 10.0"
25
+ spec.add_development_dependency "rspec"
26
+ spec.add_development_dependency "vcr", "~> 3.0"
27
+ spec.add_development_dependency "webmock", "~> 2.3"
28
+ spec.add_development_dependency "dotenv", "~> 2.0"
29
+ end
metadata ADDED
@@ -0,0 +1,168 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: shrine-storage-you_tube
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Reid Beels
8
+ - The Dyrt
9
+ autorequire:
10
+ bindir: exe
11
+ cert_chain: []
12
+ date: 2017-03-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: shrine
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - "~>"
19
+ - !ruby/object:Gem::Version
20
+ version: '2.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - "~>"
26
+ - !ruby/object:Gem::Version
27
+ version: '2.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: google-api-client
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - "~>"
33
+ - !ruby/object:Gem::Version
34
+ version: 0.10.1
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - "~>"
40
+ - !ruby/object:Gem::Version
41
+ version: 0.10.1
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - "~>"
47
+ - !ruby/object:Gem::Version
48
+ version: '1.10'
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - "~>"
54
+ - !ruby/object:Gem::Version
55
+ version: '1.10'
56
+ - !ruby/object:Gem::Dependency
57
+ name: rake
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '10.0'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '10.0'
70
+ - !ruby/object:Gem::Dependency
71
+ name: rspec
72
+ requirement: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: '0'
84
+ - !ruby/object:Gem::Dependency
85
+ name: vcr
86
+ requirement: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - "~>"
89
+ - !ruby/object:Gem::Version
90
+ version: '3.0'
91
+ type: :development
92
+ prerelease: false
93
+ version_requirements: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - "~>"
96
+ - !ruby/object:Gem::Version
97
+ version: '3.0'
98
+ - !ruby/object:Gem::Dependency
99
+ name: webmock
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - "~>"
103
+ - !ruby/object:Gem::Version
104
+ version: '2.3'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - "~>"
110
+ - !ruby/object:Gem::Version
111
+ version: '2.3'
112
+ - !ruby/object:Gem::Dependency
113
+ name: dotenv
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - "~>"
117
+ - !ruby/object:Gem::Version
118
+ version: '2.0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - "~>"
124
+ - !ruby/object:Gem::Version
125
+ version: '2.0'
126
+ description:
127
+ email:
128
+ - reid@thedyrt.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - ".gitignore"
134
+ - ".rspec"
135
+ - ".travis.yml"
136
+ - Gemfile
137
+ - README.md
138
+ - Rakefile
139
+ - bin/console
140
+ - bin/setup
141
+ - lib/shrine/storage/you_tube.rb
142
+ - lib/shrine/storage/you_tube/version.rb
143
+ - shrine-storage-you_tube.gemspec
144
+ homepage: https://github.com/thedyrt/shrine-storage-you_tube
145
+ licenses: []
146
+ metadata: {}
147
+ post_install_message:
148
+ rdoc_options: []
149
+ require_paths:
150
+ - lib
151
+ required_ruby_version: !ruby/object:Gem::Requirement
152
+ requirements:
153
+ - - ">="
154
+ - !ruby/object:Gem::Version
155
+ version: '0'
156
+ required_rubygems_version: !ruby/object:Gem::Requirement
157
+ requirements:
158
+ - - ">="
159
+ - !ruby/object:Gem::Version
160
+ version: '0'
161
+ requirements: []
162
+ rubyforge_project:
163
+ rubygems_version: 2.5.2
164
+ signing_key:
165
+ specification_version: 4
166
+ summary: Provides YouTube storage for the Shrine file upload framework.
167
+ test_files: []
168
+ has_rdoc: