nehm 1.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.
- checksums.yaml +7 -0
- data/.gitignore +10 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE +19 -0
- data/README.md +93 -0
- data/Rakefile +20 -0
- data/bin/nehm +3 -0
- data/lib/nehm/artwork.rb +24 -0
- data/lib/nehm/cacert.pem +3988 -0
- data/lib/nehm/client.rb +13 -0
- data/lib/nehm/config.rb +46 -0
- data/lib/nehm/configure.rb +24 -0
- data/lib/nehm/help.rb +57 -0
- data/lib/nehm/os.rb +7 -0
- data/lib/nehm/path_control.rb +48 -0
- data/lib/nehm/track.rb +49 -0
- data/lib/nehm/track_utils.rb +96 -0
- data/lib/nehm/user.rb +37 -0
- data/lib/nehm/user_control.rb +49 -0
- data/lib/nehm/version.rb +3 -0
- data/lib/nehm.rb +53 -0
- data/nehm.gemspec +29 -0
- metadata +188 -0
data/lib/nehm/client.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
require 'soundcloud'
|
2
|
+
require_relative 'config.rb'
|
3
|
+
|
4
|
+
module Client
|
5
|
+
# SSL Certificate
|
6
|
+
ENV['SSL_CERT_FILE'] = File.join(__dir__, 'cacert.pem')
|
7
|
+
|
8
|
+
CLIENT_ID = '11a37feb6ccc034d5975f3f803928a32'
|
9
|
+
|
10
|
+
def self.new
|
11
|
+
@client ||= Soundcloud.new(client_id: CLIENT_ID)
|
12
|
+
end
|
13
|
+
end
|
data/lib/nehm/config.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module Config
|
4
|
+
# Public
|
5
|
+
|
6
|
+
def self.[](key)
|
7
|
+
config_hash = load_config
|
8
|
+
config_hash[key.to_s]
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.[]=(key, value)
|
12
|
+
config_hash = load_config
|
13
|
+
config_hash[key.to_s] = value
|
14
|
+
save_config(config_hash)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.create
|
18
|
+
File.open(file_path, 'w+') { |f| f.write("---\napp: nehm")}
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.exist?
|
22
|
+
File.exist?(file_path)
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.key?(key)
|
26
|
+
load_config.key?(key.to_s)
|
27
|
+
end
|
28
|
+
|
29
|
+
# Private
|
30
|
+
|
31
|
+
module_function
|
32
|
+
|
33
|
+
def file_path
|
34
|
+
File.join(ENV['HOME'], '.nehmconfig')
|
35
|
+
end
|
36
|
+
|
37
|
+
def load_config
|
38
|
+
file = file_path
|
39
|
+
YAML.load_file(file)
|
40
|
+
end
|
41
|
+
|
42
|
+
def save_config(config_hash)
|
43
|
+
file = file_path
|
44
|
+
IO.write(file, config_hash.to_yaml)
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'highline'
|
2
|
+
require 'paint'
|
3
|
+
require_relative 'user_control.rb'
|
4
|
+
require_relative 'os.rb'
|
5
|
+
require_relative 'config.rb'
|
6
|
+
require_relative 'path_control.rb'
|
7
|
+
|
8
|
+
module Configure
|
9
|
+
def self.menu
|
10
|
+
puts 'Download path: ' + Paint[PathControl.dl_path, :magenta]
|
11
|
+
puts 'iTunes path: ' + Paint[PathControl.itunes_path.sub("/iTunes\ Media/Automatically\ Add\ to\ iTunes.localized", ''), :magenta] unless OS.linux?
|
12
|
+
puts 'Permalink: ' + Paint[Config[:permalink], :cyan]
|
13
|
+
puts "\n"
|
14
|
+
|
15
|
+
HighLine.new.choose do |menu|
|
16
|
+
menu.prompt = Paint['Choose setting', :yellow]
|
17
|
+
|
18
|
+
menu.choice('Edit download path') { PathControl.set_dl_path }
|
19
|
+
menu.choice('Edit itunes path') { PathControl.set_itunes_path } unless OS.linux?
|
20
|
+
menu.choice('Edit permalink') { UserControl.log_in }
|
21
|
+
menu.choice('Exit') { exit }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
data/lib/nehm/help.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
module Help
|
2
|
+
|
3
|
+
# Public
|
4
|
+
|
5
|
+
def self.available_commands
|
6
|
+
puts Paint['Avalaible nehm commands:', :yellow]
|
7
|
+
puts ' ' + Paint['get', :green] + ' - Downloading, setting tags and adding to your iTunes library last post or like from your profile'
|
8
|
+
puts ' ' + Paint['dl', :green] + ' - Downloading and setting tags last post or like from your profile'
|
9
|
+
puts ' ' + Paint['configure', :green] + ' - Configuring application'
|
10
|
+
puts "See #{Paint['nehm help [command]']} to read about a specific subcommand"
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.show(command)
|
14
|
+
case command
|
15
|
+
when 'get', 'dl', 'configure'
|
16
|
+
Help.send(command)
|
17
|
+
else
|
18
|
+
Help.available_commands
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Private
|
23
|
+
|
24
|
+
module_function
|
25
|
+
|
26
|
+
def permalink
|
27
|
+
puts 'Permalink is the last word in your profile url'
|
28
|
+
puts 'Example: for profile url ' + Paint['soundcloud.com/qwerty', :magenta] + ' permalink is ' + Paint['qwerty', :magenta]
|
29
|
+
end
|
30
|
+
|
31
|
+
def get
|
32
|
+
puts Paint['Input: ', :yellow] + 'nehm get [options]'
|
33
|
+
|
34
|
+
puts Paint['Available options:', :yellow]
|
35
|
+
puts ' ' + Paint['track', :green] + ' - Downloading, setting tags and adding to your iTunes library last post(track or repost) from your profile'
|
36
|
+
puts ' ' + Paint['[number] tracks', :green] + ' - Downloading, setting tags and adding to your iTunes library last [number] posts from your profile'
|
37
|
+
puts ' ' + Paint['like', :green] + ' - Downloading, setting tags and adding to your iTunes library your last like'
|
38
|
+
puts ' ' + Paint['[number] likes', :green] + ' - Downloading, setting tags and adding to your iTunes library your last [number] likes'
|
39
|
+
end
|
40
|
+
|
41
|
+
def dl
|
42
|
+
puts Paint['Input: ', :yellow] + 'nehm dl [options]'
|
43
|
+
|
44
|
+
puts Paint['Available options:', :yellow]
|
45
|
+
puts ' ' + Paint['track', :green] + ' - Downloading and setting tags last post(track or repost) from your profile'
|
46
|
+
puts ' ' + Paint['[number] tracks', :green] + ' - Downloading and setting tags last [number] posts from your profile'
|
47
|
+
puts ' ' + Paint['like', :green] + ' - Downloading and setting tags your last like'
|
48
|
+
puts ' ' + Paint['[number] likes', :green] + ' - Downloading and setting tags your last [number] likes'
|
49
|
+
end
|
50
|
+
|
51
|
+
def configure
|
52
|
+
puts Paint['Input: ', :yellow] + 'nehm configure'
|
53
|
+
|
54
|
+
puts Paint['Available options:', :yellow]
|
55
|
+
puts ' No options'
|
56
|
+
end
|
57
|
+
end
|
data/lib/nehm/os.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'highline'
|
2
|
+
require_relative 'config.rb'
|
3
|
+
module PathControl
|
4
|
+
def self.dl_path
|
5
|
+
Config[:dl_path]
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.set_dl_path
|
9
|
+
loop do
|
10
|
+
default_path = File.join(ENV['HOME'], '/Music')
|
11
|
+
path = HighLine.new.ask("Enter a FULL path to desirable download directory (press enter to set it to #{default_path}): ")
|
12
|
+
path = default_path if path == ''
|
13
|
+
|
14
|
+
if Dir.exist?(path)
|
15
|
+
Config[:dl_path] = path
|
16
|
+
puts Paint['Download directory set up!', :green]
|
17
|
+
break
|
18
|
+
else
|
19
|
+
puts "\n"
|
20
|
+
puts Paint["This directory doesn't exist. Please enter path again", :red]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.itunes_path
|
26
|
+
Config[:itunes_path]
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.set_itunes_path
|
30
|
+
loop do
|
31
|
+
default_path = File.join(ENV['HOME'], '/Music/iTunes')
|
32
|
+
path = HighLine.new.ask("Enter a FULL path to iTunes directory (press enter to set it to #{default_path}): ")
|
33
|
+
path = default_path if path == ''
|
34
|
+
|
35
|
+
path = File.join(path, "iTunes\ Media/Automatically\ Add\ to\ iTunes.localized")
|
36
|
+
|
37
|
+
if Dir.exist?(path)
|
38
|
+
Config[:itunes_path] = path
|
39
|
+
puts Paint['iTunes directory set up!', :green]
|
40
|
+
break
|
41
|
+
else
|
42
|
+
puts "\n"
|
43
|
+
puts Paint["This directory doesn't exist. Please enter path again", :red]
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/nehm/track.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
require_relative 'client.rb'
|
2
|
+
require_relative 'artwork.rb'
|
3
|
+
require_relative 'path_control.rb'
|
4
|
+
|
5
|
+
class Track
|
6
|
+
attr_reader :hash
|
7
|
+
|
8
|
+
def initialize(hash)
|
9
|
+
@hash = hash
|
10
|
+
end
|
11
|
+
|
12
|
+
def artist
|
13
|
+
if @hash['title'].include?('-')
|
14
|
+
title = @hash['title'].split('-')
|
15
|
+
title[0].rstrip
|
16
|
+
else
|
17
|
+
@hash['user']['username']
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def title
|
22
|
+
if @hash['title'].include?('-')
|
23
|
+
title = @hash['title'].split('-')
|
24
|
+
title[1].lstrip
|
25
|
+
else
|
26
|
+
@hash['title']
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def artwork
|
31
|
+
Artwork.new(self)
|
32
|
+
end
|
33
|
+
|
34
|
+
def dl_url
|
35
|
+
"#{@hash['stream_url']}?client_id=#{Client::CLIENT_ID}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def file_name
|
39
|
+
"#{artist} - #{title}.mp3".tr('/', '')
|
40
|
+
end
|
41
|
+
|
42
|
+
def file_path
|
43
|
+
File.join(PathControl.dl_path, file_name)
|
44
|
+
end
|
45
|
+
|
46
|
+
def id
|
47
|
+
@hash['id'].to_s
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'taglib'
|
2
|
+
require 'fileutils'
|
3
|
+
require 'paint'
|
4
|
+
require_relative 'path_control.rb'
|
5
|
+
require_relative 'client.rb'
|
6
|
+
require_relative 'user.rb'
|
7
|
+
require_relative 'track.rb'
|
8
|
+
require_relative 'os.rb'
|
9
|
+
|
10
|
+
module TrackUtils
|
11
|
+
# Public
|
12
|
+
|
13
|
+
def self.get(dl, args)
|
14
|
+
tracks = []
|
15
|
+
tracks +=
|
16
|
+
case args.last
|
17
|
+
when 'like'
|
18
|
+
User.new.likes(1)
|
19
|
+
when 'post'
|
20
|
+
User.new.posts(1)
|
21
|
+
when 'likes'
|
22
|
+
count = args[-2].to_i
|
23
|
+
User.new.likes(count)
|
24
|
+
when 'posts'
|
25
|
+
count = args[-2].to_i
|
26
|
+
User.new.posts(count)
|
27
|
+
when %r{https:\/\/soundcloud.com\/}
|
28
|
+
track_from_url(args.last)
|
29
|
+
else
|
30
|
+
puts Paint['Invalid argument(s)', :red]
|
31
|
+
puts "Input #{Paint['nehm help', :yellow]} for help"
|
32
|
+
exit
|
33
|
+
end
|
34
|
+
|
35
|
+
tracks.each do |track|
|
36
|
+
dl(track)
|
37
|
+
tag(track)
|
38
|
+
cp(track) unless (dl == :dl) || (OS.linux?)
|
39
|
+
track.artwork.suicide
|
40
|
+
end
|
41
|
+
puts Paint['Done!', :green]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Private
|
45
|
+
|
46
|
+
module_function
|
47
|
+
|
48
|
+
def track_from_url(url)
|
49
|
+
client = Client.new
|
50
|
+
track = client.get('/resolve', url: url)
|
51
|
+
tracks = []
|
52
|
+
tracks << Track.new(track)
|
53
|
+
end
|
54
|
+
|
55
|
+
def dl(track)
|
56
|
+
# Downloading track and artwork
|
57
|
+
puts 'Downloading ' + track.artist + ' - ' + track.title
|
58
|
+
path = track.file_path
|
59
|
+
dl_url = track.dl_url
|
60
|
+
command = "curl -# -o '" + path + "' -L " + dl_url
|
61
|
+
system(command)
|
62
|
+
|
63
|
+
puts 'Downloading artwork'
|
64
|
+
artwork = track.artwork
|
65
|
+
path = artwork.file_path
|
66
|
+
dl_url = artwork.dl_url
|
67
|
+
command = "curl -# -o '" + path + "' -L " + dl_url
|
68
|
+
system(command)
|
69
|
+
end
|
70
|
+
|
71
|
+
def tag(track)
|
72
|
+
path = track.file_path
|
73
|
+
TagLib::MPEG::File.open(path) do |file|
|
74
|
+
puts 'Setting tags'
|
75
|
+
#TODO: Add more tags (year)
|
76
|
+
tag = file.id3v2_tag
|
77
|
+
tag.artist = track.artist
|
78
|
+
tag.title = track.title
|
79
|
+
|
80
|
+
# Adding artwork
|
81
|
+
apic = TagLib::ID3v2::AttachedPictureFrame.new
|
82
|
+
apic.mime_type = 'image/jpeg'
|
83
|
+
apic.description = 'Cover'
|
84
|
+
apic.type = TagLib::ID3v2::AttachedPictureFrame::FrontCover
|
85
|
+
apic.picture = File.open(track.artwork.file_path, 'rb') { |f| f.read }
|
86
|
+
tag.add_frame(apic)
|
87
|
+
|
88
|
+
file.save
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
def cp(track)
|
93
|
+
puts 'Adding to iTunes library'
|
94
|
+
FileUtils.cp(track.file_path, PathControl.itunes_path)
|
95
|
+
end
|
96
|
+
end
|
data/lib/nehm/user.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'faraday'
|
3
|
+
require_relative 'client.rb'
|
4
|
+
require_relative 'config.rb'
|
5
|
+
require_relative 'user_control.rb'
|
6
|
+
|
7
|
+
class User
|
8
|
+
def initialize(permalink = nil)
|
9
|
+
client = Client.new
|
10
|
+
@id =
|
11
|
+
unless permalink.nil?
|
12
|
+
user = client.get('/resolve', url: "https://soundcloud.com/#{permalink}")
|
13
|
+
user.id
|
14
|
+
else
|
15
|
+
UserControl.default_id
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def likes(count)
|
20
|
+
client = Client.new
|
21
|
+
likes = client.get("/users/#{@id}/favorites?limit=#{count}")
|
22
|
+
likes.map { |like| Track.new(like) }
|
23
|
+
end
|
24
|
+
|
25
|
+
def posts(count)
|
26
|
+
conn = Faraday.new(url: 'https://api-v2.soundcloud.com/') do |faraday|
|
27
|
+
faraday.request :url_encoded # form-encode POST params
|
28
|
+
faraday.adapter Faraday.default_adapter # make requests with Net::HTTP
|
29
|
+
end
|
30
|
+
|
31
|
+
response = conn.get("/profile/soundcloud:users:#{@id}?limit=#{count}&offset=0")
|
32
|
+
|
33
|
+
parsed = JSON.parse(response.body)
|
34
|
+
parsed = parsed['collection']
|
35
|
+
parsed.map { |track| Track.new(track['track']) }
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'highline'
|
2
|
+
require 'paint'
|
3
|
+
require_relative 'config.rb'
|
4
|
+
require_relative 'client.rb'
|
5
|
+
|
6
|
+
module UserControl
|
7
|
+
def self.logged_in?
|
8
|
+
Config.key?(:default_id)
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.log_in
|
12
|
+
loop do
|
13
|
+
permalink = HighLine.new.ask('Please enter your permalink (last word in your profile url): ')
|
14
|
+
client = Client.new
|
15
|
+
url = "https://soundcloud.com/#{permalink}"
|
16
|
+
if UserControl.exist?(url)
|
17
|
+
user = client.get('/resolve', url: url)
|
18
|
+
Config[:default_id] = user.id
|
19
|
+
Config[:permalink] = permalink
|
20
|
+
puts Paint['Successfully logged in!', :green]
|
21
|
+
break
|
22
|
+
else
|
23
|
+
puts Paint['Invalid permalink. Please enter correct permalink', :red]
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.default_id
|
29
|
+
if UserControl.logged_in?
|
30
|
+
Config[:default_id]
|
31
|
+
else
|
32
|
+
puts "You didn't logged in"
|
33
|
+
puts "Input #{Paint['nehm configure', :yellow]} to login"
|
34
|
+
exit
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.exist?(url)
|
39
|
+
Client.new.get('/resolve', url: url)
|
40
|
+
rescue SoundCloud::ResponseError => e
|
41
|
+
if e.message =~ /404/
|
42
|
+
raise e
|
43
|
+
else
|
44
|
+
false
|
45
|
+
end
|
46
|
+
else
|
47
|
+
true
|
48
|
+
end
|
49
|
+
end
|
data/lib/nehm/version.rb
ADDED
data/lib/nehm.rb
ADDED
@@ -0,0 +1,53 @@
|
|
1
|
+
require_relative 'nehm/os.rb'
|
2
|
+
require_relative 'nehm/track_utils.rb'
|
3
|
+
require_relative 'nehm/path_control.rb'
|
4
|
+
require_relative 'nehm/user_control.rb'
|
5
|
+
require_relative 'nehm/config.rb'
|
6
|
+
require_relative 'nehm/configure.rb'
|
7
|
+
require_relative 'nehm/help.rb'
|
8
|
+
module App
|
9
|
+
# Public
|
10
|
+
|
11
|
+
def self.do(args)
|
12
|
+
init unless initialized?
|
13
|
+
|
14
|
+
command = args.shift
|
15
|
+
case command
|
16
|
+
when 'get'
|
17
|
+
TrackUtils.get(:get, args)
|
18
|
+
when 'dl'
|
19
|
+
TrackUtils.get(:dl, args)
|
20
|
+
when 'configure'
|
21
|
+
Configure.menu
|
22
|
+
when 'login'
|
23
|
+
UserControl.log_in
|
24
|
+
when 'init'
|
25
|
+
init
|
26
|
+
when 'help'
|
27
|
+
Help.show(args.first)
|
28
|
+
else
|
29
|
+
puts Paint['Invalid command', :red]
|
30
|
+
puts "Input #{Paint['nehm help', :yellow]} for all avalaible commands"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
# Private
|
35
|
+
|
36
|
+
module_function
|
37
|
+
|
38
|
+
def init
|
39
|
+
puts Paint['Hello!', :green]
|
40
|
+
puts 'Before using the nehm, you should set it up:'
|
41
|
+
Config.create unless Config.exist?
|
42
|
+
PathControl.set_dl_path
|
43
|
+
puts "\n"
|
44
|
+
PathControl.set_itunes_path unless OS.linux?
|
45
|
+
puts "\n" unless OS.linux?
|
46
|
+
UserControl.log_in
|
47
|
+
puts Paint['Now you can use nehm :)', :green]
|
48
|
+
end
|
49
|
+
|
50
|
+
def initialized?
|
51
|
+
File.exist?(File.join(ENV['HOME'], '.nehmconfig'))
|
52
|
+
end
|
53
|
+
end
|
data/nehm.gemspec
ADDED
@@ -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 'nehm/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'nehm'
|
8
|
+
spec.version = Nehm::VERSION
|
9
|
+
spec.authors = ['Albert Nigmatzianov']
|
10
|
+
spec.email = ['albertnigma@gmail.com']
|
11
|
+
|
12
|
+
spec.summary = %q{Convenient way to get tracks from SoundCloud}
|
13
|
+
spec.description = %q{nehm is a console tool, which downloads, sets IDv3 tags and adds to your iTunes library your SoundCloud posts or likes (also by url) in convenient way}
|
14
|
+
spec.homepage = 'http://www.github.com/bogem/nehm'
|
15
|
+
spec.license = 'MIT'
|
16
|
+
|
17
|
+
spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
|
18
|
+
spec.bindir = 'bin'
|
19
|
+
spec.executables = 'nehm'
|
20
|
+
spec.require_paths = ['lib']
|
21
|
+
spec.required_ruby_version = '>= 1.9'
|
22
|
+
|
23
|
+
spec.add_development_dependency 'bundler', '~> 3.2', '>= 3.2.2'
|
24
|
+
spec.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
|
25
|
+
spec.add_dependency 'soundcloud', '~> 0.3', '>= 0.3.2'
|
26
|
+
spec.add_dependency 'taglib-ruby', '~> 0.7', '>= 0.7.0'
|
27
|
+
spec.add_dependency 'faraday', '~> 0.9', '>= 0.9.1'
|
28
|
+
spec.add_dependency 'highline', '~> 1.7', '>= 1.7.2'
|
29
|
+
end
|