dropcaster 0.0.2 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.document +5 -5
- data/Gemfile +11 -11
- data/Gemfile.lock +26 -26
- data/LICENSE.txt +20 -20
- data/README.md +155 -147
- data/Rakefile +47 -47
- data/TODO +14 -13
- data/VERSION +1 -1
- data/bin/dropcaster +117 -111
- data/bin/lstags +45 -45
- data/doc/sample-channel.yml +79 -77
- data/doc/sample-sidecar.yml +19 -19
- data/dropcaster.gemspec +88 -87
- data/lib/dropcaster/channel.rb +96 -86
- data/lib/dropcaster/channel_file_locator.rb +48 -48
- data/lib/dropcaster/errors.rb +25 -19
- data/lib/dropcaster/hashkeys.rb +12 -12
- data/lib/dropcaster/item.rb +43 -43
- data/lib/dropcaster.rb +18 -18
- data/templates/{channel.rss.erb → iTunes.rss.erb} +57 -57
- data/test/extensions/windows.rb +12 -0
- data/test/fixtures/channel.yml +13 -14
- data/test/helper.rb +14 -10
- data/test/unit/test_app.rb +84 -59
- data/test/unit/test_channel.rb +32 -32
- data/test/unit/test_channel_locator.rb +91 -91
- data/test/unit/test_channel_xml.rb +87 -69
- data/test/unit/test_item.rb +63 -61
- metadata +24 -19
data/dropcaster.gemspec
CHANGED
@@ -1,87 +1,88 @@
|
|
1
|
-
# Generated by jeweler
|
2
|
-
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
-
# Instead, edit Jeweler::Tasks in
|
4
|
-
# -*- encoding: utf-8 -*-
|
5
|
-
|
6
|
-
Gem::Specification.new do |s|
|
7
|
-
s.name = %q{dropcaster}
|
8
|
-
s.version = "0.0.
|
9
|
-
|
10
|
-
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
-
s.authors = [
|
12
|
-
s.date = %q{2011-10-
|
13
|
-
s.description = %q{Dropcaster is a podcast feed generator for the command line. It is most simple to use with Dropbox, but works equally well with any other hoster.}
|
14
|
-
s.email = %q{nerab@gmx.at}
|
15
|
-
s.executables = [
|
16
|
-
s.extra_rdoc_files = [
|
17
|
-
"LICENSE.txt",
|
18
|
-
"README.md",
|
19
|
-
"TODO"
|
20
|
-
]
|
21
|
-
s.files = [
|
22
|
-
".document",
|
23
|
-
"Gemfile",
|
24
|
-
"Gemfile.lock",
|
25
|
-
"LICENSE.txt",
|
26
|
-
"README.md",
|
27
|
-
"Rakefile",
|
28
|
-
"TODO",
|
29
|
-
"VERSION",
|
30
|
-
"bin/dropcaster",
|
31
|
-
"bin/lstags",
|
32
|
-
"doc/infoPane.png",
|
33
|
-
"doc/lyricsPane.png",
|
34
|
-
"doc/sample-channel.yml",
|
35
|
-
"doc/sample-sidecar.yml",
|
36
|
-
"doc/videoPane.png",
|
37
|
-
"dropcaster.gemspec",
|
38
|
-
"lib/dropcaster.rb",
|
39
|
-
"lib/dropcaster/channel.rb",
|
40
|
-
"lib/dropcaster/channel_file_locator.rb",
|
41
|
-
"lib/dropcaster/errors.rb",
|
42
|
-
"lib/dropcaster/hashkeys.rb",
|
43
|
-
"lib/dropcaster/item.rb",
|
44
|
-
"templates/
|
45
|
-
"test/
|
46
|
-
"test/fixtures/
|
47
|
-
"test/
|
48
|
-
"test/
|
49
|
-
"test/unit/
|
50
|
-
"test/unit/
|
51
|
-
"test/unit/
|
52
|
-
"test/unit/
|
53
|
-
|
54
|
-
|
55
|
-
s.
|
56
|
-
s.
|
57
|
-
s.
|
58
|
-
s.
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
s.add_runtime_dependency(%q<
|
66
|
-
s.
|
67
|
-
s.add_development_dependency(%q<
|
68
|
-
s.add_development_dependency(%q<
|
69
|
-
s.add_development_dependency(%q<
|
70
|
-
|
71
|
-
|
72
|
-
s.add_dependency(%q<
|
73
|
-
s.add_dependency(%q<
|
74
|
-
s.add_dependency(%q<
|
75
|
-
s.add_dependency(%q<
|
76
|
-
s.add_dependency(%q<
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
s.add_dependency(%q<
|
81
|
-
s.add_dependency(%q<
|
82
|
-
s.add_dependency(%q<
|
83
|
-
s.add_dependency(%q<
|
84
|
-
s.add_dependency(%q<
|
85
|
-
|
86
|
-
end
|
87
|
-
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in rakefile, and run 'rake gemspec'
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{dropcaster}
|
8
|
+
s.version = "0.0.3"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["nerab"]
|
12
|
+
s.date = %q{2011-10-13}
|
13
|
+
s.description = %q{Dropcaster is a podcast feed generator for the command line. It is most simple to use with Dropbox, but works equally well with any other hoster.}
|
14
|
+
s.email = %q{nerab@gmx.at}
|
15
|
+
s.executables = ["lstags", "dropcaster", "dropcaster", "lstags"]
|
16
|
+
s.extra_rdoc_files = [
|
17
|
+
"LICENSE.txt",
|
18
|
+
"README.md",
|
19
|
+
"TODO"
|
20
|
+
]
|
21
|
+
s.files = [
|
22
|
+
".document",
|
23
|
+
"Gemfile",
|
24
|
+
"Gemfile.lock",
|
25
|
+
"LICENSE.txt",
|
26
|
+
"README.md",
|
27
|
+
"Rakefile",
|
28
|
+
"TODO",
|
29
|
+
"VERSION",
|
30
|
+
"bin/dropcaster",
|
31
|
+
"bin/lstags",
|
32
|
+
"doc/infoPane.png",
|
33
|
+
"doc/lyricsPane.png",
|
34
|
+
"doc/sample-channel.yml",
|
35
|
+
"doc/sample-sidecar.yml",
|
36
|
+
"doc/videoPane.png",
|
37
|
+
"dropcaster.gemspec",
|
38
|
+
"lib/dropcaster.rb",
|
39
|
+
"lib/dropcaster/channel.rb",
|
40
|
+
"lib/dropcaster/channel_file_locator.rb",
|
41
|
+
"lib/dropcaster/errors.rb",
|
42
|
+
"lib/dropcaster/hashkeys.rb",
|
43
|
+
"lib/dropcaster/item.rb",
|
44
|
+
"templates/iTunes.rss.erb",
|
45
|
+
"test/extensions/windows.rb",
|
46
|
+
"test/fixtures/channel.yml",
|
47
|
+
"test/fixtures/iTunes.mp3",
|
48
|
+
"test/helper.rb",
|
49
|
+
"test/unit/test_app.rb",
|
50
|
+
"test/unit/test_channel.rb",
|
51
|
+
"test/unit/test_channel_locator.rb",
|
52
|
+
"test/unit/test_channel_xml.rb",
|
53
|
+
"test/unit/test_item.rb"
|
54
|
+
]
|
55
|
+
s.homepage = %q{http://nerab.github.com/dropcaster}
|
56
|
+
s.licenses = ["MIT"]
|
57
|
+
s.require_paths = ["lib"]
|
58
|
+
s.rubygems_version = %q{1.6.1}
|
59
|
+
s.summary = %q{Simple Podcast Publishing with Dropbox}
|
60
|
+
|
61
|
+
if s.respond_to? :specification_version then
|
62
|
+
s.specification_version = 3
|
63
|
+
|
64
|
+
if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
|
65
|
+
s.add_runtime_dependency(%q<ruby-mp3info>, [">= 0"])
|
66
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 0"])
|
67
|
+
s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
|
68
|
+
s.add_development_dependency(%q<jeweler>, ["~> 1.6.4"])
|
69
|
+
s.add_development_dependency(%q<libxml-ruby>, [">= 0"])
|
70
|
+
s.add_development_dependency(%q<rdoc>, [">= 0"])
|
71
|
+
else
|
72
|
+
s.add_dependency(%q<ruby-mp3info>, [">= 0"])
|
73
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
74
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
75
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
76
|
+
s.add_dependency(%q<libxml-ruby>, [">= 0"])
|
77
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
78
|
+
end
|
79
|
+
else
|
80
|
+
s.add_dependency(%q<ruby-mp3info>, [">= 0"])
|
81
|
+
s.add_dependency(%q<activesupport>, [">= 0"])
|
82
|
+
s.add_dependency(%q<bundler>, ["~> 1.0.0"])
|
83
|
+
s.add_dependency(%q<jeweler>, ["~> 1.6.4"])
|
84
|
+
s.add_dependency(%q<libxml-ruby>, [">= 0"])
|
85
|
+
s.add_dependency(%q<rdoc>, [">= 0"])
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
data/lib/dropcaster/channel.rb
CHANGED
@@ -1,86 +1,96 @@
|
|
1
|
-
require 'erb'
|
2
|
-
require 'uri'
|
3
|
-
|
4
|
-
module Dropcaster
|
5
|
-
#
|
6
|
-
# Represents a podcast feed in the RSS 2.0 format
|
7
|
-
#
|
8
|
-
class Channel < DelegateClass(Hash)
|
9
|
-
include HashKeys
|
10
|
-
|
11
|
-
# Instantiate a new Channel object. +sources+ must be present and can be a String or Array
|
12
|
-
# of Strings, pointing to a one or more directories or MP3 files.
|
13
|
-
#
|
14
|
-
# +options+ is a hash with all attributes for the channel. The following attributes are
|
15
|
-
# mandatory when a new channel is created:
|
16
|
-
#
|
17
|
-
# * <tt>:title</tt> - Title (name) of the podcast
|
18
|
-
# * <tt>:url</tt> - URL to the podcast
|
19
|
-
# * <tt>:description</tt> - Short description of the podcast (a few words)
|
20
|
-
#
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
self.
|
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
|
-
item
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
1
|
+
require 'erb'
|
2
|
+
require 'uri'
|
3
|
+
|
4
|
+
module Dropcaster
|
5
|
+
#
|
6
|
+
# Represents a podcast feed in the RSS 2.0 format
|
7
|
+
#
|
8
|
+
class Channel < DelegateClass(Hash)
|
9
|
+
include HashKeys
|
10
|
+
|
11
|
+
# Instantiate a new Channel object. +sources+ must be present and can be a String or Array
|
12
|
+
# of Strings, pointing to a one or more directories or MP3 files.
|
13
|
+
#
|
14
|
+
# +options+ is a hash with all attributes for the channel. The following attributes are
|
15
|
+
# mandatory when a new channel is created:
|
16
|
+
#
|
17
|
+
# * <tt>:title</tt> - Title (name) of the podcast
|
18
|
+
# * <tt>:url</tt> - URL to the podcast
|
19
|
+
# * <tt>:description</tt> - Short description of the podcast (a few words)
|
20
|
+
#
|
21
|
+
def initialize(sources, options)
|
22
|
+
super(Hash.new)
|
23
|
+
|
24
|
+
# Assert mandatory options
|
25
|
+
[:title, :url, :description].each{|attr|
|
26
|
+
raise MissingAttributeError.new(attr) if options[attr].blank?
|
27
|
+
}
|
28
|
+
|
29
|
+
self.merge!(options)
|
30
|
+
self.categories = Array.new
|
31
|
+
@source_files = Array.new
|
32
|
+
|
33
|
+
if (sources.respond_to?(:each)) # array
|
34
|
+
sources.each{|src|
|
35
|
+
add_files(src)
|
36
|
+
}
|
37
|
+
else
|
38
|
+
# single file or directory
|
39
|
+
add_files(src)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Prepend the image URL with the channel's base to make an absolute URL
|
43
|
+
self.image_url = URI.join(self.url, self.image_url).to_s unless self.image_url.blank? || self.image_url =~ /^https?:/
|
44
|
+
|
45
|
+
channel_template = self.channel_template || File.join(File.dirname(__FILE__), '..', '..', 'templates', 'iTunes.rss.erb')
|
46
|
+
|
47
|
+
begin
|
48
|
+
@erb_template = ERB.new(File.new(channel_template), 0, "%<>")
|
49
|
+
rescue Errno::ENOENT => e
|
50
|
+
raise TemplateNotFoundError.new(e.message)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
#
|
55
|
+
# Returns this channel as an RSS representation. The actual rendering is done with the help
|
56
|
+
# of an ERB template. By default, it is expected as ../../templates/channel.rss.erb (relative)
|
57
|
+
# to channel.rb.
|
58
|
+
#
|
59
|
+
def to_rss
|
60
|
+
@erb_template.result(binding)
|
61
|
+
end
|
62
|
+
|
63
|
+
#
|
64
|
+
# Returns all items (episodes) of this channel, ordered by newest-first.
|
65
|
+
#
|
66
|
+
def items
|
67
|
+
all_items = Array.new
|
68
|
+
@source_files.each{|src|
|
69
|
+
item = Item.new(src)
|
70
|
+
|
71
|
+
# set author and image_url from channel if empty
|
72
|
+
item.tag.artist = self.author if item.artist.blank?
|
73
|
+
item.image_url = self.image_url if item.image_url.blank?
|
74
|
+
|
75
|
+
# construct absolute URL, based on the channel's enclosures_url attribute
|
76
|
+
# If enclosures_url is not given, take the channel URL as a base.
|
77
|
+
self.enclosures_url = self.url if self.enclosures_url.blank?
|
78
|
+
self.enclosures_url << '/' unless self.enclosures_url =~ /\/$/
|
79
|
+
item.url = URI.join(URI.escape(self.enclosures_url), URI.escape(item.file_name))
|
80
|
+
|
81
|
+
all_items << item
|
82
|
+
}
|
83
|
+
|
84
|
+
all_items.sort{|x, y| y.pub_date <=> x.pub_date}
|
85
|
+
end
|
86
|
+
|
87
|
+
private
|
88
|
+
def add_files(src)
|
89
|
+
if File.directory?(src)
|
90
|
+
@source_files.concat(Dir.glob(File.join(src, '*.mp3')))
|
91
|
+
else
|
92
|
+
@source_files << src
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -1,48 +1,48 @@
|
|
1
|
-
module Dropcaster
|
2
|
-
#
|
3
|
-
# Encapsulates the strategy how to find the channel definition file
|
4
|
-
#
|
5
|
-
class ChannelFileLocator
|
6
|
-
class << self
|
7
|
-
#
|
8
|
-
# Locates the channel definition file based on the <tt>sources</tt> directory.
|
9
|
-
#
|
10
|
-
# * If <tt>sources</tt> is a single file name, the channel definition file is expected
|
11
|
-
# as channel.yml in the same directory.
|
12
|
-
#
|
13
|
-
# * If <tt>sources</tt> is a single directory name, the channel definition file is
|
14
|
-
# expected as channel.yml in that directory.
|
15
|
-
#
|
16
|
-
# * If <tt>sources</tt> is an array of file names, the channel definition file is
|
17
|
-
# expected as channel.yml in the directory common to all files. If the files are
|
18
|
-
# located in more than one directory, an AmbiguousSourcesError is raised. In that
|
19
|
-
# case, the caller should specify the channel.yml as command line parameter.
|
20
|
-
#
|
21
|
-
# * If <tt>sources</tt> is an array with more than a single directory name, an
|
22
|
-
# AmbiguousSourcesError is raised. In that case, the caller should specify the
|
23
|
-
# channel.yml as command line parameter.
|
24
|
-
#
|
25
|
-
def locate(sources)
|
26
|
-
channel_source_dir = nil
|
27
|
-
|
28
|
-
if sources.respond_to?(:at)
|
29
|
-
# More than one source given. Check that they are all in the same directory.
|
30
|
-
distinct_dirs = sources.collect{|source| File.dirname(source)}.uniq
|
31
|
-
|
32
|
-
if 1 == distinct_dirs.size
|
33
|
-
# If all are the in same directory, use that as source directory where channel.yml is expected.
|
34
|
-
channel_source_dir = distinct_dirs.first
|
35
|
-
else
|
36
|
-
# Since no channel_file was specified at the command line, throw and quit
|
37
|
-
raise AmbiguousSourcesError.new(sources)
|
38
|
-
end
|
39
|
-
else
|
40
|
-
# If a single file or directory is given, use that as source directory where channel.yml is expected.
|
41
|
-
channel_source_dir = File.dirname(sources)
|
42
|
-
end
|
43
|
-
|
44
|
-
File.join(channel_source_dir, CHANNEL_YML)
|
45
|
-
end
|
46
|
-
end
|
47
|
-
end
|
48
|
-
end
|
1
|
+
module Dropcaster
|
2
|
+
#
|
3
|
+
# Encapsulates the strategy how to find the channel definition file
|
4
|
+
#
|
5
|
+
class ChannelFileLocator
|
6
|
+
class << self
|
7
|
+
#
|
8
|
+
# Locates the channel definition file based on the <tt>sources</tt> directory.
|
9
|
+
#
|
10
|
+
# * If <tt>sources</tt> is a single file name, the channel definition file is expected
|
11
|
+
# as channel.yml in the same directory.
|
12
|
+
#
|
13
|
+
# * If <tt>sources</tt> is a single directory name, the channel definition file is
|
14
|
+
# expected as channel.yml in that directory.
|
15
|
+
#
|
16
|
+
# * If <tt>sources</tt> is an array of file names, the channel definition file is
|
17
|
+
# expected as channel.yml in the directory common to all files. If the files are
|
18
|
+
# located in more than one directory, an AmbiguousSourcesError is raised. In that
|
19
|
+
# case, the caller should specify the channel.yml as command line parameter.
|
20
|
+
#
|
21
|
+
# * If <tt>sources</tt> is an array with more than a single directory name, an
|
22
|
+
# AmbiguousSourcesError is raised. In that case, the caller should specify the
|
23
|
+
# channel.yml as command line parameter.
|
24
|
+
#
|
25
|
+
def locate(sources)
|
26
|
+
channel_source_dir = nil
|
27
|
+
|
28
|
+
if sources.respond_to?(:at)
|
29
|
+
# More than one source given. Check that they are all in the same directory.
|
30
|
+
distinct_dirs = sources.collect{|source| File.dirname(source)}.uniq
|
31
|
+
|
32
|
+
if 1 == distinct_dirs.size
|
33
|
+
# If all are the in same directory, use that as source directory where channel.yml is expected.
|
34
|
+
channel_source_dir = distinct_dirs.first
|
35
|
+
else
|
36
|
+
# Since no channel_file was specified at the command line, throw and quit
|
37
|
+
raise AmbiguousSourcesError.new(sources)
|
38
|
+
end
|
39
|
+
else
|
40
|
+
# If a single file or directory is given, use that as source directory where channel.yml is expected.
|
41
|
+
channel_source_dir = File.dirname(sources)
|
42
|
+
end
|
43
|
+
|
44
|
+
File.join(channel_source_dir, CHANNEL_YML)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/dropcaster/errors.rb
CHANGED
@@ -1,19 +1,25 @@
|
|
1
|
-
module Dropcaster
|
2
|
-
class ConfigurationError < StandardError
|
3
|
-
def initialize(msg)
|
4
|
-
super(msg)
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
class MissingAttributeError < ConfigurationError
|
9
|
-
def initialize(missingAttribute)
|
10
|
-
super("#{missingAttribute} is a mandatory attribute, but it is missing.")
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
class AmbiguousSourcesError < ConfigurationError
|
15
|
-
def initialize(ambiguousSources)
|
16
|
-
super("The list of sources is ambiguous. Can't derive common directory from these: #{ambiguousSources.inspect}")
|
17
|
-
end
|
18
|
-
end
|
19
|
-
|
1
|
+
module Dropcaster
|
2
|
+
class ConfigurationError < StandardError
|
3
|
+
def initialize(msg)
|
4
|
+
super(msg)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class MissingAttributeError < ConfigurationError
|
9
|
+
def initialize(missingAttribute)
|
10
|
+
super("#{missingAttribute} is a mandatory attribute, but it is missing.")
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
class AmbiguousSourcesError < ConfigurationError
|
15
|
+
def initialize(ambiguousSources)
|
16
|
+
super("The list of sources is ambiguous. Can't derive common directory from these: #{ambiguousSources.inspect}")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class TemplateNotFoundError < ConfigurationError
|
21
|
+
def initialize(message)
|
22
|
+
super("Unable to load template file: #{message}")
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
data/lib/dropcaster/hashkeys.rb
CHANGED
@@ -1,12 +1,12 @@
|
|
1
|
-
module Dropcaster
|
2
|
-
module HashKeys
|
3
|
-
def method_missing(meth,*args)
|
4
|
-
m = meth.id2name
|
5
|
-
if /=$/ =~ m
|
6
|
-
self[m.chop.to_sym] = (args.length < 2 ? args[0] : args)
|
7
|
-
else
|
8
|
-
self[m.to_sym]
|
9
|
-
end
|
10
|
-
end
|
11
|
-
end
|
12
|
-
end
|
1
|
+
module Dropcaster
|
2
|
+
module HashKeys
|
3
|
+
def method_missing(meth,*args)
|
4
|
+
m = meth.id2name
|
5
|
+
if /=$/ =~ m
|
6
|
+
self[m.chop.to_sym] = (args.length < 2 ? args[0] : args)
|
7
|
+
else
|
8
|
+
self[m.to_sym]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
data/lib/dropcaster/item.rb
CHANGED
@@ -1,43 +1,43 @@
|
|
1
|
-
require 'mp3info'
|
2
|
-
require 'digest/sha1'
|
3
|
-
|
4
|
-
module Dropcaster
|
5
|
-
class Item < DelegateClass(Hash)
|
6
|
-
include HashKeys
|
7
|
-
|
8
|
-
def initialize(file_path, options = nil)
|
9
|
-
super(Hash.new)
|
10
|
-
|
11
|
-
Mp3Info.open(file_path){|mp3info|
|
12
|
-
self[:file_name] = Pathname.new(File.expand_path(file_path)).relative_path_from(Pathname.new(Dir.pwd)).cleanpath.to_s
|
13
|
-
self[:tag] = mp3info.tag
|
14
|
-
self[:tag2] = mp3info.tag2
|
15
|
-
self[:duration] = mp3info.length
|
16
|
-
}
|
17
|
-
|
18
|
-
self[:file_size] = File.new(self.file_name).stat.size
|
19
|
-
self[:uuid] = Digest::SHA1.hexdigest(File.read(self.file_name))
|
20
|
-
|
21
|
-
if self.tag2.TDR.blank?
|
22
|
-
self[:pub_date] = DateTime.parse(File.new(self.file_name).mtime.to_s)
|
23
|
-
else
|
24
|
-
self[:pub_date] = DateTime.parse(self.tag2.TDR)
|
25
|
-
end
|
26
|
-
|
27
|
-
# Remove iTunes normalization crap (if configured)
|
28
|
-
self.tag2.COM.delete_if{|comment|
|
29
|
-
comment =~ /^( [0-9A-F]{8}){10}$/
|
30
|
-
} if options && options[:strip_itunes_private]
|
31
|
-
|
32
|
-
# Convert lyrics frame into a hash, keyed by the three-letter language code
|
33
|
-
if tag2.ULT
|
34
|
-
lyrics_parts = tag2.ULT.split(0.chr)
|
35
|
-
|
36
|
-
if lyrics_parts && 3 == lyrics_parts.size
|
37
|
-
self.lyrics = Hash.new
|
38
|
-
self.lyrics[lyrics_parts[1]] = lyrics_parts[2]
|
39
|
-
end
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
43
|
-
end
|
1
|
+
require 'mp3info'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
module Dropcaster
|
5
|
+
class Item < DelegateClass(Hash)
|
6
|
+
include HashKeys
|
7
|
+
|
8
|
+
def initialize(file_path, options = nil)
|
9
|
+
super(Hash.new)
|
10
|
+
|
11
|
+
Mp3Info.open(file_path){|mp3info|
|
12
|
+
self[:file_name] = Pathname.new(File.expand_path(file_path)).relative_path_from(Pathname.new(Dir.pwd)).cleanpath.to_s
|
13
|
+
self[:tag] = mp3info.tag
|
14
|
+
self[:tag2] = mp3info.tag2
|
15
|
+
self[:duration] = mp3info.length
|
16
|
+
}
|
17
|
+
|
18
|
+
self[:file_size] = File.new(self.file_name).stat.size
|
19
|
+
self[:uuid] = Digest::SHA1.hexdigest(File.read(self.file_name))
|
20
|
+
|
21
|
+
if self.tag2.TDR.blank?
|
22
|
+
self[:pub_date] = DateTime.parse(File.new(self.file_name).mtime.to_s)
|
23
|
+
else
|
24
|
+
self[:pub_date] = DateTime.parse(self.tag2.TDR)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Remove iTunes normalization crap (if configured)
|
28
|
+
self.tag2.COM.delete_if{|comment|
|
29
|
+
comment =~ /^( [0-9A-F]{8}){10}$/
|
30
|
+
} if options && options[:strip_itunes_private]
|
31
|
+
|
32
|
+
# Convert lyrics frame into a hash, keyed by the three-letter language code
|
33
|
+
if tag2.ULT
|
34
|
+
lyrics_parts = tag2.ULT.split(0.chr)
|
35
|
+
|
36
|
+
if lyrics_parts && 3 == lyrics_parts.size
|
37
|
+
self.lyrics = Hash.new
|
38
|
+
self.lyrics[lyrics_parts[1]] = lyrics_parts[2]
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
data/lib/dropcaster.rb
CHANGED
@@ -1,18 +1,18 @@
|
|
1
|
-
$:.unshift File.dirname(__FILE__)
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'delegate'
|
5
|
-
require 'yaml'
|
6
|
-
require 'active_support/core_ext/date_time/conversions'
|
7
|
-
require 'active_support/core_ext/object/blank'
|
8
|
-
|
9
|
-
require 'dropcaster/errors'
|
10
|
-
require 'dropcaster/hashkeys'
|
11
|
-
require 'dropcaster/channel'
|
12
|
-
require 'dropcaster/item'
|
13
|
-
require 'dropcaster/channel_file_locator'
|
14
|
-
|
15
|
-
module Dropcaster
|
16
|
-
VERSION = File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION]))
|
17
|
-
CHANNEL_YML = 'channel.yml'
|
18
|
-
end
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
3
|
+
require 'bundler/setup'
|
4
|
+
require 'delegate'
|
5
|
+
require 'yaml'
|
6
|
+
require 'active_support/core_ext/date_time/conversions'
|
7
|
+
require 'active_support/core_ext/object/blank'
|
8
|
+
|
9
|
+
require 'dropcaster/errors'
|
10
|
+
require 'dropcaster/hashkeys'
|
11
|
+
require 'dropcaster/channel'
|
12
|
+
require 'dropcaster/item'
|
13
|
+
require 'dropcaster/channel_file_locator'
|
14
|
+
|
15
|
+
module Dropcaster
|
16
|
+
VERSION = File.read(File.join(File.dirname(__FILE__), *%w[.. VERSION]))
|
17
|
+
CHANNEL_YML = 'channel.yml'
|
18
|
+
end
|