added anime scripts and new animebyter

This commit is contained in:
marios 2020-04-03 14:29:51 +03:00
parent ddbb5b106b
commit 9854acd343
17 changed files with 589 additions and 307 deletions

37
Animebyter/Animebyter.py Normal file
View File

@ -0,0 +1,37 @@
from aiohttp import ClientSession
from feedparser import parse
from os import getenv
from json import dumps
import logging
import hashlib
web = ClientSession()
class Anime:
def __eq__(self,other):
return self.title == other.title
def __hash__(self):
return hash(self.title)
def __init__(self,name,le,tl,res):
self.title = name.replace("/","-")
self.last_episode = le
self.torrent_link = tl
self.resolution = res.strip()
self.id = str(int(hashlib.sha256(self.title.encode('utf-8')).hexdigest(), 16) % 10**8)
async def get_airing():
r = []
async with web.get("https://animebytes.tv/feed/rss_torrents_airing_anime/{}".format(getenv("ab_key"))) as res:
if res.status==200:
txt = await res.text()
rss = parse(txt)
for i in rss['entries']:
try:
title = i['ab_grouptitle']
ep = int((''.join(x for x in i['ab_torrentproperty'].split("|")[6] if x.isdigit())).strip())
link = i['link']
r.append(Anime(title,ep,link,i['ab_torrentproperty'].split("|")[3]))
except Exception as e:
logging.error(str(e))
return r

View File

@ -5,10 +5,14 @@ RUN apk add libffi-dev
RUN apk add openssl-dev
RUN apk add python3-dev
COPY requirements.txt /app/requirements.txt
RUN pip install -r /app/requirements.txt
ENV PYTHONUNBUFFERED=0
ENV interval 300
EXPOSE 5000
COPY . /app
WORKDIR /app
ENV interval 300
RUN pip install -r requirements.txt
CMD python3 animebyter.py
CMD python3 main.py

115
Animebyter/Downloader.py Normal file
View File

@ -0,0 +1,115 @@
from Animebyter import get_airing
from asyncio import sleep, Queue, get_event_loop
from aiohttp import ClientSession
from pickledb import PickleDB
import logging
import os
INTERVAL = int(os.getenv("interval","5"))
web = ClientSession()
QB_URL = os.getenv("qbit_url")
store = PickleDB(os.getenv("database"), True, False)
dl_queue = Queue()
loop = get_event_loop()
if not store.exists("watching"):
store.lcreate("watching")
if not store.exists("qbUser"):
store.set("qbUser", "")
if not store.exists("qbPass"):
store.set("qbPass", "")
class qbLoginException(Exception):
pass
class NotLoggedInException(Exception):
pass
class DownloadableItem:
def __init__(self,anime):
self.anime = anime
def complete(self):
watching = store.get("watching")
for v in watching:
if v['id'] == self.anime.id:
store.lremvalue("watching",v)
v["last_episode"] = self.anime.last_episode
store.ladd("watching",v)
return
async def login_qb(username=store.get("qbUser"),password=store.get("qbPass"), client=web):
async with client.post(QB_URL+'/login',data={'username':username,'password':password}) as res:
if res.status!=200:
raise qbLoginException(await res.text())
else:
logging.info("Logged into qBittorrent")
async def add_anime_torrent(anime):
logging.info("Adding episode {} of {}".format(anime.last_episode,anime.title))
path = os.path.join(store.get('downloadPath'),anime.title)
async with web.post(QB_URL+'/command/download',data={'urls':anime.torrent_link,'savepath':path,'category':store.get("downloadLabel")}) as res:
if res.status==200:
return 1
elif res.status==403:
raise NotLoggedInException()
else:
raise Exception(await res.text())
async def get_last_added():
async with web.get(QB_URL+"/query/torrents",params={"category":"Anime","sort":"added_on","reverse":"true"}) as res:
if res.status==200:
res = await res.json()
return res[0]
from Notifications import downloading
async def add_to_download_list(anime):
last = await get_last_added()
if last:
downloading[last["hash"]] = anime
async def downloader():
logging.info("Starting downloader")
while True:
item = await dl_queue.get()
while True:
try:
await add_anime_torrent(item.anime)
await add_to_download_list(item.anime)
item.complete()
logging.info("Added episode {} of {}".format(item.anime.last_episode,item.anime.title))
break
except NotLoggedInException:
while True:
try:
await login_qb()
break
except Exception as e:
logging.warn("Could not log into qBittorrent ({})".format(str(e)))
await sleep(5)
continue
continue
except Exception as e:
logging.error(str(e))
await sleep(3)
async def checker():
logging.info("Starting new episode checker")
while True:
try:
logging.debug("Checking for new episodes")
airing = await get_airing()
watching = store.get("watching")
for air in airing:
for watch in watching:
if air.id == watch['id'] and air.resolution == watch["resolution"]:
if air.last_episode > watch['last_episode']:
logging.debug("Attempting to add episode {} of {}".format(air.last_episode,air.title))
item = DownloadableItem(air)
await dl_queue.put(item)
except Exception as e:
logging.error(str(e))
continue
finally:
await sleep(INTERVAL)

View File

@ -0,0 +1,49 @@
from aiohttp import ClientSession
from os import getenv
from Downloader import QB_URL, login_qb, web
from asyncio import sleep
import logging
notif_web = ClientSession()
URL = getenv("gotify_url")
downloading = {}
async def _send_notification(title,message):
if not URL:
logging.debug("Ignoring notification push because gotify url not set")
return
res = await notif_web.post(URL, data={
"title": title,
"message": message
})
if res.status == 200:
return 1
else:
logging.warn("Could not push notification ({}: {})".format(res.status, await res.text()))
async def send_anime_notification(anime):
logging.info("Finished downloading episode {} of {}".format(anime.last_episode, anime.title))
title = anime.title
message = "Episode {} has finished downloading".format(anime.last_episode)
return await _send_notification(title, message)
async def dl_watchdog():
await login_qb(client=web)
logging.info("Starting download watchdog")
while True:
try:
res = await web.get(QB_URL+"/query/torrents",params={'filter':'downloading', 'category':'Anime'})
if res.status==200:
res = await res.json()
hashes = [i['hash'] for i in res]
for i in downloading:
if i not in hashes:
anime = downloading.pop(i,None)
await send_anime_notification(anime)
else:
logging.warn("Something went wrong with fetching downloads ({}: {})".format(res.status,await res.text()))
except Exception as e:
logging.error(str(e))
continue
finally:
await sleep(5)

34
Animebyter/README.md Normal file
View File

@ -0,0 +1,34 @@
# Animebyter
##### A daemon and web app that keeps track of the shows you watch and automatically downloads new episode off Animebytes.tv
----
### How to set-up
0) Set-up docker. This guide assumes that you use qBittorrent as a docker container and that you have some sort of web server with proxying capabilities, also dockerized.
1) Edit docker-compose:
- Edit the `qbit-network` external name to the one that matches your qbittorrent network.
- Edit the `qbit_url` enironment variable to the hostname of your qbittorrent server. This will typically be your container name (so make sure to set one).
- Edit the `ab_key` environment variable with your animebytes passkey. You can find this under Settings > Account > Passkey.
- Edit the `base_url` environment variable with the path that you've set on your reverse proxy. It should be `/` if you run it on a subdomain.
- Edit the `gotify_url` to a valid Gotify server URL if you want to receive notifications.
2) Run `docker-compose up -d`. If you've set-up your docker-compose correctly it should run without problems.
3) Configure your reverse proxy. The docker-compose.yml provided, exposes a network called `animebyter-network` and names the container `animebyter`. You should edit the deployment of your web-server, making sure it is connected to the `animebyter-network`. The app should then be accessible on `animebyter:5000`. Here's a sample configuration for the caddy webserver:
```
proxy /animebyter animebyter:5000 {
transparent
without /animebyter
}
redir /animebyter /animebyter/
```
### How to use
The page shows two tables: Airing and Watching. You can add any show by clicking the (+) and remove it by clicking (-). Make sure to set a download path and preferably a label also. Also make sure to log into qBittorrent.
### Development
Want to contribute? Sure
License
----
MIT

View File

@ -1,252 +0,0 @@
from feedparser import parse
from aiohttp import ClientSession
from discord.ext.commands import Bot
from tinydb import TinyDB, Query
from os.path import join
from os import getenv
from asyncio import sleep
from traceback import print_exc
client = Bot('ab!')
QB_URL = getenv("qbit_url")
INTERVAL = int(getenv("INTERVAL")) if getenv("INTERVAL") else 300
web = ClientSession()
db = TinyDB(getenv("database_path","animebyter.json"))
path = getenv("download_path")
def get_channel_id():
res = db.search(Query().type == 'channel')
if len(res) > 0:
chn = res[0]['id']
else:
db.insert({'type':'channel','id':''})
chn = None
return chn
def get_notif_id():
res = db.search(Query().type == 'notifications')
if len(res) > 0:
id = res[0]['id']
else:
db.insert({'type':'notifications','id':''})
id = None
return id
get_channel_id()
get_notif_id()
downloading = []
class Anime:
def __init__(self,name,le,tl,res):
self.title = name.replace("/","-")
self.last_episode = le
self.torrent_link = tl
self.resolution = res.strip()
def __eq__(self,other):
return self.title == other.title
def __hash__(self):
return hash(self.title)
async def login_qb():
async with web.post(QB_URL+'/login',data={'username':getenv("qbit_user"),'password':getenv("qbit_pass")}) as res:
if res.status!=200:
print("Could not authenticate with qBittorrent. Exiting...")
exit(1)
else:
print("Logged in to qBittorrent")
async def get_airing():
r = []
res = await web.get("https://animebytes.tv/feed/rss_torrents_airing_anime/{}".format(getenv("ab_key")))
if res.status==200:
txt = await res.text()
rss = parse(txt)
for i in rss['entries']:
try:
title = i['ab_grouptitle']
ep = int((''.join(x for x in i['ab_torrentproperty'].split("|")[6] if x.isdigit())).strip())
link = i['link']
r.append(Anime(title,ep,link,i['ab_torrentproperty'].split("|")[3]))
except Exception:
continue
return r
async def add_anime(anime):
print("Adding episode {} of {}".format(anime.last_episode,anime.title))
try:
await add_torrent(anime.torrent_link, join(path,anime.title), 'Anime')
except Exception as e:
print("Failed to add episode {} of {} ({})".format(anime.last_episode,anime.title,e))
else:
msg = "Added episode {} of {}".format(anime.last_episode,anime.title)
print(msg)
return msg
async def add_torrent(url,path,category):
async with web.post(QB_URL+'/command/download',data={'urls':url,'savepath':path,'category':category}) as res:
if res.status==200:
return 1
else:
raise Exception(await res.text())
async def main():
print("Starting new episode checker")
while True:
try:
print("Checking for new episodes")
airing = await get_airing()
for i in airing:
res = db.search(Query().title==i.title)
if res:
le = res[0]['last_episode']
if le<i.last_episode and i.resolution in ("1080p"):
msg = await add_anime(i)
if msg:
await client.get_channel(get_channel_id()).send(msg)
db.update({'last_episode':i.last_episode},Query().title==i.title)
except Exception as e:
print(str(e))
print_exc()
continue
finally:
await sleep(INTERVAL)
async def dl_watchdog():
print("Starting download watchdog")
while True:
try:
res = await web.get(QB_URL+"/query/torrents",params={'filter':'downloading'})
if res.status==200:
res = await res.json()
names = []
for i in res:
names.append(i['name'])
if i['name'] not in downloading:
downloading.append(i['name'])
for i in downloading:
if i not in names:
try:
downloading.remove(i)
except ValueError:
pass
await client.get_channel(get_channel_id()).send(":exclamation: <@!{}> {} has finished downloading.".format(get_notif_id(),i))
else:
print("Something went wrong with fetching downloads ({}: {})".format(res.status,await res.text()))
except Exception as e:
print(str(e))
print_exc()
continue
finally:
await sleep(10)
def chunks(s, n=1999):
for start in range(0, len(s), n):
yield s[start:start+n]
@client.command(pass_context=True)
async def add(ctx):
airing = list(set(await get_airing()))
txt = ""
for i,v in enumerate(airing):
txt+="{}) {}\n".format(i,v.title)
msgs = []
for i in chunks(txt):
msgs.append(await ctx.send(i))
msg = await client.wait_for('message',check=lambda m: m.author==ctx.author and m.channel==ctx.channel)
await ctx.channel.delete_messages(msgs)
if msg:
try:
msg = int(msg.content)
except Exception as e:
return await ctx.send(e)
if msg>=len(airing):
return await ctx.send("Invalid number")
an = airing[msg]
db.insert({'title':an.title,'last_episode':an.last_episode-1})
return await ctx.send("Added {}".format(an.title))
@client.command(pass_context=True)
async def remove(ctx):
watching = [i for i in db.all() if 'title' in i]
txt = ""
for i,v in enumerate(watching):
txt+="{}) {}\n".format(i,v['title'])
msgs = []
for i in chunks(txt):
msgs.append(await ctx.send(i))
msg = await client.wait_for('message',check=lambda m: m.author==ctx.author and m.channel==ctx.channel)
if len(msgs)==1:
await msgs[0].delete()
else:
await ctx.channel.delete_messages(msgs)
if msg:
try:
msg = int(msg.content)
except Exception as e:
return await ctx.send(e)
if msg>=len(watching):
return await ctx.send("Invalid number")
an = watching[msg]
db.remove(Query().title==an['title'])
return await ctx.send("Removed {}".format(an['title']))
@client.command(pass_context=True)
async def down(ctx):
airing = await get_airing()
txt = ""
for i,v in enumerate(airing):
txt+="{}) {} (Episode: {})[{}]\n".format(i,v.title,v.last_episode,v.resolution)
msgs = []
for i in chunks(txt):
msgs.append(await ctx.send(i))
msg = await client.wait_for('message',check=lambda m: m.author==ctx.author and m.channel==ctx.channel)
print(msg)
await ctx.channel.delete_messages(msgs)
if msg:
try:
msg = int(msg.content)
except Exception as e:
return await ctx.send(e)
if msg>=len(airing):
return await ctx.send("Invalid number")
await ctx.send(await add_anime(airing[msg]))
@client.command(pass_context=True)
async def torrent(ctx,url,path=None,category=None):
if not path:
if 'jpopsuki' in url:
path = '/Music/'
else:
path = '/Downloads/'
if not category:
if 'jpopsuki' in url:
category = 'Music'
try:
await ctx.send(await add_torrent(url,path,category))
except Exception as e:
await ctx.send("Failed to add torrent: {}".format(e))
@client.command(pass_context=True)
async def setchannel(ctx):
db.update({'id':ctx.channel.id},Query().type == 'channel')
await ctx.send("I will now send notifications to this channel!")
@client.command(pass_context=True)
async def setnotif(ctx):
db.update({'id':ctx.author.id}, Query().type == 'notifications')
await ctx.send("I will now tag you for notifications!")
@client.event
async def on_ready():
print("Starting animebyter")
await login_qb()
client.loop.create_task(main())
client.loop.create_task(dl_watchdog())
client.run(getenv("discord_token"))

View File

@ -1,21 +0,0 @@
[Unit]
Description=Adds new episodes from animebytes on qbittorrent
After=network.target qbittorrent.service
[Service]
WorkingDirectory=/home/marios/Animebyter #The location of the animebyter script
ExecStart=/usr/bin/python3.7 animebyter.py
User=marios
Restart=always
Environment=qbit_url=http://127.0.0.1:8080 #URL of your qBittorrent WebUI
Environment=qbit_user=admin #Username for qBittorrent WebUI
Environment=qbit_pass=adminadmin #Password for qBittorrent WebUI
Environment=ab_key=YOUR_ANIMEBYTES_TOKEN #Your animebytes token. You can find this by querying any of the RSS feeds
Environment=channel=THE_CHANNEL_YOU_WANT_TO_SEND_NOTIFICATIONS_IN #The ID of the channel you want to send notifications in on discord
Environment=discord_token=YOUR_DISCORD_TOKEN #The token of your discord bot
Environment=INTERVAL=300 #It will look for new episodes every x seconds
[Install]
WantedBy=multi-user.target

View File

@ -5,18 +5,22 @@ services:
container_name: animebyter
environment:
- qbit_url=http://qbittorrent:8080
- qbit_user=admin
- qbit_pass=adminadmin
- ab_key=YOUR_ANIMEBYTES_KEY
- discord_token=YOUR_DISCORD_TOKEN
- download_path=/Anime
- ab_key=YOUR_ANIMEBYTES_TOKEN
- database=/db/animebyter.json
- base_url=/animebyter
- gotify_url=Gotify_URL
- LOGLEVEL=INFO
volumes:
- ./db:/db
networks:
- qbit-network
- animebyter-network
restart: unless-stopped
networks:
animebyter-network:
driver: bridge
qbit-network:
external:
name: qbittorrent_qbit-network
name: qbittorrent_qbit-network

95
Animebyter/main.py Normal file
View File

@ -0,0 +1,95 @@
from quart import Quart, request, make_response, render_template, redirect
from Animebyter import get_airing
from Downloader import downloader, store, login_qb, qbLoginException, checker
from Notifications import dl_watchdog
from asyncio import get_event_loop,gather
import os
from sys import stdout
import logging
logging.basicConfig(level=logging.DEBUG)
app = Quart(__name__,"/static")
base_url = os.getenv("base_url")
class LastAiring:
airing = []
def get(self):
return self.airing
def sett(self,a):
self.airing = a
last_airing = LastAiring()
class FakeObj:
def __init__(self,dc):
for i in dc.keys():
setattr(self,i,dc[i])
@app.route("/")
async def home():
airing = await get_airing()
last_airing.sett(airing)
watching = store.get("watching")
dl_path = store.get("downloadPath")
dl_label = store.get("downloadLabel")
return await render_template('index.html', airing=airing, watching=[FakeObj(i) for i in watching], dl_path=dl_path, dl_label=dl_label)
@app.route("/addAnime")
async def add_show():
id = request.args.get("id")
la = last_airing.get()
show = None
for i in la:
if i.id == id:
show = i
break
if show:
show.last_episode -=1
store.ladd("watching",vars(show))
return redirect(base_url)
else:
return await render_template("error.html", message="Show does not exist", base_url=base_url)
@app.route("/removeAnime")
async def remove_show():
id = request.args.get("id")
watching = store.get("watching")
for i in watching:
if id == i['id']:
store.lremvalue("watching",i)
return redirect(base_url)
return await render_template("error.html",message="Show does not exist", base_url=base_url)
@app.route("/updatePath", methods=["POST"])
async def set_path():
path = (await request.form).get("path")
if os.path.isdir(path):
store.set("downloadPath", path)
return redirect(base_url)
else:
return await render_template("error.html", message="{} is not a valid path".format(path))
@app.route("/updateLabel", methods=["POST"])
async def set_label():
label = (await request.form).get("label")
store.set("downloadLabel", label)
return redirect(base_url)
@app.route("/updateCreds", methods=["POST"])
async def update_creds():
form = await request.form
username = form.get("user")
password = form.get("password")
try:
await login_qb(username, password)
store.set("qbUser", username)
store.set("qbPass", password)
return redirect(base_url)
except qbLoginException:
return await render_template("error.html", message="Invalid credentials. Try again", base_url=base_url)
if __name__ == '__main__':
server_task = app.run_task("0.0.0.0")
loop = get_event_loop()
loop.run_until_complete(gather(server_task,downloader(),checker(),dl_watchdog()))

View File

@ -0,0 +1,4 @@
feedparser
quart
aiohttp
pickledb

View File

@ -0,0 +1,38 @@
table {
border-collapse: collapse;
}
table, th, td {
border: 2px solid black;
}
th {
padding: 7px;
}
#topbar {
width: 100%;
height: 20%;
}
#watching {
display: inline-block;
}
#airing {
float:left;
margin-right: 150px;
}
.text {
width: 400px;
overflow-wrap: break-word;
}
input {
width: 200px;
}
form {
padding-bottom: 5px;
}

View File

@ -0,0 +1,17 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Error</title>
</head>
<body>
<h1>{{ message }}</h1>
<script>
setTimeout(function() {
window.location.href = "{{ base_url }}";
},2000)
</script>
</body>
</html>

View File

@ -0,0 +1,71 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<link rel="stylesheet" href="static/css/style.css">
<title>ABdown</title>
</head>
<body>
<div id="topbar">
<h1>ABdown</h1>
<form method="POST" action="updatePath">
Download path: <input type="text" name="path" value="{{ dl_path }}">
</form>
<form method="POST" action="updateLabel">
Download label: <input type="text" name="label" value="{{ dl_label }}">
</form>
<form method="POST" action="updateCreds">
Username: <input type="text" name="user" value="{{ qb_user }}">
Password: <input type="password" name="pass" value="********">
<button type="submit">Login</button>
</form>
</div>
<div id="airing">
<h2>Airing</h2>
<table>
<thead>
<th>Title</th>
<th>Resolution</th>
<th>Last episode</th>
<th>Add</th>
</thead>
<tbody>
{% for i in airing %}
<tr>
<td>{{ i.title }}</td>
<td>{{ i.resolution }}</td>
<td>{{ i.last_episode }}</td>
<td><a href="addAnime?id={{ i.id }}"><button>+</button></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
<div id="watching">
<h2>Watching</h2>
<table>
<thead>
<th class="text">Title</th>
<th>Resolution</th>
<th>Last episode</th>
<th>Remove</th>
</thead>
<tbody>
{% for i in watching %}
<tr>
<td class="text">{{ i.title }}</td>
<td>{{ i.resolution }}</td>
<td>{{ i.last_episode }}</td>
<td><a href="removeAnime?id={{ i.id }}"><button>-</button></a></td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</body>
</html>

View File

@ -3,20 +3,24 @@ from os import getenv
from datetime import datetime
import pymysql
class LogItem:
def __init__(self,line):
self.ip = line.split(" ")[0]
self.timestamp = datetime.strptime(line.split("[")[1].split("]")[0].split("+")[0].strip(),"%d/%b/%Y:%H:%M:%S")
req = line.split('"')[1].split(' ')
self.method = req[0]
self.path = req[1]
self.http = req[2]
self.status = int(line.split('"')[2].split(' ')[1])
def __init__(self, line):
self.ip = line.split(" ")[0]
self.timestamp = datetime.strptime(line.split("[")[1].split("]")[
0].split("+")[0].strip(), "%d/%b/%Y:%H:%M:%S")
req = line.split('"')[1].split(' ')
self.method = req[0]
self.path = req[1]
self.http = req[2]
self.status = int(line.split('"')[2].split(' ')[1])
print("Starting log server")
db_conn = pymysql.connect(host=getenv("db_host"),user=getenv("db_user"),password=getenv("db_pass"),db=getenv("db_db"),autocommit=True)
db_conn = pymysql.connect(host=getenv("db_host"), user=getenv(
"db_user"), password=getenv("db_pass"), db=getenv("db_db"), autocommit=True)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.bind((getenv("tcp_ip"),int(getenv("tcp_port"))))
s.bind((getenv("tcp_ip"), int(getenv("tcp_port"))))
s.listen(True)
with db_conn.cursor() as db:
@ -32,17 +36,23 @@ with db_conn.cursor() as db:
""")
while True:
conn, addr = s.accept()
print("Caddy connected from {}".format(addr))
while True:
data = conn.recv(4096)
if not data:
break
data = data.decode("utf-8").split("]:")[1].strip()
itm = LogItem(data)
try:
with db_conn.cursor() as db:
db.execute("INSERT INTO `caddy_logs` (`ip`, `timestamp`, `path`, `method`, `http`, `status`) VALUES (%s, %s, %s, %s, %s, %s)",(itm.ip,itm.timestamp,itm.path,itm.method,itm.http,itm.status,))
except Exception:
continue
conn.close()
conn, addr = s.accept()
print("Caddy connected from {}".format(addr))
while True:
data = conn.recv(4096)
if not data:
break
data = data.decode("utf-8").split("]:")[1].strip()
itm = LogItem(data)
while True:
try:
with db_conn.cursor() as db:
db.execute("INSERT INTO `caddy_logs` (`ip`, `timestamp`, `path`, `method`, `http`, `status`) VALUES (%s, %s, %s, %s, %s, %s)",
(itm.ip, itm.timestamp, itm.path, itm.method, itm.http, itm.status,))
break
except Exception:
db_conn = pymysql.connect(host=getenv("db_host"), user=getenv(
"db_user"), password=getenv("db_pass"), db=getenv("db_db"), autocommit=True)
continue
conn.close()

25
anime_scripts/cmpl.sh Executable file
View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
name="$1";
category="$2";
content_path="$3";
root_path="$4";
save_path="$5";
file_number="$6";
size="$7";
current_tracker="$8";
hash="$9";
export JF_DIR=/Anime/Jellyfin-Anime
cd /scripts
bash notif.sh "$name";
if [[ "$category" == "Anime" ]]; then
echo "Running jellyfin namer";
if [[ -d "$root_path" ]]; then
bash jellyfin-namer.sh "$root_path";
else
bash jellyfin-namer.sh "$save_path";
fi
fi

27
anime_scripts/jellyfin-namer.sh Executable file
View File

@ -0,0 +1,27 @@
#!/usr/bin/env bash
function get_bottom_dir() {
IFS='/';
read -ra ADDR <<< "$PWD";
echo "${ADDR[-1]}";
IFS=' ';
}
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);
}
cd "$1";
bottom_dir=$(get_bottom_dir);
cleaned_dir=$(name_clean "$bottom_dir");
mkdir "$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;

View File

@ -0,0 +1,25 @@
#!/usr/bin/env bash
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";
ffmpeg -n -i "$1" -c:s srt "${i%.$ext}.srt" > /dev/null 2&> /dev/null;
else
echo "$1 not ASS. Skipping";
fi
}
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