codemonkey

jtvStreamUpdater.py

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