moviesort 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/.gitignore +1 -0
- data/Rakefile +12 -0
- data/VERSION +1 -0
- data/bin/moviesort +6 -0
- data/features/sorting_tv_shows.feature +22 -0
- data/features/step_definitions/sorting_tv_shows_steps.rb +28 -0
- data/features/support/env.rb +15 -0
- data/lib/moviesort/command_line.rb +30 -0
- data/lib/moviesort/sorter.rb +18 -0
- data/lib/moviesort/tv_show.rb +53 -0
- data/test/sorter_test.rb +25 -0
- data/test/test_helper.rb +4 -0
- data/test/tv_show_test.rb +50 -0
- metadata +69 -0
data/.gitignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
pkg/
|
data/Rakefile
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
require 'jeweler'
|
|
2
|
+
|
|
3
|
+
Jeweler::Tasks.new do |gemspec|
|
|
4
|
+
gemspec.name = "moviesort"
|
|
5
|
+
gemspec.summary = "Small command-line utility for sorting downloaded TV shows and movies"
|
|
6
|
+
gemspec.email = "luke@lukeredpath.co.uk"
|
|
7
|
+
gemspec.authors = ["Luke Redpath"]
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Jeweler::GemcutterTasks.new
|
|
11
|
+
|
|
12
|
+
task :release => "gemcutter:release"
|
data/VERSION
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
0.1.0
|
data/bin/moviesort
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Feature: Sorting a folder of TV show episodes
|
|
2
|
+
In order to keep my TV shows organised
|
|
3
|
+
As a person trying to keep their media server in order
|
|
4
|
+
I want to be able to sort my downloaded TV episodes into folders
|
|
5
|
+
|
|
6
|
+
Scenario: First episode of a new TV show
|
|
7
|
+
Given a folder "/movies/new" containing the file "The Sopranos - S01E01 - Pilot.avi"
|
|
8
|
+
When I run Moviesort with the options "--source /movies/new --target /movies/tv"
|
|
9
|
+
Then the movie should be moved to "/movies/tv/The Sopranos/Season 1/The.Sopranos.S01E01.avi"
|
|
10
|
+
|
|
11
|
+
Scenario: First episode of a new season of an existing TV show
|
|
12
|
+
Given a folder "/movies/new" containing the file "The Sopranos - S06E01 - Members Only.avi"
|
|
13
|
+
And a folder "/movies/tv/The Sopranos" containing the folder "Season 1"
|
|
14
|
+
When I run Moviesort with the options "--source /movies/new --target /movies/tv"
|
|
15
|
+
Then the movie should be moved to "/movies/tv/The Sopranos/Season 6/The.Sopranos.S06E01.avi"
|
|
16
|
+
And the folder "/movies/tv/The Sopranos/Season 1" should exist
|
|
17
|
+
|
|
18
|
+
Scenario: Another episode of an existing season of an existing TV show
|
|
19
|
+
Given a folder "/movies/new" containing the file "The Sopranos - S01E02 - 46 Long.avi"
|
|
20
|
+
And a folder "/movies/tv/The Sopranos/Season 1" containing the file "The.Sopranos.S01E01.avi"
|
|
21
|
+
When I run Moviesort with the options "--source /movies/new --target /movies/tv"
|
|
22
|
+
Then the movie should be moved to "/movies/tv/The Sopranos/Season 1/The.Sopranos.S01E02.avi"
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'moviesort/command_line'
|
|
2
|
+
|
|
3
|
+
### givens
|
|
4
|
+
|
|
5
|
+
Given /^a folder "([^\"]*)" containing the file "([^\"]*)"$/ do |parent, file|
|
|
6
|
+
FileUtils.mkdir_p(parent)
|
|
7
|
+
FileUtils.touch(File.join(parent, file))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
Given /^a folder "([^\"]*)" containing the folder "([^\"]*)"$/ do |parent, folder|
|
|
11
|
+
FileUtils.mkdir_p(File.join(parent, folder))
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
## whens
|
|
15
|
+
|
|
16
|
+
When /^I run Moviesort with the options "([^\"]*)"$/ do |options|
|
|
17
|
+
Moviesort::CommandLine.parse(options).run
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
## thens
|
|
21
|
+
|
|
22
|
+
Then /^the movie should be moved to "([^\"]*)"$/ do |path|
|
|
23
|
+
assert File.exist?(path)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
Then /^the folder "([^\"]*)" should exist$/ do |dir|
|
|
27
|
+
assert File.directory?(dir)
|
|
28
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
require 'test/unit/assertions'
|
|
2
|
+
require 'fakefs/safe'
|
|
3
|
+
|
|
4
|
+
World(Test::Unit::Assertions)
|
|
5
|
+
|
|
6
|
+
Before do
|
|
7
|
+
FakeFS.activate!
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
After do
|
|
11
|
+
FakeFS::FileSystem.clear
|
|
12
|
+
FakeFS.deactivate!
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[.. .. lib]))
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'trollop'
|
|
2
|
+
require 'moviesort/sorter'
|
|
3
|
+
|
|
4
|
+
module Moviesort
|
|
5
|
+
class CommandLine
|
|
6
|
+
def self.parse(options)
|
|
7
|
+
new(options.split)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def initialize(argv = ARGV)
|
|
11
|
+
@options = Trollop.options(argv) do
|
|
12
|
+
banner <<-EOS.gsub(/^ {8}/, '')
|
|
13
|
+
Moviesort is a script to sort and file away your TV shows and movies
|
|
14
|
+
|
|
15
|
+
Usage: moviesort [options]
|
|
16
|
+
EOS
|
|
17
|
+
|
|
18
|
+
opt :source, "Source directory", :default => '.'
|
|
19
|
+
opt :target, "Target directory", :default => '.'
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def run
|
|
24
|
+
sorter = Sorter::TV.new(@options[:target])
|
|
25
|
+
Dir[File.join(@options[:source], '*.avi')].each do |file|
|
|
26
|
+
sorter.sort_file(file)
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
require 'moviesort/tv_show'
|
|
2
|
+
|
|
3
|
+
module Moviesort
|
|
4
|
+
module Sorter
|
|
5
|
+
class TV
|
|
6
|
+
def initialize(output_dir)
|
|
7
|
+
@output_dir = output_dir
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def sort_file(path)
|
|
11
|
+
tv_show = Moviesort::TVShow.parse(File.basename(path))
|
|
12
|
+
target_path = File.join(@output_dir, tv_show.target_path)
|
|
13
|
+
FileUtils.mkdir_p(target_path)
|
|
14
|
+
FileUtils.mv(path, File.join(target_path, tv_show.target_filename))
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
require 'pathname'
|
|
2
|
+
|
|
3
|
+
module Moviesort
|
|
4
|
+
class TVShow
|
|
5
|
+
attr_reader :show, :season, :episode
|
|
6
|
+
|
|
7
|
+
def initialize(filename, show, season, episode)
|
|
8
|
+
@show = show
|
|
9
|
+
@season = season
|
|
10
|
+
@episode = episode
|
|
11
|
+
@extension = File.extname(filename)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def self.parse(filename)
|
|
15
|
+
parser = Parser.new(filename)
|
|
16
|
+
new(filename, *parser.parse)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def target_path
|
|
20
|
+
Pathname.new(File.join(show, "Season #{season}"))
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def target_filename
|
|
24
|
+
"#{@show.gsub(/\W/, '.')}.S%.2iE%.2i#{@extension}" % [season, episode]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
private
|
|
28
|
+
|
|
29
|
+
class Parser
|
|
30
|
+
def initialize(filename)
|
|
31
|
+
@filename = sanitize(filename)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def parse
|
|
35
|
+
patterns = [
|
|
36
|
+
/(.*)\sS(\d+)E(\d+)/i,
|
|
37
|
+
/(.*)\s(\d{1,2})x(\d+)/,
|
|
38
|
+
/(.*)\s(\d{1})(\d{2})/
|
|
39
|
+
]
|
|
40
|
+
match = patterns.map { |regex| @filename.match(regex) }.compact.first
|
|
41
|
+
extract_data_from_pattern_match(match)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def extract_data_from_pattern_match(match)
|
|
45
|
+
[match[1], *match[2..3].map { |s| s.to_i }]
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def sanitize(filename)
|
|
49
|
+
filename.gsub(/\./, ' ').gsub(/-/, ' ').squeeze(' ')
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
data/test/sorter_test.rb
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'moviesort/sorter'
|
|
3
|
+
|
|
4
|
+
class SorterTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
def setup
|
|
7
|
+
@output_path = "/output"
|
|
8
|
+
@download_path = "/downloads"
|
|
9
|
+
FileUtils.mkpath(@output_path)
|
|
10
|
+
FileUtils.mkpath(@download_path)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
context "TV show sorter" do
|
|
14
|
+
setup do
|
|
15
|
+
@sorter = Moviesort::Sorter::TV.new(@output_path)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
should "move a given TV show to its target path under the root path" do
|
|
19
|
+
FileUtils.touch("/downloads/The Sopranos - S02E04.avi")
|
|
20
|
+
@sorter.sort_file("/downloads/The Sopranos - S02E04.avi")
|
|
21
|
+
assert File.exist?("/output/The Sopranos/Season 2/The.Sopranos.S02E04.avi")
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
end
|
data/test/test_helper.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'test_helper'
|
|
2
|
+
require 'moviesort/tv_show'
|
|
3
|
+
|
|
4
|
+
class TvShowTest < Test::Unit::TestCase
|
|
5
|
+
|
|
6
|
+
context "A TV show" do
|
|
7
|
+
setup do
|
|
8
|
+
@tv_show = Moviesort::TVShow.new("The.Sopranos.102.avi", "The Sopranos", season = 1, episode = 2)
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
should "have a target path based on the show name and season" do
|
|
12
|
+
assert_equal Pathname.new("The Sopranos/Season 1"), @tv_show.target_path
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
should "have a filename based on the show name and season with the original file extension" do
|
|
16
|
+
assert_equal "The.Sopranos.S01E02.avi", @tv_show.target_filename
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
context "When parsing" do
|
|
21
|
+
should "handle file in the format SHOW NAME - SXXEXX.xxx" do
|
|
22
|
+
@tv_show = Moviesort::TVShow.parse("The Sopranos - S01E02.avi")
|
|
23
|
+
assert_equal "The Sopranos", @tv_show.show
|
|
24
|
+
assert_equal 1, @tv_show.season
|
|
25
|
+
assert_equal 2, @tv_show.episode
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
should "handle file in the format SHOW NAME - sXXeXX.xxx" do
|
|
29
|
+
@tv_show = Moviesort::TVShow.parse("The Sopranos - s01e02.avi")
|
|
30
|
+
assert_equal "The Sopranos", @tv_show.show
|
|
31
|
+
assert_equal 1, @tv_show.season
|
|
32
|
+
assert_equal 2, @tv_show.episode
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
should "handle file in the format SHOW NAME - SxE.xxx" do
|
|
36
|
+
@tv_show = Moviesort::TVShow.parse("The Sopranos - 1x02.avi")
|
|
37
|
+
assert_equal "The Sopranos", @tv_show.show
|
|
38
|
+
assert_equal 1, @tv_show.season
|
|
39
|
+
assert_equal 2, @tv_show.episode
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
should "handle file in the format SHOW.NAME.XYY.xxx" do
|
|
43
|
+
@tv_show = Moviesort::TVShow.parse("The.Sopranos.102.avi")
|
|
44
|
+
assert_equal "The Sopranos", @tv_show.show
|
|
45
|
+
assert_equal 1, @tv_show.season
|
|
46
|
+
assert_equal 2, @tv_show.episode
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: moviesort
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Luke Redpath
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
|
|
12
|
+
date: 2009-11-14 00:00:00 +00:00
|
|
13
|
+
default_executable: moviesort
|
|
14
|
+
dependencies: []
|
|
15
|
+
|
|
16
|
+
description:
|
|
17
|
+
email: luke@lukeredpath.co.uk
|
|
18
|
+
executables:
|
|
19
|
+
- moviesort
|
|
20
|
+
extensions: []
|
|
21
|
+
|
|
22
|
+
extra_rdoc_files: []
|
|
23
|
+
|
|
24
|
+
files:
|
|
25
|
+
- .gitignore
|
|
26
|
+
- Rakefile
|
|
27
|
+
- VERSION
|
|
28
|
+
- bin/moviesort
|
|
29
|
+
- features/sorting_tv_shows.feature
|
|
30
|
+
- features/step_definitions/sorting_tv_shows_steps.rb
|
|
31
|
+
- features/support/env.rb
|
|
32
|
+
- lib/moviesort/command_line.rb
|
|
33
|
+
- lib/moviesort/sorter.rb
|
|
34
|
+
- lib/moviesort/tv_show.rb
|
|
35
|
+
- test/sorter_test.rb
|
|
36
|
+
- test/test_helper.rb
|
|
37
|
+
- test/tv_show_test.rb
|
|
38
|
+
has_rdoc: true
|
|
39
|
+
homepage:
|
|
40
|
+
licenses: []
|
|
41
|
+
|
|
42
|
+
post_install_message:
|
|
43
|
+
rdoc_options:
|
|
44
|
+
- --charset=UTF-8
|
|
45
|
+
require_paths:
|
|
46
|
+
- lib
|
|
47
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
48
|
+
requirements:
|
|
49
|
+
- - ">="
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: "0"
|
|
52
|
+
version:
|
|
53
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
54
|
+
requirements:
|
|
55
|
+
- - ">="
|
|
56
|
+
- !ruby/object:Gem::Version
|
|
57
|
+
version: "0"
|
|
58
|
+
version:
|
|
59
|
+
requirements: []
|
|
60
|
+
|
|
61
|
+
rubyforge_project:
|
|
62
|
+
rubygems_version: 1.3.5
|
|
63
|
+
signing_key:
|
|
64
|
+
specification_version: 3
|
|
65
|
+
summary: Small command-line utility for sorting downloaded TV shows and movies
|
|
66
|
+
test_files:
|
|
67
|
+
- test/sorter_test.rb
|
|
68
|
+
- test/test_helper.rb
|
|
69
|
+
- test/tv_show_test.rb
|