class Drum::CLI

The command line interface for drum.

Public Class Methods

exit_on_failure?() click to toggle source
# File lib/drum.rb, line 48
def self.exit_on_failure?
  true
end
new(*args) click to toggle source

Sets up the CLI by registering the services.

Calls superclass method
# File lib/drum.rb, line 23
def initialize(*args)
  super

  @hl = HighLine.new

  # Set up .drum directory
  @dot_dir = Pathname.new(Dir.home) / '.drum'
  @dot_dir.mkdir unless @dot_dir.directory?

  @cache_dir = @dot_dir / 'cache'
  @cache_dir.mkdir unless @cache_dir.directory?

  # Declare services in descending order of parse priority
  @services = [
    MockService.new,
    StdioService.new,
    AppleMusicService.new(@cache_dir),
    SpotifyService.new(@cache_dir),
    # The file service should be last since it may
    # successfully parse refs that overlap with other
    # services.
    FileService.new
  ].map { |s| [s.name, s] }.to_h
end

Public Instance Methods

__print_version() click to toggle source

Prints the version.

@return [void]

# File lib/drum.rb, line 246
def __print_version
  log.all VERSION
end
confirm(prompt) click to toggle source

Prompts the user for confirmation.

@param [String] prompt The message to be displayed

# File lib/drum.rb, line 86
def confirm(prompt)
  answer = @hl.ask "#{prompt} [y/n]"
  unless answer == 'y'
    log.info 'Okay, exiting.'
    exit
  end
end
cp(raw_src_ref, raw_dest_ref) click to toggle source

Copies a playlist from the source to the given destination.

@param [String] raw_src_ref The source playlist ref. @param [String] raw_dest_ref The destination playlist ref. @return [void]

# File lib/drum.rb, line 146
def cp(raw_src_ref, raw_dest_ref)
  src_ref = self.parse_ref(raw_src_ref)
  dest_ref = self.parse_ref(raw_dest_ref)

  if src_ref.nil?
    raise "Could not parse src ref: #{raw_src_ref}"
  end
  if dest_ref.nil?
    raise "Could not parse dest ref: #{raw_dest_ref}"
  end

  self.with_service(src_ref.service_name) do |src_name, src_service|
    self.with_service(dest_ref.service_name) do |dest_name, dest_service|
      log.info "Copying from #{src_name} to #{dest_name}..."

      # TODO: Should we handle merging at all or just copy (as currently done)?

      playlists = src_service.download(src_ref).lazy

      unless playlists.size.nil?
        bar = ProgressBar.new(playlists.size)

        # Redirect log output so the bar stays at the bottom
        log.output = bar.method(:puts)
      end

      # Apply transformations to the downloaded playlists.
      # Note that we use 'map' despite mutating the playlists
      # in-place to preserve laziness in the iteration.

      playlists = playlists.map do |playlist|
        bar&.increment!

        if options[:group_by_author]
          author_name = playlist.author_id.try { |id| playlist.users[id] }&.display_name || 'Other'
          playlist.path.unshift(author_name)
        end

        if options[:recase_paths]
          casing = options[:recase_paths]
          playlist.path.map! do |n|
            case casing
            when 'kebabcase' then n.kebabcase
            when 'startcase' then n.startcase
            when 'camelcase' then n.camelcase
            when 'pascalcase' then n.pascalcase
            else raise "Casing '#{casing}' is not implemented (yet)!"
            end
          end
        end

        if options[:note_date]
          unless playlist.description.end_with?("\n") || playlist.description.empty?
            playlist.description += "\n"
          end
          playlist.description += Time.now.strftime("Pushed with Drum on %Y-%m-%d")
        end

        playlist
      end

      dest_service.upload(dest_ref, playlists)
    end
  end
end
parse_ref(raw) click to toggle source

Parses a ref using the registered services.

@param [String] raw The raw ref to parse @return [optional, Ref] The ref, if parsed successfully with any of the services

# File lib/drum.rb, line 72
def parse_ref(raw)
  raw_ref = RawRef.parse(raw)
  @services.each_value do |service|
    ref = service.parse_ref(raw_ref)
    unless ref.nil?
      return ref
    end
  end
  return nil
end
rm(raw_ref) click to toggle source

Removes a playlist from the corresponding service.

@param [String] raw_ref The playlist ref. @return [void]

# File lib/drum.rb, line 218
def rm(raw_ref)
  ref = self.parse_ref(raw_ref)

  if ref.nil?
    raise "Could not parse ref: #{raw_ref}"
  end

  self.with_service(ref.service_name) do |name, service|
    log.info "Removing from #{name}..."
    service.remove(ref)
  end
end
services() click to toggle source

Lists available services.

@return [void]

# File lib/drum.rb, line 236
def services
  log.info @services.each_key.to_a.join("\n")
end
show(raw_ref) click to toggle source

Previews a playlist in a simplified format.

@param [String] raw_ref The (raw) playlist ref.

# File lib/drum.rb, line 100
def show(raw_ref)
  ref = self.parse_ref(raw_ref)

  if ref.nil?
    raise "Could not parse ref: #{raw_ref}"
  end

  self.with_service(ref.service_name) do |name, service|
    playlists = service.download(ref)
    
    playlists.each do |playlist|
      log.all({
        'name' => playlist.name,
        'description' => playlist&.description,
        'tracks' => playlist.tracks.each_with_index.map do |track, i|
          artists = (track.artist_ids&.filter_map { |id| playlist.artists[id]&.name } || []).join(', ')
          "#{i + 1}. #{artists} - #{track.name}"
        end
      }.compact.to_yaml)
    end
  end
end
with_service(raw_name) { |name, service| ... } click to toggle source

Performs a block with the given service, if registered.

@yield [name, service] The block to run @yieldparam [String] name The name of the service @yieldparam [Service] service The service @param [String] raw_name The name of the service

# File lib/drum.rb, line 59
def with_service(raw_name)
  name = raw_name.downcase
  service = @services[name]
  if service.nil?
    raise "Sorry, #{name} is not a valid service! Try one of these: #{@services.keys}"
  end
  yield(name, service)
end