This commit is contained in:
Blallo 2021-08-25 12:03:39 -03:00
parent 7e99e31f43
commit 5124f2d3ca
No known key found for this signature in database
GPG key ID: 0CBE577C9B72DC3F
5 changed files with 85 additions and 39 deletions

View file

@ -11,7 +11,6 @@ from .config_manager import get_config
logging.basicConfig(stream=sys.stdout)
logger = logging.getLogger("cli")
CWD = os.getcwd()
@ -60,8 +59,10 @@ class DateTimeAction(Action):
raise ValueError("'%s' is not a valid datetime" % values)
setattr(namespace, self.dest, parsed_val)
code_dir = os.path.dirname(os.path.realpath(__file__))
def common_pre():
prechecks = [pre_check_user, pre_check_permissions, pre_check_ffmpeg]
configs = ["default_config.py"]

View file

@ -7,8 +7,15 @@ import logging
import sys
from datetime import datetime, timedelta
from sqlalchemy import (Column, DateTime, Boolean, Integer, String, create_engine,
inspect)
from sqlalchemy import (
Column,
DateTime,
Boolean,
Integer,
String,
create_engine,
inspect,
)
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy.orm import sessionmaker

View file

@ -35,7 +35,7 @@ TAG_LICENSE_URI = None
# defaults
STATIC_FILES = "static/"
STATIC_PAGES = "pages/"
if getattr(sys, 'frozen', False): # pyinstaller
if getattr(sys, "frozen", False): # pyinstaller
STATIC_FILES = os.path.join(sys._MEIPASS, STATIC_FILES)
STATIC_PAGES = os.path.join(sys._MEIPASS, STATIC_PAGES)
else:

View file

@ -11,6 +11,7 @@ from .config_manager import get_config
logger = logging.getLogger("forge")
Validator = Callable[[datetime, datetime, str], bool]
def get_timefile_exact(time) -> str:
"""
time is of type `datetime`; it is not "rounded" to match the real file;
@ -21,14 +22,14 @@ def get_timefile_exact(time) -> str:
)
def round_timefile(exact:datetime) -> datetime:
def round_timefile(exact: datetime) -> datetime:
"""
This will round the datetime, so to match the file organization structure
"""
return datetime(exact.year, exact.month, exact.day, exact.hour)
def get_timefile(exact:datetime) -> str:
def get_timefile(exact: datetime) -> str:
return get_timefile_exact(round_timefile(exact))
@ -88,9 +89,17 @@ def mp3_join(named_intervals):
cmdline += ["-t", str(len(files) * 3600 - (startskip + endskip))]
return cmdline
def create_mp3(start: datetime, end: datetime, outfile: str, options={}, validator: Optional[Validator] = None, **kwargs):
def create_mp3(
start: datetime,
end: datetime,
outfile: str,
options={},
validator: Optional[Validator] = None,
**kwargs
):
if validator is None:
validator = lambda s,e,f: True
validator = lambda s, e, f: True
intervals = [
(get_timefile(begin), start_cut, end_cut)
for begin, start_cut, end_cut in get_files_and_intervals(start, end)
@ -126,9 +135,17 @@ def create_mp3(start: datetime, end: datetime, outfile: str, options={}, validat
metadata_list.append("-metadata")
metadata_list.append("%s=%s" % (tag, value))
prefix, suffix = os.path.basename(outfile).split('.', 1)
tmp_file = tempfile.NamedTemporaryFile(suffix='.%s' % suffix, prefix='forge-%s' % prefix, delete=False)
cmd = mp3_join(intervals) + metadata_list + ['-y'] + get_config()["FFMPEG_OPTIONS"] + [tmp_file.name]
prefix, suffix = os.path.basename(outfile).split(".", 1)
tmp_file = tempfile.NamedTemporaryFile(
suffix=".%s" % suffix, prefix="forge-%s" % prefix, delete=False
)
cmd = (
mp3_join(intervals)
+ metadata_list
+ ["-y"]
+ get_config()["FFMPEG_OPTIONS"]
+ [tmp_file.name]
)
logger.info("Running %s", " ".join(cmd))
p = Popen(cmd)
if get_config()["FORGE_TIMEOUT"] == 0:

View file

@ -39,14 +39,16 @@ def rec_sanitize(rec):
d["endtime"] = date_write(d["endtime"])
return d
@app.on_event("startup")
async def startup_event():
global db
common_pre()
if get_config()['DEBUG']:
if get_config()["DEBUG"]:
logging.basicConfig(level=logging.DEBUG)
db = RecDB(get_config()["DB_URI"])
@app.get("/date/date")
def date():
n = datetime.now()
@ -76,11 +78,13 @@ def help():
+ "/custom?strftime=FORMAT : get now().strftime(FORMAT)"
)
class CreateInfo(BaseModel):
starttime: Optional[str] = None
endtime: Optional[str] = None
name: str = ""
@app.post("/api/create")
async def create(req: CreateInfo = None):
ret = {}
@ -100,9 +104,11 @@ async def create(req: CreateInfo = None):
"Nuova registrazione creata! (id:%d)" % ret.id, rec=rec_sanitize(rec)
)
class DeleteInfo(BaseModel):
id: int
@app.post("/api/delete")
def delete(req: DeleteInfo):
if db.delete(req.id):
@ -113,15 +119,18 @@ def delete(req: DeleteInfo):
def timefield_factory():
return int(time.time())
TimeField = Field(default_factory=timefield_factory)
class UpdateInfo(BaseModel):
name: str = ""
starttime: int =Field(default_factory=timefield_factory)
endtime: int =Field(default_factory=timefield_factory)
starttime: int = Field(default_factory=timefield_factory)
endtime: int = Field(default_factory=timefield_factory)
filename: Optional[str] = None
@app.post("/api/update/{recid}")
async def update(recid: int, req: UpdateInfo):
newrec = {}
@ -142,10 +151,12 @@ async def update(recid: int, req: UpdateInfo):
class GenerateInfo(BaseModel):
id: int
class GenerateResponse(BaseModel):
status: str
message: str
@app.post("/api/generate/{recid}")
async def generate(recid: int, response: Response, background_tasks: BackgroundTasks):
# prendiamo la rec in causa
@ -162,9 +173,9 @@ async def generate(recid: int, response: Response, background_tasks: BackgroundT
> get_config()["FORGE_MAX_DURATION"]
):
return JSONResponse(
status_code = 400,
status= "error",
message= "The requested recording is too long"
status_code=400,
status="error",
message="The requested recording is too long"
+ " (%d seconds)" % (rec.endtime - rec.starttime).total_seconds(),
)
rec.filename = get_config()["AUDIO_OUTPUT_FORMAT"] % {
@ -204,6 +215,7 @@ async def generate(recid: int, response: Response, background_tasks: BackgroundT
rec=rec_sanitize(rec),
)
def get_duration(fname) -> float:
lineout = check_output(
[
@ -214,7 +226,8 @@ def get_duration(fname) -> float:
"format=duration",
"-i",
fname,
]).split(b'\n')
]
).split(b"\n")
duration = next(l for l in lineout if l.startswith(b"duration="))
value = duration.split(b"=")[1]
return float(value)
@ -225,23 +238,30 @@ def get_validator(expected_duration_s: float, error_threshold_s: float) -> Valid
try:
duration = get_duration(fpath)
except Exception as exc:
logger.exception('Error determining duration of %s', fpath)
logger.exception("Error determining duration of %s", fpath)
return False
logger.debug('expect %s to be %.1f±%.1fs, is %.1f', fpath, expected_duration_s, error_threshold_s, duration)
logger.debug(
"expect %s to be %.1f±%.1fs, is %.1f",
fpath,
expected_duration_s,
error_threshold_s,
duration,
)
if duration > expected_duration_s + error_threshold_s:
return False
if duration < expected_duration_s - error_threshold_s:
return False
return True
return validator
def generate_mp3(db_id: int, **kwargs):
'''creates and mark it as ready in the db'''
if get_config()['FORGE_VERIFY']:
"""creates and mark it as ready in the db"""
if get_config()["FORGE_VERIFY"]:
validator = get_validator(
(kwargs['end'] - kwargs['start']).total_seconds(),
get_config()['FORGE_VERIFY_THRESHOLD']
(kwargs["end"] - kwargs["start"]).total_seconds(),
get_config()["FORGE_VERIFY_THRESHOLD"],
)
retries = 10
else:
@ -250,11 +270,11 @@ def generate_mp3(db_id: int, **kwargs):
for i in range(retries):
result = create_mp3(validator=validator, **kwargs)
logger.debug('Create mp3 for %d -> %s', db_id, result)
logger.debug("Create mp3 for %d -> %s", db_id, result)
if result:
break
elif i < retries - 1:
logger.debug("waiting %d", i+1)
logger.debug("waiting %d", i + 1)
time.sleep(i + 1) # waiting time increases at each retry
else:
logger.warning("Could not create mp3 for %d: validation failed", db_id)
@ -266,7 +286,6 @@ def generate_mp3(db_id: int, **kwargs):
return True
@app.get("/api/ready/{recid}")
def check_job(recid: int):
rec = db._search(_id=recid)[0]
@ -279,7 +298,6 @@ def check_job(recid: int):
return ret("WIP")
@app.get("/api/get/ongoing")
def get_ongoing():
return {rec.id: rec_sanitize(rec) for rec in db.get_ongoing()}
@ -341,9 +359,12 @@ def serve_pages(request: Request):
fpath = os.path.join(get_config()["STATIC_PAGES"], page)
return FileResponse(fpath)
def main_cmd(options):
import uvicorn
uvicorn.run(app, host=get_config()['HOST'], port=int(get_config()['PORT']))
uvicorn.run(app, host=get_config()["HOST"], port=int(get_config()["PORT"]))
if __name__ == "__main__":
logger.warn("Usage of server.py is not supported anymore; use cli.py")