subfinder 0.0.1

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.
@@ -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