simple_pvr 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile.lock CHANGED
@@ -1,17 +1,18 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- simple_pvr (0.0.2)
4
+ simple_pvr (0.0.3)
5
5
  activesupport (~> 3.2)
6
6
  data_mapper (~> 1.2)
7
7
  dm-sqlite-adapter (~> 1.2)
8
8
  nokogiri (~> 1.5)
9
+ puma (~> 1.6)
9
10
  sinatra (~> 1.3)
10
11
 
11
12
  GEM
12
13
  remote: http://rubygems.org/
13
14
  specs:
14
- activesupport (3.2.8)
15
+ activesupport (3.2.12)
15
16
  i18n (~> 0.6)
16
17
  multi_json (~> 1.0)
17
18
  addressable (2.2.8)
@@ -41,7 +42,7 @@ GEM
41
42
  dm-transactions (~> 1.2.0)
42
43
  dm-types (~> 1.2.0)
43
44
  dm-validations (~> 1.2.0)
44
- data_objects (0.10.8)
45
+ data_objects (0.10.12)
45
46
  addressable (~> 2.1)
46
47
  diff-lcs (1.1.3)
47
48
  dm-aggregates (1.2.0)
@@ -78,22 +79,24 @@ GEM
78
79
  uuidtools (~> 2.1)
79
80
  dm-validations (1.2.0)
80
81
  dm-core (~> 1.2.0)
81
- do_sqlite3 (0.10.8)
82
- data_objects (= 0.10.8)
82
+ do_sqlite3 (0.10.12)
83
+ data_objects (= 0.10.12)
83
84
  fastercsv (1.5.5)
84
85
  ffi (1.1.5)
85
86
  gherkin (2.11.2)
86
87
  json (>= 1.4.6)
87
- i18n (0.6.1)
88
+ i18n (0.6.4)
88
89
  json (1.7.5)
89
- json_pure (1.7.5)
90
+ json_pure (1.7.7)
90
91
  libwebsocket (0.1.5)
91
92
  addressable
92
93
  mime-types (1.19)
93
94
  multi_json (1.3.6)
94
95
  nokogiri (1.5.5)
96
+ puma (1.6.3)
97
+ rack (~> 1.2)
95
98
  rack (1.4.1)
96
- rack-protection (1.4.0)
99
+ rack-protection (1.5.0)
97
100
  rack
98
101
  rack-test (0.6.1)
99
102
  rack (>= 1.0)
@@ -112,12 +115,12 @@ GEM
112
115
  libwebsocket (~> 0.1.3)
113
116
  multi_json (~> 1.0)
114
117
  rubyzip
115
- sinatra (1.3.3)
116
- rack (~> 1.3, >= 1.3.6)
117
- rack-protection (~> 1.2)
118
+ sinatra (1.3.6)
119
+ rack (~> 1.4)
120
+ rack-protection (~> 1.3)
118
121
  tilt (~> 1.3, >= 1.3.3)
119
- stringex (1.4.0)
120
- tilt (1.3.3)
122
+ stringex (1.5.1)
123
+ tilt (1.3.5)
121
124
  uuidtools (2.1.3)
122
125
  xpath (0.1.4)
123
126
  nokogiri (~> 1.3)
data/README.md CHANGED
@@ -61,7 +61,7 @@ First you must specify in a YAML file how the channel IDs in your XMLTV file rel
61
61
  that the HDHomeRun has found for you. Create a file called e.g. "channel_mappings.yaml", with lines like this:
62
62
 
63
63
  www.ontv.dk/tv/1: DR 1
64
- www.ontv.dk/tv/2: DR 2
64
+ www.ontv.dk/tv/2: DR 2
65
65
 
66
66
  Then read your XMLTV file and the mappings file:
67
67
 
@@ -108,14 +108,16 @@ Future?
108
108
  This projects needs to be a nice, readable, hackable, tested system. No pull requests are
109
109
  accepted that violate this.
110
110
 
111
- For version 1 of SimplePVR, I'd like to finish the following:
111
+ For version 1 of SimplePVR, there are no more features planned. Instead, there'll be a little clean-up:
112
112
 
113
- * More schedule editing, e.g.:
114
- * "Start early" and "end late" (currently 2 and 5 minutes).
115
- * Which time of day the schedule should be active (e.g. only the afternoon, ignoring all the
116
- re-runs earlier in the day).
117
- * Removal of outdated schedules (the "Record single programme" and "Don't record this specific
118
- programme").
113
+ * Updating dependencies:
114
+ * Twitter Bootstrap.
115
+ * AngularJS.
116
+ * JQuery (should be part of the project, instead of fetched through CDN).
117
+ * Testacular is now called Karma.
118
+ * Various code clean-ups (no grand plan).
119
+ * Various GUI clean-ups (no grand plan).
120
+ * Various bug fixes (no known issues at this point).
119
121
 
120
122
  There is lots of stuff I'd like to do after that, but I have no deadline - which means that pull
121
123
  requests are the only means you have for speeding things up. This includes:
data/bin/pvr_server CHANGED
@@ -5,6 +5,6 @@ require 'simple_pvr'
5
5
 
6
6
  Rack::Server.start(
7
7
  :config => SimplePvr::PvrInitializer.rackup_file_path,
8
- :Port => 4567
9
- #:server => 'sinatra'
8
+ :Port => 4567,
9
+ :server => 'puma'
10
10
  )
@@ -6,6 +6,7 @@ Feature: Scheduling
6
6
  Background:
7
7
  Given the following programmes:
8
8
  | title | subtitle | channel | day |
9
+ | Old News | Just the old news... | Channel 1 | -1 |
9
10
  | News right now | Just the news... | Channel 1 | 0 |
10
11
  | Bonderøven | Danish documentary | Channel 1 | 1 |
11
12
  | Bonderøven | Danish documentary | Channel 2 | 1 |
@@ -16,6 +17,10 @@ Scenario: Nothing is scheduled by default
16
17
  Given I am on the schedules page
17
18
  Then there should be 0 upcoming recordings
18
19
 
20
+ Scenario: I cannot record past programmes
21
+ Given I have navigated to the programme page for yesterday's "Old News" on channel "Channel 1"
22
+ Then I should not see the button "Record just this programme"
23
+
19
24
  Scenario: Schedule by title for a single channel
20
25
  Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
21
26
  And I choose to record the programme on this channel
@@ -109,4 +114,56 @@ Scenario: Editing a schedule so that only some weekdays are allowed
109
114
  And I check "Record Wednesdays"
110
115
  And I check "Record Sundays"
111
116
  And I press "Update"
112
- Then I should see "(Mondays, Wednesdays, and Sundays)"
117
+ Then I should see "(Mondays, Wednesdays, and Sundays)"
118
+
119
+ Scenario: Setting "Start early" and "End late"
120
+ Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
121
+ And I choose to record the programme on any channel
122
+ And I am on the schedules page
123
+ And I follow "Edit"
124
+ And I fill in "Start early" with "4"
125
+ And I fill in "End late" with "10"
126
+ And I press "Update"
127
+ Then I should see "(starts 4 minutes early, ends 10 minutes late)"
128
+
129
+ Scenario: Start and end times of day
130
+ Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
131
+ And I choose to record the programme on any channel
132
+ And I am on the schedules page
133
+ And I follow "Edit"
134
+ And I check "Filter by time of day"
135
+ And I fill in "From" with "17:00"
136
+ And I fill in "To" with "19:00"
137
+ And I press "Update"
138
+ Then I should see "(between 17:00 and 19:00)"
139
+
140
+ Scenario: Start and end times of day, crossing midnight
141
+ Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
142
+ And I choose to record the programme on any channel
143
+ And I am on the schedules page
144
+ And I follow "Edit"
145
+ And I check "Filter by time of day"
146
+ And I fill in "From" with "19:00"
147
+ And I fill in "To" with "3:00"
148
+ And I press "Update"
149
+ Then I should see "(between 19:00 and 3:00)"
150
+
151
+ Scenario: Start time of day
152
+ Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
153
+ And I choose to record the programme on any channel
154
+ And I am on the schedules page
155
+ And I follow "Edit"
156
+ And I check "Filter by time of day"
157
+ And I fill in "From" with "17:00"
158
+ And I press "Update"
159
+ Then I should see "(after 17:00)"
160
+
161
+ Scenario: End time of day
162
+ Given I have navigated to the programme page for "Bonderøven" on channel "Channel 1"
163
+ And I choose to record the programme on any channel
164
+ And I am on the schedules page
165
+ And I follow "Edit"
166
+ And I check "Filter by time of day"
167
+ And I fill in "To" with "7:00"
168
+ And I press "Update"
169
+ Then I should see "(before 7:00)"
@@ -28,6 +28,14 @@ Given /I have navigated to the programme page for "(.*)" on channel "(.*)"/ do |
28
28
  click_link(title)
29
29
  end
30
30
 
31
+ Given /I have navigated to the programme page for yesterday's "(.*)" on channel "(.*)"/ do |title, channel|
32
+ visit path_to('the channel overview page')
33
+ fill_in('channel_filter', :with => channel)
34
+ click_link('...')
35
+ click_link('<<')
36
+ click_link(title)
37
+ end
38
+
31
39
  Given /I choose to record just this programme/ do
32
40
  choose_to_record('Record just this programme')
33
41
  end
@@ -77,6 +85,10 @@ Then /I should not see the schedule "(.*)"/ do |text|
77
85
  end
78
86
  end
79
87
 
88
+ Then /I should not see the button "(.*)"/ do |text|
89
+ page.wait_until { page.has_no_button?(text) }
90
+ end
91
+
80
92
  Then /there should be (\d*) upcoming recordings?/ do |upcoming_recordings|
81
93
  page.wait_until { find('#upcoming_recordings').all('h2').length == upcoming_recordings.to_i }
82
94
  end
@@ -6,7 +6,7 @@ require 'rspec'
6
6
  require File.join(File.dirname(__FILE__), '../../lib/simple_pvr')
7
7
 
8
8
  SimplePvr::PvrInitializer.setup_for_integration_test
9
- SimplePvr::RecordingPlanner.read
9
+ SimplePvr::RecordingPlanner.reload
10
10
 
11
11
  Capybara.app = eval "Rack::Builder.new {( " + SimplePvr::PvrInitializer.rack_maps_file + ")}"
12
12
  Capybara.default_driver = :selenium
@@ -97,7 +97,7 @@ module SimplePvr
97
97
  end
98
98
 
99
99
  def tuner_control_file(tuner)
100
- File.dirname(__FILE__) + "/tuner#{tuner}.lock"
100
+ "tuner#{tuner}.lock"
101
101
  end
102
102
  end
103
103
  end
@@ -14,6 +14,14 @@ module SimplePvr
14
14
 
15
15
  belongs_to :channel
16
16
 
17
+ def end_time
18
+ @start_time.advance(seconds: duration)
19
+ end
20
+
21
+ def outdated?
22
+ end_time < Time.now
23
+ end
24
+
17
25
  def self.clear
18
26
  Programme.destroy
19
27
  end
@@ -3,13 +3,36 @@ module SimplePvr
3
3
  class Schedule
4
4
  include DataMapper::Resource
5
5
  storage_names[:default] = 'schedules'
6
+
7
+ def self.cleanup
8
+ Schedule.all(:end_time.lt => Time.now).each {|s| s.destroy }
9
+ end
10
+
11
+ def self.add_specification(options)
12
+ Schedule.create(
13
+ type: :specification,
14
+ title: options[:title],
15
+ channel: options[:channel],
16
+ start_time: options[:start_time],
17
+ end_time: options[:end_time])
18
+ end
6
19
 
7
20
  property :id, Serial
8
21
  property :type, Enum[:specification, :exception]
9
- property :title, String, :length => 255
22
+ property :title, String, length: 255
23
+
10
24
  # If specified (and channel is specified too), this schedule is for a specific
11
25
  # programme at a specific channel at a specific time
12
26
  property :start_time, DateTime
27
+ property :end_time, DateTime
28
+
29
+ property :custom_start_early_minutes, Integer
30
+ property :custom_end_late_minutes, Integer
31
+
32
+ property :filter_by_time_of_day, Boolean
33
+ property :from_time_of_day, String, length: 5
34
+ property :to_time_of_day, String, length: 5
35
+
13
36
  property :filter_by_weekday, Boolean
14
37
  property :monday, Boolean
15
38
  property :tuesday, Boolean
@@ -21,12 +44,12 @@ module SimplePvr
21
44
 
22
45
  belongs_to :channel, required: false
23
46
 
24
- def self.add_specification(options)
25
- Schedule.create(
26
- type: :specification,
27
- title: options[:title],
28
- channel: options[:channel],
29
- start_time: options[:start_time])
47
+ def start_early_minutes
48
+ custom_start_early_minutes || 2
49
+ end
50
+
51
+ def end_late_minutes
52
+ custom_end_late_minutes || 5
30
53
  end
31
54
  end
32
55
  end
@@ -1,6 +1,8 @@
1
1
  module SimplePvr
2
2
  class RecordingPlanner
3
- def self.read
3
+ def self.reload
4
+ Model::Schedule.cleanup
5
+
4
6
  planner = self.new
5
7
  planner.read
6
8
  end
@@ -15,24 +17,28 @@ module SimplePvr
15
17
  exceptions = schedules.find_all {|s| s.type == :exception }
16
18
 
17
19
  specifications.each do |specification|
18
- title = specification.title
19
- if specification.channel && specification.start_time
20
- programmes = Model::Programme.on_channel_with_title_and_start_time(specification.channel, specification.title, specification.start_time)
21
- elsif specification.channel
22
- programmes = Model::Programme.on_channel_with_title(specification.channel, specification.title)
23
- else
24
- programmes = Model::Programme.with_title(specification.title)
25
- end
26
-
20
+ programmes = programmes_matching(specification)
27
21
  programmes_with_exceptions_removed = programmes.find_all {|programme| !matches_exception(programme, exceptions) }
28
22
  programmes_filtered_by_weekdays = programmes_with_exceptions_removed.find_all {|programme| on_allowed_weekday(programme, specification) }
29
- add_programmes(title, programmes_filtered_by_weekdays)
23
+ programmes_filtered_by_time_of_day = programmes_filtered_by_weekdays.find_all {|programme| at_allowed_time_of_day(programme, specification) }
24
+
25
+ add_programmes(specification, programmes_filtered_by_time_of_day)
30
26
  end
31
27
 
32
28
  PvrInitializer.scheduler.recordings = @recordings
33
29
  end
34
30
 
35
31
  private
32
+ def programmes_matching(specification)
33
+ if specification.channel && specification.start_time
34
+ Model::Programme.on_channel_with_title_and_start_time(specification.channel, specification.title, specification.start_time)
35
+ elsif specification.channel
36
+ Model::Programme.on_channel_with_title(specification.channel, specification.title)
37
+ else
38
+ Model::Programme.with_title(specification.title)
39
+ end
40
+ end
41
+
36
42
  def matches_exception(programme, exceptions)
37
43
  exceptions.any? do |exception|
38
44
  programme.title == exception.title &&
@@ -56,17 +62,58 @@ module SimplePvr
56
62
  else false
57
63
  end
58
64
  end
65
+
66
+ def at_allowed_time_of_day(programme, specification)
67
+ return true unless specification.filter_by_time_of_day
68
+
69
+ # When comparing times of day below, we convert all timestamps into an integer so that we can easily compare them.
70
+ # We do this by converting e.g. "10:35" to the integer 1035.
71
+ programme_pseudo_start_time = pseudo_time_of_day_from_datetime(programme.start_time)
72
+
73
+ if specification.from_time_of_day && specification.to_time_of_day
74
+ pseudo_from_time = pseudo_time_of_day_from_string(specification.from_time_of_day)
75
+ pseudo_to_time = pseudo_time_of_day_from_string(specification.to_time_of_day)
76
+ if pseudo_from_time < pseudo_to_time
77
+ # Both times of day are at the same day
78
+ programme_pseudo_start_time >= pseudo_time_of_day_from_string(specification.from_time_of_day) &&
79
+ programme_pseudo_start_time <= pseudo_time_of_day_from_string(specification.to_time_of_day)
80
+ else
81
+ # Stretching across midnight
82
+ programme_pseudo_start_time >= pseudo_time_of_day_from_string(specification.from_time_of_day) ||
83
+ programme_pseudo_start_time <= pseudo_time_of_day_from_string(specification.to_time_of_day)
84
+ end
85
+ elsif specification.from_time_of_day
86
+ programme_pseudo_start_time >= pseudo_time_of_day_from_string(specification.from_time_of_day)
87
+ elsif specification.to_time_of_day
88
+ programme_pseudo_start_time <= pseudo_time_of_day_from_string(specification.to_time_of_day)
89
+ else
90
+ true
91
+ end
92
+ end
59
93
 
60
- def add_programmes(title, programmes)
94
+ def add_programmes(schedule, programmes)
61
95
  programmes.each do |programme|
62
- start_time = programme.start_time.advance(minutes: -2)
63
- duration = programme.duration + 7.minutes
64
- add_recording(title, programme.channel, start_time, duration, programme)
96
+ start_time = programme.start_time.advance(minutes: -schedule.start_early_minutes)
97
+ duration = programme.duration + (schedule.start_early_minutes + schedule.end_late_minutes).minutes
98
+ add_recording(schedule.title, programme.channel, start_time, duration, programme)
65
99
  end
66
100
  end
67
101
 
68
102
  def add_recording(title, channel, start_time, duration, programme=nil)
69
103
  @recordings << SimplePvr::Model::Recording.new(channel, title, start_time, duration, programme)
70
104
  end
105
+
106
+ # Given a time of day as string, e.g. "10:35", returns an integer which can be used to compare with other
107
+ # times of day, e.g. "1035"
108
+ def pseudo_time_of_day_from_string(s)
109
+ components = s.split(':')
110
+ components[0].to_i * 100 + components[1].to_i
111
+ end
112
+
113
+ # Given a datetime, representing e.g. "10:35", returns an integer which can be used to compare with other
114
+ # times of day, e.g. "1035"
115
+ def pseudo_time_of_day_from_datetime(datetime)
116
+ datetime.hour * 100 + datetime.min
117
+ end
71
118
  end
72
119
  end
@@ -29,11 +29,11 @@ module SimplePvr
29
29
  end
30
30
  end
31
31
 
32
- def is_scheduled?(programme)
32
+ def scheduled?(programme)
33
33
  @scheduled_programmes[programme.id] != nil
34
34
  end
35
35
 
36
- def is_conflicting?(programme)
36
+ def conflicting?(programme)
37
37
  @conflicting_programmes[programme.id] != nil
38
38
  end
39
39
 
@@ -24,11 +24,10 @@ module SimplePvr
24
24
  end
25
25
 
26
26
  def reload_schedules
27
- RecordingPlanner.read
27
+ RecordingPlanner.reload
28
28
  end
29
29
 
30
30
  def programme_hash(programme)
31
- is_scheduled = PvrInitializer.scheduler.is_scheduled?(programme)
32
31
  {
33
32
  id: programme.id,
34
33
  channel: { id: programme.channel.id, name: programme.channel.name },
@@ -36,8 +35,9 @@ module SimplePvr
36
35
  subtitle: programme.subtitle,
37
36
  description: programme.description,
38
37
  start_time: programme.start_time,
39
- is_scheduled: is_scheduled,
40
- episode_num: programme.episode_num
38
+ is_scheduled: PvrInitializer.scheduler.scheduled?(programme),
39
+ episode_num: programme.episode_num,
40
+ is_outdated: programme.outdated?
41
41
  }
42
42
  end
43
43
 
@@ -85,8 +85,8 @@ module SimplePvr
85
85
  id: programme.id,
86
86
  title: programme.title,
87
87
  start_time: programme.start_time,
88
- is_scheduled: PvrInitializer.scheduler.is_scheduled?(programme),
89
- is_conflicting: PvrInitializer.scheduler.is_conflicting?(programme)
88
+ is_scheduled: PvrInitializer.scheduler.scheduled?(programme),
89
+ is_conflicting: PvrInitializer.scheduler.conflicting?(programme)
90
90
  }
91
91
  end
92
92
  end
@@ -1,5 +1,5 @@
1
1
  SimplePvr::PvrInitializer.setup
2
- SimplePvr::RecordingPlanner.read
2
+ SimplePvr::RecordingPlanner.reload
3
3
 
4
4
  # Due to a lazy initialization bug in ActiveSupport:
5
5
  # http://stackoverflow.com/questions/5267700/undefined-method-encode-for-activesupportjsonmodule
@@ -30,14 +30,14 @@ module SimplePvr
30
30
 
31
31
  post '/:id/record_just_this_programme' do |id|
32
32
  programme = Model::Programme.get(id.to_i)
33
- Model::Schedule.add_specification(title: programme.title, channel: programme.channel, start_time: programme.start_time)
33
+ Model::Schedule.add_specification(title: programme.title, channel: programme.channel, start_time: programme.start_time, end_time: programme.end_time)
34
34
  reload_schedules
35
35
  programme_hash(programme).to_json
36
36
  end
37
37
 
38
38
  post '/:id/exclude' do |id|
39
39
  programme = Model::Programme.get(id.to_i)
40
- Model::Schedule.create(type: :exception, title: programme.title, channel: programme.channel, start_time: programme.start_time)
40
+ Model::Schedule.create(type: :exception, title: programme.title, channel: programme.channel, start_time: programme.start_time, end_time: programme.end_time)
41
41
  reload_schedules
42
42
  programme_hash(programme).to_json
43
43
  end
@@ -29,6 +29,14 @@ module SimplePvr
29
29
  schedule = Model::Schedule.get(id)
30
30
  schedule.title = parameters['title']
31
31
  schedule.channel = channel_id > 0 ? Model::Channel.get(channel_id) : nil
32
+
33
+ schedule.custom_start_early_minutes = parameters['custom_start_early_minutes'].present? ? parameters['custom_start_early_minutes'].to_i : nil
34
+ schedule.custom_end_late_minutes = parameters['custom_end_late_minutes'].present? ? parameters['custom_end_late_minutes'].to_i : nil
35
+
36
+ schedule.filter_by_time_of_day = parameters['filter_by_time_of_day']
37
+ schedule.from_time_of_day = parameters['from_time_of_day']
38
+ schedule.to_time_of_day = parameters['to_time_of_day']
39
+
32
40
  schedule.filter_by_weekday = parameters['filter_by_weekday'] ? true : nil
33
41
  schedule.monday = parameters['monday']
34
42
  schedule.tuesday = parameters['tuesday']
@@ -56,6 +64,16 @@ module SimplePvr
56
64
  channel: schedule.channel ? { id: schedule.channel.id, name: schedule.channel.name } : nil,
57
65
  start_time: schedule.start_time,
58
66
  is_exception: schedule.type == :exception,
67
+
68
+ custom_start_early_minutes: schedule.custom_start_early_minutes,
69
+ custom_end_late_minutes: schedule.custom_end_late_minutes,
70
+ start_early_minutes: schedule.start_early_minutes,
71
+ end_late_minutes: schedule.end_late_minutes,
72
+
73
+ filter_by_time_of_day: schedule.filter_by_time_of_day,
74
+ from_time_of_day: schedule.from_time_of_day,
75
+ to_time_of_day: schedule.to_time_of_day,
76
+
59
77
  filter_by_weekday: schedule.filter_by_weekday,
60
78
  monday: schedule.monday,
61
79
  tuesday: schedule.tuesday,
@@ -9,7 +9,7 @@ module SimplePvr
9
9
  start_time: recording.start_time,
10
10
  channel: { id: recording.channel.id, name: recording.channel.name },
11
11
  subtitle: recording.programme.subtitle,
12
- is_conflicting: PvrInitializer.scheduler.is_conflicting?(recording.programme)
12
+ is_conflicting: PvrInitializer.scheduler.conflicting?(recording.programme)
13
13
  }
14
14
  end.to_json
15
15
  end
@@ -1,3 +1,3 @@
1
1
  module SimplePvr
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
data/public/index.html CHANGED
@@ -20,6 +20,7 @@
20
20
 
21
21
  <script src="app/js/controllers.js"></script>
22
22
  <script src="app/js/services.js"></script>
23
+ <script src="app/js/filters.js"></script>
23
24
  <script src="app/js/app.js"></script>
24
25
  </head>
25
26
 
@@ -40,10 +41,7 @@
40
41
  <navbar-item route="/shows">Recordings</navbar-item>
41
42
  <navbar-item route="/status">Status</navbar-item>
42
43
  </ul>
43
- <form class="navbar-form pull-right" ng-controller="SearchProgrammesCtrl" ng-submit="search()">
44
- <input pvr-autocomplete class="span2" id="programme-search-query" type="text" placeholder="Search programmes...">
45
- <button type="submit" class="btn">Search</button>
46
- </form>
44
+ <title-search></title-search>
47
45
  </div>
48
46
  </div>
49
47
  </div>