Spaces:
Paused
Paused
| """ | |
| DaaS (Docker as a Service) is a service | |
| that allows users to run docker commands on the server side. | |
| """ | |
| from fastapi import FastAPI | |
| from fastapi.responses import StreamingResponse | |
| from fastapi import FastAPI, File, UploadFile, HTTPException | |
| from pydantic import BaseModel, Field | |
| from typing import Optional, Dict | |
| import time | |
| import os | |
| import asyncio | |
| import subprocess | |
| import uuid | |
| import glob | |
| import threading | |
| import queue | |
| from shared_utils.docker_as_service_api import DockerServiceApiComModel | |
| app = FastAPI() | |
| def python_obj_to_pickle_file_bytes(obj): | |
| import pickle | |
| import io | |
| with io.BytesIO() as f: | |
| pickle.dump(obj, f) | |
| return f.getvalue() | |
| def yield_message(message): | |
| dsacm = DockerServiceApiComModel(server_message=message) | |
| return python_obj_to_pickle_file_bytes(dsacm) | |
| def read_output(stream, output_queue): | |
| while True: | |
| line_stdout = stream.readline() | |
| # print('recv') | |
| if line_stdout: | |
| output_queue.put(line_stdout) | |
| else: | |
| break | |
| async def stream_generator(request_obj): | |
| import tempfile | |
| # Create a temporary directory | |
| with tempfile.TemporaryDirectory(ignore_cleanup_errors=True) as temp_dir: | |
| # Construct the docker command | |
| download_folder = temp_dir | |
| # Get list of existing files before download | |
| existing_file_before_download = [] | |
| video_id = request_obj.client_command | |
| cmd = [ | |
| '/root/.dotnet/tools/BBDown', | |
| video_id, | |
| '--use-app-api', | |
| '--work-dir', | |
| f'{os.path.abspath(temp_dir)}' | |
| ] | |
| cmd_chmod = [] | |
| cmd = [ | |
| 'docker', 'run', '--rm', | |
| '-v', | |
| f'{os.path.abspath(temp_dir)}:/downloads', | |
| 'bbdown', | |
| video_id, | |
| '--use-app-api', | |
| '--work-dir', | |
| '/downloads' | |
| ] | |
| cmd_chmod = [ | |
| 'docker', 'run', '--rm', | |
| '-v', | |
| f'{os.path.abspath(temp_dir)}:/downloads', | |
| '--entrypoint=""', # override the entrypoint | |
| 'bbdown', # image name | |
| # chmod -R 777 /downloads | |
| 'chmod', | |
| '-R', | |
| '777', | |
| '/downloads' | |
| ] | |
| cmd = ' '.join(cmd) | |
| yield yield_message(cmd) | |
| process = subprocess.Popen(cmd, | |
| stdout=subprocess.PIPE, | |
| stderr=subprocess.PIPE, | |
| shell=True, | |
| text=True) | |
| stdout_queue = queue.Queue() | |
| thread = threading.Thread(target=read_output, args=(process.stdout, stdout_queue)) | |
| thread.daemon = True | |
| thread.start() | |
| stderr_queue = queue.Queue() | |
| thread = threading.Thread(target=read_output, args=(process.stderr, stderr_queue)) | |
| thread.daemon = True | |
| thread.start() | |
| while True: | |
| print("looping") | |
| # Check if there is any output in the queue | |
| stdout_this_round = "" | |
| stderr_this_round = "" | |
| while True: | |
| try: | |
| output_stdout = stdout_queue.get_nowait() # Non-blocking get | |
| if output_stdout: | |
| stdout_this_round += output_stdout | |
| print(output_stdout) | |
| except queue.Empty: | |
| yield yield_message(stdout_this_round) | |
| break | |
| while True: | |
| try: | |
| output_stderr = stderr_queue.get_nowait() # Non-blocking get | |
| if output_stderr: | |
| stderr_this_round += output_stderr | |
| print(output_stderr) | |
| except queue.Empty: | |
| yield yield_message(stderr_this_round) | |
| break | |
| # Break the loop if the process has finished | |
| if process.poll() is not None: | |
| break | |
| await asyncio.sleep(0.5) | |
| # Get the return code | |
| return_code = process.returncode | |
| yield yield_message("(daas return code:) " + str(return_code)) | |
| # change files mod to 777 | |
| if cmd_chmod: | |
| docker_chmod_res = subprocess.call(' '.join(cmd_chmod), shell=True) | |
| # print(f"Successfully downloaded video {video_id}") | |
| existing_file_after_download = glob.glob(os.path.join(download_folder, '**', '*')) | |
| # existing_file_after_download = list(os.listdir(download_folder)) | |
| # get the difference | |
| downloaded_files = [ | |
| f for f in existing_file_after_download if f not in existing_file_before_download | |
| ] | |
| downloaded_files_path = [ | |
| os.path.join(download_folder, f) for f in existing_file_after_download if f not in existing_file_before_download | |
| ] | |
| # read file | |
| server_file_attach = {} | |
| for fp, fn in zip(downloaded_files_path, downloaded_files): | |
| if os.path.isdir(fp): continue | |
| with open(fp, "rb") as f: | |
| file_bytes = f.read() | |
| server_file_attach[fn] = file_bytes | |
| dsacm = DockerServiceApiComModel( | |
| server_message="complete", | |
| server_file_attach=server_file_attach, | |
| ) | |
| yield python_obj_to_pickle_file_bytes(dsacm) | |
| def simple_generator(return_obj): | |
| dsacm = DockerServiceApiComModel( | |
| server_message=return_obj, | |
| ) | |
| yield python_obj_to_pickle_file_bytes(dsacm) | |
| async def stream_response(file: UploadFile = File(...)): | |
| # read the file in memory, treat it as pickle file, and unpickle it | |
| import pickle | |
| import io | |
| content = await file.read() | |
| with io.BytesIO(content) as f: | |
| request_obj = pickle.load(f) | |
| # process the request_obj | |
| return StreamingResponse(stream_generator(request_obj), media_type="application/octet-stream") | |
| async def stream_response(file: UploadFile = File(...)): | |
| # read the file in memory, treat it as pickle file, and unpickle it | |
| import pickle | |
| import io | |
| content = await file.read() | |
| with io.BytesIO(content) as f: | |
| request_obj = pickle.load(f) | |
| # process the request_obj | |
| keyword = request_obj.client_command | |
| from experimental_mods.get_search_kw_api_stop import search_videos | |
| # Default parameters for video search | |
| csrf_token = '40a227fcf12c380d7d3c81af2cd8c5e8' # Using default from main() | |
| search_type = 'default' | |
| max_pages = 1 | |
| output_path = 'search_results' | |
| config_path = 'experimental_mods/config.json' | |
| # Search for videos and return the first result | |
| videos = search_videos( | |
| keyword=keyword, | |
| csrf_token=csrf_token, | |
| search_type=search_type, | |
| max_pages=max_pages, | |
| output_path=output_path, | |
| config_path=config_path, | |
| early_stop=True | |
| ) | |
| return StreamingResponse(simple_generator(videos), media_type="application/octet-stream") | |
| async def hi(): | |
| return "Hello, this is Docker as a Service (DaaS)! If you want to use this service, you must duplicate this space. " \ | |
| "您好,这里是Docker作为服务(DaaS)!如果您想使用此服务,您必须复制此空间。复制方法:点击https://huggingface.co/spaces/hamercity/bbdown页面右上角的三个点,然后选择“复制空间”。" \ | |
| "此外,在设置中,你还需要修改URL,例如:DAAS_SERVER_URL = \"https://你的用户名-你的空间名.hf.space/stream\"" | |
| if __name__ == "__main__": | |
| import uvicorn | |
| uvicorn.run(app, host="0.0.0.0", port=49000) | |