etvnet-seek 0.7.2 → 0.8.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. data/Gemfile +18 -8
  2. data/README +1 -1
  3. data/Rakefile +27 -5
  4. data/VERSION +1 -1
  5. data/bin/etvnet-seek +8 -3
  6. data/etvnet-seek.gemspec +79 -42
  7. data/lib/etvnet_seek/accessor.rb +64 -0
  8. data/lib/etvnet_seek/commander.rb +37 -21
  9. data/lib/etvnet_seek/core/access_page.rb +23 -6
  10. data/lib/etvnet_seek/core/audio_page.rb +26 -0
  11. data/lib/etvnet_seek/core/best_hundred_page.rb +20 -0
  12. data/lib/etvnet_seek/core/browse_media_item.rb +1 -0
  13. data/lib/etvnet_seek/core/catalog_page.rb +26 -14
  14. data/lib/etvnet_seek/core/channels_page.rb +6 -2
  15. data/lib/etvnet_seek/core/group_media_item.rb +2 -0
  16. data/lib/etvnet_seek/core/group_page.rb +9 -23
  17. data/lib/etvnet_seek/core/home_page.rb +5 -2
  18. data/lib/etvnet_seek/core/items_page.rb +140 -0
  19. data/lib/etvnet_seek/core/items_page_factory.rb +47 -0
  20. data/lib/etvnet_seek/core/login_page.rb +3 -1
  21. data/lib/etvnet_seek/core/media_info.rb +3 -18
  22. data/lib/etvnet_seek/core/media_item.rb +10 -2
  23. data/lib/etvnet_seek/core/media_page.rb +54 -27
  24. data/lib/etvnet_seek/core/new_item.rb +2 -0
  25. data/lib/etvnet_seek/core/new_items_page.rb +4 -1
  26. data/lib/etvnet_seek/core/page.rb +2 -0
  27. data/lib/etvnet_seek/core/premiere_page.rb +7 -0
  28. data/lib/etvnet_seek/core/radio_page.rb +23 -0
  29. data/lib/etvnet_seek/core/search_page.rb +3 -33
  30. data/lib/etvnet_seek/core/service_call.rb +34 -6
  31. data/lib/etvnet_seek/core/top_this_week_page.rb +20 -0
  32. data/lib/etvnet_seek/etvnet_seek.rb +1 -4
  33. data/lib/etvnet_seek/main.rb +113 -84
  34. data/spec/etvnet_seek_spec.rb +10 -8
  35. data/spec/spec_helper.rb +3 -0
  36. data/spec/unit/pages_spec.rb +77 -0
  37. metadata +182 -32
  38. data/lib/etvnet_seek/core/base_page.rb +0 -43
  39. data/lib/etvnet_seek/core/page_factory.rb +0 -51
@@ -18,8 +18,16 @@ class MediaItem
18
18
  false
19
19
  end
20
20
 
21
- def has_media_links?
22
- Nokogiri::HTML(open(Page::BASE_URL + link)).css("b a.media_file").size > 0
21
+ # def has_media_links?
22
+ # page = Page.new link
23
+ #
24
+ # page.document.css("b a.media_file").size > 0
25
+ # end
26
+
27
+ def access_page?
28
+ page = Page.new link
29
+
30
+ page.document.css(".start-video start-movie").size > 0
23
31
  end
24
32
 
25
33
  def ==(object)
@@ -1,41 +1,68 @@
1
- class MediaPage < BasePage
1
+ require 'etvnet_seek/core/items_page'
2
+ require 'etvnet_seek/core/browse_media_item'
3
+
4
+ class MediaPage < ItemsPage
5
+
2
6
  def items
3
7
  list = []
4
8
 
5
9
  document.css(".conteiner table#table-onecolumn tr").each do |item|
6
- showtime = item.css("td[1]").text.strip
7
- name = item.css("td[2]").text.strip
8
- rating_image = item.css("td[3] img").at(0) ? item.css("td[3] img").at(0).attributes['src'].value.strip : ""
9
- rating = item.css("td[4]") ? item.css("td[4]").text.strip : ""
10
- duration = item.css("td[5]") ? item.css("td[5]").text.strip : ""
11
- year = item.css("td[6]") ? item.css("td[6]").text.strip : ""
12
- channel = item.css("td[7]") ? item.css("td[7]").text.strip : ""
13
-
14
- link = item.css("td[2] a").at(0).attributes['href'].value
15
-
16
- additional_info = additional_info(item.css("td[2] a").at(0), 2)
17
-
18
- if additional_info.strip.size > 0
19
- folder = true
20
- else
10
+ unless item.css("td[1]").text.empty?
11
+ showtime = item.css("td[1]").text.strip
12
+ name = item.css("td[2]").text.strip
13
+ rating_image = item.css("td[3] img").at(0) ? item.css("td[3] img").at(0).attributes['src'].value.strip : ""
14
+ rating = item.css("td[4]") ? item.css("td[4]").text.strip : ""
15
+ duration = item.css("td[5]") ? item.css("td[5]").text.strip : ""
16
+ year = item.css("td[6]") ? item.css("td[6]").text.strip : ""
17
+ channel = item.css("td[7]") ? item.css("td[7]").text.strip : ""
18
+
19
+ link = item.css("td[2] a").at(0).attributes['href'].value
20
+
21
+ additional_info = additional_info(item.css("td[2] a").at(0), 2)
22
+
21
23
  folder = false
22
- end
23
24
 
24
- record = BrowseMediaItem.new(name, link)
25
- record.folder = folder
26
- record.showtime = showtime
27
- record.year = year
28
- record.duration = duration
29
- record.channel = channel
30
- record.rating_image = rating_image
31
- record.rating = rating
25
+ if not additional_info.nil? and additional_info.strip.size > 0
26
+ if(additional_info.strip.scan(/\((\d*)(.*)\)/)).size > 0
27
+ folder = true
28
+ end
29
+ end
32
30
 
33
- list << record
31
+ record = BrowseMediaItem.new(name, link)
32
+ record.folder = folder
33
+ record.showtime = showtime
34
+ record.year = year
35
+ record.duration = duration
36
+ record.channel = channel
37
+ record.rating_image = rating_image
38
+ record.rating = rating
39
+
40
+ list << record
41
+ end
34
42
  end
35
43
 
36
44
  list
37
- end
45
+ end
46
+
47
+ def info_movie_breadcrumbs
48
+ document.css(".path_movie_info_menu").inner_html
49
+ end
50
+
51
+ def info_movie_screenshots
52
+ collect_images ".all-inner-pages .screenshots"
53
+ end
54
+
55
+ def info_movie_descripton
56
+ document.css(".all-inner-pages .description")[0].children.at(0).text
57
+ end
58
+
59
+ def info_movie_additional_info
60
+ document.css(".all-inner-pages .description")[1].inner_html
61
+ end
38
62
 
63
+ def info_movie_additional_info2
64
+ document.css(".all-inner-pages .acc_container").inner_html
65
+ end
39
66
  end
40
67
 
41
68
 
@@ -1,3 +1,5 @@
1
+ require 'etvnet_seek/core/media_item'
2
+
1
3
  class NewItem < MediaItem
2
4
 
3
5
  attr_accessor :image
@@ -1,4 +1,7 @@
1
- class NewItemsPage < BasePage
1
+ require 'etvnet_seek/core/items_page'
2
+ require 'etvnet_seek/core/browse_media_item'
3
+
4
+ class NewItemsPage < ItemsPage
2
5
 
3
6
  def items
4
7
  list = []
@@ -1,5 +1,7 @@
1
1
  require 'nokogiri'
2
2
 
3
+ require 'etvnet_seek/core/service_call'
4
+
3
5
  class Page < ServiceCall
4
6
  BASE_URL = "http://www.etvnet.com"
5
7
 
@@ -0,0 +1,7 @@
1
+ require 'etvnet_seek/core/group_page'
2
+
3
+ class PremierePage < GroupPage
4
+ def items
5
+ get_typical_items("ul.recomendation-list li")
6
+ end
7
+ end
@@ -0,0 +1,23 @@
1
+ require 'etvnet_seek/core/items_page'
2
+ require 'etvnet_seek/core/media_item'
3
+
4
+ class RadioPage < ItemsPage
5
+ AUDIO_URL = BASE_URL + "/audio/"
6
+
7
+ def initialize url = AUDIO_URL
8
+ super(url)
9
+ end
10
+
11
+ def items
12
+ list = []
13
+
14
+ document.css("#sidebar #slideshow1 .active a").each do |item|
15
+ text = item.css("img").at(0).attributes["title"].value.strip
16
+ href = item['href']
17
+
18
+ list << MediaItem.new(text, href)
19
+ end
20
+
21
+ list
22
+ end
23
+ end
@@ -1,42 +1,12 @@
1
1
  require 'cgi'
2
2
 
3
- class SearchPage < Page
3
+ require 'etvnet_seek/core/catalog_page'
4
+
5
+ class SearchPage < CatalogPage
4
6
  SEARCH_URL = BASE_URL + "/search/"
5
7
 
6
8
  def initialize(params)
7
9
  super("#{SEARCH_URL}?q=#{CGI.escape(*params)}")
8
10
  end
9
11
 
10
- def items
11
- list = []
12
-
13
- document.css(".conteiner #results #table-onecolumn tr").each_with_index do |item, index|
14
- next if index == 0
15
-
16
- showtime = item.css("td[1]").text.strip
17
- rating_image = item.css("td[2] img").at(0) ? item.css("td[2] img").at(0).attributes['src'].value.strip : ""
18
- rating = item.css("td[3]") ? item.css("td[3]").text.strip : ""
19
- name = item.css("td[4]").text.strip
20
- duration = item.css("td[5]") ? item.css("td[5]").text.strip : ""
21
- link = item.css("td[4] a").at(0)
22
-
23
- href = link.attributes['href'].value
24
-
25
- amount_expr = name.scan(%r{(.*)+\((\d*)+ (.*)+\)})[0]
26
-
27
- folder = (not amount_expr.nil? and amount_expr.size > 1) ? true : false
28
-
29
- record = BrowseMediaItem.new(name, href)
30
- record.folder = folder
31
- record.showtime = showtime
32
- record.duration = duration
33
- #record.channel = channel
34
- record.rating_image = rating_image
35
- record.rating = rating
36
-
37
- list << record
38
- end
39
-
40
- list
41
- end
42
12
  end
@@ -1,5 +1,19 @@
1
1
  require 'open-uri'
2
2
 
3
+ require 'net/http'
4
+
5
+ # Lengthen timeout in Net::HTTP
6
+ module Net
7
+ class HTTP
8
+ alias old_initialize initialize
9
+
10
+ def initialize(*args)
11
+ old_initialize(*args)
12
+ @read_timeout = 3*60 # 3 minutes
13
+ end
14
+ end
15
+ end
16
+
3
17
  class ServiceCall
4
18
  attr_reader :url
5
19
 
@@ -8,20 +22,34 @@ class ServiceCall
8
22
  end
9
23
 
10
24
  def get
11
- open(url)
25
+ open(url, "User-Agent" => "Ruby/#{RUBY_VERSION}")
12
26
  end
13
27
 
14
- def post params, headers
15
- request = Net::HTTP::Post.new(url, headers)
16
-
28
+ def post params, headers, url = @url
29
+ request = Net::HTTP::Post.new(url, headers.merge({"User-Agent" => "Ruby/#{RUBY_VERSION}"}))
17
30
  request.set_form_data(params)
31
+ #request.basic_auth(un, pw)
32
+ #"Content-Type"=>"application/x-www-form-urlencoded",
33
+ #"Authorization" => "Basic " + Base64::encode64("account:password")
34
+ response = request(request, url)
18
35
 
19
- request(request)
36
+ if response.class == Net::HTTPMovedPermanently
37
+ response = handle_redirect response['location'], params, headers
38
+ end
39
+
40
+ response
20
41
  end
21
42
 
22
43
  protected
23
44
 
24
- def request request
45
+ def handle_redirect url, params, headers
46
+ request = Net::HTTP::Post.new(url, headers.merge({"User-Agent" => "Ruby/#{RUBY_VERSION}"}))
47
+ request.set_form_data(params)
48
+
49
+ request(request, url)
50
+ end
51
+
52
+ def request request, url
25
53
  uri = URI.parse(url)
26
54
 
27
55
  connection = Net::HTTP.new(uri.host, uri.port)
@@ -0,0 +1,20 @@
1
+ require 'etvnet_seek/core/group_page'
2
+ require 'etvnet_seek/core/group_media_item'
3
+
4
+ class TopThisWeekPage < GroupPage
5
+ def items
6
+ parent = document.css(".conteiner .coming-soon").at(0).parent
7
+ parent.css("ul.best-list li")
8
+
9
+ list = get_typical_items("ul.best-list li", parent)
10
+
11
+ node = parent.css("ul.best-list").at(0).next
12
+
13
+ unless node.nil?
14
+ link = node.attributes['href'].value
15
+ text = node.children.at(0).content
16
+
17
+ list << GroupMediaItem.new(text, link)
18
+ end
19
+ end
20
+ end
@@ -3,8 +3,5 @@ require 'rubygems' unless RUBY_VERSION =~ /1.9.*/
3
3
  $:.unshift(File.dirname(__FILE__)) unless
4
4
  $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
5
5
 
6
- require 'etvnet_seek/core/page_factory'
7
-
8
- require 'etvnet_seek/cookie_helper'
9
- require 'etvnet_seek/link_info'
6
+ require 'etvnet_seek/main'
10
7
 
@@ -4,120 +4,149 @@ require 'date'
4
4
 
5
5
  require 'etvnet_seek/commander'
6
6
  require 'etvnet_seek/user_selection'
7
+ require 'etvnet_seek/accessor'
7
8
  require 'runglish'
8
9
 
10
+ require 'etvnet_seek/link_info'
11
+ require 'etvnet_seek/core/items_page_factory'
12
+ require 'etvnet_seek/core/items_page'
13
+
9
14
  class Main
10
15
  COOKIE_FILE_NAME = ENV['HOME'] + "/.etvnet-seek"
11
16
 
12
17
  def initialize
13
- @cookie_helper = CookieHelper.new COOKIE_FILE_NAME
14
18
  @commander = Commander.new
19
+ @accessor = Accessor.new COOKIE_FILE_NAME, lambda { get_credentials }
15
20
  end
16
21
 
17
- def seek *params
18
- if @commander.search_mode?
19
- params = read_keywords(*params)
20
-
21
- puts "Keywords: #{params}" if @commander.runglish_mode?
22
+ def process *params
23
+ mode = @commander.get_initial_mode
24
+
25
+ case mode
26
+ when /(search|translit)/ then
27
+ keywords = read_keywords(*params)
28
+ puts "Keywords: #{keywords}" if @commander.translit_mode?
29
+
30
+ process_folder "search", keywords
31
+ when 'main' then
32
+ main
33
+ when 'channels' then
34
+ channels
35
+ when 'catalog' then
36
+ process_folder "catalog"
37
+ when 'best_hundred' then
38
+ best_hundred
39
+ when 'top_this_week' then
40
+ top_this_week
41
+ when 'premiere' then
42
+ process_folder "premiere"
43
+ when 'new_items' then
44
+ process_folder "new_items"
22
45
  end
23
-
24
- process @commander.get_initial_mode, params
25
46
  end
26
47
 
27
- def process mode, *params
28
- page = PageFactory.create(mode, params)
48
+ def main
49
+ process_items "main" do |item, _|
50
+ case item.link
51
+ when /tv_channels/ then
52
+ channels
53
+ when /(aired_today|catalog)/ then
54
+ process_folder "media", item.link
55
+ when /audio/ then
56
+ audio item.link
57
+ end
58
+ end
59
+ end
29
60
 
30
- if mode == 'access'
31
- process_access page, params[0]
32
- elsif mode == 'login'
33
- item = params[0]
34
- cookie = page.login(*get_credentials)
61
+ def channels
62
+ process_items "channels" do |item, user_selection|
63
+ link = user_selection.catalog? ? item.catalog_link : item.link
35
64
 
36
- @cookie_helper.save_cookie cookie
65
+ process_folder "media", link
66
+ end
67
+ end
37
68
 
38
- process("access", item)
39
- else
40
- items = page.items
41
-
42
- if items.size > 0
43
- display_items items
44
- display_bottom_menu_part(mode)
45
-
46
- user_selection = read_user_selection items
47
-
48
- if not user_selection.quit?
49
- current_item = items[user_selection.index]
50
- if mode == 'main'
51
- case current_item.link
52
- when '/' then
53
- process("main")
54
- when /aired_today/ then
55
- process('media', current_item.link)
56
- when /catalog/ then
57
- process('media', current_item.link)
58
- when /tv_channels/ then
59
- process('channels', current_item.link)
60
- end
61
- elsif mode == 'channels'
62
- p user_selection.catalog?
63
- if user_selection.catalog?
64
- process('media', current_item.catalog_link)
65
- else
66
- process('media', current_item.link)
67
- end
68
- elsif mode == 'new_items'
69
- process('access', current_item)
70
- elsif mode == 'premiere'
71
- process('access', current_item)
72
- elsif mode == 'catalog'
73
- p 'catalog'
74
- process('media', current_item.link)
75
- else # media
76
- if current_item.folder? or current_item.link =~ /(catalog|tv_channel)/
77
- process('media', current_item.link)
78
- else
79
- process("access", current_item)
80
- end
81
- end
82
- end
83
- end
69
+ def best_hundred
70
+ process_items "best_hundred" do |item, _|
71
+ process_group item, item.link =~ /best100/
84
72
  end
85
73
  end
86
74
 
87
- def process_access page, item
88
- cookie = @cookie_helper.load_cookie
75
+ def top_this_week
76
+ process_items "top_this_week" do |item, _|
77
+ process_group item, item.link =~ /top_this_week/
78
+ end
79
+ end
89
80
 
90
- if cookie.nil?
91
- process("login", item)
81
+ def process_group item, next_group
82
+ if next_group
83
+ process_items "media", item.link do |media_item, _|
84
+ access_or_media item, (folder?(media_item) or not media_item.access_page?)
85
+ end
92
86
  else
93
- expires = CookieHelper.get_expires(cookie)
94
- cookie_expire_date = DateTime.strptime(expires, "%A, %d-%b-%Y %H:%M:%S %Z")
87
+ access_or_media item, (folder?(item) or not item.access_page?)
88
+ end
89
+ end
95
90
 
96
- if cookie_expire_date < DateTime.now # cookie expired?
97
- @cookie_helper.delete_cookie
91
+ def folder? item
92
+ item.link =~ /(catalog|tv_channel)/ || item.folder?
93
+ end
98
94
 
99
- process("login", item)
100
- else
101
- media_info = page.request_media_info(item.media_file, cookie)
95
+ def process_folder name, *params
96
+ process_items name, *params do |item, _|
97
+ access_or_media item, folder?(item)
98
+ end
99
+ end
102
100
 
103
- if media_info.session_expired?
104
- @cookie_helper.delete_cookie
101
+ def audio root
102
+ process_items "audio", root do |item1, _|
103
+ process_items "radio", item1.link do |item2, _|
104
+ media_info = MediaInfo.new item2.link
105
+ LinkInfo.new(item2, media_info)
106
+ end
107
+ end
108
+ end
105
109
 
106
- process("login", item)
107
- else
108
- LinkInfo.new(item, media_info)
109
- end
110
+ def access_or_media item, folder
111
+ if folder
112
+ process_folder "media", item.link
113
+ else
114
+ result = @accessor.access item
115
+
116
+ while(@accessor.try_again? or result.nil?) do
117
+ result = @accessor.access item
110
118
  end
119
+
120
+ result
111
121
  end
112
122
  end
113
123
 
114
124
  def get_credentials
115
- username = ask("Enter username : " )
116
- password = ask("Enter password : " ) { |q| q.echo = '*' }
125
+ username = ask("Enter username : ")
126
+ password = ask("Enter password : ") { |q| q.echo = '*' }
117
127
 
118
128
  [username, password]
119
129
  end
120
130
 
131
+ def process_items mode, *params
132
+ page = ItemsPageFactory.create mode, *params
133
+
134
+ items = page.items
135
+
136
+ if items.size > 0
137
+ display_items items
138
+ display_bottom_menu_part(mode)
139
+
140
+ user_selection = read_user_selection items
141
+
142
+ if not user_selection.quit?
143
+ current_item = items[user_selection.index]
144
+
145
+ yield(current_item, user_selection) if block_given?
146
+ end
147
+ end
148
+ end
149
+
121
150
  def display_items items
122
151
  if items.size == 0
123
152
  puts "Empty search result."
@@ -146,13 +175,13 @@ class Main
146
175
  def read_keywords input
147
176
  keywords = input
148
177
 
149
- if(keywords.strip.size == 0)
178
+ if (keywords.strip.size == 0)
150
179
  while keywords.strip.size == 0
151
180
  keywords = ask("Keywords: ")
152
181
  end
153
182
  end
154
183
 
155
- if RUBY_PLATFORM =~ /mswin32/ or @commander.runglish_mode?
184
+ if RUBY_PLATFORM =~ /mswin32/ or @commander.translit_mode?
156
185
  keywords = Runglish::LatToRusConverter.new.transliterate(keywords)
157
186
  end
158
187
 
@@ -161,7 +190,7 @@ class Main
161
190
 
162
191
  def read_user_selection items
163
192
  user_selection = UserSelection.new
164
-
193
+
165
194
  while true
166
195
  user_selection.parse(ask("Select the number: "))
167
196