Delete a folder when a file is moved (use carefully, not fully tested yet) and adds some unittests to the project.
This commit is contained in:
parent
7ac88bb128
commit
c7e1f822d4
|
@ -0,0 +1,9 @@
|
|||
#python specific
|
||||
*.pyc
|
||||
|
||||
## generic files to ignore
|
||||
*~
|
||||
*.lock
|
||||
*.DS_Store
|
||||
*.swp
|
||||
*.out
|
|
@ -0,0 +1,2 @@
|
|||
include *.txt
|
||||
recursive-include docs *.txt
|
|
@ -0,0 +1,14 @@
|
|||
# -*- coding: UTF8 -*-
|
||||
# Configuration file
|
||||
|
||||
### If a folder only contains these types of files, we can delete it.
|
||||
useless_files_extensions = ('srr', 'nfo', 'sfv')
|
||||
|
||||
### Consider only files with these extensions
|
||||
shows_extensions = ('avi', 'mkv')
|
||||
|
||||
### Dictionary for special filenames
|
||||
shows_dict = {
|
||||
'white collar' : ['wc'],
|
||||
'the big bang theory' : ['tbbt']
|
||||
}
|
|
@ -0,0 +1,132 @@
|
|||
#encoding: utf-8
|
||||
|
||||
import os
|
||||
import re
|
||||
import shutil
|
||||
import filecmp
|
||||
import config
|
||||
|
||||
class Folder(object):
|
||||
""" Directory show instanciation, relative to a path on the disk
|
||||
ie. Show name
|
||||
- Season 1
|
||||
- Season 2
|
||||
- ...
|
||||
"""
|
||||
|
||||
directory = None
|
||||
name = None
|
||||
|
||||
def __init__(self, path):
|
||||
self.directory = path;
|
||||
self.name = os.path.basename(self.directory)
|
||||
|
||||
def __str__(self):
|
||||
return self.name + ' [' + self.directory + ']'
|
||||
|
||||
class Show(object):
|
||||
""" Represents a show file; ie. a file associated to its fullname """
|
||||
|
||||
path = None
|
||||
name = None
|
||||
|
||||
def __init__(self, fullname, filename):
|
||||
self.path = fullname
|
||||
self.name = filename
|
||||
|
||||
def __str__(self):
|
||||
return self.name
|
||||
|
||||
|
||||
class PigeonHole(object):
|
||||
""" Takes all the media files in a (download) folder and sort
|
||||
them into the corresponding folder, based on the found file name
|
||||
"""
|
||||
|
||||
directories = None
|
||||
series = None
|
||||
matches = None
|
||||
|
||||
downloadDir = ""
|
||||
rootShows = ""
|
||||
|
||||
def __init__(self, root, downloaddir):
|
||||
self.downloadDir = downloaddir
|
||||
self.rootShows = root
|
||||
self.directories = os.listdir(self.rootShows)
|
||||
self.series = list()
|
||||
|
||||
def walk(self):
|
||||
""" Walks through the downloaded folders and yields .avi and .mkv files """
|
||||
for root, dirs, files in os.walk(self.downloadDir):
|
||||
for filename in files:
|
||||
if filename.endswith(config.shows_extensions):
|
||||
yield Show(os.path.join(root, filename), filename)
|
||||
|
||||
def walk(self, foldername, extensions):
|
||||
for root, dirs, files in os.walk(foldername):
|
||||
for filename in files:
|
||||
if not filename.endswith(extensions):
|
||||
yield os.path.join(root, filename)
|
||||
|
||||
def process(self):
|
||||
""" Parses the directories within the 'rootShows' folder and stores them as shows in a list. """
|
||||
self.series = [ Folder(os.path.join(self.rootShows, x)) for x in self.directories]
|
||||
|
||||
for path in self.walk(config.shows_extensions):
|
||||
self.moveToFolder(path)
|
||||
|
||||
def moveToFolder(self, show):
|
||||
""" Moves a specific show to its right folder. """
|
||||
|
||||
destinationfile = self.findFolder(show)
|
||||
|
||||
if destinationfile is not None:
|
||||
self.move(show.path, destinationfile)
|
||||
|
||||
if self.isDeletable(show.path):
|
||||
shutil.rmtree(show.path)
|
||||
|
||||
def findFolder(self, show):
|
||||
"""Finds and returns the complete destinationpath for a specific show."""
|
||||
|
||||
rx = re.compile('\W+')
|
||||
result = rx.sub(' ', show.name.lower()).strip()
|
||||
|
||||
for s in self.series:
|
||||
if s.name.lower() in result:
|
||||
return os.path.join(s.directory, show.name)
|
||||
|
||||
|
||||
def move(self, originalfile, destinationfile):
|
||||
""" Moves the downloaded file to the found folder. """
|
||||
print "Moving " + show.name + " to " + destinationfile
|
||||
shutil.move(originalfile, destinationfile)
|
||||
|
||||
def isDeletable(self, foldername):
|
||||
""" Walks through the current directory and deletes it if nothing's really important in it
|
||||
ie. .nfo, .srr or .sfv files.
|
||||
"""
|
||||
if foldername is None:
|
||||
return False
|
||||
|
||||
if foldername == self.downloadDir or foldername == self.rootShows:
|
||||
return False
|
||||
|
||||
if self.downloadDir in foldername or self.rootShows in foldername:
|
||||
return False
|
||||
|
||||
if sum(1 for x in self.walk(foldername, config.useless_files_extensions)) is 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
def __str__(self):
|
||||
return 'PigeonHole module'
|
||||
|
||||
def __name__(self):
|
||||
return 'PigeonHole'
|
||||
|
||||
if __name__ == "__main__":
|
||||
pHole = PigeonHole(r'C:\test', r'C:\temp')
|
||||
pHole.process()
|
|
@ -0,0 +1,76 @@
|
|||
import random
|
||||
import unittest
|
||||
import tempfile
|
||||
import shutil
|
||||
import os
|
||||
from pigeonhole import PigeonHole
|
||||
import config
|
||||
|
||||
class TestPigeonHoleFunctions(unittest.TestCase):
|
||||
"""Test the methods defined inside the PigeonHole class"""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up the test environment"""
|
||||
self.rootdir = tempfile.mkdtemp(prefix='pigeonHole_root_')
|
||||
self.downloaddir = tempfile.mkdtemp(prefix='pigeonHole_dl_dir_')
|
||||
|
||||
# Create an environment with three folders
|
||||
os.mkdir(os.path.join(self.rootdir, 'White Collar'))
|
||||
os.mkdir(os.path.join(self.rootdir, 'The Big Bang Theory'))
|
||||
os.mkdir(os.path.join(self.rootdir, 'Being Erica'))
|
||||
|
||||
self.pigeonHole = PigeonHole(self.rootdir, self.downloaddir)
|
||||
|
||||
self.notDeletableTmpDir = tempfile.mkdtemp(prefix='pigeonHole_')
|
||||
self.deletableTmpDir = tempfile.mkdtemp(prefix='pigeonHole_')
|
||||
|
||||
def tearDown(self):
|
||||
"""Tear down the test environment"""
|
||||
self.pigeonHole = None
|
||||
|
||||
shutil.rmtree(self.notDeletableTmpDir)
|
||||
shutil.rmtree(self.deletableTmpDir)
|
||||
|
||||
shutil.rmtree(self.rootdir)
|
||||
shutil.rmtree(self.downloaddir)
|
||||
|
||||
|
||||
def test_init(self):
|
||||
""" Testing the constructor """
|
||||
self.assertEqual(self.pigeonHole.rootShows, self.rootdir)
|
||||
self.assertEqual(self.pigeonHole.downloadDir, self.downloaddir)
|
||||
self.assertTrue(str(self.pigeonHole) == 'PigeonHole module', 'The module string is not correct.')
|
||||
self.assertTrue(str(self.pigeonHole.__name__ == 'PigeonHole'), 'The module name is not correct.')
|
||||
|
||||
def test_clean(self):
|
||||
"""Testing the cleaning method"""
|
||||
|
||||
self.generatedfiles_bad = list()
|
||||
self.generatedfiles_good = list()
|
||||
|
||||
for x in config.useless_files_extensions + config.shows_extensions:
|
||||
fd, temppath = tempfile.mkstemp(x, 'tmp', self.notDeletableTmpDir)
|
||||
self.generatedfiles_bad.append(temppath)
|
||||
os.close(fd)
|
||||
|
||||
for y in config.useless_files_extensions:
|
||||
fd, temppath = tempfile.mkstemp(y, 'tmp', self.deletableTmpDir)
|
||||
self.generatedfiles_good.append(temppath)
|
||||
os.close(fd)
|
||||
|
||||
self.assertFalse(self.pigeonHole.isDeletable(self.notDeletableTmpDir))
|
||||
self.assertTrue(self.pigeonHole.isDeletable(self.deletableTmpDir))
|
||||
|
||||
self.assertFalse(self.pigeonHole.isDeletable(self.rootdir))
|
||||
self.assertFalse(self.pigeonHole.isDeletable(self.downloaddir))
|
||||
|
||||
def test_findFolder(self):
|
||||
"""Try to move a file to a specific location"""
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
|
@ -0,0 +1,15 @@
|
|||
from distutils.core import setup
|
||||
|
||||
setup {
|
||||
name='PigeonHole',
|
||||
version='0.1.0',
|
||||
author='Fred Pauchet'
|
||||
author_email='fpauchet@gmail.com',
|
||||
packages=['pigeonhole','pigeonhole.test'],
|
||||
scripts=[],
|
||||
url='',
|
||||
licence='LICENCE.txt',
|
||||
description='',
|
||||
long_description=long_description=open('README.txt').read(),
|
||||
install_require=[],
|
||||
}
|
Loading…
Reference in New Issue