sphinx_tv 0.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,18 @@
1
+ .idea
2
+ *.gem
3
+ *.rbc
4
+ .bundle
5
+ .config
6
+ .yardoc
7
+ Gemfile.lock
8
+ InstalledFiles
9
+ _yardoc
10
+ coverage
11
+ doc/
12
+ lib/bundler/man
13
+ pkg
14
+ rdoc
15
+ spec/reports
16
+ test/tmp
17
+ test/version_tmp
18
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in sphinx_tv.gemspec
4
+ gemspec
@@ -0,0 +1,20 @@
1
+ Copyright 2011 David Monagle
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,54 @@
1
+ = SphinxTV
2
+
3
+ The purpose of this gem is to provide an installer/configurator to ease the setup of a Mac Mini as a media server.
4
+ MythTV is the primary focus for this installer (MythTV + Big Cat in the OS makes for the Sphinx name.) Included in the
5
+ modules is Shepherd, an Australian TV grabber which integrates well with MythTV and allows for a great free TV guide
6
+ on the MythTV side of things.
7
+
8
+ At this point SphinxTV is regarded to be in extreme Alpha. It is on Github in the interests of the people that are
9
+ alpha testing it for me. I intend to continue to develop and expand on this as time allows (this is a hobby project
10
+ for me at this point.)
11
+
12
+ The goal is to have a website up and running in the coming months that will help detail the setup of a very functional
13
+ home entertainment system using Mac Mini hardware.
14
+
15
+ == Gem Installation ==
16
+
17
+ Easy:
18
+
19
+ gem install sphinx_tv
20
+
21
+ == Using SphinxTV ==
22
+
23
+ === Setup ===
24
+
25
+ sphinx_tv setup
26
+
27
+ Turn on the modules that you wish to install and configure.
28
+
29
+ === Download ===
30
+
31
+ sphinx_tv download
32
+
33
+ Option step but will just do all the necessary downloading for the modules that have been configured in the setup stage.
34
+
35
+ === Installation ===
36
+
37
+ sphinx_tv install
38
+
39
+ This runs through the install of each module. If the download wasn't run seperately, then the downloads will be done
40
+ on-demand at ths start of the module install.
41
+
42
+ === Configuration ===
43
+
44
+ sphinx_tv configure
45
+
46
+ Post installation configuration.
47
+
48
+ == Modules ==
49
+
50
+ === MySQL ===
51
+
52
+ === MythTV ===
53
+
54
+ === Shepherd ===
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'sphinx_tv'
4
+
5
+ s = SphinxTv.new
6
+ s.run
@@ -0,0 +1,155 @@
1
+ class MySQL < SphinxModule
2
+ def check(quiet = false)
3
+ result = true
4
+ unless mysql_installed?
5
+ result = false
6
+ puts "The MySQL server doesn't appear to be installed.'".red unless quiet
7
+ end
8
+ unless File.exists? "/etc/paths.d/mysql"
9
+ result = false
10
+ puts "MySQL paths.d file is not configured.".red unless quiet
11
+ end
12
+ unless File.exists? "/usr/lib/libmysqlclient.18.dylib"
13
+ result = false
14
+ puts "MySQL does not have a symbolic link set up for the libmysqlclient dynlib.".red unless quiet
15
+ end
16
+ unless File.exists? "/var/mysql/mysql.sock"
17
+ result = false
18
+ puts "MySQL socket does not exist.".red unless quiet
19
+ end
20
+
21
+ puts "MySQL is OK.".green if result
22
+ result
23
+ end
24
+
25
+ def configure
26
+ unless mysql_installed?
27
+ puts "\nOnce you have MySQL installed, you may return to this menu to setup root access\n"
28
+ end
29
+ exit = false
30
+ until exit do
31
+ choose do |menu|
32
+ menu.header = "\nMySQL Configuration".cyan
33
+ menu.prompt = "Select an option: "
34
+ if mysql_installed?
35
+ menu.choice("Setup root access") { set_mysql_root_access }
36
+ end
37
+ menu.choice("Done") {
38
+ exit = true
39
+ }
40
+ end
41
+ end
42
+ end
43
+
44
+ def download
45
+ url = "http://cdn.mysql.com/Downloads/MySQL-5.5/mysql-5.5.25a-osx10.6-x86_64.dmg"
46
+ puts "Downloading MySQL".cyan
47
+ puts url
48
+ result = Download.url(url, SphinxTv::download_path("mysql.dmg"))
49
+ end
50
+
51
+ def install
52
+ puts "Installing MySQL".cyan
53
+ unless check(true)
54
+ download
55
+ Download.mount(SphinxTv::download_path("mysql.dmg"), "mysql*") do |volume|
56
+ self.install_mysql_server volume
57
+ self.install_mysql_prefpane volume
58
+ self.install_mysql_startup volume
59
+ end
60
+ else
61
+ puts "MySQL is installed.".green
62
+ end
63
+ end
64
+
65
+ protected
66
+
67
+ def mysql_installed?
68
+ File.exists?("/usr/local/mysql/bin/mysql")
69
+ end
70
+
71
+ def install_mysql_server volume
72
+ puts "Installing MySQL Server".cyan
73
+ puts "This will configure the MySQL server. A graphical installer should open now."
74
+ files = Dir.glob File.join(volume, "mysql*")
75
+ if (files.size == 0)
76
+ puts "Could not find installer file in volume #{volume}".red
77
+ return
78
+ end
79
+ %x[open #{files[0]}]
80
+ ask "\nPress enter when the install is complete!".magenta
81
+
82
+ # Create the paths.d file
83
+ if mysql_installed?
84
+ puts "Creating global path file /etc/paths.d/mysql".cyan
85
+ %x[sudo #{SphinxTv::SUDO_PROMPT} bash -c 'echo "/usr/local/mysql/bin" > /etc/paths.d/mysql']
86
+ end
87
+
88
+ # Create the symbolic link to the client library
89
+ unless File.exists? "/usr/lib/libmysqlclient.18.dylib"
90
+ puts "Linking dynamic libraries".cyan
91
+ %x[sudo #{SphinxTv::SUDO_PROMPT} ln -s /usr/local/mysql/lib/libmysqlclient.18.dylib /usr/lib]
92
+ end
93
+
94
+ # Create the mysql socket
95
+ unless File.exists? "/tmp/mysql.sock"
96
+ puts "Creating link to mysql socket".cyan
97
+ %x[sudo #{SphinxTv::SUDO_PROMPT} mkdir -p /var/mysql]
98
+ %x[sudo #{SphinxTv::SUDO_PROMPT} ln -s /tmp/mysql.sock /var/mysql/]
99
+ end
100
+ end
101
+
102
+ def install_mysql_prefpane volume
103
+ puts "Installing MySQL Prefpane\n".cyan
104
+ puts "Select the option to install for all users of the computer"
105
+ puts "On the Prefpane, tick the option to start the MySQL Server on Startup"
106
+ puts "Click the button to start the MySQL Server"
107
+ files = Dir.glob File.join(volume, "MySQL.prefpane")
108
+ if (files.size == 0)
109
+ puts "Could not find Prefpane file in volume #{volume}".red
110
+ return
111
+ end
112
+ %x[open #{files[0]}]
113
+ ask "\nPress enter when the install is complete!".magenta
114
+ end
115
+
116
+ def install_mysql_startup volume
117
+ puts "Installing MySQL Startup".cyan
118
+ files = Dir.glob File.join(volume, "MySQLStartup*")
119
+ if (files.size == 0)
120
+ puts "Could not find startup installer file in volume #{volume}".red
121
+ return
122
+ end
123
+ %x[open #{files[0]}]
124
+ ask "\nPress enter when the install is complete!".magenta
125
+ end
126
+
127
+ def set_mysql_root_access
128
+ puts "This will allow you to set the root password for your database.".yellow
129
+ puts "It will give you a more secure SQL server on your network but if you lose".yellow
130
+ puts "the root password at any point, it will cause you pain.".yellow
131
+ response = ask("Set up root privileges? ") { |q| q.default = "No" }
132
+ return if !/Y/i.match(response)
133
+ old_root_pass = ask("Enter the current root password: ") { |q| q.echo = "*" }
134
+ root_pass = ask("Enter the new root password: ") { |q| q.echo = "*" }
135
+ root_pass_confirm = ask("Re-enter the new root password: ") { |q| q.echo = "*" }
136
+ if root_pass != root_pass_confirm
137
+ puts "Passwords do not match!".red
138
+ return
139
+ end
140
+ hostname = %x[hostname].strip
141
+ hostname_short = /^[^\.]+/.match(hostname)
142
+ mysql_commands = Array.new.tap do |c|
143
+ c << "DROP USER ''@'localhost';" if old_root_pass.empty?
144
+ c << "SET PASSWORD FOR 'root'@'localhost' = PASSWORD('#{root_pass}');"
145
+ c << "SET PASSWORD FOR 'root'@'127.0.0.1' = PASSWORD('#{root_pass}');"
146
+ c << "SET PASSWORD FOR 'root'@'#{hostname}' = PASSWORD('#{root_pass}');"
147
+ c << "SET PASSWORD FOR 'root'@'#{hostname_short}' = PASSWORD('#{root_pass}');"
148
+ end
149
+ password_param = old_root_pass.empty? ? "" : " --password=#{old_root_pass}"
150
+ mysql_commands.each do |sql|
151
+ puts sql
152
+ %x[/usr/local/mysql/bin/mysql -u root#{password_param} -e "#{sql}"]
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,198 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+
4
+ class MythTv < SphinxModule
5
+ def initialize
6
+ @config = {
7
+ :version => SphinxTv::VERSION
8
+ }
9
+ load_configuration
10
+ end
11
+
12
+ def check(quiet = false)
13
+ result = true
14
+ result = false unless mythtv_backend_installed?(quiet)
15
+ result = false unless mythtv_frontend_installed?(quiet)
16
+ unless @config[:database_password]
17
+ result = false
18
+ puts "Assuming no database is set up as the MythTV database password is not set.'".red unless quiet
19
+ end
20
+ unless File.exists? "/usr/local/bin/mythfilldatabase"
21
+ result = false
22
+ puts "No symbolic link to mythfilldatabase found".red unless quiet
23
+ end
24
+
25
+ puts "MythTV is OK.".green if result
26
+ result
27
+ end
28
+
29
+ def configure
30
+ exit = false
31
+ until exit do
32
+ choose do |menu|
33
+ menu.header = "\nMythTV Configuration".cyan
34
+ menu.prompt = "Select an option: "
35
+ if mythtv_backend_installed?
36
+ menu.choice("Setup database") { setup_database }
37
+ end
38
+ menu.choice("Create Storage Directories") { create_storage_directories }
39
+ if mythtv_backend_running?
40
+ menu.choice("MythTV Backend Control " + "Loaded".green) { mythtv_backend_control(false) }
41
+ else
42
+ menu.choice("MythTV Backend Control " + "Unloaded".red) { mythtv_backend_control(true) }
43
+ end
44
+ menu.choice("Done") {
45
+ exit = true
46
+ }
47
+ end
48
+ end
49
+ end
50
+
51
+ def download
52
+ doc = Nokogiri::HTML(open('http://sourceforge.net/projects/mythtvformacosx/files/'))
53
+
54
+ doc.css('tr.file a').each do |link|
55
+ if /\.dmg\/download$/.match(link[:href])
56
+ filename = nil
57
+ if /MythBack.*10\.7/.match(link[:href])
58
+ filename = "MythBackend.dmg"
59
+ elsif /MythFront.*10\.7/.match(link[:href])
60
+ filename = "MythFrontend.dmg"
61
+ end
62
+ if filename
63
+ puts "Downloading #{filename}".cyan
64
+ puts link[:href]
65
+ Download::url(link[:href], SphinxTv::download_path(filename))
66
+ end
67
+ end
68
+ end
69
+ end
70
+
71
+ def install
72
+ puts "Installing MythTV".cyan
73
+ unless check(true)
74
+ download
75
+ install_mythtv_backend unless mythtv_backend_installed?
76
+ install_mythtv_frontend unless mythtv_frontend_installed?
77
+
78
+ unless File.exists? "/usr/local/bin/mythfilldatabase"
79
+ puts "Creating link to mythfilldatabase".cyan
80
+ %x[sudo #{SphinxTv::SUDO_PROMPT} mkdir -p /usr/local/bin]
81
+ %x[sudo #{SphinxTv::SUDO_PROMPT} ln -s /Applications/MythBackend.app/Contents/MacOS/mythfilldatabase /usr/local/bin/mythfilldatabase]
82
+ end
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def mythtv_backend_running?
89
+ result = %x[ps ax | grep MythBackend | grep -v grep]
90
+ return false if result.strip.empty?
91
+ true
92
+ end
93
+
94
+ def mythtv_backend_control(load)
95
+ %x[sudo #{SphinxTv::SUDO_PROMPT} launchctl #{load ? "load" : "unload"} -w /Library/LaunchDaemons/MythBackend.plist]
96
+ end
97
+
98
+ def mythtv_frontend_installed?(quiet = true)
99
+ result = true
100
+ unless File.exists? "/Applications/MythFrontend.app"
101
+ result = false
102
+ puts "MythFrontend application not installed.".red unless quiet
103
+ end
104
+ result
105
+ end
106
+
107
+ def mythtv_backend_installed?(quiet = true)
108
+ result = true
109
+ unless File.exists? "/Applications/MythBackend.app"
110
+ result = false
111
+ puts "MythBackend application not installed.".red unless quiet
112
+ end
113
+ unless File.exists? "/Library/LaunchDaemons/MythBackend.plist"
114
+ result = false
115
+ puts "MythBackend LaunchDaemon does not exist.".red unless quiet
116
+ end
117
+ result
118
+ end
119
+
120
+ def install_mythtv_backend
121
+ Download.mount(SphinxTv::download_path("MythBackend.dmg"), "MythBackend*") do |volume|
122
+ puts "Installing MythTVBackend".cyan
123
+ mythtv_files = ["MythBackend.app", "MythTv-Setup.app"]
124
+ mythtv_files.each do |file|
125
+ %x[cp -Rf #{volume}/#{file} /Applications 2>&1]
126
+ end
127
+ @username = Etc.getlogin
128
+ unless File.exists? "/var/log/mythtv"
129
+ puts "Creating MythTV log directory...".cyan
130
+ %x[sudo #{SphinxTv::SUDO_PROMPT} mkdir -p /var/log/mythtv]
131
+ %x[sudo #{SphinxTv::SUDO_PROMPT} chmod a+rwx /var/log/mythtv]
132
+ end
133
+
134
+ puts "Creating MythBackend LaunchDaemon file...".cyan
135
+ SphinxTv::copy_template(SphinxTv::resources_path("MythTv/MythBackend.plist.erb"), SphinxTv::cache_path("MythBackend.plist"), binding)
136
+ %x[sudo #{SphinxTv::SUDO_PROMPT} cp #{SphinxTv::cache_path("MythBackend.plist")} /Library/LaunchDaemons]
137
+ end
138
+ end
139
+
140
+ def install_mythtv_frontend
141
+ Download.mount(SphinxTv::download_path("MythFrontend.dmg"), "MythFrontend*") do |volume|
142
+ puts "Installing MythTVFrontend".cyan
143
+ mythtv_files = ["MythFrontend.app", "MythWelcome.app", "MythAVTest.app"]
144
+ mythtv_files.each do |file|
145
+ %x[cp -Rf #{volume}/#{file} /Applications 2>&1]
146
+ end
147
+ end
148
+ end
149
+
150
+ def setup_database
151
+ puts "This will create the MythTV database if it doesn't already exist and set appropriate privileges."
152
+ root_pass = ask("Enter your mysql root password: ") { |q| q.echo = "*" }
153
+ @config[:database_password] ||= SphinxTv::get_password_with_confirmation("Enter a password for the mythtv MySQL user: ")
154
+ return if @config[:database_password].nil?
155
+ save_configuration
156
+ hostname = %x[hostname].strip
157
+ hostname_short = /^[^\.]+/.match(hostname)
158
+ mysql_commands = Array.new.tap do |c|
159
+ c << "CREATE DATABASE IF NOT EXISTS mythconverg;"
160
+ c << "GRANT ALL ON mythconverg.* TO mythtv@localhost IDENTIFIED BY '#{@config[:database_password]}';"
161
+ c << "GRANT ALL ON mythconverg.* TO mythtv@#{hostname} IDENTIFIED BY '#{@config[:database_password]}';"
162
+ c << "FLUSH PRIVILEGES;"
163
+ c << "GRANT CREATE TEMPORARY TABLES ON mythconverg.* TO mythtv@localhost IDENTIFIED BY '#{@config[:database_password]}';"
164
+ c << "GRANT CREATE TEMPORARY TABLES ON mythconverg.* TO mythtv@#{hostname} IDENTIFIED BY '#{@config[:database_password]}';"
165
+ c << "FLUSH PRIVILEGES;"
166
+ c << "ALTER DATABASE mythconverg DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;"
167
+ end
168
+ password_param = root_pass.empty? ? "" : " --password=#{root_pass}"
169
+ mysql_commands.each do |sql|
170
+ puts sql
171
+ %x[/usr/local/mysql/bin/mysql -u root#{password_param} -e "#{sql}"]
172
+ end
173
+
174
+ puts "Creating mysql.txt in /etc/mythtv".cyan
175
+ %x[sudo #{SphinxTv::SUDO_PROMPT} mkdir -p /etc/mythtv]
176
+ %x[sudo #{SphinxTv::SUDO_PROMPT} chmod a+rwx /etc/mythtv]
177
+ outfile = File.open("/etc/mythtv/mysql.txt", "w")
178
+ outfile.puts("DBHostName=#{hostname}")
179
+ outfile.puts("DBUserName=mythtv")
180
+ outfile.puts("DBPassword=#{@config[:database_password]}")
181
+ outfile.puts("DBName=mythconverg")
182
+ outfile.puts("DBType=QMYSQL3")
183
+ outfile.close
184
+ %x[chmod o-w /etc/mythtv/mysql.txt]
185
+ end
186
+
187
+ def create_storage_directories
188
+ storage_root = ask("Enter the storage directory root: ") { |q| q.default = "/Volumes/MythTV" }
189
+ directories = ['Backups', 'Banners', 'Coverart', 'Fanart', 'LiveTV', 'Recordings', 'Screenshots', 'Trailers']
190
+ directories.each do |directory|
191
+ full_dir = File.join(storage_root, directory)
192
+ puts full_dir
193
+ %x[sudo #{SphinxTv::SUDO_PROMPT} mkdir -p #{full_dir}]
194
+ end
195
+ %x[sudo #{SphinxTv::SUDO_PROMPT} chown -Rf #{Etc.getlogin} #{storage_root}]
196
+ %x[sudo #{SphinxTv::SUDO_PROMPT} chmod -Rf ugo+rwx #{storage_root}]
197
+ end
198
+ end
@@ -0,0 +1,95 @@
1
+ class Shepherd < SphinxModule
2
+ def initialize
3
+ @config = {
4
+ :version => SphinxTv::VERSION
5
+ }
6
+ load_configuration
7
+ end
8
+
9
+ def check(quiet = false)
10
+ result = true
11
+
12
+ unless File.exists? "/usr/local/share/xmltv"
13
+ result = false
14
+ puts "XMLTV is not installed.".red unless quiet
15
+ end
16
+
17
+ unless File.exists? File.join(Etc.getpwuid.dir, ".shepherd")
18
+ result = false
19
+ puts "Shepherd is not installed.".red unless quiet
20
+ end
21
+
22
+ puts "Shepherd is OK.".green if result
23
+ result
24
+ end
25
+
26
+ def download
27
+ doc = Nokogiri::HTML(open('http://sourceforge.net/projects/xmltv/files/xmltv/0.5.63/'))
28
+
29
+ url = "http://www.whuffy.com/shepherd/shepherd"
30
+ puts "Downloading Shepherd".cyan
31
+ puts url
32
+ result = Download.url(url, SphinxTv::download_path("shepherd"))
33
+
34
+ doc.css('tr.file a').each do |link|
35
+ if /\.tar\.bz2\/download$/.match(link[:href])
36
+ filename = nil
37
+ filename = "xmltv.tar.bz2" if /xmltv/.match(link[:href])
38
+ if filename
39
+ puts "Downloading #{filename}".cyan
40
+ puts link[:href]
41
+ Download::url(link[:href], SphinxTv::download_path(filename))
42
+ end
43
+ end
44
+ end
45
+ end
46
+
47
+ def install
48
+ unless check(true)
49
+ download
50
+ install_perl_prerequisites
51
+ install_xmltv unless File.exists? "/usr/local/share/xmltv"
52
+
53
+ unless File.exists? "/Library/Perl/5.12/Shepherd"
54
+ puts "Creating symlink for Shepherd/MythTV perl library...".cyan
55
+ home_dir = Etc.getpwuid.dir
56
+ %x[sudo #{SphinxTv::SUDO_PROMPT} ln -s #{home_dir}/.shepherd/references/Shepherd /Library/Perl/5.12]
57
+ end
58
+ puts "Installing Shepherd".cyan
59
+ system("perl #{SphinxTv::download_path("shepherd")} --configure")
60
+ end
61
+ end
62
+
63
+ def install_perl_prerequisites
64
+
65
+ Cpan::create_config_file
66
+
67
+ shepherd_mandatory_perl_modules = "YAML XML::Twig Algorithm::Diff Compress::Zlib Cwd Data::Dumper Date::Manip Getopt::Long \
68
+ List::Compare LWP::UserAgent POSIX Digest::SHA1"
69
+
70
+ shepherd_optional_perl_modules = "DateTime::Format::Strptime File::Basename File::Path HTML::Entities \
71
+ HTML::TokeParser HTML::TreeBuilder IO::File Storable Time::HiRes XML::DOM \
72
+ XML::DOM::NodeList XML::Simple Storable HTTP::Cookies File::Basename \
73
+ LWP::ConnCache Digest::MD5 Archive::Zip IO::String \
74
+ DateTime::Format::Strptime \
75
+ HTTP::Cache::Transparent Crypt::SSLeay DBD::mysql "
76
+
77
+ perl_modules = shepherd_mandatory_perl_modules.split(" ") + shepherd_optional_perl_modules.split(" ")
78
+ puts "Installing required perl modules...".cyan
79
+ Cpan::install(perl_modules)
80
+ end
81
+
82
+ def install_xmltv
83
+ puts "Extracting XMLTV...".cyan
84
+ %x[tar -jxf #{SphinxTv::download_path("xmltv.tar.bz2")} -C #{SphinxTv::cache_path}]
85
+ puts "Compiling XMLTV...".cyan
86
+ commands = Array.new.tap do |c|
87
+ c << "cd #{Sphinx.cache_path}/xmltv*"
88
+ c << "perl Makefile.PL"
89
+ c << "make"
90
+ c << "make test"
91
+ c << "sudo #{SphinxTv::SUDO_PROMPT} make install"
92
+ end
93
+ puts %x[#{commands.join(";")}]
94
+ end
95
+ end
@@ -0,0 +1,55 @@
1
+ class SphinxModule
2
+ def initialize
3
+ @config = {
4
+ :version => SphinxTv::VERSION
5
+ }
6
+ end
7
+
8
+ def load_configuration
9
+ config_file = File.join(SphinxTv::CONFIG_DIRECTORY, "#{self.class.to_s.downcase}.yml")
10
+ unless File.exists? config_file
11
+ return false
12
+ end
13
+ c = YAML::load_file config_file
14
+ @config.merge! c
15
+ return true
16
+ end
17
+
18
+ def save_configuration
19
+ def save_configuration
20
+ Dir.mkdir(SphinxTv::CONFIG_DIRECTORY) unless File.exists? SphinxTv::CONFIG_DIRECTORY
21
+ File.open(File.join(SphinxTv::CONFIG_DIRECTORY, "#{self.class.to_s.downcase}.yml"), "w") do |file|
22
+ file.write @config.to_yaml
23
+ end
24
+ end
25
+ end
26
+
27
+ def check(quiet = false)
28
+ return true
29
+ end
30
+
31
+ def setup
32
+ puts "#{self.class.to_s} does not have a setup menu.".red
33
+ return false
34
+ end
35
+
36
+ def configure
37
+ puts "#{self.class.to_s} does not have a configuration menu.".red
38
+ return false
39
+ end
40
+
41
+ def download
42
+ end
43
+
44
+ def install
45
+ puts "#{self.class.to_s} does not have an install function.".red
46
+ end
47
+
48
+ def uninstall
49
+ puts "#{self.class.to_s} does not have an uninstall function.".red
50
+ end
51
+
52
+ def update
53
+ puts "#{self.class.to_s} does not have an update function.".red
54
+ end
55
+ end
@@ -0,0 +1,233 @@
1
+ require "colorize"
2
+ require "etc"
3
+ require "yaml"
4
+ require 'optparse'
5
+ require "highline/import"
6
+ require "modules/sphinx_module"
7
+ require "sphinx_tv/download"
8
+ require "sphinx_tv/cpan"
9
+ require "erb"
10
+ require "version"
11
+
12
+ class SphinxTv
13
+ SUDO_PROMPT = "-p \"#{"Your administrator password is required to peform this step: ".yellow}\""
14
+ CONFIG_DIRECTORY = File.join(Etc.getpwuid.dir, ".sphinx_tv")
15
+ MODULES = ["MySQL", "MythTv", "Shepherd"]
16
+ MODULE_DEFAULTS = {
17
+ "MySQL" => {
18
+ :active => true,
19
+ :setup => false,
20
+ :configure => true,
21
+ },
22
+ "MythTv" => {
23
+ :active => true,
24
+ :setup => false,
25
+ :configure => true,
26
+ :depends => ["MySQL"],
27
+ },
28
+ "Shepherd" => {
29
+ :active => false,
30
+ :setup => false,
31
+ :configure => true,
32
+ :depends => ["MythTv"],
33
+ },
34
+ }
35
+
36
+ def initialize
37
+ @config = {
38
+ :version => VERSION,
39
+ :modules => MODULE_DEFAULTS
40
+ }
41
+ end
42
+
43
+ def self.root_directory
44
+ File.expand_path '../..', __FILE__
45
+ end
46
+
47
+ def self.resources_path(file = "")
48
+ File.join(root_directory, "resources", file)
49
+ end
50
+
51
+ def self.download_path(file = "")
52
+ download_directory = File.join(CONFIG_DIRECTORY, "downloads")
53
+ Dir.mkdir(download_directory) unless File.exists? download_directory
54
+ return File.join(download_directory, file)
55
+ end
56
+
57
+ def self.cache_path(file = "")
58
+ cache_directory = File.join(CONFIG_DIRECTORY, "cache")
59
+ Dir.mkdir(cache_directory) unless File.exists? cache_directory
60
+ return File.join(cache_directory, file)
61
+ end
62
+
63
+ def self.copy_template(source, dest, b)
64
+ t = ERB.new File.new(source).read, nil, "%"
65
+ outfile = File.open(dest, "w")
66
+ outfile.write(t.result(b || binding))
67
+ outfile.close
68
+ end
69
+
70
+ def self.get_password_with_confirmation(prompt = nil)
71
+ pass = ask(prompt || "Enter a password: ") { |q| q.echo = "*" }
72
+ pass_confirm = ask("Re-enter the password: ") { |q| q.echo = "*" }
73
+ if pass != pass_confirm
74
+ puts "Passwords do not match!".red
75
+ return nil
76
+ end
77
+ pass
78
+ end
79
+
80
+ def load_configuration
81
+ config_file = File.join(CONFIG_DIRECTORY, "sphinx.yml")
82
+ unless File.exists? config_file
83
+ puts "Configuration file does not exist. Using defaults.".yellow
84
+ return false
85
+ end
86
+ c = YAML::load_file config_file
87
+ @config.merge! c
88
+ return true
89
+ end
90
+
91
+ def save_configuration
92
+ Dir.mkdir(CONFIG_DIRECTORY) unless File.exists? CONFIG_DIRECTORY
93
+ File.open(File.join(CONFIG_DIRECTORY, "sphinx.yml"), "w") do |file|
94
+ file.write @config.to_yaml
95
+ end
96
+ end
97
+
98
+ def module_select
99
+ exit = false
100
+ until exit do
101
+ choose do |menu|
102
+ menu.header = "\nSelect Modules".cyan
103
+ menu.prompt = "Select optional modules to toggle: "
104
+ MODULES.each do |m|
105
+ options = @config[:modules][m]
106
+ menu.choice("#{m}: " + (options[:active] ? "On".green : "Off".red)) do |choice|
107
+ m = /^([^:]*)/.match(choice)[0]
108
+ toggle_module m
109
+ end
110
+ end
111
+ menu.choice(:done) {
112
+ exit = true
113
+ }
114
+ end
115
+ end
116
+ end
117
+
118
+ def toggle_module(m)
119
+ options = @config[:modules][m]
120
+
121
+ options[:active] = !options[:active]
122
+ if (options[:active])
123
+ if (options[:depends])
124
+ options[:depends].each do |d|
125
+ toggle_module d unless @config[:modules][d][:active]
126
+ end
127
+ end
128
+ end
129
+ end
130
+
131
+ def module_menu(action, title)
132
+ exit = false
133
+ until exit do
134
+ choose do |menu|
135
+ menu.header = "\n#{title}".cyan
136
+ menu.prompt = "Select a module: "
137
+ MODULES.each do |m|
138
+ options = @config[:modules][m]
139
+ if (options[action])
140
+ menu.choice(m) do |m|
141
+ require File.join("modules", m)
142
+ mod = eval("#{m}.new")
143
+ mod.send(action)
144
+ end
145
+ end
146
+ end
147
+ menu.choice(:done) {
148
+ exit = true
149
+ }
150
+ end
151
+ end
152
+ end
153
+
154
+ def run_selected_modules &block
155
+ MODULES.each do |m|
156
+ if @config[:modules][m][:active]
157
+ require File.join("modules", m)
158
+ mod = eval("#{m}.new")
159
+ yield mod
160
+ end
161
+ end
162
+ end
163
+
164
+ def setup
165
+ exit = false
166
+ until exit do
167
+ choose do |menu|
168
+ menu.header = "\nSphinx Installer".cyan
169
+ menu.prompt = "Select an option: "
170
+
171
+ menu.choice("Select Modules") { module_select }
172
+ menu.choice("Setup Modules") { module_menu(:setup, "Module Setup") }
173
+ menu.choice("Save Configuration") {
174
+ save_configuration
175
+ puts "Configuration Saved.".green
176
+ exit = true
177
+ }
178
+ menu.choice("Cancel") {
179
+ puts "Exiting without saving configuration.".red
180
+ exit = true
181
+ }
182
+ end
183
+ end
184
+ end
185
+
186
+ def run
187
+ @no_config = load_configuration
188
+
189
+ action = nil
190
+
191
+ optparse = OptionParser.new do |opts|
192
+ # Set a banner, displayed at the top
193
+ # of the help screen.
194
+ opts.banner = "Usage: sphinx [options] action"
195
+
196
+ # This displays the help screen, all programs are
197
+ # assumed to have this option.
198
+ opts.on('-h', '--help', 'Display this screen') do
199
+ puts opts
200
+ exit
201
+ end
202
+ end
203
+
204
+ # Parse the command-line. Remember there are two forms
205
+ # of the parse method. The 'parse' method simply parses
206
+ # ARGV, while the 'parse!' method parses ARGV and removes
207
+ # any options found there, as well as any parameters for
208
+ # the options. What's left is the list of files to resize.
209
+ optparse.parse!
210
+ # Set the selected modules unless it was overridden
211
+
212
+ action = ARGV[0]
213
+ case action
214
+ when "setup"
215
+ setup
216
+ when "configure"
217
+ module_menu(:configure, "Configure Modules")
218
+ when "check"
219
+ run_selected_modules { |m| m.check }
220
+ when "install"
221
+ run_selected_modules { |m| m.install }
222
+ when "uninstall"
223
+ run_selected_modules { |m| m.uninstall }
224
+ when "download"
225
+ run_selected_modules { |m| m.download }
226
+ when "update"
227
+ run_selected_modules { |m| m.update }
228
+ else
229
+ puts "Use the --help switch if you need a list of valid actions"
230
+ end
231
+ end
232
+
233
+ end
@@ -0,0 +1,26 @@
1
+ require 'expect'
2
+ require 'pty'
3
+
4
+ class Cpan
5
+ def self.install modules
6
+ modules.each do |m|
7
+ result = %x[perl -M#{m} -e 1 2>&1].strip
8
+ if result.empty?
9
+ puts "Module '#{m}' is installed".green
10
+ else
11
+ puts "Installing perl module: #{m}".cyan
12
+ system("sudo #{Sphinx::SUDO_PROMPT} cpan -j #{Sphinx::cache_path("MyConfig.pm")} -f -i #{m}")
13
+ end
14
+ end
15
+ end
16
+
17
+ def self.create_config_file
18
+ unless File.exists? Sphinx::cache_path("MyConfig.pm")
19
+ puts "Creating cpan config...".cyan
20
+
21
+ @cache_dir = Sphinx::cache_path
22
+ Sphinx::copy_template(Sphinx::resources_path("MyConfig.pm.erb"), Sphinx::cache_path("MyConfig.pm"), binding)
23
+ end
24
+ end
25
+
26
+ end
@@ -0,0 +1,84 @@
1
+ module Download
2
+ def self.url file_url, filename
3
+ Dir::mkdir("cache") unless FileTest::directory?("cache")
4
+
5
+ z_option = (File.exists?(filename) ? " -z #{filename}" : "")
6
+ result = %x[curl -L -w "%{http_code} %{url_effective}"#{z_option} -o #{filename}.download #{file_url}]
7
+ if /304/.match(result)
8
+ puts "Up to date: ".green + filename
9
+ return false
10
+ else
11
+ if /200/.match(result)
12
+ begin
13
+ File.rename "#{filename}.download", filename
14
+ rescue
15
+ end
16
+ puts "Updated: ".yellow + filename
17
+ return true
18
+ else
19
+ File.delete "#{filename}.download" if File.exists? "#{filename}.download"
20
+ puts "File not found: ".red + file_url
21
+ return false
22
+ end
23
+ end
24
+ end
25
+
26
+ def self.open_volume(filename, volume_search, &block)
27
+ if File.exists?(filename)
28
+ result = %x[open #{filename}]
29
+ volume = find_mounted_volume volume_search
30
+ if volume.size == 0
31
+ puts "Could not find #{filename} volume".red
32
+ else
33
+ yield volume
34
+ end
35
+ else
36
+ puts "File hasn't been downloaded #{filename}".red
37
+ end
38
+ end
39
+
40
+ def self.exists?
41
+ if File.exists?(filename)
42
+ return true
43
+ else
44
+ puts "File hasn't been downloaded #{filename}".red
45
+ return false
46
+ end
47
+ end
48
+
49
+ def self.mount(filename, volume_search, &block)
50
+ if File.exists?(filename)
51
+ result = %x[open #{filename}]
52
+ volume = self.find_mounted_volume volume_search
53
+ if volume.size == 0
54
+ puts "Could not find #{filename} volume".red
55
+ else
56
+ yield volume
57
+ end
58
+ else
59
+ yield filename
60
+ end
61
+ end
62
+
63
+ def self.find_mounted_volume search
64
+ not_found = true
65
+ retries = 0
66
+ while not_found && retries < 30
67
+ volumes = Dir.glob "/Volumes/#{search}"
68
+ if volumes.size == 0
69
+ retries += 1
70
+ sleep 2
71
+ elsif volumes.size > 1
72
+ puts "Too many similar disk images mounted, please unmount all but latest version".red
73
+ puts "Here are the mounted matching images:"
74
+ volumes.each do |volume|
75
+ puts volume
76
+ end
77
+ return ""
78
+ else
79
+ return volumes[0]
80
+ end
81
+ end
82
+ return ""
83
+ end
84
+ end
@@ -0,0 +1,3 @@
1
+ module SphinxTv
2
+ VERSION = "0.9.1"
3
+ end
@@ -0,0 +1,49 @@
1
+ $CPAN::Config = {
2
+ 'auto_commit' => q[0],
3
+ 'build_cache' => q[100],
4
+ 'build_dir' => q[<%= @cache_dir %>/.cpan/build],
5
+ 'build_dir_reuse' => q[0],
6
+ 'build_requires_install_policy' => q[yes],
7
+ 'cache_metadata' => q[1],
8
+ 'check_sigs' => q[0],
9
+ 'commandnumber_in_prompt' => q[1],
10
+ 'connect_to_internet_ok' => q[1],
11
+ 'cpan_home' => q[<%= @cache_dir %>/.cpan],
12
+ 'ftp_passive' => q[1],
13
+ 'ftp_proxy' => q[],
14
+ 'getcwd' => q[cwd],
15
+ 'halt_on_failure' => q[0],
16
+ 'http_proxy' => q[],
17
+ 'inactivity_timeout' => q[0],
18
+ 'index_expire' => q[1],
19
+ 'inhibit_startup_message' => q[0],
20
+ 'keep_source_where' => q[<%= @cache_dir %>/.cpan/sources],
21
+ 'load_module_verbosity' => q[none],
22
+ 'make_arg' => q[],
23
+ 'make_install_arg' => q[],
24
+ 'make_install_make_command' => q[],
25
+ 'makepl_arg' => q[],
26
+ 'mbuild_arg' => q[],
27
+ 'mbuild_install_arg' => q[],
28
+ 'mbuild_install_build_command' => q[./Build],
29
+ 'mbuildpl_arg' => q[],
30
+ 'no_proxy' => q[],
31
+ 'pager' => q[/usr/bin/less],
32
+ 'perl5lib_verbosity' => q[none],
33
+ 'prefer_installer' => q[MB],
34
+ 'prefs_dir' => q[<%= @cache_dir %>/.cpan/prefs],
35
+ 'prerequisites_policy' => q[follow],
36
+ 'scan_cache' => q[atstart],
37
+ 'shell' => q[/bin/bash],
38
+ 'show_upload_date' => q[0],
39
+ 'tar_verbosity' => q[none],
40
+ 'term_is_latin' => q[1],
41
+ 'term_ornaments' => q[1],
42
+ 'trust_test_report_history' => q[0],
43
+ 'urllist' => [],
44
+ 'use_sqlite' => q[0],
45
+ 'version_timeout' => q[15],
46
+ 'yaml_load_code' => q[0],
47
+ };
48
+ 1;
49
+ __END__
@@ -0,0 +1,19 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3
+ <plist version="1.0">
4
+ <dict>
5
+ <key>KeepAlive</key>
6
+ <true/>
7
+ <key>Label</key>
8
+ <string>MythBackend</string>
9
+ <key>ProgramArguments</key>
10
+ <array>
11
+ <string>/Applications/MythBackend.app/Contents/MacOS/MythBackend</string>
12
+ <string>--logpath</string>
13
+ <string>/var/log/mythtv</string>
14
+ </array>
15
+ <key>UserName</key>
16
+ <string><%= @username %></string>
17
+ </dict>
18
+ </plist>
19
+
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/sphinx_tv/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["David Monagle"]
6
+ gem.email = ["david.monagle@intrica.com.au"]
7
+ gem.date = '2012-07-15'
8
+ gem.summary = "SphinxTV is an installer/configurator for MythTV (and others) for OSX"
9
+ gem.description = "SphinxTV is an installer/configurator for MythTV (and others) for OSX"
10
+ gem.homepage = ""
11
+
12
+ gem.files = `git ls-files`.split($\)
13
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
14
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
+ gem.name = "sphinx_tv"
16
+ gem.require_paths = ["lib"]
17
+ gem.version = SphinxTv::VERSION
18
+ gem.homepage =
19
+ 'http://sphinxtv.intrica.com.au'
20
+ gem.add_dependency 'colorize'
21
+ gem.add_dependency 'highline'
22
+ gem.add_dependency 'nokogiri'
23
+ end
metadata ADDED
@@ -0,0 +1,111 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sphinx_tv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - David Monagle
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-07-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: colorize
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: highline
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: nokogiri
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ description: SphinxTV is an installer/configurator for MythTV (and others) for OSX
63
+ email:
64
+ - david.monagle@intrica.com.au
65
+ executables:
66
+ - sphinx_tv
67
+ extensions: []
68
+ extra_rdoc_files: []
69
+ files:
70
+ - .gitignore
71
+ - Gemfile
72
+ - MIT-LICENSE
73
+ - README.rdoc
74
+ - Rakefile
75
+ - bin/sphinx_tv
76
+ - lib/modules/mysql.rb
77
+ - lib/modules/mythtv.rb
78
+ - lib/modules/shepherd.rb
79
+ - lib/modules/sphinx_module.rb
80
+ - lib/sphinx_tv.rb
81
+ - lib/sphinx_tv/cpan.rb
82
+ - lib/sphinx_tv/download.rb
83
+ - lib/sphinx_tv/version.rb
84
+ - resources/MyConfig.pm.erb
85
+ - resources/MythTv/MythBackend.plist.erb
86
+ - sphinx_tv.gemspec
87
+ homepage: http://sphinxtv.intrica.com.au
88
+ licenses: []
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ required_rubygems_version: !ruby/object:Gem::Requirement
100
+ none: false
101
+ requirements:
102
+ - - ! '>='
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ requirements: []
106
+ rubyforge_project:
107
+ rubygems_version: 1.8.24
108
+ signing_key:
109
+ specification_version: 3
110
+ summary: SphinxTV is an installer/configurator for MythTV (and others) for OSX
111
+ test_files: []