junkie 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/junkie.gemspec CHANGED
@@ -25,4 +25,5 @@ Gem::Specification.new do |gem|
25
25
  gem.add_runtime_dependency(%q<sjunkieex>, [">= 0.0.1"])
26
26
  gem.add_runtime_dependency(%q<sindex>, [">= 0.0.1"])
27
27
  gem.add_runtime_dependency(%q<logging>, ["~> 1.8.0"])
28
+ gem.add_runtime_dependency(%q<twitter>, ["~> 4.4.0"])
28
29
  end
data/lib/junkie.rb CHANGED
@@ -8,10 +8,15 @@ require 'junkie/helper'
8
8
  require 'junkie/patched/sjunkieex'
9
9
  require 'junkie/pyload/api'
10
10
  require 'junkie/pyload/observer'
11
+ require 'junkie/notification/twitter'
11
12
  require 'logging'
12
13
 
14
+ Logging.appenders.stdout(
15
+ 'stdout',
16
+ :level => :debug,
17
+ :layout => Logging.layouts.pattern(:pattern => '[%d] %-5l: %m\n')
18
+ )
13
19
  Logging.logger.root.appenders = Logging.appenders.stdout
14
- Logging.logger.root.level = :debug
15
20
 
16
21
  module Junkie
17
22
 
@@ -5,7 +5,7 @@ module Junkie
5
5
 
6
6
  class Episode
7
7
  attr_reader :id, :series, :found_at, :link
8
- attr_accessor :description, :status
8
+ attr_accessor :description, :status, :pid
9
9
 
10
10
  def initialize(series, link, description=nil)
11
11
  @series = series
@@ -0,0 +1,68 @@
1
+ require 'twitter'
2
+
3
+ module Junkie
4
+ module Notification
5
+
6
+ class Twitter
7
+ include Log, Config
8
+
9
+ DEFAULT_CONFIG = {
10
+ twitter_enabled: false,
11
+ consumer_key: "",
12
+ consumer_secret: "",
13
+ oauth_token: "",
14
+ oauth_token_secret: "",
15
+ }
16
+
17
+ def initialize(channels)
18
+ @channels = channels
19
+ @config = Config.get_config(self)
20
+
21
+ if @config[:twitter_enabled]
22
+
23
+ # check the configuration
24
+ [:consumer_key, :consumer_secret, :oauth_token,
25
+ :oauth_token_secret].each do |key|
26
+
27
+ unless @config[key] && @config[key].match(/\w+/)
28
+ raise InvalidConfigError, "#{key} option is missing"
29
+ end
30
+ end
31
+
32
+ # configure
33
+ ::Twitter.configure do |config|
34
+ config.consumer_key = @config[:consumer_key]
35
+ config.consumer_secret = @config[:consumer_secret]
36
+ config.oauth_token = @config[:oauth_token]
37
+ config.oauth_token_secret = @config[:oauth_token_secret]
38
+ end
39
+
40
+ # bind to channel
41
+ @channels[:notifications].subscribe do |episode|
42
+ next unless episode.status == :extracted
43
+
44
+ log.info("Received an episode from channel. I will tweet about this")
45
+ send_notification(episode)
46
+ end
47
+
48
+ else
49
+ log.info("Twitter support is disabled")
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ # Sends a formatted message out through Twitter
56
+ #
57
+ # @param [Junkie::Episode] episode that has been downloaded
58
+ def send_notification(episode)
59
+ begin
60
+ mess = "A new episode of #{episode} has been downloaded #junkie"
61
+ ::Twitter.update(mess)
62
+ rescue Exception => e
63
+ log.error(e)
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -11,18 +11,31 @@ module Junkie
11
11
  watchdog_refresh: 10, # interval the watchdog_timer is fired
12
12
  }
13
13
 
14
- def initialize(config={})
14
+ def initialize(channels)
15
15
  @config = Config.get_config(self)
16
16
 
17
17
  @api = Junkie::Pyload::Api.new
18
18
 
19
+ @channels = channels
20
+
21
+ @found_episodes = Array.new
22
+
23
+ @active_episode = nil
24
+
19
25
  @ready_for_new_links = true
20
26
  @watchdog_enabled = false
21
- @active_downloads = Hash.new
22
-
23
27
  @skipped_timer_at_first_complete_detection = false
28
+ @should_send_it_on_channel = false
29
+
30
+ @channels[:episodes].subscribe do |episode|
31
+ next unless episode.status == :found
32
+
33
+ log.info("Got episode from Channel: #{episode}")
34
+ @found_episodes.push(episode)
35
+ end
24
36
  end
25
37
 
38
+
26
39
  # is a method that is called once from inside the reactor and allows us
27
40
  # to register custom actions into the reactor
28
41
  def setup
@@ -32,32 +45,53 @@ module Junkie
32
45
  }
33
46
 
34
47
  EM.add_periodic_timer(@config[:watchdog_refresh]) do
48
+
49
+ # temporary fix for the SystemStackError
50
+ if @should_send_it_on_channel
51
+ log.info("Sending complete episode out on the channel")
52
+ @channels[:episodes].push(@active_episode)
53
+ @active_episode = nil
54
+
55
+ @should_send_it_on_channel = false
56
+ end
57
+
35
58
  monitor_progress if @watchdog_enabled
59
+
60
+ in_fiber {
61
+ add_next_episode_to_pyload
62
+ }
36
63
  end
37
64
  end
38
65
 
39
- # Adds new episode to Pyload which downloads the episode and extracts it
40
- #
41
- # @param [Junkie::Episode] episode which should be downloaded
66
+
67
+ # Add next episode to Pyload which downloads the episode and extracts it
42
68
  #
43
69
  # @note should only be called if `is_ready?` returns true
44
- def add_episode(episode)
45
- raise InvalidStateError, "Observer is not ready" unless is_ready?
46
- @ready_for_new_links = false
70
+ def add_next_episode_to_pyload
71
+
72
+ # detect if we cann add a new episode
73
+ return unless is_ready?
74
+ return if @found_episodes.empty?
75
+
76
+ @active_episode = @found_episodes.shift
77
+ episode = @active_episode
47
78
 
79
+ @ready_for_new_links = false
48
80
  episode.status = :downloading
49
81
 
50
82
  package = "%s@%s" % [ episode.id, episode.series ]
51
83
 
52
- pid = @api.call(:addPackage, {name: package, links: [episode.link]})
84
+ pid = @api.call(:addPackage,
85
+ { name: package, links: [episode.link] })
53
86
 
54
- @active_downloads[pid] = episode
87
+ episode.pid = pid
55
88
 
56
89
  log.info("Added #{episode} to Pyload, Pid=#{pid}")
57
90
 
58
91
  (@watchdog_enabled = true) unless @watchdog_enabled
59
92
  end
60
93
 
94
+
61
95
  # checks if the Observer is ready to add new episodes to Pyload
62
96
  #
63
97
  # @returns [Boolean] true if new links can be added
@@ -72,7 +106,6 @@ module Junkie
72
106
  # It monitors the download process and reacts depending on the results
73
107
  def monitor_progress
74
108
  log.debug("Watchdog timer has been fired")
75
-
76
109
  in_fiber {
77
110
  catch(:break) {
78
111
  queue_data = @api.call(:getQueueData)
@@ -97,10 +130,22 @@ module Junkie
97
130
 
98
131
  if not pids.empty?
99
132
  if @skipped_timer_at_first_complete_detection
133
+
134
+ # post process complete active download and send it out on the
135
+ # channel
136
+ if pids.include? @active_episode.pid
137
+ log.info("'#{@active_episode}' is extracted completely")
138
+ @active_episode.pid = nil
139
+ @active_episode.status = :extracted
140
+
141
+ @should_send_it_on_channel = true
142
+ end
143
+
144
+ # remove all complete packages
100
145
  @api.call(:deletePackages, {pids: pids})
101
146
  log.info("Complete packages are removed from Pyload Queue")
102
- @skipped_timer_at_first_complete_detection = false
103
147
 
148
+ @skipped_timer_at_first_complete_detection = false
104
149
  @ready_for_new_links = true
105
150
  else
106
151
  # If a package is complete, we are sometimes so fast to remove
@@ -114,6 +159,7 @@ module Junkie
114
159
  }
115
160
  end
116
161
 
162
+
117
163
  # Searches for failed links in the pyload queue
118
164
  #
119
165
  # @param [Array] queue_data returned from :getQueueData api method
@@ -130,6 +176,7 @@ module Junkie
130
176
  false
131
177
  end
132
178
 
179
+
133
180
  # Looks for complete downloads and returns their pids
134
181
  #
135
182
  # @param [Array] queue_data returned from :getQueueData api method
@@ -169,12 +216,11 @@ module Junkie
169
216
  def update_package_ids(queue_data)
170
217
  queue_data.each do |package|
171
218
  pid = package['pid']
172
- next if @active_downloads.has_key? pid
219
+ next if @active_episode.pid == pid
173
220
 
174
- if @active_downloads.has_key? pid-1
221
+ if @active_episode.pid == pid-1
175
222
  log.info("Package ID has been changed, I will correct this")
176
- @active_downloads[pid] = @active_downloads[pid-1]
177
- @active_downloads.delete(pid-1)
223
+ @active_episode.pid = pid
178
224
  next
179
225
  end
180
226
 
@@ -18,7 +18,6 @@ module Junkie
18
18
  :hd_enabled => false,
19
19
  :hoster_id => "ul",
20
20
  :series_index_file => File.join(Dir.home, '.sindex/seriesindex.xml'),
21
- :episode_queue_timer_refresh => 5, # in seconds
22
21
  :episode_search_refresh => 15, # in minutes
23
22
  }
24
23
 
@@ -26,9 +25,19 @@ module Junkie
26
25
  def initialize
27
26
  @config = Config.get_config(self)
28
27
 
29
- @pyload_observer = Junkie::Pyload::Observer.new()
28
+ log.info("Starting Junkie #{Junkie::VERSION}")
29
+
30
+ episode_channel = EM::Channel.new
31
+ notification_channel = EM::Channel.new
32
+
33
+ @channels = {
34
+ episodes: episode_channel,
35
+ notifications: notification_channel
36
+ }
37
+
38
+ @pyload_observer = Junkie::Pyload::Observer.new(@channels)
39
+ @twitter_notification = Junkie::Notification::Twitter.new(@channels)
30
40
 
31
- @episode_queue = EM::Queue.new
32
41
  @found_episodes = Hash.new
33
42
  build_procs # has to be called here
34
43
  end
@@ -52,37 +61,28 @@ module Junkie
52
61
 
53
62
  if not @found_episodes.has_key? identifier
54
63
  log.info("Found new episode '#{episode}'")
55
- @episode_queue.push(episode)
64
+
65
+ @channels[:episodes].push(episode)
56
66
  @found_episodes[identifier] = episode
57
67
  end
58
68
  end
59
69
  }
60
70
  }
61
-
62
- # Proc that checks is Pyload-Observer is ready for new episodes and the
63
- # episode_queue contains new episodes.
64
- #
65
- # @note Is called from within the reactor
66
- @add_episodes_to_pyload = Proc.new do
67
- if @pyload_observer.is_ready?
68
- @episode_queue.pop do |episode|
69
- log.info("Popped episode '#{episode}' from queue")
70
- in_fiber {
71
- @pyload_observer.add_episode(episode)
72
- }
73
- end
74
- end
75
- end
76
71
  end
77
72
 
78
73
  ###########################################################################
79
74
  #################### The Reactor ##########################################
80
75
  ###########################################################################
81
76
  def start
82
- log.info("Starting Junkie #{Junkie::VERSION}")
83
77
 
84
78
  EM.run do
85
79
 
80
+ @channels[:episodes].subscribe do |episode|
81
+ next unless episode.status == :extracted
82
+
83
+ @channels[:notifications].push(episode)
84
+ end
85
+
86
86
  # do some initialization work
87
87
  @pyload_observer.setup
88
88
 
@@ -96,13 +96,6 @@ module Junkie
96
96
  end
97
97
  end
98
98
 
99
- # Add found episodes into Pyload if there are any episodes and pyload
100
- # is ready
101
- EM.add_periodic_timer(
102
- @config[:episode_queue_timer_refresh], @add_episodes_to_pyload)
103
-
104
- # for determining blocking operations
105
- # EM.add_periodic_timer(1) { puts Time.now.to_i }
106
99
  end
107
100
  end
108
101
  end
@@ -1,3 +1,3 @@
1
1
  module Junkie
2
- VERSION = "0.0.3"
2
+ VERSION = "0.0.4"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: junkie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.3
4
+ version: 0.0.4
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-12-02 00:00:00.000000000 Z
12
+ date: 2012-12-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: eventmachine
@@ -155,6 +155,22 @@ dependencies:
155
155
  - - ~>
156
156
  - !ruby/object:Gem::Version
157
157
  version: 1.8.0
158
+ - !ruby/object:Gem::Dependency
159
+ name: twitter
160
+ requirement: !ruby/object:Gem::Requirement
161
+ none: false
162
+ requirements:
163
+ - - ~>
164
+ - !ruby/object:Gem::Version
165
+ version: 4.4.0
166
+ type: :runtime
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ none: false
170
+ requirements:
171
+ - - ~>
172
+ - !ruby/object:Gem::Version
173
+ version: 4.4.0
158
174
  description: TV series management application
159
175
  email:
160
176
  - philipp-boehm@live.de
@@ -178,6 +194,7 @@ files:
178
194
  - lib/junkie/errors.rb
179
195
  - lib/junkie/helper.rb
180
196
  - lib/junkie/log.rb
197
+ - lib/junkie/notification/twitter.rb
181
198
  - lib/junkie/patched/sjunkieex.rb
182
199
  - lib/junkie/pyload/api.rb
183
200
  - lib/junkie/pyload/observer.rb