royw-dvdprofiler2xbmc 0.0.3 → 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +19 -3
- data/Manifest.txt +3 -3
- data/Rakefile +2 -2
- data/dvdprofiler2xbmc.gemspec +59 -0
- data/lib/dvdprofiler2xbmc.rb +1 -1
- data/lib/dvdprofiler2xbmc/app.rb +81 -70
- data/lib/dvdprofiler2xbmc/app_config.rb +20 -17
- data/lib/dvdprofiler2xbmc/cli.rb +62 -58
- data/lib/dvdprofiler2xbmc/collection.rb +7 -7
- data/lib/dvdprofiler2xbmc/extensions.rb +23 -8
- data/lib/dvdprofiler2xbmc/media.rb +106 -24
- data/lib/dvdprofiler2xbmc/media_files.rb +29 -12
- data/lib/dvdprofiler2xbmc/nfo.rb +156 -47
- metadata +7 -7
- data/lib/dvdprofiler2xbmc/imdb_extensions.rb +0 -95
data/History.txt
CHANGED
@@ -1,4 +1,20 @@
|
|
1
|
-
== 0.0.1 2009-03-18
|
2
1
|
|
3
|
-
|
4
|
-
|
2
|
+
ignore file backups and generated content
|
3
|
+
tab cleanup and added Music => Musical default genre mapping
|
4
|
+
tab cleanup
|
5
|
+
general cleanup, minor refactoring
|
6
|
+
added limited support for stacking
|
7
|
+
|
8
|
+
== 0.0.3 2009-03-20
|
9
|
+
add support for moviename.no_imdb_lookup
|
10
|
+
create rc file on first run
|
11
|
+
|
12
|
+
== 0.0.2 2009-03-19
|
13
|
+
bumping version to see if gem will generate
|
14
|
+
forgot to remove old README
|
15
|
+
refactored into gem using newgem
|
16
|
+
|
17
|
+
== 0.0.1 2009-03-18
|
18
|
+
added IMDB scraping currently to get the IMDB ID
|
19
|
+
initial files
|
20
|
+
first commit
|
data/Manifest.txt
CHANGED
@@ -4,13 +4,13 @@ PostInstall.txt
|
|
4
4
|
README.rdoc
|
5
5
|
Rakefile
|
6
6
|
bin/dvdprofiler2xbmc
|
7
|
+
dvdprofiler2xbmc.gemspec
|
7
8
|
lib/dvdprofiler2xbmc.rb
|
8
|
-
lib/dvdprofiler2xbmc/app_config.rb
|
9
9
|
lib/dvdprofiler2xbmc/app.rb
|
10
|
+
lib/dvdprofiler2xbmc/app_config.rb
|
10
11
|
lib/dvdprofiler2xbmc/cli.rb
|
11
12
|
lib/dvdprofiler2xbmc/collection.rb
|
12
13
|
lib/dvdprofiler2xbmc/extensions.rb
|
13
|
-
lib/dvdprofiler2xbmc/imdb_extensions.rb
|
14
|
-
lib/dvdprofiler2xbmc/media_files.rb
|
15
14
|
lib/dvdprofiler2xbmc/media.rb
|
15
|
+
lib/dvdprofiler2xbmc/media_files.rb
|
16
16
|
lib/dvdprofiler2xbmc/nfo.rb
|
data/Rakefile
CHANGED
@@ -11,7 +11,7 @@ $hoe = Hoe.new('dvdprofiler2xbmc', Dvdprofiler2xbmc::VERSION) do |p|
|
|
11
11
|
p.extra_deps = [
|
12
12
|
['activesupport','>= 2.0.2'],
|
13
13
|
['xml-simple','>= 1.0.12'],
|
14
|
-
['
|
14
|
+
['royw-imdb','>= 0.0.8'],
|
15
15
|
['log4r','>= 1.0.5'],
|
16
16
|
['commandline','>= 0.7.10'],
|
17
17
|
['mash','>= 0.0.3']
|
@@ -19,7 +19,7 @@ $hoe = Hoe.new('dvdprofiler2xbmc', Dvdprofiler2xbmc::VERSION) do |p|
|
|
19
19
|
p.extra_dev_deps = [
|
20
20
|
['newgem', ">= #{::Newgem::VERSION}"]
|
21
21
|
]
|
22
|
-
|
22
|
+
|
23
23
|
p.clean_globs |= %w[**/.DS_Store tmp *.log]
|
24
24
|
path = (p.rubyforge_name == p.name) ? p.rubyforge_name : "\#{p.rubyforge_name}/\#{p.name}"
|
25
25
|
p.remote_rdoc_dir = File.join(path.gsub(/^#{p.rubyforge_name}\/?/,''), 'rdoc')
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{dvdprofiler2xbmc}
|
5
|
+
s.version = "0.0.4"
|
6
|
+
|
7
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
8
|
+
s.authors = ["Roy Wright"]
|
9
|
+
s.date = %q{2009-03-25}
|
10
|
+
s.default_executable = %q{dvdprofiler2xbmc}
|
11
|
+
s.description = %q{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.}
|
12
|
+
s.email = ["roy@wright.org"]
|
13
|
+
s.executables = ["dvdprofiler2xbmc"]
|
14
|
+
s.extra_rdoc_files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc"]
|
15
|
+
s.files = ["History.txt", "Manifest.txt", "PostInstall.txt", "README.rdoc", "Rakefile", "bin/dvdprofiler2xbmc", "dvdprofiler2xbmc.gemspec", "lib/dvdprofiler2xbmc.rb", "lib/dvdprofiler2xbmc/app.rb", "lib/dvdprofiler2xbmc/app_config.rb", "lib/dvdprofiler2xbmc/cli.rb", "lib/dvdprofiler2xbmc/collection.rb", "lib/dvdprofiler2xbmc/extensions.rb", "lib/dvdprofiler2xbmc/media.rb", "lib/dvdprofiler2xbmc/media_files.rb", "lib/dvdprofiler2xbmc/nfo.rb", "test/test_dvdprofiler2xbmc.rb", "test/test_helper.rb", "test/test_dvdprofiler2xbmc_cli.rb"]
|
16
|
+
s.has_rdoc = true
|
17
|
+
s.homepage = %q{http://www.github.com/royw/dvdprofiler2xbmc}
|
18
|
+
s.post_install_message = %q{PostInstall.txt}
|
19
|
+
s.rdoc_options = ["--main", "README.rdoc"]
|
20
|
+
s.require_paths = ["lib"]
|
21
|
+
s.rubyforge_project = %q{dvdprofiler2xbmc}
|
22
|
+
s.rubygems_version = %q{1.3.1}
|
23
|
+
s.summary = %q{This script will attempt to match up media files from a set of directories to the collection.xml file exported from DVD Profiler}
|
24
|
+
s.test_files = ["test/test_dvdprofiler2xbmc.rb", "test/test_helper.rb", "test/test_dvdprofiler2xbmc_cli.rb"]
|
25
|
+
|
26
|
+
if s.respond_to? :specification_version then
|
27
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
28
|
+
s.specification_version = 2
|
29
|
+
|
30
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
31
|
+
s.add_runtime_dependency(%q<activesupport>, [">= 2.0.2"])
|
32
|
+
s.add_runtime_dependency(%q<xml-simple>, [">= 1.0.12"])
|
33
|
+
s.add_runtime_dependency(%q<royw-imdb>, [">= 0.0.8"])
|
34
|
+
s.add_runtime_dependency(%q<log4r>, [">= 1.0.5"])
|
35
|
+
s.add_runtime_dependency(%q<commandline>, [">= 0.7.10"])
|
36
|
+
s.add_runtime_dependency(%q<mash>, [">= 0.0.3"])
|
37
|
+
s.add_development_dependency(%q<newgem>, [">= 1.2.3"])
|
38
|
+
s.add_development_dependency(%q<hoe>, [">= 1.8.0"])
|
39
|
+
else
|
40
|
+
s.add_dependency(%q<activesupport>, [">= 2.0.2"])
|
41
|
+
s.add_dependency(%q<xml-simple>, [">= 1.0.12"])
|
42
|
+
s.add_dependency(%q<royw-imdb>, [">= 0.0.8"])
|
43
|
+
s.add_dependency(%q<log4r>, [">= 1.0.5"])
|
44
|
+
s.add_dependency(%q<commandline>, [">= 0.7.10"])
|
45
|
+
s.add_dependency(%q<mash>, [">= 0.0.3"])
|
46
|
+
s.add_dependency(%q<newgem>, [">= 1.2.3"])
|
47
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
48
|
+
end
|
49
|
+
else
|
50
|
+
s.add_dependency(%q<activesupport>, [">= 2.0.2"])
|
51
|
+
s.add_dependency(%q<xml-simple>, [">= 1.0.12"])
|
52
|
+
s.add_dependency(%q<royw-imdb>, [">= 0.0.8"])
|
53
|
+
s.add_dependency(%q<log4r>, [">= 1.0.5"])
|
54
|
+
s.add_dependency(%q<commandline>, [">= 0.7.10"])
|
55
|
+
s.add_dependency(%q<mash>, [">= 0.0.3"])
|
56
|
+
s.add_dependency(%q<newgem>, [">= 1.2.3"])
|
57
|
+
s.add_dependency(%q<hoe>, [">= 1.8.0"])
|
58
|
+
end
|
59
|
+
end
|
data/lib/dvdprofiler2xbmc.rb
CHANGED
data/lib/dvdprofiler2xbmc/app.rb
CHANGED
@@ -6,6 +6,8 @@
|
|
6
6
|
# app.execute
|
7
7
|
# app.report.each {|line| puts line}
|
8
8
|
class DvdProfiler2Xbmc
|
9
|
+
include Singleton
|
10
|
+
|
9
11
|
@interrupted = false
|
10
12
|
|
11
13
|
# A trap("INT") in the Runner calls this to indicate that a ^C has been detected.
|
@@ -23,37 +25,24 @@ class DvdProfiler2Xbmc
|
|
23
25
|
|
24
26
|
def initialize
|
25
27
|
@media_files = nil
|
26
|
-
@collection = nil
|
27
28
|
end
|
28
29
|
|
30
|
+
# the application's main execution loop
|
29
31
|
def execute
|
30
|
-
@media_files = MediaFiles.new(AppConfig[:directories])
|
31
|
-
|
32
32
|
collection_filepath = File.expand_path(AppConfig[:collection_filespec])
|
33
|
-
|
33
|
+
collection = Collection.new(collection_filepath)
|
34
34
|
|
35
|
+
@media_files = MediaFiles.new(AppConfig[:directories], collection)
|
35
36
|
@media_files.titles.each do |title, medias|
|
36
37
|
break if DvdProfiler2Xbmc.interrupted?
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
38
|
+
medias.each do |media|
|
39
|
+
media.load
|
40
|
+
media.update if AppConfig[:do_update]
|
41
|
+
end
|
41
42
|
end
|
42
43
|
|
43
44
|
# set file and directory permissions
|
44
|
-
AppConfig[:directories].each
|
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
|
45
|
+
AppConfig[:directories].each { |dir| set_permissions(dir) }
|
57
46
|
end
|
58
47
|
|
59
48
|
# generate the report.
|
@@ -63,63 +52,44 @@ class DvdProfiler2Xbmc
|
|
63
52
|
buf = []
|
64
53
|
unless DvdProfiler2Xbmc.interrupted?
|
65
54
|
unless @media_files.nil?
|
66
|
-
duplicates
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
end
|
71
|
-
|
72
|
-
missing_isbns = missing_isbn_report
|
73
|
-
unless missing_isbns.empty?
|
74
|
-
buf += missing_isbns
|
75
|
-
end
|
55
|
+
buf += gen_report('duplicates', 'Duplicates')
|
56
|
+
buf += gen_report('missing_isbns', 'Missing ISBNs')
|
57
|
+
buf += gen_report('missing_imdb_ids', 'Missing IMDB IDs')
|
58
|
+
buf += gen_report('missing_thumbnails', 'Missing Thumbnails')
|
76
59
|
end
|
77
60
|
end
|
78
61
|
buf
|
79
62
|
end
|
80
63
|
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
64
|
+
def gen_report(name, heading='')
|
65
|
+
buf = []
|
66
|
+
begin
|
67
|
+
lines = send("#{name}_report")
|
68
|
+
unless lines.empty?
|
69
|
+
buf << ''
|
70
|
+
buf << heading
|
71
|
+
buf += lines
|
89
72
|
end
|
73
|
+
rescue Exception => e
|
74
|
+
AppConfig[:logger].error { "Error generating #{name} report - #{e.to_s}" }
|
90
75
|
end
|
76
|
+
buf
|
91
77
|
end
|
92
78
|
|
93
|
-
|
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.path_to(: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
|
79
|
+
protected
|
111
80
|
|
112
|
-
#
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
unless
|
119
|
-
|
120
|
-
|
121
|
-
end
|
81
|
+
# set the directory and file permissions for all files and directories under
|
82
|
+
# the given directory
|
83
|
+
def set_permissions(dir)
|
84
|
+
Dir.glob(File.join(dir, '**/*')).each do |f|
|
85
|
+
begin
|
86
|
+
if File.directory?(f)
|
87
|
+
File.chmod(AppConfig[:dir_permissions], f) unless AppConfig[:dir_permissions].nil?
|
88
|
+
else
|
89
|
+
File.chmod(AppConfig[:file_permissions], f) unless AppConfig[:file_permissions].nil?
|
122
90
|
end
|
91
|
+
rescue Exception => e
|
92
|
+
AppConfig[:logger].error {e.to_s}
|
123
93
|
end
|
124
94
|
end
|
125
95
|
end
|
@@ -140,15 +110,56 @@ class DvdProfiler2Xbmc
|
|
140
110
|
end
|
141
111
|
|
142
112
|
# unable to find ISBN for these titles report
|
143
|
-
def
|
113
|
+
def missing_isbns_report
|
144
114
|
buf = []
|
145
115
|
@media_files.titles.each do |title, medias|
|
146
116
|
if medias.nil?
|
147
117
|
buf << "No media for #{title}"
|
148
118
|
else
|
149
119
|
if medias[0].isbn.nil?
|
150
|
-
|
151
|
-
medias.each
|
120
|
+
paths = []
|
121
|
+
medias.each do |media|
|
122
|
+
unless File.exist? media.path_to(:no_isbn_extension)
|
123
|
+
paths << " #{media.media_path}"
|
124
|
+
end
|
125
|
+
end
|
126
|
+
unless paths.empty?
|
127
|
+
buf += paths
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
buf
|
133
|
+
end
|
134
|
+
|
135
|
+
def missing_imdb_ids_report
|
136
|
+
buf = []
|
137
|
+
@media_files.titles.each do |title, medias|
|
138
|
+
if medias.nil?
|
139
|
+
buf << "No media for #{title}"
|
140
|
+
else
|
141
|
+
medias.each do |media|
|
142
|
+
if media.imdb_id.blank?
|
143
|
+
buf << " #{title}"
|
144
|
+
break
|
145
|
+
end
|
146
|
+
end
|
147
|
+
end
|
148
|
+
end
|
149
|
+
buf
|
150
|
+
end
|
151
|
+
|
152
|
+
def missing_thumbnails_report
|
153
|
+
buf = []
|
154
|
+
@media_files.titles.each do |title, medias|
|
155
|
+
if medias.nil?
|
156
|
+
buf << "No media for #{title}"
|
157
|
+
else
|
158
|
+
medias.each do |media|
|
159
|
+
thumbnail = media.path_to(:thumbnail_extension)
|
160
|
+
unless File.exist?(thumbnail)
|
161
|
+
buf << " #{thumbnail} #{media.imdb_id.nil? ? '' : media.imdb_id}"
|
162
|
+
end
|
152
163
|
end
|
153
164
|
end
|
154
165
|
end
|
@@ -11,39 +11,39 @@
|
|
11
11
|
module AppConfig
|
12
12
|
@config = Mash.new
|
13
13
|
@yaml_filespec = File.join(ENV['HOME'], '.dvdprofiler2xbmcrc')
|
14
|
-
|
14
|
+
|
15
15
|
def self.[](k)
|
16
16
|
@config[k]
|
17
17
|
end
|
18
|
-
|
18
|
+
|
19
19
|
def self.[]=(k,v)
|
20
20
|
@config[k] = v
|
21
21
|
end
|
22
|
-
|
22
|
+
|
23
23
|
def self.save
|
24
24
|
begin
|
25
25
|
File.delete(@yaml_filespec) if File.exist?(@yaml_filespec)
|
26
26
|
AppConfig[:logger].info { "saving: #{@yaml_filespec}" }
|
27
27
|
File.open(@yaml_filespec, "w") do |f|
|
28
|
-
|
28
|
+
YAML.dump(@config, f)
|
29
29
|
end
|
30
30
|
rescue Exception => e
|
31
31
|
AppConfig[:logger].error { "Error saving config file \"#{@yaml_filespec} - " + e.to_s + "\n" + e.backtrace.join("\n")}
|
32
32
|
end
|
33
33
|
end
|
34
|
-
|
34
|
+
|
35
35
|
def self.load
|
36
36
|
begin
|
37
37
|
if File.exist?(@yaml_filespec)
|
38
|
-
|
38
|
+
@config.merge YAML.load_file(@yaml_filespec)
|
39
39
|
end
|
40
40
|
rescue Exception => e
|
41
41
|
AppConfig[:logger].error { "Error loading config file \"#{@yaml_filespec} - " + e.to_s }
|
42
42
|
end
|
43
43
|
end
|
44
|
-
|
44
|
+
|
45
45
|
def self.default
|
46
|
-
# Note, all paths and extensions are case sensitive
|
46
|
+
# Note, all paths and extensions are case sensitive
|
47
47
|
|
48
48
|
# Array of paths to scan for media
|
49
49
|
# Note, directories underneath these will be added as genres to
|
@@ -53,10 +53,10 @@ module AppConfig
|
|
53
53
|
# Also note, that duplicate genres will be collapsed into single
|
54
54
|
# genres in the .nfo file.
|
55
55
|
@config.directories = [
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
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
60
|
]
|
61
61
|
|
62
62
|
# Typical locations are:
|
@@ -80,17 +80,20 @@ module AppConfig
|
|
80
80
|
@config.nfo_extension = 'nfo'
|
81
81
|
@config.nfo_backup_extension = 'nfo~'
|
82
82
|
@config.no_imdb_extension = 'no_imdb_lookup'
|
83
|
+
@config.no_isbn_extension = 'no_isbn'
|
83
84
|
|
84
85
|
# map some genre names
|
85
86
|
@config.genre_maps = {
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
87
|
+
'SciFi' => 'Science Fiction',
|
88
|
+
'Science-Fiction' => 'Science Fiction',
|
89
|
+
'Anime' => 'Animation',
|
90
|
+
'Musical' => 'Musicals',
|
91
|
+
'Music' => 'Musicals'
|
92
|
+
}
|
91
93
|
|
92
94
|
@config.file_permissions = 0664
|
93
95
|
@config.dir_permissions = 0777
|
94
96
|
@config.imdb_query = true
|
97
|
+
@config.do_update = true
|
95
98
|
end
|
96
99
|
end
|
data/lib/dvdprofiler2xbmc/cli.rb
CHANGED
@@ -3,7 +3,6 @@ require 'yaml'
|
|
3
3
|
require 'xmlsimple'
|
4
4
|
require 'ftools'
|
5
5
|
require 'imdb'
|
6
|
-
require 'pp'
|
7
6
|
require 'mash'
|
8
7
|
require 'log4r'
|
9
8
|
require 'commandline/optionparser'
|
@@ -13,11 +12,17 @@ require 'dvdprofiler2xbmc/app'
|
|
13
12
|
require 'dvdprofiler2xbmc/app_config'
|
14
13
|
require 'dvdprofiler2xbmc/collection'
|
15
14
|
require 'dvdprofiler2xbmc/extensions'
|
16
|
-
require 'dvdprofiler2xbmc/imdb_extensions'
|
17
15
|
require 'dvdprofiler2xbmc/media'
|
18
16
|
require 'dvdprofiler2xbmc/media_files'
|
19
17
|
require 'dvdprofiler2xbmc/nfo'
|
20
18
|
|
19
|
+
# Command Line interface for the Dvdprofiler2Xbmc application.
|
20
|
+
# All application output is via AppConfig[:logger] so we have
|
21
|
+
# to set up the logger here.
|
22
|
+
# Also handle the command line options.
|
23
|
+
# Finally creates an instance of Dvdprofiler2Xbmc and executes
|
24
|
+
# it.
|
25
|
+
|
21
26
|
module Dvdprofiler2xbmc
|
22
27
|
# == Synopsis
|
23
28
|
# Command line exit codes
|
@@ -33,47 +38,47 @@ module Dvdprofiler2xbmc
|
|
33
38
|
|
34
39
|
def self.execute(stdout, arguments=[])
|
35
40
|
exit_code = ExitCode::OK
|
36
|
-
|
37
|
-
# we start a STDOUT logger, but it will be switched after
|
41
|
+
|
42
|
+
# we start a STDOUT logger, but it will be switched after
|
38
43
|
# the config files are read if config[:logger_output] is set
|
39
44
|
logger = Log4r::Logger.new('dvdprofiler2xbmc')
|
40
45
|
logger.outputters = Log4r::StdoutOutputter.new(:console)
|
41
46
|
logger.level = Log4r::DEBUG
|
42
|
-
|
47
|
+
|
43
48
|
begin
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
49
|
+
# trap ^C interrupts and let the app instance cleanly exit any long loops
|
50
|
+
Signal.trap("INT") {DvdProfiler2Xbmc.interrupt}
|
51
|
+
|
52
|
+
# parse the command line
|
53
|
+
options = setupParser()
|
54
|
+
od = options.parse(arguments)
|
55
|
+
|
56
|
+
# load config values
|
57
|
+
AppConfig.default
|
58
|
+
|
59
|
+
# the first reinitialize_logger adds the command line logging options to the default config
|
60
|
+
# then we load the config files
|
61
|
+
# then we run reinitialize_logger again to modify the logger for any logging options from the config files
|
62
|
+
|
63
|
+
reinitialize_logger(logger, od["--quiet"], od["--debug"])
|
64
|
+
AppConfig.load
|
65
|
+
AppConfig[:imdb_query] = !od["--no_imdb_query"]
|
66
|
+
AppConfig.save
|
67
|
+
reinitialize_logger(logger, od["--quiet"], od["--debug"])
|
68
|
+
|
69
|
+
AppConfig[:do_update] = !od["--reports"]
|
70
|
+
|
71
|
+
unless od["--help"] || od["--version"]
|
72
|
+
# create and execute class instance here
|
73
|
+
app = DvdProfiler2Xbmc.instance
|
74
|
+
app.execute
|
75
|
+
app.report.each {|line| puts line}
|
76
|
+
end
|
72
77
|
rescue Exception => eMsg
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
78
|
+
logger.error {eMsg.to_s}
|
79
|
+
logger.error {options.to_s}
|
80
|
+
logger.error {eMsg.backtrace.join("\n")}
|
81
|
+
exit_code = ExitCode::CRITICAL
|
77
82
|
end
|
78
83
|
exit_code
|
79
84
|
end
|
@@ -82,33 +87,33 @@ module Dvdprofiler2xbmc
|
|
82
87
|
# Returns:: OptionParser instances
|
83
88
|
def self.setupParser()
|
84
89
|
options = OptionParser.new()
|
85
|
-
options << Option.new(:flag, :names => %w(--help -h),
|
86
|
-
|
87
|
-
|
88
|
-
options << Option.new(:flag, :names => %w(--version -v),
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
options << Option.new(:flag, :names => %w(--
|
93
|
-
options << Option.new(:flag, :names => %w(--
|
94
|
-
options << Option.new(:flag, :names => %w(--
|
90
|
+
options << Option.new(:flag, :names => %w(--help -h),
|
91
|
+
:opt_found => lambda {Log4r::Logger['dvdprofiler2xbmc'].info{options.to_s}},
|
92
|
+
:opt_description => "This usage information")
|
93
|
+
options << Option.new(:flag, :names => %w(--version -v),
|
94
|
+
:opt_found => lambda {Log4r::Logger['dvdprofiler2xbmc'].info{"Dvdprofiler2xbmc #{Dvdprofiler2xbmc::VERSION}"}},
|
95
|
+
:opt_description => "This version of dvdprofiler2xbmc")
|
96
|
+
options << Option.new(:flag, :names => %w(--no_imdb_query -n), :opt_description => 'Do not query IMDB.com')
|
97
|
+
options << Option.new(:flag, :names => %w(--quiet -q), :opt_description => 'Display error messages only')
|
98
|
+
options << Option.new(:flag, :names => %w(--debug -d), :opt_description => 'Display debug messages')
|
99
|
+
options << Option.new(:flag, :names => %w(--reports -r), :opt_description => 'Display reports only. Do not do any updates.')
|
95
100
|
options
|
96
101
|
end
|
97
|
-
|
102
|
+
|
98
103
|
# Reinitialize the logger using the loaded config.
|
99
104
|
# logger:: logger for any user messages
|
100
105
|
# config:: is the application's config hash.
|
101
106
|
def self.reinitialize_logger(logger, quiet, debug)
|
102
107
|
# switch the logger to the one specified in the config files
|
103
108
|
unless AppConfig[:logfile].nil?
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
109
|
+
logfile_outputter = Log4r::RollingFileOutputter.new(:logfile, :filename => AppConfig[:logfile], :maxsize => 1000000 )
|
110
|
+
logger.add logfile_outputter
|
111
|
+
logfile_outputter.level = Log4r::INFO
|
112
|
+
Log4r::Outputter[:logfile].formatter = Log4r::PatternFormatter.new(:pattern => "[%l] %d :: %M")
|
113
|
+
unless AppConfig[:logfile_level].nil?
|
114
|
+
level_map = {'DEBUG' => Log4r::DEBUG, 'INFO' => Log4r::INFO, 'WARN' => Log4r::WARN}
|
115
|
+
logfile_outputter.level = level_map[AppConfig[:logfile_level]] || Log4r::INFO
|
116
|
+
end
|
112
117
|
end
|
113
118
|
Log4r::Outputter[:console].level = Log4r::INFO
|
114
119
|
Log4r::Outputter[:console].level = Log4r::WARN if quiet
|
@@ -118,5 +123,4 @@ module Dvdprofiler2xbmc
|
|
118
123
|
end
|
119
124
|
end
|
120
125
|
end
|
121
|
-
|
122
|
-
|
126
|
+
|
@@ -42,7 +42,7 @@ class Collection
|
|
42
42
|
save
|
43
43
|
end
|
44
44
|
|
45
|
-
# save as a collection.yaml file unless the existing
|
45
|
+
# save as a collection.yaml file unless the existing
|
46
46
|
# collection.yaml is newer than the collection.xml
|
47
47
|
def save
|
48
48
|
unless @filespec.nil?
|
@@ -65,7 +65,7 @@ class Collection
|
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
68
|
-
# load the collection from the collection.yaml if it exists,
|
68
|
+
# load the collection from the collection.yaml if it exists,
|
69
69
|
# otherwise from the collection.xml
|
70
70
|
def reload
|
71
71
|
@title_isbn_hash.clear
|
@@ -102,10 +102,10 @@ class Collection
|
|
102
102
|
name << a['FirstName'] unless a['FirstName'].blank?
|
103
103
|
name << a['MiddleName'] unless a['MiddleName'].blank?
|
104
104
|
name << a['LastName'] unless a['LastName'].blank?
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
105
|
+
info = {}
|
106
|
+
info['name'] = name.join(' ')
|
107
|
+
info['role'] = a['Role']
|
108
|
+
info
|
109
109
|
end
|
110
110
|
end
|
111
111
|
dvd_hash[:genres] = dvd[:genres].collect{|a| a[:genre]}.flatten unless dvd[:genres].blank?
|
@@ -148,7 +148,7 @@ class Collection
|
|
148
148
|
title = src_title.dup
|
149
149
|
title.downcase!
|
150
150
|
TITLE_REPLACEMENTS.each do |replacement|
|
151
|
-
replacement.each do |regex, value|
|
151
|
+
replacement.each do |regex, value|
|
152
152
|
title.gsub!(regex, value)
|
153
153
|
end
|
154
154
|
end
|
@@ -54,13 +54,13 @@ class Object
|
|
54
54
|
def blank?
|
55
55
|
result = nil?
|
56
56
|
unless result
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
57
|
+
if respond_to? 'empty?'
|
58
|
+
if respond_to? 'strip'
|
59
|
+
result = strip.empty?
|
60
|
+
else
|
61
|
+
result = empty?
|
62
|
+
end
|
63
|
+
end
|
64
64
|
end
|
65
65
|
result
|
66
66
|
end
|
@@ -83,7 +83,22 @@ class Numeric
|
|
83
83
|
end
|
84
84
|
end
|
85
85
|
end
|
86
|
-
|
86
|
+
|
87
|
+
# # == Synopsis
|
88
|
+
# # add a mkdirs method to the File class
|
89
|
+
# class File
|
90
|
+
# ##
|
91
|
+
# # make directories including any missing in the path
|
92
|
+
# #
|
93
|
+
# # @param [String] dirspec the path to make sure exists
|
94
|
+
# def File.mkdirs(dirspec)
|
95
|
+
# unless File.exists?(dirspec)
|
96
|
+
# mkdirs(File.dirname(dirspec))
|
97
|
+
# Dir.mkdir(dirspec)
|
98
|
+
# end
|
99
|
+
# end
|
100
|
+
# end
|
101
|
+
|
87
102
|
# == Synopsis
|
88
103
|
# add a timer method to the Kernel
|
89
104
|
module Kernel
|
@@ -1,35 +1,56 @@
|
|
1
1
|
# == Synopsis
|
2
2
|
# Media encapsulates information about a single media file
|
3
3
|
class Media
|
4
|
-
attr_reader :media_path, :
|
5
|
-
attr_accessor :isbn
|
4
|
+
attr_reader :media_path, :image_files, :year, :media_subdirs, :title, :title_with_year
|
6
5
|
|
7
6
|
DISC_NUMBER_REGEX = /\.(cd|part|disk|disc)\d+/i
|
8
7
|
|
9
|
-
def initialize(directory, media_file)
|
8
|
+
def initialize(directory, media_file, collection)
|
9
|
+
@collection = collection
|
10
10
|
@media_subdirs = File.dirname(media_file)
|
11
11
|
@media_path = File.expand_path(File.join(directory, media_file))
|
12
12
|
Dir.chdir(File.dirname(@media_path))
|
13
13
|
@nfo_files = Dir.glob("*.{#{AppConfig[:nfo_extensions].join(',')}}")
|
14
|
-
@image_files = Dir.glob("*.{#{AppConfig[:
|
14
|
+
@image_files = Dir.glob("*.{#{AppConfig[:thumbnail_extension]}}")
|
15
15
|
@year = $1 if File.basename(@media_path) =~ /\s\-\s(\d{4})/
|
16
|
+
@title = find_title(@media_path)
|
17
|
+
@title_with_year = find_title_with_year(@title, @year)
|
18
|
+
|
19
|
+
@nfo = NFO.new(self, @collection)
|
20
|
+
@loaded = false
|
16
21
|
end
|
17
22
|
|
18
|
-
#
|
19
|
-
def
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
23
|
+
# load existing meta-data
|
24
|
+
def load
|
25
|
+
@nfo.load
|
26
|
+
@loaded = true
|
27
|
+
end
|
28
|
+
|
29
|
+
# update the meta-data and thumbnails
|
30
|
+
def update
|
31
|
+
load unless @loaded
|
32
|
+
@nfo.update
|
33
|
+
update_thumbnail
|
34
|
+
end
|
35
|
+
|
36
|
+
# return the ISBN or nil
|
37
|
+
def isbn
|
38
|
+
@nfo.isbn
|
39
|
+
end
|
40
|
+
|
41
|
+
# return the IMDB ID or nil
|
42
|
+
def imdb_id
|
43
|
+
@nfo.imdb_id
|
31
44
|
end
|
32
45
|
|
46
|
+
# return a path to a file file based on the media's filespec
|
47
|
+
# but without any stacking parts and with the given extension
|
48
|
+
# instead of the media's extension.
|
49
|
+
# Example:
|
50
|
+
# media_path = '/a/b/c.m4v'
|
51
|
+
# path_to('nfo') => '/a/b/c.nfo'
|
52
|
+
# media_path = '/a/b/c.part1.m4v'
|
53
|
+
# path_to('nfo') => '/a/b/c.nfo'
|
33
54
|
def path_to(type)
|
34
55
|
# ditch all extensions (ex, a.b => a, a.cd1.b => a)
|
35
56
|
new_path = File.basename(@media_path, ".*").gsub(DISC_NUMBER_REGEX, '')
|
@@ -39,13 +60,6 @@ class Media
|
|
39
60
|
File.join(File.dirname(@media_path), new_path)
|
40
61
|
end
|
41
62
|
|
42
|
-
# return the media's title but with the (year) appended
|
43
|
-
def title_with_year
|
44
|
-
name = title
|
45
|
-
name = "#{name} (#{@year})" unless @year.nil?
|
46
|
-
name
|
47
|
-
end
|
48
|
-
|
49
63
|
def to_s
|
50
64
|
buf = []
|
51
65
|
buf << @media_path
|
@@ -53,4 +67,72 @@ class Media
|
|
53
67
|
buf << title_with_year
|
54
68
|
buf.join(' ')
|
55
69
|
end
|
70
|
+
|
71
|
+
protected
|
72
|
+
|
73
|
+
# update the movie's thumbnail (.tbn) image
|
74
|
+
def update_thumbnail
|
75
|
+
if @nfo.isbn.blank?
|
76
|
+
unless @nfo.imdb_id.blank?
|
77
|
+
if @image_files.empty?
|
78
|
+
fetch_imdb_thumbnail(@nfo.imdb_id)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
else
|
82
|
+
copy_thumbnail(@nfo.isbn)
|
83
|
+
end
|
84
|
+
@nfo.save
|
85
|
+
end
|
86
|
+
|
87
|
+
# fetch the thumbnail from IMDB and save as path_to('tbn')
|
88
|
+
def fetch_imdb_thumbnail(imdb_id)
|
89
|
+
imdb_movie = ImdbMovie.new(imdb_id.gsub(/^tt/, ''))
|
90
|
+
source_uri = imdb_movie.poster.image
|
91
|
+
dest_image_filespec = path_to(:thumbnail_extension)
|
92
|
+
puts "fetch_imdb_thumbnail(#{imdb_id}) => #{source_uri}"
|
93
|
+
begin
|
94
|
+
File.open(dest_image_filespec, "wb") {|f| f.write(open(source_uri).read)}
|
95
|
+
rescue Exception => e
|
96
|
+
AppConfig[:logger].error { "Error downloading image from \"#{source_uri}\" to \"#{dest_image_filespec}\" - #{e.to_s}" }
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# copy images from .../isbn.jpg to .../basename.jpg
|
101
|
+
def copy_thumbnail(isbn)
|
102
|
+
src_image_filespec = File.join(AppConfig[:images_dir], "#{isbn}f.jpg")
|
103
|
+
if File.exist?(src_image_filespec)
|
104
|
+
dest_image_filespec = path_to(:thumbnail_extension)
|
105
|
+
do_copy = true
|
106
|
+
if File.exist?(dest_image_filespec)
|
107
|
+
if File.mtime(src_image_filespec) <= File.mtime(dest_image_filespec)
|
108
|
+
do_copy = false
|
109
|
+
end
|
110
|
+
end
|
111
|
+
begin
|
112
|
+
File.copy(src_image_filespec, dest_image_filespec) if do_copy
|
113
|
+
rescue Exception => e
|
114
|
+
AppConfig[:logger].error {e.to_s}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
# return the media's title extracted from the filename and cleaned up
|
120
|
+
def find_title(media_path)
|
121
|
+
# ditch extensions including disc number (ex, a.part2.b => a, a.cd1.b => a)
|
122
|
+
title = File.basename(media_path, ".*").gsub(DISC_NUMBER_REGEX, '')
|
123
|
+
title.gsub!(/\s\-\s\d{4}/, '') # remove year
|
124
|
+
title.gsub!(/\s\-\s0/, '') # remove "- 0", i.e., bad year
|
125
|
+
title.gsub!(/\(\d{4}\)/, '') # remove (year)
|
126
|
+
title.gsub!(/\[.+\]/, '') # remove square brackets
|
127
|
+
title.gsub!(/\s\s+/, ' ') # remove multiple whitespace
|
128
|
+
title.strip # remove leading and trailing whitespace
|
129
|
+
end
|
130
|
+
|
131
|
+
# return the media's title but with the (year) appended
|
132
|
+
def find_title_with_year(title, year)
|
133
|
+
name = title
|
134
|
+
name = "#{name} (#{year})" unless year.nil?
|
135
|
+
name
|
136
|
+
end
|
137
|
+
|
56
138
|
end
|
@@ -1,33 +1,49 @@
|
|
1
1
|
# == Synopsis
|
2
2
|
# encapsulation of all media files
|
3
3
|
class MediaFiles
|
4
|
-
attr_reader :medias, :titles
|
4
|
+
attr_reader :medias, :titles, :duplicate_titles
|
5
5
|
|
6
6
|
# given:
|
7
7
|
# directories Array of String directory pathspecs
|
8
|
-
def initialize(directories)
|
9
|
-
@
|
8
|
+
def initialize(directories, collection)
|
9
|
+
@collection = collection
|
10
|
+
@medias = find_medias(directories)
|
11
|
+
@titles = find_titles(@medias)
|
12
|
+
@duplicate_titles = find_duplicate_titles(@titles)
|
13
|
+
end
|
14
|
+
|
15
|
+
protected
|
16
|
+
|
17
|
+
# find all the media files in the given set of directories
|
18
|
+
def find_medias(directories)
|
19
|
+
medias = []
|
10
20
|
directories.each do |dir|
|
11
21
|
Dir.chdir(dir)
|
12
|
-
|
13
|
-
Media.new(dir, filename)
|
22
|
+
medias += Dir.glob("**/*.{#{AppConfig[:media_extensions].join(',')}}").collect do |filename|
|
23
|
+
Media.new(dir, filename, @collection)
|
14
24
|
end
|
15
25
|
end
|
16
|
-
|
17
|
-
|
26
|
+
medias
|
27
|
+
end
|
28
|
+
|
29
|
+
# return a hash where the key is the media's title and
|
30
|
+
# the value is an Array of Media instances
|
31
|
+
def find_titles(medias)
|
32
|
+
titles = {}
|
33
|
+
medias.each do |media|
|
18
34
|
title = media.title_with_year
|
19
|
-
|
20
|
-
|
35
|
+
titles[title] ||= []
|
36
|
+
titles[title] << media
|
21
37
|
end
|
38
|
+
titles
|
22
39
|
end
|
23
40
|
|
24
|
-
|
25
41
|
# find duplicate titles and return them in a hash
|
26
42
|
# where the key is the title and the value is an
|
27
43
|
# array of Media objects
|
28
|
-
def
|
44
|
+
def find_duplicate_titles(titles)
|
29
45
|
duplicates = {}
|
30
|
-
|
46
|
+
titles.each do |title, medias|
|
31
47
|
base_medias = medias.collect{|media| media.path_to(:base) }.uniq
|
32
48
|
if base_medias.length > 1
|
33
49
|
duplicates[title] = medias
|
@@ -35,5 +51,6 @@ class MediaFiles
|
|
35
51
|
end
|
36
52
|
duplicates
|
37
53
|
end
|
54
|
+
|
38
55
|
end
|
39
56
|
|
data/lib/dvdprofiler2xbmc/nfo.rb
CHANGED
@@ -1,81 +1,146 @@
|
|
1
|
+
|
1
2
|
# == Synopsis
|
2
3
|
# NFO (info) files
|
4
|
+
#
|
5
|
+
# the @movie hash has keys that map directly to the .nfo file
|
6
|
+
# the @dvd_hash has keys that map to DVD Profiler's Collection.xml file
|
3
7
|
class NFO
|
4
|
-
def initialize(media,
|
8
|
+
def initialize(media, collection)
|
5
9
|
@media = media
|
6
|
-
@
|
7
|
-
|
10
|
+
@collection = collection
|
11
|
+
@dvd_hash = {}
|
12
|
+
@movie = {}
|
8
13
|
end
|
9
14
|
|
10
15
|
# save as a .nfo file, creating a backup if the .nfo already exists
|
11
16
|
def save
|
12
17
|
begin
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
18
|
+
unless @movie.empty?
|
19
|
+
@movie['title'] = @media.title if @movie['title'].blank?
|
20
|
+
nfo_filespec = @media.path_to(:nfo_extension)
|
21
|
+
nfo_backup_filespec = @media.path_to(:nfo_backup_extension)
|
22
|
+
File.delete(nfo_backup_filespec) if File.exist?(nfo_backup_filespec)
|
23
|
+
File.rename(nfo_filespec, nfo_backup_filespec) if File.exist?(nfo_filespec)
|
24
|
+
File.open(nfo_filespec, "w") do |file|
|
25
|
+
file.puts(to_xml)
|
26
|
+
end
|
19
27
|
end
|
20
28
|
rescue Exception => e
|
21
|
-
AppConfig[:logger].error { "Error saving nfo file - " + e.to_s }
|
29
|
+
AppConfig[:logger].error { "Error saving nfo file - " + e.to_s + "\n" + e.backtrace.join("\n")}
|
22
30
|
end
|
23
31
|
end
|
24
32
|
|
33
|
+
# load the .nfo file into the @movie hash
|
25
34
|
def load
|
35
|
+
nfo_filespec = @media.path_to(:nfo_extension)
|
26
36
|
begin
|
27
|
-
nfo_filespec
|
28
|
-
|
37
|
+
if File.exist?(nfo_filespec) && (File.size(nfo_filespec) > 1)
|
38
|
+
File.open(nfo_filespec) do |file|
|
39
|
+
@movie = XmlSimple.xml_in(file)
|
40
|
+
end
|
41
|
+
end
|
29
42
|
rescue Exception => e
|
30
|
-
AppConfig[:logger].error { "Error loading \"#{nfo_filespec}\" - " + e.to_s }
|
43
|
+
AppConfig[:logger].error { "Error loading \"#{nfo_filespec}\" - " + e.to_s + "\n" + e.backtrace.join("\n") }
|
44
|
+
raise e
|
31
45
|
end
|
32
46
|
end
|
33
47
|
|
34
|
-
#
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
48
|
+
# merge meta-data from the DVD Profiler collection.xml and from IMDB
|
49
|
+
# into the @movie hash
|
50
|
+
def update
|
51
|
+
begin
|
52
|
+
load_from_collection
|
53
|
+
if AppConfig[:imdb_query] && imdb_id.blank?
|
54
|
+
load_from_imdb
|
41
55
|
end
|
56
|
+
@movie.merge!(to_movie(@dvd_hash))
|
57
|
+
rescue Exception => e
|
58
|
+
AppConfig[:logger].error { "Error updating \"#{nfo_filespec}\" - " + e.to_s + "\n" + e.backtrace.join("\n") }
|
59
|
+
raise e
|
42
60
|
end
|
43
|
-
|
44
|
-
@movie['mpaa'] = dvd_hash[:rating]
|
45
|
-
@movie['year'] = dvd_hash[:productionyear]
|
46
|
-
@movie['outline'] = dvd_hash[:overview]
|
47
|
-
# @movie['plot'] = dvd_hash[:overview]
|
48
|
-
@movie['runtime'] = dvd_hash[:runningtime]
|
49
|
-
@movie['genre'] = map_genres((dvd_hash[:genres] + @media.media_subdirs.split('/')).uniq)
|
50
|
-
@movie['actor'] = dvd_hash[:actors]
|
51
|
-
@movie['id'] = imdb_id unless imdb_id.nil?
|
52
|
-
@movie['isbn'] = dvd_hash[:isbn]
|
61
|
+
end
|
53
62
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
63
|
+
# return the ISBN or nil
|
64
|
+
def isbn
|
65
|
+
if @dvd_hash[:isbn].blank?
|
66
|
+
@dvd_hash[:isbn] = @movie['isbn']
|
58
67
|
end
|
68
|
+
@dvd_hash[:isbn]
|
69
|
+
end
|
70
|
+
|
71
|
+
# set the ISBN
|
72
|
+
def isbn=(isbn)
|
73
|
+
@dvd_hash[:isbn] = isbn
|
74
|
+
end
|
75
|
+
|
76
|
+
# return the IMDB ID or nil
|
77
|
+
def imdb_id
|
78
|
+
if @dvd_hash[:imdb_id].nil?
|
79
|
+
@dvd_hash[:imdb_id] = @movie['id']
|
80
|
+
end
|
81
|
+
unless @dvd_hash[:imdb_id].nil?
|
82
|
+
# make sure is not an array
|
83
|
+
@dvd_hash[:imdb_id] = [@dvd_hash[:imdb_id]].flatten.uniq.compact.first
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
# set the IMDB ID
|
88
|
+
def imdb_id=(id)
|
89
|
+
@dvd_hash[:imdb_id] = id
|
59
90
|
end
|
60
91
|
|
61
92
|
protected
|
62
93
|
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
94
|
+
# load @dvd_hash from the collection
|
95
|
+
def load_from_collection
|
96
|
+
# find ISBN for each title and assign to the media
|
97
|
+
if isbn.nil?
|
98
|
+
title_pattern = Collection.title_pattern(@media.title)
|
99
|
+
unless @collection.title_isbn_hash[title_pattern].nil?
|
100
|
+
isbn = [@collection.title_isbn_hash[title_pattern]].flatten.uniq.compact.first
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
# merge the meta-data from the collection to dvd_hash
|
105
|
+
unless isbn.nil?
|
106
|
+
collection_hash = @collection.isbn_dvd_hash[isbn]
|
107
|
+
@dvd_hash.merge!(collection_hash) unless collection_hash.blank?
|
67
108
|
end
|
68
|
-
|
109
|
+
end
|
110
|
+
|
111
|
+
# load data from IMDB.com and merge into the @dvd_hash
|
112
|
+
def load_from_imdb
|
113
|
+
unless File.exist?(@media.path_to(:no_imdb_extension))
|
114
|
+
years = (@media.year.nil? ? released_years(@dvd_hash) : [@media.year])
|
115
|
+
title = (@dvd_hash[:title].nil? ? @media.title : @dvd_hash[:title])
|
116
|
+
self.imdb_id = imdb_lookup(title, years) if self.imdb_id.blank?
|
117
|
+
unless self.imdb_id.nil?
|
118
|
+
imdb_movie = ImdbMovie.new(self.imdb_id.gsub(/^tt/, ''))
|
119
|
+
@dvd_hash.merge!(to_dvd_hash(imdb_movie))
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# convert the @movie hash into xml and return the xml as a String
|
125
|
+
def to_xml
|
126
|
+
xml = ''
|
127
|
+
begin
|
128
|
+
xml = XmlSimple.xml_out(@movie, 'NoAttr' => true, 'RootName' => 'movie')
|
129
|
+
rescue Exception => e
|
130
|
+
AppConfig[:logger].error { "Error creating nfo file - " + e.to_s}
|
131
|
+
raise e
|
132
|
+
end
|
133
|
+
xml
|
69
134
|
end
|
70
135
|
|
71
136
|
# try to find the imdb id for the movie
|
72
|
-
def imdb_lookup(
|
137
|
+
def imdb_lookup(title, years)
|
73
138
|
id = nil
|
74
|
-
|
75
|
-
|
76
|
-
|
139
|
+
|
140
|
+
AppConfig[:logger].info { "Searching IMDB for \"#{title}\"" }
|
141
|
+
unless title.blank?
|
77
142
|
begin
|
78
|
-
imdb_search = ImdbSearch.new(
|
143
|
+
imdb_search = ImdbSearch.new(title)
|
79
144
|
id = imdb_search.find_id(:years => years, :media_path => @media.media_path)
|
80
145
|
rescue Exception => e
|
81
146
|
AppConfig[:logger].error { "Error searching IMDB - " + e.to_s }
|
@@ -95,13 +160,57 @@ class NFO
|
|
95
160
|
end
|
96
161
|
unless dvd_hash[:released].blank?
|
97
162
|
years += dvd_hash[:released].collect do |date|
|
98
|
-
|
99
|
-
|
100
|
-
|
163
|
+
y = nil
|
164
|
+
y = $1.to_i if date =~ /(\d{4})\-/
|
165
|
+
y
|
101
166
|
end
|
102
167
|
end
|
103
168
|
years.flatten.uniq.compact.sort
|
104
169
|
end
|
105
170
|
|
171
|
+
# given a ImdbMovie instance, extract meta-data into and return a dvd_hash
|
172
|
+
def to_dvd_hash(imdb_movie)
|
173
|
+
dvd_hash = {}
|
174
|
+
dvd_hash[:title] = imdb_movie.title
|
175
|
+
dvd_hash[:imdb_id] = 'tt' + imdb_movie.id.gsub(/^tt/,'') unless imdb_movie.id.blank?
|
176
|
+
dvd_hash[:rating] = imdb_movie.mpaa
|
177
|
+
dvd_hash[:rating] ||= imdb_movie.certifications['USA']
|
178
|
+
dvd_hash[:productionyear] = imdb_movie.release_year
|
179
|
+
dvd_hash[:plot] = imdb_movie.plot
|
180
|
+
dvd_hash[:runningtime] = imdb_movie.length
|
181
|
+
dvd_hash[:genre] = imdb_movie.genres
|
182
|
+
dvd_hash[:actor] = imdb_movie.cast_members
|
183
|
+
dvd_hash
|
184
|
+
end
|
185
|
+
|
186
|
+
# map the given dvd_hash into a @movie hash
|
187
|
+
def to_movie(dvd_hash)
|
188
|
+
dvd_hash[:genres] ||= []
|
189
|
+
genres = map_genres((dvd_hash[:genres] + @media.media_subdirs.split('/')).uniq)
|
190
|
+
movie = {}
|
191
|
+
movie['title'] = dvd_hash[:title]
|
192
|
+
movie['mpaa'] = dvd_hash[:rating] unless dvd_hash[:rating].blank?
|
193
|
+
movie['year'] = dvd_hash[:productionyear] unless dvd_hash[:productionyear].blank?
|
194
|
+
movie['outline'] = dvd_hash[:overview] unless dvd_hash[:overview].blank?
|
195
|
+
movie['plot'] = dvd_hash[:plot] unless dvd_hash[:plot].blank?
|
196
|
+
movie['runtime'] = dvd_hash[:runningtime] unless dvd_hash[:runningtime].blank?
|
197
|
+
movie['actor'] = dvd_hash[:actors] unless dvd_hash[:actors].blank?
|
198
|
+
movie['isbn'] = dvd_hash[:isbn] unless dvd_hash[:isbn].blank?
|
199
|
+
movie['id'] = dvd_hash[:imdb_id] unless dvd_hash[:imdb_id].blank?
|
200
|
+
movie['genre'] = genres unless genres.blank?
|
201
|
+
movie
|
202
|
+
end
|
203
|
+
|
204
|
+
# map the given genres using the AppConfig[:genre_maps].
|
205
|
+
# given an Array of String genres
|
206
|
+
# returns an Array of String genres that have been mapped, are unique, and do not include any nils
|
207
|
+
def map_genres(genres)
|
208
|
+
new_genres = []
|
209
|
+
genres.each do |genre|
|
210
|
+
new_genres << (AppConfig[:genre_maps][genre].nil? ? genre : AppConfig[:genre_maps][genre])
|
211
|
+
end
|
212
|
+
new_genres.uniq.compact
|
213
|
+
end
|
214
|
+
|
106
215
|
end
|
107
216
|
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: royw-dvdprofiler2xbmc
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Roy Wright
|
@@ -9,7 +9,7 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
|
12
|
-
date: 2009-03-
|
12
|
+
date: 2009-03-25 00:00:00 -07:00
|
13
13
|
default_executable: dvdprofiler2xbmc
|
14
14
|
dependencies:
|
15
15
|
- !ruby/object:Gem::Dependency
|
@@ -33,14 +33,14 @@ dependencies:
|
|
33
33
|
version: 1.0.12
|
34
34
|
version:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
|
-
name:
|
36
|
+
name: royw-imdb
|
37
37
|
type: :runtime
|
38
38
|
version_requirement:
|
39
39
|
version_requirements: !ruby/object:Gem::Requirement
|
40
40
|
requirements:
|
41
41
|
- - ">="
|
42
42
|
- !ruby/object:Gem::Version
|
43
|
-
version: 0.0.
|
43
|
+
version: 0.0.8
|
44
44
|
version:
|
45
45
|
- !ruby/object:Gem::Dependency
|
46
46
|
name: log4r
|
@@ -111,15 +111,15 @@ files:
|
|
111
111
|
- README.rdoc
|
112
112
|
- Rakefile
|
113
113
|
- bin/dvdprofiler2xbmc
|
114
|
+
- dvdprofiler2xbmc.gemspec
|
114
115
|
- lib/dvdprofiler2xbmc.rb
|
115
|
-
- lib/dvdprofiler2xbmc/app_config.rb
|
116
116
|
- lib/dvdprofiler2xbmc/app.rb
|
117
|
+
- lib/dvdprofiler2xbmc/app_config.rb
|
117
118
|
- lib/dvdprofiler2xbmc/cli.rb
|
118
119
|
- lib/dvdprofiler2xbmc/collection.rb
|
119
120
|
- lib/dvdprofiler2xbmc/extensions.rb
|
120
|
-
- lib/dvdprofiler2xbmc/imdb_extensions.rb
|
121
|
-
- lib/dvdprofiler2xbmc/media_files.rb
|
122
121
|
- lib/dvdprofiler2xbmc/media.rb
|
122
|
+
- lib/dvdprofiler2xbmc/media_files.rb
|
123
123
|
- lib/dvdprofiler2xbmc/nfo.rb
|
124
124
|
- test/test_dvdprofiler2xbmc.rb
|
125
125
|
- test/test_helper.rb
|
@@ -1,95 +0,0 @@
|
|
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
|
-
|