def is_request_needed(self): global _request_interval global _request_sequence if not self.sid in _request_interval: _request_interval[self.sid] = cache.get_station(self.sid, "request_interval") if not _request_interval[self.sid]: _request_interval[self.sid] = 0 if not self.sid in _request_sequence: _request_sequence[self.sid] = cache.get_station(self.sid, "request_sequence") if not _request_sequence[self.sid]: _request_sequence[self.sid] = 0 # If we're ready for a request sequence, start one return_value = None if _request_interval[self.sid] <= 0 and _request_sequence[self.sid] <= 0: line_length = db.c.fetch_var("SELECT COUNT(*) FROM r4_request_line WHERE sid = %s", (self.sid,)) _request_sequence[self.sid] = 1 + math.floor(line_length / config.get_station(self.sid, "request_interval_scale")) _request_interval[self.sid] = config.get_station(self.sid, "request_interval_gap") return_value = True # If we are in a request sequence, do one elif _request_sequence[self.sid] > 0: return_value = True else: _request_interval[self.sid] -= 1 return_value = False cache.set_station(self.sid, "request_interval", _request_interval[self.sid]) cache.set_station(self.sid, "request_sequence", _request_sequence[self.sid]) return return_value
def get_updated_albums_dict(sid): global updated_album_ids if not sid in updated_album_ids: return [] previous_newest_album = cache.get_station(sid, "newest_album") if not previous_newest_album: cache.set_station(sid, "newest_album", timestamp()) else: newest_albums = db.c.fetch_list( "SELECT album_id FROM r4_albums JOIN r4_album_sid USING (album_id) WHERE sid = %s AND album_added_on > %s", (sid, previous_newest_album)) for album_id in newest_albums: updated_album_ids[sid][album_id] = True cache.set_station(sid, "newest_album", timestamp()) album_diff = [] for album_id in updated_album_ids[sid]: album = Album.load_from_id_sid(album_id, sid) album.solve_cool_lowest(sid) tmp = album.to_dict_full() # Remove user-related stuff since this gets stuffed straight down the pipe tmp.pop('rating_user', None) tmp.pop('fave', None) album_diff.append(tmp) return album_diff
def get_updated_albums_dict(sid): global updated_album_ids if not sid in updated_album_ids: return [] previous_newest_album = cache.get_station(sid, "newest_album") if not previous_newest_album: cache.set_station(sid, "newest_album", timestamp()) else: newest_albums = db.c.fetch_list( "SELECT album_id FROM r4_albums JOIN r4_album_sid USING (album_id) WHERE sid = %s AND album_added_on > %s", (sid, previous_newest_album), ) for album_id in newest_albums: updated_album_ids[sid][album_id] = True cache.set_station(sid, "newest_album", timestamp()) album_diff = [] for album_id in updated_album_ids[sid]: album = Album.load_from_id_sid(album_id, sid) album.solve_cool_lowest(sid) tmp = album.to_dict_full() # Remove user-related stuff since this gets stuffed straight down the pipe tmp.pop("rating_user", None) tmp.pop("fave", None) album_diff.append(tmp) return album_diff
def is_request_needed(self): global _request_interval global _request_sequence if not self.sid in _request_interval: _request_interval[self.sid] = cache.get_station(self.sid, "request_interval") if not _request_interval[self.sid]: _request_interval[self.sid] = 0 if not self.sid in _request_sequence: _request_sequence[self.sid] = cache.get_station(self.sid, "request_sequence") if not _request_sequence[self.sid]: _request_sequence[self.sid] = 0 log.debug("requests", "Interval %s // Sequence %s" % (_request_interval, _request_sequence)) # If we're ready for a request sequence, start one return_value = None if _request_interval[self.sid] <= 0 and _request_sequence[self.sid] <= 0: return_value = True # If we are in a request sequence, do one elif _request_sequence[self.sid] > 0: _request_sequence[self.sid] -= 1 log.debug("requests", "Still in sequence. Remainder: %s" % _request_sequence[self.sid]) return_value = True else: _request_interval[self.sid] -= 1 log.debug("requests", "Waiting on interval. Remainder: %s" % _request_interval[self.sid]) return_value = False cache.set_station(self.sid, "request_interval", _request_interval[self.sid]) cache.set_station(self.sid, "request_sequence", _request_sequence[self.sid]) return return_value
def get_next(sid): line = cache.get_station(sid, "request_line") if not line: return None song = None for pos in range(0, len(line)): if not line[pos] or not line[pos]['song_id']: pass else: entry = line.pop(pos) song = playlist.Song.load_from_id(entry['song_id'], sid) song.data['elec_request_user_id'] = entry['user_id'] song.data['elec_request_username'] = entry['username'] u = User(entry['user_id']) db.c.update("DELETE FROM r4_request_store WHERE user_id = %s AND song_id = %s", (u.id, entry['song_id'])) u.remove_from_request_line() user_sid = u.get_tuned_in_sid() if u.has_requests(): u.put_in_request_line(user_sid) request_count = db.c.fetch_var("SELECT COUNT(*) FROM r4_request_history WHERE user_id = %s", (u.id,)) + 1 db.c.update("DELETE FROM r4_request_store WHERE song_id = %s AND user_id = %s", (song.id, u.id)) db.c.update("INSERT INTO r4_request_history (user_id, song_id, request_wait_time, request_line_size, request_at_count) " "VALUES (%s, %s, %s, %s, %s)", (u.id, song.id, time.time() - entry['line_wait_start'], len(line), request_count)) # Update the user's request cache u.get_requests(refresh=True) cache.set_station(sid, "request_line", line, True) break return song
def _process_line(line, sid): new_line = [] # user_positions has user_id as a key and position as the value, this is cached for quick lookups by API requests # so users know where they are in line user_positions = {} t = int(timestamp()) albums_with_requests = [] position = 1 # For each person for row in line: add_to_line = False u = User(row['user_id']) row['song_id'] = None # If their time is up, remove them and don't add them to the new line if row['line_expiry_tune_in'] and row['line_expiry_tune_in'] <= t: log.debug("request_line", "%s: Removed user ID %s from line for tune in timeout, expiry time %s current time %s" % (sid, u.id, row['line_expiry_tune_in'], t)) u.remove_from_request_line() else: tuned_in_sid = db.c.fetch_var("SELECT sid FROM r4_listeners WHERE user_id = %s AND sid = %s AND listener_purge = FALSE", (u.id, sid)) tuned_in = True if tuned_in_sid == sid else False if tuned_in: # Get their top song ID song_id = u.get_top_request_song_id(sid) # If they have no song and their line expiry has arrived, boot 'em if not song_id and row['line_expiry_election'] and (row['line_expiry_election'] <= t): log.debug("request_line", "%s: Removed user ID %s from line for election timeout, expiry time %s current time %s" % (sid, u.id, row['line_expiry_election'], t)) u.remove_from_request_line() # Give them more chances if they still have requests # They'll get added to the line of whatever station they're tuned in to (if any!) if u.has_requests(): u.put_in_request_line(u.get_tuned_in_sid()) # If they have no song, start the expiry countdown elif not song_id and not row['line_expiry_election']: row['line_expiry_election'] = t + 900 db.c.update("UPDATE r4_request_line SET line_expiry_election = %s WHERE user_id = %s", ((t + 900), row['user_id'])) add_to_line = True # Keep 'em in line else: if song_id: albums_with_requests.append(db.c.fetch_var("SELECT album_id FROM r4_songs WHERE song_id = %s", (song_id,))) row['song_id'] = song_id add_to_line = True elif not row['line_expiry_tune_in'] or row['line_expiry_tune_in'] == 0: db.c.update("UPDATE r4_request_line SET line_expiry_tune_in = %s WHERE user_id = %s", ((t + 600), row['user_id'])) add_to_line = True else: add_to_line = True if add_to_line: new_line.append(row) user_positions[u.id] = position position = position + 1 cache.set_station(sid, "request_line", new_line, True) cache.set_station(sid, "request_user_positions", user_positions, True) db.c.update("UPDATE r4_album_sid SET album_requests_pending = NULL WHERE album_requests_pending = TRUE AND sid = %s", (sid,)) for album_id in albums_with_requests: db.c.update("UPDATE r4_album_sid SET album_requests_pending = TRUE WHERE album_id = %s AND sid = %s", (album_id, sid)) return new_line
def get_next(sid): line = cache.get_station(sid, "request_line") if not line: return None song = None for pos in range(0, len(line)): if not line[pos]: pass # ?!?! elif not line[pos]['song_id']: log.debug("request", "Passing on user %s since they have no valid first song." % line[pos]['username']) else: entry = line.pop(pos) song = playlist.Song.load_from_id(entry['song_id'], sid) log.debug("request", "Fulfilling %s's request for %s." % (entry['username'], song.filename)) song.data['elec_request_user_id'] = entry['user_id'] song.data['elec_request_username'] = entry['username'] u = User(entry['user_id']) db.c.update("DELETE FROM r4_request_store WHERE user_id = %s AND song_id = %s", (u.id, entry['song_id'])) u.remove_from_request_line() if u.has_requests(): u.put_in_request_line(u.get_tuned_in_sid()) request_count = db.c.fetch_var("SELECT COUNT(*) FROM r4_request_history WHERE user_id = %s", (u.id,)) + 1 db.c.update("DELETE FROM r4_request_store WHERE song_id = %s AND user_id = %s", (song.id, u.id)) db.c.update("INSERT INTO r4_request_history (user_id, song_id, request_wait_time, request_line_size, request_at_count, sid) " "VALUES (%s, %s, %s, %s, %s, %s)", (u.id, song.id, time.time() - entry['line_wait_start'], len(line), request_count, sid)) db.c.update("UPDATE phpbb_users SET radio_totalrequests = %s WHERE user_id = %s", (request_count, u.id)) song.update_request_count(sid) # If we fully update the line, the user may sneak in and get 2 requests in the same election. # This is not a good idea, so we leave it to the scheduler to issue the full cache update. cache.set_station(sid, "request_line", line, True) break return song
def post(self): cache.set_station(self.sid, "pause_title", self.get_argument("title")) attach_dj_info_to_request(self) self.append( self.return_name, {"success": True, "pause_title": self.get_argument("title")}, )
def post(self): new_pw = ''.join(random.SystemRandom().choice(string.ascii_uppercase + string.digits) for x in range(10)) result = liquidsoap.set_password(self.sid, new_pw) if result.startswith("Variable harbor_pw set (was"): cache.set_station(self.sid, "dj_password", new_pw, save_local=True) attach_dj_info_to_request(self) else: raise APIException("internal_error", "Internal error changing DJ key.")
def update_memcache(sid): _update_schedule_memcache(sid) cache.prime_rating_cache_for_events(sid, [ current[sid] ] + upnext[sid] + history[sid]) cache.set_station(sid, "current_listeners", listeners.get_listeners_dict(sid), True) cache.set_station(sid, "album_diff", playlist.get_updated_albums_dict(sid), True) rainwave.playlist_objects.album.clear_updated_albums(sid) cache.set_station(sid, "all_albums", playlist.get_all_albums_list(sid), True) cache.set_station(sid, "all_artists", playlist.get_all_artists_list(sid), True) cache.set_station(sid, "all_groups", playlist.get_all_groups_list(sid), True)
def post(self): cache.set_station(self.sid, "backend_paused", True) attach_dj_info_to_request(self) self.append( self.return_name, { "success": True, "message": "At 0:00 the station will go silent and wait for you.", }, )
def update_live_voting(sid): live_voting = {} upnext_sid = cache.get_station(sid, "sched_next") if not upnext_sid: return live_voting for event in upnext_sid: if event.is_election: live_voting[event.id] = db.c.fetch_all("SELECT entry_id, entry_votes, song_id FROM r4_election_entries WHERE elec_id = %s", (event.id,)) cache.set_station(sid, "live_voting", live_voting) return live_voting
def update_line(sid): # Get everyone in the line line = db.c.fetch_all( "SELECT username, user_id, line_expiry_tune_in, line_expiry_election, line_wait_start FROM r4_request_line JOIN phpbb_users USING (user_id) WHERE sid = %s ORDER BY line_wait_start", (sid, )) new_line = [] # user_positions has user_id as a key and position as the value, this is cached for quick lookups by API requests # so users know where they are in line user_positions = {} t = int(time.time()) position = 1 # For each person for row in line: add_to_line = False u = User(row['user_id']) u.refresh_sid = sid row['song_id'] = None # If their time is up, remove them and don't add them to the new line if row['line_expiry_tune_in'] and row['line_expiry_tune_in'] <= t: u.remove_from_request_line() else: u.refresh() # do nothing if they're not tuned in if not u.data['radio_tuned_in']: pass else: # Get their top song ID song_id = u.get_top_request_song_id(sid) # If they have no song and their line expiry has arrived, boot 'em if not song_id and row['line_expiry_election'] and ( row['line_expiry_election'] <= t): u.remove_from_request_line() # Give them a second chance if they still have requests # They'll get added to the line of whatever station they're tuned in to (if any!) if u.has_requests(): u.put_in_request_line(u.get_tuned_in_sid()) # If they have no song, start the expiry countdown elif not song_id: row['line_expiry_election'] = t + 600 db.c.update( "UPDATE r4_request_line SET line_expiry_election = %s WHERE user_id = %s", (row['line_expiry_election'], row['user_id'])) add_to_line = True # Keep 'em in line else: row['song_id'] = song_id add_to_line = True if add_to_line: new_line.append(row) user_positions[u.id] = position position = position + 1 cache.set_station(sid, "request_line", new_line, True) cache.set_station(sid, "request_user_positions", user_positions, True)
def update_live_voting(sid): live_voting = {} upnext_sid = cache.get_station(sid, "sched_next") if not upnext_sid: return live_voting for event in upnext_sid: if event.is_election: live_voting[event.id] = db.c.fetch_all( "SELECT entry_id, entry_votes, song_id FROM r4_election_entries WHERE elec_id = %s", (event.id, )) cache.set_station(sid, "live_voting", live_voting) return live_voting
def update_line(sid): # TODO: This needs code review # Get everyone in the line line = db.c.fetch_all("SELECT username, user_id, line_expiry_tune_in, line_expiry_election FROM r4_request_line JOIN phpbb_users USING (user_id) WHERE sid = %s ORDER BY line_wait_start", (sid,)) new_line = [] user_positions = {} t = time.time() position = 1 # For each person for row in line: add_to_line = False u = User(row['user_id']) row['song_id'] = None # If their time is up, remove them and don't add them to the new line if row['line_expiry_tune_in'] <= t: u.remove_from_request_line() else: # refresh the user to get their data, using local cache only - speed things up here u.refresh(True) # If they're not tuned in and haven't been marked as expiring yet, mark them, add to line, move on if not u.data['radio_tuned_in'] and not u.data['radio_tuned_in']: row['line_expiry_tune_in'] = t + 600 add_to_line = True # do nothing if they're not tuned in elif not u.data['radio_tuned_in']: pass else: # Get their top song ID song_id = u.get_top_request_song_id(sid) # If they have no song and their line expiry has arrived, boot 'em if not song_id and (row['line_expiry_election'] <= t): u.remove_from_request_line() # Give them a second chance if they still have requests, this is SID-indiscriminate # they'll get added to whatever line is their top request if u.has_requests(): u.put_in_request_line(u.get_top_request_sid()) # If they have no song, start the expiry countdown elif not song_id: row['line_expiry_election'] = t + 600 add_to_line = True # Keep 'em in line else: row['song_id'] = song_id add_to_line = True if add_to_line: new_line.append(row) user_positions[u.id] = position position = position + 1 cache.set_station(sid, "request_line", new_line) cache.set_station(sid, "request_user_positions", user_positions)
def update_line(sid): # Get everyone in the line line = db.c.fetch_all("SELECT username, user_id, line_expiry_tune_in, line_expiry_election, line_wait_start FROM r4_request_line JOIN phpbb_users USING (user_id) WHERE sid = %s ORDER BY line_wait_start", (sid,)) new_line = [] # user_positions has user_id as a key and position as the value, this is cached for quick lookups by API requests # so users know where they are in line user_positions = {} t = int(time.time()) position = 1 # For each person for row in line: add_to_line = False u = User(row['user_id']) u.refresh_sid = sid row['song_id'] = None # If their time is up, remove them and don't add them to the new line if row['line_expiry_tune_in'] and row['line_expiry_tune_in'] <= t: u.remove_from_request_line() else: u.refresh() # do nothing if they're not tuned in if not u.data['radio_tuned_in']: pass else: # Get their top song ID song_id = u.get_top_request_song_id(sid) # If they have no song and their line expiry has arrived, boot 'em if not song_id and row['line_expiry_election'] and (row['line_expiry_election'] <= t): u.remove_from_request_line() # Give them a second chance if they still have requests # They'll get added to the line of whatever station they're tuned in to (if any!) if u.has_requests(): u.put_in_request_line(u.get_tuned_in_sid()) # If they have no song, start the expiry countdown elif not song_id: row['line_expiry_election'] = t + 600 db.c.update("UPDATE r4_request_line SET line_expiry_election = %s WHERE user_id = %s", (row['line_expiry_election'], row['user_id'])) add_to_line = True # Keep 'em in line else: row['song_id'] = song_id add_to_line = True if add_to_line: new_line.append(row) user_positions[u.id] = position position = position + 1 cache.set_station(sid, "request_line", new_line, True) cache.set_station(sid, "request_user_positions", user_positions, True)
def get(self, sid): #pylint: disable=W0221 self.success = False self.sid = None if int(sid) in config.station_ids: self.sid = int(sid) else: return if cache.get_station(self.sid, "backend_paused") and cache.get_station(self.sid, "backend_pause_extend"): self.write(self._get_pause_file()) cache.set_station(self.sid, "backend_pause_extend", False) cache.set_station(self.sid, "backend_paused_playing", True) return else: cache.set_station(self.sid, "backend_pause_extend", False) cache.set_station(self.sid, "backend_paused", False) cache.set_station(self.sid, "backend_paused_playing", False) # This program must be run on 1 station for 1 instance, which would allow this operation to be safe. # Also works if 1 process is serving all stations. Pinging any instance for any station # would break the program here, though. if cache.get_station(self.sid, "get_next_socket_timeout") and sid_output[self.sid]: log.warn("backend", "Using previous output to prevent flooding.") self.write(sid_output[self.sid]) sid_output[self.sid] = None self.success = True else: try: schedule.advance_station(self.sid) except (psycopg2.OperationalError, psycopg2.InterfaceError) as e: log.warn("backend", e.diag.message_primary) db.close() db.connect() raise except psycopg2.extensions.TransactionRollbackError as e: log.warn("backend", "Database transaction deadlock. Re-opening database and setting retry timeout.") db.close() db.connect() raise to_send = None if not config.get("liquidsoap_annotations"): to_send = schedule.get_advancing_file(self.sid) else: to_send = self._get_annotated(schedule.get_advancing_event(self.sid)) sid_output[self.sid] = to_send self.success = True if not cache.get_station(self.sid, "get_next_socket_timeout"): self.write(to_send)
def dj_heartbeat_check(): # Don't do this in testing environments if config.get("developer_mode"): return for sid in config.station_ids: if cache.get_station(sid, "backend_paused_playing"): hb = cache.get_station(sid, "dj_heartbeat") hbs = cache.get_station(sid, "dj_heartbeat_start") if not hbs or ((timestamp() - hbs) <= 10): pass elif not hb or ((timestamp() - hb) >= 15): log.warn("dj_heartbeat", "DJ heart attack - resetting station to normal.") cache.set_station(sid, "backend_paused", False) cache.set_station(sid, "backend_paused_playing", False) liquidsoap.kick_dj(sid) liquidsoap.skip(sid)
def get(self, sid): self.success = False self.sid = None if int(sid) in config.station_ids: self.sid = int(sid) else: return if cache.get_station(self.sid, "backend_paused"): if not cache.get_station(self.sid, "dj_heartbeat_start"): log.debug("dj", "Setting server start heatbeat.") cache.set_station(self.sid, "dj_heartbeat_start", timestamp()) self.write(self._get_pause_file()) schedule.set_upnext_crossfade(self.sid, False) cache.set_station(self.sid, "backend_paused_playing", True) sync_to_front.sync_frontend_dj(self.sid) return else: cache.set_station(self.sid, "dj_heartbeat_start", False) cache.set_station(self.sid, "backend_paused", False) cache.set_station(self.sid, "backend_paused_playing", False) try: schedule.advance_station(self.sid) except (psycopg2.OperationalError, psycopg2.InterfaceError) as e: log.warn("backend", e.diag.message_primary) db.close() db.connect() raise except psycopg2.extensions.TransactionRollbackError as e: log.warn( "backend", "Database transaction deadlock. Re-opening database and setting retry timeout.", ) db.close() db.connect() raise to_send = None if not config.get("liquidsoap_annotations"): to_send = schedule.get_advancing_file(self.sid) else: to_send = self._get_annotated( schedule.get_advancing_event(self.sid)) self.success = True if not cache.get_station(self.sid, "get_next_socket_timeout"): self.write(to_send)
def is_request_needed(self): global _request_interval global _request_sequence if not self.sid in _request_interval: _request_interval[self.sid] = cache.get_station( self.sid, "request_interval") if not _request_interval[self.sid]: _request_interval[self.sid] = 0 if not self.sid in _request_sequence: _request_sequence[self.sid] = cache.get_station( self.sid, "request_sequence") if not _request_sequence[self.sid]: _request_sequence[self.sid] = 0 log.debug( "requests", "Interval %s // Sequence %s" % (_request_interval, _request_sequence), ) # If we're ready for a request sequence, start one return_value = None if _request_interval[self.sid] <= 0 and _request_sequence[ self.sid] <= 0: return_value = True # If we are in a request sequence, do one elif _request_sequence[self.sid] > 0: _request_sequence[self.sid] -= 1 log.debug( "requests", "Still in sequence. Remainder: %s" % _request_sequence[self.sid], ) return_value = True else: _request_interval[self.sid] -= 1 log.debug( "requests", "Waiting on interval. Remainder: %s" % _request_interval[self.sid], ) return_value = False cache.set_station(self.sid, "request_interval", _request_interval[self.sid]) cache.set_station(self.sid, "request_sequence", _request_sequence[self.sid]) return return_value
def post(self): if not cache.get_station(self.sid, "backend_paused"): result = "Station seems unpaused already. " else: result = "Unpausing station. " cache.set_station(self.sid, "backend_paused", False) cache.set_station(self.sid, "backend_pause_extend", False) if (cache.get_station(self.sid, "backend_paused_playing")): result += "Automatically starting music. " result += "\n" result += liquidsoap.skip(self.sid) else: result += "If station remains silent, music will start playing within 5 minutes unless you hit skip." if (self.get_argument("kick_dj", default=False)): result += "Kicking DJ. " result += "\n" result += liquidsoap.kick_dj(self.sid) self.append(self.return_name, { "success": True, "message": result })
def post(self): if not cache.get_station(self.sid, "backend_paused"): result = "Station seems unpaused already. " else: result = "Unpausing station. " cache.set_station(self.sid, "backend_paused", False) if (cache.get_station(self.sid, "backend_paused_playing")): result += "Automatically starting music. " result += "\n" result += liquidsoap.skip(self.sid) else: result += "If station remains silent, music will start playing within 5 minutes unless you hit skip." if (self.get_argument("kick_dj", default=False)): result += "Kicking DJ. " result += "\n" result += liquidsoap.kick_dj(self.sid) attach_dj_info_to_request(self) self.append(self.return_name, { "success": True, "message": result })
def get(self, sid): #pylint: disable=W0221 self.success = False self.sid = None if int(sid) in config.station_ids: self.sid = int(sid) else: return if cache.get_station(self.sid, "backend_paused"): if not cache.get_station(self.sid, "dj_heartbeat_start"): log.debug("dj", "Setting server start heatbeat.") cache.set_station(self.sid, "dj_heartbeat_start", timestamp()) self.write(self._get_pause_file()) schedule.set_upnext_crossfade(self.sid, False) cache.set_station(self.sid, "backend_paused_playing", True) sync_to_front.sync_frontend_dj(self.sid) return else: cache.set_station(self.sid, "dj_heartbeat_start", False) cache.set_station(self.sid, "backend_paused", False) cache.set_station(self.sid, "backend_paused_playing", False) try: schedule.advance_station(self.sid) except (psycopg2.OperationalError, psycopg2.InterfaceError) as e: log.warn("backend", e.diag.message_primary) db.close() db.connect() raise except psycopg2.extensions.TransactionRollbackError as e: log.warn("backend", "Database transaction deadlock. Re-opening database and setting retry timeout.") db.close() db.connect() raise to_send = None if not config.get("liquidsoap_annotations"): to_send = schedule.get_advancing_file(self.sid) else: to_send = self._get_annotated(schedule.get_advancing_event(self.sid)) self.success = True if not cache.get_station(self.sid, "get_next_socket_timeout"): self.write(to_send)
def get_next(sid): line = cache.get_station(sid, "request_line") if not line: return None song = None for pos in range(0, len(line)): if not line[pos] or not line[pos]['song_id']: pass else: entry = line.pop(pos) song = playlist.Song.load_from_id(entry['song_id'], sid) song.data['elec_request_user_id'] = entry['user_id'] song.data['elec_request_username'] = entry['username'] u = User(entry['user_id']) db.c.update( "DELETE FROM r4_request_store WHERE user_id = %s AND song_id = %s", (u.id, entry['song_id'])) u.remove_from_request_line() user_sid = u.get_tuned_in_sid() if u.has_requests(): u.put_in_request_line(user_sid) request_count = db.c.fetch_var( "SELECT COUNT(*) FROM r4_request_history WHERE user_id = %s", (u.id, )) + 1 db.c.update( "DELETE FROM r4_request_store WHERE song_id = %s AND user_id = %s", (song.id, u.id)) db.c.update( "INSERT INTO r4_request_history (user_id, song_id, request_wait_time, request_line_size, request_at_count) " "VALUES (%s, %s, %s, %s, %s)", (u.id, song.id, time.time() - entry['line_wait_start'], len(line), request_count)) # Update the user's request cache u.get_requests(refresh=True) cache.set_station(sid, "request_line", line, True) break return song
def get_next(sid): # TODO: Code review line = cache.get_station(sid, "request_line") song = None for pos in range(0, len(line)): if not line['song_id']: pass else: entry = line.pop(pos) song = playlist.Song.load_from_id(entry['song_id'], sid) song.data['elec_request_user_id'] = entry['user_id'] song.data['elec_request_username'] = entry['username'] db.c.update("DELETE FROM r4_request_store WHERE user_id = %s AND song_id = %s", (u.id, request['song_id'])) u = User(entry['user_id']) u.remove_from_request_line() if u.has_requests(): u.put_in_request_line(u.get_top_request_sid()) cache.set_station(sid, "request_line", line) break return song
def get_updated_albums_dict(sid): global updated_album_ids if not sid in updated_album_ids: return [] previous_newest_album = cache.get_station(sid, "newest_album") if not previous_newest_album: cache.set_station(sid, "newest_album", timestamp()) else: newest_albums = db.c.fetch_list( "SELECT album_id FROM r4_albums JOIN r4_album_sid USING (album_id) WHERE sid = %s AND album_added_on > %s", (sid, previous_newest_album), ) for album_id in newest_albums: updated_album_ids[sid][album_id] = True cache.set_station(sid, "newest_album", timestamp()) album_diff = [] for album_id in updated_album_ids[sid]: album = Album.load_from_id_sid(album_id, sid) album.solve_cool_lowest(sid) album_diff.append(album.to_album_diff()) return album_diff
def reset_schedule(sid): # Reset the schedule schedule.current = {} schedule.upnext = {} schedule.history = {} cache.set_station(sid, "sched_current", None, True) cache.set_station(sid, "sched_next", None, True) cache.set_station(sid, "sched_history", None, True) playlist.remove_all_locks(1)
parser = argparse.ArgumentParser( description="Fetches the next song from a Rainwave backend daemon.") parser.add_argument("--dest", "-d", required=False, default="127.0.0.1") parser.add_argument("--sid", "-s", required=False, default=1) parser.add_argument("--config", "-c", required=False, default=None) args = parser.parse_args() config.load(args.config) cache.open() params = urllib.urlencode({"sid": args.sid}) try: conn = httplib.HTTPConnection(args.dest, config.get("backend_port"), timeout=10) conn.request("GET", "/advance/%s" % args.sid) result = conn.getresponse() if result.status == 200: print result.read() else: raise Exception("Backend HTTP Error %s" % result.status) cache.set_station(args.sid, "backend_ok", True) cache.set_station(args.sid, "backend_message", "OK") conn.close() except Exception as e: cache.set_station(args.sid, "backend_ok", False) cache.set_station(args.sid, "backend_status", repr(e)) if conn: conn.close() raise
def _update_memcache(sid): cache.set_station(sid, "sched_current", current[sid])
def _update_schedule_memcache(sid): cache.set_station(sid, "sched_current", current[sid], True) cache.set_station(sid, "sched_next", upnext[sid], True) cache.set_station(sid, "sched_history", history[sid], True) sched_current_dict = current[sid].to_dict() cache.set_station(sid, "sched_current_dict", sched_current_dict, True) next_dict_list = [] for event in upnext[sid]: next_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_next_dict", next_dict_list, True) history_dict_list = [] for event in history[sid]: history_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_history_dict", history_dict_list, True) all_station = {} if 'songs' in sched_current_dict: all_station['title'] = sched_current_dict['songs'][0]['title'] all_station['album'] = sched_current_dict['songs'][0]['albums'][0]['name'] all_station['art'] = sched_current_dict['songs'][0]['albums'][0]['art'] else: all_station['title'] = sched_current_dict['name'] all_station['album'] = "" all_station['art'] = None cache.set_station(sid, "all_station_info", all_station, True)
def _process_line(line, sid): new_line = [] # user_positions has user_id as a key and position as the value, this is cached for quick lookups by API requests # so users know where they are in line user_positions = {} t = int(timestamp()) albums_with_requests = [] position = 1 user_viewable_position = 1 valid_positions = 0 # For each person for row in line: add_to_line = False u = User(row["user_id"]) row["song_id"] = None # If their time is up, remove them and don't add them to the new line if row["line_expiry_tune_in"] and row["line_expiry_tune_in"] <= t: log.debug( "request_line", "%s: Removed user ID %s from line for tune in timeout, expiry time %s current time %s" % (sid, u.id, row["line_expiry_tune_in"], t), ) u.remove_from_request_line() else: tuned_in_sid = db.c.fetch_var( "SELECT sid FROM r4_listeners WHERE user_id = %s AND sid = %s AND listener_purge = FALSE", (u.id, sid), ) tuned_in = True if tuned_in_sid == sid else False if tuned_in: # Get their top song ID song_id = u.get_top_request_song_id(sid) if song_id and not row["line_has_had_valid"]: row["line_has_had_valid"] = True db.c.update( "UPDATE r4_request_line SET line_has_had_valid = TRUE WHERE user_id = %s", (u.id, ), ) if row["line_has_had_valid"]: valid_positions += 1 # If they have no song and their line expiry has arrived, boot 'em if (not song_id and row["line_expiry_election"] and (row["line_expiry_election"] <= t)): log.debug( "request_line", "%s: Removed user ID %s from line for election timeout, expiry time %s current time %s" % (sid, u.id, row["line_expiry_election"], t), ) u.remove_from_request_line() # Give them more chances if they still have requests # They'll get added to the line of whatever station they're tuned in to (if any!) if u.has_requests(): u.put_in_request_line(u.get_tuned_in_sid()) # If they have no song and they're in 2nd or 1st, start the expiry countdown elif not song_id and not row[ "line_expiry_election"] and position <= 2: log.debug( "request_line", "%s: User ID %s has no valid requests, beginning boot countdown." % (sid, u.id), ) row["line_expiry_election"] = t + 900 db.c.update( "UPDATE r4_request_line SET line_expiry_election = %s WHERE user_id = %s", ((t + 900), row["user_id"]), ) add_to_line = True # Keep 'em in line else: log.debug("request_line", "%s: User ID %s is in line." % (sid, u.id)) if song_id: albums_with_requests.append( db.c.fetch_var( "SELECT album_id FROM r4_songs WHERE song_id = %s", (song_id, ), )) row["song"] = db.c.fetch_row( "SELECT song_id AS id, song_title AS title, album_name FROM r4_songs JOIN r4_albums USING (album_id) WHERE song_id = %s", (song_id, ), ) else: row["song"] = None row["song_id"] = song_id add_to_line = True elif not row["line_expiry_tune_in"] or row[ "line_expiry_tune_in"] == 0: log.debug( "request_line", "%s: User ID %s being marked as tuned out." % (sid, u.id), ) db.c.update( "UPDATE r4_request_line SET line_expiry_tune_in = %s WHERE user_id = %s", ((t + 600), row["user_id"]), ) add_to_line = True else: log.debug( "request_line", "%s: User ID %s not tuned in, waiting on expiry for action." % (sid, u.id), ) add_to_line = True row["skip"] = not add_to_line row["position"] = user_viewable_position new_line.append(row) user_positions[u.id] = user_viewable_position user_viewable_position = user_viewable_position + 1 if add_to_line: position = position + 1 log.debug("request_line", "Request line valid positions: %s" % valid_positions) cache.set_station(sid, "request_valid_positions", valid_positions) cache.set_station(sid, "request_line", new_line, True) cache.set_station(sid, "request_user_positions", user_positions, True) db.c.update( "UPDATE r4_album_sid SET album_requests_pending = NULL WHERE album_requests_pending = TRUE AND sid = %s", (sid, ), ) for album_id in albums_with_requests: db.c.update( "UPDATE r4_album_sid SET album_requests_pending = TRUE WHERE album_id = %s AND sid = %s", (album_id, sid), ) return new_line
def post(self): cache.set_station(self.sid, "dj_heartbeat", timestamp()) self.append(self.return_name, { "success": True })
def _update_memcache(sid): cache.set_station(sid, "sched_current", current[sid], True) cache.set_station(sid, "sched_next", next[sid], True) cache.set_station(sid, "sched_history", history[sid], True) cache.set_station(sid, "sched_current_dict", current[sid].to_dict(), True) next_dict_list = [] for event in next[sid]: next_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_next_dict", next_dict_list, True) history_dict_list = [] for event in history[sid]: history_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_history_dict", history_dict_list, True) cache.prime_rating_cache_for_events([current[sid]] + next[sid] + history[sid]) cache.set_station(sid, "listeners_current", listeners.get_listeners_dict(sid), True) cache.set_station(sid, "album_diff", playlist.get_updated_albums_dict(sid), True) playlist.clear_updated_albums(sid) cache.set_station(sid, "all_albums", playlist.get_all_albums_list(sid), True) cache.set_station(sid, "all_artists", playlist.get_all_artists_list(sid), True)
def _process_line(line, sid): new_line = [] # user_positions has user_id as a key and position as the value, this is cached for quick lookups by API requests # so users know where they are in line user_positions = {} t = int(timestamp()) albums_with_requests = [] position = 1 user_viewable_position = 1 valid_positions = 0 # For each person for row in line: add_to_line = False u = User(row['user_id']) row['song_id'] = None # If their time is up, remove them and don't add them to the new line if row['line_expiry_tune_in'] and row['line_expiry_tune_in'] <= t: log.debug("request_line", "%s: Removed user ID %s from line for tune in timeout, expiry time %s current time %s" % (sid, u.id, row['line_expiry_tune_in'], t)) u.remove_from_request_line() else: tuned_in_sid = db.c.fetch_var("SELECT sid FROM r4_listeners WHERE user_id = %s AND sid = %s AND listener_purge = FALSE", (u.id, sid)) tuned_in = True if tuned_in_sid == sid else False if tuned_in: # Get their top song ID song_id = u.get_top_request_song_id(sid) if song_id and not row['line_has_had_valid']: row['line_has_had_valid'] = True db.c.update("UPDATE r4_request_line SET line_has_had_valid = TRUE WHERE user_id = %s", (u.id, )) if row['line_has_had_valid']: valid_positions += 1 # If they have no song and their line expiry has arrived, boot 'em if not song_id and row['line_expiry_election'] and (row['line_expiry_election'] <= t): log.debug("request_line", "%s: Removed user ID %s from line for election timeout, expiry time %s current time %s" % (sid, u.id, row['line_expiry_election'], t)) u.remove_from_request_line() # Give them more chances if they still have requests # They'll get added to the line of whatever station they're tuned in to (if any!) if u.has_requests(): u.put_in_request_line(u.get_tuned_in_sid()) # If they have no song and they're in 2nd or 1st, start the expiry countdown elif not song_id and not row['line_expiry_election'] and position <= 2: log.debug("request_line", "%s: User ID %s has no valid requests, beginning boot countdown." % (sid, u.id)) row['line_expiry_election'] = t + 900 db.c.update("UPDATE r4_request_line SET line_expiry_election = %s WHERE user_id = %s", ((t + 900), row['user_id'])) add_to_line = True # Keep 'em in line else: log.debug("request_line", "%s: User ID %s is in line." % (sid, u.id)) if song_id: albums_with_requests.append(db.c.fetch_var("SELECT album_id FROM r4_songs WHERE song_id = %s", (song_id,))) row['song'] = db.c.fetch_row("SELECT song_id AS id, song_title AS title, album_name FROM r4_songs JOIN r4_albums USING (album_id) WHERE song_id = %s", (song_id,)) else: row['song'] = None row['song_id'] = song_id add_to_line = True elif not row['line_expiry_tune_in'] or row['line_expiry_tune_in'] == 0: log.debug("request_line", "%s: User ID %s being marked as tuned out." % (sid, u.id)) db.c.update("UPDATE r4_request_line SET line_expiry_tune_in = %s WHERE user_id = %s", ((t + 600), row['user_id'])) add_to_line = True else: log.debug("request_line", "%s: User ID %s not tuned in, waiting on expiry for action." % (sid, u.id)) add_to_line = True row['skip'] = not add_to_line row['position'] = user_viewable_position new_line.append(row) user_positions[u.id] = user_viewable_position user_viewable_position = user_viewable_position + 1 if add_to_line: position = position + 1 log.debug("request_line", "Request line valid positions: %s" % valid_positions) cache.set_station(sid, 'request_valid_positions', valid_positions) cache.set_station(sid, "request_line", new_line, True) cache.set_station(sid, "request_user_positions", user_positions, True) db.c.update("UPDATE r4_album_sid SET album_requests_pending = NULL WHERE album_requests_pending = TRUE AND sid = %s", (sid,)) for album_id in albums_with_requests: db.c.update("UPDATE r4_album_sid SET album_requests_pending = TRUE WHERE album_id = %s AND sid = %s", (album_id, sid)) return new_line
def post(self): cache.set_station(self.sid, "backend_paused", True) cache.set_station(self.sid, "backend_pause_extend", True) self.append(self.return_name, { "success": True, "message": "At 0:00 the station will go silent and wait for you." })
else: dest_port += int(list(config.station_ids)[0]) timeout = 5 if cache.get_station(args.sid, "backend_ok") else 120 conn = httplib.HTTPConnection(args.dest, config.get("backend_port") + int(args.sid), timeout=timeout) conn.request("GET", "/advance/%s" % args.sid) result = conn.getresponse() if result.status == 200: next_song_filename = result.read() if not next_song_filename or len(next_song_filename) == 0: raise Exception("Got zero-length filename from backend!") if os.name == "nt": next_song_filename = next_song_filename.replace("\\", "/") print next_song_filename else: raise Exception("HTTP Error %s trying to reach backend!" % result.status) cache.set_station(args.sid, "backend_ok", True) cache.set_station(args.sid, "backend_message", "OK") conn.close() except socket.timeout as e: cache.set_station(args.sid, "backend_ok", False) cache.set_station(args.sid, "backend_status", repr(e)) time.sleep(2) raise except Exception as e: cache.set_station(args.sid, "backend_ok", False) cache.set_station(args.sid, "backend_status", repr(e)) if conn: conn.close() time.sleep(2) raise
def _update_schedule_memcache(sid): cache.set_station(sid, "sched_current", current[sid], True) cache.set_station(sid, "sched_next", upnext[sid], True) cache.set_station(sid, "sched_history", history[sid], True) sched_current_dict = current[sid].to_dict() cache.set_station(sid, "sched_current_dict", sched_current_dict, True) next_dict_list = [] for event in upnext[sid]: next_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_next_dict", next_dict_list, True) history_dict_list = [] for event in history[sid]: history_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_history_dict", history_dict_list, True) all_station = {} if "songs" in sched_current_dict: all_station["title"] = sched_current_dict["songs"][0]["title"] all_station["album"] = sched_current_dict["songs"][0]["albums"][0]["name"] all_station["art"] = sched_current_dict["songs"][0]["albums"][0]["art"] all_station["artists"] = ", ".join(artist['name'] for artist in sched_current_dict["songs"][0]["artists"]) else: all_station["title"] = None all_station["album"] = None all_station["art"] = None all_station["artists"] = None all_station["event_name"] = sched_current_dict["name"] all_station["event_type"] = sched_current_dict["type"] cache.set_station(sid, "all_station_info", all_station, True)
def update_memcache(sid): _update_schedule_memcache(sid) update_live_voting(sid) cache.prime_rating_cache_for_events(sid, [current[sid]] + upnext[sid] + history[sid]) cache.set_station(sid, "current_listeners", listeners.get_listeners_dict(sid), True) cache.set_station(sid, "album_diff", playlist.get_updated_albums_dict(sid), True) rainwave.playlist_objects.album.clear_updated_albums(sid) cache.set_station(sid, "all_albums", playlist.get_all_albums_list(sid), True) cache.set_station(sid, "all_artists", playlist.get_all_artists_list(sid), True) cache.set_station(sid, "all_groups", playlist.get_all_groups_list(sid), True) cache.set_station(sid, "all_groups_power", playlist.get_all_groups_for_power(sid), True) potential_dj_ids = [] if getattr(current[sid], "dj_user_id", None): potential_dj_ids.append(current[sid].dj_user_id) for evt in upnext[sid]: if getattr(evt, "dj_user_id", None): potential_dj_ids.append(evt.dj_user_id) if len(history[sid]) and history[sid][-1] and getattr(history[sid][-1], "dj_user_id", None): potential_dj_ids.append(history[sid][-1].dj_user_id) cache.set_station(sid, "dj_user_ids", potential_dj_ids)
def _update_schedule_memcache(sid): cache.set_station(sid, "sched_current", current[sid], True) cache.set_station(sid, "sched_next", upnext[sid], True) cache.set_station(sid, "sched_history", history[sid], True) sched_current_dict = current[sid].to_dict() cache.set_station(sid, "sched_current_dict", sched_current_dict, True) next_dict_list = [] for event in upnext[sid]: next_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_next_dict", next_dict_list, True) history_dict_list = [] for event in history[sid]: history_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_history_dict", history_dict_list, True) all_station = {} if "songs" in sched_current_dict: all_station["title"] = sched_current_dict["songs"][0]["title"] all_station["album"] = sched_current_dict["songs"][0]["albums"][0]["name"] all_station["art"] = sched_current_dict["songs"][0]["albums"][0]["art"] else: all_station["title"] = None all_station["album"] = None all_station["art"] = None all_station["event_name"] = sched_current_dict["name"] all_station["event_type"] = sched_current_dict["type"] cache.set_station(sid, "all_station_info", all_station, True)
def _listen(self, task_id): zeromq.init_pub() zeromq.init_sub() import api_requests.sync api_requests.sync.init() # task_ids start at zero, so we gobble up ports starting at the base port and work up port_no = int(config.get("api_base_port")) + task_id pid = os.getpid() pid_file = open("%s/api_%s.pid" % (config.get_directory("pid_dir"), port_no), 'w') pid_file.write(str(pid)) pid_file.close() # Log according to configured directory and port # we're operating on log_file = "%s/rw_api_%s.log" % (config.get_directory("log_dir"), port_no) if config.test_mode and os.path.exists(log_file): os.remove(log_file) log.init(log_file, config.get("log_level")) log.debug("start", "Server booting, port %s." % port_no) db.connect() cache.connect() memory_trace.setup(port_no) api.locale.load_translations() api.locale.compile_static_language_files() if config.get("web_developer_mode"): for station_id in config.station_ids: playlist.prepare_cooldown_algorithm(station_id) # automatically loads every station ID and fills things in if there's no data schedule.load() for station_id in config.station_ids: schedule.update_memcache(station_id) rainwave.request.update_line(station_id) rainwave.request.update_expire_times() cache.set_station(station_id, "backend_ok", True) cache.set_station(station_id, "backend_message", "OK") cache.set_station(station_id, "get_next_socket_timeout", False) for sid in config.station_ids: cache.update_local_cache_for_sid(sid) playlist.prepare_cooldown_algorithm(sid) playlist.update_num_songs() # If we're not in developer, remove development-related URLs if not config.get("developer_mode"): i = 0 while (i < len(request_classes)): if request_classes[i][0].find("/test/") != -1: request_classes.pop(i) i = i - 1 i = i + 1 # Make sure all other errors get handled in an API-friendly way request_classes.append((r"/api/.*", api.web.Error404Handler)) request_classes.append((r"/api4/.*", api.web.Error404Handler)) request_classes.append((r".*", api.web.HTMLError404Handler)) # Initialize the help (rather than it scan all URL handlers every time someone hits it) api.help.sectionize_requests() # Fire ze missiles! global app app = tornado.web.Application(request_classes, debug=(config.test_mode or config.get("developer_mode")), template_path=os.path.join(os.path.dirname(__file__), "../templates"), static_path=os.path.join(os.path.dirname(__file__), "../static"), autoescape=None) http_server = tornado.httpserver.HTTPServer(app, xheaders = True) http_server.listen(port_no) if config.get("api_user") and config.get("api_group"): chuser.change_user(config.get("api_user"), config.get("api_group")) for request in request_classes: log.debug("start", " Handler: %s" % str(request)) log.info("start", "API server on port %s ready to go." % port_no) self.ioloop = tornado.ioloop.IOLoop.instance() try: self.ioloop.start() finally: self.ioloop.stop() http_server.stop() db.close() log.info("stop", "Server has been shutdown.") log.close()
def post(self): cache.set_station(self.sid, "backend_paused", False) result = liquidsoap.unpause(self.sid) self.append(self.return_name, { "success": True, "message": result })
# Windows, no multiprocessing else: dest_port += int(list(config.station_ids)[0]) conn = httplib.HTTPConnection(args.dest, config.get("backend_port") + int(args.sid), timeout=3) conn.request("GET", "/advance/%s" % args.sid) result = conn.getresponse() if result.status == 200: next_song_filename = result.read() if not next_song_filename or len(next_song_filename) == 0: raise Exception("Got zero-length filename from backend!") print next_song_filename else: raise Exception("Backend HTTP Error %s" % result.status) cache.set_station(args.sid, "backend_ok", True) cache.set_station(args.sid, "backend_message", "OK") cache.set_station(args.sid, "get_next_socket_timeout", False) conn.close() except socket.timeout as e: cache.set_station(args.sid, "backend_ok", False) cache.set_station(args.sid, "backend_status", repr(e)) cache.set_station(args.sid, "get_next_socket_timeout", True) time.sleep(2) raise except Exception as e: cache.set_station(args.sid, "backend_ok", False) cache.set_station(args.sid, "backend_status", repr(e)) if conn: conn.close() time.sleep(2)
def _listen(self, task_id): zeromq.init_pub() zeromq.init_sub() import api_requests.sync api_requests.sync.init() # task_ids start at zero, so we gobble up ports starting at the base port and work up port_no = int(config.get("api_base_port")) + task_id # Log according to configured directory and port # we're operating on log_file = "%s/rw_api_%s.log" % (config.get_directory("log_dir"), port_no) log.init(log_file, config.get("log_level")) log.debug("start", "Server booting, port %s." % port_no) db.connect(auto_retry=False, retry_only_this_time=True) cache.connect() memory_trace.setup(port_no) api.locale.load_translations() api.locale.compile_static_language_files() if config.get("developer_mode"): for station_id in config.station_ids: playlist.prepare_cooldown_algorithm(station_id) # automatically loads every station ID and fills things in if there's no data schedule.load() for station_id in config.station_ids: schedule.update_memcache(station_id) rainwave.request.update_line(station_id) rainwave.request.update_expire_times() cache.set_station(station_id, "backend_ok", True) cache.set_station(station_id, "backend_message", "OK") cache.set_station(station_id, "get_next_socket_timeout", False) for sid in config.station_ids: cache.update_local_cache_for_sid(sid) playlist.prepare_cooldown_algorithm(sid) playlist.update_num_songs() # If we're not in developer, remove development-related URLs if not config.get("developer_mode"): i = 0 while i < len(request_classes): if request_classes[i][0].find("/test/") != -1: request_classes.pop(i) i = i - 1 i = i + 1 # Make sure all other errors get handled in an API-friendly way request_classes.append((r"/api/.*", api.web.Error404Handler)) request_classes.append((r"/api4/.*", api.web.Error404Handler)) request_classes.append((r".*", api.web.HTMLError404Handler)) # Initialize the help (rather than it scan all URL handlers every time someone hits it) api.help.sectionize_requests() # Fire ze missiles! global app debug = config.get("developer_mode") app = tornado.web.Application( request_classes, debug=debug, template_path=os.path.join(os.path.dirname(__file__), "../templates"), static_path=os.path.join(os.path.dirname(__file__), "../static"), autoescape=None, autoreload=debug, serve_traceback=debug, ) http_server = tornado.httpserver.HTTPServer(app, xheaders=True) http_server.listen(port_no) for request in request_classes: log.debug("start", " Handler: %s" % str(request)) log.info("start", "API server on port %s ready to go." % port_no) self.ioloop = tornado.ioloop.IOLoop.instance() db_keepalive = tornado.ioloop.PeriodicCallback(db.connection_keepalive, 10000) db_keepalive.start() try: self.ioloop.start() finally: self.ioloop.stop() http_server.stop() db.close() log.info("stop", "Server has been shutdown.") log.close()
def update_memcache(sid): _update_schedule_memcache(sid) update_live_voting(sid) cache.prime_rating_cache_for_events( sid, [current[sid]] + upnext[sid] + history[sid] ) cache.set_station(sid, "current_listeners", listeners.get_listeners_dict(sid), True) cache.set_station(sid, "album_diff", playlist.get_updated_albums_dict(sid), True) rainwave.playlist_objects.album.clear_updated_albums(sid) cache.set_station(sid, "all_albums", playlist.get_all_albums_list(sid), True) cache.set_station(sid, "all_artists", playlist.get_all_artists_list(sid), True) cache.set_station(sid, "all_groups", playlist.get_all_groups_list(sid), True) cache.set_station(sid, "all_groups_power", playlist.get_all_groups_for_power(sid), True) potential_dj_ids = [] if getattr(current[sid], "dj_user_id", None): potential_dj_ids.append(current[sid].dj_user_id) for evt in upnext[sid]: if getattr(evt, "dj_user_id", None): potential_dj_ids.append(evt.dj_user_id) if ( history[sid] and history[sid][-1] and getattr(history[sid][-1], "dj_user_id", None) ): potential_dj_ids.append(history[sid][-1].dj_user_id) cache.set_station(sid, "dj_user_ids", potential_dj_ids)
def _listen(self, task_id): import api_requests.sync api_requests.sync.init() # task_ids start at zero, so we gobble up ports starting at the base port and work up port_no = int(config.get("api_base_port")) + task_id pid = os.getpid() pid_file = open( "%s/api_%s.pid" % (config.get_directory("pid_dir"), port_no), 'w') pid_file.write(str(pid)) pid_file.close() # Log according to configured directory and port # we're operating on log_file = "%s/rw_api_%s.log" % (config.get_directory("log_dir"), port_no) if config.test_mode and os.path.exists(log_file): os.remove(log_file) log.init(log_file, config.get("log_level")) log.debug("start", "Server booting, port %s." % port_no) db.connect() cache.connect() memory_trace.setup(port_no) if config.get("web_developer_mode"): for station_id in config.station_ids: playlist.prepare_cooldown_algorithm(station_id) # automatically loads every station ID and fills things in if there's no data schedule.load() for station_id in config.station_ids: schedule.update_memcache(station_id) rainwave.request.update_line(station_id) rainwave.request.update_expire_times() cache.set_station(station_id, "backend_ok", True) cache.set_station(station_id, "backend_message", "OK") cache.set_station(station_id, "get_next_socket_timeout", False) for sid in config.station_ids: cache.update_local_cache_for_sid(sid) playlist.prepare_cooldown_algorithm(sid) playlist.update_num_songs() # If we're not in developer, remove development-related URLs if not config.get("developer_mode"): i = 0 while (i < len(request_classes)): if request_classes[i][0].find("/test/") != -1: request_classes.pop(i) i = i - 1 i = i + 1 # Make sure all other errors get handled in an API-friendly way request_classes.append((r"/api/.*", api.web.Error404Handler)) request_classes.append((r"/api4/.*", api.web.Error404Handler)) request_classes.append((r".*", api.web.HTMLError404Handler)) # Initialize the help (rather than it scan all URL handlers every time someone hits it) api.help.sectionize_requests() # Fire ze missiles! app = tornado.web.Application( request_classes, debug=(config.test_mode or config.get("developer_mode")), template_path=os.path.join(os.path.dirname(__file__), "../templates"), static_path=os.path.join(os.path.dirname(__file__), "../static"), autoescape=None) http_server = tornado.httpserver.HTTPServer(app, xheaders=True) http_server.listen(port_no) if config.get("api_user") and config.get("api_group"): chuser.change_user(config.get("api_user"), config.get("api_group")) if task_id == 0: buildtools.bake_css() buildtools.bake_js() buildtools.bake_beta_js() for request in request_classes: log.debug("start", " Handler: %s" % str(request)) log.info("start", "API server on port %s ready to go." % port_no) self.ioloop = tornado.ioloop.IOLoop.instance() try: self.ioloop.start() finally: self.ioloop.stop() http_server.stop() db.close() log.info("stop", "Server has been shutdown.") log.close()
def _update_memcache(sid): cache.set_station(sid, "sched_current", current[sid], True) cache.set_station(sid, "sched_next", next[sid], True) cache.set_station(sid, "sched_history", history[sid], True) cache.set_station(sid, "sched_current_dict", current[sid].to_dict(), True) next_dict_list = [] for event in next[sid]: next_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_next_dict", next_dict_list, True) history_dict_list = [] for event in history[sid]: history_dict_list.append(event.to_dict()) cache.set_station(sid, "sched_history_dict", history_dict_list, True) cache.prime_rating_cache_for_events([ current[sid] ] + next[sid] + history[sid]) cache.set_station(sid, "listeners_current", listeners.get_listeners_dict(sid), True) cache.set_station(sid, "album_diff", playlist.get_updated_albums_dict(sid), True) playlist.clear_updated_albums(sid) cache.set_station(sid, "all_albums", playlist.get_all_albums_list(sid), True) cache.set_station(sid, "all_artists", playlist.get_all_artists_list(sid), True)
if hasattr(os, "fork"): dest_port += int(args.sid) # Windows, no multiprocessing else: dest_port += int(list(config.station_ids)[0]) conn = httplib.HTTPConnection(args.dest, config.get("backend_port") + int(args.sid), timeout=3) conn.request("GET", "/advance/%s" % args.sid) result = conn.getresponse() if result.status == 200: next_song_filename = result.read() if not next_song_filename or len(next_song_filename) == 0: raise Exception("Got zero-length filename from backend!") print next_song_filename else: raise Exception("Backend HTTP Error %s" % result.status) cache.set_station(args.sid, "backend_ok", True) cache.set_station(args.sid, "backend_message", "OK") cache.set_station(args.sid, "get_next_socket_timeout", False) conn.close() except socket.timeout as e: cache.set_station(args.sid, "backend_ok", False) cache.set_station(args.sid, "backend_status", repr(e)) cache.set_station(args.sid, "get_next_socket_timeout", True) time.sleep(2) raise except Exception as e: cache.set_station(args.sid, "backend_ok", False) cache.set_station(args.sid, "backend_status", repr(e)) if conn: conn.close() time.sleep(2)
def update_line(sid): # Get everyone in the line line = db.c.fetch_all( "SELECT username, user_id, line_expiry_tune_in, line_expiry_election, line_wait_start FROM r4_request_line JOIN phpbb_users USING (user_id) WHERE r4_request_line.sid = %s AND radio_requests_paused = FALSE ORDER BY line_wait_start", (sid, )) new_line = [] # user_positions has user_id as a key and position as the value, this is cached for quick lookups by API requests # so users know where they are in line user_positions = {} t = int(time.time()) albums_with_requests = [] position = 1 # For each person for row in line: add_to_line = False u = User(row['user_id']) row['song_id'] = None # If their time is up, remove them and don't add them to the new line if row['line_expiry_tune_in'] and row['line_expiry_tune_in'] <= t: log.debug( "request_line", "%s: Removed user ID %s from line for tune in timeout, expiry time %s current time %s" % (sid, u.id, row['line_expiry_tune_in'], t)) u.remove_from_request_line() else: tuned_in_sid = db.c.fetch_var( "SELECT sid FROM r4_listeners WHERE user_id = %s AND sid = %s AND listener_purge = FALSE", (u.id, sid)) tuned_in = True if tuned_in_sid == sid else False if tuned_in: # Get their top song ID song_id = u.get_top_request_song_id(sid) # If they have no song and their line expiry has arrived, boot 'em if not song_id and row['line_expiry_election'] and ( row['line_expiry_election'] <= t): log.debug( "request_line", "%s: Removed user ID %s from line for election timeout, expiry time %s current time %s" % (sid, u.id, row['line_expiry_election'], t)) u.remove_from_request_line() # Give them more chances if they still have requests # They'll get added to the line of whatever station they're tuned in to (if any!) if u.has_requests(): u.put_in_request_line(u.get_tuned_in_sid()) # If they have no song, start the expiry countdown elif not song_id and not row['line_expiry_election']: row['line_expiry_election'] = t + 900 db.c.update( "UPDATE r4_request_line SET line_expiry_election = %s WHERE user_id = %s", ((t + 900), row['user_id'])) add_to_line = True # Keep 'em in line else: if song_id: albums_with_requests.append( db.c.fetch_var( "SELECT album_id FROM r4_songs WHERE song_id = %s", (song_id, ))) row['song_id'] = song_id add_to_line = True elif not row['line_expiry_tune_in'] or row[ 'line_expiry_tune_in'] == 0: db.c.update( "UPDATE r4_request_line SET line_expiry_tune_in = %s WHERE user_id = %s", ((t + 900), row['user_id'])) add_to_line = True else: add_to_line = True if add_to_line: new_line.append(row) user_positions[u.id] = position position = position + 1 cache.set_station(sid, "request_line", new_line, True) cache.set_station(sid, "request_user_positions", user_positions, True) db.c.update( "UPDATE r4_album_sid SET album_requests_pending = NULL WHERE album_requests_pending = TRUE AND sid = %s", (sid, )) for album_id in albums_with_requests: db.c.update( "UPDATE r4_album_sid SET album_requests_pending = TRUE WHERE album_id = %s AND sid = %s", (album_id, sid)) return new_line
def post(self): cache.set_station(self.sid, "pause_title", self.get_argument("title")) self.append(self.return_name, { "success": True, "pause_title": self.get_argument("title") })