diff --git a/anime-namer/Dockerfile b/anime-namer/Dockerfile new file mode 100644 index 0000000..923b848 --- /dev/null +++ b/anime-namer/Dockerfile @@ -0,0 +1,8 @@ +FROM python:3.7 + +RUN pip3 install flask requests + +COPY . /app +WORKDIR /app + +CMD python3 app.py diff --git a/anime-namer/app.py b/anime-namer/app.py new file mode 100644 index 0000000..ac4d6c5 --- /dev/null +++ b/anime-namer/app.py @@ -0,0 +1,95 @@ +from flask import Flask, render_template, request, redirect, jsonify, make_response +from os import listdir, getenv, path +from requests import post +from random import randint + +JELLYFIN_DIRECTORY = getenv("JF_DIR") +FLAG_DIRECTORY = getenv("FLAG_DIR") +GOTIFY_URL = getenv("GOTIFY_URL") +BASE_URL = getenv("BASE_URL") + +app = Flask(__name__) +pending = {} + +class PendingNaming: + def __init__(self, dl_path): + self.dl_path = dl_path + self.id = str(randint(100000, 999999)) + + def resolve(self): + pending.pop(self.id, None) + +@app.route("/") +def index(): + return render_template("index.html", pending=[pending[i] for i in pending], BASE_URL=BASE_URL) + +@app.route("/") +def index_with_id(id): + id = str(id) + if id in pending: + item = pending[id] + return render_template("resolve.html", item=item, BASE_URL=BASE_URL) + else: + return make_response("Pending item not found", 404) + +@app.route("/addPending", methods=["POST", "GET"]) +def add_pending(): + dl_path = request.args.get("title") + if not dl_path: + dl_path = request.form.get("title") + if not dl_path: + return make_response("Title not provided", 400) + p = PendingNaming(dl_path) + pending[p.id] = p + + url = "{}/{}".format(BASE_URL, p.id) + post(GOTIFY_URL, json={ + "extras": { + "client::display": { + "contentType": "text/markdown" + }, + "client::android": { + "autoLink": "web" + } + }, + "title": "Intervention needed", + "message": "[{} could not be named]({})".format(dl_path, url), + "priority": 9 + }) + return str(p.id) + +@app.route("/resolve", methods=["POST"]) +def resolve(): + id = request.form.get("id") + title = request.form.get("title") + season = request.form.get("season") + if not id in pending: + return make_response("Pending item not found", 404) + item = pending[id] + with open(path.join(FLAG_DIRECTORY, id), "w+") as f: + f.write("{0}|{0}|{1}".format(title, season)) + item.resolve() + return redirect(BASE_URL) + +@app.route("/delete/", methods=["GET"]) +def delete(id): + id = str(id) + if not id in pending: + return make_response("Pending item not found", 404) + item = pending[id] + with open(path.join(FLAG_DIRECTORY, id), "w+") as f: + f.write("delete") + item.resolve() + return redirect(BASE_URL) + +@app.route("/autocomplete") +def autocomplete(): + dirs = listdir(JELLYFIN_DIRECTORY) + query = request.args.get("term").lower() + if len(query) >= 2: + return jsonify([i for i in dirs if query in i.lower()]) + return jsonify([]) + + +if __name__=='__main__': + app.run("0.0.0.0", getenv("PORT")) diff --git a/anime-namer/docker-compose.yml b/anime-namer/docker-compose.yml new file mode 100644 index 0000000..65b3dfc --- /dev/null +++ b/anime-namer/docker-compose.yml @@ -0,0 +1,22 @@ +version: '3' +services: + anime-namer: + build: . + container_name: anime-namer + networks: + - anamer-network + ports: + - 9010:9010 + environment: + - JF_DIR=THE_JELLYFIN_DIRECTORY + - FLAG_DIR=THE_FLAG_DIRECTORY + - BASE_URL=YOUR_BASE_URL + - GOTIFY_URL=YOUR_GOTIFY_URL + - PORT=9010 + volumes: + - /mnt/Storage/Anime:/Anime + restart: unless-stopped + +networks: + anamer-network: + driver: bridge diff --git a/anime-namer/templates/index.html b/anime-namer/templates/index.html new file mode 100644 index 0000000..d3e163d --- /dev/null +++ b/anime-namer/templates/index.html @@ -0,0 +1,83 @@ + + + + + + + + Anime namer + + + + +

Anime namer

+
+

Pending:

+ +
+ + + diff --git a/anime-namer/templates/resolve.html b/anime-namer/templates/resolve.html new file mode 100644 index 0000000..442ca57 --- /dev/null +++ b/anime-namer/templates/resolve.html @@ -0,0 +1,73 @@ + + + + + + + + + + + Anime namer + + + + +
+

{{ item.dl_path }}

+
+ + + + + + + +
+
+ + + + diff --git a/anime_scripts/jellyfin-namer-new.sh b/anime_scripts/jellyfin-namer-new.sh new file mode 100755 index 0000000..5a98dd6 --- /dev/null +++ b/anime_scripts/jellyfin-namer-new.sh @@ -0,0 +1,74 @@ +#!/bin/bash + +#Gets the last part of the current directory +function get_bottom_dir() { + IFS='/'; + read -ra ADDR <<< "$PWD"; + echo "${ADDR[-1]}"; + IFS=' '; +} + +#Removes braces, parenteres along with everything in them and strips leading and trailing whitespace +function name_clean() { + local _out=$(echo "$1" | sed -e 's/\[[^][]*\]//g'); + _out=$(echo "$_out" | sed -e 's/([^()]*)//g'); + _out=$(echo "$_out" | sed 's/_/ /g'); + echo $(echo "$_out" | xargs); +} + +#Get series via seasons.py. Will fall-back to manual intervention if fail. +function get_series() { + local sanitized_name=$(name_clean "$1"); + local output; + output=$(python3 /scripts/seasons.py "$sanitized_name"; exit "$?";); + if [[ "$?" -ne "0" ]]; then + echo "seasons.py failed. Waiting for manual intevention."; + local mi_id; + mi_id=$(curl --fail -F "dl_path=$sanitized_name" "$MI_URL"; exit "$?";); + if [[ "$?" -eq "0" ]]; then + while [[ ! -f "/Anime/flags/$mi_id" ]]; do + sleep 1; + done + output=$(cat "/Anime/flags/$mi_id"); + rm "/Anime/flags/$mi_id"; + if [[ "$output" -eq "delete" ]]; then + return 1; + fi + else + return 1; + fi + fi + + IFS="|"; + read -ra STR <<< "$output" + + TITLE_ROMAJI="${STR[0]}"; + TITLE_ENGLISH="${STR[1]}"; + SEASON="${STR[2]}"; + IFS=" "; + return 0; +} +cd "$1"; + +bottom_dir=$(get_bottom_dir); +cleaned_bottom_dir=$(name_clean "$bottom_dir"); +get_series "$cleaned_bottom_dir"; +if [[ "$?" -eq "0" ]]; then + if [[ -d "$JF_DIR/$TITLE_ROMAJI" ]]; then + cleaned_dir="$TITLE_ROMAJI/Season $SEASON"; + elif [[ -d "$JF_DIR/$TITLE_ENGLISH" ]]; then + cleaned_dir="$TITLE_ENGLISH/Season $SEASON"; + else + cleaned_dir="$TITLE_ROMAJI/Season $SEASON"; + fi +else + cleaned_dir="$cleaned_bottom_dir"; +fi +mkdir -p "$JF_DIR/$cleaned_dir"; + +for i in *; do + cleaned_name=$(name_clean "$i"); + ln "$PWD/$i" "$JF_DIR/$cleaned_dir/$cleaned_name" >/dev/null 2>/dev/null; +done; + +echo "$JF_DIR/$cleaned_dir"; \ No newline at end of file diff --git a/anime_scripts/seasons.py b/anime_scripts/seasons.py new file mode 100755 index 0000000..2843cfb --- /dev/null +++ b/anime_scripts/seasons.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 + +from requests import post +from datetime import datetime +from sys import argv + +BASE_URL = "https://graphql.anilist.co" + +class BaseShowNotInList(Exception): + def __init__(self, items): + self.items = items + super().__init__("Base show not in sequel/prequel list.") + +class SortableAnime: + def __init__(self, id, year, month, day, reltype, title, frmt): + self.id = id + self.timestamp = datetime(year if year else 9999, month if month else 12, day if day else 31) + self.type = reltype + self.frmt = frmt + self.title = title + + def dict(self): + return { + "id": self.id, + "timestamp": self.timestamp.strftime("%d-%m-%Y"), + "type": self.type, + "title": self.title, + "format": self.frmt + } + + def __eq__(self, other): + return self.id == other.id + + def __hash__(self): + return self.id + + def __str__(self): + return str(self.title) + + +def search(query): + response = post(BASE_URL, json={ + 'query': """ + query ($q: String) { + Media (search: $q) { + id + } + } + """, + 'variables': {"q": query} + }) + if response.ok: + return response.json()["data"]["Media"]["id"] + raise ValueError("Show does not exist") + +def get_show(id): + response = post(BASE_URL, json={ + 'query': """ + query ($id: Int) { + Media (id: $id) { + id + title { + english + romaji + } + startDate { + year + month + day + } + format + relations { + nodes { + id + format + startDate { + year + month + day + } + title { + english + romaji + } + } + edges{ + relationType + } + } + } + } + """, + 'variables': {"id": id} + }) + if response.ok: + return response.json() + raise ValueError("Bad show") + +def get_base_show(res): + base = res["data"]["Media"] + return SortableAnime(base["id"], base["startDate"]["year"], base["startDate"]["month"], base["startDate"]["day"], "BASE", base["title"], base["format"]) + +def process_shows(res): + ls = [] + ls.append(get_base_show(res)) + for i,v in enumerate(res["data"]["Media"]["relations"]["nodes"]): + ls.append(SortableAnime(v["id"], v["startDate"]["year"], v["startDate"]["month"], v["startDate"]["day"], res["data"]["Media"]["relations"]["edges"][i]["relationType"], v["title"], v["format"])) + pass + return ls + +def main(query): + items = [] + show_id = search(query) + res = get_show(show_id) + items.extend(process_shows(res)) + base_show = get_base_show(res) + + if "PREQUEL" not in [i.type for i in items]: + season = 1 + final_items = items + else: + ignore = [] + while True: + f = False + for i in items: + if i.type == "PREQUEL" and i not in ignore: + fi = [i for i in process_shows(get_show(i.id)) if i not in items] + items.extend(fi) + ignore.append(i) + f = True + if not f: + break + final_items = [i for i in items if i.frmt == "TV" and (i.type == "PREQUEL" or i.type == "SEQUEL" or i.type == "BASE")] + if not base_show in final_items: + final_items = [i for i in items if (i.type == "PREQUEL" or i.type == "SEQUEL" or i.type == "BASE")] + final_items.sort(key=lambda i: i.timestamp) + if base_show in final_items: + season = final_items.index(base_show) + if season: + season += 1 + else: + raise Exception("Cannot determine season") + else: + raise BaseShowNotInList(final_items) + return season, base_show, final_items + +if __name__ == "__main__": + season, show, items = main(argv[1]) + base_title = items[0].title + print("{}|{}|{}".format(base_title["romaji"], base_title["english"], season)) diff --git a/anime_scripts/subtitle-converter.sh b/anime_scripts/subtitle-converter.sh index 9286e60..199c0f4 100755 --- a/anime_scripts/subtitle-converter.sh +++ b/anime_scripts/subtitle-converter.sh @@ -4,8 +4,7 @@ function convert() { res=$(ffprobe -v error -show_entries stream=codec_type,codec_name -of compact "$1" | grep -s "subtitle"); if [[ "$res" == *"ass"* ]]; then echo "Converting $1"; - ext="${1##*.}"; - ffmpeg -n -i "$1" -c:s srt "${i%.$ext}.srt" > /dev/null 2&> /dev/null; + ffmpeg -n -i "$1" -c:s srt "${i%.$ext}.srt" > /dev/null else echo "$1 not ASS. Skipping"; fi @@ -15,10 +14,12 @@ if [[ -d "$1" ]]; then cd "$1"; shopt -s globstar for i in **/*; do + ext="${i##*.}"; convert "$i"; done elif [[ -f "$1" ]]; then cd $(dirname "$1"); name=$(basename "$1"); + ext="${name##*.}"; convert "$name"; fi