2020"""video2commons web API."""
2121
2222import json
23+ import os
2324import traceback
2425import re
2526
4546 do_validate_filename ,
4647 do_validate_filedesc ,
4748 sanitize ,
49+ predict_task_type_ffprobe ,
50+ DEFAULT_QUEUE ,
51+ HEAVY_QUEUE ,
4852)
4953from video2commons .frontend .upload import upload as _upload , status as _uploadstatus
5054from video2commons .shared import stats
5660 r"(watch\?.*?(?=v=)v=|embed/|v/|.+\?v=)?([^&=%\?]{11})"
5761)
5862
63+ FILEKEY_REGEX = re .compile (
64+ r"^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$"
65+ )
66+
67+ UPLOADS_DIR = os .path .join (
68+ os .path .dirname (os .path .realpath (__file__ )), "static/uploads"
69+ )
70+
71+ VALID_QUEUES = {DEFAULT_QUEUE , HEAVY_QUEUE }
72+
5973api = Blueprint ("api" , __name__ )
6074
6175
@@ -426,6 +440,22 @@ def run_task():
426440 downloadkey , convertkey = get_backend_keys (request .form ["format" ])
427441 username = session ["username" ]
428442 oauth = (session ["access_token_key" ], session ["access_token_secret" ])
443+ queue = request .form .get ("queue" )
444+
445+ # If no queue is specified, such is the case with manually uploaded files
446+ # due to yt-dlp not being used and extracturl not being called, determine
447+ # which queue to process the file on based on file metadata such as bitrate
448+ # and resolution. We do this to avoid OOM errors with heavy files.
449+ if not queue :
450+ if url .startswith ("uploads:" ):
451+ filekey = url .split (":" , 1 )[1 ]
452+ if not FILEKEY_REGEX .match (filekey ):
453+ return jsonify (error = "Invalid file key format" ), 400
454+
455+ filepath = os .path .join (UPLOADS_DIR , filekey )
456+ queue = predict_task_type_ffprobe (filepath )
457+ else :
458+ queue = DEFAULT_QUEUE
429459
430460 taskid = run_task_internal (
431461 filename ,
@@ -440,17 +470,23 @@ def run_task():
440470 username ,
441471 oauth ,
442472 ),
473+ queue ,
443474 )
444475
445476 return jsonify (id = taskid , step = "success" )
446477
447478
448- def run_task_internal (filename , params ):
479+ def run_task_internal (filename , params , queue ):
449480 """Internal run task function to accept whatever params given."""
450481 banned = check_banned ()
451482 assert not banned , "You are banned from using this tool! Reason: " + banned
452483
453- res = worker .main .delay (* params )
484+ # Validate queue to prevent tasks being sent to non-existent queues.
485+ # Unfortunately Celery tries to be helpful and creates queues on demand.
486+ if queue not in VALID_QUEUES :
487+ queue = DEFAULT_QUEUE
488+
489+ res = worker .main .apply_async (args = params , queue = queue )
454490 taskid = res .id
455491
456492 expire = 14 * 24 * 3600 # 2 weeks
@@ -491,7 +527,9 @@ def restart_task():
491527 params = redisconnection .get ("params:" + id )
492528 assert params , "Could not extract the task parameters."
493529
494- newid = run_task_internal (filename , json .loads (params ))
530+ # Always restart failed tasks on the heavy queue as a failsafe in case the
531+ # task failed earlier due to being misprioritized.
532+ newid = run_task_internal (filename , json .loads (params ), HEAVY_QUEUE )
495533 redisconnection .set ("restarted:" + id , newid )
496534
497535 redis_publish ("update" , {"taskid" : id , "data" : _status (id )})
0 commit comments