initial commit
Marco Andronaco andronacomarco@gmail.com
Sun, 09 Jul 2023 15:43:42 +0200
3 files changed,
242 insertions(+),
1 deletions(-)
M
main.py
→
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()
A
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>