Advertisement
Guest User

minimad

a guest
Jan 21st, 2009
149
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 15.51 KB | None | 0 0
  1. #!/usr/bin/env python
  2. import urllib
  3. import xml.dom.minidom
  4. import re
  5. import time
  6. import os
  7. import sys
  8. import string
  9.  
  10. from stat import S_ISDIR
  11.  
  12. from tvdb_api import (tvdb_error, tvdb_shownotfound, tvdb_seasonnotfound,
  13.     tvdb_episodenotfound, tvdb_episodenotfound, tvdb_attributenotfound, tvdb_userabort)
  14. from tvdb_api import Tvdb
  15.  
  16. ######
  17. # TV #
  18. ######
  19.  
  20. ################################### CHANGE LOG ######################################################
  21. #
  22. # 090103 1600   Now uses SAB 0.4.6 illegal character replacement behaviour
  23. # 090112 2300   Uses tvdb_api to validate show name
  24. # 090114 2300   Cleaned up the code, used re.pattern in print statements where possible
  25. #               and tried to limit number of places to change for new formats by using vars
  26. # 090117 2300   Fixed bug - Queued name is meant to be original report, NOT reformated version
  27. # 090117 2330   Added required video format functionality
  28. # 090117 2345   Moved Season/Episode regex formats to user config area
  29. # 090120 2045   Added Debug setting for 'print' statements
  30. #               Fixed IsMissing regex to look for both cases of S and E i.e. s and S
  31. #               Fixed season_to_check variable
  32. #               A few wording changes
  33. # 090121 2330   Made 'Smart' queue optional
  34. #               Removing Illegal Chars at end of cleanSeriesName if Win32
  35. #               Moved Format check into isWanted.  Check format before checking for show.
  36. #               Should reduce processing time by not checking all shows then finding out that format isn't wanted
  37. #               Removed sum_file_size function as not
  38. # 090122 0000   Added 'extra' debug messages
  39. #               Fixed 'Smart' Queue
  40. #               Moved Change Log to Top of Script
  41. #               Added 'Known Issues'
  42. #               Made Code more consistant
  43. #
  44. ################################### CHANGE LOG ######################################################
  45.  
  46. ################################### KNOWN ISSUES ####################################################
  47. #
  48. # Trying to fetch NZB from
  49. #
  50. ################################### KNOWN ISSUES ####################################################
  51.  
  52.  
  53. ################################### IMPORTANT INFO ##################################################
  54. #  To ignore a whole season, create a file or directory in the directory for your show with this name
  55. #  ignore Season x
  56. #  e.g. c:\tv\lost\ignore Season 1
  57. ################################### IMPORTANT INFO ##################################################
  58.  
  59. #################################### CHANGE TO YOUR OWN VALUES ######################################
  60. #
  61. # set this to the location of your downloads.  the first entry must be your live download area
  62. if sys.platform == "win32":
  63.     tv_dirs=[ "Z:" ]
  64. else:
  65.     tv_dirs=[ "/shares/LVM/TV_Shows" ]
  66.  
  67. # the RSS link to your search
  68. tvurl="http://feeds.feedburner.com/minimad"
  69.  
  70. # your SABNZB address
  71. my_SAB="http://192.168.1.3:9002"
  72.  
  73. # additional SAB parameters to e.g. specify the category, and/or script to run.  see http://sabnzbd.wikidot.com/automation-support
  74. my_SAB_params="&cat=tv"
  75.  
  76. # Use NewzBin feed
  77. # Changes several sections to use NewzBin specific functionality
  78. NewzBin=False
  79.  
  80. # theTVDB api
  81. # Set tvdb_debug=True to see debug info from theTVDB api
  82. tvdb_debug=False
  83. # Set tvdb_interactive=True to select shows returned, otherwise it takes first show returned (usually the right one)
  84. tvdb_interactive=False
  85.  
  86. # Required video formats
  87. # for x264 enter [xX]264
  88. # for xvid enter [xX][vV][iI][dD]
  89. # for 720p enter 720[pP]
  90. # for 1080p enter 1080[pP]
  91. # for multiple formats put | between them and brackets around each group e.g.
  92. # ([xX]264)|([xX][vV][iI][dD])
  93. #
  94. video_formats="[xX][vV][iI][dD]"
  95.  
  96. # Season/Episode Formats
  97. # Standard Regex with brackets around each format and seperated by |
  98. season_formats="([sS][0-9][0-9])|([0-9]?[0-9][xX])"
  99. episode_formats="([eE][0-9][0-9])|([xX][0-9][0-9])"
  100.  
  101. # DEBUG
  102. # Set to True, to print what it's doing
  103. debug = True
  104.  
  105. # 'Smart' Queue checking
  106. # Set to True to use 'Smart' Queue checking
  107. # Looks for the Show, Episode in Queue rather than the filename
  108. useSmartQueue = True
  109.  
  110. ################################### CHANGE TO YOUR OWN VALUES #####################################
  111.  
  112.  
  113. # Class to allow an exception to be raised
  114. # if format is not wanted
  115. class FormatNotWanted:
  116.     pass
  117.  
  118. illegal = r'\/<>?*:;|"'
  119. legal   = r'++{}!@--#`'
  120. translate_table = string.maketrans(illegal, legal)
  121.  
  122.  
  123. #Directory Seperator Character (default linux)
  124. ds="/"
  125. #If Win32 then double backslash
  126. if sys.platform == "win32":
  127.     ds="\\"
  128.  
  129. already=[]
  130.  
  131. #RegEx Compiles
  132. daily=re.compile("[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]")
  133. season_re = re.compile( season_formats )
  134. episode_re = re.compile( episode_formats )
  135. video_formats_re = re.compile( video_formats )
  136.  
  137. def removeIllegalChars(origstring):
  138.         try:
  139.             return str(origstring).translate(translate_table)
  140.         except:
  141.             return origstring
  142. #end def removeIllegalChars
  143.  
  144. def isInQueue(download_title):
  145.     queue=xml.dom.minidom.parse(urllib.urlopen(my_SAB+"/sabnzbd/api?mode=qstatus&output=xml"))
  146.     for job in queue.getElementsByTagName("job"):
  147.         filename=job.getElementsByTagName("filename")[0].childNodes[0].data
  148.         regex_download_title_string = removeIllegalChars(download_title)
  149.         regex_download_title = re.compile(regex_download_title_string, re.IGNORECASE)
  150.         if re.match(regex_download_title, filename):
  151.             if debug:
  152.                 print("IS IN QUEUE: " + regex_download_title.pattern)
  153.             #end if debug
  154.             return 1
  155.         #end if re.match
  156.     #end for job
  157.     if debug:
  158.         print("IS NOT IN QUEUE: " + regex_download_title.pattern)
  159.     #end if debug
  160.     return 0
  161. #end def isInQueue
  162.  
  163. def isInQueueSmart(series, season, episode):
  164.     # Compile the regex
  165.     regex_download_title = re.compile(series + " - S" + season + "E" + episode, re.IGNORECASE)
  166.    
  167.     # Get the Queue
  168.     queue=xml.dom.minidom.parse(urllib.urlopen(my_SAB+"/sabnzbd/api?mode=qstatus&output=xml"))
  169.    
  170.     # Process each job in Queue
  171.     for job in queue.getElementsByTagName("job"):
  172.         filename=job.getElementsByTagName("filename")[0].childNodes[0].data
  173.         try:
  174.             if debug:
  175.                 print("Checking Queue Entry: " + filename)
  176.             #end if debug
  177.             filename=reformatReport(filename)
  178.             series,epstr,title=report.split(' - ')        
  179.             if re.search(regex_download_title, filename):
  180.                 if debug:
  181.                     print("IS IN QUEUE: " + regex_download_title.pattern)
  182.                 #end if debug
  183.                 return 1
  184.             #end if re.match
  185.         except:
  186.             # Queue Check Failed, so assume it's not in Queue
  187.             pass
  188.     #end for job
  189.     if debug:
  190.         print("IS NOT IN QUEUE: " + regex_download_title.pattern)
  191.     #end if debug
  192.     return 0
  193. #end def isInQueue
  194.  
  195. def isWanted(series_title, report):
  196.     if debug:
  197.         print("Checking if " + series_title + " is wanted")
  198.     #end if debug
  199.    
  200.     # if not using NewzBin, perform format check
  201.     # return 0 if its not wanted
  202.     # No point checking your TV directory if the format isn't wanted in the first place
  203.     if not NewzBin:
  204.         if not isFormatWanted(report):
  205.             if debug:
  206.                 print("Video Format not Wanted")
  207.             #end if debug
  208.             return 0
  209.         if debug:
  210.             print("Video Format is Wanted")
  211.         #end if debug
  212.     #end if not NewzBin
  213.        
  214.     for dir in tv_dirs:
  215.         series_path = dir + ds+ series_title
  216.         #    print series_path
  217.         if os.path.exists(series_path):
  218.             if debug:
  219.                 print("WANTED: " + series_title)
  220.             #end if debug
  221.             return 1
  222.         else:
  223.             if debug:
  224.                 print("NOT WANTED: " + series_title)
  225.             #end if debug
  226.             return 0
  227.         #end if os.path
  228.     #end for dir
  229. #end def isWanted
  230.  
  231. def isFormatWanted(report):
  232.     if debug:
  233.         print("Checking format")
  234.     #end if debug
  235.    
  236.     format_object = video_formats_re.search( report )
  237.     if format_object:
  238.         return 1
  239.     #end if format_object
  240.    
  241.     if debug:
  242.         print("Format Not Wanted: " + report)
  243.     #end if debug
  244.    
  245.     raise FormatNotWanted
  246.    
  247. #end def isFormatWanted
  248.  
  249. def isMissing(series_title, season_number, episode_number):
  250.     #
  251.     #regex_episode = re.compile(episode_number)
  252.     ############### uncomment this regex episode code if you want this to work how it used to
  253.     regex_episode = re.compile(series_title + " - S" + season_number + "E" + episode_number, re.IGNORECASE)
  254.     print "Looking for: " + regex_episode.pattern
  255.     ###############
  256.     for dir in tv_dirs:
  257.         #
  258.         series_path=series_title + ds
  259.         season_path=series_path + series_title + " - " + season_number
  260.         ###############
  261.         print "checking: "+dir
  262.         # ignore this season if told to do so (existence of file or directory called ignore Season x)
  263.         if os.path.exists(dir + ds + series_path + "ignore Season " + season_number):
  264.             if debug:
  265.                 print "ignoring: Season " + season_number
  266.             #end if debug
  267.             return 0
  268.         #end if os.path.exists
  269.         if os.path.exists(dir + ds + season_path):
  270.             if debug:
  271.                 print(season_path + " exists...")
  272.             #end if debug
  273.             for tested_episode in os.listdir(dir + ds + season_path):
  274.                 if debug:
  275.                     print("checking: " + tested_episode)
  276.                 #end if debug
  277.                 if re.match(regex_episode, tested_episode):
  278.                     if debug:
  279.                         print("FOUND: " + regex_episode.pattern)
  280.                     #end if debug
  281.                     return 0
  282.                 #end if re.match
  283.             #end for tested_episode
  284.         #end if os.path.exists
  285.         #end for dir
  286.         if debug:
  287.             print("MISSING: " + regex_episode.pattern)
  288.         #end if debug
  289.         # create the destination directory (PRIMARY tv dir).  this should prevent multiple downloads of the same thing in this session, or if we delete the download by hand
  290.         #os.makedirs(tv_dirs[0] + ds + series_path + season_path + ds + episode_number)
  291.     return 1
  292. #end def isMissing
  293.  
  294. def download(report_id):
  295.     print "Adding to QUEUE: " + time.strftime("%Y-%m-%d %H:%M:%S")+": "+ report
  296.     #put your address for the api stuff here
  297.     if NewzBin:
  298.         #NewzBin Style
  299.         urllib.urlopen(my_SAB+"/sabnzbd/api?mode=addid&name="+report_id+my_SAB_params)
  300.     else:
  301.         #'Other' NZB RSS
  302.         urllib.urlopen(my_SAB+"/sabnzbd/api?mode=addurl&name="+report_id+my_SAB_params)
  303.     #end if NewzBin
  304. #end def download
  305.  
  306. def cleanSeriesName(series_title):
  307.     good_series_title = removeIllegalChars(series_title)
  308.     good_series_title = good_series_title.replace("(", "\(").replace(")", "\)")
  309.     good_series_title = re.sub('\.|_', ' ', good_series_title)
  310.        
  311.     theTVDB = Tvdb(debug = tvdb_debug, interactive = tvdb_interactive)
  312.     if debug:
  313.         print "ORIG: ", good_series_title
  314.     #end if debug
  315.    
  316.     try:
  317.         good_series_title = theTVDB[ good_series_title ]['seriesname']
  318.         if debug:
  319.             print "TVDB: ", good_series_title
  320.         #end if debug
  321.        
  322.     except tvdb_shownotfound:
  323.         # Didn't find the show @ theTVDB,
  324.         # most likely has 'and' and theTVDB has '&'
  325.         try:
  326.             good_series_title = theTVDB[ re.sub(' [aA]nd ', ' & ', good_series_title) ]['seriesname']
  327.             if debug:
  328.                 print "TVDB2: ", good_series_title
  329.             #end if debug
  330.            
  331.         except tvdb_shownotfound:
  332.             # Well, didn't find it a second time, even using '&'
  333.             # return cleaned good_series_title
  334.             good_series_title = good_series_title
  335.             if debug:
  336.                 print "ERR: ", good_series_title
  337.             #end if debug
  338.                
  339.     #If Win32 then remove illegal chars
  340.     if sys.platform == "win32":
  341.         good_series_title = removeIllegalChars(good_series_title)
  342.     #end if sys.platform
  343.    
  344.     return good_series_title
  345. #end def cleanSeriesName
  346.  
  347. def reformatReport(report):
  348.     # Reformat report into ' - ' seperated parts
  349.     # show_name - seasonepisode - title
  350.     seasonobject = season_re.search(report)
  351.     episodeobject = episode_re.search(report)
  352.    
  353.     # Only reformat if we have a 'report' in '.SxxExx.' or '0x00' format
  354.     if seasonobject:
  355.         # ShowName = Everything before '.Sxx'
  356.         ShowName = report[:seasonobject.start()-1]
  357.        
  358.         # SeasonNumber = 'Sxx'
  359.         SeasonLength = seasonobject.end() - seasonobject.start()
  360.         if report[seasonobject.end()-1].lower() == 'x':
  361.             SeasonNumber = 'S0' + report[seasonobject.start()]
  362.         else:
  363.             SeasonNumber = report[seasonobject.start():seasonobject.end()]
  364.         #end if SeasonLength
  365.        
  366.         # EpisodeNumber = 'Exx'
  367.         if report[episodeobject.start()] == 'x':
  368.             EpisodeNumber = 'E' + report[episodeobject.start()+1:episodeobject.end()]
  369.         else:
  370.             EpisodeNumber = report[episodeobject.start():episodeobject.end()]
  371.         #end if report
  372.        
  373.         # EpisodeName = Everything after 'Exx.'
  374.         EpisodeName = report[episodeobject.end()+1:]
  375.        
  376.         # Reformat 'report'
  377.         report=ShowName + ' - ' + SeasonNumber + EpisodeNumber + ' - ' + EpisodeName
  378.        
  379.     return report
  380.    
  381. #def reformatReport
  382.  
  383. dom=xml.dom.minidom.parse(urllib.urlopen(tvurl))
  384. for node in dom.getElementsByTagName("item"):
  385.     report=node.getElementsByTagName("title")[0].childNodes[0].data
  386.     report_original=report
  387.     print "Processing :"+report
  388.     #try-catch block because some of this stuff fails hard when it gets a weird report, and pythons default behavior is to terminate
  389.     try:
  390.         # Next Section for 'Other' RSS feeds
  391.         if not NewzBin:
  392.             report=reformatReport(report)
  393.         #end if not Newzbin
  394.        
  395.         #Split report into 3 parts
  396.         series,epstr,title=report.split(' - ')
  397.        
  398.         if daily.match(epstr):
  399.             year,month,date=epstr.split("-")
  400.             season=year+"-"+month
  401.             episode=date
  402.         else:
  403.             if NewzBin:
  404.                 season,episode=epstr.split("x")
  405.             else:
  406.                 season=epstr[1:3]
  407.                 episode=epstr[4:]
  408.             #end if NewzBin
  409.         #end if daily.match
  410.        
  411.         # Clean series name here (reduces number of times the same code is called)
  412.         series=cleanSeriesName(series)
  413.        
  414.         if not useSmartQueue:
  415.             print("Using Original Queue Check")
  416.             if isWanted(series, report) and isMissing(series, season, episode) and (not isInQueue(report_original)):
  417.                 msgid=node.getElementsByTagName("link")[0].childNodes[0].data
  418.         msgid=urllib.quote(msgid)
  419.                 download(msgid)
  420.             #end if isWanted(series)
  421.         else:
  422.             print("Using Smart Queue Check")
  423.             if isWanted(series, report) and isMissing(series, season, episode) and (not isInQueueSmart(series, season, episode)):
  424.                 msgid=node.getElementsByTagName("link")[0].childNodes[0].data
  425.                 msgid=urllib.quote(msgid)
  426.                 download(msgid)
  427.             #end if isWanted(seies)
  428.         #end if not useSmartQueue
  429.    
  430.     except:
  431.         pass
  432.    
  433.     #end try:
  434. #end for node
  435.  
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement