vk_music_loader 0.2.0 → 0.3.1
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 +4 -4
- data/README.md +11 -4
- data/lib/vk_music_loader.rb +2 -3
- data/lib/vk_music_loader/authorizer.rb +31 -45
- data/lib/vk_music_loader/songs_downloader.rb +84 -47
- data/lib/vk_music_loader/version.rb +1 -1
- metadata +2 -18
- data/lib/vk_music_loader/trash.rb +0 -32
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: d90ff47d1e518d8014179a9bb389fcae197c4e19
|
4
|
+
data.tar.gz: b4c5432cf4e3a91fe72ebb7ea1e2edd7d879234a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d2651974018eae6dc81846e626bc4053acb0ec3003bf7948c4a8611a863e36f6201a54ca6c6b5e180fba7ac9e79bc420bf96513de34072047dc0687b99fe3ce1
|
7
|
+
data.tar.gz: bed398b138a2e19a3b563110ed52f419193fb06a2a9b19ccdeb4c598bd687620db84ad2ce615e8c50bdcc7dae4ed7cd307673dc1111d96e7f263d9d4d8820657
|
data/README.md
CHANGED
@@ -2,7 +2,8 @@
|
|
2
2
|
|
3
3
|
<a href="https://vk.com"><img src="https://cdn2.iconfinder.com/data/icons/vkontakte-3/154/api-vk-vkontakte-programming-128.png" align="left" hspace="10" vspace="6" width="50" height="50"></a>
|
4
4
|
|
5
|
-
**VK Music Loader** is a simple CLI Ruby gem to download music from VK (ВКонта́кте) using easy and convenient way of authorization - [Implicit Flow](https://new.vk.com/dev/implicit_flow_user)
|
5
|
+
**VK Music Loader** is a simple CLI Ruby gem to download music from VK (ВКонта́кте) ~~using easy and convenient way of authorization - [Implicit Flow](https://new.vk.com/dev/implicit_flow_user)~~
|
6
|
+
using [mirror of VK Audio API](http://api.xn--41a.ws/)
|
6
7
|
This gem aimed at Ruby 2.0 or later.
|
7
8
|
|
8
9
|
## Installation
|
@@ -13,13 +14,19 @@ $ gem install vk_music_loader
|
|
13
14
|
|
14
15
|
## Usage
|
15
16
|
|
16
|
-
To login you will need [create Standalone Application](https://new.vk.com/editapp?act=create) or use the author's application (default APP ID: `5377636`)
|
17
|
+
~~To login you will need [create Standalone Application](https://new.vk.com/editapp?act=create) or use the author's application (default APP ID: `5377636`)~~
|
18
|
+
From December 16, 2016, the public API for working with audio files will be disabled.
|
19
|
+
Existing methods for the audio section will be unavailable for calling, except for methods regarding audio file uploads.
|
20
|
+
([source](source))
|
21
|
+
|
22
|
+
So you need to use a [mirror of VK Audio API](http://api.xn--41a.ws/) and buy an authentication key: http://api.xn--41a.ws/addmoney/.
|
23
|
+
I think 1$ is enough for a long time (100k requests) for personal use or also you can write to me to share my key.
|
17
24
|
|
18
25
|
```sh
|
19
26
|
$ vk_music_loader -id USER_OR_GROUP_ID
|
20
27
|
$ vk_music_loader -q QUERY_SEARCH
|
21
|
-
[ -
|
22
|
-
[ -count (or count, -count, --count, c, -c, --c) count_of_songs ]
|
28
|
+
[ -key (or key, --key, -k) your_auth_key (need only enter the first time after it is saved) ]
|
29
|
+
[ -count (or count, -count, --count, c, -c, --c) count_of_songs (default: 300) ]
|
23
30
|
[ -folder (or -folder, --folder, path, -path, --path, -p) folder_path_to_download_music (default: 'music') ]
|
24
31
|
[ -random (or --random, shuffle, -shuffle, --shuffle, -r) shuffle_download_flag (default: false) ]
|
25
32
|
```
|
data/lib/vk_music_loader.rb
CHANGED
@@ -8,7 +8,6 @@ require 'net/http'
|
|
8
8
|
require 'openssl'
|
9
9
|
require 'json'
|
10
10
|
require 'slop'
|
11
|
-
require 'launchy'
|
12
11
|
|
13
12
|
require 'vk_music_loader/authorizer'
|
14
13
|
require 'vk_music_loader/songs_downloader'
|
@@ -17,7 +16,7 @@ module VkMusicLoader
|
|
17
16
|
def self.call
|
18
17
|
begin
|
19
18
|
opts = Slop.parse uppress_errors: true do |o|
|
20
|
-
o.
|
19
|
+
o.string 'key', '-key', '--key', '-k'
|
21
20
|
o.integer 'id', '-id', '--id', '-user-id', '--user-id', '-group-id', '--group-id'
|
22
21
|
o.string 'query', '-query', '--query', '-q', 'search', '-search', '--search'
|
23
22
|
o.integer 'count', '-count', '--count', 'c', '-c', '--c'
|
@@ -26,7 +25,7 @@ module VkMusicLoader
|
|
26
25
|
end
|
27
26
|
|
28
27
|
if opts[:id] || opts[:query]
|
29
|
-
auth_token = VkMusicLoader::Authorizer.new(opts[:
|
28
|
+
auth_token = VkMusicLoader::Authorizer.new(opts[:key]).perform
|
30
29
|
VkMusicLoader::SongsDownloader.new(auth_token, opts).perform
|
31
30
|
else
|
32
31
|
puts 'No user id or group id or query'
|
@@ -2,78 +2,64 @@ module VkMusicLoader
|
|
2
2
|
class Authorizer
|
3
3
|
AUTH_FILE_PATH = File.expand_path('~') + '/.vk_music_loader/auth_data'
|
4
4
|
|
5
|
-
|
6
|
-
QUERY_PARAMS = {
|
7
|
-
scope: 'audio',
|
8
|
-
redirect_uri: 'http://oauth.vk.com/blank.html',
|
9
|
-
display: 'page',
|
10
|
-
response_type: 'token'
|
11
|
-
}
|
5
|
+
API_CHECK_AUDIO_METHOD_PATH = 'http://api.xn--41a.ws/api.php'
|
12
6
|
|
13
|
-
def initialize(
|
14
|
-
@
|
7
|
+
def initialize(auth_key = nil)
|
8
|
+
@auth_key = auth_key
|
15
9
|
end
|
16
10
|
|
17
|
-
def
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
private
|
11
|
+
def self.auth_key_valid?(auth_key)
|
12
|
+
uri = URI(API_CHECK_AUDIO_METHOD_PATH)
|
13
|
+
uri.query = URI.encode_www_form(key: auth_key, method: :by_owner, owner_id: :dummy)
|
22
14
|
|
23
|
-
|
15
|
+
req = Net::HTTP::Get.new(uri)
|
16
|
+
res = Net::HTTP.new(uri.host, uri.port).request(req)
|
24
17
|
|
25
|
-
|
26
|
-
QUERY_PARAMS.merge(client_id: app_id)
|
18
|
+
res.body != 'wrong key' && res.body != 'key wrong format'
|
27
19
|
end
|
28
20
|
|
29
|
-
def
|
30
|
-
|
31
|
-
uri.query = URI.encode_www_form(query_params)
|
32
|
-
uri
|
21
|
+
def perform
|
22
|
+
get_auth_token
|
33
23
|
end
|
34
24
|
|
35
|
-
|
36
|
-
ARGV.clear
|
37
|
-
|
38
|
-
Launchy.open(uri)
|
39
|
-
puts 'Paste full URL from browser bar and press ENTER: '
|
40
|
-
CGI.parse(URI(gets.chomp.strip).fragment)
|
41
|
-
end
|
25
|
+
private
|
42
26
|
|
43
|
-
|
44
|
-
auth_params = get_auth_params_from_browser_bar(build_uri)
|
27
|
+
attr_reader :auth_key
|
45
28
|
|
29
|
+
def save_auth_params_to_file(auth_key)
|
46
30
|
dir_path = File.dirname(AUTH_FILE_PATH)
|
47
31
|
Dir.mkdir(dir_path) unless File.exists?(dir_path)
|
48
32
|
|
49
33
|
auth_file = File.open(AUTH_FILE_PATH, 'w')
|
50
|
-
auth_file.puts(
|
51
|
-
auth_file.puts(auth_params['access_token'].first)
|
52
|
-
auth_file.puts(auth_params['user_id'].first)
|
34
|
+
auth_file.puts(auth_key)
|
53
35
|
auth_file.close
|
54
36
|
end
|
55
37
|
|
56
38
|
def get_auth_params_from_file
|
57
|
-
|
39
|
+
begin
|
40
|
+
auth_file = File.open(AUTH_FILE_PATH, 'r')
|
58
41
|
|
59
|
-
|
60
|
-
|
61
|
-
auth_file.close
|
42
|
+
auth_key = auth_file.readline.chomp
|
43
|
+
auth_file.close
|
62
44
|
|
63
|
-
|
45
|
+
auth_key
|
46
|
+
rescue
|
47
|
+
nil
|
48
|
+
end
|
64
49
|
end
|
65
50
|
|
66
51
|
def get_auth_token
|
67
|
-
|
68
|
-
|
69
|
-
|
52
|
+
auth_token = auth_key || get_auth_params_from_file
|
53
|
+
|
54
|
+
unless auth_token.nil?
|
55
|
+
if Authorizer.auth_key_valid?(auth_token)
|
56
|
+
save_auth_params_to_file(auth_token)
|
70
57
|
auth_token
|
71
58
|
else
|
72
|
-
|
59
|
+
abort('Invalid authentication key')
|
73
60
|
end
|
74
|
-
|
75
|
-
|
76
|
-
retry
|
61
|
+
else
|
62
|
+
abort('No authentication key')
|
77
63
|
end
|
78
64
|
end
|
79
65
|
end
|
@@ -1,87 +1,126 @@
|
|
1
1
|
module VkMusicLoader
|
2
2
|
class SongsDownloader
|
3
|
-
|
4
|
-
get: 'https://api.vk.com/method/audio.get',
|
5
|
-
search: 'https://api.vk.com/method/audio.search'
|
6
|
-
}
|
3
|
+
API_AUDIO_METHOD_PATH = 'http://api.xn--41a.ws/api.php'
|
7
4
|
|
8
5
|
QUERY_PARAMS = {
|
9
|
-
|
6
|
+
by_owner: {
|
7
|
+
method: 'by_owner'
|
8
|
+
},
|
9
|
+
search: {
|
10
|
+
method: 'search'
|
11
|
+
},
|
12
|
+
get: {
|
13
|
+
method: 'get.audio'
|
14
|
+
}
|
10
15
|
}
|
11
16
|
|
12
|
-
|
13
|
-
|
17
|
+
SONGS_LIMIT = 300
|
18
|
+
SONGS_LIMIT_REQUEST = 10
|
19
|
+
|
20
|
+
def initialize(auth_key, opts)
|
21
|
+
@auth_key = auth_key
|
14
22
|
@opts = opts
|
15
23
|
end
|
16
24
|
|
17
25
|
def perform
|
18
|
-
|
19
|
-
|
20
|
-
if raw_playlist['error']
|
21
|
-
puts raw_playlist['error']['error_msg']
|
22
|
-
else
|
23
|
-
download_songs(get_milled_playlist(raw_playlist))
|
24
|
-
end
|
26
|
+
get_songs_urls_and_download(get_playlist)
|
25
27
|
end
|
26
28
|
|
27
29
|
private
|
28
30
|
|
29
|
-
attr_reader :
|
31
|
+
attr_reader :opts, :auth_key
|
30
32
|
|
31
|
-
def
|
32
|
-
merged_query_params =
|
33
|
+
def build_query_params_for_ids
|
34
|
+
merged_query_params = audio_method_params
|
33
35
|
merged_query_params[:owner_id] = opts[:id] unless opts[:query]
|
34
36
|
merged_query_params[:q] = opts[:query] unless opts[:id]
|
35
|
-
merged_query_params[:
|
36
|
-
|
37
|
+
merged_query_params[:key] = auth_key
|
38
|
+
|
39
|
+
merged_query_params
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_query_params_for_urls(songs_ids_list)
|
43
|
+
merged_query_params = QUERY_PARAMS[:get]
|
44
|
+
merged_query_params[:ids] = songs_ids_list.join(',')
|
45
|
+
merged_query_params[:key] = auth_key
|
37
46
|
|
38
47
|
merged_query_params
|
39
48
|
end
|
40
49
|
|
41
|
-
def
|
50
|
+
def audio_method_params
|
42
51
|
if opts[:id]
|
43
|
-
|
52
|
+
QUERY_PARAMS[:by_owner]
|
44
53
|
elsif opts[:query]
|
45
|
-
|
54
|
+
QUERY_PARAMS[:search]
|
46
55
|
end
|
47
56
|
end
|
48
57
|
|
49
|
-
def build_uri
|
50
|
-
uri = URI(
|
58
|
+
def build_uri(query_params)
|
59
|
+
uri = URI(API_AUDIO_METHOD_PATH)
|
51
60
|
uri.query = URI.encode_www_form(query_params)
|
52
61
|
uri
|
53
62
|
end
|
54
63
|
|
55
64
|
def build_http(uri)
|
56
|
-
|
57
|
-
http.use_ssl = true
|
58
|
-
http.verify_mode = OpenSSL::SSL::VERIFY_NONE
|
59
|
-
http
|
65
|
+
Net::HTTP.new(uri.host, uri.port)
|
60
66
|
end
|
61
67
|
|
62
|
-
def
|
63
|
-
uri = build_uri
|
68
|
+
def get_playlist
|
69
|
+
uri = build_uri(build_query_params_for_ids)
|
70
|
+
|
64
71
|
req = Net::HTTP::Get.new(uri)
|
65
72
|
res = build_http(uri).request(req)
|
66
73
|
|
67
|
-
|
74
|
+
begin
|
75
|
+
JSON.parse(res.body)
|
76
|
+
rescue
|
77
|
+
abort('Perhaps a user or group with such id does not exist or limited by privacy settings')
|
78
|
+
end
|
68
79
|
end
|
69
80
|
|
70
|
-
def
|
71
|
-
|
72
|
-
|
81
|
+
def get_songs_urls_and_download(raw_playlist)
|
82
|
+
songs_ids_list = raw_playlist['list'].map { |song_info| "#{song_info[1]}_#{song_info[0]}" }
|
83
|
+
filtered_songs_ids_list = filter_playlist(songs_ids_list)
|
73
84
|
|
74
|
-
opts[:random] ? songs.sample(songs_count) : songs.take(songs_count)
|
75
|
-
end
|
76
|
-
|
77
|
-
def download_songs(playlist)
|
78
85
|
Dir.mkdir(opts[:folder]) unless File.exists?(opts[:folder])
|
86
|
+
final_downloads_count = 0
|
79
87
|
|
80
|
-
|
88
|
+
filtered_songs_ids_list.each_slice(SONGS_LIMIT_REQUEST) do |ids|
|
89
|
+
query_params = build_query_params_for_urls(ids)
|
81
90
|
|
82
|
-
|
83
|
-
|
84
|
-
|
91
|
+
uri = build_uri(query_params)
|
92
|
+
req = Net::HTTP::Get.new(uri)
|
93
|
+
res = build_http(uri).request(req)
|
94
|
+
|
95
|
+
begin
|
96
|
+
final_downloads_count += download_songs(JSON.parse(res.body)
|
97
|
+
.map { |song_info| { url: song_info[2], title: song_info[3], artist: song_info[4] } })
|
98
|
+
rescue JSON::ParserError
|
99
|
+
puts 'Some songs download failed'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
puts "-----> Downloaded #{final_downloads_count} songs in #{opts[:folder]}"
|
104
|
+
puts "-----> The other #{filtered_songs_ids_list.count - final_downloads_count} songs have been already downloaded" if
|
105
|
+
filtered_songs_ids_list.count - final_downloads_count > 0
|
106
|
+
end
|
107
|
+
|
108
|
+
def filter_playlist(playlist)
|
109
|
+
if !opts[:random] && opts[:count]
|
110
|
+
playlist.take(opts[:count])
|
111
|
+
elsif opts[:random] && !opts[:count]
|
112
|
+
playlist.sample(SONGS_LIMIT)
|
113
|
+
elsif opts[:random] && opts[:count]
|
114
|
+
playlist.sample(opts[:count])
|
115
|
+
else
|
116
|
+
playlist
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
def download_songs(playlist)
|
121
|
+
playlist.inject(0) do |count, song|
|
122
|
+
song_url = URI.parse(song[:url])
|
123
|
+
file_name = "#{song[:artist]} - #{song[:title]}".slice(0, 100).gsub(/[\x00\/\\:\*\?\"<>\|]/, '_') + '.mp3'
|
85
124
|
file_path = "#{opts[:folder]}/#{file_name}"
|
86
125
|
|
87
126
|
unless File.file?(file_path)
|
@@ -89,15 +128,13 @@ module VkMusicLoader
|
|
89
128
|
f.write Net::HTTP.get(song_url)
|
90
129
|
f.close
|
91
130
|
|
92
|
-
downloads_count += 1
|
93
131
|
puts "Downloaded: #{file_name}"
|
132
|
+
count += 1
|
94
133
|
end
|
95
134
|
end
|
96
|
-
end
|
97
135
|
|
98
|
-
|
99
|
-
|
100
|
-
playlist.count - downloads_count > 0
|
136
|
+
count
|
137
|
+
end
|
101
138
|
end
|
102
139
|
end
|
103
140
|
end
|
metadata
CHANGED
@@ -1,29 +1,15 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: vk_music_loader
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anatoly Ryabov
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
|
-
- !ruby/object:Gem::Dependency
|
14
|
-
name: launchy
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
16
|
-
requirements:
|
17
|
-
- - "~>"
|
18
|
-
- !ruby/object:Gem::Version
|
19
|
-
version: '2.0'
|
20
|
-
type: :runtime
|
21
|
-
prerelease: false
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
23
|
-
requirements:
|
24
|
-
- - "~>"
|
25
|
-
- !ruby/object:Gem::Version
|
26
|
-
version: '2.0'
|
27
13
|
- !ruby/object:Gem::Dependency
|
28
14
|
name: slop
|
29
15
|
requirement: !ruby/object:Gem::Requirement
|
@@ -52,7 +38,6 @@ files:
|
|
52
38
|
- lib/vk_music_loader.rb
|
53
39
|
- lib/vk_music_loader/authorizer.rb
|
54
40
|
- lib/vk_music_loader/songs_downloader.rb
|
55
|
-
- lib/vk_music_loader/trash.rb
|
56
41
|
- lib/vk_music_loader/version.rb
|
57
42
|
homepage: https://github.com/m1neral/vk_music_loader
|
58
43
|
licenses:
|
@@ -79,4 +64,3 @@ signing_key:
|
|
79
64
|
specification_version: 4
|
80
65
|
summary: Simple gem to download VK user/group audio playlist.
|
81
66
|
test_files: []
|
82
|
-
has_rdoc:
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require 'rubygems'
|
2
|
-
require 'time'
|
3
|
-
require 'cgi'
|
4
|
-
require 'uri'
|
5
|
-
require 'net/http'
|
6
|
-
require 'openssl'
|
7
|
-
require 'json'
|
8
|
-
require 'slop'
|
9
|
-
require 'launchy'
|
10
|
-
|
11
|
-
require '../vk_music_loader/authorizer'
|
12
|
-
require '../vk_music_loader/songs_downloader'
|
13
|
-
|
14
|
-
begin
|
15
|
-
opts = Slop.parse uppress_errors: true do |o|
|
16
|
-
o.integer 'app', '-app', '--app', '-application', '-application', default: 5377636 # author's application (you can use it)
|
17
|
-
o.integer 'id', '-id', '--id', '-user-id', '--user-id', '-group-id', '--group-id'
|
18
|
-
o.string 'query', '-query', '--query', '-q', 'search', '-search', '--search'
|
19
|
-
o.integer 'count', '-count', '--count', 'c', '-c', '--c'
|
20
|
-
o.string 'folder', '-folder', '--folder', 'path', '-path', '--path', '-p', default: 'audio'
|
21
|
-
o.bool 'random', '-random', '--random', 'shuffle', '-shuffle', '--shuffle', '-r'
|
22
|
-
end
|
23
|
-
|
24
|
-
if opts[:id] || opts[:query]
|
25
|
-
auth_token = VkMusicLoader::Authorizer.new(opts[:app]).perform
|
26
|
-
VkMusicLoader::SongsDownloader.new(auth_token, opts).perform
|
27
|
-
else
|
28
|
-
puts 'No user id or group id or query'
|
29
|
-
end
|
30
|
-
rescue Slop::Error => e
|
31
|
-
puts e.message
|
32
|
-
end
|