pirate-autosub 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,57 @@
1
+ # AutoSub
2
+
3
+ Ruby tool to automatically download subtitles (srt) inside your TV Shows folder.
4
+ Currently only english and french subtitles are supported.
5
+
6
+ ## Install
7
+
8
+ sudo gem install pirate-autosub --source http://gems.github.com
9
+
10
+ ## Usage
11
+
12
+ In your terminal:
13
+
14
+ autosub -p /path/of/your/TV Shows/folder -lang fr,en
15
+
16
+ more details with:
17
+
18
+ autosub -h
19
+
20
+ ## Folder Name Convention
21
+
22
+ AutoNZB use (and needs) specific folders/episodes name for your TV Shows:
23
+
24
+ + TV Shows
25
+ + Dexter (TV Show's name needs to be 'clean')
26
+ + Season 01
27
+ - Dexter S01E01.mkv (.mkv for 720p HD episode)
28
+ - Dexter 1x02.avi (.avi for SD episode)
29
+ - S01E03.mkv (TV Show's name in episode file's name not needed)
30
+ - S01E03.fr.srt (.fr.srt used to define subtitle's language)
31
+ + Fringe
32
+ - (season folder not needed)
33
+ - Fringe S01E01.mkv
34
+ - 1x01.avi
35
+
36
+ ## License
37
+
38
+ Copyright (c) 2008 Pirate
39
+
40
+ Permission is hereby granted, free of charge, to any person obtaining
41
+ a copy of this software and associated documentation files (the
42
+ "Software"), to deal in the Software without restriction, including
43
+ without limitation the rights to use, copy, modify, merge, publish,
44
+ distribute, sublicense, and/or sell copies of the Software, and to
45
+ permit persons to whom the Software is furnished to do so, subject to
46
+ the following conditions:
47
+
48
+ The above copyright notice and this permission notice shall be
49
+ included in all copies or substantial portions of the Software.
50
+
51
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
52
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
53
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
54
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
55
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
56
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
57
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,16 @@
1
+ # Rakefile
2
+ require 'rubygems'
3
+ require 'rake'
4
+ require 'echoe'
5
+
6
+ Echoe.new('autosub', '0.2.1') do |p|
7
+ p.description = "Ruby tool to automatically download subtitles (srt) inside your TV Shows folder"
8
+ p.url = "http://github.com/pirate/autosub"
9
+ p.author = "Pirate"
10
+ p.email = "pirate.2061@gmail.com"
11
+ p.ignore_pattern = ["tmp/*", "script/*"]
12
+ p.executable_pattern = "bin/autosub"
13
+ p.runtime_dependencies = ["hpricot", "rubyzip", "optiflag", "simple-rss"]
14
+ end
15
+
16
+ Dir["#{File.dirname(__FILE__)}/tasks/*.rake"].sort.each { |ext| load ext }
Binary file
@@ -0,0 +1,48 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{autosub}
5
+ s.version = "0.2.1"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 1.2") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Pirate"]
9
+ s.date = %q{2008-12-28}
10
+ s.default_executable = %q{autosub}
11
+ s.description = %q{Ruby tool to automatically download subtitles (srt) inside your TV Shows folder}
12
+ s.email = %q{pirate.2061@gmail.com}
13
+ s.executables = ["autosub"]
14
+ s.extra_rdoc_files = ["bin/autosub", "lib/episode.rb", "lib/inspector.rb", "lib/sites/seriessub.rb", "lib/sites/tvsubtitle.rb", "README.markdown"]
15
+ s.files = ["asset/srt.png", "autosub.gemspec", "bin/autosub", "lib/episode.rb", "lib/inspector.rb", "lib/sites/seriessub.rb", "lib/sites/tvsubtitle.rb", "Manifest", "Rakefile", "README.markdown"]
16
+ s.has_rdoc = true
17
+ s.homepage = %q{http://github.com/pirate/autosub}
18
+ s.rdoc_options = ["--line-numbers", "--inline-source", "--title", "Autosub", "--main", "README.markdown"]
19
+ s.require_paths = ["lib"]
20
+ s.rubyforge_project = %q{autosub}
21
+ s.rubygems_version = %q{1.3.1}
22
+ s.summary = %q{Ruby tool to automatically download subtitles (srt) inside your TV Shows folder}
23
+
24
+ if s.respond_to? :specification_version then
25
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
26
+ s.specification_version = 2
27
+
28
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
29
+ s.add_runtime_dependency(%q<hpricot>, [">= 0"])
30
+ s.add_runtime_dependency(%q<rubyzip>, [">= 0"])
31
+ s.add_runtime_dependency(%q<optiflag>, [">= 0"])
32
+ s.add_runtime_dependency(%q<simple-rss>, [">= 0"])
33
+ s.add_development_dependency(%q<echoe>, [">= 0"])
34
+ else
35
+ s.add_dependency(%q<hpricot>, [">= 0"])
36
+ s.add_dependency(%q<rubyzip>, [">= 0"])
37
+ s.add_dependency(%q<optiflag>, [">= 0"])
38
+ s.add_dependency(%q<simple-rss>, [">= 0"])
39
+ s.add_dependency(%q<echoe>, [">= 0"])
40
+ end
41
+ else
42
+ s.add_dependency(%q<hpricot>, [">= 0"])
43
+ s.add_dependency(%q<rubyzip>, [">= 0"])
44
+ s.add_dependency(%q<optiflag>, [">= 0"])
45
+ s.add_dependency(%q<simple-rss>, [">= 0"])
46
+ s.add_dependency(%q<echoe>, [">= 0"])
47
+ end
48
+ end
@@ -0,0 +1,23 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optiflag'
4
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'inspector')
5
+
6
+ module AutoSub extend OptiFlagSet
7
+ flag "p" do
8
+ description "Directory path with many TV Shows directories"
9
+ end
10
+ optional_flag "lang" do
11
+ description "Language(s) you want for your subtitles, default: fr,en
12
+ (Order is important to define if a srt is needed)"
13
+ end
14
+
15
+ and_process!
16
+ end
17
+
18
+ begin
19
+ inspector = Inspector.new(ARGV.flags.p, ARGV.flags.lang)
20
+ rescue => e
21
+ p e.to_s
22
+ Inspector.growl("AutoSub Error!", 'look into the console log', 'failure.png')
23
+ end
@@ -0,0 +1,89 @@
1
+ class Episode
2
+
3
+ attr_accessor :path, :name, :ext, :format, :srt, :tv_show, :season, :number
4
+
5
+ def initialize(tv_show, path)
6
+ @path = path
7
+ @ext = File.extname(@path)
8
+ @name = File.basename(path, @ext)
9
+ @tv_show = tv_show
10
+ @srt = []
11
+
12
+ search_existing_srt
13
+ define_format
14
+ define_season_and_episode_number
15
+ end
16
+
17
+ def filename
18
+ @name + @ext
19
+ end
20
+
21
+ def episode_name
22
+ "#{@tv_show} S#{@season < 10 ? "0#{@season}" : @season}E#{@number < 10 ? "0#{@number}" : @number}"
23
+ end
24
+
25
+ def episode_name_with_format
26
+ episode_name + "#{format == 'hd' ? ' 720p' : ''}"
27
+ end
28
+
29
+ def need_srt?(langs)
30
+ need = -1
31
+ langs = langs.reverse
32
+ langs.each do |lang|
33
+ need = langs.index(lang) if @srt.include?(lang) && need < langs.index(lang)
34
+ end
35
+ need != langs.size - 1
36
+ end
37
+
38
+ def srt_filename(lang)
39
+ "#{File.dirname(path)}/#{name}.#{lang}.srt"
40
+ end
41
+
42
+ def self.valid?(filename)
43
+ filename.match(/.*(([0-9]{1,2}X[0-9]{1,2})|(S[0-9][0-9]E[0-9][0-9])).*((\.avi)|(\.mkv))$/i)
44
+ end
45
+
46
+ def good_format?(filename)
47
+ case @format
48
+ when 'hd'
49
+ filename.include?('720p')
50
+ when 'sd'
51
+ !filename.include?('720p')
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def search_existing_srt
58
+ dir = Dir.new(File.dirname(@path))
59
+ dir.select { |el| File.extname(el) == '.srt' && el.include?(@name) && !el.include?('._') }.each do |srt|
60
+ case srt
61
+ when /\.(french|fr|fre|fra)\.srt/i
62
+ @srt << 'fr'
63
+ when /\.(english|en|eng)\.srt/i
64
+ @srt << 'en'
65
+ end
66
+ end
67
+ end
68
+
69
+ def define_format
70
+ case @ext
71
+ when '.mkv'
72
+ @format = 'hd'
73
+ when '.avi'
74
+ @format = 'sd'
75
+ end
76
+ end
77
+
78
+ def define_season_and_episode_number
79
+ case @name
80
+ when /S[0-9][0-9]E[0-9][0-9]/i # S02E03
81
+ @season = @name.match(/.*S([0-9]{1,2})E.*$/i)[1].to_i
82
+ @number = @name.match(/.*E([0-9]{1,2}).*$/i)[1].to_i
83
+ when /[0-9]{1,2}X[0-9]{1,2}/i # 2X03
84
+ @season = @name.match(/.*([0-9]{1,2})X[0-9]{1,2}.*$/i)[1].to_i
85
+ @number = @name.match(/.*[0-9]{1,2}X([0-9]{1,2}).*$/i)[1].to_i
86
+ end
87
+ end
88
+
89
+ end
@@ -0,0 +1,76 @@
1
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'episode')
2
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'sites', 'seriessub')
3
+ require File.join(File.dirname(__FILE__), '..', 'lib', 'sites', 'tvsubtitle')
4
+
5
+ class Inspector
6
+
7
+ attr_accessor :tv_shows, :episodes, :langs
8
+
9
+ def initialize(path , langs = nil)
10
+ @path = path.gsub(/\/$/,'')
11
+ @langs = langs ? langs.split(',') : ['fr','en']
12
+
13
+ @tv_shows = [] # {:name => ..., :path => ...}
14
+ initialize_tv_shows
15
+ $stdout.print "Found #{@tv_shows.size} TV Show(s) in #{@path}\n"
16
+
17
+ @episodes = []
18
+ initialize_episodes
19
+ # keep only episodes who needs srt
20
+ $stdout.print "Found #{@episodes.select { |e| e.need_srt?(@langs) }.size} episode(s) who need(s) srt\n"
21
+
22
+ # Find new srt
23
+ TVSubtitle.new(self)
24
+ SeriesSub.new(self)
25
+ end
26
+
27
+ def growl_episode(episode, lang)
28
+ Inspector.growl episode.episode_name_with_format, "#{lang_name(lang)} srt added", "srt.png"
29
+ end
30
+
31
+ def self.growl(title, msg, icon, pri = 0)
32
+ system("/usr/local/bin/growlnotify -w -n autosub --image #{File.dirname(__FILE__) + "/../asset/#{icon}"} -p #{pri} -m #{msg.inspect} #{title} &")
33
+ end
34
+
35
+ private
36
+
37
+ def initialize_tv_shows
38
+ # path is a dir with many tv_shows dir
39
+ base_dir = clean_dir(Dir.new(@path))
40
+ base_dir.each do |tv_show_name|
41
+ @tv_shows << { :name => tv_show_name, :path => "#{@path}/#{tv_show_name}" }
42
+ end
43
+ end
44
+
45
+ def initialize_episodes
46
+ @tv_shows.each do |tv_show|
47
+ search_episodes(tv_show[:name], tv_show[:path])
48
+ end
49
+ end
50
+
51
+ def search_episodes(tv_show_name, path)
52
+ elements = clean_dir(Dir.new(path))
53
+ elements.each do |el|
54
+ el_path = "#{path}/#{el}"
55
+ if File.directory?(el_path)
56
+ search_episodes(tv_show_name, el_path)
57
+ elsif Episode.valid?(el)
58
+ episodes << Episode.new(tv_show_name, el_path)
59
+ end
60
+ end
61
+ end
62
+
63
+ def clean_dir(dir)
64
+ dir.select { |e| !["..", ".", ".DS_Store", ".com.apple.timemachine.supported"].include?(e) }
65
+ end
66
+
67
+ def lang_name(lang)
68
+ case lang
69
+ when 'en'
70
+ 'English'
71
+ when 'fr'
72
+ 'French'
73
+ end
74
+ end
75
+
76
+ end
@@ -0,0 +1,116 @@
1
+ require 'hpricot'
2
+ require 'open-uri'
3
+ require 'zip/zip'
4
+
5
+ # http://www.seriessub.com/
6
+ class SeriesSub
7
+
8
+ def initialize(inspector)
9
+ @inspector = inspector
10
+ episodes = @inspector.episodes.select { |e| e.need_srt?(@inspector.langs) }
11
+
12
+ $stdout.print "------------------------------------------\n"
13
+ $stdout.print "Start srt searching from www.seriessub.com\n"
14
+ $stdout.print "------------------------------------------\n"
15
+ episodes.each do |e|
16
+ @inspector.langs.each do |l|
17
+ unless e.srt.include?(l)
18
+ $stdout.print "#{e.episode_name_with_format} [#{l}]: "
19
+ if get_srt(e, l)
20
+ $stdout.print "FOUND\n"
21
+ e.srt << l
22
+ @inspector.growl_episode(e, l)
23
+ break
24
+ else
25
+ $stdout.print "NOT FOUND\n"
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def get_srt(episode, lang)
35
+ found = false
36
+ doc = Hpricot(open(url(episode)))
37
+ doc.search("td.gst_fichier a").each do |el|
38
+ text = el.inner_text.gsub(/(\_|\.)/, ' ')
39
+ if good_episode?(episode, text) && text.include?(lang_version(lang))
40
+ if text.include?('zip')
41
+ Tempfile.open("tv.zip") do |tempfile|
42
+ tempfile.write(open(el[:href]).read) # download the zip
43
+ tempfile.close # tempfile need to be closed for unzip it
44
+ Zip::ZipFile.open(tempfile.path) do |zip_file|
45
+ zip_file.each do |f|
46
+ if good_episode?(episode, f.name) && f.name.include?(lang_version(lang)) && episode.good_format?(f.name) && no_tag?(f.name)
47
+ zip_file.extract(f, episode.srt_filename(lang))
48
+ found = true
49
+ end
50
+ end
51
+ end
52
+ end
53
+ elsif episode.good_format?(text) && no_tag?(text) # just a single srt file
54
+ Tempfile.open("tv.srt") do |tempfile|
55
+ tempfile.write(open(el[:href]).read) # download the zip
56
+ tempfile.close
57
+ File.move(tempfile.path, episode.srt_filename(lang))
58
+ found = true
59
+ end
60
+ end
61
+ end
62
+ end
63
+ found
64
+ end
65
+
66
+
67
+ def url(episode)
68
+ "http://www.seriessub.com/sous-titres/#{tv_show(episode)}/#{season(episode)}"
69
+ end
70
+
71
+ def tv_show(episode)
72
+ name = episode.tv_show.gsub(/\s\([0-9]{4}\)/,'')
73
+ case name
74
+ when "Terminator The Sarah Connor Chronicles"
75
+ "The Sarah Connor Chronicles"
76
+ when "The IT Crowd"
77
+ "IT Crowd"
78
+ else
79
+ name
80
+ end.downcase.gsub(/\s/, '_')
81
+ end
82
+
83
+ def season(episode)
84
+ "saison_#{episode.season}"
85
+ end
86
+
87
+ def lang_version(lang)
88
+ case lang
89
+ when 'fr'
90
+ "VF"
91
+ when 'en'
92
+ "VO"
93
+ end
94
+ end
95
+
96
+ def num(episode)
97
+ "#{episode.season}#{episode.number < 10 ? "0#{episode.number}" : episode.number}"
98
+ end
99
+
100
+ def good_episode?(episode, name)
101
+ num2 = "S#{episode.season < 10 ? "0#{episode.season}" : episode.season}E#{episode.number < 10 ? "0#{episode.number}" : episode.number}"
102
+ name.include?(num(episode)) || name.include?(num2)
103
+ end
104
+
105
+ def no_tag?(filename)
106
+ case filename
107
+ when /notag/i
108
+ true
109
+ when /tag/i
110
+ false
111
+ else
112
+ true
113
+ end
114
+ end
115
+
116
+ end
@@ -0,0 +1,70 @@
1
+ require 'simple-rss'
2
+ require 'open-uri'
3
+ require 'zip/zip'
4
+ require 'cgi'
5
+
6
+ # http://www.tvsubtitles.net/
7
+ class TVSubtitle
8
+
9
+ def initialize(inspector)
10
+ @inspector = inspector
11
+ episodes = @inspector.episodes.select { |e| e.need_srt?(@inspector.langs) }
12
+
13
+ $stdout.print "--------------------------------------------\n"
14
+ $stdout.print "Searching fot srt in www.tvsubtitles.net RSS\n"
15
+ $stdout.print "--------------------------------------------\n"
16
+ @inspector.langs.each do |l|
17
+ rss(l).items.each do |item|
18
+ episodes.each do |e|
19
+ unless e.srt.include?(l)
20
+ if episode_title(e, l) == item_title(item)
21
+ $stdout.print "FOUND: #{e.episode_name_with_format} [#{l}]\n"
22
+ download_srt(item, e, l)
23
+ e.srt << l
24
+ @inspector.growl_episode(e, l)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def srt_link(item)
35
+ id = item.link.match(/([0-9]+)/)[1]
36
+ http = Net::HTTP.new('www.tvsubtitles.net')
37
+ resp, data = http.get("/download-#{id}.html")
38
+ "http://www.tvsubtitles.net/#{resp.response['Location'].gsub(/\s/, '%20')}"
39
+ end
40
+
41
+ def download_srt(item, episode, lang)
42
+ url = srt_link(item)
43
+ Tempfile.open("srt") do |tempfile|
44
+ tempfile.write(open(url).read) # download the srt
45
+ tempfile.close
46
+ Zip::ZipFile.open(tempfile.path) do |zip_file|
47
+ zip_file.each do |f|
48
+ zip_file.extract(f, episode.srt_filename(lang))
49
+ end
50
+ end
51
+ end
52
+ end
53
+
54
+ def rss(lang)
55
+ SimpleRSS.parse open("http://fr.tvsubtitles.net/rss#{lang}.xml")
56
+ end
57
+
58
+ def num(episode)
59
+ "#{episode.season}X#{episode.number < 10 ? "0#{episode.number}" : episode.number}"
60
+ end
61
+
62
+ def episode_title(episode, lang)
63
+ "#{episode.tv_show} #{num(episode)} #{lang} #{episode.format}".downcase
64
+ end
65
+
66
+ def item_title(item)
67
+ "#{item.title} #{item.description.include?('720p') ? 'hd' : 'sd'}".downcase
68
+ end
69
+
70
+ end
metadata ADDED
@@ -0,0 +1,116 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pirate-autosub
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.1
5
+ platform: ruby
6
+ authors:
7
+ - Pirate
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2008-12-28 00:00:00 -08:00
13
+ default_executable: autosub
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: hpricot
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: rubyzip
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: "0"
32
+ version:
33
+ - !ruby/object:Gem::Dependency
34
+ name: optiflag
35
+ version_requirement:
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: "0"
41
+ version:
42
+ - !ruby/object:Gem::Dependency
43
+ name: simple-rss
44
+ version_requirement:
45
+ version_requirements: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: "0"
50
+ version:
51
+ - !ruby/object:Gem::Dependency
52
+ name: echoe
53
+ version_requirement:
54
+ version_requirements: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ description: Ruby tool to automatically download subtitles (srt) inside your TV Shows folder
61
+ email: pirate.2061@gmail.com
62
+ executables:
63
+ - autosub
64
+ extensions: []
65
+
66
+ extra_rdoc_files:
67
+ - bin/autosub
68
+ - lib/episode.rb
69
+ - lib/inspector.rb
70
+ - lib/sites/seriessub.rb
71
+ - lib/sites/tvsubtitle.rb
72
+ - README.markdown
73
+ files:
74
+ - asset/srt.png
75
+ - autosub.gemspec
76
+ - bin/autosub
77
+ - lib/episode.rb
78
+ - lib/inspector.rb
79
+ - lib/sites/seriessub.rb
80
+ - lib/sites/tvsubtitle.rb
81
+ - Manifest
82
+ - Rakefile
83
+ - README.markdown
84
+ has_rdoc: true
85
+ homepage: http://github.com/pirate/autosub
86
+ post_install_message:
87
+ rdoc_options:
88
+ - --line-numbers
89
+ - --inline-source
90
+ - --title
91
+ - Autosub
92
+ - --main
93
+ - README.markdown
94
+ require_paths:
95
+ - lib
96
+ required_ruby_version: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - ">="
99
+ - !ruby/object:Gem::Version
100
+ version: "0"
101
+ version:
102
+ required_rubygems_version: !ruby/object:Gem::Requirement
103
+ requirements:
104
+ - - ">="
105
+ - !ruby/object:Gem::Version
106
+ version: "1.2"
107
+ version:
108
+ requirements: []
109
+
110
+ rubyforge_project: autosub
111
+ rubygems_version: 1.2.0
112
+ signing_key:
113
+ specification_version: 2
114
+ summary: Ruby tool to automatically download subtitles (srt) inside your TV Shows folder
115
+ test_files: []
116
+