From 1c2827aee0cb2cd92d8456de274ecbf39994df64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3nos?= Date: Mon, 19 Sep 2022 22:29:13 +0200 Subject: [PATCH 1/8] Fix (#73) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Gli episodi che non hanno un numero assoluto vengono **scartati** _solo se_ la serie mappata sulla `Tabella Di Conversione` è `ABSOLUTE`. - Riorganizzazione del codice. --- Dockerfile | 4 +- config/app/api.py | 6 +- config/app/app.py | 5 +- .../{anime_downloader/__init__.py => job.py} | 25 +- config/logger.py | 80 ---- config/main.py | 64 +-- config/{ => other}/constants.py | 2 - .../{anime_downloader => other}/exceptions.py | 0 config/{ => other}/texts.py | 1 + config/utility.py | 399 ------------------ config/utility/classproperty.py | 5 + config/utility/connections.py | 103 +++++ .../functions.py | 23 +- config/utility/logger.py | 66 +++ config/utility/settings.py | 126 ++++++ .../{anime_downloader => utility}/sonarr.py | 11 +- config/utility/table.py | 174 ++++++++ documentation/examples/json/connections.json | 10 +- documentation/examples/json/settings.json | 2 +- documentation/examples/json/table.json | 11 +- 20 files changed, 567 insertions(+), 550 deletions(-) rename config/{anime_downloader/__init__.py => job.py} (87%) delete mode 100644 config/logger.py rename config/{ => other}/constants.py (87%) rename config/{anime_downloader => other}/exceptions.py (100%) rename config/{ => other}/texts.py (98%) delete mode 100644 config/utility.py create mode 100644 config/utility/classproperty.py create mode 100644 config/utility/connections.py rename config/{anime_downloader => utility}/functions.py (91%) create mode 100644 config/utility/logger.py create mode 100644 config/utility/settings.py rename config/{anime_downloader => utility}/sonarr.py (94%) create mode 100644 config/utility/table.py diff --git a/Dockerfile b/Dockerfile index ac56978..6daceb8 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,6 +8,8 @@ RUN export DEBIAN_FRONTEND=noninteractive; \ apt-get -y upgrade; \ apt-get -y install --no-install-recommends; \ apt-get -y install curl; \ + apt-get -y install ffmpeg; \ + apt-get -y install rtmpdump; \ apt-get -y install tzdata; \ apt-get -y install locales && locale-gen it_IT.UTF-8; \ apt-get clean; \ @@ -46,7 +48,7 @@ ENV PIP_ROOT_USER_ACTION ignore # USER dockeruser ENV USER_NAME dockeruser -ENV VERSION "1.8.1" +ENV VERSION "1.8.2" EXPOSE 5000 diff --git a/config/app/api.py b/config/app/api.py index f332142..b087ae8 100644 --- a/config/app/api.py +++ b/config/app/api.py @@ -3,8 +3,10 @@ import sys, os, json from .app import app -from utility import Table, Settings, Connections -from constants import SONARR_URL, API_KEY +from utility.table import Table +from utility.settings import Settings +from utility.connections import Connections +from other.constants import SONARR_URL, API_KEY @app.route('/api/table', methods=['GET']) diff --git a/config/app/app.py b/config/app/app.py index 1afcac3..72fe451 100644 --- a/config/app/app.py +++ b/config/app/app.py @@ -1,11 +1,12 @@ +from distutils.log import debug from flask import * from flask_socketio import * -from constants import VERSION, SONARR_URL, API_KEY +from other.constants import VERSION, SONARR_URL, API_KEY app = Flask(__name__) app.config['SECRET_KEY'] = 'secret!' -socketio = SocketIO(app, logger=False, async_handlers=False) +socketio = SocketIO(app, logger=True, async_handlers=False) @app.route('/index') @app.route('/') diff --git a/config/anime_downloader/__init__.py b/config/job.py similarity index 87% rename from config/anime_downloader/__init__.py rename to config/job.py index 5777e7a..59ab4e5 100644 --- a/config/anime_downloader/__init__.py +++ b/config/job.py @@ -4,14 +4,15 @@ import animeworld as aw import os -import texts as txt -from logger import logger, message -from constants import DOWNLOAD_FOLDER, VERSION -from utility import Settings +import other.texts as txt +import utility.functions as fun +from utility.logger import logger, message +from utility.settings import Settings +from utility import sonarr +from other.constants import DOWNLOAD_FOLDER, VERSION +from other.exceptions import UnauthorizedSonarr + -from .functions import converting, fixEps, movefile, downloadProgress, getLatestVersion -from . import sonarr -from .exceptions import UnauthorizedSonarr def job(): """ @@ -19,7 +20,7 @@ def job(): """ logger.warning('\n' + txt.START_BLOCK_LOG.format(time=time.strftime('%d %b %Y %H:%M:%S')) + '\n') - latest_version_container = getLatestVersion() + latest_version_container = fun.getLatestVersion() if VERSION != latest_version_container: logger.info('\n' + txt.UPDATE_CONTAINER.format(version=latest_version_container)) @@ -27,7 +28,7 @@ def job(): try: raw_series = sonarr.getMissingEpisodes() if len(raw_series)!=0: - series = converting(raw_series) + series = fun.converting(raw_series) for anime in series: for season in anime["seasons"]: @@ -39,7 +40,7 @@ def job(): logger.info(txt.EPISODE_RESEARCH_LOG.format(episode=", ".join([x["num"] for x in season["episodes"]])) + '\n') - episodi = fixEps([x.getEpisodes() for x in results]) + episodi = fun.fixEps([x.getEpisodes() for x in results]) for episode in season["episodes"]: logger.info('\n' + txt.CHECK_EPISODE_AVAILABILITY_LOG.format(season=episode["season"], episode=episode["num"]) + '\n') @@ -52,14 +53,14 @@ def job(): title = f'{anime["title"]} - S{episode["season"]}E{episode["num"]}' - file = ep.download(title, DOWNLOAD_FOLDER, downloadProgress) + file = ep.download(title, DOWNLOAD_FOLDER, fun.downloadProgress) if file: logger.info(txt.DOWNLOAD_COMPLETED_LOG + '\n') if Settings.data["MoveEp"]: logger.info(txt.EPISODE_SHIFT_LOG.format(season=episode["season"], episode=episode["num"], folder=anime["path"]) + '\n') - if movefile(os.path.join(DOWNLOAD_FOLDER,file), anime["path"]): + if fun.movefile(os.path.join(DOWNLOAD_FOLDER,file), anime["path"]): logger.info(txt.EPISODE_SHIFT_DONE_LOG + '\n') logger.info(txt.ANIME_REFRESH_LOG.format(anime=anime["title"]) + '\n') diff --git a/config/logger.py b/config/logger.py deleted file mode 100644 index 4d4ee7e..0000000 --- a/config/logger.py +++ /dev/null @@ -1,80 +0,0 @@ -import logging.config - -from utility import Settings, Connections -from app import socketio -import requests -import time -import os -import subprocess - - -class MySocketHandler(logging.handlers.SocketHandler): - def __init__(self, host='localhost', port=None): - super().__init__(host=host, port=port) - - self.last_pos = 0 - - def emit(self, record): - socketio.emit("log", record.msg) - -class ConnectionsHandler(logging.StreamHandler): - def emit(self, record): - for connection in Connections.data: - if connection["active"]: - script = os.path.join("connections", connection["script"]) - if os.path.isfile(script): - subprocess.check_call([script, record.msg], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) - -logging.config.dictConfig({ - 'version': 1, - 'disable_existing_loggers': False, - 'formatters': { - 'default_formatter': { - 'format': '%(message)s' - }, - }, - 'handlers': { - 'stream_handler': { - 'class': 'logging.StreamHandler', - 'formatter': 'default_formatter', - '.': { - 'terminator': '' - } - }, - 'file_handler': { - 'class': 'logging.FileHandler', - 'formatter': 'default_formatter', - 'filename': 'log.log', - 'mode': 'w+', - '.': { - 'terminator': '' - } - }, - 'socket_handler': { - 'class': 'logger.MySocketHandler', - 'formatter': 'default_formatter', - '.': { - 'terminator': '' - } - }, - 'connections_handler':{ - 'class': 'logger.ConnectionsHandler' - } - - }, - 'loggers': { - 'mylogger': { - 'handlers': ['stream_handler','file_handler', 'socket_handler'], - 'level': Settings.data["LogLevel"], - 'propagate': True - }, - 'message': { - 'handlers': ['connections_handler'], - 'level': Settings.data["LogLevel"], - 'propagate': True - } - } -}) - -logger = logging.getLogger('mylogger') -message = logging.getLogger('message') \ No newline at end of file diff --git a/config/main.py b/config/main.py index 311cc08..383dd60 100644 --- a/config/main.py +++ b/config/main.py @@ -5,68 +5,68 @@ import threading import requests -from logger import logger, message -from constants import SONARR_URL, API_KEY, VERSION -import texts as txt -from utility import Settings -from anime_downloader import job + +from other.constants import SONARR_URL, API_KEY, VERSION +import other.texts as txt +from utility.settings import Settings +import utility.logger as log +from job import job from app import app def main(): - logger.warning(txt.START_LOG.format(time=time.strftime('%d %b %Y %H:%M:%S'), version=VERSION) + '\n') + log.logger.warning(txt.START_LOG.format(time=time.strftime('%d %b %Y %H:%M:%S'), version=VERSION) + '\n') Settings.refresh = refresh # aggiornamento metodo di Settings per il refresh delle impostazioni + if SONARR_URL is None: - logger.warning(txt.SONARR_URL_ERROR_LOG + '\n') + log.logger.warning(txt.SONARR_URL_ERROR_LOG + '\n') else: - logger.info(txt.SONARR_URL_CHECK_LOG.format(sonar_url=SONARR_URL) + '\n') + log.logger.info(txt.SONARR_URL_CHECK_LOG.format(sonar_url=SONARR_URL) + '\n') if API_KEY is None: - logger.warning(txt.API_KEY_ERROR_LOG + '\n') + log.logger.warning(txt.API_KEY_ERROR_LOG + '\n') else: - logger.info(txt.API_KEY_CHECK_LOG.format(api_key=API_KEY) + '\n') + log.logger.info(txt.API_KEY_CHECK_LOG.format(api_key=API_KEY) + '\n') if None not in (SONARR_URL, API_KEY): - logger.info('\n' + txt.SETTINGS_SCAN_DELAY_LOG.format(delay= Settings.data['ScanDelay']) + '\n') - logger.info(txt.SETTINGS_MOVE_EPISODE_LOG.format(status='ON' if Settings.data['MoveEp'] else 'OFF') + '\n') - logger.info(txt.SETTINGS_RENAME_EPISODE_LOG.format(status='ON' if Settings.data['RenameEp'] else 'OFF') + '\n') - logger.info(txt.SETTINGS_AUTO_BIND_LINK_LOG.format(status='ON' if Settings.data['AutoBind'] else 'OFF') + '\n') - logger.info(txt.SETTINGS_LOG_LEVEL_LOG.format(level=Settings.data['LogLevel']) + '\n') + log.logger.info('\n' + txt.SETTINGS_SCAN_DELAY_LOG.format(delay= Settings.data['ScanDelay']) + '\n') + log.logger.info(txt.SETTINGS_MOVE_EPISODE_LOG.format(status='ON' if Settings.data['MoveEp'] else 'OFF') + '\n') + log.logger.info(txt.SETTINGS_RENAME_EPISODE_LOG.format(status='ON' if Settings.data['RenameEp'] else 'OFF') + '\n') + log.logger.info(txt.SETTINGS_AUTO_BIND_LINK_LOG.format(status='ON' if Settings.data['AutoBind'] else 'OFF') + '\n') + log.logger.info(txt.SETTINGS_LOG_LEVEL_LOG.format(level=Settings.data['LogLevel']) + '\n') - logger.info('\n' + txt.START_SERVER_LOG + '\n') + log.logger.info('\n' + txt.START_SERVER_LOG + '\n') job_thread = threading.Thread(target=server) job_thread.start() schedule.every(Settings.data["ScanDelay"]).minutes.do(job).run() # Fa una prima esecuzione e poi lo imposta per la ripetizione periodica +# Refresh settings ########################################### - - -@classmethod def refresh(self, silent=False): """ Aggiorna le impostazioni a livello globale. """ if not silent: - logger.info(txt.SEPARATOR_LOG + '\n') - logger.info(txt.SETTINGS_UPDATED_LOG + '\n') + log.logger.info(txt.SEPARATOR_LOG + '\n') + log.logger.info(txt.SETTINGS_UPDATED_LOG + '\n') - logger.info('\n' + txt.SETTINGS_SCAN_DELAY_LOG.format(delay= Settings.data['ScanDelay']) + '\n') - logger.info(txt.SETTINGS_MOVE_EPISODE_LOG.format(status='ON' if Settings.data['MoveEp'] else 'OFF') + '\n') - logger.info(txt.SETTINGS_RENAME_EPISODE_LOG.format(status='ON' if Settings.data['RenameEp'] else 'OFF') + '\n') - logger.info(txt.SETTINGS_AUTO_BIND_LINK_LOG.format(status='ON' if Settings.data['AutoBind'] else 'OFF') + '\n') - logger.info(txt.SETTINGS_LOG_LEVEL_LOG.format(level=Settings.data['LogLevel']) + '\n') + log.logger.info('\n' + txt.SETTINGS_SCAN_DELAY_LOG.format(delay= self.data['ScanDelay']) + '\n') + log.logger.info(txt.SETTINGS_MOVE_EPISODE_LOG.format(status='ON' if self.data['MoveEp'] else 'OFF') + '\n') + log.logger.info(txt.SETTINGS_RENAME_EPISODE_LOG.format(status='ON' if self.data['RenameEp'] else 'OFF') + '\n') + log.logger.info(txt.SETTINGS_AUTO_BIND_LINK_LOG.format(status='ON' if self.data['AutoBind'] else 'OFF') + '\n') + log.logger.info(txt.SETTINGS_LOG_LEVEL_LOG.format(level=self.data['LogLevel']) + '\n') - logger.info(txt.SEPARATOR_LOG + '\n') + log.logger.info(txt.SEPARATOR_LOG + '\n') - logger.setLevel(Settings.data["LogLevel"]) - message.setLevel(Settings.data["LogLevel"]) + log.logger.setLevel(self.data["LogLevel"]) + log.message.setLevel(self.data["LogLevel"]) schedule.clear() - schedule.every(Settings.data["ScanDelay"]).minutes.do(job) - - + schedule.every(self.data["ScanDelay"]).minutes.do(job) + +############################################################################################# def server(): diff --git a/config/constants.py b/config/other/constants.py similarity index 87% rename from config/constants.py rename to config/other/constants.py index 9a39482..f50f075 100644 --- a/config/constants.py +++ b/config/other/constants.py @@ -1,7 +1,5 @@ import os -from utility import Settings - SONARR_URL = os.getenv('SONARR_URL') # Indirizzo ip + porta di sonarr API_KEY = os.getenv('API_KEY') # Chiave api di sonarr VERSION = os.getenv('VERSION') # versione diff --git a/config/anime_downloader/exceptions.py b/config/other/exceptions.py similarity index 100% rename from config/anime_downloader/exceptions.py rename to config/other/exceptions.py diff --git a/config/texts.py b/config/other/texts.py similarity index 98% rename from config/texts.py rename to config/other/texts.py index a1854ff..6b3d0ab 100644 --- a/config/texts.py +++ b/config/other/texts.py @@ -40,6 +40,7 @@ END_BLOCK_LOG = "\033[90m╰───────────────────────────────────「{time}」───────────────────────────────────╯\033[0m" ANIME_REJECTED_LOG = "⁉️ Serie '{anime}' S{season} scartata per mancanza di informazioni." +EPISODE_REJECTED_LOG = "⁉️ Episodio '{anime}' S{season}E{episode} scartato per mancanza di numerazione assoluta." CONNECTION_ERROR_LOG = "⚠️ Errore di connessione. ({res_error})" diff --git a/config/utility.py b/config/utility.py deleted file mode 100644 index 5faba89..0000000 --- a/config/utility.py +++ /dev/null @@ -1,399 +0,0 @@ -from typing import Callable, Dict, List, Optional, Union - -import json -import os -import requests - -class classproperty(object): - def __init__(self, fget): - self.fget = fget - def __get__(self, owner_self, owner_cls): - return self.fget(owner_cls) - -class Settings: - file = "json/settings.json" - tmp = None # variabile temporanea contenente le impostazioni, in caso di cambiamenti ritorna a None - - @classproperty - def data(self) -> Dict[str,str]: - """ - Legge le impostazione dal file `json/settings.json`. - - ``` - return { - "LogLevel": "DEBUG", - "RenameEp": True, - "MoveEp": True, - "ScanDelay": 30, - "AutoBind": False - } - ``` - """ - - if self.tmp is not None: return self.tmp - - data = { - "LogLevel": "DEBUG", - "RenameEp": True, - "MoveEp": True, - "ScanDelay": 30, - "AutoBind": False - } - - update_fix = False - - settings = {} - - if os.path.exists(self.file): - with open(self.file, 'r') as f: - settings = json.loads(f.read()) - - for info in data: - if info not in settings: - settings[info] = data[info] - update_fix = True - - if settings["ScanDelay"] < 30 : settings["ScanDelay"] = 30 - - else: - settings = data - update_fix = True - - if update_fix: - self.write(settings) - - self.tmp = settings - - return settings - - @classmethod - def update(self, AutoBind:Optional[bool], LogLevel:Optional[str], MoveEp:Optional[bool], RenameEp:Optional[bool], ScanDelay:Optional[int]) -> str: - settings = self.data - - log = "Nessuna modifica avvenuta." # messaggio - - if AutoBind is not None: - settings["AutoBind"] = AutoBind - log = "Auto Ricerca Link aggiornato." - if LogLevel is not None: - settings["LogLevel"] = LogLevel - log = "Livello del Log aggiornato." - if MoveEp is not None: - settings["MoveEp"] = MoveEp - log = "Sposta Episodi aggiornato." - if RenameEp is not None: - settings["RenameEp"] = RenameEp - log = "Rinomina Episodi aggiornato." - if ScanDelay is not None: - settings["ScanDelay"] = ScanDelay - log = "Intervallo Scan aggiornato." - - self.write(settings) - - self.tmp = None # invalida il tmp - self.refresh() - - return log - - @classmethod - def isValid(self, settings:Dict[str,str]) -> bool: - """ - Controlla se le informazioni sono valide. - """ - - if "AutoBind" not in settings: return False - if "LogLevel" not in settings: return False - if "MoveEp" not in settings: return False - if "RenameEp" not in settings: return False - if "ScanDelay" not in settings: return False - - return True - - - @classmethod - def write(self, data:Dict[str,str]): - """ - Sovrascrive le impostazioni con le nuove informazioni. - """ - if self.isValid(data): - with open(self.file, 'w') as f: - f.write(json.dumps(data, indent=4)) - return True - else: - return False - - @classmethod - def refresh(self, silent=False): - """ - Aggiorna le impostazioni a livello globale. - """ - # Funzione inizializzata nel main per problemi di circular import - pass - - -class Table: - file = "json/table.json" - - @classproperty - def data(self) -> List[Dict]: - """ - Lista di dizionari contenente tutta la tabella di conversione. - """ - if not os.path.exists(self.file): - self.write([]) - return [] - - - with open(self.file, 'r') as f: - return json.loads(f.read()) - - @classmethod - def append(self, data: Dict) -> str: - """ - Aggiunge un nuovo anime o delle informazioni. - """ - table = self.data - - log = "" # messaggio - - - for anime in table: - if data["title"] == anime["title"]: # Se esiste già l'anime nella tabella - if data["season"] in anime["seasons"]: # Se esiste già la stagione - for link in data["links"]: - if link not in anime["seasons"][data["season"]]: # Se il link non è già presente - anime["seasons"][data["season"]].append(link) # aggiunge un'altro link - log = "Nuovo link aggiunto." - else: - log = "Il link è già presente." - break - else: - log = f"La stagione {data['season']} è già presente." - else: - if not anime["absolute"] and not data["absolute"]: # Se la numerazione non è assoluta - anime["seasons"][data["season"]] = list(data["links"]) # inizializza una nuova stagione - log = f"Stagione {data['season']} di {data['title']} aggiunta." - else: - log = "Impossibile aggiungere una stagione se la numerazione è assoluta." - break - else: # se non è stato trovato nessun anime - table.append({ - "title": data["title"], - "seasons": {data["season"] if not data["absolute"] else "absolute": data["links"]}, - "absolute": data["absolute"] - }) - log = f"{data['title']} aggiunto." - - table.sort(key=lambda s: s["title"]) - self.write(table) - - return log - - @classmethod - def remove(self, title:str, season:str=None, link:str=None) -> str: - """ - Rimuove un anime o delle informazioni. - """ - - table = self.data - - log = "" # messaggio - - for anime in table: - if anime["title"] == title: - if not (season or link): - table.remove(anime) - log = f"{title} rimosso." - break - else: - for seasonIndex in anime["seasons"]: - if seasonIndex == str(season): - if not link: - anime["seasons"].pop(seasonIndex) - log = f"Stagione {season} di {title} rimossa." - break - else: - for url in anime["seasons"][seasonIndex]: - if url == link: - anime["seasons"][seasonIndex].remove(url) - log = "Link rimosso." - break - else: - log = "Link inesistente." - break - else: - log = f"Stagione {season} di {title} inesistente." - break - else: - log = f"{title} inesistente." - - self.write(table) - return log - - @classmethod - def edit(self, title:Union[str,list], season:Union[str,list]=None, link:list=None) -> str: - """ - modifica un anime o delle informazioni. - """ - - table = self.data - - edit = None # valore da editare - - log = "" # messaggio - - for anime in table: - if isinstance(title, list): - if anime["title"] == title[0]: - anime["title"] = title[1] - log = f"{title[0]} modificato." - break - else: - if anime["title"] == title: - for seasonIndex in anime["seasons"]: - if isinstance(season, list): - if seasonIndex == str(season[0]): - anime["seasons"][season[1]] = anime["seasons"].pop(seasonIndex) - anime["absolute"] = False - log = f"Stagione {season[0]} di {title} modificata." - break - else: - for index, url in enumerate(anime["seasons"][seasonIndex]): - if url == link[0]: - anime["seasons"][seasonIndex][index] = link[1] - log = "Link modificato." - break - else: - log = "Link inesistente." - break - else: - log = f"Stagione {season[0] if isinstance(season, list) else season} di {title} inesistente." - break - else: - log = f"{title[0] if isinstance(title, list) else title} inesistente." - - self.write(table) - return log - - @classmethod - def isValid(self, table:List) -> bool: - """ - Controlla se le informazioni sono valide. - """ - for anime in table: - if "absolute" not in anime: return False - if "seasons" not in anime: return False - if "title" not in anime: return False - - return True - - @classmethod - def write(self, table:List): - """ - Sovrascrive la tabella con le nuove informazioni. - """ - if self.isValid(table): - with open(self.file, 'w') as f: - f.write(json.dumps(table, indent=4)) - - return True - else: - return False - -class Connections: - file = "json/connections.json" - - @classproperty - def data(self) -> List[Dict]: - """ - Lista di dizionari contenente tutte le informazioni. - """ - if not os.path.exists(self.file): - self.write([]) - return [] - - - with open(self.file, 'r') as f: - return json.loads(f.read()) - - @classmethod - def toggle(self, connection_name:str): - - log = f"La Connection {connection_name} è stata " - - connections = self.data - - for connection in connections: - if connection["name"] == connection_name: - file = os.path.join("connections", connection["script"]) - if os.path.isfile(file) or connection["active"]: - log += "spenta." if connection["active"] else "accesa." - connection["active"] = not connection["active"] - break - else: - return f"Il file {connection_name} non esiste." - else: - return f"Non è stato trovato nessuna Connection con il nome {connection_name}." - - self.write(connections) - return log - - @classmethod - def remove(self, connection_name:str): - - conn = None - connections = self.data - - for connection in connections: - if connection["name"] == connection_name: - conn = connection - break - else: - return f"Non è stato trovato nessuna Connection con il nome {connection_name}." - - connections.remove(conn) - - self.write(connections) - return f"La Connection {connection_name} è stata rimossa." - - @classmethod - def add(self, name:str, script:str, active:bool): - if self.isValid(name): - connections = self.data - connections.append({ - "name": name, - "script": script, - "active": active - }) - self.write(connections) - return f"La Connection {name} è stata aggiunta." - else: - return f"È gia presente una Connection con il nome {name}." - - - @classmethod - def isValid(self, name:str): - for connection in self.data: - if connection["name"] == name: - return False - else: - return True - - @classmethod - def write(self, connections:List): - """ - Sovrascrive le Connections con le nuove informazioni. - """ - - for i in range(len(connections)): - for j in range(len(connections)): - if i==j: continue - - if connections[i]["name"] == connections[j]["name"]: return False - - with open(self.file, 'w') as f: - f.write(json.dumps(connections, indent=4)) - - return True - - diff --git a/config/utility/classproperty.py b/config/utility/classproperty.py new file mode 100644 index 0000000..a1987c9 --- /dev/null +++ b/config/utility/classproperty.py @@ -0,0 +1,5 @@ +class classproperty(object): + def __init__(self, fget): + self.fget = fget + def __get__(self, owner_self, owner_cls): + return self.fget(owner_cls) \ No newline at end of file diff --git a/config/utility/connections.py b/config/utility/connections.py new file mode 100644 index 0000000..1d46f0f --- /dev/null +++ b/config/utility/connections.py @@ -0,0 +1,103 @@ +from typing import Dict, List +import json +import os + +from .classproperty import classproperty + +class Connections: + file = "json/connections.json" + + @classproperty + def data(self) -> List[Dict]: + """ + Lista di dizionari contenente tutte le informazioni. + """ + if not os.path.exists(self.file): + self.write([]) + return [] + + + with open(self.file, 'r') as f: + return json.loads(f.read()) + + @classmethod + def toggle(self, connection_name:str): + + log = f"La Connection {connection_name} è stata " + + connections = self.data + + for connection in connections: + if connection["name"] == connection_name: + file = os.path.join("connections", connection["script"]) + if os.path.isfile(file) or connection["active"]: + log += "spenta." if connection["active"] else "accesa." + connection["active"] = not connection["active"] + break + else: + return f"Il file {connection_name} non esiste." + else: + return f"Non è stato trovato nessuna Connection con il nome {connection_name}." + + self.write(connections) + return log + + @classmethod + def remove(self, connection_name:str): + + conn = None + connections = self.data + + for connection in connections: + if connection["name"] == connection_name: + conn = connection + break + else: + return f"Non è stato trovato nessuna Connection con il nome {connection_name}." + + connections.remove(conn) + + self.write(connections) + return f"La Connection {connection_name} è stata rimossa." + + @classmethod + def add(self, name:str, script:str, active:bool): + if self.isValid(name): + connections = self.data + connections.append({ + "name": name, + "script": script, + "active": active + }) + self.write(connections) + return f"La Connection {name} è stata aggiunta." + else: + return f"È gia presente una Connection con il nome {name}." + + + @classmethod + def isValid(self, name:str): + for connection in self.data: + if connection["name"] == name: + return False + else: + return True + + @classmethod + def write(self, connections:List): + """ + Sovrascrive le Connections con le nuove informazioni. + """ + + for i in range(len(connections)): + for j in range(len(connections)): + if i==j: continue + + if connections[i]["name"] == connections[j]["name"]: return False + + with open(self.file, 'w') as f: + f.write(json.dumps(connections, indent=4)) + + return True + + diff --git a/config/anime_downloader/functions.py b/config/utility/functions.py similarity index 91% rename from config/anime_downloader/functions.py rename to config/utility/functions.py index dc429da..efd7ca1 100644 --- a/config/anime_downloader/functions.py +++ b/config/utility/functions.py @@ -8,13 +8,14 @@ from datetime import datetime from typing import Dict, List -from utility import Table + from app import socketio -from logger import logger, message -from utility import Settings -import texts as txt -from .exceptions import TableFormattingError +from .table import Table +from .logger import logger, message +from .settings import Settings +import other.texts as txt +from other.exceptions import TableFormattingError def converting(data:List[Dict]) -> List[Dict]: """ @@ -90,11 +91,17 @@ def linkSearch(title: str, season: str, tvdbID:int) -> str: except IndexError: # finche esistono break else: - season_absolute["episodes"].extend(season["episodes"]) # compatta ogni episodio in un una stagione - anime["seasons"].append(season_absolute) + for episode in season["episodes"]: # controlla che ogni episodio della stagione abbia la numerazione assoluta + if episode["abs"]: + season_absolute["episodes"].append(episode) # aggiunge ogni episodio nella stagione ABSOLUTE + else: + logger.debug(txt.EPISODE_REJECTED_LOG.format(anime=anime["title"], season=season["num"], episode=episode["num"]) + '\n') + + if len(season_absolute["episodes"]) != 0: + anime["seasons"].append(season_absolute) break else: - # La serie risolta con ordinamento assoluto ma non esiste la stagione "absolute" nella tabella + # La serie risulta con ordinamento assoluto ma non esiste la stagione "absolute" nella tabella logger.debug(txt.SEASON_INEXISTENT_LOG.format(season=", ".join([x["num"] for x in anime["seasons"]]), anime=anime["title"]) + '\n') if Settings.data["AutoBind"]: # ricerca automatica links diff --git a/config/utility/logger.py b/config/utility/logger.py new file mode 100644 index 0000000..700d7d6 --- /dev/null +++ b/config/utility/logger.py @@ -0,0 +1,66 @@ +import logging, logging.handlers +import requests +import time +import os +import subprocess + + +from app import socketio +import utility.settings as sett +import utility.connections as conn + + +class MySocketHandler(logging.handlers.SocketHandler): + def __init__(self, host='localhost', port=None): + super().__init__(host=host, port=port) + + self.last_pos = 0 + + def emit(self, record): + socketio.emit("log", record.msg) + +class MyConnectionsHandler(logging.StreamHandler): + def emit(self, record): + for connection in conn.Connections.data: + if connection["active"]: + script = os.path.join("connections", connection["script"]) + if os.path.isfile(script): + subprocess.check_call([script, record.msg], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) + +# FORMATTERS ########################################### + +default_formatter = logging.Formatter('%(message)s') + +# HANDLERS ########################################### + +stream_handler = logging.StreamHandler() +stream_handler.terminator = '' +stream_handler.setFormatter(default_formatter) + +file_handler = logging.FileHandler(filename='log.log') +file_handler.terminator = '' +file_handler.setFormatter(default_formatter) + +socket_handler = MySocketHandler() +socket_handler.terminator = '' +socket_handler.setFormatter(default_formatter) + +connections_handler = MyConnectionsHandler() +connections_handler.terminator = '' +connections_handler.setFormatter(default_formatter) + +# LOGGERS ########################################### + +logger = logging.getLogger('mylogger') +logger.addHandler(stream_handler) +logger.addHandler(file_handler) +logger.addHandler(socket_handler) +logger.setLevel(sett.Settings.data["LogLevel"]) +logger.propagate = True + +message = logging.getLogger('message') +message.addHandler(connections_handler) +message.setLevel(sett.Settings.data["LogLevel"]) +message.propagate = True + + diff --git a/config/utility/settings.py b/config/utility/settings.py new file mode 100644 index 0000000..0fe53ed --- /dev/null +++ b/config/utility/settings.py @@ -0,0 +1,126 @@ +from typing import Dict, Optional +import os +import json + +from utility.classproperty import classproperty + +class Settings: + file = "json/settings.json" + tmp = None # variabile temporanea contenente le impostazioni, in caso di cambiamenti ritorna a None + + @classproperty + def data(self) -> Dict[str,str]: + """ + Legge le impostazione dal file `json/settings.json`. + + ``` + return { + "LogLevel": "DEBUG", + "RenameEp": True, + "MoveEp": True, + "ScanDelay": 30, + "AutoBind": False + } + ``` + """ + + if self.tmp is not None: return self.tmp + + data = { + "LogLevel": "DEBUG", + "RenameEp": True, + "MoveEp": True, + "ScanDelay": 30, + "AutoBind": False + } + + update_fix = False + + settings = {} + + if os.path.exists(self.file): + with open(self.file, 'r') as f: + settings = json.loads(f.read()) + + for info in data: + if info not in settings: + settings[info] = data[info] + update_fix = True + + if settings["ScanDelay"] < 30 : settings["ScanDelay"] = 30 + + else: + settings = data + update_fix = True + + if update_fix: + self.write(settings) + + self.tmp = settings + + return settings + + @classmethod + def update(self, AutoBind:Optional[bool], LogLevel:Optional[str], MoveEp:Optional[bool], RenameEp:Optional[bool], ScanDelay:Optional[int]) -> str: + settings = self.data + + log = "Nessuna modifica avvenuta." # messaggio + + if AutoBind is not None: + settings["AutoBind"] = AutoBind + log = "Auto Ricerca Link aggiornato." + if LogLevel is not None: + settings["LogLevel"] = LogLevel + log = "Livello del Log aggiornato." + if MoveEp is not None: + settings["MoveEp"] = MoveEp + log = "Sposta Episodi aggiornato." + if RenameEp is not None: + settings["RenameEp"] = RenameEp + log = "Rinomina Episodi aggiornato." + if ScanDelay is not None: + settings["ScanDelay"] = ScanDelay + log = "Intervallo Scan aggiornato." + + self.write(settings) + + self.tmp = None # invalida il tmp + self.refresh(self) + return log + + @classmethod + def refresh(self, silent=False): + """ + Aggiorna le impostazioni a livello globale. + """ + # Funzione inizializzata in main per problemi di circular import + pass + + @classmethod + def isValid(self, settings:Dict[str,str]) -> bool: + """ + Controlla se le informazioni sono valide. + """ + + if "AutoBind" not in settings: return False + if "LogLevel" not in settings: return False + if "MoveEp" not in settings: return False + if "RenameEp" not in settings: return False + if "ScanDelay" not in settings: return False + + return True + + + @classmethod + def write(self, data:Dict[str,str]): + """ + Sovrascrive le impostazioni con le nuove informazioni. + """ + if self.isValid(data): + with open(self.file, 'w') as f: + f.write(json.dumps(data, indent=4)) + return True + else: + return False + + \ No newline at end of file diff --git a/config/anime_downloader/sonarr.py b/config/utility/sonarr.py similarity index 94% rename from config/anime_downloader/sonarr.py rename to config/utility/sonarr.py index 9fed6ee..e7900dc 100644 --- a/config/anime_downloader/sonarr.py +++ b/config/utility/sonarr.py @@ -2,10 +2,10 @@ import requests import time -from logger import logger -from constants import SONARR_URL, API_KEY -import texts as txt -from .exceptions import UnauthorizedSonarr +from .logger import logger +from other.constants import SONARR_URL, API_KEY +import other.texts as txt +from other.exceptions import UnauthorizedSonarr def getMissingEpisodes() -> List[Dict]: """ @@ -69,9 +69,10 @@ def addData(): if anime["ID"] == record["seriesId"]: for season in anime["seasons"]: if season["num"] == str(record["seasonNumber"]): + season["episodes"].append({ "num": str(record["episodeNumber"]), - "abs": str(record["absoluteEpisodeNumber"]), + "abs": str(record["absoluteEpisodeNumber"]) if "absoluteEpisodeNumber" in record else None, "season": str(record["seasonNumber"]), "title": record["title"], "ID": record["id"] diff --git a/config/utility/table.py b/config/utility/table.py new file mode 100644 index 0000000..5f25fca --- /dev/null +++ b/config/utility/table.py @@ -0,0 +1,174 @@ +from typing import Dict, List, Union +import os +import json + +from .classproperty import classproperty + +class Table: + file = "json/table.json" + + @classproperty + def data(self) -> List[Dict]: + """ + Lista di dizionari contenente tutta la tabella di conversione. + """ + if not os.path.exists(self.file): + self.write([]) + return [] + + + with open(self.file, 'r') as f: + return json.loads(f.read()) + + @classmethod + def append(self, data: Dict) -> str: + """ + Aggiunge un nuovo anime o delle informazioni. + """ + table = self.data + + log = "" # messaggio + + + for anime in table: + if data["title"] == anime["title"]: # Se esiste già l'anime nella tabella + if data["season"] in anime["seasons"]: # Se esiste già la stagione + for link in data["links"]: + if link not in anime["seasons"][data["season"]]: # Se il link non è già presente + anime["seasons"][data["season"]].append(link) # aggiunge un'altro link + log = "Nuovo link aggiunto." + else: + log = "Il link è già presente." + break + else: + log = f"La stagione {data['season']} è già presente." + else: + if not anime["absolute"] and not data["absolute"]: # Se la numerazione non è assoluta + anime["seasons"][data["season"]] = list(data["links"]) # inizializza una nuova stagione + log = f"Stagione {data['season']} di {data['title']} aggiunta." + else: + log = "Impossibile aggiungere una stagione se la numerazione è assoluta." + break + else: # se non è stato trovato nessun anime + table.append({ + "title": data["title"], + "seasons": {data["season"] if not data["absolute"] else "absolute": data["links"]}, + "absolute": data["absolute"] + }) + log = f"{data['title']} aggiunto." + + table.sort(key=lambda s: s["title"]) + self.write(table) + + return log + + @classmethod + def remove(self, title:str, season:str=None, link:str=None) -> str: + """ + Rimuove un anime o delle informazioni. + """ + + table = self.data + + log = "" # messaggio + + for anime in table: + if anime["title"] == title: + if not (season or link): + table.remove(anime) + log = f"{title} rimosso." + break + else: + for seasonIndex in anime["seasons"]: + if seasonIndex == str(season): + if not link: + anime["seasons"].pop(seasonIndex) + log = f"Stagione {season} di {title} rimossa." + break + else: + for url in anime["seasons"][seasonIndex]: + if url == link: + anime["seasons"][seasonIndex].remove(url) + log = "Link rimosso." + break + else: + log = "Link inesistente." + break + else: + log = f"Stagione {season} di {title} inesistente." + break + else: + log = f"{title} inesistente." + + self.write(table) + return log + + @classmethod + def edit(self, title:Union[str,list], season:Union[str,list]=None, link:list=None) -> str: + """ + modifica un anime o delle informazioni. + """ + + table = self.data + + edit = None # valore da editare + + log = "" # messaggio + + for anime in table: + if isinstance(title, list): + if anime["title"] == title[0]: + anime["title"] = title[1] + log = f"{title[0]} modificato." + break + else: + if anime["title"] == title: + for seasonIndex in anime["seasons"]: + if isinstance(season, list): + if seasonIndex == str(season[0]): + anime["seasons"][season[1]] = anime["seasons"].pop(seasonIndex) + anime["absolute"] = False + log = f"Stagione {season[0]} di {title} modificata." + break + else: + for index, url in enumerate(anime["seasons"][seasonIndex]): + if url == link[0]: + anime["seasons"][seasonIndex][index] = link[1] + log = "Link modificato." + break + else: + log = "Link inesistente." + break + else: + log = f"Stagione {season[0] if isinstance(season, list) else season} di {title} inesistente." + break + else: + log = f"{title[0] if isinstance(title, list) else title} inesistente." + + self.write(table) + return log + + @classmethod + def isValid(self, table:List) -> bool: + """ + Controlla se le informazioni sono valide. + """ + for anime in table: + if "absolute" not in anime: return False + if "seasons" not in anime: return False + if "title" not in anime: return False + + return True + + @classmethod + def write(self, table:List): + """ + Sovrascrive la tabella con le nuove informazioni. + """ + if self.isValid(table): + with open(self.file, 'w') as f: + f.write(json.dumps(table, indent=4)) + + return True + else: + return False \ No newline at end of file diff --git a/documentation/examples/json/connections.json b/documentation/examples/json/connections.json index 6753971..2c999e7 100644 --- a/documentation/examples/json/connections.json +++ b/documentation/examples/json/connections.json @@ -1,7 +1,7 @@ [ - { - "name": "Telegram", - "script": "telegram.sh", - "active": true - } + { + "name": "Telegram", + "script": "telegram.sh", + "active": false + } ] \ No newline at end of file diff --git a/documentation/examples/json/settings.json b/documentation/examples/json/settings.json index 1f01ff6..0935e51 100644 --- a/documentation/examples/json/settings.json +++ b/documentation/examples/json/settings.json @@ -1,5 +1,5 @@ { - "AutoBind": false, + "AutoBind": true, "LogLevel": "DEBUG", "MoveEp": true, "RenameEp": true, diff --git a/documentation/examples/json/table.json b/documentation/examples/json/table.json index cc075c9..1c916ad 100644 --- a/documentation/examples/json/table.json +++ b/documentation/examples/json/table.json @@ -307,6 +307,15 @@ }, "title": "Log Horizon" }, + { + "title": "Made in Abyss", + "seasons": { + "2": [ + "https://www.animeworld.tv/play/made-in-abyss-2.T7h4U" + ] + }, + "absolute": false + }, { "absolute": false, "seasons": { @@ -367,7 +376,7 @@ "absolute": true, "seasons": { "absolute": [ - "https://www.animeworld.tv/play/one-piece-subita.sORn4" + "https://www.animeworld.tv/play/one-piece-subita.qzG-L" ] }, "title": "One Piece" From b103a0a780b5fb20bab305a08daaea89afdf1019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3nos?= Date: Tue, 4 Oct 2022 13:14:01 +0200 Subject: [PATCH 2/8] Create CONTRIBUTING.md --- CONTRIBUTING.md | 38 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..82b1f48 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,38 @@ +# Sviluppo + +È possibile compilare l'immagine tramite [docker cli](https://www.docker.com/) o [Visual Studio Code](https://code.visualstudio.com/), se volete debuggure il codice consiglio la seconda. + +### Docker CLI +Per cotruire il container: +```bash +docker build -t mainkronos/anime_downloader . +``` +- ⚠️ Il flag `-t` indica il tag del container. +- ⚠️ Il `.` NON è un errore di battitura, serve per indicare che il file `dockerfile` che contiene le istruzioni di compilazione si trova della directory corrente. + +Per avviare: +``` +docker run -d \ + --name=AnimeDownloader \ + -v /path/to/data:/script/json/ \ + -v /path/to/animeSeries:/tv \ + -v /path/to/downloads:/downloads \ + -v /path/to/connections:/script/connections \ + -p {port}:5000 \ + --env SONARR_URL='http://{url}:{port}' \ + --env API_KEY='1234567890abcdefghijklmn' \ + --env CHAT_ID=123456789 \ + --env BOT_TOKEN='123456789:ABCDEFGHIJKLM-abc_AbCdEfGhI12345678' \ + --env TZ=Europe/Rome \ + mainkronos/anime_downloader +``` +- ⚠️ L'ultima riga deve COINCIDERE con il tag (inserito con il flag `-t`) usato al comando precedente. + +### Visual Studio Code +Aprire la cartella del progetto in Visual Studio Code e modificate a vostro piacere il file [`tasks.json`](.vscode/tasks.json) + +- Per modificare i valori delle variabili d'ambiente cambiate [questi valori](.vscode/tasks.json#L16-L20) +- Per modificare la porta esterna del container cambiate [questo valore](.vscode/tasks.json#L25) +- Per modificare i volumi cambiate [questi valori](.vscode/tasks.json#L29-L36) + +E per avviare -> In Visual Studio Code -> `Esegui` -> `Avvia debug`. From 6e44509159e8b1c1ca3b4ebc5155fe915981c9a4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3nos?= Date: Tue, 4 Oct 2022 13:15:10 +0200 Subject: [PATCH 3/8] Update README.md --- README.md | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/README.md b/README.md index a0fb65b..33a01c6 100644 --- a/README.md +++ b/README.md @@ -194,43 +194,3 @@ Le _*frequently asked questions*_ si trovano [qui](FAQ.md). ## Star History ![Star History Chart](https://api.star-history.com/svg?repos=MainKronos/Sonarr-AnimeDownloader&type=Date) - - -## Sviluppo - -È possibile compilare l'immagine tramite [docker cli](https://www.docker.com/) o [Visual Studio Code](https://code.visualstudio.com/), se volete debuggure il codice consiglio la seconda. - -### Docker CLI -Per cotruire il container: -```bash -docker build -t mainkronos/anime_downloader . -``` -- ⚠️ Il flag `-t` indica il tag del container. -- ⚠️ Il `.` NON è un errore di battitura, serve per indicare che il file `dockerfile` che contiene le istruzioni di compilazione si trova della directory corrente. - -Per avviare: -``` -docker run -d \ - --name=AnimeDownloader \ - -v /path/to/data:/script/json/ \ - -v /path/to/animeSeries:/tv \ - -v /path/to/downloads:/downloads \ - -v /path/to/connections:/script/connections \ - -p {port}:5000 \ - --env SONARR_URL='http://{url}:{port}' \ - --env API_KEY='1234567890abcdefghijklmn' \ - --env CHAT_ID=123456789 \ - --env BOT_TOKEN='123456789:ABCDEFGHIJKLM-abc_AbCdEfGhI12345678' \ - --env TZ=Europe/Rome \ - mainkronos/anime_downloader -``` -- ⚠️ L'ultima riga deve COINCIDERE con il tag (inserito con il flag `-t`) usato al comando precedente. - -### Visual Studio Code -Aprire la cartella del progetto in Visual Studio Code e modificate a vostro piacere il file [`tasks.json`](.vscode/tasks.json) - -- Per modificare i valori delle variabili d'ambiente cambiate [questi valori](.vscode/tasks.json#L16-L20) -- Per modificare la porta esterna del container cambiate [questo valore](.vscode/tasks.json#L25) -- Per modificare i volumi cambiate [questi valori](.vscode/tasks.json#L29-L36) - -E per avviare -> In Visual Studio Code -> `Esegui` -> `Avvia debug`. From 16db57cb0ba9a2069bca9f3c674fcbe285283ade Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3nos?= Date: Tue, 4 Oct 2022 13:18:38 +0200 Subject: [PATCH 4/8] Create CODE_OF_CONDUCT.md --- CODE_OF_CONDUCT.md | 128 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 CODE_OF_CONDUCT.md diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md new file mode 100644 index 0000000..18c9147 --- /dev/null +++ b/CODE_OF_CONDUCT.md @@ -0,0 +1,128 @@ +# Contributor Covenant Code of Conduct + +## Our Pledge + +We as members, contributors, and leaders pledge to make participation in our +community a harassment-free experience for everyone, regardless of age, body +size, visible or invisible disability, ethnicity, sex characteristics, gender +identity and expression, level of experience, education, socio-economic status, +nationality, personal appearance, race, religion, or sexual identity +and orientation. + +We pledge to act and interact in ways that contribute to an open, welcoming, +diverse, inclusive, and healthy community. + +## Our Standards + +Examples of behavior that contributes to a positive environment for our +community include: + +* Demonstrating empathy and kindness toward other people +* Being respectful of differing opinions, viewpoints, and experiences +* Giving and gracefully accepting constructive feedback +* Accepting responsibility and apologizing to those affected by our mistakes, + and learning from the experience +* Focusing on what is best not just for us as individuals, but for the + overall community + +Examples of unacceptable behavior include: + +* The use of sexualized language or imagery, and sexual attention or + advances of any kind +* Trolling, insulting or derogatory comments, and personal or political attacks +* Public or private harassment +* Publishing others' private information, such as a physical or email + address, without their explicit permission +* Other conduct which could reasonably be considered inappropriate in a + professional setting + +## Enforcement Responsibilities + +Community leaders are responsible for clarifying and enforcing our standards of +acceptable behavior and will take appropriate and fair corrective action in +response to any behavior that they deem inappropriate, threatening, offensive, +or harmful. + +Community leaders have the right and responsibility to remove, edit, or reject +comments, commits, code, wiki edits, issues, and other contributions that are +not aligned to this Code of Conduct, and will communicate reasons for moderation +decisions when appropriate. + +## Scope + +This Code of Conduct applies within all community spaces, and also applies when +an individual is officially representing the community in public spaces. +Examples of representing our community include using an official e-mail address, +posting via an official social media account, or acting as an appointed +representative at an online or offline event. + +## Enforcement + +Instances of abusive, harassing, or otherwise unacceptable behavior may be +reported to the community leaders responsible for enforcement at +. +All complaints will be reviewed and investigated promptly and fairly. + +All community leaders are obligated to respect the privacy and security of the +reporter of any incident. + +## Enforcement Guidelines + +Community leaders will follow these Community Impact Guidelines in determining +the consequences for any action they deem in violation of this Code of Conduct: + +### 1. Correction + +**Community Impact**: Use of inappropriate language or other behavior deemed +unprofessional or unwelcome in the community. + +**Consequence**: A private, written warning from community leaders, providing +clarity around the nature of the violation and an explanation of why the +behavior was inappropriate. A public apology may be requested. + +### 2. Warning + +**Community Impact**: A violation through a single incident or series +of actions. + +**Consequence**: A warning with consequences for continued behavior. No +interaction with the people involved, including unsolicited interaction with +those enforcing the Code of Conduct, for a specified period of time. This +includes avoiding interactions in community spaces as well as external channels +like social media. Violating these terms may lead to a temporary or +permanent ban. + +### 3. Temporary Ban + +**Community Impact**: A serious violation of community standards, including +sustained inappropriate behavior. + +**Consequence**: A temporary ban from any sort of interaction or public +communication with the community for a specified period of time. No public or +private interaction with the people involved, including unsolicited interaction +with those enforcing the Code of Conduct, is allowed during this period. +Violating these terms may lead to a permanent ban. + +### 4. Permanent Ban + +**Community Impact**: Demonstrating a pattern of violation of community +standards, including sustained inappropriate behavior, harassment of an +individual, or aggression toward or disparagement of classes of individuals. + +**Consequence**: A permanent ban from any sort of public interaction within +the community. + +## Attribution + +This Code of Conduct is adapted from the [Contributor Covenant][homepage], +version 2.0, available at +https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. + +Community Impact Guidelines were inspired by [Mozilla's code of conduct +enforcement ladder](https://github.com/mozilla/diversity). + +[homepage]: https://www.contributor-covenant.org + +For answers to common questions about this code of conduct, see the FAQ at +https://www.contributor-covenant.org/faq. Translations are available at +https://www.contributor-covenant.org/translations. From 1fb72c45c7e668cf99568439645a6872eb02412c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3nos?= Date: Tue, 4 Oct 2022 13:29:47 +0200 Subject: [PATCH 5/8] Update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 7ecea47..9847c6c 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,19 +1,12 @@ --- name: Bug Report about: Template per gli errori -title: '' +title: "[BUG] " labels: bug -assignees: '' +assignees: MainKronos --- -### Informazioni generali -> _Compilare i campi_ -* **Nome Anime**: -* **Stagione**: -* **Episodio**: -* **Link di AnimeWorld**: - ### Livello di Errore: > _Selezionare solo un livello di errore_ - `🆆🅰🆁🅽🅸🅽🅶` @@ -24,5 +17,12 @@ assignees: '' ### Messaggio di Errore > _Insere qui il messaggio di errore._ +### Informazioni generali +> _Compilare i campi se correlato_ +- **Nome Anime**: +- **Stagione**: +- **Episodio**: +- **Link di AnimeWorld**: + ### Descrizione [_Opzionale_] > _Inserire qui una breve descrizione del problema._ From d733dfde3f185b72629ca97baa8c1a643a9dbaa8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3nos?= Date: Tue, 4 Oct 2022 13:31:32 +0200 Subject: [PATCH 6/8] Update issue templates --- .github/ISSUE_TEMPLATE/bug-report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug-report.md b/.github/ISSUE_TEMPLATE/bug-report.md index 9847c6c..639b238 100644 --- a/.github/ISSUE_TEMPLATE/bug-report.md +++ b/.github/ISSUE_TEMPLATE/bug-report.md @@ -1,7 +1,7 @@ --- name: Bug Report about: Template per gli errori -title: "[BUG] " +title: "[BUG] Nome Errore" labels: bug assignees: MainKronos From 10f8dd3669b90750719e862cd329342c1f99f9cb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3nos?= Date: Tue, 4 Oct 2022 13:47:48 +0200 Subject: [PATCH 7/8] Update issue templates --- .github/ISSUE_TEMPLATE/feature_request.md | 27 +++++++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/feature_request.md diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 0000000..4357312 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,27 @@ +--- +name: Feature request +about: Suggerisci una nuova feature +title: "[Enhancement] Nome Richiesta" +labels: enhancement +assignees: MainKronos + +--- + +### Settore +> _Seleziona l'ambito della tua richiesta._ +- Interfaccia web +- Libreria Animeworld +- Integrazioni (Sonarr, telegram, etc...) +- Nucleo del programma + +### È una richiesta relativa a un problema? +> _Descrivi in modo chiaro e conciso che tipo di problema si tratta. Es. Sono davvero frustrato quando [...]_ + +### Descrivi l'idea che hai in mente o che ti piacerebbe vedere realizzata. +> _Descrivi in modo chiaro e conciso che cosa vuoi che accada e come dovrebbe funzionare._ + +### Descrivi le alternative che hai considerato. +> _Descrivi in modo chiaro e conciso quale idee, soluzioni o caratteristiche hai considerato._ + +### Contenuto aggiuntivo +> _Aggiungi qualsiasi altro contenuto, ad esempio immagini o altri file, se ne ritieni necessario._ From 1a6cbdf0175f1042afc34580c9ccd6aba0277a49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kr=C3=B3nos?= Date: Tue, 4 Oct 2022 21:28:43 +0200 Subject: [PATCH 8/8] Update Dockerfile --- Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 6daceb8..f31684d 100644 --- a/Dockerfile +++ b/Dockerfile @@ -48,10 +48,10 @@ ENV PIP_ROOT_USER_ACTION ignore # USER dockeruser ENV USER_NAME dockeruser -ENV VERSION "1.8.2" +ENV VERSION "1.8.3" EXPOSE 5000 VOLUME [ "/downloads", "/script/json", "/script/connections" ] -CMD ["/script/start.sh"] \ No newline at end of file +CMD ["/script/start.sh"]