Browse Source

Merge branch 'refs/heads/develop'

pull/2961/merge
Ruud 11 years ago
parent
commit
b19f98ef5b
  1. 1
      CouchPotato.py
  2. 9
      couchpotato/core/event.py
  3. 9
      couchpotato/core/helpers/request.py
  4. 24
      couchpotato/core/helpers/variable.py
  5. 4
      couchpotato/core/media/movie/searcher/main.py
  6. 34
      couchpotato/core/notifications/boxcar2/__init__.py
  7. 39
      couchpotato/core/notifications/boxcar2/main.py
  8. 50
      couchpotato/core/plugins/base.py
  9. 6
      couchpotato/core/plugins/dashboard/main.py
  10. 46
      couchpotato/core/plugins/renamer/main.py
  11. 36
      couchpotato/core/plugins/scanner/main.py
  12. 7
      couchpotato/core/providers/nzb/nzbclub/main.py
  13. 2
      couchpotato/core/providers/torrent/thepiratebay/main.py
  14. 7
      couchpotato/core/providers/userscript/reddit/__init__.py
  15. 17
      couchpotato/core/providers/userscript/reddit/main.py
  16. 7
      libs/axl/axel.py

1
CouchPotato.py

@ -136,6 +136,7 @@ if __name__ == '__main__':
except socket.error as e: except socket.error as e:
# log when socket receives SIGINT, but continue. # log when socket receives SIGINT, but continue.
# previous code would have skipped over other types of IO errors too. # previous code would have skipped over other types of IO errors too.
nr, msg = e
if nr != 4: if nr != 4:
try: try:
l.log.critical(traceback.format_exc()) l.log.critical(traceback.format_exc())

9
couchpotato/core/event.py

@ -1,5 +1,5 @@
from axl.axel import Event from axl.axel import Event
from couchpotato.core.helpers.variable import mergeDicts, natcmp from couchpotato.core.helpers.variable import mergeDicts, natsortKey
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
import threading import threading
import traceback import traceback
@ -51,11 +51,6 @@ def addEvent(name, handler, priority = 100):
}) })
def removeEvent(name, handler):
e = events[name]
e -= handler
def fireEvent(name, *args, **kwargs): def fireEvent(name, *args, **kwargs):
if name not in events: return if name not in events: return
@ -106,7 +101,7 @@ def fireEvent(name, *args, **kwargs):
result = e(*args, **kwargs) result = e(*args, **kwargs)
result_keys = result.keys() result_keys = result.keys()
result_keys.sort(natcmp) result_keys.sort(key = natsortKey)
if options['single'] and not options['merge']: if options['single'] and not options['merge']:
results = None results = None

9
couchpotato/core/helpers/request.py

@ -1,5 +1,5 @@
from couchpotato.core.helpers.encoding import toUnicode from couchpotato.core.helpers.encoding import toUnicode
from couchpotato.core.helpers.variable import natcmp from couchpotato.core.helpers.variable import natsortKey
from urllib import unquote from urllib import unquote
import re import re
@ -8,8 +8,13 @@ def getParams(params):
reg = re.compile('^[a-z0-9_\.]+$') reg = re.compile('^[a-z0-9_\.]+$')
# Sort keys
param_keys = params.keys()
param_keys.sort(key = natsortKey)
temp = {} temp = {}
for param, value in sorted(params.items()): for param in param_keys:
value = params[param]
nest = re.split("([\[\]]+)", param) nest = re.split("([\[\]]+)", param)
if len(nest) > 1: if len(nest) > 1:

24
couchpotato/core/helpers/variable.py

@ -214,16 +214,9 @@ def tryFloat(s):
return float(s) return float(s)
except: return 0 except: return 0
def natsortKey(string_):
def natsortKey(s): """See http://www.codinghorror.com/blog/archives/001018.html"""
return map(tryInt, re.findall(r'(\d+|\D+)', s)) return [int(s) if s.isdigit() else s for s in re.split(r'(\d+)', string_)]
def natcmp(a, b):
a2 = natsortKey(a)
b2 = natsortKey(b)
return (a2 > b2) - (a2 < b2)
def toIterable(value): def toIterable(value):
@ -295,3 +288,14 @@ def dictIsSubset(a, b):
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 # Returns True if sub_folder is the same as or inside base_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) 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)
# From SABNZBD
re_password = [re.compile(r'([^/\\]+)[/\\](.+)'), re.compile(r'(.+){{([^{}]+)}}$'), re.compile(r'(.+)\s+password\s*=\s*(.+)$', re.I)]
def scanForPassword(name):
m = None
for reg in re_password:
m = reg.search(name)
if m: break
if m:
return m.group(1).strip('. '), m.group(2).strip()

4
couchpotato/core/media/movie/searcher/main.py

@ -282,13 +282,15 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
now = int(time.time()) now = int(time.time())
now_year = date.today().year now_year = date.today().year
now_month = date.today().month
if (year is None or year < now_year - 1) and (not dates or (dates.get('theater', 0) == 0 and dates.get('dvd', 0) == 0)): if (year is None or year < now_year - 1) and (not dates or (dates.get('theater', 0) == 0 and dates.get('dvd', 0) == 0)):
return True return True
else: else:
# Don't allow movies with years to far in the future # Don't allow movies with years to far in the future
if year is not None and year > now_year + 1: add_year = 1 if now_month > 10 else 0 # Only allow +1 year if end of the year
if year is not None and year > (now_year + add_year):
return False return False
# For movies before 1972 # For movies before 1972

34
couchpotato/core/notifications/boxcar2/__init__.py

@ -0,0 +1,34 @@
from .main import Boxcar2
def start():
return Boxcar2()
config = [{
'name': 'boxcar2',
'groups': [
{
'tab': 'notifications',
'list': 'notification_providers',
'name': 'boxcar2',
'options': [
{
'name': 'enabled',
'default': 0,
'type': 'enabler',
},
{
'name': 'token',
'description': ('Your Boxcar access token.', 'Can be found in the app under settings')
},
{
'name': 'on_snatch',
'default': 0,
'type': 'bool',
'advanced': True,
'description': 'Also send message when movie is snatched.',
},
],
}
],
}]

39
couchpotato/core/notifications/boxcar2/main.py

@ -0,0 +1,39 @@
from couchpotato.core.helpers.encoding import toUnicode
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
log = CPLog(__name__)
class Boxcar2(Notification):
url = 'https://new.boxcar.io/api/notifications'
def notify(self, message = '', data = None, listener = None):
if not data: data = {}
try:
message = message.strip()
long_message = ''
if listener == 'test':
long_message = 'This is a test message'
elif data.get('identifier'):
long_message = 'More movie info <a href="http://www.imdb.com/title/%s/">on IMDB</a>' % data['identifier']
data = {
'user_credentials': self.conf('token'),
'notification[title]': toUnicode(message),
'notification[long_message]': toUnicode(long_message),
}
self.urlopen(self.url, data = data)
except:
log.error('Make sure the token provided is for the correct device')
return False
log.info('Boxcar notification successful.')
return True
def isEnabled(self):
return super(Boxcar2, self).isEnabled() and self.conf('token')

50
couchpotato/core/plugins/base.py

@ -1,7 +1,7 @@
from couchpotato.core.event import fireEvent, addEvent from couchpotato.core.event import fireEvent, addEvent
from couchpotato.core.helpers.encoding import ss, toSafeString, \ from couchpotato.core.helpers.encoding import ss, toSafeString, \
toUnicode, sp toUnicode, sp
from couchpotato.core.helpers.variable import getExt, md5, isLocalIP from couchpotato.core.helpers.variable import getExt, md5, isLocalIP, scanForPassword, tryInt
from couchpotato.core.logger import CPLog from couchpotato.core.logger import CPLog
from couchpotato.environment import Env from couchpotato.environment import Env
import requests import requests
@ -283,8 +283,17 @@ class Plugin(object):
return value return value
def createNzbName(self, data, media): def createNzbName(self, data, media):
release_name = data.get('name')
tag = self.cpTag(media) tag = self.cpTag(media)
return '%s%s' % (toSafeString(toUnicode(data.get('name'))[:127 - len(tag)]), tag)
# Check if password is filename
name_password = scanForPassword(data.get('name'))
if name_password:
release_name, password = name_password
tag += '{{%s}}' % password
max_length = 127 - len(tag) # Some filesystems don't support 128+ long filenames
return '%s%s' % (toSafeString(toUnicode(release_name)[:max_length]), tag)
def createFileName(self, data, filedata, media): def createFileName(self, data, filedata, media):
name = sp(os.path.join(self.createNzbName(data, media))) name = sp(os.path.join(self.createNzbName(data, media)))
@ -298,6 +307,43 @@ class Plugin(object):
return '' return ''
def checkFilesChanged(self, files, unchanged_for = 60):
now = time.time()
file_too_new = False
for cur_file in files:
# File got removed while checking
if not os.path.isfile(cur_file):
file_too_new = now
break
# File has changed in last 60 seconds
file_time = self.getFileTimes(cur_file)
for t in file_time:
if t > now - unchanged_for:
file_too_new = tryInt(time.time() - t)
break
if file_too_new:
break
if file_too_new:
try:
time_string = time.ctime(file_time[0])
except:
try:
time_string = time.ctime(file_time[1])
except:
time_string = 'unknown'
return file_too_new, time_string
return False, None
def getFileTimes(self, file_path):
return [os.path.getmtime(file_path), os.path.getctime(file_path) if os.name != 'posix' else 0]
def isDisabled(self): def isDisabled(self):
return not self.isEnabled() return not self.isEnabled()

6
couchpotato/core/plugins/dashboard/main.py

@ -1,3 +1,4 @@
from datetime import date
from couchpotato import get_session from couchpotato import get_session
from couchpotato.api import addApiView from couchpotato.api import addApiView
from couchpotato.core.event import fireEvent from couchpotato.core.event import fireEvent
@ -65,6 +66,7 @@ class Dashboard(Plugin):
active = q.all() active = q.all()
movies = [] movies = []
now_year = date.today().year
if len(active) > 0: if len(active) > 0:
@ -91,8 +93,8 @@ class Dashboard(Plugin):
if coming_soon: if coming_soon:
# Don't list older movies # Don't list older movies
if ((not late and (not eta.get('dvd') and not eta.get('theater') or eta.get('dvd') and eta.get('dvd') > (now - 2419200))) or if ((not late and (year >= now_year-1) and (not eta.get('dvd') and not eta.get('theater') or eta.get('dvd') and eta.get('dvd') > (now - 2419200))) or
(late and (eta.get('dvd', 0) > 0 or eta.get('theater')) and eta.get('dvd') < (now - 2419200))): (late and ((year < now_year-1) or ((eta.get('dvd', 0) > 0 or eta.get('theater')) and eta.get('dvd') < (now - 2419200))))):
movie_ids.append(movie_id) movie_ids.append(movie_id)
if len(movie_ids) >= limit: if len(movie_ids) >= limit:

46
couchpotato/core/plugins/renamer/main.py

@ -736,8 +736,15 @@ Remove it if you want it to be renamed (again, or at least let it try again)
def moveFile(self, old, dest, forcemove = False): def moveFile(self, old, dest, forcemove = False):
dest = ss(dest) dest = ss(dest)
try: try:
if forcemove: if forcemove or self.conf('file_action') not in ['copy', 'link']:
shutil.move(old, dest) try:
shutil.move(old, dest)
except:
if os.path.exists(dest):
log.error('Successfully moved file "%s", but something went wrong: %s', (dest, traceback.format_exc()))
os.unlink(old)
else:
raise
elif self.conf('file_action') == 'copy': elif self.conf('file_action') == 'copy':
shutil.copy(old, dest) shutil.copy(old, dest)
elif self.conf('file_action') == 'link': elif self.conf('file_action') == 'link':
@ -755,8 +762,6 @@ Remove it if you want it to be renamed (again, or at least let it try again)
os.rename(old + '.link', old) os.rename(old + '.link', old)
except: except:
log.error('Couldn\'t symlink file "%s" to "%s". Copied instead. Error: %s. ', (old, dest, traceback.format_exc())) log.error('Couldn\'t symlink file "%s" to "%s". Copied instead. Error: %s. ', (old, dest, traceback.format_exc()))
else:
shutil.move(old, dest)
try: try:
os.chmod(dest, Env.getPermission('file')) os.chmod(dest, Env.getPermission('file'))
@ -764,15 +769,6 @@ Remove it if you want it to be renamed (again, or at least let it try again)
os.popen('icacls "' + dest + '"* /reset /T') os.popen('icacls "' + dest + '"* /reset /T')
except: except:
log.error('Failed setting permissions for file: %s, %s', (dest, traceback.format_exc(1))) log.error('Failed setting permissions for file: %s, %s', (dest, traceback.format_exc(1)))
except OSError as err:
# Copying from a filesystem with octal permission to an NTFS file system causes a permission error. In this case ignore it.
if not hasattr(os, 'chmod') or err.errno != errno.EPERM:
raise
else:
if os.path.exists(dest):
os.unlink(old)
except: except:
log.error('Couldn\'t move file "%s" to "%s": %s', (old, dest, traceback.format_exc())) log.error('Couldn\'t move file "%s" to "%s": %s', (old, dest, traceback.format_exc()))
raise raise
@ -1127,29 +1123,9 @@ Remove it if you want it to be renamed (again, or at least let it try again)
# Check if archive is fresh and maybe still copying/moving/downloading, ignore files newer than 1 minute # Check if archive is fresh and maybe still copying/moving/downloading, ignore files newer than 1 minute
if check_file_date: if check_file_date:
file_too_new = False files_too_new, time_string = self.checkFilesChanged(archive['files'])
for cur_file in archive['files']:
if not os.path.isfile(cur_file):
file_too_new = time.time()
break
file_time = [os.path.getmtime(cur_file), os.path.getctime(cur_file)]
for t in file_time:
if t > time.time() - 60:
file_too_new = tryInt(time.time() - t)
break
if file_too_new:
break
if file_too_new:
try:
time_string = time.ctime(file_time[0])
except:
try:
time_string = time.ctime(file_time[1])
except:
time_string = 'unknown'
if files_too_new:
log.info('Archive seems to be still copying/moving/downloading or just copied/moved/downloaded (created on %s), ignoring for now: %s', (time_string, os.path.basename(archive['file']))) log.info('Archive seems to be still copying/moving/downloading or just copied/moved/downloaded (created on %s), ignoring for now: %s', (time_string, os.path.basename(archive['file'])))
continue continue

36
couchpotato/core/plugins/scanner/main.py

@ -291,41 +291,21 @@ class Scanner(Plugin):
break break
# Check if movie is fresh and maybe still unpacking, ignore files newer than 1 minute # Check if movie is fresh and maybe still unpacking, ignore files newer than 1 minute
file_too_new = False if check_file_date:
for cur_file in group['unsorted_files']: files_too_new, time_string = self.checkFilesChanged(group['unsorted_files'])
if not os.path.isfile(cur_file): if files_too_new:
file_too_new = time.time() log.info('Files seem to be still unpacking or just unpacked (created on %s), ignoring for now: %s', (time_string, identifier))
break
file_time = [os.path.getmtime(cur_file), os.path.getctime(cur_file)]
for t in file_time:
if t > time.time() - 60:
file_too_new = tryInt(time.time() - t)
break
if file_too_new:
break
if check_file_date and file_too_new:
try:
time_string = time.ctime(file_time[0])
except:
try:
time_string = time.ctime(file_time[1])
except:
time_string = 'unknown'
log.info('Files seem to be still unpacking or just unpacked (created on %s), ignoring for now: %s', (time_string, identifier))
# Delete the unsorted list # Delete the unsorted list
del group['unsorted_files'] del group['unsorted_files']
continue continue
# Only process movies newer than x # Only process movies newer than x
if newer_than and newer_than > 0: if newer_than and newer_than > 0:
has_new_files = False has_new_files = False
for cur_file in group['unsorted_files']: for cur_file in group['unsorted_files']:
file_time = [os.path.getmtime(cur_file), os.path.getctime(cur_file)] file_time = self.getFileTimes(cur_file)
if file_time[0] > newer_than or file_time[1] > newer_than: if file_time[0] > newer_than or file_time[1] > newer_than:
has_new_files = True has_new_files = True
break break

7
couchpotato/core/providers/nzb/nzbclub/main.py

@ -22,8 +22,11 @@ class NZBClub(NZBProvider, RSS):
q = '"%s %s"' % (title, movie['library']['year']) q = '"%s %s"' % (title, movie['library']['year'])
params = tryUrlencode({ q_param = tryUrlencode({
'q': q, 'q': q,
})
params = tryUrlencode({
'ig': 1, 'ig': 1,
'rpp': 200, 'rpp': 200,
'st': 5, 'st': 5,
@ -31,7 +34,7 @@ class NZBClub(NZBProvider, RSS):
'ns': 1, 'ns': 1,
}) })
nzbs = self.getRSSData(self.urls['search'] % params) nzbs = self.getRSSData(self.urls['search'] % ('%s&%s' % (q_param, params)))
for nzb in nzbs: for nzb in nzbs:

2
couchpotato/core/providers/torrent/thepiratebay/main.py

@ -37,7 +37,7 @@ class ThePirateBay(TorrentMagnetProvider):
'http://nl.tpb.li', 'http://nl.tpb.li',
'http://proxybay.eu', 'http://proxybay.eu',
'https://www.getpirate.com', 'https://www.getpirate.com',
'http://pirateproxy.ca', 'http://piratebay.io',
] ]
def _searchOnTitle(self, title, movie, quality, results): def _searchOnTitle(self, title, movie, quality, results):

7
couchpotato/core/providers/userscript/reddit/__init__.py

@ -0,0 +1,7 @@
from .main import Reddit
def start():
return Reddit()
config = []

17
couchpotato/core/providers/userscript/reddit/main.py

@ -0,0 +1,17 @@
from couchpotato import fireEvent
from couchpotato.core.helpers.variable import splitString
from couchpotato.core.providers.userscript.base import UserscriptBase
class Reddit(UserscriptBase):
includes = ['*://www.reddit.com/r/Ijustwatched/comments/*']
def getMovie(self, url):
name = splitString(url, '/')[-1]
if name.startswith('ijw_'):
name = name[4:]
year_name = fireEvent('scanner.name_year', name, single = True)
return self.search(year_name.get('name'), year_name.get('year'))

7
libs/axl/axel.py

@ -11,7 +11,7 @@
# Source: http://pypi.python.org/pypi/axel # Source: http://pypi.python.org/pypi/axel
# Docs: http://packages.python.org/axel # Docs: http://packages.python.org/axel
from couchpotato.core.helpers.variable import natcmp from couchpotato.core.helpers.variable import natsortKey
import Queue import Queue
import hashlib import hashlib
import sys import sys
@ -158,7 +158,10 @@ class Event(object):
t.daemon = True t.daemon = True
t.start() t.start()
for handler in sorted(self.handlers.iterkeys(), cmp = natcmp): handler_keys = self.handlers.keys()
handler_keys.sort(key = natsortKey)
for handler in handler_keys:
self.queue.put(handler) self.queue.put(handler)
if self.asynchronous: if self.asynchronous:

Loading…
Cancel
Save