serienmover 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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