storyboard 0.2.3

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.
Files changed (41) hide show
  1. data/.gitignore +3 -0
  2. data/.gitmodules +3 -0
  3. data/.rvmrc +1 -0
  4. data/Gemfile +10 -0
  5. data/Gemfile.lock +55 -0
  6. data/README.md +40 -0
  7. data/TODO +6 -0
  8. data/bin/storyboard +82 -0
  9. data/bin/storyboard-ffprobe +0 -0
  10. data/lib/.DS_Store +0 -0
  11. data/lib/storyboard/generators/pdf.rb +46 -0
  12. data/lib/storyboard/generators/sub.rb +32 -0
  13. data/lib/storyboard/subtitles.rb +96 -0
  14. data/lib/storyboard/thread-util.rb +308 -0
  15. data/lib/storyboard/time.rb +34 -0
  16. data/lib/storyboard/version.rb +3 -0
  17. data/lib/storyboard.rb +119 -0
  18. data/storyboard.gemspec +56 -0
  19. data/vendor/suby/.gitignore +3 -0
  20. data/vendor/suby/LICENSE +19 -0
  21. data/vendor/suby/README.md +27 -0
  22. data/vendor/suby/bin/suby +30 -0
  23. data/vendor/suby/lib/suby/downloader/addic7ed.rb +65 -0
  24. data/vendor/suby/lib/suby/downloader/opensubtitles.rb +83 -0
  25. data/vendor/suby/lib/suby/downloader/tvsubtitles.rb +90 -0
  26. data/vendor/suby/lib/suby/downloader.rb +177 -0
  27. data/vendor/suby/lib/suby/filename_parser.rb +103 -0
  28. data/vendor/suby/lib/suby/interface.rb +17 -0
  29. data/vendor/suby/lib/suby/movie_hasher.rb +31 -0
  30. data/vendor/suby/lib/suby.rb +89 -0
  31. data/vendor/suby/spec/fixtures/.gitkeep +0 -0
  32. data/vendor/suby/spec/mock_http.rb +22 -0
  33. data/vendor/suby/spec/spec_helper.rb +3 -0
  34. data/vendor/suby/spec/suby/downloader/addict7ed_spec.rb +28 -0
  35. data/vendor/suby/spec/suby/downloader/opensubtitles_spec.rb +33 -0
  36. data/vendor/suby/spec/suby/downloader/tvsubtitles_spec.rb +50 -0
  37. data/vendor/suby/spec/suby/downloader_spec.rb +11 -0
  38. data/vendor/suby/spec/suby/filename_parser_spec.rb +66 -0
  39. data/vendor/suby/spec/suby_spec.rb +27 -0
  40. data/vendor/suby/suby.gemspec +20 -0
  41. metadata +232 -0
@@ -0,0 +1,177 @@
1
+ require 'net/http'
2
+ require 'cgi/util'
3
+ require 'nokogiri'
4
+ require 'xmlrpc/client'
5
+ require 'zlib'
6
+ require 'stringio'
7
+
8
+ module Suby
9
+ class Downloader
10
+ DOWNLOADERS = []
11
+ def self.inherited(downloader)
12
+ DOWNLOADERS << downloader
13
+ end
14
+
15
+ attr_reader :show, :season, :episode, :video_data, :file, :lang
16
+
17
+ def initialize(file, *args)
18
+ @file = file
19
+ @lang = (args.last || 'en').to_sym
20
+ @video_data = FilenameParser.parse(file)
21
+ if video_data[:type] == :tvshow
22
+ @show, @season, @episode = video_data.values_at(:show, :season, :episode)
23
+ end
24
+ end
25
+
26
+ def support_video_type?
27
+ self.class::SUBTITLE_TYPES.include? video_data[:type]
28
+ end
29
+
30
+ def to_s
31
+ self.class.name.sub(/^.+::/, '')
32
+ end
33
+
34
+ def http
35
+ @http ||= Net::HTTP.new(self.class::SITE).start
36
+ end
37
+
38
+ def xmlrpc
39
+ @xmlrpc ||= XMLRPC::Client.new(self.class::SITE, self.class::XMLRPC_PATH)
40
+ end
41
+
42
+ def get(path, initheader = {}, parse_response = true)
43
+ response = http.get(path, initheader)
44
+ if parse_response
45
+ unless Net::HTTPSuccess === response
46
+ raise DownloaderError, "Invalid response for #{path}: #{response}"
47
+ end
48
+ response.body
49
+ else
50
+ response
51
+ end
52
+ end
53
+
54
+ def post(path, data = {}, initheader = {})
55
+ post = Net::HTTP::Post.new(path, initheader)
56
+ post.form_data = data
57
+ response = http.request(post)
58
+ unless Net::HTTPSuccess === response
59
+ raise DownloaderError, "Invalid response for #{path}(#{data}): " +
60
+ response.inspect
61
+ end
62
+ response.body
63
+ end
64
+
65
+ def get_redirection(path, initheader = {})
66
+ response = http.get(path, initheader)
67
+ location = response['Location']
68
+ unless (Net::HTTPFound === response or
69
+ Net::HTTPSuccess === response) and location
70
+ raise DownloaderError, "Invalid response for #{path}: " +
71
+ "#{response}: location: #{location.inspect}, #{response.body}"
72
+ end
73
+ location
74
+ end
75
+
76
+ def download
77
+ save extract download_url
78
+ end
79
+
80
+ def subtitles(url_or_response = download_url)
81
+ if Net::HTTPSuccess === url_or_response
82
+ url_or_response.body
83
+ else
84
+ get(url_or_response)
85
+ end
86
+ end
87
+
88
+ def save(contents)
89
+ sub_name(contents).write
90
+ end
91
+
92
+ def extract(url_or_response)
93
+ contents = subtitles(url_or_response)
94
+ http.finish
95
+ format = self.class::FORMAT
96
+ case format
97
+ when :file
98
+ # nothing special to do
99
+ when :gz
100
+ begin
101
+ gz = Zlib::GzipReader.new(StringIO.new(contents))
102
+ contents = gz.read
103
+ ensure
104
+ gz.close if gz
105
+ end
106
+ when :zip
107
+ TEMP_ARCHIVE.write contents
108
+ Suby.extract_sub_from_archive(TEMP_ARCHIVE, format, TEMP_SUBTITLES)
109
+ contents = TEMP_SUBTITLES.read
110
+ else
111
+ raise "unknown subtitles format: #{format}"
112
+ end
113
+ encode contents
114
+ end
115
+
116
+ def sub_name(contents)
117
+ file.sub_ext sub_extension(contents)
118
+ end
119
+
120
+ def sub_extension(contents)
121
+ if contents[0..10] =~ /1\r?\n/
122
+ 'srt'
123
+ else
124
+ 'sub'
125
+ end
126
+ end
127
+
128
+ def imdbid
129
+ @imdbid ||= begin
130
+ nfo_file = find_nfo_file
131
+ convert_to_utf8_from_latin1(nfo_file.read)[%r!imdb\.[^/]+/title/tt(\d+)!i, 1] if nfo_file
132
+ end
133
+ end
134
+
135
+ def find_nfo_file
136
+ @file.dir.children.find { |file| file.ext == "nfo" }
137
+ end
138
+
139
+ def convert_to_utf8_from_latin1(content)
140
+ if content.valid_encoding?
141
+ content
142
+ else
143
+ enc = content.encoding
144
+ if content.force_encoding("ISO-8859-1").valid_encoding?
145
+ yield if block_given?
146
+ content.encode("UTF-8")
147
+ else
148
+ # restore original encoding
149
+ subtitles.force_encoding(enc)
150
+ end
151
+ end
152
+ end
153
+
154
+ def success_message
155
+ "Found"
156
+ end
157
+
158
+ def encode(subtitles)
159
+ if @lang == :fr
160
+ convert_to_utf8_from_latin1(subtitles) do
161
+ def self.success_message
162
+ "#{super} (transcoded from ISO-8859-1)"
163
+ end
164
+ end
165
+ else
166
+ subtitles
167
+ end
168
+ end
169
+ end
170
+ end
171
+
172
+ # Defines downloader order
173
+ %w[
174
+ opensubtitles
175
+ tvsubtitles
176
+ addic7ed
177
+ ].each { |downloader| require_relative "downloader/#{downloader}" }
@@ -0,0 +1,103 @@
1
+ module Suby
2
+ module FilenameParser
3
+ extend self
4
+
5
+ # from tvnamer @ ab2c6c, with author's agreement, adapted
6
+ # See https://github.com/dbr/tvnamer/blob/master/tvnamer/config_defaults.py
7
+ TVSHOW_PATTERNS = [
8
+ # foo.s0101
9
+ /^(?<show>.+?)
10
+ [ \._\-]
11
+ [Ss](?<season>[0-9]{2})
12
+ [\.\- ]?
13
+ (?<episode>[0-9]{2})
14
+ [^0-9]*$/x,
15
+
16
+ # foo.1x09*
17
+ /^(?<show>.+?)
18
+ [ \._\-]
19
+ \[?
20
+ (?<season>[0-9]+)
21
+ [xX]
22
+ (?<episode>[0-9]+)
23
+ \]?
24
+ [^\/]*$/x,
25
+
26
+ # foo.s01.e01, foo.s01_e01
27
+ /^(?<show>.+?)
28
+ [ \._\-]
29
+ \[?
30
+ [Ss](?<season>[0-9]+)[\. _-]?
31
+ [Ee]?(?<episode>[0-9]+)
32
+ \]?
33
+ [^\/]*$/x,
34
+
35
+ # foo - [01.09]
36
+ /^(?<show>.+?)
37
+ [ \._\-]?
38
+ \[
39
+ (?<season>[0-9]+?)
40
+ [.]
41
+ (?<episode>[0-9]+?)
42
+ \]
43
+ [ \._\-]?
44
+ [^\/]*$/x,
45
+
46
+ # Foo - S2 E 02 - etc
47
+ /^(?<show>.+?)
48
+ [ ]?[ \._\-][ ]?
49
+ [Ss](?<season>[0-9]+)[\.\- ]?
50
+ [Ee]?[ ]?(?<episode>[0-9]+)
51
+ [^\/]*$/x,
52
+
53
+ # Show - Episode 9999 [S 12 - Ep 131] - etc
54
+ /(?<show>.+)
55
+ [ ]-[ ]
56
+ [Ee]pisode[ ]\d+
57
+ [ ]
58
+ \[
59
+ [sS][ ]?(?<season>\d+)
60
+ ([ ]|[ ]-[ ]|-)
61
+ ([eE]|[eE]p)[ ]?(?<episode>\d+)
62
+ \]
63
+ .*$/x,
64
+
65
+ # foo.103*
66
+ /^(?<show>.+)
67
+ [ \._\-]
68
+ (?<season>[0-9]{1})
69
+ (?<episode>[0-9]{2})
70
+ [\._ -][^\/]*$/x,
71
+ ]
72
+ MOVIE_PATTERN = /^(?<movie>.*)[.\[( ](?<year>(?:19|20)\d{2})/
73
+
74
+ def parse(file)
75
+ filename = file.basename.to_s
76
+ if TVSHOW_PATTERNS.find { |pattern| pattern.match(filename) }
77
+ m = $~
78
+ { type: :tvshow, show: clean_show_name(m[:show]), season: m[:season].to_i, episode: m[:episode].to_i }
79
+ elsif m = MOVIE_PATTERN.match(filename)
80
+ { type: :movie, name: clean_show_name(m[:movie]), year: m[:year].to_i }
81
+ else
82
+ { type: :unknown, name: filename }
83
+ end
84
+ end
85
+
86
+ # from https://github.com/dbr/tvnamer/blob/master/tvnamer/utils.py#L78-95
87
+ # Cleans up series name by removing any . and _
88
+ # characters, along with any trailing hyphens.
89
+ #
90
+ # Is basically equivalent to replacing all _ and . with a
91
+ # space, but handles decimal numbers in string.
92
+ #
93
+ # clean_show_name("an.example.1.0.test") # => "an example 1.0 test"
94
+ # clean_show_name("an_example_1.0_test") # => "an example 1.0 test"
95
+ def clean_show_name show
96
+ show.gsub!(/(?<!\d)[.]|[.](?!\d)/, ' ')
97
+ show.tr!('_', ' ')
98
+ show.chomp!('-')
99
+ show.strip!
100
+ show
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,17 @@
1
+ require 'term/ansicolor'
2
+
3
+ module Suby
4
+ module Interface
5
+ def success message
6
+ puts Term::ANSIColor.green message
7
+ end
8
+
9
+ def failure message
10
+ puts Term::ANSIColor.blue message
11
+ end
12
+
13
+ def error message
14
+ puts Term::ANSIColor.red message
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,31 @@
1
+ module Suby
2
+ # from http://trac.opensubtitles.org/projects/opensubtitles/wiki/HashSourceCodes
3
+ module MovieHasher
4
+
5
+ CHUNK_SIZE = 64 * 1024 # in bytes
6
+ MASK64 = 0xffffffffffffffff # 2^64 - 1
7
+
8
+ def self.compute_hash(file)
9
+ filesize = file.size
10
+ hash = filesize
11
+
12
+ # Read 64 kbytes, divide up into 64 bits and add each
13
+ # to hash. Do for beginning and end of file.
14
+ file.open('rb') do |f|
15
+ # Q = unsigned long long = 64 bit
16
+ f.read(CHUNK_SIZE).unpack("Q*").each do |n|
17
+ hash = (hash + n) & MASK64
18
+ end
19
+
20
+ f.seek([0, filesize - CHUNK_SIZE].max, IO::SEEK_SET)
21
+
22
+ # And again for the end of the file
23
+ f.read(CHUNK_SIZE).unpack("Q*").each do |n|
24
+ hash = (hash + n) & MASK64
25
+ end
26
+ end
27
+
28
+ "%016x" % hash
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,89 @@
1
+ require 'path'
2
+ require 'zip/zip'
3
+ require 'mime/types'
4
+
5
+ Path.require_tree 'suby', except: %w[downloader/]
6
+
7
+ module Suby
8
+ NotFoundError = Class.new StandardError
9
+ DownloaderError = Class.new StandardError
10
+
11
+ SUB_EXTENSIONS = %w[srt sub]
12
+ TMPDIR = Path.tmpdir
13
+ TEMP_ARCHIVE = TMPDIR / 'archive'
14
+ TEMP_SUBTITLES = TMPDIR / 'subtitles'
15
+
16
+ class << self
17
+ include Interface
18
+
19
+ def download_subtitles(files, options = {})
20
+ Zip.options[:on_exists_proc] = options[:force]
21
+ files.each { |file|
22
+ file = Path(file)
23
+ if file.dir?
24
+ download_subtitles(file.children, options)
25
+ elsif SUB_EXTENSIONS.include?(file.ext)
26
+ # ignore already downloaded subtitles
27
+ elsif !options[:force] and SUB_EXTENSIONS.any? { |ext| f = file.sub_ext(ext) and f.exist? and !f.empty? }
28
+ puts "Skipping: #{file}"
29
+ elsif !file.exist? or video?(file)
30
+ download_subtitles_for_file(file, options)
31
+ end
32
+ }
33
+ ensure
34
+ TMPDIR.rm_rf
35
+ end
36
+
37
+ def video?(file)
38
+ MIME::Types.type_for(file.path).any? { |type| type.media_type == "video" }
39
+ end
40
+
41
+ def download_subtitles_for_file(file, options)
42
+ begin
43
+ puts file
44
+ success = Downloader::DOWNLOADERS.find { |downloader_class|
45
+ try_downloader(downloader_class.new(file, options[:lang]))
46
+ }
47
+ error "\nNo downloader could find subtitles for #{file}" unless success
48
+ rescue
49
+ error "\nThe download of the subtitles failed for #{file}:"
50
+ error "#{$!.class}: #{$!.message}"
51
+ $stderr.puts $!.backtrace
52
+ end
53
+ end
54
+
55
+ def try_downloader(downloader)
56
+ return false unless downloader.support_video_type?
57
+ begin
58
+ print " #{downloader.to_s.ljust(20)}"
59
+ downloader.download
60
+ rescue Suby::NotFoundError => error
61
+ failure "Failed: #{error.message}"
62
+ false
63
+ rescue Suby::DownloaderError => error
64
+ error "Error: #{error.message}"
65
+ false
66
+ else
67
+ success downloader.success_message
68
+ true
69
+ end
70
+ end
71
+
72
+ def extract_sub_from_archive(archive, format, file)
73
+ case format
74
+ when :zip
75
+ Zip::ZipFile.open(archive.to_s) { |zip|
76
+ sub = zip.entries.find { |entry|
77
+ entry.to_s =~ /\.#{Regexp.union SUB_EXTENSIONS}$/
78
+ }
79
+ raise "no subtitles in #{archive}" unless sub
80
+ sub.extract(file.to_s)
81
+ }
82
+ else
83
+ raise "unknown archive type (#{archive})"
84
+ end
85
+ ensure
86
+ archive.unlink if archive.exist?
87
+ end
88
+ end
89
+ end
File without changes
@@ -0,0 +1,22 @@
1
+ class Suby::Downloader
2
+ caches = {}
3
+ [:get, :post, :get_redirection].each { |meth|
4
+ original_method = instance_method(meth)
5
+ remove_method meth
6
+ define_method(meth) { |*args|
7
+ file = 'spec/fixtures/' + self.class::SITE.downcase + '.marshal'
8
+ caches[file] ||= File.exist?(file) ? Marshal.load(IO.read(file)) : {}
9
+ data = caches[file]
10
+
11
+ if data[args]
12
+ data[args]
13
+ else
14
+ puts "doing the real request: #{meth}(#{args * ', '})"
15
+ value = original_method.bind(self).call(*args)
16
+ data[args] = value
17
+ File.write(file, Marshal.dump(data))
18
+ value
19
+ end
20
+ }
21
+ }
22
+ end
@@ -0,0 +1,3 @@
1
+ require 'rspec'
2
+ require_relative '../lib/suby'
3
+ require_relative 'mock_http'
@@ -0,0 +1,28 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Suby::Downloader::Addic7ed do
4
+ file = Path('The Glee Project 01x03.avi')
5
+ downloader = Suby::Downloader::Addic7ed.new file
6
+
7
+ it 'finds the right subtitles' do
8
+ begin
9
+ downloader.subtitles[0..100].should include "Ellis, best first kiss?"
10
+ rescue Suby::NotFoundError => e
11
+ if e.message == "download exceeded"
12
+ pending e.message
13
+ else
14
+ raise e
15
+ end
16
+ end
17
+ end
18
+
19
+ it 'fails gently when the show or the episode does not exist' do
20
+ d = Suby::Downloader::Addic7ed.new(Path('Not Existing Show 1x1.mkv'))
21
+ -> { d.download_url }.should raise_error(Suby::NotFoundError, "show/season/episode not found")
22
+ end
23
+
24
+ it 'fails gently when there is no subtitles available' do
25
+ d = Suby::Downloader::Addic7ed.new(file, :es)
26
+ -> { p d.download_url }.should raise_error(Suby::NotFoundError, "no subtitles available")
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Suby::Downloader::OpenSubtitles do
4
+ file = Path("breaking.bad.s05e04.hdtv.x264-fqm.mp4")
5
+ downloader = Suby::Downloader::OpenSubtitles.new file
6
+ correct_query = [{ moviehash: "709b9ff887cf987d", moviebytesize: "308412149", sublanguageid: "eng" }]
7
+ wrong_query = correct_query.first.merge({ sublanguageid: "wrong_language" })
8
+
9
+ it 'finds the right subtitles' do
10
+ response = downloader.search_subtitles(correct_query)['data']
11
+ response.should_not be_false
12
+ response.first['MovieName'].should == '"Breaking Bad" Fifty-One'
13
+ end
14
+
15
+ it 'finds the right download link' do
16
+ url = downloader.download_url
17
+ url.should match(%r{http(s)?://.*opensubtitles.org/en/download/.*})
18
+ end
19
+
20
+ it "doesn't find anything for bad query" do
21
+ response = downloader.search_subtitles(wrong_query)['data']
22
+ response.should be_false
23
+ end
24
+
25
+ it "gets right token" do
26
+ downloader.token.should match(/\A[a-z0-9]{26}\z/)
27
+ end
28
+
29
+ it 'fails gently when there is no subtitles available' do
30
+ d = Suby::Downloader::OpenSubtitles.new(Path("Not Existing Show 1x1.mkv"), :eawdad)
31
+ -> { d.download_url }.should raise_error(Suby::NotFoundError, "no subtitles available")
32
+ end
33
+ end
@@ -0,0 +1,50 @@
1
+ require_relative '../../spec_helper'
2
+
3
+ describe Suby::Downloader::TVSubtitles do
4
+ file = Path('How I Met Your Mother 3x9 - Slapsgiving.mkv')
5
+ downloader = Suby::Downloader::TVSubtitles.new(file)
6
+ downloader_fr = Suby::Downloader::TVSubtitles.new(file, :fr)
7
+
8
+ it 'finds the right show url' do
9
+ downloader.show_url.should == '/tvshow-110.html'
10
+ end
11
+
12
+ it 'finds the right season url' do
13
+ downloader.season_url.should == '/tvshow-110-3.html'
14
+ end
15
+
16
+ it 'finds the right episode url' do
17
+ downloader.episode_url.should == '/episode-7517-en.html'
18
+ downloader_fr.episode_url.should == '/episode-7517-fr.html'
19
+ end
20
+
21
+ it 'finds the right subtitles url' do
22
+ downloader.subtitles_url.should == '/subtitle-9339.html'
23
+ downloader_fr.subtitles_url.should == '/subtitle-31249.html'
24
+ end
25
+
26
+ it 'finds the right download url' do
27
+ downloader.download_url.should == '/files/How%20I%20Met%20Your%20Mother_3x09_en.zip'
28
+ downloader_fr.download_url.should == '/files/How%20I%20Met%20Your%20Mother_3x09_fr.zip'
29
+ end
30
+
31
+ it 'fails gently when the show does not exist' do
32
+ d = Suby::Downloader::TVSubtitles.new(Path('Not Existing Show 1x1.mkv'))
33
+ -> { d.show_url }.should raise_error(Suby::NotFoundError, "show not found")
34
+ end
35
+
36
+ it 'fails gently when the season does not exist' do
37
+ d = Suby::Downloader::TVSubtitles.new(Path('How I Met Your Mother 99x1.mkv'))
38
+ -> { d.episode_url }.should raise_error(Suby::NotFoundError, "season not found")
39
+ end
40
+
41
+ it 'fails gently when the episode does not exist' do
42
+ d = Suby::Downloader::TVSubtitles.new(Path('How I Met Your Mother 3x99.mkv'))
43
+ -> { d.episode_url }.should raise_error(Suby::NotFoundError, "episode not found")
44
+ end
45
+
46
+ it 'fails gently when there is no subtitles available' do
47
+ d = Suby::Downloader::TVSubtitles.new(Path('Batman: The Animated Series 1x03.mkv'))
48
+ -> { d.subtitles_url }.should raise_error(Suby::NotFoundError, "no subtitles available")
49
+ end
50
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Suby::Downloader do
4
+ downloader = Suby::Downloader.new Path('How I Met Your Mother 3x9 - Slapsgiving.mkv')
5
+ it 'guess the right type of subtitles from the contents' do
6
+ downloader.sub_extension("1\r\n00").should == 'srt'
7
+ downloader.sub_extension("\xEF\xBB\xBF1\r\n00:00:14,397").should == 'srt'
8
+ downloader.sub_extension("\xEF\xBB\xBF1\r\n00:00:14,397").should == 'srt'
9
+ downloader.sub_extension("{346}{417}informa").should == 'sub'
10
+ end
11
+ end
@@ -0,0 +1,66 @@
1
+ require_relative '../spec_helper'
2
+
3
+ describe Suby::Downloader do
4
+ show = 'How I Met Your Mother'
5
+ season, episode = 3, 9
6
+ title = 'Slapsgiving'
7
+ ext = 'mkv'
8
+ dot_show = show.tr(' ', '.')
9
+ und_show = show.tr(' ', '_')
10
+ movie_name = "Somemovie.2002.720p.BluRay"
11
+ unknown_name = "crf-hp4"
12
+
13
+ context "cleans correctly the show name" do
14
+ {
15
+ dot_show => show,
16
+ und_show => show,
17
+ "an.example.1.0.test" => "an example 1.0 test",
18
+ "an_example_1.0_test" => "an example 1.0 test",
19
+ "an_3.0.1.example_1.0_test" => "an 3.0.1 example 1.0 test",
20
+ }.each_pair { |raw_show, true_show|
21
+ it raw_show do
22
+ # dup because a literal String is frozen and no point to keep the raw show
23
+ Suby::FilenameParser.clean_show_name(raw_show.dup).should == true_show
24
+ end
25
+ }
26
+ end
27
+
28
+ context "parse correctly the file name" do
29
+ [
30
+ {
31
+ examples:
32
+ [
33
+ "#{show} #{season}x#{episode}",
34
+ "#{show} #{season}x#{"%.2d" % episode}",
35
+ "#{show} #{season}x#{episode} - #{title}",
36
+ "#{show} #{season}x#{"%.2d" % episode} - #{title}",
37
+ "#{dot_show}.s0309",
38
+ "#{dot_show}.3x09",
39
+ "#{dot_show}.s03.e09",
40
+ "#{und_show}.s03_e09",
41
+ "#{show} - [03.09]",
42
+ "#{show} - S3 E 09",
43
+ "#{show} - Episode 9999 [S 3 - Ep 9]",
44
+ "#{show} - Episode 9999 [S 3 - Ep 9] - ",
45
+ "#{dot_show}.309",
46
+ ],
47
+ expected: { type: :tvshow, show: show, season: season, episode: episode }
48
+ },
49
+ {
50
+ examples: [movie_name],
51
+ expected: { type: :movie, name: "Somemovie", year: 2002 }
52
+ },
53
+ {
54
+ examples: [unknown_name],
55
+ expected: { type: :unknown, name: "#{unknown_name}.#{ext}"}
56
+ }
57
+ ].each do |type|
58
+ type[:examples].each do |filename|
59
+ it filename do
60
+ file = Path(filename).add_ext(ext)
61
+ Suby::FilenameParser.parse(file).should == type[:expected]
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,27 @@
1
+ require_relative 'spec_helper'
2
+ require 'tmpdir'
3
+ require 'digest/md5'
4
+
5
+ describe Suby do
6
+ file = 'How I Met Your Mother 3x9 - Slapsgiving.mkv'
7
+ srt = 'How I Met Your Mother 3x9 - Slapsgiving.srt'
8
+
9
+ it 'works :D', full: true do
10
+ Dir.mktmpdir do |dir|
11
+ suby = File.expand_path('../../bin/suby', __FILE__)
12
+ Dir.chdir(dir) do
13
+ system suby, file
14
+ Digest::MD5.hexdigest(File.read(srt)).should == '7c4b89452782c20c6086ba5c1f4e9d74'
15
+ end
16
+ end
17
+ end
18
+
19
+ it 'can detect videos' do
20
+ %w[avi mp4 mkv].each { |ext|
21
+ Suby.should be_a_video(Path("file").add_ext(ext))
22
+ }
23
+ %w[txt srt sub].each { |ext|
24
+ Suby.should_not be_a_video(Path("file").add_ext(ext))
25
+ }
26
+ end
27
+ end
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'suby'
3
+ s.summary = "Subtitles' downloader"
4
+ s.description = "Find and download subtitles"
5
+ s.author = 'eregon'
6
+ s.email = 'eregontp@gmail.com'
7
+ s.homepage = 'https://github.com/eregon/suby'
8
+
9
+ s.files = Dir['bin/*', 'lib/**/*.rb', '.gitignore', 'README.md', 'suby.gemspec']
10
+ s.executables = ['suby']
11
+
12
+ s.required_ruby_version = '>= 1.9.2'
13
+ s.add_dependency 'path', '>= 1.3.0'
14
+ s.add_dependency 'nokogiri'
15
+ s.add_dependency 'rubyzip'
16
+ s.add_dependency 'term-ansicolor'
17
+ s.add_dependency 'mime-types', '>= 1.19'
18
+
19
+ s.version = '0.4.0'
20
+ end