Class: Dropcaster::Channel

Inherits:
Object
  • Object
show all
Includes:
Logging, ERB::Util
Defined in:
lib/dropcaster/channel.rb

Overview

Represents a podcast feed in the RSS 2.0 format

Constant Summary collapse

STORAGE_UNITS =
%w[Byte KB MB GB TB].freeze
MAX_KEYWORD_COUNT =
12

Instance Attribute Summary

Attributes included from Logging

#logger

Instance Method Summary collapse

Constructor Details

#initialize(sources, attributes) ⇒ Channel

Instantiate a new Channel object. sources must be present and can be a String or Array of Strings, pointing to a one or more directories or MP3 files.

attributes is a hash with all attributes for the channel. The following attributes are mandatory when a new channel is created:

  • :title - Title (name) of the podcast

  • :url - URL to the podcast

  • :description - Short description of the podcast (a few words)


28
29
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
# File 'lib/dropcaster/channel.rb', line 28

def initialize(sources, attributes)
  # Assert mandatory attributes
  %i[title url description].each { |attr|
    raise MissingAttributeError.new(attr) if attributes[attr].blank?
  }

  @attributes = attributes
  @attributes[:explicit] = yes_no_or_input(attributes[:explicit])
  @source_files = []

  # if (sources.respond_to?(:each)) # array
  if sources.is_a? Array
    sources.each do |src|
      add_files(src)
    end
  else
    # single file or directory
    add_files(sources)
  end

  # If not absolute, prepend the image URL with the channel's base to make an absolute URL
  unless @attributes[:image_url].blank? || @attributes[:image_url] =~ /^https?:/
    logger.info("Channel image URL '#{@attributes[:image_url]}' is relative, so we prepend it with the channel URL '#{@attributes[:url]}'")
    @attributes[:image_url] = (URI.parse(@attributes[:url]) + @attributes[:image_url]).to_s
  end

  # If enclosures_url is not given, take the channel URL as a base.
  if @attributes[:enclosures_url].blank?
    logger.info("No enclosure URL given, using the channel's enclosure URL")
    @attributes[:enclosures_url] = @attributes[:url]
  end

  # Warn if keyword count is larger than recommended
  assert_keyword_count(@attributes[:keywords])

  channel_template = @attributes[:channel_template] || File.join(File.dirname(__FILE__), '..', '..', 'templates', 'channel.rss.erb')

  begin
    @erb_template = ERB.new(IO.read(channel_template), 0, '%<>')
  rescue Errno::ENOENT => e
    raise TemplateNotFoundError.new(e.message)
  end
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(meth, *args) ⇒ Object

Delegate all unknown methods to @attributes

rubocop:disable Style/MethodMissing


146
147
148
149
150
151
152
153
# File 'lib/dropcaster/channel.rb', line 146

def method_missing(meth, *args)
  m = meth.id2name
  if /=$/.match?(m)
    @attributes[m.chop.to_sym] = (args.length < 2 ? args[0] : args)
  else
    @attributes[m.to_sym]
  end
end

Instance Method Details

#humanize_size(number) ⇒ Object

Fixed version of gist.github.com/260184


125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
# File 'lib/dropcaster/channel.rb', line 125

def humanize_size(number)
  return nil if number.nil?

  if number.to_i < 1024
    unit = number > 1 ? 'Bytes' : 'Byte'
  else
    max_exp  = STORAGE_UNITS.size - 1
    number   = Float(number)
    exponent = (Math.log(number) / Math.log(1024)).to_i # Convert to base 1024
    exponent = max_exp if exponent > max_exp # we need this to avoid overflow for the highest unit
    number  /= 1024**exponent
    unit = STORAGE_UNITS[exponent]
  end

  '%n %u'.gsub(/%n/, number.to_i.to_s).gsub(/%u/, unit)
end

#humanize_time(secs) ⇒ Object


115
116
117
118
119
120
121
122
# File 'lib/dropcaster/channel.rb', line 115

def humanize_time(secs)
  [[60, :s], [60, :m], [24, :h], [1000, :d]].map { |count, name|
    if secs.positive?
      secs, n = secs.divmod(count)
      "#{n.to_i}#{name}"
    end
  }.compact.reverse.join(' ')
end

#itemsObject

Returns all items (episodes) of this channel, ordered by newest-first.


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
112
# File 'lib/dropcaster/channel.rb', line 84

def items
  all_items = []
  @source_files.each { |src|
    item = Item.new(src)

    logger.debug("Adding new item from file #{src}")

    # set author and image_url from channel if empty
    if item.artist.blank?
      logger.info("#{src} has no artist, using the channel's author")
      item.tag.artist = @attributes[:author]
    end

    if item.image_url.blank?
      logger.info("#{src} has no image URL set, using the channel's image URL")
      item.image_url = @attributes[:image_url]
    end

    # construct absolute URL, based on the channel's enclosures_url attribute
    item.url = URI.parse(enclosures_url) + item.file_name

    # Warn if keyword count is larger than recommended
    assert_keyword_count(item.keywords)

    all_items << item
  }

  all_items.sort { |x, y| y.pub_date <=> x.pub_date }
end

#respond_to_missing?(meth) ⇒ Boolean

Returns:

  • (Boolean)

155
156
157
# File 'lib/dropcaster/channel.rb', line 155

def respond_to_missing?(meth, *)
  /=$/.match?(meth.id2name) || super
end

#to_rssObject

Returns this channel as an RSS representation. The actual rendering is done with the help of an ERB template. By default, it is expected as ../../templates/channel.rss.erb (relative) to channel.rb.


77
78
79
# File 'lib/dropcaster/channel.rb', line 77

def to_rss
  @erb_template.result(binding)
end