Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

0.3.2 发版 #47

Merged
merged 14 commits into from
Nov 9, 2024
Merged

0.3.2 发版 #47

merged 14 commits into from
Nov 9, 2024

Conversation

linyqh
Copy link
Owner

@linyqh linyqh commented Nov 9, 2024

[0.3.2] - 2024/11/10

重构

性能优化

  • 对项目进行了性能优化,显著减少了加载时间和提高了处理速度。

新功能

  • 新增缓存机制,减少了数据加载时间,提高了系统效率。
  • 新增narratoapi接口,待发布后可以申请api_key,增强了系统的扩展性和灵活性。

@linyqh linyqh merged commit 960db5e into main Nov 9, 2024
1 check passed
Copy link
Owner Author

@linyqh linyqh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code review by ChatGPT

@@ -130,7 +130,7 @@ docker-compose up
## 开发 💻
1. 安装依赖
```shell
conda create -n narratoai python=3.10
conda create -n narratoai python=3.11
conda activate narratoai
cd narratoai
pip install -r requirements.txt
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

该代码补丁主要是更新了 Python 的版本从 3.10 到 3.11。下面是一些潜在的风险和改进建议:

  1. 兼容性风险:更新 Python 的版本可能会导致依赖库或代码不兼容。需要测试代码以确保其在 Python 3.11 下正常工作。
  2. 依赖库版本:如果依赖库没有适配 Python 3.11,可能会导致安装或运行错误。
  3. 环境一致性:确保开发、测试和生产环境中的 Python 版本一致,以避免环境间的兼容性问题。

改进建议:

  1. 在更新 Python 版本之前,备份当前环境和依赖库,以便在出现问题时可以快速回滚。
  2. 使用 conda 的虚拟环境管理工具,确保依赖库和环境的一致性。
  3. 验证依赖库的版本,确保它们支持 Python 3.11。
  4. 测试代码以确保其在 Python 3.11 下正常工作。
  5. 考虑使用自动化测试和持续集成工具,以确保代码的质量和一致性。

示例测试方法:

  • 运行 conda info 命令,查看环境和依赖库的信息。
  • 使用 pip freeze 命令,验证依赖库的版本。
  • 执行自动化测试,确保代码在 Python 3.11 下正常工作。

总的来说,该代码补丁相对简单,但需要注意的是依赖库和环境的兼容性问题。通过适当的测试和验证,可以确保代码的质量和一致性。

finally:
# 处理完成后删除临时文件
if os.path.exists(video_path):
os.remove(video_path)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以下是一些代码审查的结果:

Bug 风险

  1. transcribe_video 函数中,如果 video_fileNone,可能会导致 video_file.filename 抛出 AttributeError。建议在函数开始处添加 video_file 检查。
  2. 如果 UPLOAD_DIR 目录不存在或无法写入,os.makedirs(UPLOAD_DIR, exist_ok=True) 可能会抛出 OSError。建议捕获这个异常并处理。
  3. finally 块中,如果 video_path 不存在,os.remove(video_path) 将会抛出 FileNotFoundError。虽然已经进行了 if os.path.exists(video_path) 检查,但仍建议捕获这个异常并处理。

改进建议

  1. 可以使用类型注解来指定 video_namelanguage 的类型。
  2. 可以使用 logging 模块记录函数的执行日志和异常信息。
  3. transcribe_video 函数中,可以使用 async with 语句来确保文件安全关闭。
  4. 可以考虑使用 try-except 块捕获所有异常并返回错误响应,而不是仅捕获 Exception
  5. 可以使用环境变量或配置文件来配置 UPLOAD_DIR 的值,而不是硬编码。
  6. 可以考虑使用其他异常处理机制,例如 fastapiHTTPException
  7. 可以添加测试用例来验证函数的正确性和异常处理。

其他建议

  1. 可以考虑使用 pathlib 模块来代替 os 模块进行文件系统操作。
  2. 可以考虑使用 typing 模块来指定函数参数和返回值的类型。
  3. 可以考虑使用 docstring 来注释函数和变量。

以下是一些示例代码,展示了如何实施这些建议:

from fastapi import HTTPException
from fastapi.responses import JSONResponse
import logging
import os
from pathlib import Path
import typing as t

logger = logging.getLogger(__name__)

@router.post(
    "/transcription",
    response_model=VideoTranscriptionResponse, 
    summary="Transcribe video content using Gemini"
)
async def transcribe_video(
    request: Request,
    video_name: str,
    language: str = "zh-CN",
    video_file: UploadFile = File(...)
):
    try:
        # Checks and validation 
        if not video_file:
            raise HTTPException(status_code=400, detail="Video file is required")
        
        # Get the directory path
        upload_dir = Path(os.environ.get("UPLOAD_DIR", "/tmp/uploads"))
        upload_dir.mkdir(parents=True, exist_ok=True)
        
        # Save the file
        video_path = upload_dir / video_file.filename
        async with open(video_path, "wb") as buffer:
            content = await video_file.read()
            buffer.write(content)
        
        # Transcribe the video
        transcription = llm.gemini_video_transcription(
            video_name=video_name,
            video_path=str(video_path),
            language=language,
            llm_provider_video=config.app.get("video_llm_provider", "gemini")
        )
        
        # Return the result
        response = {"transcription": transcription}
        return JSONResponse(content=response, status_code=200)
    
    except Exception as e:
        logger.error(f"Error processing video transcription: {e}")
        raise HTTPException(status_code=500, detail="Internal Server Error")
    
    finally:
        # Remove the file if it exists
        if video_path.exists():
            try:
                video_path.unlink()
            except OSError as e:
                logger.error(f"Error removing file: {e}")

注意:此示例只是一个参考,具体实现可能需要根据您的应用程序的特定需求进行调整。

arbitrary_types_allowed = True

class VideoTranscriptionResponse(BaseModel):
transcription: str
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这片代码看上去是定义了几个用于描述视频剪辑参数和视频转录请求、响应的Pydantic模型。以下是一些我的建议和可能存在的Bug风险:

1.模型字段的类型和默认值:

  • voice_pitchvoice_ratevoice_volume 的类型是 Optional[float],但它们的default值是 1.0,也就是说这些值在使用时必须传入,如果没有传入就会使用默认值。为了明确这个行为,可能考虑将它们的类型改为float
  • voice_namebgm_namebgm_type 的类型是 Optional[str],但它们的描述都是关于名称或类型的,这些字段是否应该是必填项?如果是,应该将它们的类型改为 str

2.模型字段的描述:

  • custom_position 的描述是 "自定义位置",但它的类型是 float,这意味着它不再仅仅是一个位置信息,也可能是一个百分比值,描述信息可能需要调整。

3.新引入的模型:

  • VideoTranscriptionRequest 模型中的 video_name 字段类型是 str,这意味着它是必填项。如果未传入该值,模型会抛出异常。
  • VideoTranscriptionResponse 模型中的 transcription 字段类型是 str,这意味着它是必填项。如果未传入该值,模型会抛出异常。

综上所述,建议对模型字段的类型、默认值和描述信息进行重新评估以确保他们符合实际使用场景。

return int(parts[0]) * 60 + int(parts[1])
except (ValueError, IndexError) as e:
logger.error(f"Error parsing time {time_str}: {str(e)}")
return 0


if __name__ == "__main__":
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

我对这个代码进行了简要的审查,发现了一些可以改进的地方。

改进建议

  1. time_to_seconds 函数中,由于已经在函数开始处将 _ 替换为 `:,因此似乎不需要在注释中再次提及。如果要保留注释,建议将其移到函数定义后面,作为函数的行为描述。
  2. time_to_seconds 函数的错误处理中,返回值被设置为 0,但是没有提供任何提示来告知函数的调用者发生了错误。如果可能的话,建议抛出异常或返回一个特殊值(如 None)来表示函数执行失败。
  3. parse_timestamp 函数的注释中提到“解析时间戳字符串为秒数”,但是函数实际上并没有用到 timestamp 的任何特定信息,而只是直接将其传给 time_to_seconds 函数。因此,建议将函数的注释修改为直接使用time_to_seconds 函数的注释,或者,删除该函数。
  4. extract_timestamp 函数使用了固定的文件名格式,可能无法处理其他格式的文件名。如果需要处理其他格式的文件名,建议使用正则表达式或其他更加灵活的方法来提取时间戳。
  5. 函数的参数命名可以更加具体,例如 time_str 可以改为 timestamp_str,这样更能反映其含义。

潜在风险

  1. time_to_seconds 函数没有检查时间戳的有效性,例如 “-1:00”、“60:00”等无效时间戳可能会被错误的解析为 0 秒。
  2. extract_timestamp 函数使用了固定的文件名格式,如果文件名格式发生变化,函数可能无法正常工作。

其他

  1. 函数的注释可以更加详细,例如可以添加有关函数参数、返回值和可能抛出的异常的描述。
  2. 可以使用 Python 的类型提示来指定函数参数和返回值的类型。

# print("脚本生成成功:\n", res)
# res = clean_model_output(res)
# aaa = json.loads(res)
# print(json.dumps(aaa, indent=2, ensure_ascii=False))
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

代码审查结果:

优点

  • 代码组织结构清晰,有明确的函数分离和注释。
  • 使用类型提示(Union[str, TextIO])来声明函数参数的类型,这有助于代码的可读性和可维护性。

缺点和建议

  • 代码中有多个相似的函数引用的注释,但这些注释没有对函数的功能进行解释。建议增加函数的文档注释以提高代码的可读性。
  • _generate_response_video 函数的参数 video_file 类型由 str | File 改为 Union[str, TextIO],这意味着该函数现在支持TextIO类型的参数。但是,这个改变并没有在函数内部进行相应的处理。建议增加对TextIO类型参数的处理代码。
  • if __name__ == "__main__": 块中的代码看起来像是测试代码,建议将其放入单独的测试文件中,避免污染主代码。
  • 有一些废弃的代码,建议删除它们以保持代码的整洁。
  • suggest Sử dụng_consistent 的空白符和缩进来保持代码的可读性。

潜在bug

  • _generate_response_video 函数的 video_file 参数类型改变可能导致函数内部的处理逻辑不正确,可能导致程序崩溃或异常结果。
  • if __name__ == "__main__": 块中的测试代码可能会影响主程序的运行,建议将其放入单独的测试文件中。

"Text API Key": "文案生成 API 密钥",
"Text Base URL": "文案生成接口地址",
"Text Model Name": "文案生成模型名称",
"Account ID": "账户 ID"
}
} No newline at end of file
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个代码.patch 文件似乎是一个翻译更新补丁,主要目的是更新界面文本的翻译。以下是一些简短的代码审查反馈:

Bug 风险

  1. 未知的翻译问题:有些翻译可能不准确或不完整,可能导致界面显示不正常或者不易理解。
  2. 编码问题:如果本地环境的编码格式不支持 Unicode ,可能会导致一些特殊字符显示不正常。
  3. 需要确认 HTTP_PROXYHTTPS_PROXY 的使用方式。

改进建议

  1. 检查翻译的准确性和完整性。
  2. 强制使用 Unicode 编码(UTF-8)来避免编码问题。
  3. 补充 HTTP_PROXYHTTPS_PROXY 的使用说明。
  4. 考虑使用一个自动化测试工具来检查翻译是否正确和界面是否显示正常。

总体来说,这个.patch 文件似乎是一个常规的翻译更新补丁,没有发现明显的 bug 风险。然而,为了确保翻译的准确性和界面的正常显示,仍然需要进行人工检查和测试。

__all__ = [
'monitor_performance',
'PerformanceMonitor'
] No newline at end of file
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这段代码看起来是一段Python模块的导入和定义。以下是一些简要的代码评审结果:

存在的问题:

  1. 空行不必要:文件开头和结尾的空行不必要,可以删除。
  2. 缺少结尾换行符:文件结尾缺少换行符,虽然可以通过 -no-inconsistent-newlines 选项关闭相应警告,但推荐手动添加。
  3. 通配符导入不推荐:from .cache import *from .file_utils import * 使用通配符导入不推荐,因为这样可能会导致命名空间冲突和代码不易维护。如果需要导入多个函数或类,可以明确名称导入,例如 from .cache import cache1, cache2
  4. __all__ списка定义不必要:如果模块中所有函数和类都在 __all__ 列表中定义,则没有必要详细定义 __all__ 列表,可以直接注释掉。

改进建议:

  1. 添加文档注释:对于模块和函数,应该添加文档注释,描述模块和函数的功能、用途和使用方法。
  2. 使用显式导入:建议使用显式导入代替通配符导入,明确导入需要的函数或类。
  3. 检查依赖关系:检查依赖关系是否正确,避免循环依赖关系。

以下是改进后的代码示例:

# -*- coding: utf-8 -*-
"""
模块描述
"""

from .performance import monitor_performance, PerformanceMonitor
from .cache import cache1, cache2
from .file_utils import file_utils1, file_utils2

# __all__ = ['monitor_performance', 'PerformanceMonitor']

请注意,这是一个简要的代码评审,不包含所有可能的问题和改进建议。如果你有具体的问题或需求,可以进一步讨论。

if file.endswith(".mp3"):
songs.append(file)
st.session_state['songs_cache'] = songs
return st.session_state['songs_cache'] No newline at end of file
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

您的代码似乎实现了缓存三个不同类型的文件:字体文件、视频文件和歌曲文件。这里是一些潜在的问题和改进建议:

  1. get_fonts_cacheget_video_files_cacheget_songs_cache 函数中,您在使用缓存之前检查缓存是否存在。如果缓存存在,您直接返回缓存的值。但是,如果缓存不存在,您会根据文件系统的内容构建缓存,并将其存储在会话状态中。但是,没有任何机制来更新缓存,以便在文件系统发生变化时刷新缓存。这可能会导致数据不一致。您可以考虑增加更新缓存的机制。

  2. get_fonts_cache 函数中,您在缓存字体文件之前,使用 fonts.sort() 排序字体文件。这可能会增加函数的执行时间,尤其是当字体文件很多时。您可以考虑延迟排序,或者使用其他排序算法。

  3. get_video_files_cache 函数中,您使用 glob.glob 函数查找特定的视频文件类型。但是,您可能需要考虑其他视频文件类型,如 *.wmv*.flv 等。

  4. get_songs_cache 函数中,您已将歌曲文件缓存到会话状态中,但您没有进行排序或其他处理。您可能需要考虑增加排序或其他处理,这样可以更好地处理歌曲文件。

  5. 在缓存的实现中,您可能需要考虑增加错误处理。如果在读取文件系统或存储缓存时出现错误,您应该能够捕获和处理异常。

  6. 您可能需要考虑增加缓存失效的机制。例如,可能您可以在缓存创建后指定一个失效时间,当缓存超过失效时间时,可以自动更新缓存。

  7. 您可能需要考虑增加缓存的容量限制。例如,如果缓存过大,可能会占用过多内存。您可以考虑增加缓存的容量限制,并进行过期处理。

总的来说,您的代码看起来较为干净,您已经开始实现缓存来提高应用的性能。通过增加更新缓存的机制、错误处理和缓存失效的机制,您可以进一步改善应用的鲁棒性和性能。下面是一个改进后的示例:

import streamlit as st
import os
import glob
from app.utils import utils
import time

# 缓存过期时间(一小时)
CACHE_EXPIRE_TIME = 3600

def get_fonts_cache(font_dir):
    if 'fonts_cache' not in st.session_state or time.time() - st.session_state['fonts_cache_time'] > CACHE_EXPIRE_TIME:
        fonts = []
        try:
            for root, dirs, files in os.walk(font_dir):
                for file in files:
                    if file.endswith(".ttf") or file.endswith(".ttc"):
                        fonts.append(file)
            fonts.sort()
            st.session_state['fonts_cache'] = fonts
            st.session_state['fonts_cache_time'] = time.time()
        except Exception as e:
            st.error(f"读取字体文件失败:{str(e)}")
    return st.session_state.get('fonts_cache', [])

def get_video_files_cache():
    if 'video_files_cache' not in st.session_state or time.time() - st.session_state['video_files_cache_time'] > CACHE_EXPIRE_TIME:
        video_files = []
        try:
            for suffix in ["*.mp4", "*.mov", "*.avi", "*.mkv"]:
                video_files.extend(glob.glob(os.path.join(utils.video_dir(), suffix)))
            st.session_state['video_files_cache'] = video_files[::-1]
            st.session_state['video_files_cache_time'] = time.time()
        except Exception as e:
            st.error(f"读取视频文件失败:{str(e)}")
    return st.session_state.get('video_files_cache', [])

def get_songs_cache(song_dir):
    if 'songs_cache' not in st.session_state or time.time() - st.session_state['songs_cache_time'] > CACHE_EXPIRE_TIME:
        songs = []
        try:
            for root, dirs, files in os.walk(song_dir):
                for file in files:
                    if file.endswith(".mp3"):
                        songs.append(file)
            st.session_state['songs_cache'] = songs
            st.session_state['songs_cache_time'] = time.time()
        except Exception as e:
            st.error(f"读取歌曲文件失败:{str(e)}")
    return st.session_state.get('songs_cache', [])

这个示例增加了缓存失效的机制、错误处理和缓存时间戳。同时,它还增加了缓存容量限制和过期处理。这可以进一步改善应用的鲁棒性和性能。


except Exception as e:
logger.error(f"创建zip文件失败: {e}")
return False No newline at end of file
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这是一个 Python 脚本,包含多个文件处理函数。以下是我的代码评审:

1. 函数命名和注释

函数命名清晰,易于理解。注释也较为详细,描述了函数的功能和参数。但是,有些函数的注释中,这些函数的返回值没有详细说明。

2. 错误处理

大部分函数都有 try-except 块来处理错误,这是很好的实践。然而,在一些函数中,当发生错误时,只是打印错误日志,而没有进一步的处理或反馈。这可能会导致程序继续运行,但实际上可能已经出错。

3. 函数签名

函数签名清晰,参数类型也相对明确。但是,有些函数的参数类型没有明确指定,例如 get_file_list 函数的 file_types 参数。

4. 文件操作

文件操作函数基本正确,但有些函数没有检查文件是否存在或是否可读写。

5. 可重用性

这些函数大多数都能独立使用,这是很好的实践。然而,有些函数可能与特定的项目或模块紧密相关,难以在其他项目中重用。

6. 重复代码

有些函数有重复的代码,例如 save_uploaded_filecreate_temp_file 中的 try 块有一些重复代码。考虑抽取为单独函数可能更好。

一些具体建议

  1. get_file_list 函数中,增加对 sort_by 参数的检查,以确保它是合法的。
  2. save_uploaded_file 函数中,考虑使用 os.path.join 代替 string拼接目录和文件名。
  3. create_zip 函数中,增加对 files 参数的检查,以确保它是一个列表。
  4. ensure_directory 函数中,增加对 directory 参数的检查,以确保它是一个字符串。
  5. 考虑使用单例模式或缓存机制来优化文件操作函数的性能。

总的来说,这些函数都是很好的实践,清晰易读,并且有相对明确的注释和错误处理。但是,有些函数可能需要进一步优化和完善。

return result
finally:
PerformanceMonitor.cleanup_resources()
return wrapper No newline at end of file
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

以下是一些对该代码的简短审查和建议:

代码结构和风格

  • 代码使用了合理的函数结构和注释,清晰易读。
  • 但有些函数或方法的名字可以更具描述性。例如,monitor_memory 可能可以改为 log_memory_usage
  • 代码中使用了 Python 的标准库和第三方库,导入顺序也正确。

逻辑和性能

  • PerformanceMonitor 类中的两个方法都使用静态方法,这意味着不需要实例化该类就可以使用这些方法。
  • monitor_memory 方法使用了 psutil 库来获取当前进程的内存使用信息,这是正确的。
  • cleanup_resources 方法首先释放了 CUDA 的缓存,然后手动调用了 Python 的垃圾回收,这是正确的。
  • monitor_performance 装饰器的设计是好的,它在函数调用前后记录了内存使用信息,并在函数调用后释放了资源。
  • 但装饰器的使用可能会造成一些微小的性能损失,因为它需要额外执行一些函数调用和异常处理。

可能的 bug 风险

  • 如果函数调用过程中出现异常,finally 块中的 PerformanceMonitor.cleanup_resources() 调用可能会因为异常而无法执行。
  • monitor_memory 方法中对 memory_info.rss 进行了除法运算,如果结果为 0 或非常小,可能会导致除零异常或精度问题。
  • cleanup_resources 方法中 gc.collect() 的调用可能会导致程序暂停一段时间,影响性能。

改进建议

  • 考虑使用 logging 的 Handler 的功能来记录内存使用信息,而不是直接使用 logger.debug()
  • monitor_memory 方法中添加一个检查,以防止除零异常。
  • cleanup_resources 方法中添加一个 try-except 块来捕获 gc.collect() 可能抛出的异常。
  • 考虑使用 Python 的异步功能或多线程来执行 cleanup_resources 方法,以减少对性能的影响。
  • 基于应用的需求来考虑记录原始的内存使用信息(字节数),而不是 MB 或 GB,以此来减少精度问题。

总体而言,代码基本正确和合理,但存在一些微小的风险和优化空间。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant