rubyVDRconvert 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.
- data/bin/rubyVDRconvert +4 -0
- data/lib/file_dir.rb +31 -0
- data/lib/processing_thread.rb +8 -0
- data/lib/recording.rb +76 -0
- data/lib/recording_ts.rb +22 -0
- data/lib/recording_vdr.rb +21 -0
- data/lib/recordings.rb +59 -0
- metadata +82 -0
data/bin/rubyVDRconvert
ADDED
data/lib/file_dir.rb
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
require 'find'
|
|
2
|
+
|
|
3
|
+
class FileDir
|
|
4
|
+
def self.delete!(filedir)
|
|
5
|
+
case Dir.exist?(filedir)
|
|
6
|
+
when true
|
|
7
|
+
begin
|
|
8
|
+
Dir.rmdir(filedir)
|
|
9
|
+
rescue
|
|
10
|
+
Dir.foreach(filedir) { | entry |
|
|
11
|
+
delete!(entry) unless (entry == ".") or (entry == "..")
|
|
12
|
+
}
|
|
13
|
+
retry
|
|
14
|
+
end
|
|
15
|
+
else
|
|
16
|
+
File.delete(filedir)
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
def self.mkdirtree(directory)
|
|
20
|
+
begin
|
|
21
|
+
Dir.mkdir(directory)
|
|
22
|
+
rescue SystemCallError => ex
|
|
23
|
+
parent = File.dirname(directory)
|
|
24
|
+
# Prevent an endless loop
|
|
25
|
+
return true if Dir.exist?(directory)
|
|
26
|
+
raise ex if parent.length == '/'
|
|
27
|
+
self.mkdirtree(parent)
|
|
28
|
+
retry
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
data/lib/recording.rb
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
|
|
3
|
+
require 'recording_ts'
|
|
4
|
+
require 'recording_vdr'
|
|
5
|
+
require 'file_dir'
|
|
6
|
+
|
|
7
|
+
class Recording
|
|
8
|
+
attr_reader :directory, :target_dir, :processed, :friendly_name
|
|
9
|
+
def initialize(directory)
|
|
10
|
+
@mutex = Mutex.new
|
|
11
|
+
@directory = directory
|
|
12
|
+
@recording = nil
|
|
13
|
+
@title = nil
|
|
14
|
+
@subtitle = nil
|
|
15
|
+
@friendly_name = nil
|
|
16
|
+
@target_dir = nil
|
|
17
|
+
@processed = false
|
|
18
|
+
@timestamp = File.mtime(@directory + '/index')
|
|
19
|
+
load_info
|
|
20
|
+
update_target_dir
|
|
21
|
+
determine_stream_type
|
|
22
|
+
end
|
|
23
|
+
def delete!()
|
|
24
|
+
@mutex.synchronize {
|
|
25
|
+
@recording = nil
|
|
26
|
+
GC.start
|
|
27
|
+
FileDir.delete!(@target_dir)
|
|
28
|
+
}
|
|
29
|
+
end
|
|
30
|
+
def load_info()
|
|
31
|
+
#begin
|
|
32
|
+
info = File.read(@directory + '/info')
|
|
33
|
+
info.force_encoding('BINARY')
|
|
34
|
+
cd = CharDet.detect(info)
|
|
35
|
+
encoding = cd['encoding']
|
|
36
|
+
info.each_line { | line |
|
|
37
|
+
case line[0..1]
|
|
38
|
+
when 'T '
|
|
39
|
+
@title = line[2..256].chomp
|
|
40
|
+
@title.force_encoding(encoding)
|
|
41
|
+
p "Found title: #{@title}"
|
|
42
|
+
when 'S '
|
|
43
|
+
@subtitle = line[2..256].chomp
|
|
44
|
+
@subtitle.force_encoding(encoding)
|
|
45
|
+
p "Found subtitle: #{@subtitle}"
|
|
46
|
+
end
|
|
47
|
+
}
|
|
48
|
+
#rescue
|
|
49
|
+
#end
|
|
50
|
+
@title = 'Unknown' if not @title
|
|
51
|
+
@subtitle = 'Unknown' if not @subtitle
|
|
52
|
+
end
|
|
53
|
+
def determine_stream_type()
|
|
54
|
+
case
|
|
55
|
+
when File.file?(@directory + '/00001.ts')
|
|
56
|
+
@recording = RecordingTS.new(self)
|
|
57
|
+
when File.file?(@directory + '/00001.vdr')
|
|
58
|
+
@recording = RecordingVDR.new(self)
|
|
59
|
+
else
|
|
60
|
+
raise 'Unknown recording'
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
def update_target_dir()
|
|
64
|
+
title = @title
|
|
65
|
+
subtitle = @subtitle
|
|
66
|
+
title.gsub!('/', '!')
|
|
67
|
+
subtitle.gsub!('/', '!')
|
|
68
|
+
@target_dir = "/video/#{title}/#{subtitle}"
|
|
69
|
+
@friendly_name = "#{title} - #{subtitle}"
|
|
70
|
+
end
|
|
71
|
+
def process!()
|
|
72
|
+
@mutex.synchronize{
|
|
73
|
+
@processed = true if @recording.process!
|
|
74
|
+
}
|
|
75
|
+
end
|
|
76
|
+
end
|
data/lib/recording_ts.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'file_dir'
|
|
2
|
+
|
|
3
|
+
class RecordingTS
|
|
4
|
+
def initialize(recording)
|
|
5
|
+
@parent = recording
|
|
6
|
+
end
|
|
7
|
+
def process!()
|
|
8
|
+
target_file = @parent.target_dir + "/#{@parent.friendly_name}.ts"
|
|
9
|
+
FileDir.mkdirtree(@parent.target_dir)
|
|
10
|
+
tf = File.new(target_file, 'wb:ascii-8bit')
|
|
11
|
+
source_files = Dir.glob("#{@parent.directory}/*.ts")
|
|
12
|
+
source_files.sort!
|
|
13
|
+
source_files.each { | source_file |
|
|
14
|
+
sf = File.new(source_file, 'rb:ascii-8bit')
|
|
15
|
+
while not sf.eof?
|
|
16
|
+
tf.write(sf.read(1*(2**20))) # Only read some bytes at once
|
|
17
|
+
sleep(0.01) # Be friendly to other processes
|
|
18
|
+
end
|
|
19
|
+
sf.close
|
|
20
|
+
}
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
class RecordingVDR
|
|
2
|
+
def initialize(recording)
|
|
3
|
+
@parent = recording
|
|
4
|
+
end
|
|
5
|
+
def process!()
|
|
6
|
+
return false
|
|
7
|
+
target_file = @parent.target_dir + "/#{@parent.friendly_name}.ts"
|
|
8
|
+
FileDir.mkdirtree(@parent.target_dir)
|
|
9
|
+
tf = File.new(target_file, 'wb:ascii-8bit')
|
|
10
|
+
source_files = Dir.glob("#{@parent.directory}/*.ts")
|
|
11
|
+
source_files.sort!
|
|
12
|
+
source_files.each { | source_file |
|
|
13
|
+
sf = File.new(source_file, 'rb:ascii-8bit')
|
|
14
|
+
while not sf.eof?
|
|
15
|
+
tf.write(sf.read(1*(2**20))) # Only read some bytes at once
|
|
16
|
+
sleep(0.01) # Be friendly to other processes
|
|
17
|
+
end
|
|
18
|
+
sf.close
|
|
19
|
+
}
|
|
20
|
+
end
|
|
21
|
+
end
|
data/lib/recordings.rb
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rchardet'
|
|
3
|
+
require 'find'
|
|
4
|
+
|
|
5
|
+
require 'recording'
|
|
6
|
+
|
|
7
|
+
class Recordings
|
|
8
|
+
def initialize()
|
|
9
|
+
@recordings_db = Hash.new
|
|
10
|
+
end
|
|
11
|
+
def scan()
|
|
12
|
+
basedir = '/var/vdr/video'
|
|
13
|
+
found_directories = Array.new
|
|
14
|
+
Find.find(basedir) { | entry |
|
|
15
|
+
entry.force_encoding('BINARY')
|
|
16
|
+
# Guess encoding of filename
|
|
17
|
+
cd = CharDet.detect(entry)
|
|
18
|
+
encoding = cd['encoding']
|
|
19
|
+
#p encoding
|
|
20
|
+
entry.force_encoding(encoding)
|
|
21
|
+
# Look for the index file created by VDR
|
|
22
|
+
if File.basename(entry) == "index" then
|
|
23
|
+
directory = File.dirname(entry)
|
|
24
|
+
found_directories << directory
|
|
25
|
+
if @recordings_db[directory] == nil then
|
|
26
|
+
# Found a new entry
|
|
27
|
+
p directory
|
|
28
|
+
add(directory)
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
}
|
|
32
|
+
@recordings_db.each { | recording |
|
|
33
|
+
recording.delete! unless found_directories.include?(recording.directory)
|
|
34
|
+
}
|
|
35
|
+
end
|
|
36
|
+
def dump()
|
|
37
|
+
File.new('/var/vdr/video/rubyVDRconvert.recordings.marshal', 'wb:ascii-8bit') { | f |
|
|
38
|
+
f.write(Marshal.dump(@recordings_db))
|
|
39
|
+
}
|
|
40
|
+
end
|
|
41
|
+
def load()
|
|
42
|
+
begin
|
|
43
|
+
@recordings_db = Marshal.load(File.read('/var/vdr/video/rubyVDRconvert.recordings.marshal'))
|
|
44
|
+
rescue
|
|
45
|
+
@recordings_db = Hash.new
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
def add(directory)
|
|
49
|
+
# TODO: Add a new recording that was found in the specified directory
|
|
50
|
+
# Ensure that VDR has finished writing the recording by checking the mtime of the index file
|
|
51
|
+
# Wait at least 30 seconds after VDR has finished
|
|
52
|
+
return false if (Time.now - File.mtime(directory + '/index')) > 30
|
|
53
|
+
@recordings_db[directory] = Recording.new(directory)
|
|
54
|
+
@recordings_db[directory].process!
|
|
55
|
+
end
|
|
56
|
+
def delete(recording)
|
|
57
|
+
# TODO: Delete the recording
|
|
58
|
+
end
|
|
59
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rubyVDRconvert
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
prerelease: false
|
|
5
|
+
segments:
|
|
6
|
+
- 0
|
|
7
|
+
- 0
|
|
8
|
+
- 1
|
|
9
|
+
version: 0.0.1
|
|
10
|
+
platform: ruby
|
|
11
|
+
authors:
|
|
12
|
+
- Daniel Frank
|
|
13
|
+
autorequire:
|
|
14
|
+
bindir: bin
|
|
15
|
+
cert_chain: []
|
|
16
|
+
|
|
17
|
+
date: 2010-11-07 00:00:00 +01:00
|
|
18
|
+
default_executable:
|
|
19
|
+
dependencies:
|
|
20
|
+
- !ruby/object:Gem::Dependency
|
|
21
|
+
name: rchardet
|
|
22
|
+
prerelease: false
|
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
|
24
|
+
requirements:
|
|
25
|
+
- - ">="
|
|
26
|
+
- !ruby/object:Gem::Version
|
|
27
|
+
segments:
|
|
28
|
+
- 1
|
|
29
|
+
- 3
|
|
30
|
+
- 0
|
|
31
|
+
version: 1.3.0
|
|
32
|
+
type: :runtime
|
|
33
|
+
version_requirements: *id001
|
|
34
|
+
description:
|
|
35
|
+
email: scripts@danielfrank.net
|
|
36
|
+
executables:
|
|
37
|
+
- rubyVDRconvert
|
|
38
|
+
extensions: []
|
|
39
|
+
|
|
40
|
+
extra_rdoc_files: []
|
|
41
|
+
|
|
42
|
+
files:
|
|
43
|
+
- lib/file_dir.rb
|
|
44
|
+
- lib/processing_thread.rb
|
|
45
|
+
- lib/recording.rb
|
|
46
|
+
- lib/recording_ts.rb
|
|
47
|
+
- lib/recording_vdr.rb
|
|
48
|
+
- lib/recordings.rb
|
|
49
|
+
has_rdoc: true
|
|
50
|
+
homepage:
|
|
51
|
+
licenses: []
|
|
52
|
+
|
|
53
|
+
post_install_message:
|
|
54
|
+
rdoc_options: []
|
|
55
|
+
|
|
56
|
+
require_paths:
|
|
57
|
+
- lib
|
|
58
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
59
|
+
requirements:
|
|
60
|
+
- - ">="
|
|
61
|
+
- !ruby/object:Gem::Version
|
|
62
|
+
segments:
|
|
63
|
+
- 1
|
|
64
|
+
- 9
|
|
65
|
+
- 2
|
|
66
|
+
version: 1.9.2
|
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
68
|
+
requirements:
|
|
69
|
+
- - ">="
|
|
70
|
+
- !ruby/object:Gem::Version
|
|
71
|
+
segments:
|
|
72
|
+
- 0
|
|
73
|
+
version: "0"
|
|
74
|
+
requirements: []
|
|
75
|
+
|
|
76
|
+
rubyforge_project:
|
|
77
|
+
rubygems_version: 1.3.6
|
|
78
|
+
signing_key:
|
|
79
|
+
specification_version: 3
|
|
80
|
+
summary: background maintenance script for VDR
|
|
81
|
+
test_files: []
|
|
82
|
+
|