yt_mp3 0.0.1 → 1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.travis.yml +7 -0
- data/Gemfile +1 -0
- data/README.md +21 -13
- data/bin/yt_mp3 +72 -17
- data/lib/yt_mp3.rb +28 -2
- data/lib/yt_mp3/badge_progress_bar.rb +21 -0
- data/lib/yt_mp3/sync.rb +37 -0
- data/lib/yt_mp3/version.rb +1 -1
- data/lib/yt_mp3/youtube_mp3.rb +28 -14
- data/spec/spec_helper.rb +8 -1
- data/spec/sync_spec.rb +35 -0
- data/spec/youtube_mp3_spec.rb +15 -0
- data/yt_mp3.gemspec +4 -3
- metadata +27 -5
data/.travis.yml
ADDED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,29 +1,37 @@
|
|
1
|
-
#
|
1
|
+
# YTMp3 [![Build Status](https://secure.travis-ci.org/Kofel/yt_mp3.png)](http://travis-ci.org/Kofel/yt_mp3)
|
2
2
|
|
3
|
-
|
3
|
+
Quickly download video from YouTube to mp3. Sync the folder with playlists or favorites!
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
$ gem install yt_mp3
|
7
7
|
|
8
8
|
## Usage
|
9
9
|
|
10
|
+
###Just download
|
11
|
+
|
10
12
|
$ yt_mp3 http://www.youtube.com/watch?v=DYyWVlh8NLM
|
13
|
+
|
14
|
+
###Syncing
|
15
|
+
**syncing user favorites**
|
16
|
+
|
17
|
+
$ yt_mp3 init favorites kofels
|
18
|
+
$ yt_mp3 sync
|
19
|
+
|
20
|
+
**syncing playlist**
|
11
21
|
|
12
|
-
|
13
|
-
|
22
|
+
$ yt_mp3 init playlist 39D3FA44E3F75572
|
23
|
+
$ yt_mp3 sync
|
24
|
+
|
25
|
+
**and when something has been pushed to playlist type**
|
14
26
|
|
15
|
-
|
16
|
-
GET: http://www.youtube-mp3.org/api/pushItem/?item=#{video_url}&xy=yx&bf=false&r=#{Time.now.to_i}
|
17
|
-
RESPONSE: video_id
|
27
|
+
$ yt_mp3 sync
|
18
28
|
|
19
|
-
|
20
|
-
|
29
|
+
## YouTube MP3 API
|
30
|
+
This gem was build on top of [YouTube-Mp3.org](http://youtube-mp3.org).
|
21
31
|
|
22
|
-
|
23
|
-
GET: http://www.youtube-mp3.org/api/itemInfo/?video_id=#{video_id}&ac=www&r=#{Time.now.to_i}
|
24
|
-
RESPONSE: info JSON fetched by response.body.match(/\Ainfo = (.*?);\z/)[1]
|
32
|
+
[There I've described an API](https://github.com/Kofel/yt_mp3/wiki/YouTube-MP3-API)
|
25
33
|
|
26
|
-
|
34
|
+
Please note that *yt_mp3* is not responsible for failed conversions. There are limitations ex. 15 convertions for 30 minutes.
|
27
35
|
|
28
36
|
## Contributing
|
29
37
|
|
data/bin/yt_mp3
CHANGED
@@ -1,28 +1,83 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
require 'trollop'
|
4
|
-
|
5
3
|
$:.unshift(File.join(File.dirname(__FILE__), "/../lib"))
|
6
4
|
require "yt_mp3"
|
5
|
+
require "json"
|
7
6
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
7
|
+
banner = <<-EOS
|
8
|
+
yt_mp3 #{YTMp3::VERSION} (c) 2012 #{YTMp3::AUTHORS.join(', ')}
|
9
|
+
USAGE:
|
10
|
+
#{$0} [url]
|
11
|
+
OR:
|
12
|
+
initalize playlist sync:
|
13
|
+
#{$0} init playlist PLAYLIST_ID
|
14
|
+
initalize user favourite sync:
|
15
|
+
#{$0} init favorites USERNAME
|
16
|
+
and then:
|
17
|
+
#{$0} sync
|
12
18
|
EOS
|
13
|
-
end
|
14
19
|
|
15
20
|
if ARGV.empty?
|
16
|
-
STDERR.puts
|
17
|
-
STDERR.puts "USAGE: #{$0} [url]"
|
21
|
+
STDERR.puts banner
|
18
22
|
else
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
23
|
+
opts = ARGV.first
|
24
|
+
|
25
|
+
if opts == "--help" || opts == "-h"
|
26
|
+
puts banner
|
27
|
+
elsif opts == "init"
|
28
|
+
if ARGV[1] and ARGV[2]
|
29
|
+
options = {:synced => Array.new}
|
30
|
+
|
31
|
+
if ARGV[1] == "playlist"
|
32
|
+
options["playlist"] = ARGV[2]
|
33
|
+
puts "Playlist #{ARGV[2]} syncing initalized..."
|
34
|
+
elsif ARGV[1] == "favorites"
|
35
|
+
options["favorites"] = ARGV[2]
|
36
|
+
puts "User favorites \"#{ARGV[2]}\" syncing initalized..."
|
37
|
+
else
|
38
|
+
STDERR.puts banner
|
39
|
+
Kernel.exit
|
40
|
+
end
|
41
|
+
|
42
|
+
File.open(".yt_mp3.json","w") {|f| f.puts options.to_json}
|
43
|
+
else
|
44
|
+
STDERR.puts banner
|
45
|
+
end
|
46
|
+
elsif opts == "sync"
|
47
|
+
#begin
|
48
|
+
if File.exists?(".yt_mp3.json") && (options = JSON.parse(File.read(".yt_mp3.json")))
|
49
|
+
sync = nil
|
26
50
|
|
27
|
-
|
51
|
+
if options["playlist"]
|
52
|
+
sync = YTMp3::Sync.new :playlist => options["playlist"]
|
53
|
+
else
|
54
|
+
sync = YTMp3::Sync.new :favorites => options["favorites"]
|
55
|
+
end
|
56
|
+
|
57
|
+
sync.synced = options["synced"] || Array.new
|
58
|
+
|
59
|
+
if (n_count = sync.new_videos.count ) > 0
|
60
|
+
puts "Found #{n_count} new videos. Syncing..."
|
61
|
+
else
|
62
|
+
puts "Nothing to sync. Exiting."
|
63
|
+
Kernel.exit
|
64
|
+
end
|
65
|
+
|
66
|
+
options["synced"] = sync.sync do |mp3|
|
67
|
+
YTMp3::download_progress mp3
|
68
|
+
end
|
69
|
+
|
70
|
+
File.open(".yt_mp3.json","w") {|f| f.puts options.to_json}
|
71
|
+
|
72
|
+
puts "Syncing done."
|
73
|
+
else
|
74
|
+
STDERR.puts "Syncing not initialized yet. Please type:\n#{$1} init (...)"
|
75
|
+
end
|
76
|
+
#rescue Exception => e
|
77
|
+
# e.inspect
|
78
|
+
# STDERR.puts "Error found: #{e}\nPlease initalize syncing by:\n#{$1} init (...)"
|
79
|
+
#end
|
80
|
+
else
|
81
|
+
YTMp3::download_progress YTMp3::YouTubeMP3.new(opts)
|
82
|
+
end
|
28
83
|
end
|
data/lib/yt_mp3.rb
CHANGED
@@ -1,5 +1,8 @@
|
|
1
1
|
require "yt_mp3/version"
|
2
2
|
require "yt_mp3/youtube_mp3"
|
3
|
+
require "yt_mp3/sync"
|
4
|
+
|
5
|
+
require "yt_mp3/badge_progress_bar"
|
3
6
|
|
4
7
|
module YTMp3
|
5
8
|
def self.uri?(string)
|
@@ -11,7 +14,30 @@ module YTMp3
|
|
11
14
|
false
|
12
15
|
end
|
13
16
|
|
14
|
-
|
17
|
+
def self.download_progress(mp3)
|
18
|
+
puts ":: #{mp3.url}\n"
|
19
|
+
|
20
|
+
bar = BadgeProgressBar.new
|
21
|
+
bar.badge = "Converting"
|
15
22
|
|
16
|
-
|
23
|
+
begin
|
24
|
+
mp3.convert do |info|
|
25
|
+
bar.count = (info["progress"].to_f * 100).abs
|
26
|
+
bar.write
|
27
|
+
end
|
28
|
+
|
29
|
+
begin
|
30
|
+
mp3.download
|
31
|
+
rescue DownloadError => e
|
32
|
+
STDERR.puts "Failed to download. Video cannot be converted. Could not exists. (#{e})"
|
33
|
+
end
|
34
|
+
rescue RequestFailed => e
|
35
|
+
STDERR.puts e
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class RequestFailed < StandardError; end
|
40
|
+
class DownloadError < StandardError; end
|
41
|
+
class SyncError < StandardError; end
|
17
42
|
end
|
43
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require "progress_bar"
|
2
|
+
|
3
|
+
module YTMp3
|
4
|
+
class BadgeProgressBar < ProgressBar
|
5
|
+
attr_accessor :badge
|
6
|
+
|
7
|
+
def initialize(*args)
|
8
|
+
super 100, :badge, :bar, :percentage, :elapsed
|
9
|
+
end
|
10
|
+
|
11
|
+
protected
|
12
|
+
|
13
|
+
def render_badge
|
14
|
+
@badge
|
15
|
+
end
|
16
|
+
|
17
|
+
def badge_width
|
18
|
+
@badge.length
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
data/lib/yt_mp3/sync.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'youtube_it'
|
2
|
+
|
3
|
+
module YTMp3
|
4
|
+
class Sync
|
5
|
+
attr_reader :videos_playlist
|
6
|
+
attr_accessor :synced
|
7
|
+
|
8
|
+
def initialize(options)
|
9
|
+
client = YouTubeIt::Client.new
|
10
|
+
videos = {}
|
11
|
+
|
12
|
+
if options[:favorites]
|
13
|
+
videos = client.videos_by(:favorites, :user => options[:favorites]).videos
|
14
|
+
elsif options[:playlist]
|
15
|
+
videos = client.playlist(options[:playlist].gsub(/\APL/, "")).videos
|
16
|
+
else
|
17
|
+
raise SyncError, "Sync by what?"
|
18
|
+
end
|
19
|
+
|
20
|
+
@videos_playlist = videos.collect { |v| v.unique_id }
|
21
|
+
@synced = Array.new
|
22
|
+
|
23
|
+
raise SyncError, "Empty playlist to synchronize" if @videos_playlist.empty?
|
24
|
+
end
|
25
|
+
|
26
|
+
def new_videos
|
27
|
+
(@videos_playlist - @synced)
|
28
|
+
end
|
29
|
+
|
30
|
+
def sync
|
31
|
+
new_videos.each do |video|
|
32
|
+
@synced << video
|
33
|
+
yield YTMp3::YouTubeMP3.new(video)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/yt_mp3/version.rb
CHANGED
data/lib/yt_mp3/youtube_mp3.rb
CHANGED
@@ -1,4 +1,3 @@
|
|
1
|
-
require 'timeout'
|
2
1
|
require 'httparty'
|
3
2
|
require 'uri'
|
4
3
|
require 'json'
|
@@ -13,27 +12,42 @@ module YTMp3
|
|
13
12
|
end
|
14
13
|
|
15
14
|
def convert
|
16
|
-
r = HTTParty.get "http://www.youtube-mp3.org/api/pushItem/?item=#{
|
15
|
+
r = HTTParty.get "http://www.youtube-mp3.org/api/pushItem/?item=#{CGI.escape(@url)}&xy=yx&bf=false&r=#{Time.now.to_i}", :headers => {"Accept-Location" => "*"}
|
17
16
|
|
18
|
-
raise RequestFailed, "
|
17
|
+
raise RequestFailed, "Something went wrong (not HTTP OK)" unless r.response.class == Net::HTTPOK
|
18
|
+
handle_error(r.body)
|
19
19
|
|
20
20
|
video_id = r.body
|
21
21
|
|
22
|
-
|
23
|
-
|
24
|
-
r = HTTParty.get "http://www.youtube-mp3.org/api/itemInfo/?video_id=#{video_id}&ac=www&r=#{Time.now.to_i}"
|
22
|
+
while true
|
23
|
+
r = HTTParty.get "http://www.youtube-mp3.org/api/itemInfo/?video_id=#{video_id}&ac=www&r=#{Time.now.to_i}", :headers => {"Accept-Location" => "*"}
|
25
24
|
|
26
|
-
|
25
|
+
raise RequestFailed, "Something went wrong (not HTTP OK)" unless r.response.class == Net::HTTPOK
|
27
26
|
|
28
|
-
|
27
|
+
info = JSON.parse(r.body.match(/\Ainfo = (.*?);\z/)[1])
|
29
28
|
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
end
|
29
|
+
if info["status"] == "serving"
|
30
|
+
@downloadable = "http://www.youtube-mp3.org/get?video_id=#{video_id}&h=#{info['h']}&r=#{Time.now.to_i}"
|
31
|
+
@title = info["title"]
|
32
|
+
break
|
35
33
|
end
|
36
|
-
|
34
|
+
|
35
|
+
yield info if block_given?
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def download
|
40
|
+
raise DownloadError, "File not converted yet" unless @downloadable
|
41
|
+
system "curl --progress-bar -L \"#{@downloadable}\" > \"#{@title.gsub(/\//,"-")}.mp3\""
|
42
|
+
end
|
43
|
+
|
44
|
+
protected
|
45
|
+
|
46
|
+
def handle_error( body )
|
47
|
+
case body
|
48
|
+
when "$$$LIMIT$$$" then raise RequestFailed, "YouTubeMP3 limit reached! Only 15 convertions per 30 minutes."
|
49
|
+
when "$$$ERROR$$$" then raise RequestFailed, "There was an Error caused by YouTube, we cannot deliver this Video! This error is mostly caused by copyright issues or the length of the video. YouTubeMP3 only support videos with maximum of 20 minutes."
|
50
|
+
end
|
37
51
|
end
|
38
52
|
end
|
39
53
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,6 +3,13 @@ require 'bundler/setup'
|
|
3
3
|
|
4
4
|
require 'yt_mp3' # and any other gems you need
|
5
5
|
|
6
|
-
|
6
|
+
module Helpers
|
7
|
+
def temp
|
8
|
+
system("mkdir -p tmp")
|
9
|
+
Dir.chdir "tmp/"
|
10
|
+
end
|
11
|
+
end
|
7
12
|
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.include Helpers
|
8
15
|
end
|
data/spec/sync_spec.rb
ADDED
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe YTMp3::Sync do
|
4
|
+
before do
|
5
|
+
temp
|
6
|
+
end
|
7
|
+
|
8
|
+
it "loads playlist" do
|
9
|
+
sync = YTMp3::Sync.new :playlist => "39D3FA44E3F75572"
|
10
|
+
sync.new_videos.count.should be > 0
|
11
|
+
end
|
12
|
+
|
13
|
+
it "loads playlist with prefix" do
|
14
|
+
sync = YTMp3::Sync.new :playlist => "PL39D3FA44E3F75572"
|
15
|
+
sync.new_videos.count.should be > 0
|
16
|
+
end
|
17
|
+
|
18
|
+
it "loads user favorites" do
|
19
|
+
sync = YTMp3::Sync.new :favorites => "SuchyKanal"
|
20
|
+
sync.new_videos.count.should be > 0
|
21
|
+
end
|
22
|
+
|
23
|
+
it "does sync" do
|
24
|
+
sync = YTMp3::Sync.new :playlist => "PL912D4D7B38309EAA"
|
25
|
+
sync.synced.count.should eq 0
|
26
|
+
|
27
|
+
new_videos = sync.new_videos
|
28
|
+
|
29
|
+
sync.sync do |mp3|
|
30
|
+
mp3.should be_an_instance_of YTMp3::YouTubeMP3
|
31
|
+
end
|
32
|
+
|
33
|
+
sync.synced.should eq new_videos
|
34
|
+
end
|
35
|
+
end
|
data/spec/youtube_mp3_spec.rb
CHANGED
@@ -1,6 +1,10 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe YTMp3::YouTubeMP3 do
|
4
|
+
before do
|
5
|
+
temp
|
6
|
+
end
|
7
|
+
|
4
8
|
it "initialize by URL" do
|
5
9
|
YTMp3::YouTubeMP3.new("http://www.youtube.com/watch?v=KlJy_Cb21Lw").url.should eq "http://www.youtube.com/watch?v=KlJy_Cb21Lw"
|
6
10
|
end
|
@@ -20,4 +24,15 @@ describe YTMp3::YouTubeMP3 do
|
|
20
24
|
mp3.convert
|
21
25
|
mp3.title.should eq "Lady Antebellum - Need You Now (HQ) [Lyrics]"
|
22
26
|
end
|
27
|
+
|
28
|
+
it "downloads" do
|
29
|
+
mp3 = YTMp3::YouTubeMP3.new("KlJy_Cb21Lw")
|
30
|
+
mp3.convert
|
31
|
+
mp3.download
|
32
|
+
end
|
33
|
+
|
34
|
+
it "cannot download while not converted" do
|
35
|
+
mp3 = YTMp3::YouTubeMP3.new("KlJy_Cb21Lw")
|
36
|
+
expect { mp3.download }.to raise_error(YTMp3::DownloadError)
|
37
|
+
end
|
23
38
|
end
|
data/yt_mp3.gemspec
CHANGED
@@ -4,13 +4,14 @@ require File.expand_path('../lib/yt_mp3/version', __FILE__)
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.authors = YTMp3::AUTHORS
|
6
6
|
gem.email = ["kofels@gmail.com"]
|
7
|
-
gem.description = %q{
|
8
|
-
gem.summary =
|
7
|
+
gem.description = %q{Quickly download video from YouTube to mp3. Sync the folder with playlists or favorites!}
|
8
|
+
gem.summary = %q{Downloading YouTube videos as mp3}
|
9
9
|
gem.homepage = "http://kofel.github.com/yt_mp3/"
|
10
10
|
|
11
11
|
gem.rubyforge_project = "yt_mp3"
|
12
12
|
gem.add_dependency "httparty"
|
13
|
-
gem.add_dependency "
|
13
|
+
gem.add_dependency "youtube_it"
|
14
|
+
gem.add_dependency "progress_bar"
|
14
15
|
|
15
16
|
gem.files = `git ls-files`.split($\)
|
16
17
|
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: yt_mp3
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: '1.0'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-
|
12
|
+
date: 2012-06-07 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: httparty
|
@@ -28,7 +28,7 @@ dependencies:
|
|
28
28
|
- !ruby/object:Gem::Version
|
29
29
|
version: '0'
|
30
30
|
- !ruby/object:Gem::Dependency
|
31
|
-
name:
|
31
|
+
name: youtube_it
|
32
32
|
requirement: !ruby/object:Gem::Requirement
|
33
33
|
none: false
|
34
34
|
requirements:
|
@@ -43,7 +43,24 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: '0'
|
46
|
-
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: progress_bar
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: '0'
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
description: Quickly download video from YouTube to mp3. Sync the folder with playlists
|
63
|
+
or favorites!
|
47
64
|
email:
|
48
65
|
- kofels@gmail.com
|
49
66
|
executables:
|
@@ -53,15 +70,19 @@ extra_rdoc_files: []
|
|
53
70
|
files:
|
54
71
|
- .gitignore
|
55
72
|
- .rspec
|
73
|
+
- .travis.yml
|
56
74
|
- Gemfile
|
57
75
|
- LICENSE
|
58
76
|
- README.md
|
59
77
|
- Rakefile
|
60
78
|
- bin/yt_mp3
|
61
79
|
- lib/yt_mp3.rb
|
80
|
+
- lib/yt_mp3/badge_progress_bar.rb
|
81
|
+
- lib/yt_mp3/sync.rb
|
62
82
|
- lib/yt_mp3/version.rb
|
63
83
|
- lib/yt_mp3/youtube_mp3.rb
|
64
84
|
- spec/spec_helper.rb
|
85
|
+
- spec/sync_spec.rb
|
65
86
|
- spec/youtube_mp3_spec.rb
|
66
87
|
- yt_mp3.gemspec
|
67
88
|
homepage: http://kofel.github.com/yt_mp3/
|
@@ -87,7 +108,8 @@ rubyforge_project: yt_mp3
|
|
87
108
|
rubygems_version: 1.8.22
|
88
109
|
signing_key:
|
89
110
|
specification_version: 3
|
90
|
-
summary:
|
111
|
+
summary: Downloading YouTube videos as mp3
|
91
112
|
test_files:
|
92
113
|
- spec/spec_helper.rb
|
114
|
+
- spec/sync_spec.rb
|
93
115
|
- spec/youtube_mp3_spec.rb
|