apple-tv-converter 0.4.4 → 0.5.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -2,26 +2,37 @@ module AppleTvConverter
2
2
  class MediaConverterAdapter
3
3
  include AppleTvConverter
4
4
 
5
+ attr_accessor :conversion_options
6
+
7
+ def initialize(options)
8
+ self.conversion_options = options
9
+ end
10
+
5
11
  def search_subtitles(media, languages)
6
12
  # Load the subtitles into memory and get IMDB id from them
7
13
  AppleTvConverter::SubtitlesFetcher::Opensubtitles.new(languages) do |fetcher|
8
14
  fetcher.search_subtitles media do |subtitles|
9
- media.imdb_id ||= subtitles.first['IDMovieImdb']
15
+ media.imdb_id = subtitles.first['IDMovieImdb'] if media.imdb_id.nil? || media.imdb_id.to_s.strip == ''
10
16
  end
11
17
  end
12
18
  end
13
19
 
14
20
  def download_subtitles(media, languages)
15
- puts "* Downloading subtitles"
16
21
  AppleTvConverter::SubtitlesFetcher::Opensubtitles.new(languages) do |fetcher|
17
- fetcher.download_subtitles media do |step, subtitles|
18
- case step
19
- when :downloading then printf " * Downloading: \##{subtitles['IDSubtitleFile']} (#{get_language_name(subtitles['SubLanguageID'])}) - #{subtitles['SubFileName']}"
20
- when :downloaded then puts " [DONE]"
22
+ if fetcher.has_found_subtitles?(media)
23
+ printf "* Downloading subtitles"
24
+ fetcher.download_subtitles media do |step, subtitles|
25
+ case step
26
+ when :search then puts %Q[ (#{subtitles.map { |l, subs| "#{subs.count} #{AppleTvConverter.get_language_name(l)}" }.join(', ') })]
27
+ when :downloading then printf " * Downloading: \##{subtitles['IDSubtitleFile']} (#{AppleTvConverter.get_language_name(subtitles['SubLanguageID'])}) - #{subtitles['SubFileName']}"
28
+ when :downloaded then puts " [DONE]"
29
+ end
21
30
  end
31
+ puts " * All subtitles downloaded"
32
+ else
33
+ puts "* No subtitles found to download"
22
34
  end
23
35
  end
24
- puts " * All subtitles downloaded"
25
36
  end
26
37
 
27
38
  def extract_subtitles(media, languages)
@@ -97,10 +108,17 @@ module AppleTvConverter
97
108
  options[:extra] << " -ab #{[audio_bitrate, (media.ffmpeg_data.audio_bitrate || 1000000)].min}k"
98
109
  end
99
110
 
111
+ # Ensure the languages are 'stored' as symbols, for comparison
112
+ languages = (languages || []).map(&:to_sym)
113
+
114
+ # If we're filtering by language, ensure that unknown and undetermined language
115
+ # streams are also mapped
116
+ languages += [nil, :und, :unk] if languages.any?
117
+
100
118
  # If the file has more than one audio track, map all tracks but subtitles when transcoding
101
119
  if media.audio_streams.length > 0
102
120
  media.streams.each do |stream|
103
- options[:map] << " -map #{stream.input_number}:#{stream.stream_number}" if stream.type == :video || (stream.type == :audio && (languages.nil? || languages.empty? || ([nil, 'und', 'unk'] + languages).include?(stream.language)))
121
+ options[:map] << " -map #{stream.input_number}:#{stream.stream_number}" if stream.type == :video || (stream.type == :audio && (languages.empty? || languages.include?(stream.language ? stream.language.to_sym : stream.language)))
104
122
  end
105
123
  end
106
124
 
@@ -142,19 +160,99 @@ module AppleTvConverter
142
160
  raise NotImplementedYetException
143
161
  end
144
162
 
145
- def get_imdb_info(media)
146
- printf "* Getting info from IMDB"
163
+ def get_metadata(media)
164
+ if media.is_tv_show_episode?
165
+ get_tv_show_db_info media
166
+ else
167
+ get_imdb_info media
168
+ end
169
+ end
170
+
171
+ def get_tv_show_db_info(media)
172
+ printf "* Getting info from TheTVDB"
173
+
174
+ media.tvdb_movie = TvDbFetcher.search(media, conversion_options.interactive)
175
+ if media.tvdb_movie
176
+ media.imdb_id = media.tvdb_movie[:show][:series]['IMDB_ID'] if media.tvdb_movie.has_key?(:show) && media.tvdb_movie[:show].has_key?(:series)
177
+ media.imdb_id = media.imdb_id.gsub(/\D+/, '') if media.imdb_id
178
+
179
+ # Update the episode name, if available
180
+ media.episode_title = media.tvdb_movie_data('EpisodeName')
181
+ media.tvdb_id = media.tvdb_movie_data('seriesid')
182
+ media.tvdb_season_id = media.tvdb_movie_data('seasonid')
183
+ media.tvdb_episode_id = media.tvdb_movie_data('id')
184
+ media.imdb_episode_id = media.tvdb_movie_data('IMDB_ID')
185
+ media.imdb_episode_id = media.imdb_episode_id.gsub(/\D+/, '') if media.imdb_episode_id
186
+
187
+ unless media.imdb_id.nil? || media.imdb_id.blank?
188
+ puts "" # Line break just for output layout
189
+ get_imdb_info(media, false)
190
+ end
191
+
192
+ puts " [DONE]"
193
+ else
194
+ puts " [NOT FOUND]"
195
+ end
196
+ end
197
+
198
+ def get_imdb_info(media, feedback = true)
199
+ printf "* Getting info from IMDB" if feedback
147
200
 
148
201
  if media.imdb_id
149
202
  media.imdb_movie = Imdb::Movie.new(media.imdb_id)
150
203
  elsif Dir[File.join(File.dirname(media.original_filename), '*.imdb')].any?
151
204
  media.imdb_movie = Imdb::Movie.new(File.basename(Dir[File.join(File.dirname(media.original_filename), '*.imdb')].first).gsub(/\.imdb$/i, ''))
152
205
  else
153
- puts " [SKIPPING - COULDN'T FIND IMDB ID]"
154
- return
206
+
207
+ search = Imdb::Search.new(media.show)
208
+
209
+ search.movies.delete_if do |item|
210
+ item.title.strip =~ /(?:(?:\(TV\s*(?:Movie|(?:Mini.?)?Series|Episode))|(?:Video(?:\s*Game)?))/i
211
+ end
212
+
213
+ media.imdb_id = if search.movies.length > 1 && conversion_options.interactive
214
+ choice = 0
215
+ puts "\n *"
216
+ while true
217
+ puts %Q[ | Several movies found, choose the intended one#{" (showing only the first 20 of #{search.movies.length} results)" if search.movies.length > 20}:]
218
+
219
+ search.movies[0...20].each_with_index do |item, index|
220
+ puts " | #{(index + 1).to_s.rjust(search.movies.length.to_s.length)} - #{item.title.strip} (id: #{item.id})"
221
+ puts " | #{' '.rjust(search.movies.length.to_s.length)} AKA: #{item.also_known_as.join(', ').strip}" if item.also_known_as.any?
222
+ end
223
+
224
+ printf " |\n *- What's your choice (1..#{[search.movies.length, 20].min})? "
225
+ choice = STDIN.gets.chomp.to_i
226
+
227
+ break if choice.between?(1, [search.movies.length, 20].min)
228
+
229
+ puts " | Invalid choice!"
230
+ puts " |"
231
+ end
232
+
233
+ printf " * Getting info from IMDB"
234
+ search.movies[choice - 1].id
235
+ else
236
+ search.movies.first.id rescue nil
237
+ end
238
+
239
+ media.imdb_movie = Imdb::Movie.new(media.imdb_id) if media.imdb_id
155
240
  end
156
241
 
157
- puts " [DONE]"
242
+ begin
243
+ media.imdb_movie.year
244
+ puts " [DONE]" if feedback
245
+ rescue OpenURI::HTTPError => e
246
+ media.imdb_id = nil
247
+ media.imdb_movie = nil
248
+ puts (e.message =~ /404/ ? " [NOT FOUND]" : " [ERROR]") if feedback
249
+ rescue
250
+ if media.imdb_id.nil?
251
+ puts " [NOT FOUND]" if feedback
252
+ else
253
+ raise e
254
+ end
255
+ end
158
256
  end
159
257
 
160
258
  def tag(media)
@@ -181,13 +279,24 @@ module AppleTvConverter
181
279
  FileUtils.rm_r list_files(media.original_filename.gsub(File.extname(media.original_filename), '*.srt'))
182
280
  FileUtils.rm(media.backup_filename) if File.exists?(media.backup_filename)
183
281
 
184
-
185
282
  puts " [DONE]"
186
283
  rescue
187
284
  puts " [ERROR]"
188
285
  end
189
286
  end
190
287
 
288
+ def rename_to_plex_format(media)
289
+ printf "* Renaming to PLEX format"
290
+ begin
291
+ plex_format_filename = media.plex_format_filename
292
+ FileUtils.mv(media.converted_filename_equals_original_filename? ? media.original_filename : media.converted_filename, plex_format_filename) unless media.converted_filename == plex_format_filename
293
+
294
+ puts " [DONE]"
295
+ rescue => e
296
+ puts " [ERROR]"
297
+ end
298
+ end
299
+
191
300
  protected
192
301
 
193
302
  def list_files(ls)
@@ -204,16 +313,5 @@ module AppleTvConverter
204
313
 
205
314
  options
206
315
  end
207
-
208
- def load_movie_from_imdb(media)
209
- begin
210
- search = Imdb::Search.new(media.show)
211
-
212
- return search.movies.first if search.movies.count == 1
213
- rescue
214
- end
215
-
216
- return nil
217
- end
218
316
  end
219
317
  end
@@ -8,7 +8,7 @@ module AppleTvConverter
8
8
  subtitle_filename =~ /\.(\w{3})\.srt$/i
9
9
  language_code = $1 || 'und'
10
10
 
11
- language_name = get_language_name(language_code)
11
+ language_name = AppleTvConverter.get_language_name(language_code)
12
12
 
13
13
  command_line = [
14
14
  Shellwords.escape(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'bin', 'SublerCLI'))),
@@ -24,10 +24,15 @@ module AppleTvConverter
24
24
 
25
25
  if RUBY_VERSION =~ /^1\.8/
26
26
  output, error = Open3.popen3(command_line) { |stdin, stdout, stderr| [ stdout.read, stderr.read ] }
27
- puts error.strip == '' ? " [DONE]" : " [ERROR]"
27
+ puts error.strip == '' || error =~ /guessed encoding/i ? " [DONE]" : " [ERROR] #{error}"
28
28
  else
29
29
  output, error, exit_status = Open3.popen3(command_line) { |stdin, stdout, stderr, wait_thr| [ stdout.read, stderr.read, wait_thr.value ] }
30
- puts exit_status.exitstatus == 0 ? " [DONE]" : " [ERROR]"
30
+ if exit_status.exitstatus == 0
31
+ puts" [DONE]"
32
+ else
33
+ puts " [ERROR]"
34
+ puts command_line
35
+ end
31
36
  end
32
37
  end
33
38
  else
@@ -36,45 +41,74 @@ module AppleTvConverter
36
41
  end
37
42
 
38
43
  def tag(media)
39
- metadata = ''
40
- if media.imdb_movie
41
- metadata << %Q[{Name: #{media.imdb_movie.title.gsub(/"/, '\\"')}}]
42
- metadata << %Q[{Genre: #{media.imdb_movie.genres.first.gsub(/"/, '\\"')}}]
43
- metadata << %Q[{Description: #{media.imdb_movie.plot.gsub(/"/, '\\"')}}] if media.imdb_movie.plot
44
- metadata << %Q[{Release Date: #{media.imdb_movie.year}}]
45
- metadata << %Q[{Director: #{(media.imdb_movie.director.first || '').gsub(/"/, '\\"')}}]
46
- metadata << %Q[{Codirector: #{media.imdb_movie.director[1].gsub(/"/, '\\"')}}] if media.imdb_movie.director.length > 1
47
-
48
- if media.imdb_movie.poster
49
- open(media.imdb_movie.poster) do |f|
50
- File.open(media.artwork_filename,"wb") do |file|
51
- file.puts f.read
52
- end
53
- end
54
-
55
- metadata << %Q[{Artwork: #{media.artwork_filename}}]
44
+ metadata = {}
45
+
46
+ if media.is_tv_show_episode? && media.tvdb_movie
47
+ # ap [media.tvdb_movie[:show][:series], media.tvdb_movie[:episode]]
48
+ metadata['Name'] = media.tvdb_movie_data('EpisodeName')
49
+ metadata['Name'] ||= "#{media.show} S#{media.season.to_s.rjust(2, '0')}E#{media.number.to_s.rjust(2, '0')}"
50
+ metadata['Genre'] = media.tvdb_movie[:show][:series]['Genre'].gsub(/(?:^\|)|(?:\|$)/, '').split('|').first rescue nil
51
+ metadata['Description'] = media.tvdb_movie_data('Overview')
52
+ metadata['Release Date'] = media.tvdb_movie_data('FirstAired')
53
+ metadata['Director'] = media.tvdb_movie_data('Director')
54
+ metadata['TV Show'] = media.tvdb_movie[:show][:series]['SeriesName']
55
+ metadata['TV Show'] ||= media.show
56
+ metadata['TV Season'] = media.tvdb_movie_data('SeasonNumber')
57
+ metadata['TV Season'] ||= media.season
58
+ metadata['TV Episode #'] = media.tvdb_movie_data('EpisodeNumber')
59
+ metadata['TV Episode #'] ||= media.number
60
+ metadata['TV Network'] ||= media.tvdb_movie[:show][:series]['Network']
61
+ metadata['Screenwriters'] = media.tvdb_movie_data('Writer').gsub(/(?:^\|)|(?:\|$)/, '').split('|').join(', ') if media.tvdb_movie_data('Writer')
62
+
63
+ if media.imdb_movie
64
+ # Fallback to IMDB data if present
65
+ metadata['Genre'] ||= media.imdb_movie.genres.first if media.imdb_movie.genres && media.imdb_movie.genres.any?
66
+ metadata['Description'] ||= media.imdb_movie.plot if media.imdb_movie.plot
67
+ metadata['Release Date'] ||= media.imdb_movie.year if media.imdb_movie.year > 0
68
+ metadata['Director'] ||= media.imdb_movie.director.first
56
69
  end
57
- end
58
-
59
- if media.is_tv_show_episode?
60
- metadata << %Q[{TV Show: #{media.show}}]
61
- metadata << %Q[{TV Season: #{media.season}}]
62
- metadata << %Q[{TV Episode #: #{media.number}}]
63
- end
64
70
 
65
- metadata << %Q[{HD Video: true}] if media.hd?
71
+ if File.exists?(media.tvdb_movie_poster)
72
+ AppleTvConverter.copy media.tvdb_movie_poster, media.artwork_filename
73
+ metadata['Artwork'] = media.artwork_filename
74
+ end
75
+ else
76
+ if media.imdb_movie
77
+ unless media.is_tv_show_episode?
78
+ metadata['Name'] = media.imdb_movie.title.gsub(/"/, '"')
79
+ end
80
+ metadata['Genre'] = media.imdb_movie.genres.first.gsub(/"/, '"')
81
+ metadata['Description'] = media.imdb_movie.plot.gsub(/"/, '"') if media.imdb_movie.plot
82
+ metadata['Release Date'] = media.imdb_movie.year if media.imdb_movie.year
83
+ metadata['Director'] = (media.imdb_movie.director.first || '').gsub(/"/, '"') if media.imdb_movie.director.any?
84
+ metadata['Codirector'] = media.imdb_movie.director[1].gsub(/"/, '"') if media.imdb_movie.director.length > 1
85
+
86
+ if media.imdb_movie.poster
87
+ AppleTvConverter.copy media.imdb_movie.poster, media.artwork_filename
88
+ metadata['Artwork'] = media.artwork_filename
89
+ end
90
+ end
66
91
 
67
- if !media.imdb_movie
92
+ # Overwrite the name and genre to group the episode correctly
68
93
  if media.is_tv_show_episode?
69
- metadata << %Q[{Name: #{media.show} S#{media.season.to_s.rjust(2, '0')}E#{media.number.to_s.rjust(2, '0')}}]
70
- metadata << %Q[{Genre: #{media.genre}}]
71
- else
72
- metadata << %Q[{Name: #{media.show}}]
73
- metadata << %Q[{Genre: #{media.genre}}]
94
+ metadata['Name'] = "#{media.show} S#{media.season.to_s.rjust(2, '0')}E#{media.number.to_s.rjust(2, '0')}"
95
+ # metadata['Genre'] = media.genre
96
+ metadata['TV Show'] = media.show
97
+ metadata['TV Season'] = media.season
98
+ metadata['TV Episode #'] = media.number
99
+ elsif !media.imdb_movie
100
+ metadata['Name'] = media.show
101
+ metadata['Genre'] = media.genre
74
102
  end
75
-
76
103
  end
77
104
 
105
+ metadata['HD Video'] = true if media.hd?
106
+ metadata['Media Kind'] = media.is_tv_show_episode? ? 'TV Show' : 'Movie'
107
+
108
+ metadata = metadata.map do |key, value|
109
+ value.nil? ? nil : %Q[{#{key}: #{value.to_s.gsub(/"/, '\\"')}}]
110
+ end.compact.join
111
+
78
112
  command_line = [
79
113
  Shellwords.escape(File.expand_path(File.join(File.dirname(__FILE__), '..', '..', 'bin', 'SublerCLI'))),
80
114
  %Q[-metadata "#{metadata}"],
@@ -115,7 +149,7 @@ module AppleTvConverter
115
149
  command_line = [
116
150
  'osascript <<EOF',
117
151
  'tell application "iTunes"',
118
- %Q[add POSIX file "#{media.converted_filename.gsub(/"/, '\\"')}"],
152
+ %Q[add POSIX file "#{media.resulting_filename.gsub(/"/, '\\"')}"],
119
153
  'end tell',
120
154
  'EOF'
121
155
  ].join("\n")
@@ -2,68 +2,18 @@ module AppleTvConverter
2
2
  class MediaConverterWindowsAdapter < MediaConverterAdapter
3
3
  require 'win32ole'
4
4
 
5
- def add_subtitles(media)
6
- puts "* Adding subtitles"
7
-
8
- printf " * Removing any previous subtitles"
9
-
10
- command_line = %Q[./SublerCLI -remove -dest "#{media.converted_filename}"]
11
-
12
- AppleTvConverter.logger.debug "Executing:"
13
- AppleTvConverter.logger.debug command_line
14
-
15
- output = Open3.popen3(command_line) { |stdin, stdout, stderr, wait_thr| stdout.read }
16
-
17
- puts output.strip.empty? ? " [DONE]" : (output.strip == 'Error: (null)' ? " [NONE FOUND]" : " [ERROR]")
18
-
19
- if has_subtitles?(media)
20
- list_files(File.join(File.dirname(media.original_filename), '*.srt')).map do |subtitle_filename|
21
- subtitle_filename =~ /(\w{3})\.srt$/
22
- language_code = $1 || 'eng'
23
-
24
- language = ::LanguageList::LanguageInfo.find_by_iso_639_3(language_code)
25
- language ||= ::LanguageList::LanguageInfo.find_by_iso_639_3('eng')
26
-
27
-
28
- command_line = "./SublerCLI "
29
- command_line << %Q[-source "#{subtitle_filename}" ]
30
- command_line << %Q[-language "#{language.name}" ]
31
- command_line << %Q[-dest "#{media.converted_filename}"]
32
-
33
- AppleTvConverter.logger.debug "Executing:"
34
- AppleTvConverter.logger.debug command_line
35
-
36
- printf " * Adding #{language.name} subtitles"
37
- output = Open3.popen3(command_line) { |stdin, stdout, stderr, wait_thr| stdout.read }
38
-
39
- puts output.strip.empty? ? " [DONE]" : " [ERROR]"
40
- end
41
- else
42
- puts " * No new subtitles found"
43
- end
5
+ def handbrake_location
6
+ return File.expand_path("./HandBrakeCLI#{'_x64' if is_windows_64bit?}.exe")
44
7
  end
45
8
 
46
- def tag(media)
47
- metadata = ''
48
- metadata << %Q[{Name: #{media.show} S#{media.season.to_s.rjust(2, '0')}E#{media.number.to_s.rjust(2, '0')}}] if media.is_tv_show_episode?
49
- metadata << %Q[{Name: #{media.show}}] if media.is_movie?
50
- metadata << %Q[{Genre: #{media.show}}{TV Show: #{media.show}}{TV Season: #{media.season}}{TV Episode #: #{media.number}}] if media.is_tv_show_episode?
51
- metadata << %Q[{Genre: #{media.quality} Movie}] if media.is_movie?
52
-
53
- command_line = %Q[./SublerCLI -metadata "#{metadata}" -dest "#{media.converted_filename}"]
54
-
55
- AppleTvConverter.logger.debug "Executing:"
56
- AppleTvConverter.logger.debug command_line
57
-
58
- printf "* Tagging"
59
-
60
- output = Open3.popen3(command_line) { |stdin, stdout, stderr, wait_thr| stdout.read }
61
-
62
- puts output.strip.empty? ? " [DONE]" : " [ERROR]"
63
-
64
- return output.strip.empty?
9
+ def atomic_parsley_location
10
+ return File.expand_path('./AtomicParsley.exe')
65
11
  end
66
12
 
13
+ def clean_up_command_line(command_line)
14
+ return command_line.gsub(/\//, '\\')
15
+ end
16
+
67
17
  def add_to_itunes(episode)
68
18
  printf "* Adding to iTunes"
69
19
  @itunes ||= WIN32OLE.new("iTunes.Application")
@@ -73,23 +23,6 @@ module AppleTvConverter
73
23
  puts " [DONE]"
74
24
  end
75
25
 
76
- def list_files(ls)
77
- `ls -1 #{ls.gsub(/\s/, '\ ').gsub(/\[/, '\[').gsub(/\]/, '\]')} 2>/dev/null`.split("\n")
78
- end
79
-
80
-
81
- def handbrake_location
82
- return File.expand_path("./HandBrakeCLI#{'_x64' if is_windows_64bit?}.exe")
83
- end
84
-
85
- def atomic_parsley_location
86
- return File.expand_path('./AtomicParsley.exe')
87
- end
88
-
89
- def clean_up_command_line(command_line)
90
- return command_line.gsub(/\//, '\\')
91
- end
92
-
93
26
  def line_ending
94
27
  end
95
28