PawinC commited on
Commit
65a646d
1 Parent(s): 72f56f6

Upload 13 files

Browse files
.gitignore ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Created by https://www.toptal.com/developers/gitignore/api/python
2
+ # Edit at https://www.toptal.com/developers/gitignore?templates=python
3
+
4
+ real-esrgan/
5
+
6
+ ### Python ###
7
+ # Byte-compiled / optimized / DLL files
8
+ __pycache__/
9
+ *.py[cod]
10
+ *$py.class
11
+
12
+ # C extensions
13
+ *.so
14
+
15
+ # Distribution / packaging
16
+ .Python
17
+ build/
18
+ develop-eggs/
19
+ dist/
20
+ downloads/
21
+ eggs/
22
+ .eggs/
23
+ lib/
24
+ lib64/
25
+ parts/
26
+ sdist/
27
+ var/
28
+ wheels/
29
+ share/python-wheels/
30
+ *.egg-info/
31
+ .installed.cfg
32
+ *.egg
33
+ MANIFEST
34
+
35
+ # PyInstaller
36
+ # Usually these files are written by a python script from a template
37
+ # before PyInstaller builds the exe, so as to inject date/other infos into it.
38
+ *.manifest
39
+ *.spec
40
+
41
+ # Installer logs
42
+ pip-log.txt
43
+ pip-delete-this-directory.txt
44
+
45
+ # Unit test / coverage reports
46
+ htmlcov/
47
+ .tox/
48
+ .nox/
49
+ .coverage
50
+ .coverage.*
51
+ .cache
52
+ nosetests.xml
53
+ coverage.xml
54
+ *.cover
55
+ *.py,cover
56
+ .hypothesis/
57
+ .pytest_cache/
58
+ cover/
59
+
60
+ # Translations
61
+ *.mo
62
+ *.pot
63
+
64
+ # Django stuff:
65
+ *.log
66
+ local_settings.py
67
+ db.sqlite3
68
+ db.sqlite3-journal
69
+
70
+ # Flask stuff:
71
+ instance/
72
+ .webassets-cache
73
+
74
+ # Scrapy stuff:
75
+ .scrapy
76
+
77
+ # Sphinx documentation
78
+ docs/_build/
79
+
80
+ # PyBuilder
81
+ .pybuilder/
82
+ target/
83
+
84
+ # Jupyter Notebook
85
+ .ipynb_checkpoints
86
+
87
+ # IPython
88
+ profile_default/
89
+ ipython_config.py
90
+
91
+ # pyenv
92
+ # For a library or package, you might want to ignore these files since the code is
93
+ # intended to run in multiple environments; otherwise, check them in:
94
+ # .python-version
95
+
96
+ # pipenv
97
+ # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
98
+ # However, in case of collaboration, if having platform-specific dependencies or dependencies
99
+ # having no cross-platform support, pipenv may install dependencies that don't work, or not
100
+ # install all needed dependencies.
101
+ #Pipfile.lock
102
+
103
+ # poetry
104
+ # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
+ # This is especially recommended for binary packages to ensure reproducibility, and is more
106
+ # commonly ignored for libraries.
107
+ # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
+ #poetry.lock
109
+
110
+ # pdm
111
+ # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
112
+ #pdm.lock
113
+ # pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
114
+ # in version control.
115
+ # https://pdm.fming.dev/#use-with-ide
116
+ .pdm.toml
117
+
118
+ # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
119
+ __pypackages__/
120
+
121
+ # Celery stuff
122
+ celerybeat-schedule
123
+ celerybeat.pid
124
+
125
+ # SageMath parsed files
126
+ *.sage.py
127
+
128
+ # Environments
129
+ .env
130
+ .venv
131
+ env/
132
+ venv/
133
+ ENV/
134
+ env.bak/
135
+ venv.bak/
136
+
137
+ # Spyder project settings
138
+ .spyderproject
139
+ .spyproject
140
+
141
+ # Rope project settings
142
+ .ropeproject
143
+
144
+ # mkdocs documentation
145
+ /site
146
+
147
+ # mypy
148
+ .mypy_cache/
149
+ .dmypy.json
150
+ dmypy.json
151
+
152
+ # Pyre type checker
153
+ .pyre/
154
+
155
+ # pytype static type analyzer
156
+ .pytype/
157
+
158
+ # Cython debug symbols
159
+ cython_debug/
160
+
161
+ # PyCharm
162
+ # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
163
+ # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
164
+ # and can be added to the global gitignore or merged into this file. For a more nuclear
165
+ # option (not recommended) you can uncomment the following to ignore the entire idea folder.
166
+ #.idea/
167
+
168
+ ### Python Patch ###
169
+ # Poetry local configuration file - https://python-poetry.org/docs/configuration/#local-configuration
170
+ poetry.toml
171
+
172
+ # ruff
173
+ .ruff_cache/
174
+
175
+ # LSP config files
176
+ pyrightconfig.json
177
+
178
+ # End of https://www.toptal.com/developers/gitignore/api/python
Dockerfile ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:3.11
5
+
6
+ WORKDIR /code
7
+
8
+ COPY ./requirements.txt /code/requirements.txt
9
+
10
+ RUN pip install --no-cache-dir --upgrade -r /code/requirements.txt
11
+
12
+ RUN useradd -m -u 1000 user
13
+
14
+ USER user
15
+
16
+ ENV HOME=/home/user \
17
+ PATH=/home/user/.local/bin:$PATH
18
+
19
+ WORKDIR $HOME/app
20
+
21
+ COPY --chown=user . $HOME/app
22
+
23
+ RUN ["python", "initialSetup.py"]
24
+
25
+ CMD ["python", "main.py"]
UpscalerBotV1.py ADDED
@@ -0,0 +1,213 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, datetime, pytz, discord, shutil, requests, subprocess
2
+ from discord.ext import commands
3
+ from time import perf_counter
4
+
5
+ CLIENT_SECRET = "Ca3tbHTsbImaDmlzXqnpUQLONYtBY1eA"
6
+ TOKEN = "OTE5ODk0NDEwODkwNzI3NDg1.G96t2g.XV5efYvSNKqsNvPFxwmviLi-oY6oAYVPTzV78A"
7
+ ADMINS = [766145655038410763, 937495666471628831]
8
+ basepath = r"C:\Users\Pawin\Software\real-esrgan\discordbot"
9
+
10
+ def getloggingtime():
11
+ return datetime.datetime.now(pytz.timezone('Asia/Bangkok')).strftime('%d/%m/%Y %H:%M:%S')
12
+
13
+ def generate_id(path):
14
+ return str(len(os.listdir(path))+1).zfill(3)
15
+
16
+ def file_cleanup():
17
+ dirlist = [fr"{basepath}\input", fr"{basepath}\output"]
18
+ for directory in dirlist:
19
+ shutil.rmtree(directory)
20
+ os.mkdir(directory)
21
+ print("BOT: Sucessfully cleaned directories")
22
+
23
+ file_cleanup()
24
+
25
+ client = commands.Bot(
26
+ command_prefix = "!",
27
+ case_insensitive = True,
28
+ help_command = None,
29
+ activity = discord.Streaming(name = f"/help | Local Instance", url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ")
30
+ )
31
+
32
+ """BOT EVENTS"""
33
+ #Show Status When Bot Is Ready
34
+ @client.event
35
+ async def on_ready():
36
+ print('BOT: We have successfully logged in as {0.user}\n'.format(client))
37
+
38
+
39
+ @client.event
40
+ async def on_message(message):
41
+ msg = str(message.content)
42
+ validMediaChannels = [1083235916354703430, 896342418859892736, 915795164721709077]
43
+ channel = message.channel
44
+ author = message.author
45
+
46
+ channelId = int(channel.id)
47
+ authorId = int(author.id)
48
+ #print(f"Channel id is {channelId}, author id is {authorId}")
49
+
50
+ imageList = []
51
+ for item in message.attachments:
52
+ if item.content_type.startswith("image"):
53
+ imageList.append(item.url)
54
+
55
+ if message.author.bot or msg.startswith("p!"):
56
+ pass
57
+
58
+ elif (authorId in ADMINS) and (channelId in validMediaChannels) and (message.attachments) and (imageList):
59
+ print(f'IMAGE: New attachment recieved from {author} in channel {channel}. Processing...')
60
+ jobReceiptMessage = await message.channel.send(f"\nUpscale job received for message {message.id}")
61
+ jobStatusMessage = await message.channel.send(f"Status: Starting upscale.")
62
+
63
+
64
+ attachmentCount = len(imageList)
65
+ multipleImages = attachmentCount > 1
66
+ if multipleImages:
67
+ batchstart = perf_counter()
68
+ taskType = "Batch Upscale"
69
+ else:
70
+ taskType = "Image Upscaling"
71
+
72
+ for i in range(attachmentCount):
73
+
74
+ """PREPROCESSING"""
75
+ fileid = f"{message.id}_{i}"
76
+ statusTemplate = f"__Status__: %CURRENTSTEP% image from message *{message.id}* **({i+1}/{attachmentCount})**."
77
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "🔄 Preprocessing"))
78
+
79
+ starttime = perf_counter()
80
+ url = imageList[i]
81
+
82
+ extension = url.split(".")[-1]
83
+ #fileid = generate_id(f"{basepath}\input")
84
+
85
+ inputfile = fr"{basepath}\input\{fileid}.{extension}"
86
+ outputfile = fr"{basepath}\output\{fileid}.png"
87
+
88
+ with open (inputfile, "wb") as f:
89
+ f.write(requests.get(url).content)
90
+
91
+
92
+
93
+ """UPSCALE CONFIG"""
94
+ ai_model = "realesrgan-x4plus-anime"
95
+ #Can be realesr-animevideov3 (default) | realesrgan-x4plus | realesrgan-x4plus-anime | realesrnet-x4plus
96
+ if ai_model == "realesr-animevideov3":
97
+ scale = "2"
98
+ tilesize = "768"
99
+ resizeRequired = False
100
+ else:
101
+ scale = "4"
102
+ tilesize = "256"
103
+ resizeRequired = True
104
+
105
+ #Scale 2 only works with the default model
106
+ execute_upscale = fr"C:\Users\Pawin\Software\real-esrgan\realesrgan-ncnn-vulkan.exe -i {inputfile} -o {outputfile} -s {scale} -n {ai_model} -f png -t {tilesize} -j 1:1:1"
107
+
108
+
109
+
110
+ """UPSCALING"""
111
+ pendtime = perf_counter()
112
+ preprocessingtime = round((pendtime-starttime), 2)
113
+ print(f"PREPROCESS: Completed in {preprocessingtime}s.\nUPSCALE:")
114
+ await jobStatusMessage.edit(content=(statusTemplate.replace("%CURRENTSTEP%", "🧠 Upscaling")+"\nThis may take a while..."))
115
+ #os.system(execute_upscale)
116
+ subprocess.run(execute_upscale, shell=True)
117
+
118
+ uendtime = perf_counter()
119
+
120
+ upscaletime = round((uendtime-pendtime),2)
121
+ print(f"UPSCALE: Completed in {upscaletime}s.")
122
+
123
+
124
+
125
+ """RESIZING"""
126
+ if resizeRequired:
127
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "⚙️ Resizing"))
128
+ subprocess.run(f"mogrify -resize 50% {outputfile}", shell=True)
129
+ rendtime = perf_counter()
130
+ resizingtime = round((rendtime-uendtime), 2)
131
+ print(f"RESIZE: Completed in {resizingtime}s.")
132
+ else:
133
+ resizingtime = 0
134
+
135
+
136
+
137
+ """DELIVERING"""
138
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "✉️ Sending"))
139
+
140
+ try:
141
+ file=discord.File(outputfile)
142
+ imgurl = f"attachment://{fileid}.png"
143
+ prepembed=discord.Embed(title="Sucessfully Upscaled Image")
144
+ prepembed.set_image(url=imgurl)
145
+
146
+ jobResultMessage = await message.channel.send(embed=prepembed, file=file)
147
+
148
+ outputImageUrl = jobResultMessage.embeds[0].image.url
149
+ #outputImageUrl = jobResultMessage.attachments[0].url
150
+
151
+ #print(outputImageUrl)
152
+ embed = discord.Embed(title="Upscaled Image", url=outputImageUrl)
153
+ embed.set_author(name=author, icon_url=message.author.avatar_url)
154
+ embed.set_image(url=imgurl)
155
+ processingstats = f"Preprocessing took {preprocessingtime}s | Resizing took {resizingtime}s."
156
+ embed.set_footer(text=f"Took {upscaletime}s to upscale {fileid}.\nUpscaling done with {ai_model}.\n{processingstats}")
157
+
158
+ await jobResultMessage.edit(embed=embed)
159
+
160
+ except discord.errors.HTTPException as e:
161
+ baseErrorMessage = f"There was an error sending the output for image id {fileid}."
162
+ if '413 Payload Too Large' in str(e):
163
+ await message.channel.send(f"{baseErrorMessage} It was probably too large for discord to handle.\n```python\n{e}```")
164
+ else:
165
+ await message.channel.send(f"{baseErrorMessage}\n```python\n{e}```")
166
+
167
+ except Exception as e:
168
+ await message.channel.send(f"Encountered an error while processing attachment {fileid}\n```python\n{e}```")
169
+
170
+
171
+
172
+ """CLEANING UP"""
173
+ #Already finished the whole job.
174
+ await jobStatusMessage.edit(content=f"\n__Status__: ✅ Job Completed for all {attachmentCount} attachments from message {message.id} at {getloggingtime()}".replace("all 1 attachments", "an attachment"))
175
+ await message.delete()
176
+ await jobReceiptMessage.delete()
177
+
178
+
179
+ print(f"IMAGE: Task completed for messageID {message.id}\n")
180
+
181
+
182
+ else:
183
+ print(f"MESSAGE: {channel}- {author}: {msg}")
184
+ await client.process_commands(message)
185
+
186
+
187
+ """BOT COMMANDS"""
188
+
189
+ @client.command()
190
+ async def test(ctx):
191
+ await ctx.send("Hello!")
192
+
193
+ @client.command()
194
+ async def ping(ctx):
195
+ await ctx.send(f'The ping is {round(client.latency * 1000)}ms.')
196
+
197
+ @client.command()
198
+ async def clear(ctx, amount=None):
199
+ if ctx.author.id in ADMINS:
200
+ if amount == "all":
201
+ amount = 100
202
+ elif amount == None:
203
+ amount = 2
204
+ else:
205
+ amount = int(amount)
206
+ await ctx.channel.purge(limit=amount)
207
+ await ctx.send(f"Finished clearing {amount} messages {ctx.message.author.mention}", delete_after=4)
208
+ else:
209
+ await ctx.send('You do not have the permissions to to clear messages using this bot.')
210
+
211
+ logmsg = f"BOT: Attempting to starting the bot at {getloggingtime()} GMT+7"
212
+ print(logmsg)
213
+ client.run(TOKEN)
initialSetup.py ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, requests, zipfile, io, subprocess
2
+
3
+ os.makedirs('./real-esrgan/discordbot/input', exist_ok=True)
4
+ os.makedirs('./real-esrgan/discordbot/output', exist_ok=True)
5
+
6
+
7
+ with requests.get('https://github.com/xinntao/Real-ESRGAN/releases/download/v0.2.5.0/realesrgan-ncnn-vulkan-20220424-ubuntu.zip') as r:
8
+ zip_file = zipfile.ZipFile(io.BytesIO(r.content))
9
+ zip_file.extractall('./real-esrgan')
10
+
11
+ subprocess.run(['chmod', '+x', './real-esrgan/realesrgan-ncnn-vulkan'])
local.py ADDED
@@ -0,0 +1,228 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, datetime, pytz, discord, shutil, requests, subprocess, asyncio
2
+ from discord.ext import commands
3
+ from time import perf_counter
4
+
5
+ CLIENT_SECRET = "Ca3tbHTsbImaDmlzXqnpUQLONYtBY1eA"
6
+ TOKEN = "OTE5ODk0NDEwODkwNzI3NDg1.G96t2g.XV5efYvSNKqsNvPFxwmviLi-oY6oAYVPTzV78A"
7
+ ADMINS = [766145655038410763, 937495666471628831]
8
+ basepath = r"C:\Users\Pawin\Software\real-esrgan\discordbot"
9
+
10
+ def getloggingtime():
11
+ return datetime.datetime.now(pytz.timezone('Asia/Bangkok')).strftime('%d/%m/%Y %H:%M:%S')
12
+
13
+ def generate_id(path):
14
+ return str(len(os.listdir(path))+1).zfill(3)
15
+
16
+ def file_cleanup():
17
+ dirlist = [fr"{basepath}\input", fr"{basepath}\output"]
18
+ for directory in dirlist:
19
+ shutil.rmtree(directory)
20
+ os.mkdir(directory)
21
+ print("BOT: Sucessfully cleaned directories")
22
+
23
+ file_cleanup()
24
+
25
+ client = commands.Bot(
26
+ command_prefix = "!",
27
+ case_insensitive = True,
28
+ help_command = None,
29
+ activity = discord.Streaming(name = f"/help | Local Instance", url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ")
30
+ )
31
+
32
+ """BOT EVENTS"""
33
+ #Show Status When Bot Is Ready
34
+ @client.event
35
+ async def on_ready():
36
+ print('BOT: We have successfully logged in as {0.user}\n'.format(client))
37
+
38
+ CurrentlyProcessingJob = False
39
+
40
+ @client.event
41
+ async def on_message(message):
42
+ global CurrentlyProcessingJob
43
+
44
+ msg = str(message.content)
45
+ validMediaChannels = [1083235916354703430, 896342418859892736, 915795164721709077]
46
+ channel = message.channel
47
+ author = message.author
48
+
49
+ channelId = int(channel.id)
50
+ authorId = int(author.id)
51
+ #print(f"Channel id is {channelId}, author id is {authorId}")
52
+
53
+ imageList = []
54
+ for item in message.attachments:
55
+ if item.content_type.startswith("image"):
56
+ imageList.append(item.url)
57
+
58
+ if message.author.bot or msg.startswith("p!"):
59
+ pass
60
+
61
+ elif (authorId in ADMINS) and (channelId in validMediaChannels) and (message.attachments) and (imageList):
62
+ print(f'IMAGE: New attachment recieved from {author} in channel {channel}. Processing...')
63
+
64
+ jobReceiptMessage = await message.reply(f"\nUpscale job received for message {message.id}", mention_author=False)
65
+ jobStatusMessage = await message.reply(f"Status: Upscale queued.", mention_author=False)
66
+
67
+ if CurrentlyProcessingJob:
68
+ pendingTemplate = f"__Status__: Upscale %CURRENTSTEP% for job *{message.id}*"
69
+ await jobStatusMessage.edit(content=pendingTemplate.replace("%CURRENTSTEP%", "Pending 🕰️"))
70
+ print(f"IMAGE: There is already a job running. Message id {message.id} has been put on queue")
71
+ while CurrentlyProcessingJob:
72
+ await asyncio.sleep(1)
73
+ #wait untill it finishes
74
+ await jobStatusMessage.edit(content=pendingTemplate.replace("%CURRENTSTEP%", "Starting 🏃"))
75
+ print(f"IMAGE: Starting job {message.id}")
76
+ CurrentlyProcessingJob = True
77
+
78
+ attachmentCount = len(imageList)
79
+ multipleImages = attachmentCount > 1
80
+ if multipleImages:
81
+ batchstart = perf_counter()
82
+ taskType = "Batch Upscale"
83
+ else:
84
+ taskType = "Image Upscaling"
85
+
86
+ for i in range(attachmentCount):
87
+
88
+ """PREPROCESSING"""
89
+ fileid = f"{message.id}_{i}"
90
+ statusTemplate = f"__Status__: %CURRENTSTEP% image from message *{message.id}* **({i+1}/{attachmentCount})**."
91
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "🔄 Preprocessing"))
92
+
93
+ starttime = perf_counter()
94
+ url = imageList[i]
95
+
96
+ extension = url.split(".")[-1]
97
+ #fileid = generate_id(f"{basepath}\input")
98
+
99
+ inputfile = fr"{basepath}\input\{fileid}.{extension}"
100
+ outputfile = fr"{basepath}\output\{fileid}.png"
101
+
102
+ with open (inputfile, "wb") as f:
103
+ f.write(requests.get(url).content)
104
+
105
+
106
+
107
+ """UPSCALE CONFIG"""
108
+ ai_model = "realesrgan-x4plus-anime"
109
+ #Can be realesr-animevideov3 (default) | realesrgan-x4plus | realesrgan-x4plus-anime | realesrnet-x4plus
110
+ if ai_model == "realesr-animevideov3":
111
+ scale = "2"
112
+ tilesize = "768"
113
+ resizeRequired = False
114
+ else:
115
+ scale = "4"
116
+ tilesize = "256"
117
+ resizeRequired = True
118
+
119
+ #Scale 2 only works with the default model
120
+ execute_upscale = fr"C:\Users\Pawin\Software\real-esrgan\realesrgan-ncnn-vulkan.exe -i {inputfile} -o {outputfile} -s {scale} -n {ai_model} -f png -t {tilesize} -j 1:1:1"
121
+
122
+
123
+
124
+ """UPSCALING"""
125
+ pendtime = perf_counter()
126
+ preprocessingtime = round((pendtime-starttime), 2)
127
+ print(f"PREPROCESS: Completed in {preprocessingtime}s.\nUPSCALE:")
128
+ await jobStatusMessage.edit(content=(statusTemplate.replace("%CURRENTSTEP%", "🧠 Upscaling")+"\nThis may take a while..."))
129
+ #os.system(execute_upscale)
130
+ subprocess.run(execute_upscale, shell=True)
131
+
132
+ uendtime = perf_counter()
133
+
134
+ upscaletime = round((uendtime-pendtime),2)
135
+ print(f"UPSCALE: Completed in {upscaletime}s.")
136
+
137
+
138
+
139
+ """RESIZING"""
140
+ if resizeRequired:
141
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "⚙️ Resizing"))
142
+ subprocess.run(f"mogrify -resize 50% {outputfile}", shell=True)
143
+ rendtime = perf_counter()
144
+ resizingtime = round((rendtime-uendtime), 2)
145
+ print(f"RESIZE: Completed in {resizingtime}s.")
146
+ else:
147
+ resizingtime = 0
148
+
149
+
150
+
151
+ """DELIVERING"""
152
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "✉️ Sending"))
153
+
154
+ try:
155
+ #file=discord.File(outputfile)
156
+ localImageUrl = f"http://thinkpad.pawin.tk/upscale/{fileid}.png"
157
+
158
+
159
+ #outputImageUrl = jobResultMessage.embeds[0].image.url
160
+ #uncomment above to use outside local network
161
+ #outputImageUrl = jobResultMessage.attachments[0].url
162
+
163
+ #print(outputImageUrl)
164
+ embed = discord.Embed(title="Upscaled Image", url=localImageUrl, description="Local mode is on. Please use the link above for upscaled image.")
165
+ embed.set_author(name=author, icon_url=message.author.avatar_url)
166
+ embed.set_image(url=url)
167
+ processingstats = f"Preprocessing took {preprocessingtime}s | Resizing took {resizingtime}s."
168
+ embed.set_footer(text=f"Took {upscaletime}s to upscale {fileid}.\nUpscaling done with {ai_model}.\n{processingstats}")
169
+
170
+ await message.channel.send(embed=embed)
171
+
172
+ except discord.errors.HTTPException as e:
173
+ baseErrorMessage = f"There was an error sending the output for image id {fileid}."
174
+ if '413 Payload Too Large' in str(e):
175
+ await message.reply(f"{baseErrorMessage} It was probably too large for discord to handle.\n```python\n{e}```")
176
+ else:
177
+ await message.reply(f"{baseErrorMessage}\n```python\n{e}```")
178
+
179
+ except Exception as e:
180
+ await message.reply(f"Encountered an error while processing attachment {fileid}\n```python\n{e}```")
181
+
182
+
183
+
184
+ """CLEANING UP"""
185
+ #Already finished the whole job.
186
+ await jobStatusMessage.edit(content=f"\n__Status__: ✅ Job Completed for all {attachmentCount} attachments from message {message.id} at {getloggingtime()}".replace("all 1 attachments", "an attachment"), mention_author=False)
187
+ await message.delete()
188
+ await jobReceiptMessage.delete()
189
+ await asyncio.sleep(2)
190
+ await jobStatusMessage.delete()
191
+
192
+
193
+
194
+ print(f"IMAGE: Job finished for messageID {message.id}\n")
195
+ CurrentlyProcessingJob = False
196
+
197
+ else:
198
+ print(f"MESSAGE: {channel}- {author}: {msg}")
199
+ await client.process_commands(message)
200
+
201
+
202
+ """BOT COMMANDS"""
203
+
204
+ @client.command()
205
+ async def test(ctx):
206
+ await ctx.send("Hello!")
207
+
208
+ @client.command()
209
+ async def ping(ctx):
210
+ await ctx.send(f'The ping is {round(client.latency * 1000)}ms.')
211
+
212
+ @client.command()
213
+ async def clear(ctx, amount=None):
214
+ if ctx.author.id in ADMINS:
215
+ if amount == "all":
216
+ amount = 100
217
+ elif amount == None:
218
+ amount = 2
219
+ else:
220
+ amount = int(amount)
221
+ await ctx.channel.purge(limit=amount)
222
+ await ctx.send(f"Finished clearing {amount} messages {ctx.message.author.mention}", delete_after=4)
223
+ else:
224
+ await ctx.send('You do not have the permissions to to clear messages using this bot.')
225
+
226
+ logmsg = f"BOT: Attempting to starting the bot at {getloggingtime()} GMT+7"
227
+ print(logmsg)
228
+ client.run(TOKEN)
main(backup).py ADDED
@@ -0,0 +1,285 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, datetime, pytz, discord, shutil, requests, subprocess, asyncio, platform
2
+ from discord.ext import commands
3
+ from time import perf_counter, sleep
4
+ from threading import Thread
5
+
6
+ CLIENT_SECRET = "Ca3tbHTsbImaDmlzXqnpUQLONYtBY1eA"
7
+ TOKEN = "OTE5ODk0NDEwODkwNzI3NDg1.G96t2g.XV5efYvSNKqsNvPFxwmviLi-oY6oAYVPTzV78A"
8
+ ADMINS = [766145655038410763, 937495666471628831]
9
+
10
+ ostype = platform.system()
11
+ print(f"\nDetected OS: {ostype}")
12
+ if ostype == "Windows":
13
+ basepath = r"C:\Users\Pawin\Software\real-esrgan"
14
+ executablepath = fr"{basepath}\realesrgan-ncnn-vulkan.exe"
15
+ webserverpath = r"C:\Users\Pawin\Code\local-server\main.py"
16
+ startWScmd = f"python {webserverpath}"
17
+ serverurl = "http://thinkpad.pawin.tk/upscale"
18
+
19
+ elif ostype == "Linux":
20
+ basepath = r"/home/pawin/Software/real-esrgan"
21
+ executablepath = fr"{basepath}/realesrgan-ncnn-vulkan"
22
+ webserverpath = r"/media/pawin/Windows/Users/Pawin/Code/local-server/main.py"
23
+ startWScmd = f'python3 {webserverpath}'
24
+ serverurl = "http://thinkpad.pawin.tk:8080/upscale"
25
+
26
+ imagepath = os.path.join(basepath, "discordbot")
27
+ inputpath = os.path.join(imagepath, "input")
28
+ outputpath = os.path.join(imagepath, "output")
29
+
30
+
31
+ outext = "jpg" #or png
32
+ #ai_model = "realesr-animevideov3"
33
+ ai_model = "realesrgan-x4plus"
34
+ #Can be realesr-animevideov3 (default) | realesrgan-x4plus | realesrgan-x4plus-anime | realesrnet-x4plus
35
+
36
+
37
+ def getloggingtime():
38
+ return datetime.datetime.now(pytz.timezone('Asia/Bangkok')).strftime('%d/%m/%Y %H:%M:%S')
39
+
40
+ def generate_id(path):
41
+ return str(len(os.listdir(path))+1).zfill(3)
42
+
43
+ def file_cleanup():
44
+ dirlist = [inputpath, outputpath]
45
+ contentlength = len(os.listdir(inputpath))
46
+ for directory in dirlist:
47
+ shutil.rmtree(directory)
48
+ os.mkdir(directory)
49
+ print("BOT: Sucessfully cleaned directories")
50
+ return contentlength
51
+
52
+ def start_webserver():
53
+ if webserverStartEnabled:
54
+ try:
55
+ print("Calling Webserver...")
56
+ subprocess.run(startWScmd, shell=True)
57
+ except:
58
+ print("Something went wrong with the webserver.")
59
+ print("Webserver process launched.")
60
+ else:
61
+ print("Webserver Start Skipped. If this is a local version, please run it manually.")
62
+
63
+ print(f"Starting the bot...")
64
+ webserverStartEnabled = input("Do you want to start the webserver also? (y/N): ").lower() == "y"
65
+ print(f"BTW, automatic directory cleaning have been disabled. To clean directories, please run !cleandir")
66
+
67
+ client = commands.Bot(
68
+ command_prefix = "!",
69
+ case_insensitive = True,
70
+ help_command = None,
71
+ activity = discord.Streaming(name = f"/help | Local Instance", url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ")
72
+ )
73
+
74
+ """BOT EVENTS"""
75
+ #Show Status When Bot Is Ready
76
+ @client.event
77
+ async def on_ready():
78
+ print('BOT: We have successfully logged in as {0.user}\n'.format(client))
79
+ Thread(target=start_webserver, daemon=True).start()
80
+
81
+ CurrentlyProcessingJob = False
82
+
83
+ @client.event
84
+ async def on_message(message):
85
+ global CurrentlyProcessingJob
86
+
87
+ msg = str(message.content)
88
+ validMediaChannels = [1083235916354703430, 896342418859892736, 915795164721709077]
89
+ channel = message.channel
90
+ author = message.author
91
+
92
+ channelId = int(channel.id)
93
+ authorId = int(author.id)
94
+ #print(f"Channel id is {channelId}, author id is {authorId}")
95
+
96
+ imageList = []
97
+ for item in message.attachments:
98
+ if item.content_type.startswith("image"):
99
+ imageList.append(item.url)
100
+
101
+ if message.author.bot or msg.startswith("p!"):
102
+ pass
103
+
104
+ elif (authorId in ADMINS) and (channelId in validMediaChannels) and (message.attachments) and (imageList):
105
+ print(f'IMAGE: New attachment recieved from {author} in channel {channel}. Processing...')
106
+
107
+ await message.add_reaction("📬")
108
+ jobStatusMessage = await message.reply(f"Status: Upscale queued.", mention_author=False)
109
+
110
+ if CurrentlyProcessingJob:
111
+ pendingTemplate = f"__Status__: Upscale %CURRENTSTEP% for job *{message.id}*"
112
+ await jobStatusMessage.edit(content=pendingTemplate.replace("%CURRENTSTEP%", "Pending 🕰️"))
113
+ print(f"IMAGE: There is already a job running. Message id {message.id} has been put on queue")
114
+ while CurrentlyProcessingJob:
115
+ await asyncio.sleep(1)
116
+ #wait untill it finishes
117
+ await jobStatusMessage.edit(content=pendingTemplate.replace("%CURRENTSTEP%", "Starting 🏃"))
118
+ print(f"IMAGE: Starting job {message.id}")
119
+ CurrentlyProcessingJob = True
120
+
121
+ attachmentCount = len(imageList)
122
+ multipleImages = attachmentCount > 1
123
+ if multipleImages:
124
+ batchstart = perf_counter()
125
+ taskType = "Batch Upscale"
126
+ else:
127
+ taskType = "Image Upscaling"
128
+
129
+ for i in range(attachmentCount):
130
+
131
+ """PREPROCESSING"""
132
+ fileid = f"{message.id}_{i}"
133
+ statusTemplate = f"__Status__: %CURRENTSTEP% image from message *{message.id}* **({i+1}/{attachmentCount})**."
134
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "🔄 Preprocessing"))
135
+
136
+ starttime = perf_counter()
137
+ url = imageList[i]
138
+
139
+ extension = url.split(".")[-1]
140
+ #fileid = generate_id(f"{imagepath}\input")
141
+
142
+ inputfile = os.path.join(inputpath, f"{fileid}.{extension}")
143
+ outputfile = os.path.join(outputpath, f"{fileid}.{outext}")
144
+
145
+ with open (inputfile, "wb") as f:
146
+ f.write(requests.get(url).content)
147
+
148
+
149
+
150
+ """UPSCALE AUTO-CONFIG"""
151
+
152
+ if ai_model == "realesr-animevideov3":
153
+ scale = "2"
154
+ tilesize = "768"
155
+ resizeRequired = False
156
+ else:
157
+ scale = "4"
158
+ tilesize = "256"
159
+ resizeRequired = True
160
+
161
+ #Scale 2 only works with the default model
162
+ execute_upscale = fr"{executablepath} -i {inputfile} -o {outputfile.replace('jpg','png')} -s {scale} -n {ai_model} -f png -t {tilesize} -j 1:1:1"
163
+
164
+ """UPSCALING"""
165
+ pendtime = perf_counter()
166
+ preprocessingtime = round((pendtime-starttime), 2)
167
+ print(f"PREPROCESS: Completed in {preprocessingtime}s.\nUPSCALE:")
168
+ await jobStatusMessage.edit(content=(statusTemplate.replace("%CURRENTSTEP%", "🧠 Upscaling")+"\nThis may take a while..."))
169
+ #os.system(execute_upscale)
170
+ subprocess.run(execute_upscale, shell=True)
171
+
172
+ uendtime = perf_counter()
173
+
174
+ upscaletime = round((uendtime-pendtime),2)
175
+ print(f"UPSCALE: Completed in {upscaletime}s.")
176
+
177
+
178
+
179
+ """RESIZING"""
180
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "⚙️ Resizing"))
181
+ if resizeRequired:
182
+ subprocess.run(f"mogrify -format {outext} -resize 50% {outputfile.replace('jpg','png')}", shell=True)
183
+ else:
184
+ subprocess.run(f"mogrify -format {outext} {outputfile.replace('jpg','png')}", shell=True)
185
+ rendtime = perf_counter()
186
+ resizingtime = round((rendtime-uendtime), 2)
187
+ print(f"RESIZE: Completed in {resizingtime}s.")
188
+
189
+
190
+
191
+ """DELIVERING"""
192
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "✉️ Sending"))
193
+
194
+ try:
195
+
196
+ file=discord.File(outputfile)
197
+ imgurl = f"attachment://{fileid}.{outext}"
198
+ prepembed=discord.Embed(title="Sucessfully Upscaled Image")
199
+ prepembed.set_image(url=imgurl)
200
+
201
+ jobResultMessage = await message.channel.send(embed=prepembed, file=file)
202
+ sendtime = perf_counter()
203
+ localImageUrl = f"{serverurl}/{fileid}.{outext}"
204
+
205
+
206
+ outputImageUrl = jobResultMessage.embeds[0].image.url
207
+ #print(outputImageUrl)
208
+ #outputImageUrl = jobResultMessage.attachments[0].url
209
+
210
+ #print(outputImageUrl)
211
+ embed = discord.Embed(title="Upscaled Image", url=outputImageUrl, description=f"[Local File]({localImageUrl})")
212
+ embed.set_author(name=author, icon_url=message.author.avatar_url)
213
+ embed.set_image(url=imgurl)
214
+
215
+ sendingtime = round((sendtime-rendtime), 2)
216
+ processingstats = f"Preprocessing: {preprocessingtime}s | Resizing: {resizingtime}s | Sending: {sendingtime}s."
217
+ embed.set_footer(text=f"Took {upscaletime}s to upscale {fileid}.\nUpscaling done with {ai_model}.\n{processingstats}")
218
+
219
+ await jobResultMessage.edit(embed=embed)
220
+
221
+ except discord.errors.HTTPException as e:
222
+ baseErrorMessage = f"There was an error sending the output for image id {fileid}."
223
+ if '413 Payload Too Large' in str(e):
224
+ await message.reply(f"{baseErrorMessage} It was too large for discord to handle.\n```python\n{e}```")
225
+ else:
226
+ await message.reply(f"{baseErrorMessage}\n```python\n{e}```")
227
+
228
+ except Exception as e:
229
+ await message.reply(f"Encountered an error while processing attachment {fileid}\n```python\n{e}```")
230
+
231
+
232
+
233
+ """CLEANING UP"""
234
+ #Already finished the whole job.
235
+ await jobStatusMessage.edit(content=f"\n__Status__: ✅ Job Completed for all {attachmentCount} attachments from message {message.id} at {getloggingtime()}".replace("all 1 attachments", "an attachment"), mention_author=False)
236
+ await message.delete()
237
+ await asyncio.sleep(2)
238
+ await jobStatusMessage.delete()
239
+
240
+
241
+
242
+ print(f"IMAGE: Job finished for messageID {message.id}\n")
243
+ CurrentlyProcessingJob = False
244
+
245
+ else:
246
+ print(f"MESSAGE: {channel}- {author}: {msg}")
247
+ await client.process_commands(message)
248
+
249
+
250
+ """BOT COMMANDS"""
251
+
252
+ @client.command()
253
+ async def test(ctx):
254
+ await ctx.send("Hello!")
255
+
256
+ @client.command()
257
+ async def ping(ctx):
258
+ await ctx.send(f'The ping is {round(client.latency * 1000)}ms.')
259
+
260
+ @client.command()
261
+ async def cleandir(ctx):
262
+ if ctx.author.id in ADMINS:
263
+ await ctx.channel.purge(limit=file_cleanup()+1)
264
+ await ctx.send(f'Successfully images directories', delete_after= 2)
265
+ else:
266
+ await ctx.send('You do not have the permissions to perform this action.')
267
+
268
+ @client.command()
269
+ async def clear(ctx, amount=None):
270
+ if ctx.author.id in ADMINS:
271
+ if amount == "all":
272
+ amount = 100
273
+ else:
274
+ try:
275
+ amount = int(amount)
276
+ except:
277
+ amount = 2
278
+ await ctx.channel.purge(limit=amount)
279
+ await ctx.send(f"Finished clearing {amount} messages {ctx.message.author.mention}", delete_after=4)
280
+ else:
281
+ await ctx.send('You do not have the permissions to clear messages using this bot.')
282
+
283
+ logmsg = f"\nBOT: Attempting to starting the bot at {getloggingtime()} GMT+7"
284
+ print(logmsg)
285
+ client.run(TOKEN)
main.py ADDED
@@ -0,0 +1,336 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os, datetime, pytz, discord, shutil, requests, subprocess, asyncio, platform, json
2
+ from discord.ext import commands
3
+ from time import perf_counter, sleep
4
+ from threading import Thread
5
+ from dotenv import load_dotenv
6
+ load_dotenv()
7
+
8
+ CLIENT_SECRET = os.environ['ClientSecret']
9
+ TOKEN = os.environ['TestingToken']
10
+ ADMINS = [766145655038410763, 937495666471628831]
11
+
12
+ ostype = platform.system()
13
+ print(f"\nDetected OS: {ostype}")
14
+ if ostype == "Windows":
15
+ basepath = r"C:\Users\Pawin\Software\real-esrgan"
16
+ executablepath = fr"{basepath}\realesrgan-ncnn-vulkan.exe"
17
+ webserverpath = r"C:\Users\Pawin\Code\local-server\main.py"
18
+ startWScmd = f"python {webserverpath}"
19
+ serverurl = "http://thinkpad.pawin.tk/upscale"
20
+
21
+ elif ostype == "Linux":
22
+ basepath = r"./real-esrgan"
23
+ executablepath = fr"{basepath}/realesrgan-ncnn-vulkan"
24
+ webserverpath = r"/Code/Flask/main.py"
25
+ startWScmd = f'python3 {webserverpath}'
26
+ serverurl = "https://dev.pawin.tk/upscale"
27
+
28
+ imagepath = os.path.join(basepath, "discordbot")
29
+ inputpath = os.path.join(imagepath, "input")
30
+ outputpath = os.path.join(imagepath, "output")
31
+
32
+
33
+ outext = "jpg" #or png
34
+ ai_model = "realesr-animevideov3"
35
+ targetScale = "2" #2 or 4 (string)
36
+ #ai_model = "realesrgan-x4plus-anime"
37
+ #Can be realesr-animevideov3(default) | realesrgan-x4plus | realesrgan-x4plus-anime | realesrnet-x4plus
38
+
39
+
40
+ def getloggingtime():
41
+ return datetime.datetime.now(pytz.timezone('Asia/Bangkok')).strftime('%d/%m/%Y %H:%M:%S')
42
+
43
+ def generate_id(path):
44
+ return str(len(os.listdir(path))+1).zfill(3)
45
+
46
+ def file_cleanup():
47
+ dirlist = [inputpath, outputpath]
48
+ contentlength = len(os.listdir(inputpath))
49
+ for directory in dirlist:
50
+ shutil.rmtree(directory)
51
+ os.mkdir(directory)
52
+ print("BOT: Sucessfully cleaned directories")
53
+ return contentlength
54
+
55
+ def start_webserver():
56
+ if webserverStartEnabled:
57
+ try:
58
+ print("Calling Webserver...")
59
+ subprocess.run(startWScmd, shell=True)
60
+ except:
61
+ print("Something went wrong with the webserver.")
62
+ print("Webserver process launched.")
63
+ else:
64
+ print("Webserver Start Skipped.")
65
+
66
+ def loadMediaChannels():
67
+ global mediaChannels
68
+ with open("resources/mediaChannels.json", "r") as f:
69
+ mediaChannels = json.load(f)
70
+ #print("Successfully loaded media channels.")
71
+
72
+ def backupMediaChannels():
73
+ with open("resources/mediaChannels.json", "w") as f:
74
+ json.dump(mediaChannels, f, indent=2, ensure_ascii=False)
75
+ #print("Successfully backed up media channel.")
76
+
77
+ def toggleMediaChannel(channelId: int):
78
+ global mediaChannels
79
+ loadMediaChannels()
80
+ if channelId in mediaChannels:
81
+ mediaChannels.remove(channelId)
82
+ actionDone = "Unmarked"
83
+ else:
84
+ mediaChannels.append(int(channelId))
85
+ actionDone = "Marked"
86
+
87
+ backupMediaChannels()
88
+ resultMsg = f"Successfully {actionDone} this channel as a media channel."
89
+ print(resultMsg)
90
+ return resultMsg
91
+
92
+
93
+ print(f"Starting the upscaler bot...")
94
+ #webserverStartEnabled = input("Do you want to start the webserver also? (y/N): ").lower() == "y"
95
+ webserverStartEnabled = False
96
+ #print(f"BTW, automatic directory cleaning have been disabled. To clean directories, please run u!cleandir")
97
+
98
+ loadMediaChannels()
99
+
100
+ client = commands.Bot(
101
+ intents = discord.Intents.all(),
102
+ command_prefix = "u!",
103
+ case_insensitive = True,
104
+ help_command = None,
105
+ activity = discord.Streaming(name = f"u!help | Currently Running Upscaler Bot ", url = "https://www.youtube.com/watch?v=dQw4w9WgXcQ")
106
+ )
107
+
108
+ """BOT EVENTS"""
109
+ #Show Status When Bot Is Ready
110
+ @client.event
111
+ async def on_ready():
112
+ print('\nBOT: We have successfully logged in as {0.user}'.format(client))
113
+ file_cleanup()
114
+ Thread(target=start_webserver, daemon=True).start()
115
+
116
+ CurrentlyProcessingJob = False
117
+
118
+ @client.event
119
+ async def on_message(message):
120
+ global CurrentlyProcessingJob
121
+
122
+ msg = str(message.content)
123
+ validMediaChannels = mediaChannels
124
+ channel = message.channel
125
+ author = message.author
126
+
127
+ channelId = int(channel.id)
128
+ authorId = int(author.id)
129
+ #print(f"Channel id is {channelId}, author id is {authorId}")
130
+
131
+ imageList = []
132
+ for item in message.attachments:
133
+ if item.content_type.startswith("image"):
134
+ imageList.append(item.url)
135
+
136
+ if message.author.bot or msg.startswith("p!"):
137
+ pass
138
+
139
+ #elif (authorId in ADMINS) and (channelId in validMediaChannels) and (message.attachments) and (imageList):
140
+ elif (authorId) and (channelId in validMediaChannels) and (message.attachments) and (imageList):
141
+
142
+ print(f'IMAGE: New attachment recieved from {author} in channel {channel}. Processing...')
143
+
144
+ await message.add_reaction("📬")
145
+ jobStatusMessage = await message.reply(f"Status: Upscale queued.", mention_author=False)
146
+
147
+ if CurrentlyProcessingJob:
148
+ pendingTemplate = f"__Status__: Upscale %CURRENTSTEP% for job *{message.id}*"
149
+ await jobStatusMessage.edit(content=pendingTemplate.replace("%CURRENTSTEP%", "Pending 🕰️"))
150
+ print(f"IMAGE: There is already a job running. Message id {message.id} has been put on queue")
151
+ while CurrentlyProcessingJob:
152
+ await asyncio.sleep(1)
153
+ #wait untill it finishes
154
+ await jobStatusMessage.edit(content=pendingTemplate.replace("%CURRENTSTEP%", "Starting 🏃"))
155
+ print(f"IMAGE: Starting job {message.id}")
156
+ CurrentlyProcessingJob = True
157
+
158
+ attachmentCount = len(imageList)
159
+ multipleImages = attachmentCount > 1
160
+ if multipleImages:
161
+ batchstart = perf_counter()
162
+ taskType = "Batch Upscale"
163
+ else:
164
+ taskType = "Image Upscaling"
165
+
166
+ for i in range(attachmentCount):
167
+
168
+ """PREPROCESSING"""
169
+ fileid = f"{message.id}_{i}"
170
+ statusTemplate = f"__Status__: %CURRENTSTEP% image from message *{message.id}* **({i+1}/{attachmentCount})**."
171
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "🔄 Preprocessing"))
172
+
173
+ starttime = perf_counter()
174
+ url = imageList[i]
175
+
176
+ extension = url.split(".")[-1]
177
+ #fileid = generate_id(f"{imagepath}\input")
178
+
179
+ inputfile = os.path.join(inputpath, f"{fileid}.{extension}").split("?")[0]
180
+ outputpng = os.path.join(outputpath, f"{fileid}.png")
181
+ outputfile = os.path.join(outputpath, f"{fileid}.{outext}")
182
+
183
+ with open (inputfile, "wb") as f:
184
+ f.write(requests.get(url).content)
185
+
186
+
187
+ """UPSCALE AUTO-CONFIG"""
188
+
189
+ if ai_model == "realesr-animevideov3":
190
+ scale = targetScale
191
+ tilesize = "768"
192
+ resizeRequired = False
193
+ else:
194
+ scale = "4"
195
+ tilesize = "256"
196
+ resizeRequired = True if targetScale == "2" else False
197
+
198
+ #Scale 2 only works with the default model
199
+ execute_upscale = fr"{executablepath} -i {inputfile} -o {outputpng} -n {ai_model} -s {scale} -f png -t {tilesize}"
200
+ print(execute_upscale)
201
+ """UPSCALING"""
202
+ pendtime = perf_counter()
203
+ preprocessingtime = round((pendtime-starttime), 2)
204
+ print(f"PREPROCESS: Completed in {preprocessingtime}s.\nUPSCALE:")
205
+ await jobStatusMessage.edit(content=(statusTemplate.replace("%CURRENTSTEP%", "🧠 Upscaling")+"\nThis may take a while..."))
206
+ #os.system(execute_upscale)
207
+ #subprocess.run(execute_upscale, shell=True)
208
+ upscaleProcess = await asyncio.create_subprocess_shell(execute_upscale)
209
+ await upscaleProcess.wait()
210
+
211
+ uendtime = perf_counter()
212
+
213
+ upscaletime = round((uendtime-pendtime),2)
214
+ print(f"UPSCALE: Completed in {upscaletime}s.")
215
+
216
+
217
+
218
+ """RESIZING"""
219
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "⚙️ Resizing"))
220
+
221
+ #qualityArgs = f" -quality 95 " if outext == "jpg" else ""
222
+ qualityArgs = ""
223
+
224
+ resizeCommand = f"mogrify -format {outext}{qualityArgs} -resize 50% {outputpng}"
225
+ convertCommand = f"mogrify -format {outext}{qualityArgs} {outputpng}"
226
+
227
+ resizeProcess = await asyncio.create_subprocess_shell(resizeCommand if resizeRequired else convertCommand)
228
+ await resizeProcess.wait()
229
+
230
+ rendtime = perf_counter()
231
+ resizingtime = round((rendtime-uendtime), 2)
232
+ print(f"RESIZE: Completed in {resizingtime}s.")
233
+
234
+
235
+
236
+ """DELIVERING"""
237
+ await jobStatusMessage.edit(content=statusTemplate.replace("%CURRENTSTEP%", "✉️ Sending"))
238
+
239
+ try:
240
+
241
+ file=discord.File(outputfile)
242
+ imgurl = f"attachment://{fileid}.{outext}"
243
+ prepembed=discord.Embed(title="Sucessfully Upscaled Image")
244
+ prepembed.set_image(url=imgurl)
245
+
246
+ jobResultMessage = await message.channel.send(embed=prepembed, file=file)
247
+ sendtime = perf_counter()
248
+ localImageUrl = f"{serverurl}/{fileid}.{outext}"
249
+
250
+
251
+ outputImageUrl = jobResultMessage.embeds[0].image.url
252
+ #print(outputImageUrl)
253
+ #outputImageUrl = jobResultMessage.attachments[0].url
254
+
255
+ #print(outputImageUrl)
256
+ embed = discord.Embed(title="Upscaled Image", url=outputImageUrl, description=f"[Local File]({localImageUrl})")
257
+ embed.set_author(name=author, icon_url=message.author.avatar)
258
+ embed.set_image(url=imgurl)
259
+
260
+ sendingtime = round((sendtime-rendtime), 2)
261
+ processingstats = f"Preprocessing: {preprocessingtime}s | Resizing: {resizingtime}s | Sending: {sendingtime}s."
262
+ embed.set_footer(text=f"Took {upscaletime}s to upscale {fileid}.\nUpscaling done with {ai_model}.\n{processingstats}")
263
+
264
+ await jobResultMessage.edit(embed=embed)
265
+
266
+ except discord.errors.HTTPException as e:
267
+ baseErrorMessage = f"There was an error sending the output for image id {fileid}."
268
+ if '413 Payload Too Large' in str(e):
269
+ await message.reply(f"{baseErrorMessage} It was too large for discord to handle.\n```python\n{e}```")
270
+ else:
271
+ await message.reply(f"{baseErrorMessage}\n```python\n{e}```")
272
+
273
+ except Exception as e:
274
+ await message.reply(f"Encountered an error while processing attachment {fileid}\n```python\n{e}```")
275
+
276
+
277
+
278
+ """CLEANING UP"""
279
+ #Already finished the whole job.
280
+ await jobStatusMessage.edit(content=f"\n__Status__: ✅ Job Completed for all {attachmentCount} attachments from message {message.id} at {getloggingtime()}".replace("all 1 attachments", "an attachment"))
281
+ #await message.delete()
282
+ await asyncio.sleep(2)
283
+ await jobStatusMessage.delete()
284
+
285
+
286
+
287
+ print(f"IMAGE: Job finished for messageID {message.id}\n")
288
+ CurrentlyProcessingJob = False
289
+
290
+ else:
291
+ print(f"MESSAGE: {channel}- {author}: {msg}")
292
+ await client.process_commands(message)
293
+
294
+
295
+ """BOT COMMANDS"""
296
+
297
+ @client.command()
298
+ async def test(ctx):
299
+ await ctx.send("Hello!")
300
+
301
+ @client.command()
302
+ async def ping(ctx):
303
+ await ctx.send(f'The ping is {round(client.latency * 1000)}ms.')
304
+
305
+ @client.command()
306
+ async def cleandir(ctx):
307
+ if ctx.author.id in ADMINS:
308
+ await ctx.channel.purge(limit=file_cleanup()+1)
309
+ await ctx.send(f'Successfully images directories', delete_after= 2)
310
+ else:
311
+ await ctx.send('You do not have the permissions to perform this action.')
312
+
313
+ @client.command()
314
+ async def clear(ctx, amount=None):
315
+ if ctx.author.id in ADMINS:
316
+ if amount == "all":
317
+ amount = 100
318
+ else:
319
+ try:
320
+ amount = int(amount)
321
+ except:
322
+ amount = 2
323
+ await ctx.channel.purge(limit=amount)
324
+ await ctx.send(f"Finished clearing {amount} messages {ctx.message.author.mention}", delete_after=4)
325
+ else:
326
+ await ctx.send('You do not have the permissions to clear messages using this bot.')
327
+
328
+ @client.command(aliases = ['enable-upscale'])
329
+ async def toggleUpscale(ctx):
330
+ result = toggleMediaChannel(int(ctx.channel.id))
331
+ #print(result)
332
+ await ctx.send(result)
333
+
334
+ logmsg = f"\nBOT: Attempting to starting the bot at {getloggingtime()} GMT+7"
335
+ print(logmsg)
336
+ client.run(TOKEN)
readme.md ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Upscaler bot
2
+
3
+ ## A discord bot to upscale images!
4
+
5
+ ## Features:
6
+ - Easy Upscaling: simply paste the image in the specified channel, and it'll be done in a minute!
7
+ - Local mode: option to receive image instantly via the local network (requires the Flask repository).
8
+ - Multiple modes: choose whether optimize for speed, general photos, or anime/graphics!
9
+
10
+ ## Requirements:
11
+ - discord, pytz, python-dotenv, requests
12
+ - Use `pip install -r requirements.txt`
13
+ - [Real-ESRGAN](https://github.com/xinntao/Real-ESRGAN/releases/tag/v0.2.5.0) vulkan executable.
14
+ - Download the zip file from the link above and extract to the real-esrgan folder.
15
+ - My [Flask server](https://github.com/PawinChan/Flask).
16
+ - Clone to ~/Code/Flask and run.
real-esrgan/Upscaling Notes.txt ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ Sample Command:
2
+
3
+ ./realesrgan-ncnn-vulkan -i input -o output -s 2 -t 512 -n realesrgan-x4plus-anime -g 0 -j 1:1:1 -f png -v
4
+ ===============================================================================
5
+
6
+ Usage: realesrgan-ncnn-vulkan -i infile -o outfile [options]...
7
+
8
+ -h show this help
9
+ -i input-path input image path (jpg/png/webp) or directory
10
+ -s scale upscale ratio (can be 2, 3, 4. default=4)
11
+ -t tile-size tile size (>=32/0=auto, default=0) can be 0,0,0 for multi-gpu
12
+ -m model-path folder path to the pre-trained models. default=models
13
+ -n model-name model name (default=realesr-animevideov3, can be realesr-animevideov3 | realesrgan-x4plus | realesrgan-x4plus-anime | realesrnet-x4plus)
14
+ -g gpu-id gpu device to use (default=auto) can be 0,1,2 for multi-gpu
15
+ -x enable tta mode
16
+ -f format output image format (jpg/png/webp, default=ext/png)
17
+ -v verbose output
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ discord
2
+ requests
3
+ python-dotenv
4
+ pytz
resources/mediaChannels.json ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ [
2
+ 1083235916354703430,
3
+ 896342418859892736,
4
+ 915795164721709077,
5
+ 1086939553341911141,
6
+ 1119562864995344394
7
+ ]
upscaler-bot.code-workspace ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "folders": [
3
+ {
4
+ "path": "."
5
+ },
6
+ {
7
+ "path": "../../Software/real-esrgan/discordbot"
8
+ }
9
+ ],
10
+ "settings": {}
11
+ }
upscaler-docs.md ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+
2
+ ## Help Command
3
+ ```
4
+ PS C:\Users\Pawin\Software\real-esrgan> ./realesrgan-ncnn-vulkan.exe
5
+ Usage: realesrgan-ncnn-vulkan -i infile -o outfile [options]...
6
+
7
+ -h show this help
8
+ -i input-path input image path (jpg/png/webp) or directory
9
+ -o output-path output image path (jpg/png/webp) or directory
10
+ -s scale upscale ratio (can be 2, 3, 4. default=4)
11
+ -t tile-size tile size (>=32/0=auto, default=0) can be 0,0,0 for multi-gpu
12
+ -m model-path folder path to the pre-trained models. default=models
13
+ -n model-name model name (default=realesr-animevideov3, can be realesr-animevideov3 | realesrgan-x4plus | realesrgan-x4plus-anime | realesrnet-x4plus)
14
+ -g gpu-id gpu device to use (default=auto) can be 0,1,2 for multi-gpu
15
+ -j load:proc:save thread count for load/proc/save (default=1:2:2) can be 1:2,2,2:2 for multi-gpu
16
+ -x enable tta mode
17
+ -f format output image format (jpg/png/webp, default=ext/png)
18
+ -v verbose output
19
+ ```
20
+
21
+ ## STDOUT Output
22
+ ```
23
+ [0 Intel(R) HD Graphics 520] queueC=0[1] queueG=0[1] queueT=0[1]
24
+ [0 Intel(R) HD Graphics 520] bugsbn1=0 bugbilz=209 bugcopc=0 bugihfa=0
25
+ [0 Intel(R) HD Graphics 520] fp16-p/s/a=1/1/1 int8-p/s/a=1/1/1
26
+ [0 Intel(R) HD Graphics 520] subgroup=32 basic=1 vote=1 ballot=1 shuffle=1
27
+ 0.00%
28
+ input.png -> output.png done
29
+ ```