class ComerDeTapas::Client

Attributes

episodes[R]
feed_xml[R]

Public Instance Methods

download(force: false) click to toggle source
# File lib/comer_de_tapas/client.rb, line 20
def download(force: false)
  fetch_episodes!(force)
  prepare_save_folder!
  authenticate
  load_episodes
  download_all_tapas!
end
init!() click to toggle source

Initialize comer de tapas $ mkdir -p ~/.rubytapas/ $ touch ~/.rubytapas/.credentials

# File lib/comer_de_tapas/client.rb, line 12
def init!
  if RUBYTAPAS_DIR.exist? && CREDENTIAL_FILE.exist?
    abort "Credentials found. type `comer_de_tapas download` to download."
  end
  create_rubytapas_files!
  puts "~/.rubytapas/.credentials folder and file has been created."
end

Private Instance Methods

attachments(links) click to toggle source

Given links, parse to { filename: '123.rb', '/subscriber/download?file_id=34567' } @return [Array<Hash>]

# File lib/comer_de_tapas/client.rb, line 199
def attachments links
  links.each_with_object([]) do |link, episode|
    if link['href'] =~ /rubytapas.dpdcart.com\/subscriber\/download/
      episode << { filename: link.content, link: link['href'].gsub(/#{BASE_URL}/, '') }
    end
  end
end
authenticate() click to toggle source

Authenticate and return Cookie

# File lib/comer_de_tapas/client.rb, line 58
def authenticate
  @cookie ||= HTTP.post(LOGIN_URL, form_params).headers["Set-Cookie"]
end
authenticate_params() click to toggle source

Params for basic authentication

# File lib/comer_de_tapas/client.rb, line 162
def authenticate_params
  { user: credentials[:email], pass: credentials[:password] }
end
create_rubytapas_files!() click to toggle source

mkdir -p ~/.rubytapas touch ~/.rubytapas/.credentials

# File lib/comer_de_tapas/client.rb, line 134
def create_rubytapas_files!
  RUBYTAPAS_DIR.mkpath
  FileUtils.touch CREDENTIAL_FILE
  CREDENTIAL_FILE.write credential_template
end
credential_template() click to toggle source

Use to create empty credential file

# File lib/comer_de_tapas/client.rb, line 141
def credential_template
  require "yaml"
  {"credentials"=>[{"email"=>nil}, {"password"=>nil}, {"save_path"=>nil}]}.to_yaml
end
credentials() click to toggle source

RubyTapas subscription credentials (~/.rubytapas/.credentials) @return [Hash]

# File lib/comer_de_tapas/client.rb, line 168
def credentials
  Subscription.new.to_h
end
dasherize_file_name(file_name) click to toggle source

Before: 999 Array.first, Foo::Bar, Class<<self After: 999-array-first-foo-bar-class<<self

# File lib/comer_de_tapas/client.rb, line 221
def dasherize_file_name file_name
  file_name.downcase.gsub('&lt;', '<').gsub(/[^\w<>#?!$]+/, '-')
end
download_all_tapas!() click to toggle source

Download episode in parallel using Actors Powered by Celluloid::IO

# File lib/comer_de_tapas/client.rb, line 69
def download_all_tapas!
  episodes.each do |episode|
    FileUtils.cd(save_folder) do
      episode_title = episode["title"]
      puts "Downloading Epsiode #{episode_title}..."

      episode_folder = save_folder.join(sanitized episode_title)

      FileUtils.mkdir_p episode_folder unless episode_folder.exist?

      FileUtils.cd episode_folder do
        fetcher = Fetcher.new
        file_and_links = episode["links"]
        downloadables = find_downloadables file_and_links, fetcher

        if downloadables.all? &:nil?
          puts "Already downloaded, skip."
          next
        end

        download_parallelly! downloadables
        puts "Episode #{episode_title} content all saved."
      end
    end
  end
end
download_parallelly!(downloadables) click to toggle source

Download episode all attachments compact to remove attachment that already downloaded @param [Array] downloadables

# File lib/comer_de_tapas/client.rb, line 101
def download_parallelly! downloadables
  downloadables.compact.each do |file, future|
    puts "Downloading #{file}..."
    response = future.value.to_s
    IO.write file, response
    puts "#{file} saved."
  end
end
fetch_episodes!(force=false) click to toggle source

Fetch latest feed on rubytapas.dpdcart.com Parse it to episode, save episodes data as json to ~/.rubytapas.json

# File lib/comer_de_tapas/client.rb, line 32
def fetch_episodes! force=false
  return puts "Use cached episode data." if fresh? && !force

  puts "Force fetching. Getting latest Ruby Tapas..." if force
  puts "Fetching episodes..."
  if get_feed_with_basic_auth
    save_feed_data parse_xml_feed
    puts "Episodes successfully fetched and saved."
  end
end
find_downloadables(file_and_links, fetcher) click to toggle source

Find episode's attachment that has not been downloaded @param [Array] file_and_links

# File lib/comer_de_tapas/client.rb, line 112
def find_downloadables file_and_links, fetcher
  file_and_links.map do |file_and_link|
    file_name = file_and_link["filename"]

    # mp4 less than 3MB considered as unfinished. Redownload it.
    FileUtils.rm file_name if small_mp4? file_name

    next if File.exist? file_name

    q, v = file_and_link["link"].split("?").last.split("=")
    [file_name, fetcher.future.fetch(DOWNLOAD_URL, cookie, { q => v })]
  end
end
form_params() click to toggle source

Form params to post for HTTP @return [Hash]

# File lib/comer_de_tapas/client.rb, line 227
def form_params
  { form: { username: credentials[:email], password: credentials[:password] } }
end
fresh?() click to toggle source

If the episodes json was made of 259_200.seconds.ago (3 days) @return [Boolean] true if episodes.json creation time < 3 days

# File lib/comer_de_tapas/client.rb, line 233
def fresh?
  return false unless EPISODES_JSON_FILE.exist?
  Time.now - EPISODES_JSON_FILE.ctime < 259_200
end
get_feed_with_basic_auth() click to toggle source

Get raw feed data (XML), RSS

# File lib/comer_de_tapas/client.rb, line 147
def get_feed_with_basic_auth
  puts "Authorizing..."
  response = HTTP.auth(:basic, authenticate_params).get(FEED_URL).to_s

  if response.empty?
    abort "Authroized failed. Please check your email & password in #{CREDENTIAL_FILE}"
  end

  if @feed_xml = response
    puts "Authroized."
    return true
  end
end
load_episodes() click to toggle source

Load episodes json from EPISODES_JSON_FILE

# File lib/comer_de_tapas/client.rb, line 63
def load_episodes
  @episodes ||= JSON.parse(EPISODES_JSON_FILE.read)
end
parse_xml_feed() click to toggle source

Parse raw feed data (XML), retrive episode's title and links. @return [Array<Hash>]

# File lib/comer_de_tapas/client.rb, line 174
def parse_xml_feed
  puts 'Parsing Episodes...'

  require 'nokogiri'
  items = Nokogiri::XML(feed_xml).xpath('//item')

  epsiode_data = items.map do |item|
    children = item.children

    title       = (children / 'title').first.child.content
    description = (children / 'description').first.child.content

    links = Nokogiri::HTML(description).css('ul li a')

    { title: title, links: attachments(links) }
  end

  puts 'Episodes parsed successfully.'

  return epsiode_data
end
prepare_save_folder!() click to toggle source

Create user specified folder: credentials

# File lib/comer_de_tapas/client.rb, line 44
def prepare_save_folder!
  return puts "#{save_folder} found." if save_folder.exist?

  save_folder.mkpath
  puts "#{save_folder} created."
end
sanitized(title) click to toggle source

Return each episode's folder name

# File lib/comer_de_tapas/client.rb, line 215
def sanitized title
  dasherize_file_name(title)
end
save_feed_data(feed) click to toggle source

Write episodes data to ~/.rubytapas/episodes.json

# File lib/comer_de_tapas/client.rb, line 208
def save_feed_data feed
  puts 'Saving episodes data to ~/.rubytapas/episodes.json...'
  EPISODES_JSON_FILE.write feed.to_json
  puts 'Saved.'
end
save_folder() click to toggle source

User spefified folder to save episodes. @return [Pathname]

# File lib/comer_de_tapas/client.rb, line 53
def save_folder
  Pathname(credentials[:save_path]).expand_path
end
small_mp4?(file) click to toggle source

Return true if file is a mp4 and its size less than 3MB.

# File lib/comer_de_tapas/client.rb, line 127
def small_mp4?(file)
  return false unless File.exist? file
  File.size(file) < 3*1024*1024 && File.extname(file) == ".mp4"
end