jeeves-pvr 0.2.0
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.
- checksums.yaml +15 -0
- data/Bugs.rdoc +6 -0
- data/History.txt +49 -0
- data/LICENCE.rdoc +159 -0
- data/README.md +60 -0
- data/bin/jeeves +122 -0
- data/bin/jeeves-install +27 -0
- data/bin/jeeves-old +375 -0
- data/bin/jeeves.wrapper +26 -0
- data/etc/jerbil/jeeves.rb +46 -0
- data/lib/jeeves.rb +68 -0
- data/lib/jeeves/config.rb +141 -0
- data/lib/jeeves/errors.rb +70 -0
- data/lib/jeeves/listings.rb +116 -0
- data/lib/jeeves/parser/listings.rb +41 -0
- data/lib/jeeves/parser/store.rb +167 -0
- data/lib/jeeves/parser/videos.rb +136 -0
- data/lib/jeeves/partition.rb +174 -0
- data/lib/jeeves/scheduler/base.rb +209 -0
- data/lib/jeeves/scheduler/old_base.rb +148 -0
- data/lib/jeeves/store.rb +544 -0
- data/lib/jeeves/tags.rb +329 -0
- data/lib/jeeves/utils.rb +124 -0
- data/lib/jeeves/version.rb +13 -0
- data/lib/jeeves/video.rb +79 -0
- metadata +176 -0
data/bin/jeeves.wrapper
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
#!/usr/bin/ruby18
|
2
|
+
#
|
3
|
+
# This file was generated by RubyGems.
|
4
|
+
#
|
5
|
+
# The application 'jeeves-pvr' is installed as part of a gem, and
|
6
|
+
# this file is here to facilitate running it.
|
7
|
+
#
|
8
|
+
|
9
|
+
require 'rubygems'
|
10
|
+
|
11
|
+
version = ">= 0"
|
12
|
+
|
13
|
+
if ARGV.first
|
14
|
+
str = ARGV.first
|
15
|
+
str = str.dup.force_encoding("BINARY") if str.respond_to? :force_encoding
|
16
|
+
if str =~ /\A_(.*)_\z/
|
17
|
+
version = $1
|
18
|
+
ARGV.shift
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
#gem 'jeeves-pvr', version
|
23
|
+
#require 'jeeves'
|
24
|
+
executable = Gem.bin_path('jeeves-pvr', 'jeeves', version)
|
25
|
+
puts executable
|
26
|
+
load executable
|
@@ -0,0 +1,46 @@
|
|
1
|
+
#
|
2
|
+
# Configuration Options for: Jeeves::Config
|
3
|
+
#
|
4
|
+
|
5
|
+
# Add a file partition for Jeeves. Must have the following parameters:
|
6
|
+
# :path - valid file path to the partition to be added (not a device path),
|
7
|
+
# :key - the key given to the partition when it was initialised,
|
8
|
+
# :mountable - true if the partition should be mounted directly,
|
9
|
+
# :mounted_on - path of mount point if the partition is not one itself
|
10
|
+
#
|
11
|
+
# Use :mountable=>true to enable the partition to be mounted - needs to be an entry in /etc/fstab
|
12
|
+
# If the partition is not directly mountable but is on a mountable device partition that you also want
|
13
|
+
# mounted then use :mounted_on=>/path/to/partition instead
|
14
|
+
#add_partition
|
15
|
+
add_partition :path=>'/home/jeeves', :key=>'d1520a02b294db739bddb2324482310116f39d07'
|
16
|
+
add_partition :path=>'/home/jeeves_2', :key=>'9227567edca1bb8018d2c7bda592dbb69a89af35'
|
17
|
+
|
18
|
+
# Specify what to do if a specified partition cannot be added, e.g. because it has not been mounted
|
19
|
+
# Can be one of the following:
|
20
|
+
# :ignore - log an info message and carry on
|
21
|
+
# :warn - let the user know but carry on
|
22
|
+
# :fatal - do not continue without the partition
|
23
|
+
#
|
24
|
+
# The default is to warn but carry on. Jeeves should still be able to allocate a file even if there are
|
25
|
+
# missing partitions in the store
|
26
|
+
store_errors :warn
|
27
|
+
|
28
|
+
# Specify the environment within which Jeeves services (Jetuner etc) are expected
|
29
|
+
# to be operating. Should be one of :dev, :test or :prod
|
30
|
+
#environment :dev
|
31
|
+
|
32
|
+
# URL to the Jeeves listing/TV application that has info on programme listings
|
33
|
+
jeeves_tv_url 'http://tv.jeeves.osburn-sharp.ath.cx'
|
34
|
+
|
35
|
+
# Set the base rate for estimating data storage in kbytes per second. Note
|
36
|
+
# for some tuners this may need to be scaled, which can be done at the time
|
37
|
+
# of allocation.
|
38
|
+
#base_data_rate 416
|
39
|
+
|
40
|
+
# Specify a directory where jeeves can put working files etc. If you are working
|
41
|
+
# across a network then this directory needs to be shared across the network too.
|
42
|
+
working_dir '/home/jeeves/.jeeves'
|
43
|
+
|
44
|
+
# URL to the Jeeves video application to which recorded videos are referred etc
|
45
|
+
jeeves_url 'http://jeeves.osburn-sharp.ath.cx'
|
46
|
+
|
data/lib/jeeves.rb
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#
|
2
|
+
# Author:: R.J.Sharp
|
3
|
+
# Email:: robert(a)osburn-sharp.ath.cx
|
4
|
+
# Copyright:: Copyright (c) 2011
|
5
|
+
# License:: Open Software Licence v3.0
|
6
|
+
#
|
7
|
+
# This software is licensed for use under the Open Software Licence v. 3.0
|
8
|
+
# The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
|
9
|
+
# and in the file LICENCE. Under the terms of this licence, all derivative works
|
10
|
+
# must themselves be licensed under the Open Software Licence v. 3.0
|
11
|
+
#
|
12
|
+
#
|
13
|
+
require 'jerbil/jerbil_service/base'
|
14
|
+
require 'jerbil/jerbil_service/support'
|
15
|
+
require 'jerbil/support'
|
16
|
+
require 'jerbil/config'
|
17
|
+
|
18
|
+
# pull in things to do with jeeves
|
19
|
+
|
20
|
+
require 'jeeves/listings'
|
21
|
+
require 'jeeves/store'
|
22
|
+
require 'jeeves/tags'
|
23
|
+
require 'jeeves/config'
|
24
|
+
require 'jeeves/errors'
|
25
|
+
|
26
|
+
#
|
27
|
+
# == Jeeves Service
|
28
|
+
#
|
29
|
+
# Synopsis of the whole service
|
30
|
+
#
|
31
|
+
module Jeeves
|
32
|
+
|
33
|
+
# add generic class methods
|
34
|
+
extend JerbilService::Support
|
35
|
+
|
36
|
+
# === Jeeves Service class
|
37
|
+
#
|
38
|
+
# Description of how the service class works
|
39
|
+
#
|
40
|
+
# class Service < JerbilService::Base
|
41
|
+
#
|
42
|
+
# # Document the constructor as required.
|
43
|
+
# #
|
44
|
+
# # pkey:: string - private key that must be used for
|
45
|
+
# # supervision calls (e.g. stop_callback)
|
46
|
+
# # options:: hash of options, preferably as created by Jeckyl. See documentation
|
47
|
+
# # on JerbilService::Base for assumed options re logging etc
|
48
|
+
# #
|
49
|
+
# def initialize(pkey, options)
|
50
|
+
#
|
51
|
+
# # do things that cannot be done when $SAFE > 0
|
52
|
+
#
|
53
|
+
# # the symbol should be the app name and must correspond to a service in /etc/services
|
54
|
+
# super(:jeeves, pkey, options)
|
55
|
+
#
|
56
|
+
# # DRb is now working, this process may have daemonized and $SAFE = 1
|
57
|
+
#
|
58
|
+
# end
|
59
|
+
#
|
60
|
+
# #
|
61
|
+
# # define additional class methods to make things happen
|
62
|
+
# #
|
63
|
+
#
|
64
|
+
# # do not redefine the superclass methods unless you are absolutely sure you know
|
65
|
+
# # what you are doing.
|
66
|
+
#
|
67
|
+
# end
|
68
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
# = Configuration settings for Jeeves Applications etc
|
4
|
+
#
|
5
|
+
# == Jeckyl config
|
6
|
+
#
|
7
|
+
# Author:: Robert Sharp
|
8
|
+
# Copyright:: Copyright (c) 2013 Robert Sharp
|
9
|
+
# License:: Open Software Licence v3.0
|
10
|
+
#
|
11
|
+
# This software is licensed for use under the Open Software Licence v. 3.0
|
12
|
+
# The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
|
13
|
+
# and in the file copyright.txt. Under the terms of this licence, all derivative works
|
14
|
+
# must themselves be licensed under the Open Software Licence v. 3.0
|
15
|
+
#
|
16
|
+
#
|
17
|
+
#
|
18
|
+
require 'jeckyl'
|
19
|
+
|
20
|
+
module Jeeves
|
21
|
+
|
22
|
+
# define all of the configuration settings for Jeeves. Derived from
|
23
|
+
# Jellog to provide logging options for store operations
|
24
|
+
#
|
25
|
+
class Config < Jeckyl::Config
|
26
|
+
|
27
|
+
def configure_add_partition(params={})
|
28
|
+
comment "Add a file partition for Jeeves. Must have the following parameters:",
|
29
|
+
" :path - valid file path to the partition to be added (not a device path),",
|
30
|
+
" :key - the key given to the partition when it was initialised,",
|
31
|
+
" :mountable - true if the partition should be mounted directly,",
|
32
|
+
" :mounted_on - path of mount point if the partition is not one itself",
|
33
|
+
" ",
|
34
|
+
"Use :mountable=>true to enable the partition to be mounted - needs to be an entry in /etc/fstab",
|
35
|
+
"If the partition is not directly mountable but is on a mountable device partition that you also want",
|
36
|
+
"mounted then use :mounted_on=>/path/to/partition instead"
|
37
|
+
|
38
|
+
# ignore calls to get a default, which will not have the right params
|
39
|
+
return unless params.kind_of?(Hash)
|
40
|
+
|
41
|
+
@stores ||= Hash.new
|
42
|
+
|
43
|
+
if @stores.has_key?(params[:path]) then
|
44
|
+
# should not have defined the store already...
|
45
|
+
raise Jeckyl::ConfigError, "Duplicate store: #{params[:path]}"
|
46
|
+
end
|
47
|
+
|
48
|
+
# make sure this is a writable directory
|
49
|
+
# but if its not mounted it may not be!
|
50
|
+
#a_writable_dir(params[:path])
|
51
|
+
|
52
|
+
# if Jeeves.store_key(params[:path]) != params[:key] then
|
53
|
+
# # store has not been initialised with this key
|
54
|
+
# raise Jeckyl::ConfigError, "Incorrect/missing key for store: #{params[:path]}"
|
55
|
+
# end
|
56
|
+
|
57
|
+
@stores[params[:path]] = {:key=>params[:key]}
|
58
|
+
|
59
|
+
# check that both mount options have not been provided
|
60
|
+
if params[:mountable] then
|
61
|
+
if params.has_key?(:mounted_on) then
|
62
|
+
raise Jeckyl::ConfigError, "Please provide :mountable or :mounted_on but not both"
|
63
|
+
end
|
64
|
+
@stores[params[:path]][:mount_point] = params[:path]
|
65
|
+
end
|
66
|
+
|
67
|
+
if params.has_key?(:mounted_on) then
|
68
|
+
a_readable_file(params[:mounted_on]) # the directory may not be writable!
|
69
|
+
@stores[params[:path]][:mount_point] = params[:mounted_on]
|
70
|
+
end
|
71
|
+
|
72
|
+
return @stores
|
73
|
+
|
74
|
+
end
|
75
|
+
|
76
|
+
def configure_base_data_rate(kbpm)
|
77
|
+
default 25000 / 60 # reasonable rate for dvb
|
78
|
+
comment "Set the base rate for estimating data storage in kbytes per second. Note",
|
79
|
+
"for some tuners this may need to be scaled, which can be done at the time",
|
80
|
+
"of allocation."
|
81
|
+
|
82
|
+
a_type_of(kbpm, Integer) && in_range(kbpm, 100, 10000)
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
def configure_jeeves_url(url)
|
87
|
+
comment "URL to the Jeeves video application to which recorded videos are referred etc"
|
88
|
+
|
89
|
+
a_matching_string(url, /https?:\/\/[\S]+/)
|
90
|
+
end
|
91
|
+
|
92
|
+
def configure_jeeves_tv_url(url)
|
93
|
+
comment "URL to the Jeeves listing/TV application that has info on programme listings"
|
94
|
+
|
95
|
+
a_matching_string(url, /https?:\/\/[\S]+/)
|
96
|
+
end
|
97
|
+
|
98
|
+
def configure_jeeves_uri_timeout(timeout)
|
99
|
+
default 2 * 60 # 2 minutes
|
100
|
+
comment "Set the timeout on updating Jeeves TV with the listings data. This can",
|
101
|
+
"take a long time so usually it exceeds the default. Needs to be in seconds.",
|
102
|
+
"To set an infinite timeout, use 0"
|
103
|
+
|
104
|
+
a_type_of(timeout, Integer) && in_range(timeout, 0, 10000) # some very high upper limit
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
def configure_store_errors(symb)
|
109
|
+
default :warn
|
110
|
+
comment "Specify what to do if a specified partition cannot be added, e.g. because it has not been mounted",
|
111
|
+
"Can be one of the following:",
|
112
|
+
" :ignore - log an info message and carry on",
|
113
|
+
" :warn - let the user know but carry on",
|
114
|
+
" :fatal - do not continue without the partition",
|
115
|
+
"",
|
116
|
+
"The default is to warn but carry on. Jeeves should still be able to allocate a file even if there are",
|
117
|
+
"missing partitions in the store"
|
118
|
+
|
119
|
+
a_member_of(symb, [:ignore, :warn, :fatal])
|
120
|
+
|
121
|
+
end
|
122
|
+
|
123
|
+
def configure_working_dir(dir)
|
124
|
+
default '/tmp'
|
125
|
+
comment "Specify a directory where jeeves can put working files etc. If you are working",
|
126
|
+
"across a network then this directory needs to be shared across the network too."
|
127
|
+
|
128
|
+
a_writable_dir(dir)
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
def configure_environment(env)
|
133
|
+
default :dev
|
134
|
+
comment "Specify the environment within which Jeeves services (Jetuner etc) are expected",
|
135
|
+
"to be operating. Should be one of :dev, :test or :prod"
|
136
|
+
a_member_of(env, [:dev, :test, :prod])
|
137
|
+
end
|
138
|
+
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
#
|
2
|
+
# Author:: R.J.Sharp
|
3
|
+
# Email:: robert(a)osburn-sharp.ath.cx
|
4
|
+
# Copyright:: Copyright (c) 2011
|
5
|
+
# License:: Open Software Licence v3.0
|
6
|
+
#
|
7
|
+
# This software is licensed for use under the Open Software Licence v. 3.0
|
8
|
+
# The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
|
9
|
+
# and in the file LICENCE. Under the terms of this licence, all derivative works
|
10
|
+
# must themselves be licensed under the Open Software Licence v. 3.0
|
11
|
+
#
|
12
|
+
#
|
13
|
+
# This file groups together all the errors for jeeves.
|
14
|
+
# Preceed each class with a description of the error
|
15
|
+
|
16
|
+
module Jeeves
|
17
|
+
|
18
|
+
# A general class for all errors created by this project. All specific exceptions
|
19
|
+
# should be children of this class
|
20
|
+
class JeevesError < RuntimeError; end
|
21
|
+
|
22
|
+
class VersionError < JeevesError; end
|
23
|
+
|
24
|
+
# errors raised by the scheduler
|
25
|
+
class JscheduleError < JeevesError; end
|
26
|
+
|
27
|
+
#the tuner type for a scheduler is not valid
|
28
|
+
class InvalidTuner < JscheduleError; end
|
29
|
+
|
30
|
+
# the tuner does not have the given mode
|
31
|
+
class InvalidMode < JscheduleError; end
|
32
|
+
|
33
|
+
# a schedule request is missing a required parameter
|
34
|
+
class MissingParam < JscheduleError; end
|
35
|
+
|
36
|
+
# the tuner is busy
|
37
|
+
class TunerBusy < JscheduleError; end
|
38
|
+
|
39
|
+
# store related errors
|
40
|
+
class StoreError < JeevesError; end
|
41
|
+
|
42
|
+
class InvalidPartition < StoreError; end
|
43
|
+
|
44
|
+
class InvalidPartitionKey < StoreError; end
|
45
|
+
|
46
|
+
class DiskError < JeevesError; end
|
47
|
+
|
48
|
+
# listings related errors
|
49
|
+
class ListingError < JeevesError; end
|
50
|
+
class VideoError < JeevesError; end
|
51
|
+
|
52
|
+
|
53
|
+
class TaggerError < JeevesError; end
|
54
|
+
|
55
|
+
# A Jeckyl option that is needed has not been provided
|
56
|
+
class MissingOption < TaggerError; end
|
57
|
+
|
58
|
+
# the file provided cannot be read
|
59
|
+
class FileError < TaggerError; end
|
60
|
+
|
61
|
+
# the url does not work
|
62
|
+
class UrlError < TaggerError; end
|
63
|
+
|
64
|
+
# there was an error with the XML tags
|
65
|
+
class XMLError < TaggerError; end
|
66
|
+
|
67
|
+
# error creating matroska file
|
68
|
+
class MkvError < TaggerError; end
|
69
|
+
|
70
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
#
|
2
|
+
#
|
3
|
+
# = Xmltv
|
4
|
+
#
|
5
|
+
# == methods for interfacing xmltv with jeeves
|
6
|
+
#
|
7
|
+
# Author:: Robert Sharp
|
8
|
+
# Copyright:: Copyright (c) 2013 Robert Sharp
|
9
|
+
# License:: Open Software Licence v3.0
|
10
|
+
#
|
11
|
+
# This software is licensed for use under the Open Software Licence v. 3.0
|
12
|
+
# The terms of this licence can be found at http://www.opensource.org/licenses/osl-3.0.php
|
13
|
+
# and in the file copyright.txt. Under the terms of this licence, all derivative works
|
14
|
+
# must themselves be licensed under the Open Software Licence v. 3.0
|
15
|
+
#
|
16
|
+
#
|
17
|
+
#
|
18
|
+
require 'jeckyl/errors'
|
19
|
+
require 'jeeves/errors'
|
20
|
+
|
21
|
+
module Jeeves
|
22
|
+
|
23
|
+
# load the latest listings data into Jeeves
|
24
|
+
#
|
25
|
+
# jeeves will use existing listings data if it was downloaded
|
26
|
+
# less than 6 hours ago. This can be overriden with the :force option
|
27
|
+
#
|
28
|
+
# If the :reset option is set to true then the listings data will replace
|
29
|
+
# all existing data and not just append to it.
|
30
|
+
#
|
31
|
+
# @param [Hash] params to control the loading
|
32
|
+
# @option [String] :working_dir, see config
|
33
|
+
# @option [String] :jeeves_tv_url, see config
|
34
|
+
# @option [Boolean] :force - set to true to force new listings data
|
35
|
+
# @option [Boolean] :reset - set to true to reset all listings data
|
36
|
+
def Jeeves.update_listings(params={})
|
37
|
+
|
38
|
+
working_dir = params[:working_dir]
|
39
|
+
unless File.directory?(working_dir) && File.writable?(working_dir)
|
40
|
+
# if config file is used this should not be possible
|
41
|
+
raise Jeckyl::ConfigError, "Cannot write to #{working_dir}"
|
42
|
+
end
|
43
|
+
|
44
|
+
chan_txt_file = File.join(working_dir, 'channels.txt')
|
45
|
+
listings_file = File.join(working_dir, 'listings.xml')
|
46
|
+
|
47
|
+
age = 0
|
48
|
+
# see how old it is
|
49
|
+
age = Time.now - File.mtime(listings_file) if FileTest.exists?(listings_file)
|
50
|
+
|
51
|
+
age = 0 if params[:update_listings] == true
|
52
|
+
|
53
|
+
jeeves_tv_url = params[:jeeves_tv_url]
|
54
|
+
|
55
|
+
# check how old the listings file is
|
56
|
+
unless age > 0 && age < (60*60*6) then
|
57
|
+
# its more than 6 hours old, so renew it
|
58
|
+
# or you forced renewal!
|
59
|
+
|
60
|
+
# make sure it does not already exist
|
61
|
+
FileUtils.rm_f(chan_txt_file)
|
62
|
+
FileUtils.rm_f(listings_file)
|
63
|
+
|
64
|
+
|
65
|
+
# get the channels info from jeeves_tv
|
66
|
+
cmd = "/usr/bin/wget #{jeeves_tv_url}/bchannels.txt -O #{chan_txt_file}"
|
67
|
+
|
68
|
+
|
69
|
+
unless system(cmd)
|
70
|
+
raise ListingError, "#{cmd}\n failed with error #{$?}"
|
71
|
+
end
|
72
|
+
|
73
|
+
unless File.exists?(chan_txt_file)
|
74
|
+
raise ListingError, "Failed to create file: #{chan_txt_file}"
|
75
|
+
end
|
76
|
+
|
77
|
+
# now copy this file to where xmltv expects to find it.
|
78
|
+
# cheat with shell cos of ~
|
79
|
+
cmd = "cp #{chan_txt_file} ~/.xmltv/tv_grab_uk_rt.conf"
|
80
|
+
unless system(cmd)
|
81
|
+
raise ListingError, "Failed to copy file: #{cmd}"
|
82
|
+
end
|
83
|
+
|
84
|
+
cmd = "/usr/bin/tv_grab_uk_rt >#{listings_file}"
|
85
|
+
unless system(cmd)
|
86
|
+
raise ListingError, "Failed to grab listings: #{$?}\n #{cmd}"
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
listings_url = URI.encode(listings_file) # ensure it is OK as url
|
91
|
+
|
92
|
+
# and now poke jeeves-tv to let it know there is something to process
|
93
|
+
#cmd = "/usr/bin/wget #{jeeves_tv_url}/load?file=#{listings_url}"
|
94
|
+
url = "#{jeeves_tv_url}/load?file=#{listings_url}"
|
95
|
+
url << "&reset=true" if params[:reset_listings]
|
96
|
+
|
97
|
+
# unless system(cmd)
|
98
|
+
# raise ListingError, "Failed to load programme info: #{$?}\n#{cmd}"
|
99
|
+
# end
|
100
|
+
|
101
|
+
rto = params[:jeeves_uri_timeout]
|
102
|
+
rto = nil if rto == 0 # makes it infinite
|
103
|
+
|
104
|
+
response = Array.new
|
105
|
+
open(url, read_timeout:rto) do |resp|
|
106
|
+
resp.each_line do |line|
|
107
|
+
response << line
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# test that the response was OK
|
112
|
+
|
113
|
+
return true
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|