lib/dvdprofiler2xbmc/app_config.rb in royw-dvdprofiler2xbmc-0.0.6 vs lib/dvdprofiler2xbmc/app_config.rb in royw-dvdprofiler2xbmc-0.0.8
- old
+ new
@@ -7,21 +7,90 @@
# AppConfig['images_dir']
# Note this is because the implementation is a Mash instead of
# a Hash and does cause a limitation where the key must be either
# a symbol or a string.
module AppConfig
- @config = Mash.new
+
+ # @config, @help, @initial, @data_type, @validate, and @validate_item
+ # are mashes that share the same keys. For example @help.foo would be
+ # the help text for @config.foo
+
+ # the current config values
+ @config = Mash.new
+
+ # help about the config item
+ @help = Mash.new
+
+ # initial (default) config values
+ @initial = Mash.new
+
+ # data type constants used in the editor
+ @data_type = Mash.new
+
+ # validate the config item
+ @validate = Mash.new
+
+ # validate an entry for a config item, note usually need to allow
+ # empty string for either array entry termination or to accept
+ # a default value
+ @validate_item = Mash.new
+
+ # the @navigation array contains hashes where each hash is a "page"
+ # that contains an array of @config keys. This provides a natural
+ # organization for UIs.
+ @navigation = []
+
+ class << self
+ attr_reader :help, :initial, :navigation, :config, :data_type, :validate, :validate_item
+ end
+
@yaml_filespec = File.join(ENV['HOME'], '.dvdprofiler2xbmcrc')
+ # shortcut accessor for @config items
def self.[](k)
@config[k]
end
+ # shortcut accessor for @config items
def self.[]=(k,v)
@config[k] = v
end
+ # does the config file exist?
+ def self.exist?
+ File.exist?(@yaml_filespec)
+ end
+
+ # load the config file, overwriting current values
+ def self.load
+ begin
+ if File.exist?(@yaml_filespec)
+ cfg = YAML.load_file(@yaml_filespec)
+ cfg.delete('logger')
+ if cfg.version != @config.version
+ AppConfig[:logger].info {"config file (#{@yaml_filespec}) version mismatch"}
+ AppConfig[:logger].info {"file version => #{cfg.version}"}
+ AppConfig[:logger].info {"config version => #{@config.version}"}
+ # remove from @config any keys that are not in cfg
+ file_keys = cfg.keys.sort
+ current_keys = @config.keys.sort
+ intersection_keys = file_keys & current_keys
+ obsolete_keys = file_keys - intersection_keys
+ obsolete_keys.each do |key|
+ AppConfig[:logger].info { "removing obsolete key #{key}"}
+ cfg.delete(key)
+ end
+ cfg.delete('version')
+ end
+ @config.merge! cfg
+ end
+ rescue Exception => e
+ AppConfig[:logger].error { "Error loading config file \"#{@yaml_filespec} - " + e.to_s + "\n" + e.backtrace.join("\n") }
+ end
+ end
+
+ # save the config file
def self.save
begin
unless @config.blank?
File.delete(@yaml_filespec) if File.exist?(@yaml_filespec)
AppConfig[:logger].info { "saving: #{@yaml_filespec}" }
@@ -34,82 +103,343 @@
rescue Exception => e
AppConfig[:logger].error { "Error saving config file \"#{@yaml_filespec} - " + e.to_s + "\n" + e.backtrace.join("\n")}
end
end
- def self.load
- begin
- if File.exist?(@yaml_filespec)
- cfg = YAML.load_file(@yaml_filespec)
- cfg.delete('logger')
- @config.merge! cfg
+ # generate a string for displaying the current config
+ def self.to_s
+ buf = []
+ @navigation.each do |page|
+ page.each do |heading, keys|
+ buf << heading
+ buf << ''
+ keys.each do |key|
+ buf << key
+ buf << @help[key].split("\n").collect{|line| " " + line}.join("\n") unless @help[key].blank?
+ buf << "Initial:"
+ buf << @initial[key].pretty_inspect.collect{|line| " " + line.rstrip}
+ buf << "Current:"
+ buf << @config[key].pretty_inspect.collect{|line| " " + line.rstrip}
+ buf << ''
+ end
+ buf << ''
end
- rescue Exception => e
- AppConfig[:logger].error { "Error loading config file \"#{@yaml_filespec} - " + e.to_s }
end
+ buf.join("\n")
end
+ # is the current config valid?
+ def self.valid?
+ valid = true
+ @validate.each do |field, value|
+ unless @validate[field].call(@config[field])
+ valid = false
+ end
+ end
+ valid
+ end
+
+ # set the config to the default values
def self.default
# Note, all paths and extensions are case sensitive
- # Array of paths to scan for media
- # Note, directories underneath these will be added as genres to
- # each .nfo file. For example:
- # /media/royw-gentoo/public/data/movies/Action/Bond/Goldeneye.m4v
- # will add 'Action' and 'Bond' genres to Goldeneye.nfo
- # Also note, that duplicate genres will be collapsed into single
- # genres in the .nfo file.
+ # this is the version of the rc file and is used to trigger
+ # removal of no longer existing keys
+ @config.version = '0.1.0'
+
+ @config.color_enabled = true
+
+ @navigation = [
+ {'Application Options' => %w(color_enabled)},
+ {'Setup Paths' => %w(directories subdirs_as_genres collection_filespec images_dir)},
+ {'Setup Permissions'=> %w(file_permissions dir_permissions)},
+ {'Setup Genre Mapping' => %w(genre_maps)},
+ {'File Naming' => %w(media_extensions image_extensions naming)},
+ {'Parsing' => %w(part_regex media_parsers)}
+ ]
+
+ @help.directories = [
+ 'Array of paths to scan for media. Replace with your paths.'
+ ].join("\n")
+ @initial.directories = []
+ # My directories are:
@config.directories = [
- '/media/dad-kubuntu/public/data/videos_iso',
- '/media/dcerouter/public/data/videos_iso',
- '/media/royw-gentoo/public/data/videos_iso',
- '/media/royw-gentoo/public/data/movies'
+# '/media/dad-kubuntu/public/data/videos_iso',
+# '/media/dcerouter/public/data/videos_iso',
+# '/media/royw-gentoo/public/data/videos_iso',
+# '/media/royw-gentoo/public/data/movies'
]
+ @data_type.directories = :ARRAY_OF_PATHSPECS
+ @validate.directories = lambda do |directories|
+ valid = false
+ unless directories.empty?
+ valid = true
+ directories.each do |dir|
+ unless File.exist?(dir) && File.directory?(dir)
+ valid = false
+ end
+ end
+ end
+ valid
+ end
+ @validate_item.directories = lambda do |dir|
+ dir.empty? || (File.exist?(dir) && File.directory?(dir))
+ end
+ @help.subdirs_as_genres = [
+ 'Directories underneath these will be added as genres to each .nfo file.',
+ 'For example:',
+ ' /media/movies/Action/Bond/Goldeneye.m4v',
+ 'will add "Action" and "Bond" genres to Goldeneye.nfo',
+ 'Also note, that duplicate genres will be collapsed into single genres in the .nfo file.'
+ ].join("\n")
+ @initial.subdirs_as_genres = true
+ @config.subdirs_as_genres = @initial.subdirs_as_genres
+ @data_type.subdirs_as_genres = :BOOLEAN
+
# Typical locations are:
# @config.collection_filespec = File.join(ENV['HOME'], 'DVD Profiler/Databases/Exports/Collection.xml')
# @config.images_dir = File.join(ENV['HOME'], 'DVD Profiler/Databases/Default/Images')
#
- # My locations are:
- @config.collection_filespec = '/home/royw/DVD Profiler/Shared/Collection.xml'
- @config.images_dir = '/home/royw/DVD Profiler/Shared/Images'
+ @help.collection_filespec = [
+ 'The location of DVD Profiler\'s exported Collection.xml'
+ ].join("\n")
+ @initial.collection_filespec = '~/DVD Profiler/Databases/Exports/Collection.xml'
+ # My location is:
+ @config.collection_filespec = @initial.collection_filespec
+# @config.collection_filespec = '/home/royw/DVD Profiler/Shared/Collection.xml'
+ @data_type.collection_filespec = :FILESPEC
+ @validate.collection_filespec = lambda do |filespec|
+ File.exist?(filespec) && File.file?(filespec)
+ end
+ @validate_item.collection_filespec = lambda do |filespec|
+ filespec.empty? || (File.exist?(filespec) && File.file?(filespec))
+ end
+ @help.images_dir = [
+ 'The location of DVD Profiler\'s cover scan images.'
+ ].join("\n")
+ @initial.images_dir = '~/DVD Profiler/Databases/Exports/Images'
+ # My location is:
+ @config.images_dir = @initial.images_dir
+# @config.images_dir = '/home/royw/DVD Profiler/Shared/Images'
+ @data_type.images_dir = :PATHSPEC
+ @validate.images_dir = lambda do |directory|
+ File.exist?(directory) && File.directory?(directory)
+ end
+ @validate_item.images_dir = lambda do |directory|
+ directory.empty? || (File.exist?(directory) && File.directory?(directory))
+ end
+
# You will probably need to edit the MEDIA_EXTENSIONS to specify
# the containers used in your library
- @config.media_extensions = [ 'iso', 'm4v' ]
+ @help.media_extensions = [
+ 'The supported file extensions for movie media.',
+ 'You probably will not need to edit this list.'
+ ].join("\n")
+ @initial.media_extensions = %w(iso m4v mp4 mpeg wmv asf flv mkv mov aac nut ogg ogm ram rm rv ra rmvb 3gp vivo pva nuv nsv nsa fli flc)
+ @config.media_extensions = @initial.media_extensions
+ @data_type.media_extensions = :ARRAY_OF_STRINGS
+ @validate.media_extensions = lambda do |extensions|
+ !extensions.collect{|ext| ext.blank? ? nil : ext}.compact.empty?
+ end
+ @validate_item.media_extensions = lambda do |extension|
+ true
+ end
# You probably will not need to change these
# Source file extensions.
- @config.image_extensions = [ 'jpg', 'jpeg', 'png', 'gif' ]
- @config.nfo_extensions = [ 'nfo' ]
- # Destination file extensions
- @config.thumbnail_extension = 'tbn'
- @config.nfo_extension = 'nfo'
- @config.no_imdb_extension = 'no_imdb_lookup'
- @config.no_tmdb_extension = 'no_tmdb_lookup'
- @config.no_isbn_extension = 'no_isbn'
- @config.dvdprofiler_xml_extension = 'dvdprofiler.xml'
- @config.dvdprofiler_yaml_extension = 'dvdprofiler.yaml'
- @config.imdb_xml_extension = 'imdb.xml'
- @config.imdb_yaml_extension = 'imdb.yaml'
- @config.tmdb_xml_extension = 'tmdb.xml'
- @config.tmdb_yaml_extension = 'tmdb.yaml'
- @config.fanart_extension = '-fanart'
- @config.new_extension = '.new'
- @config.backup_extension = '~'
+ @help.image_extensions = [
+ 'The file extensions for image files such as cover art, fan art, and thumbnails.',
+ 'You probably will not need to edit this list.'
+ ].join("\n")
+ @initial.image_extensions = %w(jpg jpeg png gif bmp tbn)
+ @config.image_extensions = @initial.image_extensions
+ @data_type.image_extensions = :ARRAY_OF_STRINGS
+ @validate.image_extensions = lambda do |extensions|
+ !extensions.collect{|ext| ext.blank? ? nil : ext}.compact.empty?
+ end
+ @validate_item.media_extensions = lambda do |extension|
+ true
+ end
# map some genre names
- @config.genre_maps = {
- 'SciFi' => 'Science Fiction',
- 'Science-Fiction' => 'Science Fiction',
- 'Anime' => 'Animation',
- 'Musical' => 'Musicals',
- 'Music' => 'Musicals'
+ @help.genre_maps = [
+ 'Change the name of genres.',
+ 'For example, "SciFi" can be mapped to "Science Fiction"'
+ ].join("\n")
+ @initial.genre_maps = {
+ 'SciFi' => 'Science Fiction',
+ 'Science-Fiction' => 'Science Fiction',
+ 'Anime' => 'Animation',
+ 'Musical' => 'Musicals',
+ 'Music' => 'Musicals',
+ 'War Film' => 'War'
}
+ @config.genre_maps = @initial.genre_maps
+ @data_type.genre_maps = :HASH_STRING_KEYS_STRING_VALUES
- @config.file_permissions = 0664
- @config.dir_permissions = 0777
- @config.imdb_query = true
- @config.tmdb_query = true
- @config.do_update = true
+ @help.file_permissions = [
+ 'Set the file permissions of all files in the scanned directories to this value.',
+ 'This is useful to maintain consistancy of file permissions'
+ ].join("\n")
+ @initial.file_permissions = 0664.to_s(8)
+ @config.file_permissions = @initial.file_permissions
+ @data_type.file_permissions = :PERMISSIONS
+ @validate.file_permissions = lambda do |permissions|
+ (permissions.to_i(8) >= 0) && (permissions.to_i(8) <= 07777)
+ end
+ @validate_item.file_permissions = lambda do |permissions|
+ permissions.empty? || ((permissions.to_i(8) >= 0) && (permissions.to_i(8) <= 07777))
+ end
+
+ @help.dir_permissions = [
+ 'Set the directory permissions of all sub-directories in the scanned directories to this value.',
+ 'This is useful to maintain consistancy of directory permissions'
+ ].join("\n")
+ @initial.dir_permissions = 0777.to_s(8)
+ @config.dir_permissions = @initial.dir_permissions
+ @data_type.dir_permissions = :PERMISSIONS
+ @validate.dir_permissions = lambda do |permissions|
+ permissions.empty? || ((permissions.to_i(8) >= 0) && (permissions.to_i(8) <= 07777))
+ end
+
+ # This maps the file type to extension.
+ # The one unusual case in the list is for :fanart where
+ # the actually media extension will be appended to this
+ # extension (see FanartController)
+ @help.extensions = [
+ 'Internally used to map types to file extensions.',
+ 'You may change the values if you need different file extensions.',
+ 'Do not change the keys unless you really know what you are doing!'
+ ].join("\n")
+ @initial.extensions = {
+ :fanart => '-fanart',
+ :thumbnail => 'tbn',
+ :nfo => 'nfo',
+ :dvdprofiler_xml => 'dvdprofiler.xml',
+ :imdb_xml => 'imdb.xml',
+ :tmdb_xml => 'tmdb.xml',
+ :new => 'new',
+ :backup => '~',
+ :no_isbn => 'no_isbn',
+ :no_imdb_lookup => 'no_imdb_lookup',
+ :no_tmdb_lookup => 'no_tmdb_lookup',
+ }
+ @config.extensions = @initial.extensions
+# @data_type.extensions = :HASH_FIXED_SYMBOL_KEYS_STRING_VALUES
+
+ # substitutions:
+ # %t => movie title
+ # %e => extension from @config.extensions
+ # %p => part
+ @help.naming = [
+ 'Defines the various formatting of generated file names.',
+ 'Do not change the naming keys (ex: :fanart, :thumbnail,...).',
+ 'The :part key/value defines the format for a multi-part media while :no_part defines the format for single part media.',
+ 'The substutions are:',
+ ' %t => movie title',
+ ' %e => appropriate file extension',
+ ' %p => the part substring from the media file (ex: \'cd1\', \'disc3\')',
+ ' %r => video resolution',
+ ' %y => year'
+ ].join("\n")
+ @initial.naming = {
+ :fanart => {:part => '%t%e', :no_part => '%t%e'},
+ :thumbnail => {:part => '%t.%e', :no_part => '%t.%e'},
+ :nfo => {:part => '%t.%e', :no_part => '%t.%e'},
+ :dvdprofiler_xml => {:part => '%t.%e', :no_part => '%t.%e'},
+ :imdb_xml => {:part => '%t.%e', :no_part => '%t.%e'},
+ :tmdb_xml => {:part => '%t.%e', :no_part => '%t.%e'}
+ }
+ @config.naming = @initial.naming
+
+ # recognized multi-part tokens where N is an integer:
+ # *.cdN.*
+ # *.ptN.*
+ # *.diskN.*
+ # *.discN.*
+ @help.part_regex = [
+ 'The regular expression for parsing the part sub-string from the media file.',
+ 'For example to find "cd2" in "movie.cd2.avi"'
+ ].join("\n")
+ @initial.part_regex = /\.(cd|pt|disk|disc)\d+/i
+ @config.part_regex = @initial.part_regex
+
+ # media filename parsers.
+ # The :tokens array refers to the matches in the regex, ex:
+ # {:regex => /^\s*(.*\S)\s*\.([^.]+)\s*$/, :tokens => [:title, :extension]}
+ # :title will be assigned the first match (.*\S) and
+ # :extension will be assigned the second match ([^.]+)
+ # See (Media.parse)
+ @help.media_parsers = [
+ 'Media filename parsers.',
+ 'The :tokens array refers to the matches in the regex, ex:',
+ ' {:regex => /^\s*(.*\S)\s*\.([^.]+)\s*$/, :tokens => [:title, :extension]}',
+ ':title will be assigned the first match (.*\S) and',
+ ':extension will be assigned the second match ([^.]+).',
+ 'Valid tokens are: :title, :year, :resolution, :part, :extension'
+ ].join("\n")
+ @initial.media_parsers = [
+ # "movie title - yyyy[res].partN.ext"
+ {:regex => /^\s*(.*\S)\s*\-\s*(\d{4})\s*\[(\S*)\]\s*\.(cd\d+|pt\d+|disk\d+|disc\d+)\.([^.]+)\s*$/,
+ :tokens => [:title, :year, :resolution, :part, :extension]
+ },
+ # "movie title (yyyy)[res].partN.ext"
+ {:regex => /^\s*(.*\S)\s*\(\s*(\d{4})\s*\)\s*\[(\S*)\]\s*\.(cd\d+|pt\d+|disk\d+|disc\d+)\.([^.]+)\s*$/,
+ :tokens => [:title, :year, :resolution, :part, :extension]
+ },
+ # "movie title[res].partN.ext"
+ {:regex => /^\s*(.*\S)\s*\[(\S*)\]\s*\.(cd\d+|pt\d+|disk\d+|disc\d+)\.([^.]+)\s*$/,
+ :tokens => [:title, :resolution, :part, :extension]
+ },
+ # "movie title - yyyy[res].ext"
+ {:regex => /^\s*(.*\S)\s*\-\s*(\d{4})\s*\[(\S*)\]\s*\.([^.]+)\s*$/,
+ :tokens => [:title, :year, :resolution, :extension]
+ },
+ # "movie title (yyyy)[res].ext"
+ {:regex => /^\s*(.*\S)\s*\(\s*(\d{4})\s*\)\s*\[(\S*)\]\s*\.([^.]+)\s*$/,
+ :tokens => [:title, :year, :resolution, :extension]
+ },
+ # "movie title[res].ext"
+ {:regex => /^\s*(.*\S)\s*\[(\S*)\]\s*\.([^.]+)\s*$/,
+ :tokens => [:title, :resolution, :extension]
+ },
+ # "movie title - yyyy.partN.ext"
+ {:regex => /^\s*(.*\S)\s*\-\s*(\d{4})\s*\.(cd\d+|pt\d+|disk\d+|disc\d+)\.([^.]+)\s*$/,
+ :tokens => [:title, :year, :part, :extension]
+ },
+ # "movie title (yyyy).partN.ext"
+ {:regex => /^\s*(.*\S)\s*\(\s*(\d{4})\s*\)\s*\.(cd\d+|pt\d+|disk\d+|disc\d+)\.([^.]+)\s*$/,
+ :tokens => [:title, :year, :part, :extension]
+ },
+ # "movie title.partN.ext"
+ {:regex => /^\s*(.*\S)\s*\.(cd\d+|pt\d+|disk\d+|disc\d+)\.([^.]+)\s*$/,
+ :tokens => [:title, :part, :extension]
+ },
+ # "movie title - yyyy.ext"
+ {:regex => /^\s*(.*\S)\s*\-\s*(\d{4})\s*\.([^.]+)\s*$/,
+ :tokens => [:title, :year, :extension]
+ },
+ # "movie title (yyyy).ext"
+ {:regex => /^\s*(.*\S)\s*\(\s*(\d{4})\s*\)\s*\.([^.]+)\s*$/,
+ :tokens => [:title, :year, :extension]
+ },
+ # "movie title.ext"
+ {:regex => /^\s*(.*\S)\s*\.([^.]+)\s*$/,
+ :tokens => [:title, :extension]
+ }
+ ]
+ @config.media_parsers = @initial.media_parsers
+
+ @help.do_update = [
+ 'Perform update.'
+ ].join("\n")
+ @initial.do_update = true
+ @config.do_update = @initial.do_update
+ end
+
+ private
+ def initialize
+ AppConfig[:logger].error {"Should never be instantiated"}
end
end