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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 9487bed8735df5dec33b4295a2d70b28cec9d7f1
4
- data.tar.gz: 3320b8a52fcf60e408c0aa412ab5f9508d87d8f8
3
+ metadata.gz: d90ff47d1e518d8014179a9bb389fcae197c4e19
4
+ data.tar.gz: b4c5432cf4e3a91fe72ebb7ea1e2edd7d879234a
5
5
  SHA512:
6
- metadata.gz: 8da45f9d965ef76e98dc6e088354389d76c9bf47de021e3c740ecd16a4ac8aadc2810ff58d79775dff019c3d2ae87856e34acdef9a0b5107bac21f219b2af160
7
- data.tar.gz: c852c6fc59de8494da3d92917d3be15f15d1b447e97a4de9d80ceae21ef2e76e5c10611d40dfa53c0a43ec2d0756bf82d5ac58694127520378ac0c94299c23e7
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
- [ -app (or -app, --app, -application, --application) your_standalone_app_id (default: 5377636) ]
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
  ```
@@ -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.integer 'app', '-app', '--app', '-application', '-application', default: 5377636 # author's application (you can use it)
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[:app]).perform
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
- AUTHORIZE_API_PATH = 'https://oauth.vk.com/authorize'
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(app_id)
14
- @app_id = app_id
7
+ def initialize(auth_key = nil)
8
+ @auth_key = auth_key
15
9
  end
16
10
 
17
- def perform
18
- get_auth_token
19
- end
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
- attr_reader :app_id
15
+ req = Net::HTTP::Get.new(uri)
16
+ res = Net::HTTP.new(uri.host, uri.port).request(req)
24
17
 
25
- def query_params
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 build_uri
30
- uri = URI(AUTHORIZE_API_PATH)
31
- uri.query = URI.encode_www_form(query_params)
32
- uri
21
+ def perform
22
+ get_auth_token
33
23
  end
34
24
 
35
- def get_auth_params_from_browser_bar(uri)
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
- def save_auth_params_to_file
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(Time.new + auth_params['expires_in'].first.to_i)
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
- auth_file = File.open(AUTH_FILE_PATH, 'r')
39
+ begin
40
+ auth_file = File.open(AUTH_FILE_PATH, 'r')
58
41
 
59
- expires_in = auth_file.readline
60
- access_token = auth_file.readline.chomp
61
- auth_file.close
42
+ auth_key = auth_file.readline.chomp
43
+ auth_file.close
62
44
 
63
- access_token if Time.parse(expires_in) > Time.new
45
+ auth_key
46
+ rescue
47
+ nil
48
+ end
64
49
  end
65
50
 
66
51
  def get_auth_token
67
- begin
68
- auth_token = get_auth_params_from_file
69
- unless auth_token.nil?
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
- raise 'The validity of the token has expired'
59
+ abort('Invalid authentication key')
73
60
  end
74
- rescue
75
- save_auth_params_to_file
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
- API_AUDIO_METHOD_PATHS = {
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
- v: '5.53'
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
- def initialize(auth_token, opts)
13
- @auth_token = auth_token
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
- raw_playlist = get_raw_playlist
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 :auth_token, :opts
31
+ attr_reader :opts, :auth_key
30
32
 
31
- def query_params
32
- merged_query_params = QUERY_PARAMS.merge(access_token: auth_token)
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[:count] = 300 if opts[:query]
36
- # In other cases, do not set the number of audios, because bug: https://new.vk.com/bugs?act=show&id=5502832_1
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 audio_method_path
50
+ def audio_method_params
42
51
  if opts[:id]
43
- API_AUDIO_METHOD_PATHS[:get]
52
+ QUERY_PARAMS[:by_owner]
44
53
  elsif opts[:query]
45
- API_AUDIO_METHOD_PATHS[:search]
54
+ QUERY_PARAMS[:search]
46
55
  end
47
56
  end
48
57
 
49
- def build_uri
50
- uri = URI(audio_method_path)
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
- http = Net::HTTP.new(uri.host, uri.port)
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 get_raw_playlist
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
- JSON.parse(res.body)
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 get_milled_playlist(raw_playlist)
71
- songs = raw_playlist['response']['items']
72
- songs_count = opts[:count] || songs.count
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
- downloads_count = 0
88
+ filtered_songs_ids_list.each_slice(SONGS_LIMIT_REQUEST) do |ids|
89
+ query_params = build_query_params_for_urls(ids)
81
90
 
82
- playlist.each do |song|
83
- song_url = URI.parse(song['url'])
84
- file_name = "#{song['artist']} - #{song['title']}".slice(0, 100).gsub(/[\x00\/\\:\*\?\"<>\|]/, '_') + '.mp3'
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
- puts "-----> Downloaded #{downloads_count} songs in #{opts[:folder]}"
99
- puts "-----> The other #{playlist.count - downloads_count} songs have been already downloaded" if
100
- playlist.count - downloads_count > 0
136
+ count
137
+ end
101
138
  end
102
139
  end
103
140
  end
@@ -1,3 +1,3 @@
1
1
  module VkMusicLoader
2
- VERSION = '0.2.0'
2
+ VERSION = '0.3.1'
3
3
  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.2.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: 2016-08-15 00:00:00.000000000 Z
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