royw-dvdprofiler2xbmc 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/History.txt ADDED
@@ -0,0 +1,4 @@
1
+ == 0.0.1 2009-03-18
2
+
3
+ * 1 major enhancement:
4
+ * refactored into gem using newgem
data/Manifest.txt ADDED
@@ -0,0 +1,16 @@
1
+ History.txt
2
+ Manifest.txt
3
+ PostInstall.txt
4
+ README.rdoc
5
+ Rakefile
6
+ bin/dvdprofiler2xbmc
7
+ lib/dvdprofiler2xbmc.rb
8
+ lib/dvdprofiler2xbmc/app_config.rb
9
+ lib/dvdprofiler2xbmc/app.rb
10
+ lib/dvdprofiler2xbmc/cli.rb
11
+ lib/dvdprofiler2xbmc/collection.rb
12
+ lib/dvdprofiler2xbmc/extensions.rb
13
+ lib/dvdprofiler2xbmc/imdb_extensions.rb
14
+ lib/dvdprofiler2xbmc/media_files.rb
15
+ lib/dvdprofiler2xbmc/media.rb
16
+ lib/dvdprofiler2xbmc/nfo.rb
data/PostInstall.txt ADDED
@@ -0,0 +1,7 @@
1
+
2
+ For more information on dvdprofiler2xbmc, see http://dvdprofiler2xbmc.rubyforge.org
3
+
4
+ NOTE: Change this information in PostInstall.txt
5
+ You can also delete it if you don't want it.
6
+
7
+
data/README.rdoc ADDED
@@ -0,0 +1,104 @@
1
+ = dvdprofiler2xbmc
2
+
3
+ * http://www.github.com/royw/dvdprofiler2xbmc
4
+
5
+ == DESCRIPTION:
6
+
7
+ This script will attempt to match up media files from a set of directories
8
+ to the collection.xml file exported from DVD Profiler. For matches, the
9
+ script will then create a {moviename}.nfo from the data in collections.xml
10
+ and also copy the front cover image to {moviename}.tbn. Both files will
11
+ be placed in the same directory as the source media file.
12
+
13
+ Then on XBMC, set the source content to none to remove the meta data from
14
+ the library, then set the source content back to Movies to import the
15
+ media. This time, the data in the .nfo files will be used instead of
16
+ scraping.
17
+
18
+ == NOTES:
19
+
20
+ 1) currently you lose a few meta data fields such as Rating and Director
21
+ using this script instead of a scraper.
22
+
23
+ 2) Currently only supports file based media containers, not directory
24
+ based.
25
+
26
+ 3) Media filename convention is to take the media's title from DVD Profiler,
27
+ replace any punctuation with a space character, then replace any multiple
28
+ spaces with a single space. Next remove any leading or trailing spaces.
29
+ Optionally can append " - YYYY" where YYYY is the movie's release year.
30
+ Naturally the extension is the media's container type. Note, you should
31
+ not include in the title edition info like "Widescreen" or "Special Edition"
32
+ eventhough there are some mistakes in the DVD Profiler profiles that do
33
+ include these in the title.
34
+
35
+ == FEATURES/PROBLEMS:
36
+
37
+ Features:
38
+
39
+ * Creates .nfo files from exported collection.xml from DVD Profiler
40
+ * If .nfo does not have an <id> tag, then tries to find the IMDB ID by
41
+ using the title and production/release years to search IMDB.
42
+ * Sets file permissions for files and directories
43
+ * Media can be contained in a set of directories (they can be mount points)
44
+ * Adds sub-directory names as genres to .nfo files
45
+
46
+ Problems:
47
+
48
+ * Always overwriting .nfo. Needs to only write if there has been a change.
49
+ * Loses any tags not directly supported. Needs to change from overwrite
50
+ to merge.
51
+ * IMDB ID scraping doesn't handle boxed sets or multiple movies per media
52
+ file. I don't see how to work around this exept to eliminate from media
53
+ library.
54
+ * Needs a better method to setup AppConfig defaults.
55
+ * Needs to support selectable/multiple regex based naming conventions
56
+ * Needs to support directory containers
57
+ * Does not support stacking
58
+
59
+ == SYNOPSIS:
60
+
61
+ Edit the defaults in lib/dvdprofiler2xbmc/app_config.rb to reflect your
62
+ system. Then run:
63
+
64
+ bin/dvdprofiler2xbmc
65
+
66
+ For help, run bin/dvdprofiler2xbmc --help
67
+
68
+ == REQUIREMENTS:
69
+
70
+ * ruby 1.8.x
71
+ * rubygem
72
+
73
+ == INSTALL:
74
+
75
+ sudo gem install xml-simple
76
+ sudo gem install porras-imdb
77
+ sudo gem install log4r
78
+ sudo gem install commandline
79
+ sudo gem install mash
80
+
81
+ == LICENSE:
82
+
83
+ (The MIT License)
84
+
85
+ Copyright (c) 2009 Roy Wright
86
+
87
+ Permission is hereby granted, free of charge, to any person obtaining
88
+ a copy of this software and associated documentation files (the
89
+ 'Software'), to deal in the Software without restriction, including
90
+ without limitation the rights to use, copy, modify, merge, publish,
91
+ distribute, sublicense, and/or sell copies of the Software, and to
92
+ permit persons to whom the Software is furnished to do so, subject to
93
+ the following conditions:
94
+
95
+ The above copyright notice and this permission notice shall be
96
+ included in all copies or substantial portions of the Software.
97
+
98
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
99
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
100
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
101
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
102
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
103
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
104
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,33 @@
1
+ %w[rubygems rake rake/clean fileutils newgem rubigen].each { |f| require f }
2
+ require File.dirname(__FILE__) + '/lib/dvdprofiler2xbmc'
3
+
4
+ # Generate all the Rake tasks
5
+ # Run 'rake -T' to see list of generated tasks (from gem root directory)
6
+ $hoe = Hoe.new('dvdprofiler2xbmc', Dvdprofiler2xbmc::VERSION) do |p|
7
+ p.developer('Roy Wright', 'roy@wright.org')
8
+ p.changes = p.paragraphs_of("History.txt", 0..1).join("\n\n")
9
+ p.post_install_message = 'PostInstall.txt' # TODO remove if post-install message not required
10
+ p.rubyforge_name = p.name # TODO this is default value
11
+ p.extra_deps = [
12
+ ['activesupport','>= 2.0.2'],
13
+ ['xml-simple','>= 1.0.12'],
14
+ ['porras-imdb','>= 0.0.5'],
15
+ ['log4r','>= 1.0.5'],
16
+ ['commandline','>= 0.7.10'],
17
+ ['mash','>= 0.0.3']
18
+ ]
19
+ p.extra_dev_deps = [
20
+ ['newgem', ">= #{::Newgem::VERSION}"]
21
+ ]
22
+
23
+ p.clean_globs |= %w[**/.DS_Store tmp *.log]
24
+ path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
25
+ p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
26
+ p.rsync_args = '-av --delete --ignore-errors'
27
+ end
28
+
29
+ require 'newgem/tasks' # load /tasks/*.rake
30
+ Dir['tasks/**/*.rake'].each { |t| load t }
31
+
32
+ # TODO - want other tests/tasks run by default? Add them to the list
33
+ # task :default => [:spec, :features]
@@ -0,0 +1,10 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # Created on 2009-3-19.
4
+ # Copyright (c) 2009. All rights reserved.
5
+
6
+ require File.expand_path(File.dirname(__FILE__) + "/../lib/dvdprofiler2xbmc")
7
+
8
+ require "dvdprofiler2xbmc/cli"
9
+
10
+ Dvdprofiler2xbmc::CLI.execute(STDOUT, ARGV)
@@ -0,0 +1,160 @@
1
+ # == Synopsis
2
+ # Transfer media meta data from DvdProfiler to the format that XBMC needs it (.tbn and .nfo files)
3
+ #
4
+ # usage:
5
+ # app = DvdProfiler2Xbmc.new
6
+ # app.execute
7
+ # app.report.each {|line| puts line}
8
+ class DvdProfiler2Xbmc
9
+ @interrupted = false
10
+
11
+ # A trap("INT") in the Runner calls this to indicate that a ^C has been detected.
12
+ # Note, once set, it is never cleared
13
+ def self.interrupt
14
+ AppConfig[:logger].error { "control-C detected, finishing current task" }
15
+ @interrupted = true
16
+ end
17
+
18
+ # Long loops should poll this method to see if they should abort
19
+ # Returns:: true if the application has trapped an "INT", false otherwise
20
+ def self.interrupted?
21
+ @interrupted
22
+ end
23
+
24
+ def initialize
25
+ @media_files = nil
26
+ @collection = nil
27
+ end
28
+
29
+ def execute
30
+ @media_files = MediaFiles.new(AppConfig[:directories])
31
+
32
+ collection_filepath = File.expand_path(AppConfig[:collection_filespec])
33
+ @collection = Collection.new(collection_filepath)
34
+
35
+ @media_files.titles.each do |title, medias|
36
+ break if DvdProfiler2Xbmc.interrupted?
37
+ # the following lines are order dependent
38
+ find_isbns(title, medias)
39
+ copy_thumbnails(title, medias)
40
+ create_nfos(title, medias)
41
+ end
42
+
43
+ # set file and directory permissions
44
+ AppConfig[:directories].each do |dir|
45
+ Dir.glob(File.join(dir, '**/*')).each do |f|
46
+ begin
47
+ if File.directory?(f)
48
+ File.chmod(AppConfig[:dir_permissions], f) unless AppConfig[:dir_permissions].nil?
49
+ else
50
+ File.chmod(AppConfig[:file_permissions], f) unless AppConfig[:file_permissions].nil?
51
+ end
52
+ rescue Exception => e
53
+ AppConfig[:logger].error {e.to_s}
54
+ end
55
+ end
56
+ end
57
+ end
58
+
59
+ # generate the report.
60
+ # Note, must be ran after execute()
61
+ # returns an array of lines
62
+ def report
63
+ buf = []
64
+ unless DvdProfiler2Xbmc.interrupted?
65
+ unless @media_files.nil?
66
+ duplicates = duplicates_report
67
+ unless duplicates.empty?
68
+ buf << "Duplicates:\n"
69
+ buf += duplicates
70
+ end
71
+
72
+ missing_isbns = missing_isbn_report
73
+ unless missing_isbns.empty?
74
+ buf += missing_isbns
75
+ end
76
+ end
77
+ end
78
+ buf
79
+ end
80
+
81
+ protected
82
+
83
+ # find ISBN for each title and assign to the media
84
+ def find_isbns(title, medias)
85
+ title_pattern = Collection.title_pattern(title)
86
+ unless @collection.title_isbn_hash[title_pattern].nil?
87
+ medias.each do |media|
88
+ media.isbn = @collection.title_isbn_hash[title_pattern]
89
+ end
90
+ end
91
+ end
92
+
93
+ # copy images from .../isbn.jpg to .../basename.jpg
94
+ def copy_thumbnails(title, medias)
95
+ medias.each do |media|
96
+ unless media.isbn.nil?
97
+ media.isbn.each do |isbn|
98
+ src_image_filespec = File.join(AppConfig[:images_dir], "#{isbn}f.jpg")
99
+ if File.exist?(src_image_filespec)
100
+ dest_image_filespec = media.media_path.ext(".#{AppConfig[:thumbnail_extension]}")
101
+ begin
102
+ File.copy(src_image_filespec, dest_image_filespec)
103
+ rescue Exception => e
104
+ AppConfig[:logger].error {e.to_s}
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
111
+
112
+ # create nfo files from collection.isbn_dvd_hash
113
+ def create_nfos(title, medias)
114
+ medias.each do |media|
115
+ unless media.isbn.nil?
116
+ media.isbn.each do |isbn|
117
+ dvd_hash = @collection.isbn_dvd_hash[isbn]
118
+ unless dvd_hash.nil?
119
+ nfo = NFO.new(media, dvd_hash)
120
+ nfo.save
121
+ end
122
+ end
123
+ end
124
+ end
125
+ end
126
+
127
+ # duplicate media file report
128
+ def duplicates_report
129
+ buf = []
130
+ duplicates = @media_files.duplicate_titles
131
+ unless duplicates.empty?
132
+ duplicates.each do |title, medias|
133
+ if medias.length > 1
134
+ buf << title
135
+ medias.each {|media| buf << " #{media.media_path}"}
136
+ end
137
+ end
138
+ end
139
+ buf
140
+ end
141
+
142
+ # unable to find ISBN for these titles report
143
+ def missing_isbn_report
144
+ buf = []
145
+ @media_files.titles.each do |title, medias|
146
+ if medias.nil?
147
+ buf << "No media for #{title}"
148
+ else
149
+ if medias[0].isbn.nil?
150
+ buf << "ISBN not found for #{title}"
151
+ medias.each {|media| buf << " #{media.media_path}"}
152
+ end
153
+ end
154
+ end
155
+ buf
156
+ end
157
+
158
+ end
159
+
160
+
@@ -0,0 +1,95 @@
1
+ # == Synopsis
2
+ # This module encapsulates the application's config hash by adding
3
+ # default, load, and save methods. Also behaves as a global hash,
4
+ # meaning you can access it from anywhere in your code like:
5
+ # AppConfig[:images_dir]
6
+ # or
7
+ # AppConfig['images_dir']
8
+ # Note this is because the implementation is a Mash instead of
9
+ # a Hash and does cause a limitation where the key must be either
10
+ # a symbol or a string.
11
+ module AppConfig
12
+ @config = Mash.new
13
+ @yaml_filespec = File.join(ENV['HOME'], '.dvdprofiler2xbmcrc')
14
+
15
+ def self.[](k)
16
+ @config[k]
17
+ end
18
+
19
+ def self.[]=(k,v)
20
+ @config[k] = v
21
+ end
22
+
23
+ def self.save
24
+ begin
25
+ File.delete(@yaml_filespec) if File.exist?(yaml_filespec)
26
+ AppConfig[:logger].info { "saving: #{yaml_filespec}" }
27
+ File.open(yaml_filespec, "w") do |f|
28
+ YAML.dump(@config, f)
29
+ end
30
+ rescue Exception => e
31
+ AppConfig[:logger].error { "Error saving config file \"#{@yaml_filespec} - " + e.to_s }
32
+ end
33
+ end
34
+
35
+ def self.load
36
+ begin
37
+ if File.exist?(@yaml_filespec)
38
+ @config.merge YAML.load_file(@yaml_filespec)
39
+ end
40
+ rescue Exception => e
41
+ AppConfig[:logger].error { "Error loading config file \"#{@yaml_filespec} - " + e.to_s }
42
+ end
43
+ end
44
+
45
+ def self.default
46
+ # Note, all paths and extensions are case sensitive
47
+
48
+ # Array of paths to scan for media
49
+ # Note, directories underneath these will be added as genres to
50
+ # each .nfo file. For example:
51
+ # /media/royw-gentoo/public/data/movies/Action/Bond/Goldeneye.m4v
52
+ # will add 'Action' and 'Bond' genres to Goldeneye.nfo
53
+ # Also note, that duplicate genres will be collapsed into single
54
+ # genres in the .nfo file.
55
+ @config.directories = [
56
+ '/media/dad-kubuntu/public/data/videos_iso',
57
+ '/media/dcerouter/public/data/videos_iso',
58
+ '/media/royw-gentoo/public/data/videos_iso',
59
+ '/media/royw-gentoo/public/data/movies'
60
+ ]
61
+
62
+ # Typical locations are:
63
+ # @config.collection_filespec = File.join(ENV['HOME'], 'DVD Profiler/Databases/Exports/Collection.xml')
64
+ # @config.images_dir = File.join(ENV['HOME'], 'DVD Profiler/Databases/Default/Images')
65
+ #
66
+ # My locations are:
67
+ @config.collection_filespec = '/home/royw/DVD Profiler/Shared/Collection.xml'
68
+ @config.images_dir = '/home/royw/DVD Profiler/Shared/Images'
69
+
70
+ # You will probably need to edit the MEDIA_EXTENSIONS to specify
71
+ # the containers used in your library
72
+ @config.media_extensions = [ 'iso', 'm4v' ]
73
+
74
+ # You probably will not need to change these
75
+ # Source file extensions.
76
+ @config.image_extensions = [ 'jpg', 'jpeg', 'png', 'gif' ]
77
+ @config.nfo_extensions = [ 'nfo' ]
78
+ # Destination file extensions
79
+ @config.thumbnail_extension = 'tbn'
80
+ @config.nfo_extension = 'nfo'
81
+ @config.nfo_backup_extension = 'nfo~'
82
+
83
+ # map some genre names
84
+ @config.genre_maps = {
85
+ 'SciFi' => 'Science Fiction',
86
+ 'Science-Fiction' => 'Science Fiction',
87
+ 'Anime' => 'Animation',
88
+ 'Musical' => 'Musicals'
89
+ }
90
+
91
+ @config.file_permissions = 0664
92
+ @config.dir_permissions = 0777
93
+ @config.imdb_query = true
94
+ end
95
+ end
@@ -0,0 +1,118 @@
1
+ require 'rubygems'
2
+ require 'yaml'
3
+ require 'xmlsimple'
4
+ require 'ftools'
5
+ require 'imdb'
6
+ require 'pp'
7
+ require 'mash'
8
+ require 'log4r'
9
+ require 'commandline/optionparser'
10
+ include CommandLine
11
+
12
+ require 'dvdprofiler2xbmc/app'
13
+ require 'dvdprofiler2xbmc/app_config'
14
+ require 'dvdprofiler2xbmc/collection'
15
+ require 'dvdprofiler2xbmc/extensions'
16
+ require 'dvdprofiler2xbmc/imdb_extensions'
17
+ require 'dvdprofiler2xbmc/media'
18
+ require 'dvdprofiler2xbmc/media_files'
19
+ require 'dvdprofiler2xbmc/nfo'
20
+
21
+ module Dvdprofiler2xbmc
22
+ # == Synopsis
23
+ # Command line exit codes
24
+ class ExitCode
25
+ UNKNOWN = 3
26
+ CRITICAL = 2
27
+ WARNING = 1
28
+ OK = 0
29
+ end
30
+
31
+ class CLI
32
+ include AppConfig
33
+
34
+ def self.execute(stdout, arguments=[])
35
+ exit_code = ExitCode::OK
36
+
37
+ # we start a STDOUT logger, but it will be switched after
38
+ # the config files are read if config[:logger_output] is set
39
+ logger = Log4r::Logger.new('dvdprofiler2xbmc')
40
+ logger.outputters = Log4r::StdoutOutputter.new(:console)
41
+ logger.level = Log4r::DEBUG
42
+
43
+ begin
44
+ # trap ^C interrupts and let the app instance cleanly exit any long loops
45
+ Signal.trap("INT") {DvdProfiler2Xbmc.interrupt}
46
+
47
+
48
+ # parse the command line
49
+ options = setupParser()
50
+ od = options.parse(arguments)
51
+
52
+ unless od["--help"]
53
+ # load config values
54
+ AppConfig.default
55
+ AppConfig[:pretend] = od["--pretend"]
56
+ AppConfig[:imdb_query] = !od["--no_imdb_query"]
57
+
58
+ # the first reinitialize_logger adds the command line logging options to the default config
59
+ # then we load the config files
60
+ # then we run reinitialize_logger again to modify the logger for any logging options from the config files
61
+
62
+ reinitialize_logger(logger, od["--verbose"], od["--debug"])
63
+ AppConfig.load
64
+ reinitialize_logger(logger, od["--verbose"], od["--debug"])
65
+
66
+ # create and execute class instance here
67
+ app = DvdProfiler2Xbmc.new
68
+ app.execute
69
+ app.report.each {|line| puts line}
70
+ end
71
+ rescue Exception => eMsg
72
+ logger.error {eMsg.to_s}
73
+ logger.error {options.to_s}
74
+ logger.error {eMsg.backtrace.join("\n")}
75
+ exit_code = ExitCode::CRITICAL
76
+ end
77
+ exit_code
78
+ end
79
+
80
+ # Setup the command line option parser
81
+ # Returns:: OptionParser instances
82
+ def self.setupParser()
83
+ options = OptionParser.new()
84
+ options << Option.new(:flag, :names => %w(--help),
85
+ :opt_found => lambda {Log4r::Logger['dvdprofiler2xbmc'].info{options.to_s}},
86
+ :opt_description => "This usage information")
87
+ options << Option.new(:flag, :names => %w(--pretend -p))
88
+ options << Option.new(:flag, :names => %w(--no_imdb_query -n))
89
+ options << Option.new(:flag, :names => %w(--verbose -v))
90
+ options << Option.new(:flag, :names => %w(--debug -d))
91
+ options
92
+ end
93
+
94
+ # Reinitialize the logger using the loaded config.
95
+ # logger:: logger for any user messages
96
+ # config:: is the application's config hash.
97
+ def self.reinitialize_logger(logger, verbose, debug)
98
+ # switch the logger to the one specified in the config files
99
+ unless AppConfig[:logfile].nil?
100
+ logfile_outputter = Log4r::RollingFileOutputter.new(:logfile, :filename => AppConfig[:logfile], :maxsize => 1000000 )
101
+ logger.add logfile_outputter
102
+ logfile_outputter.level = Log4r::INFO
103
+ Log4r::Outputter[:logfile].formatter = Log4r::PatternFormatter.new(:pattern => "[%l] %d :: %M")
104
+ unless AppConfig[:logfile_level].nil?
105
+ level_map = {'DEBUG' => Log4r::DEBUG, 'INFO' => Log4r::INFO, 'WARN' => Log4r::WARN}
106
+ logfile_outputter.level = level_map[AppConfig[:logfile_level]] || Log4r::INFO
107
+ end
108
+ end
109
+ Log4r::Outputter[:console].level = Log4r::WARN
110
+ Log4r::Outputter[:console].level = Log4r::INFO if verbose
111
+ Log4r::Outputter[:console].level = Log4r::DEBUG if debug
112
+ # logger.trace = true
113
+ AppConfig[:logger] = logger
114
+ end
115
+ end
116
+ end
117
+
118
+
@@ -0,0 +1,161 @@
1
+ # == Synopsis
2
+ # This model encapsulates the DVDProfiler Collection.xml
3
+ class Collection
4
+ # various regexes used to clean up a title for matching purposes.
5
+ # used in TITLE_REPLACEMENTS hash below
6
+ PUNCTUATION = /[\?\:\!\"\'\,\.\-\/]/
7
+ HTML_ESCAPES = /\&[a-zA-Z]+\;/
8
+ SQUARE_BRACKET_ENCLOSURES = /\[.*?\]/
9
+ PARENTHESIS_ENCLOSURES = /\(.*?\)/
10
+ MULTIPLE_WHITESPACES= /\s+/
11
+ STANDALONE_AMPERSAND = /\s\&\s/
12
+ WIDESCREEN = /widescreen/i
13
+ SPECIAL_EDITION = /special edition/i
14
+
15
+ # array of hashes is intentional as the order is critical
16
+ # the enclosures [...] & (...) must be removed first,
17
+ # then " & " must be replaced by " and ",
18
+ # then html escapes &...; must be replaced by a space,
19
+ # then remaining punctuation is replacesed by a space,
20
+ # finally multiple whitespaces are reduced to single whitespace
21
+ TITLE_REPLACEMENTS = [
22
+ { SQUARE_BRACKET_ENCLOSURES => '' },
23
+ { PARENTHESIS_ENCLOSURES => '' },
24
+ { STANDALONE_AMPERSAND => ' and ' },
25
+ { HTML_ESCAPES => ' ' },
26
+ { WIDESCREEN => ' ' },
27
+ { SPECIAL_EDITION => ' ' },
28
+ { PUNCTUATION => ' ' },
29
+ { MULTIPLE_WHITESPACES => ' ' },
30
+ ]
31
+
32
+ attr_reader :isbn_dvd_hash, :title_isbn_hash, :isbn_title_hash
33
+
34
+ @filespec = nil
35
+
36
+ def initialize(filename = 'Collection.xml')
37
+ @title_isbn_hash = {}
38
+ @isbn_dvd_hash = {}
39
+ @isbn_title_hash = {}
40
+ @filespec = filename
41
+ reload
42
+ save
43
+ end
44
+
45
+ # save as a collection.yaml file unless the existing
46
+ # collection.yaml is newer than the collection.xml
47
+ def save
48
+ unless @filespec.nil?
49
+ yaml_filespec = @filespec.ext('.yaml')
50
+ if !File.exist?(yaml_filespec) || (File.mtime(@filespec) > File.mtime(yaml_filespec))
51
+ AppConfig[:logger].info { "saving: #{yaml_filespec}" }
52
+ File.open(yaml_filespec, "w") do |f|
53
+ YAML.dump(
54
+ {
55
+ :title_isbn_hash => @title_isbn_hash,
56
+ :isbn_title_hash => @isbn_title_hash,
57
+ :isbn_dvd_hash => @isbn_dvd_hash,
58
+ }, f)
59
+ end
60
+ else
61
+ AppConfig[:logger].info { "not saving, yaml file is newer than xml file" }
62
+ end
63
+ else
64
+ AppConfig[:logger].error { "can not save, the filespec is nil" }
65
+ end
66
+ end
67
+
68
+ # load the collection from the collection.yaml if it exists,
69
+ # otherwise from the collection.xml
70
+ def reload
71
+ @title_isbn_hash.clear
72
+ @isbn_dvd_hash.clear
73
+ @isbn_title_hash.clear
74
+ collection = {}
75
+ yaml_filespec = @filespec.ext('.yaml')
76
+ if File.exist?(yaml_filespec) && (File.mtime(yaml_filespec) > File.mtime(@filespec))
77
+ AppConfig[:logger].info { "Loading #{yaml_filespec}" }
78
+ data = YAML.load_file(yaml_filespec)
79
+ @title_isbn_hash = data[:title_isbn_hash]
80
+ @isbn_dvd_hash = data[:isbn_dvd_hash]
81
+ @isbn_title_hash = data[:isbn_title_hash]
82
+ else
83
+ elapsed_time = timer do
84
+ AppConfig[:logger].info { "Loading #{@filespec}" }
85
+ collection = XmlSimple.xml_in(@filespec, { 'KeyToSymbol' => true})
86
+ end
87
+ AppConfig[:logger].info { "XmlSimple.xml_in elapse time: #{elapsed_time.elapsed_time_s}" }
88
+ collection[:dvd].each do |dvd|
89
+ isbn = dvd[:id][0]
90
+ original_title = dvd[:title][0]
91
+ title = Collection.title_pattern(dvd[:title][0])
92
+ unless isbn.blank? || title.blank?
93
+ @title_isbn_hash[title] ||= []
94
+ @title_isbn_hash[title] << isbn
95
+ @isbn_title_hash[isbn] = original_title
96
+ dvd_hash = {}
97
+ dvd_hash[:isbn] = isbn
98
+ dvd_hash[:title] = original_title
99
+ unless dvd[:actors].blank?
100
+ dvd_hash[:actors] = dvd[:actors].compact.collect {|a| a[:actor]}.flatten.compact.collect do |a|
101
+ name = []
102
+ name << a['FirstName'] unless a['FirstName'].blank?
103
+ name << a['MiddleName'] unless a['MiddleName'].blank?
104
+ name << a['LastName'] unless a['LastName'].blank?
105
+ info = {}
106
+ info['name'] = name.join(' ')
107
+ info['role'] = a['Role']
108
+ info
109
+ end
110
+ end
111
+ dvd_hash[:genres] = dvd[:genres].collect{|a| a[:genre]}.flatten unless dvd[:genres].blank?
112
+ dvd_hash[:studios] = dvd[:studios].collect{|a| a[:studio]}.flatten unless dvd[:studios].blank?
113
+ dvd_hash[:productionyear] = [dvd[:productionyear].join(',')] unless dvd[:productionyear].blank?
114
+ dvd_hash[:rating] = [dvd[:rating].join(',')] unless dvd[:rating].blank?
115
+ dvd_hash[:runningtime] = [dvd[:runningtime].join(',')] unless dvd[:runningtime].blank?
116
+ dvd_hash[:released] = [dvd[:released].join(',')] unless dvd[:released].blank?
117
+ dvd_hash[:overview] = [dvd[:overview].join(',')] unless dvd[:overview].blank?
118
+ dvd_hash[:lastedited] = dvd[:lastedited][0] unless dvd[:lastedited].blank?
119
+ @isbn_dvd_hash[isbn] = dvd_hash
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ # == Synopsis
126
+ # The titles found between LMCE's Amazon lookup and DVDProfiler sometimes differ in
127
+ # whether or not a prefix of "The", "A", or "An" is included in the title. Here we
128
+ # create an Array of possible titles with and without these prefix words.
129
+ def Collection.title_permutations(base_title)
130
+ titles = []
131
+ unless base_title.nil? || base_title.empty?
132
+ titles << base_title
133
+ ['the', 'a', 'an'].each do |prefix|
134
+ titles << "#{prefix} " + base_title unless base_title =~ /^#{prefix}\s/
135
+ titles << $1 if base_title =~ /^#{prefix}\s(.*)$/
136
+ end
137
+ end
138
+ titles
139
+ end
140
+
141
+ # == Synopsis
142
+ # the titles found between LMCE's Amazon lookup and DVDProfiler quite often differ in the
143
+ # inclusion of punctuation and capitalization. So we create a pattern of lower case words
144
+ # without punctuation and with single spaces between words.
145
+ def Collection.title_pattern(src_title)
146
+ title = nil
147
+ unless src_title.nil?
148
+ title = src_title.dup
149
+ title.downcase!
150
+ TITLE_REPLACEMENTS.each do |replacement|
151
+ replacement.each do |regex, value|
152
+ title.gsub!(regex, value)
153
+ end
154
+ end
155
+ title.strip!
156
+ end
157
+ title
158
+ end
159
+
160
+ end
161
+
@@ -0,0 +1,103 @@
1
+ ######################################################################
2
+ # my extensions to Module. (taken from rake, named changed to not clash
3
+ # when rake is used for this rails project.
4
+ #
5
+ class Module
6
+ # Check for an existing method in the current class before extending. IF
7
+ # the method already exists, then a warning is printed and the extension is
8
+ # not added. Otherwise the block is yielded and any definitions in the
9
+ # block will take effect.
10
+ #
11
+ # Usage:
12
+ #
13
+ # class String
14
+ # rake_extension("xyz") do
15
+ # def xyz
16
+ # ...
17
+ # end
18
+ # end
19
+ # end
20
+ #
21
+ def my_extension(method)
22
+ unless instance_methods.include?(method.to_s) || instance_methods.include?(method.to_sym)
23
+ yield
24
+ end
25
+ end
26
+ end # module Module
27
+
28
+ ######################################################################
29
+ # User defined methods to be added to String.
30
+ #
31
+ class String
32
+ my_extension("ext") do
33
+ # Replace the file extension with +newext+. If there is no extenson on
34
+ # the string, append the new extension to the end. If the new extension
35
+ # is not given, or is the empty string, remove any existing extension.
36
+ #
37
+ # +ext+ is a user added method for the String class.
38
+ def ext(newext='')
39
+ return self.dup if ['.', '..'].include? self
40
+ if newext != ''
41
+ newext = (newext =~ /^\./) ? newext : ("." + newext)
42
+ end
43
+ dup.sub!(%r(([^/\\])\.[^./\\]*$)) { $1 + newext } || self + newext
44
+ end
45
+ end
46
+ end # class String
47
+
48
+ # == Synopsis
49
+ # add a blank? method to all Objects
50
+ class Object
51
+ my_extension("blank?") do
52
+ # return asserted if object is nil or empty
53
+ # TODO: not the safest coding, probably should dup before stripping. Maybe should also compact
54
+ def blank?
55
+ result = nil?
56
+ unless result
57
+ if respond_to? 'empty?'
58
+ if respond_to? 'strip'
59
+ result = strip.empty?
60
+ else
61
+ result = empty?
62
+ end
63
+ end
64
+ end
65
+ result
66
+ end
67
+ end
68
+ end
69
+
70
+ # == Synopsis
71
+ # add an elapse_time_s method to Numeric
72
+ class Numeric
73
+ my_extension("elapsed_time_s") do
74
+ # return String formated as "HH:MM:SS"
75
+ def elapsed_time_s
76
+ seconds = self
77
+ hours = minutes = 0
78
+ hours = seconds.div 3600
79
+ seconds = seconds - (hours * 3600)
80
+ minutes = seconds.div 60
81
+ seconds = seconds - (minutes * 60)
82
+ sprintf("%2.2d:%2.2d:%2.2d", hours, minutes, seconds)
83
+ end
84
+ end
85
+ end
86
+
87
+ # == Synopsis
88
+ # add a timer method to the Kernel
89
+ module Kernel
90
+
91
+ my_extension("timer") do
92
+ # == Synopsis
93
+ # a simple elapse time for the give block
94
+ # == Usage
95
+ # elapse_seconds = timer {...}
96
+ def timer
97
+ start_time = Time.now
98
+ yield
99
+ Time.now - start_time
100
+ end
101
+ end
102
+ end
103
+
@@ -0,0 +1,95 @@
1
+ class ImdbMovie
2
+ def raw_title
3
+ document.at("h1").innerText
4
+ end
5
+
6
+ def video_game?
7
+ raw_title =~ /\(VG\)/
8
+ end
9
+
10
+ def release_year
11
+ document.search("//h5[text()^='Release Date']/..").innerHTML[/\d{4}/]
12
+ end
13
+
14
+ # return an Array of Strings containing AKA titles
15
+ def also_known_as
16
+ el = document.search("//h5[text()^='Also Known As:']/..").at('h5')
17
+ aka = []
18
+ while(!el.nil?)
19
+ aka << el.to_s unless el.elem?
20
+ el = el.next
21
+ end
22
+ aka.collect!{|a| a.gsub(/\([^\)]*\)/, '').strip}
23
+ aka.uniq!
24
+ aka.collect!{|a| a.blank? ? nil : a}
25
+ aka.compact!
26
+ aka
27
+ end
28
+ end
29
+
30
+ class ImdbSearch
31
+ # Find the IMDB ID for the current search title
32
+ # The find can be helped a lot by including a years option that contains
33
+ # an Array of integers that are the production year (plus/minus a year)
34
+ # and the release year.
35
+ def find_id(options={})
36
+ id = nil
37
+ found_movies = self.movies
38
+ unless found_movies.nil?
39
+ desired_movies = found_movies.select do |m|
40
+ aka = m.also_known_as
41
+ result = imdb_compare_titles(m.title, aka, @query) && !m.video_game? && !m.release_year.blank?
42
+ if result
43
+ AppConfig[:logger].debug { m.title }
44
+ AppConfig[:logger].debug { "m.release_year => #{m.release_year}" }
45
+ unless options[:years].blank?
46
+ result = options[:years].include?(m.release_year.to_i)
47
+ end
48
+ end
49
+ result
50
+ end
51
+ ids = desired_movies.collect{|m| m.id}.uniq.compact
52
+ if ids.length == 1
53
+ id = "tt#{ids[0]}"
54
+ else
55
+ AppConfig[:logger].debug { options[:media_path] } unless options[:media_path].nil?
56
+ AppConfig[:logger].debug { options[:years].pretty_inspect }
57
+ desired_movies.collect{|m| [m.raw_title, m.id, m.title, m.url, m.release_year.blank? ? 'no release date' : m.release_year]}.uniq.compact.each do |m|
58
+ AppConfig[:logger].debug { m.pretty_inspect }
59
+ end
60
+ end
61
+ end
62
+ id
63
+ end
64
+
65
+ protected
66
+
67
+ # compare the imdb title and the imdb title's AKAs against the media title.
68
+ # note, on exact match lookups, IMDB will sometimes set the title to
69
+ # 'trailers and videos' instead of the correct title.
70
+ def imdb_compare_titles(imdb_title, aka_titles, media_title)
71
+ result = fuzzy_compare_titles(imdb_title, media_title)
72
+ unless result
73
+ result = fuzzy_compare_titles(imdb_title, 'trailers and videos')
74
+ unless result
75
+ aka_titles.each do |aka|
76
+ result = fuzzy_compare_titles(aka, media_title)
77
+ break if result
78
+ end
79
+ end
80
+ end
81
+ result
82
+ end
83
+
84
+ # a fuzzy compare that is case insensitive and replaces '&' with 'and'
85
+ # (because that is what IMDB occasionally does)
86
+ def fuzzy_compare_titles(title1, title2)
87
+ t1 = title1.downcase
88
+ t2 = title2.downcase
89
+ (t1 == t2) ||
90
+ (t1.gsub(/&/, 'and') == t2.gsub(/&/, 'and')) ||
91
+ (t1.gsub(/[-:]/, ' ') == t2.gsub(/[-:]/, ' ')) ||
92
+ (t1.gsub('more at imdbpro ?', '') == t2)
93
+ end
94
+ end
95
+
@@ -0,0 +1,44 @@
1
+ # == Synopsis
2
+ # Media encapsulates information about a single media file
3
+ class Media
4
+ attr_reader :media_path, :nfo_files, :image_files, :year, :media_subdirs
5
+ attr_accessor :isbn
6
+
7
+ def initialize(directory, media_file)
8
+ @media_subdirs = File.dirname(media_file)
9
+ @media_path = File.expand_path(File.join(directory, media_file))
10
+ Dir.chdir(File.dirname(@media_path))
11
+ @nfo_files = Dir.glob("*.{#{AppConfig[:nfo_extensions].join(',')}}")
12
+ @image_files = Dir.glob("*.{#{AppConfig[:media_extensions].join(',')}}")
13
+ @year = $1 if File.basename(@media_path, ".*") =~ /\s\-\s(\d{4})/
14
+ end
15
+
16
+ # return the media's title extracted from the filename and cleaned up
17
+ def title
18
+ if @title.nil?
19
+ @title = File.basename(@media_path, ".*")
20
+ @title.gsub!(/\s\-\s\d{4}/, '') # remove year
21
+ @title.gsub!(/\s\-\s0/, '') # remove "- 0", i.e., bad year
22
+ @title.gsub!(/\(\d{4}\)/, '') # remove (year)
23
+ @title.gsub!(/\[.+\]/, '') # remove square brackets
24
+ @title.gsub!(/\s\s+/, ' ') # remove multiple whitespace
25
+ @title = @title.strip # remove leading and trailing whitespace
26
+ end
27
+ @title
28
+ end
29
+
30
+ # return the media's title but with the (year) appended
31
+ def title_with_year
32
+ name = title
33
+ name = "#{name} (#{@year})" unless @year.nil?
34
+ name
35
+ end
36
+
37
+ def to_s
38
+ buf = []
39
+ buf << @media_path
40
+ buf << '-'
41
+ buf << title_with_year
42
+ buf.join(' ')
43
+ end
44
+ end
@@ -0,0 +1,38 @@
1
+ # == Synopsis
2
+ # encapsulation of all media files
3
+ class MediaFiles
4
+ attr_reader :medias, :titles
5
+
6
+ # given:
7
+ # directories Array of String directory pathspecs
8
+ def initialize(directories)
9
+ @medias = []
10
+ directories.each do |dir|
11
+ Dir.chdir(dir)
12
+ @medias += Dir.glob("**/*.{#{AppConfig[:media_extensions].join(',')}}").collect do |filename|
13
+ Media.new(dir, filename)
14
+ end
15
+ end
16
+ @titles = {}
17
+ @medias.each do |media|
18
+ title = media.title_with_year
19
+ @titles[title] ||= []
20
+ @titles[title] << media
21
+ end
22
+ end
23
+
24
+
25
+ # find duplicate titles and return them in a hash
26
+ # where the key is the title and the value is an
27
+ # array of Media objects
28
+ def duplicate_titles
29
+ duplicates = {}
30
+ @titles.each do |title, medias|
31
+ if medias.length > 1
32
+ duplicates[title] = medias
33
+ end
34
+ end
35
+ duplicates
36
+ end
37
+ end
38
+
@@ -0,0 +1,103 @@
1
+ # == Synopsis
2
+ # NFO (info) files
3
+ class NFO
4
+ def initialize(media, dvd_hash)
5
+ @media = media
6
+ @dvd_hash = dvd_hash
7
+ load
8
+ end
9
+
10
+ # save as a .nfo file, creating a backup if the .nfo already exists
11
+ def save
12
+ begin
13
+ nfo_filespec = @media.media_path.ext(".#{AppConfig[:nfo_extension]}")
14
+ nfo_backup_filespec = @media.media_path.ext(".#{AppConfig[:nfo_backup_extension]}")
15
+ File.delete(nfo_backup_filespec) if File.exist?(nfo_backup_filespec)
16
+ File.rename(nfo_filespec, nfo_backup_filespec) if File.exist?(nfo_filespec)
17
+ File.open(nfo_filespec, "w") do |file|
18
+ file.puts(to_nfo(@dvd_hash))
19
+ end
20
+ rescue Exception => e
21
+ AppConfig[:logger].error { "Error saving nfo file - " + e.to_s }
22
+ end
23
+ end
24
+
25
+ def load
26
+ begin
27
+ nfo_filespec = @media.media_path.ext(".#{AppConfig[:nfo_extension]}")
28
+ @movie = XmlSimple.xml_in(nfo_filespec) if File.exist? nfo_filespec
29
+ rescue Exception => e
30
+ AppConfig[:logger].error { "Error loading \"#{nfo_filespec}\" - " + e.to_s }
31
+ end
32
+ end
33
+
34
+ # return a nfo xml String from the given dvd_hash (from Collection)
35
+ def to_nfo(dvd_hash)
36
+ @movie ||= {}
37
+ imdb_id = @movie['id']
38
+ imdb_id = imdb_lookup(dvd_hash) if AppConfig[:imdb_query] && imdb_id.blank?
39
+ @movie['title'] = dvd_hash[:title]
40
+ @movie['mpaa'] = dvd_hash[:rating]
41
+ @movie['year'] = dvd_hash[:productionyear]
42
+ @movie['outline'] = dvd_hash[:overview]
43
+ # @movie['plot'] = dvd_hash[:overview]
44
+ @movie['runtime'] = dvd_hash[:runningtime]
45
+ @movie['genre'] = map_genres((dvd_hash[:genres] + @media.media_subdirs.split('/')).uniq)
46
+ @movie['actor'] = dvd_hash[:actors]
47
+ @movie['id'] = imdb_id unless imdb_id.nil?
48
+ @movie['isbn'] = dvd_hash[:isbn]
49
+
50
+ begin
51
+ XmlSimple.xml_out(@movie, 'NoAttr' => true, 'RootName' => 'movie')
52
+ rescue Exception => e
53
+ AppConfig[:logger].error { "Error creating nfo file - " + e.to_s }
54
+ end
55
+ end
56
+
57
+ protected
58
+
59
+ def map_genres(genres)
60
+ new_genres = []
61
+ genres.each do |genre|
62
+ new_genres << (AppConfig[:genre_maps][genre].nil? ? genre : AppConfig[:genre_maps][genre])
63
+ end
64
+ new_genres.uniq.compact
65
+ end
66
+
67
+ # try to find the imdb id for the movie
68
+ def imdb_lookup(dvd_hash)
69
+ id = nil
70
+ AppConfig[:logger].info { "Searching IMDB for \"#{dvd_hash[:title]}\"" }
71
+ unless dvd_hash[:title].blank?
72
+ years = released_years(dvd_hash)
73
+ begin
74
+ imdb_search = ImdbSearch.new(dvd_hash[:title])
75
+ id = imdb_search.find_id(:years => years, :media_path => @media.media_path)
76
+ rescue Exception => e
77
+ AppConfig[:logger].error { "Error searching IMDB - " + e.to_s }
78
+ AppConfig[:logger].error { e.backtrace.join("\n") }
79
+ end
80
+ end
81
+ AppConfig[:logger].info { "IMDB id => #{id}" } unless id.nil?
82
+ id
83
+ end
84
+
85
+ # Different databases seem to mix up released versus production years.
86
+ # So we combine both into a Array of integer years.
87
+ def released_years(dvd_hash)
88
+ years = []
89
+ unless dvd_hash[:productionyear].blank?
90
+ years += dvd_hash[:productionyear].collect{|y| [y.to_i - 1, y.to_i, y.to_i + 1]}.flatten
91
+ end
92
+ unless dvd_hash[:released].blank?
93
+ years += dvd_hash[:released].collect do |date|
94
+ y = nil
95
+ y = $1.to_i if date =~ /(\d{4})\-/
96
+ y
97
+ end
98
+ end
99
+ years.flatten.uniq.compact.sort
100
+ end
101
+
102
+ end
103
+
@@ -0,0 +1,6 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ module Dvdprofiler2xbmc
5
+ VERSION = '0.0.2'
6
+ end
metadata ADDED
@@ -0,0 +1,157 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: royw-dvdprofiler2xbmc
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.2
5
+ platform: ruby
6
+ authors:
7
+ - Roy Wright
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-03-19 00:00:00 -07:00
13
+ default_executable: dvdprofiler2xbmc
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: activesupport
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 2.0.2
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: xml-simple
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 1.0.12
34
+ version:
35
+ - !ruby/object:Gem::Dependency
36
+ name: porras-imdb
37
+ type: :runtime
38
+ version_requirement:
39
+ version_requirements: !ruby/object:Gem::Requirement
40
+ requirements:
41
+ - - ">="
42
+ - !ruby/object:Gem::Version
43
+ version: 0.0.5
44
+ version:
45
+ - !ruby/object:Gem::Dependency
46
+ name: log4r
47
+ type: :runtime
48
+ version_requirement:
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: 1.0.5
54
+ version:
55
+ - !ruby/object:Gem::Dependency
56
+ name: commandline
57
+ type: :runtime
58
+ version_requirement:
59
+ version_requirements: !ruby/object:Gem::Requirement
60
+ requirements:
61
+ - - ">="
62
+ - !ruby/object:Gem::Version
63
+ version: 0.7.10
64
+ version:
65
+ - !ruby/object:Gem::Dependency
66
+ name: mash
67
+ type: :runtime
68
+ version_requirement:
69
+ version_requirements: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: 0.0.3
74
+ version:
75
+ - !ruby/object:Gem::Dependency
76
+ name: newgem
77
+ type: :development
78
+ version_requirement:
79
+ version_requirements: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ version: 1.2.3
84
+ version:
85
+ - !ruby/object:Gem::Dependency
86
+ name: hoe
87
+ type: :development
88
+ version_requirement:
89
+ version_requirements: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: 1.8.0
94
+ version:
95
+ description: This script will attempt to match up media files from a set of directories to the collection.xml file exported from DVD Profiler. For matches, the script will then create a {moviename}.nfo from the data in collections.xml and also copy the front cover image to {moviename}.tbn. Both files will be placed in the same directory as the source media file. Then on XBMC, set the source content to none to remove the meta data from the library, then set the source content back to Movies to import the media. This time, the data in the .nfo files will be used instead of scraping.
96
+ email:
97
+ - roy@wright.org
98
+ executables:
99
+ - dvdprofiler2xbmc
100
+ extensions: []
101
+
102
+ extra_rdoc_files:
103
+ - History.txt
104
+ - Manifest.txt
105
+ - PostInstall.txt
106
+ - README.rdoc
107
+ files:
108
+ - History.txt
109
+ - Manifest.txt
110
+ - PostInstall.txt
111
+ - README.rdoc
112
+ - Rakefile
113
+ - bin/dvdprofiler2xbmc
114
+ - lib/dvdprofiler2xbmc.rb
115
+ - lib/dvdprofiler2xbmc/app_config.rb
116
+ - lib/dvdprofiler2xbmc/app.rb
117
+ - lib/dvdprofiler2xbmc/cli.rb
118
+ - lib/dvdprofiler2xbmc/collection.rb
119
+ - lib/dvdprofiler2xbmc/extensions.rb
120
+ - lib/dvdprofiler2xbmc/imdb_extensions.rb
121
+ - lib/dvdprofiler2xbmc/media_files.rb
122
+ - lib/dvdprofiler2xbmc/media.rb
123
+ - lib/dvdprofiler2xbmc/nfo.rb
124
+ - test/test_dvdprofiler2xbmc.rb
125
+ - test/test_helper.rb
126
+ - test/test_dvdprofiler2xbmc_cli.rb
127
+ has_rdoc: true
128
+ homepage: http://www.github.com/royw/dvdprofiler2xbmc
129
+ post_install_message: PostInstall.txt
130
+ rdoc_options:
131
+ - --main
132
+ - README.rdoc
133
+ require_paths:
134
+ - lib
135
+ required_ruby_version: !ruby/object:Gem::Requirement
136
+ requirements:
137
+ - - ">="
138
+ - !ruby/object:Gem::Version
139
+ version: "0"
140
+ version:
141
+ required_rubygems_version: !ruby/object:Gem::Requirement
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ version: "0"
146
+ version:
147
+ requirements: []
148
+
149
+ rubyforge_project: dvdprofiler2xbmc
150
+ rubygems_version: 1.2.0
151
+ signing_key:
152
+ specification_version: 2
153
+ summary: This script will attempt to match up media files from a set of directories to the collection.xml file exported from DVD Profiler
154
+ test_files:
155
+ - test/test_dvdprofiler2xbmc.rb
156
+ - test/test_helper.rb
157
+ - test/test_dvdprofiler2xbmc_cli.rb