vk_music 3.1.7 → 4.1.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.
Files changed (71) hide show
  1. checksums.yaml +4 -4
  2. data/.env.example +3 -0
  3. data/.github/workflows/ruby.yml +35 -0
  4. data/.gitignore +6 -0
  5. data/.rspec +1 -0
  6. data/.rubocop.yml +56 -0
  7. data/Gemfile +38 -10
  8. data/Gemfile.lock +124 -70
  9. data/LICENSE.txt +0 -0
  10. data/README.md +121 -94
  11. data/Rakefile +15 -22
  12. data/bin/console +18 -24
  13. data/lib/vk_music.rb +32 -18
  14. data/lib/vk_music/audio.rb +112 -187
  15. data/lib/vk_music/client.rb +193 -677
  16. data/lib/vk_music/playlist.rb +44 -97
  17. data/lib/vk_music/request.rb +13 -0
  18. data/lib/vk_music/request/artist.rb +24 -0
  19. data/lib/vk_music/request/audios_reload.rb +29 -0
  20. data/lib/vk_music/request/base.rb +75 -0
  21. data/lib/vk_music/request/login.rb +35 -0
  22. data/lib/vk_music/request/my_page.rb +21 -0
  23. data/lib/vk_music/request/playlist.rb +31 -0
  24. data/lib/vk_music/request/playlist_section.rb +35 -0
  25. data/lib/vk_music/request/post.rb +22 -0
  26. data/lib/vk_music/request/profile.rb +24 -0
  27. data/lib/vk_music/request/search.rb +34 -0
  28. data/lib/vk_music/request/wall_section.rb +34 -0
  29. data/lib/vk_music/utility.rb +8 -78
  30. data/lib/vk_music/utility/artist_loader.rb +17 -0
  31. data/lib/vk_music/utility/artist_url_parser.rb +22 -0
  32. data/lib/vk_music/utility/audio_data_parser.rb +37 -0
  33. data/lib/vk_music/utility/audio_items_parser.rb +18 -0
  34. data/lib/vk_music/utility/audio_node_parser.rb +59 -0
  35. data/lib/vk_music/utility/audios_from_ids_loader.rb +21 -0
  36. data/lib/vk_music/utility/audios_ids_getter.rb +25 -0
  37. data/lib/vk_music/utility/audios_loader.rb +37 -0
  38. data/lib/vk_music/utility/data_type_guesser.rb +48 -0
  39. data/lib/vk_music/utility/duration_parser.rb +17 -0
  40. data/lib/vk_music/utility/last_profile_post_loader.rb +26 -0
  41. data/lib/vk_music/utility/link_decoder.rb +107 -0
  42. data/lib/vk_music/utility/node_text_children_reader.rb +14 -0
  43. data/lib/vk_music/utility/playlist_loader.rb +30 -0
  44. data/lib/vk_music/utility/playlist_node_parser.rb +21 -0
  45. data/lib/vk_music/utility/playlist_section_loader.rb +29 -0
  46. data/lib/vk_music/utility/playlist_url_parser.rb +32 -0
  47. data/lib/vk_music/utility/post_loader.rb +23 -0
  48. data/lib/vk_music/utility/post_url_parser.rb +24 -0
  49. data/lib/vk_music/utility/profile_id_resolver.rb +58 -0
  50. data/lib/vk_music/utility/wall_loader.rb +25 -0
  51. data/lib/vk_music/version.rb +7 -5
  52. data/lib/vk_music/web_parser.rb +9 -0
  53. data/lib/vk_music/web_parser/artist.rb +16 -0
  54. data/lib/vk_music/web_parser/audios_reload.rb +20 -0
  55. data/lib/vk_music/web_parser/base.rb +27 -0
  56. data/lib/vk_music/web_parser/login.rb +13 -0
  57. data/lib/vk_music/web_parser/my_page.rb +19 -0
  58. data/lib/vk_music/web_parser/playlist.rb +33 -0
  59. data/lib/vk_music/web_parser/playlist_section.rb +53 -0
  60. data/lib/vk_music/web_parser/post.rb +15 -0
  61. data/lib/vk_music/web_parser/profile.rb +33 -0
  62. data/lib/vk_music/web_parser/search.rb +56 -0
  63. data/lib/vk_music/web_parser/wall_section.rb +53 -0
  64. data/vk_music.gemspec +36 -40
  65. metadata +63 -77
  66. data/.travis.yml +0 -7
  67. data/bin/setup +0 -8
  68. data/lib/vk_music/constants.rb +0 -78
  69. data/lib/vk_music/exceptions.rb +0 -21
  70. data/lib/vk_music/link_decoder.rb +0 -102
  71. data/lib/vk_music/utility/log.rb +0 -51
@@ -1,97 +1,44 @@
1
- module VkMusic
2
- ##
3
- # VK playlist.
4
- class Playlist
5
- include Enumerable
6
-
7
- ##
8
- # @return [Integer, nil] playlist ID.
9
- attr_reader :id
10
- ##
11
- # @return [Integer, nil] owner of playlist ID.
12
- attr_reader :owner_id
13
- ##
14
- # @return [String, nil] access hash which should be part of link for some playlists.
15
- attr_reader :access_hash
16
- ##
17
- # @return [String] playlist title.
18
- attr_reader :title
19
- ##
20
- # @return [String, nil] playlist subtitle. May be empty.
21
- attr_reader :subtitle
22
- ##
23
- # @return [Integer, nil] real size of playlist or +nil+ if unknown.
24
- attr_reader :real_size
25
-
26
- ##
27
- # @!visibility private
28
- attr_reader :list
29
-
30
- ##
31
- # @return [String] playlist description in Russian.
32
- def to_s
33
- (@subtitle && !@subtitle.empty? ? "#{@subtitle} - " : "") +
34
- @title +
35
- (@real_size ? "(#{self.length} из #{@real_size} аудиозаписей загружено)" : " (#{self.length} аудиозаписей)")
36
- end
37
- ##
38
- # @return [String] Same to {#to_s}, but also outputs list of audios.
39
- def pp
40
- "#{to_s}:\n#{@list.map(&:pp).join("\n")}"
41
- end
42
-
43
- ##
44
- # @return [Array<Audio>] Returns duplicate of array of playlist audios.
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,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module VkMusic
4
+ module Request
5
+ # Artist audios page request
6
+ class Artist < Base
7
+ # Initialize new request
8
+ # @param name [String]
9
+ # @param client_id [Integer]
10
+ def initialize(name, client_id)
11
+ @client_id = client_id
12
+ super("#{VK_ROOT}/artist/#{name}/top_audios", {}, 'GET', {})
13
+ end
14
+
15
+ def_delegators :@parser, :audios
16
+
17
+ private
18
+
19
+ def after_call
20
+ @parser = WebParser::Artist.new(@response, client_id: @client_id)
21
+ end
22
+ end
23
+ end
24
+ end
@@ -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