octopod-exe 0.9.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +8 -0
- data/Rakefile +11 -0
- data/assets/_config.yml.sample +48 -0
- data/assets/_includes/disqus_count.html +13 -0
- data/assets/_includes/disqus_thread.html +13 -0
- data/assets/_includes/flattr_loader.html +14 -0
- data/assets/_includes/isso_thread.html +6 -0
- data/assets/_includes/post.html +20 -0
- data/assets/_includes/post_header.html +17 -0
- data/assets/_includes/post_line.html +3 -0
- data/assets/_includes/sidebar.html +109 -0
- data/assets/_includes/tweet_us.html +3 -0
- data/assets/_layouts/default.html +86 -0
- data/assets/_layouts/feed.xml +107 -0
- data/assets/_layouts/jsonfeed.json +48 -0
- data/assets/_layouts/page.html +15 -0
- data/assets/_layouts/player_index.html +36 -0
- data/assets/_layouts/post.html +10 -0
- data/assets/_layouts/with_twitter_card.html +98 -0
- data/assets/_posts/2016-03-22-episode0.md +24 -0
- data/assets/_sass/_overrides.scss +68 -0
- data/assets/apple-touch-icon-precomposed.png +0 -0
- data/assets/episodes.m4a.rss +5 -0
- data/assets/episodes.mp3.rss +5 -0
- data/assets/episodes.ogg.rss +5 -0
- data/assets/episodes/episode0.m4a +0 -0
- data/assets/episodes/episode0.mp3 +0 -0
- data/assets/episodes/episode0.ogg +0 -0
- data/assets/favicon.ico +0 -0
- data/assets/feed.m4a.json +5 -0
- data/assets/feed.mp3.json +5 -0
- data/assets/feed.ogg.json +5 -0
- data/assets/general_feed.xml +30 -0
- data/assets/img/favicon.ico +0 -0
- data/assets/img/logo-360x360.png +0 -0
- data/assets/img/logo-itunes.jpg +0 -0
- data/assets/img/logo.jpg +0 -0
- data/assets/img/logo/android-icon-144x144.png +0 -0
- data/assets/img/logo/android-icon-192x192.png +0 -0
- data/assets/img/logo/android-icon-36x36.png +0 -0
- data/assets/img/logo/android-icon-48x48.png +0 -0
- data/assets/img/logo/android-icon-72x72.png +0 -0
- data/assets/img/logo/android-icon-96x96.png +0 -0
- data/assets/img/logo/apple-icon-114x114.png +0 -0
- data/assets/img/logo/apple-icon-120x120.png +0 -0
- data/assets/img/logo/apple-icon-144x144.png +0 -0
- data/assets/img/logo/apple-icon-152x152.png +0 -0
- data/assets/img/logo/apple-icon-180x180.png +0 -0
- data/assets/img/logo/apple-icon-57x57.png +0 -0
- data/assets/img/logo/apple-icon-60x60.png +0 -0
- data/assets/img/logo/apple-icon-72x72.png +0 -0
- data/assets/img/logo/apple-icon-76x76.png +0 -0
- data/assets/img/logo/apple-icon-precomposed.png +0 -0
- data/assets/img/logo/apple-icon.png +0 -0
- data/assets/img/logo/browserconfig.xml +2 -0
- data/assets/img/logo/favicon-16x16.png +0 -0
- data/assets/img/logo/favicon-32x32.png +0 -0
- data/assets/img/logo/favicon-96x96.png +0 -0
- data/assets/img/logo/favicon.ico +0 -0
- data/assets/img/logo/manifest.json +41 -0
- data/assets/img/logo/ms-icon-144x144.png +0 -0
- data/assets/img/logo/ms-icon-150x150.png +0 -0
- data/assets/img/logo/ms-icon-310x310.png +0 -0
- data/assets/img/logo/ms-icon-70x70.png +0 -0
- data/assets/imprint.md +19 -0
- data/assets/index.md +14 -0
- data/bin/octopod +273 -0
- data/lib/jekyll-octopod.rb +11 -0
- data/lib/jekyll/date_de.rb +54 -0
- data/lib/jekyll/flattr_filters.rb +107 -0
- data/lib/jekyll/octopod_filters.rb +336 -0
- data/lib/jekyll/paged_feed_page.rb +22 -0
- data/lib/jekyll/paged_feed_page_generator.rb +20 -0
- data/lib/jekyll/podcast_player_page.rb +24 -0
- data/lib/jekyll/podcast_player_page_generator.rb +14 -0
- data/lib/jekyll/podigee_player_tag.rb +44 -0
- data/lib/jekyll/static_file.rb +20 -0
- data/lib/jekyll/update_config.rb +14 -0
- data/lib/octopod/version.rb +11 -0
- metadata +580 -0
@@ -0,0 +1,336 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'digest/sha1'
|
3
|
+
|
4
|
+
module Jekyll
|
5
|
+
module OctopodFilters
|
6
|
+
JSON_ENTITIES = { '&' => '\u0026', '>' => '\u003E', '<' => '\u003C', "'" => '\u0027' }
|
7
|
+
|
8
|
+
# Escapes some text for CDATA
|
9
|
+
def cdata_escape(input)
|
10
|
+
input.gsub(/<!\[CDATA\[/, '<![CDATA[').gsub(/\]\]>/, ']]>')
|
11
|
+
end
|
12
|
+
|
13
|
+
# Escapes HTML entities in JSON strings.
|
14
|
+
# More or less a copy of the equivalent method in Active Support.
|
15
|
+
# https://github.com/rails/rails/tree/master/activesupport
|
16
|
+
def j(str)
|
17
|
+
str.to_s.gsub(/[&"><']/) { |e| JSON_ENTITIES[e] }
|
18
|
+
end
|
19
|
+
|
20
|
+
# Replaces relative urls with full urls
|
21
|
+
#
|
22
|
+
# {{ "about.html" | expand_urls }} => "/about.html"
|
23
|
+
# {{ "about.html" | expand_urls:site.url }} => "http://example.com/about.html"
|
24
|
+
def expand_urls(input, url='')
|
25
|
+
url ||= '/'
|
26
|
+
input.gsub /(\s+(href|src)\s*=\s*["|']{1})(\/[^\"'>]*)/ do
|
27
|
+
$1+url+$3
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
# Removes scripts tag and audio tags in multiline moderator
|
32
|
+
def remove_script_and_audio(input)
|
33
|
+
input.gsub(/<audio.*audio>/m, '').gsub(/<script.*script>/m, '')
|
34
|
+
end
|
35
|
+
|
36
|
+
# Formats a Time to be RSS compatible like "Wed, 15 Jun 2005 19:00:00 GMT"
|
37
|
+
#
|
38
|
+
# {{ site.time | time_to_rssschema }}
|
39
|
+
def time_to_rssschema(time)
|
40
|
+
time.strftime("%a, %d %b %Y %H:%M:%S %z")
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns the first argument if it's not nil or empty otherwise it returns
|
44
|
+
# the second one.
|
45
|
+
#
|
46
|
+
# {{ post.author | otherwise:site.author }}
|
47
|
+
def otherwise(first, second)
|
48
|
+
first = first.to_s
|
49
|
+
first.empty? ? second : first
|
50
|
+
end
|
51
|
+
|
52
|
+
# Returns the audio file name of a given hash. Is no key as second parameter given, it
|
53
|
+
# trys first "mp3", than "m4a" and than it will return a more or less random
|
54
|
+
# value.
|
55
|
+
#
|
56
|
+
# {{ post.audio | audio:"m4a" }} => "my-episode.m4a"
|
57
|
+
def audio(hsh, key = nil)
|
58
|
+
if key.nil?
|
59
|
+
hsh['mp3'] ? hsh['mp3'] : hsh['m4a'] ? hsh['m4a'] : hsh['ogg'] ? hsh['ogg'] : hsh['opus'] ? hsh['opus'] : hsh.values.first
|
60
|
+
else
|
61
|
+
hsh[key]
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
# Returns the audio-type of a given hash. Is no key as second parameter given, it
|
67
|
+
# trys first "mp3", than "m4a" and than it will return a more or less random
|
68
|
+
# value.
|
69
|
+
#
|
70
|
+
# {{ post.audio | audiotype }} => "my-episode.m4a"
|
71
|
+
def audio_type(hsh)
|
72
|
+
hsh['mp3'] ? mime_type('mp3') : hsh['m4a'] ? mime_type('m4a') : hsh['ogg'] ? mime_type('ogg') : mime_type('opus')
|
73
|
+
end
|
74
|
+
|
75
|
+
|
76
|
+
# Returns the MIME-Type of a given file format.
|
77
|
+
#
|
78
|
+
# {{ "m4a" | mime_type }} => "audio/mp4a-latm"
|
79
|
+
def mime_type(format)
|
80
|
+
types = {
|
81
|
+
'mp3' => 'mpeg',
|
82
|
+
'm4a' => 'mp4a-latm',
|
83
|
+
'ogg' => 'ogg; codecs=vorbis',
|
84
|
+
'opus' => 'ogg; codecs=opus'
|
85
|
+
}
|
86
|
+
|
87
|
+
"audio/#{types[format]}"
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns the size of a given file in bytes. If there is just a filename
|
91
|
+
# without a path, this method assumes that the file is an episode audio file
|
92
|
+
# which lives in /episodes.
|
93
|
+
#
|
94
|
+
# {{ "example.m4a" | file_size }} => 4242
|
95
|
+
def file_size(path, rel = nil)
|
96
|
+
return 0 if path.nil?
|
97
|
+
path = path =~ /\// ? path : File.join('episodes', path)
|
98
|
+
path = rel + path if rel
|
99
|
+
File.size(path)
|
100
|
+
end
|
101
|
+
|
102
|
+
# Returns a slug based on the id of a given page.
|
103
|
+
#
|
104
|
+
# {{ page | slug }} => '2012_10_02_octopod'
|
105
|
+
def slug(page)
|
106
|
+
page['id'][1..-1].gsub('/', '_')
|
107
|
+
end
|
108
|
+
|
109
|
+
# Returns the image of the post or the default logo.
|
110
|
+
#
|
111
|
+
# {{ page | image_with_fallback }} => '/path/to/image.png'
|
112
|
+
def image_with_fallback(page)
|
113
|
+
if page["image"]
|
114
|
+
"/img/" + page["image"]
|
115
|
+
else
|
116
|
+
"/img/logo-itunes.jpg"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Returns the dowrload url with fallback to the site's episode folder url
|
121
|
+
def download_url_with_fallback(site)
|
122
|
+
if site["download_url"] == "" or site["download_url"] == nil
|
123
|
+
site["url"] + "/episodes"
|
124
|
+
else
|
125
|
+
site["download_url"]
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# Splits a chapter, like it is written to the post YAML front matter into
|
131
|
+
# the components 'start' which refers to a single point in time relative to
|
132
|
+
# the beginning of the media file nad 'title' which defines the text to be
|
133
|
+
# the title of the chapter.
|
134
|
+
#
|
135
|
+
# {{ '00:00:00.000 Welcome to Octopod!' | split_chapter }}
|
136
|
+
# => { 'start' => '00:00:00.000', 'title' => 'Welcome to Octopod!' }
|
137
|
+
#
|
138
|
+
# {{ '00:00:00.000 Welcome to Octopod!' | split_chapter:'title' }}
|
139
|
+
# => 'Welcome to Octopod!'
|
140
|
+
#
|
141
|
+
# {{ '00:00:00.000 Welcome to Octopod!' | split_chapter:'start' }}
|
142
|
+
# => '00:00:00.000'
|
143
|
+
def split_chapter(chapter_str, attribute = nil)
|
144
|
+
attributes = chapter_str.split(/ /, 2)
|
145
|
+
return nil unless attributes.first.match(/\A(\d|:|\.)+\z/)
|
146
|
+
|
147
|
+
if attribute.nil?
|
148
|
+
{ 'start' => attributes.first, 'title' => attributes.last }
|
149
|
+
else
|
150
|
+
attribute == 'start' ? attributes.first : attributes.last
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
|
155
|
+
# Gets a number of seconds and returns an human readable duration string of
|
156
|
+
# it.
|
157
|
+
#
|
158
|
+
# {{ 1252251 | string_of_duration }} => "00:03:13"
|
159
|
+
def string_of_duration(duration)
|
160
|
+
seconds = duration.to_i
|
161
|
+
minutes = seconds / 60
|
162
|
+
hours = minutes / 60
|
163
|
+
|
164
|
+
"#{"%02d" % hours}:#{"%02d" % (minutes % 60)}:#{"%02d" % (seconds % 60)}"
|
165
|
+
end
|
166
|
+
|
167
|
+
# Gets a number of bytes and returns an human readable string of it.
|
168
|
+
#
|
169
|
+
# {{ 1252251 | string_of_size }} => "1.19M"
|
170
|
+
def string_of_size(bytes)
|
171
|
+
bytes = bytes.to_i.to_f
|
172
|
+
out = '0'
|
173
|
+
return out if bytes == 0.0
|
174
|
+
|
175
|
+
jedec = %w[b K M G]
|
176
|
+
[3, 2, 1, 0].each { |i|
|
177
|
+
if bytes > 1024 ** i
|
178
|
+
out = "%.1f#{jedec[i]}" % (bytes / 1024 ** i)
|
179
|
+
break
|
180
|
+
end
|
181
|
+
}
|
182
|
+
|
183
|
+
return out
|
184
|
+
end
|
185
|
+
|
186
|
+
# Returns the host a given url
|
187
|
+
#
|
188
|
+
# {{ 'https://github.com/pattex/octopod' | host_from_url }} => "github.com"
|
189
|
+
def host_from_url(url)
|
190
|
+
URI.parse(url).host
|
191
|
+
end
|
192
|
+
|
193
|
+
# Generates the config for disqus integration
|
194
|
+
# If a page object is given, it generates the config variables only for this
|
195
|
+
# page. Otherwise it generate only the global config variables.
|
196
|
+
#
|
197
|
+
# {{ site | disqus_config }}
|
198
|
+
# {{ site | disqus_config:page }}
|
199
|
+
def disqus_config(site, page = nil)
|
200
|
+
if page
|
201
|
+
disqus_vars = {
|
202
|
+
'disqus_identifier' => page['url'],
|
203
|
+
'disqus_url' => "#{site['url']}#{page['url']}",
|
204
|
+
'disqus_category_id' => page['disqus_category_id'] || site['disqus_category_id'],
|
205
|
+
'disqus_title' => j(page['title'] || site['site'])
|
206
|
+
}
|
207
|
+
else
|
208
|
+
disqus_vars = {
|
209
|
+
'disqus_developer' => site['disqus_developer'],
|
210
|
+
'disqus_shortname' => site['disqus_shortname']
|
211
|
+
}
|
212
|
+
end
|
213
|
+
|
214
|
+
disqus_vars.delete_if { |_, v| v.nil? }
|
215
|
+
disqus_vars.map { |k, v| "var #{k} = '#{v}';" }.compact.join("\n")
|
216
|
+
end
|
217
|
+
|
218
|
+
# Returns the hex-encoded hash value of a given string. The optional
|
219
|
+
# second argument defines the length of the returned string.
|
220
|
+
#
|
221
|
+
# {{ "Octopod" | sha1 }} => "8b20a59c"
|
222
|
+
# {{ "Octopod" | sha1:23 }} => "8b20a59c8e2dcb5e1f845ba"
|
223
|
+
def sha1(str, lenght = 8)
|
224
|
+
sha1 = Digest::SHA1.hexdigest(str)
|
225
|
+
sha1[0, lenght.to_i]
|
226
|
+
end
|
227
|
+
|
228
|
+
# Returns a, ready to use, navigation list of all pages that have
|
229
|
+
# <tt>navigation</tt> set in their YAML front matter. The list is sorted by
|
230
|
+
# the value of <tt>navigation</tt>.
|
231
|
+
#
|
232
|
+
# {{ site | navigation_list:page }}
|
233
|
+
def navigation_list(site, page)
|
234
|
+
pages = site['pages'].select { |p|
|
235
|
+
p.data['navigation'] && p.data['title']
|
236
|
+
}.sort_by { |p| p.data['navigation'] }
|
237
|
+
|
238
|
+
list = []
|
239
|
+
list << pages.map { |p|
|
240
|
+
active = (p.url == page['url']) || (page.key?('next') && File.join(p.dir, p.basename) == '/index')
|
241
|
+
navigation_list_item(File.join(site['url'], p.url), p.data['title'], active)
|
242
|
+
}
|
243
|
+
list.join("\n")
|
244
|
+
end
|
245
|
+
|
246
|
+
def talk_list(site, page)
|
247
|
+
pages = site['pages'].select { |p|
|
248
|
+
p.data['talk'] && p.data['title']
|
249
|
+
}.sort_by { |p| p.data['talk'] }
|
250
|
+
|
251
|
+
list = ['<li class="dropdown"><a class="dropdown-toggle" data-toggle="dropdown" href="#" role="button" aria-haspopup="true" aria-expanded="false"> Talks <span class="caret"></span>
|
252
|
+
</a><ul class="dropdown-menu">']
|
253
|
+
list << pages.map { |p|
|
254
|
+
active = (p.url == page['url']) || (page.key?('next') && File.join(p.dir, p.basename) == '/index')
|
255
|
+
navigation_list_item(File.join(site['url'], p.url), p.data['title'], active)
|
256
|
+
}
|
257
|
+
list << ['</ul></li>']
|
258
|
+
|
259
|
+
list.join("\n")
|
260
|
+
end
|
261
|
+
|
262
|
+
def navigation_list_item(url, title, active = false)
|
263
|
+
a_class = active ? ' class="active"' : ''
|
264
|
+
%Q{<li#{a_class}><a #{a_class} href="#{url}">#{title}</a></li>}
|
265
|
+
end
|
266
|
+
|
267
|
+
# Returns an array of all episode feeds named by the convetion
|
268
|
+
# 'episodes.<episode_file_format>.rss' within the root directory. Also it
|
269
|
+
# contains all additional feeds specified by 'additional_feeds' in the
|
270
|
+
# '_config.yml'. If an 'episode_file_format' or key of 'additional_feeds'
|
271
|
+
# equals the optional parameter 'except', it will be skipped.
|
272
|
+
#
|
273
|
+
# episode_feeds(site, except = nil) =>
|
274
|
+
# [
|
275
|
+
# ["m4a Episode RSS-Feed", "/episodes.m4a.rss"],
|
276
|
+
# ["mp3 Episode RSS-Feed", "/episodes.mp3.rss"],
|
277
|
+
# ["Torrent Feed m4a", "http://bitlove.org/octopod/octopod_m4a/feed"],
|
278
|
+
# ["Torrent Feed mp3", "http://bitlove.org/octopod/octopod_mp3/feed"]
|
279
|
+
# ]
|
280
|
+
def episode_feeds(site, except = nil)
|
281
|
+
feeds = []
|
282
|
+
|
283
|
+
if site['episode_feed_formats']
|
284
|
+
site['episode_feed_formats'].map { |f|
|
285
|
+
feeds << ["#{f} Episode RSS-Feed", "#{site['url']}/episodes.#{f}.rss"] unless f == except
|
286
|
+
}
|
287
|
+
end
|
288
|
+
if site['additional_feeds']
|
289
|
+
site['additional_feeds'].each { |k, v|
|
290
|
+
feeds << [k.gsub('_', ' '), v] unless k == except
|
291
|
+
}
|
292
|
+
end
|
293
|
+
|
294
|
+
feeds
|
295
|
+
end
|
296
|
+
|
297
|
+
# Returns HTML links to all episode feeds named by the convetion
|
298
|
+
# 'episodes.<episode_file_format>.rss' within the root directory. Also it
|
299
|
+
# returns all additional feeds specified by 'additional_feeds' in the
|
300
|
+
# '_config.yml'. If an 'episode_file_format' or key of 'additional_feeds'
|
301
|
+
# equals the optional parameter 'except', it will be skipped.
|
302
|
+
#
|
303
|
+
# {{ site | episode_feeds_html:'m4a' }} =>
|
304
|
+
# <link rel="alternate" type="application/rss+xml" title="mp3 Episode RSS-Feed" href="/episodes.mp3.rss" />
|
305
|
+
# <link rel="alternate" type="application/rss+xml" title="Torrent Feed m4a" href="http://bitlove.org/octopod/octopod_m4a/feed" />
|
306
|
+
# <link rel="alternate" type="application/rss+xml" title="Torrent Feed mp3" href="http://bitlove.org/octopod/octopod_mp3/feed" />
|
307
|
+
def episode_feeds_html(site, except = nil)
|
308
|
+
episode_feeds(site, except).map { |f|
|
309
|
+
%Q{<link rel="alternate" type="application/rss+xml" title="#{f.first || f.last}" href="#{f.last}" />}
|
310
|
+
}.join("\n")
|
311
|
+
end
|
312
|
+
|
313
|
+
# Returns RSS-XML links to all episode feeds named by the convetion
|
314
|
+
# 'episodes.<episode_file_format>.rss' within the root directory. Also it
|
315
|
+
# returns all additional feeds specified by 'additional_feeds' in the
|
316
|
+
# '_config.yml'. If an 'episode_file_format' or key of 'additional_feeds'
|
317
|
+
# equals the optional parameter 'except', it will be skipped.
|
318
|
+
#
|
319
|
+
# {{ site | episode_feeds_rss:'m4a' }} =>
|
320
|
+
# <atom:link rel="alternate" href="/episodes.mp3.rss" type="application/rss+xml" title="mp3 Episode RSS-Feed"/>
|
321
|
+
# <atom:link rel="alternate" href="http://bitlove.org/octopod/octopod_m4a/feed" type="application/rss+xml" title="Torrent Feed m4a"/>
|
322
|
+
# <atom:link rel="alternate" href="http://bitlove.org/octopod/octopod_mp3/feed" type="application/rss+xml" title="Torrent Feed mp3"/>
|
323
|
+
def episode_feeds_rss(site, except = nil)
|
324
|
+
episode_feeds(site, except).map { |f|
|
325
|
+
%Q{<atom:link rel="alternate" href="#{f.last}" type="application/rss+xml" title="#{f.first || f.last}"/>}
|
326
|
+
}.join("\n")
|
327
|
+
end
|
328
|
+
|
329
|
+
# Prints out current version
|
330
|
+
def version_string(site)
|
331
|
+
Jekyll::Octopod::VERSION::STRING
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|
335
|
+
|
336
|
+
Liquid::Template.register_filter(Jekyll::OctopodFilters)
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Jekyll
|
2
|
+
class PagedFeedPage < Page
|
3
|
+
def initialize(site, base, dir, name, page_number, pages_total, format)
|
4
|
+
@site = site
|
5
|
+
@dir = "/"
|
6
|
+
@name = name
|
7
|
+
|
8
|
+
self.process(@name)
|
9
|
+
self.read_yaml(File.join(base, '_layouts'), 'feed.xml')
|
10
|
+
self.data['next'] = pages_total > page_number ? (page_number + 1).to_s : nil
|
11
|
+
self.data['last'] = pages_total > 1 ? pages_total.to_s : nil
|
12
|
+
self.data['prev'] = case page_number
|
13
|
+
when 1 then nil
|
14
|
+
when 2 then ""
|
15
|
+
else (page_number - 1).to_s
|
16
|
+
end
|
17
|
+
self.data['myself'] = page_number == 1 ? nil : page_number.to_s
|
18
|
+
self.data['format'] = format
|
19
|
+
self.data['page_number'] = page_number
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
module Jekyll
|
2
|
+
class PagedFeedPageGenerator < Generator
|
3
|
+
safe true
|
4
|
+
|
5
|
+
def generate(site)
|
6
|
+
pages_total = (site.posts.docs.count.to_f / site.config["episodes_per_feed_page"]).ceil
|
7
|
+
|
8
|
+
site.config["episode_feed_formats"].each do |page_format|
|
9
|
+
name = "episodes." + page_format + ".rss"
|
10
|
+
page = PagedFeedPage.new(site, site.source, ".", name, 1, pages_total, page_format)
|
11
|
+
site.pages << page
|
12
|
+
(1..pages_total).each do |page_number|
|
13
|
+
name = "episodes" + page_number.to_s + "." + page_format + ".rss"
|
14
|
+
page = PagedFeedPage.new(site, site.source, ".", name, page_number, pages_total, page_format)
|
15
|
+
site.pages << page
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Jekyll
|
2
|
+
class PodcastPlayerPage < Page
|
3
|
+
def initialize(site, base, dir, post)
|
4
|
+
@site = site
|
5
|
+
@base = base
|
6
|
+
@dir = dir
|
7
|
+
@name = 'index.html'
|
8
|
+
|
9
|
+
self.process(@name)
|
10
|
+
self.read_yaml(File.join(base, '_layouts'), 'player_index.html')
|
11
|
+
|
12
|
+
self.data['title'] = post.data['title']
|
13
|
+
self.data['subtitle'] = post.data['subtitle']
|
14
|
+
self.data['datum'] = post.data['datum']
|
15
|
+
self.data['author'] = post.data['author']
|
16
|
+
self.data['duration'] = post.data['duration']
|
17
|
+
self.data['summary'] = post.data['summary']
|
18
|
+
self.data['explicit'] = post.data['explicit']
|
19
|
+
self.data['audio'] = post.data['audio']
|
20
|
+
self.data['chapters'] = post.data['chapters']
|
21
|
+
self.data['template'] = 'player_index'
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Jekyll
|
2
|
+
class PodcastPlayerPageGenerator < Generator
|
3
|
+
safe true
|
4
|
+
|
5
|
+
def generate(site)
|
6
|
+
if site.layouts.key? 'player_index'
|
7
|
+
dir = site.config['players_dir'] || 'players'
|
8
|
+
site.posts.docs.each do |post|
|
9
|
+
site.pages << PodcastPlayerPage.new(site, site.source, File.join(dir, post.data['slug']), post)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Jekyll
|
2
|
+
class PodigeePlayerTag < Liquid::Tag
|
3
|
+
def playerconfig(context)
|
4
|
+
config = context.registers[:site].config
|
5
|
+
page = context.registers[:page]
|
6
|
+
|
7
|
+
audio = {}
|
8
|
+
download_url = config["download_url"] || config["url"] + "/episodes"
|
9
|
+
page["audio"].each { |key, value| audio[key] = download_url + "/" + value}
|
10
|
+
|
11
|
+
{ options: { theme: "default",
|
12
|
+
startPanel: "ChapterMarks" },
|
13
|
+
extensions: { ChapterMarks: {},
|
14
|
+
EpisodeInfo: {},
|
15
|
+
Playlist: {} },
|
16
|
+
title: options['title'],
|
17
|
+
episode: { media: audio,
|
18
|
+
coverUrl: config['url'] + (page["episode_cover"] || '/img/logo-360x360.png'),
|
19
|
+
title: page["title"],
|
20
|
+
subtitle: page["subtitle"],
|
21
|
+
url: config['url'] + page["url"],
|
22
|
+
description: page["description"],
|
23
|
+
chaptermarks: page["chapters"].map {|chapter| { start: chapter[0..12], title: chapter[13..255] }}
|
24
|
+
}
|
25
|
+
}.to_json
|
26
|
+
end
|
27
|
+
|
28
|
+
def render(context)
|
29
|
+
config = context.registers[:site].config
|
30
|
+
page = context.registers[:page]
|
31
|
+
return unless page["audio"]
|
32
|
+
return <<~HTML
|
33
|
+
<script>
|
34
|
+
window.playerConfiguration = #{playerconfig(context)}
|
35
|
+
</script>
|
36
|
+
<script class="podigee-podcast-player" data-configuration="playerConfiguration"
|
37
|
+
src="#{config["url"].split(":").first}://cdn.podigee.com/podcast-player/javascripts/podigee-podcast-player.js">
|
38
|
+
</script>
|
39
|
+
HTML
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
Liquid::Template.register_tag('podigee_player', Jekyll::PodigeePlayerTag)
|