apple-tv-converter 0.5.9 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG CHANGED
@@ -1,3 +1,9 @@
1
+ v0.6.0
2
+ ------
3
+ * Added command line option to allow resizing the converted video
4
+ * Enchanced the --os option to allow supplying an username:password combination
5
+ * Other fixes
6
+
1
7
  v0.5.9
2
8
  ------
3
9
  * Fixed #5 Command line options now correctly override the data file (thanks @shir)
data/README.md CHANGED
@@ -19,7 +19,7 @@ Usage: apple-tv-converter [options] [file]
19
19
  -l, --languages eng,por,... Only keep audio and subtitles in the specified languages
20
20
  -d, --dir DIRECTORY Process all files in DIRECTORY recursively
21
21
  --itunes Add processed file to iTunes library, if it isn't present yet
22
- --os Download subtitles and infer IMDB ID from opensubtitles.org
22
+ --os [USERNAME:PASSWORD] Download subtitles and infer IMDB ID from opensubtitles.org
23
23
  --plex Rename file(s) to Plex Media Server recommended format
24
24
 
25
25
  --no-transcoding Don't transcode video or audio
@@ -35,6 +35,8 @@ Advanced options:
35
35
  Set the episode number padding length (ie, 3 for 001, 002, etc.)
36
36
  -s, --season NUMBER Set the season number for TV Shows in case folder/file naming scheme doesn't contain right season
37
37
  -e, --episode NUMBER Set the episode number for TV Shows in case folder/file naming scheme doesn't contain right episode number
38
+ --width NUMBER Resize the video to the specified width. If used with --height, can result in a different aspect ratio
39
+ --height NUMBER Resize the video to the specified height. If used with --width, can result in a different aspect ratio
38
40
 
39
41
  Other options:
40
42
  -f, --ffmpeg LOCATION Set path to ffmpeg binary
@@ -49,14 +49,14 @@ module FFMPEG
49
49
  @audio_stream = $1
50
50
 
51
51
  if video_stream
52
- @video_codec, @colorspace, resolution, video_bitrate = video_stream.split(/\s?,\s?/)
52
+ @video_codec, @colorspace, resolution, video_bitrate = video_stream.scan(/([^,()\[\]]+(?:\s*(?:\[|\().*?(?:\]|\)))*)\s?,?\s?/).flatten
53
53
  @video_bitrate = video_bitrate =~ %r(\A(\d+) kb/s\Z) ? $1.to_i : nil
54
54
  @resolution = resolution.split(" ").first rescue nil # get rid of [PAR 1:1 DAR 16:9]
55
55
  @dar = $1 if video_stream[/DAR (\d+:\d+)/]
56
56
  end
57
57
 
58
58
  if audio_stream
59
- @audio_codec, audio_sample_rate, @audio_channels, unused, audio_bitrate = audio_stream.split(/\s?,\s?/)
59
+ @audio_codec, audio_sample_rate, @audio_channels, unused, audio_bitrate = audio_stream.scan(/([^,()\[\]]+(?:\s*(?:\[|\().*?(?:\]|\)))*)\s?,?\s?/).flatten
60
60
  @audio_bitrate = audio_bitrate =~ %r(\A(\d+) kb/s(?: \(default\))?\Z) ? $1.to_i : nil
61
61
  @audio_sample_rate = audio_sample_rate[/\d*/].to_i
62
62
  end
@@ -13,8 +13,12 @@ module FFMPEG
13
13
  attr_writer :subtitle_codec, :subtitle_format
14
14
 
15
15
  def audio_channels=(value)
16
- if value.strip =~ /^(\d+)\.(\d+)$/
16
+ if value.strip =~ /^(\d+)\.(\d+)(?:\(.*?\))?$/
17
17
  @audio_channels = [$1.to_i + $2.to_i, 6].min
18
+ elsif value.strip =~ /stereo/i
19
+ @audio_channels = 2
20
+ elsif value.strip =~ /mono/i
21
+ @audio_channels = 1
18
22
  else
19
23
  @audio_channels = value
20
24
  end
@@ -50,6 +50,8 @@ module AppleTvConverter
50
50
  options.media = []
51
51
  options.season = nil
52
52
  options.episode = nil
53
+ options.width = -1
54
+ options.height = -1
53
55
 
54
56
  opts = OptionParser.new do |opts|
55
57
  opts.banner = "Usage: apple-tv-converter [options] [file]\n" +
@@ -101,8 +103,15 @@ module AppleTvConverter
101
103
  options.add_to_itunes = true
102
104
  end
103
105
 
104
- opts.on('--os', "Download subtitles and infer IMDB ID from opensubtitles.org") do |i|
106
+ opts.on('--os [USERNAME:PASSWORD]', "Download subtitles and infer IMDB ID from opensubtitles.org") do |username_password|
105
107
  options.download_subtitles = true
108
+ if username_password =~ /^(.*?)\:(.*?)/
109
+ options.download_subtitles_username = $1 if username_password =~ /^(.+?)\:.+$/
110
+ options.download_subtitles_password = $1 if username_password =~ /^.+?\:(.+)$/
111
+ end
112
+
113
+ options.download_subtitles_username = nil if options.download_subtitles_username == ''
114
+ options.download_subtitles_password = nil if options.download_subtitles_password == ''
106
115
  end
107
116
 
108
117
  opts.on('--plex', 'Rename file(s) to Plex Media Server recommended format') do
@@ -155,6 +164,16 @@ module AppleTvConverter
155
164
  options.episode = i.to_i
156
165
  end
157
166
 
167
+ opts.on('--width NUMBER', 'Resize the video to the specified width. If used with --height, can result in a different aspect ratio') do |i|
168
+ options.width = i.to_i
169
+ options.width -= options.width % 2 # Ensure it's always a even number
170
+ end
171
+
172
+ opts.on('--height NUMBER', 'Resize the video to the specified height. If used with --width, can result in a different aspect ratio') do |i|
173
+ options.height = i.to_i
174
+ options.height -= options.height % 2 # Ensure it's always a even number
175
+ end
176
+
158
177
  opts.separator ""
159
178
  opts.separator "Other options:"
160
179
 
@@ -29,8 +29,8 @@ module AppleTvConverter
29
29
  end
30
30
 
31
31
  def format1_match
32
- # /.*?S(\d+)E(\d+)(?:(?:[-E]+(\d+))*).*?/ -> S00E01, S00E01(E02)+, S00E01(-E02)+, S00E01(-02)+
33
- @format1_match ||= basename.match(/.*?S(\d+)E(\d+)(?:(?:[-E]+(\d+))*).*?/i)
32
+ # /.*?S(\d+)E(\d+)(?:(?:[-E]+(\d+))*).*?/ -> S00E01, S00E01(E02)+, S00E01(-E02)+, S00E01(-02)+, S00 E01( E02)+
33
+ @format1_match ||= basename.match(/.*?S(\d+)\s*E(\d+)(?:(?:[-E]+(\d+)\s*)*).*?/i)
34
34
  end
35
35
 
36
36
  def format2_match
@@ -14,19 +14,7 @@ module AppleTvConverter
14
14
  def original_filename=(value)
15
15
  @original_filename = value
16
16
 
17
- if @original_filename =~ /.*?\.mp4$/
18
- Dir[@original_filename.gsub(File.extname(@original_filename), '*')].each do |file|
19
- if @original_filename != file && !(Media.subtitle_extensions + Media.ignored_extensions).include?(file.downcase.gsub(/.*\./, ''))
20
- @original_filename = file
21
- break
22
- end
23
- end
24
- end
25
-
26
- if converted_filename == original_filename && needs_transcoding?
27
- @converted_filename = original_filename.gsub(File.extname(original_filename), "_2#{File.extname(original_filename)}")
28
- @converted_filename_equals_original_filename = true
29
- end
17
+ check_filename_clashing
30
18
 
31
19
  load_data_file
32
20
 
@@ -120,7 +108,21 @@ module AppleTvConverter
120
108
 
121
109
  def needs_audio_conversion? ; return ffmpeg_data.audio_codec !~ /(?:aac)/i ; end
122
110
 
123
- def needs_video_conversion? ; return ffmpeg_data.video_codec !~ /(?:.*?h264|^mpeg4).*/i || ffmpeg_data.video_codec =~ /.*(?:xvid|divx).*/i || ffmpeg_data.video_stream =~ /h264.*?yuv420p10le/i ; end
111
+ def needs_video_conversion?
112
+ return [
113
+ ffmpeg_data.video_codec !~ /(?:.*?h264|^mpeg4).*/i,
114
+ ffmpeg_data.video_codec =~ /.*(?:xvid|divx).*/i,
115
+ ffmpeg_data.video_stream =~ /h264.*?yuv420p10le/i,
116
+ needs_video_resizing?
117
+ ].any?
118
+ end
119
+
120
+ def needs_video_resizing?
121
+ return [
122
+ movie_width > 0 && movie_width != ffmpeg_data.width,
123
+ movie_height > 0 && movie_height != ffmpeg_data.height
124
+ ].any?
125
+ end
124
126
 
125
127
  def needs_subtitles_conversion? ; return ffmpeg_data.subtitle_streams.any? ; end
126
128
 
@@ -132,6 +134,22 @@ module AppleTvConverter
132
134
 
133
135
  def movie_hash ; @movie_hash ||= AppleTvConverter::MovieHasher.compute_hash(original_filename) ; end
134
136
 
137
+ def movie_width ; @movie_width || 0 ; end
138
+ def movie_width=(value)
139
+ @movie_width = value
140
+ assert_movie_dimensions!
141
+ check_filename_clashing
142
+ @movie_width
143
+ end
144
+
145
+ def movie_height ; @movie_height || 0 ; end
146
+ def movie_height=(value)
147
+ @movie_height = value
148
+ assert_movie_dimensions!
149
+ check_filename_clashing
150
+ @movie_height
151
+ end
152
+
135
153
  def tvdb_movie_data(key, default = nil) ;
136
154
  return tvdb_movie[:episode][key].gsub(/`/, '') if tvdb_movie && tvdb_movie.has_key?(:episode) && tvdb_movie[:episode].has_key?(key) && !tvdb_movie[:episode][key].blank? rescue default
137
155
  return default
@@ -193,5 +211,33 @@ module AppleTvConverter
193
211
  ap ['e', e]
194
212
  end
195
213
  end
214
+
215
+ def assert_movie_dimensions!
216
+ if movie_width > 0 && movie_height <= 0
217
+ @movie_height = ffmpeg_data.height.to_f * (movie_width.to_f / ffmpeg_data.width.to_f)
218
+ @movie_height -= @movie_height % 2
219
+ @movie_height = @movie_height.to_i
220
+ elsif movie_width <= 0 && movie_height > 0
221
+ @movie_width = ffmpeg_data.width.to_f * (movie_height.to_f / ffmpeg_data.height.to_f)
222
+ @movie_width -= movie_width % 2
223
+ @movie_width = @movie_width.to_i
224
+ end
225
+ end
226
+
227
+ def check_filename_clashing
228
+ if @original_filename =~ /.*?\.mp4$/
229
+ Dir[@original_filename.gsub(File.extname(@original_filename), '*')].each do |file|
230
+ if @original_filename != file && !(Media.subtitle_extensions + Media.ignored_extensions).include?(file.downcase.gsub(/.*\./, ''))
231
+ @original_filename = file
232
+ break
233
+ end
234
+ end
235
+ end
236
+
237
+ if converted_filename == original_filename && needs_transcoding?
238
+ @converted_filename = original_filename.gsub(File.extname(original_filename), "_2#{File.extname(original_filename)}")
239
+ @converted_filename_equals_original_filename = true
240
+ end
241
+ end
196
242
  end
197
243
  end
@@ -68,7 +68,7 @@ module AppleTvConverter
68
68
  end
69
69
  end
70
70
 
71
- if @options.skip_subtitles != true && @options.download_subtitles && media.subtitle_streams.empty? && @adapter.list_files(media.original_filename.gsub(/.{4}$/, '.*srt')).empty?
71
+ if @options.skip_subtitles != true && @options.download_subtitles && media.subtitle_streams.select { |s| @options.languages.include?(s.language.to_s) }.empty? && @adapter.list_files(media.original_filename.gsub(/.{4}$/, '.*srt')).empty?
72
72
  @adapter.search_subtitles(media, @options.languages)
73
73
  @adapter.download_subtitles(media, @options.languages)
74
74
  end
@@ -104,6 +104,8 @@ module AppleTvConverter
104
104
  media.number = @options.episode if @options.episode
105
105
  media.use_absolute_episode_numbering = @options.use_absolute_numbering
106
106
  media.episode_number_padding = @options.episode_number_padding if @options.episode_number_padding
107
+ media.movie_width = @options.width if @options.width > 0
108
+ media.movie_height = @options.height if @options.height > 0
107
109
  end
108
110
  end
109
111
  end
@@ -10,7 +10,7 @@ module AppleTvConverter
10
10
 
11
11
  def search_subtitles(media, languages)
12
12
  # Load the subtitles into memory and get IMDB id from them
13
- AppleTvConverter::SubtitlesFetcher::Opensubtitles.new(languages) do |fetcher|
13
+ AppleTvConverter::SubtitlesFetcher::Opensubtitles.new(languages, self.conversion_options.download_subtitles_username, self.conversion_options.download_subtitles_password) do |fetcher|
14
14
  fetcher.search_subtitles media do |subtitles|
15
15
  media.imdb_id = subtitles.first['IDMovieImdb'] if media.imdb_id.nil? || media.imdb_id.to_s.strip == ''
16
16
  end
@@ -18,17 +18,37 @@ module AppleTvConverter
18
18
  end
19
19
 
20
20
  def download_subtitles(media, languages)
21
- AppleTvConverter::SubtitlesFetcher::Opensubtitles.new(languages) do |fetcher|
21
+ AppleTvConverter::SubtitlesFetcher::Opensubtitles.new(languages, self.conversion_options.download_subtitles_username, self.conversion_options.download_subtitles_password) do |fetcher|
22
22
  if fetcher.has_found_subtitles?(media)
23
- printf "* Downloading subtitles"
24
- fetcher.download_subtitles media do |step, subtitles|
23
+ printf "* Downloading subtitles#{%Q[ using user "#{self.conversion_options.download_subtitles_username}"] unless self.conversion_options.download_subtitles_username.nil?}"
24
+ status = {
25
+ :total => 0,
26
+ :ok => 0,
27
+ :error => 0
28
+ }
29
+
30
+ fetcher.download_subtitles media do |step, subtitles, message|
25
31
  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]"
32
+ when :search then puts %Q[ (#{subtitles.map { |l, subs| "#{subs.count} #{AppleTvConverter.get_language_name(l)}" }.join(', ') })]
33
+ when :downloading
34
+ status[:total] += 1
35
+ printf " * Downloading: \##{subtitles['IDSubtitleFile']} (#{AppleTvConverter.get_language_name(subtitles['SubLanguageID'])}) - #{subtitles['SubFileName']}"
36
+ when :downloaded then
37
+ status[:ok] += 1
38
+ puts " [DONE]"
39
+ when :download_failed then
40
+ status[:error] += 1
41
+ puts " [ERROR - #{message}]"
29
42
  end
30
43
  end
31
- puts " * All subtitles downloaded"
44
+
45
+ if status[:total] == status[:ok]
46
+ puts " * All subtitles downloaded"
47
+ elsif status[:total] == status[:error]
48
+ puts " * Couldn't download any subtitle"
49
+ else
50
+ puts " * Downloaded #{status[:ok]} of #{status[:total]} subtitles"
51
+ end
32
52
  else
33
53
  puts "* No subtitles found to download"
34
54
  end
@@ -71,7 +91,7 @@ module AppleTvConverter
71
91
  end
72
92
 
73
93
  def transcode(media, languages = nil)
74
- if media.needs_transcoding?
94
+ if media.needs_transcoding? || needs_transformation?(media)
75
95
  puts "* Transcoding"
76
96
 
77
97
  options = {}
@@ -83,10 +103,7 @@ module AppleTvConverter
83
103
 
84
104
  # Better video and audio transcoding quality
85
105
  if media.needs_video_conversion?
86
- # Ensure divisible by 2 width and height
87
- dimensions = "-s #{(media.ffmpeg_data.width % 2 > 0) ? (media.ffmpeg_data.width + 1) : media.ffmpeg_data.width}x#{(media.ffmpeg_data.height % 2 > 0) ? (media.ffmpeg_data.height + 1) : media.ffmpeg_data.height}" if media.ffmpeg_data.width % 2 > 0 || media.ffmpeg_data.height % 2 > 0
88
-
89
- options[:extra] << " #{dimensions} -mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -g 300 -pass 1 -q:v 1 -r 23.98"
106
+ options[:extra] << " #{get_transcoded_dimensions_options(media)} -mbd rd -flags +mv4+aic -trellis 2 -cmp 2 -subcmp 2 -g 300 -pass 1 -q:v 1 -r 23.98 -pix_fmt yuv420p"
90
107
  end
91
108
 
92
109
  if media.needs_audio_conversion?
@@ -319,6 +336,10 @@ module AppleTvConverter
319
336
  list_files(File.join(File.dirname(media.original_filename), '*.srt')).any?
320
337
  end
321
338
 
339
+ def needs_transformation?(media)
340
+ media.needs_video_resizing?
341
+ end
342
+
322
343
  def get_transcode_options(media)
323
344
  options = " -vcodec #{media.needs_video_conversion? ? 'libx264' : 'copy'}"
324
345
  options << " -acodec #{media.needs_audio_conversion? ? 'libfaac' : 'copy'}"
@@ -333,5 +354,9 @@ module AppleTvConverter
333
354
 
334
355
  audio_channels
335
356
  end
357
+
358
+ def get_transcoded_dimensions_options(media)
359
+ "-s #{(media.movie_width % 2 > 0) ? (media.movie_width + 1) : media.movie_width}x#{(media.movie_height % 2 > 0) ? (media.movie_height + 1) : media.movie_height}" if media.needs_video_resizing?
360
+ end
336
361
  end
337
362
  end
@@ -3,8 +3,10 @@ module AppleTvConverter
3
3
  class Opensubtitles
4
4
  attr_reader :languages, :token
5
5
 
6
- def initialize(languages)
6
+ def initialize(languages, username = nil, password = nil)
7
7
  @languages = languages
8
+ @username = username
9
+ @password = password
8
10
  @server = XMLRPC::Client.new(SERVER, PATH, PORT)
9
11
  @token = nil
10
12
 
@@ -73,8 +75,12 @@ module AppleTvConverter
73
75
  media_subtitles.each do |language_code, subtitles|
74
76
  subtitles.each do |subtitle|
75
77
  block.call :downloading, subtitle
76
- download_subtitle(media, subtitle)
77
- block.call :downloaded, subtitle
78
+ result = download_subtitle(media, subtitle)
79
+ if result == true
80
+ block.call :downloaded, subtitle
81
+ else
82
+ block.call :download_failed, subtitle, result
83
+ end
78
84
  end
79
85
  end
80
86
  end
@@ -92,7 +98,7 @@ module AppleTvConverter
92
98
  def logged_in? ; return !@token.nil? ; end
93
99
 
94
100
  def login
95
- response = make_call("LogIn", '', '', '', USER_AGENT)
101
+ response = make_call("LogIn", @username || '', @password || '', '', USER_AGENT)
96
102
  parse_response! response
97
103
 
98
104
  @token = response['token'] if response[:success]
@@ -180,6 +186,10 @@ module AppleTvConverter
180
186
  File.open(media.get_new_subtitle_filename(subtitle['SubLanguageID'], subtitle_data['idsubtitlefile']), 'wb') { |file| file.write(unzipped_data) }
181
187
  end
182
188
  end
189
+
190
+ return true
191
+ else
192
+ return response['status']
183
193
  end
184
194
  end
185
195
 
@@ -1,3 +1,3 @@
1
1
  module AppleTvConverter
2
- VERSION = "0.5.9"
2
+ VERSION = "0.6.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: apple-tv-converter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.9
4
+ version: 0.6.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2014-04-29 00:00:00.000000000 Z
12
+ date: 2014-06-22 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec