simple_pvr 1.0.0 → 1.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +1 -4
- data/Gemfile.lock +72 -61
- data/README.md +51 -33
- data/bin/pvr_server +14 -6
- data/changelog.txt +11 -0
- data/development_server +14 -6
- data/features/channel_overview.feature +1 -1
- data/features/recordings.feature +30 -0
- data/features/step_definitions/pvr_steps.rb +34 -0
- data/features/support/env.rb +1 -2
- data/features/support/paths.rb +2 -0
- data/lib/simple_pvr.rb +2 -0
- data/lib/simple_pvr/model/database_initializer.rb +8 -0
- data/lib/simple_pvr/model/programme.rb +12 -2
- data/lib/simple_pvr/model/programme_actor.rb +14 -0
- data/lib/simple_pvr/model/programme_category.rb +14 -0
- data/lib/simple_pvr/model/programme_director.rb +13 -0
- data/lib/simple_pvr/model/programme_presenter.rb +13 -0
- data/lib/simple_pvr/model/recording.rb +12 -0
- data/lib/simple_pvr/programme_icon_fetcher.rb +15 -0
- data/lib/simple_pvr/pvr_initializer.rb +4 -4
- data/lib/simple_pvr/recorder.rb +4 -3
- data/lib/simple_pvr/recording_manager.rb +44 -24
- data/lib/simple_pvr/recording_planner.rb +3 -3
- data/lib/simple_pvr/scheduler.rb +12 -4
- data/lib/simple_pvr/server/base_controller.rb +12 -13
- data/lib/simple_pvr/server/channels_controller.rb +5 -5
- data/lib/simple_pvr/server/programmes_controller.rb +2 -2
- data/lib/simple_pvr/server/schedules_controller.rb +4 -4
- data/lib/simple_pvr/server/secured_controller.rb +71 -0
- data/lib/simple_pvr/server/shows_controller.rb +13 -8
- data/lib/simple_pvr/server/status_controller.rb +1 -1
- data/lib/simple_pvr/server/upcoming_recordings_controller.rb +1 -1
- data/lib/simple_pvr/version.rb +1 -1
- data/lib/simple_pvr/xmltv_reader.rb +37 -4
- data/public/css/typeahead.js-bootstrap.css +83 -0
- data/public/index.html +45 -37
- data/public/js/angular/http-auth-interceptor.js +122 -0
- data/public/js/app.js +4 -37
- data/public/js/controllers.js +22 -14
- data/public/js/directives.js +102 -0
- data/public/js/filters.js +0 -31
- data/public/js/services.js +64 -0
- data/public/js/typeahead/typeahead.min.js +7 -0
- data/public/partials/about.html +15 -13
- data/public/partials/channels.html +41 -36
- data/public/partials/programme.html +20 -4
- data/public/partials/programmeListing.html +14 -13
- data/public/partials/schedule.html +91 -86
- data/public/partials/schedules.html +28 -16
- data/public/partials/search.html +10 -11
- data/public/partials/show.html +26 -9
- data/public/partials/shows.html +5 -6
- data/public/partials/status.html +1 -1
- data/public/templates/loginDialog.html +30 -0
- data/public/templates/logoutLink.html +1 -0
- data/public/templates/titleSearch.html +5 -3
- data/simple_pvr.gemspec +6 -4
- data/spec/resources/dummyImage.png +1 -0
- data/spec/resources/programmes-with-categories.xmltv +27 -0
- data/spec/resources/programmes-with-credits.xmltv +24 -0
- data/spec/resources/programmes-with-icons.xmltv +25 -0
- data/spec/resources/programmes-with-presenters.xmltv +19 -0
- data/spec/resources/{programs-without-icon.xmltv → programmes-without-icon.xmltv} +0 -0
- data/spec/resources/{programs.xmltv → programmes.xmltv} +0 -4
- data/spec/simple_pvr/ffmpeg_spec.rb +24 -22
- data/spec/simple_pvr/hdhomerun_spec.rb +69 -67
- data/spec/simple_pvr/model/channel_spec.rb +101 -101
- data/spec/simple_pvr/model/programme_spec.rb +104 -104
- data/spec/simple_pvr/model/schedule_spec.rb +74 -74
- data/spec/simple_pvr/programme_icon_fetcher_spec.rb +25 -0
- data/spec/simple_pvr/pvr_initializer_spec.rb +40 -38
- data/spec/simple_pvr/recorder_spec.rb +37 -26
- data/spec/simple_pvr/recording_manager_spec.rb +160 -133
- data/spec/simple_pvr/recording_planner_spec.rb +213 -211
- data/spec/simple_pvr/scheduler_spec.rb +189 -172
- data/spec/simple_pvr/server/secured_controller_spec.rb +118 -0
- data/spec/simple_pvr/xmltv_reader_spec.rb +89 -41
- data/test/karma.conf.js +7 -4
- data/test/unit/filtersSpec.js +0 -36
- metadata +79 -63
- data/public/css/bootstrap-responsive.min.css +0 -9
- data/public/css/bootstrap.min.css +0 -9
- data/public/img/glyphicons-halflings-white.png +0 -0
- data/public/img/glyphicons-halflings.png +0 -0
- data/public/js/angular/angular-resource.min.js +0 -10
- data/public/js/angular/angular.min.js +0 -162
- data/public/js/bootstrap/bootstrap.min.js +0 -6
- data/public/js/jquery/jquery.min.js +0 -5
- data/test/lib/angular/angular-mocks.js +0 -1768
@@ -1,40 +1,52 @@
|
|
1
1
|
<div class="row">
|
2
|
-
<div class="
|
2
|
+
<div class="col-md-6" id="schedules">
|
3
3
|
<h1>Existing schedules</h1>
|
4
4
|
<div class="row" ng-repeat="schedule in schedules">
|
5
|
-
<div class="
|
5
|
+
<div class="col-md-12">
|
6
6
|
<p><span ng-show="schedule.is_exception">Exception:</span> {{schedule.title}}
|
7
7
|
<span ng-show="schedule.channel">on {{schedule.channel.name}}</span>
|
8
8
|
<span ng-show="schedule.start_time">{{schedule.start_time | date:'fullDate'}} at {{schedule.start_time | date:'HH:mm'}}</span>
|
9
9
|
{{schedule | filteredWeekdays}} {{schedule | timeOfDay}} {{schedule | startEarlyEndLate}}
|
10
10
|
</p>
|
11
11
|
<p>
|
12
|
-
<a ng-href="/schedules/{{schedule.id}}" class="btn" ng-hide="schedule.is_exception">Edit</a>
|
12
|
+
<a ng-href="/schedules/{{schedule.id}}" class="btn btn-default" ng-hide="schedule.is_exception">Edit</a>
|
13
13
|
<button type="button" ng-click="deleteSchedule(schedule)" class="btn btn-warning">Delete</button>
|
14
14
|
</p>
|
15
15
|
</div>
|
16
16
|
</div>
|
17
17
|
<div class="row">
|
18
|
-
<div class="
|
18
|
+
<div class="col-md-12">
|
19
19
|
<h1>Create new schedule</h1>
|
20
|
-
<form>
|
21
|
-
<
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
20
|
+
<form class="form-horizontal" role="form">
|
21
|
+
<div class="form-group">
|
22
|
+
<label class="control-label col-lg-2" for="newScheduleTitle">Name</label>
|
23
|
+
<div class="col-lg-10">
|
24
|
+
<input id="newScheduleTitle" type="text" class="form-control" ng-model="newSchedule.title"></p>
|
25
|
+
</div>
|
26
|
+
</div>
|
27
|
+
<div class="form-group">
|
28
|
+
<label class="control-label col-lg-2" for="newScheduleChannel">Channel</label>
|
29
|
+
<div class="col-lg-10">
|
30
|
+
<select id="newScheduleChannel" class="form-control" ng-model="newSchedule.channelId">
|
31
|
+
<option value="0">-- Any channel --</option>
|
32
|
+
<option ng-repeat="channel in channels" value="{{channel.id}}">{{channel.name}}</option>
|
33
|
+
</select></p>
|
34
|
+
</div>
|
35
|
+
</div>
|
36
|
+
<div class="form-group">
|
37
|
+
<div class="col-lg-offset-2 col-lg-10">
|
38
|
+
<button ng-click="createSchedule()" class="btn btn-default" ng-disabled="!newSchedule.title">Create schedule</button>
|
39
|
+
</div>
|
40
|
+
</div>
|
29
41
|
</form>
|
30
42
|
</div>
|
31
43
|
</div>
|
32
44
|
</div>
|
33
|
-
<div class="
|
45
|
+
<div class="col-md-6" id="upcoming_recordings">
|
34
46
|
<h1>Upcoming recordings</h1>
|
35
47
|
<div class="row" ng-repeat="recording in upcomingRecordings">
|
36
|
-
<div class="
|
37
|
-
<h2>{{recording.show_name}}<span ng-show="recording.is_conflicting" style="color: red"> (Conflicting)</span> <a
|
48
|
+
<div class="col-md-12">
|
49
|
+
<h2>{{recording.show_name}}<span ng-show="recording.is_conflicting" style="color: red"> (Conflicting)</span> <a href ng-click="excludeRecording(recording)" title="Do not record this specific show"><span class="glyphicon glyphicon-remove" style="color:black; font-size:15px; vertical-align: top;"></span></a></h2>
|
38
50
|
<p>{{recording.start_time | date:'fullDate'}} at {{recording.start_time | date:'HH:mm'}} on {{recording.channel.name}}</p>
|
39
51
|
<p ng-show="recording.subtitle">{{recording.subtitle}}</p>
|
40
52
|
<p><a ng-href="/programmes/{{recording.programme_id}}">View details</a></p>
|
data/public/partials/search.html
CHANGED
@@ -1,17 +1,16 @@
|
|
1
1
|
<div class="row">
|
2
|
-
<div class="
|
2
|
+
<div class="col-md-12">
|
3
3
|
<h1>Results for {{query}}</h1>
|
4
4
|
</div>
|
5
5
|
</div>
|
6
|
-
<div class="
|
7
|
-
<
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
<
|
14
|
-
<p>This programme is being recorded.</p>
|
15
|
-
</div>
|
6
|
+
<div class="col-md-12" ng-repeat-start="programme in result">
|
7
|
+
<h2>{{programme.title}}</h2>
|
8
|
+
<p>{{programme.start_time | date:'fullDate'}} at {{programme.start_time | date:'HH:mm'}} on channel {{programme.channel.name}}.</p>
|
9
|
+
<p>{{programme.subtitle}}</p>
|
10
|
+
<p>{{programme.description}}</p>
|
11
|
+
<p><a ng-href="/programmes/{{programme.id}}">Show details</a></p>
|
12
|
+
<div ng-show="programme.is_scheduled">
|
13
|
+
<p>This programme is being recorded.</p>
|
16
14
|
</div>
|
17
15
|
</div>
|
16
|
+
<div class="clearfix" ng-show="$index % 2 == 1" ng-repeat-end></div>
|
data/public/partials/show.html
CHANGED
@@ -1,21 +1,38 @@
|
|
1
1
|
<div class="row">
|
2
|
-
<div class="
|
2
|
+
<div class="col-md-12">
|
3
3
|
<h1>Recordings of {{show.name}}</h1>
|
4
4
|
</div>
|
5
5
|
</div>
|
6
|
-
<div class="row" ng-repeat="
|
7
|
-
<div class="
|
8
|
-
|
9
|
-
<
|
6
|
+
<div class="row" ng-repeat="recording in recordings | orderBy:'start_time'">
|
7
|
+
<div class="col-md-12 media">
|
8
|
+
<img class="media-object pull-left" ng-if="recording.has_icon" ng-src="/api/shows/{{show.name}}/recordings/{{recording.id}}/icon">
|
9
|
+
<h1 class="media-heading">{{recording.episode}}</h1>
|
10
|
+
<img ng-if="recording.has_thumbnail" class="thumbnail" ng-src="/api/shows/{{show.name}}/recordings/{{recording.id}}/thumbnail.png">
|
10
11
|
<p>{{recording.subtitle}}</p>
|
11
12
|
<p>{{recording.start_time | date:'fullDate'}} at {{recording.start_time | date:'HH:mm'}} from channel {{recording.channel_name}}.</p>
|
12
13
|
<p>{{recording.description}}</p>
|
14
|
+
<p ng-repeat="director in recording.directors">
|
15
|
+
<strong>Director:</strong> {{director}}
|
16
|
+
</p>
|
17
|
+
<p ng-repeat="presenter in recording.presenters">
|
18
|
+
<strong>Presenter:</strong> {{presenter}}
|
19
|
+
</p>
|
20
|
+
<p ng-repeat="actor in recording.actors">
|
21
|
+
<strong ng-show="actor.role_name">{{actor.role_name}}:</strong> {{actor.actor_name}}
|
22
|
+
</p>
|
23
|
+
<p ng-show="recording.categories">
|
24
|
+
<strong>Categories:</strong>
|
25
|
+
<span ng-repeat="category in recording.categories">
|
26
|
+
{{category}}<span ng-hide="$last">, </span>
|
27
|
+
</span>
|
28
|
+
</p>
|
29
|
+
|
13
30
|
<p>
|
14
|
-
<a target="_self" ng-href="/api/shows/{{show.name}}/recordings/{{recording.
|
31
|
+
<a target="_self" ng-href="/api/shows/{{show.name}}/recordings/{{recording.id}}/stream.ts">Raw stream</a>
|
15
32
|
-
|
16
|
-
<a ng-show="recording.has_webm" target="_self" ng-href="/api/shows/{{show.name}}/recordings/{{recording.
|
17
|
-
<button ng-hide="recording.has_webm" class="btn" type="button" ng-click="startTranscoding(recording)">Prepare for playing in Firefox/Chrome</button>
|
33
|
+
<a ng-show="recording.has_webm" target="_self" ng-href="/api/shows/{{show.name}}/recordings/{{recording.id}}/stream.webm">View in browser (Firefox/Chrome)</a>
|
34
|
+
<button ng-hide="recording.has_webm" class="btn btn-default" type="button" ng-click="startTranscoding(recording)">Prepare for playing in Firefox/Chrome</button>
|
18
35
|
</p>
|
19
|
-
<p><button class="btn" type="button" ng-click="deleteRecording(recording)">Delete recording</button></p>
|
36
|
+
<p><button class="btn btn-warning" type="button" ng-click="deleteRecording(recording)">Delete recording</button></p>
|
20
37
|
</div>
|
21
38
|
</div>
|
data/public/partials/shows.html
CHANGED
@@ -1,7 +1,6 @@
|
|
1
|
-
<div class="
|
2
|
-
<
|
3
|
-
|
4
|
-
|
5
|
-
<button class="btn" type="button" ng-click="deleteEpisodes(show)">Delete all episodes</button></p>
|
6
|
-
</div>
|
1
|
+
<div class="col-md-6" ng-repeat-start="show in shows | orderBy:'name'">
|
2
|
+
<h2>{{show.name}}</h2>
|
3
|
+
<p><a class="btn btn-default" ng-href="/shows/{{show.id}}">Episodes</a>
|
4
|
+
<button class="btn btn-warning" type="button" ng-click="deleteEpisodes(show)">Delete all episodes</button></p>
|
7
5
|
</div>
|
6
|
+
<div class="clearfix" ng-show="$index % 2 == 1" ng-repeat-end></div>
|
data/public/partials/status.html
CHANGED
@@ -0,0 +1,30 @@
|
|
1
|
+
<div class="modal fade" id="loginModal" tabindex="-1" role="dialog" aria-labelledby="loginModalLabel" aria-hidden="true" data-backdrop="static">
|
2
|
+
<div class="modal-dialog">
|
3
|
+
<div class="modal-content">
|
4
|
+
<form ng-submit="submit()" name="loginCredentialsForm" role="form">
|
5
|
+
<div class="modal-header">
|
6
|
+
<h4 class="modal-title" id="loginModalLabel">Enter credentials</h4>
|
7
|
+
</div>
|
8
|
+
<div class="modal-body">
|
9
|
+
<div class="form-horizontal">
|
10
|
+
<div class="form-group">
|
11
|
+
<label class="col-lg-4 control-label" for="userName">User name</label>
|
12
|
+
<div class="col-lg-4">
|
13
|
+
<input type="text" class="form-control" ng-model="credentials.userName" id="userName" placeholder="User name">
|
14
|
+
</div>
|
15
|
+
</div>
|
16
|
+
<div class="form-group">
|
17
|
+
<label class="col-lg-4 control-label" for="password">Password</label>
|
18
|
+
<div class="col-lg-4">
|
19
|
+
<input type="password" class="form-control" ng-model="credentials.password" id="password" placeholder="Password">
|
20
|
+
</div>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
</div>
|
24
|
+
<div class="modal-footer">
|
25
|
+
<input type="submit" class="btn btn-primary" value="Log in"></input>
|
26
|
+
</div>
|
27
|
+
</form>
|
28
|
+
</div>
|
29
|
+
</div>
|
30
|
+
</div>
|
@@ -0,0 +1 @@
|
|
1
|
+
<p ng-show="isLoggedIn()" class="navbar-text navbar-right"><a href="" ng-click="logOut()">Log out</a></p>
|
@@ -1,4 +1,6 @@
|
|
1
|
-
<form class="navbar-form
|
2
|
-
<
|
3
|
-
|
1
|
+
<form class="navbar-form navbar-right" role="search" ng-submit="search()">
|
2
|
+
<div class="form-group">
|
3
|
+
<input class="form-control" name="programme-search-query" type="text" ng-model="title" placeholder="Search programmes...">
|
4
|
+
</div>
|
5
|
+
<button type="submit" class="btn btn-default">Search</button>
|
4
6
|
</form>
|
data/simple_pvr.gemspec
CHANGED
@@ -22,11 +22,13 @@ Gem::Specification.new do |gem|
|
|
22
22
|
gem.add_dependency 'data_mapper', '~> 1.2'
|
23
23
|
gem.add_dependency 'dm-sqlite-adapter', '~> 1.2'
|
24
24
|
gem.add_dependency 'sinatra', '~> 1.3'
|
25
|
-
gem.add_dependency 'puma', '~>
|
25
|
+
gem.add_dependency 'puma', '~> 2.7'
|
26
26
|
|
27
27
|
gem.add_development_dependency 'rake', '>= 10.0.0'
|
28
28
|
gem.add_development_dependency 'rspec', '~> 2.11'
|
29
|
-
gem.add_development_dependency 'cucumber', '~> 1.
|
30
|
-
gem.add_development_dependency 'capybara', '~> 2.
|
31
|
-
gem.add_development_dependency '
|
29
|
+
gem.add_development_dependency 'cucumber', '~> 1.3'
|
30
|
+
gem.add_development_dependency 'capybara', '~> 2.2'
|
31
|
+
gem.add_development_dependency 'selenium-webdriver'
|
32
|
+
gem.add_development_dependency 'poltergeist', '~> 1.5'
|
33
|
+
gem.add_development_dependency 'pry'
|
32
34
|
end
|
@@ -0,0 +1 @@
|
|
1
|
+
This is not really a PNG file...
|
@@ -0,0 +1,27 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<tv generator-info-name="www.ontv.dk/xmltv">
|
3
|
+
<channel id="www.ontv.dk/tv/1">
|
4
|
+
<display-name lang="dk">DR1 DK</display-name>
|
5
|
+
</channel>
|
6
|
+
<channel id="www.ontv.dk/tv/2">
|
7
|
+
<display-name lang="dk">DR2 DK</display-name>
|
8
|
+
</channel>
|
9
|
+
|
10
|
+
<programme channel="www.ontv.dk/tv/1" start="20120717060000 +0200" stop="20120717061000 +0200">
|
11
|
+
<title lang="dk">Noddy</title>
|
12
|
+
<sub-title lang="dk">Bare vær dig selv, Noddy.</sub-title>
|
13
|
+
<desc lang="dk">Tegnefilm.
|
14
|
+
Her kommer Noddy - så kom ud og leg! Den lille dreng af træ har altid travlt med at køre sine venner rundt i Legebyen - og du kan altid høre, når han er på vej!</desc>
|
15
|
+
<category lang="EN">kids</category>
|
16
|
+
<category lang="EN">serie</category>
|
17
|
+
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/1/11342497600.jpg&height=300&width=300"/>
|
18
|
+
</programme>
|
19
|
+
<programme channel="www.ontv.dk/tv/1" start="20120717120000 +0200" stop="20120717123000 +0200">
|
20
|
+
<title lang="dk">Black Business</title>
|
21
|
+
<desc lang="dk">Hvis du har den rigtige idé, mod og eventyrlyst, kan du gøre store forretninger og tjene mange penge i Afrika. For Afrika er økonomisk set blandt de hurtigste voksende regioner i verden. Det sker lige nu, og det gælder om at komme først. Esben Sørensen er en rødhåret dansker på 30 år, der er vokset op delvist i Afrika og delvist i Danmark. Han har et pladeselskab, hvor han indtil nu kun har udgivet en enkelt plade - med sig selv. Drømmen er at Caravan Records bliver det største pladeselskab i Østafrika, og det er absolut ikke urealistisk, for Østafrika er fyldt med musikalsk talent, markedet er gigantisk og afrikanernes købekraft stiger.
|
22
|
+
Sendt første gang 25.01.11</desc>
|
23
|
+
<category lang="EN">documentary</category>
|
24
|
+
<category lang="EN">serie</category>
|
25
|
+
<episode-num system="xmltv_ns"> .1/4. </episode-num>
|
26
|
+
</programme>
|
27
|
+
</tv>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<tv generator-info-name="www.ontv.dk/xmltv">
|
3
|
+
<channel id="www.ontv.dk/tv/1">
|
4
|
+
<display-name lang="dk">DR1 DK</display-name>
|
5
|
+
<icon src="http://ontv.dk/imgs/epg/logos/dr1_big.png"/>
|
6
|
+
</channel>
|
7
|
+
<channel id="www.ontv.dk/tv/2">
|
8
|
+
<display-name lang="dk">DR2 DK</display-name>
|
9
|
+
<icon src="http://ontv.dk/imgs/epg/logos/dr2_big.png"/>
|
10
|
+
</channel>
|
11
|
+
|
12
|
+
<programme channel="www.ontv.dk/tv/1" start="20131118001000 +0100" stop="20131118005500 +0100">
|
13
|
+
<title lang="dk">Maria Wern: Alle de stille døde</title>
|
14
|
+
<sub-title lang="dk">Svensk krimi-miniserie fra 2010.</sub-title>
|
15
|
+
<desc lang="dk">En advokat med voldelige tendenser er meldt savnet. Samtidig er liget af en anden mand blevet fisket op af havet. Det viser sig, at begge mænd havde gjort tjeneste som FN-soldater på Cypern. Snart meldes et tredje medlem af samme kompagni savnet, og vicekriminalkommissær Maria Wern må finde ud af, om de tre mænd havde en fælles fjende.</desc><category lang="EN">serie</category>
|
16
|
+
<episode-num system="xmltv_ns"> .1/2. </episode-num>
|
17
|
+
<credits>
|
18
|
+
<actor role="Maria Wern">Eva Röse</actor>
|
19
|
+
<actor role="Thomas Hartman">Allan Svensson</actor>
|
20
|
+
<actor role="Ivan Sirén">Thomas W. Gabrielsson</actor>
|
21
|
+
<director>Erik Leijonborg</director>
|
22
|
+
</credits>
|
23
|
+
</programme>
|
24
|
+
</tv>
|
@@ -0,0 +1,25 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<tv generator-info-name="www.ontv.dk/xmltv">
|
3
|
+
<channel id="www.ontv.dk/tv/1">
|
4
|
+
<display-name lang="dk">DR1 DK</display-name>
|
5
|
+
<icon src="http://ontv.dk/imgs/epg/logos/dr1_big.png"/>
|
6
|
+
</channel>
|
7
|
+
<channel id="www.ontv.dk/tv/2">
|
8
|
+
<display-name lang="dk">DR2 DK</display-name>
|
9
|
+
<icon src="http://ontv.dk/imgs/epg/logos/dr2_big.png"/>
|
10
|
+
</channel>
|
11
|
+
|
12
|
+
<programme channel="www.ontv.dk/tv/1" start="20131118001000 +0100" stop="20131118005500 +0100">
|
13
|
+
<title lang="dk">Maria Wern: Alle de stille døde</title>
|
14
|
+
<sub-title lang="dk">Svensk krimi-miniserie fra 2010.</sub-title>
|
15
|
+
<desc lang="dk">En advokat med voldelige tendenser er meldt savnet. Samtidig er liget af en anden mand blevet fisket op af havet. Det viser sig, at begge mænd havde gjort tjeneste som FN-soldater på Cypern. Snart meldes et tredje medlem af samme kompagni savnet, og vicekriminalkommissær Maria Wern må finde ud af, om de tre mænd havde en fælles fjende.</desc><category lang="EN">serie</category>
|
16
|
+
<episode-num system="xmltv_ns"> .1/2. </episode-num>
|
17
|
+
<credits>
|
18
|
+
<actor role="Maria Wern">Eva Röse</actor>
|
19
|
+
<actor role="Thomas Hartman">Allan Svensson</actor>
|
20
|
+
<actor role="Ivan Sirén">Thomas W. Gabrielsson</actor>
|
21
|
+
<director>Erik Leijonborg</director>
|
22
|
+
</credits>
|
23
|
+
<icon src="http://static.timefor.tv/imgs/print_img.php?sti=imgs/epg/channel/2013-11-17/528381020f0c1.jpg&height=300&width=300" />
|
24
|
+
</programme>
|
25
|
+
</tv>
|
@@ -0,0 +1,19 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
2
|
+
<tv generator-info-name="www.ontv.dk/xmltv">
|
3
|
+
<channel id="www.ontv.dk/tv/1">
|
4
|
+
<display-name lang="dk">DR1 DK</display-name>
|
5
|
+
</channel>
|
6
|
+
<channel id="www.ontv.dk/tv/2">
|
7
|
+
<display-name lang="dk">DR2 DK</display-name>
|
8
|
+
</channel>
|
9
|
+
|
10
|
+
<programme channel="www.ontv.dk/tv/1" start="20131213015000 +0100" stop="20131213022000 +0100">
|
11
|
+
<title lang="dk">Natholdet</title>
|
12
|
+
<sub-title lang="dk">Show med Anders Breinholt</sub-title>
|
13
|
+
<desc lang="dk">Anders Breinholt, i godt selskab med komiker Magnus Millang, kaster sit falkeblik på de absurditeter og morsomheder, vores tilværelse er fuld af. Idéerne til programmet stammer fra seerne, der hver dag sender masser link og klip ind til Anders Breinholt og resten af redaktionen. Derfor tager programmet altid afsæt i den virkelighed, som seerne oplever rundt om i riget og på internettet.</desc><category lang="EN">entertainment</category><category lang="EN">serie</category>
|
14
|
+
<credits>
|
15
|
+
<presenter>Anders Breinholt.</presenter>
|
16
|
+
</credits>
|
17
|
+
<icon src="http://static.timefor.tv/imgs/print_img.php?sti=imgs/epg/channel/2013-12-12/524d7aa4d161c.jpg&height=300&width=300" />
|
18
|
+
</programme>
|
19
|
+
</tv>
|
File without changes
|
@@ -17,7 +17,6 @@ Her kommer Noddy - så kom ud og leg! Den lille dreng af træ har altid travlt m
|
|
17
17
|
<category lang="EN">kids</category>
|
18
18
|
<category lang="EN">serie</category>
|
19
19
|
<episode-num system="xmltv_ns"> .2/12. </episode-num>
|
20
|
-
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/1/11342497600.jpg&height=300&width=300"/>
|
21
20
|
</programme>
|
22
21
|
<programme channel="www.ontv.dk/tv/1" start="20120717061000 +0200" stop="20120717062000 +0200">
|
23
22
|
<title lang="dk">Sprutte-Patruljen</title>
|
@@ -44,7 +43,6 @@ Her kommer Noddy - så kom ud og leg! Den lille dreng af træ har altid travlt m
|
|
44
43
|
Slå griseører og øjne ud, for modige Olivia er klar til at tage verden med storm! Den skæve, skøre og sjove gris Olivia er god til alt muligt, bl.a. at gøre folk rigtig trætte. Olivia kan nemlig som ingen anden prøve tøj fra skabet eller male billeder på væggen. Olivia har også startet sit eget band og sågar optrådt i cirkus.</desc>
|
45
44
|
<category lang="EN">kids</category>
|
46
45
|
<category lang="EN">serie</category>
|
47
|
-
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/1/11342500600.jpg&height=300&width=300"/>
|
48
46
|
</programme>
|
49
47
|
|
50
48
|
<programme channel="www.ontv.dk/tv/2" start="20120717080500 +0200" stop="20120717120000 +0200">
|
@@ -85,7 +83,6 @@ Sendt første gang 16.08.06</desc>
|
|
85
83
|
<desc lang="dk">Den tidligere jægersoldat Marco Tancredis hustru, Lidia, er blevet skudt. Marco er sikker på, at det var overlagt mord, og han er parat til at gøre hvad som helst for at finde ud af, hvem der står bag. Snart gør han en forbløffende opdagelse - nemlig at Lidia arbejdede for den italienske efterretningstjeneste. Men hvad arbejdede hun på? Og hvorfor skulle hun slås ihjel?</desc>
|
86
84
|
<category lang="EN">serie</category>
|
87
85
|
<episode-num system="xmltv_ns"> .1/12. </episode-num>
|
88
|
-
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/2/21342524600.jpg&height=300&width=300"/>
|
89
86
|
</programme>
|
90
87
|
<programme channel="www.ontv.dk/tv/2" start="20120717142500 +0200" stop="20120717152000 +0200">
|
91
88
|
<title lang="dk">Intelligence - i hemmelig tjeneste</title>
|
@@ -93,6 +90,5 @@ Sendt første gang 16.08.06</desc>
|
|
93
90
|
<desc lang="dk">Den tidligere jægersoldat Marco Tancredis hustru er blevet myrdet. Marco har opdaget, at hun arbejdede for den italienske efterretningstjeneste, og nu har han selv ladet sig ansætte som efterretningsagent for at finde frem til sandheden om hendes død. Marcos jagt på svar bringer ham til Rumænien - og ansigt til ansigt med en kvinde, der er ude på sit eget private hævntogt.</desc>
|
94
91
|
<category lang="EN">serie</category>
|
95
92
|
<episode-num system="xmltv_ns"> .2/12. </episode-num>
|
96
|
-
<icon src="http://ontv.dk//imgs/print_img.php?sti=/imgs/epg/channel/2012-07-17/2/21342527900.jpg&height=300&width=300"/>
|
97
93
|
</programme>
|
98
94
|
</tv>
|
@@ -1,26 +1,28 @@
|
|
1
1
|
require 'simple_pvr'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
3
|
+
module SimplePvr
|
4
|
+
describe Ffmpeg do
|
5
|
+
it 'spawns an ffmpeg process for creating thumbnails' do
|
6
|
+
Process.should_receive(:spawn)
|
7
|
+
Process.should_receive(:detach)
|
8
|
+
|
9
|
+
Ffmpeg.create_thumbnail_for('path/to/show')
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'spawns an ffmpeg process for transcoding to WebM' do
|
13
|
+
File.should_receive(:exists?).with('path/to/show/stream.webm').and_return(false)
|
14
|
+
Process.should_receive(:spawn)
|
15
|
+
Process.should_receive(:detach)
|
16
|
+
|
17
|
+
Ffmpeg.transcode_to_webm('path/to/show')
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'does not start ffmpeg for transcoding to WebM if WebM file already exists' do
|
21
|
+
File.should_receive(:exists?).with('path/to/show/stream.webm').and_return(true)
|
22
|
+
Process.should_not_receive(:spawn)
|
23
|
+
Process.should_not_receive(:detach)
|
24
|
+
|
25
|
+
Ffmpeg.transcode_to_webm('path/to/show')
|
26
|
+
end
|
25
27
|
end
|
26
28
|
end
|
@@ -1,82 +1,84 @@
|
|
1
1
|
require 'simple_pvr'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
context 'when initializing' do
|
11
|
-
it 'discovers a device when present' do
|
12
|
-
@pipe.stub(:read => 'hdhomerun device ABCDEF01 found at 10.0.0.4')
|
13
|
-
|
14
|
-
SimplePvr::HDHomeRun.new.device_id.should == 'ABCDEF01'
|
3
|
+
module SimplePvr
|
4
|
+
describe HDHomeRun do
|
5
|
+
before do
|
6
|
+
@pipe = double('pipe')
|
7
|
+
IO.should_receive(:popen).with('hdhomerun_config discover').and_yield(@pipe)
|
8
|
+
FileUtils.stub(:exists? => false)
|
15
9
|
end
|
16
10
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
expect { SimplePvr::HDHomeRun.new }.to raise_error(Exception, 'No device found: no devices found')
|
21
|
-
end
|
22
|
-
end
|
11
|
+
context 'when initializing' do
|
12
|
+
it 'discovers a device when present' do
|
13
|
+
@pipe.stub(:read => 'hdhomerun device ABCDEF01 found at 10.0.0.4')
|
23
14
|
|
24
|
-
|
25
|
-
|
26
|
-
@pipe.stub(:read => 'hdhomerun device ABCDEF01 found at 10.0.0.4')
|
27
|
-
@hdhomerun = SimplePvr::HDHomeRun.new
|
28
|
-
@hdhomerun.stub(:tuner_control_file).with(0).and_return('tuner0.lock')
|
29
|
-
@hdhomerun.stub(:tuner_control_file).with(1).and_return('tuner1.lock')
|
30
|
-
@file = File.open(File.dirname(__FILE__) + '/../resources/channels.txt', 'r:UTF-8')
|
31
|
-
end
|
15
|
+
HDHomeRun.new.device_id.should == 'ABCDEF01'
|
16
|
+
end
|
32
17
|
|
33
|
-
|
34
|
-
|
35
|
-
end
|
18
|
+
it 'raises an exception when no device is present' do
|
19
|
+
@pipe.stub(:read => 'no devices found')
|
36
20
|
|
37
|
-
|
38
|
-
|
39
|
-
SimplePvr::Model::Channel.should_receive(:clear)
|
40
|
-
File.should_receive(:open).with('channels.txt', 'r:UTF-8').and_yield(@file)
|
41
|
-
SimplePvr::Model::Channel.should_receive(:add).with('DR K', 282000000, 1098)
|
42
|
-
SimplePvr::Model::Channel.should_receive(:add).with('DR HD', 282000000, 1165)
|
43
|
-
|
44
|
-
@hdhomerun.scan_for_channels
|
21
|
+
expect { HDHomeRun.new }.to raise_error(Exception, 'No device found: no devices found')
|
22
|
+
end
|
45
23
|
end
|
46
24
|
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
25
|
+
context 'when initialized' do
|
26
|
+
before do
|
27
|
+
@pipe.stub(:read => 'hdhomerun device ABCDEF01 found at 10.0.0.4')
|
28
|
+
@hdhomerun = HDHomeRun.new
|
29
|
+
@hdhomerun.stub(:tuner_control_file).with(0).and_return('tuner0.lock')
|
30
|
+
@hdhomerun.stub(:tuner_control_file).with(1).and_return('tuner1.lock')
|
31
|
+
@file = File.open(File.dirname(__FILE__) + '/../resources/channels.txt', 'r:UTF-8')
|
32
|
+
end
|
52
33
|
|
53
|
-
|
54
|
-
|
34
|
+
after do
|
35
|
+
@file.close
|
36
|
+
end
|
55
37
|
|
56
|
-
|
57
|
-
|
58
|
-
|
38
|
+
it 'can do a channel scan' do
|
39
|
+
@hdhomerun.should_receive(:system).with('hdhomerun_config ABCDEF01 scan /tuner0 channels.txt')
|
40
|
+
Model::Channel.should_receive(:clear)
|
41
|
+
File.should_receive(:open).with('channels.txt', 'r:UTF-8').and_yield(@file)
|
42
|
+
Model::Channel.should_receive(:add).with('DR K', 282000000, 1098)
|
43
|
+
Model::Channel.should_receive(:add).with('DR HD', 282000000, 1165)
|
44
|
+
|
45
|
+
@hdhomerun.scan_for_channels
|
46
|
+
end
|
59
47
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
48
|
+
it 'can start recording on one tuner' do
|
49
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/channel auto:282000000")
|
50
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/program 1098")
|
51
|
+
@hdhomerun.should_receive(:spawn) #... with a lot of arguments ...
|
52
|
+
FileUtils.should_receive(:touch).with('tuner0.lock')
|
53
|
+
|
54
|
+
@hdhomerun.start_recording(0, 282000000, 1098, 'test directory')
|
55
|
+
end
|
56
|
+
|
57
|
+
it 'can stop recording on one tuner' do
|
58
|
+
@hdhomerun.stub(:system)
|
59
|
+
@hdhomerun.stub(:spawn => 32)
|
60
|
+
|
61
|
+
FileUtils.should_receive(:touch).with('tuner0.lock')
|
62
|
+
FileUtils.should_receive(:rm).with('tuner0.lock')
|
63
|
+
Process.should_receive(:wait).with(32)
|
64
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/channel none")
|
65
|
+
|
66
|
+
@hdhomerun.start_recording(0, 282000000, 1098, 'test directory')
|
67
|
+
@hdhomerun.stop_recording(0)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'can start and stop recording on another tuner' do
|
71
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/channel auto:282000000")
|
72
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/program 1098")
|
73
|
+
@hdhomerun.stub(:spawn => 32)
|
74
|
+
FileUtils.should_receive(:touch).with('tuner1.lock')
|
75
|
+
FileUtils.should_receive(:rm).with('tuner1.lock')
|
76
|
+
Process.should_receive(:wait).with(32)
|
77
|
+
@hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/channel none")
|
78
|
+
|
79
|
+
@hdhomerun.start_recording(1, 282000000, 1098, 'test directory')
|
80
|
+
@hdhomerun.stop_recording(1)
|
81
|
+
end
|
80
82
|
end
|
81
83
|
end
|
82
84
|
end
|