Merge remote-tracking branch 'RuudBurger/develop' into tv
Conflicts: couchpotato/core/plugins/quality/main.py
This commit is contained in:
@@ -263,7 +263,7 @@ class Plugin(object):
|
||||
def afterCall(self, handler):
|
||||
self.isRunning('%s.%s' % (self.getName(), handler.__name__), False)
|
||||
|
||||
def doShutdown(self):
|
||||
def doShutdown(self, *args, **kwargs):
|
||||
self.shuttingDown(True)
|
||||
return True
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ import os
|
||||
import string
|
||||
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.helpers.encoding import sp
|
||||
from couchpotato.core.helpers.variable import getUserDir
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
import six
|
||||
@@ -50,6 +51,7 @@ class FileBrowser(Plugin):
|
||||
path = '/'
|
||||
|
||||
dirs = []
|
||||
path = sp(path)
|
||||
for f in os.listdir(path):
|
||||
p = os.path.join(path, f)
|
||||
if os.path.isdir(p) and ((self.is_hidden(p) and bool(int(show_hidden))) or not self.is_hidden(p)):
|
||||
|
||||
@@ -5,7 +5,7 @@ from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode
|
||||
from couchpotato.core.helpers.variable import md5, getExt
|
||||
from couchpotato.core.helpers.variable import md5, getExt, isSubFolder
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
@@ -32,6 +32,8 @@ class FileManager(Plugin):
|
||||
|
||||
fireEvent('schedule.interval', 'file.cleanup', self.cleanup, hours = 24)
|
||||
|
||||
addEvent('app.test', self.doSubfolderTest)
|
||||
|
||||
def cleanup(self):
|
||||
|
||||
# Wait a bit after starting before cleanup
|
||||
@@ -76,3 +78,33 @@ class FileManager(Plugin):
|
||||
|
||||
self.createFile(dest, filedata, binary = True)
|
||||
return dest
|
||||
|
||||
def doSubfolderTest(self):
|
||||
|
||||
tests = {
|
||||
('/test/subfolder', '/test/sub'): False,
|
||||
('/test/sub/folder', '/test/sub'): True,
|
||||
('/test/sub/folder', '/test/sub2'): False,
|
||||
('/sub/fold', '/test/sub/fold'): False,
|
||||
('/sub/fold', '/test/sub/folder'): False,
|
||||
('/opt/couchpotato', '/var/opt/couchpotato'): False,
|
||||
('/var/opt', '/var/opt/couchpotato'): False,
|
||||
('/CapItaLs/Are/OK', '/CapItaLs/Are/OK'): True,
|
||||
('/CapItaLs/Are/OK', '/CapItaLs/Are/OK2'): False,
|
||||
('/capitals/are/not/OK', '/capitals/are/NOT'): False,
|
||||
('\\\\Mounted\\Volume\\Test', '\\\\Mounted\\Volume'): True,
|
||||
('C:\\\\test\\path', 'C:\\\\test2'): False
|
||||
}
|
||||
|
||||
failed = 0
|
||||
for x in tests:
|
||||
if isSubFolder(x[0], x[1]) is not tests[x]:
|
||||
log.error('Failed subfolder test %s %s', x)
|
||||
failed += 1
|
||||
|
||||
if failed > 0:
|
||||
log.error('Subfolder test failed %s tests', failed)
|
||||
else:
|
||||
log.info('Subfolder test succeeded')
|
||||
|
||||
return failed == 0
|
||||
@@ -1,13 +1,12 @@
|
||||
import ctypes
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, addEvent, fireEventAsync
|
||||
from couchpotato.core.helpers.encoding import sp
|
||||
from couchpotato.core.helpers.variable import splitString, getTitle, tryInt, getIdentifier
|
||||
from couchpotato.core.helpers.variable import splitString, getTitle, tryInt, getIdentifier, getFreeSpace
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.environment import Env
|
||||
@@ -136,6 +135,7 @@ class Manage(Plugin):
|
||||
# Get movies with done status
|
||||
total_movies, done_movies = fireEvent('media.list', types = 'movie', status = 'done', release_status = 'done', status_or = True, single = True)
|
||||
|
||||
deleted_releases = []
|
||||
for done_movie in done_movies:
|
||||
if getIdentifier(done_movie) not in added_identifiers:
|
||||
fireEvent('media.delete', media_id = done_movie['_id'], delete_from = 'all')
|
||||
@@ -165,12 +165,10 @@ class Manage(Plugin):
|
||||
already_used = used_files.get(release_file)
|
||||
|
||||
if already_used:
|
||||
# delete current one
|
||||
if already_used.get('last_edit', 0) < release.get('last_edit', 0):
|
||||
fireEvent('release.delete', release['_id'], single = True)
|
||||
# delete previous one
|
||||
else:
|
||||
fireEvent('release.delete', already_used['_id'], single = True)
|
||||
release_id = release['_id'] if already_used.get('last_edit', 0) < release.get('last_edit', 0) else already_used['_id']
|
||||
if release_id not in deleted_releases:
|
||||
fireEvent('release.delete', release_id, single = True)
|
||||
deleted_releases.append(release_id)
|
||||
break
|
||||
else:
|
||||
used_files[release_file] = release
|
||||
@@ -180,6 +178,10 @@ class Manage(Plugin):
|
||||
if self.shuttingDown():
|
||||
break
|
||||
|
||||
if not self.shuttingDown():
|
||||
db = get_db()
|
||||
db.reindex()
|
||||
|
||||
Env.prop(last_update_key, time.time())
|
||||
except:
|
||||
log.error('Failed updating library: %s', (traceback.format_exc()))
|
||||
@@ -269,31 +271,7 @@ class Manage(Plugin):
|
||||
fireEvent('release.add', group = group)
|
||||
|
||||
def getDiskSpace(self):
|
||||
|
||||
free_space = {}
|
||||
for folder in self.directories():
|
||||
|
||||
size = None
|
||||
if os.path.isdir(folder):
|
||||
if os.name == 'nt':
|
||||
_, total, free = ctypes.c_ulonglong(), ctypes.c_ulonglong(), \
|
||||
ctypes.c_ulonglong()
|
||||
if sys.version_info >= (3,) or isinstance(folder, unicode):
|
||||
fun = ctypes.windll.kernel32.GetDiskFreeSpaceExW #@UndefinedVariable
|
||||
else:
|
||||
fun = ctypes.windll.kernel32.GetDiskFreeSpaceExA #@UndefinedVariable
|
||||
ret = fun(folder, ctypes.byref(_), ctypes.byref(total), ctypes.byref(free))
|
||||
if ret == 0:
|
||||
raise ctypes.WinError()
|
||||
used = total.value - free.value
|
||||
return [total.value, used, free.value]
|
||||
else:
|
||||
s = os.statvfs(folder)
|
||||
size = [s.f_blocks * s.f_frsize / (1024 * 1024), (s.f_bavail * s.f_frsize) / (1024 * 1024)]
|
||||
|
||||
free_space[folder] = size
|
||||
|
||||
return free_space
|
||||
return getFreeSpace(self.directories())
|
||||
|
||||
|
||||
config = [{
|
||||
|
||||
@@ -88,6 +88,7 @@ class ProfilePlugin(Plugin):
|
||||
'core': kwargs.get('core', False),
|
||||
'qualities': [],
|
||||
'wait_for': [],
|
||||
'stop_after': [],
|
||||
'finish': [],
|
||||
'3d': []
|
||||
}
|
||||
@@ -97,6 +98,7 @@ class ProfilePlugin(Plugin):
|
||||
for type in kwargs.get('types', []):
|
||||
profile['qualities'].append(type.get('quality'))
|
||||
profile['wait_for'].append(tryInt(kwargs.get('wait_for', 0)))
|
||||
profile['stop_after'].append(tryInt(kwargs.get('stop_after', 0)))
|
||||
profile['finish'].append((tryInt(type.get('finish')) == 1) if order > 0 else True)
|
||||
profile['3d'].append(tryInt(type.get('3d')))
|
||||
order += 1
|
||||
@@ -217,6 +219,7 @@ class ProfilePlugin(Plugin):
|
||||
'qualities': profile.get('qualities'),
|
||||
'finish': [],
|
||||
'wait_for': [],
|
||||
'stop_after': [],
|
||||
'3d': []
|
||||
}
|
||||
|
||||
@@ -224,6 +227,7 @@ class ProfilePlugin(Plugin):
|
||||
for q in profile.get('qualities'):
|
||||
pro['finish'].append(True)
|
||||
pro['wait_for'].append(0)
|
||||
pro['stop_after'].append(0)
|
||||
pro['3d'].append(threed.pop() if threed else False)
|
||||
|
||||
db.insert(pro)
|
||||
|
||||
@@ -43,9 +43,8 @@
|
||||
}
|
||||
|
||||
.profile .wait_for {
|
||||
position: absolute;
|
||||
right: 60px;
|
||||
top: 0;
|
||||
padding-top: 0;
|
||||
padding-bottom: 20px;
|
||||
}
|
||||
|
||||
.profile .wait_for input {
|
||||
|
||||
@@ -37,20 +37,28 @@ var Profile = new Class({
|
||||
'placeholder': 'Profile name'
|
||||
})
|
||||
),
|
||||
new Element('div.wait_for.ctrlHolder').adopt(
|
||||
new Element('span', {'text':'Wait'}),
|
||||
new Element('input.inlay.xsmall', {
|
||||
'type':'text',
|
||||
'value': data.wait_for && data.wait_for.length > 0 ? data.wait_for[0] : 0
|
||||
}),
|
||||
new Element('span', {'text':'day(s) for a better quality.'})
|
||||
),
|
||||
new Element('div.qualities.ctrlHolder').adopt(
|
||||
new Element('label', {'text': 'Search for'}),
|
||||
self.type_container = new Element('ol.types'),
|
||||
new Element('div.formHint', {
|
||||
'html': "Search these qualities (2 minimum), from top to bottom. Use the checkbox, to stop searching after it found this quality."
|
||||
})
|
||||
),
|
||||
new Element('div.wait_for.ctrlHolder').adopt(
|
||||
// "Wait the entered number of days for a checked quality, before downloading a lower quality release."
|
||||
new Element('span', {'text':'Wait'}),
|
||||
new Element('input.inlay.wait_for_input.xsmall', {
|
||||
'type':'text',
|
||||
'value': data.wait_for && data.wait_for.length > 0 ? data.wait_for[0] : 0
|
||||
}),
|
||||
new Element('span', {'text':'day(s) for a better quality '}),
|
||||
new Element('span.advanced', {'text':'and keep searching'}),
|
||||
// "After a checked quality is found and downloaded, continue searching for even better quality releases for the entered number of days."
|
||||
new Element('input.inlay.xsmall.stop_after_input.advanced', {
|
||||
'type':'text',
|
||||
'value': data.stop_after && data.stop_after.length > 0 ? data.stop_after[0] : 0
|
||||
}),
|
||||
new Element('span.advanced', {'text':'day(s) for a better (checked) quality.'})
|
||||
)
|
||||
);
|
||||
|
||||
@@ -116,7 +124,8 @@ var Profile = new Class({
|
||||
var data = {
|
||||
'id' : self.data._id,
|
||||
'label' : self.el.getElement('.quality_label input').get('value'),
|
||||
'wait_for' : self.el.getElement('.wait_for input').get('value'),
|
||||
'wait_for' : self.el.getElement('.wait_for_input').get('value'),
|
||||
'stop_after' : self.el.getElement('.stop_after_input').get('value'),
|
||||
'types': []
|
||||
};
|
||||
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import traceback
|
||||
import re
|
||||
from CodernityDB.database import RecordNotFound
|
||||
|
||||
from CodernityDB.database import RecordNotFound
|
||||
from couchpotato import get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import addEvent
|
||||
from couchpotato.core.event import addEvent, fireEvent
|
||||
from couchpotato.core.helpers.encoding import toUnicode, ss
|
||||
from couchpotato.core.helpers.variable import mergeDicts, getExt, tryInt
|
||||
from couchpotato.core.helpers.variable import mergeDicts, getExt, tryInt, splitString
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.plugins.base import Plugin
|
||||
from couchpotato.core.plugins.quality.index import QualityIndex
|
||||
@@ -22,17 +22,17 @@ class QualityPlugin(Plugin):
|
||||
}
|
||||
|
||||
qualities = [
|
||||
{'identifier': 'bd50', 'hd': True, 'allow_3d': True, 'size': (20000, 60000), 'label': 'BR-Disk', 'alternative': ['bd25'], 'allow': ['1080p'], 'ext':['iso', 'img'], 'tags': ['bdmv', 'certificate', ('complete', 'bluray'), 'avc', 'mvc']},
|
||||
{'identifier': '1080p', 'hd': True, 'allow_3d': True, 'size': (4000, 20000), 'label': '1080p', 'width': 1920, 'height': 1080, 'alternative': [], 'allow': [], 'ext':['mkv', 'm2ts'], 'tags': ['m2ts', 'x264', 'h264']},
|
||||
{'identifier': 'bd50', 'hd': True, 'allow_3d': True, 'size': (20000, 60000), 'label': 'BR-Disk', 'alternative': ['bd25', ('br', 'disk')], 'allow': ['1080p'], 'ext':['iso', 'img'], 'tags': ['bdmv', 'certificate', ('complete', 'bluray'), 'avc', 'mvc']},
|
||||
{'identifier': '1080p', 'hd': True, 'allow_3d': True, 'size': (4000, 20000), 'label': '1080p', 'width': 1920, 'height': 1080, 'alternative': [], 'allow': [], 'ext':['mkv', 'm2ts', 'ts'], 'tags': ['m2ts', 'x264', 'h264']},
|
||||
{'identifier': '720p', 'hd': True, 'allow_3d': True, 'size': (3000, 10000), 'label': '720p', 'width': 1280, 'height': 720, 'alternative': [], 'allow': [], 'ext':['mkv', 'ts'], 'tags': ['x264', 'h264']},
|
||||
{'identifier': 'brrip', 'hd': True, 'allow_3d': True, 'size': (700, 7000), 'label': 'BR-Rip', 'alternative': ['bdrip'], 'allow': ['720p', '1080p'], 'ext':[], 'tags': ['hdtv', 'hdrip', 'webdl', ('web', 'dl')]},
|
||||
{'identifier': 'dvdr', 'size': (3000, 10000), 'label': 'DVD-R', 'alternative': ['br2dvd'], 'allow': [], 'ext':['iso', 'img', 'vob'], 'tags': ['pal', 'ntsc', 'video_ts', 'audio_ts', ('dvd', 'r'), 'dvd9']},
|
||||
{'identifier': 'dvdrip', 'size': (600, 2400), 'label': 'DVD-Rip', 'width': 720, 'alternative': [], 'allow': [], 'ext':[], 'tags': [('dvd', 'rip'), ('dvd', 'xvid'), ('dvd', 'divx')]},
|
||||
{'identifier': 'brrip', 'hd': True, 'allow_3d': True, 'size': (700, 7000), 'label': 'BR-Rip', 'alternative': ['bdrip', ('br', 'rip')], 'allow': ['720p', '1080p'], 'ext':['mp4', 'avi'], 'tags': ['hdtv', 'hdrip', 'webdl', ('web', 'dl')]},
|
||||
{'identifier': 'dvdr', 'size': (3000, 10000), 'label': 'DVD-R', 'alternative': ['br2dvd', ('dvd', 'r')], 'allow': [], 'ext':['iso', 'img', 'vob'], 'tags': ['pal', 'ntsc', 'video_ts', 'audio_ts', ('dvd', 'r'), 'dvd9']},
|
||||
{'identifier': 'dvdrip', 'size': (600, 2400), 'label': 'DVD-Rip', 'width': 720, 'alternative': [('dvd', 'rip')], 'allow': [], 'ext':['avi'], 'tags': [('dvd', 'rip'), ('dvd', 'xvid'), ('dvd', 'divx')]},
|
||||
{'identifier': 'scr', 'size': (600, 1600), 'label': 'Screener', 'alternative': ['screener', 'dvdscr', 'ppvrip', 'dvdscreener', 'hdscr'], 'allow': ['dvdr', 'dvdrip', '720p', '1080p'], 'ext':[], 'tags': ['webrip', ('web', 'rip')]},
|
||||
{'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr'], 'ext':[]},
|
||||
{'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': [], 'ext':[]},
|
||||
{'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': [], 'ext':[]},
|
||||
{'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': [], 'ext':[]},
|
||||
{'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr', '720p'], 'ext':[]},
|
||||
{'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': ['720p'], 'ext':[]},
|
||||
{'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': ['720p'], 'ext':[]},
|
||||
{'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': ['720p'], 'ext':[]},
|
||||
|
||||
# TODO come back to this later, think this could be handled better, this is starting to get out of hand....
|
||||
# BluRay
|
||||
@@ -113,15 +113,14 @@ class QualityPlugin(Plugin):
|
||||
|
||||
db = get_db()
|
||||
|
||||
qualities = db.all('quality', with_doc = True)
|
||||
|
||||
temp = []
|
||||
for quality in qualities:
|
||||
quality = quality['doc']
|
||||
q = mergeDicts(self.getQuality(quality.get('identifier')), quality)
|
||||
for quality in self.qualities:
|
||||
quality_doc = db.get('quality', quality.get('identifier'), with_doc = True)['doc']
|
||||
q = mergeDicts(quality, quality_doc)
|
||||
temp.append(q)
|
||||
|
||||
self.cached_qualities = temp
|
||||
if len(temp) == len(self.qualities):
|
||||
self.cached_qualities = temp
|
||||
|
||||
return temp
|
||||
|
||||
@@ -227,10 +226,15 @@ class QualityPlugin(Plugin):
|
||||
|
||||
for cur_file in files:
|
||||
words = re.split('\W+', cur_file.lower())
|
||||
name_year = fireEvent('scanner.name_year', cur_file, file_name = cur_file, single = True)
|
||||
threed_words = words
|
||||
if name_year and name_year.get('name'):
|
||||
split_name = splitString(name_year.get('name'), ' ')
|
||||
threed_words = [x for x in words if x not in split_name]
|
||||
|
||||
for quality in qualities:
|
||||
contains_score = self.containsTagScore(quality, words, cur_file)
|
||||
threedscore = self.contains3D(quality, words, cur_file) if quality.get('allow_3d') else (0, None)
|
||||
threedscore = self.contains3D(quality, threed_words, cur_file) if quality.get('allow_3d') else (0, None)
|
||||
|
||||
self.calcScore(score, quality, contains_score, threedscore)
|
||||
|
||||
@@ -275,6 +279,9 @@ class QualityPlugin(Plugin):
|
||||
cur_file = ss(cur_file)
|
||||
score = 0
|
||||
|
||||
extension = words[-1]
|
||||
words = words[:-1]
|
||||
|
||||
points = {
|
||||
'identifier': 10,
|
||||
'label': 10,
|
||||
@@ -294,7 +301,7 @@ class QualityPlugin(Plugin):
|
||||
log.debug('Found %s via %s %s in %s', (quality['identifier'], tag_type, quality.get(tag_type), cur_file))
|
||||
score += points.get(tag_type)
|
||||
|
||||
if isinstance(alt, (str, unicode)) and ss(alt.lower()) in cur_file.lower():
|
||||
if isinstance(alt, (str, unicode)) and ss(alt.lower()) in words:
|
||||
log.debug('Found %s via %s %s in %s', (quality['identifier'], tag_type, quality.get(tag_type), cur_file))
|
||||
score += points.get(tag_type) / 2
|
||||
|
||||
@@ -304,8 +311,8 @@ class QualityPlugin(Plugin):
|
||||
|
||||
# Check extention
|
||||
for ext in quality.get('ext', []):
|
||||
if ext == words[-1]:
|
||||
log.debug('Found %s extension in %s', (ext, cur_file))
|
||||
if ext == extension:
|
||||
log.debug('Found %s with .%s extension in %s', (quality['identifier'], ext, cur_file))
|
||||
score += points['ext']
|
||||
|
||||
return score
|
||||
@@ -390,26 +397,31 @@ class QualityPlugin(Plugin):
|
||||
if score.get(q.get('identifier')):
|
||||
score[q.get('identifier')]['score'] -= 1
|
||||
|
||||
def isFinish(self, quality, profile):
|
||||
def isFinish(self, quality, profile, release_age = 0):
|
||||
if not isinstance(profile, dict) or not profile.get('qualities'):
|
||||
return False
|
||||
# No profile so anything (scanned) is good enough
|
||||
return True
|
||||
|
||||
try:
|
||||
quality_order = [i for i, identifier in enumerate(profile['qualities']) if identifier == quality['identifier'] and bool(profile['3d'][i] if profile.get('3d') else 0) == bool(quality.get('is_3d', 0))][0]
|
||||
return profile['finish'][quality_order]
|
||||
index = [i for i, identifier in enumerate(profile['qualities']) if identifier == quality['identifier'] and bool(profile['3d'][i] if profile.get('3d') else False) == bool(quality.get('is_3d', False))][0]
|
||||
|
||||
if index == 0 or (profile['finish'][index] and int(release_age) >= int(profile.get('stop_after', [0])[0])):
|
||||
return True
|
||||
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def isHigher(self, quality, compare_with, profile = None):
|
||||
if not isinstance(profile, dict) or not profile.get('qualities'):
|
||||
profile = {'qualities': self.order}
|
||||
profile = fireEvent('profile.default', single = True)
|
||||
|
||||
# Try to find quality in profile, if not found: a quality we do not want is lower than anything else
|
||||
try:
|
||||
quality_order = [i for i, identifier in enumerate(profile['qualities']) if identifier == quality['identifier'] and bool(profile['3d'][i] if profile.get('3d') else 0) == bool(quality.get('is_3d', 0))][0]
|
||||
except:
|
||||
log.debug('Quality %s not found in profile identifiers %s', (quality['identifier'] + (' 3D' if quality.get('is_3d', 0) else ''), \
|
||||
[identifier + ('3D' if (profile['3d'][i] if profile.get('3d') else 0) else '') for i, identifier in enumerate(profile['qualities'])]))
|
||||
[identifier + (' 3D' if (profile['3d'][i] if profile.get('3d') else 0) else '') for i, identifier in enumerate(profile['qualities'])]))
|
||||
return 'lower'
|
||||
|
||||
# Try to find compare quality in profile, if not found: anything is higher than a not wanted quality
|
||||
@@ -451,7 +463,18 @@ class QualityPlugin(Plugin):
|
||||
'Movie Monuments 2013 BrRip 720p': {'size': 1300, 'quality': 'brrip'},
|
||||
'The.Movie.2014.3D.1080p.BluRay.AVC.DTS-HD.MA.5.1-GroupName': {'size': 30000, 'quality': 'bd50', 'is_3d': True},
|
||||
'/home/namehou/Movie Monuments (2013)/Movie Monuments.mkv': {'size': 4500, 'quality': '1080p', 'is_3d': False},
|
||||
'/home/namehou/Movie Monuments (2013)/Movie Monuments Full-OU.mkv': {'size': 4500, 'quality': '1080p', 'is_3d': True}
|
||||
'/home/namehou/Movie Monuments (2013)/Movie Monuments Full-OU.mkv': {'size': 4500, 'quality': '1080p', 'is_3d': True},
|
||||
'/volume1/Public/3D/Moviename/Moviename (2009).3D.SBS.ts': {'size': 7500, 'quality': '1080p', 'is_3d': True},
|
||||
'/volume1/Public/Moviename/Moviename (2009).ts': {'size': 5500, 'quality': '1080p'},
|
||||
'/movies/BluRay HDDVD H.264 MKV 720p EngSub/QuiQui le fou (criterion collection #123, 1915)/QuiQui le fou (1915) 720p x264 BluRay.mkv': {'size': 5500, 'quality': '720p'},
|
||||
'C:\\movies\QuiQui le fou (collection #123, 1915)\QuiQui le fou (1915) 720p x264 BluRay.mkv': {'size': 5500, 'quality': '720p'},
|
||||
'C:\\movies\QuiQui le fou (collection #123, 1915)\QuiQui le fou (1915) half-sbs 720p x264 BluRay.mkv': {'size': 5500, 'quality': '720p', 'is_3d': True},
|
||||
'Moviename 2014 720p HDCAM XviD DualAudio': {'size': 4000, 'quality': 'cam'},
|
||||
'Moviename (2014) - 720p CAM x264': {'size': 2250, 'quality': 'cam'},
|
||||
'Movie Name (2014).mp4': {'size': 750, 'quality': 'brrip'},
|
||||
'Moviename.2014.720p.R6.WEB-DL.x264.AC3-xyz': {'size': 750, 'quality': 'r5'},
|
||||
'Movie name 2014 New Source 720p HDCAM x264 AC3 xyz': {'size': 750, 'quality': 'cam'},
|
||||
'Movie.Name.2014.720p.HD.TS.AC3.x264': {'size': 750, 'quality': 'ts'}
|
||||
}
|
||||
|
||||
correct = 0
|
||||
@@ -459,7 +482,10 @@ class QualityPlugin(Plugin):
|
||||
test_quality = self.guess(files = [name], extra = tests[name].get('extra', None), size = tests[name].get('size', None)) or {}
|
||||
success = test_quality.get('identifier') == tests[name]['quality'] and test_quality.get('is_3d') == tests[name].get('is_3d', False)
|
||||
if not success:
|
||||
log.error('%s failed check, thinks it\'s %s', (name, test_quality.get('identifier')))
|
||||
log.error('%s failed check, thinks it\'s "%s" expecting "%s"', (name,
|
||||
test_quality.get('identifier') + (' 3D' if test_quality.get('is_3d') else ''),
|
||||
tests[name]['quality'] + (' 3D' if tests[name].get('is_3d') else '')
|
||||
))
|
||||
|
||||
correct += success
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ var QualityBase = new Class({
|
||||
|
||||
self.qualities = data.qualities;
|
||||
|
||||
self.profiles_list = null;
|
||||
self.profiles = [];
|
||||
Array.each(data.profiles, self.createProfilesClass.bind(self));
|
||||
|
||||
@@ -35,7 +36,7 @@ var QualityBase = new Class({
|
||||
}).pick();
|
||||
}
|
||||
catch(e){}
|
||||
|
||||
|
||||
return {}
|
||||
},
|
||||
|
||||
@@ -106,14 +107,13 @@ var QualityBase = new Class({
|
||||
createProfileOrdering: function(){
|
||||
var self = this;
|
||||
|
||||
var profile_list;
|
||||
self.settings.createGroup({
|
||||
'label': 'Profile Defaults',
|
||||
'description': '(Needs refresh \'' +(App.isMac() ? 'CMD+R' : 'F5')+ '\' after editing)'
|
||||
}).adopt(
|
||||
new Element('.ctrlHolder#profile_ordering').adopt(
|
||||
new Element('label[text=Order]'),
|
||||
profile_list = new Element('ul'),
|
||||
self.profiles_list = new Element('ul'),
|
||||
new Element('p.formHint', {
|
||||
'html': 'Change the order the profiles are in the dropdown list. Uncheck to hide it completely.<br />First one will be default.'
|
||||
})
|
||||
@@ -133,7 +133,7 @@ var QualityBase = new Class({
|
||||
'text': profile.data.label
|
||||
}),
|
||||
new Element('span.handle')
|
||||
).inject(profile_list);
|
||||
).inject(self.profiles_list);
|
||||
|
||||
new Form.Check(check);
|
||||
|
||||
@@ -141,7 +141,7 @@ var QualityBase = new Class({
|
||||
|
||||
// Sortable
|
||||
var sorted_changed = false;
|
||||
self.profile_sortable = new Sortables(profile_list, {
|
||||
self.profile_sortable = new Sortables(self.profiles_list, {
|
||||
'revert': true,
|
||||
'handle': '.handle',
|
||||
'opacity': 0.5,
|
||||
@@ -163,7 +163,7 @@ var QualityBase = new Class({
|
||||
ids = [],
|
||||
hidden = [];
|
||||
|
||||
self.profile_sortable.list.getElements('li').each(function(el, nr){
|
||||
self.profiles_list.getElements('li').each(function(el, nr){
|
||||
ids.include(el.get('data-id'));
|
||||
hidden[nr] = +!el.getElement('input[type=checkbox]').get('checked');
|
||||
});
|
||||
|
||||
@@ -3,7 +3,7 @@ import os
|
||||
import time
|
||||
import traceback
|
||||
|
||||
from CodernityDB.database import RecordDeleted
|
||||
from CodernityDB.database import RecordDeleted, RecordNotFound
|
||||
from couchpotato import md5, get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, addEvent
|
||||
@@ -79,6 +79,13 @@ class Release(Plugin):
|
||||
try:
|
||||
db.get('id', release.get('key'))
|
||||
media_exist.append(release.get('key'))
|
||||
|
||||
try:
|
||||
if release['doc'].get('status') == 'ignore':
|
||||
release['doc']['status'] = 'ignored'
|
||||
db.update(release['doc'])
|
||||
except:
|
||||
log.error('Failed fixing mis-status tag: %s', traceback.format_exc())
|
||||
except RecordDeleted:
|
||||
db.delete(release['doc'])
|
||||
log.debug('Deleted orphaned release: %s', release['doc'])
|
||||
@@ -100,9 +107,11 @@ class Release(Plugin):
|
||||
if rel['status'] in ['available']:
|
||||
self.delete(rel['_id'])
|
||||
|
||||
# Set all snatched and downloaded releases to ignored to make sure they are ignored when re-adding the move
|
||||
# Set all snatched and downloaded releases to ignored to make sure they are ignored when re-adding the media
|
||||
elif rel['status'] in ['snatched', 'downloaded']:
|
||||
self.updateStatus(rel['_id'], status = 'ignore')
|
||||
self.updateStatus(rel['_id'], status = 'ignored')
|
||||
|
||||
fireEvent('media.untag', media.get('_id'), 'recent', single = True)
|
||||
|
||||
def add(self, group, update_info = True, update_id = None):
|
||||
|
||||
@@ -149,7 +158,7 @@ class Release(Plugin):
|
||||
r = db.get('release_identifier', release_identifier, with_doc = True)['doc']
|
||||
r['media_id'] = media['_id']
|
||||
except:
|
||||
log.error('Failed updating release by identifier: %s', traceback.format_exc())
|
||||
log.debug('Failed updating release by identifier "%s". Inserting new.', release_identifier)
|
||||
r = db.insert(release)
|
||||
|
||||
# Update with ref and _id
|
||||
@@ -162,7 +171,7 @@ class Release(Plugin):
|
||||
release['files'] = dict((k, [toUnicode(x) for x in v]) for k, v in group['files'].items() if v)
|
||||
db.update(release)
|
||||
|
||||
fireEvent('media.restatus', media['_id'])
|
||||
fireEvent('media.restatus', media['_id'], single = True)
|
||||
|
||||
return True
|
||||
except:
|
||||
@@ -184,7 +193,7 @@ class Release(Plugin):
|
||||
db.delete(rel)
|
||||
return True
|
||||
except RecordDeleted:
|
||||
log.error('Already deleted: %s', release_id)
|
||||
log.debug('Already deleted: %s', release_id)
|
||||
return True
|
||||
except:
|
||||
log.error('Failed: %s', traceback.format_exc())
|
||||
@@ -318,7 +327,7 @@ class Release(Plugin):
|
||||
log_movie = '%s (%s) in %s' % (getTitle(media), media['info'].get('year'), rls['quality'])
|
||||
snatch_message = 'Snatched "%s": %s' % (data.get('name'), log_movie)
|
||||
log.info(snatch_message)
|
||||
fireEvent('%s.snatched' % data['type'], message = snatch_message, data = rls)
|
||||
fireEvent('%s.snatched' % data['type'], message = snatch_message, data = media)
|
||||
|
||||
# Mark release as snatched
|
||||
if renamer_enabled:
|
||||
@@ -329,22 +338,14 @@ class Release(Plugin):
|
||||
|
||||
if media['status'] == 'active':
|
||||
profile = db.get('id', media['profile_id'])
|
||||
finished = False
|
||||
if rls['quality'] in profile['qualities']:
|
||||
nr = profile['qualities'].index(rls['quality'])
|
||||
finished = profile['finish'][nr]
|
||||
|
||||
if finished:
|
||||
if fireEvent('quality.isfinish', {'identifier': rls['quality'], 'is_3d': rls.get('is_3d', False)}, profile, single = True):
|
||||
log.info('Renamer disabled, marking media as finished: %s', log_movie)
|
||||
|
||||
# Mark release done
|
||||
self.updateStatus(rls['_id'], status = 'done')
|
||||
|
||||
# Mark media done
|
||||
mdia = db.get('id', media['_id'])
|
||||
mdia['status'] = 'done'
|
||||
mdia['last_edit'] = int(time.time())
|
||||
db.update(mdia)
|
||||
fireEvent('media.restatus', media['_id'], single = True)
|
||||
|
||||
return True
|
||||
|
||||
@@ -371,7 +372,11 @@ class Release(Plugin):
|
||||
continue
|
||||
|
||||
if rel['score'] <= 0:
|
||||
log.info('Ignored, score to low: %s', rel['name'])
|
||||
log.info('Ignored, score "%s" to low: %s', (rel['score'], rel['name']))
|
||||
continue
|
||||
|
||||
if rel['size'] <= 50:
|
||||
log.info('Ignored, size "%sMB" to low: %s', (rel['size'], rel['name']))
|
||||
continue
|
||||
|
||||
rel['wait_for'] = False
|
||||
@@ -469,7 +474,7 @@ class Release(Plugin):
|
||||
rel = db.get('id', release_id)
|
||||
if rel and rel.get('status') != status:
|
||||
|
||||
release_name = rel['info'].get('name')
|
||||
release_name = None
|
||||
if rel.get('files'):
|
||||
for file_type in rel.get('files', {}):
|
||||
if file_type == 'movie':
|
||||
@@ -477,6 +482,9 @@ class Release(Plugin):
|
||||
release_name = os.path.basename(release_file)
|
||||
break
|
||||
|
||||
if not release_name and rel.get('info'):
|
||||
release_name = rel['info'].get('name')
|
||||
|
||||
#update status in Db
|
||||
log.debug('Marking release %s as %s', (release_name, status))
|
||||
rel['status'] = status
|
||||
@@ -500,8 +508,15 @@ class Release(Plugin):
|
||||
status = list(status if isinstance(status, (list, tuple)) else [status])
|
||||
|
||||
for s in status:
|
||||
for ms in db.get_many('release_status', s, with_doc = with_doc):
|
||||
yield ms['doc'] if with_doc else ms
|
||||
for ms in db.get_many('release_status', s):
|
||||
if with_doc:
|
||||
try:
|
||||
doc = db.get('id', ms['_id'])
|
||||
yield doc
|
||||
except RecordNotFound:
|
||||
log.debug('Record not found, skipping: %s', ms['_id'])
|
||||
else:
|
||||
yield ms
|
||||
|
||||
def forMedia(self, media_id):
|
||||
|
||||
|
||||
@@ -123,11 +123,6 @@ class Renamer(Plugin):
|
||||
no_process = [to_folder]
|
||||
cat_list = fireEvent('category.all', single = True) or []
|
||||
no_process.extend([item['destination'] for item in cat_list])
|
||||
try:
|
||||
if Env.setting('library', section = 'manage').strip():
|
||||
no_process.extend([sp(manage_folder) for manage_folder in splitString(Env.setting('library', section = 'manage'), '::')])
|
||||
except:
|
||||
pass
|
||||
|
||||
# Check to see if the no_process folders are inside the "from" folder.
|
||||
if not os.path.isdir(base_folder) or not os.path.isdir(to_folder):
|
||||
@@ -136,7 +131,7 @@ class Renamer(Plugin):
|
||||
else:
|
||||
for item in no_process:
|
||||
if isSubFolder(item, base_folder):
|
||||
log.error('To protect your data, the media libraries can\'t be inside of or the same as the "from" folder.')
|
||||
log.error('To protect your data, the media libraries can\'t be inside of or the same as the "from" folder. "%s" in "%s"', (item, base_folder))
|
||||
return
|
||||
|
||||
# Check to see if the no_process folders are inside the provided media_folder
|
||||
@@ -168,7 +163,7 @@ class Renamer(Plugin):
|
||||
if media_folder:
|
||||
for item in no_process:
|
||||
if isSubFolder(item, media_folder):
|
||||
log.error('To protect your data, the media libraries can\'t be inside of or the same as the provided media folder.')
|
||||
log.error('To protect your data, the media libraries can\'t be inside of or the same as the provided media folder. "%s" in "%s"', (item, media_folder))
|
||||
return
|
||||
|
||||
# Make sure a checkSnatched marked all downloads/seeds as such
|
||||
@@ -202,14 +197,18 @@ class Renamer(Plugin):
|
||||
db = get_db()
|
||||
|
||||
# Extend the download info with info stored in the downloaded release
|
||||
keep_original = self.moveTypeIsLinked()
|
||||
is_torrent = False
|
||||
if release_download:
|
||||
release_download = self.extendReleaseDownload(release_download)
|
||||
is_torrent = self.downloadIsTorrent(release_download)
|
||||
keep_original = True if is_torrent and self.conf('file_action') not in ['move'] else keep_original
|
||||
|
||||
# Unpack any archives
|
||||
extr_files = None
|
||||
if self.conf('unrar'):
|
||||
folder, media_folder, files, extr_files = self.extractFiles(folder = folder, media_folder = media_folder, files = files,
|
||||
cleanup = self.conf('cleanup') and not self.downloadIsTorrent(release_download))
|
||||
cleanup = self.conf('cleanup') and not keep_original)
|
||||
|
||||
groups = fireEvent('scanner.scan', folder = folder if folder else base_folder,
|
||||
files = files, release_download = release_download, return_ignored = False, single = True) or []
|
||||
@@ -326,7 +325,7 @@ class Renamer(Plugin):
|
||||
if file_type is 'nfo' and not self.conf('rename_nfo'):
|
||||
log.debug('Skipping, renaming of %s disabled', file_type)
|
||||
for current_file in group['files'][file_type]:
|
||||
if self.conf('cleanup') and (not self.downloadIsTorrent(release_download) or self.fileIsAdded(current_file, group)):
|
||||
if self.conf('cleanup') and (not keep_original or self.fileIsAdded(current_file, group)):
|
||||
remove_files.append(current_file)
|
||||
continue
|
||||
|
||||
@@ -446,19 +445,22 @@ class Renamer(Plugin):
|
||||
# Before renaming, remove the lower quality files
|
||||
remove_leftovers = True
|
||||
|
||||
# Mark movie "done" once it's found the quality with the finish check
|
||||
# Get media quality profile
|
||||
profile = None
|
||||
try:
|
||||
if media.get('status') == 'active' and media.get('profile_id'):
|
||||
if media.get('profile_id'):
|
||||
try:
|
||||
profile = db.get('id', media['profile_id'])
|
||||
if fireEvent('quality.isfinish', group['meta_data']['quality'], profile, single = True):
|
||||
mdia = db.get('id', media['_id'])
|
||||
mdia['status'] = 'done'
|
||||
mdia['last_edit'] = int(time.time())
|
||||
db.update(mdia)
|
||||
except:
|
||||
# Set profile to None as it does not exist anymore
|
||||
mdia = db.get('id', media['_id'])
|
||||
mdia['profile_id'] = None
|
||||
db.update(mdia)
|
||||
log.error('Error getting quality profile for %s: %s', (media_title, traceback.format_exc()))
|
||||
else:
|
||||
log.debug('Media has no quality profile: %s', media_title)
|
||||
|
||||
except:
|
||||
log.error('Failed marking movie finished: %s', (traceback.format_exc()))
|
||||
# Mark media for dashboard
|
||||
mark_as_recent = False
|
||||
|
||||
# Go over current movie releases
|
||||
for release in fireEvent('release.for_media', media['_id'], single = True):
|
||||
@@ -468,7 +470,7 @@ class Renamer(Plugin):
|
||||
|
||||
# This is where CP removes older, lesser quality releases or releases that are not wanted anymore
|
||||
is_higher = fireEvent('quality.ishigher', \
|
||||
group['meta_data']['quality'], {'identifier': release['quality'], 'is_3d': release.get('is_3d', 0)}, profile, single = True)
|
||||
group['meta_data']['quality'], {'identifier': release['quality'], 'is_3d': release.get('is_3d', False)}, profile, single = True)
|
||||
|
||||
if is_higher == 'higher':
|
||||
log.info('Removing lesser or not wanted quality %s for %s.', (media_title, release.get('quality')))
|
||||
@@ -493,7 +495,7 @@ class Renamer(Plugin):
|
||||
self.tagRelease(group = group, tag = 'exists')
|
||||
|
||||
# Notify on rename fail
|
||||
download_message = 'Renaming of %s (%s) cancelled, exists in %s already.' % (media_title, group['meta_data']['quality']['label'], release.get('identifier'))
|
||||
download_message = 'Renaming of %s (%s) cancelled, exists in %s already.' % (media_title, group['meta_data']['quality']['label'], release.get('quality'))
|
||||
fireEvent('movie.renaming.canceled', message = download_message, data = group)
|
||||
remove_leftovers = False
|
||||
|
||||
@@ -506,14 +508,21 @@ class Renamer(Plugin):
|
||||
# Set the release to downloaded
|
||||
fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True)
|
||||
group['release_download'] = release_download
|
||||
mark_as_recent = True
|
||||
elif release_download['status'] == 'seeding':
|
||||
# Set the release to seeding
|
||||
fireEvent('release.update_status', release['_id'], status = 'seeding', single = True)
|
||||
mark_as_recent = True
|
||||
|
||||
elif release.get('identifier') == group['meta_data']['quality']['identifier']:
|
||||
elif release.get('quality') == group['meta_data']['quality']['identifier']:
|
||||
# Set the release to downloaded
|
||||
fireEvent('release.update_status', release['_id'], status = 'downloaded', single = True)
|
||||
group['release_download'] = release_download
|
||||
mark_as_recent = True
|
||||
|
||||
# Mark media for dashboard
|
||||
if mark_as_recent:
|
||||
fireEvent('media.tag', group['media'].get('_id'), 'recent', update_edited = True, single = True)
|
||||
|
||||
# Remove leftover files
|
||||
if not remove_leftovers: # Don't remove anything
|
||||
@@ -522,7 +531,7 @@ class Renamer(Plugin):
|
||||
log.debug('Removing leftover files')
|
||||
for current_file in group['files']['leftover']:
|
||||
if self.conf('cleanup') and not self.conf('move_leftover') and \
|
||||
(not self.downloadIsTorrent(release_download) or self.fileIsAdded(current_file, group)):
|
||||
(not keep_original or self.fileIsAdded(current_file, group)):
|
||||
remove_files.append(current_file)
|
||||
|
||||
# Remove files
|
||||
@@ -569,7 +578,7 @@ class Renamer(Plugin):
|
||||
self.makeDir(os.path.dirname(dst))
|
||||
|
||||
try:
|
||||
self.moveFile(src, dst, forcemove = not self.downloadIsTorrent(release_download) or self.fileIsAdded(src, group))
|
||||
self.moveFile(src, dst, use_default = not is_torrent or self.fileIsAdded(src, group))
|
||||
group['renamed_files'].append(dst)
|
||||
except:
|
||||
log.error('Failed renaming the file "%s" : %s', (os.path.basename(src), traceback.format_exc()))
|
||||
@@ -585,7 +594,7 @@ class Renamer(Plugin):
|
||||
self.untagRelease(group = group, tag = 'failed_rename')
|
||||
|
||||
# Tag folder if it is in the 'from' folder and it will not be removed because it is a torrent
|
||||
if self.movieInFromFolder(media_folder) and self.downloadIsTorrent(release_download):
|
||||
if self.movieInFromFolder(media_folder) and keep_original:
|
||||
self.tagRelease(group = group, tag = 'renamed_already')
|
||||
|
||||
# Remove matching releases
|
||||
@@ -596,7 +605,7 @@ class Renamer(Plugin):
|
||||
except:
|
||||
log.error('Failed removing %s: %s', (release, traceback.format_exc()))
|
||||
|
||||
if group['dirname'] and group['parentdir'] and not self.downloadIsTorrent(release_download):
|
||||
if group['dirname'] and group['parentdir'] and not keep_original:
|
||||
if media_folder:
|
||||
# Delete the movie folder
|
||||
group_folder = media_folder
|
||||
@@ -758,10 +767,15 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
|
||||
return False
|
||||
|
||||
def moveFile(self, old, dest, forcemove = False):
|
||||
def moveFile(self, old, dest, use_default = False):
|
||||
dest = sp(dest)
|
||||
try:
|
||||
if forcemove or self.conf('file_action') not in ['copy', 'link']:
|
||||
|
||||
move_type = self.conf('file_action')
|
||||
if use_default:
|
||||
move_type = self.conf('default_file_action')
|
||||
|
||||
if move_type not in ['copy', 'link']:
|
||||
try:
|
||||
shutil.move(old, dest)
|
||||
except:
|
||||
@@ -770,16 +784,16 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
os.unlink(old)
|
||||
else:
|
||||
raise
|
||||
elif self.conf('file_action') == 'copy':
|
||||
elif move_type == 'copy':
|
||||
shutil.copy(old, dest)
|
||||
elif self.conf('file_action') == 'link':
|
||||
else:
|
||||
# First try to hardlink
|
||||
try:
|
||||
log.debug('Hardlinking file "%s" to "%s"...', (old, dest))
|
||||
link(old, dest)
|
||||
except:
|
||||
# Try to simlink next
|
||||
log.debug('Couldn\'t hardlink file "%s" to "%s". Simlinking instead. Error: %s.', (old, dest, traceback.format_exc()))
|
||||
log.debug('Couldn\'t hardlink file "%s" to "%s". Symlinking instead. Error: %s.', (old, dest, traceback.format_exc()))
|
||||
shutil.copy(old, dest)
|
||||
try:
|
||||
symlink(dest, old + '.link')
|
||||
@@ -1079,6 +1093,9 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
return False
|
||||
return src in group['before_rename']
|
||||
|
||||
def moveTypeIsLinked(self):
|
||||
return self.conf('default_file_action') in ['copy', 'link']
|
||||
|
||||
def statusInfoComplete(self, release_download):
|
||||
return release_download.get('id') and release_download.get('downloader') and release_download.get('folder')
|
||||
|
||||
@@ -1130,14 +1147,20 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
|
||||
log.info('Archive %s found. Extracting...', os.path.basename(archive['file']))
|
||||
try:
|
||||
rar_handle = RarFile(archive['file'])
|
||||
rar_handle = RarFile(archive['file'], custom_path = self.conf('unrar_path'))
|
||||
extr_path = os.path.join(from_folder, os.path.relpath(os.path.dirname(archive['file']), folder))
|
||||
self.makeDir(extr_path)
|
||||
for packedinfo in rar_handle.infolist():
|
||||
if not packedinfo.isdir and not os.path.isfile(sp(os.path.join(extr_path, os.path.basename(packedinfo.filename)))):
|
||||
extr_file_path = sp(os.path.join(extr_path, os.path.basename(packedinfo.filename)))
|
||||
if not packedinfo.isdir and not os.path.isfile(extr_file_path):
|
||||
log.debug('Extracting %s...', packedinfo.filename)
|
||||
rar_handle.extract(condition = [packedinfo.index], path = extr_path, withSubpath = False, overwrite = False)
|
||||
extr_files.append(sp(os.path.join(extr_path, os.path.basename(packedinfo.filename))))
|
||||
if self.conf('unrar_modify_date'):
|
||||
try:
|
||||
os.utime(extr_file_path, (os.path.getatime(archive['file']), os.path.getmtime(archive['file'])))
|
||||
except:
|
||||
log.error('Rar modify date enabled, but failed: %s', traceback.format_exc())
|
||||
extr_files.append(extr_file_path)
|
||||
del rar_handle
|
||||
except Exception as e:
|
||||
log.error('Failed to extract %s: %s %s', (archive['file'], e, traceback.format_exc()))
|
||||
@@ -1272,6 +1295,18 @@ config = [{
|
||||
'description': 'Extract rar files if found.',
|
||||
'default': False,
|
||||
},
|
||||
{
|
||||
'advanced': True,
|
||||
'name': 'unrar_path',
|
||||
'description': 'Custom path to unrar bin',
|
||||
},
|
||||
{
|
||||
'advanced': True,
|
||||
'name': 'unrar_modify_date',
|
||||
'type': 'bool',
|
||||
'description': ('Set modify date of unrar-ed files to the rar-file\'s date.', 'This will allow XBMC to recognize extracted files as recently added even if the movie was released some time ago.'),
|
||||
'default': False,
|
||||
},
|
||||
{
|
||||
'name': 'cleanup',
|
||||
'type': 'bool',
|
||||
@@ -1322,14 +1357,23 @@ config = [{
|
||||
'label': 'Folder-Separator',
|
||||
'description': ('Replace all the spaces with a character.', 'Example: ".", "-" (without quotes). Leave empty to use spaces.'),
|
||||
},
|
||||
{
|
||||
'name': 'default_file_action',
|
||||
'label': 'Default File Action',
|
||||
'default': 'move',
|
||||
'type': 'dropdown',
|
||||
'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')],
|
||||
'description': ('<strong>Link</strong>, <strong>Copy</strong> or <strong>Move</strong> after download completed.',
|
||||
'Link first tries <a href="http://en.wikipedia.org/wiki/Hard_link">hard link</a>, then <a href="http://en.wikipedia.org/wiki/Sym_link">sym link</a> and falls back to Copy.'),
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
'name': 'file_action',
|
||||
'label': 'Torrent File Action',
|
||||
'default': 'link',
|
||||
'type': 'dropdown',
|
||||
'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')],
|
||||
'description': ('<strong>Link</strong>, <strong>Copy</strong> or <strong>Move</strong> after download completed.',
|
||||
'Link first tries <a href="http://en.wikipedia.org/wiki/Hard_link">hard link</a>, then <a href="http://en.wikipedia.org/wiki/Sym_link">sym link</a> and falls back to Copy. It is perfered to use link when downloading torrents as it will save you space, while still beeing able to seed.'),
|
||||
'description': 'See above. It is prefered to use link when downloading torrents as it will save you space, while still beeing able to seed.',
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -105,7 +105,7 @@ class Scanner(Plugin):
|
||||
'HDTV': ['hdtv']
|
||||
}
|
||||
|
||||
clean = '([ _\,\.\(\)\[\]\-]|^)(3d|hsbs|sbs|ou|extended.cut|directors.cut|french|fr|swedisch|sw|danish|dutch|nl|swesub|subs|spanish|german|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdr|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip' \
|
||||
clean = '([ _\,\.\(\)\[\]\-]|^)(3d|hsbs|sbs|half.sbs|full.sbs|ou|half.ou|full.ou|extended|extended.cut|directors.cut|french|fr|swedisch|sw|danish|dutch|nl|swesub|subs|spanish|german|ac3|dts|custom|dc|divx|divx5|dsr|dsrip|dutch|dvd|dvdr|dvdrip|dvdscr|dvdscreener|screener|dvdivx|cam|fragment|fs|hdtv|hdrip' \
|
||||
'|hdtvrip|webdl|web.dl|webrip|web.rip|internal|limited|multisubs|ntsc|ogg|ogm|pal|pdtv|proper|repack|rerip|retail|r3|r5|bd5|se|svcd|swedish|german|read.nfo|nfofix|unrated|ws|telesync|ts|telecine|tc|brrip|bdrip|video_ts|audio_ts|480p|480i|576p|576i|720p|720i|1080p|1080i|hrhd|hrhdtv|hddvd|bluray|x264|h264|xvid|xvidvd|xxx|www.www|hc|\[.*\])(?=[ _\,\.\(\)\[\]\-]|$)'
|
||||
multipart_regex = [
|
||||
'[ _\.-]+cd[ _\.-]*([0-9a-d]+)', #*cd1
|
||||
@@ -553,7 +553,7 @@ class Scanner(Plugin):
|
||||
scan_result = []
|
||||
for p in paths:
|
||||
if not group['is_dvd']:
|
||||
video = Video.from_path(toUnicode(p))
|
||||
video = Video.from_path(sp(p))
|
||||
video_result = [(video, video.scan())]
|
||||
scan_result.extend(video_result)
|
||||
|
||||
@@ -634,7 +634,14 @@ class Scanner(Plugin):
|
||||
|
||||
name_year = self.getReleaseNameYear(identifier, file_name = filename if not group['is_dvd'] else None)
|
||||
if name_year.get('name') and name_year.get('year'):
|
||||
movie = fireEvent('movie.search', q = '%(name)s %(year)s' % name_year, merge = True, limit = 1)
|
||||
search_q = '%(name)s %(year)s' % name_year
|
||||
movie = fireEvent('movie.search', q = search_q, merge = True, limit = 1)
|
||||
|
||||
# Try with other
|
||||
if len(movie) == 0 and name_year.get('other') and name_year['other'].get('name') and name_year['other'].get('year'):
|
||||
search_q2 = '%(name)s %(year)s' % name_year.get('other')
|
||||
if search_q2 != search_q:
|
||||
movie = fireEvent('movie.search', q = search_q2, merge = True, limit = 1)
|
||||
|
||||
if len(movie) > 0:
|
||||
imdb_id = movie[0].get('imdb')
|
||||
@@ -903,6 +910,7 @@ class Scanner(Plugin):
|
||||
log.debug('Could not detect via guessit "%s": %s', (file_name, traceback.format_exc()))
|
||||
|
||||
# Backup to simple
|
||||
release_name = os.path.basename(release_name.replace('\\', '/'))
|
||||
cleaned = ' '.join(re.split('\W+', simplifyString(release_name)))
|
||||
cleaned = re.sub(self.clean, ' ', cleaned)
|
||||
|
||||
@@ -937,8 +945,11 @@ class Scanner(Plugin):
|
||||
pass
|
||||
|
||||
if cp_guess.get('year') == guess.get('year') and len(cp_guess.get('name', '')) > len(guess.get('name', '')):
|
||||
cp_guess['other'] = guess
|
||||
return cp_guess
|
||||
elif guess == {}:
|
||||
cp_guess['other'] = guess
|
||||
return cp_guess
|
||||
|
||||
guess['other'] = cp_guess
|
||||
return guess
|
||||
|
||||
@@ -32,7 +32,7 @@ class Trailer(Plugin):
|
||||
destination = os.path.join(group['destination_dir'], filename)
|
||||
if not os.path.isfile(destination):
|
||||
trailer_file = fireEvent('file.download', url = trailer, dest = destination, urlopen_kwargs = {'headers': {'User-Agent': 'Quicktime'}}, single = True)
|
||||
if os.path.getsize(trailer_file) < (1024 * 1024): # Don't trust small trailers (1MB), try next one
|
||||
if trailer_file and os.path.getsize(trailer_file) < (1024 * 1024): # Don't trust small trailers (1MB), try next one
|
||||
os.unlink(trailer_file)
|
||||
continue
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user