Advertisement
Guest User

Untitled

a guest
May 5th, 2017
97
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 30.26 KB | None | 0 0
  1. #!/usr/bin/env python
  2. #encoding:utf-8
  3.  
  4. import urllib
  5. import xml.dom.minidom
  6. import re
  7. import time
  8. import os
  9. import sys
  10. import string
  11. import pickle
  12. import feedparser
  13. import logging
  14. import dailylogrotation
  15. import ConfigParser
  16.  
  17. from tvdb_api import (tvdb_error, tvdb_shownotfound, tvdb_seasonnotfound,
  18.     tvdb_episodenotfound, tvdb_episodenotfound, tvdb_attributenotfound, tvdb_userabort)
  19. from tvdb_api import Tvdb
  20.  
  21. ################################
  22. # SABzndb Smart Downlader - TV #
  23. ################################
  24.  
  25. __author__ = "minimad/Neil"
  26. __version__ = "0.9.3"
  27.  
  28.  
  29. # Use a config File
  30. # defaults to same dir as script
  31. config = ConfigParser.RawConfigParser()
  32. config.read('SSD.cfg')
  33.  
  34. # Use a Alias file
  35. aliases = ConfigParser.RawConfigParser()
  36. aliases.read('Aliases.cfg')
  37.  
  38. ################################### START VALUES FROM CONFIG FILE #####################################
  39.  
  40. # Media Location
  41. tv_dirs = []
  42. if sys.platform == "win32":
  43.     for dir in config.items('MEDIA LOCATION - WIN32'):
  44.         tv_dirs.append(dir[1])
  45. else:
  46.     for dir in config.items('MEDIA LOCATION - LINUX'):
  47.         tv_dirs.append(dir[1])
  48.  
  49. # Define Log Files
  50. errorlog = config.get('LOGGING' , 'ERROR_LOG')
  51. debuglog = config.get('LOGGING' , 'DEBUG_LOG')
  52.  
  53. # Season Directory Numbering Format
  54. SeasonType = config.get('NAMING_FORMAT' , 'SeasonType')
  55.  
  56. # Episode File Numbering Format
  57. EpisodeType = config.get('NAMING_FORMAT' , 'EpisodeType')
  58.  
  59. # RSS Feeds
  60. nzbfeeds = []
  61. feeds = config.items('NZB_FEEDS')
  62. for feed in feeds:
  63.     nzbfeeds.append(feed[1])
  64.  
  65. # SABnzbd Config
  66. my_SAB = config.get('SABnzbd Config' , 'URL')
  67. my_SAB_params = config.get('SABnzbd Config' , 'ADD_PARAMS')
  68. my_SAB_username = config.get('SABnzbd Config' , 'USERNAME')
  69. my_SAB_password = config.get('SABnzbd Config' , 'PASSWORD')
  70. SAB_API_KEY = config.get('SABnzbd Config' , 'API_KEY')
  71.  
  72. # theTVDB api
  73. tvdb_interactive = config.getboolean('TVDB_API' , 'INTERACTIVE')
  74. tvdb_debug = config.getboolean('TVDB_API' , 'DEBUG')
  75.  
  76. # Video Format
  77. video_formats = config.get('VIDEO_FORMAT' , 'FORMAT')
  78.  
  79. # DEBUG
  80. debug = config.getboolean('SSD' , 'DEBUG')
  81.  
  82. # 'Smart' Queue checking
  83. useSmartQueue = config.getboolean('SSD' , 'SMART_QUEUE')
  84.  
  85. # SABnzbd or tvnamer.py style illegal char replaement
  86. SABnzbdIllegalCharsStyle = config.getboolean('SSD' , 'SABnzbdIllegalCharsStyle')
  87.  
  88. # Windows Compliant Names
  89. force_windows_compliant_filenames = config.getboolean('SSD' , 'WINDOWS_COMPLIANT')
  90.  
  91. # NZBDownload
  92. NZBdownload = config.getboolean('SSD' , 'NZB_DOWNLOAD')
  93.  
  94. # NZB 'blackhole' directory
  95. if sys.platform == "win32":
  96.     NZB_BlackHole = config.get('SSD' , 'NZB_BLACKHOLE_WIN32')
  97. else:
  98.     NZB_BlackHole = config.get('SSD' , 'NZB_BLACKHOLE_LINUX')
  99.  
  100. # NZBs.org UserID & password
  101. nzbs_org_userid = config.get('SSD' , 'NZBS_ORG_USERID')
  102. nzbs_org_password = config.get('SSD' , 'NZBS_ORG_PASSWORD')
  103.  
  104. ################################### END VALUES FROM CONFIG FILE #####################################
  105.  
  106. # Setup Logging
  107. # create logger
  108. logger = logging.getLogger("SABnzbd Smart Downloader")
  109. logger.setLevel(logging.DEBUG)
  110.  
  111. # create console handler and set level to debug
  112. consoleloghandler = logging.StreamHandler()
  113. consoleloghandler.setLevel(logging.DEBUG)
  114. # create formatter
  115. formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
  116. # add formatter to consoleloghandler
  117. consoleloghandler.setFormatter(formatter)
  118. # add consoleloghandler to logger
  119. logger.addHandler(consoleloghandler)
  120.  
  121. # create file log handler and set level to debug
  122. #fileloghandler = logging.FileHandler(errorlog)
  123. # Use new Daily Log Rotation
  124. fileloghandler = dailylogrotation.DailyRollingFileHandler(errorlog)
  125. fileloghandler.setLevel(logging.DEBUG)
  126. # add formatter to fileloghandler
  127. fileloghandler.setFormatter(formatter)
  128. # add fileloghandler to logger
  129. logger.addHandler(fileloghandler)
  130.  
  131. config = {}
  132.  
  133. reportdict = {}
  134.  
  135. nzbDetails = {}
  136.  
  137. config['valid_filename_chars'] = """0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!@£$%^&*()_+=-[]:{}"'.,<>`~? """
  138.  
  139. config['with_ep_name'] = '%(seriesname)s - S%(seasno)02dE%(epno)02d - %(epname)s'
  140.  
  141. # Force the stripping of invalid Windows characters, even if the current
  142. # platform is not detected as Windows
  143. config['force_windows_compliant_filenames'] = True
  144.  
  145. if sys.platform == "win32" or config['force_windows_compliant_filenames']:
  146.     # " * : < > ? | \ are all invalid on Windows
  147.     config['valid_filename_chars'] = config['valid_filename_chars'].replace("\"*:<>?|\\","")
  148.  
  149. config['valid_filename_chars_regex'] = re.escape(config['valid_filename_chars'])
  150.  
  151. # Regex's to parse filenames with. Must have 3 groups, seriesname, season number
  152. # and episode number. Use (?: optional) non-capturing groups if you need others.
  153. # taken from tvnamer.py script
  154. config['name_parse'] = [
  155.     # foo_[s01]_[e01]
  156.     re.compile('''^([%s]+?)[ \._\-]\[[Ss]([0-9]+?)\]_\[[Ee]([0-9]+?)\]?([%s]+)[^\\/]*$''' % (config['valid_filename_chars_regex'], config['valid_filename_chars_regex']) ),
  157.     # foo.1x09*
  158.     re.compile('''^([%s]+?)[ \._\-]\[?([0-9]+)x([0-9]+)([%s]+)[^\\/]*$''' % (config['valid_filename_chars_regex'], config['valid_filename_chars_regex']) ),
  159.     # foo.s01e01.title
  160.     re.compile('''^([%s]+?)[ \._\-][Ss]([0-9]+)[\.\- ]?[Ee]([0-9]+)[ \._\-]([%s]+)[^\\/]*$''' % (config['valid_filename_chars_regex'], config['valid_filename_chars_regex']) ),
  161.     # foo.s01.e01, foo.s01_e01, foo.s01e01
  162.     re.compile('''^([%s]+?)[ \._\-][Ss]([0-9]+)[\.\- ]?[Ee]([0-9]+)([%s]+)[^\\/]*$''' % (config['valid_filename_chars_regex'], config['valid_filename_chars_regex']) ),
  163.     # foo.103*
  164.     re.compile('''^([%s]+)[ \._\-]([0-9]{1})([0-9]{2})[\._ -]([%s]+)[^\\/]*$''' % (config['valid_filename_chars_regex'], config['valid_filename_chars_regex']) ),
  165.     # foo.0103*
  166.     re.compile('''^([%s]+)[ \._\-]([0-9]{2})([0-9]{2,3})[\._ -]([%s]+)[^\\/]*$''' % (config['valid_filename_chars_regex'], config['valid_filename_chars_regex']) ),
  167. ]
  168.  
  169.  
  170. # Class to allow an exception to be raised
  171. # if format is not wanted
  172. class FormatNotWanted:
  173.     pass
  174.  
  175. class formatError:
  176.     pass
  177.  
  178. # SABnzbd style illegal chars
  179. illegal = r'\/<>?*:;|"'
  180. legal   = r'++{}!@--#`'
  181. translate_table = string.maketrans(illegal, legal)
  182.  
  183. #Directory Seperator Character (default linux)
  184. ds="/"
  185. #If Win32 then double backslash
  186. if sys.platform == "win32":
  187.     ds="\\"
  188.  
  189. already=[]
  190.  
  191. #RegEx Compiles
  192. daily=re.compile("([0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9])|([0-9][0-9][0-9][0-9]\.[0-9][0-9]\.[0-9][0-9])")
  193. video_formats_re = re.compile( video_formats, re.IGNORECASE )
  194.  
  195. class SimpleCache:
  196.     def __init__(self, filename="TVDBNames.cache", debug=1, safe=1):
  197.  
  198.         self.file = os.path.join(os.path.dirname(sys.argv[0]),filename)
  199.         self.debug = debug
  200.         self.safe = safe
  201.         self.init = 0
  202.  
  203.         self.msg("loading " + self.file + " .. ")
  204.         try:
  205.             cached = file(self.file,"r")
  206.         except IOError, EOFError:
  207.             try:
  208.                 self.msg("new cache file: " + self.file)
  209.                 cached = file(self.file,"w")
  210.                 self.data = {'evalcacheversion_creation':'20090212a-jmf'}
  211.                 self.msg(self.data)
  212.                 pickle.dump(self.data, cached)
  213.                 cached.close()
  214.                 self.msg(".. created.")
  215.                 cached = file(self.file,"r")
  216.             except IOError, EOFError:
  217.                 self.msg("cannot open/create " + self.file)
  218.                 return 0
  219.  
  220.         self.data = pickle.load(cached)
  221.         self.msg(".. done.")
  222.         self.msg(self.data)
  223.         cached.close()
  224.  
  225.     def msg(self, val):
  226.         if self.debug:
  227.             print val
  228.  
  229.     def save(self):
  230.         self.msg("saving " + self.file)
  231.         self.msg(self.data)
  232.         try:
  233.             cached = file(self.file,"w")
  234.             pickle.dump(self.data, cached)
  235.             cached.close()
  236.         except IOError:
  237.             self.msg("error")
  238.         self.msg(".. done.")
  239.  
  240.     def read(self, key):
  241.         self.msg("looking for " + str(key))
  242.         if str(key) in self.data:
  243.             if self.debug:
  244.                 self.msg("hit: " + str(key) + " = " + str(self.data[str(key)]))
  245.             return self.data[str(key)]
  246.  
  247.         self.msg("not cached.")
  248.         return 0
  249.  
  250.     def write(self, key, value):
  251.         self.msg("storing " + str(key))
  252.         self.data[str(key)] = str(value)
  253.  
  254.         if self.debug and self.data[str(key)]:
  255.             self.msg(".. done: " + str(self.data[str(key)]))
  256.  
  257.         if self.safe:
  258.             self.save()
  259.  
  260.         return self.data[str(key)]
  261. # end class SimpleCache
  262.  
  263. cache = SimpleCache()
  264.  
  265. def removeIllegalChars(origstring):
  266.  
  267.     if SABnzbdIllegalCharsStyle:
  268.         try:
  269.             return str(origstring).translate(translate_table)
  270.         except:
  271.             return origstring
  272.     else:
  273.         try:
  274.             # convert unicode to ASCII
  275.             newstring = origstring.encode('ascii', 'ignore')
  276.             # replace illegal chars with nothing
  277.             return re.sub("[*:<>|]", "", newstring).strip()
  278.         except:
  279.             return origstring
  280.  
  281.     #end if SABnzbdIllegalStyle
  282.  
  283. #end def removeIllegalChars
  284.  
  285. def isInQueue(download_title):
  286.     queue=xml.dom.minidom.parse(urllib.urlopen(my_SAB + "/sabnzbd/api?mode=qstatus&output=xml" + "&apikey=" + SAB_API_KEY + "&ma_username=" + my_SAB_username + "&ma_password=" + my_SAB_password))
  287.     for job in queue.getElementsByTagName("job"):
  288.         filename=job.getElementsByTagName("filename")[0].childNodes[0].data
  289.         regex_download_title_string = removeIllegalChars(download_title)
  290.         regex_download_title = re.compile(regex_download_title_string, re.IGNORECASE)
  291.         if re.match(regex_download_title, filename):
  292.             if debug:
  293.                 logger.info("IS IN QUEUE: " + regex_download_title.pattern)
  294.             #end if debug
  295.             return 1
  296.         #end if re.match
  297.     #end for job
  298.     if debug:
  299.         logger.info("IS NOT IN QUEUE: " + regex_download_title.pattern)
  300.     #end if debug
  301.     return 0
  302. #end def isInQueue
  303.  
  304. def isInQueueSmart(nzbDetails):
  305.     logger.info("ENTERED isInQueueSmart")
  306.     # Compile the regex
  307.     regex_download_title = re.compile(nzbDetails['SeriesName'] + " - S" + str(nzbDetails['SeasonNumber_Type' + SeasonType]) + "E" + str(nzbDetails['EpisodeNumber_Type' + EpisodeType]), re.IGNORECASE)
  308.  
  309.     # Get the Queue
  310.     queue=xml.dom.minidom.parse(urllib.urlopen(my_SAB + "/sabnzbd/api?mode=qstatus&output=xml" + "&apikey=" + SAB_API_KEY + "&ma_username=" + my_SAB_username + "&ma_password=" + my_SAB_password))
  311.  
  312.     # Process each job in Queue
  313.     for job in queue.getElementsByTagName("job"):
  314.         filename=job.getElementsByTagName("filename")[0].childNodes[0].data
  315.         try:
  316.             if debug:
  317.                 logger.info("CHECK QUEUE: " + filename)
  318.             #end if debug
  319.  
  320.             filename = formatReport(filename)
  321.             series,epstr,eptitle=filename.split(' - ')
  322.             series=cleanSeriesName(series)
  323.             filename=series + " - " + epstr + " - " + eptitle
  324.  
  325.             if debug:
  326.                 logger.info("CHECK REFORMAT QUEUE: " + filename)
  327.             #end if debug
  328.             if re.search(regex_download_title, filename):
  329.                 if debug:
  330.                     logger.info("IS IN QUEUE: " + regex_download_title.pattern)
  331.                 #end if debug
  332.                 return 1
  333.             #end if re.match
  334.         except:
  335.             # Queue Check Failed, so assume it's not in Queue
  336.             pass
  337.     #end for job
  338.     if debug:
  339.         logger.info("IS NOT IN QUEUE: " + regex_download_title.pattern)
  340.     #end if debug
  341.     return 0
  342. #end def isInQueue
  343.  
  344. def isInHistorySmart(series, season, episode):
  345.     # Compile the regex
  346.     regex_download_title = re.compile(series + " - S" + season + "E" + episode, re.IGNORECASE)
  347.  
  348.     # Get the Histroy
  349.     queue=xml.dom.minidom.parse(urllib.urlopen(my_SAB + "/sabnzbd/api?mode=history&output=xml" + "&apikey=" + SAB_API_KEY + "&ma_username=" + my_SAB_username + "&ma_password=" + my_SAB_password))
  350.  
  351.     # Process each job in Queue
  352.     for job in queue.getElementsByTagName("slot"):
  353.         jobname=job.getElementsByTagName("name")[0].childNodes[0].data
  354.         try:
  355.             if debug:
  356.                 logger.info("CHECK HISTORY: " + jobname)
  357.             #end if debug
  358.             jobname=reformatReport(filename)
  359.             series,epstr,eptitle=jobname.split(' - ')
  360.             series=cleanSeriesName(series)
  361.             jobname=series + " - " + epstr + " - " + eptitle
  362.             if debug:
  363.                 logger.info("CHECK REFORMAT HISTORY: " + jobname)
  364.             #end if debug
  365.             if re.search(regex_download_title, jobname):
  366.                 if debug:
  367.                     printlogger.info("IS IN HISTORY: " + regex_download_title.pattern)
  368.                 #end if debug
  369.                 return 1
  370.             #end if re.match
  371.         except:
  372.             # Queue Check Failed, so assume it's not in Queue
  373.             pass
  374.     #end for job
  375.     if debug:
  376.         logger.info("IS NOT IN HISTORY: " + regex_download_title.pattern)
  377.     #end if debug
  378.     return 0
  379. #end def isInHistorySmart
  380.  
  381. def isWanted(nzbDetails):
  382.     if debug:
  383.         logger.info("CHECK WANTED: " + nzbDetails['SeriesName'].lower() )
  384.     #end if debug
  385.  
  386.     for dir in tv_dirs:
  387.         for filename in os.listdir(dir):
  388.             #if debug:
  389.             #    logger.info("DEBUG: " + filename.lower() )
  390.             #end if debug
  391.             if filename.lower() == nzbDetails['SeriesName'].lower():
  392.                 if debug:
  393.                     logger.info("SHOW WANTED: " + nzbDetails['SeriesName'])
  394.                 #end if debug
  395.                 return 1
  396.             #end if os.path
  397.         #end for filename
  398.     #end for dir
  399.     if debug:
  400.         logger.info("SHOW NOT WANTED: " + nzbDetails['SeriesName'])
  401.     #end if debug
  402.     return 0
  403. #end def isWanted
  404.  
  405. def isFormatWanted(nzbDetails, feed_type):
  406.     # Override Format checking for NewzBin feeds
  407.     if feed_type == 'NewzBin':
  408.         if debug:
  409.             logger.info("NEWZBIN FORMAT OVERRIDE: " + nzbDetails['Report-Formatted'])
  410.         #end if debug
  411.         return 1
  412.     #end if feed_type
  413.  
  414.     # Allow per Show/Season Format OverRide
  415.     for dir in tv_dirs:
  416.         if os.path.exists(dir + ds + nzbDetails['SeriesName'] ):
  417.             for filename in os.listdir(dir + ds + nzbDetails['SeriesName'] ):
  418.                 if filename[9:] == 'format':
  419.                     if filename[1:3] == season:
  420.                         if re.search(filename[4:8], report , re.IGNORECASE):
  421.                             if debug:
  422.                                 logger.info("SHOW FORMAT WANTED: " + nzbDetails['Report-Formatted'] )
  423.                             #end if debug
  424.                             return 1
  425.                         else:
  426.                             if debug:
  427.                                 logger.info("SHOW FORMAT NOT WANTED: " + nzbDetails['Report-Formatted'] )
  428.                             #end if debug
  429.                             return 0
  430.                         #end if re.search
  431.                     #end if file[1:3]
  432.                 #end if file[9:]
  433.             #end for file
  434.         #end if os.path
  435.     #end for dir
  436.  
  437.     # General Format Wanted
  438.     # Used if Show/Season not specified
  439.     format_object = video_formats_re.search( nzbDetails['Report-Formatted'] , re.IGNORECASE)
  440.     if format_object:
  441.         if debug:
  442.             logger.info("FORMAT WANTED: " + nzbDetails['Report-Formatted'] )
  443.         #end if debug
  444.         return 1
  445.     #end if format_object
  446.  
  447.     if debug:
  448.  
  449.         logger.info("FORMAT NOT WANTED: " + video_formats_re.pattern + " " + nzbDetails['Report-Formatted'] )
  450.     #end if debug
  451.  
  452.     raise FormatNotWanted
  453.  
  454. #end def isFormatWanted
  455.  
  456. def isMissing(nzbDetails):
  457.  
  458.     logger.info("LOOKING: " + "Season " + str(nzbDetails['SeasonNumber']) + ", Episode " + str(nzbDetails['EpisodeNumber']) )
  459.  
  460.     for dir in tv_dirs:
  461.  
  462.         nzbDetails['FolderName'] = showToFolder(nzbDetails['SeriesName'])
  463.  
  464.         logger.info("FOLDERNAME: " + nzbDetails['FolderName'] )
  465.  
  466.         series_path=dir + ds + nzbDetails['FolderName'] + ds + "Season " + str(nzbDetails['SeasonNumber_Type' + SeasonType])
  467.  
  468.         logger.info("CHECK: " + nzbDetails['SeriesName'] + " for seasons to IGNORE")
  469.  
  470.         # ignore this season if told to do so (existence of file or directory called ignore Season x)
  471.         if os.path.exists(dir + ds + nzbDetails['FolderName'] + ds + "ignore Season " + str(nzbDetails['SeasonNumber_Type' + SeasonType]) ):
  472.             if debug:
  473.                 logger.info("IGNORE: Season " + str(nzbDetails['SeasonNumber']) )
  474.             #end if debug
  475.             return 0
  476.         else:
  477.             if debug:
  478.                 logger.info("PROCESS: Season " + str(nzbDetails['SeasonNumber_Type' + SeasonType]) )
  479.             #end if debug
  480.         #end if os.path.exists
  481.  
  482.         if os.path.exists(series_path):
  483.             if debug:
  484.                 logger.info("EXIST: " + series_path)
  485.             #end if debug
  486.             for tested_episode in os.listdir(series_path):
  487.                 if debug:
  488.                     logger.info("CHECK: " + tested_episode)
  489.                 #end if debug
  490.                 for r in config['name_parse']:
  491.                     match = r.match(tested_episode)
  492.                     if match:
  493.                         logger.info("FOUND " + str(match.lastindex) + " Items.")
  494.                         if match.lastindex == 4:
  495.                             seriesname, seasno, epno, epname = match.groups()
  496.                         elif match.lastindex == 3:
  497.                             seriesname, seasno, epno = match.groups()
  498.                         #end if
  499.                         if re.search(nzbDetails['SeriesName'], seriesname, re.IGNORECASE):
  500.                              if seasno == nzbDetails['SeasonNumber_Type' + SeasonType]:
  501.                                 if epno == nzbDetails['EpisodeNumber_Type' + SeasonType]:
  502.                                     if debug:
  503.                                         logger.info("MATCH FOUND: " + "Season " + str(nzbDetails['SeasonNumber']) + ", Episode " + str(nzbDetails['EpisodeNumber']) )
  504.                                     #end if debug
  505.                                     return 0
  506.                                 #end if epno
  507.                             #end if seasno
  508.                         #end if seriesname
  509.                     #end if match
  510.                 #end for r
  511.             #end for tested_episode
  512.         else:
  513.             if debug:
  514.                 logger.info("NOT EXIST: " + series_path)
  515.             #end if debug
  516.         #end if os.path.exists
  517.  
  518.         if debug:
  519.             logger.info("MISSING: " + "Season " + str(nzbDetails['SeasonNumber']) + ", Episode " + str(nzbDetails['EpisodeNumber']) )
  520.         #end if debug
  521.     #end for dir
  522.     return 1
  523. #end def isMissing
  524.  
  525. def addToQueue(report_id, feed_type):
  526.  
  527.     logger.info("ADD QUEUE: " + time.strftime("%Y-%m-%d %H:%M:%S")+": "+ report_id)
  528.  
  529.     #if feed_type == 'NewzBin':
  530.         # NewzBin RSS
  531.         #newzbin_id = re.search('[0-9][0-9][0-9]+',report_id)
  532.         #if newzbin_id:
  533.         #    report_id = re.sub("/", "", newzbin_id.group(0))
  534.      #   logger.info("REPORT_ID: " + report_id)
  535.       #  urllib.urlopen(my_SAB + "/sabnzbd/api?mode=addid&name=" + report_id + my_SAB_params + "&apikey=" + SAB_API_KEY + "&ma_username=" + my_SAB_username + "&ma_password=" + my_SAB_password)
  536.  
  537.     if feed_type == 'NewzBin':
  538.         # NewzBin RSS - Commented Out....
  539.         newzbin_id = re.search('[0-9][0-9][0-9]+',report_id)
  540.         if newzbin_id:
  541.             report_id = re.sub("/", "", newzbin_id.group(0))
  542.             logger.info("REPORT_ID: " + report_id)
  543.             urllib.urlopen(my_SAB + "/sabnzbd/api?mode=addid"+ "&name=" + report_id  + my_SAB_params + "&apikey=" + SAB_API_KEY)
  544.             #urllib.urlopen(my_SAB + "/sabnzbd/api?mode=addid"+ "&name=" + report_id + my_SAB_params + "&ma_username=" + my_SAB_username + "&ma_password=" + my_SAB_password + "&apikey=" + SAB_API_KEY)
  545.  
  546.     else:
  547.         # 'Other' NZB RSS
  548.         urllib.urlopen(my_SAB + "/sabnzbd/api?mode=addurl&name=" + report_id + my_SAB_params + "&apikey=" + SAB_API_KEY + "&ma_username=" + my_SAB_username + "&ma_password=" + my_SAB_password)
  549.     #end if
  550.  
  551. #end def download
  552.  
  553. def downloadNZB(nzb_url, report):
  554.  
  555.     logger.info("DOWNLOADING: " + nzb_url)
  556.  
  557.     # Code originally taken from urlgrabber.py (SABnzbd Team)
  558.  
  559.     opener = urllib.FancyURLopener({})
  560.     opener.prompt_user_passwd = None
  561.     opener.addheader('Accept-encoding','gzip')
  562.     filename = NZB_BlackHole + ds + report + '.nzb'
  563.     try:
  564.         fn, header = opener.retrieve(nzb_url, filename)
  565.         return 1
  566.     except:
  567.         fn = None
  568.         filename = True
  569.         return 0
  570.  
  571. #end def downloadNZB
  572.  
  573.  
  574. def cleanSeriesNameCache(series_title):
  575.     global cache
  576.  
  577.     theTVDB = Tvdb(debug = tvdb_debug, interactive = tvdb_interactive)
  578.  
  579.     good_series_title = removeIllegalChars(series_title)
  580.     #good_series_title = good_series_title.replace("(", "\(").replace(")", "\)")
  581.     good_series_title = re.sub('\.|_', ' ', good_series_title)
  582.  
  583.     if debug:
  584.         logger.info("NZB: " + good_series_title)
  585.     #end if debug
  586.  
  587.     try:
  588.         good_series_title = showAlias(good_series_title)
  589.  
  590.         if cache.read(good_series_title.lower()):
  591.             good_series_title = cache.read(good_series_title.lower())
  592.         else:
  593.             good_series_title = cache.write(good_series_title.lower(),theTVDB[good_series_title]['seriesname'])
  594.         #end if cache.read
  595.  
  596.         if debug:
  597.             logger.info("TVDB: " + good_series_title)
  598.         #end if debug
  599.  
  600.     except tvdb_shownotfound:
  601.         # Didn't find the show @ theTVDB,
  602.         # most likely has 'and' and theTVDB has '&'
  603.         try:
  604.             if cache.read(re.sub(' [aA]nd ', ' & ', good_series_title.lower())):
  605.                 good_series_title = cache.read(re.sub(' [aA]nd ', ' & ', good_series_title.lower()))
  606.             else:
  607.                 good_series_title = cache.write(good_series_title.strtolower(),theTVDB[re.sub(' [aA]nd ', ' & ', good_series_title.strtolower())]['seriesname'])
  608.             #end if cache.read
  609.  
  610.             if debug:
  611.                 logger.info("TVDB: " + good_series_title)
  612.             #end if debug
  613.  
  614.         except tvdb_shownotfound:
  615.             # Well, didn't find it a second time, even using '&'
  616.             # return cleaned good_series_title
  617.             good_series_title = good_series_title
  618.             if debug:
  619.                 logger.info("TVDB NOT FOUND: " + good_series_title)
  620.             #end if debug
  621.  
  622.     #If Win32 then remove illegal chars
  623.     if sys.platform == "win32" or force_windows_compliant_filenames:
  624.         good_series_title = removeIllegalChars(good_series_title)
  625.     #end if sys.platform
  626.  
  627.     return good_series_title
  628.  
  629. #end def cleanSeriesNameCache
  630.  
  631.  
  632. def cleanSeriesName(series_title):
  633.     good_series_title = removeIllegalChars(series_title)
  634.     #good_series_title = good_series_title.replace("(", "\(").replace(")", "\)")
  635.     good_series_title = re.sub('\.|_', ' ', good_series_title)
  636.  
  637.     theTVDB = Tvdb(debug = tvdb_debug, interactive = tvdb_interactive)
  638.     if debug:
  639.         logger.info("NZB: " + good_series_title)
  640.     #end if debug
  641.  
  642.     try:
  643.         good_series_title = showAlias(good_series_title)
  644.         good_series_title = theTVDB[ good_series_title ]['seriesname']
  645.         if debug:
  646.             logger.info("TVDB: " + good_series_title)
  647.         #end if debug
  648.  
  649.     except tvdb_shownotfound:
  650.         # Didn't find the show @ theTVDB,
  651.         # most likely has 'and' and theTVDB has '&'
  652.         try:
  653.             good_series_title = theTVDB[ re.sub(' [aA]nd ', ' & ', good_series_title) ]['seriesname']
  654.             if debug:
  655.                 logger.info("TVDB: " + good_series_title)
  656.             #end if debug
  657.  
  658.         except tvdb_shownotfound:
  659.             # Well, didn't find it a second time, even using '&'
  660.             # return cleaned good_series_title
  661.             good_series_title = good_series_title
  662.             if debug:
  663.                 logger.info("TVDB NOT FOUND: " + good_series_title)
  664.             #end if debug
  665.  
  666.     #If Win32 then remove illegal chars
  667.     if sys.platform == "win32" or force_windows_compliant_filenames:
  668.         good_series_title = removeIllegalChars(good_series_title)
  669.     #end if sys.platform
  670.  
  671.     return good_series_title
  672.  
  673. #end def cleanSeriesName
  674.  
  675.  
  676. def formatReport( report ):
  677.     # format into ' - ' seperated parts
  678.     # showname - seasonepisode - title
  679.  
  680.     for r in config['name_parse']:
  681.         match = r.match( report )
  682.         if match:
  683.             seriesname, seasno, epno, epname = match.groups()
  684.  
  685.             # Remove any space dash space that might be left over
  686.             # We're going to put them in anyway but need to make sure we don't 'double' them
  687.             reportdict['seriesname'] = re.sub("\s\-\s|\s\-", "", seriesname)
  688.             reportdict['seasno'] = int(seasno)
  689.             reportdict['epno'] = int(epno)
  690.             reportdict['epname'] = re.sub("\s\-\s|\s\-", "", epname)
  691.  
  692.             # Format the report name
  693.             report = config['with_ep_name'] % (reportdict)
  694.  
  695.             return report
  696.         #end if match
  697.     #end for r
  698.     raise formatError
  699. #end def formatReport
  700.  
  701.  
  702. def showAlias(showname):
  703.     # Takes a showname and matches it to an alias
  704.     # e.g. CSI should map to CSI: Crime Scene Investigation and not CSI: NY
  705.     # as returned by theTVDB api
  706.  
  707.     for alias in aliases.items('ALIASES'):
  708.         if re.match(alias[0], showname, re.IGNORECASE):
  709.             logger.info("MATCHED " + alias[1])
  710.             return alias[1]
  711.         #end if re.match
  712.     #end for alias
  713.  
  714.     # Didn't match anything so return the passed showname
  715.     return showname
  716.  
  717. #def showAlias
  718.  
  719.  
  720. def showToFolder(showname):
  721.     # Takes a showname and returns the foldername associated
  722.     # e.g. CSI NY could return CSI New York
  723.  
  724.     for folder_alias in aliases.items('FOLDER ALIASES'):
  725.         if re.match(folder_alias[0], showname, re.IGNORECASE):
  726.             logger.info("MATCHED " + folder_alias[1])
  727.             return folder_alias[1]
  728.         #end if re.match
  729.     #end for alias
  730.  
  731.     # Didn't match anything so return the passed showname
  732.     return showname
  733.  
  734. #def showToFolder
  735.  
  736.  
  737. def Main():
  738.     for feed in nzbfeeds:
  739.         logger.info("PROCESS NZB FEED: " + feed)
  740.  
  741.         # Set feed type
  742.         # NewzBin feeds have different 'titles'
  743.         if re.search('newzbin', feed, re.IGNORECASE):
  744.             feed_type = 'NewzBin'
  745.             if debug:
  746.                 logger.debug("FEED: " + feed_type)
  747.             #end if debug
  748.         elif re.search('nzbs.org', feed, re.IGNORECASE):
  749.             feed_type = 'nzbs.org'
  750.             if debug:
  751.                 logger.debug("FEED: " + feed_type)
  752.             #end if debug
  753.         else:
  754.             feed_type = 'Other'
  755.             if debug:
  756.                 logger.debug("FEED: " + feed_type)
  757.             #end if debug
  758.         #end if re.search
  759.  
  760.         # use feedparser to parse the 'feed'
  761.         RSSfeed = feedparser.parse( feed )
  762.  
  763.         for RSSitem in RSSfeed.entries:
  764.             nzbDetails['Report-Original'] = RSSitem.title
  765.             logger.info("PROCESSING: " + nzbDetails['Report-Original'] )
  766.             try:
  767.                 nzbDetails['Report-Formatted'] = formatReport( nzbDetails['Report-Original'] )
  768.                 #Split report into 3 parts
  769.                 series,epstr,title = nzbDetails['Report-Formatted'].split(' - ')
  770.  
  771.                 if daily.match(epstr):
  772.                     year,month,date=epstr.split("-")
  773.                     season=year+"-"+month
  774.                     episode=date
  775.                 else:
  776.                     season = epstr[1:3]
  777.                     episode = epstr[4:]
  778.                 #end if daily.match
  779.  
  780.                 # Clean series name here (reduces number of times the same code is called)
  781.                 nzbDetails['SeriesName'] =cleanSeriesName(series)
  782.                 nzbDetails['SeasonNumber'] = int(season)
  783.                 nzbDetails['EpisodeNumber'] = int(episode)
  784.                 nzbDetails['SeasonNumber_Type2'] = '%(SeasonNumber)d' % nzbDetails
  785.                 nzbDetails['SeasonNumber_Type1'] = '%(SeasonNumber)02d' % nzbDetails
  786.                 nzbDetails['EpisodeNumber_Type2'] = '%(EpisodeNumber)d' % nzbDetails
  787.                 nzbDetails['EpisodeNumber_Type1'] = '%(EpisodeNumber)02d' % nzbDetails
  788.  
  789.                 toDownload = False
  790.  
  791.                 if isWanted(nzbDetails):
  792.                     if isFormatWanted(nzbDetails, feed_type):
  793.                         if isMissing(nzbDetails):
  794.                             if not useSmartQueue:
  795.                                 if not isInQueue(nzbDetails):
  796.                                     toDownload = True
  797.                                 #end if not isInQueue
  798.                             else:
  799.                                 if not isInQueueSmart(nzbDetails):
  800.                                     toDownload = True
  801.                                 #end if not isInQueueSmart
  802.                             #end if useSmartQueue
  803.                         #end if isMissing
  804.                     #end if isFormatWanted
  805.                 #end if isWanted(seies)
  806.  
  807.                 if toDownload:
  808.                     if feed_type == 'NewzBin':
  809.                         msgid=RSSitem.id
  810.                     elif feed_type == 'nzbs.org':
  811.                         msgid=RSSitem.guid
  812.                     else:
  813.                         msgid=RSSitem.link
  814.                     #end if NewzBin
  815.                     msgid=urllib.quote(msgid, safe=":/")
  816.                     if NZBdownload:
  817.                         downloadNZB(msgid, nzbDetails['Report-Formatted'] )
  818.                     else:
  819.                         addToQueue(msgid, feed_type)
  820.                     #end if NZBdownload
  821.                 #end if toDownload
  822.  
  823.             except KeyboardInterrupt:
  824.                 logger.info("CTRL-C Intercepted.")
  825.                 logger.info("Exiting......")
  826.                 logging.shutdown()
  827.                 sys.exit(0)
  828.  
  829.             except:
  830.                 logger.info("FAILED PROCESSING: " + nzbDetails['Report-Original'] )
  831.                 pass
  832.  
  833.             #end try:
  834.         #end for RSSitem
  835.     #end for feed
  836.  
  837.     # Shutdown logging
  838.     logging.shutdown()
  839.     sys.exit(0)
  840. #end def Main
  841.  
  842. if __name__ == '__main__':
  843.     Main()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement