gst-kitchen 0.2.0
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/.gitignore +24 -0
- data/Gemfile +5 -0
- data/LICENSE.txt +22 -0
- data/README.md +87 -0
- data/Rakefile +1 -0
- data/bin/gst-kitchen +45 -0
- data/gst-kitchen.gemspec +23 -0
- data/lib/gst-kitchen.rb +18 -0
- data/lib/gst-kitchen/auphonic.rb +2 -0
- data/lib/gst-kitchen/auphonic/account.rb +33 -0
- data/lib/gst-kitchen/auphonic/production.rb +22 -0
- data/lib/gst-kitchen/episode.rb +74 -0
- data/lib/gst-kitchen/media.rb +60 -0
- data/lib/gst-kitchen/podcast.rb +112 -0
- data/lib/gst-kitchen/version.rb +3 -0
- data/templates/episodes.rss.erb +50 -0
- metadata +116 -0
data/.gitignore
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
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
|
+
|
|
19
|
+
# my playground
|
|
20
|
+
sandbox.rb
|
|
21
|
+
media
|
|
22
|
+
episodes
|
|
23
|
+
episodes*rss
|
|
24
|
+
podcast.yml
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2012 Sebastian Cohnen
|
|
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,87 @@
|
|
|
1
|
+
# Geekstammtisch's Podcast Kitchen (gst-kitchen)
|
|
2
|
+
|
|
3
|
+
Publishing podcats like a nerd!
|
|
4
|
+
|
|
5
|
+
This gem helps you to publish podcasts using Auphonic. It can fetch a list of Auphonic productions using
|
|
6
|
+
their API. You can add episodes and generate seperated feeds per audio format.
|
|
7
|
+
|
|
8
|
+
The Geekstammtisch website and this gem is inspired by [fanboys-IGOR] [igor].
|
|
9
|
+
|
|
10
|
+
**Note** that there might be some Geekstammtisch specific behavior left in this gem. If you find something,
|
|
11
|
+
drop me a note!
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
Add this line to your application's Gem file:
|
|
17
|
+
|
|
18
|
+
gem 'gst-kitchen'
|
|
19
|
+
|
|
20
|
+
And then execute:
|
|
21
|
+
|
|
22
|
+
$ bundle
|
|
23
|
+
|
|
24
|
+
Or install it yourself as:
|
|
25
|
+
|
|
26
|
+
$ gem install gst-kitchen
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
## Conventions
|
|
30
|
+
|
|
31
|
+
gst-kitchen assumes various conventions about (file) naming and URLs.
|
|
32
|
+
|
|
33
|
+
* feeds: feeds must be located at: `<website_url>/episodes.<format_file_ext>.rss`, e.g. `http://geekstammtisch.de/episodes.m4a.rss`
|
|
34
|
+
* episode media files must be located at: `<media_url>/<downcased handle><padded episode_nr>.<format_file_ext>`, e.g. `http://media.geekstammtisch.de/episodes/gst000.m4a`
|
|
35
|
+
|
|
36
|
+
Meta data from each episodes is read from your Auphonic production. The episode number is read from the `title`,
|
|
37
|
+
which must start with `<handle><number>`. The episode name is read from `subtitle`, summary and description is
|
|
38
|
+
set to Auphonic's `summary` field. Filesizes, playtime etc. is also read from the production.
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
## Usage
|
|
42
|
+
|
|
43
|
+
First you need to place your Auphonic credentials as JSON into `~/.auphonic`:
|
|
44
|
+
|
|
45
|
+
echo '{"user": "joe@example.com", "pass": "secret"}' > ~/.auphonic
|
|
46
|
+
|
|
47
|
+
Then you need to create a `podcast.yml` containing your podcast metadata.
|
|
48
|
+
You can take a look at https://github.com/tisba/gst-website/blob/master/podcast.yml as an example.
|
|
49
|
+
Important fields are
|
|
50
|
+
|
|
51
|
+
* basic meta data like, `title`, `subtutle`, `author`, `email` and `language`
|
|
52
|
+
* `handle` is a short *handle* for your podcast. It can be an abbreviation or acronym, or anything you like. For geekstammtisch it's `GST`.
|
|
53
|
+
* base URLs for the website and media location
|
|
54
|
+
* list of available audio formats: fileext_encoding, e.g. `m4a_aac` or `mp3_mp3`
|
|
55
|
+
* `rss_output_path` specifies where the generated feeds will be located
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
### CLI Usage
|
|
59
|
+
|
|
60
|
+
Get a list of all Auphonic productions
|
|
61
|
+
|
|
62
|
+
$ gst-kitchen list
|
|
63
|
+
|
|
64
|
+
Add a production to the podcast
|
|
65
|
+
|
|
66
|
+
$ gst-kitchen process --uuid=<PRODUCTION-UUID>
|
|
67
|
+
|
|
68
|
+
Render all configured feeds based on all episodes in `episodes/`
|
|
69
|
+
|
|
70
|
+
$ gst-kitchen feeds
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
### API Usage
|
|
74
|
+
|
|
75
|
+
**TBD**
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
## Contributing
|
|
79
|
+
|
|
80
|
+
1. Fork it
|
|
81
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
|
82
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
|
83
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
|
84
|
+
5. Create new Pull Request
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
[igor]: https://github.com/m4p/fanboys-IGOR "IGOR"
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/gst-kitchen
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
|
|
3
|
+
$LOAD_PATH.unshift File.join(File.dirname(__FILE__), '..', 'lib')
|
|
4
|
+
require "trollop"
|
|
5
|
+
require "gst-kitchen"
|
|
6
|
+
require "gst-kitchen/version"
|
|
7
|
+
|
|
8
|
+
require "fileutils"
|
|
9
|
+
|
|
10
|
+
SUB_COMMANDS = %w(feed process list version)
|
|
11
|
+
global_opts = Trollop::options do
|
|
12
|
+
banner "magic file deleting and copying utility"
|
|
13
|
+
opt :podcast, "path of podcast YAML specification", :default => "podcast.yml"
|
|
14
|
+
stop_on SUB_COMMANDS
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
case ARGV.shift
|
|
18
|
+
when "version"
|
|
19
|
+
puts GstKitchen::VERSION
|
|
20
|
+
when "feed"
|
|
21
|
+
subcmd_opts = Trollop::options do
|
|
22
|
+
end
|
|
23
|
+
Podcast.from_yaml(global_opts[:podcast]).render_all_feeds
|
|
24
|
+
when "list"
|
|
25
|
+
productions = Auphonic::Account.init_from_system.productions
|
|
26
|
+
productions.each do |production|
|
|
27
|
+
puts "#{production.meta["uuid"]} => #{production.meta["metadata"]["title"]}"
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
when "process"
|
|
31
|
+
subcmd_opts = Trollop::options do
|
|
32
|
+
opt :uuid, "Auphonic's production UUID", :type => :string
|
|
33
|
+
end
|
|
34
|
+
production = Auphonic::Account.init_from_system.production(subcmd_opts[:uuid])
|
|
35
|
+
|
|
36
|
+
podcast = Podcast.from_yaml("podcast.yml")
|
|
37
|
+
FileUtils.mkdir_p podcast.episodes_path
|
|
38
|
+
episode = podcast.create_episode_from_auphonic production
|
|
39
|
+
|
|
40
|
+
puts "Episode: #{episode}"
|
|
41
|
+
puts "Writing episode YAML to #{File.join(podcast.episodes_path, episode.handle.downcase)}.yml"
|
|
42
|
+
podcast.export_episode(episode)
|
|
43
|
+
else
|
|
44
|
+
Trollop::die "unknown subcommand #{subcmd.inspect}"
|
|
45
|
+
end
|
data/gst-kitchen.gemspec
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'gst-kitchen/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |gem|
|
|
7
|
+
gem.name = "gst-kitchen"
|
|
8
|
+
gem.version = GstKitchen::VERSION
|
|
9
|
+
gem.authors = ["Sebastian Cohnen"]
|
|
10
|
+
gem.email = ["sebastian.cohnen@gmx.net"]
|
|
11
|
+
gem.description = %q{gst-kitchen is a gem to publish podcasts like a nerd with auphonic!}
|
|
12
|
+
gem.summary = %q{gst-kitchen is a gem to publish podcasts like a nerd with auphonic!}
|
|
13
|
+
gem.homepage = ""
|
|
14
|
+
|
|
15
|
+
gem.files = `git ls-files`.split($/)
|
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
|
18
|
+
gem.require_paths = ["lib"]
|
|
19
|
+
|
|
20
|
+
gem.add_dependency "yajl-ruby"
|
|
21
|
+
gem.add_dependency "trollop"
|
|
22
|
+
gem.add_dependency "rake"
|
|
23
|
+
end
|
data/lib/gst-kitchen.rb
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require "erb"
|
|
2
|
+
require "time"
|
|
3
|
+
require "uri"
|
|
4
|
+
|
|
5
|
+
require "yajl"
|
|
6
|
+
require "yaml"
|
|
7
|
+
|
|
8
|
+
require "gst-kitchen/version"
|
|
9
|
+
require "gst-kitchen/podcast"
|
|
10
|
+
require "gst-kitchen/episode"
|
|
11
|
+
require "gst-kitchen/media"
|
|
12
|
+
|
|
13
|
+
require "gst-kitchen/auphonic"
|
|
14
|
+
require "gst-kitchen/auphonic/production"
|
|
15
|
+
require "gst-kitchen/auphonic/account"
|
|
16
|
+
|
|
17
|
+
module GstKitchen
|
|
18
|
+
end
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
class Auphonic::Account
|
|
2
|
+
attr_reader :username, :password
|
|
3
|
+
|
|
4
|
+
def self.init_from_system
|
|
5
|
+
auphonic_credentials = Yajl::Parser.parse(File.read(File.expand_path("~/.auphonic")))
|
|
6
|
+
self.new(auphonic_credentials)
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def initialize(options={})
|
|
10
|
+
@username = options["user"]
|
|
11
|
+
@password = options["pass"]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def production(uuid)
|
|
15
|
+
Auphonic::Production.new(self, uuid)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def productions
|
|
19
|
+
request("https://auphonic.com/api/productions.json")["data"].map do |meta|
|
|
20
|
+
production = Auphonic::Production.new(self)
|
|
21
|
+
production.meta = meta
|
|
22
|
+
production
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
def request(url)
|
|
30
|
+
json = open(url, http_basic_authentication: [username, password])
|
|
31
|
+
Yajl::Parser.parse(json.read)
|
|
32
|
+
end
|
|
33
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require "open-uri"
|
|
2
|
+
|
|
3
|
+
class Auphonic::Production
|
|
4
|
+
attr_accessor :meta
|
|
5
|
+
|
|
6
|
+
def initialize(account, uuid=nil)
|
|
7
|
+
@account = account
|
|
8
|
+
fetch uuid if uuid
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
private
|
|
13
|
+
|
|
14
|
+
def fetch(uuid)
|
|
15
|
+
@meta = request("https://auphonic.com/api/production/#{uuid}.json")
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def request(url)
|
|
19
|
+
json = open(url, http_basic_authentication: [@account.username, @account.password])
|
|
20
|
+
Yajl::Parser.parse(json.read)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :published_at, :summary, :description)
|
|
2
|
+
include Comparable
|
|
3
|
+
|
|
4
|
+
def self.from_auphonic(production)
|
|
5
|
+
data = production.meta
|
|
6
|
+
/GST(?<number>\d{3})/ =~ data["data"]["metadata"]["title"]
|
|
7
|
+
|
|
8
|
+
metadata = {
|
|
9
|
+
auphonic_uuid: data["data"]["uuid"],
|
|
10
|
+
number: number.to_i,
|
|
11
|
+
length: data["data"]["length"],
|
|
12
|
+
name: data["data"]["metadata"]["subtitle"].strip,
|
|
13
|
+
summary: data["data"]["metadata"]["summary"].strip,
|
|
14
|
+
description: data["data"]["metadata"]["summary"].strip
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
media = data["data"]["output_files"].each_with_object({}) do |item, obj|
|
|
18
|
+
obj[item["format"]] = {
|
|
19
|
+
"size" => item["size"],
|
|
20
|
+
"file_ext" => item["ending"]
|
|
21
|
+
}
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
episode = self.new
|
|
25
|
+
|
|
26
|
+
episode.number = metadata[:number]
|
|
27
|
+
episode.name = metadata[:name]
|
|
28
|
+
episode.length = metadata[:length].round
|
|
29
|
+
episode.auphonic_uuid = metadata[:auphonic_uuid]
|
|
30
|
+
episode.published_at = Time.now
|
|
31
|
+
episode.summary = metadata[:summary]
|
|
32
|
+
episode.description = metadata[:description]
|
|
33
|
+
episode.media = media
|
|
34
|
+
|
|
35
|
+
episode
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def self.from_yaml(yaml_file)
|
|
39
|
+
YAML.load_file(yaml_file)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def initialize(*args)
|
|
43
|
+
super
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def <=>(other)
|
|
47
|
+
self.number <=> other.number
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def title
|
|
51
|
+
"#{handle} - #{name}"
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def handle
|
|
55
|
+
"GST#{"%03i" % self.number}"
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def rfc_2822_date
|
|
59
|
+
self.published_at.rfc2822
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
def duration
|
|
63
|
+
hours = length / (60 * 60)
|
|
64
|
+
minutes = (length - hours * 60 * 60) / 60
|
|
65
|
+
seconds = length % 60
|
|
66
|
+
|
|
67
|
+
"#{"%02i" % hours}:#{"%02i" % minutes}:#{"%02i" % seconds}"
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def to_s
|
|
71
|
+
"#{title} (#{duration}) https://auphonic.com/engine/status/#{auphonic_uuid}"
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# encoding: UTF-8
|
|
2
|
+
|
|
3
|
+
class Media
|
|
4
|
+
class Format < Struct.new(:format, :file_ext, :mime_type)
|
|
5
|
+
Mp3 = self.new("mp3", "mp3", "audio/mpeg")
|
|
6
|
+
M4a = self.new("aac", "m4a", "audio/x-m4a")
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def self.format(format)
|
|
10
|
+
case format
|
|
11
|
+
when :m4a_aac then Media::Format::M4a
|
|
12
|
+
when :mp3_mp3 then Media::Format::Mp3
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def initialize(file)
|
|
17
|
+
@file = file
|
|
18
|
+
@md5_digest = Digest::MD5.file(@file).hexdigest.force_encoding("UTF-8")
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def length
|
|
22
|
+
media_info["length"].to_i
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def title
|
|
26
|
+
media_info["title"]
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def file_size
|
|
30
|
+
@file_size ||= File.size(@file)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def duration
|
|
34
|
+
return @duration if @duration
|
|
35
|
+
|
|
36
|
+
hours = length / (60 * 60)
|
|
37
|
+
minutes = (length - hours * 60 * 60) / 60
|
|
38
|
+
seconds = length % 60
|
|
39
|
+
|
|
40
|
+
@duration = "#{"%02i" % hours}:#{"%02i" % minutes}:#{"%02i" % seconds}"
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def aquire_meta_data!
|
|
44
|
+
[:duration, :file_size].each { |m| send(m) }
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
private
|
|
48
|
+
|
|
49
|
+
def media_info
|
|
50
|
+
return @media_info if @media_info
|
|
51
|
+
|
|
52
|
+
@media_info = TagLib::FileRef.open(@file) do |ref|
|
|
53
|
+
{
|
|
54
|
+
"title" => ref.tag.title,
|
|
55
|
+
"length" => ref.audio_properties.length
|
|
56
|
+
}
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
class Podcast
|
|
2
|
+
attr_accessor :title,
|
|
3
|
+
:subtitle,
|
|
4
|
+
:author,
|
|
5
|
+
:email,
|
|
6
|
+
:language,
|
|
7
|
+
:summary,
|
|
8
|
+
:handle,
|
|
9
|
+
:website,
|
|
10
|
+
:cover,
|
|
11
|
+
:media_url,
|
|
12
|
+
:episodes,
|
|
13
|
+
:explicit,
|
|
14
|
+
:rss_output_path,
|
|
15
|
+
:episodes_path
|
|
16
|
+
|
|
17
|
+
attr_accessor :formats
|
|
18
|
+
|
|
19
|
+
def self.from_yaml(yaml_file="podcast.yml")
|
|
20
|
+
hash = YAML.load_file(yaml_file)
|
|
21
|
+
|
|
22
|
+
podcast = self.new
|
|
23
|
+
podcast.title = hash["title"]
|
|
24
|
+
podcast.subtitle = hash["subtitle"]
|
|
25
|
+
podcast.author = hash["author"]
|
|
26
|
+
podcast.email = hash["email"]
|
|
27
|
+
podcast.language = hash["language"]
|
|
28
|
+
podcast.summary = hash["summary"]
|
|
29
|
+
podcast.handle = hash["handle"]
|
|
30
|
+
podcast.website = hash["website"]
|
|
31
|
+
podcast.cover = hash["cover"]
|
|
32
|
+
podcast.media_url = hash["media_url"]
|
|
33
|
+
podcast.explicit = hash["explicit"] || false
|
|
34
|
+
podcast.formats = hash["formats"].map { |format| Media.format(format) }
|
|
35
|
+
podcast.episodes_path = hash["episodes_path"] || "episodes/"
|
|
36
|
+
podcast.rss_output_path = hash["rss_output_path"] || "."
|
|
37
|
+
|
|
38
|
+
podcast.load_episodes
|
|
39
|
+
|
|
40
|
+
podcast
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def load_episodes
|
|
44
|
+
self.episodes = Dir[File.join(self.episodes_path, "#{handle.downcase}*.yml")].map do |yml|
|
|
45
|
+
Episode.from_yaml(yml)
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
def create_episode_from_auphonic(production)
|
|
50
|
+
Episode.from_auphonic production
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def feed_url(format)
|
|
54
|
+
url = URI(self.website)
|
|
55
|
+
url.path = "/#{rss_file(format)}"
|
|
56
|
+
url.to_s
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def episode_media_url(episode, format)
|
|
60
|
+
url = URI(self.media_url)
|
|
61
|
+
url.path += "/#{episode.handle.downcase}.#{format.file_ext}"
|
|
62
|
+
url.to_s
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def cover_url
|
|
66
|
+
url = URI(self.website)
|
|
67
|
+
url.path = self.cover
|
|
68
|
+
url.to_s
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def deep_link_url(episode)
|
|
72
|
+
url = URI(self.website)
|
|
73
|
+
url.fragment = "#{episode.number}:"
|
|
74
|
+
url.to_s
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def podcast
|
|
78
|
+
self
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def current_format
|
|
82
|
+
@current_format
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def render_feed(format)
|
|
86
|
+
@current_format = format
|
|
87
|
+
template = ERB.new File.read(File.join(File.dirname(__FILE__), "../..", "templates/episodes.rss.erb"))
|
|
88
|
+
File.open(File.join(rss_output_path, rss_file(format)), "w") do |rss|
|
|
89
|
+
rss.write template.result(binding)
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def export_episode(episode)
|
|
94
|
+
destination = File.join(episodes_path, "#{episode.handle.downcase}.yml")
|
|
95
|
+
File.open(destination, "w") { |file| file.write episode.to_yaml}
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def render_all_feeds
|
|
99
|
+
self.formats.each do |format|
|
|
100
|
+
render_feed(format)
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
def other_formats(format)
|
|
105
|
+
self.formats - [format]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
private
|
|
109
|
+
def rss_file(format)
|
|
110
|
+
"episodes.#{format.file_ext}.rss"
|
|
111
|
+
end
|
|
112
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
|
2
|
+
<rss xmlns:itunes="http://www.itunes.com/dtds/podcast-1.0.dtd" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0">
|
|
3
|
+
<channel>
|
|
4
|
+
<title><%= podcast.title %></title>
|
|
5
|
+
<link><%= podcast.website %></link>
|
|
6
|
+
<atom:link rel="self" href="<%= podcast.feed_url(podcast.current_format) %>" type="application/rss+xml" title="<%= podcast.current_format.file_ext.upcase %> Audio"/>
|
|
7
|
+
<% podcast.other_formats(current_format).each do |format| %>
|
|
8
|
+
<atom:link rel="alternate" href="<%= podcast.feed_url(format) %>" type="application/rss+xml" title="<%= format.file_ext.upcase %> Audio"/>
|
|
9
|
+
<% end %>
|
|
10
|
+
<language><%= podcast.language %></language>
|
|
11
|
+
<generator>gst-kitchen</generator>
|
|
12
|
+
<copyright><%= podcast.author %>, cc-by-nc-sa</copyright>
|
|
13
|
+
<itunes:subtitle><%= podcast.subtitle %></itunes:subtitle>
|
|
14
|
+
<itunes:author><%= podcast.author %></itunes:author>
|
|
15
|
+
<managingEditor><%= podcast.email %> (<%= podcast.author %>)</managingEditor>
|
|
16
|
+
<itunes:summary><%= podcast.summary %></itunes:summary>
|
|
17
|
+
<itunes:explicit><%= podcast.explicit ? "yes" : "no" %></itunes:explicit>
|
|
18
|
+
<description><%= podcast.summary %></description>
|
|
19
|
+
<itunes:owner>
|
|
20
|
+
<itunes:name><%= podcast.author %></itunes:name>
|
|
21
|
+
<itunes:email><%= podcast.email %></itunes:email>
|
|
22
|
+
</itunes:owner>
|
|
23
|
+
|
|
24
|
+
<itunes:image href="<%= podcast.cover_url %>"/>
|
|
25
|
+
|
|
26
|
+
<itunes:category text="Technology"/>
|
|
27
|
+
<itunes:category text="Technology">
|
|
28
|
+
<itunes:category text="Gadgets"/>
|
|
29
|
+
</itunes:category>
|
|
30
|
+
<itunes:category text="Technology">
|
|
31
|
+
<itunes:category text="Tech News"/>
|
|
32
|
+
</itunes:category>
|
|
33
|
+
|
|
34
|
+
<% podcast.episodes.each do |episode| %>
|
|
35
|
+
<item>
|
|
36
|
+
<title><%= episode.title %></title>
|
|
37
|
+
<itunes:explicit><%= podcast.explicit ? "yes" : "no" %></itunes:explicit>
|
|
38
|
+
<itunes:author><%= podcast.author %></itunes:author>
|
|
39
|
+
<itunes:subtitle><%= episode.title %></itunes:subtitle>
|
|
40
|
+
<enclosure url="<%= podcast.episode_media_url(episode, current_format) %>" length="<%= episode.media[current_format.format]["size"] %>" type="<%= current_format.mime_type %>"/>
|
|
41
|
+
<guid isPermaLink="false"><%= episode.handle %></guid>
|
|
42
|
+
<pubDate><%= episode.rfc_2822_date %></pubDate>
|
|
43
|
+
<itunes:duration><%= episode.duration %></itunes:duration>
|
|
44
|
+
<itunes:summary><![CDATA[<%= episode.summary %>]]></itunes:summary>
|
|
45
|
+
<description><![CDATA[<%= episode.description %>]]></description>
|
|
46
|
+
<atom:link rel="http://podlove.org/deep-link" href="<%= podcast.deep_link_url(episode) %>" />
|
|
47
|
+
</item>
|
|
48
|
+
<% end %>
|
|
49
|
+
</channel>
|
|
50
|
+
</rss>
|
metadata
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: gst-kitchen
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.2.0
|
|
5
|
+
prerelease:
|
|
6
|
+
platform: ruby
|
|
7
|
+
authors:
|
|
8
|
+
- Sebastian Cohnen
|
|
9
|
+
autorequire:
|
|
10
|
+
bindir: bin
|
|
11
|
+
cert_chain: []
|
|
12
|
+
date: 2012-12-12 00:00:00.000000000 Z
|
|
13
|
+
dependencies:
|
|
14
|
+
- !ruby/object:Gem::Dependency
|
|
15
|
+
name: yajl-ruby
|
|
16
|
+
requirement: !ruby/object:Gem::Requirement
|
|
17
|
+
none: false
|
|
18
|
+
requirements:
|
|
19
|
+
- - ! '>='
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
none: false
|
|
26
|
+
requirements:
|
|
27
|
+
- - ! '>='
|
|
28
|
+
- !ruby/object:Gem::Version
|
|
29
|
+
version: '0'
|
|
30
|
+
- !ruby/object:Gem::Dependency
|
|
31
|
+
name: trollop
|
|
32
|
+
requirement: !ruby/object:Gem::Requirement
|
|
33
|
+
none: false
|
|
34
|
+
requirements:
|
|
35
|
+
- - ! '>='
|
|
36
|
+
- !ruby/object:Gem::Version
|
|
37
|
+
version: '0'
|
|
38
|
+
type: :runtime
|
|
39
|
+
prerelease: false
|
|
40
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
41
|
+
none: false
|
|
42
|
+
requirements:
|
|
43
|
+
- - ! '>='
|
|
44
|
+
- !ruby/object:Gem::Version
|
|
45
|
+
version: '0'
|
|
46
|
+
- !ruby/object:Gem::Dependency
|
|
47
|
+
name: rake
|
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
|
49
|
+
none: false
|
|
50
|
+
requirements:
|
|
51
|
+
- - ! '>='
|
|
52
|
+
- !ruby/object:Gem::Version
|
|
53
|
+
version: '0'
|
|
54
|
+
type: :runtime
|
|
55
|
+
prerelease: false
|
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
57
|
+
none: false
|
|
58
|
+
requirements:
|
|
59
|
+
- - ! '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
description: gst-kitchen is a gem to publish podcasts like a nerd with auphonic!
|
|
63
|
+
email:
|
|
64
|
+
- sebastian.cohnen@gmx.net
|
|
65
|
+
executables:
|
|
66
|
+
- gst-kitchen
|
|
67
|
+
extensions: []
|
|
68
|
+
extra_rdoc_files: []
|
|
69
|
+
files:
|
|
70
|
+
- .gitignore
|
|
71
|
+
- Gemfile
|
|
72
|
+
- LICENSE.txt
|
|
73
|
+
- README.md
|
|
74
|
+
- Rakefile
|
|
75
|
+
- bin/gst-kitchen
|
|
76
|
+
- gst-kitchen.gemspec
|
|
77
|
+
- lib/gst-kitchen.rb
|
|
78
|
+
- lib/gst-kitchen/auphonic.rb
|
|
79
|
+
- lib/gst-kitchen/auphonic/account.rb
|
|
80
|
+
- lib/gst-kitchen/auphonic/production.rb
|
|
81
|
+
- lib/gst-kitchen/episode.rb
|
|
82
|
+
- lib/gst-kitchen/media.rb
|
|
83
|
+
- lib/gst-kitchen/podcast.rb
|
|
84
|
+
- lib/gst-kitchen/version.rb
|
|
85
|
+
- templates/episodes.rss.erb
|
|
86
|
+
homepage: ''
|
|
87
|
+
licenses: []
|
|
88
|
+
post_install_message:
|
|
89
|
+
rdoc_options: []
|
|
90
|
+
require_paths:
|
|
91
|
+
- lib
|
|
92
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
93
|
+
none: false
|
|
94
|
+
requirements:
|
|
95
|
+
- - ! '>='
|
|
96
|
+
- !ruby/object:Gem::Version
|
|
97
|
+
version: '0'
|
|
98
|
+
segments:
|
|
99
|
+
- 0
|
|
100
|
+
hash: -203743376280899826
|
|
101
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
102
|
+
none: false
|
|
103
|
+
requirements:
|
|
104
|
+
- - ! '>='
|
|
105
|
+
- !ruby/object:Gem::Version
|
|
106
|
+
version: '0'
|
|
107
|
+
segments:
|
|
108
|
+
- 0
|
|
109
|
+
hash: -203743376280899826
|
|
110
|
+
requirements: []
|
|
111
|
+
rubyforge_project:
|
|
112
|
+
rubygems_version: 1.8.24
|
|
113
|
+
signing_key:
|
|
114
|
+
specification_version: 3
|
|
115
|
+
summary: gst-kitchen is a gem to publish podcasts like a nerd with auphonic!
|
|
116
|
+
test_files: []
|