about summary refs log tree commit diff stats
diff options
context:
space:
mode:
authorMarco Andronaco <andronacomarco@gmail.com>2023-07-09 15:43:42 +0200
committerMarco Andronaco <andronacomarco@gmail.com>2023-07-09 15:43:42 +0200
commiteb08af3998f02f0445a6a0a70558592335a0e59d (patch)
tree644477331c2ce00cc384670548f2d8858696961d
parent649c266701b16c7eb9ca2582c42e7408de385d1c (diff)
downloadtnt-search-eb08af3998f02f0445a6a0a70558592335a0e59d.tar.gz
initial commit
-rw-r--r--.gitignore1
-rwxr-xr-xmain.py115
-rw-r--r--templates/index.html127
3 files changed, 242 insertions, 1 deletions
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..5ceb386
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1 @@
+venv
diff --git a/main.py b/main.py
index af7f0e8..d0077d1 100755
--- a/main.py
+++ b/main.py
@@ -1,3 +1,116 @@
 #!/usr/local/bin/python3
+import os, csv, json
+from datetime import datetime
+from flask import Flask, request, render_template
+
+INPUT_PATH = os.path.join("tntvillage-release-dump", "tntvillage-release-dump.csv")
+CSV_SEPARATOR = ","
+HEADER = [ "DATA", "HASH", "TOPIC", "POST", "AUTORE", "TITOLO", "DESCRIZIONE", "DIMENSIONE", "CATEGORIA" ]
+TABLE_HEADER = [ "DATA", "CATEGORIA", "TITOLO", "DESCRIZIONE", "AUTORE", "DIMENSIONE", "HASH" ]
+CATEGORIE = {
+    1: "Film TV e programmi",
+    2: "Musica",
+    3: "E Books",
+    4: "Film",
+    6: "Linux",
+    7: "Anime",
+    8: "Cartoni",
+    9: "Macintosh",
+    10: "Windows Software",
+    11: "Pc Game",
+    12: "Playstation",
+    13: "Students Releases",
+    14: "Documentari",
+    21: "Video Musicali",
+    22: "Sport",
+    23: "Teatro",
+    24: "Wrestling",
+    25: "Varie",
+    26: "Xbox",
+    27: "Immagini sfondi",
+    28: "Altri Giochi",
+    29: "Serie TV",
+    30: "Fumetteria",
+    31: "Trash",
+    32: "Nintendo",
+    34: "A Book",
+    35: "Podcast",
+    36: "Edicola",
+    37: "Mobile"
+}
+MAGNET_STR = '<button onclick=\"location.href=\'magnet:?xt=urn:btih:{}\'\">m</button>'
+
+def handle_content(content: str):
+    return { x: content[idx] for idx, x in enumerate(HEADER) }
+    
+def search_keyword(content, keyword: str, category=0):
+    results = [ x for x in content if keyword.lower() in x['TITOLO'].lower()]
+    if category == 0:
+        return results
+    return [ x for x in results if category == x['CATEGORIA']]
+
+def get_last_torrents(content, page=1, amt=50):
+    tmp = sorted(content, key=lambda x:x['DATA'])
+    tmp_length = len(content)
+    
+    start_from = tmp_length - (amt * page)
+    end_with = tmp_length
+    return tmp[start_from:end_with]
+
+def load_content(input_path=INPUT_PATH):
+    with open(input_path, "r", encoding="utf-8") as in_file:
+        csv_iterator = csv.reader(in_file, quotechar='"', delimiter=',', quoting=csv.QUOTE_NONNUMERIC, skipinitialspace=True)
+        next(csv_iterator)
+        return [ handle_content(x) for x in csv_iterator ]
+
+def sizeof_fmt(num, suffix="B"):
+    for unit in ("", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei", "Zi"):
+        if abs(num) < 1024.0:
+            return f"{num:3.1f}{unit}{suffix}"
+        num /= 1024.0
+    return f"{num:.1f}Yi{suffix}"
+
+def parse_values(result):
+    tmp = result.copy()
+    tmp['DATA'] = datetime.strptime(tmp['DATA'], "%Y-%m-%dT%H:%M:%S").strftime("%d/%m/%Y")
+    tmp['CATEGORIA'] = CATEGORIE[int(tmp['CATEGORIA'])]
+    tmp['HASH'] = MAGNET_STR.format(tmp['HASH'])
+    tmp['DIMENSIONE'] = sizeof_fmt(tmp['DIMENSIONE'])
+    tmp['DESCRIZIONE'] = f"<span title='Autore: {tmp['AUTORE']}'>{tmp['DESCRIZIONE']}</span>"
+    return tmp
+
+def format_results(results, headers=HEADER):
+    print(len(results))
+    contents = [parse_values(x) for x in results]
+    return [[result[header] for header in headers] for result in contents]
+
+def get_args(args):
+    keywords = args.get("keywords") or ""
+    category = args.get("category") or 0
+    page = args.get("page") or 1
+    return keywords, category, page
+
+content = load_content()
+app = Flask(__name__)
+
+@app.route('/api/header')
+def route_api_header():
+    return json.dumps(HEADER)
+
+@app.route('/api')
+def route_api():
+    keywords, category, page = get_args(request.args)
+    results = search_keyword(content, keywords, int(category))
+    results = get_last_torrents(results, page=int(page))
+    return json.dumps(results)
+
+@app.route('/')
+def route_main():
+    keywords, category, page = get_args(request.args)
+    results = search_keyword(content, keywords, int(category))
+    results = get_last_torrents(results, page=int(page))
+    results = format_results(results, headers=TABLE_HEADER)
+    return render_template("index.html", headers=TABLE_HEADER, content=results, categories=CATEGORIE.items(), page=page)  
+
 if __name__ == '__main__':
-    print("Hello world")
+    app.run()
diff --git a/templates/index.html b/templates/index.html
new file mode 100644
index 0000000..bbcf28c
--- /dev/null
+++ b/templates/index.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html>
+<html>
+    <head>
+      <title>TNT Search</title>
+      <style>
+        .row {
+          margin-bottom: 40px;
+        }
+      </style>
+      <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-EVSTQN3/azprG1Anm3QDgpJLIm9Nao0Yz1ztcQTwFspd3yD65VohhpuuCOmLASjC" crossorigin="anonymous">
+    </head>
+    <body style="background-color: black; color: white;">
+      <div class="container mt-5">
+
+        <div class="row d-flex justify-content-center">
+          <div class="col-md-6">
+            <select class="form-select" id="form2" aria-label="Category" onchange="search_button()" data-bs-theme="dark">
+              <option selected value="0">Tutte</option>
+                {% for n_cat, cat in categories %}
+                <option value="{{ n_cat }}">{{ cat }}</option>
+                {% endfor %}
+            </select>
+          </div>
+          <div class="col-md-6">
+            <div class="input-group justify-content-center">
+              <div class="form-outline">
+                <input type="search" id="form1" class="form-control" />
+              </div>
+              <button type="button" class="btn btn-primary" onclick="search_button()">
+                Cerca
+                <!--
+                  <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="c="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-search"><circle cx="11" cy="11" r="8"></circle><line x1="21" y1="21" x2="16.65" y2="16.65"></line></svg>
+                -->
+              </button>
+            </div>
+          </div>
+        </div>
+        <div class="row d-flex justify-content-center">
+          <div class="col-md-4">
+            <a href="#" onclick="change_page(-1)">
+              Pagina precedente
+            </a>
+          </div>
+          <div class="col-md-4" style="text-align: center;" id="page_indicator">
+          </div>
+          <div class="col-md-4" style="text-align: right;">
+            <a href="#" onclick="change_page()">
+              Pagina successiva
+            </a>
+          </div>
+        </div>
+
+        <div class="row">
+          <table class="table table-striped table-dark">
+            <thead>
+              <tr>
+                {% for header in headers %}
+                <th scope="col">{{ header }}</th>
+                {% endfor %}
+              </tr>
+            </thead>
+            <tbody>
+              {% for row in content %}
+              <tr>
+                {% for el in row %}
+                <td>{{ el|safe }}</td>
+                {% endfor %}
+              </tr>
+              {% endfor %}
+            </tbody>
+          </table>
+        </div>
+      </div>
+        <script>
+          const search_field = document.getElementById("form1");
+          const category_field = document.getElementById("form2");
+          const page_indicator = document.getElementById("page_indicator");
+
+          const param_keywords = "keywords";
+          const param_category = "category";
+          const param_page = "page";
+
+          function get_query_info() {
+            const queryString = window.location.search;
+            const urlParams = new URLSearchParams(queryString);
+
+            const query = urlParams.has(param_keywords) ? urlParams.get(param_keywords) : "";
+            const category = urlParams.has(param_category) ? urlParams.get(param_category) : 0;
+            const page = urlParams.has(param_page) ? urlParams.get(param_page) : 1;
+
+            return [ query, parseInt(category), parseInt(page) ];
+          }
+
+          function search(query="", category=0, page=1) {
+            const params_temp = new URLSearchParams({"keywords": query, "category": category, "page": page});
+            const url = window.location.origin + window.location.pathname + "?" + params_temp.toString();
+            window.location.assign(url);
+          }
+
+          function search_button() {
+            const query = search_field.value;
+            const category = parseInt(category_field.value);
+            search(query, category, 1);
+          }
+
+          function change_page(increase = 1) {
+            page = result[2] + increase;
+
+            search(result[0], result[1], page > 0 ? page : 1);
+          }
+          
+          result = get_query_info();
+
+          search_field.value = result[0];
+          console.log(category_field.value, result[1]);
+          category_field.value = result[1];
+          console.log(category_field.value)
+          page_indicator.innerHTML = "Pagina " + result[2];
+          search_field.addEventListener("keyup", ({key}) => {
+            if (key === "Enter") {
+              search_button()
+            }
+          })
+        </script>
+        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.0.2/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
+    </body>
+</html>
\ No newline at end of file