diff --git a/gui/slick/interfaces/default/config_general.tmpl b/gui/slick/interfaces/default/config_general.tmpl index 1fe55e8f..b098c75e 100644 --- a/gui/slick/interfaces/default/config_general.tmpl +++ b/gui/slick/interfaces/default/config_general.tmpl @@ -589,6 +589,16 @@ +
+ +
+ diff --git a/lib/pastebin.py b/lib/pastebin.py new file mode 100644 index 00000000..3211d06d --- /dev/null +++ b/lib/pastebin.py @@ -0,0 +1,710 @@ +#!/usr/bin/env python + +############################################################################# +# Pastebin.py - Python 3.2 Pastebin API. +# Copyright (C) 2012 Ian Havelock +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . +# +############################################################################# + +# This software is a derivative work of: +# http://winappdbg.sourceforge.net/blog/pastebin.py + +############################################################################# + + +__all__ = ['delete_paste', 'user_details', 'trending', 'pastes_by_user', + 'generate_user_key', 'paste', 'PastebinAPI', 'PastebinError'] + +import sys +import urllib + +class PastebinError(RuntimeError): + """Pastebin API error. + + The error message returned by the web application is stored as the Python + exception message.""" + +class PastebinAPI(object): + """Pastebin API interaction object. + + Public functions: + + paste -- Pastes a user-specified file or string using the new API-key POST + method. + + generate_user_key -- Generates a session-key that is required for other + functions. + + pastes_by_user -- Returns all public pastes submitted by the specified login + credentials. + + trending -- Returns the top trending paste. + + user_details -- Returns details about the user for the specified API user + key. + + delete_paste -- Adds two numbers together and returns the result.""" + + # String to determine bad API requests + _bad_request = 'Bad API request' + + # Base domain name + _base_domain = 'pastebin.com' + + # Valid Pastebin URLs begin with this string (kinda bvious) + _prefix_url = 'http://%s/' % _base_domain + + # Valid Pastebin URLs with a custom subdomain begin with this string + _subdomain_url = 'http://%%s.%s/' % _base_domain + + # DEPRECATED + # URL to the LEGACY POST API + #_legacy_api_url = 'http://%s/api_public.php' % _base_domain + + # URL to the POST API + _api_url = 'http://%s/api/api_post.php' % _base_domain + + # URL to the Login API + _api_login_url = 'http://%s/api/api_login.php' % _base_domain + + # Valid paste_expire_date values (Never, 10 minutes, 1 Hour, 1 Day, 1 Month) + paste_expire_date = ('N', '10M', '1H', '1D', '1M') + + # Valid paste_expire_date values (0 = public, 1 = unlisted, 2 = private) + paste_private = ('public', 'unlisted', 'private') + + # Valid parse_format values + paste_format = ( + '4cs', # 4CS + '6502acme', # 6502 ACME Cross Assembler + '6502kickass', # 6502 Kick Assembler + '6502tasm', # 6502 TASM/64TASS + 'abap', # ABAP + 'actionscript', # ActionScript + 'actionscript3', # ActionScript 3 + 'ada', # Ada + 'algol68', # ALGOL 68 + 'apache', # Apache Log + 'applescript', # AppleScript + 'apt_sources', # APT Sources + 'asm', # ASM (NASM) + 'asp', # ASP + 'autoconf', # autoconf + 'autohotkey', # Autohotkey + 'autoit', # AutoIt + 'avisynth', # Avisynth + 'awk', # Awk + 'bascomavr', # BASCOM AVR + 'bash', # Bash + 'basic4gl', # Basic4GL + 'bibtex', # BibTeX + 'blitzbasic', # Blitz Basic + 'bnf', # BNF + 'boo', # BOO + 'bf', # BrainFuck + 'c', # C + 'c_mac', # C for Macs + 'cil', # C Intermediate Language + 'csharp', # C# + 'cpp', # C++ + 'cpp-qt', # C++ (with QT extensions) + 'c_loadrunner', # C: Loadrunner + 'caddcl', # CAD DCL + 'cadlisp', # CAD Lisp + 'cfdg', # CFDG + 'chaiscript', # ChaiScript + 'clojure', # Clojure + 'klonec', # Clone C + 'klonecpp', # Clone C++ + 'cmake', # CMake + 'cobol', # COBOL + 'coffeescript', # CoffeeScript + 'cfm', # ColdFusion + 'css', # CSS + 'cuesheet', # Cuesheet + 'd', # D + 'dcs', # DCS + 'delphi', # Delphi + 'oxygene', # Delphi Prism (Oxygene) + 'diff', # Diff + 'div', # DIV + 'dos', # DOS + 'dot', # DOT + 'e', # E + 'ecmascript', # ECMAScript + 'eiffel', # Eiffel + 'email', # Email + 'epc', # EPC + 'erlang', # Erlang + 'fsharp', # F# + 'falcon', # Falcon + 'fo', # FO Language + 'f1', # Formula One + 'fortran', # Fortran + 'freebasic', # FreeBasic + 'freeswitch', # FreeSWITCH + 'gambas', # GAMBAS + 'gml', # Game Maker + 'gdb', # GDB + 'genero', # Genero + 'genie', # Genie + 'gettext', # GetText + 'go', # Go + 'groovy', # Groovy + 'gwbasic', # GwBasic + 'haskell', # Haskell + 'hicest', # HicEst + 'hq9plus', # HQ9 Plus + 'html4strict', # HTML + 'html5', # HTML 5 + 'icon', # Icon + 'idl', # IDL + 'ini', # INI file + 'inno', # Inno Script + 'intercal', # INTERCAL + 'io', # IO + 'j', # J + 'java', # Java + 'java5', # Java 5 + 'javascript', # JavaScript + 'jquery', # jQuery + 'kixtart', # KiXtart + 'latex', # Latex + 'lb', # Liberty BASIC + 'lsl2', # Linden Scripting + 'lisp', # Lisp + 'llvm', # LLVM + 'locobasic', # Loco Basic + 'logtalk', # Logtalk + 'lolcode', # LOL Code + 'lotusformulas', # Lotus Formulas + 'lotusscript', # Lotus Script + 'lscript', # LScript + 'lua', # Lua + 'm68k', # M68000 Assembler + 'magiksf', # MagikSF + 'make', # Make + 'mapbasic', # MapBasic + 'matlab', # MatLab + 'mirc', # mIRC + 'mmix', # MIX Assembler + 'modula2', # Modula 2 + 'modula3', # Modula 3 + '68000devpac', # Motorola 68000 HiSoft Dev + 'mpasm', # MPASM + 'mxml', # MXML + 'mysql', # MySQL + 'newlisp', # newLISP + 'text', # None + 'nsis', # NullSoft Installer + 'oberon2', # Oberon 2 + 'objeck', # Objeck Programming Langua + 'objc', # Objective C + 'ocaml-brief', # OCalm Brief + 'ocaml', # OCaml + 'pf', # OpenBSD PACKET FILTER + 'glsl', # OpenGL Shading + 'oobas', # Openoffice BASIC + 'oracle11', # Oracle 11 + 'oracle8', # Oracle 8 + 'oz', # Oz + 'pascal', # Pascal + 'pawn', # PAWN + 'pcre', # PCRE + 'per', # Per + 'perl', # Perl + 'perl6', # Perl 6 + 'php', # PHP + 'php-brief', # PHP Brief + 'pic16', # Pic 16 + 'pike', # Pike + 'pixelbender', # Pixel Bender + 'plsql', # PL/SQL + 'postgresql', # PostgreSQL + 'povray', # POV-Ray + 'powershell', # Power Shell + 'powerbuilder', # PowerBuilder + 'proftpd', # ProFTPd + 'progress', # Progress + 'prolog', # Prolog + 'properties', # Properties + 'providex', # ProvideX + 'purebasic', # PureBasic + 'pycon', # PyCon + 'python', # Python + 'q', # q/kdb+ + 'qbasic', # QBasic + 'rsplus', # R + 'rails', # Rails + 'rebol', # REBOL + 'reg', # REG + 'robots', # Robots + 'rpmspec', # RPM Spec + 'ruby', # Ruby + 'gnuplot', # Ruby Gnuplot + 'sas', # SAS + 'scala', # Scala + 'scheme', # Scheme + 'scilab', # Scilab + 'sdlbasic', # SdlBasic + 'smalltalk', # Smalltalk + 'smarty', # Smarty + 'sql', # SQL + 'systemverilog', # SystemVerilog + 'tsql', # T-SQL + 'tcl', # TCL + 'teraterm', # Tera Term + 'thinbasic', # thinBasic + 'typoscript', # TypoScript + 'unicon', # Unicon + 'uscript', # UnrealScript + 'vala', # Vala + 'vbnet', # VB.NET + 'verilog', # VeriLog + 'vhdl', # VHDL + 'vim', # VIM + 'visualprolog', # Visual Pro Log + 'vb', # VisualBasic + 'visualfoxpro', # VisualFoxPro + 'whitespace', # WhiteSpace + 'whois', # WHOIS + 'winbatch', # Winbatch + 'xbasic', # XBasic + 'xml', # XML + 'xorg_conf', # Xorg Config + 'xpp', # XPP + 'yaml', # YAML + 'z80', # Z80 Assembler + 'zxbasic', # ZXBasic + ) + + def __init__(self): + pass + + + def delete_paste(self, api_dev_key, api_user_key, api_paste_key): + """Delete the paste specified by the api_paste_key. + + + Usage Example:: + >>> from pastebin import PastebinAPI + >>> x = PastebinAPI() + >>> paste_to_delete = x.delete_paste('453a994e0e2f1efae07f8759e59e075b', + ... 'c57a18e6c0ae228cd4bd16fe36da381a', + ... 'WkgcTFtv') + >>> print paste_to_delete + Paste Removed + + + @type api_dev_key: string + @param api_dev_key: The API Developer Key of a registered U{http://pastebin.com} account. + + @type api_user_key: string + @param api_user_key: The API User Key of a U{http://pastebin.com} registered user. + + @type api_paste_key: string + @param api_paste_key: The Paste Key of the paste to be deleted (string after final / in U{http://pastebin.com} URL). + + @rtype: string + @returns: A successful deletion returns 'Paste Removed'. + """ + + # Valid api developer key + argv = {'api_dev_key' : str(api_dev_key) } + + # Requires pre-registered account + if api_user_key is not None: + argv['api_user_key'] = str(api_user_key) + + # Key of the paste to be deleted. + if api_paste_key is not None: + argv['api_paste_key'] = str(api_paste_key) + + # Valid API option - 'user_details' in this instance + argv['api_option'] = str('delete') + + + # lets try to read the URL that we've just built. + request = urllib.urlopen(self._api_url, urllib.urlencode(argv)) + response = request_string.read() + + return response + + + def user_details(self, api_dev_key, api_user_key): + """Return user details of the user specified by the api_user_key. + + + Usage Example:: + >>> from pastebin import PastebinAPI + >>> x = PastebinAPI() + >>> details = x.user_details('453a994e0e2f1efae07f8759e59e075b', + ... 'c57a18e6c0ae228cd4bd16fe36da381a') + >>> print details + + MonkeyPuzzle + python + N + http://pastebin.com/i/guest.gif + 0 + + user@email.com + + 0 + + + + @type api_dev_key: string + @param api_dev_key: The API Developer Key of a registered U{http://pastebin.com} account. + + @type api_user_key: string + @param api_user_key: The API User Key of a U{http://pastebin.com} registered user. + + @rtype: string + @returns: Returns an XML string containing user information. + """ + + # Valid api developer key + argv = {'api_dev_key' : str(api_dev_key) } + + # Requires pre-registered account to generate an api_user_key + # (see generate_user_key) + if api_user_key is not None: + argv['api_user_key'] = str(api_user_key) + + # Valid API option - 'user_details' in this instance + argv['api_option'] = str('userdetails') + + # lets try to read the URL that we've just built. + request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv)) + response = request_string.read() + + # do some basic error checking here so we can gracefully handle any + # errors we are likely to encounter + if response.startswith(self._bad_request): + raise PastebinError(response) + + elif not response.startswith(''): + raise PastebinError(response) + + return response + + + def trending(self, api_dev_key): + """Returns the top trending paste details. + + + Usage Example:: + >>> from pastebin import PastebinAPI + >>> x = PastebinAPI() + >>> details = x.trending('453a994e0e2f1efae07f8759e59e075b') + >>> print details + + jjMRFDH6 + 1333230838 + + 6416 + 0 + 0 + None + text + http://pastebin.com/jjMRFDH6 + 6384 + + + Note: Returns multiple trending pastes, not just 1. + + + @type api_dev_key: string + @param api_dev_key: The API Developer Key of a registered U{http://pastebin.com} account. + + + @rtype: string + @return: Returns the string (XML formatted) containing the top trending pastes. + """ + + # Valid api developer key + argv = {'api_dev_key' : str(api_dev_key) } + + # Valid API option - 'trends' is returns trending pastes + argv['api_option'] = str('trends') + + # lets try to read the URL that we've just built. + request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv)) + response = request_string.read() + + # do some basic error checking here so we can gracefully handle any + # errors we are likely to encounter + if response.startswith(self._bad_request): + raise PastebinError(response) + + elif not response.startswith(''): + raise PastebinError(response) + + return response + + + def pastes_by_user(self, api_dev_key, api_user_key, results_limit = None): + """Returns all pastes for the provided api_user_key. + + + Usage Example:: + >>> from pastebin import PastebinAPI + >>> x = PastebinAPI() + >>> details = x.user_details('453a994e0e2f1efae07f8759e59e075b', + ... 'c57a18e6c0ae228cd4bd16fe36da381a', + ... 100) + >>> print details + + DLiSspYT + 1332714730 + Pastebin.py - Python 3.2 Pastebin.com API + 25300 + 0 + 0 + Python + python + http://pastebin.com/DLiSspYT + 70 + + + Note: Returns multiple pastes, not just 1. + + + @type api_dev_key: string + @param api_dev_key: The API Developer Key of a registered U{http://pastebin.com} account. + + @type api_user_key: string + @param api_user_key: The API User Key of a U{http://pastebin.com} registered user. + + @type results_limit: number + @param results_limit: The number of pastes to return between 1 - 1000. + + @rtype: string + @returns: Returns an XML string containing number of specified pastes by user. + """ + + # Valid api developer key + argv = {'api_dev_key' : str(api_dev_key) } + + # Requires pre-registered account + if api_user_key is not None: + argv['api_user_key'] = str(api_user_key) + + # Number of results to return - between 1 & 1000, default = 50 + if results_limit is None: + argv['api_results_limit'] = 50 + + if results_limit is not None: + if results_limit < 1: + argv['api_results_limit'] = 50 + elif results_limit > 1000: + argv['api_results_limit'] = 1000 + else: + argv['api_results_limit'] = int(results_limit) + + # Valid API option - 'paste' is default for new paste + argv['api_option'] = str('list') + + # lets try to read the URL that we've just built. + request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv)) + response = request_string.read() + + # do some basic error checking here so we can gracefully handle any + # errors we are likely to encounter + if response.startswith(self._bad_request): + raise PastebinError(response) + + elif not response.startswith(''): + raise PastebinError(response) + + return response + + + def generate_user_key(self, api_dev_key, username, password): + """Generate a user session key - needed for other functions. + + + Usage Example:: + >>> from pastebin import PastebinAPI + >>> x = PastebinAPI() + >>> my_key = x.generate_user_key('453a994e0e2f1efae07f8759e59e075b', + ... 'MonkeyPuzzle', + ... '12345678') + >>> print my_key + c57a18e6c0ae228cd4bd16fe36da381a + + + @type api_dev_key: string + @param api_dev_key: The API Developer Key of a registered U{http://pastebin.com} account. + + @type username: string + @param username: The username of a registered U{http://pastebin.com} account. + + @type password: string + @param password: The password of a registered U{http://pastebin.com} account. + + @rtype: string + @returns: Session key (api_user_key) to allow authenticated interaction to the API. + + """ + # Valid api developer key + argv = {'api_dev_key' : str(api_dev_key) } + + # Requires pre-registered pastebin account + if username is not None: + argv['api_user_name'] = str(username) + + # Requires pre-registered pastebin account + if password is not None: + argv['api_user_password'] = str(password) + + # lets try to read the URL that we've just built. + request_string = urllib.urlopen(self._api_login_url, urllib.urlencode(argv)) + response = request_string.read() + + # do some basic error checking here so we can gracefully handle any errors we are likely to encounter + if response.startswith(self._bad_request): + raise PastebinError(response) + + return response + + + def paste(self, api_dev_key, api_paste_code, + api_user_key = None, paste_name = None, paste_format = None, + paste_private = None, paste_expire_date = None): + + """Submit a code snippet to Pastebin using the new API. + + + Usage Example:: + >>> from pastebin import PastebinAPI + >>> x = PastebinAPI() + >>> url = x.paste('453a994e0e2f1efae07f8759e59e075b' , + ... 'Snippet of code to paste goes here', + ... paste_name = 'title of paste', + ... api_user_key = 'c57a18e6c0ae228cd4bd16fe36da381a', + ... paste_format = 'python', + ... paste_private = 'unlisted', + ... paste_expire_date = '10M') + >>> print url + http://pastebin.com/tawPUgqY + + + @type api_dev_key: string + @param api_dev_key: The API Developer Key of a registered U{http://pastebin.com} account. + + @type api_paste_code: string + @param api_paste_code: The file or string to paste to body of the U{http://pastebin.com} paste. + + @type api_user_key: string + @param api_user_key: The API User Key of a U{http://pastebin.com} registered user. + If none specified, paste is made as a guest. + + @type paste_name: string + @param paste_name: (Optional) Title of the paste. + Default is to paste anonymously. + + @type paste_format: string + @param paste_format: (Optional) Programming language of the code being + pasted. This enables syntax highlighting when reading the code in + U{http://pastebin.com}. Default is no syntax highlighting (text is + just text and not source code). + + @type paste_private: string + @param paste_private: (Optional) C{'public'} if the paste is public (visible + by everyone), C{'unlisted'} if it's public but not searchable. + C{'private'} if the paste is private and not searchable or indexed. + The Pastebin FAQ (U{http://pastebin.com/faq}) claims + private pastes are not indexed by search engines (aka Google). + + @type paste_expire_date: str + @param paste_expire_date: (Optional) Expiration date for the paste. + Once past this date the paste is deleted automatically. Valid + values are found in the L{PastebinAPI.paste_expire_date} class member. + If not provided, the paste never expires. + + @rtype: string + @return: Returns the URL to the newly created paste. + """ + + + # Valid api developer key + argv = {'api_dev_key' : str(api_dev_key) } + + # Code snippet to submit + if api_paste_code is not None: + argv['api_paste_code'] = str(api_paste_code) + + # Valid API option - 'paste' is default for new paste + argv['api_option'] = str('paste') + + # API User Key + if api_user_key is not None: + argv['api_user_key'] = str(api_user_key) + elif api_user_key is None: + argv['api_user_key'] = str('') + + # Name of the poster + if paste_name is not None: + argv['api_paste_name'] = str(paste_name) + + # Syntax highlighting + if paste_format is not None: + paste_format = str(paste_format).strip().lower() + argv['api_paste_format'] = paste_format + + # Is the snippet private? + if paste_private is not None: + if paste_private == 'public': + argv['api_paste_private'] = int(0) + elif paste_private == 'unlisted': + argv['api_paste_private'] = int(1) + elif paste_private == 'private': + argv['api_paste_private'] = int(2) + + # Expiration for the snippet + if paste_expire_date is not None: + paste_expire_date = str(paste_expire_date).strip().upper() + argv['api_paste_expire_date'] = paste_expire_date + + # lets try to read the URL that we've just built. + request_string = urllib.urlopen(self._api_url, urllib.urlencode(argv)) + response = request_string.read() + + # do some basic error checking here so we can gracefully handle any + # errors we are likely to encounter + if response.startswith(self._bad_request): + raise PastebinError(response) + elif not response.startswith(self._prefix_url): + raise PastebinError(response) + + return response + + +###################################################### + +delete_paste = PastebinAPI.delete_paste +user_details = PastebinAPI.user_details +trending = PastebinAPI.trending +pastes_by_user = PastebinAPI.pastes_by_user +generate_user_key = PastebinAPI.generate_user_key +paste = PastebinAPI.paste + +###################################################### + +if __name__ == "__main__": + main() diff --git a/sickbeard/__init__.py b/sickbeard/__init__.py index 95dfbe8c..9f842a38 100755 --- a/sickbeard/__init__.py +++ b/sickbeard/__init__.py @@ -118,6 +118,7 @@ GIT_REPO = 'SickRage' GIT_USERNAME = None GIT_PASSWORD = None GIT_PATH = None +GIT_AUTOISSUES = False INIT_LOCK = Lock() started = False @@ -524,7 +525,8 @@ def initialize(consoleLogging=True): USE_FAILED_DOWNLOADS, DELETE_FAILED, ANON_REDIRECT, LOCALHOST_IP, TMDB_API_KEY, DEBUG, PROXY_SETTING, PROXY_INDEXERS, \ AUTOPOSTPROCESSER_FREQUENCY, DEFAULT_AUTOPOSTPROCESSER_FREQUENCY, MIN_AUTOPOSTPROCESSER_FREQUENCY, \ ANIME_DEFAULT, NAMING_ANIME, ANIMESUPPORT, USE_ANIDB, ANIDB_USERNAME, ANIDB_PASSWORD, ANIDB_USE_MYLIST, \ - ANIME_SPLIT_HOME, SCENE_DEFAULT, PLAY_VIDEOS, BACKLOG_DAYS, GIT_ORG, GIT_REPO, GIT_USERNAME, GIT_PASSWORD, gh + ANIME_SPLIT_HOME, SCENE_DEFAULT, PLAY_VIDEOS, BACKLOG_DAYS, GIT_ORG, GIT_REPO, GIT_USERNAME, GIT_PASSWORD, \ + GIT_AUTOISSUES, gh if __INITIALIZED__: return False @@ -551,6 +553,16 @@ def initialize(consoleLogging=True): CheckSection(CFG, 'Pushbullet') CheckSection(CFG, 'Subtitles') + GIT_AUTOISSUES = bool(check_setting_int(CFG, 'General', 'git_autoissues', 0)) + + # git login info + GIT_USERNAME = check_setting_str(CFG, 'General', 'git_username', '') + GIT_PASSWORD = check_setting_str(CFG, 'General', 'git_password', '', censor_log=True) + + # github api + try:gh = Github(user_agent="SiCKRAGE").get_organization(GIT_ORG).get_repo(GIT_REPO) + except:gh = None + # debugging DEBUG = bool(check_setting_int(CFG, 'General', 'debug', 0)) @@ -565,16 +577,6 @@ def initialize(consoleLogging=True): # init logging logger.initLogging(consoleLogging=consoleLogging, fileLogging=fileLogging, debugLogging=DEBUG) - # git login info - GIT_USERNAME = check_setting_str(CFG, 'General', 'git_username', '') - GIT_PASSWORD = check_setting_str(CFG, 'General', 'git_password', '', censor_log=True) - - # github api - try: - gh = Github(user_agent="SiCKRAGE").get_organization(GIT_ORG).get_repo(GIT_REPO) - except: - gh = None - # git reset on update GIT_RESET = bool(check_setting_int(CFG, 'General', 'git_reset', 0)) @@ -1409,6 +1411,7 @@ def save_config(): # For passwords you must include the word `password` in the item_name and add `helpers.encrypt(ITEM_NAME, ENCRYPTION_VERSION)` in save_config() new_config['General'] = {} + new_config['General']['git_autoissues'] = int(GIT_AUTOISSUES) new_config['General']['git_username'] = GIT_USERNAME new_config['General']['git_password'] = GIT_PASSWORD new_config['General']['git_reset'] = int(GIT_RESET) diff --git a/sickbeard/logger.py b/sickbeard/logger.py index 265959e0..97adb0b3 100644 --- a/sickbeard/logger.py +++ b/sickbeard/logger.py @@ -18,14 +18,16 @@ from __future__ import with_statement import os - import sys import logging import logging.handlers import threading +import platform import sickbeard from sickbeard import classes +from github import Github +from pastebin import PastebinAPI # log levels ERROR = logging.ERROR @@ -115,12 +117,15 @@ class Logger(object): message = meThread + u" :: " + msg # pass exception information if debugging enabled - if level == ERROR: - kwargs["exc_info"] = 1 - classes.ErrorViewer.add(classes.UIError(message)) + kwargs["exc_info"] = 1 if level == ERROR else 0 self.logger.log(level, message, *args, **kwargs) + if level == ERROR: + classes.ErrorViewer.add(classes.UIError(message)) + #if sickbeard.GIT_AUTOISSUES: + # self.submit_errors() + def log_error_and_exit(self, error_msg, *args, **kwargs): self.log(error_msg, ERROR, *args, **kwargs) @@ -129,6 +134,52 @@ class Logger(object): else: sys.exit(1) + def submit_errors(self): + if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD and len(classes.ErrorViewer.errors) > 0): + return + + title = "[APP SUBMITTED]: " + + gh_org = sickbeard.GIT_ORG or 'SiCKRAGETV' + gh_repo = 'sickrage-issues' + + self.gh_issues = Github(login_or_token=sickbeard.GIT_USERNAME, password=sickbeard.GIT_PASSWORD, + user_agent="SiCKRAGE").get_organization(gh_org).get_repo(gh_repo) + + pastebin_url = None + try: + if os.path.isfile(logger.logFile): + with ek.ek(open, logger.logFile) as f: + data = f.readlines(50) + pastebin_url = PastebinAPI().paste('f59b8e9fa1fc2d033e399e6c7fb09d19', data) + except: + pass + + try: + for curError in sorted(classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]: + message = u"### INFO\n" + message += u"Python Version: **" + sys.version[:120] + "**\n" + message += u"Operating System: **" + platform.platform() + "**\n" + message += u"Branch: **" + sickbeard.BRANCH + "**\n" + message += u"Commit: SiCKRAGETV/SickRage@" + sickbeard.CUR_COMMIT_HASH + "\n" + if pastebin_url: + message += u"Pastebin Log URL: " + pastebin_url + "\n" + message += u"### ERROR\n" + message += u"```\n" + message += curError.message + message += u"```\n" + message += u"---\n" + message += u"_STAFF NOTIFIED_: @SiCKRAGETV/owners @SiCKRAGETV/moderators" + + issue = self.gh_issues.create_issue(title + curError.title, message) + if issue: + ui.notifications.message('Your issue ticket #%s was submitted successfully!' % issue.number) + classes.ErrorViewer.clear() + + except Exception as e: + self.log(ex(e), logger.ERROR) + + class Wrapper(object): instance = Logger() diff --git a/sickbeard/webserve.py b/sickbeard/webserve.py index 12916106..ba4b1772 100644 --- a/sickbeard/webserve.py +++ b/sickbeard/webserve.py @@ -24,11 +24,8 @@ import time import urllib import re import datetime -import sys -import platform import sickbeard -from github import Github from sickbeard import config, sab from sickbeard import clients from sickbeard import history, notifiers, processTV @@ -3440,7 +3437,7 @@ class ConfigGeneral(Config): calendar_unprotected=None, fuzzy_dating=None, trim_zero=None, date_preset=None, date_preset_na=None, time_preset=None, indexer_timeout=None, play_videos=None, rootDir=None, theme_name=None, - git_reset=None, git_username=None, git_password=None): + git_reset=None, git_username=None, git_password=None, git_autoissues=None): results = [] @@ -3465,6 +3462,7 @@ class ConfigGeneral(Config): sickbeard.GIT_USERNAME = git_username sickbeard.GIT_PASSWORD = git_password sickbeard.GIT_RESET = config.checkbox_to_value(git_reset) + sickbeard.GIT_AUTOISSUES = config.checkbox_to_value(git_autoissues) sickbeard.GIT_PATH = git_path sickbeard.GIT_REMOTE = git_remote sickbeard.CALENDAR_UNPROTECTED = config.checkbox_to_value(calendar_unprotected) @@ -4643,7 +4641,7 @@ class ErrorLogs(WebRoot): def ErrorLogsMenu(self): menu = [ {'title': 'Clear Errors', 'path': 'errorlogs/clearerrors/'}, - {'title': 'Submit Errors', 'path': 'errorlogs/submit_errors/', 'requires': self.haveGitHub}, + {'title': 'Submit Errors', 'path': 'errorlogs/submit_errors/', 'requires': self.haveErrors}, ] return menu @@ -4655,8 +4653,8 @@ class ErrorLogs(WebRoot): return t.respond() - def haveGitHub(self): - if sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD: + def haveErrors(self): + if len(classes.ErrorViewer.errors) > 0: return True def clearerrors(self): @@ -4717,36 +4715,9 @@ class ErrorLogs(WebRoot): return t.respond() def submit_errors(self): - title = "[APP SUBMITTED]: " - - gh_org = sickbeard.GIT_ORG or 'SiCKRAGETV' - gh_repo = 'sickrage-issues' - - self.gh_issues = Github(login_or_token=sickbeard.GIT_USERNAME, password=sickbeard.GIT_PASSWORD, - user_agent="SiCKRAGE").get_organization(gh_org).get_repo(gh_repo) - - try: - for curError in sorted(classes.ErrorViewer.errors, key=lambda error: error.time, reverse=True)[:500]: - title = title + curError.title - - message = u"### INFO\n" - message += u"Python Version: **" + sys.version[:120] + "**\n" - message += u"Operating System: **" + platform.platform() + "**\n" - message += u"Branch: **" + sickbeard.BRANCH + "**\n" - message += u"Commit: SiCKRAGETV/SickRage@" + sickbeard.CUR_COMMIT_HASH + "\n" - message += u"### ERROR\n" - message += u"```\n" - message += curError.message - message += u"```\n" - message += u"---\n" - message += u"_STAFF NOTIFIED_: @SiCKRAGETV/owners @SiCKRAGETV/moderators" - - issue = self.gh_issues.create_issue(title, message) - if issue: - ui.notifications.message('Your issue ticket #%s was submitted successfully!' % issue.number) - classes.ErrorViewer.clear() - - except Exception as e: - logger.log(ex(e), logger.ERROR) + if not (sickbeard.GIT_USERNAME and sickbeard.GIT_PASSWORD): + logger.log(u'Please set your GitHub username and password in the config, unable to submit issue ticket to GitHub!') + else: + logger.submit_errors() return self.redirect("/errorlogs/") \ No newline at end of file