vk_music 1.1.1 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +9 -7
- data/Rakefile +5 -0
- data/lib/vk_music.rb +3 -3
- data/lib/vk_music/audio.rb +143 -79
- data/lib/vk_music/client.rb +543 -250
- data/lib/vk_music/constants.rb +78 -33
- data/lib/vk_music/exceptions.rb +14 -17
- data/lib/vk_music/link_decoder.rb +9 -5
- data/lib/vk_music/playlist.rb +47 -36
- data/lib/vk_music/utility.rb +57 -19
- data/test/test_attached_audios_amount.rb +82 -0
- data/test/test_audios.rb +92 -0
- data/test/test_find.rb +43 -0
- data/test/test_from_id.rb +61 -0
- data/test/test_last_post_id.rb +46 -0
- data/test/test_login.rb +1 -1
- data/test/test_page_id.rb +113 -0
- data/test/test_playlist.rb +39 -10
- data/test/test_post.rb +21 -19
- data/test/test_wall.rb +56 -0
- data/vk_music.gemspec +4 -2
- metadata +31 -11
- data/test/test_audios_by_id.rb +0 -49
- data/test/test_get_id.rb +0 -113
- data/test/test_search.rb +0 -30
- data/test/test_user_or_group_audios.rb +0 -77
data/lib/vk_music/constants.rb
CHANGED
@@ -1,46 +1,91 @@
|
|
1
1
|
module VkMusic
|
2
2
|
|
3
|
-
|
3
|
+
##
|
4
|
+
# Constants.
|
4
5
|
module Constants
|
5
|
-
|
6
|
+
|
7
|
+
##
|
8
|
+
# Web user agent.
|
6
9
|
# DEFAULT_USER_AGENT = "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1636.0 Safari/537.36"
|
7
10
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
11
|
+
##
|
12
|
+
# Different URLs
|
13
|
+
module URL
|
14
|
+
|
15
|
+
##
|
16
|
+
# Hash with URLs to VK pages which are used by library.
|
17
|
+
VK = {
|
18
|
+
scheme: "https",
|
19
|
+
host: "m.vk.com",
|
20
|
+
home: "https://m.vk.com",
|
21
|
+
profile: "https://m.vk.com/id0",
|
22
|
+
feed: "https://m.vk.com/feed",
|
23
|
+
audios: "https://m.vk.com/audio",
|
24
|
+
login: "https://m.vk.com/login",
|
25
|
+
login_action: "https://login.vk.com",
|
26
|
+
wall: "https://m.vk.com/wall",
|
27
|
+
audio_unavailable: "https://m.vk.com/mp3/audio_api_unavailable.mp3"
|
28
|
+
}
|
29
|
+
|
30
|
+
end
|
31
|
+
|
32
|
+
##
|
33
|
+
# Regular expressions
|
34
|
+
module Regex
|
35
|
+
|
36
|
+
##
|
37
|
+
# VK user or group ID.
|
38
|
+
VK_ID_STR = /^-?\d+$/
|
39
|
+
|
40
|
+
##
|
41
|
+
# VK user or group ID.
|
42
|
+
VK_ID = /-?\d+/
|
43
|
+
|
44
|
+
##
|
45
|
+
# VK prefixed user or group ID.
|
46
|
+
VK_PREFIXED_ID_STR = /^(?:id|club|group|public|event)(\d+)$/
|
47
|
+
|
48
|
+
##
|
49
|
+
# VK custom ID regular expression.
|
50
|
+
VK_CUSTOM_ID = /^\w+$/
|
51
|
+
|
52
|
+
##
|
53
|
+
# VK URL regular expression.
|
54
|
+
VK_URL = /(?:https?:\/\/)?(?:m\.|www\.)?vk\.com\/([\w\-]+)/
|
55
|
+
|
56
|
+
##
|
57
|
+
# +href+ attribute with VK ID regular expression.
|
58
|
+
VK_HREF_ID_CONTAINING = /(?:audios|photo|write|owner_id=|friends\?id=)(-?\d+)/
|
59
|
+
|
60
|
+
##
|
61
|
+
# VK audios page regular expression.
|
62
|
+
VK_AUDIOS_URL_POSTFIX = /^audios(-?\d+)$/
|
63
|
+
|
64
|
+
##
|
65
|
+
# Playlist URL regular expression.
|
66
|
+
VK_PLAYLIST_URL_POSTFIX = /.*audio_playlist(-?\d+)_(\d+)(?:(?:(?:&access_hash=)|\/|%2F)([\da-z]+))?/
|
67
|
+
|
68
|
+
##
|
69
|
+
# Post URL regular expression #1.
|
70
|
+
VK_POST_URL_POSTFIX = /.*post(-?\d+)_(\d+)/
|
71
|
+
|
72
|
+
##
|
73
|
+
# Post URL regular expression #2.
|
74
|
+
VK_WALL_URL_POSTFIX = /.*wall(-?\d+)_(\d+)/
|
75
|
+
|
76
|
+
end
|
19
77
|
|
78
|
+
##
|
79
|
+
# Names used in VK login form.
|
20
80
|
VK_LOGIN_FORM_NAMES = {
|
21
|
-
:
|
22
|
-
:
|
81
|
+
username: "email",
|
82
|
+
password: "pass"
|
23
83
|
}
|
24
|
-
|
25
|
-
|
26
|
-
VK_ID_REGEX = /^-?\d+$/
|
27
|
-
VK_AUDIOS_REGEX = /^audios-?\d+$/
|
28
|
-
VK_PREFIXED_ID_REGEX = /^(?:id|club|group|public|event)\d+$/ # TODO: Rework. This one is REALLY dirty. Not quite sure every page can return correct id with this regex
|
29
|
-
VK_CUSTOM_ID_REGEX = /^\w+$/
|
30
|
-
VK_URL_REGEX = /(?:https?:\/\/)?(?:m\.|www\.)?vk\.com\/([\w\-]+)/
|
31
|
-
|
32
|
-
VK_HREF_ID_CONTAINING_REGEX = /(?:audios|photo|write|owner_id=|friends\?id=)-?\d+/
|
33
|
-
|
34
|
-
# Playlist
|
35
|
-
PLAYLIST_URL_REGEX = /.*audio_playlist(-?\d+)_(\d+)(?:(?:(?:&access_hash=)|\/|%2F)([\da-z]+))?/
|
36
84
|
|
37
|
-
|
38
|
-
|
85
|
+
##
|
86
|
+
# Maximum amount of audios in VK playlist.
|
87
|
+
MAXIMUM_PLAYLIST_SIZE = 10000
|
39
88
|
|
40
|
-
|
41
|
-
# QUESTION: Should I move ALL the constants (string, regex etc) here? It would make code more flexible, but seems like overkill
|
42
89
|
end
|
43
90
|
|
44
|
-
include Constants
|
45
|
-
|
46
91
|
end
|
data/lib/vk_music/exceptions.rb
CHANGED
@@ -1,33 +1,30 @@
|
|
1
1
|
module VkMusic
|
2
2
|
|
3
|
+
##
|
3
4
|
# Exceptions.
|
4
5
|
module Exceptions
|
6
|
+
|
7
|
+
##
|
5
8
|
# General class for all the errors.
|
6
9
|
class VkMusicError < RuntimeError; end
|
7
10
|
|
11
|
+
##
|
8
12
|
# Failed to login.
|
9
13
|
class LoginError < VkMusicError; end
|
10
|
-
|
11
|
-
# Unable to parse audios from somewhere.
|
12
|
-
class AudiosParseError < VkMusicError; end
|
13
14
|
|
14
|
-
|
15
|
-
|
15
|
+
##
|
16
|
+
# Failed to get request. _Only_ thron when Mechanize failed to load page
|
17
|
+
class RequestError < VkMusicError; end
|
16
18
|
|
17
|
-
|
18
|
-
|
19
|
+
##
|
20
|
+
# Parse error. Request is OK, but something went wrong while parsing reply.
|
21
|
+
# It might be missing playlist/post as well.
|
22
|
+
class ParseError < VkMusicError; end
|
19
23
|
|
20
|
-
|
21
|
-
|
24
|
+
##
|
25
|
+
# Unable to login.
|
26
|
+
class LoginError < VkMusicError; end
|
22
27
|
|
23
|
-
# Unable to convert string to id.
|
24
|
-
class IdParseError < AudiosParseError; end
|
25
|
-
|
26
|
-
# Unable to parse audios from wall.
|
27
|
-
class WallParseError < AudiosParseError; end
|
28
|
-
|
29
|
-
# Unable to parse audios from post.
|
30
|
-
class PostParseError < AudiosParseError; end
|
31
28
|
end
|
32
29
|
|
33
30
|
include Exceptions
|
@@ -2,9 +2,11 @@ require "execjs"
|
|
2
2
|
|
3
3
|
module VkMusic
|
4
4
|
|
5
|
+
##
|
5
6
|
# Module containing link decoding utilities.
|
6
7
|
module LinkDecoder
|
7
8
|
|
9
|
+
##
|
8
10
|
# JS code which creates function to unmask audio URL.
|
9
11
|
js_code = <<~HEREDOC
|
10
12
|
function vk_unmask_link(link, vk_id) {
|
@@ -87,17 +89,19 @@ module VkMusic
|
|
87
89
|
}
|
88
90
|
HEREDOC
|
89
91
|
|
92
|
+
##
|
93
|
+
# JS context with unmasking link.
|
90
94
|
@@js_context = ExecJS.compile(js_code)
|
91
95
|
|
96
|
+
##
|
92
97
|
# Unmask audio download URL.
|
93
98
|
#
|
94
|
-
#
|
95
|
-
#
|
96
|
-
# * [+client_id+] (+Integer+) - ID of user which got this link. ID is required for decoding.
|
99
|
+
# @param link [String] encoded link to audio. Usually looks like "https://m.vk.com/mp3/audio_api_unavailable.mp3?extra=...".
|
100
|
+
# @param client_id [Integer] ID of user which got this link. ID is required for decoding.
|
97
101
|
#
|
98
|
-
#
|
99
|
-
# * (+String+) - audio download URL, which can be used from current IP.
|
102
|
+
# @return [String] audio download URL, which can be used only from current IP.
|
100
103
|
def self.unmask_link(link, client_id)
|
104
|
+
Utility.debug("Unmasking link.")
|
101
105
|
@@js_context.call("vk_unmask_link", link.to_s, client_id.to_i)
|
102
106
|
end
|
103
107
|
|
data/lib/vk_music/playlist.rb
CHANGED
@@ -1,87 +1,98 @@
|
|
1
1
|
module VkMusic
|
2
2
|
|
3
|
-
|
3
|
+
##
|
4
|
+
# VK playlist.
|
4
5
|
class Playlist
|
5
6
|
include Enumerable
|
6
7
|
|
7
|
-
|
8
|
+
##
|
9
|
+
# @return [Integer, nil] playlist ID.
|
8
10
|
attr_reader :id
|
9
|
-
|
11
|
+
|
12
|
+
##
|
13
|
+
# @return [Integer, nil] owner of playlist ID.
|
10
14
|
attr_reader :owner_id
|
11
|
-
|
15
|
+
|
16
|
+
##
|
17
|
+
# @return [String, nil] access hash which should be part of link for some playlists.
|
12
18
|
attr_reader :access_hash
|
13
|
-
|
19
|
+
|
20
|
+
##
|
21
|
+
# @return [String] playlist title.
|
14
22
|
attr_reader :title
|
15
|
-
|
23
|
+
|
24
|
+
##
|
25
|
+
# @return [String, nil] playlist subtitle. May be empty.
|
16
26
|
attr_reader :subtitle
|
17
27
|
|
18
|
-
|
28
|
+
##
|
29
|
+
# @return [String] playlist description in Russian.
|
19
30
|
def to_s
|
20
|
-
(@subtitle
|
31
|
+
(@subtitle ? "#{@subtitle} - " : "") + "#{@title} (#{self.length} аудиозаписей)"
|
21
32
|
end
|
22
33
|
|
23
|
-
|
34
|
+
##
|
35
|
+
# @return [String] Same to {#to_s}, but also outputs list of audios.
|
24
36
|
def pp
|
25
|
-
"#{to_s}:\n#{@list.map(&:
|
37
|
+
"#{to_s}:\n#{@list.map(&:pp).join("\n")}"
|
26
38
|
end
|
27
39
|
|
28
|
-
|
40
|
+
##
|
41
|
+
# @return [Array<Audio>] Returns duplicate of array of playlist audios.
|
29
42
|
def to_a
|
30
43
|
@list.dup
|
31
44
|
end
|
32
45
|
|
33
|
-
|
46
|
+
##
|
47
|
+
# @see Array#each
|
34
48
|
def each(&block)
|
35
49
|
@list.each(&block)
|
36
50
|
end
|
37
51
|
|
38
|
-
|
52
|
+
##
|
53
|
+
# @see Array#length
|
39
54
|
def length
|
40
55
|
@list.length
|
41
56
|
end
|
42
57
|
alias size length
|
43
58
|
|
59
|
+
##
|
60
|
+
# @see Array#empty?
|
44
61
|
def empty?
|
45
62
|
@list.empty?
|
46
63
|
end
|
47
|
-
# :startdoc:
|
48
64
|
|
49
|
-
|
50
|
-
#
|
51
|
-
# ===== Parameters:
|
52
|
-
# * [+index+] (+Integer+) - index of audio (starting from 0).
|
65
|
+
##
|
66
|
+
# Access audios from playlist.
|
53
67
|
#
|
54
|
-
#
|
55
|
-
#
|
68
|
+
# @param index [Integer] index of audio (starting from 0).
|
69
|
+
#
|
70
|
+
# @return [Audio, nil] audio or +nil+ if out of range.
|
56
71
|
def [](index)
|
57
72
|
@list[index]
|
58
73
|
end
|
59
74
|
|
75
|
+
##
|
60
76
|
# Initialize new playlist.
|
61
77
|
#
|
62
|
-
#
|
63
|
-
# * [+list+] (+Array+) - list of audios in album.
|
64
|
-
# * [+options+] (+Hash+)
|
78
|
+
# @param list [Array] list of audios in playlist.
|
65
79
|
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
#
|
69
|
-
#
|
70
|
-
#
|
71
|
-
# * [+:subtitle+]
|
80
|
+
# @option options [Integer] :id
|
81
|
+
# @option options [Integer] :owner_id
|
82
|
+
# @option options [String] :access_hash
|
83
|
+
# @option options [String] :title
|
84
|
+
# @option options [String] :subtitle
|
72
85
|
def initialize(list, options = {})
|
73
|
-
|
74
|
-
raise ArgumentError, "array of audios must be provided", caller unless list.class == Array
|
75
|
-
|
86
|
+
raise ArgumentError, "Bad arguments", caller unless list.class == Array
|
76
87
|
# Saving list
|
77
88
|
@list = list.dup
|
78
89
|
|
79
90
|
# Setting up attributes
|
80
|
-
@id = options[:id]
|
81
|
-
@owner_id = options[:owner_id]
|
82
|
-
@access_hash = options[:access_hash]
|
91
|
+
@id = Utility.unless_nil_to Integer, options[:id]
|
92
|
+
@owner_id = Utility.unless_nil_to Integer, options[:owner_id]
|
93
|
+
@access_hash = Utility.unless_nil_to String, options[:access_hash]
|
83
94
|
@title = options[:title].to_s
|
84
|
-
@subtitle = options[:subtitle]
|
95
|
+
@subtitle = Utility.unless_nil_to String, options[:subtitle]
|
85
96
|
end
|
86
97
|
|
87
98
|
end
|
data/lib/vk_music/utility.rb
CHANGED
@@ -1,53 +1,55 @@
|
|
1
1
|
require "cgi"
|
2
|
+
require "logger"
|
2
3
|
|
3
4
|
module VkMusic
|
4
5
|
|
6
|
+
##
|
5
7
|
# Utility methods.
|
6
8
|
module Utility
|
7
9
|
|
10
|
+
##
|
8
11
|
# Turn amount of seconds into string.
|
9
12
|
#
|
10
|
-
#
|
11
|
-
# * [+s+] (+Integer+) - amount of seconds.
|
13
|
+
# @param s [Integer] amount of seconds.
|
12
14
|
#
|
13
|
-
#
|
14
|
-
# * (+String+) - formatted string.
|
15
|
+
# @return [String] formatted string.
|
15
16
|
def self.format_seconds(s)
|
16
17
|
s = s.to_i # Require integer
|
17
18
|
"#{(s / 60).to_s.rjust(2, "0")}:#{(s % 60).to_s.rjust(2, "0")}";
|
18
19
|
end
|
19
20
|
|
21
|
+
##
|
20
22
|
# Guess type of request by from string.
|
21
23
|
#
|
22
|
-
#
|
23
|
-
# * [+str+] (+String+) - request from user for some audios.
|
24
|
-
#
|
25
|
-
# ===== Returns:
|
24
|
+
# Possible results:
|
26
25
|
# * +:playlist+ - if string match playlist URL.
|
27
26
|
# * +:post+ - if string match post URL.
|
28
27
|
# * +:audios+ - if string match user or group URL.
|
29
28
|
# * +:find+ - in rest of cases.
|
29
|
+
#
|
30
|
+
# @param str [String] request from user for some audios.
|
31
|
+
#
|
32
|
+
# @return [Symbol]
|
30
33
|
def self.guess_request_type(str)
|
31
34
|
# Guess what type of request is this. Returns Symbol: :find, :playlist, :audios
|
32
35
|
case str
|
33
|
-
when Constants::
|
36
|
+
when Constants::Regex::VK_PLAYLIST_URL_POSTFIX
|
34
37
|
:playlist
|
35
|
-
when Constants::
|
38
|
+
when Constants::Regex::VK_WALL_URL_POSTFIX, Constants::Regex::VK_POST_URL_POSTFIX
|
36
39
|
:post
|
37
|
-
when Constants::
|
40
|
+
when Constants::Regex::VK_URL
|
38
41
|
:audios
|
39
42
|
else
|
40
43
|
:find
|
41
44
|
end
|
42
45
|
end
|
43
46
|
|
47
|
+
##
|
44
48
|
# Turn hash into URL query string.
|
45
49
|
#
|
46
|
-
#
|
47
|
-
# * [+hash+] (+Hash+)
|
50
|
+
# @param hash [Hash]
|
48
51
|
#
|
49
|
-
#
|
50
|
-
# * (+String+)
|
52
|
+
# @return [String]
|
51
53
|
def self.hash_to_params(hash = {})
|
52
54
|
qs = ""
|
53
55
|
hash.each_key do |key|
|
@@ -62,12 +64,48 @@ module VkMusic
|
|
62
64
|
qs
|
63
65
|
end
|
64
66
|
|
67
|
+
##
|
68
|
+
# Utility loggers
|
69
|
+
@@loggers = {
|
70
|
+
debug: Logger.new(STDOUT),
|
71
|
+
warn: Logger.new(STDERR)
|
72
|
+
}
|
73
|
+
@@loggers[:debug].level = Logger::DEBUG
|
74
|
+
@@loggers[:warn].level = Logger::WARN
|
75
|
+
|
76
|
+
##
|
65
77
|
# Send warning.
|
66
78
|
def self.warn(*args)
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
79
|
+
@@loggers[:warn].warn(args.join("\n"))
|
80
|
+
end
|
81
|
+
|
82
|
+
##
|
83
|
+
# Send debug message.
|
84
|
+
def self.debug(*args)
|
85
|
+
@@loggers[:debug].debug(args.join("\n")) if $DEBUG
|
86
|
+
end
|
87
|
+
|
88
|
+
##
|
89
|
+
# Function to turn values into given class unless nil provided
|
90
|
+
#
|
91
|
+
# Supported types:
|
92
|
+
# * +String+
|
93
|
+
# * +Integer+
|
94
|
+
#
|
95
|
+
# @param new_class [Class] class to transform to.
|
96
|
+
# @param obj [Object] object to check.
|
97
|
+
#
|
98
|
+
# @return object transformed to given class or +nil+ if object was +nil+ already.
|
99
|
+
def self.unless_nil_to(new_class, obj)
|
100
|
+
case
|
101
|
+
when obj.nil?
|
102
|
+
nil
|
103
|
+
when String <= new_class
|
104
|
+
obj.to_s
|
105
|
+
when Integer <= new_class
|
106
|
+
obj.to_i
|
107
|
+
else
|
108
|
+
raise ArgumentError, "Bad arguments", caller
|
71
109
|
end
|
72
110
|
end
|
73
111
|
|