8 changed files with 383 additions and 5 deletions
@ -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.', |
|||
} |
|||
], |
|||
}, |
|||
], |
|||
}] |
@ -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 |
@ -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.', |
|||
}, |
|||
], |
|||
} |
|||
], |
|||
}] |
Loading…
Reference in new issue