tivohmo 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (99) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +1 -1
  3. data/CHANGELOG +30 -0
  4. data/Gemfile +2 -0
  5. data/README.md +4 -6
  6. data/TODO +1 -0
  7. data/contrib/tivohmo.conf +2 -2
  8. data/contrib/tivohmo.plist +1 -1
  9. data/contrib/tivohmo.yml +60 -0
  10. data/lib/tivohmo/adapters/filesystem/file_item.rb +2 -3
  11. data/lib/tivohmo/adapters/plex/category.rb +87 -6
  12. data/lib/tivohmo/adapters/plex/episode.rb +30 -11
  13. data/lib/tivohmo/adapters/plex/group.rb +20 -0
  14. data/lib/tivohmo/adapters/plex/movie.rb +15 -3
  15. data/lib/tivohmo/adapters/plex/qualified_category.rb +14 -3
  16. data/lib/tivohmo/adapters/plex/season.rb +2 -2
  17. data/lib/tivohmo/adapters/plex/section.rb +5 -5
  18. data/lib/tivohmo/adapters/plex/transcoder.rb +5 -1
  19. data/lib/tivohmo/adapters/plex.rb +1 -0
  20. data/lib/tivohmo/adapters/settings/application.rb +37 -0
  21. data/lib/tivohmo/adapters/settings/display_item.rb +35 -0
  22. data/lib/tivohmo/adapters/settings/key_container.rb +35 -0
  23. data/lib/tivohmo/adapters/settings/metadata.rb +26 -0
  24. data/lib/tivohmo/adapters/settings/reset_defaults_item.rb +39 -0
  25. data/lib/tivohmo/adapters/settings/set_value_item.rb +38 -0
  26. data/lib/tivohmo/adapters/settings/transcoder.rb +23 -0
  27. data/lib/tivohmo/adapters/settings.rb +7 -0
  28. data/lib/tivohmo/adapters/streamio/transcoder.rb +39 -1
  29. data/lib/tivohmo/api/container.rb +5 -1
  30. data/lib/tivohmo/api/item.rb +2 -0
  31. data/lib/tivohmo/api/subtitle.rb +13 -0
  32. data/lib/tivohmo/api/transcoder.rb +1 -3
  33. data/lib/tivohmo/api.rb +1 -0
  34. data/lib/tivohmo/beacon.rb +3 -3
  35. data/lib/tivohmo/cli.rb +175 -48
  36. data/lib/tivohmo/config.rb +157 -0
  37. data/lib/tivohmo/server/views/_container.builder +1 -1
  38. data/lib/tivohmo/server.rb +6 -3
  39. data/lib/tivohmo/version.rb +1 -1
  40. data/lib/tivohmo.rb +1 -0
  41. data/spec/adapters/filesystem/file_item_spec.rb +1 -1
  42. data/spec/adapters/plex/application_spec.rb +10 -1
  43. data/spec/adapters/plex/category_spec.rb +93 -20
  44. data/spec/adapters/plex/episode_spec.rb +34 -15
  45. data/spec/adapters/plex/metadata_spec.rb +5 -7
  46. data/spec/adapters/plex/movie_spec.rb +10 -7
  47. data/spec/adapters/plex/qualified_category_spec.rb +17 -17
  48. data/spec/adapters/plex/season_spec.rb +23 -4
  49. data/spec/adapters/plex/section_spec.rb +4 -4
  50. data/spec/adapters/plex/show_spec.rb +19 -4
  51. data/spec/adapters/plex/transcoder_spec.rb +2 -8
  52. data/spec/adapters/settings/application_spec.rb +25 -0
  53. data/spec/adapters/settings/display_item_spec.rb +29 -0
  54. data/spec/adapters/settings/key_container_spec.rb +33 -0
  55. data/spec/adapters/settings/metadata_spec.rb +33 -0
  56. data/spec/adapters/settings/reset_defaults_item_spec.rb +54 -0
  57. data/spec/adapters/settings/set_value_item_spec.rb +54 -0
  58. data/spec/api/container_spec.rb +12 -0
  59. data/spec/api/item_spec.rb +2 -0
  60. data/spec/api/transcoder_spec.rb +0 -1
  61. data/spec/beacon_spec.rb +3 -1
  62. data/spec/cli_spec.rb +57 -19
  63. data/spec/config_spec.rb +224 -0
  64. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Application/_children/should_get_children.yml +61 -0
  65. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_allow_disabling_subtitles.yml +424 -0
  66. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_display_non-zero_child_count_once_children_fetched.yml +653 -0
  67. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children.yml +599 -0
  68. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children_with_embedded_subtitles.yml +595 -0
  69. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_have_children_with_subtitles.yml +482 -0
  70. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_memoize.yml +478 -0
  71. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_refresh_children_when_config_changes.yml +1245 -0
  72. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_children/should_use_category_value_for_children.yml +395 -0
  73. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_instantiate.yml +61 -0
  74. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_set_presorted_if_present.yml +61 -0
  75. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Category/_initialize/should_use_category_value_for_title_if_present.yml +61 -0
  76. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_initialize/should_instantiate.yml +444 -0
  77. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_metadata/should_allow_disabling_series_id_in_metadata.yml +444 -0
  78. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Episode/_metadata/should_populate_metadata.yml +579 -0
  79. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Metadata/_initialize/should_instantiate.yml +296 -0
  80. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Movie/_initialize/should_instantiate.yml +296 -0
  81. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Movie/_metadata/should_populate_metadata.yml +296 -0
  82. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_display_non-zero_child_count_once_children_fetched.yml +220 -0
  83. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_have_children.yml +393 -0
  84. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_children/should_memoize.yml +219 -0
  85. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_QualifiedCategory/_initialize/should_instantiate.yml +61 -0
  86. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_children/should_have_children.yml +444 -0
  87. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_children/should_memoize.yml +444 -0
  88. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Season/_initialize/should_instantiate.yml +250 -0
  89. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_children/should_have_category_children.yml +61 -0
  90. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_children/should_memoize.yml +61 -0
  91. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Section/_initialize/should_instantiate.yml +61 -0
  92. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_children/should_have_children.yml +250 -0
  93. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_children/should_memoize.yml +250 -0
  94. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Show/_initialize/should_instantiate.yml +166 -0
  95. data/spec/fixtures/vcr/TivoHMO_Adapters_Plex_Transcoder/_initialize/should_instantiate.yml +296 -0
  96. data/spec/server_spec.rb +31 -2
  97. data/spec/spec_helper.rb +24 -36
  98. data/tivohmo.gemspec +2 -2
  99. metadata +109 -3
@@ -0,0 +1,35 @@
1
+ module TivoHMO
2
+ module Adapters
3
+ module Settings
4
+
5
+ # A Container for config keys
6
+ class KeyContainer
7
+ include TivoHMO::API::Container
8
+ include GemLogger::LoggerSupport
9
+ include MonitorMixin
10
+
11
+ def initialize(key)
12
+ super(key)
13
+ self.presorted = true
14
+ end
15
+
16
+ def children
17
+ synchronize do
18
+ if super.blank?
19
+ spec = Config.instance.known_config[identifier]
20
+ add_child(DisplayItem.new("Help", spec[:description]))
21
+ add_child(DisplayItem.new("Default Value: #{spec[:default_value]}"))
22
+ val = Config.instance.get(identifier)
23
+ add_child(DisplayItem.new("Current Value: #{!!val}"))
24
+ add_child(SetValueItem.new(identifier, !val))
25
+ end
26
+ end
27
+
28
+ super
29
+ end
30
+
31
+ end
32
+
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,26 @@
1
+ module TivoHMO
2
+ module Adapters
3
+ module Settings
4
+
5
+ # Dummy metadata
6
+ class Metadata
7
+ include TivoHMO::API::Metadata
8
+ include GemLogger::LoggerSupport
9
+
10
+ attr_accessor :item_detail_callback
11
+
12
+ def initialize(item)
13
+ super(item)
14
+ end
15
+
16
+ # hack - star_rating only gets called when viewing item_detail
17
+ def star_rating
18
+ item_detail_callback.try(:call, self)
19
+ return nil
20
+ end
21
+
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,39 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'listen'
3
+
4
+ module TivoHMO
5
+ module Adapters
6
+ module Settings
7
+
8
+ # An Item for toggling boolean bvalue
9
+ class ResetDefaultsItem
10
+ include TivoHMO::API::Item
11
+ include GemLogger::LoggerSupport
12
+ include MonitorMixin
13
+
14
+ def initialize()
15
+ super('reset_all')
16
+ self.title = "Reset Defaults"
17
+ end
18
+
19
+ def metadata
20
+ md = super
21
+
22
+ md.description = "All runtime config has now been reset to defaults, hit back to return"
23
+
24
+ md.item_detail_callback = Proc.new do
25
+ logger.info("Resetting defaults")
26
+ Config.instance.known_config.each do |key, spec|
27
+ Config.instance.set(key, spec[:default_value])
28
+ end
29
+ parent.children.clear
30
+ end
31
+
32
+ md
33
+ end
34
+
35
+ end
36
+
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,38 @@
1
+ require 'active_support/core_ext/string/inflections'
2
+ require 'listen'
3
+
4
+ module TivoHMO
5
+ module Adapters
6
+ module Settings
7
+
8
+ # An Item for toggling boolean bvalue
9
+ class SetValueItem
10
+ include TivoHMO::API::Item
11
+ include GemLogger::LoggerSupport
12
+ include MonitorMixin
13
+
14
+ def initialize(key, new_value)
15
+ super(key)
16
+ @new_value = new_value
17
+ self.title = "Set value to #{new_value}"
18
+ end
19
+
20
+ def metadata
21
+ md = super
22
+
23
+ md.description = "Value has now been set to #{@new_value}, hit back to return"
24
+
25
+ md.item_detail_callback = Proc.new do
26
+ logger.info("Setting #{identifier} to: #{@new_value}")
27
+ Config.instance.set(identifier, @new_value)
28
+ parent.children.clear
29
+ end
30
+
31
+ md
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,23 @@
1
+ module TivoHMO
2
+ module Adapters
3
+ module Settings
4
+
5
+
6
+ # Dummy transcoder
7
+ class Transcoder
8
+ include TivoHMO::API::Transcoder
9
+ include GemLogger::LoggerSupport
10
+
11
+ def transcode(writeable_io, format="video/x-tivo-mpeg")
12
+ nil
13
+ end
14
+
15
+ def transcoder_options(format="video/x-tivo-mpeg")
16
+ {}
17
+ end
18
+
19
+ end
20
+
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,7 @@
1
+ require_relative 'settings/transcoder'
2
+ require_relative 'settings/metadata'
3
+ require_relative 'settings/reset_defaults_item'
4
+ require_relative 'settings/set_value_item'
5
+ require_relative 'settings/display_item'
6
+ require_relative 'settings/key_container'
7
+ require_relative 'settings/application'
@@ -50,6 +50,7 @@ module TivoHMO
50
50
  opts = select_audio_bitrate(opts)
51
51
  opts = select_audio_sample_rate(opts)
52
52
  opts = select_container(opts)
53
+ opts = select_subtitle(opts)
53
54
 
54
55
  custom = opts.delete(:custom)
55
56
  opts[:custom] = custom.join(" ") if custom
@@ -61,7 +62,7 @@ module TivoHMO
61
62
  protected
62
63
 
63
64
  def movie
64
- @movie ||= FFMPEG::Movie.new(source_filename)
65
+ @movie ||= FFMPEG::Movie.new(item.file)
65
66
  end
66
67
 
67
68
  def video_info
@@ -199,6 +200,43 @@ module TivoHMO
199
200
  opts
200
201
  end
201
202
 
203
+ def select_subtitle(opts)
204
+
205
+ st = item.subtitle
206
+ if st
207
+ case st.type
208
+ when :file
209
+ code = st.language_code
210
+ file = st.location
211
+
212
+ # TODO: This is a little hacky
213
+ # we have a leaky abstraction here but the file globbing is a bad
214
+ # performance hit to UI when generating Containers on first view
215
+ file_glob = file + ".*.srt"
216
+ sub_file = Dir[file_glob].find do |f|
217
+ file_code = f.split('.')[-2].downcase
218
+ file_code == code || file_code.starts_with?(code) || code.starts_with?(file_code)
219
+ end
220
+
221
+ if sub_file
222
+ logger.info "Using subtitles present at: #{sub_file}"
223
+ opts[:custom] << "-vf subtitles=\"#{sub_file}\""
224
+ else
225
+ logger.error "Subtitle doesn't exist at: #{file_glob}"
226
+ end
227
+ when :embedded
228
+ file = item.file
229
+ idx = st.location
230
+ logger.info "Using embedded subtitles [#{idx}] present at: #{file}"
231
+ opts[:custom] << "-vf subtitles=\"#{file}\":si=#{idx}"
232
+ else
233
+ logger.error "Unknown subtitle type: #{st.type}"
234
+ end
235
+ end
236
+
237
+ opts
238
+ end
239
+
202
240
  def run_transcode(output_filename, format)
203
241
 
204
242
  logger.info "Movie Info: " +
@@ -10,12 +10,13 @@ module TivoHMO
10
10
  include Node
11
11
  include GemLogger::LoggerSupport
12
12
 
13
- attr_accessor :uuid
13
+ attr_accessor :uuid, :presorted
14
14
 
15
15
 
16
16
  def initialize(identifier)
17
17
  super(identifier)
18
18
  self.uuid = SecureRandom.uuid
19
+ self.presorted = false
19
20
 
20
21
  self.content_type = "x-tivo-container/tivo-videos"
21
22
  self.source_format = "x-tivo-container/folder"
@@ -25,6 +26,9 @@ module TivoHMO
25
26
  self.children = []
26
27
  end
27
28
 
29
+ def child_count
30
+ children.size
31
+ end
28
32
  end
29
33
 
30
34
  end
@@ -8,6 +8,8 @@ module TivoHMO
8
8
  include Node
9
9
  include GemLogger::LoggerSupport
10
10
 
11
+ attr_accessor :file, :subtitle
12
+
11
13
  def initialize(identifier)
12
14
  super(identifier)
13
15
  self.content_type = "video/x-tivo-mpeg"
@@ -0,0 +1,13 @@
1
+ module TivoHMO
2
+ module API
3
+
4
+ # Represents a subtitle
5
+ class Subtitle
6
+ include GemLogger::LoggerSupport
7
+
8
+ attr_accessor :language, :language_code, :format, :type, :location
9
+
10
+ end
11
+
12
+ end
13
+ end
@@ -17,12 +17,10 @@ module TivoHMO
17
17
  AUDIO_CODECS = %w[ac3 liba52 mp2]
18
18
  AUDIO_SAMPLE_RATES = %w[44100 48000]
19
19
 
20
- attr_accessor :item,
21
- :source_filename
20
+ attr_accessor :item
22
21
 
23
22
  def initialize(item)
24
23
  self.item = item
25
- self.source_filename = item.identifier
26
24
  end
27
25
 
28
26
  def transcode(writeable_io, format)
data/lib/tivohmo/api.rb CHANGED
@@ -1,5 +1,6 @@
1
1
  require 'tivohmo/api/node'
2
2
  require 'tivohmo/api/item'
3
+ require 'tivohmo/api/subtitle'
3
4
  require 'tivohmo/api/container'
4
5
  require 'tivohmo/api/transcoder'
5
6
  require 'tivohmo/api/metadata'
@@ -11,8 +11,6 @@ module TivoHMO
11
11
  @interval = interval
12
12
  @limit = limit
13
13
  @uid = SecureRandom.uuid
14
- @socket = UDPSocket.new(Socket::AF_INET)
15
- @socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
16
14
  @services = ['TiVoMediaServer:%s/http' % service_port]
17
15
  @running = false
18
16
  end
@@ -66,7 +64,9 @@ module TivoHMO
66
64
  bcast_port = 2190
67
65
  packet = beacon_data('broadcast')
68
66
  logger.debug "Sending beacon packet: #{packet}"
69
- @socket.send(packet, 0, bcast_ip, bcast_port)
67
+ socket = UDPSocket.new(Socket::AF_INET)
68
+ socket.setsockopt(Socket::SOL_SOCKET, Socket::SO_BROADCAST, true)
69
+ socket.send(packet, 0, bcast_ip, bcast_port)
70
70
  end
71
71
 
72
72
  end
data/lib/tivohmo/cli.rb CHANGED
@@ -22,18 +22,26 @@ module TivoHMO
22
22
 
23
23
  e.g.
24
24
 
25
- tivohmo -a TivoHMO::Adapters::Filesystem::Application -i ~/Video/Movies \\
26
- -a TivoHMO::Adapters::Filesystem::Application -i ~/Video/TV
25
+ tivohmo -t Movies
26
+ -a TivoHMO::Adapters::Filesystem::Application \\
27
+ -i ~/Video/Movies \\
28
+ -t "TV Shows" \\
29
+ -a TivoHMO::Adapters::Filesystem::Application \\
30
+ -i ~/Video/TV
27
31
 
28
32
  to run two top level filesystem video serving apps for different dirs, or
29
33
 
30
- tivohmo -a TivoHMO::Adapters::Filesystem::Application -t Vids -i ~/Video
34
+ tivohmo -t Vids \\
35
+ -a TivoHMO::Adapters::Filesystem::Application \\
36
+ -i ~/Video
31
37
 
32
- to run the single filesystem app with a custom title, or
38
+ to run the single filesystem app, or
33
39
 
34
- tivohmo -a TivoHMO::Adapters::Plex::Application -t PlexVideo -i localhost
40
+ tivohmo -t PlexVideo \\
41
+ -a TivoHMO::Adapters::Plex::Application \\
42
+ -i localhost
35
43
 
36
- to run the single plex app with a custom title
44
+ to run the single plex app
37
45
  DESC
38
46
  desc.split("\n").collect(&:strip).join("\n")
39
47
  end
@@ -62,6 +70,13 @@ module TivoHMO
62
70
  option ["-f", "--configuration"],
63
71
  "FILE", "load configuration from given filename\n"
64
72
 
73
+ option ["-s", "--settings"],
74
+ "FILE", "a writable file for storing runtime settings\n"
75
+
76
+ option ["-t", "--title"],
77
+ "TITLE", "setup an application for the given title\n",
78
+ multivalued: true
79
+
65
80
  option ["-a", "--application"],
66
81
  "CLASSNAME", "use the given application class\n",
67
82
  multivalued: true
@@ -71,10 +86,6 @@ module TivoHMO
71
86
  "a string that has meaning to the application\n",
72
87
  multivalued: true
73
88
 
74
- option ["-t", "--title"],
75
- "TITLE", "use the given title for the application\n",
76
- multivalued: true
77
-
78
89
  option ["-T", "--transcoder"],
79
90
  "CLASSNAME", "override the application's transcoder class\n",
80
91
  multivalued: true
@@ -86,59 +97,37 @@ module TivoHMO
86
97
  option ["-b", "--beacon"],
87
98
  "LIMIT:INTERVAL", "configure beacon limit and/or interval\n"
88
99
 
100
+ option ["-l", "--install"],
101
+ :flag, "install tivohmo into your system\n"
102
+
89
103
  def execute
104
+
90
105
  if version?
91
106
  puts "TivoHMO Version #{TivoHMO::VERSION}"
92
107
  return
93
108
  end
94
109
 
95
- setup_logging
96
-
97
- logger.info "TivoHMO #{TivoHMO::VERSION} starting up"
110
+ install_tivohmo if install?
98
111
 
99
- if configuration
100
- config = YAML.load_file(configuration)
112
+ c = File.expand_path(configuration) if configuration
113
+ s = File.expand_path(settings) if settings
114
+ TivoHMO::Config.instance.setup(c, s)
101
115
 
102
- # allow cli option to override config file
103
- set_if_default(:port, config['port'].to_i)
104
- end
116
+ setup_logging
105
117
 
106
- signal_usage_error "at least one application is required" unless application_list.present?
107
- signal_usage_error "an initializer is needed for each application" unless
108
- application_list.size == identifier_list.size
118
+ logger.info "TivoHMO #{TivoHMO::VERSION} starting up"
109
119
 
110
- (application_list + transcoder_list + metadata_list).each do |c|
111
- if c && c.starts_with?('TivoHMO::Adapters::')
112
- path = c.downcase.split('::')[0..-2].join('/')
113
- require path
114
- end
115
- end
120
+ # allow cli option to override config file
121
+ set_if_default(:port, TivoHMO::Config.instance.get(:port).try(:to_i))
116
122
 
117
123
  server = TivoHMO::API::Server.new
118
-
119
- apps_with_config = application_list.zip(identifier_list,
120
- title_list,
121
- transcoder_list,
122
- metadata_list)
123
-
124
- apps_with_config.each do |app_classname, identifier, title, transcoder, metadata|
125
- app_class = app_classname.constantize
126
- app = app_class.new(identifier)
127
-
128
- if title
129
- app.title = title
130
- else
131
- app.title = "#{app.title} on #{server.title}"
132
- end
133
-
134
- app.transcoder_class = transcoder.constantize if transcoder
135
- app.metadata_class = metadata.constantize if metadata
136
- server.add_child(app)
137
- end
124
+ apps = setup_applications
125
+ apps.each {|app| server.add_child(app) }
138
126
 
139
127
  preload_containers(server) if preload?
140
128
 
141
129
  opts = {}
130
+ set_if_default(:beacon, TivoHMO::Config.instance.get(:beacon))
142
131
  if beacon.present?
143
132
  limit, interval = beacon.split(":")
144
133
  opts[:limit] = limit.to_i if limit.present?
@@ -154,8 +143,10 @@ module TivoHMO
154
143
  private
155
144
 
156
145
  def setup_logging
146
+ set_if_default(:debug, TivoHMO::Config.instance.get(:debug))
157
147
  Logging.logger.root.level = :debug if debug?
158
148
 
149
+ set_if_default(:logfile, TivoHMO::Config.instance.get(:logfile))
159
150
  if logfile.present?
160
151
  appender = Logging.appenders.rolling_file(
161
152
  logfile,
@@ -176,7 +167,64 @@ module TivoHMO
176
167
  end
177
168
 
178
169
  def set_if_default(attr, new_value)
179
- self.send("#{attr}=", new_value) if self.send(attr) == self.send("default_#{attr}")
170
+ opt = self.class.find_option("--#{attr}")
171
+ raise "Unknonwn cli attribute" unless opt
172
+ self.send("#{attr}=", new_value) if new_value && self.send(opt.read_method) == opt.default_value
173
+ end
174
+
175
+ def load_adapter(clazz)
176
+ if clazz && clazz.starts_with?('TivoHMO::Adapters::')
177
+ path = clazz.downcase.split('::')[0..-2].join('/')
178
+ require path
179
+ end
180
+ end
181
+
182
+ def setup_applications
183
+ app_specs = TivoHMO::Config.instance.get(:applications) || {}
184
+
185
+ title_list.each_with_index do |title, i|
186
+ app = app_specs[title]
187
+ app_was_in_config = app.present?
188
+ app = {} unless app_was_in_config
189
+
190
+ app[:application] = application_list[i] if application_list[i]
191
+ signal_usage_error "an application class is needed for each application" unless app[:application]
192
+
193
+ app[:identifier] = identifier_list[i] if identifier_list[i]
194
+ signal_usage_error "an initializer is needed for each application" unless app[:identifier]
195
+
196
+
197
+ app[:transcoder] = transcoder_list[i] if transcoder_list[i]
198
+ app[:metadata] = metadata_list[i] if metadata_list[i]
199
+
200
+ logger.debug "Merged app: #{title} - #{app.inspect}"
201
+ app_specs[title] = app unless app_was_in_config
202
+ end
203
+
204
+ signal_usage_error "at least one application is required" unless app_specs.present?
205
+
206
+ apps = app_specs.collect do |title, app_spec|
207
+ logger.debug "Adding app: #{title} - #{app_spec.inspect}"
208
+ load_adapter(app_spec[:application])
209
+
210
+ app_class = app_spec[:application].constantize
211
+ app = app_class.new(app_spec[:identifier])
212
+ app.title = title
213
+
214
+ if app_spec[:transcoder]
215
+ load_adapter(app_spec[:transcoder])
216
+ app.transcoder_class = app_spec[:transcoder].constantize
217
+ end
218
+
219
+ if app_spec[:metadata]
220
+ load_adapter(app_spec[:metadata])
221
+ app.metadata_class = app_spec[:metadata].constantize
222
+ end
223
+
224
+ app
225
+ end
226
+
227
+ apps
180
228
  end
181
229
 
182
230
  def preload_containers(server)
@@ -204,6 +252,85 @@ module TivoHMO
204
252
  end
205
253
  end
206
254
 
255
+ # Opens the file for writing by root
256
+ def sudo_open(path, mode, perms=0755, &block)
257
+ open("|sudo tee #{path} > /dev/null", perms, &block)
258
+ end
259
+
260
+ def get_binding(config)
261
+ binding
262
+ end
263
+
264
+ def install_file(src, dst, config={}, sudo=false)
265
+ src = File.expand_path(src)
266
+ dst = File.expand_path(dst)
267
+
268
+ block = Proc.new do |f|
269
+ template = ERB.new(File.read(src), nil, "-")
270
+ result = template.result(get_binding(config))
271
+ f.write(result)
272
+ end
273
+
274
+ puts "Installing #{dst}"
275
+
276
+ if sudo
277
+ sudo_open(dst, "w", &block)
278
+ else
279
+ open(dst, "w", &block)
280
+ end
281
+ end
282
+
283
+ def install_tivohmo
284
+ puts "Installing TivoHMO"
285
+
286
+ contrib_path = File.expand_path("../../../contrib", __FILE__)
287
+
288
+ case RUBY_PLATFORM
289
+ when /darwin/
290
+
291
+ config = {
292
+ daemon_config: "~/Library/LaunchAgents/tivohmo.plist",
293
+ configuration: "~/Library/Preferences/tivohmo.yml",
294
+ logfile: "~/Library/Logs/tivohmo.log",
295
+ settings: "~/Library/Preferences/tivohmo.runtime.yml"
296
+ }
297
+
298
+ install_file("#{contrib_path}/tivohmo.yml", config[:configuration], config)
299
+ install_file("#{contrib_path}/tivohmo.plist", config[:daemon_config], config)
300
+
301
+ puts "TivoHMO installed"
302
+ puts "To start tivohmo, execute the command:"
303
+ puts "\tlaunchctl load #{config[:daemon_config]}"
304
+ puts "To stop tivohmo, execute the command:"
305
+ puts "\tlaunchctl unload #{config[:daemon_config]}"
306
+
307
+ when /linux/
308
+
309
+ config = {
310
+ daemon_config: "/etc/init/tivohmo.conf",
311
+ configuration: "/etc/tivohmo.yml",
312
+ logfile: "/var/log/tivohmo.log",
313
+ settings: "/var/run/tivohmo.runtime.yml"
314
+ }
315
+
316
+ puts "Sudo password needed to install files"
317
+ install_file("#{contrib_path}/tivohmo.yml", config[:configuration], config, true)
318
+ install_file("#{contrib_path}/tivohmo.conf", config[:daemon_config], config, true)
319
+
320
+ puts "TivoHMO installed"
321
+ puts "To start tivohmo, execute the command:"
322
+ puts "\tsudo service tivohmo start"
323
+ puts "To stop tivohmo, execute the command:"
324
+ puts "\tsudo service tivohmo stop"
325
+
326
+ else
327
+ $stderr.puts "Unsupported OS: #{RUBY_PLATFORM}"
328
+ exit(1)
329
+ end
330
+
331
+ exit(0)
332
+ end
333
+
207
334
  end
208
335
 
209
336
  end