podcastinator 0.0.1

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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 21da3fceb4c3af6e6e5ef5f14f140e794542b492
4
+ data.tar.gz: 1f81cf0e6eb0ffaf13631f919101abe1594d25da
5
+ SHA512:
6
+ metadata.gz: 8cfd8c97c41820482c3ab8176517a873b2ab1a0071997f3fa0796818c7c92ae84d5950c0c4f73bcdb208d8972773a258c5f32c7813d531435ae0429d4fb89e08
7
+ data.tar.gz: 6580d5ad9c0a6ab5f932d77efc0307a4e5dfa5dade9ef45fcbc6e15edde0356f7ee9a0e2debefbead1a6441546773541fbc88ef253f4e43f33e34a5390fcf069
data/.gitignore ADDED
@@ -0,0 +1,22 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.bundle
19
+ *.so
20
+ *.o
21
+ *.a
22
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in podcastinator.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 Alex McHale
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # Podcastinator
2
+
3
+ TODO: Write a gem description
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'podcastinator'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install podcastinator
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Contributing
24
+
25
+ 1. Fork it ( https://github.com/[my-github-username]/podcastinator/fork )
26
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
27
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
28
+ 4. Push to the branch (`git push origin my-new-feature`)
29
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/podcastinator ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "podcastinator"
4
+ Podcastinator.start
@@ -0,0 +1,60 @@
1
+ require "optparse"
2
+ require "erb"
3
+ require "digest/md5"
4
+ require "time"
5
+ require "date"
6
+ require "pp"
7
+ require "yaml"
8
+ require "uri"
9
+
10
+ require "nokogiri"
11
+ require "taglib"
12
+ require "mime-types"
13
+
14
+ require "podcastinator/version"
15
+ require "podcastinator/extensions"
16
+ require "podcastinator/feed"
17
+ require "podcastinator/generator"
18
+
19
+ module Podcastinator
20
+
21
+ def self.start(options = self.parse_cli_options)
22
+ feed = Podcastinator::Feed.new(options)
23
+ puts Podcastinator::Generator.new(feed).to_xml
24
+ end
25
+
26
+ def self.parse_cli_options
27
+ options = {
28
+ :path => Dir.pwd,
29
+ }
30
+
31
+ OptionParser.new do |opts|
32
+ opts.banner = "Usage: podcastinator [options] [audio files ...]"
33
+
34
+ opts.on("-o", "--output [FILENAME]", "Write the XML to the specified file") do |filename|
35
+ options[:output] = filename
36
+ end
37
+
38
+ opts.on("-r", "--root [PATH]", "The root path in the URL to access the audio files") do |path|
39
+ options[:path] = path
40
+ end
41
+ end.parse!
42
+
43
+ add_channel_yml(options)
44
+
45
+ options
46
+ end
47
+
48
+ def self.add_channel_yml(options)
49
+ yml_filename = File.join(options[:path], "channel.yml")
50
+ return unless File.file? yml_filename
51
+
52
+ yml_content = File.read(yml_filename)
53
+ yml_hash = YAML.load(yml_content)
54
+
55
+ yml_hash.each do |key, value|
56
+ options[key.to_sym] ||= value
57
+ end
58
+ end
59
+
60
+ end
@@ -0,0 +1,15 @@
1
+ class Object
2
+
3
+ def present?
4
+ !blank?
5
+ end
6
+
7
+ def blank?
8
+ return true if self == nil
9
+ return true if kind_of?(String) && self =~ /\A\s*\z/
10
+ return true if respond_to?(:length) && length == 0
11
+ return true if to_s.length == 0
12
+ return false
13
+ end
14
+
15
+ end
@@ -0,0 +1,149 @@
1
+ module Podcastinator
2
+
3
+ class Feed
4
+
5
+ attr_reader *%i(
6
+ title
7
+ url
8
+ description
9
+ language
10
+ copyright
11
+ subtitle
12
+ author
13
+ image_url
14
+ keywords
15
+ owner
16
+ items
17
+ local_path
18
+ )
19
+
20
+ def initialize(options = {})
21
+ @title = options[:title]
22
+ @url = options[:url]
23
+ @local_path = options[:path]
24
+ @description = options[:description]
25
+ @language = options[:language]
26
+ @copyright = options[:copyright]
27
+ @subtitle = options[:subtitle]
28
+ @author = options[:author]
29
+ @image_url = options[:image_url]
30
+ @keywords = options[:keywords]
31
+ @owner = Owner.build(options[:owner_name], options[:owner_email])
32
+
33
+ @items =
34
+ Dir.chdir(local_path) do
35
+ Dir[*Item.globs].uniq.map do |filename|
36
+ Item.new(self, filename)
37
+ end
38
+ end
39
+ end
40
+
41
+ class Owner < Struct.new(:name, :email)
42
+
43
+ def self.build(name, email)
44
+ new(name, email) if name.present? || email.present?
45
+ end
46
+
47
+ end
48
+
49
+ class Item
50
+
51
+ attr_reader *%i(
52
+ author
53
+ filename
54
+ image_url
55
+ local_filename
56
+ subtitle
57
+ time
58
+ title
59
+ url
60
+ file_size
61
+ mime_type
62
+ duration
63
+ )
64
+
65
+ def initialize(feed, filename)
66
+ @feed = feed
67
+ @filename = filename
68
+ @local_filename = File.join(feed.local_path, filename)
69
+ @url = "#{ feed.url }/#{ URI.escape @filename.to_s }".gsub("//", "/")
70
+
71
+ @image_url =
72
+ if File.file? File.join(feed.local_path, "#{ filename }.jpg")
73
+ "#{ @url }.jpg"
74
+ else
75
+ feed.image_url
76
+ end
77
+
78
+ TagLib::FileRef.open(local_filename) do |fileref|
79
+ tag = fileref.tag
80
+
81
+ @title = tag.title
82
+ @author = tag.artist
83
+ @album = tag.album
84
+ @track = if tag.track.present? && tag.track != 0 then tag.to_s.track[/^\d+/] end
85
+ @subtitle = if author.present? && @track.present? then %[#{ album } ##{ track }] end
86
+ @duration = fileref.audio_properties.length
87
+ end
88
+ end
89
+
90
+ def mime_type
91
+ case File.extname(filename).downcase
92
+ when ".mp3" then "audio/mpeg"
93
+ else raise UnknownMimeType, "podcastinator can't handle #{ filename }"
94
+ end
95
+ end
96
+
97
+ def guid
98
+ md5 = Digest::MD5.new
99
+
100
+ File.open(local_filename, 'rb') do |io|
101
+ while (buf = io.read(4096)) && (buf.length > 0)
102
+ md5.update(buf)
103
+ end
104
+ end
105
+
106
+ md5.to_s
107
+ end
108
+
109
+ def time
110
+ @time || File.mtime(local_filename)
111
+ end
112
+
113
+ def keywords
114
+ []
115
+ end
116
+
117
+ def self.mime_types
118
+ {
119
+ "mp3" => "audio/mpeg",
120
+ }
121
+ end
122
+
123
+ def self.globs
124
+ mime_types.keys.map { |ext| "**/*.#{ ext }" }
125
+ end
126
+
127
+ def self.build(feed, files)
128
+ files.map do |filename|
129
+ title, subtitle, author, time, image_url = nil
130
+
131
+ TagLib::FileRef.open(local_filename) do |fileref|
132
+ tag = fileref.tag
133
+
134
+ title = tag.title
135
+ author = tag.artist
136
+ album = tag.album
137
+ track = if tag.track.present? && tag.track != 0 then tag.to_s.track[/^\d+/] end
138
+ subtitle = if album.present? && track.present? then %[#{ album } ##{ track }] end
139
+ end
140
+
141
+ new(feed, filename, title, subtitle, author, time, image_url)
142
+ end
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+
149
+ end
@@ -0,0 +1,68 @@
1
+ module Podcastinator
2
+
3
+ class Generator
4
+
5
+ attr_accessor :feed
6
+
7
+ def initialize(feed)
8
+ @feed = feed
9
+ end
10
+
11
+ def xml_builder
12
+ Nokogiri::XML::Builder.new do |xml|
13
+ xml.rss("xmlns:itunes" => "http://www.itunes.com/dtds/podcast-1.0.dtd", "version" => "2.0") do
14
+ xml.channel do
15
+ # Required channel attributes
16
+ xml.title feed.title
17
+ xml.link feed.url
18
+ xml.description feed.description
19
+ xml["itunes"].summary feed.description
20
+ xml["itunes"].image(href: feed.image_url)
21
+ xml.generator "Podcastinator #{ Podcastinator::VERSION }"
22
+
23
+ # Optional channel attributes
24
+ xml.language feed.language if feed.language.present?
25
+ xml.copyright feed.copyright if feed.copyright.present?
26
+ xml.subtitle feed.subtitle if feed.subtitle.present?
27
+ xml.author feed.author if feed.author.present?
28
+ xml["itunes"].keywords feed.keywords.join(",") if feed.keywords.present?
29
+
30
+ # Owner details
31
+ if feed.owner
32
+ xml["itunes"].owner do
33
+ xml["itunes"].name feed.owner.name
34
+ xml["itunes"].email feed.owner.email
35
+ end
36
+ end
37
+
38
+ # Category details
39
+
40
+ # Explicit flag
41
+
42
+ # Channel items
43
+ feed.items.each do |item|
44
+ xml.item do
45
+ xml.title item.title
46
+ xml["itunes"].author item.author
47
+ xml["itunes"].subtitle item.subtitle if item.subtitle.present?
48
+ xml["itunes"].summary item.subtitle if item.subtitle.present?
49
+ xml["itunes"].image(href: if item.image_url.present? then item.image_url else feed.image_url end)
50
+ xml.enclosure(url: item.url, length: item.file_size, type: item.mime_type)
51
+ xml.guid(item.guid, isPermaLink: "false")
52
+ xml.pubDate item.time.iso8601
53
+ xml["itunes"].duration item.duration.to_i
54
+ xml["itunes"].keywords item.keywords.join(",") if item.keywords.present?
55
+ end
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+
62
+ def to_xml
63
+ xml_builder.to_xml
64
+ end
65
+
66
+ end
67
+
68
+ end
@@ -0,0 +1,3 @@
1
+ module Podcastinator
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,27 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'podcastinator/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "podcastinator"
8
+ spec.version = Podcastinator::VERSION
9
+ spec.authors = ["Alex McHale"]
10
+ spec.email = ["alex@anticlever.com"]
11
+ spec.summary = %q{A ruby library for generating podcast feeds.}
12
+ spec.description = %q{A ruby library for generating podcast feeds from a source or via an API.}
13
+ spec.homepage = "http://github.com/alexmchale/podcastinator"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_dependency "nokogiri", "~> 1.6"
22
+ spec.add_dependency "taglib", "~> 0.6"
23
+ spec.add_dependency "mime-types", "~> 2.3"
24
+
25
+ spec.add_development_dependency "bundler", "~> 1.6"
26
+ spec.add_development_dependency "rake", "~> 10.3"
27
+ end
metadata ADDED
@@ -0,0 +1,128 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: podcastinator
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Alex McHale
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-07 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: nokogiri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.6'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: taglib
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: mime-types
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '2.3'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '2.3'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.6'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.6'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rake
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '10.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '10.3'
83
+ description: A ruby library for generating podcast feeds from a source or via an API.
84
+ email:
85
+ - alex@anticlever.com
86
+ executables:
87
+ - podcastinator
88
+ extensions: []
89
+ extra_rdoc_files: []
90
+ files:
91
+ - ".gitignore"
92
+ - Gemfile
93
+ - LICENSE.txt
94
+ - README.md
95
+ - Rakefile
96
+ - bin/podcastinator
97
+ - lib/podcastinator.rb
98
+ - lib/podcastinator/extensions.rb
99
+ - lib/podcastinator/feed.rb
100
+ - lib/podcastinator/generator.rb
101
+ - lib/podcastinator/version.rb
102
+ - podcastinator.gemspec
103
+ homepage: http://github.com/alexmchale/podcastinator
104
+ licenses:
105
+ - MIT
106
+ metadata: {}
107
+ post_install_message:
108
+ rdoc_options: []
109
+ require_paths:
110
+ - lib
111
+ required_ruby_version: !ruby/object:Gem::Requirement
112
+ requirements:
113
+ - - ">="
114
+ - !ruby/object:Gem::Version
115
+ version: '0'
116
+ required_rubygems_version: !ruby/object:Gem::Requirement
117
+ requirements:
118
+ - - ">="
119
+ - !ruby/object:Gem::Version
120
+ version: '0'
121
+ requirements: []
122
+ rubyforge_project:
123
+ rubygems_version: 2.2.2
124
+ signing_key:
125
+ specification_version: 4
126
+ summary: A ruby library for generating podcast feeds.
127
+ test_files: []
128
+ has_rdoc: