瀏覽代碼

implement vtuner paging

should(tm) now be compatible with older avrs which do not get the whole listing at once
milaq 6 年之前
父節點
當前提交
1123f3e55b
共有 3 個文件被更改,包括 78 次插入58 次删除
  1. 12 6
      ycast/radiobrowser.py
  2. 61 51
      ycast/server.py
  3. 5 1
      ycast/vtuner.py

+ 12 - 6
ycast/radiobrowser.py

@@ -1,9 +1,11 @@
 import requests
 
+import ycast.vtuner as vtuner
+
 MINIMUM_COUNT_GENRE = 5
 MINIMUM_COUNT_COUNTRY = 5
 MINIMUM_BITRATE = 64
-STATION_LIMIT_DEFAULT = 99
+STATION_LIMIT = 1000
 ID_PREFIX = "RB_"
 
 
@@ -27,6 +29,10 @@ class Station:
         self.codec = get_json_attr(station_json, 'codec')
         self.bitrate = get_json_attr(station_json, 'bitrate')
 
+    def to_vtuner(self):
+        return vtuner.Station(self.id, self.name, ', '.join(self.tags), self.url, self.icon,
+                              self.tags[0], self.country, self.codec, self.bitrate, None)
+
 
 def request(url):
     headers = {'content-type': 'application/json', 'User-Agent': 'YCast'}
@@ -42,10 +48,10 @@ def get_station_by_id(uid):
     return Station(station_json[0])
 
 
-def search(name):
+def search(name, limit=STATION_LIMIT):
     stations = []
     stations_json = request('stations/search?order=votes&reverse=true&bitrateMin=' +
-                            str(MINIMUM_BITRATE) + '&name=' + str(name))
+                            str(MINIMUM_BITRATE) + '&limit=' + str(limit) + '&name=' + str(name))
     for station_json in stations_json:
         stations.append(Station(station_json))
     return stations
@@ -71,7 +77,7 @@ def get_genres():
     return genres
 
 
-def get_stations_by_country(country, limit=STATION_LIMIT_DEFAULT):
+def get_stations_by_country(country, limit=STATION_LIMIT):
     stations = []
     stations_json = request('stations/search?order=votes&reverse=true&bitrateMin=' +
                             str(MINIMUM_BITRATE) + '&limit=' + str(limit) +
@@ -81,7 +87,7 @@ def get_stations_by_country(country, limit=STATION_LIMIT_DEFAULT):
     return stations
 
 
-def get_stations_by_genre(genre, limit=STATION_LIMIT_DEFAULT):
+def get_stations_by_genre(genre, limit=STATION_LIMIT):
     stations = []
     stations_json = request('stations/search?order=votes&reverse=true&bitrateMin=' +
                             str(MINIMUM_BITRATE) + '&limit=' + str(limit) + '&tagExact=true&tag=' + str(genre))
@@ -90,7 +96,7 @@ def get_stations_by_genre(genre, limit=STATION_LIMIT_DEFAULT):
     return stations
 
 
-def get_stations_by_votes(limit=STATION_LIMIT_DEFAULT):
+def get_stations_by_votes(limit=STATION_LIMIT):
     stations = []
     stations_json = request('stations?order=votes&reverse=true&limit=' + str(limit))
     for station_json in stations_json:

+ 61 - 51
ycast/server.py

@@ -43,6 +43,48 @@ def get_stations(config):
         return
 
 
+def get_directories_page(subdir, directories, startitems, enditems):
+    page = vtuner.Page()
+    if len(directories) == 0:
+        page.add(vtuner.Display("No entries found."))
+        return page
+    page.set_count(len(directories))
+    offset = 0
+    limit = len(directories)
+    if startitems and enditems:
+        offset = int(startitems) - 1
+        limit = int(enditems)
+        if offset > len(directories):
+            offset = len(directories)
+        if limit > len(directories):
+            limit = len(directories)
+    for directory_num in range(offset, limit):
+        directory = directories[directory_num]
+        page.add(vtuner.Directory(directory, url_for(subdir, _external=True, directory=directory)))
+    return page
+
+
+def get_stations_page(stations, startitems, enditems):
+    page = vtuner.Page()
+    if len(stations) == 0:
+        page.add(vtuner.Display("No stations found."))
+        return page
+    page.set_count(len(stations))
+    offset = 0
+    limit = len(stations)
+    if startitems and enditems:
+        offset = int(startitems) - 1
+        limit = int(enditems)
+        if offset > len(stations):
+            offset = len(stations)
+        if limit > len(stations):
+            limit = len(stations)
+    for station_num in range(offset, limit):
+        station = stations[station_num]
+        page.add(station.to_vtuner())
+    return page
+
+
 # TODO: vtuner doesn't do https (e.g. for logos). make an icon cache
 
 
@@ -99,68 +141,39 @@ def radiobrowser_landing():
 
 @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/')
 def radiobrowser_countries():
-    countries = radiobrowser.get_countries()
-    page = vtuner.Page()
-    page.add(vtuner.Previous(url_for('radiobrowser_landing', _external=True)))
-    for country in countries:
-        page.add(vtuner.Directory(country, url_for('radiobrowser_country_stations', _external=True, country=country)))
-    return page.to_string()
+    directories = radiobrowser.get_countries()
+    return get_directories_page('radiobrowser_country_stations', directories,
+                                request.args.get('startitems'), request.args.get('enditems')).to_string()
 
 
-@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/<country>')
-def radiobrowser_country_stations(country):
-    stations = radiobrowser.get_stations_by_country(country)
-    page = vtuner.Page()
-    page.add(vtuner.Previous(url_for('radiobrowser_countries', _external=True)))
-    if len(stations) == 0:
-        page.add(vtuner.Display("No stations found for country '" + country + "'"))
-    else:
-        for station in stations:
-            page.add(vtuner.Station(station.id, station.name, ', '.join(station.tags), station.url, station.icon,
-                                    station.tags[0], station.country, station.codec, station.bitrate, None))
-    return page.to_string()
+@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_COUNTRY + '/<directory>')
+def radiobrowser_country_stations(directory):
+    stations = radiobrowser.get_stations_by_country(directory)
+    return get_stations_page(stations, request.args.get('startitems'), request.args.get('enditems')).to_string()
 
 
 @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/')
 def radiobrowser_genres():
-    genres = radiobrowser.get_genres()
-    page = vtuner.Page()
-    page.add(vtuner.Previous(url_for('radiobrowser_landing', _external=True)))
-    for genre in genres:
-        page.add(vtuner.Directory(genre, url_for('radiobrowser_genre_stations', _external=True, genre=genre)))
-    return page.to_string()
+    directories = radiobrowser.get_genres()
+    return get_directories_page('radiobrowser_genre_stations', directories,
+                                request.args.get('startitems'), request.args.get('enditems')).to_string()
 
 
-@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/<genre>')
-def radiobrowser_genre_stations(genre):
-    stations = radiobrowser.get_stations_by_genre(genre)
-    page = vtuner.Page()
-    page.add(vtuner.Previous(url_for('radiobrowser_genres', _external=True)))
-    if len(stations) == 0:
-        page.add(vtuner.Display("No stations found for genre '" + genre + "'"))
-    else:
-        for station in stations:
-            page.add(vtuner.Station(station.id, station.name, ', '.join(station.tags), station.url, station.icon,
-                                    station.tags[0], station.country, station.codec, station.bitrate, None))
-    return page.to_string()
+@app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_GENRE + '/<directory>')
+def radiobrowser_genre_stations(directory):
+    stations = radiobrowser.get_stations_by_genre(directory)
+    return get_stations_page(stations, request.args.get('startitems'), request.args.get('enditems')).to_string()
 
 
 @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_POPULAR + '/')
 def radiobrowser_popular():
     stations = radiobrowser.get_stations_by_votes()
-    page = vtuner.Page()
-    page.add(vtuner.Previous(url_for('radiobrowser_landing', _external=True)))
-    for station in stations:
-        page.add(vtuner.Station(station.id, station.name, ', '.join(station.tags), station.url, station.icon,
-                                station.tags[0], station.country, station.codec, station.bitrate, None))
-    return page.to_string()
+    return get_stations_page(stations, request.args.get('startitems'), request.args.get('enditems')).to_string()
 
 
 @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_SEARCH, defaults={'path': ''})
 @app.route('/' + PATH_ROOT + '/' + PATH_RADIOBROWSER + '/' + PATH_RADIOBROWSER_SEARCH + '<path:path>')
 def radiobrowser_search(path):
-    page = vtuner.Page()
-    page.add(vtuner.Previous(url_for('radiobrowser_landing', _external=True)))
     # vtuner does totally weird stuff here: TWO request arguments are passed to the search URI
     # thus, we need to parse the search query as path
     query = None
@@ -168,13 +181,10 @@ def radiobrowser_search(path):
         path_search = path[path.find('search'):]
         query = path_search.partition('=')[2]
     if not query or len(query) < 3:
+        page = vtuner.Page()
+        page.add(vtuner.Previous(url_for('landing', _external=True)))
         page.add(vtuner.Display("Search query too short."))
+        return page.to_string()
     else:
         stations = radiobrowser.search(query)
-        if len(stations) == 0:
-            page.add(vtuner.Display("No results for '" + query + "'"))
-        else:
-            for station in stations:
-                page.add(vtuner.Station(station.id, station.name, ', '.join(station.tags), station.url, station.icon,
-                                        station.tags[0], station.country, station.codec, station.bitrate, None))
-    return page.to_string()
+        return get_stations_page(stations, request.args.get('startitems'), request.args.get('enditems')).to_string()

+ 5 - 1
ycast/vtuner.py

@@ -10,13 +10,17 @@ def get_init_token():
 class Page:
     def __init__(self):
         self.items = []
+        self.count = -1
 
     def add(self, item):
         self.items.append(item)
 
+    def set_count(self, count):
+        self.count = count
+
     def to_xml(self):
         xml = etree.Element('ListOfItems')
-        etree.SubElement(xml, 'ItemCount').text = str(len(self.items))
+        etree.SubElement(xml, 'ItemCount').text = str(self.count)
         for item in self.items:
             item.append_to_xml(xml)
         return xml