-
Notifications
You must be signed in to change notification settings - Fork 5
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add TAS2GIF #3
base: slash
Are you sure you want to change the base?
Add TAS2GIF #3
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,131 @@ | ||
from discord.ext import commands | ||
from discord.ext import tasks | ||
import discord | ||
import requests | ||
from config import celia_home | ||
import asyncio, aiohttp | ||
import os | ||
import subprocess, shutil | ||
|
||
gif_processing = False | ||
|
||
class tas2gif(commands.Cog): | ||
|
||
def __init__(self, bot): | ||
self.bot = bot | ||
|
||
@commands.command() | ||
async def tas2gif(self, ctx, *args): | ||
|
||
global gif_processing | ||
if (gif_processing): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'd rather not use this global variable at all here, instead @commands.cooldown of ~1 minute (which is the longest file you can upload) with a global bucket should work just as well while being simpler and less prone to bugs |
||
await ctx.send('A GIF is currently processing, try again later.') | ||
return | ||
gif_processing = True | ||
|
||
if len(args) not in (2, 3): | ||
await tas2gif_error(f'Usage: !tas2gif <cartname> <level_index> [url]', ctx) | ||
return | ||
|
||
cartname = args[0] | ||
level = args[1] | ||
|
||
# Download file | ||
attachment_url = args[2] if len(args) > 2 else ctx.message.attachments[0].url | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not having an attachment or a url causes the command to fail silently (and worse, gif_processing stays True forever) |
||
try: | ||
attachment_file = await fetch(attachment_url) | ||
except ConnectionError as e: | ||
await tas2gif_error(f"Error while getting TAS file (HTTP {e}).", ctx) | ||
return | ||
except: | ||
await tas2gif_error("Could not download TAS file!", ctx) | ||
return | ||
|
||
|
||
# Write to disk | ||
tas_file = "files/" + "recordme_" + cartname + "_" + level + ".tas" | ||
with open(tas_file, "w+b") as f: | ||
f.write(attachment_file) | ||
|
||
# Estimate length of TAS file | ||
try: | ||
total_frames = attachment_file.decode("utf-8").count(",") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Balloon seeding (the stuff in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Check out the process_inputs function in tas.py since it does something similar |
||
except UnicodeDecodeError: | ||
await tas2gif_error("Error decoding TAS file!", ctx) | ||
return | ||
|
||
max_seconds = total_frames / 30 + 2 | ||
if max_seconds > 62: | ||
await tas2gif_error("TAS file exceeds the maximum of 1 minute!", ctx) | ||
return | ||
|
||
|
||
|
||
# Run Celia | ||
shutil.copy2(tas_file, celia_home + tas_file) | ||
|
||
return_code = await run_celia(celia_home, cartname, max_seconds, ctx) | ||
if return_code == 1: return | ||
|
||
# Linux only | ||
gif_path = (os.getenv("HOME") + '/.local/share/love/Celia/gifs' + '/') | ||
|
||
gif = discord.File(gif_path + 'out.gif') | ||
gif_processing = False | ||
await ctx.send(file=gif) | ||
await cleanup() | ||
|
||
async def setup(bot): | ||
if not os.path.exists("files"): os.mkdir("files") | ||
if not os.path.exists(celia_home + "files"): os.mkdir(celia_home + "/files") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You probably meant for these to be consistent (see acedic's comments about celia_home) |
||
await bot.add_cog(tas2gif(bot)) | ||
|
||
async def tas2gif_error(message, ctx): | ||
await cleanup() | ||
await ctx.send(message) | ||
return 1 | ||
|
||
async def fetch(url, params=None): | ||
async with aiohttp.ClientSession() as session: | ||
async with session.get(url, params=params) as response: | ||
if response.status != 200: raise ConnectionError(response.status) | ||
r = await response.read() | ||
return r | ||
|
||
async def run_celia(celia_home, cartname, max_seconds, ctx): | ||
cmd = [f'{celia_home}love', '.', 'cctas', f'{cartname}', '-producegif'] | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why should the love executable be inside the celia_home? I'd say it's better to assume that it's in PATH already |
||
process = await asyncio.create_subprocess_exec(*cmd, cwd=celia_home) | ||
|
||
try: | ||
await asyncio.wait_for(process.wait(), max_seconds) | ||
except asyncio.TimeoutError: | ||
process.terminate() | ||
print("yuh") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You probably forgot to remove this |
||
await tas2gif_error("TAS took much longer than expected! Malformed input?", ctx) | ||
return 1 | ||
|
||
if process.returncode == 1: | ||
await tas2gif_error("Celia exited wrong!", ctx) | ||
return 1 | ||
elif process.returncode == 2: | ||
await tas2gif_error("TAS took longer than expected.", ctx) | ||
return 1 | ||
elif process.returncode == 3: | ||
await tas2gif_error("Invalid input file.", ctx) | ||
return 1 | ||
elif (process.returncode != 0): | ||
await tas2gif_error("Celia exited with an error.", ctx) | ||
return 1 | ||
|
||
async def cleanup(): | ||
global gif_processing | ||
gif_processing = False | ||
try: | ||
# Make sure there are no remaining files | ||
for filename in os.listdir('files/'): | ||
shutil.os.unlink('files/' + filename) | ||
|
||
for filename in os.listdir(celia_home + 'files/'): | ||
shutil.os.unlink(celia_home + 'files/' + filename ) | ||
except: | ||
pass |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
colorama | ||
discord.py | ||
aiohttp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The code assumes that celia_home ends with a "/", but paths aren't usually specified like that in configuration. This should either be communicated clearly or preferably the code should assume that it doesn't end in a "/". After all
/directory//file
usually results in the same thing as/directory/file
but/directoryfile
will definitely result in an error.