Merge branch 'refs/heads/develop'
This commit is contained in:
@@ -1,4 +1,5 @@
|
|||||||
import collections
|
import collections
|
||||||
|
import ctypes
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import platform
|
import platform
|
||||||
@@ -291,9 +292,14 @@ def dictIsSubset(a, b):
|
|||||||
return all([k in b and b[k] == v for k, v in a.items()])
|
return all([k in b and b[k] == v for k, v in a.items()])
|
||||||
|
|
||||||
|
|
||||||
|
# Returns True if sub_folder is the same as or inside base_folder
|
||||||
def isSubFolder(sub_folder, base_folder):
|
def isSubFolder(sub_folder, base_folder):
|
||||||
# Returns True if sub_folder is the same as or inside base_folder
|
if base_folder and sub_folder:
|
||||||
return base_folder and sub_folder and ss(os.path.normpath(base_folder).rstrip(os.path.sep) + os.path.sep) in ss(os.path.normpath(sub_folder).rstrip(os.path.sep) + os.path.sep)
|
base = sp(os.path.realpath(base_folder)) + os.path.sep
|
||||||
|
subfolder = sp(os.path.realpath(sub_folder)) + os.path.sep
|
||||||
|
return os.path.commonprefix([subfolder, base]) == base
|
||||||
|
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
# From SABNZBD
|
# From SABNZBD
|
||||||
@@ -341,3 +347,37 @@ def removePyc(folder, only_excess = True, show_logs = True):
|
|||||||
os.rmdir(full_path)
|
os.rmdir(full_path)
|
||||||
except:
|
except:
|
||||||
log.error('Couldn\'t remove empty directory %s: %s', (full_path, traceback.format_exc()))
|
log.error('Couldn\'t remove empty directory %s: %s', (full_path, traceback.format_exc()))
|
||||||
|
|
||||||
|
|
||||||
|
def getFreeSpace(directories):
|
||||||
|
|
||||||
|
single = not isinstance(directories, (tuple, list))
|
||||||
|
if single:
|
||||||
|
directories = [directories]
|
||||||
|
|
||||||
|
free_space = {}
|
||||||
|
for folder in 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)]
|
||||||
|
|
||||||
|
if single: return size
|
||||||
|
|
||||||
|
free_space[folder] = size
|
||||||
|
|
||||||
|
return free_space
|
||||||
|
|||||||
@@ -477,7 +477,8 @@ class MediaPlugin(MediaBase):
|
|||||||
if fireEvent('quality.isfinish', {'identifier': release['quality'], 'is_3d': release.get('is_3d', False)}, profile, timedelta(seconds = time.time() - release['last_edit']).days, single = True):
|
if fireEvent('quality.isfinish', {'identifier': release['quality'], 'is_3d': release.get('is_3d', False)}, profile, timedelta(seconds = time.time() - release['last_edit']).days, single = True):
|
||||||
m['status'] = 'done'
|
m['status'] = 'done'
|
||||||
except RecordNotFound:
|
except RecordNotFound:
|
||||||
log.debug('Failed restatus: %s', traceback.format_exc())
|
log.debug('Failed restatus, keeping previous: %s', traceback.format_exc())
|
||||||
|
m['status'] = previous_status
|
||||||
|
|
||||||
# Only update when status has changed
|
# Only update when status has changed
|
||||||
if previous_status != m['status']:
|
if previous_status != m['status']:
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ from couchpotato import get_db
|
|||||||
from couchpotato.api import addApiView
|
from couchpotato.api import addApiView
|
||||||
from couchpotato.core.event import addEvent, fireEvent
|
from couchpotato.core.event import addEvent, fireEvent
|
||||||
from couchpotato.core.helpers.encoding import toUnicode
|
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.logger import CPLog
|
||||||
from couchpotato.core.plugins.base import Plugin
|
from couchpotato.core.plugins.base import Plugin
|
||||||
from couchpotato.environment import Env
|
from couchpotato.environment import Env
|
||||||
@@ -32,6 +32,8 @@ class FileManager(Plugin):
|
|||||||
|
|
||||||
fireEvent('schedule.interval', 'file.cleanup', self.cleanup, hours = 24)
|
fireEvent('schedule.interval', 'file.cleanup', self.cleanup, hours = 24)
|
||||||
|
|
||||||
|
addEvent('app.test', self.doSubfolderTest)
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
|
|
||||||
# Wait a bit after starting before cleanup
|
# Wait a bit after starting before cleanup
|
||||||
@@ -76,3 +78,33 @@ class FileManager(Plugin):
|
|||||||
|
|
||||||
self.createFile(dest, filedata, binary = True)
|
self.createFile(dest, filedata, binary = True)
|
||||||
return dest
|
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,6 +1,4 @@
|
|||||||
import ctypes
|
|
||||||
import os
|
import os
|
||||||
import sys
|
|
||||||
import time
|
import time
|
||||||
import traceback
|
import traceback
|
||||||
|
|
||||||
@@ -8,7 +6,7 @@ from couchpotato import get_db
|
|||||||
from couchpotato.api import addApiView
|
from couchpotato.api import addApiView
|
||||||
from couchpotato.core.event import fireEvent, addEvent, fireEventAsync
|
from couchpotato.core.event import fireEvent, addEvent, fireEventAsync
|
||||||
from couchpotato.core.helpers.encoding import sp
|
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.logger import CPLog
|
||||||
from couchpotato.core.plugins.base import Plugin
|
from couchpotato.core.plugins.base import Plugin
|
||||||
from couchpotato.environment import Env
|
from couchpotato.environment import Env
|
||||||
@@ -273,31 +271,7 @@ class Manage(Plugin):
|
|||||||
fireEvent('release.add', group = group)
|
fireEvent('release.add', group = group)
|
||||||
|
|
||||||
def getDiskSpace(self):
|
def getDiskSpace(self):
|
||||||
|
return getFreeSpace(self.directories())
|
||||||
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
|
|
||||||
|
|
||||||
|
|
||||||
config = [{
|
config = [{
|
||||||
|
|||||||
@@ -47,14 +47,14 @@ var Profile = new Class({
|
|||||||
new Element('div.wait_for.ctrlHolder').adopt(
|
new Element('div.wait_for.ctrlHolder').adopt(
|
||||||
// "Wait the entered number of days for a checked quality, before downloading a lower quality release."
|
// "Wait the entered number of days for a checked quality, before downloading a lower quality release."
|
||||||
new Element('span', {'text':'Wait'}),
|
new Element('span', {'text':'Wait'}),
|
||||||
new Element('input.inlay.xsmall', {
|
new Element('input.inlay.wait_for_input.xsmall', {
|
||||||
'type':'text',
|
'type':'text',
|
||||||
'value': data.wait_for && data.wait_for.length > 0 ? data.wait_for[0] : 0
|
'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', {'text':'day(s) for a better quality '}),
|
||||||
new Element('span.advanced', {'text':'and keep searching'}),
|
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."
|
// "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.advanced', {
|
new Element('input.inlay.xsmall.stop_after_input.advanced', {
|
||||||
'type':'text',
|
'type':'text',
|
||||||
'value': data.stop_after && data.stop_after.length > 0 ? data.stop_after[0] : 0
|
'value': data.stop_after && data.stop_after.length > 0 ? data.stop_after[0] : 0
|
||||||
}),
|
}),
|
||||||
@@ -124,8 +124,8 @@ var Profile = new Class({
|
|||||||
var data = {
|
var data = {
|
||||||
'id' : self.data._id,
|
'id' : self.data._id,
|
||||||
'label' : self.el.getElement('.quality_label input').get('value'),
|
'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'),
|
'stop_after' : self.el.getElement('.stop_after_input').get('value'),
|
||||||
'types': []
|
'types': []
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -387,8 +387,8 @@ class QualityPlugin(Plugin):
|
|||||||
try:
|
try:
|
||||||
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]
|
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['stop_after'][0]):
|
if index == 0 or (profile['finish'][index] and int(release_age) >= int(profile.get('stop_after', [0])[0])):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
return False
|
return False
|
||||||
except:
|
except:
|
||||||
|
|||||||
@@ -639,9 +639,9 @@ class Scanner(Plugin):
|
|||||||
|
|
||||||
# Try with other
|
# Try with other
|
||||||
if len(movie) == 0 and name_year.get('other') and name_year['other'].get('name') and name_year['other'].get('year'):
|
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
|
search_q2 = '%(name)s %(year)s' % name_year.get('other')
|
||||||
if search_q2 != search_q:
|
if search_q2 != search_q:
|
||||||
movie = fireEvent('movie.search', q = '%(name)s %(year)s' % name_year.get('other'), merge = True, limit = 1)
|
movie = fireEvent('movie.search', q = search_q2, merge = True, limit = 1)
|
||||||
|
|
||||||
if len(movie) > 0:
|
if len(movie) > 0:
|
||||||
imdb_id = movie[0].get('imdb')
|
imdb_id = movie[0].get('imdb')
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ class Trailer(Plugin):
|
|||||||
destination = os.path.join(group['destination_dir'], filename)
|
destination = os.path.join(group['destination_dir'], filename)
|
||||||
if not os.path.isfile(destination):
|
if not os.path.isfile(destination):
|
||||||
trailer_file = fireEvent('file.download', url = trailer, dest = destination, urlopen_kwargs = {'headers': {'User-Agent': 'Quicktime'}}, single = True)
|
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)
|
os.unlink(trailer_file)
|
||||||
continue
|
continue
|
||||||
else:
|
else:
|
||||||
|
|||||||
+10
-1
@@ -17,7 +17,7 @@ from couchpotato import KeyHandler, LoginHandler, LogoutHandler
|
|||||||
from couchpotato.api import NonBlockHandler, ApiHandler
|
from couchpotato.api import NonBlockHandler, ApiHandler
|
||||||
from couchpotato.core.event import fireEventAsync, fireEvent
|
from couchpotato.core.event import fireEventAsync, fireEvent
|
||||||
from couchpotato.core.helpers.encoding import sp
|
from couchpotato.core.helpers.encoding import sp
|
||||||
from couchpotato.core.helpers.variable import getDataDir, tryInt
|
from couchpotato.core.helpers.variable import getDataDir, tryInt, getFreeSpace
|
||||||
import requests
|
import requests
|
||||||
from tornado.httpserver import HTTPServer
|
from tornado.httpserver import HTTPServer
|
||||||
from tornado.web import Application, StaticFileHandler, RedirectHandler
|
from tornado.web import Application, StaticFileHandler, RedirectHandler
|
||||||
@@ -195,6 +195,15 @@ def runCouchPotato(options, base_path, args, data_dir = None, log_dir = None, En
|
|||||||
log = CPLog(__name__)
|
log = CPLog(__name__)
|
||||||
log.debug('Started with options %s', options)
|
log.debug('Started with options %s', options)
|
||||||
|
|
||||||
|
# Check available space
|
||||||
|
try:
|
||||||
|
total_space, available_space = getFreeSpace(data_dir)
|
||||||
|
if available_space < 100:
|
||||||
|
log.error('Shutting down as CP needs some space to work. You\'ll get corrupted data otherwise. Only %sMB left', available_space)
|
||||||
|
return
|
||||||
|
except:
|
||||||
|
log.error('Failed getting diskspace: %s', traceback.format_exc())
|
||||||
|
|
||||||
def customwarn(message, category, filename, lineno, file = None, line = None):
|
def customwarn(message, category, filename, lineno, file = None, line = None):
|
||||||
log.warning('%s %s %s line:%s', (category, message, filename, lineno))
|
log.warning('%s %s %s line:%s', (category, message, filename, lineno))
|
||||||
warnings.showwarning = customwarn
|
warnings.showwarning = customwarn
|
||||||
|
|||||||
Reference in New Issue
Block a user