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.
Files changed (93) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +1 -0
  3. data/.ruby-version +1 -0
  4. data/.travis.yml +1 -4
  5. data/Gemfile.lock +72 -61
  6. data/README.md +51 -33
  7. data/bin/pvr_server +14 -6
  8. data/changelog.txt +11 -0
  9. data/development_server +14 -6
  10. data/features/channel_overview.feature +1 -1
  11. data/features/recordings.feature +30 -0
  12. data/features/step_definitions/pvr_steps.rb +34 -0
  13. data/features/support/env.rb +1 -2
  14. data/features/support/paths.rb +2 -0
  15. data/lib/simple_pvr.rb +2 -0
  16. data/lib/simple_pvr/model/database_initializer.rb +8 -0
  17. data/lib/simple_pvr/model/programme.rb +12 -2
  18. data/lib/simple_pvr/model/programme_actor.rb +14 -0
  19. data/lib/simple_pvr/model/programme_category.rb +14 -0
  20. data/lib/simple_pvr/model/programme_director.rb +13 -0
  21. data/lib/simple_pvr/model/programme_presenter.rb +13 -0
  22. data/lib/simple_pvr/model/recording.rb +12 -0
  23. data/lib/simple_pvr/programme_icon_fetcher.rb +15 -0
  24. data/lib/simple_pvr/pvr_initializer.rb +4 -4
  25. data/lib/simple_pvr/recorder.rb +4 -3
  26. data/lib/simple_pvr/recording_manager.rb +44 -24
  27. data/lib/simple_pvr/recording_planner.rb +3 -3
  28. data/lib/simple_pvr/scheduler.rb +12 -4
  29. data/lib/simple_pvr/server/base_controller.rb +12 -13
  30. data/lib/simple_pvr/server/channels_controller.rb +5 -5
  31. data/lib/simple_pvr/server/programmes_controller.rb +2 -2
  32. data/lib/simple_pvr/server/schedules_controller.rb +4 -4
  33. data/lib/simple_pvr/server/secured_controller.rb +71 -0
  34. data/lib/simple_pvr/server/shows_controller.rb +13 -8
  35. data/lib/simple_pvr/server/status_controller.rb +1 -1
  36. data/lib/simple_pvr/server/upcoming_recordings_controller.rb +1 -1
  37. data/lib/simple_pvr/version.rb +1 -1
  38. data/lib/simple_pvr/xmltv_reader.rb +37 -4
  39. data/public/css/typeahead.js-bootstrap.css +83 -0
  40. data/public/index.html +45 -37
  41. data/public/js/angular/http-auth-interceptor.js +122 -0
  42. data/public/js/app.js +4 -37
  43. data/public/js/controllers.js +22 -14
  44. data/public/js/directives.js +102 -0
  45. data/public/js/filters.js +0 -31
  46. data/public/js/services.js +64 -0
  47. data/public/js/typeahead/typeahead.min.js +7 -0
  48. data/public/partials/about.html +15 -13
  49. data/public/partials/channels.html +41 -36
  50. data/public/partials/programme.html +20 -4
  51. data/public/partials/programmeListing.html +14 -13
  52. data/public/partials/schedule.html +91 -86
  53. data/public/partials/schedules.html +28 -16
  54. data/public/partials/search.html +10 -11
  55. data/public/partials/show.html +26 -9
  56. data/public/partials/shows.html +5 -6
  57. data/public/partials/status.html +1 -1
  58. data/public/templates/loginDialog.html +30 -0
  59. data/public/templates/logoutLink.html +1 -0
  60. data/public/templates/titleSearch.html +5 -3
  61. data/simple_pvr.gemspec +6 -4
  62. data/spec/resources/dummyImage.png +1 -0
  63. data/spec/resources/programmes-with-categories.xmltv +27 -0
  64. data/spec/resources/programmes-with-credits.xmltv +24 -0
  65. data/spec/resources/programmes-with-icons.xmltv +25 -0
  66. data/spec/resources/programmes-with-presenters.xmltv +19 -0
  67. data/spec/resources/{programs-without-icon.xmltv → programmes-without-icon.xmltv} +0 -0
  68. data/spec/resources/{programs.xmltv → programmes.xmltv} +0 -4
  69. data/spec/simple_pvr/ffmpeg_spec.rb +24 -22
  70. data/spec/simple_pvr/hdhomerun_spec.rb +69 -67
  71. data/spec/simple_pvr/model/channel_spec.rb +101 -101
  72. data/spec/simple_pvr/model/programme_spec.rb +104 -104
  73. data/spec/simple_pvr/model/schedule_spec.rb +74 -74
  74. data/spec/simple_pvr/programme_icon_fetcher_spec.rb +25 -0
  75. data/spec/simple_pvr/pvr_initializer_spec.rb +40 -38
  76. data/spec/simple_pvr/recorder_spec.rb +37 -26
  77. data/spec/simple_pvr/recording_manager_spec.rb +160 -133
  78. data/spec/simple_pvr/recording_planner_spec.rb +213 -211
  79. data/spec/simple_pvr/scheduler_spec.rb +189 -172
  80. data/spec/simple_pvr/server/secured_controller_spec.rb +118 -0
  81. data/spec/simple_pvr/xmltv_reader_spec.rb +89 -41
  82. data/test/karma.conf.js +7 -4
  83. data/test/unit/filtersSpec.js +0 -36
  84. metadata +79 -63
  85. data/public/css/bootstrap-responsive.min.css +0 -9
  86. data/public/css/bootstrap.min.css +0 -9
  87. data/public/img/glyphicons-halflings-white.png +0 -0
  88. data/public/img/glyphicons-halflings.png +0 -0
  89. data/public/js/angular/angular-resource.min.js +0 -10
  90. data/public/js/angular/angular.min.js +0 -162
  91. data/public/js/bootstrap/bootstrap.min.js +0 -6
  92. data/public/js/jquery/jquery.min.js +0 -5
  93. data/test/lib/angular/angular-mocks.js +0 -1768
@@ -1,40 +1,52 @@
1
1
  <div class="row">
2
- <div class="span6" id="schedules">
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="span6">
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="span6">
18
+ <div class="col-md-12">
19
19
  <h1>Create new schedule</h1>
20
- <form>
21
- <label class="control-label" for="newScheduleTitle">Name</label>
22
- <input id="newScheduleTitle" type="text" ng-model="newSchedule.title"></p>
23
- <label class="control-label" for="newScheduleChannel">Channel</label>
24
- <select id="newScheduleChannel" ng-model="newSchedule.channelId">
25
- <option value="0">-- Any channel --</option>
26
- <option ng-repeat="channel in channels" value="{{channel.id}}">{{channel.name}}</option>
27
- </select></p>
28
- <span ng-show="newSchedule.title"><button ng-click="createSchedule()" class="btn">Create schedule</button></span>
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="span6" id="upcoming_recordings">
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="span6">
37
- <h2>{{recording.show_name}}<span ng-show="recording.is_conflicting" style="color: red"> (Conflicting)</span> <a ng-href="#" ng-click="excludeRecording(recording)" title="Do not record this specific show"><i class="icon-remove"></i></a></h2>
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>
@@ -1,17 +1,16 @@
1
1
  <div class="row">
2
- <div class="span12">
2
+ <div class="col-md-12">
3
3
  <h1>Results for {{query}}</h1>
4
4
  </div>
5
5
  </div>
6
- <div class="row" ng-repeat="programmeChunk in result | chunk:2">
7
- <div class="span6" ng-repeat="programme in programmeChunk">
8
- <h2>{{programme.title}}</h2>
9
- <p>{{programme.start_time | date:'fullDate'}} at {{programme.start_time | date:'HH:mm'}} on channel {{programme.channel.name}}.</p>
10
- <p>{{programme.subtitle}}</p>
11
- <p>{{programme.description}}</p>
12
- <p><a ng-href="/programmes/{{programme.id}}">Show details</a></p>
13
- <div ng-show="programme.is_scheduled">
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>
@@ -1,21 +1,38 @@
1
1
  <div class="row">
2
- <div class="span12">
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="recordingChunk in recordings | orderBy:'start_time' | chunk:2">
7
- <div class="span6" ng-repeat="recording in recordingChunk">
8
- <h2>{{recording.episode}}</h2>
9
- <img ng-show="recording.has_thumbnail" class="thumbnail" ng-src="/api/shows/{{show.name}}/recordings/{{recording.episode}}/thumbnail.png">
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.episode}}/stream.ts">Raw stream</a>
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.episode}}/stream.webm">View in browser (Firefox/Chrome)</a>
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>
@@ -1,7 +1,6 @@
1
- <div class="row" ng-repeat="showChunk in shows | orderBy:'name' | chunk:2">
2
- <div class="span6" ng-repeat="show in showChunk">
3
- <h2>{{show.name}}</h2>
4
- <p><a class="btn btn-primary" ng-href="/shows/{{show.id}}">Episodes</a>
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>
@@ -1,5 +1,5 @@
1
1
  <div class="row">
2
- <div class="span12">
2
+ <div class="col-md-12">
3
3
  <h1>Current status</h2>
4
4
  <p>{{status.status_text}}</p>
5
5
  </div>
@@ -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 pull-right" ng-submit="search()">
2
- <input class="span2" name="programme-search-query" type="text" ng-model="title" placeholder="Search programmes...">
3
- <button type="submit" class="btn">Search</button>
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>
@@ -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', '~> 1.6'
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.2'
30
- gem.add_development_dependency 'capybara', '~> 2.0'
31
- gem.add_development_dependency 'poltergeist', '~> 1.1'
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&amp;height=300&amp;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&amp;height=300&amp;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&amp;height=300&amp;width=300" />
18
+ </programme>
19
+ </tv>
@@ -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&amp;height=300&amp;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&amp;height=300&amp;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&amp;height=300&amp;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&amp;height=300&amp;width=300"/>
97
93
  </programme>
98
94
  </tv>
@@ -1,26 +1,28 @@
1
1
  require 'simple_pvr'
2
2
 
3
- describe SimplePvr::Ffmpeg do
4
- it 'spawns an ffmpeg process for creating thumbnails' do
5
- Process.should_receive(:spawn)
6
- Process.should_receive(:detach)
7
-
8
- SimplePvr::Ffmpeg.create_thumbnail_for('path/to/show')
9
- end
10
-
11
- it 'spawns an ffmpeg process for transcoding to WebM' do
12
- File.should_receive(:exists?).with('path/to/show/stream.webm').and_return(false)
13
- Process.should_receive(:spawn)
14
- Process.should_receive(:detach)
15
-
16
- SimplePvr::Ffmpeg.transcode_to_webm('path/to/show')
17
- end
18
-
19
- it 'does not start ffmpeg for transcoding to WebM if WebM file already exists' do
20
- File.should_receive(:exists?).with('path/to/show/stream.webm').and_return(true)
21
- Process.should_not_receive(:spawn)
22
- Process.should_not_receive(:detach)
23
-
24
- SimplePvr::Ffmpeg.transcode_to_webm('path/to/show')
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
- describe SimplePvr::HDHomeRun do
4
- before do
5
- @pipe = double('pipe')
6
- IO.should_receive(:popen).with('hdhomerun_config discover').and_yield(@pipe)
7
- FileUtils.stub(:exists? => false)
8
- end
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
- it 'raises an exception when no device is present' do
18
- @pipe.stub(:read => 'no devices found')
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
- context 'when initialized' do
25
- before do
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
- after do
34
- @file.close
35
- end
18
+ it 'raises an exception when no device is present' do
19
+ @pipe.stub(:read => 'no devices found')
36
20
 
37
- it 'can do a channel scan' do
38
- @hdhomerun.should_receive(:system).with('hdhomerun_config ABCDEF01 scan /tuner0 channels.txt')
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
- it 'can start recording on one tuner' do
48
- @hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/channel auto:282000000")
49
- @hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/program 1098")
50
- @hdhomerun.should_receive(:spawn) #... with a lot of arguments ...
51
- FileUtils.should_receive(:touch).with('tuner0.lock')
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
- @hdhomerun.start_recording(0, 282000000, 1098, 'test directory')
54
- end
34
+ after do
35
+ @file.close
36
+ end
55
37
 
56
- it 'can stop recording on one tuner' do
57
- @hdhomerun.stub(:system)
58
- @hdhomerun.stub(:spawn => 32)
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
- FileUtils.should_receive(:touch).with('tuner0.lock')
61
- FileUtils.should_receive(:rm).with('tuner0.lock')
62
- Process.should_receive(:wait).with(32)
63
- @hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner0/channel none")
64
-
65
- @hdhomerun.start_recording(0, 282000000, 1098, 'test directory')
66
- @hdhomerun.stop_recording(0)
67
- end
68
-
69
- it 'can start and stop recording on another tuner' do
70
- @hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/channel auto:282000000")
71
- @hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/program 1098")
72
- @hdhomerun.stub(:spawn => 32)
73
- FileUtils.should_receive(:touch).with('tuner1.lock')
74
- FileUtils.should_receive(:rm).with('tuner1.lock')
75
- Process.should_receive(:wait).with(32)
76
- @hdhomerun.should_receive(:system).with("hdhomerun_config ABCDEF01 set /tuner1/channel none")
77
-
78
- @hdhomerun.start_recording(1, 282000000, 1098, 'test directory')
79
- @hdhomerun.stop_recording(1)
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