vk_music 3.1.3 → 4.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.env.example +3 -0
- data/.gitignore +6 -0
- data/.rspec +1 -0
- data/.rubocop.yml +56 -0
- data/Gemfile +38 -10
- data/Gemfile.lock +78 -24
- data/LICENSE.txt +0 -0
- data/README.md +111 -94
- data/Rakefile +12 -22
- data/bin/console +18 -24
- data/lib/vk_music.rb +32 -18
- data/lib/vk_music/audio.rb +111 -187
- data/lib/vk_music/client.rb +187 -615
- data/lib/vk_music/playlist.rb +44 -97
- data/lib/vk_music/request.rb +13 -0
- data/lib/vk_music/request/audios.rb +29 -0
- data/lib/vk_music/request/base.rb +75 -0
- data/lib/vk_music/request/login.rb +35 -0
- data/lib/vk_music/request/my_page.rb +21 -0
- data/lib/vk_music/request/playlist.rb +31 -0
- data/lib/vk_music/request/playlist_section.rb +35 -0
- data/lib/vk_music/request/post.rb +22 -0
- data/lib/vk_music/request/profile.rb +24 -0
- data/lib/vk_music/request/search.rb +34 -0
- data/lib/vk_music/request/wall_section.rb +34 -0
- data/lib/vk_music/utility.rb +8 -78
- data/lib/vk_music/utility/audio_data_parser.rb +37 -0
- data/lib/vk_music/utility/audio_items_parser.rb +18 -0
- data/lib/vk_music/utility/audio_node_parser.rb +59 -0
- data/lib/vk_music/utility/audios_from_ids_loader.rb +21 -0
- data/lib/vk_music/utility/audios_ids_getter.rb +25 -0
- data/lib/vk_music/utility/audios_loader.rb +37 -0
- data/lib/vk_music/utility/data_type_guesser.rb +43 -0
- data/lib/vk_music/utility/duration_parser.rb +17 -0
- data/lib/vk_music/utility/last_profile_post_loader.rb +26 -0
- data/lib/vk_music/utility/link_decoder.rb +106 -0
- data/lib/vk_music/utility/node_text_children_reader.rb +14 -0
- data/lib/vk_music/utility/playlist_loader.rb +30 -0
- data/lib/vk_music/utility/playlist_node_parser.rb +21 -0
- data/lib/vk_music/utility/playlist_section_loader.rb +29 -0
- data/lib/vk_music/utility/playlist_url_parser.rb +32 -0
- data/lib/vk_music/utility/post_loader.rb +23 -0
- data/lib/vk_music/utility/post_url_parser.rb +24 -0
- data/lib/vk_music/utility/profile_id_resolver.rb +51 -0
- data/lib/vk_music/utility/wall_loader.rb +25 -0
- data/lib/vk_music/version.rb +8 -5
- data/lib/vk_music/web_parser.rb +9 -0
- data/lib/vk_music/web_parser/audios.rb +20 -0
- data/lib/vk_music/web_parser/base.rb +27 -0
- data/lib/vk_music/web_parser/login.rb +13 -0
- data/lib/vk_music/web_parser/my_page.rb +19 -0
- data/lib/vk_music/web_parser/playlist.rb +33 -0
- data/lib/vk_music/web_parser/playlist_section.rb +53 -0
- data/lib/vk_music/web_parser/post.rb +15 -0
- data/lib/vk_music/web_parser/profile.rb +33 -0
- data/lib/vk_music/web_parser/search.rb +56 -0
- data/lib/vk_music/web_parser/wall_section.rb +53 -0
- data/vk_music.gemspec +36 -40
- metadata +58 -77
- data/.travis.yml +0 -7
- data/bin/setup +0 -8
- data/lib/vk_music/constants.rb +0 -78
- data/lib/vk_music/exceptions.rb +0 -21
- data/lib/vk_music/link_decoder.rb +0 -102
- data/lib/vk_music/utility/log.rb +0 -51
data/lib/vk_music/playlist.rb
CHANGED
@@ -1,97 +1,44 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# @return [Integer, nil] owner
|
12
|
-
attr_reader :owner_id
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
# @return [String] playlist
|
18
|
-
attr_reader :
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
#
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
# @
|
32
|
-
def
|
33
|
-
|
34
|
-
@
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
def to_a
|
46
|
-
@list.dup
|
47
|
-
end
|
48
|
-
|
49
|
-
##
|
50
|
-
# @!visibility private
|
51
|
-
def each(&block)
|
52
|
-
@list.each(&block)
|
53
|
-
end
|
54
|
-
##
|
55
|
-
# @return [Integer] amount of audios. This can be less than real size as not all audios might be loaded.
|
56
|
-
def length
|
57
|
-
@list.length
|
58
|
-
end
|
59
|
-
alias size length
|
60
|
-
##
|
61
|
-
# Access audios from playlist.
|
62
|
-
# @param index [Integer] index of audio (starting from 0).
|
63
|
-
# @return [Audio, nil] audio or +nil+ if out of range.
|
64
|
-
def [](index)
|
65
|
-
@list[index]
|
66
|
-
end
|
67
|
-
##
|
68
|
-
# @return [Boolean] whether no audios loaded for this playlist.
|
69
|
-
def empty?
|
70
|
-
@list.empty?
|
71
|
-
end
|
72
|
-
|
73
|
-
##
|
74
|
-
# Initialize new playlist.
|
75
|
-
#
|
76
|
-
# @param list [Array] list of audios in playlist.
|
77
|
-
# @param id [Integer, nil]
|
78
|
-
# @param owner_id [Integer, nil]
|
79
|
-
# @param access_hash [String, nil]
|
80
|
-
# @param title [String]
|
81
|
-
# @param subtitle [String, nil]
|
82
|
-
# @param real_size [Integer, nil]
|
83
|
-
def initialize(list, id: nil, owner_id: nil, access_hash: nil, title: "", subtitle: nil, real_size: nil)
|
84
|
-
raise ArgumentError unless list.is_a?(Array)
|
85
|
-
# Saving list
|
86
|
-
@list = list.dup
|
87
|
-
|
88
|
-
# Setting up attributes
|
89
|
-
@id = id
|
90
|
-
@owner_id = owner_id
|
91
|
-
@access_hash = access_hash
|
92
|
-
@title = title
|
93
|
-
@subtitle = subtitle
|
94
|
-
@real_size = real_size
|
95
|
-
end
|
96
|
-
end
|
97
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
# Class representing VK playlist
|
5
|
+
class Playlist
|
6
|
+
extend Forwardable
|
7
|
+
include Enumerable
|
8
|
+
|
9
|
+
# @return [Integer, nil] playlist ID
|
10
|
+
attr_reader :id
|
11
|
+
# @return [Integer, nil] playlist owner ID
|
12
|
+
attr_reader :owner_id
|
13
|
+
# @return [String, nil] access hash which should be part of link for some playlists
|
14
|
+
attr_reader :access_hash
|
15
|
+
# @return [String] playlist title
|
16
|
+
attr_reader :title
|
17
|
+
# @return [String, nil] playlist subtitle. May be empty
|
18
|
+
attr_reader :subtitle
|
19
|
+
# @return [Integer, nil] real size of playlist or +nil+ if unknown
|
20
|
+
attr_reader :real_size
|
21
|
+
# @return [Array] audios array
|
22
|
+
attr_reader :audios
|
23
|
+
|
24
|
+
# Initialize new playlist
|
25
|
+
# @param audios [Array] list of audios in playlist
|
26
|
+
# @param id [Integer, nil]
|
27
|
+
# @param owner_id [Integer, nil]
|
28
|
+
# @param access_hash [String, nil]
|
29
|
+
# @param title [String]
|
30
|
+
# @param subtitle [String, nil]
|
31
|
+
# @param real_size [Integer, nil]
|
32
|
+
def initialize(audios, id: nil, owner_id: nil, access_hash: nil, title: '', subtitle: nil, real_size: nil)
|
33
|
+
@audios = audios.dup
|
34
|
+
@id = id
|
35
|
+
@owner_id = owner_id
|
36
|
+
@access_hash = access_hash
|
37
|
+
@title = title.to_s.strip
|
38
|
+
@subtitle = subtitle
|
39
|
+
@real_size = real_size
|
40
|
+
end
|
41
|
+
|
42
|
+
def_delegators :@audios, :each, :length, :size, :[], :empty?
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
# Bunch of different web requests
|
5
|
+
module Request
|
6
|
+
# VK root URL
|
7
|
+
VK_ROOT = 'https://m.vk.com'
|
8
|
+
public_constant :VK_ROOT
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
require_relative 'request/base'
|
13
|
+
Dir[File.join(__dir__, 'request', '*.rb')].each { |file| require_relative file }
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# Audios reload
|
6
|
+
class AudiosReload < Base
|
7
|
+
# Initialize new request
|
8
|
+
# @param ids [Array<String>]
|
9
|
+
# @param client_id [Integer]
|
10
|
+
def initialize(ids, client_id)
|
11
|
+
@client_id = client_id
|
12
|
+
super(
|
13
|
+
"#{VK_ROOT}/audio",
|
14
|
+
{ act: 'reload_audio', ids: ids.join(','), utf8: true },
|
15
|
+
'GET',
|
16
|
+
{}
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def_delegators :@parser, :audios
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def after_call
|
25
|
+
@parser = WebParser::AudiosReload.new(@response.body, client_id: @client_id)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# Base class for most of requests
|
6
|
+
class Base
|
7
|
+
extend Forwardable
|
8
|
+
|
9
|
+
# @return [String]
|
10
|
+
attr_reader :path
|
11
|
+
# @return [Hash]
|
12
|
+
attr_reader :data
|
13
|
+
# @return [String]
|
14
|
+
attr_reader :method
|
15
|
+
# @return [Hash]
|
16
|
+
attr_reader :headers
|
17
|
+
# @return [Mechanize::File?]
|
18
|
+
attr_reader :response
|
19
|
+
|
20
|
+
# Initialize new request
|
21
|
+
# @param path [String]
|
22
|
+
# @param data [Hash]
|
23
|
+
# @param method [String]
|
24
|
+
# @param headers [Hash]
|
25
|
+
def initialize(path, data = {}, method = 'GET', headers = {})
|
26
|
+
@path = path
|
27
|
+
@data = data
|
28
|
+
@method = method.upcase
|
29
|
+
@headers = headers
|
30
|
+
|
31
|
+
@response = nil
|
32
|
+
end
|
33
|
+
|
34
|
+
# @param agent [Mechanize]
|
35
|
+
# @return [self]
|
36
|
+
def call(agent)
|
37
|
+
before_call
|
38
|
+
|
39
|
+
log
|
40
|
+
@response = case method
|
41
|
+
when 'GET' then get(agent)
|
42
|
+
when 'POST' then post(agent)
|
43
|
+
else raise(ArgumentError, "unsupported method #{method}")
|
44
|
+
end
|
45
|
+
|
46
|
+
after_call
|
47
|
+
|
48
|
+
self
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def log
|
54
|
+
VkMusic.log.debug('request') do
|
55
|
+
"#{method} to '#{path}', with data: #{data}, headers: #{headers}"
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def get(agent)
|
60
|
+
uri = URI(path)
|
61
|
+
uri.query = URI.encode_www_form(data)
|
62
|
+
agent.get(uri, [], nil, headers)
|
63
|
+
end
|
64
|
+
|
65
|
+
def post(agent)
|
66
|
+
uri = URI(path)
|
67
|
+
agent.post(uri, data, headers)
|
68
|
+
end
|
69
|
+
|
70
|
+
def before_call; end
|
71
|
+
|
72
|
+
def after_call; end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# Logging in request
|
6
|
+
class Login < Base
|
7
|
+
# Initialize new request
|
8
|
+
def initialize
|
9
|
+
super("#{VK_ROOT}/login", {}, 'GET', {})
|
10
|
+
@success = false
|
11
|
+
end
|
12
|
+
|
13
|
+
# @return [Boolean]
|
14
|
+
def success?
|
15
|
+
!!@success
|
16
|
+
end
|
17
|
+
|
18
|
+
# Send login form
|
19
|
+
def send_form(login, password, agent)
|
20
|
+
form = @parser.login_form
|
21
|
+
form['email'] = login
|
22
|
+
form['pass'] = password
|
23
|
+
page = agent.submit(form)
|
24
|
+
|
25
|
+
@success = (page.uri.to_s == 'https://m.vk.com/feed')
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def after_call
|
31
|
+
@parser = WebParser::Login.new(@response)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# Logging in request
|
6
|
+
class MyPage < Base
|
7
|
+
# Initialize new request
|
8
|
+
def initialize
|
9
|
+
super("#{VK_ROOT}/id0", {}, 'GET', {})
|
10
|
+
end
|
11
|
+
|
12
|
+
def_delegators :@parser, :id, :name
|
13
|
+
|
14
|
+
private
|
15
|
+
|
16
|
+
def after_call
|
17
|
+
@parser = WebParser::MyPage.new(@response)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# Playlist in web-mobile request
|
6
|
+
class Playlist < Base
|
7
|
+
# Initialize new request
|
8
|
+
# @param owner_id [Integer]
|
9
|
+
# @param playlist_id [Integer]
|
10
|
+
# @param access_hash [String, nil]
|
11
|
+
# @param client_id [Integer]
|
12
|
+
def initialize(owner_id, playlist_id, access_hash, client_id)
|
13
|
+
@client_id = client_id
|
14
|
+
super(
|
15
|
+
"#{VK_ROOT}/audio",
|
16
|
+
{ act: "audio_playlist#{owner_id}_#{playlist_id}", access_hash: access_hash },
|
17
|
+
'GET',
|
18
|
+
{}
|
19
|
+
)
|
20
|
+
end
|
21
|
+
|
22
|
+
def_delegators :@parser, :audios, :title, :subtitle, :real_size
|
23
|
+
|
24
|
+
private
|
25
|
+
|
26
|
+
def after_call
|
27
|
+
@parser = WebParser::Playlist.new(@response.body)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# Playlist in web-mobile request
|
6
|
+
class PlaylistSection < Base
|
7
|
+
# Initialize new request
|
8
|
+
# @param owner_id [Integer]
|
9
|
+
# @param playlist_id [Integer]
|
10
|
+
# @param access_hash [String, nil]
|
11
|
+
# @param offset [Integer]
|
12
|
+
# @param client_id [Integer]
|
13
|
+
def initialize(owner_id, playlist_id, access_hash, offset, client_id)
|
14
|
+
@client_id = client_id
|
15
|
+
super(
|
16
|
+
"#{VK_ROOT}/audio",
|
17
|
+
{
|
18
|
+
act: 'load_section', type: 'playlist', offset: offset, utf8: true,
|
19
|
+
owner_id: owner_id, playlist_id: playlist_id, access_hash: access_hash.to_s
|
20
|
+
},
|
21
|
+
'GET',
|
22
|
+
{}
|
23
|
+
)
|
24
|
+
end
|
25
|
+
|
26
|
+
def_delegators :@parser, :audios, :title, :subtitle, :real_size, :more?
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def after_call
|
31
|
+
@parser = WebParser::PlaylistSection.new(@response, client_id: @client_id)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# Post page
|
6
|
+
class Post < Base
|
7
|
+
# Initialize new request
|
8
|
+
def initialize(owner_id, post_id, client_id)
|
9
|
+
@client_id = client_id
|
10
|
+
super("#{VK_ROOT}/wall#{owner_id}_#{post_id}", {}, 'GET', {})
|
11
|
+
end
|
12
|
+
|
13
|
+
def_delegators :@parser, :audios
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def after_call
|
18
|
+
@parser = WebParser::Post.new(@response, client_id: @client_id)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# User or group page
|
6
|
+
class Profile < Base
|
7
|
+
# Initialize new request
|
8
|
+
def initialize(profile_id: nil, profile_custom_path: nil)
|
9
|
+
profile_path =
|
10
|
+
profile_custom_path ||
|
11
|
+
"#{profile_id.negative? ? 'club' : 'id'}#{profile_id.abs}"
|
12
|
+
super("#{VK_ROOT}/#{profile_path}", {}, 'GET', {})
|
13
|
+
end
|
14
|
+
|
15
|
+
def_delegators :@parser, :id, :last_post_id
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def after_call
|
20
|
+
@parser = WebParser::Profile.new(@response)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module VkMusic
|
4
|
+
module Request
|
5
|
+
# Logging in request
|
6
|
+
class Search < Base
|
7
|
+
# Initialize new request
|
8
|
+
# @param query [String]
|
9
|
+
# @param client_id [Integer]
|
10
|
+
def initialize(query, client_id)
|
11
|
+
@client_id = client_id
|
12
|
+
super(
|
13
|
+
"#{VK_ROOT}/audio",
|
14
|
+
{ q: query, _ajax: 1 },
|
15
|
+
'POST',
|
16
|
+
{ 'content-type' => 'application/x-www-form-urlencoded', 'x-requested-with' => 'XMLHttpRequest' }
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
def_delegators :@parser, :audios, :audios_all_path, :playlists, :playlists_all_path
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def after_call
|
25
|
+
json = JSON.parse(@response.body.strip)
|
26
|
+
raise 'Captcha requested' if json['key'] == 'captcha_key'
|
27
|
+
|
28
|
+
inner = json['data'][2]
|
29
|
+
html = Nokogiri::HTML.fragment(CGI.unescapeElement(inner))
|
30
|
+
@parser = WebParser::Search.new(html)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|