suby 0.0.4 → 0.0.5
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/bin/suby +5 -99
- data/lib/suby.rb +100 -0
- metadata +2 -1
data/bin/suby
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
|
3
|
+
require 'suby'
|
3
4
|
require 'optparse'
|
4
5
|
|
5
|
-
options = {
|
6
|
-
lang: 'en'
|
7
|
-
}
|
8
|
-
|
6
|
+
options = {}
|
9
7
|
option_parser = OptionParser.new do |opts|
|
10
8
|
opts.banner = "#{File.basename $0} [options] video"
|
11
9
|
opts.separator ' Find and download subtitles for the given video file'
|
@@ -23,100 +21,8 @@ end
|
|
23
21
|
|
24
22
|
option_parser.parse!
|
25
23
|
|
26
|
-
if ARGV.empty?
|
27
|
-
puts option_parser
|
28
|
-
exit
|
29
|
-
end
|
30
|
-
|
31
|
-
require 'net/http'
|
32
|
-
require 'nokogiri'
|
33
|
-
|
34
|
-
SUB_EXTENSIONS = %w[srt sub]
|
35
|
-
archive = '__archive__'
|
36
|
-
site = 'www.tvsubtitles.net'
|
37
|
-
search_url = '/search.php'
|
24
|
+
puts option_parser if ARGV.empty?
|
38
25
|
|
39
|
-
|
40
|
-
|
41
|
-
when /Zip archive data/
|
42
|
-
subs = `unzip -qql #{archive}`.scan(/\d{2}:\d{2} (.+?(?:#{SUB_EXTENSIONS.join '|'}))$/).map(&:first)
|
43
|
-
raise "no subtitles in #{archive}" if subs.empty?
|
44
|
-
subs_for_unzip = subs.map { |sub| sub.gsub(/(\[|\])/) { "\\#{$1}" } }
|
45
|
-
system 'unzip', archive, *subs_for_unzip, 1 => :close
|
46
|
-
puts "found subtitles: #{subs.join(', ')}"
|
47
|
-
else
|
48
|
-
raise "unknown archive type (#{archive})"
|
49
|
-
end
|
50
|
-
|
51
|
-
# Cleaning
|
52
|
-
File.unlink archive
|
53
|
-
subs
|
26
|
+
ARGV.each do |file|
|
27
|
+
Suby.download_subtitles file, options
|
54
28
|
end
|
55
|
-
|
56
|
-
# cache
|
57
|
-
show_urls = {}
|
58
|
-
show_pages = {}
|
59
|
-
|
60
|
-
ARGV.each { |file|
|
61
|
-
next if SUB_EXTENSIONS.include? File.extname(file)
|
62
|
-
next(puts "Skipping: #{file}") if SUB_EXTENSIONS.any? { |ext|
|
63
|
-
File.exist? File.basename(file, File.extname(file)) + ".#{ext}" }
|
64
|
-
|
65
|
-
unless /^(?<show>.+) (?<season>\d{1,2})x(?<episode>\d{1,2}) - .+\.[a-z]+?$/ =~ file
|
66
|
-
next puts "wrong file format (#{file}). Must be:\n<show> <season>x<episode> - <title>.<ext>"
|
67
|
-
end
|
68
|
-
season, episode = [season, episode].map(&:to_i)
|
69
|
-
puts "Searching subtitles for #{file}:"
|
70
|
-
puts "Show: #{show}, Season: #{season}, Episode: #{episode}"
|
71
|
-
|
72
|
-
Net::HTTP.start(site) { |tvsubtitles|
|
73
|
-
# search show
|
74
|
-
show_url = (show_urls[show] ||= begin
|
75
|
-
post = Net::HTTP::Post.new(search_url)
|
76
|
-
post.form_data = { 'q' => show }
|
77
|
-
results = Nokogiri tvsubtitles.request(post).body
|
78
|
-
url = results.css('ul li div a').first[:href]
|
79
|
-
|
80
|
-
raise 'could not find the show' unless /^\/tvshow-(\d+)\.html$/ =~ url
|
81
|
-
"/tvshow-#{$1}-#{season}.html"
|
82
|
-
end)
|
83
|
-
puts "show url: #{show_url}"
|
84
|
-
|
85
|
-
# search episode
|
86
|
-
show = (show_pages[show] ||= Nokogiri tvsubtitles.get(show_url).body)
|
87
|
-
tr = show.css('div.left_articles table tr').find { |tr|
|
88
|
-
tr.children.find { |td| td.name == 'td' && td.text =~ /\A#{season}x0?#{episode}\z/ }
|
89
|
-
}
|
90
|
-
episode_url = nil
|
91
|
-
tr.children.find { |td|
|
92
|
-
td.children.find { |a|
|
93
|
-
a.name == 'a' && a[:href].start_with?('episode') && episode_url = a[:href]
|
94
|
-
}
|
95
|
-
}
|
96
|
-
|
97
|
-
raise "invalid episode url: #{episode_url}" unless episode_url =~ /^episode-(\d+)\.html$/
|
98
|
-
episode_url = "/episode-#{$1}-#{options[:lang]}.html"
|
99
|
-
puts "episode url: #{episode_url}"
|
100
|
-
|
101
|
-
# subtitles
|
102
|
-
subtitles = Nokogiri tvsubtitles.get(episode_url).body
|
103
|
-
|
104
|
-
# TODO: choose 720p or most downloaded instead of first found
|
105
|
-
subtitle_url = subtitles.css('div.left_articles a').find { |a| a.name == 'a' && a[:href].start_with?('/subtitle') }[:href]
|
106
|
-
raise 'invalid subtitle url' unless subtitle_url =~ /^\/subtitle-(\d+)\.html/
|
107
|
-
puts "subtitle url: #{subtitle_url}"
|
108
|
-
|
109
|
-
# download
|
110
|
-
download_url = tvsubtitles.get("/download-#{$1}.html")['Location']
|
111
|
-
download_url = URI.escape('/'+download_url)
|
112
|
-
puts "download url: #{download_url}"
|
113
|
-
|
114
|
-
# extract
|
115
|
-
zip = tvsubtitles.get(download_url).body
|
116
|
-
open(archive, 'wb') { |f| f.write zip }
|
117
|
-
subs = extract_subs_from_archive(archive)
|
118
|
-
new_name = File.basename(file, File.extname(file))+File.extname(subs.first)
|
119
|
-
File.rename(subs.first, new_name)
|
120
|
-
puts "Renaming to #{new_name}"
|
121
|
-
}
|
122
|
-
}
|
data/lib/suby.rb
ADDED
@@ -0,0 +1,100 @@
|
|
1
|
+
require 'net/http'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Suby
|
5
|
+
extend self
|
6
|
+
|
7
|
+
DEFAULT_OPTIONS = {
|
8
|
+
lang: 'en'
|
9
|
+
}
|
10
|
+
|
11
|
+
SUB_EXTENSIONS = %w[srt sub]
|
12
|
+
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
|
+
|
74
|
+
# extract
|
75
|
+
zip = tvsubtitles.get(download_url).body
|
76
|
+
open(TEMP_ARCHIVE_NAME, 'wb') { |f| f.write zip }
|
77
|
+
subs = extract_subs_from_archive(TEMP_ARCHIVE_NAME)
|
78
|
+
new_name = File.basename(file, File.extname(file))+File.extname(subs.first)
|
79
|
+
File.rename(subs.first, new_name)
|
80
|
+
puts "Renaming to #{new_name}"
|
81
|
+
}
|
82
|
+
end
|
83
|
+
|
84
|
+
def extract_subs_from_archive(archive)
|
85
|
+
case `file #{archive}`
|
86
|
+
when /Zip archive data/
|
87
|
+
subs = `unzip -qql #{archive}`.scan(/\d{2}:\d{2} (.+?(?:#{SUB_EXTENSIONS.join '|'}))$/).map(&:first)
|
88
|
+
raise "no subtitles in #{archive}" if subs.empty?
|
89
|
+
subs_for_unzip = subs.map { |sub| sub.gsub(/(\[|\])/) { "\\#{$1}" } }
|
90
|
+
system 'unzip', archive, *subs_for_unzip, 1 => :close
|
91
|
+
puts "found subtitles: #{subs.join(', ')}"
|
92
|
+
else
|
93
|
+
raise "unknown archive type (#{archive})"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Cleaning
|
97
|
+
File.unlink archive
|
98
|
+
subs
|
99
|
+
end
|
100
|
+
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.5
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -19,6 +19,7 @@ extensions: []
|
|
19
19
|
extra_rdoc_files: []
|
20
20
|
files:
|
21
21
|
- bin/suby
|
22
|
+
- lib/suby.rb
|
22
23
|
homepage:
|
23
24
|
licenses: []
|
24
25
|
post_install_message:
|