# -*- coding: UTF-8 -*-
# SoundCloud Leecher v0.5
#
# By Elektro H@cker
# Description:
# -----------
#
# SoundCloud Leecher it's a tool that helps you to keep updated your music collection.
#
# This tool is intended for (almost) daily use.
#
# The script retreives all the downloadable links of the first "X" pages from a "X" group of SoundCloud,
# And then list the links into a text file for use it with a download magaer like "JDownloader".
#
# All the links are stored permanently into a log file to be excluded at the next use,
# that's the reason why this tool is intended for daily use.
# Info:
# ----
#
# SoundCloud unfortunately do not have a date search mode,
# which would be much easier to search/sort by "music style + today's date" than "groups + first sorted pages",
# The site has an advanced search engine that can search by "release date" but that is not the same than "uploaded date",
# So i think the only way to sort by date is by entering a group page because it shows the content sorted by the last date.
# How to use:
# ----------
#
# Configure your favorite groups and the other settings by entering into the configuration menu.
require 'net/http'
require 'win32/registry'
require "highline/system_extensions"
include HighLine::SystemExtensions
exit if Object.const_defined?(:Ocra)
def logo()
puts "
_, _, , , , , ,_ _ , _, , , ,_ , _, _, _ , , _,,_
(_ / \\ | | |\\ | | \\ / | / \\ | | | \\ | /_,/_,/ |_| /_,|_)
_) \\_/ \\__| | \\| |_/ \\_ |__ \\_/ \\__| |_/ |__ \\_ \\_ \\_ | | \\_ | \\
' ` ` ` ` ' ` ` `' ` `'
v0.5
By Elektro H@cker"
end
def main()
puts "\n\n[+] Press \"C\" to config or press any other key to start leeching..."
key = get_character
config("") if key.to_s =~ /^67$|^99$/
get_downloads_list()
puts "\n\n\n[+] All links are stored in: #{$leecherfile}"
end
def get_settings()
begin
$logfile = regread("LOGFILEPATH")
rescue
$logfile = "#{ENV['WINDIR']}\\Soundcloud LOG.txt"
end
begin
$leecherfile = regread("LEECHERFILEPATH") + "Soundcloud Leecher #{Time.new.strftime("%Y-%m-%d")}.txt"
rescue
$leecherfile = "#{ENV['USERPROFILE']}\\Desktop\\Soundcloud Leecher #{Time.new.strftime("%Y-%m-%d")}.txt"
end
begin
$groups = regread("GROUPS")
rescue
$groups = ""
end
begin
$max_pages = regread("MAXPAGES")
$max_pages = $max_pages.to_i
rescue
$max_pages = 5
end
begin
$min_duration = regread("MINDURATION")+"00"
$min_duration = $min_duration.to_i
rescue
$min_duration = 130
end
begin
$max_duration = regread("MAXDURATION")+"00"
$max_duration = $max_duration.to_i
rescue
$max_duration = 1200
end
begin
$max_size = regread("MAXSIZE")+"00"
$max_size = $max_size.to_i
rescue
$max_size = 10000
end
begin
$excluded_exts = regread("EXCLUDED EXTENSIONS")
rescue
$excluded_exts = ""
end
begin
$excluded_tags = regread("EXCLUDED TAGNAMES")
rescue
$excluded_tags = ""
end
begin
$verbose = regread("VERBOSEMODE")
rescue
$verbose = "no"
end
end
def config(errorcode)
puts "
º---------------------------------º---------------------------------º
| [1] Set the log file path | [+] Max. track duration |
| [2] Set the leecher file path | [-] Min. track duration |
| [3] Delete the log file | [M] Max. track filesize |
|---------------------------------|---------------------------------|
| [A] Add a group name | [P] Pages per group to parse |
| [D] Delete a group name | [F] Filetypes to exclude |
| [L] List the group names | [T] Tagnames to exclude |
|---------------------------------|---------------------------------|
| [V] Verbose mode (ON/OFF) | [S] Show the current settings |
|---------------------------------| [R] Reset the settings |
| [E] Exit | [Z] Backup all the config. |
º---------------------------------º---------------------------------º
"
get_settings()
puts errorcode
keystr = "NOTHING"
until keystr =~ /^43$|^45$|^49$|^50$|^51$|^65$|^68$|^69$|^70$|^76$|^77$|^80$|^82$|^83$|^84$|^86$|^90$|^97$|^100$|^101$|^102$|^108$|^109$|^112$|^114$|^115$|^116$|^118$|^121$|^122$/
print "\nChoose an option >> "
key = get_character
keystr = key.to_s
end
# Log file path
if key == 49
logfilepath = "NOTHING"
until File.directory?(logfilepath)
print "\n\nEnter the path to an existing directory for the logfile: "
logfilepath = STDIN.gets.chomp
end
if logfilepath[-1] == '\\' then logfilepath = logfilepath[0..-2] end
regwrite("LOGFILEPATH", logfilepath + "\\Soundcloud LOG.txt")
config("[+] Operation completed.")
end
# Leecher file path
if key == 50
leecherfile = "NOTHING"
until File.directory?(leecherfile)
print "\n\Enter the path to an existing directory for the leecher file: "
leecherfile = STDIN.gets.chomp
end
if not leecherfile[-1] == '\\' then leecherfile = leecherfile+"\\" end
regwrite("LEECHERFILEPATH", leecherfile)
config("[+] Operation completed.")
end
# delete log file
if key == 51
filedel = "NOTHING"
while not filedel =~ /^78$|^89$|^110$|^121$/
print "\n\nWant to delete the log file, Are you sure? [Y/N]: "
filedel = get_character.to_s
end
begin
if filedel =~ /^89$|^121$/ then File.delete($logfile) end
config("[+] Operation completed.")
rescue
config("\n\nERROR\n\nCan't find the logfile.")
end
end
# Add groupname
if key == 65 or key == 97
addname = "NOTHING"
until not addname == "NOTHING"
print "\n\nEnter a group name to add\nor enter the word \"none\" to reset the group list: "
addname = STDIN.gets.chomp
end
begin
groups=regread("GROUPS")
rescue
groups=""
end
for addgroupname in regread("GROUPS").split(";").sort do
if not addgroupname =~ /^#{Regexp.escape(addname)}$/i then addfinalgroups="#{addfinalgroups};#{addgroupname}" else found = "yes" end
end
if found == "yes" then config("\n\n[+] ERROR\n\nThe groupname already exists.") end
if addname =~ /^none$/i then regwrite("GROUPS", "") else regwrite("GROUPS", "#{groups};#{addname}") end
config("[+] Operation completed.")
end
# delete groupname
if key == 68 or key == 100
delnum = "NOTHING"
num=0
for listname in $groups.split(";").each.sort do
num=num+1
puts " [#{num}] #{listname}"
instance_variable_set "@_#{num}", listname
end
until not delnum == "NOTHING"
print "\n\nEnter a reference number to delete the group\nor enter the word \"none\" to reset the group list: "
delnum = STDIN.gets.chomp
end
choose = instance_variable_get "@_#{delnum}"
begin
groups=regread("GROUPS")
rescue
groups=""
end
begin
for delgroupname in regread("GROUPS").split(";").sort do
if not delgroupname =~ /^#{Regexp.escape(choose)}$/i then delfinalgroups="#{delfinalgroups};#{delgroupname}" end
end
rescue
config("\n\n[+] ERROR\n\nCan't find the delgroupname.")
end
regwrite("GROUPS", delfinalgroups[1..-1])
config("[+] Operation completed.")
end
# list groupnames
if key == 76 or key == 108
num = 0
puts "\n\n[+] All your groups:\n\n"
for listname in $groups.split(";").each.sort do
num=num+1
puts " [#{num}] #{listname}"
end
puts "\n\n[+] Press any other key to continue..."
get_character
config("[+] Operation completed.")
end
# Max pages
if key == 80 or key == 112
maxpages = "NOTHING"
puts "\n\nThis option lets you configure the maximum pages when leeching a group..."
until not maxpages[/[a-z]/i]
print "\nEnter a number for the max pages value: "
maxpages = STDIN.gets.chomp
end
regwrite("MAXPAGES", maxpages)
config("[+] Operation completed.")
end
# Min track duration
if key == 45
puts "\n\nThis option lets you configure the minimum accepted minutes duration when leeching tracks..."
minlenght = "NOTHING"
until minlenght[/^[0-9]+$/]
print "\nEnter a number to set the min. minutes for a track (Example: \"1\"): "
minlenght = STDIN.gets.chomp
end
regwrite("MINDURATION", minlenght)
config("[+] Operation completed.")
end
# Max track duration
if key == 43
puts "\n\nThis option lets you configure the maximum accepted minutes duration when leeching tracks..."
maxlenght = "NOTHING"
until maxlenght[/^[0-9]+$/]
print "\nEnter a number to set the max. minutes for a track (Example: \"15\"): "
maxlenght = STDIN.gets.chomp
end
regwrite("MAXDURATION", maxlenght)
config("[+] Operation completed.")
end
# Max track filesize
if key == 77 or key == 109
puts "\n\nThis option lets you configure the maximum accepted filesize when leeching tracks..."
maxsize = "NOTHING"
until maxsize[/^[0-9]+$/]
print "\nEnter a number to set the max. MB for a track (Example: \"20\"): "
maxsize = STDIN.gets.chomp
end
regwrite("MAXSIZE", maxsize)
config("[+] Operation completed.")
end
# exclude filetypes
if key == 102 or key == 70
addext = "NOTHING"
puts "\n\nThis option lets you exclude tracks by their file extension..."
until not addext == "NOTHING"
print "\n\nEnter a file extension (Example: \"wav\") \nor enter the word \"none\" to reset the exclusions: "
addext = STDIN.gets.chomp
end
begin
exts=regread("EXCLUDED EXTENSIONS")
rescue
exts=""
end
if addext =~ /^none$/i then regwrite("EXCLUDED EXTENSIONS", "") else regwrite("EXCLUDED EXTENSIONS", "#{exts};#{addext.gsub(".","")}") end
config("[+] Operation completed.")
end
# exclude tagnames
if key == 84 or key == 116
addtag = "NOTHING"
puts "\n\nThis option lets you exclude tracks by a tagname..."
until not addtag == "NOTHING"
print "\n(The tagnames are not case sensitive)\n\nEnter a tagname (Example: \"session\") \nor enter the word \"none\" to reset the exclusions: "
addtag = STDIN.gets.chomp
end
begin
tags=regread("EXCLUDED TAGNAMES")
rescue
tags=""
end
if addtag =~ /^none$/i then regwrite("EXCLUDED TAGNAMES", "") else regwrite("EXCLUDED TAGNAMES", "#{tags};#{addtag}") end
config("[+] Operation completed.")
end
# verbose mode
if key == 118 or key == 86
verbkey = "NOTHING"
puts "\n\nThe verbose mode lets you can see more info when leeching the group pages..."
while not verbkey =~ /^78$|^89$|^110$|^121$/
print "\n\nWant to use the verbose mode? [Y/N]: "
verbkey = get_character.to_s
end
if verbkey =~ /^89$|^121$/ then regwrite("VERBOSEMODE", "yes") else regwrite("VERBOSEMODE", "no") end
config("[+] Operation completed.")
end
# show the current settings
if key == 83 or key == 115
config("
Log file path : #{$logfile}
Leecher file path : #{$leecherfile}
Max. pages per group: #{$max_pages} pages
Min. track duration : #{$min_duration.to_s[0..-3]} min
Max. track duration : #{$max_duration.to_s[0..-3]} min
Max. track filesize : #{$max_size.to_s[0..-3]} mb
Verbose mode : #{$verbose}
Excluded extensions : #{$excluded_exts.gsub(";","|")}
Excluded tagnames : #{$excluded_tags.gsub(";","|")}
")
end
# reset the settings
if key == 114 or key == 82
resetkey = "NOTHING"
while not resetkey =~ /^78$|^89$|^110$|^121$/
print "\n(This not affect to group list)\n\nWant to reset all the settings, Are you sure? [Y/N]: "
resetkey = get_character.to_s
end
if resetkey =~ /^89$|^121$/
Win32::Registry::HKEY_CURRENT_USER.open("SOFTWARE\\Soundcloud Leecher\\", Win32::Registry::KEY_ALL_ACCESS) do |reg|
reg['LOGFILEPATH'] = 'DELETE'
reg.delete_value("LOGFILEPATH")
reg['LEECHERFILEPATH'] = 'DELETE'
reg.delete_value("LEECHERFILEPATH")
reg['MAXPAGES'] = 'DELETE'
reg.delete_value("MAXPAGES")
reg['MINDURATION'] = 'DELETE'
reg.delete_value("MINDURATION")
reg['MAXDURATION'] = 'DELETE'
reg.delete_value("MAXDURATION")
reg['MAXSIZE'] = 'DELETE'
reg.delete_value("MAXSIZE")
reg['VERBOSEMODE'] = 'DELETE'
reg.delete_value("VERBOSEMODE")
reg['EXCLUDED EXTENSIONS'] = 'DELETE'
reg.delete_value("EXCLUDED EXTENSIONS")
reg['EXCLUDED TAGNAMES'] = 'DELETE'
reg.delete_value("EXCLUDED TAGNAMES")
end
config("[+] Operation completed.")
end
end
# backup the settings
if key == 90 or key == 122
system %[ regedit /e \"%USERPROFILE%\\Desktop\\Soundcloud settings %DATE:/=-%.reg\" \"HKEY_CURRENT_USER\\Software\\Soundcloud Leecher\" ]
config("\n\n[+] Settings stored in #{ENV['USERPROFILE']}\\Desktop\\SoundCloud settings.reg")
end
# exit
if key == 69 or key == 101 then main() end
end
def regread(keyname)
Win32::Registry::HKEY_CURRENT_USER.open("SOFTWARE\\Soundcloud Leecher\\") do |reg| reg[keyname] end
end
def regwrite(keyname, value)
Win32::Registry::HKEY_CURRENT_USER.create("SOFTWARE\\Soundcloud Leecher\\") do |reg| reg[keyname, Win32::Registry::REG_SZ] = value end
end
def filewrite()
if not File.exist?($logfile) then File.open($logfile, "w") do |write| write.puts "SoundCloud leeched URLs\n" end end
if not File.open($logfile, "r").read[@track_url.split("/download").first.split("http://soundcloud.com/").last.gsub("-","").gsub("/","")]
if $verbose == "yes" then puts @info end
File.open($logfile, "a+") do |write| write.puts @track_url.split("/download").first.split("http://soundcloud.com/").last.gsub("-","").gsub("/","") end
File.open($leecherfile, "a+") do |write| write.puts "Added at #{Time.new.strftime("%H:%M")}: #{@track_url}" end
end
end
def get_downloads_list()
if $verbose == "no"
for name in $groups.split(";").each.sort do
@group = name.chomp
@page = 0
print "\n\n[+] Group: #{@group}\n[+] Leeching pages "
for i in 1..$max_pages do
@page = @page+1
@url = "http://soundcloud.com/groups/#{@group}/tracks?page=#{@page}"
print "#{@page}/#{$max_pages}..."
Net::HTTP.get_response(URI.parse(@url)).body.each_line do |line|
if (line['class="info-header"'])
@filename = line.split('</a').first.split('>').last.gsub(/ Artwork$/,'').gsub('"','"').gsub('&','&')
for excluded_tag in $excluded_tags.split(";").each do
if not excluded_tag == "" and @filename[/#{Regexp.escape(excluded_tag)}/i] then @downloadable = "no" end
end
end
if (line['button download']) then @downloadable = "yes" end
if (line['button download']) then @track_url = "http://soundcloud.com#{line.split('href="').last.split('"').first}\n" end
if (line['This track is not downloadable']) then @downloadable = "no" end
if (line['class="file-type"'])
@ext = line.split('</').first.split('>').last
for excluded_ext in $excluded_exts.split(";").each do
if not excluded_ext == "" and @ext =~ /^#{Regexp.escape(excluded_ext)}$/i then @downloadable = "no" end
end
end
if (line['class="content-size"'])
@size = line.split('</').first.split('>').last
if @size.gsub(".","").to_i > $max_size then @downloadable = "no" end
end
if (line['class="duration"']) then @duration = line.split('</').first.split('>').last.gsub(".","").to_i end
if (line['class="duration"']) and @downloadable == "yes" and @duration > $min_duration and @duration < $max_duration
filewrite()
end
end
end
end
end
if $verbose == "yes"
for name in $groups.split(";").each.sort do
@group = name.chomp
@page = 0
print "\n\n[+] Group: #{@group}\n[+] Leeching pages "
for i in 1..$max_pages do
@page = @page+1
@url = "http://soundcloud.com/groups/#{@group}/tracks?page=#{@page}"
print "#{@page}/#{$max_pages}..."
Net::HTTP.get_response(URI.parse(@url)).body.each_line do |line|
if (line['class="info-header"'])
@filename = line.split('</a').first.split('>').last.gsub(/ Artwork$/,'').gsub('"','"').gsub('&','&')
for excluded_tag in $excluded_tags.split(";").each do
if not excluded_tag == "" and @filename[/#{Regexp.escape(excluded_tag)}/i] then @downloadable = "no" end
end
end
if (line['button download']) then @downloadable = "yes" end
if (line['button download']) then @track_url = "http://soundcloud.com#{line.split('href="').last.split('"').first}\n" end
if (line['This track is not downloadable']) then @downloadable = "no" end
if (line['class="file-type"'])
@ext = line.split('</').first.split('>').last
for excluded_ext in $excluded_exts.split(";").each do
if not excluded_ext == "" and @ext =~ /^#{Regexp.escape(excluded_ext)}$/i then @downloadable = "no" end
end
end
if (line['class="content-size"'])
@size = line.split('</').first.split('>').last
if @size.gsub(".","").to_i > $max_size then @downloadable = "no" end
end
if (line['class="duration"']) then @duration = line.split('</').first.split('>').last end
if (line['class="duration"']) and @downloadable == "yes" and @duration.gsub(".","").to_i > $min_duration and @duration.gsub(".","").to_i < $max_duration
@info = "\n\nFilename : #{@filename} \nExtension: #{@ext} \nFilesize : #{@size} \nDuration : #{@duration} minutes\nURL : #{@track_url}\n"
filewrite()
end
end
end
end
end
end
logo()
get_settings()
main()
__END__