gst-kitchen 0.2.0 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -35,7 +35,7 @@ gst-kitchen assumes various conventions about (file) naming and URLs.
35
35
 
36
36
  Meta data from each episodes is read from your Auphonic production. The episode number is read from the `title`,
37
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.
38
+ set to Auphonic's `summary` field. File sizes, playtime etc. are also read from the production.
39
39
 
40
40
 
41
41
  ## Usage
@@ -48,8 +48,8 @@ Then you need to create a `podcast.yml` containing your podcast metadata.
48
48
  You can take a look at https://github.com/tisba/gst-website/blob/master/podcast.yml as an example.
49
49
  Important fields are
50
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`.
51
+ * basic meta data like, `title`, `subtitle`, `author`, `email` and `language`
52
+ * `handle` is a short *handle* for your podcast. It can be an abbreviation, acronym, or anything you like. For geekstammtisch it's `GST`.
53
53
  * base URLs for the website and media location
54
54
  * list of available audio formats: fileext_encoding, e.g. `m4a_aac` or `mp3_mp3`
55
55
  * `rss_output_path` specifies where the generated feeds will be located
@@ -84,4 +84,4 @@ Render all configured feeds based on all episodes in `episodes/`
84
84
  5. Create new Pull Request
85
85
 
86
86
 
87
- [igor]: https://github.com/m4p/fanboys-IGOR "IGOR"
87
+ [igor]: https://github.com/m4p/fanboys-IGOR "IGOR"
data/bin/gst-kitchen CHANGED
@@ -9,7 +9,7 @@ require "fileutils"
9
9
 
10
10
  SUB_COMMANDS = %w(feed process list version)
11
11
  global_opts = Trollop::options do
12
- banner "magic file deleting and copying utility"
12
+ banner "Geekstammtischs Kitchen to publish podcats..."
13
13
  opt :podcast, "path of podcast YAML specification", :default => "podcast.yml"
14
14
  stop_on SUB_COMMANDS
15
15
  end
@@ -17,10 +17,12 @@ end
17
17
  case ARGV.shift
18
18
  when "version"
19
19
  puts GstKitchen::VERSION
20
+
20
21
  when "feed"
21
22
  subcmd_opts = Trollop::options do
22
23
  end
23
24
  Podcast.from_yaml(global_opts[:podcast]).render_all_feeds
25
+
24
26
  when "list"
25
27
  productions = Auphonic::Account.init_from_system.productions
26
28
  productions.each do |production|
@@ -33,13 +35,58 @@ when "process"
33
35
  end
34
36
  production = Auphonic::Account.init_from_system.production(subcmd_opts[:uuid])
35
37
 
36
- podcast = Podcast.from_yaml("podcast.yml")
38
+ podcast = Podcast.from_yaml(global_opts[:podcast])
37
39
  FileUtils.mkdir_p podcast.episodes_path
38
40
  episode = podcast.create_episode_from_auphonic production
39
41
 
40
42
  puts "Episode: #{episode}"
41
43
  puts "Writing episode YAML to #{File.join(podcast.episodes_path, episode.handle.downcase)}.yml"
42
44
  podcast.export_episode(episode)
45
+
46
+ when "stats"
47
+ podcast = Podcast.from_yaml(global_opts[:podcast])
48
+ if STDIN.tty?
49
+ STDERR.puts "You need to pipe logs to gst-kitchen stats, e.g. cat logs/* | gst-kitchen stats"
50
+ exit
51
+ end
52
+ downloads = {}
53
+ while (logline = STDIN.gets)
54
+ # 93.194.154.91 - - [13/Dec/2012:16:22:38 +0100] "GET /episodes/gst000.m4a HTTP/1.1" 200 18676513 "-" "iTunes/11.0 (Macintosh; OS X 10.8.2) AppleWebKit/536.26.14"
55
+
56
+ / - - \[(?<date>.*?)\].*?(?<filename>gst\d{3}\....).*?2\d\d (?<size>\d+) / =~ logline
57
+
58
+ next unless filename
59
+
60
+ handle, format = filename.split(".")
61
+
62
+ next unless episode = podcast.episode_by_handle(handle)
63
+ next unless ["mp3", "m4a"].include? format
64
+
65
+ downloads[handle] ||= {}
66
+ downloads[handle][format] ||= {}
67
+
68
+ media_format = format == "mp3" ? "mp3" : "aac"
69
+
70
+ downloads[handle][format][:size] ||= episode.media[media_format]["size"]
71
+ downloads[handle][format][:downloaded] ||= 0
72
+ downloads[handle][format][:downloaded] += size.to_i
73
+ end
74
+
75
+ total_downloads = {}
76
+ downloads.each do |handle, episode|
77
+ episode.each do |format, stats|
78
+ downloads[handle][format][:count] = stats[:downloaded].to_f / stats[:size]
79
+ total_downloads[handle] ||= {}
80
+ total_downloads[handle]["total_downloads"] ||= 0
81
+ total_downloads[handle]["total_downloads"] += downloads[handle][format][:count]
82
+ end
83
+ end
84
+
85
+ downloads.keys.each do |handle|
86
+ downloads[handle].merge!(total_downloads[handle])
87
+ end
88
+
89
+ puts downloads.to_yaml
43
90
  else
44
91
  Trollop::die "unknown subcommand #{subcmd.inspect}"
45
92
  end
data/gst-kitchen.gemspec CHANGED
@@ -10,7 +10,7 @@ Gem::Specification.new do |gem|
10
10
  gem.email = ["sebastian.cohnen@gmx.net"]
11
11
  gem.description = %q{gst-kitchen is a gem to publish podcasts like a nerd with auphonic!}
12
12
  gem.summary = %q{gst-kitchen is a gem to publish podcasts like a nerd with auphonic!}
13
- gem.homepage = ""
13
+ gem.homepage = "http://github.com/tisba/gst-kitchen"
14
14
 
15
15
  gem.files = `git ls-files`.split($/)
16
16
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
@@ -20,4 +20,5 @@ Gem::Specification.new do |gem|
20
20
  gem.add_dependency "yajl-ruby"
21
21
  gem.add_dependency "trollop"
22
22
  gem.add_dependency "rake"
23
+ gem.add_dependency "redcarpet"
23
24
  end
data/lib/gst-kitchen.rb CHANGED
@@ -7,6 +7,7 @@ require "yaml"
7
7
 
8
8
  require "gst-kitchen/version"
9
9
  require "gst-kitchen/podcast"
10
+ require "gst-kitchen/feed"
10
11
  require "gst-kitchen/episode"
11
12
  require "gst-kitchen/media"
12
13
 
@@ -1,4 +1,4 @@
1
- class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :published_at, :summary, :description)
1
+ class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :published_at, :summary)
2
2
  include Comparable
3
3
 
4
4
  def self.from_auphonic(production)
@@ -11,7 +11,6 @@ class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :pub
11
11
  length: data["data"]["length"],
12
12
  name: data["data"]["metadata"]["subtitle"].strip,
13
13
  summary: data["data"]["metadata"]["summary"].strip,
14
- description: data["data"]["metadata"]["summary"].strip
15
14
  }
16
15
 
17
16
  media = data["data"]["output_files"].each_with_object({}) do |item, obj|
@@ -29,7 +28,6 @@ class Episode < Struct.new(:number, :name, :length, :media, :auphonic_uuid, :pub
29
28
  episode.auphonic_uuid = metadata[:auphonic_uuid]
30
29
  episode.published_at = Time.now
31
30
  episode.summary = metadata[:summary]
32
- episode.description = metadata[:description]
33
31
  episode.media = media
34
32
 
35
33
  episode
@@ -0,0 +1,71 @@
1
+ require 'redcarpet'
2
+ require 'ruby-debug'
3
+
4
+ class GstKitchen::Feed
5
+
6
+ class ShownotesRenderer < Redcarpet::Render::HTML
7
+
8
+ TWITTER_HANDLE_PATTER = /(\W)@(\w+)\b/
9
+
10
+ def preprocess(full_document)
11
+ return full_document if full_document.nil?
12
+
13
+ # Duplicate the string so we don't alter the original, then call to_str
14
+ # to cast it back to a String instead of a SafeBuffer. This is required
15
+ # for gsub calls to work as we need them to.
16
+ full_document = full_document.dup.to_str
17
+
18
+ # Extract pre blocks so they are not altered
19
+ # from http://github.github.com/github-flavored-markdown/
20
+ extractions = {}
21
+ full_document.gsub!(%r{<pre>.*?</pre>|<code>.*?</code>}m) do |match|
22
+ md5 = Digest::MD5.hexdigest(match)
23
+ extractions[md5] = match
24
+ "{gfm-extraction-#{md5}}"
25
+ end
26
+
27
+ full_document = parse(full_document)
28
+
29
+ # Insert pre block extractions
30
+ full_document.gsub!(/\{gfm-extraction-(\h{32})\}/) do
31
+ extractions[$1]
32
+ end
33
+
34
+ return full_document
35
+ end
36
+
37
+ private
38
+
39
+ def parse(document)
40
+ parse_twitter_handle(document)
41
+
42
+ return document
43
+ end
44
+
45
+ def parse_twitter_handle(document)
46
+ document.gsub!(TWITTER_HANDLE_PATTER, '\1[@\2](https://twitter.com/\2)')
47
+ end
48
+
49
+ end
50
+
51
+ TEMPLATE_PATH = File.join(File.dirname(__FILE__), "..", "..", "templates")
52
+
53
+ attr_reader :format, :template
54
+
55
+ def initialize(options = {})
56
+ @format = options[:format]
57
+ @template = ERB.new(File.read(File.join(TEMPLATE_PATH, "#{options[:template]}.rss.erb")))
58
+ end
59
+
60
+ def render_as_markdown(text)
61
+ markdown = Redcarpet::Markdown.new(ShownotesRenderer, autolink: true)
62
+ markdown.render text
63
+ end
64
+
65
+ def to_xml(variables = {})
66
+ variables.each { |var, value| instance_variable_set("@#{var}", value) }
67
+
68
+ @template.result(binding)
69
+ end
70
+
71
+ end
@@ -50,6 +50,12 @@ class Podcast
50
50
  Episode.from_auphonic production
51
51
  end
52
52
 
53
+ def episode_by_handle(handle)
54
+ self.episodes.find do |episode|
55
+ episode.handle.downcase == handle.downcase
56
+ end
57
+ end
58
+
53
59
  def feed_url(format)
54
60
  url = URI(self.website)
55
61
  url.path = "/#{rss_file(format)}"
@@ -78,15 +84,10 @@ class Podcast
78
84
  self
79
85
  end
80
86
 
81
- def current_format
82
- @current_format
83
- end
84
-
85
87
  def render_feed(format)
86
- @current_format = format
87
- template = ERB.new File.read(File.join(File.dirname(__FILE__), "../..", "templates/episodes.rss.erb"))
88
+ feed = GstKitchen::Feed.new format: format, template: "episodes"
88
89
  File.open(File.join(rss_output_path, rss_file(format)), "w") do |rss|
89
- rss.write template.result(binding)
90
+ rss.write feed.to_xml(podcast: podcast)
90
91
  end
91
92
  end
92
93
 
@@ -106,6 +107,7 @@ class Podcast
106
107
  end
107
108
 
108
109
  private
110
+
109
111
  def rss_file(format)
110
112
  "episodes.#{format.file_ext}.rss"
111
113
  end
@@ -1,3 +1,3 @@
1
1
  module GstKitchen
2
- VERSION = "0.2.0"
2
+ VERSION = "0.4.0"
3
3
  end
@@ -1,27 +1,27 @@
1
1
  <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2
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
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"/>
4
+ <title><%= @podcast.title %></title>
5
+ <link><%= @podcast.website %></link>
6
+ <atom:link rel="self" href="<%= @podcast.feed_url(@format) %>" type="application/rss+xml" title="<%= @format.file_ext.upcase %> Audio"/>
7
+ <% @podcast.other_formats(@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
9
  <% end %>
10
- <language><%= podcast.language %></language>
10
+ <language><%= @podcast.language %></language>
11
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>
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
19
  <itunes:owner>
20
- <itunes:name><%= podcast.author %></itunes:name>
21
- <itunes:email><%= podcast.email %></itunes:email>
20
+ <itunes:name><%= @podcast.author %></itunes:name>
21
+ <itunes:email><%= @podcast.email %></itunes:email>
22
22
  </itunes:owner>
23
23
 
24
- <itunes:image href="<%= podcast.cover_url %>"/>
24
+ <itunes:image href="<%= @podcast.cover_url %>"/>
25
25
 
26
26
  <itunes:category text="Technology"/>
27
27
  <itunes:category text="Technology">
@@ -31,19 +31,19 @@
31
31
  <itunes:category text="Tech News"/>
32
32
  </itunes:category>
33
33
 
34
- <% podcast.episodes.each do |episode| %>
34
+ <% @podcast.episodes.each do |episode| %>
35
35
  <item>
36
36
  <title><%= episode.title %></title>
37
- <itunes:explicit><%= podcast.explicit ? "yes" : "no" %></itunes:explicit>
38
- <itunes:author><%= podcast.author %></itunes:author>
37
+ <itunes:explicit><%= @podcast.explicit ? "yes" : "no" %></itunes:explicit>
38
+ <itunes:author><%= @podcast.author %></itunes:author>
39
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 %>"/>
40
+ <enclosure url="<%= @podcast.episode_media_url(episode, @format) %>" length="<%= episode.media[@format.format]["size"] %>" type="<%= @format.mime_type %>"/>
41
41
  <guid isPermaLink="false"><%= episode.handle %></guid>
42
42
  <pubDate><%= episode.rfc_2822_date %></pubDate>
43
43
  <itunes:duration><%= episode.duration %></itunes:duration>
44
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) %>" />
45
+ <description><![CDATA[<%= render_as_markdown episode.summary %>]]></description>
46
+ <atom:link rel="http://podlove.org/deep-link" href="<%= @podcast.deep_link_url(episode) %>" />
47
47
  </item>
48
48
  <% end %>
49
49
  </channel>
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: gst-kitchen
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.0
4
+ version: 0.4.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-12 00:00:00.000000000 Z
12
+ date: 2013-01-28 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: yajl-ruby
@@ -59,6 +59,22 @@ dependencies:
59
59
  - - ! '>='
60
60
  - !ruby/object:Gem::Version
61
61
  version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: redcarpet
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
62
78
  description: gst-kitchen is a gem to publish podcasts like a nerd with auphonic!
63
79
  email:
64
80
  - sebastian.cohnen@gmx.net
@@ -79,11 +95,12 @@ files:
79
95
  - lib/gst-kitchen/auphonic/account.rb
80
96
  - lib/gst-kitchen/auphonic/production.rb
81
97
  - lib/gst-kitchen/episode.rb
98
+ - lib/gst-kitchen/feed.rb
82
99
  - lib/gst-kitchen/media.rb
83
100
  - lib/gst-kitchen/podcast.rb
84
101
  - lib/gst-kitchen/version.rb
85
102
  - templates/episodes.rss.erb
86
- homepage: ''
103
+ homepage: http://github.com/tisba/gst-kitchen
87
104
  licenses: []
88
105
  post_install_message:
89
106
  rdoc_options: []
@@ -97,7 +114,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
97
114
  version: '0'
98
115
  segments:
99
116
  - 0
100
- hash: -203743376280899826
117
+ hash: 1700055491858221400
101
118
  required_rubygems_version: !ruby/object:Gem::Requirement
102
119
  none: false
103
120
  requirements:
@@ -106,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
106
123
  version: '0'
107
124
  segments:
108
125
  - 0
109
- hash: -203743376280899826
126
+ hash: 1700055491858221400
110
127
  requirements: []
111
128
  rubyforge_project:
112
129
  rubygems_version: 1.8.24