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 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,6 @@
1
+ #!/usr/bin/env ruby
2
+ lib_path = File.expand_path(File.join(File.dirname(__FILE__), *%w[.. lib]))
3
+ $LOAD_PATH.unshift(lib_path) unless $LOAD_PATH.include?(lib_path)
4
+
5
+ require 'moviesort/command_line'
6
+ Moviesort::CommandLine.new.run
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), *%w[.. lib]))
2
+ require 'test/unit'
3
+ require 'shoulda'
4
+ require 'fakefs'
@@ -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