vk_music 1.1.1 → 2.0.0

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.
@@ -1,46 +1,91 @@
1
1
  module VkMusic
2
2
 
3
- # Constants
3
+ ##
4
+ # Constants.
4
5
  module Constants
5
- # Web
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
- VK_URL = {
9
- :scheme => "https",
10
- :host => "m.vk.com",
11
- :home => "https://m.vk.com",
12
- :profile => "https://m.vk.com/id0",
13
- :feed => "https://m.vk.com/feed",
14
- :audios => "https://m.vk.com/audio",
15
- :login => "https://m.vk.com/login",
16
- :login_action => "https://login.vk.com",
17
- :wall => "https://m.vk.com/wall"
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
- :username => "email",
22
- :password => "pass",
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
- # Post
38
- POST_URL_REGEX = /.*wall(-?\d+)_(\d+)/
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
@@ -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
- # Unable to find playlist or got permission error.
15
- class PlaylistParseError < AudiosParseError; end
15
+ ##
16
+ # Failed to get request. _Only_ thron when Mechanize failed to load page
17
+ class RequestError < VkMusicError; end
16
18
 
17
- # Unable to load or parse audios section from json.
18
- class AudiosSectionParseError < AudiosParseError; end
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
- # Unable to load or parse all of audios by ids.
21
- class ReloadAudiosParseError < AudiosParseError; end
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
- # ===== Parameters:
95
- # * [+link+] (+String+) - encoded link to audio. Usually looks like "https://m.vk.​com/mp3/audio_api_unavailable.mp3?extra=...".
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
- # ===== Returns:
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
 
@@ -1,87 +1,98 @@
1
1
  module VkMusic
2
2
 
3
- # VK playlist. Extended with Enumerable.
3
+ ##
4
+ # VK playlist.
4
5
  class Playlist
5
6
  include Enumerable
6
7
 
7
- # Playlist id.
8
+ ##
9
+ # @return [Integer, nil] playlist ID.
8
10
  attr_reader :id
9
- # Owner of playlist.
11
+
12
+ ##
13
+ # @return [Integer, nil] owner of playlist ID.
10
14
  attr_reader :owner_id
11
- # Access hash which should be part of link for some playlists.
15
+
16
+ ##
17
+ # @return [String, nil] access hash which should be part of link for some playlists.
12
18
  attr_reader :access_hash
13
- # Playlist title.
19
+
20
+ ##
21
+ # @return [String] playlist title.
14
22
  attr_reader :title
15
- # Playlist subtitle. May be empty.
23
+
24
+ ##
25
+ # @return [String, nil] playlist subtitle. May be empty.
16
26
  attr_reader :subtitle
17
27
 
18
- # Return string describing playlist in Russian.
28
+ ##
29
+ # @return [String] playlist description in Russian.
19
30
  def to_s
20
- (@subtitle.empty? ? "" : "#{@subtitle} - ") + "#{@title} (#{self.length} аудиозаписей)"
31
+ (@subtitle ? "#{@subtitle} - " : "") + "#{@title} (#{self.length} аудиозаписей)"
21
32
  end
22
33
 
23
- # Same to +to_s+, but also outputs list of audios.
34
+ ##
35
+ # @return [String] Same to {#to_s}, but also outputs list of audios.
24
36
  def pp
25
- "#{to_s}:\n#{@list.map(&:to_s).join("\n")}"
37
+ "#{to_s}:\n#{@list.map(&:pp).join("\n")}"
26
38
  end
27
39
 
28
- # Returns audios array.
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
- # :nodoc:
46
+ ##
47
+ # @see Array#each
34
48
  def each(&block)
35
49
  @list.each(&block)
36
50
  end
37
51
 
38
- # :stopdoc:
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
- # Access to audios from playlist.
50
- #
51
- # ===== Parameters:
52
- # * [+index+] (+Integer+) - index of audio (starting from 0).
65
+ ##
66
+ # Access audios from playlist.
53
67
  #
54
- # ===== Returns:
55
- # * (+Audio+, +nil+) - audio or +nil+ if out of range.
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
- # ===== Parameters:
63
- # * [+list+] (+Array+) - list of audios in album.
64
- # * [+options+] (+Hash+)
78
+ # @param list [Array] list of audios in playlist.
65
79
  #
66
- # ===== Options:
67
- # * [+:id+]
68
- # * [+:owner_id+]
69
- # * [+:access_hash+]
70
- # * [+:title+]
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
- # Arguments check
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].to_s
81
- @owner_id = options[:owner_id].to_s
82
- @access_hash = options[:access_hash].to_s
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].to_s
95
+ @subtitle = Utility.unless_nil_to String, options[:subtitle]
85
96
  end
86
97
 
87
98
  end
@@ -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
- # ===== Parameters:
11
- # * [+s+] (+Integer+) - amount of seconds.
13
+ # @param s [Integer] amount of seconds.
12
14
  #
13
- # ===== Returns:
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
- # ===== Parameters:
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::PLAYLIST_URL_REGEX
36
+ when Constants::Regex::VK_PLAYLIST_URL_POSTFIX
34
37
  :playlist
35
- when Constants::POST_URL_REGEX
38
+ when Constants::Regex::VK_WALL_URL_POSTFIX, Constants::Regex::VK_POST_URL_POSTFIX
36
39
  :post
37
- when Constants::VK_URL_REGEX
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
- # ===== Parameters:
47
- # * [+hash+] (+Hash+)
50
+ # @param hash [Hash]
48
51
  #
49
- # ===== Returns:
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
- if defined?(Warning.warn)
68
- Warning.warn args.join("\n")
69
- else
70
- STDERR.puts "Warning:", *args
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