View difference between Paste ID: DdbdGeVG and q57pe29Z
SHOW: | | - or go back to the newest paste.
1-
# ===============================
1+
# ================================
2-
# Ensure env variables arecorrect
2+
# Ensure env variables are correct
3-
# ===============================
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-
def updateLoop():
173+
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-
        totalTicks = STATUSUPDATES_PER_5MIN + INFOUPDATES_PER_5MIN
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-
            if i > (STATUSUPDATES_PER_5MIN - 1):
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-
            if i > (INFOUPDATES_PER_5MIN - 1):
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-
        # Start over
261+
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-
        updateLoop()
306+
307
308
Commands:
309-
        print 'newinfo!'
309+
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