simple_pvr 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.rspec +1 -0
- data/Gemfile +4 -0
- data/Gemfile.lock +133 -0
- data/LICENSE.txt +13 -0
- data/README.md +169 -0
- data/Rakefile +16 -0
- data/bin/pvr_server +10 -0
- data/bin/pvr_xmltv +18 -0
- data/features/channel_overview.feature +48 -0
- data/features/programme_search.feature +20 -0
- data/features/scheduling.feature +112 -0
- data/features/step_definitions/pvr_steps.rb +104 -0
- data/features/step_definitions/web_steps.rb +219 -0
- data/features/support/env.rb +28 -0
- data/features/support/paths.rb +17 -0
- data/features/week_overview.feature +39 -0
- data/lib/simple_pvr/ffmpeg.rb +22 -0
- data/lib/simple_pvr/hdhomerun.rb +103 -0
- data/lib/simple_pvr/hdhomerun_save.sh +10 -0
- data/lib/simple_pvr/model/channel.rb +72 -0
- data/lib/simple_pvr/model/database_initializer.rb +36 -0
- data/lib/simple_pvr/model/programme.rb +58 -0
- data/lib/simple_pvr/model/recording.rb +45 -0
- data/lib/simple_pvr/model/schedule.rb +33 -0
- data/lib/simple_pvr/pvr_initializer.rb +47 -0
- data/lib/simple_pvr/pvr_logger.rb +14 -0
- data/lib/simple_pvr/recorder.rb +25 -0
- data/lib/simple_pvr/recording_manager.rb +101 -0
- data/lib/simple_pvr/recording_planner.rb +72 -0
- data/lib/simple_pvr/scheduler.rb +124 -0
- data/lib/simple_pvr/server/app_controller.rb +13 -0
- data/lib/simple_pvr/server/base_controller.rb +94 -0
- data/lib/simple_pvr/server/channels_controller.rb +68 -0
- data/lib/simple_pvr/server/config.ru +8 -0
- data/lib/simple_pvr/server/programmes_controller.rb +46 -0
- data/lib/simple_pvr/server/rack_maps.rb +7 -0
- data/lib/simple_pvr/server/schedules_controller.rb +71 -0
- data/lib/simple_pvr/server/shows_controller.rb +63 -0
- data/lib/simple_pvr/server/status_controller.rb +11 -0
- data/lib/simple_pvr/server/upcoming_recordings_controller.rb +18 -0
- data/lib/simple_pvr/version.rb +3 -0
- data/lib/simple_pvr/xmltv_reader.rb +83 -0
- data/lib/simple_pvr.rb +22 -0
- data/public/css/bootstrap-responsive.min.css +9 -0
- data/public/css/bootstrap.min.css +9 -0
- data/public/css/simplepvr.css +11 -0
- data/public/img/glyphicons-halflings-white.png +0 -0
- data/public/img/glyphicons-halflings.png +0 -0
- data/public/index.html +55 -0
- data/public/js/angular/angular-resource.min.js +10 -0
- data/public/js/angular/angular.min.js +157 -0
- data/public/js/app.js +145 -0
- data/public/js/bootstrap/bootstrap.min.js +6 -0
- data/public/js/controllers.js +156 -0
- data/public/js/services.js +27 -0
- data/public/partials/about.html +5 -0
- data/public/partials/channels.html +41 -0
- data/public/partials/programme.html +20 -0
- data/public/partials/programmeListing.html +18 -0
- data/public/partials/schedule.html +80 -0
- data/public/partials/schedules.html +44 -0
- data/public/partials/search.html +17 -0
- data/public/partials/show.html +21 -0
- data/public/partials/shows.html +7 -0
- data/public/partials/status.html +6 -0
- data/simple_pvr.gemspec +30 -0
- data/spec/resources/channels.txt +11 -0
- data/spec/resources/programs-without-icon.xmltv +95 -0
- data/spec/resources/programs.xmltv +98 -0
- data/spec/simple_pvr/ffmpeg_spec.rb +26 -0
- data/spec/simple_pvr/hdhomerun_spec.rb +82 -0
- data/spec/simple_pvr/model/channel_spec.rb +114 -0
- data/spec/simple_pvr/model/programme_spec.rb +110 -0
- data/spec/simple_pvr/model/schedule_spec.rb +47 -0
- data/spec/simple_pvr/pvr_initializer_spec.rb +50 -0
- data/spec/simple_pvr/recorder_spec.rb +32 -0
- data/spec/simple_pvr/recording_manager_spec.rb +158 -0
- data/spec/simple_pvr/recording_planner_spec.rb +104 -0
- data/spec/simple_pvr/scheduler_spec.rb +201 -0
- data/spec/simple_pvr/xmltv_reader_spec.rb +49 -0
- data/test/config/jsTestDriver-scenario.conf +10 -0
- data/test/config/jsTestDriver.conf +12 -0
- data/test/config/jstd-scenario-adapter-config.js +6 -0
- data/test/filtersSpec.js +97 -0
- data/test/lib/angular/angular-mocks.js +1719 -0
- data/test/lib/angular/angular-scenario.js +25937 -0
- data/test/lib/angular/jstd-scenario-adapter.js +185 -0
- data/test/lib/angular/version.txt +1 -0
- data/test/lib/jasmine/MIT.LICENSE +20 -0
- data/test/lib/jasmine/index.js +180 -0
- data/test/lib/jasmine/jasmine-html.js +190 -0
- data/test/lib/jasmine/jasmine.css +166 -0
- data/test/lib/jasmine/jasmine.js +2476 -0
- data/test/lib/jasmine/jasmine_favicon.png +0 -0
- data/test/lib/jasmine/version.txt +1 -0
- data/test/lib/jasmine-jstd-adapter/JasmineAdapter.js +196 -0
- data/test/lib/jasmine-jstd-adapter/version.txt +1 -0
- data/test/lib/jstestdriver/JsTestDriver.jar +0 -0
- data/test/lib/jstestdriver/version.txt +1 -0
- data/test/scripts/test-server.sh +14 -0
- data/test/scripts/test.sh +8 -0
- metadata +342 -0
data/.rspec
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
--colour --format documentation
|
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,133 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
simple_pvr (0.0.1)
|
5
|
+
activesupport (~> 3.2)
|
6
|
+
data_mapper (~> 1.2)
|
7
|
+
dm-sqlite-adapter (~> 1.2)
|
8
|
+
nokogiri (~> 1.5)
|
9
|
+
sinatra (~> 1.3)
|
10
|
+
|
11
|
+
GEM
|
12
|
+
remote: http://rubygems.org/
|
13
|
+
specs:
|
14
|
+
activesupport (3.2.8)
|
15
|
+
i18n (~> 0.6)
|
16
|
+
multi_json (~> 1.0)
|
17
|
+
addressable (2.2.8)
|
18
|
+
bcrypt-ruby (3.0.1)
|
19
|
+
builder (3.1.0)
|
20
|
+
capybara (1.1.2)
|
21
|
+
mime-types (>= 1.16)
|
22
|
+
nokogiri (>= 1.3.3)
|
23
|
+
rack (>= 1.0.0)
|
24
|
+
rack-test (>= 0.5.4)
|
25
|
+
selenium-webdriver (~> 2.0)
|
26
|
+
xpath (~> 0.1.4)
|
27
|
+
childprocess (0.3.5)
|
28
|
+
ffi (~> 1.0, >= 1.0.6)
|
29
|
+
cucumber (1.2.1)
|
30
|
+
builder (>= 2.1.2)
|
31
|
+
diff-lcs (>= 1.1.3)
|
32
|
+
gherkin (~> 2.11.0)
|
33
|
+
json (>= 1.4.6)
|
34
|
+
data_mapper (1.2.0)
|
35
|
+
dm-aggregates (~> 1.2.0)
|
36
|
+
dm-constraints (~> 1.2.0)
|
37
|
+
dm-core (~> 1.2.0)
|
38
|
+
dm-migrations (~> 1.2.0)
|
39
|
+
dm-serializer (~> 1.2.0)
|
40
|
+
dm-timestamps (~> 1.2.0)
|
41
|
+
dm-transactions (~> 1.2.0)
|
42
|
+
dm-types (~> 1.2.0)
|
43
|
+
dm-validations (~> 1.2.0)
|
44
|
+
data_objects (0.10.8)
|
45
|
+
addressable (~> 2.1)
|
46
|
+
diff-lcs (1.1.3)
|
47
|
+
dm-aggregates (1.2.0)
|
48
|
+
dm-core (~> 1.2.0)
|
49
|
+
dm-constraints (1.2.0)
|
50
|
+
dm-core (~> 1.2.0)
|
51
|
+
dm-core (1.2.0)
|
52
|
+
addressable (~> 2.2.6)
|
53
|
+
dm-do-adapter (1.2.0)
|
54
|
+
data_objects (~> 0.10.6)
|
55
|
+
dm-core (~> 1.2.0)
|
56
|
+
dm-migrations (1.2.0)
|
57
|
+
dm-core (~> 1.2.0)
|
58
|
+
dm-serializer (1.2.2)
|
59
|
+
dm-core (~> 1.2.0)
|
60
|
+
fastercsv (~> 1.5)
|
61
|
+
json (~> 1.6)
|
62
|
+
json_pure (~> 1.6)
|
63
|
+
multi_json (~> 1.0)
|
64
|
+
dm-sqlite-adapter (1.2.0)
|
65
|
+
dm-do-adapter (~> 1.2.0)
|
66
|
+
do_sqlite3 (~> 0.10.6)
|
67
|
+
dm-timestamps (1.2.0)
|
68
|
+
dm-core (~> 1.2.0)
|
69
|
+
dm-transactions (1.2.0)
|
70
|
+
dm-core (~> 1.2.0)
|
71
|
+
dm-types (1.2.2)
|
72
|
+
bcrypt-ruby (~> 3.0)
|
73
|
+
dm-core (~> 1.2.0)
|
74
|
+
fastercsv (~> 1.5)
|
75
|
+
json (~> 1.6)
|
76
|
+
multi_json (~> 1.0)
|
77
|
+
stringex (~> 1.4)
|
78
|
+
uuidtools (~> 2.1)
|
79
|
+
dm-validations (1.2.0)
|
80
|
+
dm-core (~> 1.2.0)
|
81
|
+
do_sqlite3 (0.10.8)
|
82
|
+
data_objects (= 0.10.8)
|
83
|
+
fastercsv (1.5.5)
|
84
|
+
ffi (1.1.5)
|
85
|
+
gherkin (2.11.2)
|
86
|
+
json (>= 1.4.6)
|
87
|
+
i18n (0.6.1)
|
88
|
+
json (1.7.5)
|
89
|
+
json_pure (1.7.5)
|
90
|
+
libwebsocket (0.1.5)
|
91
|
+
addressable
|
92
|
+
mime-types (1.19)
|
93
|
+
multi_json (1.3.6)
|
94
|
+
nokogiri (1.5.5)
|
95
|
+
rack (1.4.1)
|
96
|
+
rack-protection (1.2.0)
|
97
|
+
rack
|
98
|
+
rack-test (0.6.1)
|
99
|
+
rack (>= 1.0)
|
100
|
+
rake (10.0.3)
|
101
|
+
rspec (2.11.0)
|
102
|
+
rspec-core (~> 2.11.0)
|
103
|
+
rspec-expectations (~> 2.11.0)
|
104
|
+
rspec-mocks (~> 2.11.0)
|
105
|
+
rspec-core (2.11.1)
|
106
|
+
rspec-expectations (2.11.3)
|
107
|
+
diff-lcs (~> 1.1.3)
|
108
|
+
rspec-mocks (2.11.2)
|
109
|
+
rubyzip (0.9.9)
|
110
|
+
selenium-webdriver (2.25.0)
|
111
|
+
childprocess (>= 0.2.5)
|
112
|
+
libwebsocket (~> 0.1.3)
|
113
|
+
multi_json (~> 1.0)
|
114
|
+
rubyzip
|
115
|
+
sinatra (1.3.3)
|
116
|
+
rack (~> 1.3, >= 1.3.6)
|
117
|
+
rack-protection (~> 1.2)
|
118
|
+
tilt (~> 1.3, >= 1.3.3)
|
119
|
+
stringex (1.4.0)
|
120
|
+
tilt (1.3.3)
|
121
|
+
uuidtools (2.1.3)
|
122
|
+
xpath (0.1.4)
|
123
|
+
nokogiri (~> 1.3)
|
124
|
+
|
125
|
+
PLATFORMS
|
126
|
+
ruby
|
127
|
+
|
128
|
+
DEPENDENCIES
|
129
|
+
capybara (~> 1.1)
|
130
|
+
cucumber (~> 1.2)
|
131
|
+
rake (>= 10.0.0)
|
132
|
+
rspec (~> 2.11)
|
133
|
+
simple_pvr!
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
2
|
+
Version 2, December 2004
|
3
|
+
|
4
|
+
Copyright (C) 2004 Sam Hocevar <sam@hocevar.net>
|
5
|
+
|
6
|
+
Everyone is permitted to copy and distribute verbatim or modified
|
7
|
+
copies of this license document, and changing it is allowed as long
|
8
|
+
as the name is changed.
|
9
|
+
|
10
|
+
DO WHAT THE FUCK YOU WANT TO PUBLIC LICENSE
|
11
|
+
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
12
|
+
|
13
|
+
0. You just DO WHAT THE FUCK YOU WANT TO.
|
data/README.md
ADDED
@@ -0,0 +1,169 @@
|
|
1
|
+
What?
|
2
|
+
=====
|
3
|
+
A really, really simple PVR (Personal Video Recorder) system which only supports the
|
4
|
+
[HDHomeRun network tuners](http://www.silicondust.com/). It's written in Ruby and is highly hackable. If
|
5
|
+
you don't want to hack it, but just want a solid PVR system, no worries: It's dead-simple to use.
|
6
|
+
|
7
|
+
SimplePVR does not contain its own player, but currently provides an XBMC plug-in and some half-hearted
|
8
|
+
browser playback. Apart from that, all recordings are stored in a simple directory structure (see below
|
9
|
+
for an explanation), so that you can just point your favorite player to the recordings.
|
10
|
+
|
11
|
+
Why?
|
12
|
+
====
|
13
|
+
MythTV stopped working for me and my HDHomeRun box in the 0.25 release. And even though MythTV has loads
|
14
|
+
of merits, I just have no idea what to do when it stops working - I am not in control of my media center.
|
15
|
+
|
16
|
+
During the last couple of years, I have spent a substantial amount of time on bugs that suddenly appeared
|
17
|
+
in MythTV and suddenly went away. I really don't like using systems this brittle.
|
18
|
+
|
19
|
+
So I wanted to create a really simple PVR in Ruby, making it possible for others to hack away and have
|
20
|
+
fun while recording TV shows for the rest of the family.
|
21
|
+
|
22
|
+
It's based on the HDHomeRun command-line utility, which means it's:
|
23
|
+
|
24
|
+
* built on something that's officially supported by SiliconDust (the makers of HDHomeRun).
|
25
|
+
* really simple.
|
26
|
+
* limited to supporting HDHomeRun tuners.
|
27
|
+
|
28
|
+
Installation
|
29
|
+
============
|
30
|
+
First of all, you need a computer and an HDHomeRun tuner box. On your computer, you need to have the
|
31
|
+
"hdhomerun_config" tool on the path.
|
32
|
+
|
33
|
+
You need Ruby 1.9.2 or newer (1.9.0 or newer is probably enough).
|
34
|
+
|
35
|
+
gem install simple_pvr
|
36
|
+
|
37
|
+
It might not always be completely straightforward... simple_pvr uses DataMapper, which in turn relies on
|
38
|
+
bcrypt-ruby, which compiles some native stuff. So on MacOS, you need to install XCode and its command-line
|
39
|
+
utilities, or get "make" in some other way. On Linux, it should just work. Don't know about Windows.
|
40
|
+
|
41
|
+
If you'd like thumbnails for the recorded shows and ability to transcode recordings to WebM (so you can view
|
42
|
+
them directly in your browser), you need FFMPEG on the command-line. Install it using MacPorts, Homebrew,
|
43
|
+
"apt-get", or whatever.
|
44
|
+
|
45
|
+
Starting the server
|
46
|
+
===================
|
47
|
+
Run
|
48
|
+
|
49
|
+
pvr_server
|
50
|
+
|
51
|
+
...and go to [http://localhost:4567](http://localhost:4567). If you want to expose this URL to the outside
|
52
|
+
world, you'd better supply a username and password:
|
53
|
+
|
54
|
+
username=me password=secret pvr_server
|
55
|
+
|
56
|
+
This will secure the application with Basic HTTP Authentication.
|
57
|
+
|
58
|
+
XMLTV
|
59
|
+
=====
|
60
|
+
First you must specify in a YAML file how the channel IDs in your XMLTV file relates to the channel names
|
61
|
+
that the HDHomeRun has found for you. Create a file called e.g. "channel_mappings.yaml", with lines like this:
|
62
|
+
|
63
|
+
www.ontv.dk/tv/1: DR 1
|
64
|
+
www.ontv.dk/tv/2: DR 2
|
65
|
+
|
66
|
+
Then read your XMLTV file and the mappings file:
|
67
|
+
|
68
|
+
pvr_xmltv programmes.xmltv channel_mappings.yaml
|
69
|
+
|
70
|
+
...and wait a little. You can tell the webserver to update its schedules without restarting the server. This is
|
71
|
+
done by POST'ing to /api/schedules/reload on the server, e.g.:
|
72
|
+
|
73
|
+
curl -d "" localhost:4567/api/schedules/reload
|
74
|
+
|
75
|
+
Or, if you've secured your web server with Basic HTTP Authentication like I told you to, specify username and password:
|
76
|
+
|
77
|
+
curl -d "" -u me:secret localhost:4567/api/schedules/reload
|
78
|
+
|
79
|
+
Recordings
|
80
|
+
==========
|
81
|
+
The recordings are laid out like this, from the directory where you ran pvr_server:
|
82
|
+
|
83
|
+
* recordings/
|
84
|
+
* Borgias/
|
85
|
+
* 1/
|
86
|
+
* 2/
|
87
|
+
* Sports news/
|
88
|
+
* 1/
|
89
|
+
* 2/
|
90
|
+
* 3/
|
91
|
+
* ...
|
92
|
+
|
93
|
+
Inside the numbered directories are these files:
|
94
|
+
|
95
|
+
* stream.ts: The actual stream. Let VLC or another media player show these for you.
|
96
|
+
* hdhomerun_save.log: The output from the actual recording command.
|
97
|
+
* metadata.yml: Recording time, title, channel, etc.
|
98
|
+
|
99
|
+
...and a few other files (thumbnails, transcoded version of the recording, etc.).
|
100
|
+
|
101
|
+
XBMC Plug-In
|
102
|
+
============
|
103
|
+
There's a very simple XBMC plug-in for SimplePVR. See [the home page](https://github.com/olefriis/simplepvr)
|
104
|
+
for more information.
|
105
|
+
|
106
|
+
Future?
|
107
|
+
=======
|
108
|
+
This projects needs to be a nice, readable, hackable, tested system. No pull requests are
|
109
|
+
accepted that violate this.
|
110
|
+
|
111
|
+
For version 1 of SimplePVR, I'd like to finish the following:
|
112
|
+
|
113
|
+
* "Gemify" the stuff, so installation becomes a breeze.
|
114
|
+
* More schedule editing, e.g.:
|
115
|
+
* "Start early" and "end late" (currently 2 and 5 minutes).
|
116
|
+
* Which time of day the schedule should be active (e.g. only the afternoon, ignoring all the
|
117
|
+
re-runs earlier in the day).
|
118
|
+
* Removal of outdated schedules (the "Record single programme" and "Don't record this specific
|
119
|
+
programme").
|
120
|
+
|
121
|
+
There is lots of stuff I'd like to do after that, but I have no deadline - which means that pull
|
122
|
+
requests are the only means you have for speeding things up. This includes:
|
123
|
+
|
124
|
+
* Web interface:
|
125
|
+
* "Dashboard" giving "the big picture" of the status of the system (next 5 upcoming recordings,
|
126
|
+
last 5 recorded programmes, current status, last couple of errors, whether there are any
|
127
|
+
upcoming conflicts, etc.).
|
128
|
+
* Better overview of recordings (a flat view).
|
129
|
+
* Better overview pages, e.g. "all children programmes", "all movies this week", "tonight's
|
130
|
+
programmes", ...
|
131
|
+
* Setting up schedules defined by a channel, a start time, and a duration (and a name,
|
132
|
+
probably), so that the web GUI is usable even without XMLTV.
|
133
|
+
* Schedule editing: Show which programmes match the edited schedule, to make it easier to create
|
134
|
+
a schedule which exactly matches your needs.
|
135
|
+
* XMLTV import:
|
136
|
+
* Let SimplePVR itself fetch XMLTV URLs at specified times of day.
|
137
|
+
* Set-up of matching XMLTV IDs to channels could make good use of a GUI.
|
138
|
+
* Parse and make use of programme icons etc.
|
139
|
+
* Searching for tuners and scanning for channels would be nice through a GUI.
|
140
|
+
* Saving with the hdhomerun_config command is done through a shell script, so we can shut it down properly. I'd
|
141
|
+
like a simpler solution, but haven't found anything that works both on OS X and Linux.
|
142
|
+
[Bluepill](https://github.com/arya/bluepill) seems to do the job, but seems like too big a hammer...
|
143
|
+
|
144
|
+
Some features would be cool to have, but I don't have a personal need for them, so they will only
|
145
|
+
happen if *you* implement them and send me a pull request. Besides, some of them I have no clue how
|
146
|
+
to implement...
|
147
|
+
|
148
|
+
* Some kind of live TV.
|
149
|
+
* Create metadata for XBMC and Serviio.
|
150
|
+
* Duplication detection.
|
151
|
+
* Commercial detection.
|
152
|
+
* Record multiple programmes on same multiplex, so we are not restricted to only recording two
|
153
|
+
programmes at once.
|
154
|
+
|
155
|
+
Development
|
156
|
+
===========
|
157
|
+
Run the specs and features like this:
|
158
|
+
|
159
|
+
rake test
|
160
|
+
|
161
|
+
Run the JavaScript tests by first calling
|
162
|
+
|
163
|
+
test/scripts/test-server.sh
|
164
|
+
|
165
|
+
then opening a browser on the shown URL. Capture the browser in strict mode. After this, you can run
|
166
|
+
|
167
|
+
test/scripts/test.sh
|
168
|
+
|
169
|
+
any number of times. However, the test-server needs to be restarted from time to time.
|
data/Rakefile
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'bundler/setup'
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'cucumber/rake/task'
|
5
|
+
|
6
|
+
directory 'output'
|
7
|
+
|
8
|
+
namespace :test do
|
9
|
+
RSpec::Core::RakeTask.new(:spec)
|
10
|
+
Cucumber::Rake::Task.new(:features)
|
11
|
+
end
|
12
|
+
|
13
|
+
desc 'Run specs and features'
|
14
|
+
task :test => ['test:spec', 'test:features']
|
15
|
+
|
16
|
+
task :default => ['test']
|
data/bin/pvr_server
ADDED
data/bin/pvr_xmltv
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'simple_pvr'
|
4
|
+
require 'yaml'
|
5
|
+
require 'open-uri'
|
6
|
+
|
7
|
+
if ARGV.length != 2
|
8
|
+
puts "Requires two arguments: The XMLTV file name, and the channel mapping file name"
|
9
|
+
exit 1
|
10
|
+
end
|
11
|
+
|
12
|
+
SimplePvr::PvrInitializer.setup
|
13
|
+
|
14
|
+
xmltv_file = File.new(ARGV[0])
|
15
|
+
mapping_to_channels = YAML.load_file(ARGV[1])
|
16
|
+
|
17
|
+
reader = SimplePvr::XmltvReader.new(mapping_to_channels)
|
18
|
+
reader.read(xmltv_file)
|
@@ -0,0 +1,48 @@
|
|
1
|
+
Feature: Channel overview
|
2
|
+
In order to find what's on TV
|
3
|
+
As a user
|
4
|
+
I want a good channel overview
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given the following channels:
|
8
|
+
| name |
|
9
|
+
| Channel 1 |
|
10
|
+
| Channel 2 |
|
11
|
+
| Animals |
|
12
|
+
And the following programmes:
|
13
|
+
| title | subtitle | channel | day |
|
14
|
+
| Blast from past | Prehistoric documentary | Channel 1 | -1 |
|
15
|
+
| Bonderøven | Danish documentary | Channel 1 | 0 |
|
16
|
+
| Blood and Bone | American action movie from 2009 | Channel 1 | 0 |
|
17
|
+
| Noddy | Children's programme | Channel 2 | 0 |
|
18
|
+
|
19
|
+
Scenario: Channel overview shows channel names
|
20
|
+
Given I am on the channel overview page
|
21
|
+
Then I should see "Channel 1"
|
22
|
+
And I should see "Channel 2"
|
23
|
+
And I should see "Animals"
|
24
|
+
|
25
|
+
Scenario: Channel overview shows upcoming programmes
|
26
|
+
Given I am on the channel overview page
|
27
|
+
Then I should see "Bonderøven"
|
28
|
+
But I should not see "Blast from past"
|
29
|
+
|
30
|
+
Scenario: Clicking on an upcoming programme reveals more programme information
|
31
|
+
Given I am on the channel overview page
|
32
|
+
And I follow "Bonderøven"
|
33
|
+
Then I should see "Danish documentary"
|
34
|
+
|
35
|
+
Scenario: Channel filtering
|
36
|
+
Given I am on the channel overview page
|
37
|
+
And I fill in "channel_filter" with "channel"
|
38
|
+
Then I should see "Channel 1"
|
39
|
+
And I should see "Channel 2"
|
40
|
+
But I should not see "Animals"
|
41
|
+
|
42
|
+
Scenario: I can go to a week overview for a given channel
|
43
|
+
Given I am on the channel overview page
|
44
|
+
And I fill in "channel_filter" with "Channel 1"
|
45
|
+
And I follow "..."
|
46
|
+
Then I should see "Bonderøven"
|
47
|
+
And I should see "Blood and Bone"
|
48
|
+
But I should not see "Noddy"
|
@@ -0,0 +1,20 @@
|
|
1
|
+
Feature: Programme search
|
2
|
+
In order to find interesting programmes
|
3
|
+
As a user
|
4
|
+
I want to search through all programmes
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given the following programmes:
|
8
|
+
| title | subtitle |
|
9
|
+
| Bonderøven | Danish documentary |
|
10
|
+
| Blood and Bone | American action movie from 2009 |
|
11
|
+
| Noddy | Children's programme |
|
12
|
+
And I am on the schedules page
|
13
|
+
|
14
|
+
Scenario: Autocomplete in programme search
|
15
|
+
Given I enter "Bon" in the programme search field
|
16
|
+
Then I should see the programme title suggestion "Bonderøven"
|
17
|
+
|
18
|
+
Scenario: Searching for a text reveals summary of programmes with matching titles
|
19
|
+
Given I search for programmes with title "Bon"
|
20
|
+
Then I should see "Danish documentary" in the page contents
|
@@ -0,0 +1,112 @@
|
|
1
|
+
Feature: Scheduling
|
2
|
+
In order to record programmes
|
3
|
+
As a user
|
4
|
+
I want to set up recording schedules
|
5
|
+
|
6
|
+
Background:
|
7
|
+
Given the following programmes:
|
8
|
+
| title | subtitle | channel | day |
|
9
|
+
| News right now | Just the news... | Channel 1 | 0 |
|
10
|
+
| Bonderøven | Danish documentary | Channel 1 | 1 |
|
11
|
+
| Bonderøven | Danish documentary | Channel 2 | 1 |
|
12
|
+
| Bonderøven | Danish documentary | Channel 1 | 3 |
|
13
|
+
| Noddy | Children's programme | Channel 1 | 1 |
|
14
|
+
|
15
|
+
Scenario: Nothing is scheduled by default
|
16
|
+
Given I am on the schedules page
|
17
|
+
Then there should be 0 upcoming recordings
|
18
|
+
|
19
|
+
Scenario: Schedule by title for a single channel
|
20
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
21
|
+
And I choose to record the programme on this channel
|
22
|
+
And I am on the schedules page
|
23
|
+
Then I should see the schedule "Bonderøven on Channel 1"
|
24
|
+
And there should be 2 upcoming recordings
|
25
|
+
|
26
|
+
Scenario: Schedule by title for all channels
|
27
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
28
|
+
And I choose to record the programme on any channel
|
29
|
+
And I am on the schedules page
|
30
|
+
Then I should see the schedule "Bonderøven"
|
31
|
+
But I should not see the schedule "Bonderøven on Channel 1"
|
32
|
+
And there should be 3 upcoming recordings
|
33
|
+
|
34
|
+
Scenario: Schedule a specific programme
|
35
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
36
|
+
And I choose to record just this programme
|
37
|
+
And I am on the schedules page
|
38
|
+
Then I should see the timed schedule "Bonderøven on Channel 1"
|
39
|
+
And there should be 1 upcoming recordings
|
40
|
+
|
41
|
+
Scenario: Set up schedule manually
|
42
|
+
Given I am on the schedules page
|
43
|
+
And I fill in "Name" with "Bonderøven"
|
44
|
+
And I select "Channel 1" from "Channel"
|
45
|
+
And I press "Create schedule"
|
46
|
+
Then I should see the schedule "Bonderøven on Channel 1"
|
47
|
+
And there should be 2 upcoming recordings
|
48
|
+
|
49
|
+
Scenario: Scheduling the current programme starts recording immediately
|
50
|
+
Given I am on the status page
|
51
|
+
Then I should see "Idle"
|
52
|
+
When I have navigated to the programme page for "News right now" on channel "Channel 1"
|
53
|
+
And I choose to record the programme on this channel
|
54
|
+
And I wait 2 seconds
|
55
|
+
When I am on the status page
|
56
|
+
Then I should see "Recording 'News right now' on channel 'Channel 1'"
|
57
|
+
|
58
|
+
Scenario: Remove schedule
|
59
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
60
|
+
And I choose to record the programme on this channel
|
61
|
+
And I am on the schedules page
|
62
|
+
And I follow "Delete"
|
63
|
+
Then I should not see the schedule "Bonderøven on Channel 1"
|
64
|
+
And there should be 0 upcoming recordings
|
65
|
+
|
66
|
+
Scenario: Defining conflicting schedules
|
67
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
68
|
+
And I choose to record the programme on any channel
|
69
|
+
And I have navigated to the programme page for "Noddy" on channel "Channel 1"
|
70
|
+
And I choose to record the programme on this channel
|
71
|
+
When I am on the schedules page
|
72
|
+
Then there should be a conflict
|
73
|
+
|
74
|
+
Scenario: Fixing conflicts by removing a schedule
|
75
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
76
|
+
And I choose to record the programme on any channel
|
77
|
+
And I have navigated to the programme page for "Noddy" on channel "Channel 1"
|
78
|
+
And I choose to record the programme on this channel
|
79
|
+
When I am on the schedules page
|
80
|
+
And I follow "Delete"
|
81
|
+
Then there should be no conflicts
|
82
|
+
|
83
|
+
Scenario: Removing a specific recording from a schedule
|
84
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
85
|
+
And I choose to record the programme on any channel
|
86
|
+
And I am on the schedules page
|
87
|
+
Then there should be 3 upcoming recordings
|
88
|
+
When I follow "Do not record this specific show"
|
89
|
+
Then there should be 2 upcoming recordings
|
90
|
+
And I should see "Exception: Bonderøven"
|
91
|
+
|
92
|
+
Scenario: Editing a schedule
|
93
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
94
|
+
And I choose to record the programme on any channel
|
95
|
+
And I am on the schedules page
|
96
|
+
And I follow "Edit"
|
97
|
+
And I fill in "Name of show" with "Noddy"
|
98
|
+
And I press "Update"
|
99
|
+
Then there should be 1 upcoming recording
|
100
|
+
And I should not see "Bonderøven"
|
101
|
+
|
102
|
+
Scenario: Editing a schedule so that only some weekdays are allowed
|
103
|
+
Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
|
104
|
+
And I choose to record the programme on any channel
|
105
|
+
And I am on the schedules page
|
106
|
+
And I follow "Edit"
|
107
|
+
And I check "Filter by weekday"
|
108
|
+
And I check "Record Mondays"
|
109
|
+
And I check "Record Wednesdays"
|
110
|
+
And I check "Record Sundays"
|
111
|
+
And I press "Update"
|
112
|
+
Then I should see "(Mondays, Wednesdays, and Sundays)"
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
|
3
|
+
Given /the following programmes\:/ do |programme_table|
|
4
|
+
programme_table.hashes.each do |programme|
|
5
|
+
channel = find_or_create_channel_with_name(programme['channel'] || 'Channel 1')
|
6
|
+
air_time = Time.now.advance(days: (programme['day'] || '0').to_i)
|
7
|
+
SimplePvr::Model::Programme.add(channel, programme['title'] || '', programme['subtitle'] || '',
|
8
|
+
programme['description'] || '', air_time, 60.minutes, ' .23/40. ')
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
Given /the following channels\:/ do |channel_table|
|
13
|
+
channel_table.hashes.each do |channel|
|
14
|
+
find_or_create_channel_with_name(channel['name'])
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
Given /I have navigated to the week overview for channel "(.*)"/ do |channel|
|
19
|
+
visit path_to('the channel overview page')
|
20
|
+
fill_in('channel_filter', :with => channel)
|
21
|
+
click_link('...')
|
22
|
+
end
|
23
|
+
|
24
|
+
Given /I have navigated to the programme page for "(.*)" on channel "(.*)"/ do |title, channel|
|
25
|
+
visit path_to('the channel overview page')
|
26
|
+
fill_in('channel_filter', :with => channel)
|
27
|
+
click_link('...')
|
28
|
+
click_link(title)
|
29
|
+
end
|
30
|
+
|
31
|
+
Given /I choose to record just this programme/ do
|
32
|
+
choose_to_record('Record just this programme')
|
33
|
+
end
|
34
|
+
|
35
|
+
Given /I choose to record the programme on this channel/ do
|
36
|
+
choose_to_record('Record on this channel')
|
37
|
+
end
|
38
|
+
|
39
|
+
Given /I choose to record the programme on any channel/ do
|
40
|
+
choose_to_record('Record on any channel')
|
41
|
+
end
|
42
|
+
|
43
|
+
When /I enter "(.*)" in the programme search field/ do |query|
|
44
|
+
fill_in('programme-search-query', :with => query)
|
45
|
+
end
|
46
|
+
|
47
|
+
When /I search for programmes with title "(.*)"/ do |query|
|
48
|
+
fill_in('programme-search-query', :with => query)
|
49
|
+
click_button('Search')
|
50
|
+
end
|
51
|
+
|
52
|
+
Then /I should see the programme title suggestion "(.*)"/ do |suggestion|
|
53
|
+
page.wait_until { page.text.include? suggestion }
|
54
|
+
end
|
55
|
+
|
56
|
+
Then /I should see "(.*)" in the page contents/ do |text|
|
57
|
+
within('#contents') do
|
58
|
+
page.wait_until { page.text.include? text }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
Then /I should see the schedule "(.*)"/ do |text|
|
63
|
+
within('#schedules') do
|
64
|
+
page.wait_until { page.text.include? text }
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
Then /I should see the timed schedule "(.*)"/ do |text|
|
69
|
+
within('#schedules') do
|
70
|
+
page.wait_until { page.text =~ /#{text} .* \d+, \d{4} at \d?\d:\d\d/ }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
Then /I should not see the schedule "(.*)"/ do |text|
|
75
|
+
within('#schedules') do
|
76
|
+
page.wait_until { !(page.text.include? text) }
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
Then /there should be (\d*) upcoming recordings?/ do |upcoming_recordings|
|
81
|
+
page.wait_until { find('#upcoming_recordings').all('h2').length == upcoming_recordings.to_i }
|
82
|
+
end
|
83
|
+
|
84
|
+
Then /^there should be a conflict$/ do
|
85
|
+
page.wait_until { page.text.include? '(Conflicting)' }
|
86
|
+
end
|
87
|
+
|
88
|
+
Then /^there should be no conflicts$/ do
|
89
|
+
page.wait_until { !(page.text.include? '(Conflicting)') }
|
90
|
+
end
|
91
|
+
|
92
|
+
Then /I wait (\d*) seconds/ do |seconds|
|
93
|
+
sleep seconds.to_i
|
94
|
+
end
|
95
|
+
|
96
|
+
def find_or_create_channel_with_name(name)
|
97
|
+
channel = SimplePvr::Model::Channel.first(name: name)
|
98
|
+
channel ? channel : SimplePvr::Model::Channel.add(name, 0, 0)
|
99
|
+
end
|
100
|
+
|
101
|
+
def choose_to_record(button_text)
|
102
|
+
click_button(button_text)
|
103
|
+
page.wait_until { page.text.include? 'This programme is being recorded' }
|
104
|
+
end
|