youtube-searcher 0.0.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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 8da9d76a91ca3d6ebc73b681e5909233ef2a8f49
4
+ data.tar.gz: a47c1ac70a86a62afc87f6475ce3255c9fd4d360
5
+ SHA512:
6
+ metadata.gz: 56f7e3f4500cee625864fe7cb1842596aa943ad09731dc95ebdc5c35e7beae341cff6084a127e0b0020cc491b259bd1701503a7cbc85554772283ac782a82201
7
+ data.tar.gz: 07eabde63daac26a804692eb63e9d128a9d060a4a6de6b70189763843618bebca0829b557c877fdb01b4c0679cc6d1f23fbebc5d29743507ba15e02c4a45db7e
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .gem/*
19
+ api_key
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in youtube-searcher.gemspec
4
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Aziz Light
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,23 @@
1
+ # Youtube::Searcher
2
+
3
+ Search Youtube from the command line and download videos using `youtube-dl`.
4
+
5
+ ## Installation
6
+
7
+ ```
8
+ > gem install youtube-searcher
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```
14
+ > yts --help
15
+ ```
16
+
17
+ ## Contributing
18
+
19
+ 1. Fork it ( http://github.com/<my-github-username>/youtube-searcher/fork )
20
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
21
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
22
+ 4. Push to the branch (`git push origin my-new-feature`)
23
+ 5. Create new Pull Request
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/bin/yts ADDED
@@ -0,0 +1,175 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "optparse"
4
+ require 'open3'
5
+ require "rainbow/ext/string"
6
+ require "ruby-progressbar"
7
+
8
+ require_relative "../lib/youtube-searcher"
9
+
10
+ config = YoutubeSearcher::Config.instance.config
11
+ audio_only = false
12
+
13
+ parser = OptionParser.new do |opts|
14
+ opts.banner = "Usage: yts [options] <query>"
15
+
16
+ opts.separator ""
17
+ opts.separator "Options:"
18
+
19
+ opts.on("-m", "--max-results=[NUM]", "Maximum number of search results") do |num|
20
+ config[:max_results] = num.to_i.to_s if num.to_i > 0
21
+ end
22
+
23
+ opts.on("-d", "--dir=[DIR]", "Download directory") do |dir|
24
+ config[:download_directory] = dir
25
+ end
26
+
27
+ opts.on("-a", "--audio-only", "Download the audio only") do
28
+ audio_only = true
29
+ end
30
+
31
+ opts.on("--audio-quality=[QUALITY]", "Audio quality (0-9, 0 being the best)") do |quality|
32
+ config[:audio_quality] = quality
33
+ end
34
+
35
+ opts.on("--audio-format=[FORMAT]", "Audio format (best/aac/vorbis/mp3/m4a/opus/wav)") do |format|
36
+ config[:audio_format] = format
37
+ end
38
+
39
+ opts.on("--no-download", "Just search") do
40
+ config[:always_download] = false
41
+ end
42
+
43
+ opts.on_tail("-h", "--help", "Show this help message") do
44
+ puts opts
45
+
46
+ puts
47
+ puts "Config file:"
48
+ puts "\tOptions can be made persistent using a config file (#{File.join(ENV["HOME"], ".ytsrc")})"
49
+ puts
50
+ puts "\tYou can generate a default config file by running:"
51
+ puts
52
+ puts "\t\t yts --config-gen"
53
+
54
+ exit
55
+ end
56
+
57
+ opts.on_tail("-v", "--version", "Show version") do
58
+ puts "yts #{YoutubeSearcher::VERSION}"
59
+ exit
60
+ end
61
+
62
+ opts.on_tail("--config-gen", "Generate a config file in the home directory") do
63
+ config_file = File.join(ENV["HOME"], ".ytsrc")
64
+ if File.file?(config_file)
65
+ puts "You already have a config file!".color(:red)
66
+ status_code = 1
67
+ else
68
+ YoutubeSearcher::Config.instance.generate
69
+ puts "Generated a config file: #{config_file}".color(:green)
70
+ status_code = 0
71
+ end
72
+
73
+ exit status_code
74
+ end
75
+ end
76
+
77
+ parser.parse! ARGV
78
+
79
+ client = YoutubeSearcher::Client.instance
80
+ videos = client.search(ARGV.join(" "), max_results: config[:max_results])
81
+
82
+ videos.each_with_index do |video, index|
83
+ index = config[:always_download] ? "#{(index + 1).to_s.background(:yellow).foreground(:black)} " : ""
84
+ puts index + video[:title]
85
+ puts "\t#{video[:url]}"
86
+ end
87
+
88
+ if config[:always_download]
89
+ def which(cmd)
90
+ exts = ENV['PATHEXT'] ? ENV['PATHEXT'].split(';') : ['']
91
+
92
+ ENV['PATH'].split(File::PATH_SEPARATOR).each do |path|
93
+ exts.each { |ext|
94
+ exe = File.join(path, "#{cmd}#{ext}")
95
+ return exe if File.executable? exe
96
+ }
97
+ end
98
+
99
+ return nil
100
+ end
101
+
102
+ unless which("youtube-dl")
103
+ puts "`youtube-dl` not found!".color(:red)
104
+ exit 1
105
+ end
106
+
107
+ unless which("ffmpeg") && audio_only
108
+ puts "`ffmpeg` not found!".color(:red)
109
+ exit 1
110
+ end
111
+
112
+ def prompt(message)
113
+ m = "\n==> ".color(:yellow)
114
+ m << "#{message}"
115
+ m << "\n==> ".color(:yellow)
116
+ m << ("=" * message.length)
117
+ m << "\n==> ".color(:yellow)
118
+ m
119
+ end
120
+
121
+ print prompt "Enter n° of videos to download (space separated. ex: 1 2 3)"
122
+ indices = $stdin.gets.chomp.split(" ").map { |i| i.to_i - 1 }
123
+
124
+ unless indices.empty?
125
+ output_dir = "#{config[:download_directory]}/%(title)s.%(ext)s"
126
+
127
+ urls = Array.new
128
+
129
+ indices.each do |index|
130
+ unless videos[index].nil?
131
+ urls << videos[index][:url]
132
+ end
133
+ end
134
+
135
+ if urls.empty?
136
+ puts "Invalid video numbers...".color(:red)
137
+ exit 1
138
+ end
139
+
140
+ puts "Downloading videos...\n".color(:green)
141
+ progressbar = ProgressBar.create(:starting_at => 20, :total => nil)
142
+
143
+ urls.map! { |url| "\"#{url}\"" }
144
+ cmd = "youtube-dl -o '#{output_dir}' "
145
+ cmd << "-x " if audio_only
146
+ cmd << "--audio-quality #{config[:audio_quality]} "
147
+ cmd << "--audio-format '#{config[:audio_format]}' "
148
+ cmd << urls.join(" ")
149
+
150
+ status_code = 0
151
+ Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
152
+ stop = false
153
+ Signal.trap('SIGCHLD') do
154
+ stop = true
155
+ progressbar.stop
156
+ end
157
+
158
+ counter = 0
159
+ until stop
160
+ progressbar.increment if counter % 2000000 == 0
161
+ counter += 1
162
+ end
163
+
164
+ status_code = wait_thr.value.exitstatus
165
+ end
166
+
167
+ if status_code == 0
168
+ puts "\nVideos downloaded!".color(:green)
169
+ else
170
+ puts "\nAn error occured...".color(:red)
171
+ end
172
+
173
+ exit status_code
174
+ end
175
+ end
@@ -0,0 +1,6 @@
1
+ require_relative "./youtube-searcher/version"
2
+ require_relative "./youtube-searcher/config"
3
+ require_relative "./youtube-searcher/client"
4
+
5
+ module YoutubeSearcher
6
+ end
@@ -0,0 +1,51 @@
1
+ require "singleton"
2
+ require "google/api_client"
3
+
4
+ module YoutubeSearcher
5
+ class Client
6
+ include Singleton
7
+
8
+ APPLICATION_NAME = "Youtube Searcher"
9
+ YOUTUBE_API_SERVICE_NAME = "youtube"
10
+ YOUTUBE_API_VERSION = "v3"
11
+
12
+ def search(query, max_results: 20)
13
+ parameters = Hash.new
14
+ parameters[:part] = "id,snippet"
15
+ parameters[:type] = "video"
16
+ parameters[:maxResults] = max_results
17
+ parameters[:q] = query
18
+
19
+ response = client.execute!(
20
+ api_method: youtube.search.list,
21
+ parameters: parameters
22
+ )
23
+
24
+ videos = Array.new
25
+
26
+ response.data.items.each do |result|
27
+ video = Hash.new
28
+ video[:title] = result.snippet.title
29
+ video[:url] = "http://www.youtube.com/watch?v=#{result.id.videoId}"
30
+
31
+ videos << video
32
+ end
33
+
34
+ videos
35
+ end
36
+
37
+ def client
38
+ @client ||= Google::APIClient.new(key: api_key, authorization: nil, application_name: APPLICATION_NAME, application_version: YoutubeSearcher::VERSION)
39
+ end
40
+
41
+ def youtube
42
+ @youtube ||= @client.discovered_api(YOUTUBE_API_SERVICE_NAME, YOUTUBE_API_VERSION)
43
+ end
44
+
45
+ private
46
+
47
+ def api_key
48
+ @api_key ||= File.read(File.join(File.expand_path(__dir__), "..", "..", "api_key"))
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,39 @@
1
+ require "psych"
2
+ require "singleton"
3
+ require "erb"
4
+
5
+ class YoutubeSearcher::Config
6
+ include Singleton
7
+
8
+ CONFIG_FILE = ".ytsrc"
9
+
10
+ def config
11
+ @config ||= Psych.load(raw).inject({}) { |memo, (k, v)| memo[k.to_sym] = v; memo }
12
+ end
13
+ alias_method :get, :config
14
+
15
+ def generate
16
+ default_config = raw.gsub(/download_directory.*\n/, "download_directory: \"#{ENV["HOME"]}\"\n")
17
+ File.open(File.join(ENV["HOME"], ".ytsrc"), "w") do |f|
18
+ f.puts default_config
19
+ end
20
+ end
21
+
22
+ def raw
23
+ @raw ||= ERB.new(File.read(config_file)).result
24
+ end
25
+
26
+ private
27
+
28
+ def config_file
29
+ unless @config_file
30
+ if File.file?(File.join(ENV["HOME"], CONFIG_FILE))
31
+ @config_file = File.join(ENV["HOME"], CONFIG_FILE)
32
+ else
33
+ @config_file = File.expand_path(File.join(File.dirname(__FILE__), "..", "..", "ytsrc"))
34
+ end
35
+ end
36
+
37
+ @config_file
38
+ end
39
+ end
@@ -0,0 +1,3 @@
1
+ module YoutubeSearcher
2
+ VERSION = "0.0.2"
3
+ end
@@ -0,0 +1,24 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'youtube-searcher/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "youtube-searcher"
8
+ spec.version = YoutubeSearcher::VERSION
9
+ spec.authors = ["Aziz Light"]
10
+ spec.email = ["aziz@azizlight.me"]
11
+ spec.summary = %q{Search Youtube from the command line and download videos using `youtube-dl`.}
12
+ spec.homepage = "https://github.com/AzizLight/youtube-searcher"
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.require_paths = ["lib"]
18
+
19
+ spec.add_development_dependency "bundler", "~> 1.5"
20
+ spec.add_development_dependency "rake"
21
+ spec.add_runtime_dependency "google-api-client"
22
+ spec.add_runtime_dependency "rainbow"
23
+ spec.add_runtime_dependency "ruby-progressbar"
24
+ end
data/ytsrc ADDED
@@ -0,0 +1,6 @@
1
+ ---
2
+ max_results: 20
3
+ download_directory: "<%= Dir.getwd %>"
4
+ audio_quality: 7
5
+ audio_format: "mp3"
6
+ always_download: true
metadata ADDED
@@ -0,0 +1,127 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: youtube-searcher
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Aziz Light
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-02-24 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.5'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.5'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: google-api-client
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rainbow
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: ruby-progressbar
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :runtime
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ description:
84
+ email:
85
+ - aziz@azizlight.me
86
+ executables:
87
+ - yts
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - Gemfile
93
+ - LICENSE
94
+ - README.md
95
+ - Rakefile
96
+ - bin/yts
97
+ - lib/youtube-searcher.rb
98
+ - lib/youtube-searcher/client.rb
99
+ - lib/youtube-searcher/config.rb
100
+ - lib/youtube-searcher/version.rb
101
+ - youtube-searcher.gemspec
102
+ - ytsrc
103
+ homepage: https://github.com/AzizLight/youtube-searcher
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.2.0
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: Search Youtube from the command line and download videos using `youtube-dl`.
127
+ test_files: []