Skip to content
This repository was archived by the owner on Nov 18, 2022. It is now read-only.

Commit a3ee92c

Browse files
committed
version 4.0
Historical commit version 4.0 (16.10.2013).
1 parent 86603c5 commit a3ee92c

12 files changed

Lines changed: 172 additions & 46 deletions

ChangeLog.txt

Lines changed: 16 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1+
videosort-4.0:
2+
- improved detection of obfuscated file and directory names;
3+
- added support for DNZB-Headers "X-DNZB-ProperName", "X-DNZB-EpisodeName",
4+
"X-DNZB-MovieYear";
5+
- removed support for DNZB-Header "X-DNZB-UseNZBName";
6+
- new option "DNZBHeaders" to disable using of DNZB-Headers if necessary;
7+
- new format specifier "%imdb" and "%cpimdb" (thanks Chris Hamilton for the patch);
8+
- removing invalid characters from generated file name;
9+
- updated guessit-library to the newest release - this fixes several issues.
10+
111
videosort-3.0:
2-
- added for seasoned TV shows: if year in the file name goes directly after
3-
show name, it will be added to show name. This may be necessary for
4-
media players like XBMC, Boxee or Plex (or anyone using TheTVDB) to
5-
properly index TV show. New option option "SeriesYear";
6-
- added detection of obfuscated file names; if such file name is detected
7-
a nzb-name is used instead.
12+
- added for seasoned TV shows: if year in the file name goes directly after
13+
show name, it will be added to show name. This may be necessary for
14+
media players like XBMC, Boxee or Plex (or anyone using TheTVDB) to
15+
properly index TV show. New option option "SeriesYear";
16+
- added detection of obfuscated file names; if such file name is detected
17+
a nzb-name is used instead.
818

919
videosort-2.0:
1020
- new options "TvCategories", "OtherTvDir" and "OtherTvFormat" for TV shows, whose file names look like movies (neither series nor dated shows);

VideoSort.py

Lines changed: 127 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
# Author: Andrey Prygunkov ([email protected]).
3737
# Web-site: http://nzbget.sourceforge.net/VideoSort.
3838
# License: GPLv3 (http://www.gnu.org/licenses/gpl.html).
39-
# PP-Script Version: 3.0.
39+
# PP-Script Version: 4.0.
4040
#
4141
# NOTE: This script requires Python 2.x to be installed on your system.
4242

@@ -106,6 +106,8 @@
106106
# %y - year;
107107
# %decade - two-digits decade (90, 00, 10);
108108
# %0decade - four-digits decade (1990, 2000, 2010).
109+
# %imdb - IMDb ID;
110+
# %cpimdb - IMDb ID (formatted for CouchPotato);
109111
#
110112
# Common specifiers (for movies, series and dated tv shows):
111113
# %dn - original directory name (nzb-name);
@@ -191,6 +193,13 @@
191193
# This option has effect on "case-adjusted"-specifiers.
192194
#UpperWords=III,II,IV
193195

196+
# Use information from Direct-NZB headers (yes, no).
197+
#
198+
# NZB-sites may provide extended information about videos,
199+
# which is usually more confident than the information extracted
200+
# from file names.
201+
#DNZBHeaders=yes
202+
194203
# Overwrite files at destination (yes, no).
195204
#
196205
# If not active the files are still moved into destination but
@@ -224,7 +233,6 @@
224233
sys.path.append(dirname(__file__) + '/lib')
225234

226235
import os
227-
import string
228236
import traceback
229237
import re
230238
import shutil
@@ -288,17 +296,18 @@
288296
series_year=os.environ.get('NZBPO_SERIESYEAR', 'yes') == 'yes'
289297

290298
tv_categories=os.environ['NZBPO_TVCATEGORIES'].lower().split(',')
291-
category=os.environ.get('NZBPP_CATEGORY', '');
299+
category=os.environ.get('NZBPP_CATEGORY', '')
292300
force_tv=category.lower() in tv_categories
293301

294-
force_nzbname=os.environ.get('NZBPR__DNZB_USENZBNAME', '').lower() == 'yes'
302+
dnzb_headers=os.environ.get('NZBPO_DNZBHEADERS', 'yes') == 'yes'
303+
dnzb_proper_name=os.environ.get('NZBPR__DNZB_PROPERNAME', '')
304+
dnzb_episode_name=os.environ.get('NZBPR__DNZB_EPISODENAME', '')
305+
dnzb_movie_year=os.environ.get('NZBPR__DNZB_MOVIEYEAR', '')
306+
dnzb_more_info=os.environ.get('NZBPR__DNZB_MOREINFO', '')
295307

296308
if preview:
297309
print('[WARNING] *** PREVIEW MODE ON - NO CHANGES TO FILE SYSTEM ***')
298310

299-
if verbose and force_nzbname:
300-
print('[INFO] Forcing use of nzb-name (X-DNZB-UseNZBName)')
301-
302311
if verbose and force_tv:
303312
print('[INFO] Forcing TV sorting (category: %s)' % category)
304313

@@ -425,7 +434,7 @@ def cleanup_download_dir():
425434
' ': ' ',
426435
'//': '/',
427436
' - - ': ' - ',
428-
'__': '_'
437+
'--': '-'
429438
}
430439

431440
def path_subst(path, mapping):
@@ -458,9 +467,9 @@ def get_titles(name, titleing=False):
458467
a lot of little hacks to make it better and for more control
459468
'''
460469

461-
title = name.replace('.', ' ').replace('_', ' ')
462-
title = title.strip().strip('(').strip('_').strip('-').strip().strip('_')
463-
470+
#make valid filename
471+
title = re.sub('[\"\:\?\*\\\/\<\>\|]', ' ', name)
472+
464473
if titleing:
465474
title = titler(title) # title the show name so it is in a consistant letter case
466475

@@ -702,6 +711,10 @@ def add_movies_mapping(guess, mapping):
702711
mapping.append(('%decade', decade))
703712
mapping.append(('%0decade', decade_two))
704713

714+
# imdb
715+
mapping.append(('%imdb', guess.get('imdb', '')))
716+
mapping.append(('%cpimdb', guess.get('cpimdb', '')))
717+
705718
def add_dated_mapping(guess, mapping):
706719

707720
# title
@@ -753,22 +766,110 @@ def add_dated_mapping(guess, mapping):
753766
mapping.append(('%d', day))
754767
mapping.append(('%0d', day.rjust(2, '0')))
755768

756-
def guess_info(filename):
757-
""" Parses the filename using guessit-library """
769+
def os_path_split(path):
770+
parts = []
771+
while True:
772+
newpath, tail = os.path.split(path)
773+
if newpath == path:
774+
if path: parts.append(path)
775+
break
776+
parts.append(tail)
777+
path = newpath
778+
parts.reverse()
779+
return parts
780+
781+
def deobfuscate_path(filename):
782+
start = os.path.dirname(download_dir)
783+
new_name = filename[len(start)+1:]
784+
if verbose:
785+
print('stripped filename: %s' % new_name)
758786

759-
use_nzbname = force_nzbname
787+
parts = os_path_split(new_name)
788+
if verbose:
789+
print(parts)
760790

761-
if not use_nzbname:
762-
fn = os.path.splitext(os.path.basename(filename))[0]
791+
part_removed = 0
792+
for x in range(0, len(parts)-1):
793+
fn = parts[x]
763794
if fn.find('.')==-1 and fn.find('_')==-1 and fn.find(' ')==-1:
764-
print("Detected obfuscated filename %s, using NZB-Name instead" % os.path.basename(filename))
765-
use_nzbname = True
766-
767-
if use_nzbname:
768-
guessfilename = os.path.join(os.path.dirname(filename), os.path.basename(download_dir)) + os.path.splitext(filename)[1]
795+
print('Detected obfuscated directory name %s, removing from guess path' % fn)
796+
parts[x] = None
797+
part_removed += 1
798+
799+
fn = os.path.splitext(parts[len(parts)-1])[0]
800+
if fn.find('.')==-1 and fn.find('_')==-1 and fn.find(' ')==-1:
801+
print('Detected obfuscated filename %s, removing from guess path' % os.path.basename(filename))
802+
parts[len(parts)-1] = '-' + os.path.splitext(filename)[1]
803+
part_removed += 1
804+
805+
if part_removed < len(parts):
806+
new_name = ''
807+
for x in range(0, len(parts)):
808+
if parts[x] != None:
809+
new_name = os.path.join(new_name, parts[x])
769810
else:
770-
guessfilename = filename
811+
print("All file path parts are obfuscated, using obfuscated NZB-Name")
812+
new_name = os.path.basename(download_dir) + os.path.splitext(filename)[1]
813+
814+
return new_name
815+
816+
def remove_year(title):
817+
""" Removes year from series name (if exist) """
818+
m = re.compile('..*(\((19|20)\d\d\))').search(title)
819+
if not m:
820+
m = re.compile('..*((19|20)\d\d)').search(title)
821+
if m:
822+
if verbose:
823+
print('Removing year from series name')
824+
title = title.replace(m.group(1), '').strip()
825+
return title
771826

827+
def apply_dnzb_headers(guess):
828+
""" Applies DNZB headers (if exist) """
829+
830+
dnzb_used = False
831+
if dnzb_proper_name != '':
832+
dnzb_used = True
833+
if verbose:
834+
print('Using DNZB-ProperName')
835+
if guess['vtype'] == 'series':
836+
proper_name = dnzb_proper_name
837+
if not series_year:
838+
proper_name = remove_year(proper_name)
839+
guess['series'] = proper_name
840+
else:
841+
guess['title'] = dnzb_proper_name
842+
843+
if dnzb_episode_name != '' and guess['vtype'] == 'series':
844+
dnzb_used = True
845+
if verbose:
846+
print('Using DNZB-EpisodeName')
847+
guess['title'] = dnzb_episode_name
848+
849+
if dnzb_movie_year != '':
850+
dnzb_used = True
851+
if verbose:
852+
print('Using DNZB-MovieYear')
853+
guess['year'] = dnzb_movie_year
854+
855+
if dnzb_more_info != '':
856+
dnzb_used = True
857+
if verbose:
858+
print('Using DNZB-MoreInfo')
859+
if guess['type'] == 'movie':
860+
regex = re.compile(r'^http://www.imdb.com/title/(tt[0-9]+)/$', re.IGNORECASE)
861+
matches = regex.match(dnzb_more_info)
862+
if matches:
863+
guess['imdb'] = matches.group(1)
864+
guess['cpimdb'] = 'cp(' + guess['imdb'] + ')'
865+
866+
if verbose and dnzb_used:
867+
print(guess.nice_string())
868+
869+
def guess_info(filename):
870+
""" Parses the filename using guessit-library """
871+
872+
guessfilename = deobfuscate_path(filename)
772873
if verbose:
773874
print('Guessing: %s' % guessfilename)
774875

@@ -818,6 +919,9 @@ def guess_info(filename):
818919
elif guess['type'] == 'episode':
819920
guess['vtype'] = 'series'
820921

922+
if dnzb_headers:
923+
apply_dnzb_headers(guess)
924+
821925
if verbose:
822926
print('Type: %s' % guess['vtype'])
823927

@@ -829,7 +933,7 @@ def construct_path(filename):
829933
if verbose:
830934
print("filename: %s" % filename)
831935

832-
guess = guess_info(filename);
936+
guess = guess_info(filename)
833937
type = guess.get('vtype')
834938
mapping = []
835939
add_common_mapping(filename, guess, mapping)

lib/guessit/ISO-3166-1_utf8.txt

100755100644
File mode changed.

lib/guessit/ISO-639-2_utf-8.txt

100755100644
File mode changed.

lib/guessit/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@
2020

2121
from __future__ import unicode_literals
2222

23-
__version__ = '0.7-dev'
23+
__version__ = '0.7.dev0'
2424
__all__ = ['Guess', 'Language',
2525
'guess_file_info', 'guess_video_info',
2626
'guess_movie_info', 'guess_episode_info']

lib/guessit/fileutils.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,13 +44,14 @@ def split_path(path):
4444
result = []
4545
while True:
4646
head, tail = os.path.split(path)
47+
headlen = len(head)
4748

4849
# on Unix systems, the root folder is '/'
49-
if head == '/' and tail == '':
50+
if head and head == '/'*headlen and tail == '':
5051
return ['/'] + result
5152

5253
# on Windows, the root folder is a drive letter (eg: 'C:\') or for shares \\
53-
if ((len(head) == 3 and head[1:] == ':\\') or (len(head) == 2 and head == '\\\\')) and tail == '':
54+
if ((headlen == 3 and head[1:] == ':\\') or (headlen == 2 and head == '\\\\')) and tail == '':
5455
return [head] + result
5556

5657
if head == '' and tail == '':
@@ -61,6 +62,7 @@ def split_path(path):
6162
path = head
6263
continue
6364

65+
# otherwise, add the last path fragment and keep splitting
6466
result = [tail] + result
6567
path = head
6668

lib/guessit/guess.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ def choose_string(g1, g2):
181181
elif v1l in v2l:
182182
return (v1, combined_prob)
183183

184-
# in case of conflict, return the one with highest priority
184+
# in case of conflict, return the one with highest confidence
185185
else:
186186
if c1 > c2:
187187
return (v1, c1 - c2)

lib/guessit/matcher.py

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
from __future__ import unicode_literals
2222
from guessit import PY3, u, base_text_type
2323
from guessit.matchtree import MatchTree
24-
from guessit.textutils import normalize_unicode
24+
from guessit.textutils import normalize_unicode, clean_string
2525
import logging
2626

2727
log = logging.getLogger(__name__)
@@ -62,9 +62,9 @@ def __init__(self, filename, filetype='autodetect', opts=None):
6262
(for more info, see guess.matchtree.to_string)
6363
6464
65-
Second, it tries to merge all this information into a single object
66-
containing all the found properties, and does some (basic) conflict
67-
resolution when they arise.
65+
Second, it tries to merge all this information into a single object
66+
containing all the found properties, and does some (basic) conflict
67+
resolution when they arise.
6868
"""
6969

7070
valid_filetypes = ('autodetect', 'subtitle', 'video',
@@ -84,6 +84,11 @@ def __init__(self, filename, filetype='autodetect', opts=None):
8484
opts = opts.split()
8585

8686
self.match_tree = MatchTree(filename)
87+
88+
# sanity check: make sure we don't process a (mostly) empty string
89+
if clean_string(filename) == '':
90+
return
91+
8792
mtree = self.match_tree
8893
mtree.guess.set('type', filetype, confidence=1.0)
8994

lib/guessit/patterns.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
#(r'[Ss](?P<season>[0-9]{1,3})[^0-9]?(?P<bonusNumber>(?:-?[xX-][0-9]{1,3})+)[^0-9]', 1.0, (0, -1)),
5050

5151
# ... 2x13 ...
52-
(r'[^0-9](?P<season>[0-9]{1,2})[^0-9]?(?P<episodeNumber>(?:-?[xX][0-9]{1,3})+)[^0-9]', 1.0, (1, -1)),
52+
(r'[^0-9](?P<season>[0-9]{1,2})[^0-9 .-]?(?P<episodeNumber>(?:-?[xX][0-9]{1,3})+)[^0-9]', 1.0, (1, -1)),
5353

5454
# ... s02 ...
5555
#(sep + r's(?P<season>[0-9]{1,2})' + sep, 0.6, (1, -1)),

lib/guessit/slogging.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -31,14 +31,15 @@
3131
RESET_FONT = "\x1B[0m"
3232

3333

34-
def setupLogging(colored=True, with_time=False, with_thread=False, filename=None):
34+
def setupLogging(colored=True, with_time=False, with_thread=False, filename=None, with_lineno=False):
3535
"""Set up a nice colored logger as the main application logger."""
3636

3737
class SimpleFormatter(logging.Formatter):
3838
def __init__(self, with_time, with_thread):
3939
self.fmt = (('%(asctime)s ' if with_time else '') +
4040
'%(levelname)-8s ' +
41-
'[%(name)s:%(funcName)s]' +
41+
'[%(name)s:%(funcName)s' +
42+
(':%(lineno)s' if with_lineno else '') + ']' +
4243
('[%(threadName)s]' if with_thread else '') +
4344
' -- %(message)s')
4445
logging.Formatter.__init__(self, self.fmt)
@@ -47,7 +48,8 @@ class ColoredFormatter(logging.Formatter):
4748
def __init__(self, with_time, with_thread):
4849
self.fmt = (('%(asctime)s ' if with_time else '') +
4950
'-CC-%(levelname)-8s ' +
50-
BLUE_FONT + '[%(name)s:%(funcName)s]' +
51+
BLUE_FONT + '[%(name)s:%(funcName)s' +
52+
(':%(lineno)s' if with_lineno else '') + ']' +
5153
RESET_FONT + ('[%(threadName)s]' if with_thread else '') +
5254
' -- %(message)s')
5355

0 commit comments

Comments
 (0)