sinatra-torrent 0.0.3 → 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,4 +1,3 @@
1
- require 'rubygems'
2
1
  require 'rake'
3
2
 
4
3
  begin
data/Readme.md CHANGED
@@ -15,13 +15,9 @@ Usage
15
15
 
16
16
  All files you put in the `downloads` directory at the root of your sinatra app will be downloadable at `/downloads/your_file.ext` and it's torrent will be dynamically generated (and cached) at `/torrents/your_file.ext.torrent`. You will have trouble with larger files as it currently hashes as part of the request first time round. I'm planning on pushing this out to workers at some point. Not yet sure how I'm going to do that…
17
17
 
18
- **NB.** Files that take longer than 1s to hash will fail at the moment!
19
-
20
- The extension is in it's early stages at the moment, so many of the settings aren't adhered to, and there are some issues with the webseeding… however it *does* work.
21
-
22
18
  ### I want options!
23
19
 
24
- There needs to be a database of torrents and peers, this is taken care of by a database adapter. Currently I've written (a breally basic) one for active record, so many databases are supported. I'm still finding my way around the Sinatra extensions api, so this is how you specify your own ActiveRecord settings:
20
+ There needs to be a database of torrents and peers, this is taken care of by a database adapter. Currently I've written (a really basic) one for active record, so many databases are supported as is, but you can write your own for others (eg. mongo). I'm still finding my way around the Sinatra extensions api, so this is how you specify your own ActiveRecord settings:
25
21
 
26
22
  require 'sinatra'
27
23
  require 'sinatra/torrent/activerecord'
@@ -31,6 +27,36 @@ There needs to be a database of torrents and peers, this is taken care of by a d
31
27
  }
32
28
  require 'sinatra/torrent'
33
29
 
30
+ Rake
31
+ ----
32
+
33
+ If a torrent takes longer than 1 second to generate on-the-fly, it'll be added to a queue for processing in a background task. If you require a special script in your Rakefile you'll be able to process the queue, add to it or pre-hash all the files in your download directory:
34
+
35
+ require 'rake'
36
+
37
+ # These need to be the same adaptor and settings as your app, of course!
38
+ require 'sinatra/torrent/activerecord'
39
+ Sinatra::Torrent::Database.settings = {
40
+ 'adapter' => 'sqlite3',
41
+ 'database' => 'torrents.db'
42
+ }
43
+ require 'sinatra/torrent/hashing'
44
+
45
+ # This line is optional, 'downloads' is the default
46
+ # If you've used `set :download_directory, 'files'` in your sinatra app, you need to do:
47
+ Sinatra::Torrent::DOWNLOAD_DIRECTORY = 'files'
48
+
49
+ # The rest of your Rakefile
50
+
51
+ * `rake hash:add filename.ext` will add `filename.ext` inside your download directory to the hash queue
52
+ * `rake hash:queue` will process all the hash jobs in the queue (you may want to set up a cron job to run this)
53
+ * `rake hash:all` will hash all files in the downloads directory right now, so none will be processed on-the-fly when the .torrent file is downloaded
54
+
55
+ To Do
56
+ -----
57
+
58
+ * Execute a user-writen block of code when an on-the-fly torrent creation times out. This will allow people to trigger the background rake task immediately, rather than waiting on cron.
59
+
34
60
  Ummmm
35
61
  -----
36
62
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.0.4
@@ -32,7 +32,11 @@ module Sinatra
32
32
  end
33
33
 
34
34
  add_index :torrents, :infohash, :unique => true
35
+ end
36
+ end
35
37
 
38
+ unless Peer.table_exists?
39
+ ActiveRecord::Schema.define do
36
40
  create_table :peers do |table|
37
41
  table.string :torrent_infohash, :null => false
38
42
  table.string :peer_id
@@ -45,7 +49,19 @@ module Sinatra
45
49
  table.timestamps
46
50
  end
47
51
 
48
- add_index :peers, [:peer_id,:torrent_infohash],:unique => true
52
+ add_index :peers, [:peer_id,:torrent_infohash], :unique => true
53
+ end
54
+ end
55
+
56
+ unless DelayedJob.table_exists?
57
+ ActiveRecord::Schema.define do
58
+ create_table :delayed_jobs do |table|
59
+ table.string :filename, :null => false
60
+
61
+ table.timestamp :created_at
62
+ end
63
+
64
+ add_index :delayed_jobs, :filename, :unique => true
49
65
  end
50
66
  end
51
67
  end
@@ -129,15 +145,34 @@ module Sinatra
129
145
 
130
146
  peer.save
131
147
  end
148
+
149
+
150
+ # Registers a file to be hashed offline
151
+ def add_hashjob(filename)
152
+ DelayedJob.find_or_create_by_filename(filename)
153
+ # return eta in seconds, where possible
154
+ nil
155
+ end
156
+
157
+ # Lists outstanding files to be hashed, by request date hopefully!
158
+ def list_hashjobs
159
+ DelayedJob.find(:all,:order => 'created_at DESC').collect{|dj| dj.filename}
160
+ end
161
+
162
+ # Removes a file from the 'to be hashed' list
163
+ def remove_hashjob(filename)
164
+ DelayedJob.find_by_filename(filename).delete rescue false
165
+ end
132
166
 
133
167
  private
168
+
134
169
  class Torrent < ActiveRecord::Base
135
170
  serialize :metadata, Hash
136
171
  end
137
172
 
138
- class Peer < ActiveRecord::Base
139
-
140
- end
173
+ class Peer < ActiveRecord::Base; end
174
+
175
+ class DelayedJob < ActiveRecord::Base; end
141
176
  end
142
177
  end
143
178
  end
@@ -0,0 +1,83 @@
1
+ require 'sinatra/torrent/helpers'
2
+
3
+ namespace :hash do
4
+ begin
5
+ torrent_db = Sinatra::Torrent::Database.new
6
+ rescue NameError
7
+ raise RuntimeError, "Please require a sinatra-torrent database adapter in your rakefile."
8
+ end
9
+
10
+ task :add do
11
+ ARGV[1..-1].each do |rel_location|
12
+ if (File.exists?(File.join(Sinatra::Torrent::DOWNLOADS_DIRECTORY,rel_location)))
13
+ torrent_db.add_hashjob(rel_location)
14
+ $stdout.puts "added to queue: #{rel_location}"
15
+ else
16
+ $stderr.puts "#{rel_location} doesn't exist in the downloads directory"
17
+ end
18
+ end
19
+ end
20
+
21
+ task :all do
22
+ completed = 0
23
+ failed = 0
24
+
25
+ Dir[File.join(Sinatra::Torrent::DOWNLOADS_DIRECTORY,'**')].each do |filename|
26
+ rel_location = filename[Sinatra::Torrent::DOWNLOADS_DIRECTORY.length+1..-1]
27
+
28
+ begin
29
+ if !torrent_db.torrent_by_path_and_timestamp(rel_location,File.mtime(filename))
30
+ d = Sinatra::Torrent.create(filename)
31
+
32
+ torrent_db.store_torrent(rel_location,File.mtime(filename),d['metadata'],d['infohash'])
33
+
34
+ torrent_db.remove_hashjob(rel_location)
35
+ $stdout.puts "Hashed: #{rel_location}"
36
+
37
+ completed += 1
38
+ else
39
+ $stdout.puts "Already hashed: #{rel_location}"
40
+ end
41
+ rescue
42
+ $stderr.puts "Not hashed: #{rel_location} (Unknown error)"
43
+ failed += 1
44
+ end
45
+ end
46
+
47
+ $stdout.puts "#{completed} hash job#{(completed == 1) ? '' : 's'} completed sucessfully"
48
+ $stderr.puts "#{failed} failed!" unless failed == 0
49
+ end
50
+
51
+ task :queue do
52
+ completed = 0
53
+ failed = 0
54
+
55
+ torrent_db.list_hashjobs.each do |rel_location|
56
+ filename = File.join(Sinatra::Torrent::DOWNLOADS_DIRECTORY,rel_location)
57
+ begin
58
+ if torrent_db.torrent_by_path_and_timestamp(rel_location,File.mtime(filename))
59
+ torrent_db.remove_hashjob(rel_location)
60
+ else
61
+ d = Sinatra::Torrent.create(filename)
62
+
63
+ torrent_db.store_torrent(rel_location,File.mtime(filename),d['metadata'],d['infohash'])
64
+
65
+ completed += 1
66
+ torrent_db.remove_hashjob(rel_location)
67
+ end
68
+ rescue Errno::ENOENT
69
+ $stderr.puts "#{rel_location} no longer exists to be hashed. Removing job."
70
+ torrent_db.remove_hashjob(rel_location)
71
+
72
+ failed += 1
73
+ rescue
74
+ $stderr.puts "Uncaught failure reason for #{rel_location}"
75
+ # TODO: trace?
76
+ failed += 1
77
+ end
78
+ end
79
+
80
+ $stdout.puts "#{completed} queued hash job#{(completed == 1) ? '' : 's'} completed sucessfully"
81
+ $stderr.puts "#{failed} failed!" unless failed == 0
82
+ end
83
+ end
@@ -0,0 +1,38 @@
1
+ require 'time'
2
+ require 'digest/sha1'
3
+ require 'bencode'
4
+
5
+ module Sinatra
6
+ module Torrent
7
+ DOWNLOADS_DIRECTORY = 'downloads'
8
+
9
+ def self.create(filename)
10
+ d = {
11
+ 'metadata' => {
12
+ 'created by' => "sinatra-torrent (#{File.read(File.expand_path(File.join(__FILE__,'..','..','..','..','VERSION'))).strip}) (http://github.com/jphastings/sinatra-torrent)",
13
+ 'creation date' => Time.now.to_i,
14
+ 'info' => {
15
+ 'name' => File.basename(filename),
16
+ 'length' => File.size(filename),
17
+ 'piece length' => 2**10, # TODO: Choose reasonable piece size
18
+ 'pieces' => ''
19
+ }
20
+ }
21
+ }
22
+
23
+ begin
24
+ file = open(filename,'r')
25
+
26
+ begin
27
+ d['metadata']['info']['pieces'] += Digest::SHA1.digest(file.read(d['metadata']['info']['piece length']))
28
+ end until file.eof?
29
+ ensure
30
+ file.close
31
+ end
32
+
33
+ d['infohash'] = Digest::SHA1.hexdigest(d['metadata']['info'].bencode)
34
+
35
+ return d
36
+ end
37
+ end
38
+ end
@@ -1,8 +1,6 @@
1
1
  require 'timeout'
2
- require 'time'
3
- require 'digest/sha1'
4
- require 'bencode'
5
2
  require 'sinatra/base'
3
+ require 'sinatra/torrent/helpers'
6
4
 
7
5
  # This extension will serve up the contents of the specified folder as web seeded torrents.
8
6
  # Both webseed versions are supported (shad0w's and GetRight's) and there is an inbuilt tracker
@@ -16,7 +14,7 @@ module Sinatra
16
14
  # Putting the annouce URL of a tracker in here will use that tracker rather than the inbuilt one
17
15
  app.set :external_tracker, nil
18
16
  # Directory which holds all the files which will be provided as torrents
19
- app.set :downloads_directory, File.dirname(__FILE__)+'downloads'
17
+ app.set :downloads_directory, File.join(File.dirname(__FILE__),Sinatra::Torrent::DOWNLOADS_DIRECTORY)
20
18
  # Mount point for the downloads directory
21
19
  app.set :downloads_mount, 'downloads'
22
20
  # Mount point for the torrents directory
@@ -41,38 +39,31 @@ module Sinatra
41
39
  filename = File.join(options.downloads_directory, rel_location)
42
40
  halt(404, "That file doesn't exist! #{filename}") unless File.exists?(filename)
43
41
 
44
- if true #!(d = options.database_adapter.torrent_by_path_and_timestamp(filename,File.mtime(filename)))
45
-
46
- d = {
47
- 'metadata' => {
48
- # TODO: Version?
49
- 'created by' => 'sinatra-torrent (0.0.1) (http://github.com/jphastings/sinatra-torrent)',
50
- 'creation date' => Time.now.to_i,
51
- 'info' => {
52
- 'name' => File.basename(rel_location),
53
- 'length' => File.size(filename),
54
- 'piece length' => 2**10, # TODO: Choose reasonable piece size
55
- 'pieces' => ''
56
- }
57
- }
58
- }
42
+ if !(d = options.database_adapter.torrent_by_path_and_timestamp(filename,File.mtime(filename)))
59
43
 
60
44
  begin
61
- file = open(filename,'r')
62
-
63
45
  Timeout::timeout(1) do
64
- begin
65
- d['metadata']['info']['pieces'] += Digest::SHA1.digest(file.read(d['metadata']['info']['piece length']))
66
- end until file.eof?
46
+ d = Sinatra::Torrent.create(filename)
67
47
  end
68
48
  rescue Timeout::Error
69
- # TODO: Actually run it in the background!
70
- halt(503,"This torrent is taking too long to build, we're [not currently supporting!] running it in the background. Please try again in a few minutes.")
71
- ensure
72
- file.close
49
+ eta = options.database_adapter.add_hashjob(filename)
50
+
51
+ begin
52
+ wait = case (eta/60).floor
53
+ when 0
54
+ 'under a minute'
55
+ when 1
56
+ 'about a minute'
57
+ else
58
+ "about #{(eta/60).floor} minutes"
59
+ end
60
+ rescue NoMethodError
61
+ wait = "a short while"
62
+ end
63
+
64
+ halt(503,"This torrent is taking too long to build, we're running it in the background. Please try again in #{wait}.")
73
65
  end
74
66
 
75
- d['infohash'] = Digest::SHA1.hexdigest(d['metadata']['info'].bencode)
76
67
  options.database_adapter.store_torrent(filename,File.mtime(filename),d['metadata'],d['infohash'])
77
68
  end
78
69
 
@@ -128,8 +119,15 @@ module Sinatra
128
119
  end
129
120
 
130
121
  # INDEX PAGE
122
+
123
+ # TODO: Have a 'fallback' index view?
131
124
  app.get "/#{app.options.torrents_mount}/" do
132
- haml :torrents_index,:locals => {:torrents => Dir.glob("#{options.downloads_directory}/**").collect {|f| f[options.downloads_directory.length+1..-1] } }
125
+ locals = {:torrents => (Dir.glob("#{options.downloads_directory}/**").collect {|f| f[options.downloads_directory.length+1..-1] } rescue [])}
126
+ begin
127
+ haml :torrents_index,:locals => locals
128
+ rescue Errno::ENOENT
129
+ "<ul>"<<locals[:torrents].collect{|t| "<li><a href=\"/#{options.torrents_mount}/#{t}.torrent\">#{t}</a></li>" }.join<<"</ul>"
130
+ end
133
131
  end
134
132
 
135
133
  # DATA
@@ -158,4 +156,4 @@ module Sinatra
158
156
  end
159
157
 
160
158
  register Torrent
161
- end
159
+ end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{sinatra-torrent}
8
- s.version = "0.0.3"
8
+ s.version = "0.0.4"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["JP Hastings-Spital"]
12
- s.date = %q{2011-02-08}
12
+ s.date = %q{2011-02-09}
13
13
  s.description = %q{An extension to Sinatra which will allow you to run a webseeded torrent tracker of files in the folder you specify.}
14
14
  s.email = %q{jphastings@gmail.com}
15
15
  s.files = [
@@ -18,8 +18,9 @@ Gem::Specification.new do |s|
18
18
  "VERSION",
19
19
  "lib/sinatra/torrent.rb",
20
20
  "lib/sinatra/torrent/activerecord.rb",
21
- "sinatra-torrent.gemspec",
22
- "views/torrents_index.haml"
21
+ "lib/sinatra/torrent/hashing.rb",
22
+ "lib/sinatra/torrent/helpers.rb",
23
+ "sinatra-torrent.gemspec"
23
24
  ]
24
25
  s.homepage = %q{http://github.com/jphastings/sinatra-torrent}
25
26
  s.rdoc_options = ["--charset=UTF-8"]
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sinatra-torrent
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 23
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 3
10
- version: 0.0.3
9
+ - 4
10
+ version: 0.0.4
11
11
  platform: ruby
12
12
  authors:
13
13
  - JP Hastings-Spital
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2011-02-08 00:00:00 +00:00
18
+ date: 2011-02-09 00:00:00 +00:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -48,8 +48,9 @@ files:
48
48
  - VERSION
49
49
  - lib/sinatra/torrent.rb
50
50
  - lib/sinatra/torrent/activerecord.rb
51
+ - lib/sinatra/torrent/hashing.rb
52
+ - lib/sinatra/torrent/helpers.rb
51
53
  - sinatra-torrent.gemspec
52
- - views/torrents_index.haml
53
54
  has_rdoc: true
54
55
  homepage: http://github.com/jphastings/sinatra-torrent
55
56
  licenses: []
@@ -1,5 +0,0 @@
1
- %ul
2
- -locals[:torrents].each do |torrent|
3
- %li
4
- %a{:href=>"/#{options.torrents_mount}/#{torrent}.torrent"}=torrent
5
-