Skip to content
This repository has been archived by the owner on Oct 14, 2023. It is now read-only.

Commit

Permalink
Reworked library interface. Added few more methods
Browse files Browse the repository at this point in the history
  • Loading branch information
fizvlad committed Aug 8, 2019
1 parent 74d2d70 commit 480f783
Show file tree
Hide file tree
Showing 24 changed files with 1,322 additions and 694 deletions.
16 changes: 9 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ gem install vk_music-*.gem

## Usage

You can take a look on documentation [here](https://www.rubydoc.info/gems/vk_music/).

### Logging in
Firstly, it is required to create new *VkMusic::Client* and provide login credentials:
```ruby
Expand All @@ -38,36 +40,36 @@ client = VkMusic::Client.new(username: "+71234567890", password: "password")
### Searching for audios
You can search audios by name with following method:
```ruby
audios = client.find_audio("Acid Spit - Mega Drive")
audios = client.find("Acid Spit - Mega Drive")
puts audios[0] # Basic information about audio
puts audios[0].url # URL to access audio. Notice that it is only accessible from your IP
```

### Parsing playlists
You can load all the audios from playlist using following method:
```ruby
playlist = client.get_playlist("https://vk.com/audio?z=audio_playlist-37661843_1/0e420c32c8b69e6637")
playlist = client.playlist("https://vk.com/audio?z=audio_playlist-37661843_1/0e420c32c8b69e6637")
```
It is only possible to load up to 100 audios from playlist per request, so you can reduce amount of requests by setting up how many audios from playlist you actually need.
For example, following method will perform only one HTML request:
```ruby
playlist = client.get_playlist("https://vk.com/audio?z=audio_playlist121570739_7", 100)
playlist = client.playlist("https://vk.com/audio?z=audio_playlist121570739_7", up_to: 100)
urls = playlist.map(&:url) # URLs for every audio
```

### User or group audios
You can load first 100 audios from user or group page. Those audios will be returned as playlist. To do it simply pass user or group id:
```ruby
user_playlist = client.get_audios("8024985")
group_playlist = client.get_audios("-4790861") # Group and public id starts with '-'
user_playlist = client.audios(owner_id: 8024985)
group_playlist = client.audios(owner_id: -4790861) # Group and public id starts with '-'
```
You can set how many audios you actually need as well:
```ruby
user_playlist = client.get_audios("8024985", 10)
user_playlist = client.audios(owner_id: 8024985, up_to: 10)
```

### Audios from post
You can load up to 10 audios attached to some post. Those audios will be returned as array:
```ruby
audios = client.get_audios_from_post("https://vk.com/wall-4790861_5453")
audios = client.post("https://vk.com/wall-4790861_5453")
```
6 changes: 3 additions & 3 deletions lib/vk_music.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
require_relative "vk_music/utility.rb"
require_relative "vk_music/constants.rb"
require_relative "vk_music/exceptions.rb"
require_relative "vk_music/audio.rb"
require_relative "vk_music/utility.rb"
require_relative "vk_music/link_decoder.rb"
require_relative "vk_music/audio.rb"
require_relative "vk_music/playlist.rb"
require_relative "vk_music/client.rb"
require_relative "vk_music/client.rb"
178 changes: 112 additions & 66 deletions lib/vk_music/audio.rb
Original file line number Diff line number Diff line change
@@ -1,65 +1,76 @@
require "cgi"

##
# @!macro [new] options_hash_param
# @param options [Hash] hash with options.

module VkMusic

##
# Class representing VK audio.
class Audio

##
# @return [Integer] ID of audio.
# @return [Integer, nil] ID of audio.
attr_reader :id

##
# @return [Integer] ID of audio owner.
# @return [Integer, nil] ID of audio owner.
attr_reader :owner_id

##
# @return [String] part of secret hash which used when using +act=reload_audio+.
# @return [String, nil] part of secret hash which used when using +act=reload_audio+.
attr_reader :secret_1, :secret_2

##
# @return [String]
# @return [String] name of artist.
attr_reader :artist

##
# @return [String]
# @return [String] title of song.
attr_reader :title

##
# @return [Integer] duration of track in seconds.
attr_reader :duration

##
# @return [String] decoded download URL.
attr_reader :url
# Access decoded download URL.
#
# If link was already decoded, returns cached value. Else decodes existing link.
# If no link can be provided, returns +nil+.
#
# @return [String, nil] decoded download URL or +nil+ if not available.
def url
if url_cached?
@url
elsif @url_encoded && @client_id
@url = VkMusic::LinkDecoder.unmask_link(@url_encoded, @client_id)
else
@url # nil
end
end

##
# @return [String] encoded download URL.
# @return [String, nil] encoded download URL.
attr_reader :url_encoded

##
# Update audio URLs.
#
# If +:url+ is provided - just saving it.
# If +:url_encoded+ and +:client_id+ provided - unmasking link first.
#
# @option options [String] :url decoded download URL.
# @option options [String] :url_encoded decoded download URL.
# @option options [String] :client_id decoded download URL.
#
# @return [String] decoded URL.
def update_url(options)
raise ArgumentError, "options hash must be provided", caller unless options.class == Hash
if !options[:url].to_s.empty?
@url_encoded = ""
@url = options[:url].to_s
elsif !options[:url].to_s.empty? && options[:client_id]
@url_encoded = options[:url_encoded].to_s
@url = VkMusic::LinkDecoder.unmask_link(options[:url_encoded], options[:client_id])
else
raise ArgumentError, "You should either provide :url or :url_encoded and :client_id", caller
end
# @return [Boolean] whether decoded URL is already cached.
def url_cached?
!!(@url)
end

##
# @return [Boolean] whether able to get download URL without web requests.
def url_available?
!!(url_cached? || (@url_encoded && @client_id))
end

##
# @return [Boolean] whether it's possible to get download URL with {Client#from_id}.
def url_accessable?
!!(@id && @owner_id && @secret_1 && @secret_2)
end

##
Expand All @@ -71,38 +82,72 @@ def to_s
##
# @return [String] extended information about audio.
def pp
"#{to_s} (Got decoded URL: #{@url ? "yes" : "no"}, able to get URL from VK: #{@id && @owner_id && @secret_1 && @secret_2 ? "yes" : "no"})"
"#{to_s} (Able to get decoded URL: #{url_available? ? "yes" : "no"}, able to get URL from VK: #{url_accessable? ? "yes" : "no"})"
end

##
# Update audio URLs.
#
# @overload update_url(decoded_url)
# Simply save download URL.
# @param decoded_url [String]
#
# @overload update_url(audio)
# Copy URLs from this audio (no checks applied).
# @param audio [Audio]
#
# @overload update_url(options)
# @macro options_hash_param
# @option options [String, nil] :url decoded download URL.
# @option options [String, nil] :url_encoded decoded download URL.
# @option options [String, nil] :client_id decoded download URL.
#
# @return [self]
def update_url(arg)
case arg
when String
@url = arg
when Audio
# Only save URL if it's already decoded and cached
@url = arg.url if arg.url_cached?
@url_encoded = arg.url_encoded
@client_id = arg.client_id
when Hash
@url_encoded = Utility.unless_nil_to String, options[:url_encoded]
@url = Utility.unless_nil_to String, options[:url]
@client_id = Utility.unless_nil_to Integer, options[:client_id]
else
raise ArgumentError, "Bad arguments", caller
end
self
end

##
# Initialize new audio.
#
# @option options [Integer] :id
# @option options [Integer] :owner_id
# @option options [String] :secret_1
# @option options [String] :secret_2
# @option options [String] :artist *required*
# @option options [String] :title *required*
# @option options [Integer] :duration *required*
# @option options [String] :url_encoded
# @option options [String] :url
def initialize(options)
# Arguments check
raise ArgumentError, "options hash must be provided", caller unless options.class == Hash
raise ArgumentError, "artist is not provided", caller unless options.has_key?(:artist)
raise ArgumentError, "title is not provided", caller unless options.has_key?(:title)
raise ArgumentError, "duration is not provided", caller unless options.has_key?(:duration)

# Setting up attributes
@id = options[:id].to_s
@owner_id = options[:owner_id].to_s
@secret_1 = options[:secret_1].to_s
@secret_2 = options[:secret_2].to_s
# @macro options_hash_param
#
# @option options [Integer, nil] :id
# @option options [Integer, nil] :owner_id
# @option options [String, nil] :secret_1
# @option options [String, nil] :secret_2
# @option options [String] :artist
# @option options [String] :title
# @option options [Integer] :duration
# @option options [String, nil] :url_encoded
# @option options [String, nil] :url
# @option options [Integer, nil] :client_id
def initialize(options = {})
@id = Utility.unless_nil_to Integer, options[:id]
@owner_id = Utility.unless_nil_to Integer, options[:owner_id]
@secret_1 = Utility.unless_nil_to String, options[:secret_1]
@secret_2 = Utility.unless_nil_to String, options[:secret_2]
@artist = options[:artist].to_s
@title = options[:title].to_s
@duration = options[:duration].to_i
@url_encoded = options[:url_encoded].to_s
@url = options[:url].to_s
@url_encoded = Utility.unless_nil_to String, options[:url_encoded]
@url = Utility.unless_nil_to String, options[:url]
@client_id = Utility.unless_nil_to Integer, options[:client_id]
end

##
Expand All @@ -114,17 +159,18 @@ def initialize(options)
# @return [Audio]
def self.from_node(node, client_id)
url_encoded = node.at_css("input").attribute("value").to_s
url_encoded = nil if url_encoded == "https://m.vk.com/mp3/audio_api_unavailable.mp3"
url_encoded = nil if url_encoded == Constants::URL::VK[:audio_unavailable]
id_array = node.attribute("data-id").to_s.split("_")

new({
:id => id_array[1],
:owner_id => id_array[0],
:id => id_array[1].to_i,
:owner_id => id_array[0].to_i,
:artist => node.at_css(".ai_artist").text.strip,
:title => node.at_css(".ai_title").text.strip,
:duration => node.at_css(".ai_dur").attribute("data-dur").to_s.to_i,
:url_encoded => url_encoded,
:url => url_encoded ? VkMusic::LinkDecoder.unmask_link(url_encoded, client_id) : nil,
:url_encoded => url_encoded.to_s,
:url => nil,
:client_id => client_id
})
end

Expand All @@ -135,22 +181,22 @@ def self.from_node(node, client_id)
# @param client_id [Integer]
#
# @return [Audio]
def self.from_data_array(data, client_id)
url_encoded = data[2]
url_encoded = nil if url_encoded == ""
def self.from_data(data, client_id)
url_encoded = data[2].to_s

secrets = data[13].split("/")
secrets = data[13].to_s.split("/")

new({
:id => data[0],
:owner_id => data[1],
:id => data[0].to_i,
:owner_id => data[1].to_i,
:secret_1 => secrets[3],
:secret_2 => secrets[5],
:artist => CGI.unescapeHTML(data[4]),
:title => CGI.unescapeHTML(data[3]),
:duration => data[5],
:duration => data[5].to_i,
:url_encoded => url_encoded,
:url => url_encoded ? VkMusic::LinkDecoder.unmask_link(url_encoded, client_id) : nil,
:url => nil,
:client_id => client_id
})
end

Expand Down
Loading

0 comments on commit 480f783

Please sign in to comment.