Take IPP support back, update doc, few other thing

This commit is contained in:
NaitLee 2022-04-13 02:37:53 +08:00
parent 2f4e111419
commit 1768c5055e
9 changed files with 273 additions and 63 deletions

View File

@ -1,4 +1,4 @@
English | [Deutsch](./README.de_DE.md) | [简体中文](./README.zh_CN.md) English | [Deutsch](./readme.i18n/README.de_DE.md) | [简体中文](./readme.i18n/README.zh_CN.md)
# Cat-Printer # Cat-Printer
@ -16,22 +16,37 @@ Currently:
## Features ## Features
*Currently it's in development. More will be here soon!*
| Available | Partial | Planned |
|-----------------|-----------|---------------|
| Web Interface | CUPS/IPP* | Visual Editor |
| Print a Picture | | Help/Manual |
| Command-line | | Text Printing |
<!-- May comment the line below if there are no * -->
\* In development code. Will be released in a short period.
*Along with...*
- Simple! - Simple!
- Operate via a Web UI just in browser, - Operate via a Web UI just in browser,
- or get the Android release! - or get the Android release!
- ~~Feature-rich~~
- Currently it's in development. More will be there soon!
- Friendly! - Friendly!
- Language support! You can participate in translation! - Language support! You can participate in translation!
- Good user interface, adaptive to PC/mobile and light/dark theme! - Good user interface, adaptive to PC/mobile and light/dark theme!
- Cross platform! - Cross platform!
- Newer Windows 10 and above - Newer Windows 10 and above
- GNU/Linux - GNU/Linux
- MacOS *(Needs testing)* - MacOS *(Needs testing)*
- and a lot of extra efforts for Android! - and a lot of extra efforts for Android!
- Free, as in [freedom](https://www.gnu.org/philosophy/free-sw.html)! - Free, as in [freedom](https://www.gnu.org/philosophy/free-sw.html)!
- Unlike the "original" proprietary app, - Unlike the "original" proprietary app,
this project is for everyone that concerns *open-mind and freedom*! this project is for everyone that concerns *open-mind and freedom*!
- and Fun! - and Fun!
- Do whatever you like! - Do whatever you like!
@ -99,7 +114,7 @@ See file `COPYING`, `LICENSE`, and detail of used JavaScript in file `www/jslice
## Development ## Development
You may interested in language support, anyway. See the translation files in directory `www/lang`! You may interested in language support, anyway. See the translation files in directory `www/lang` and `readme.i18n`!
Note: you can correct some mistakes in them, if there are any. Also feel free to make it (truly) better! Note: you can correct some mistakes in them, if there are any. Also feel free to make it (truly) better!
Also interested in code development? See [development.md](development.md)! Also interested in code development? See [development.md](development.md)!
@ -111,5 +126,5 @@ Also interested in code development? See [development.md](development.md)!
- [roddeh-i18n](https://github.com/roddeh/i18njs), good work! - [roddeh-i18n](https://github.com/roddeh/i18njs), good work!
- [python-for-android](https://python-for-android.readthedocs.io/en/latest/), though there are some painful troubles - [python-for-android](https://python-for-android.readthedocs.io/en/latest/), though there are some painful troubles
- [AdvancedWebView](https://github.com/delight-im/Android-AdvancedWebView) for saving my life from Java - [AdvancedWebView](https://github.com/delight-im/Android-AdvancedWebView) for saving my life from Java
- Stack Overflow & the whole Internet, you let me know Android `Activity` all from empty - Stack Overflow & the whole Internet, you let me know Android `Activity` all from beginning
- ... Everyone is Awesome! - ... Everyone is Awesome!

12
TODO
View File

@ -1,8 +1,16 @@
Note: not ordered. do whatever I/you want
+ Check GB03, again
+ Hacky text printing, typewriter-like, with PF2 font + Hacky text printing, typewriter-like, with PF2 font
+ Better MTU/freq moderation ok I won't forget frontend, but it's different
+ Better CLI, e.g. invoke imagemagick if input is not PBM + Better CLI, e.g. invoke imagemagick if input is not PBM
+ (Re-)support CUPS & IPP + Consider better MTU adaption
+ Consider better BLE traffic regulation, with BLE notification
+ Consider the printer 'text' mode, it can make things faster
+ Consider more control to something like 'energy'
+ Consider keeping server backend (more) secure to be used by IPP
+ Clean up CUPS/IPP code
+ Make a build guide for android: + Make a build guide for android:
Summary the hacks to p4a, bleak p4a recipe, p4a webview bootstrap, and AdvancedWebView Summary the hacks to p4a, bleak p4a recipe, p4a webview bootstrap, and AdvancedWebView
+ More frontend usability, more functions + More frontend usability, more functions

0
additional/__init__.py Normal file
View File

View File

@ -26,10 +26,12 @@ class I18n():
self.load_file(os.path.join(search_path, name)) self.load_file(os.path.join(search_path, name))
def load_file(self, name): def load_file(self, name):
'Load an i18n json file'
with open(name, 'r', encoding='utf-8') as file: with open(name, 'r', encoding='utf-8') as file:
self.load_data(file.read()) self.load_data(file.read())
def load_data(self, raw_json): def load_data(self, raw_json):
'Load i18n json data (from str)'
data = json.loads(raw_json) data = json.loads(raw_json)
for key in data['values']: for key in data['values']:
self.data['values'][key] = data['values'][key] self.data['values'][key] = data['values'][key]
@ -60,5 +62,5 @@ class I18n():
if string is None: if string is None:
string = data string = data
for j in i: for j in i:
string = string.replace('%%{%s}' % j, i[j]) string = string.replace(f'%%{j}', i[j])
return string return string

100
additional/ipp.py Normal file
View File

@ -0,0 +1,100 @@
''' Provide *very* basic CUPS/IPP support
Extracted from version 0.0.2, do more cleaning later...
'''
import platform
import subprocess
class IPP():
'https://datatracker.ietf.org/doc/html/rfc8010'
server = None
printer = None
def __init__(self, server, printer):
self.server = server
self.printer = printer
async def handle_ipp(self, data):
'Handle an IPP protocol request'
server = self.server
# len_data = len(data)
# ipp_version_number = data[0:2]
# ipp_operation_id = data[2:4]
# ipp_request_id = data[4:8]
ipp_operation_attributes_tag = data[8]
attributes = {}
data_to_print = b''
# this is silly. i want to use io.BytesIO
if ipp_operation_attributes_tag == 0x01:
pointer = 9
next_name_length_at = 10
next_value_length_at = 10
name = b''
value = b''
while data[pointer] != 0x03:
tag = data[pointer:pointer + 1]
pointer += 1
if tag[0] < 0x10: # delimiter-tag
continue
next_name_length_at = pointer + data[pointer] * 0x0100 + data[pointer + 1] + 2
pointer += 2
while pointer < next_name_length_at:
name = name + data[pointer:pointer + 1]
pointer += 1
next_value_length_at = pointer + data[pointer] * 0x0100 + data[pointer + 1] + 2
pointer += 2
while pointer < next_value_length_at:
value = value + data[pointer:pointer + 1]
pointer += 1
attributes[name] = (tag, value)
name = b''
value = b''
pointer += 1
data_to_print = data[pointer:]
# there are hard coded minimal response. this "just works" on cups
if data_to_print == b'':
try:
server.send_response(200)
server.send_header('Content-Type', 'application/ipp')
server.end_headers()
server.wfile.write(
b'\x01\x01\x00\x00\x00\x00\x00\x01\x01\x03'
)
except BrokenPipeError:
pass
return
platform_system = platform.system()
# https://ghostscript.com/doc/9.54.0/Use.htm#Output_device
if platform_system == 'Windows':
gsexe = 'gswin32c.exe'
elif platform_system == 'OS/2':
gsexe = 'gsos2'
else:
gsexe = 'gs'
gsproc = subprocess.Popen([
gsexe,
'-q', '-sDEVICE=pbmraw', '-dNOPAUSE', '-dBATCH', '-dSAFER',
'-dFIXEDMEDIA', '-g384x543', '-r46.4441219158x46.4441219158',
'-dFitPage', '-dFitPage',
'-sOutputFile=-', '-'
], executable=gsexe, stdin=subprocess.PIPE, stdout=subprocess.PIPE)
pbm_data, _ = gsproc.communicate(data_to_print)
try:
if gsproc.wait() == 0:
info = server.path[1:]
is_found = await server.printer.filter_device(info, server.settings.scan_time)
if not is_found:
... # TODO: Make IPP can report some errors
raise Exception(f'No printer found with info: {info}')
await server.printer.print_data(pbm_data)
else:
raise Exception('Error on invoking Ghostscript')
server.send_response(200)
server.send_header('Content-Type', 'application/ipp')
server.end_headers()
server.wfile.write(
b'\x01\x01\x00\x00\x00\x00\x00\x01\x01\x03'
)
except Exception as _:
server.send_response(500)
server.send_header('Content-Type', 'application/ipp')
server.end_headers()
server.wfile.write(b'')

View File

@ -9,11 +9,14 @@ from bleak import BleakClient, BleakScanner
from bleak.exc import BleakError, BleakDBusError from bleak.exc import BleakError, BleakDBusError
try: try:
from i18n import I18n from additional.i18n import I18n
except ImportError: except ImportError:
class I18n(): class I18n():
'Dummy i18n in case "full" version is missing'
def __init__(self, _search_path=None, _lang=None, _fallback=None): def __init__(self, _search_path=None, _lang=None, _fallback=None):
pass pass
def __getitem__(self, keys): def __getitem__(self, keys):
if not isinstance(keys, tuple): if not isinstance(keys, tuple):
keys = (keys, ) keys = (keys, )
@ -21,9 +24,11 @@ except ImportError:
i18n = I18n('www/lang') i18n = I18n('www/lang')
class PrinterError(Exception): class PrinterError(Exception):
'Error of Printer driver' 'Error of Printer driver'
models = ('GT01', 'GB01', 'GB02', 'GB03') models = ('GT01', 'GB01', 'GB02', 'GB03')
crc8_table = [ crc8_table = [
@ -75,7 +80,10 @@ def reverse_binary(value: int):
return int(f"{bin(value)[2:]:0>8}"[::-1], 2) return int(f"{bin(value)[2:]:0>8}"[::-1], 2)
def make_command(command: int, payload: Union[bytes, bytearray], *, prefix: List[int]=None) -> bytearray: def make_command(
command: int, payload: Union[bytes, bytearray], *,
prefix: List[int] = None
) -> bytearray:
'Make a `bytearray` with command data, which can be sent to printer directly to operate' 'Make a `bytearray` with command data, which can be sent to printer directly to operate'
if len(payload) > 0x100: if len(payload) > 0x100:
raise Exception('Too large payload') raise Exception('Too large payload')
@ -164,40 +172,48 @@ class PrinterDriver():
def _read_pbm(self, path: str = None, data: bytes = None): def _read_pbm(self, path: str = None, data: bytes = None):
if path is not None and path != '-': if path is not None and path != '-':
file = open(path, 'rb') file = open(path, 'rb')
data = file.read()
file.close()
elif data is not None: elif data is not None:
file = io.BytesIO(data) pass
else: else:
file = sys.stdin.buffer data = sys.stdin.buffer.read()
signature = file.readline() if data[0:3] != b'P4\n':
if signature != b'P4\n':
raise Exception('Specified file is not a PBM image') raise Exception('Specified file is not a PBM image')
width, height = self.standard_width, 0 # there can be several "chunks", by e.g. cat-ing several files, or ghostscript output
chunks = data.split(b'P4\n')[1:]
result = b''
total_height = 0
for chunk in chunks:
page = io.BytesIO(chunk)
while True: while True:
# There can be comments. Skip them # There can be comments. Skip them
line = file.readline()[0:-1] line = page.readline()[0:-1]
if line[0:1] != b'#': if line[0:1] != b'#':
break break
width, height = [int(x) for x in line.split(b' ')[0:2]] width, height = [int(x) for x in line.split(b' ')[0:2]]
if width != self.standard_width: if width != self.standard_width:
raise Exception('PBM image width is not 384px') raise Exception('PBM image width is not 384px')
total_height += height
expected_data_size = self.pbm_data_per_line * height expected_data_size = self.pbm_data_per_line * height
data = file.read() raw_data = page.read()
if path is not None and path != '-': data_size = len(raw_data)
file.close()
data_size = len(data)
if data_size != expected_data_size: if data_size != expected_data_size:
raise Exception('Broken PBM file data') raise Exception('Broken PBM file data')
if self.dry_run: if self.dry_run:
# Dry run: put blank data # Dry run: put blank data
data = b'\x00' * expected_data_size result += b'\x00' * expected_data_size
return PBMData(width, height, data) else:
result += raw_data
return PBMData(self.standard_width, total_height, result)
def _pbm_data_to_raw(self, data: PBMData): def _pbm_data_to_raw(self, data: PBMData):
buffer = bytearray() buffer = bytearray()
# new/old print command # new/old print command
if self.name == 'GB03': if self.name == 'GB03':
buffer.append(0x12) buffer.append(0x12)
buffer += bytearray([0x51, 0x78, 0xa3, 0x00, 0x01, 0x00, 0x00, 0x00, 0xff]) buffer += bytearray([0x51, 0x78, 0xa3, 0x00,
0x01, 0x00, 0x00, 0x00, 0xff])
for key in data.args: for key in data.args:
buffer += make_command(key, data.args[key]) buffer += make_command(key, data.args[key])
buffer += make_command( buffer += make_command(
@ -267,6 +283,7 @@ class PrinterDriver():
if device.name in models: if device.name in models:
result.append(device) result.append(device)
return result return result
async def search_printer(self, timeout: int): async def search_printer(self, timeout: int):
'Search for a printer, returns `None` if not found' 'Search for a printer, returns `None` if not found'
timeout = timeout or 3 timeout = timeout or 3
@ -289,6 +306,26 @@ class PrinterDriver():
buffer = self._pbm_data_to_raw(pbm_data) buffer = self._pbm_data_to_raw(pbm_data)
await self.send_buffer(buffer, address) await self.send_buffer(buffer, address)
async def filter_device(self, info: str, timeout: float = 5.0) -> bool:
'Find a suitable device with `info`: Bluetooth name or MAC address, or empty string'
devices = await self.search_all_printers(timeout)
if len(devices) == 0:
return False
if info in models:
for device in devices:
if device.name == info:
self.name, self.address = device.name, device.address
break
elif info[2::3] == ':::::':
for device in devices:
if device.address.lower() == info.lower():
self.name, self.address = device.name, device.address
break
else:
device = devices[0]
self.name, self.address = device.name, device.address
return True
async def _main(): async def _main():
'Main routine for direct command line execution' 'Main routine for direct command line execution'
@ -335,6 +372,7 @@ async def _main():
await printer.print_file(cmdargs.file) await printer.print_file(cmdargs.file)
print(i18n['finished']) print(i18n['finished'])
async def main(): async def main():
'Run the `_main` routine while catching exceptions' 'Run the `_main` routine while catching exceptions'
try: try:

View File

@ -15,21 +15,36 @@ Gegenwärtig:
## Funktionen ## Funktionen
*Derzeit befindet sich die Software im Alpha-Stadium. Mehr wird es bald geben!*
| Available | Partial | Planned |
|-----------------|-----------|---------------|
| Web Interface | CUPS/IPP* | Visual Editor |
| Print a Picture | | Help/Manual |
| Command-line | | Text Printing |
<!-- May comment the line below if there are none -->
\* In development code. Will be released in a short period.
*Along with…*
- Simple! - Simple!
- Bedienung über eine Web-UI direkt im Browser, - Bedienung über eine Web-UI direkt im Browser,
- oder besorgen Sie sich die Android-Version! - oder besorgen Sie sich die Android-Version!
- ~~Umfangreiche Funktionen~~
- Derzeit befindet sich die Software im Alpha-Stadium. Mehr wird es bald geben!
- Friendly! - Friendly!
- Sprachunterstützung! Sie können sich an der Übersetzung beteiligen! - Sprachunterstützung! Sie können sich an der Übersetzung beteiligen!
- Gute Benutzeroberfläche, mit PC-/Mobil-/Licht-/Dunkelmodus-Varianten! (Systemkonfiguration adaptiv) - Gute Benutzeroberfläche, mit PC-/Mobil-/Licht-/Dunkelmodus-Varianten! (Systemkonfiguration adaptiv)
- Plattformübergreifend! - Plattformübergreifend!
- Neuere Windows 10 und darüber - Neuere Windows 10 und darüber
- GNU/Linux - GNU/Linux
- MacOS *(muss getestet werden* - MacOS *(muss getestet werden*
- und eine Menge zusätzlicher Anstrengungen für Android! - und eine Menge zusätzlicher Anstrengungen für Android!
- Frei, wie in [freedom](https://www.gnu.org/philosophy/free-sw.html)! - Frei, wie in [freedom](https://www.gnu.org/philosophy/free-sw.html)!
- Anders als die "offizielle" proprietäre App, ist dieses Projekt für alle, denen *offener Geist und Freiheit* wichtig sind! - Anders als die "offizielle" proprietäre App, ist dieses Projekt für alle, denen *offener Geist und Freiheit* wichtig sind!
- und Fun! - und Fun!
- Mach, was du willst! - Mach, was du willst!
@ -92,7 +107,7 @@ Siehe Datei `COPYING`, `LICENSE` und Details zum verwendeten JavaScript in der D
## Development ## Development
Vielleicht sind Sie ohnehin an der Sprachunterstützung interessiert. Siehe die Übersetzungsdateien im Verzeichnis `www/lang`! Vielleicht sind Sie ohnehin an der Sprachunterstützung interessiert. Siehe die Übersetzungsdateien im Verzeichnis `www/lang` und `readme.i18n`!
Interessieren Sie sich auch für Code-Entwicklung? Siehe [development.md](development.md)! Interessieren Sie sich auch für Code-Entwicklung? Siehe [development.md](development.md)!

View File

@ -15,21 +15,36 @@
## 特性 ## 特性
*当前仍在继续开发。以后会有更多!*
| 可用 | 部分 | 计划 |
|-----------|-----------|---------------|
| 网页界面 | CUPS/IPP* | 可视化编辑器 |
| 打印图片 | | 帮助/文档 |
| 命令行 | | 文本打印 |
<!-- 若没有 *,可注释下一句 -->
\* 存在于开发代码中。将在短时间内发布。
*当然还有……*
- 简易! - 简易!
- 在网页界面进行操作, - 在网页界面进行操作,
- 或者获取安卓应用! - 或者获取安卓应用!
- ~~功能丰富~~
- 当前仍在继续开发。以后会有更多!
- 友好! - 友好!
- 语言支持!您可参与翻译! - 语言支持!您可参与翻译!
- 良好的用户界面,可适应桌面/移动端/明暗主题! - 良好的用户界面,可适应桌面/移动端/明暗主题!
- 跨平台! - 跨平台!
- 较新的 Windows 10 及以上 - 较新的 Windows 10 及以上
- GNU/Linux - GNU/Linux
- MacOS *(需要测试)* - MacOS *(需要测试)*
- 在安卓上也花了些功夫呢! - 在安卓上也花了些功夫呢!
- 是[自由软件](https://www.gnu.org/philosophy/free-sw.html) - 是[自由软件](https://www.gnu.org/philosophy/free-sw.html)
- 不像“原版”专有应用,此作品为在乎*开放思想与计算自由*的人而生! - 不像“原版”专有应用,此作品为在乎*开放思想与计算自由*的人而生!
- 有意思! - 有意思!
- 做什么都可以! - 做什么都可以!
@ -97,19 +112,20 @@ Copyright © 2022 NaitLee Soft. 保留一些权利。
## 开发 ## 开发
您可能对翻译工作感兴趣。可于目录 `www/lang` 中查看翻译文件! 您可能对翻译工作感兴趣。可于目录 `www/lang``readme.i18n` 中查看翻译文件!
注: 注:
1. 通常英语与简体中文同时更新。请考虑其他,如繁体中文(需注意在繁体中与简体的用字、技术术语差别)。 1. 通常英语与简体中文同时更新。请考虑其他,如繁体中文(需注意在繁体中与简体的用字、技术术语差别)。
2. 如果您有(真的)能力,您也可以纠正/改善某些翻译! 2. 如果(真的)能力,您也可以纠正/改善某些翻译!
还想写代码?看看 [development.md](development.md)!(英文) 还想写代码?看看 [development.md](development.md)!(英文)
### 鸣谢 ### 鸣谢
- 当然不能没有 Python 和 Web 技术! - 当然不能没有 Python 和 Web 技术!
- [Bleak](https://bleak.readthedocs.io/en/latest/) 蓝牙低功耗库,牛! - [Bleak](https://bleak.readthedocs.io/en/latest/) 跨平台蓝牙低功耗库,牛!
- [roddeh-i18n](https://github.com/roddeh/i18njs),好 - [roddeh-i18n](https://github.com/roddeh/i18njs)好!
- [python-for-android](https://python-for-android.readthedocs.io/en/latest/),虽然有些麻烦的地方 - [python-for-android](https://python-for-android.readthedocs.io/en/latest/),虽然有些麻烦的地方
- [AdvancedWebView](https://github.com/delight-im/Android-AdvancedWebView) 从 Java 拯救了我的生命 - [AdvancedWebView](https://github.com/delight-im/Android-AdvancedWebView) 从 Java 拯救了我的生命
- Stack Overflow 和互联网,你们让我无中生有地了解了安卓“活动” `Activity` - Stack Overflow 和整个互联网,你们让我从零开始了解了安卓“活动” `Activity`
- ……每个人都是好样的! - ……每个人都是好样的!

View File

@ -34,9 +34,6 @@ class PrinterServerError(Exception):
if len_args > 1: if len_args > 1:
self.details = args[1] self.details = args[1]
Printer = PrinterDriver()
server = None
def log(message): def log(message):
'For logging a message' 'For logging a message'
print(message) print(message)
@ -66,6 +63,8 @@ class PrinterServer(BaseHTTPRequestHandler):
'frequency': 0.8, 'frequency': 0.8,
'dry_run': False 'dry_run': False
}) })
printer = PrinterDriver()
ipp = None
def log_request(self, _code=200, _size=0): def log_request(self, _code=200, _size=0):
pass pass
def log_error(self, *_args): def log_error(self, *_args):
@ -126,22 +125,23 @@ class PrinterServer(BaseHTTPRequestHandler):
'Save config file' 'Save config file'
with open(self.settings.config_path, 'w', encoding='utf-8') as file: with open(self.settings.config_path, 'w', encoding='utf-8') as file:
json.dump(self.settings, file, indent=4) json.dump(self.settings, file, indent=4)
def update_printer(self):
'Update `PrinterDriver` state/config'
self.printer.dry_run = self.settings.dry_run
self.printer.frequency = float(self.settings.frequency)
if self.settings.printer is not None:
self.printer.name, self.printer.address = self.settings.printer.split(',')
def handle_api(self): def handle_api(self):
'Handle API request from POST' 'Handle API request from POST'
content_length = int(self.headers.get('Content-Length')) content_length = int(self.headers.get('Content-Length'))
body = self.rfile.read(content_length) body = self.rfile.read(content_length)
api = self.path[1:] api = self.path[1:]
if api == 'print': if api == 'print':
if self.settings.printer is None: self.update_printer()
# usually can't encounter, though
raise PrinterServerError('No printer address specified')
Printer.dry_run = self.settings.dry_run
Printer.frequency = float(self.settings.frequency)
Printer.name, Printer.address = self.settings.printer.split(',')
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
try: try:
devices = loop.run_until_complete( devices = loop.run_until_complete(
Printer.print_data(body) self.printer.print_data(body)
) )
self.api_success() self.api_success()
finally: finally:
@ -152,7 +152,7 @@ class PrinterServer(BaseHTTPRequestHandler):
loop = asyncio.new_event_loop() loop = asyncio.new_event_loop()
try: try:
devices = loop.run_until_complete( devices = loop.run_until_complete(
Printer.search_all_printers(float(self.settings.scan_time)) self.printer.search_all_printers(float(self.settings.scan_time))
) )
finally: finally:
loop.close() loop.close()
@ -196,6 +196,23 @@ class PrinterServer(BaseHTTPRequestHandler):
self.send_header('Content-Type', mime('txt')) self.send_header('Content-Type', mime('txt'))
self.end_headers() self.end_headers()
return return
if self.headers.get('Content-Type') == 'application/ipp':
if self.ipp is None:
try:
from additional.ipp import IPP
self.load_config()
except ImportError:
# TODO: Better response?
return
self.ipp = IPP(self, self.printer)
self.update_printer()
body = self.rfile.read(content_length)
loop = asyncio.new_event_loop()
try:
loop.run_until_complete(self.ipp.handle_ipp(body))
finally:
loop.close()
return
try: try:
self.handle_api() self.handle_api()
return return
@ -231,7 +248,6 @@ def serve():
if '-a' in sys.argv: if '-a' in sys.argv:
print('Will listen on ALL addresses') print('Will listen on ALL addresses')
listen_all = True listen_all = True
global server
# Again, Don't use ThreadingHTTPServer if you're going to use pyjnius! # Again, Don't use ThreadingHTTPServer if you're going to use pyjnius!
server = HTTPServer(('' if listen_all else address, port), PrinterServer) server = HTTPServer(('' if listen_all else address, port), PrinterServer)
service_url = f'http://{address}:{port}/' service_url = f'http://{address}:{port}/'