subfinder 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,112 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://subscene.com/subtitles/english-text/MGNU9jG42CkOL8vcxVwLzwaKhIvuPAE0Bb7OqO62gY8-mFppv1EKkdjJVrTFiRBfXA2sxFE5E8je3FtYz11K6jGRckMYNf6S5LznnuUB5aV_PjsPghQ
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept:
11
+ - "*/*"
12
+ Accept-Encoding:
13
+ - gzip, deflate
14
+ User-Agent:
15
+ - rest-client/2.0.2 (darwin18 x86_64) ruby/2.3.7p456
16
+ Host:
17
+ - subscene.com
18
+ response:
19
+ status:
20
+ code: 200
21
+ message: OK
22
+ headers:
23
+ Date:
24
+ - Fri, 25 Jan 2019 13:12:49 GMT
25
+ Content-Type:
26
+ - text/html; charset=utf-8
27
+ Transfer-Encoding:
28
+ - chunked
29
+ Connection:
30
+ - keep-alive
31
+ Set-Cookie:
32
+ - __cfduid=d54bf78588c18d70a094eac3fc7e5ce8a1548421969; expires=Sat, 25-Jan-20
33
+ 13:12:49 GMT; path=/; domain=.subscene.com; HttpOnly; Secure
34
+ Cache-Control:
35
+ - private
36
+ Vary:
37
+ - Accept-Encoding
38
+ Expect-Ct:
39
+ - max-age=604800, report-uri="https://report-uri.cloudflare.com/cdn-cgi/beacon/expect-ct"
40
+ Server:
41
+ - cloudflare
42
+ Cf-Ray:
43
+ - 49eafe5f2f01730d-AMS
44
+ Content-Encoding:
45
+ - gzip
46
+ body:
47
+ encoding: ASCII-8BIT
48
+ string: !binary |-
49
+ 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
109
+ HAAA
110
+ http_version:
111
+ recorded_at: Fri, 25 Jan 2019 13:12:50 GMT
112
+ recorded_with: VCR 4.0.0
@@ -0,0 +1,19 @@
1
+ require 'json'
2
+ require 'colorize'
3
+ require 'logger'
4
+ require 'optparse'
5
+ require 'zip'
6
+
7
+ require_relative 'subfinder/version'
8
+ require_relative 'subfinder/subtitle'
9
+ require_relative 'subfinder/config'
10
+ require_relative 'subfinder/main'
11
+ require_relative 'subfinder/logger'
12
+
13
+ require_relative 'subfinder/parser/args'
14
+ require_relative 'subfinder/parser/files'
15
+ require_relative 'subfinder/parser/subscene'
16
+ require_relative 'subfinder/parser/download'
17
+
18
+ module Subfinder
19
+ end
@@ -0,0 +1,8 @@
1
+ module Subfinder
2
+ class Config
3
+ @video_formats = %w[.avi .mp4 .mkv]
4
+ class << self
5
+ attr_accessor :subtitles, :language, :url, :working_dir, :video_formats, :debug
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,22 @@
1
+ module Subfinder
2
+ class Logger < ::Logger
3
+ class << self
4
+ def setting
5
+ logger = Logger.new STDOUT
6
+ logger.level = Logger::INFO
7
+ logger.formatter = proc do |_severity, _datetime, _progname, msg|
8
+ "#{msg}\n"
9
+ end
10
+ @logger = logger
11
+ end
12
+
13
+ def info(str)
14
+ @logger.info(str)
15
+ end
16
+
17
+ def debug(str)
18
+ @logger.info(str) if Config.debug
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ module Subfinder
2
+ class Main
3
+ class << self
4
+ def run(args)
5
+ Subfinder::Logger.setting
6
+ Logger.info welcome_message
7
+ Subfinder::Parser::Args.new args
8
+ Subfinder::Subtitle.new.match
9
+ end
10
+
11
+ def welcome_message
12
+ '''
13
+
14
+ welcome to subfinder Gem!
15
+
16
+ '''.green
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,41 @@
1
+ module Subfinder
2
+ module Parser
3
+ class Args
4
+ def initialize(args)
5
+ @args = args
6
+ parse_args
7
+ validate_required_args
8
+ end
9
+
10
+ def parse_args
11
+ opts = OptionParser.new
12
+ opts.banner = usage_msg
13
+ opts.separator ''
14
+ opts.separator 'options:'
15
+ opts.on('-u', '--url url', 'Set subscene url page') { |url| Config.url = url }
16
+ opts.on('-l', '--language language', 'Set subtitle language') { |language| Config.language = language }
17
+ opts.on('-v', '--version', 'Show the Compare Crypto version') { puts("compare-crypto #{CompareCrypto::VERSION}"); exit }
18
+ opts.on('-h', '--help', 'Show this message') { puts(opts); exit }
19
+ opts.on('-d', '--debug', 'debug') { Config.debug = true }
20
+
21
+ opts.parse!(@args)
22
+ end
23
+
24
+ private
25
+
26
+ def validate_required_args
27
+ Config.language ||= 'en'
28
+ Config.working_dir = Dir.pwd
29
+ end
30
+
31
+ def usage_msg
32
+ usage = '
33
+ Usage:
34
+ subfinder [options]
35
+ See https://github.com/sizief/subfinder for more information. Emm, nothing there really, just source code, if you want to improve the code or report a bug visit the page.
36
+ '
37
+ usage
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,35 @@
1
+ require 'rest-client'
2
+ module Subfinder
3
+ module Parser
4
+ class Download
5
+ def initialize(url)
6
+ @url = url
7
+ end
8
+
9
+ def save
10
+ res = RestClient.get @url
11
+ return false unless response_is_healthy? res
12
+
13
+ file_name = res.headers[:content_disposition].split('=')[1]
14
+ File.write("#{Config.working_dir}/#{file_name}", res.body)
15
+ Logger.info "Downloaded to #{Config.working_dir}/#{file_name}"
16
+ true
17
+ rescue StandardError => e
18
+ Logger.info "Error when downloading '#{@url}'\n Error message: #{e}\n".red
19
+ false
20
+ end
21
+
22
+ def response_is_healthy?(res)
23
+ if res.code != 200
24
+ Logger.info "Error when downloading '#{@url}'\n Error message: resposnse code is #{res.code}\n".red
25
+ false
26
+ elsif res.body.include? 'An error occurred while processing your request.'
27
+ Logger.info "Error when downloading '#{@url}'\n Error message: 'An error occurred while processing your request.'\n".red
28
+ false
29
+ else
30
+ true
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,40 @@
1
+ module Subfinder
2
+ module Parser
3
+ class Files
4
+ class << self
5
+ def list
6
+ prepare_file_list
7
+ Dir["#{Config.working_dir}/*"]
8
+ end
9
+
10
+ def prepare_file_list
11
+ files = Dir["#{Config.working_dir}/*"]
12
+ compressed_files = files.select { |file| ['.zip', '.rar'].include? File.extname(file) }
13
+ extract_all compressed_files
14
+ move_compressed_files compressed_files
15
+ end
16
+
17
+ def extract_all(array_list)
18
+ array_list.each do |file|
19
+ Zip::File.open(file) do |zip_file|
20
+ zip_file.each do |entry|
21
+ file_path = Config.working_dir + '/' + entry.name
22
+ unless File.exist? file_path
23
+ Logger.info "Extracting #{entry.name}"
24
+ entry.extract(file_path)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
30
+
31
+ def move_compressed_files(compressed_files)
32
+ unless compressed_files.empty?
33
+ Dir.mkdir(Config.working_dir + '/compressed') unless File.exist?(Config.working_dir + '/compressed')
34
+ compressed_files.each { |f| File.rename(f, File.dirname(f) + '/compressed/' + File.basename(f)) }
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,93 @@
1
+ require 'nokogiri'
2
+ require 'open-uri'
3
+
4
+ module Subfinder
5
+ module Parser
6
+ class Subscene
7
+ attr_reader :link
8
+ DOMAIN = 'https://subscene.com'.freeze
9
+
10
+ def initialize(file_name)
11
+ @file_name = file_name
12
+ end
13
+
14
+ def get
15
+ target_uri = find_link(create_links_list(open_online_document(Config.url)))
16
+ Subfinder::Parser::Download.new(find_download_link(open_online_document(DOMAIN + target_uri))).save
17
+ end
18
+
19
+ def open_online_document(url)
20
+ open url
21
+ rescue StandardError => e
22
+ Logger.info "Error when connecting to '#{url}'\n Error message: #{e}\n".red
23
+ abort('Check your internet connection or VPN and try again')
24
+ end
25
+
26
+ def find_download_link(page)
27
+ doc = Nokogiri::HTML(page)
28
+ download_link = nil
29
+ doc.css('.download').each do |link|
30
+ download_link = link.xpath('./a/@href').to_s.strip # link
31
+ end
32
+ Logger.debug "download_link: #{download_link}"
33
+ DOMAIN + download_link
34
+ end
35
+
36
+ def find_link(links)
37
+ max_match_point = 0
38
+ winner = ''
39
+
40
+ links.each do |link|
41
+ next if link[0] != Config.language
42
+
43
+ match_point = match_point_for link[1]
44
+ if match_point > max_match_point
45
+ max_match_point = match_point
46
+ winner = link[2]
47
+ end
48
+ end
49
+ Logger.debug "winner is #{winner}"
50
+ winner
51
+ end
52
+
53
+ def match_point_for(target)
54
+ point = 0
55
+ file_name_array = @file_name.split('.')
56
+ # next we want to know if name of the sub is seprataed by . or space
57
+ target_array = target.split('.').size > target.split(' ').size ? target.split('.') : target.split(' ')
58
+ target_array = target_array.map(&:downcase)
59
+
60
+ file_name_array.each do |word|
61
+ point += 1 if target_array.include? word.downcase
62
+ end
63
+ (point * 100) / file_name_array.size
64
+ end
65
+
66
+ def create_links_list(page)
67
+ array = []
68
+ doc = Nokogiri::HTML(page)
69
+ doc.xpath('//table//tr').each do |link|
70
+ next if link.xpath('./td/a/span/text()')[0].nil? # language
71
+
72
+ array << [convert_to_code(link.xpath('./td/a/span/text()')[0].to_s.strip), # language
73
+ link.xpath('./td/a/span/text()')[1].to_s.strip, # name
74
+ link.xpath('./td/a/@href')[0].to_s.strip] # link
75
+ end
76
+ Logger.debug "array_list: #{array}"
77
+ array
78
+ end
79
+
80
+ def convert_to_code(language)
81
+ case language
82
+ when 'Farsi/Persian'
83
+ 'fa'
84
+ when 'English'
85
+ 'en'
86
+ else
87
+ language
88
+ end
89
+ # TODO: add other languages codes
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,59 @@
1
+ module Subfinder
2
+ class Subtitle
3
+ def initialize
4
+ @success = 0
5
+ @failure = 0
6
+ end
7
+
8
+ def match
9
+ Subfinder::Parser::Files.list.each do |video_file|
10
+ next unless Subfinder::Config.video_formats.include? File.extname(video_file)
11
+ next if subtitle_exists? video_file
12
+
13
+ if episode_number(video_file).nil?
14
+ Logger.info "Can not find the episode and season number for: #{File.basename(video_file)}".red
15
+ @failure += 1
16
+ next
17
+ else
18
+ find_subtitle_for video_file
19
+ end
20
+ end
21
+ Logger.info "Sub added: #{@success}, Sub not found: #{@failure}, Total file proccessed: #{@failure + @success}".green
22
+ end
23
+
24
+ private
25
+
26
+ def episode_number(video_file)
27
+ video_file.scan(/[a-zA-Z]+\d+\d+[a-zA-Z]+\d+\d/).first.downcase
28
+ end
29
+
30
+ def subtitle_exists?(video_file)
31
+ return true if Subfinder::Parser::Files.list.include? video_file.split('.')[0..-2].join('.') + '.srt'
32
+ end
33
+
34
+ def find_subtitle_for(video_file)
35
+ subtitles = Subfinder::Parser::Files.list.select { |item| item[/#{episode_number(video_file)}/i] && item[/.srt/] }
36
+ if subtitles.empty?
37
+ Logger.info "Subtitle for #{File.basename(video_file)} is not exists on disk "
38
+ subscene = Subfinder::Parser::Subscene.new(video_file)
39
+ if !Config.url.nil? && subscene.get
40
+ Subfinder::Parser::Files.prepare_file_list
41
+ Logger.info "Subtitle downloaded for #{File.basename(video_file)}"
42
+ else
43
+ Logger.info "Subtitle can not found on Subscene for #{File.basename(video_file)}".red
44
+ Logger.info 'Please provide a url with "-u" siwtch'.red if Config.url.nil?
45
+ @failure += 1
46
+ return
47
+ end
48
+ end
49
+ rename_subtitle video_file
50
+ end
51
+
52
+ def rename_subtitle(video_file)
53
+ subtitles = Subfinder::Parser::Files.list.select { |item| item[/#{episode_number(video_file)}/i] && item[/.srt/] }
54
+ File.rename(subtitles.first, File.dirname(subtitles.first) + '/' + File.basename(video_file).split('.')[0..-2].join('.') + '.srt')
55
+ Logger.info "Subtitle renamed for: #{File.basename(video_file)}"
56
+ @success += 1
57
+ end
58
+ end
59
+ end