Browse Source

zwischenstand

Thomas Hanika 3 years ago
parent
commit
ac3674555c
6 changed files with 261 additions and 77 deletions
  1. 4 0
      ycast/generic.py
  2. 21 2
      ycast/server.py
  3. 124 9
      ycast/static/script.js
  4. 53 24
      ycast/static/style.css
  5. 56 40
      ycast/templates/index.html
  6. 3 2
      ycast/test_YCast.py

+ 4 - 0
ycast/generic.py

@@ -23,6 +23,10 @@ class Directory:
         else:
             self.displayname = name
 
+    def to_dict(self):
+        return {'name': self.name , 'displayname': self.displayname, 'count': self.item_count }
+
+
 
 def mk_writeable_dir(path):
     try:

+ 21 - 2
ycast/server.py

@@ -149,7 +149,7 @@ def landing_api(path):
         if category.endswith('language'):
             language = request.args.get('language','german')
             stations = radiobrowser.get_stations_by_language(language)
-        if category.endswith('countrycode'):
+        if category.endswith('country'):
             country = request.args.get('country','Germany')
             stations = radiobrowser.get_stations_by_country(country)
 
@@ -163,7 +163,26 @@ def landing_api(path):
     if path.endswith('bookmarks'):
         category = request.args.get('category')
         stations = my_stations.get_stations_by_category(category)
-        return flask.jsonify({'stations': stations})
+        if stations is not None:
+            stations_dict = []
+            for station in stations:
+                stations_dict.append(station.to_dict())
+            return flask.jsonify(stations_dict)
+
+    if path.endswith('paramlist'):
+        category = request.args.get('category')
+        directories = None
+        if category.endswith('language'):
+            directories = radiobrowser.get_language_directories();
+        if category.endswith('country'):
+            directories = radiobrowser.get_country_directories();
+        if directories is not None:
+            directories_dict = []
+            for directory in directories:
+                directories_dict.append(directory.to_dict())
+            return flask.jsonify(directories_dict)
+
+
     return abort(400,'Not implemented: ' + path)
 
 

+ 124 - 9
ycast/static/script.js

@@ -1,5 +1,28 @@
-function createItem(name, icon, description) {
+window.onload = function () {
+    category = document.getElementById('id_category').value;
+    param = document.getElementById('id_param').value;
+    requestStationList(category, param) 
     
+}
+
+function initSearch() {
+    var stationsearch = document.getElementById('stationsearch');
+    stationsearch.value = '';
+    stationsearch.onkeyup = function () {
+        var filter = stationsearch.value.toUpperCase();
+        var lis = document.getElementsByTagName('li');
+        for (var i = 0; i < lis.length; i++) {
+            var searchval = lis[i].dataset.search;
+            if (searchval.indexOf(filter) > -1)
+                lis[i].style.display = 'flex';
+            else
+                lis[i].style.display = 'none';
+        }
+    }
+}
+
+function createItem(name, icon, description) {
+
     var itemElem = document.createElement("div");
     itemElem.className = "item";
 
@@ -7,16 +30,17 @@ function createItem(name, icon, description) {
     itemicon.className = "itemicon";
     var itemiconimg = document.createElement("img");
     itemiconimg.src = icon;
+    itemiconimg.className = "itemicon";
 
     var itemtext = document.createElement("div");
     itemtext.className = "itemtext";
-    var h4text = document.createElement("h4"); 
+    var h4text = document.createElement("h4");
     h4text.textContent = name;
     var desc = document.createElement("p");
     desc.textContent = description;
-    
+
     itemicon.appendChild(itemiconimg);
-    
+
     itemtext.appendChild(h4text);
     itemtext.appendChild(desc);
 
@@ -26,11 +50,102 @@ function createItem(name, icon, description) {
     return itemElem;
 }
 
-function stationsAddItem() {
-    var listElemet = document.createElement("li");
-    listElemet.className = "item";
-    listElemet.appendChild(createItem(" Halle self created","http://www.klassikradio.de/_nuxt/icons/icon_64.a00w80w0000.png","classic, poppi"));
+function requestStationList(category, param) {
+    var url = 'api/stations?category=' + category;
 
-    document.getElementById("stationList").appendChild(listElemet);
+    if (category.indexOf('language') > -1) {
+        url = url + '&language=' + param.toLowerCase();
+    }
+    if (category.indexOf('country') > -1) {
+        url = url + '&country=' + param;
+    }
+    var myRequest = new Request(url);
+    var myOldList = document.getElementById("stationList");
+
+    var myList = myOldList.cloneNode(false);
+    myOldList.parentNode.replaceChild(myList, myOldList);
+
+    fetch(myRequest)
+        .then(response => response.json())
+        .then(data => {
+            for (const station of data) {
+                let listItem = document.createElement('li');
+                listItem.appendChild(
+                    createItem(station.name, station.icon, station.description)
+                );
+                listItem.dataset.json = JSON.stringify(station);
+                listItem.dataset.search = (station.name + '#' + station.description).toUpperCase();
+                myList.appendChild(listItem);
+            }
+        })
+        .catch(console.error);
+    initSearch();
+}
+
+function onInputSelect(e, objElem) {
+
+    if (objElem.id == 'id_category') {
+        paramElem = document.getElementById('id_param')
+        param = paramElem.value
+        category = objElem.value
+        switch (category) {
+            case 'language':
+                setParamlist();
+                try {paramElem.fokus();} catch(e) {};
+                return;
+            case 'country':
+                setParamlist();
+                try {paramElem.fokus();} catch(e) {};
+                return;
+            default:
+                paramElem.disabled = true;
+                break;
+        }
+        requestStationList(category, param);
+    }
 }
 
+function setParamlist() {
+    var category = document.getElementById('id_category').value
+    var url = 'api/paramlist?category=' + category;
+    document.getElementById('id_param').value = '';
+    var myRequest = new Request(url);
+    var myOldList = document.getElementById('paramlist');
+
+    var myList = myOldList.cloneNode(false);
+    myOldList.parentNode.replaceChild(myList, myOldList);
+
+
+    fetch(myRequest)
+        .then(response => response.json())
+        .then(data => {
+            for (const param of data) {
+                var option = document.createElement('option');
+                option.value = param.name;
+                myList.appendChild(option);
+            }
+        })
+        .catch(console.error);
+    document.getElementById('id_param').disabled = false;
+}
+
+function keyUpEvent(e, objElem) {
+    switch (objElem.id) {
+        case 'id_param':
+            param = objElem.value;
+            category = document.getElementById('id_category').value;
+            if (e instanceof KeyboardEvent) {
+                // it is a keyboard event!
+                if (e.code == 'Enter') {
+                    requestStationList(category, param);
+                } else if (e.code == 'Backspace')
+                    this.value = '';
+            } else if (e instanceof Event) {
+                // one Element from selection is selected
+                requestStationList(category, param);
+            }
+            break;
+        default:
+            break;
+    }
+}

+ 53 - 24
ycast/static/style.css

@@ -1,61 +1,78 @@
 body {
     font-family: sans-serif;
     height: 100%;
+    align-items: center;
 }
+
+.page {
+    align-items: center;
+    height: 100%;
+    width: 100%;
+}
+
 .header {
-    height = 30rem;
-    width: 90%;
+    height=30rem;
+    width: 38.9rem;
+    margin: 0rem;
     background-color: blue;
     color: white;
     text-align: center;
-    border: 4px #eee solid;
     min-width: 20rem;
+    max-width: 40rem;
+    padding: 0.5rem
 }
+
 .container {
     display: block;
     width: 100%;
-    height: 100%;
-    max-height: auto;
+    bottom: 0rem;
     background-color: aquamarine;
 }
+
 .content {
-    position: relative;
-    border: 2px #eee solid;
-    width: 45%;
+    display: block;
+    width: 20rem;
     min-width: 20rem;
     float: left;
     margin: 0rem;
-    height: 20rem;
+    height: calc(100% - 13rem);
+    max-height: 30rem;
 }
 
 .contentheader {
+    display: block;
     background-color: blue;
     color: white;
     align-items: center;
     margin: 0rem;
     padding: 0.2rem;
+    border: 2px #eee solid;
+    height: 5rem;
+    width: 100%
 }
 
 .contentitems {
-    position: absolute;
-    overflow-y: scroll;
+    display: block;
+    overflow-y: auto;
     overflow-x: hidden;
+    top: 6rem;
     width: 100%;
-    height: 100%;
-    display: block;
+    height: calc(100% - 20rem);
+    max-height: 30rem;
+    border: 2px #eee solid ;
 }
 
 .item {
+    display: flex;
     border: 2px #eee solid;
     margin: 0rem;
-    display: flex;
     height: 2.4rem;
     align-items: center;
-        
 }
+
 .item:hover {
-        background-color: beige;
-    }
+    background-color: beige;
+}
 
 .itemicon {
     height: 2.4rem;
@@ -63,7 +80,9 @@ body {
 }
 
 .itemtext {
-    
+    white-space: nowrap;
+    overflow: hidden;
+    text-overflow: ellipsis;
 }
 
 ul {
@@ -73,16 +92,23 @@ ul {
     width: 100%;
     display: contents;
 }
+
 li {
     width: 95%;
     padding: 0rem;
     margin: 0rem;
 }
+
+h2 {
+    padding: 0rem;
+    margin: 0.5rem;
+}
+
 h3 {
-    border: 2px #eee solid;
     text-align: center;
     padding: 10px;
     margin: 0rem;
+    padding: 0.5rem;
 }
 
 h4 {
@@ -90,13 +116,16 @@ h4 {
     padding-left: 1rem;
     margin: auto
 }
+
 p {
     padding-left: 1.1rem;
     margin: auto;
     font-size: 0.8rem;
-        
+
 }
-img {
-    height: 100%;
-    width: 100%;
-}
+
+label {
+    padding-left: 1.1rem;
+}
+
+input

+ 56 - 40
ycast/templates/index.html

@@ -1,56 +1,72 @@
-<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
-        "http://www.w3.org/TR/html4/loose.dtd">
+<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
 <html>
+
 <head>
     <meta charset="UTF-8">
-    <link rel="stylesheet" href="../static/style.css">
-    <script type="text/javascript"
-         src="../static/script.js"></script>    <title>YCast</title>
+    <link rel="stylesheet" href="/static/style.css">
+    <script type="text/javascript" src="/static/script.js"></script>
+    <title>YCast</title>
 </head>
+
 <body>
-<div class="header">
-    <h2>Hallo YCast</h2>
-</div>
-<div class="content">
-    <div class="contentheader" onclick="stationsAddItem()">
-        <h3>Stations</h3>
-    </div>
-    <div class="contentitems">
-    <ul id="stationList">
-    <li class="item">
-        <div class="itemicon">
-             <img src="http://www.klassikradio.de/_nuxt/icons/icon_64.a00w80w0000.png">
+    <div class="page">
+        <div class="header">
+            <h2>YCast advanced</h2>
+            <div>
+                <label for="id_category">station-source:</label>
+                <select tabindex="1" id="id_category" name="category" oninput="onInputSelect(event, this)">
+                    <option value="recently">recently</option>
+                    <option value="voted">voted</option>
+                    <option value="language">language</option>
+                    <option value="country">country</option>
+                </select>
+            </div>
+            <div>
+                <label for="id_param">language or country:</label>
+                <input tabindex="2" list="paramlist" id="id_param" autocomplete=off disabled=true onclick="this.value=''" onfocus="this.value=''" onkeyup="keyUpEvent(event,this)">
+                <datalist id="paramlist">
+                </datalist>
+            </div>
         </div>
-        <div class="itemtext">
-            <h4>Radio</h4>
-            <p>ard,actuell</p>
+        <div class="content">
+            <div class="contentheader">
+                <h3 onclick="stations_request()">Stations</h3>
+                <label for="stationsearch">search:</label>
+                <input tabindex="3" type="search" id="stationsearch">
+            </div>
+            <div class="contentitems">
+                <ul id="stationList">
+                </ul>
+            </div>
         </div>
-    </li>
-    <li class="item">
+        <div class="content">
+            <div class="contentheader">
+                <h3>Bookmarks</h3>
+                <label for="idCategory">category:</label>
+                <input tabindex="10000" list="category" id="idCategory">
+                <datalist id="category">
+                    <option value="Actual">
+                    <option value="Pop">
+                    <option value="Classics">
+                </datalist>
+
+            </div>
+            <div class="contentitems">
+                <ul id="bookmarkList">
+                         <li class="item">
         <div class="itemicon">
-            <img src="http://wgbh.org//apple-touch-icon.png">
+             <img class="itemicon" src="http://www.klassikradio.de/_nuxt/icons/icon_64.a00w80w0000.png">
         </div>
         <div class="itemtext">
             <h4>Radio</h4>
             <p>ard,actuell</p>
         </div>
     </li>
-    </ul>
-    </div>
-</div>
-<div class="content">
-    <div class="contentheader"><h3>Bookmarks</h3></div>
-    <ul>
-     <li class="item">
-        <div class="itemicon">
-             <img src="http://www.klassikradio.de/_nuxt/icons/icon_64.a00w80w0000.png">
+
+                </ul>
+            </div>
         </div>
-        <div class="itemtext">
-            <h4>Radio</h4>
-            <p>ard,actuell</p>
-        </div>
-    </li>
-    </ul>
-</div>
+    </div>
 </body>
-</html>
+
+</html>

+ 3 - 2
ycast/test_YCast.py

@@ -67,6 +67,9 @@ class MyTestCase(unittest.TestCase):
         assert len(result) == 3
 
     def test_get_countries(self):
+        generic.init_base_dir('.ycast')
+        my_filter.init_filter_file()
+
         result = radiobrowser.get_country_directories()
         assert len(result) == 4
 
@@ -79,8 +82,6 @@ class MyTestCase(unittest.TestCase):
         assert result == 20
 
     def test_jsonable_classes(self):
-        generic.init_base_dir('.ycast')
-        my_filter.init_filter_file()
         stations = radiobrowser.get_stations_by_country('Germany')
         station = stations[0]
         text = station.to_vtuner()