Browse Source

Merge branch 'develop' into redesign

pull/5180/head
Ruud 10 years ago
parent
commit
195a4764a3
  1. 9
      couchpotato/core/_base/_core.py
  2. 232
      couchpotato/core/media/_base/providers/torrent/rarbg.py
  3. 11
      couchpotato/core/media/movie/providers/torrent/rarbg.py
  4. 126
      couchpotato/core/notifications/slack.py
  5. 2
      couchpotato/core/plugins/base.py
  6. 2
      couchpotato/core/plugins/renamer.py
  7. 4
      couchpotato/templates/login.html
  8. 2
      init/freebsd

9
couchpotato/core/_base/_core.py

@ -5,6 +5,7 @@ import signal
import time import time
import traceback import traceback
import webbrowser import webbrowser
import sys
from couchpotato.api import addApiView from couchpotato.api import addApiView
from couchpotato.core.event import fireEvent, addEvent from couchpotato.core.event import fireEvent, addEvent
@ -64,6 +65,14 @@ class Core(Plugin):
import socket import socket
socket.setdefaulttimeout(30) socket.setdefaulttimeout(30)
# Don't check ssl by default
try:
if sys.version_info >= (2, 7, 9):
import ssl
ssl._create_default_https_context = ssl._create_unverified_context
except:
log.debug('Failed setting default ssl context: %s', traceback.format_exc())
def md5Password(self, value): def md5Password(self, value):
return md5(value) if value else '' return md5(value) if value else ''

232
couchpotato/core/media/_base/providers/torrent/rarbg.py

@ -0,0 +1,232 @@
import re
import traceback
import random
import time
from datetime import datetime
from couchpotato.core.helpers.variable import tryInt, getIdentifier
from couchpotato.core.logger import CPLog
from couchpotato.core.media._base.providers.torrent.base import TorrentMagnetProvider
log = CPLog(__name__)
tokenreceived = 0
rarbgtoken = 0
class Base(TorrentMagnetProvider):
urls = {
'test': 'https://torrentapi.org/pubapi_v2.php?app_id=couchpotato',
'token': 'https://torrentapi.org/pubapi_v2.php?get_token=get_token&app_id=couchpotato',
'search': 'https://torrentapi.org/pubapi_v2.php?token=%s&mode=search&search_imdb=%s&min_seeders=%s&min_leechers'
'=%s&ranked=%s&category=movies&format=json_extended&app_id=couchpotato',
}
http_time_between_calls = 2 # Seconds
user_agent = 'CouchPotato/1.0'
@staticmethod
def find_info(filename):
# CODEC #
codec = 'x264'
v = re.search("(?i)(x265|h265|h\.265)", filename)
if v:
codec = 'x265'
v = re.search("(?i)(xvid)", filename)
if v:
codec = 'xvid'
# RESOLUTION #
resolution = 'SD'
a = re.search("(?i)(720p)", filename)
if a:
resolution = '720p'
a = re.search("(?i)(1080p)", filename)
if a:
resolution = '1080p'
a = re.search("(?i)(2160p)", filename)
if a:
resolution = '2160p'
# SOURCE #
source = 'HD-Rip'
s = re.search("(?i)(WEB-DL|WEB_DL|WEB\.DL)", filename)
if s:
source = 'WEB-DL'
s = re.search("(?i)(WEBRIP)", filename)
if s:
source = 'WEBRIP'
s = re.search("(?i)(DVDR|DVDRip|DVD-Rip)", filename)
if s:
source = 'DVD-R'
s = re.search("(?i)(BRRIP|BDRIP|BluRay)", filename)
if s:
source = 'BR-Rip'
s = re.search("(?i)BluRay(.*)REMUX", filename)
if s:
source = 'BluRay-Remux'
s = re.search("(?i)BluRay(.*)\.(AVC|VC-1)\.", filename)
if s:
source = 'BluRay-Full'
return_info = [codec, resolution, source]
return return_info
@staticmethod
def get_token(self):
global tokenreceived
global rarbgtoken
now = int(time.time())
if (rarbgtoken == 0) or (tokenreceived == 0) or (now > (tokenreceived+(15*60))):
log.debug("RARBG: Getting Rarbg token")
tokendata = self.getJsonData(self.urls['token'])
if tokendata:
try:
tokenreceived = int(time.time())
rarbgtoken = tokendata['token']
log.debug("RARBG: GOT TOKEN: %s", rarbgtoken)
except RuntimeError:
log.error('RARBG: Failed getting token from Rarbg')
rarbgtoken = 0
# return token
def _search(self, movie, quality, results):
hasresults = 0
curryear = datetime.now().year
self.get_token(self)
movieid = getIdentifier(movie)
try:
movieyear = movie['info']['year']
except:
log.error("RARBG: Couldn't get movie year")
movieyear = 0
if (rarbgtoken != 0) and (movieyear == 0 or movieyear <= curryear):
data = self.getJsonData(self.urls['search'] % (rarbgtoken, movieid, self.conf('min_seeders'),
self.conf('min_leechers'), self.conf('ranked_only')))
if data:
if 'error_code' in data:
if data['error'] == 'No results found':
log.debug("RARBG: No results returned from Rarbg")
else:
if data['error_code'] == 10:
log.error(data['error'], movieid)
else:
log.error("RARBG: There is an error in the returned JSON: %s", data['error'])
else:
hasresults = 1
try:
if hasresults:
for result in data['torrent_results']:
name = result['title']
titlesplit = re.split("-", name)
releasegroup = titlesplit[len(titlesplit)-1]
xtrainfo = self.find_info(name)
encoding = xtrainfo[0]
resolution = xtrainfo[1]
# source = xtrainfo[2]
pubdate = result['pubdate'] # .strip(" +0000")
try:
pubdate = datetime.strptime(pubdate, '%Y-%m-%d %H:%M:%S +0000')
now = datetime.utcnow()
age = (now - pubdate).days
except ValueError:
log.debug("RARBG: Bad pubdate")
age = 0
torrentscore = self.conf('extra_score')
seeders = tryInt(result['seeders'])
torrent_desc = '/ %s / %s / %s / %s seeders' % (releasegroup, resolution, encoding, seeders)
if seeders == 0:
torrentscore = 0
sliceyear = result['pubdate'][0:4]
year = tryInt(sliceyear)
results.append({
'id': random.randint(100, 9999),
'name': re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) %s' % (name, year, torrent_desc)),
'url': result['download'],
'detail_url': result['info_page'],
'size': tryInt(result['size']/1048576), # rarbg sends in bytes
'seeders': tryInt(result['seeders']),
'leechers': tryInt(result['leechers']),
'age': tryInt(age),
'score': torrentscore
})
except RuntimeError:
log.error('RARBG: Failed getting results from %s: %s', (self.getName(), traceback.format_exc()))
config = [{
'name': 'rarbg',
'groups': [
{
'tab': 'searcher',
'list': 'torrent_providers',
'name': 'Rarbg',
'wizard': True,
'description': '<a href="https://rarbg.to/torrents.php">RARBG</a>',
'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAB+UlEQVQ4jYXTP2hcRxDH8c8JJZjbYNy8V7gIr0qhg5AiFnETX'
'+PmVAtSmKDaUhUiFyGxjXFlp0hhHy5cqFd9lSGcU55cBU6EEMIj5dsmMewSjNGmOJ3852wysMyww37n94OdXimlh49xDR/hxGr'
'8hZ/xx0qnlHK5lPKk/H/8U0r5oZTyQSmltzzr+AKfT+ed8UFLeHNAH1UVbA2r88NBfQcX8O2yv74sUqKNWT+T01sy2+zpUbS/w'
'/awvo7H+O0NQEA/LPKlQWXrSgUmR9HxcZQwmbZGw/pc4MsVAIT+IjcNw80aTjaaem1vPCNlGakj1C6uWFiqeDtyTvoyqAKhBn+'
'+E7CkxC6Zzjop57XpUSenpIuMhpXAc/zyHkAicRSjw6fHZ1ewPdqwszWAB2hXACln8+NWSlld9zX9YN7GhajQXz5+joPXR66de'
'U1J27Zi7FzaqE0OdmwNGzF2Ymzt3j+E8/gJH64AFlozKS4+Be7tjwyaIKVsOpnavX0II9x8ByDLKco5SwvjL0MI/z64tyOcwsf'
'jQw8PJvAdvsb6GSBlxI7UyTnD37i7OWhe3NrflvOit3djbDKdwR181SulXMXdrkubbdvKaOpK09S/4jP8iG9m8zmJjCoEg0HzO'
'77vna7zp7ju1TqfYIyZxT7dwCd4eWr7BR7h2X8S6gShJlbKYQAAAABJRU5ErkJggg==',
'options': [
{
'name': 'enabled',
'type': 'enabler',
'default': False,
},
{
'name': 'ranked_only',
'advanced': True,
'label': 'Ranked Only',
'type': 'int',
'default': 1,
'description': 'Only ranked torrents (internal), scene releases, rarbg releases. '
'Enter 1 (true) or 0 (false)',
},
{
'name': 'min_seeders',
'advanced': True,
'label': 'Minimum Seeders',
'type': 'int',
'default': 10,
'description': 'Minium amount of seeders the release must have.',
},
{
'name': 'min_leechers',
'advanced': True,
'label': 'Minimum leechers',
'type': 'int',
'default': 0,
'description': 'Minium amount of leechers the release must have.',
},
{
'name': 'extra_score',
'advanced': True,
'label': 'Extra Score',
'type': 'int',
'default': 0,
'description': 'Starting score for each release found via this provider.',
}
],
},
],
}]

11
couchpotato/core/media/movie/providers/torrent/rarbg.py

@ -0,0 +1,11 @@
from couchpotato.core.logger import CPLog
from couchpotato.core.media._base.providers.torrent.rarbg import Base
from couchpotato.core.media.movie.providers.base import MovieProvider
log = CPLog(__name__)
autoload = 'Rarbg'
class Rarbg(MovieProvider, Base):
pass

126
couchpotato/core/notifications/slack.py

@ -0,0 +1,126 @@
import json
from couchpotato.core.logger import CPLog
from couchpotato.core.notifications.base import Notification
log = CPLog(__name__)
autoload = 'Slack'
class Slack(Notification):
url = 'https://slack.com/api/chat.postMessage'
required_confs = ('token', 'channels',)
def notify(self, message='', data=None, listener=None):
for key in self.required_confs:
if not self.conf(key):
log.warning('Slack notifications are enabled, but '
'"{0}" is not specified.'.format(key))
return False
data = data or {}
message = message.strip()
if self.conf('include_imdb') and 'identifier' in data:
template = ' http://www.imdb.com/title/{0[identifier]}/'
message += template.format(data)
payload = {
'token': self.conf('token'),
'text': message,
'username': self.conf('bot_name'),
'unfurl_links': self.conf('include_imdb'),
'as_user': self.conf('as_user'),
'icon_url': self.conf('icon_url'),
'icon_emoji': self.conf('icon_emoji')
}
channels = self.conf('channels').split(',')
for channel in channels:
payload['channel'] = channel.strip()
response = self.urlopen(self.url, data=payload)
response = json.loads(response)
if not response['ok']:
log.warning('Notification sending to Slack has failed. Error '
'code: %s.', response['error'])
return False
return True
config = [{
'name': 'slack',
'groups': [
{
'tab': 'notifications',
'list': 'notification_providers',
'name': 'slack',
'options': [
{
'name': 'enabled',
'default': 0,
'type': 'enabler',
},
{
'name': 'token',
'description': (
'Your Slack authentication token.',
'Can be created at https://api.slack.com/web'
)
},
{
'name': 'channels',
'description': (
'Channel to send notifications to.',
'Can be a public channel, private group or IM '
'channel. Can be an encoded ID or a name '
'(staring with a hashtag, e.g. #general). '
'Separate with commas in order to notify multiple '
'channels. It is however recommended to send '
'notifications to only one channel due to '
'the Slack API rate limits.'
)
},
{
'name': 'include_imdb',
'default': True,
'type': 'bool',
'descrpition': 'Include a link to the movie page on IMDB.'
},
{
'name': 'bot_name',
'description': 'Name of bot.',
'default': 'CouchPotato',
'advanced': True,
},
{
'name': 'as_user',
'description': 'Send message as the authentication token '
' user.',
'default': False,
'type': 'bool',
'advanced': True
},
{
'name': 'icon_url',
'description': 'URL to an image to use as the icon for '
'notifications.',
'advanced': True,
},
{
'name': 'icon_emoji',
'description': (
'Emoji to use as the icon for notifications.',
'Overrides icon_url'
),
'advanced': True,
},
{
'name': 'on_snatch',
'default': 0,
'type': 'bool',
'advanced': True,
'description': 'Also send message when movie is snatched.',
},
],
}
],
}]

2
couchpotato/core/plugins/base.py

@ -145,7 +145,7 @@ class Plugin(object):
f.close() f.close()
os.chmod(path, Env.getPermission('file')) os.chmod(path, Env.getPermission('file'))
except: except:
log.error('Unable writing to file "%s": %s', (path, traceback.format_exc())) log.error('Unable to write file "%s": %s', (path, traceback.format_exc()))
if os.path.isfile(path): if os.path.isfile(path):
os.remove(path) os.remove(path)

2
couchpotato/core/plugins/renamer.py

@ -1447,7 +1447,7 @@ config = [{
'default': 'link', 'default': 'link',
'type': 'dropdown', 'type': 'dropdown',
'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')], 'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')],
'description': 'See above. It is prefered 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 being able to seed.',
'advanced': True, 'advanced': True,
}, },
{ {

4
couchpotato/templates/login.html

@ -36,8 +36,8 @@
<body class="page login"> <body class="page login">
<form action="" method="post"> <form action="" method="post">
<h1>CouchPotato</h1> <h1>CouchPotato</h1>
<div class="ctrlHolder"><input class="inlay" name="username" type="text" placeholder="Username" autocomplete="off" /></div> <div class="ctrlHolder"><input class="inlay" name="username" type="text" placeholder="Username" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /></div>
<div class="ctrlHolder"><input class="inlay" name="password" type="password" placeholder="Password" autocomplete="off" /></div> <div class="ctrlHolder"><input class="inlay" name="password" type="password" placeholder="Password" autocomplete="off" autocorrect="off" autocapitalize="off" spellcheck="false" /></div>
<div class="ctrlHolder"> <div class="ctrlHolder">
<label class="remember_me" title="for 30 days"><input class="inlay" id="remember_me" name="remember_me" type="checkbox" value="1" checked="checked" /> Remember me</label> <label class="remember_me" title="for 30 days"><input class="inlay" id="remember_me" name="remember_me" type="checkbox" value="1" checked="checked" /> Remember me</label>
<input class="button" name="submit" type="submit" value="Login" /> <input class="button" name="submit" type="submit" value="Login" />

2
init/freebsd

@ -1,7 +1,7 @@
#!/bin/sh #!/bin/sh
# #
# PROVIDE: couchpotato # PROVIDE: couchpotato
# REQUIRE: DAEMON # REQUIRE: LOGIN
# KEYWORD: shutdown # KEYWORD: shutdown
# Add the following lines to /etc/rc.conf to enable couchpotato: # Add the following lines to /etc/rc.conf to enable couchpotato:

Loading…
Cancel
Save