Class: VkMusic::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/vk_music/client.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(options) ⇒ Client

Returns a new instance of Client.

Raises:

  • (ArgumentError)


13
14
15
16
17
18
19
20
21
22
# File 'lib/vk_music/client.rb', line 13

def initialize(options)
  # Arguments check
  raise ArgumentError, "options hash must be provided", caller unless options.class == Hash
  raise ArgumentError, "username is not provided", caller unless options.has_key?(:username)
  raise ArgumentError, "password is not provided", caller unless options.has_key?(:password)
  
  # Setting up client
  @agent = Mechanize.new
  (options[:username], options[:password])
end

Instance Attribute Details

#idObject (readonly)

Returns the value of attribute id.



8
9
10
# File 'lib/vk_music/client.rb', line 8

def id
  @id
end

#nameObject (readonly)

Returns the value of attribute name.



8
9
10
# File 'lib/vk_music/client.rb', line 8

def name
  @name
end

Instance Method Details

#find_audio(query) ⇒ Object



24
25
26
27
28
# File 'lib/vk_music/client.rb', line 24

def find_audio(query)
  uri = URI(VK_URL[:audios])
  uri.query = URI.encode_www_form({ "act" => "search", "q" => query.to_s })
  load_audios_from_page(uri)
end

#get_audios(obj, up_to = nil) ⇒ Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/vk_music/client.rb', line 78

def get_audios(obj, up_to = nil)
  if up_to && up_to > 100 && defined?(Warning.warn)
    Warning.warn("Current implementation of method VkMusic::Client#get_audios is only able to load first 100 audios from user page.\n")
  end
  # NOTICE: this method is only able to load first 100 audios
  # NOTICE: it is possible to download 50 audios per request on "https://m.vk.com/audios#{owner_id}?offset=#{offset}", so it will cost A LOT to download all of audios (up to 200 requests).
  # NOTICE: it is possible to load up to 2000 audios **without url** if offset is negative
  
  # Firstly, we need to get numeric id
  id = get_id(obj.to_s)      
  
  # Trying to parse out audios
  begin
    first_json = load_playlist_json_section(id: id.to_s, playlist_id: -1, offset: 0)
    first_data = first_json["data"][0]
    first_data_audios = load_audios_from_data(first_data)
  rescue Exception => error
    raise AudiosSectionParseError, "unable to load or parse audios section: #{error.message}", caller
  end
  
  #total_count = first_data["totalCount"] # NOTICE: not used due to restrictions described above
  total_count = first_data_audios.length
  up_to = total_count if (up_to.nil? || up_to < 0 || up_to > total_count)
  list = first_data_audios[0, up_to]      
  
  # It turns out user audios are just playlist with id -1
  Playlist.new(list, {
    :id => first_data["id"],
    :owner_id => first_data["owner_id"],
    :access_hash => first_data["access_hash"],
    :title => CGI.unescapeHTML(first_data["title"].to_s),
    :subtitle => CGI.unescapeHTML(first_data["subtitle"].to_s),
  })
end

#get_id(str) ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
# File 'lib/vk_music/client.rb', line 113

def get_id(str)
  case str
    when VK_URL_REGEX
      path = str.match(VK_URL_REGEX)[1]
      get_id(path) # Recursive call
    when VK_ID_REGEX
      str
    when VK_AUDIOS_REGEX
      str.match(/-?\d+/).to_s # Numbers with sigh
    when VK_PREFIXED_ID_REGEX
      id = str.match(/\d+/).to_s # Just numbers. Sign needed
      id = "-#{id}" unless str.start_with?("id")
      id
    when VK_CUSTOM_ID_REGEX
      begin
        page = load_page("#{VK_URL[:home]}/#{str}")
      rescue Exception => error
        raise IdParseError, "unable to load page by id \"#{str}\". Error: #{error.message}"
      end
      
      unless page.at_css(".PageBlock .owner_panel")
        # Ensure this isn't some random vk page
        raise IdParseError, "page #{str} doesn't seem to be a group or user page"
      end
      
      id = page.link_with(href: VK_HREF_ID_CONTAINING_REGEX).href.slice(/-?\d+/) # Numbers with sign
      id
  else
    raise IdParseError, "unable to convert \"#{str}\" into id"
  end
end

#get_playlist(url, up_to = nil) ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/vk_music/client.rb', line 30

def get_playlist(url, up_to = nil)
  # NOTICE: it is possible to use same type of requests as in get_audios method
  begin
    url, owner_id, id, access_hash = url.to_s.match(PLAYLIST_URL_REGEX).to_a
  
    # Load first page and get info
    first_page = load_playlist_page(owner_id: owner_id, id: id, access_hash: access_hash, offset: 0)
    
    # Parse out essential data
    title = first_page.at_css(".audioPlaylist__title").text.strip
    subtitle = first_page.at_css(".audioPlaylist__subtitle").text.strip
    
    footer_node = first_page.at_css(".audioPlaylist__footer")
    if footer_node
      footer_match = footer_node.text.strip.match(/^\d+/)
      playlist_size = footer_match ? footer_match[0].to_i : 0
    else
      playlist_size = 0
    end
  rescue Exception => error
    raise PlaylistParseError, "unable to parse playlist page. Error: #{error.message}", caller
  end
  # Now we can be sure we are on correct page
  
  first_page_audios = load_audios_from_page(first_page)
  
  # Check whether need to make additional requests
  up_to = playlist_size if (up_to.nil? || up_to < 0 || up_to > playlist_size)
  if first_page_audios.length >= up_to
    list = first_page_audios[0, up_to]
  else        
    list = first_page_audios
    loop do
      playlist_page = load_playlist_page(owner_id: owner_id, id: id, access_hash: access_hash, offset: list.length)
      list.concat(load_audios_from_page(playlist_page)[0, up_to - list.length])
      break if list.length == up_to
    end        
  end
  
  Playlist.new(list, {
    :id => id,
    :owner_id => owner_id,
    :access_hash => access_hash,
    :title => title,
    :subtitle => subtitle,
  })
end