vk_music 2.2.1 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,17 +1,14 @@
1
1
  module VkMusic
2
-
3
2
  ##
4
3
  # Constants.
5
4
  module Constants
6
-
7
5
  ##
8
- # Web user agent.
9
- DEFAULT_USER_AGENT = "Mozilla/5.0 (Linux; Android 5.1.1; Redmi 3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.132 Mobile Safari/537.36 OPR/54.2.2672.49907"
6
+ # Default web user agent.
7
+ DEFAULT_USER_AGENT = "" # Using empty user agent confuses VK and it returnes MP3
10
8
 
11
9
  ##
12
10
  # Different URLs
13
11
  module URL
14
-
15
12
  ##
16
13
  # Hash with URLs to VK pages which are used by library.
17
14
  VK = {
@@ -27,55 +24,43 @@ module VkMusic
27
24
  audio_unavailable: "https://m.vk.com/mp3/audio_api_unavailable.mp3",
28
25
  profile_audios: "https://m.vk.com/audios",
29
26
  }
30
-
31
27
  end
32
28
 
33
29
  ##
34
30
  # Regular expressions
35
31
  module Regex
36
-
37
32
  ##
38
33
  # VK user or group ID.
39
34
  VK_ID_STR = /^-?\d+$/
40
-
41
35
  ##
42
36
  # VK user or group ID.
43
37
  VK_ID = /-?\d+/
44
-
45
38
  ##
46
39
  # VK prefixed user or group ID.
47
40
  VK_PREFIXED_ID_STR = /^(?:id|club|group|public|event)(\d+)$/
48
-
49
41
  ##
50
42
  # VK custom ID regular expression.
51
43
  VK_CUSTOM_ID = /^\w+$/
52
-
53
44
  ##
54
45
  # VK URL regular expression.
55
46
  VK_URL = /(?:https?:\/\/)?(?:m\.|www\.)?vk\.com\/([\w\-]+)/
56
-
57
47
  ##
58
48
  # +href+ attribute with VK ID regular expression.
59
49
  VK_HREF_ID_CONTAINING = /(?:audios|photo|write|owner_id=|friends\?id=)(-?\d+)/
60
-
61
50
  ##
62
51
  # VK audios page regular expression.
63
52
  VK_AUDIOS_URL_POSTFIX = /^audios(-?\d+)$/
64
-
65
53
  ##
66
54
  # Playlist URL regular expression.
67
55
  VK_PLAYLIST_URL_POSTFIX = /.*(?:audio_playlist|album\/)(-?\d+)_(\d+)(?:(?:(?:.*(?=&access_hash=)&access_hash=)|\/|%2F|_)([\da-z]+))?/
68
-
69
56
  ##
70
57
  # Post URL regular expression #1.
71
58
  VK_POST_URL_POSTFIX = /.*post(-?\d+)_(\d+)/
72
-
73
59
  ##
74
60
  # Post URL regular expression #2.
75
61
  VK_WALL_URL_POSTFIX = /.*wall(-?\d+)_(\d+)/
76
-
77
62
  end
78
-
63
+
79
64
  ##
80
65
  # Names used in VK login form.
81
66
  VK_LOGIN_FORM_NAMES = {
@@ -86,7 +71,5 @@ module VkMusic
86
71
  ##
87
72
  # Maximum amount of audios in VK playlist.
88
73
  MAXIMUM_PLAYLIST_SIZE = 10000
89
-
90
74
  end
91
-
92
75
  end
@@ -1,32 +1,21 @@
1
1
  module VkMusic
2
-
3
2
  ##
4
3
  # Exceptions.
5
4
  module Exceptions
6
-
7
5
  ##
8
6
  # General class for all the errors.
9
7
  class VkMusicError < RuntimeError; end
10
-
11
8
  ##
12
9
  # Failed to login.
13
10
  class LoginError < VkMusicError; end
14
-
15
11
  ##
16
- # Failed to get request. _Only_ thron when Mechanize failed to load page
12
+ # Failed to get request. _Only_ thrown when Mechanize failed to load page
17
13
  class RequestError < VkMusicError; end
18
-
19
14
  ##
20
15
  # Parse error. Request is OK, but something went wrong while parsing reply.
21
16
  # It might be missing playlist/post as well.
22
17
  class ParseError < VkMusicError; end
23
-
24
- ##
25
- # Unable to login.
26
- class LoginError < VkMusicError; end
27
-
28
18
  end
29
19
 
30
20
  include Exceptions
31
-
32
21
  end
@@ -1,9 +1,7 @@
1
1
  module VkMusic
2
-
3
2
  ##
4
3
  # Module containing link decoding utilities.
5
4
  module LinkDecoder
6
-
7
5
  ##
8
6
  # JS code which creates function to unmask audio URL.
9
7
  js_code = <<~HEREDOC
@@ -90,19 +88,15 @@ module VkMusic
90
88
  ##
91
89
  # JS context with unmasking link.
92
90
  @@js_context = ExecJS.compile(js_code)
93
-
91
+
94
92
  ##
95
93
  # Unmask audio download URL.
96
- #
97
94
  # @param link [String] encoded link to audio. Usually looks like "https://m.vk.​com/mp3/audio_api_unavailable.mp3?extra=...".
98
95
  # @param client_id [Integer] ID of user which got this link. ID is required for decoding.
99
- #
100
96
  # @return [String] audio download URL, which can be used only from current IP.
101
97
  def self.unmask_link(link, client_id)
102
- Utility.debug("Unmasking link.")
98
+ VkMusic.debug("Unmasking link.")
103
99
  @@js_context.call("vk_unmask_link", link.to_s, client_id.to_i)
104
100
  end
105
-
106
101
  end
107
-
108
102
  end
@@ -1,34 +1,32 @@
1
1
  module VkMusic
2
-
3
2
  ##
4
3
  # VK playlist.
5
4
  class Playlist
6
5
  include Enumerable
7
-
6
+
8
7
  ##
9
8
  # @return [Integer, nil] playlist ID.
10
9
  attr_reader :id
11
-
12
10
  ##
13
11
  # @return [Integer, nil] owner of playlist ID.
14
12
  attr_reader :owner_id
15
-
16
13
  ##
17
14
  # @return [String, nil] access hash which should be part of link for some playlists.
18
15
  attr_reader :access_hash
19
-
20
16
  ##
21
17
  # @return [String] playlist title.
22
18
  attr_reader :title
23
-
24
19
  ##
25
20
  # @return [String, nil] playlist subtitle. May be empty.
26
21
  attr_reader :subtitle
27
-
28
22
  ##
29
23
  # @return [Integer, nil] real size of playlist or +nil+ if unknown.
30
24
  attr_reader :real_size
31
-
25
+
26
+ ##
27
+ # @!visibility private
28
+ attr_reader :list
29
+
32
30
  ##
33
31
  # @return [String] playlist description in Russian.
34
32
  def to_s
@@ -36,7 +34,6 @@ module VkMusic
36
34
  @title +
37
35
  (@real_size ? "(#{self.length} из #{@real_size} аудиозаписей загружено)" : " (#{self.length} аудиозаписей)")
38
36
  end
39
-
40
37
  ##
41
38
  # @return [String] Same to {#to_s}, but also outputs list of audios.
42
39
  def pp
@@ -48,61 +45,53 @@ module VkMusic
48
45
  def to_a
49
46
  @list.dup
50
47
  end
51
-
48
+
52
49
  ##
53
- # @see Array#each
50
+ # @!visibility private
54
51
  def each(&block)
55
52
  @list.each(&block)
56
53
  end
57
-
58
54
  ##
59
- # @see Array#length
55
+ # @return [Integer] amount of audios. This can be less than real size as not all audios might be loaded.
60
56
  def length
61
57
  @list.length
62
58
  end
63
59
  alias size length
64
-
65
- ##
66
- # @see Array#empty?
67
- def empty?
68
- @list.empty?
69
- end
70
-
71
60
  ##
72
61
  # Access audios from playlist.
73
- #
74
62
  # @param index [Integer] index of audio (starting from 0).
75
- #
76
63
  # @return [Audio, nil] audio or +nil+ if out of range.
77
64
  def [](index)
78
65
  @list[index]
79
66
  end
80
-
67
+ ##
68
+ # @return [Boolean] whether no audios loaded for this playlist.
69
+ def empty?
70
+ @list.empty?
71
+ end
72
+
81
73
  ##
82
74
  # Initialize new playlist.
83
75
  #
84
76
  # @param list [Array] list of audios in playlist.
85
- #
86
- # @option options [Integer, nil] :id
87
- # @option options [Integer, nil] :owner_id
88
- # @option options [String, nil] :access_hash
89
- # @option options [String] :title
90
- # @option options [String, nil] :subtitle
91
- # @option options [Integer, nil] :real_size
92
- def initialize(list, options = {})
93
- raise ArgumentError, "Bad arguments", caller unless list.class == Array
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)
94
85
  # Saving list
95
86
  @list = list.dup
96
-
87
+
97
88
  # Setting up attributes
98
- @id = Utility.unless_nil_to Integer, options[:id]
99
- @owner_id = Utility.unless_nil_to Integer, options[:owner_id]
100
- @access_hash = Utility.unless_nil_to String, options[:access_hash]
101
- @title = options[:title].to_s
102
- @subtitle = Utility.unless_nil_to String, options[:subtitle]
103
- @real_size = Utility.unless_nil_to Integer, options[:real_size]
89
+ @id = id
90
+ @owner_id = owner_id
91
+ @access_hash = access_hash
92
+ @title = title
93
+ @subtitle = subtitle
94
+ @real_size = real_size
104
95
  end
105
-
106
96
  end
107
-
108
97
  end
@@ -1,20 +1,18 @@
1
- module VkMusic
1
+ require_relative "utility/log"
2
2
 
3
+ module VkMusic
3
4
  ##
4
5
  # Utility methods.
5
6
  module Utility
6
-
7
7
  ##
8
8
  # Turn amount of seconds into string.
9
- #
10
9
  # @param s [Integer] amount of seconds.
11
- #
12
10
  # @return [String] formatted string.
13
11
  def self.format_seconds(s)
14
- s = s.to_i # Require integer
12
+ s = s.to_i # Require integer
15
13
  "#{(s / 60).to_s.rjust(2, "0")}:#{(s % 60).to_s.rjust(2, "0")}";
16
14
  end
17
-
15
+
18
16
  ##
19
17
  # Guess type of request by from string.
20
18
  #
@@ -23,12 +21,9 @@ module VkMusic
23
21
  # * +:post+ - if string match post URL.
24
22
  # * +:audios+ - if string match user or group URL.
25
23
  # * +:find+ - in rest of cases.
26
- #
27
24
  # @param str [String] request from user for some audios.
28
- #
29
25
  # @return [Symbol]
30
26
  def self.guess_request_type(str)
31
- # Guess what type of request is this. Returns Symbol: :find, :playlist, :audios
32
27
  case str
33
28
  when Constants::Regex::VK_PLAYLIST_URL_POSTFIX
34
29
  :playlist
@@ -43,9 +38,7 @@ module VkMusic
43
38
 
44
39
  ##
45
40
  # Turn hash into URL query string.
46
- #
47
41
  # @param hash [Hash]
48
- #
49
42
  # @return [String]
50
43
  def self.hash_to_params(hash = {})
51
44
  qs = ""
@@ -61,56 +54,9 @@ module VkMusic
61
54
  qs
62
55
  end
63
56
 
64
- ##
65
- # Utility loggers
66
- @@loggers = {
67
- debug: Logger.new(STDOUT),
68
- warn: Logger.new(STDERR)
69
- }
70
- @@loggers[:debug].level = Logger::DEBUG
71
- @@loggers[:warn].level = Logger::WARN
72
-
73
- ##
74
- # Send warning.
75
- def self.warn(*args)
76
- @@loggers[:warn].warn(args.join("\n"))
77
- end
78
-
79
- ##
80
- # Send debug message.
81
- def self.debug(*args)
82
- @@loggers[:debug].debug(args.join("\n")) if $DEBUG
83
- end
84
-
85
- ##
86
- # Function to turn values into given class unless nil provided
87
- #
88
- # Supported types:
89
- # * +String+
90
- # * +Integer+
91
- #
92
- # @param new_class [Class] class to transform to.
93
- # @param obj [Object] object to check.
94
- #
95
- # @return object transformed to given class or +nil+ if object was +nil+ already.
96
- def self.unless_nil_to(new_class, obj)
97
- case
98
- when obj.nil?
99
- nil
100
- when String <= new_class
101
- obj.to_s
102
- when Integer <= new_class
103
- obj.to_i
104
- else
105
- raise ArgumentError, "Bad arguments", caller
106
- end
107
- end
108
-
109
57
  ##
110
58
  # Get content of text children of provided Node.
111
- #
112
59
  # @param node [Nokogiri::Xml::Node]
113
- #
114
60
  # @return [String]
115
61
  def self.plain_text(node)
116
62
  node.children.select(&:text?).map(&:text).join ""
@@ -118,9 +64,7 @@ module VkMusic
118
64
 
119
65
  ##
120
66
  # Turn human readable track length to its size in seconds.
121
- #
122
- # @param str [String] string in format "(HH:MM:SS)" or something alike.
123
- #
67
+ # @param str [String] string in format "HH:MM:SS" or something alike (+/d++ Regex selector is used).
124
68
  # @return [Integer] amount of seconds.
125
69
  def self.parse_duration(str)
126
70
  str.scan(/\d+/)
@@ -128,7 +72,5 @@ module VkMusic
128
72
  .reverse
129
73
  .each_with_index.reduce(0) { |m, arr| m + arr[0] * 60**arr[1] }
130
74
  end
131
-
132
75
  end
133
-
134
76
  end
@@ -0,0 +1,51 @@
1
+ module VkMusic
2
+ ##
3
+ # @!group Logger
4
+
5
+ ##
6
+ # Default logger
7
+ @@logger = Logger.new(defined?($LOG_FILE) ? $LOGFILE : STDOUT,
8
+ formatter: Proc.new do |severity, datetime, progname, msg|
9
+ "[#{datetime}] #{severity}#{progname ? " - #{progname}" : ""}:\t #{msg}\n"
10
+ end
11
+ )
12
+ @@logger.level = $DEBUG ? Logger::DEBUG : Logger::INFO
13
+
14
+ ##
15
+ # Setup new logger.
16
+ # @param new_logger [Logger, nil]
17
+ def self.logger=(new_logger)
18
+ @@logger = new_logger
19
+ end
20
+ ##
21
+ # Access current logger.
22
+ # @return [Logger, nil]
23
+ def self.logger
24
+ @@logger
25
+ end
26
+
27
+ ##
28
+ # Log message.
29
+ def self.log(severity, message = nil, progname = nil)
30
+ @@logger.log(severity, message, progname) if @@logger
31
+ end
32
+
33
+ ##
34
+ # Log warn message.
35
+ def self.warn(message = nil, progname = nil)
36
+ @@logger.log(Logger::WARN, message, progname)
37
+ end
38
+ ##
39
+ # Log info message.
40
+ def self.info(message = nil, progname = nil)
41
+ @@logger.log(Logger::INFO, message, progname)
42
+ end
43
+ ##
44
+ # Log debug message.
45
+ def self.debug(message = nil, progname = nil)
46
+ @@logger.log(Logger::DEBUG, message, progname)
47
+ end
48
+
49
+ ##
50
+ # @!endgroup
51
+ end