mirror of
https://github.com/rembo10/headphones.git
synced 2026-05-08 20:59:25 +01:00
Merge pull request #1906 from basilfx/ratelimit
Initial version of rate limiting on requests
This commit is contained in:
@@ -38,7 +38,7 @@
|
||||
%>
|
||||
<tr class="grade${grade}">
|
||||
%if type == 'album':
|
||||
<td id="albumart" style=" text-align: center; vertical-align: middle;"><div id="artistImg"><img title="${result['albumid']}" class="albumArt" height="50" width="50" onerror="this.onerror=null;this.src='${caa_group_url}'"></div></td>
|
||||
<td id="albumart" style=" text-align: center; vertical-align: middle;"><div id="artistImg"><img title="${result['albumid']}" class="albumArt" height="50" width="50" onerror="tryCCA(this, '${caa_group_url}')"></div></td>
|
||||
%else:
|
||||
<td id="albumart"><div id="artistImg"><img title="${result['id']}" class="albumArt" height="50" width="50"></div></td>
|
||||
%endif
|
||||
@@ -72,7 +72,13 @@
|
||||
<script src="js/libs/jquery.dataTables.min.js"></script>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
function tryCCA(element, url) {
|
||||
element.onerror = function() {
|
||||
element.onerror = null;
|
||||
element.src = "interfaces/default/images/no-cover-art.png";
|
||||
};
|
||||
element.src = url;
|
||||
}
|
||||
function getArt() {
|
||||
$("table#searchresults_table tr td#albumart img").each(function(){
|
||||
var id = $(this).attr('title');
|
||||
@@ -85,7 +91,6 @@
|
||||
});
|
||||
}
|
||||
function initThisPage() {
|
||||
getArt();
|
||||
$('#searchresults_table').dataTable({
|
||||
"bDestroy": true,
|
||||
"aoColumnDefs": [
|
||||
@@ -103,8 +108,10 @@
|
||||
"aaSorting": []
|
||||
});
|
||||
$('#searchresults_table').on("draw.dt", function () {
|
||||
getArt();
|
||||
$("img").unveil();
|
||||
});
|
||||
getArt();
|
||||
resetFilters("album");
|
||||
}
|
||||
$(document).ready(function(){
|
||||
|
||||
@@ -13,18 +13,23 @@
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with Headphones. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
import random
|
||||
import time
|
||||
import random
|
||||
import threading
|
||||
import headphones
|
||||
|
||||
from headphones import db, logger, request
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
TIMEOUT = 60 # seconds
|
||||
TIMEOUT = 60.0 # seconds
|
||||
REQUEST_LIMIT = 1.0 # seconds
|
||||
ENTRY_POINT = "http://ws.audioscrobbler.com/2.0/"
|
||||
API_KEY = "395e6ec6bb557382fc41fde867bce66f"
|
||||
|
||||
# Required for API request limit
|
||||
lock = threading.Lock()
|
||||
|
||||
def request_lastfm(method, **kwargs):
|
||||
"""
|
||||
Call a Last.FM API method. Automatically sets the method and API key. Method
|
||||
@@ -43,7 +48,8 @@ def request_lastfm(method, **kwargs):
|
||||
logger.debug("Calling Last.FM method: %s", method)
|
||||
logger.debug("Last.FM call parameters: %s", kwargs)
|
||||
|
||||
data = request.request_json(ENTRY_POINT, timeout=TIMEOUT, params=kwargs)
|
||||
data = request.request_json(ENTRY_POINT, timeout=TIMEOUT, params=kwargs,
|
||||
rate_limit=(lock, REQUEST_LIMIT))
|
||||
|
||||
# Parse response and check for errors.
|
||||
if not data:
|
||||
|
||||
@@ -18,16 +18,27 @@ from headphones import logger
|
||||
from xml.dom import minidom
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
import time
|
||||
import requests
|
||||
import feedparser
|
||||
import headphones
|
||||
import collections
|
||||
|
||||
# Dictionary with last request times, for rate limiting.
|
||||
last_requests = collections.defaultdict(int)
|
||||
|
||||
def request_response(url, method="get", auto_raise=True,
|
||||
whitelist_status_code=None, **kwargs):
|
||||
whitelist_status_code=None, rate_limit=None, **kwargs):
|
||||
"""
|
||||
Convenient wrapper for `requests.get', which will capture the exceptions and
|
||||
log them. On success, the Response object is returned. In case of a
|
||||
exception, None is returned.
|
||||
|
||||
Additionally, there is support for rate limiting. To use this feature,
|
||||
supply a tuple of (lock, request_limit). The lock is used to make sure no
|
||||
other request with the same lock is executed. The request limit is the
|
||||
minimal time between two requests (and so 1/request_limit is the number of
|
||||
requests per seconds).
|
||||
"""
|
||||
|
||||
# Convert whitelist_status_code to a list if needed
|
||||
@@ -43,9 +54,32 @@ def request_response(url, method="get", auto_raise=True,
|
||||
request_method = getattr(requests, method.lower())
|
||||
|
||||
try:
|
||||
# Request the URL
|
||||
# Enfore request rate limit if applicable. This uses the lock so there
|
||||
# is synchronized access to the API. If no limit is enforced, just do
|
||||
# it as usual.
|
||||
logger.debug("Requesting URL via %s method: %s", method.upper(), url)
|
||||
response = request_method(url, **kwargs)
|
||||
|
||||
if rate_limit:
|
||||
lock, request_limit = rate_limit
|
||||
|
||||
with lock:
|
||||
delta = time.time() - last_requests[lock]
|
||||
limit = int(1.0 / request_limit)
|
||||
|
||||
if delta < request_limit:
|
||||
logger.debug("Sleeping %.2f seconds for request, limit " \
|
||||
"is %d req/sec.", request_limit - delta, limit)
|
||||
|
||||
# Sleep the remaining time
|
||||
time.sleep(request_limit - delta)
|
||||
|
||||
# Update last request time and start with request. Basically, if
|
||||
# the request takes N seconds, next request will sleep N seconds
|
||||
# less.
|
||||
last_requests[lock] = time.time()
|
||||
response = request_method(url, **kwargs)
|
||||
else:
|
||||
response = request_method(url, **kwargs)
|
||||
|
||||
# If status code != OK, then raise exception, except if the status code
|
||||
# is white listed.
|
||||
|
||||
Reference in New Issue
Block a user