serienmover 0.0.1 → 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.
data/Rakefile CHANGED
@@ -1,2 +1,10 @@
1
1
  #!/usr/bin/env rake
2
- require "bundler/gem_tasks"
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs = ["lib"]
7
+ t.warning = false
8
+ t.verbose = true
9
+ t.test_files = FileList['spec/*_spec.rb']
10
+ end
data/bin/serienmover ADDED
@@ -0,0 +1,207 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- ruby -*-
3
+ # encoding: UTF-8
4
+
5
+ $LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib')
6
+
7
+ require 'serienmover'
8
+ require 'serienrenamer'
9
+ require 'fileutils'
10
+ require 'hashconfig'
11
+ require 'optparse'
12
+ require 'highline/import'
13
+ require "highline/system_extensions"
14
+ include HighLine::SystemExtensions
15
+
16
+ # create program configuration dirs/files
17
+ CONFIG_DIR = File.join( File.expand_path("~"), ".serienmover" )
18
+ CONFIG_FILE = File.join( CONFIG_DIR, "config.yml" )
19
+ FileUtils.mkdir(CONFIG_DIR) unless File.directory?(CONFIG_DIR)
20
+
21
+ ###
22
+ # configuration
23
+ STANDARD_CONFIG = {
24
+ :default_directory => File.join(File.expand_path("~"), "Downloads"),
25
+ :series_directories => [],
26
+ :read_episode_info => false,
27
+ :store_path => '',
28
+ :byte_count_for_md5 => 2048,
29
+ :collective_directory => '',
30
+ }
31
+
32
+ config = STANDARD_CONFIG.merge_with_serialized(CONFIG_FILE)
33
+
34
+ ###
35
+ # option definition and handling
36
+ options = {}
37
+ OptionParser.new do |opts|
38
+ opts.banner = "Usage: #{File.basename($PROGRAM_NAME)} [DIR]"
39
+
40
+ opts.separator("")
41
+ opts.separator("Tool that moves series episodes into a specific")
42
+ opts.separator("directory structure.")
43
+ opts.separator("")
44
+ opts.separator(" Options:")
45
+
46
+ opts.on( "-s", "--seriesdir=DIR", String,
47
+ "Directory that contains series data (multiple allowed)") do |dirs|
48
+ dirs = [ dirs ] if dirs.is_a? String
49
+
50
+ dirs.each do |d|
51
+ if File.directory?(d)
52
+ config[:series_directories] << d
53
+ end
54
+ end
55
+ end
56
+
57
+ opts.on( "-i", "--ignore-seriesinfo",
58
+ "do not use the information from the infostore") do |opt|
59
+ config[:read_episode_info] = false
60
+ end
61
+
62
+ opts.on( "-v", "--version",
63
+ "Outputs the version number.") do |opt|
64
+ puts Serienmover::VERSION
65
+ exit
66
+ end
67
+
68
+ opts.separator("")
69
+ opts.separator(" Arguments:")
70
+ opts.separator(" DIR The path that includes the episodes")
71
+ opts.separator(" defaults to ~/Downloads")
72
+ opts.separator("")
73
+
74
+ end.parse!
75
+
76
+ ###
77
+ # change into DIR
78
+ episode_directory = ARGV.pop || config[:default_directory]
79
+
80
+ fail "'#{episode_directory}' does not exist or is not a directory" unless
81
+ Dir.exists?(episode_directory)
82
+
83
+ Dir.chdir(episode_directory)
84
+
85
+ ###
86
+ # instantiate the series_store
87
+ store = Serienmover::SeriesStore.new(config[:series_directories])
88
+
89
+ ###
90
+ # instantiate information store
91
+ info_store = Serienrenamer::InformationStore.new(
92
+ config[:store_path], config[:byte_count_for_md5])
93
+
94
+ ###
95
+ # iterate through all episode files
96
+ episode_actions = []
97
+
98
+ Dir.new('.').to_a.sort.each do |file|
99
+
100
+ next if file.match(/^\./)
101
+ next unless File.file? file
102
+ next unless Serienrenamer::Episode.determine_video_file(file)
103
+
104
+ p file
105
+
106
+ episode = Serienrenamer::Episode.new(file)
107
+
108
+ # get seriesname from the informationstore which is used by
109
+ # serienrenamer to store the seriesname when it renames files
110
+ md5 = episode.md5sum(config[:byte_count_for_md5])
111
+ series = info_store.episode_hash[md5]
112
+
113
+ options = {}
114
+ if config[:read_episode_info] && series && series.match(/\w+/)
115
+ options[:series] = series
116
+ end
117
+
118
+ targets = store.find_suitable_target(episode, options)
119
+ selected_target = nil
120
+
121
+ ###
122
+ # process the targets
123
+ case targets.size
124
+ when 0
125
+ puts "No suitable target found\n"
126
+ next
127
+ when 1
128
+ selected_target = targets[0]
129
+ else
130
+
131
+ begin
132
+ puts "Available targets:"
133
+ choose do |menu|
134
+ menu.prompt = "Choose the right target: "
135
+
136
+ targets.each do |t|
137
+ menu.choice t.series do lambda { selected_target = t }.call end
138
+ end
139
+ end
140
+ rescue Interrupt
141
+ puts ""
142
+ end
143
+
144
+ end
145
+
146
+ if selected_target
147
+ puts ">> '%s'" % selected_target
148
+ episode.target = selected_target
149
+
150
+ ###
151
+ # ask for the action (copy/move)
152
+ print "What should be done ( [c]opy (*) , [m]ove ): "
153
+ char = get_character
154
+ print char.chr unless char.chr.match(/\r/)
155
+
156
+ unless char.chr.match(/[kcmv\r]/i)
157
+ puts "\nwill be skipped ...\n\n"
158
+ next
159
+ end
160
+
161
+ if char.chr.match(/[kc\r]/i)
162
+ episode.set_action(copy: true)
163
+ print " ... copy"
164
+ else
165
+ episode.set_action(move: true)
166
+ print " ... move"
167
+ end
168
+
169
+ ###
170
+ # save the episode and set the target as used
171
+ store.set_target_to_used(episode, selected_target)
172
+
173
+ episode_actions << episode
174
+ end
175
+
176
+ puts "\n\n"
177
+ end
178
+
179
+ exit if episode_actions.empty?
180
+
181
+ ####
182
+ # Process the actions on the episodes
183
+ print "Start processing the episodes ? [yJ]"
184
+ char = get_character
185
+ print char.chr
186
+
187
+ unless char.chr.match(/[jy\r]/i)
188
+ puts "\nwill exit ...\n\n"
189
+ exit
190
+ end
191
+
192
+ puts "\nEpisodes will be processed now"
193
+ episode_actions.each do |episode|
194
+ puts "%s '%s' to '%s'" % [episode.action.capitalize, episode, episode.target]
195
+
196
+ episode.process_action
197
+
198
+ # move copied file into a collective_directory if needed
199
+ if config[:collective_directory] &&
200
+ File.directory?(config[:collective_directory]) &&
201
+ episode.action.match(/copy/i)
202
+
203
+ remote_file = File.join(config[:collective_directory],
204
+ File.basename(episode.episodepath))
205
+ FileUtils.mv(episode.episodepath, remote_file)
206
+ end
207
+ end
@@ -0,0 +1,39 @@
1
+ require 'serienrenamer'
2
+
3
+ class ::Serienrenamer::Episode
4
+ attr_accessor :target, :action
5
+
6
+ # Public: Sets the action that should be processed on this episode
7
+ #
8
+ # options - generale options (default: {copy: false, move: false})
9
+ # :copy - if true than the file will be copied (higher priority)
10
+ # :move - if true than the file wille be moved
11
+ def set_action(options = {})
12
+ opt = {move: false, copy: false}.merge(options)
13
+
14
+ if opt[:copy]
15
+ @action = 'copy'
16
+ elsif opt[:move]
17
+ @action = 'move'
18
+ end
19
+ end
20
+
21
+ # Public: Move/Copy the file to the target directory
22
+ def process_action
23
+ raise ArgumentError, "target needed" unless self.target
24
+ raise ArgumentError, "action needed" unless
25
+ self.action.match(/(move|copy)/i)
26
+
27
+ FileUtils.mkdir_p(self.target.targetdir) unless
28
+ File.directory?(self.target.targetdir)
29
+
30
+ remote_file = File.join(self.target.targetdir,
31
+ File.basename(self.episodepath))
32
+
33
+ if self.action.match(/copy/i)
34
+ FileUtils.cp(self.episodepath, self.target.targetdir)
35
+ elsif self.action.match(/move/i)
36
+ FileUtils.mv(self.episodepath, remote_file)
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,239 @@
1
+ require 'find'
2
+ require 'serienrenamer'
3
+
4
+ module Serienmover
5
+
6
+ # Public: holds information from the series directories and is able to
7
+ # search for possible targets for an supplied episode
8
+ class SeriesStore
9
+
10
+ attr_reader :series_data, :series_dirs
11
+ alias_method :series, :series_data
12
+
13
+ # Public: Initialize a SeriesStore
14
+ #
15
+ # series_directories - array of Strings that contains the
16
+ # series_directories
17
+ def initialize(series_directories)
18
+
19
+ @series_data = {}
20
+ @series_dirs = []
21
+
22
+ series_directories.each do |series_directory|
23
+ next unless File.directory? series_directory
24
+ @series_dirs << series_directory
25
+
26
+ Dir.new(series_directory).each do |series|
27
+ next if series.match(/^\.*$/)
28
+
29
+ seriesdir = File.join(series_directory, series)
30
+
31
+ episodes = {}
32
+ Find.find(seriesdir) do |file|
33
+ next unless File.file?(file)
34
+
35
+ infos = Serienrenamer::Episode.extract_episode_information(
36
+ File.basename(file))
37
+
38
+ if infos
39
+ index = SeriesStore.build_index(infos[:season], infos[:episode])
40
+ episodes[index] = file
41
+ end
42
+ end
43
+
44
+ series_data[series] = episodes
45
+ end
46
+ end
47
+ end
48
+
49
+ # Public: finds a suitable target in the store
50
+ #
51
+ # episode - Serienrenamer::Episode instance
52
+ # options - optional arguments (default: {):}
53
+ # :series - if this option is set than it returns only targets
54
+ # where the series matches the supplied arguments
55
+ #
56
+ # Returns an array of EpisodeTargets
57
+ def find_suitable_target(episode, options = {})
58
+ raise ArgumentError, "Serienrenamer::Episode needed" unless
59
+ episode.is_a? Serienrenamer::Episode
60
+
61
+ targets = []
62
+ current = SeriesStore.build_index(episode.season, episode.episode)
63
+ before = SeriesStore.build_index(episode.season, episode.episode-1)
64
+
65
+ series_data.each do |series, episodes|
66
+
67
+ # restrict series to the supplied seriesname and skip otherwise
68
+ if options.include?(:series) && options[:series].match(/\w+/)
69
+ next unless SeriesStore.does_match_series?(series, options[:series])
70
+ end
71
+
72
+ target_directory = nil
73
+
74
+ if episode.episode <= 1
75
+ # find possible targets for a new season
76
+ current_season = episodes.select do |e|
77
+ ! e.match(/^#{episode.season.to_s}_/).nil?
78
+ end
79
+ before_season = episodes.select do |e|
80
+ ! e.match(/^#{(episode.season-1).to_s}_/).nil?
81
+ end
82
+
83
+ if current_season.empty? && ! before_season.empty?
84
+ season_before_dir = File.dirname(before_season.values[0])
85
+
86
+ dir_part = File.basename(season_before_dir)
87
+ dir_part.gsub!(/#{(episode.season-1).to_s }/, episode.season.to_s)
88
+
89
+ # let 'Staffel 010' be 'Staffel 10' as a side effect of
90
+ # the regex before
91
+ dir_part.gsub!(/010/, '10')
92
+
93
+ target_directory =
94
+ File.join( File.dirname(season_before_dir), dir_part )
95
+ end
96
+
97
+ elsif episodes.include?(before) && ! episodes.include?(current)
98
+ # look for the episode before the current which
99
+ # should be not existant and which hash no seasons afterwards
100
+
101
+ # check for seasons that follows the current
102
+ episodes_of_following_seasons =
103
+ episodes.select { |e| e.match(/#{(episode.season + 1).to_s}_/) }
104
+
105
+ if episodes_of_following_seasons.empty?
106
+ target_directory = File.dirname(episodes[before])
107
+ end
108
+
109
+ elsif ! episodes.include?(before) && ! episodes.include?(current)
110
+ # look for episodes that are released out of order so that
111
+ # S05E10 is released before S05E07, for which we are searching
112
+ # for a target
113
+ episode.episode.upto(50).each do |e|
114
+ index = SeriesStore.build_index(episode.season, e)
115
+ if episodes.include?(index)
116
+ target_directory = File.dirname(episodes[index])
117
+ end
118
+ end
119
+ end
120
+
121
+ # build up target instance
122
+ if target_directory
123
+ target = EpisodeTarget.new(series, target_directory)
124
+ targets << target
125
+ end
126
+ end
127
+
128
+ targets
129
+ end
130
+
131
+
132
+ # Public: set the supplied target as used so that it is not
133
+ # available for future searches
134
+ #
135
+ # episode - Serienrenamer::Episode instcnace
136
+ # target - EpisodeTarget instance
137
+ #
138
+ # Returns nothing
139
+ def set_target_to_used(episode, target)
140
+ raise ArgumentError, "Serienrenamer::Episode needed" unless
141
+ episode.is_a? Serienrenamer::Episode
142
+ raise ArgumentError, "Serienmover::EpisodeTarget needed" unless
143
+ target.is_a? Serienmover::EpisodeTarget
144
+
145
+ index = SeriesStore.build_index(episode.season, episode.episode)
146
+ @series_data[target.series][index] =
147
+ File.join(target.targetdir, episode.to_s)
148
+ end
149
+
150
+ class << self
151
+
152
+ # Public: tries to match the suppplied seriesname pattern
153
+ # agains the series
154
+ #
155
+ # seriesname - the seriesname that comes from the series_directory
156
+ # series_pattern - the series_name that has to be checked
157
+ # agains the seriesname
158
+ #
159
+ # Returns true if it matches otherwise false
160
+ def does_match_series?(seriesname, series_pattern)
161
+
162
+ if seriesname.match(/#{series_pattern}/i)
163
+ # if pattern matches the series directly
164
+ return true
165
+
166
+ else
167
+ # start with a pattern that includes all words from
168
+ # series_pattern and if this does not match, it cuts
169
+ # off the first word and tries to match again
170
+ #
171
+ # if the pattern contains one word and if this
172
+ # still not match, the last word is splitted
173
+ # characterwise, so that:
174
+ # crmi ==> Criminal Minds
175
+ name_words = series_pattern.split(/ /)
176
+ word_splitted = false
177
+
178
+ while ! name_words.empty?
179
+
180
+ pattern = name_words.join('.*')
181
+ return true if seriesname.match(/#{pattern}/i)
182
+
183
+ # split characterwise if last word does not match
184
+ if name_words.length == 1 && ! word_splitted
185
+ name_words = pattern.split(//)
186
+ word_splitted = true
187
+ next
188
+ end
189
+
190
+ # if last word was splitted and does not match than break
191
+ # and return empty resultset
192
+ break if word_splitted
193
+
194
+ name_words.delete_at(0)
195
+ end
196
+ end
197
+
198
+ false
199
+ end
200
+
201
+ # Public: builds up an index 'd_d' where the d's are the supplied args
202
+ #
203
+ # season - Number that is the first part of the 'd_d'
204
+ # season - Number that is the second part of 'd_d'
205
+ #
206
+ # Examples
207
+ #
208
+ # build_index('09', '12')
209
+ # # => '9_12'
210
+ #
211
+ # Returns the index
212
+ def build_index(season, episode)
213
+ return '%d_%d' % [ season.to_i, episode.to_i ]
214
+ end
215
+
216
+ end
217
+ end
218
+
219
+ # Public: holds information about suitable target directories
220
+ #
221
+ # Examples
222
+ #
223
+ # target = EpisodeTarget.new('Chuck', '/path/to/series')
224
+ # target.series
225
+ # # => "Chuck"
226
+ #
227
+ class EpisodeTarget
228
+ attr_accessor :series, :targetdir
229
+
230
+ def initialize(seriesname, targetdir)
231
+ @series = seriesname
232
+ @targetdir = targetdir
233
+ end
234
+
235
+ def to_s
236
+ @series
237
+ end
238
+ end
239
+ end
@@ -1,3 +1,3 @@
1
1
  module Serienmover
2
- VERSION = "0.0.1"
2
+ VERSION = "0.1.0"
3
3
  end
data/lib/serienmover.rb CHANGED
@@ -1,5 +1,8 @@
1
- require "serienmover/version"
1
+ require "serienmover/series_store.rb"
2
+ require "serienmover/episode.rb"
3
+ require "serienmover/version.rb"
4
+ require "serienrenamer"
2
5
 
3
6
  module Serienmover
4
- # Your code goes here...
7
+
5
8
  end
@@ -0,0 +1,51 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
2
+ require 'serienrenamer'
3
+
4
+ describe Serienrenamer::Episode do
5
+
6
+ before(:each) do
7
+ @episodes = TestData.create
8
+
9
+ @store = Serienmover::SeriesStore.new([ TestHelper::SERIES_STORAGE_DIR ])
10
+ end
11
+
12
+ after(:each) do
13
+ TestData.clean
14
+ end
15
+
16
+ it "should copy the episode properly" do
17
+ episode = @episodes[:crmi]
18
+
19
+ results = @store.find_suitable_target(episode, series: "Criminal Minds")
20
+ results.size.should be == 1
21
+ results[0].series.should eq "Criminal Minds"
22
+
23
+ target = results[0]
24
+ episode.target = target
25
+ episode.set_action(copy: true)
26
+ episode.process_action
27
+
28
+ File.file?(episode.episodepath).should be_true
29
+
30
+ remote_path = File.join(target.targetdir, File.basename(episode.episodepath))
31
+ File.file?(remote_path).should be_true
32
+ end
33
+
34
+ it "should move the episode properly" do
35
+ episode = @episodes[:crmi]
36
+
37
+ results = @store.find_suitable_target(episode, series: "Criminal Minds")
38
+ results.size.should be == 1
39
+ results[0].series.should eq "Criminal Minds"
40
+
41
+ target = results[0]
42
+ episode.target = target
43
+ episode.set_action(move: true)
44
+ episode.process_action
45
+
46
+ File.file?(episode.episodepath).should_not be_true
47
+
48
+ remote_path = File.join(target.targetdir, File.basename(episode.episodepath))
49
+ File.file?(remote_path).should be_true
50
+ end
51
+ end
@@ -1,17 +1,23 @@
1
- require 'spec_helper'
2
-
3
- require File.dirname(__FILE__) + '/../lib/serienmover'
1
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
2
+ require 'serienrenamer'
4
3
 
5
4
  describe Serienmover do
6
5
 
7
- before(:all) do
6
+
7
+ before(:each) do
8
+ @episodes = TestData.create
9
+ end
10
+
11
+ after(:each) do
12
+ TestData.clean
8
13
  end
9
14
 
10
- after(:all) do
15
+ it "should build up Episode instances successfully" do
16
+ @episodes[:tbbt].should_not be_nil
11
17
  end
12
18
 
13
- it "should be true" do
14
- true.should be_true
19
+ it "should extract the right information from the files" do
20
+ @episodes[:crmi].episode.should eq 4
15
21
  end
16
22
  end
17
23
 
@@ -0,0 +1,110 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
2
+
3
+ describe Serienmover::SeriesStore do
4
+
5
+ before(:each) do
6
+ @episodes = TestData.create
7
+
8
+ @store = Serienmover::SeriesStore.new([ TestHelper::SERIES_STORAGE_DIR ])
9
+ end
10
+
11
+ after(:each) do
12
+ TestData.clean
13
+ end
14
+
15
+ it "should be possible to instantiate the store" do
16
+ @store.should_not be_nil
17
+ end
18
+
19
+ it "should process the series directories" do
20
+ @store.series_dirs.size > 0
21
+ end
22
+
23
+ it "should build up a hash for series with all episodes" do
24
+ @store.series["Chuck"].size > 0
25
+ end
26
+
27
+ it "should return the right target for 'Chuck'" do
28
+ episode = @episodes[:chuck]
29
+ results = @store.find_suitable_target(episode)
30
+ results.select { |t| t.series == "Chuck" }.size.should be >= 1
31
+ end
32
+
33
+ it "should return the right target for 'Criminal Minds'" do
34
+ episode = @episodes[:crmi]
35
+ results = @store.find_suitable_target(episode)
36
+ results.select { |t| t.series == "Criminal Minds" }.size.should be >= 1
37
+ end
38
+
39
+ it "should only return the Criminal Minds target if a name is supplied" do
40
+ episode = @episodes[:crmi]
41
+
42
+ results = @store.find_suitable_target(episode, series: "Criminal Minds")
43
+ results.size.should be == 1
44
+ results[0].series.should eq "Criminal Minds"
45
+
46
+ results = @store.find_suitable_target(episode, series: "crmi")
47
+ results.size.should be == 1
48
+ results[0].series.should eq "Criminal Minds"
49
+
50
+ results = @store.find_suitable_target(episode, series: "sof criminal minds")
51
+ results.size.should be == 1
52
+ results[0].series.should eq "Criminal Minds"
53
+ end
54
+
55
+ it "should return the right target for 'The Big Bang Theory'" do
56
+ episode = @episodes[:tbbt]
57
+ results = @store.find_suitable_target(episode)
58
+ results.select { |t| t.series == "The Big Bang Theory" }.size.should be >= 1
59
+ end
60
+
61
+
62
+ it "should only return the Criminal Minds target if a name is supplied" do
63
+ episode = @episodes[:tbbt]
64
+
65
+ results = @store.find_suitable_target(episode, series: "sof tbbt")
66
+ results.size.should be == 1
67
+ results[0].series.should eq "The Big Bang Theory"
68
+ end
69
+
70
+ it "should return the right target for 'Dr House' (first epi of season)" do
71
+ episode = @episodes[:drhou]
72
+ results = @store.find_suitable_target(episode)
73
+ results.select { |t| t.series == "Dr House" }.size.should be >= 1
74
+ end
75
+
76
+ it "should build up the right path for an episode of a new season" do
77
+ episode = @episodes[:drhou]
78
+ results = @store.find_suitable_target(episode, series: 'Dr House')
79
+ results.size.should == 1
80
+
81
+ results[0].targetdir.should match(/House.*Staffel.05/i)
82
+ end
83
+
84
+ it "should build up the right path for an episode S10E01" do
85
+ episode = @episodes[:spook]
86
+ results = @store.find_suitable_target(episode, series: 'Spooks')
87
+ results.size.should == 1
88
+
89
+ results[0].targetdir.should match(/Spooks.*Staffel.10/i)
90
+ end
91
+
92
+ it "should exclude season of series where more season exists" do
93
+ episode = @episodes[:numbe]
94
+ results = @store.find_suitable_target(episode)
95
+ results.size.should == 1
96
+ end
97
+
98
+ it "should set target to used and skipped this target in future searches" do
99
+ episode = @episodes[:crmi]
100
+ results = @store.find_suitable_target(episode)
101
+ results.size.should be == 2
102
+
103
+ crmi_target = results.select { |t| t.series == "Criminal Minds" }[0]
104
+ @store.set_target_to_used(episode, crmi_target)
105
+
106
+ episode = @episodes[:seap]
107
+ less_results = @store.find_suitable_target(episode)
108
+ less_results.size.should be 1
109
+ end
110
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,5 +1,92 @@
1
1
  require 'bundler/setup'
2
2
  require 'fileutils'
3
+ require 'rspec'
4
+
5
+ require File.dirname(__FILE__) + '/../lib/serienmover'
6
+ require File.join(File.dirname(__FILE__), "spec_testdata.rb")
3
7
 
4
8
  RSpec.configure do |config|
5
9
  end
10
+
11
+ class TestHelper
12
+
13
+ TESTFILE_DIRECTORY = File.join(File.dirname(__FILE__), 'testfiles')
14
+ SERIES_STORAGE_DIR = File.join(TESTFILE_DIRECTORY, 'series')
15
+
16
+ class << self
17
+
18
+ # create the supplied Files in the testfiles directory
19
+ def create_test_files(files)
20
+ _create_directories
21
+
22
+ files.each do |f|
23
+ FileUtils.touch File.join(TESTFILE_DIRECTORY, f)
24
+ end
25
+ end
26
+
27
+ # this method creates a directory structure for a given
28
+ # series with the opportunity to exclude some episodes
29
+ def create_series(seriesname, options={})
30
+ default = { :from => '1_0', :to => '1_0',
31
+ :max => 30, :exclude => [] }
32
+ options = default.merge(options)
33
+
34
+ series_dir = File.join(SERIES_STORAGE_DIR, seriesname)
35
+ _create_dir(series_dir)
36
+
37
+ from = _split_entry(options[:from])
38
+ to = _split_entry(options[:to])
39
+
40
+ for season in from[0]..to[0]
41
+
42
+ season_dir = File.join(series_dir, "Staffel %02d" % season)
43
+ _create_dir(season_dir)
44
+
45
+ episodes = (season == to[0]) ? to[1] : options[:max]
46
+
47
+ for episode in 1..episodes.to_i
48
+
49
+ # check for excludes
50
+ definition = "%d_%d" % [ season, episode ]
51
+ next if options[:exclude].include? definition
52
+
53
+ # build and create file
54
+ file = "S%02dE%02d - Epi%02d.avi" % [ season, episode,episode ]
55
+ episode_file = File.join(season_dir, file)
56
+ _create_file(episode_file)
57
+ end
58
+ end
59
+
60
+
61
+ end
62
+
63
+ # returns the absolute path to the given file
64
+ def path(element)
65
+ File.absolute_path(File.join(TESTFILE_DIRECTORY, element))
66
+ end
67
+
68
+ # remove testfile directory
69
+ def clean
70
+ if File.directory?(TESTFILE_DIRECTORY)
71
+ FileUtils.remove_dir(TESTFILE_DIRECTORY)
72
+ end
73
+ end
74
+
75
+ def _split_entry(definition)
76
+ definition.split(/_/)
77
+ end
78
+
79
+ def _create_directories
80
+ _create_dir TESTFILE_DIRECTORY
81
+ _create_dir SERIES_STORAGE_DIR
82
+ end
83
+
84
+ def _create_dir(dir)
85
+ FileUtils.mkdir(dir) unless File.directory?(dir)
86
+ end
87
+
88
+ def _create_file(file)
89
+ FileUtils.touch(file) unless File.file?(file)
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,62 @@
1
+ require File.join(File.dirname(__FILE__), "spec_helper.rb")
2
+
3
+ class TestData
4
+
5
+ EPISODES = {
6
+ :chuck => { :filename => "S04E11 - Pilot.avi",
7
+ :series => "Chuck",
8
+ :data => { :from => '1_1', :to => '4_10' }
9
+ },
10
+ :tbbt => { :filename => "S05E06 - Pilot.avi",
11
+ :series => "The Big Bang Theory",
12
+ :data => { :from => '1_1', :to => '5_9',
13
+ :exclude => ['5_5', '5_6', '5_7', '5_8', ] }
14
+ },
15
+ :crmi => { :filename => "S01E04 - Pilot.avi",
16
+ :series => "Criminal Minds",
17
+ :data => { :from => '1_1', :to => '1_3' }
18
+ },
19
+ :seap => { :filename => "S01E04 - Pilot.avi",
20
+ :series => "Sea Patrol",
21
+ :data => { :from => '1_1', :to => '1_3' }
22
+ },
23
+ :drhou => { :filename => "S05E01 - First Episode.avi",
24
+ :series => "Dr House",
25
+ :data => { :from => '1_1', :to => '4_20' }
26
+ },
27
+ :spook => { :filename => "S10E01 - First Episode.avi",
28
+ :series => "Spooks",
29
+ :data => { :from => '1_1', :to => '9_20' }
30
+ },
31
+ :numbe => { :filename => "S04E31 - High Episode.avi",
32
+ :series => "Numb3rs",
33
+ :data => { :from => '1_1', :to => '4_30' }
34
+ },
35
+ }
36
+
37
+ # create test data
38
+ def self.create
39
+ episodes = Hash.new
40
+
41
+ EPISODES.each do |key,value|
42
+ TestHelper.create_test_files([ value[:filename] ])
43
+ TestHelper.create_series(value[:series], value[:data])
44
+
45
+ path = TestHelper.path(value[:filename])
46
+ episode = Serienrenamer::Episode.new(path)
47
+
48
+ episodes[key] = episode
49
+ end
50
+ return episodes
51
+ end
52
+
53
+ # remove files
54
+ def self.clean
55
+ TestHelper.clean
56
+ end
57
+
58
+ def self.get(key)
59
+ EPISODES[key]
60
+ end
61
+
62
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serienmover
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.1.0
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-03-23 00:00:00.000000000 Z
12
+ date: 2012-04-08 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: serienrenamer
@@ -30,7 +30,8 @@ dependencies:
30
30
  description: Tool that moves your episodes into a specific directory structure
31
31
  email:
32
32
  - philipp@i77i.de
33
- executables: []
33
+ executables:
34
+ - serienmover
34
35
  extensions: []
35
36
  extra_rdoc_files: []
36
37
  files:
@@ -40,11 +41,17 @@ files:
40
41
  - LICENSE
41
42
  - README.md
42
43
  - Rakefile
44
+ - bin/serienmover
43
45
  - lib/serienmover.rb
46
+ - lib/serienmover/episode.rb
47
+ - lib/serienmover/series_store.rb
44
48
  - lib/serienmover/version.rb
45
49
  - serienmover.gemspec
50
+ - spec/episode_spec.rb
46
51
  - spec/serienmover_spec.rb
52
+ - spec/series_store_spec.rb
47
53
  - spec/spec_helper.rb
54
+ - spec/spec_testdata.rb
48
55
  homepage: http://github.com/pboehm/serienmover
49
56
  licenses: []
50
57
  post_install_message:
@@ -70,5 +77,8 @@ signing_key:
70
77
  specification_version: 3
71
78
  summary: Tool that moves your episodes into a specific directory structure
72
79
  test_files:
80
+ - spec/episode_spec.rb
73
81
  - spec/serienmover_spec.rb
82
+ - spec/series_store_spec.rb
74
83
  - spec/spec_helper.rb
84
+ - spec/spec_testdata.rb