in_our_time 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 +7 -0
- data/.gitignore +4 -0
- data/Gemfile +5 -0
- data/Rakefile +15 -0
- data/VERSION +1 -0
- data/bin/iot +4 -0
- data/in_our_time.gemspec +23 -0
- data/lib/in_our_time.rb +4 -0
- data/lib/iot/colour_prompt.rb +129 -0
- data/lib/iot/iot.rb +539 -0
- metadata +83 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d9043f6f2dbe432df896bf146dd287d879aa5869
|
4
|
+
data.tar.gz: c1952b8d0ea58b5a4019a9842b1ad36078683bb3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9e09695c6a2cbe9f8ee92220461bfb8d749be4c2edc469a16de2e18acecc3364aed07d482ae9d22b37a8917312b7a85f878e98b1e027613b3f5dc483dc6a569e
|
7
|
+
data.tar.gz: 9fba64691995283bce6b0737676056e118a68f413d380cb3a9001bfd4a1b65709d5e29148f756bd55992cbd65562d88500745e20647a1eecd3ce20510bb87280
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'bundler/gem_tasks'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/version_task'
|
4
|
+
require 'yard'
|
5
|
+
|
6
|
+
Rake::VersionTask.new
|
7
|
+
|
8
|
+
Rake::TestTask.new do |t|
|
9
|
+
t.pattern = "test/test_*.rb"
|
10
|
+
end
|
11
|
+
|
12
|
+
YARD::Rake::YardocTask.new do |t|
|
13
|
+
t.files = ['lib/**/*.rb']
|
14
|
+
t.options = ['--any', '--extra', '--opts']
|
15
|
+
end
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
data/bin/iot
ADDED
data/in_our_time.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
|
5
|
+
Gem::Specification.new do |spec|
|
6
|
+
spec.name = 'in_our_time'
|
7
|
+
spec.version = File.read('VERSION')
|
8
|
+
spec.date = '2016-09-26'
|
9
|
+
spec.authors = ["Martyn Jago"]
|
10
|
+
spec.email = ["martyn.jago@btinternet.com"]
|
11
|
+
spec.description = "In Our Time Player"
|
12
|
+
spec.summary = "Select, play, and download BBC \'In Our Time\' podcasts - all from the command line"
|
13
|
+
spec.homepage = 'https://github.com/mjago/In_Our_Time'
|
14
|
+
spec.files = `git ls-files`.split($/)
|
15
|
+
spec.executables = "iot"
|
16
|
+
spec.bindir = 'bin'
|
17
|
+
spec.license = 'MIT'
|
18
|
+
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
spec.required_ruby_version = '>= 2.0.0'
|
21
|
+
spec.add_runtime_dependency 'nokogiri', '>= 1.6.8'
|
22
|
+
spec.add_development_dependency 'version', '>= 1.0.0'
|
23
|
+
end
|
data/lib/in_our_time.rb
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
# ==========================================
|
2
|
+
# Unity Project - A Test Framework for C
|
3
|
+
# Copyright (c) 2007 Mike Karlesky, Mark VanderVoord, Greg Williams
|
4
|
+
# [Released under MIT License. Please refer to license.txt for details]
|
5
|
+
# ==========================================
|
6
|
+
|
7
|
+
if RUBY_PLATFORM =~/(win|w)32$/
|
8
|
+
begin
|
9
|
+
require 'Win32API'
|
10
|
+
rescue LoadError
|
11
|
+
puts "ERROR! \"Win32API\" library not found"
|
12
|
+
puts "\"Win32API\" is required for colour on a windows machine"
|
13
|
+
puts " try => \"gem install Win32API\" on the command line"
|
14
|
+
puts
|
15
|
+
end
|
16
|
+
# puts
|
17
|
+
# puts 'Windows Environment Detected...'
|
18
|
+
# puts 'Win32API Library Found.'
|
19
|
+
# puts
|
20
|
+
end
|
21
|
+
|
22
|
+
class ColourCommandLine
|
23
|
+
def initialize
|
24
|
+
if RUBY_PLATFORM =~/(win|w)32$/
|
25
|
+
get_std_handle = Win32API.new("kernel32", "GetStdHandle", ['L'], 'L')
|
26
|
+
@set_console_txt_attrb =
|
27
|
+
Win32API.new("kernel32","SetConsoleTextAttribute",['L','N'], 'I')
|
28
|
+
@hout = get_std_handle.call(-11)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def change_to(new_colour)
|
33
|
+
if RUBY_PLATFORM =~/(win|w)32$/
|
34
|
+
@set_console_txt_attrb.call(@hout,self.win32_colour(new_colour))
|
35
|
+
else
|
36
|
+
"\033[30;#{posix_colour(new_colour)};22m"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def win32_colour(colour)
|
41
|
+
case colour
|
42
|
+
when :black then 0
|
43
|
+
when :dark_blue then 1
|
44
|
+
when :dark_green then 2
|
45
|
+
when :dark_cyan then 3
|
46
|
+
when :dark_red then 4
|
47
|
+
when :dark_purple then 5
|
48
|
+
when :dark_yellow, :narrative then 6
|
49
|
+
when :default_white, :default, :dark_white then 7
|
50
|
+
when :silver then 8
|
51
|
+
when :blue then 9
|
52
|
+
when :green, :success then 10
|
53
|
+
when :cyan, :output then 11
|
54
|
+
when :red, :failure then 12
|
55
|
+
when :purple then 13
|
56
|
+
when :yellow then 14
|
57
|
+
when :white then 15
|
58
|
+
else
|
59
|
+
0
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def posix_colour(colour)
|
64
|
+
# ANSI Escape Codes - Foreground colors
|
65
|
+
# | Code | Color |
|
66
|
+
# | 39 | Default foreground color |
|
67
|
+
# | 30 | Black |
|
68
|
+
# | 31 | Red |
|
69
|
+
# | 32 | Green |
|
70
|
+
# | 33 | Yellow |
|
71
|
+
# | 34 | Blue |
|
72
|
+
# | 35 | Magenta |
|
73
|
+
# | 36 | Cyan |
|
74
|
+
# | 37 | Light gray |
|
75
|
+
# | 90 | Dark gray |
|
76
|
+
# | 91 | Light red |
|
77
|
+
# | 92 | Light green |
|
78
|
+
# | 93 | Light yellow |
|
79
|
+
# | 94 | Light blue |
|
80
|
+
# | 95 | Light magenta |
|
81
|
+
# | 96 | Light cyan |
|
82
|
+
# | 97 | White |
|
83
|
+
|
84
|
+
case colour
|
85
|
+
when :black then 30
|
86
|
+
when :red, :failure then 31
|
87
|
+
when :green, :success then 32
|
88
|
+
when :yellow then 33
|
89
|
+
when :blue, :narrative then 34
|
90
|
+
when :purple, :magenta then 35
|
91
|
+
when :cyan, :output then 36
|
92
|
+
when :white, :default_white then 37
|
93
|
+
when :default then 39
|
94
|
+
when :dark_gray then 90 # Dark gray
|
95
|
+
when :light_red then 91 # Light red
|
96
|
+
when :light_green then 92 # Light green
|
97
|
+
when :light_yellow then 93 # Light yellow
|
98
|
+
when :light_blue then 94 # Light blue
|
99
|
+
when :light_magenta then 95 # Light magenta
|
100
|
+
when :light_cyan then 96 # Light cyan
|
101
|
+
when :White then 97 # White
|
102
|
+
|
103
|
+
else
|
104
|
+
39
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
def out_c(mode, colour, str, out = :stdout)
|
109
|
+
buf = case out
|
110
|
+
when :stdout
|
111
|
+
$stdout
|
112
|
+
else
|
113
|
+
out = out
|
114
|
+
end
|
115
|
+
case RUBY_PLATFORM
|
116
|
+
when /(win|w)32$/
|
117
|
+
change_to(colour)
|
118
|
+
buf.puts str if mode == :puts
|
119
|
+
buf.print str if mode == :print
|
120
|
+
change_to(:default_white)
|
121
|
+
else
|
122
|
+
buf.puts("#{change_to(colour)}#{str}\033[0m") if mode == :puts
|
123
|
+
$stdout.print("#{change_to(colour)}#{str}\033[0m") if mode == :print
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end # ColourCommandLine
|
127
|
+
|
128
|
+
def colour_puts(role,str, out = :stdout) ColourCommandLine.new.out_c(:puts, role, str, out) end
|
129
|
+
def colour_print(role,str, out = :stdout) ColourCommandLine.new.out_c(:print, role, str, out) end
|
data/lib/iot/iot.rb
ADDED
@@ -0,0 +1,539 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'rss'
|
3
|
+
require 'open-uri'
|
4
|
+
require 'net/http'
|
5
|
+
require 'open-uri'
|
6
|
+
require 'yaml'
|
7
|
+
require 'fileutils'
|
8
|
+
|
9
|
+
class InOurTime
|
10
|
+
ROOT = File.expand_path '~/'
|
11
|
+
IN_OUR_TIME = File.join ROOT, '.in_our_time'
|
12
|
+
CONFIG = File.join IN_OUR_TIME, 'config.yml'
|
13
|
+
UPDATE_INTERVAL = 604800
|
14
|
+
AUDIO_DIRECTORY = 'audio'
|
15
|
+
RSS_DIRECTORY = 'rss'
|
16
|
+
PAGE_HEIGHT = 20
|
17
|
+
PAGE_WIDTH = 80
|
18
|
+
|
19
|
+
class KeyboardEvents
|
20
|
+
|
21
|
+
@arrow = 0
|
22
|
+
|
23
|
+
def input
|
24
|
+
sleep 0.001
|
25
|
+
begin
|
26
|
+
system("stty raw -echo")
|
27
|
+
str = STDIN.getc
|
28
|
+
ensure
|
29
|
+
system("stty -raw echo")
|
30
|
+
end
|
31
|
+
|
32
|
+
case @arrow
|
33
|
+
when 1
|
34
|
+
if str == "["
|
35
|
+
@arrow = 2
|
36
|
+
else
|
37
|
+
@arrow = 0
|
38
|
+
end
|
39
|
+
|
40
|
+
when 2
|
41
|
+
return :previous if str == "A"
|
42
|
+
return :next if str == "B"
|
43
|
+
return :page_forward if str == "C"
|
44
|
+
return :previous if str == "D"
|
45
|
+
@arrow = 0
|
46
|
+
end
|
47
|
+
|
48
|
+
case str
|
49
|
+
when "\e"
|
50
|
+
@arrow = 1
|
51
|
+
when "l",'L'
|
52
|
+
:list
|
53
|
+
when ' '
|
54
|
+
:page_forward
|
55
|
+
when "q",'Q', "\u0003", "\u0004"
|
56
|
+
:quit
|
57
|
+
when 'p', 'P'
|
58
|
+
:previous
|
59
|
+
when 'n', 'N'
|
60
|
+
:next
|
61
|
+
when 's', 'S'
|
62
|
+
:stop
|
63
|
+
when 'x', 'X', "\r"
|
64
|
+
:play
|
65
|
+
when 'i', 'I'
|
66
|
+
:info
|
67
|
+
when '?', 'h'
|
68
|
+
:help
|
69
|
+
else
|
70
|
+
:unknown
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def initialize
|
76
|
+
@programs, @selected = [], 0
|
77
|
+
setup
|
78
|
+
load_config
|
79
|
+
check_remote
|
80
|
+
parse_rss
|
81
|
+
sort_titles
|
82
|
+
run
|
83
|
+
end
|
84
|
+
|
85
|
+
def iot_print x, role = :default
|
86
|
+
colour_print(role, x) if @config[:colour]
|
87
|
+
print(x) unless @config[:colour]
|
88
|
+
end
|
89
|
+
|
90
|
+
def iot_puts x, role = :default
|
91
|
+
colour_puts(role, x) if @config[:colour]
|
92
|
+
puts(x) unless @config[:colour]
|
93
|
+
end
|
94
|
+
|
95
|
+
def now
|
96
|
+
Time.now.to_i
|
97
|
+
end
|
98
|
+
|
99
|
+
def setup
|
100
|
+
iot = IN_OUR_TIME
|
101
|
+
audio = File.join iot, AUDIO_DIRECTORY
|
102
|
+
pages = File.join iot, RSS_DIRECTORY
|
103
|
+
Dir.mkdir iot unless Dir.exist? iot
|
104
|
+
Dir.mkdir audio unless Dir.exist? audio
|
105
|
+
unless Dir.exist? pages
|
106
|
+
Dir.mkdir pages
|
107
|
+
local_rss.map{|f| FileUtils.touch(File.join pages, f)}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def update_remote?
|
112
|
+
now - @config[:update_interval] > @config[:last_update]
|
113
|
+
end
|
114
|
+
|
115
|
+
def new_config
|
116
|
+
{:last_update => now - UPDATE_INTERVAL - 1,
|
117
|
+
:update_interval => UPDATE_INTERVAL,
|
118
|
+
:colour => true,
|
119
|
+
:mpg_player => :afplay,
|
120
|
+
:sort => :age,
|
121
|
+
:show_count => true,
|
122
|
+
:page_height => PAGE_HEIGHT,
|
123
|
+
:page_width => PAGE_WIDTH
|
124
|
+
}
|
125
|
+
end
|
126
|
+
|
127
|
+
def load_config
|
128
|
+
unless File.exist? CONFIG
|
129
|
+
save_config new_config
|
130
|
+
end
|
131
|
+
@config = YAML::load_file(CONFIG)
|
132
|
+
@line_count = @config[:page_height]
|
133
|
+
end
|
134
|
+
|
135
|
+
def save_config cfg = @config
|
136
|
+
File.open(CONFIG, 'w') { |f| f.write cfg.to_yaml}
|
137
|
+
end
|
138
|
+
|
139
|
+
def rss_addresses
|
140
|
+
host = 'http://www.bbc.co.uk/programmes'
|
141
|
+
[ "/b006qykl/episodes/downloads.rss",
|
142
|
+
"/p01drwny/episodes/downloads.rss",
|
143
|
+
"/p01dh5yg/episodes/downloads.rss",
|
144
|
+
"/p01f0vzr/episodes/downloads.rss",
|
145
|
+
"/p01gvqlg/episodes/downloads.rss",
|
146
|
+
"/p01gyd7j/episodes/downloads.rss"
|
147
|
+
].collect{|page| host + page}
|
148
|
+
end
|
149
|
+
|
150
|
+
def local_rss
|
151
|
+
[
|
152
|
+
"culture.rss",
|
153
|
+
"history.rss",
|
154
|
+
"in_our_time.rss",
|
155
|
+
"philosophy.rss",
|
156
|
+
"religion.rss",
|
157
|
+
"science.rss"
|
158
|
+
]
|
159
|
+
end
|
160
|
+
|
161
|
+
def fetch_uri uri, file
|
162
|
+
open(file, "wb") do |f|
|
163
|
+
open(uri) do |ur|
|
164
|
+
f.write(ur.read)
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def filename_from_title title
|
170
|
+
temp = title.gsub(/[^0-9a-z ]/i, '').gsub(' ', '_').strip + '.mp3'
|
171
|
+
File.join(File.join IN_OUR_TIME, AUDIO_DIRECTORY, temp.downcase)
|
172
|
+
end
|
173
|
+
|
174
|
+
def download_audio program, addr
|
175
|
+
res = Net::HTTP.get_response(URI.parse(addr))
|
176
|
+
case res
|
177
|
+
when Net::HTTPOK
|
178
|
+
File.open(filename_from_title(program[:title]) , 'wb') do |f|
|
179
|
+
iot_print "writing #{filename_from_title(program[:title])}...", :yellow
|
180
|
+
f.print(res.body)
|
181
|
+
iot_puts " written.", :yellow
|
182
|
+
end
|
183
|
+
program[:have_locally] = true
|
184
|
+
else
|
185
|
+
iot_puts 'audio download failed. Retrying...', :yellow
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def have_locally? title
|
190
|
+
filename = filename_from_title(title)
|
191
|
+
if File.exists?(filename)
|
192
|
+
return true
|
193
|
+
end
|
194
|
+
false
|
195
|
+
end
|
196
|
+
|
197
|
+
def rss_files
|
198
|
+
local_rss.map{|f| File.join IN_OUR_TIME, RSS_DIRECTORY, f }
|
199
|
+
end
|
200
|
+
|
201
|
+
def check_remote
|
202
|
+
if update_remote?
|
203
|
+
iot_print "checking rss feeds ", :yellow
|
204
|
+
local_rss.length.times do |count|
|
205
|
+
print '.'
|
206
|
+
fetch_uri rss_addresses[count], rss_files[count]
|
207
|
+
end
|
208
|
+
iot_puts ''
|
209
|
+
@config[:last_update] = now
|
210
|
+
save_config
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
def uniquify_programs
|
215
|
+
@programs = @programs.uniq{|pr| pr[:title]}
|
216
|
+
unless @programs.uniq.length == @programs.length
|
217
|
+
iot_puts "Error ensuring Programs unique!", :red
|
218
|
+
exit 1
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
def parse_rss
|
223
|
+
rss_files.each do |file|
|
224
|
+
@doc = Nokogiri::XML(File.open(file))
|
225
|
+
titles = @doc.xpath("//item//title")
|
226
|
+
descs = @doc.xpath("//item//description")
|
227
|
+
subtitles = @doc.xpath("//item//itunes:subtitle")
|
228
|
+
summarys = @doc.xpath("//item//itunes:summary")
|
229
|
+
durations = @doc.xpath("//item//itunes:duration")
|
230
|
+
dates = @doc.xpath("//item//pubDate")
|
231
|
+
links = @doc.xpath("//item//link")
|
232
|
+
|
233
|
+
0.upto (titles.length - 1) do |idx|
|
234
|
+
program = {}
|
235
|
+
program[:title] = titles[idx].text
|
236
|
+
# program[:description] = descs[idx]
|
237
|
+
program[:subtitle] = subtitles[idx].text
|
238
|
+
program[:summary] = summarys[idx].text
|
239
|
+
program[:duration] = durations[idx].text
|
240
|
+
program[:date] = (dates[idx].text)[0..15]
|
241
|
+
program[:link] = links[idx].text
|
242
|
+
program[:have_locally] = have_locally?(titles[idx].text)
|
243
|
+
@programs << program
|
244
|
+
end
|
245
|
+
end
|
246
|
+
uniquify_programs
|
247
|
+
end
|
248
|
+
|
249
|
+
def select_program title
|
250
|
+
@programs.each do |pr|
|
251
|
+
if pr[:title].strip == title.strip
|
252
|
+
return pr
|
253
|
+
end
|
254
|
+
end
|
255
|
+
nil
|
256
|
+
end
|
257
|
+
|
258
|
+
def sort_titles
|
259
|
+
@sorted_titles = []
|
260
|
+
@sorted_titles = @programs.collect { |pr| pr[:title] }
|
261
|
+
# @sorted_titles = @sorted_titles.uniq{|x| x.downcase}
|
262
|
+
@sorted_titles = @sorted_titles.sort unless @config[:sort] == :age
|
263
|
+
end
|
264
|
+
|
265
|
+
def date
|
266
|
+
@programs.map {|pr| return pr[:date] if pr[:title] == @playing}
|
267
|
+
end
|
268
|
+
|
269
|
+
def pre_delay
|
270
|
+
x = DateTime.strptime("Mon, 20 Jun 2016", '%a, %d %b %Y')
|
271
|
+
y = DateTime.strptime(date, '%a, %d %b %Y')
|
272
|
+
y < x ? '410' : '435'
|
273
|
+
end
|
274
|
+
|
275
|
+
def player_cmd
|
276
|
+
case @config[:mpg_player]
|
277
|
+
when :mpg123
|
278
|
+
"mpg123 -qk#{pre_delay}"
|
279
|
+
else
|
280
|
+
"afplay"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
|
284
|
+
def kill_cmd
|
285
|
+
"killall " +
|
286
|
+
case @config[:mpg_player]
|
287
|
+
when :mpg123
|
288
|
+
"mpg123"
|
289
|
+
else
|
290
|
+
"afplay"
|
291
|
+
end
|
292
|
+
end
|
293
|
+
|
294
|
+
def run_program prg
|
295
|
+
unless prg[:have_locally]
|
296
|
+
retries = 0
|
297
|
+
iot_puts "fetching #{prg[:title]}", :yellow
|
298
|
+
10.times do
|
299
|
+
res = Net::HTTP.get_response(URI.parse(prg[:link]))
|
300
|
+
case res
|
301
|
+
when Net::HTTPFound
|
302
|
+
iot_puts 'redirecting...', :yellow
|
303
|
+
@doc = Nokogiri::XML(res.body)
|
304
|
+
redirect = @doc.css("body p a").text
|
305
|
+
break if download_audio(prg, redirect)
|
306
|
+
sleep 2
|
307
|
+
else
|
308
|
+
iot_puts 'Error! Expected to be redirected!', :red
|
309
|
+
exit 1
|
310
|
+
end
|
311
|
+
retries += 1
|
312
|
+
end
|
313
|
+
if retries >= 10
|
314
|
+
iot_puts "Max retries downloading #{prg[:title]}", :red
|
315
|
+
exit 1
|
316
|
+
end
|
317
|
+
end
|
318
|
+
@play = Thread.new do
|
319
|
+
@playing = prg[:title]
|
320
|
+
system player_cmd + ' ' +
|
321
|
+
filename_from_title(@playing)
|
322
|
+
@playing = nil
|
323
|
+
end
|
324
|
+
end
|
325
|
+
|
326
|
+
def print_playing_maybe
|
327
|
+
iot_puts ''
|
328
|
+
if @playing
|
329
|
+
iot_puts "Playing '#{@playing}'"
|
330
|
+
elsif @started.nil?
|
331
|
+
@started = true
|
332
|
+
iot_puts "? or h for instructions"
|
333
|
+
else
|
334
|
+
iot_puts ''
|
335
|
+
end
|
336
|
+
end
|
337
|
+
|
338
|
+
def kill_audio
|
339
|
+
if @playing
|
340
|
+
system kill_cmd if @play
|
341
|
+
@play.kill if @play
|
342
|
+
@playing = nil
|
343
|
+
end
|
344
|
+
end
|
345
|
+
|
346
|
+
def idx_format idx
|
347
|
+
sprintf("%03d, ", idx + 1)
|
348
|
+
end
|
349
|
+
|
350
|
+
def show_count_maybe idx
|
351
|
+
if have_locally?(@sorted_titles[idx])
|
352
|
+
iot_print idx_format(idx), :cyan if @config[:show_count]
|
353
|
+
else
|
354
|
+
iot_print idx_format(idx), :yellow if @config[:show_count]
|
355
|
+
end
|
356
|
+
end
|
357
|
+
|
358
|
+
def draw_page
|
359
|
+
if @line_count <= @sorted_titles.length
|
360
|
+
@line_count.upto(@line_count + @config[:page_height] - 1) do |idx|
|
361
|
+
if idx < @sorted_titles.length
|
362
|
+
iot_print "> " if(idx == @selected) unless @config[:colour]
|
363
|
+
show_count_maybe idx
|
364
|
+
iot_puts @sorted_titles[idx], :purple if (idx == @selected)
|
365
|
+
iot_puts @sorted_titles[idx], :default unless(idx == @selected)
|
366
|
+
end
|
367
|
+
end
|
368
|
+
else
|
369
|
+
@line_count = 0
|
370
|
+
0.upto(@config[:page_height] - 1) do |idx|
|
371
|
+
iot_print "> " if(idx == @selected)
|
372
|
+
show_count_maybe(idx) unless @sorted_titles[idx].nil?
|
373
|
+
iot_puts @sorted_titles[idx] unless @sorted_titles[idx].nil?
|
374
|
+
end
|
375
|
+
end
|
376
|
+
@line_count += @config[:page_height]
|
377
|
+
print_playing_maybe
|
378
|
+
end
|
379
|
+
|
380
|
+
def display_list action
|
381
|
+
system 'clear'
|
382
|
+
case action
|
383
|
+
when :draw_page
|
384
|
+
draw_page
|
385
|
+
when :previous_page
|
386
|
+
if @line_count > 0
|
387
|
+
@line_count -= (@config[:page_height] * 2)
|
388
|
+
else
|
389
|
+
@line_count = @sorted_titles.length
|
390
|
+
@selected = @line_count
|
391
|
+
end
|
392
|
+
draw_page
|
393
|
+
when :same_page
|
394
|
+
@line_count -= @config[:page_height]
|
395
|
+
draw_page
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def help
|
400
|
+
unless @help
|
401
|
+
system 'clear'
|
402
|
+
iot_puts ''
|
403
|
+
iot_puts " In Our Time Player (Help) "
|
404
|
+
iot_puts ''
|
405
|
+
iot_puts " Next - N (down arrow) "
|
406
|
+
iot_puts " Previous - P (up arrow) "
|
407
|
+
iot_puts " Next Page - (right arrow) "
|
408
|
+
iot_puts " Next Page - (space) "
|
409
|
+
iot_puts " Play - X (return) "
|
410
|
+
iot_puts " Stop - S "
|
411
|
+
iot_puts " List - L "
|
412
|
+
iot_puts " Info - I "
|
413
|
+
iot_puts " Help - H "
|
414
|
+
iot_puts " Quit - Q "
|
415
|
+
iot_puts ''
|
416
|
+
iot_puts " tl;dr: "
|
417
|
+
iot_puts ''
|
418
|
+
iot_puts " Select: up/down arrows "
|
419
|
+
iot_puts " Play: enter "
|
420
|
+
18.upto(@config[:page_height] - 1) {iot_puts}
|
421
|
+
print_playing_maybe
|
422
|
+
@help = true
|
423
|
+
else
|
424
|
+
display_list :same_page
|
425
|
+
@help = nil
|
426
|
+
end
|
427
|
+
end
|
428
|
+
|
429
|
+
def reformat info
|
430
|
+
info.gsub('With ', "\nWith ")
|
431
|
+
.gsub('With: ', "\nWith: ")
|
432
|
+
.gsub('Producer', "- Producer")
|
433
|
+
end
|
434
|
+
|
435
|
+
def justify info
|
436
|
+
collect, top, bottom = [], 0, @config[:page_width]
|
437
|
+
loop do
|
438
|
+
if(bottom >= info.length)
|
439
|
+
collect << info[top..-1].strip
|
440
|
+
break
|
441
|
+
end
|
442
|
+
loop do
|
443
|
+
break unless info[top] == ' '
|
444
|
+
top += 1 ; bottom += 1
|
445
|
+
end
|
446
|
+
loop do
|
447
|
+
if idx = info[top..bottom].index("\n")
|
448
|
+
collect << info[top..top + idx]
|
449
|
+
bottom, top = top + idx + @config[:page_width] + 1, top + idx + 1
|
450
|
+
next
|
451
|
+
else
|
452
|
+
break if (info[bottom] == ' ')
|
453
|
+
bottom -= 1
|
454
|
+
end
|
455
|
+
end
|
456
|
+
collect << info[top..bottom]
|
457
|
+
bottom, top = bottom + @config[:page_width], bottom
|
458
|
+
end
|
459
|
+
collect
|
460
|
+
end
|
461
|
+
|
462
|
+
def info
|
463
|
+
if @info.nil?
|
464
|
+
system 'clear'
|
465
|
+
iot_puts ''
|
466
|
+
prg = select_program @sorted_titles[@selected]
|
467
|
+
iot_puts justify(prg[:subtitle].gsub(/\s+/, ' '))
|
468
|
+
iot_puts ''
|
469
|
+
iot_puts "Date Broadcast: #{prg[:date]}"
|
470
|
+
iot_puts "Duration: #{prg[:duration].to_i/60} mins"
|
471
|
+
iot_puts "Availability: " +
|
472
|
+
(prg[:have_locally] ? "Downloaded" : "Requires Download")
|
473
|
+
@info = 1
|
474
|
+
elsif @info == 1
|
475
|
+
system 'clear'
|
476
|
+
iot_puts ''
|
477
|
+
prg = select_program @sorted_titles[@selected]
|
478
|
+
info = prg[:summary].gsub(/\s+/, ' ')
|
479
|
+
iot_puts justify(reformat(info))
|
480
|
+
@info = -1
|
481
|
+
else
|
482
|
+
display_list :same_page
|
483
|
+
@info = nil
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
def run
|
488
|
+
display_list :same_page
|
489
|
+
key = KeyboardEvents.new
|
490
|
+
loop do
|
491
|
+
ip = key.input
|
492
|
+
@info = nil unless ip == :info
|
493
|
+
@help = nil unless ip == :help
|
494
|
+
|
495
|
+
case ip
|
496
|
+
when :list
|
497
|
+
@line_count = 0
|
498
|
+
@selected = 0
|
499
|
+
display_list :draw_page
|
500
|
+
when :page_forward
|
501
|
+
@selected = @line_count
|
502
|
+
display_list :draw_page
|
503
|
+
when :previous
|
504
|
+
@selected -= 1 if @selected > 0
|
505
|
+
if @selected >= @line_count -
|
506
|
+
@config[:page_height]
|
507
|
+
display_list :same_page
|
508
|
+
else
|
509
|
+
display_list :previous_page
|
510
|
+
end
|
511
|
+
when :next
|
512
|
+
@selected += 1
|
513
|
+
if @selected <= @line_count - 1
|
514
|
+
display_list :same_page
|
515
|
+
else
|
516
|
+
display_list :draw_page
|
517
|
+
end
|
518
|
+
when :play
|
519
|
+
kill_audio
|
520
|
+
title = @sorted_titles[@selected]
|
521
|
+
pr = select_program title
|
522
|
+
run_program pr
|
523
|
+
display_list :same_page
|
524
|
+
when :stop
|
525
|
+
kill_audio
|
526
|
+
when :info
|
527
|
+
info
|
528
|
+
when :help
|
529
|
+
help
|
530
|
+
when :quit
|
531
|
+
kill_audio
|
532
|
+
exit 0
|
533
|
+
end
|
534
|
+
sleep 0.001
|
535
|
+
end
|
536
|
+
end
|
537
|
+
end
|
538
|
+
|
539
|
+
InOurTime.new if __FILE__ == $0
|
metadata
ADDED
@@ -0,0 +1,83 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: in_our_time
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Martyn Jago
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-09-26 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.8
|
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.8
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: version
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.0
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 1.0.0
|
41
|
+
description: In Our Time Player
|
42
|
+
email:
|
43
|
+
- martyn.jago@btinternet.com
|
44
|
+
executables:
|
45
|
+
- iot
|
46
|
+
extensions: []
|
47
|
+
extra_rdoc_files: []
|
48
|
+
files:
|
49
|
+
- ".gitignore"
|
50
|
+
- Gemfile
|
51
|
+
- Rakefile
|
52
|
+
- VERSION
|
53
|
+
- bin/iot
|
54
|
+
- in_our_time.gemspec
|
55
|
+
- lib/in_our_time.rb
|
56
|
+
- lib/iot/colour_prompt.rb
|
57
|
+
- lib/iot/iot.rb
|
58
|
+
homepage: https://github.com/mjago/In_Our_Time
|
59
|
+
licenses:
|
60
|
+
- MIT
|
61
|
+
metadata: {}
|
62
|
+
post_install_message:
|
63
|
+
rdoc_options: []
|
64
|
+
require_paths:
|
65
|
+
- lib
|
66
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
67
|
+
requirements:
|
68
|
+
- - ">="
|
69
|
+
- !ruby/object:Gem::Version
|
70
|
+
version: 2.0.0
|
71
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
requirements: []
|
77
|
+
rubyforge_project:
|
78
|
+
rubygems_version: 2.6.6
|
79
|
+
signing_key:
|
80
|
+
specification_version: 4
|
81
|
+
summary: Select, play, and download BBC 'In Our Time' podcasts - all from the command
|
82
|
+
line
|
83
|
+
test_files: []
|