torganiser 0.0.5 → 0.1.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 +4 -4
- data/Rakefile +10 -5
- data/bin/torganiser +28 -18
- data/lib/torganiser.rb +11 -8
- data/lib/torganiser/arranger.rb +18 -12
- data/lib/torganiser/episode_file.rb +3 -42
- data/lib/torganiser/file_query.rb +5 -9
- data/lib/torganiser/match_one.rb +12 -0
- data/lib/torganiser/matcher.rb +63 -0
- data/lib/torganiser/runner.rb +5 -12
- data/lib/torganiser/scanner.rb +4 -6
- data/lib/torganiser/series.rb +1 -5
- data/lib/torganiser/version.rb +2 -1
- data/spec/spec_helper.rb +1 -1
- data/spec/torganiser/arranger_spec.rb +26 -9
- data/spec/torganiser/episode_file_spec.rb +46 -105
- data/spec/torganiser/file_query_spec.rb +28 -28
- data/spec/torganiser/match_one_spec.rb +10 -0
- data/spec/torganiser/matcher_spec.rb +142 -0
- data/spec/torganiser/runner_spec.rb +13 -49
- data/spec/torganiser/scanner_spec.rb +34 -35
- data/spec/torganiser/series_spec.rb +5 -6
- data/torganiser.gemspec +19 -18
- metadata +22 -2
data/lib/torganiser/series.rb
CHANGED
@@ -1,11 +1,9 @@
|
|
1
1
|
module Torganiser
|
2
|
-
|
3
2
|
# Models the series information from an episode file
|
4
3
|
class Series
|
5
|
-
|
6
4
|
attr_reader :name, :year
|
7
5
|
|
8
|
-
def initialize
|
6
|
+
def initialize(name, year: nil)
|
9
7
|
@name = name
|
10
8
|
@year = year
|
11
9
|
end
|
@@ -13,7 +11,5 @@ module Torganiser
|
|
13
11
|
def display_name
|
14
12
|
@display_name = year ? "#{name} (#{year})" : name
|
15
13
|
end
|
16
|
-
|
17
14
|
end
|
18
|
-
|
19
15
|
end
|
data/lib/torganiser/version.rb
CHANGED
data/spec/spec_helper.rb
CHANGED
@@ -1,36 +1,53 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
# Main module
|
3
4
|
module Torganiser
|
4
|
-
|
5
5
|
describe Arranger do
|
6
6
|
|
7
|
-
describe
|
8
|
-
subject { Arranger.new(
|
7
|
+
describe 'for a collection' do
|
8
|
+
subject { Arranger.new('/my/media') }
|
9
9
|
|
10
|
-
context
|
10
|
+
context 'when arranging a file' do
|
11
11
|
|
12
12
|
before do
|
13
13
|
allow(FileUtils).to receive(:mkdir_p)
|
14
14
|
allow(FileUtils).to receive(:mv)
|
15
15
|
end
|
16
16
|
|
17
|
-
let(:file) {
|
17
|
+
let(:file) { '/tmp/stuff/Waffle.Cone.2007.S01E02.HDTV.x264-LOL.mp4' }
|
18
18
|
|
19
|
-
it
|
19
|
+
it 'creates a destination by series and season' do
|
20
20
|
expect(FileUtils).to receive(:mkdir_p).with(
|
21
|
-
|
21
|
+
'/my/media/Waffle Cone (2007)/Season 1'
|
22
22
|
)
|
23
23
|
subject.arrange file
|
24
24
|
end
|
25
25
|
|
26
|
-
it
|
26
|
+
it 'moves the file to the destination' do
|
27
27
|
expect(FileUtils).to receive(:mv).with(
|
28
28
|
file,
|
29
|
-
|
29
|
+
'/my/media/Waffle Cone (2007)/Season 1'
|
30
30
|
)
|
31
31
|
subject.arrange file
|
32
32
|
end
|
33
33
|
|
34
|
+
context 'when set up to copy, rather than move' do
|
35
|
+
|
36
|
+
before do
|
37
|
+
allow(FileUtils).to receive(:cp)
|
38
|
+
end
|
39
|
+
|
40
|
+
subject { Arranger.new('/my/media', copy: true) }
|
41
|
+
|
42
|
+
it 'copies the file to the destination' do
|
43
|
+
expect(FileUtils).to receive(:cp).with(
|
44
|
+
file,
|
45
|
+
'/my/media/Waffle Cone (2007)/Season 1'
|
46
|
+
)
|
47
|
+
subject.arrange file
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
34
51
|
end
|
35
52
|
end
|
36
53
|
end
|
@@ -1,69 +1,72 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
# Main module
|
3
4
|
module Torganiser
|
4
|
-
|
5
5
|
describe EpisodeFile do
|
6
6
|
|
7
7
|
subject { EpisodeFile.new(file) }
|
8
8
|
|
9
|
-
|
9
|
+
let(:match) do
|
10
|
+
{
|
11
|
+
name: 'Hello',
|
12
|
+
season: '02',
|
13
|
+
episode: '01'
|
14
|
+
}
|
15
|
+
end
|
16
|
+
|
17
|
+
let(:file) { 'file/path/Hello.S02E01.mp4' }
|
10
18
|
|
11
|
-
|
19
|
+
before do
|
20
|
+
allow(Matcher).to receive(:match).and_return match
|
21
|
+
end
|
22
|
+
|
23
|
+
context 'when initialized with a filename' do
|
12
24
|
|
13
25
|
it 'extracts the base file name' do
|
14
26
|
expect(subject.basename).to eq 'Hello.S02E01.mp4'
|
15
27
|
end
|
16
28
|
|
17
|
-
it '
|
18
|
-
expect(
|
29
|
+
it 'matches the basename using the Matcher' do
|
30
|
+
expect(Matcher).to receive(:match).with('Hello.S02E01.mp4')
|
31
|
+
subject.series
|
19
32
|
end
|
20
33
|
|
21
|
-
it 'extracts
|
22
|
-
expect(subject.
|
34
|
+
it 'extracts season number as an integer' do
|
35
|
+
expect(subject.season).to eq 2
|
23
36
|
end
|
24
37
|
|
25
|
-
it '
|
26
|
-
expect(
|
27
|
-
"Hello", year: nil
|
28
|
-
)
|
29
|
-
subject.series
|
38
|
+
it 'extracts episode number as an integer' do
|
39
|
+
expect(subject.episode).to eq 1
|
30
40
|
end
|
31
41
|
|
32
|
-
context
|
33
|
-
|
34
|
-
|
42
|
+
context 'that contains year information' do
|
43
|
+
let(:match) do
|
44
|
+
{
|
45
|
+
name: 'Hello',
|
46
|
+
year: '2008'
|
47
|
+
}
|
48
|
+
end
|
35
49
|
|
36
50
|
it 'creates a series with a name and year' do
|
37
51
|
expect(Series).to receive(:new).with(
|
38
|
-
|
52
|
+
'Hello', year: 2008
|
39
53
|
)
|
40
54
|
subject.series
|
41
55
|
end
|
42
56
|
|
43
57
|
end
|
44
58
|
|
45
|
-
context
|
59
|
+
context 'that has a series name in dot-format' do
|
46
60
|
|
47
|
-
let(:
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
end
|
52
|
-
|
53
|
-
it 'extracts first episode number' do
|
54
|
-
expect(subject.episode).to eq 1
|
61
|
+
let(:match) do
|
62
|
+
{
|
63
|
+
name: 'Goodbye.Hello.Hamburger'
|
64
|
+
}
|
55
65
|
end
|
56
66
|
|
57
|
-
|
58
|
-
end
|
59
|
-
|
60
|
-
context "that has a series name in dot-format" do
|
61
|
-
|
62
|
-
let(:file) { "file/path/Goodbye.Hello.Hamburger.2008.S02E01.mp4"}
|
63
|
-
|
64
67
|
it 'creates a series with a name with spaces' do
|
65
68
|
expect(Series).to receive(:new).with(
|
66
|
-
|
69
|
+
'Goodbye Hello Hamburger',
|
67
70
|
anything
|
68
71
|
)
|
69
72
|
subject.series
|
@@ -71,96 +74,34 @@ module Torganiser
|
|
71
74
|
|
72
75
|
end
|
73
76
|
|
74
|
-
context
|
75
|
-
let(:file) { "file/path/Hello.2014.302.hdtv-lol.mp4" }
|
76
|
-
|
77
|
-
it 'extracts season number' do
|
78
|
-
expect(subject.season).to eq 3
|
79
|
-
end
|
77
|
+
context 'that is dash-separated' do
|
80
78
|
|
81
|
-
|
82
|
-
|
79
|
+
let(:match) do
|
80
|
+
{
|
81
|
+
name: "Wiffle's Berry - "
|
82
|
+
}
|
83
83
|
end
|
84
84
|
|
85
|
-
|
86
|
-
let(:file) { "file/path/Hello.2014.4x07.hdtv-lol.mp4" }
|
87
|
-
|
88
|
-
it 'extracts season number' do
|
89
|
-
expect(subject.season).to eq 4
|
90
|
-
end
|
91
|
-
|
92
|
-
it 'extracts episode number' do
|
93
|
-
expect(subject.episode).to eq 7
|
94
|
-
end
|
95
|
-
|
96
|
-
end
|
97
|
-
|
98
|
-
context "surrounded in square brackets" do
|
99
|
-
let(:file) { "file/path/Wootle [3x08] Some title here.avi" }
|
100
|
-
|
101
|
-
it 'extracts season number' do
|
102
|
-
expect(subject.season).to eq 3
|
103
|
-
end
|
104
|
-
|
105
|
-
it 'extracts episode number' do
|
106
|
-
expect(subject.episode).to eq 8
|
107
|
-
end
|
108
|
-
|
109
|
-
end
|
110
|
-
|
111
|
-
end
|
112
|
-
|
113
|
-
context "that is dash-separated" do
|
114
|
-
let(:file) { "Wiffle's Berry - S01E12-the title.avi" }
|
115
|
-
|
116
|
-
it 'extracts series correctly' do
|
85
|
+
it 'creates a series with the correct name' do
|
117
86
|
expect(Series).to receive(:new).with(
|
118
87
|
"Wiffle's Berry", year: nil
|
119
88
|
)
|
120
89
|
subject.series
|
121
90
|
end
|
122
91
|
|
123
|
-
it 'extracts season number' do
|
124
|
-
expect(subject.season).to eq 1
|
125
|
-
end
|
126
|
-
|
127
|
-
it 'extracts episode number' do
|
128
|
-
expect(subject.episode).to eq 12
|
129
|
-
end
|
130
|
-
end
|
131
|
-
|
132
|
-
context "that is a special" do
|
133
|
-
let(:file) { "file/path/Hello.2014.s00.hdtv-lol.mp4" }
|
134
|
-
|
135
|
-
it 'extracts season number as zero' do
|
136
|
-
expect(subject.season).to eq 0
|
137
|
-
end
|
138
|
-
|
139
|
-
it 'extracts episode number as zero' do
|
140
|
-
expect(subject.episode).to eq 0
|
141
|
-
end
|
142
|
-
|
143
|
-
context "with a season specified" do
|
144
|
-
let(:file) { "file/path/Hello.2014.s01.special.hdtv-lol.mp4" }
|
145
|
-
|
146
|
-
it 'extracts season number' do
|
147
|
-
expect(subject.season).to eq 1
|
148
|
-
end
|
149
|
-
|
150
|
-
end
|
151
92
|
end
|
152
93
|
|
153
94
|
end
|
154
95
|
|
155
|
-
context
|
96
|
+
context 'when initialised with a file whose name cannot be parsed' do
|
156
97
|
|
157
|
-
let(:
|
98
|
+
let(:match) { nil }
|
158
99
|
|
159
100
|
it 'blows up in a more helpful manner' do
|
160
|
-
expect{ subject.series }.to raise_error(/Unable to parse #{file}/)
|
101
|
+
expect { subject.series }.to raise_error(/Unable to parse #{file}/)
|
102
|
+
end
|
161
103
|
|
162
104
|
end
|
163
105
|
|
164
106
|
end
|
165
|
-
|
166
107
|
end
|
@@ -1,49 +1,49 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
+
# Main module
|
3
4
|
module Torganiser
|
4
|
-
|
5
5
|
describe FileQuery do
|
6
6
|
|
7
|
-
context
|
7
|
+
context 'when initialised' do
|
8
8
|
|
9
|
-
context
|
9
|
+
context 'with no arguments' do
|
10
10
|
|
11
|
-
it
|
11
|
+
it 'has no directories' do
|
12
12
|
expect(subject.directories).to be_empty
|
13
13
|
end
|
14
14
|
|
15
|
-
it
|
15
|
+
it 'has no media extensions' do
|
16
16
|
expect(subject.extensions).to be_empty
|
17
17
|
end
|
18
18
|
|
19
|
-
it
|
19
|
+
it 'is empty' do
|
20
20
|
expect(subject).to be_empty
|
21
21
|
end
|
22
22
|
|
23
23
|
end
|
24
24
|
|
25
|
-
context
|
26
|
-
subject { FileQuery.new(directories:
|
25
|
+
context 'with a directory' do
|
26
|
+
subject { FileQuery.new(directories: 'woot/waffle') }
|
27
27
|
|
28
|
-
it
|
28
|
+
it 'is not empty' do
|
29
29
|
expect(subject).not_to be_empty
|
30
30
|
end
|
31
31
|
|
32
|
-
it
|
32
|
+
it 'adds that directory to the list of search directories' do
|
33
33
|
expect(subject.directories).to eq ['woot/waffle']
|
34
34
|
end
|
35
35
|
|
36
36
|
end
|
37
37
|
|
38
|
-
context
|
38
|
+
context 'with a directory and a media extension' do
|
39
39
|
|
40
40
|
subject { FileQuery.new(directories: 'mydir', extensions: 'mp4') }
|
41
41
|
|
42
|
-
it
|
42
|
+
it 'adds that directory to the list of search directories' do
|
43
43
|
expect(subject.directories).to eq ['mydir']
|
44
44
|
end
|
45
45
|
|
46
|
-
it
|
46
|
+
it 'adds that extension to the list of media extensions' do
|
47
47
|
expect(subject.extensions).to eq ['mp4']
|
48
48
|
end
|
49
49
|
|
@@ -51,7 +51,7 @@ module Torganiser
|
|
51
51
|
|
52
52
|
end
|
53
53
|
|
54
|
-
describe
|
54
|
+
describe 'search pattern' do
|
55
55
|
|
56
56
|
let(:extensions) { nil }
|
57
57
|
|
@@ -64,49 +64,49 @@ module Torganiser
|
|
64
64
|
)
|
65
65
|
end
|
66
66
|
|
67
|
-
context
|
67
|
+
context 'with one directory' do
|
68
68
|
|
69
|
-
let(:directories) {
|
69
|
+
let(:directories) { 'stuff' }
|
70
70
|
|
71
|
-
it
|
71
|
+
it 'includes only that directory' do
|
72
72
|
expect(subject.pattern).to match(/^stuff\/\*\*/)
|
73
73
|
end
|
74
74
|
|
75
75
|
end
|
76
76
|
|
77
|
-
context
|
77
|
+
context 'with multiple directories' do
|
78
78
|
|
79
|
-
let(:directories) {
|
79
|
+
let(:directories) { %w(one two) }
|
80
80
|
|
81
|
-
it
|
81
|
+
it 'includes all specified directories' do
|
82
82
|
expect(subject.pattern).to match(/{one,two}\/\*\*/)
|
83
83
|
end
|
84
84
|
|
85
85
|
end
|
86
86
|
|
87
|
-
context
|
87
|
+
context 'with no extensions' do
|
88
88
|
|
89
|
-
it
|
89
|
+
it 'matches all files' do
|
90
90
|
expect(subject.pattern).to match(/\*\*\/\*/)
|
91
91
|
end
|
92
92
|
|
93
93
|
end
|
94
94
|
|
95
|
-
context
|
95
|
+
context 'with one extension' do
|
96
96
|
|
97
|
-
let(:extensions) {
|
97
|
+
let(:extensions) { 'mp4' }
|
98
98
|
|
99
|
-
it
|
99
|
+
it 'includes only that extension' do
|
100
100
|
expect(subject.pattern).to match(/\*\*\/\*mp4/)
|
101
101
|
end
|
102
102
|
|
103
103
|
end
|
104
104
|
|
105
|
-
context
|
105
|
+
context 'with multiple extensions' do
|
106
106
|
|
107
|
-
let(:extensions) {
|
107
|
+
let(:extensions) { %w(mp4 mov qt) }
|
108
108
|
|
109
|
-
it
|
109
|
+
it 'includes all specified extensions' do
|
110
110
|
expect(subject.pattern).to match(/\*\*\/\*{mp4,mov,qt}/)
|
111
111
|
end
|
112
112
|
|