suby 0.0.5 → 0.0.6
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/.gitignore +1 -0
- data/README.md +23 -0
- data/bin/suby +1 -3
- data/lib/suby.rb +14 -73
- data/lib/suby/downloader.rb +91 -0
- data/suby.gemspec +14 -0
- metadata +20 -5
data/.gitignore
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
*.gem
|
data/README.md
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# suby
|
2
|
+
|
3
|
+
Find and download subtitles
|
4
|
+
|
5
|
+
suby is a little script to find and download subtitles for TV series
|
6
|
+
|
7
|
+
## Install
|
8
|
+
|
9
|
+
gem install suby
|
10
|
+
|
11
|
+
## Synopsis
|
12
|
+
|
13
|
+
suby 'my show 1x01 - Pilot.avi' # => Downloads my show 1x01 - Pilot.srt
|
14
|
+
|
15
|
+
## Status
|
16
|
+
|
17
|
+
Under heavy development.
|
18
|
+
|
19
|
+
* Currently only accept one format for input.
|
20
|
+
You could use [tvnamer](https://github.com/dbr/tvnamer) to rename the video files before.
|
21
|
+
|
22
|
+
* Search only on tvsubtitles.net.
|
23
|
+
Many series have subtitles there, but not all.
|
data/bin/suby
CHANGED
data/lib/suby.rb
CHANGED
@@ -1,83 +1,24 @@
|
|
1
|
-
|
2
|
-
require 'nokogiri'
|
1
|
+
require_relative 'suby/downloader'
|
3
2
|
|
4
3
|
module Suby
|
5
4
|
extend self
|
6
5
|
|
7
|
-
DEFAULT_OPTIONS = {
|
8
|
-
lang: 'en'
|
9
|
-
}
|
10
|
-
|
11
6
|
SUB_EXTENSIONS = %w[srt sub]
|
12
7
|
TEMP_ARCHIVE_NAME = '__archive__'
|
13
|
-
SITE = 'www.tvsubtitles.net'
|
14
|
-
SEARCH_URL = '/search.php'
|
15
|
-
|
16
|
-
# cache
|
17
|
-
SHOW_URLS = {}
|
18
|
-
SHOW_PAGES = {}
|
19
|
-
|
20
|
-
def download_subtitles(file, options = {})
|
21
|
-
options = DEFAULT_OPTIONS.merge options
|
22
|
-
return if SUB_EXTENSIONS.include? File.extname(file)
|
23
|
-
return puts "Skipping: #{file}" if SUB_EXTENSIONS.any? { |ext|
|
24
|
-
File.exist? File.basename(file, File.extname(file)) + ".#{ext}" }
|
25
|
-
|
26
|
-
unless /^(?<show>.+) (?<season>\d{1,2})x(?<episode>\d{1,2}) - .+\.[a-z]+?$/ =~ file
|
27
|
-
return puts "wrong file format (#{file}). Must be:\n<show> <season>x<episode> - <title>.<ext>"
|
28
|
-
end
|
29
|
-
season, episode = [season, episode].map(&:to_i)
|
30
|
-
puts "Searching subtitles for #{file}:"
|
31
|
-
puts "Show: #{show}, Season: #{season}, Episode: #{episode}"
|
32
|
-
|
33
|
-
Net::HTTP.start(SITE) { |tvsubtitles|
|
34
|
-
# search show
|
35
|
-
show_url = (SHOW_URLS[show] ||= begin
|
36
|
-
post = Net::HTTP::Post.new(SEARCH_URL)
|
37
|
-
post.form_data = { 'q' => show }
|
38
|
-
results = Nokogiri tvsubtitles.request(post).body
|
39
|
-
url = results.css('ul li div a').first[:href]
|
40
|
-
|
41
|
-
raise 'could not find the show' unless /^\/tvshow-(\d+)\.html$/ =~ url
|
42
|
-
"/tvshow-#{$1}-#{season}.html"
|
43
|
-
end)
|
44
|
-
puts "show url: #{show_url}"
|
45
|
-
|
46
|
-
# search episode
|
47
|
-
show = (SHOW_PAGES[show] ||= Nokogiri tvsubtitles.get(show_url).body)
|
48
|
-
episode_url = nil
|
49
|
-
show.css('div.left_articles table tr').find { |tr|
|
50
|
-
tr.children.find { |td| td.name == 'td' && td.text =~ /\A#{season}x0?#{episode}\z/ }
|
51
|
-
}.children.find { |td|
|
52
|
-
td.children.find { |a|
|
53
|
-
a.name == 'a' && a[:href].start_with?('episode') && episode_url = a[:href]
|
54
|
-
}
|
55
|
-
}
|
56
|
-
|
57
|
-
raise "invalid episode url: #{episode_url}" unless episode_url =~ /^episode-(\d+)\.html$/
|
58
|
-
episode_url = "/episode-#{$1}-#{options[:lang]}.html"
|
59
|
-
puts "episode url: #{episode_url}"
|
60
|
-
|
61
|
-
# subtitles
|
62
|
-
subtitles = Nokogiri tvsubtitles.get(episode_url).body
|
63
|
-
|
64
|
-
# TODO: choose 720p or most downloaded instead of first found
|
65
|
-
subtitle_url = subtitles.css('div.left_articles a').find { |a| a.name == 'a' && a[:href].start_with?('/subtitle') }[:href]
|
66
|
-
raise 'invalid subtitle url' unless subtitle_url =~ /^\/subtitle-(\d+)\.html/
|
67
|
-
puts "subtitle url: #{subtitle_url}"
|
68
|
-
|
69
|
-
# download
|
70
|
-
download_url = tvsubtitles.get("/download-#{$1}.html")['Location']
|
71
|
-
download_url = URI.escape('/'+download_url)
|
72
|
-
puts "download url: #{download_url}"
|
73
8
|
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
9
|
+
def download_subtitles(files, options = {})
|
10
|
+
files.each { |file|
|
11
|
+
next if SUB_EXTENSIONS.include? File.extname(file)
|
12
|
+
next puts "Skipping: #{file}" if SUB_EXTENSIONS.any? { |ext|
|
13
|
+
File.exist? File.basename(file, File.extname(file)) + ".#{ext}" }
|
14
|
+
|
15
|
+
begin
|
16
|
+
Downloader.new(file, options[:lang]).download
|
17
|
+
rescue
|
18
|
+
puts " The download of the subtitles failed for #{file}:"
|
19
|
+
puts " #{$!.class}: #{$!.message}"
|
20
|
+
puts $!.backtrace.map { |line| line.prepend ' '*4 }
|
21
|
+
end
|
81
22
|
}
|
82
23
|
end
|
83
24
|
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Suby
|
5
|
+
class Downloader
|
6
|
+
SITE = 'www.tvsubtitles.net'
|
7
|
+
SEARCH_URL = '/search.php'
|
8
|
+
|
9
|
+
# cache
|
10
|
+
SHOW_URLS = {}
|
11
|
+
SHOW_PAGES = {}
|
12
|
+
|
13
|
+
attr_reader :show, :season, :episode, :file, :lang
|
14
|
+
|
15
|
+
def initialize file, lang = nil
|
16
|
+
@file, @lang = file, lang || 'en'
|
17
|
+
unless /^(?<show>.+) (?<season>\d{1,2})x(?<episode>\d{1,2})(?: - .+)?\.[a-z]+?$/ =~ file
|
18
|
+
raise "wrong file format (#{file}). Must be:\n<show> <season>x<episode>[ - <title>].<ext>"
|
19
|
+
end
|
20
|
+
@show, @season, @episode = show, season.to_i, episode.to_i
|
21
|
+
end
|
22
|
+
|
23
|
+
def http
|
24
|
+
@http ||= Net::HTTP.new(SITE).start
|
25
|
+
end
|
26
|
+
|
27
|
+
def show_url
|
28
|
+
SHOW_URLS[show] ||= begin
|
29
|
+
post = Net::HTTP::Post.new(SEARCH_URL)
|
30
|
+
post.form_data = { 'q' => show }
|
31
|
+
results = Nokogiri http.request(post).body
|
32
|
+
url = results.css('ul li div a').first[:href]
|
33
|
+
|
34
|
+
raise 'could not find the show' unless /^\/tvshow-(\d+)\.html$/ =~ url
|
35
|
+
"/tvshow-#{$1}-#{season}.html"
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def episode_url
|
40
|
+
@episode_url ||= begin
|
41
|
+
SHOW_PAGES[show] ||= Nokogiri http.get(show_url).body
|
42
|
+
|
43
|
+
url = nil
|
44
|
+
SHOW_PAGES[show].css('div.left_articles table tr').find { |tr|
|
45
|
+
tr.children.find { |td| td.name == 'td' && td.text =~ /\A#{season}x0?#{episode}\z/ }
|
46
|
+
}.children.find { |td|
|
47
|
+
td.children.find { |a|
|
48
|
+
a.name == 'a' && a[:href].start_with?('episode') && url = a[:href]
|
49
|
+
}
|
50
|
+
}
|
51
|
+
raise "invalid episode url: #{episode_url}" unless url =~ /^episode-(\d+)\.html$/
|
52
|
+
"/episode-#{$1}-#{lang}.html"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def subtitles_url
|
57
|
+
@subtitles_url ||= begin
|
58
|
+
subtitles = Nokogiri http.get(episode_url).body
|
59
|
+
|
60
|
+
# TODO: choose 720p or most downloaded instead of first found
|
61
|
+
url = subtitles.css('div.left_articles a').find { |a| a.name == 'a' && a[:href].start_with?('/subtitle') }[:href]
|
62
|
+
raise 'invalid subtitle url' unless url =~ /^\/subtitle-(\d+)\.html/
|
63
|
+
url
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def download_url
|
68
|
+
@download_url ||= URI.escape '/' + http.get(subtitles_url.sub('subtitle', 'download'))['Location']
|
69
|
+
end
|
70
|
+
|
71
|
+
def download
|
72
|
+
puts "Searching subtitles for #{file}:"
|
73
|
+
puts "Show: #{show}, Season: #{season}, Episode: #{episode}"
|
74
|
+
|
75
|
+
puts "show url: #{show_url}"
|
76
|
+
puts "episode url: #{episode_url}"
|
77
|
+
puts "subtitle url: #{subtitles_url}"
|
78
|
+
puts "download url: #{download_url}"
|
79
|
+
|
80
|
+
# extract
|
81
|
+
zip = http.get(download_url).body
|
82
|
+
http.finish
|
83
|
+
open(TEMP_ARCHIVE_NAME, 'wb') { |f| f.write zip }
|
84
|
+
subs = Suby.extract_subs_from_archive(TEMP_ARCHIVE_NAME)
|
85
|
+
|
86
|
+
new_name = File.basename(file, File.extname(file))+File.extname(subs.first)
|
87
|
+
File.rename subs.first, new_name
|
88
|
+
puts "Renaming to #{new_name}"
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/suby.gemspec
ADDED
@@ -0,0 +1,14 @@
|
|
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
|
+
|
7
|
+
s.files = Dir['bin/*', 'lib/**/*.rb', '.gitignore', 'README.md', 'suby.gemspec']
|
8
|
+
s.executables = ['suby']
|
9
|
+
|
10
|
+
s.required_ruby_version = '>= 1.9.2'
|
11
|
+
s.add_dependency 'nokogiri'
|
12
|
+
|
13
|
+
s.version = '0.0.6'
|
14
|
+
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: suby
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.6
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,8 +9,19 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2011-
|
13
|
-
dependencies:
|
12
|
+
date: 2011-07-13 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: nokogiri
|
16
|
+
requirement: &2153880860 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *2153880860
|
14
25
|
description: Find and download subtitles
|
15
26
|
email:
|
16
27
|
executables:
|
@@ -19,7 +30,11 @@ extensions: []
|
|
19
30
|
extra_rdoc_files: []
|
20
31
|
files:
|
21
32
|
- bin/suby
|
33
|
+
- lib/suby/downloader.rb
|
22
34
|
- lib/suby.rb
|
35
|
+
- .gitignore
|
36
|
+
- README.md
|
37
|
+
- suby.gemspec
|
23
38
|
homepage:
|
24
39
|
licenses: []
|
25
40
|
post_install_message:
|
@@ -31,7 +46,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
31
46
|
requirements:
|
32
47
|
- - ! '>='
|
33
48
|
- !ruby/object:Gem::Version
|
34
|
-
version:
|
49
|
+
version: 1.9.2
|
35
50
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
36
51
|
none: false
|
37
52
|
requirements:
|
@@ -40,7 +55,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
40
55
|
version: '0'
|
41
56
|
requirements: []
|
42
57
|
rubyforge_project:
|
43
|
-
rubygems_version: 1.8.5
|
58
|
+
rubygems_version: 1.8.5.1
|
44
59
|
signing_key:
|
45
60
|
specification_version: 3
|
46
61
|
summary: Subtitles' downloader
|