codemonkey

jtvStreamUpdater.py

Nov 2nd, 2011
204
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 9.01 KB | None | 0 0
  1. # ===============================
  2. # Ensure env variables arecorrect
  3. # ===============================
  4.  
  5. from os.path import abspath, dirname, join
  6. from os import environ
  7. from sys import path
  8.  
  9. # Get absolute paths from relative paths
  10. honsappPath = abspath(dirname(__file__))
  11. honstreamsPath = abspath(join(honsappPath, '../'))
  12.  
  13. # Make sure they are in sys.path
  14. if honsappPath not in path:
  15.     path.append(honsappPath)
  16. if honstreamsPath not in path:
  17.     path.append(honstreamsPath)
  18.  
  19. # Make sure DJANGO_SETTINGS_MODULE is set
  20. if 'DJANGO_SETTINGS_MODULE' not in environ.keys():
  21.     environ['DJANGO_SETTINGS_MODULE'] = 'settings'
  22.  
  23. # =======
  24. # Imports
  25. # =======
  26.  
  27. from jtvSettings import *
  28. from honsapp.models import Stream
  29. import time
  30. import math
  31. import datetime
  32. import json
  33. import logging
  34.  
  35. # =======
  36. # Globals
  37. # =======
  38.  
  39. DEBUG = False
  40.  
  41. # Interval length is 5 minutes in seconds
  42. INTERVAL_LENGTH = 60 * 5
  43.  
  44. # Update return codes
  45. UPDATED = 1
  46. SKIPPED = 2
  47. UPDATED_TO_ONLINE = 3
  48. UPDATED_TO_OFFLINE = 4
  49.  
  50. # =============
  51. # Configuration
  52. # =============
  53.  
  54. # Set up logging
  55. logging.basicConfig(filename=join(honsappPath, 'jtvStreamsUpdater.log'),
  56.                     level=logging.ERROR,
  57.                     format='%(asctime)s:%(levelname)s: %(message)s')
  58.  
  59. # ====================
  60. # Function definitions
  61. # ====================
  62.  
  63. def getJtvClient():
  64.     "Fetch an authenticated jtv client"
  65.    
  66.     from jtvClient import JtvClient
  67.     from jtvSettings import CONSUMER_KEY, CONSUMER_SECRET
  68.    
  69.     return JtvClient(CONSUMER_KEY, CONSUMER_SECRET)
  70.  
  71. def updateStreamStatus(stream):
  72.    
  73.     returncode = SKIPPED
  74.    
  75.     # Create a jtv client
  76.     client = getJtvClient()
  77.    
  78.     # Poll the jtv API
  79.     response = client.get('/stream/list.json?channel=%s' % stream.name)
  80.     result = json.loads(response.read())
  81.    
  82.     # Check for errors
  83.     if 'error' in result:
  84.         print result['error']
  85.         logging.error(result['error'])
  86.         exit()
  87.    
  88.     if len(result) > 0:
  89.         # The stream is live
  90.         if stream.online != True:
  91.             stream.online = True
  92.             returncode = UPDATED_TO_ONLINE
  93.     else:
  94.         # The stream is not live
  95.         if stream.online != False:
  96.             stream.online = False
  97.             returncode = UPDATED_TO_OFFLINE
  98.    
  99.     # Set the stream to updated now
  100.     stream.statusUpdated = datetime.datetime.now()
  101.    
  102.     # Save the stream
  103.     stream.save()
  104.    
  105.     return returncode
  106.  
  107. def updateStreamInfo(stream):
  108.    
  109.     # Import StreamInfo
  110.     from honsapp.models import StreamInfo
  111.    
  112.     returncode = SKIPPED
  113.    
  114.     # Get a jtv client
  115.     client = getJtvClient()
  116.    
  117.     # Poll jtv API for channel info
  118.     response = client.get('/channel/show/%s.json' % stream.name)
  119.  
  120.     result = json.loads(response.read())
  121.    
  122.     # Check for errors
  123.     if 'error' in result:
  124.         print result['error']
  125.         logging.error(result['error'])
  126.         exit()
  127.    
  128.     # Create StreamInfo object
  129.     sinfo = StreamInfo()
  130.     sinfo.title = result['title']
  131.     sinfo.description = result['description']
  132.     sinfo.about = result['about']
  133.    
  134.     sinfo.stream_url = result['channel_url']
  135.    
  136.     sinfo.backgroundImage = result['channel_background_image_url']
  137.     sinfo.headerImage = result['channel_header_image_url']
  138.     sinfo.mediumChannelImage = result['image_url_medium']
  139.     sinfo.largeChannelImage = result['image_url_large']
  140.    
  141.     sinfo.mediumScreenCap = result['screen_cap_url_medium']
  142.     sinfo.largeScreenCap = result['screen_cap_url_large']
  143.    
  144.     sinfo.backgroundColor = result['channel_background_color']
  145.     sinfo.columnColor = result['channel_column_color']
  146.     sinfo.textColor = result['channel_text_color']
  147.     sinfo.linkColor = result['channel_link_color']
  148.    
  149.     # Compare the new stream info to the old
  150.     if stream.info == sinfo:
  151.         # If identical, set returncode to skipped
  152.         returncode = SKIPPED
  153.     else:
  154.         # Save the new steam info
  155.         sinfo.save()
  156.        
  157.         # If there's an old one, delete it
  158.         if stream.info != None:
  159.             stream.info.delete()
  160.        
  161.         # Link the new stream info to the stream
  162.         stream.info = sinfo
  163.        
  164.         # Set the returncode to updated
  165.         returncode = UPDATED
  166.    
  167.     # Mark stream as updated
  168.     stream.infoUpdated = datetime.datetime.now()
  169.     stream.save()
  170.    
  171.     return returncode
  172.  
  173. def updateLoop():
  174.     """
  175.    Updates the status of all streams to either online or offline, as well as
  176.    updating the stream info, respecting the global restrictions.
  177.    """
  178.    
  179.     def streamText(stream):
  180.         """
  181.        Returns a string representation of the stream displaying last updated
  182.        information
  183.        """
  184.         # return '<Stream name="%s", statusUpdated="%s", infoUpdated="%s">' \
  185.         #             % (stream.name, stream.statusUpdated, stream.infoUpdated)
  186.        
  187.         return '<Stream name="%s">' % stream.name
  188.    
  189.     # Run for ever!
  190.     while True:
  191.         # Start time
  192.         intervalStart = time.time()
  193.        
  194.         # The total amount of ticks this interval
  195.         totalTicks = STATUSUPDATES_PER_5MIN + INFOUPDATES_PER_5MIN
  196.        
  197.         # Get all jtv streams sorted by last updated status
  198.         statusStreams = Stream.objects.filter(network__name='justin.tv')\
  199.                                       .order_by('statusUpdated', 'id')
  200.                                      
  201.         # Get all jtv streams sorted by last updated info
  202.         infoStreams = Stream.objects.filter(network__name='justin.tv')\
  203.                                     .order_by('infoUpdated', 'id')
  204.        
  205.         # Update the status of the streams with the least recently updated
  206.         # status
  207.         for i, stream in enumerate(statusStreams):
  208.            
  209.             # Remember the limits
  210.             if i > (STATUSUPDATES_PER_5MIN - 1):
  211.                 break
  212.            
  213.             print 'Updating status on %s...' % streamText(stream),
  214.            
  215.             # Update stream here
  216.             result = updateStreamStatus(stream)
  217.            
  218.             # Print out what happened
  219.             if result == SKIPPED:
  220.                 print 'Unchanged'
  221.             elif result == UPDATED_TO_ONLINE:
  222.                 print 'Updated to online'
  223.             elif result == UPDATED_TO_OFFLINE:
  224.                 print 'Updated to offline'
  225.        
  226.         # Update the info of the streams with the least recently updated info
  227.         for i, stream in enumerate(infoStreams):
  228.            
  229.             # Remember the limits
  230.             if i > (INFOUPDATES_PER_5MIN - 1):
  231.                 break
  232.            
  233.             print 'Updating info on %s...' % streamText(stream),
  234.            
  235.             # Update stream here
  236.             result = updateStreamInfo(stream)
  237.            
  238.             # Print out what happened
  239.             if result == SKIPPED:
  240.                 print 'Unchanged'
  241.             else:
  242.                 print 'Saved new info'
  243.        
  244.         # Wait 5 minutes
  245.         while True:
  246.             timeWaited = math.ceil(time.time() - intervalStart)
  247.            
  248.             # If we've waited over 5 minutes
  249.             # if timeWaited > 10:
  250.             if timeWaited > INTERVAL_LENGTH:
  251.                 break
  252.            
  253.             if DEBUG:
  254.                 # Print a message every 5 seconds
  255.                 if timeWaited % 30 == 0:
  256.                     print '### %d seconds to next interval...' \
  257.                           % (INTERVAL_LENGTH - timeWaited)
  258.  
  259.             time.sleep(1)
  260.        
  261.         # Start over
  262.  
  263. # ===============
  264. # Run this script
  265. # ===============
  266. if(__name__ == '__main__'):
  267.     # Dependencies
  268.     import argparse
  269.     import textwrap
  270.    
  271.     # Some constants
  272.     commandchoices = ['scrapeloop', 'newinfo']
  273.     commandhelp = '''This module handles refreshing Justin TV streams
  274.  
  275. Commands:
  276.  scrapeloop    Starts a loop that updates all the streams statuses, respecting
  277.                the global limits. Also updated stream information, but at a
  278.                much slower rate
  279.          
  280.  newinfo       Fetches stream information for streams that still has no
  281.                information row'''
  282.    
  283.     # Create a parser
  284.     parser = argparse.ArgumentParser(description=commandhelp,
  285.                 formatter_class=argparse.RawDescriptionHelpFormatter)
  286.    
  287.     # Take exactly one argument out of the command choices
  288.     parser.add_argument('command', metavar='command', type=str,
  289.                         choices=commandchoices,
  290.                         help='The command to give the script')
  291.    
  292.     # Flag for debug
  293.     parser.add_argument('-d', '--debug', action='store_true',
  294.                         help='Print debug messages')
  295.    
  296.     # Get arguments
  297.     args = parser.parse_args()
  298.     command = args.command
  299.    
  300.     # Debug?
  301.     if args.debug:
  302.         DEBUG = True
  303.    
  304.     # Start the if loop
  305.     if(command == 'scrapeloop'):
  306.         updateLoop()
  307.        
  308.     elif(command == 'newinfo'):
  309.         print 'newinfo!'
  310.  
  311.  
Add Comment
Please, Sign In to add comment