apple-tv-converter 0.5.9 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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