Skip to content

Commit

Permalink
refactor: add ffmpeg automatic installation
Browse files Browse the repository at this point in the history
  • Loading branch information
ihmily committed Nov 29, 2024
1 parent b963d8a commit eeeed2f
Show file tree
Hide file tree
Showing 4 changed files with 248 additions and 19 deletions.
21 changes: 16 additions & 5 deletions douyinliverecorder/initializer.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,16 @@
current_env_path = os.environ.get('PATH')


def unzip_file(zip_path: str | Path, extract_to: str | Path) -> None:
def unzip_file(zip_path: str | Path, extract_to: str | Path, delete: bool = True) -> None:
if not os.path.exists(extract_to):
os.makedirs(extract_to)

with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(extract_to)

if delete and os.path.exists(zip_path):
os.remove(zip_path)


def install_nodejs_windows():
try:
Expand Down Expand Up @@ -148,14 +151,22 @@ def install_nodejs_mac():
logger.error(f"An unexpected error occurred: {e}")


def get_package_manager():
dist_id = distro.id()
if dist_id in ["centos", "fedora", "rhel", "amzn", "oracle", "scientific", "opencloudos", "alinux"]:
return "RHS"
else:
return "DBS"


def install_nodejs() -> bool:
if current_platform == "Windows":
return install_nodejs_windows()
elif current_platform == "Linux":
dist = distro.id()
if dist.lower() == "centos":
os_type = get_package_manager()
if os_type == "RHS":
return install_nodejs_centos()
elif dist.lower() == "ubuntu":
else:
return install_nodejs_ubuntu()
elif current_platform == "Darwin":
return install_nodejs_mac()
Expand Down Expand Up @@ -206,4 +217,4 @@ def check_nodejs_installed() -> bool:

def check_node() -> bool:
if not check_nodejs_installed():
return install_nodejs()
return install_nodejs()
Binary file removed ffmpeg.exe
Binary file not shown.
221 changes: 221 additions & 0 deletions ffmpeg_install.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,221 @@
# -*- coding: utf-8 -*-

"""
Author: Hmily
GitHub: https://github.com/ihmily
Copyright (c) 2024 by Hmily, All Rights Reserved.
"""

import os
import re
import subprocess
import sys
import platform
import zipfile
from pathlib import Path
import requests
from tqdm import tqdm
from douyinliverecorder.logger import logger

current_platform = platform.system()
execute_dir = os.path.split(os.path.realpath(sys.argv[0]))[0]
current_env_path = os.environ.get('PATH')


def unzip_file(zip_path: str | Path, extract_to: str | Path, delete: bool = True) -> None:
if not os.path.exists(extract_to):
os.makedirs(extract_to)

with zipfile.ZipFile(zip_path, 'r') as zip_ref:
zip_ref.extractall(extract_to)

if delete and os.path.exists(zip_path):
os.remove(zip_path)


def get_lanzou_download_link(url: str, password: str | None = None) -> str | None:
try:
headers = {
'accept-language': 'zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6',
'Origin': 'https://wweb.lanzouv.com',
'Referer': 'https://wweb.lanzouv.com/iXncv0dly6mh',
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) '
'Chrome/121.0.0.0 Safari/537.36 Edg/121.0.0.0',
}
response = requests.get(url, headers=headers)
sign = re.search("var skdklds = '(.*?)';", response.text).group(1)
data = {
'action': 'downprocess',
'sign': sign,
'p': password,
'kd': '1',
}
response = requests.post('https://wweb.lanzouv.com/ajaxm.php', headers=headers, data=data)
json_data = response.json()
download_url = json_data['dom'] + "/file/" + json_data['url']
response = requests.get(download_url, headers=headers)
return response.url
except Exception as e:
logger.error(f"Failed to obtain ffmpeg download address. {e}")


def install_ffmpeg_windows():
try:
logger.warning("ffmpeg is not installed.")
logger.debug("Installing the stable version of ffmpeg for Windows...")
ffmpeg_url = get_lanzou_download_link('https://wweb.lanzouv.com/in54b2gmj24b', 'e3ut')
if ffmpeg_url:
full_file_name = 'ffmpeg-7.1.zip'
version = '7.1'
zip_file_path = Path(execute_dir) / full_file_name
if Path(zip_file_path).exists():
logger.debug("ffmpeg installation file already exists, start install...")
else:
response = requests.get(ffmpeg_url, stream=True)
total_size = int(response.headers.get('Content-Length', 0))
block_size = 1024

with tqdm(total=total_size, unit="B", unit_scale=True,
ncols=100, desc=f'Downloading ffmpeg ({version})') as t:
with open(zip_file_path, 'wb') as f:
for data in response.iter_content(block_size):
t.update(len(data))
f.write(data)

unzip_file(zip_file_path, execute_dir)
extract_dir_path = str(zip_file_path).rsplit('.', maxsplit=1)[0]
os.environ['PATH'] = execute_dir + '/' + extract_dir_path + os.pathsep + current_env_path
result = subprocess.run(["ffmpeg", "-version"], capture_output=True)
if result.returncode == 0:
logger.debug('ffmpeg installation was successful')
else:
logger.error('ffmpeg installation failed. Please manually install ffmpeg by yourself')
return True
else:
logger.error("Please manually install ffmpeg by yourself")
except Exception as e:
logger.error(f"type: {type(e).__name__}, ffmpeg installation failed {e}")


def install_ffmpeg_mac():
logger.warning("ffmpeg is not installed.")
logger.debug("Installing the stable version of ffmpeg for macOS...")
try:
result = subprocess.run(["brew", "install", "ffmpeg"], capture_output=True)
if result.returncode == 0:
logger.debug('ffmpeg installation was successful. Restart for changes to take effect.')
return True
else:
logger.error("ffmpeg installation failed")
except subprocess.CalledProcessError as e:
logger.error(f"Failed to install ffmpeg using Homebrew. {e}")
logger.error("Please install ffmpeg manually or check your Homebrew installation.")
except Exception as e:
logger.error(f"An unexpected error occurred: {e}")


def install_ffmpeg_linux():
is_RHS = True

try:
logger.warning("ffmpeg is not installed.")
logger.debug("Trying to install the stable version of ffmpeg")
result = subprocess.run(['yum', '-y', 'update'], capture_output=True)
if result.returncode != 0:
logger.error(f"Failed to update package lists using yum.")
return False

result = subprocess.run(['yum', 'install', '-y', 'ffmpeg'], capture_output=True)
if result.returncode == 0:
logger.debug("ffmpeg installation was successful using yum. Restart for changes to take effect.")
return True
logger.error(result.stderr.decode('utf-8').strip())
except FileNotFoundError:
logger.debug("yum command not found, trying to install using apt...")
is_RHS = False
except Exception as e:
logger.error(f"An error occurred while trying to install ffmpeg using yum: {e}")

if not is_RHS:
try:
logger.debug("Trying to install the stable version of ffmpeg for Linux using apt...")
result = subprocess.run(['apt', 'update'], capture_output=True)
if result.returncode != 0:
logger.error("Failed to update package lists using apt")
return False

result = subprocess.run(['apt', 'install', '-y', 'ffmpeg'], capture_output=True)
if result.returncode == 0:
logger.debug("ffmpeg installation was successful using apt. Restart for changes to take effect.")
return True
else:
logger.error(result.stderr.decode('utf-8').strip())
except FileNotFoundError:
logger.error("apt command not found, unable to install ffmpeg. Please manually install ffmpeg by yourself")
except Exception as e:
logger.error(f"An error occurred while trying to install ffmpeg using apt: {e}")
logger.error("Manual installation of ffmpeg is required. Please manually install ffmpeg by yourself.")
return False


def install_ffmpeg() -> bool:
if current_platform == "Windows":
return install_ffmpeg_windows()
elif current_platform == "Linux":
return install_ffmpeg_linux()
elif current_platform == "Darwin":
return install_ffmpeg_mac()
else:
logger.debug(f"ffmpeg auto installation is not supported on this platform: {current_platform}. "
f"Please install ffmpeg manually.")
return False


def ensure_ffmpeg_installed(func):
def wrapper(*args, **kwargs):
try:
result = subprocess.run(['ffmpeg', '-version'], capture_output=True)
version = result.stdout.strip()
if result.returncode == 0 and version:
return func(*args, **kwargs)
except FileNotFoundError:
pass
return False

def wrapped_func(*args, **kwargs):
if sys.version_info >= (3, 7):
res = wrapper(*args, **kwargs)
else:
res = wrapper(*args, **kwargs)
if not res:
install_ffmpeg()
res = wrapper(*args, **kwargs)

if not res:
raise RuntimeError("ffmpeg is not installed.")

return func(*args, **kwargs)

return wrapped_func


def check_ffmpeg_installed() -> bool:
try:
result = subprocess.run(['ffmpeg', '-version'], capture_output=True)
version = result.stdout.strip()
if result.returncode == 0 and version:
return True
except FileNotFoundError:
pass
except OSError as e:
print(f"OSError occurred: {e}. ffmpeg may not be installed correctly or is not available in the system PATH.")
print("Please delete the ffmpeg and try to download and install again.")
except Exception as e:
print(f"An unexpected error occurred: {e}")
return False


def check_ffmpeg() -> bool:
if not check_ffmpeg_installed():
return install_ffmpeg()
return True
25 changes: 11 additions & 14 deletions main.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
Author: Hmily
GitHub: https://github.com/ihmily
Date: 2023-07-17 23:52:05
Update: 2024-11-16 04:28:00
Update: 2024-11-29 19:53:00
Copyright (c) 2023-2024 by Hmily, All Rights Reserved.
Function: Record live stream video.
"""
Expand All @@ -27,6 +27,7 @@
from urllib.error import URLError, HTTPError
from typing import Any
import configparser
from ffmpeg_install import check_ffmpeg
from douyinliverecorder import spider, stream
from douyinliverecorder.proxy import ProxyDetector
from douyinliverecorder.utils import logger
Expand Down Expand Up @@ -1063,7 +1064,8 @@ def start_record(url_data: tuple, count_variable: int = -1) -> None:
_filepath, _ = urllib.request.urlretrieve(flv_url, save_file_path)
record_finished = True
recording.discard(record_name)
print(f"\n{anchor_name} {time.strftime('%Y-%m-%d %H:%M:%S')} 直播录制完成\n")
print(
f"\n{anchor_name} {time.strftime('%Y-%m-%d %H:%M:%S')} 直播录制完成\n")
else:
logger.debug("未找到FLV直播流,跳过录制")
except Exception as e:
Expand Down Expand Up @@ -1435,33 +1437,29 @@ def backup_file_start() -> None:
logger.error(f"备份配置文件失败, 错误信息: {e}")


# --------------------------检查是否存在ffmpeg-------------------------------------
def check_ffmpeg_existence() -> bool:
dev_null = open(os.devnull, 'wb')
try:
subprocess.run(['ffmpeg', '--help'], stdout=dev_null, stderr=dev_null, check=True)
except subprocess.CalledProcessError as e:
logger.error(e)
return False
except FileNotFoundError:
ffmpeg_file_check = subprocess.getoutput(ffmpeg_path)
if ffmpeg_file_check.find("run") > -1 and os.path.isfile(ffmpeg_path):
os.environ['PATH'] += os.pathsep + os.path.dirname(os.path.abspath(ffmpeg_path))
# print(f"已将ffmpeg路径添加到环境变量:{ffmpeg_path}")
return True
else:
logger.error("未检测到ffmpeg,请确保ffmpeg位于系统路径中,或将其路径添加到环境变量。")
sys.exit(0)
finally:
dev_null.close()
return True
if check_ffmpeg():
time.sleep(1)
return True
return False


# --------------------------初始化程序-------------------------------------
if not check_ffmpeg_existence():
logger.error("缺少ffmpeg无法进行录制,程序退出")
sys.exit(1)

# --------------------------初始化程序-------------------------------------
print("-----------------------------------------------------")
print("| DouyinLiveRecorder |")
print("-----------------------------------------------------")
Expand Down Expand Up @@ -1524,7 +1522,8 @@ def read_config_value(config_parser: configparser.RawConfigParser, section: str,
except HTTPError as err:
print(f"HTTP error occurred: {err.code} - {err.reason}")
except URLError as err:
color_obj.print_colored(f"INFO:未检测到全局/规则网络代理,请检查代理配置(若无需录制海外直播请忽略此条提示)", color_obj.YELLOW)
color_obj.print_colored(f"INFO:未检测到全局/规则网络代理,请检查代理配置(若无需录制海外直播请忽略此条提示)",
color_obj.YELLOW)
except Exception as err:
print("An unexpected error occurred:", err)

Expand Down Expand Up @@ -1873,5 +1872,3 @@ def contains_url(string: str) -> bool:
first_run = False

time.sleep(3)


0 comments on commit eeeed2f

Please sign in to comment.