diff --git a/.gitignore b/.gitignore index f97b2e34a..6b7419846 100644 --- a/.gitignore +++ b/.gitignore @@ -1,12 +1,9 @@ -# Byte-compiled / optimized / DLL files +# Virtual environment +venv/ __pycache__/ -*.py[cod] -*$py.class - -# C extensions -*.so - -# Distribution / packaging +*.pyc +*.pyo +*.pyd .Python build/ develop-eggs/ @@ -20,117 +17,167 @@ parts/ sdist/ var/ wheels/ -pip-wheel-metadata/ -share/python-wheels/ *.egg-info/ .installed.cfg *.egg MANIFEST -# PyInstaller -# Usually these files are written by a python script from a template -# before PyInstaller builds the exe, so as to inject date/other infos into it. -*.manifest - -# Installer logs -pip-log.txt -pip-delete-this-directory.txt - -# Unit test / coverage reports -htmlcov/ -.tox/ -.nox/ -.coverage -.coverage.* -.cache -nosetests.xml -coverage.xml -*.cover -*.py,cover -.hypothesis/ -.pytest_cache/ - -# Translations -*.mo -*.pot - -# Django stuff: +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# OS +.DS_Store +.DS_Store? +._* +.Spotlight-V100 +.Trashes +ehthumbs.db +Thumbs.db + +# Logs *.log -local_settings.py -db.sqlite3 -db.sqlite3-journal -# Flask stuff: -instance/ -.webassets-cache +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Coverage directory used by tools like istanbul +coverage/ -# Scrapy stuff: -.scrapy +# nyc test coverage +.nyc_output -# Sphinx documentation -docs/_build/ +# Grunt intermediate storage +.grunt -# PyBuilder -target/ +# Bower dependency directory +bower_components -# Jupyter Notebook -.ipynb_checkpoints +# node_modules +node_modules/ -# IPython -profile_default/ -ipython_config.py +# Optional npm cache directory +.npm -# pyenv -.python-version +# Optional eslint cache +.eslintcache -# pipenv -# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. -# However, in case of collaboration, if having platform-specific dependencies or dependencies -# having no cross-platform support, pipenv may install dependencies that don't work, or not -# install all needed dependencies. -#Pipfile.lock +# Microbundle cache +.rpt2_cache/ +.rts2_cache_cjs/ +.rts2_cache_es/ +.rts2_cache_umd/ -# PEP 582; used by e.g. github.com/David-OConnor/pyflow -__pypackages__/ +# Optional REPL history +.node_repl_history -# Celery stuff -celerybeat-schedule -celerybeat.pid +# Output of 'npm pack' +*.tgz -# SageMath parsed files -*.sage.py +# Yarn Integrity file +.yarn-integrity -# Environments +# dotenv environment variables file .env -.venv -env/ -venv/ -ENV/ -env.bak/ -venv.bak/ +.env.test -# Spyder project settings -.spyderproject -.spyproject +# parcel-bundler cache +.cache +.parcel-cache -# Rope project settings -.ropeproject +# Next.js build output +.next -# mkdocs documentation -/site +# Nuxt.js build / generate output +.nuxt +dist -# mypy -.mypy_cache/ -.dmypy.json -dmypy.json +# Gatsby files +.cache/ +public -# Pyre type checker -.pyre/ +# Storybook build outputs +.out +.storybook-out -# hook script generated for pyinstaller -ver_hook.py -# cached data -*.cache.json +# Temporary folders +tmp/ +temp/ +# Editor directories and files +.vscode/ +!.vscode/extensions.json .idea -.vscode +*.suo +*.ntvs* +*.njsproj +*.sln +*.sw? + +# JavSP specific +*.mp4 +*.avi +*.mkv +*.wmv +*.flv +*.mov +*.jpg +*.jpeg +*.png +*.gif +*.bmp +*.webp +*.nfo +*.txt +!requirements.txt +!README.txt +!*.md + +# Config backups +config*.bak +config*副本* + +# Test files +test_*.py +*_test.py +simple_crawler_test.py +debug_*.py +问题*.txt + +# Backup directories +backup/ +old/ + +# Build and distribution files +build/ +dist/ +*.pkg +*.dmg +*.app +*.exe +*.msi + +# Large binary files +*.dylib +*.so +*.dll +*.bin + +# macOS specific +.DS_Store +.AppleDouble +.LSOverride + +# Obsolescence +obs/ + +# Log files +*.log +FileMove.log +JavSP.log!requirements-mac.txt diff --git a/config.ini b/config.ini new file mode 100755 index 000000000..7bc85ea59 --- /dev/null +++ b/config.ini @@ -0,0 +1,156 @@ + + +# 推测番号前忽略文件名中的特定字符串(忽略大小写,以英文分号;分隔) +# 大多数情况软件能够自动识别番号,只有当文件名中特定的部分导致番号识别错误时才需要更新此设置 +[MovieID] +# 要忽略的字串(全词匹配) +ignore_whole_word = 144P;240P;360P;480P;720P;1080P;2K;4K +# 要忽略的正则表达式(如果你不熟悉正则表达式,请不要修改此配置,否则可能严重影响番号识别效果) +ignore_regex = \!+;\w+2048\.com;Carib(beancom)?;[^a-z\d](f?hd|lt)[^a-z\d] + +[File] +# 整理哪个文件夹下的影片?(此项留空时将在运行时询问) +scan_dir = +## 哪些后缀的文件应当视为影片? +media_ext = 3gp;avi;f4v;flv;iso;m2ts;m4v;mkv;mov;mp4;mpeg;rm;rmvb;ts;vob;webm;wmv;strm;nrg;divx +# 扫描影片文件时忽略指定的文件夹(以.开头的文件夹不需要设置也会被忽略) +ignore_folder = #recycle;#整理完成;不要扫描 +# 匹配番号时忽略小于指定大小的文件(以MiB为单位,0表示禁用此功能) +ignore_video_file_less_than = 232 +# 整理时是否移动文件: yes-移动所有文件到新文件夹; no-数据保存到同级文件夹,不移动文件 +enable_file_move = yes + +[Network] +# 是否启用代理 +use_proxy = no +# 设置代理服务器地址,支持 http, socks5/socks5h 代理。示例格式如下: +proxy = socks5://127.0.0.1:1080 +# 网络问题导致抓取数据失败时的重试次数,通常3次就差不多了 +retry = 3 +timeout = 10 + +# 要使用的爬虫列表(汇总数据时从前到后进行) +# airav avsox avwiki fanza fc2 fc2fan javbus javdb javlib javmenu jav321 mgstage prestige +[CrawlerSelect] +normal = javdb,airav,avsox,javbus,javlib,jav321,mgstage,prestige +fc2 = javdb,fc2,msin,avsox,javmenu +cid = fanza +getchu = dl_getchu +gyutto = gyutto + +[Crawler] +# 爬虫至少要获取到哪些字段才可以视为抓取成功? +required_keys = cover,title +# 努力爬取更准确更丰富的信息(会略微增加部分站点的爬取耗时) +hardworking_mode = yes +# 使用网页番号作为最终番号(启用时会对番号大小写等进行更正) +respect_site_avid = yes +# fc2fan已关站。如果你有镜像,请设置本地镜像文件夹的路径,此文件夹内要有类似'FC2-12345.html'的网页文件 +fc2fan_local_path = +# 标题处理:删除尾部可能存在的女优名 +title__remove_actor = yes +# 标题处理:优先使用中文标题(如果能获取到的话) +title__chinese_first = yes +# 刮削一部电影后的等待时间(秒,设置为0禁用此功能) +sleep_after_scraping = 1 +# 禁用javdb的封面(auto/yes/no, 默认auto: 如果能从别的站点获得封面则不用javdb的以避免水印) +ignore_javdb_cover = auto +# 是否统一女优艺名。启用时会尝试将女优的多个艺名统一成一个 +unify_actress_name = yes + + +# 各个站点的免代理地址。地址失效时软件会自动尝试获取新地址,你也可以手动设置 +[ProxyFree] +avsox = https://avsox.click +javdb = https://javdb571.com +javbus = https://www.busdmm.shop +javlib = https://www.v90f.com/ + +# 配置整理时的命名规则 +# save_dir, nfo_title和filename中可以使用变量来引用影片的数据,支持的变量列表见下面的地址: +# https://github.com/Yuukiy/JavSP/wiki/NamingRule-%7C-%E5%91%BD%E5%90%8D%E8%A7%84%E5%88%99 +[NamingRule] +# 设置媒体服务器类型 (universal/jellyfin/video_station, 默认universal: 按兼容性最高的方式命名封面和nfo) +media_servers = universal +# 整理后的影片和封面等文件的保存位置 +output_folder = #整理完成 +# 存放影片、封面等文件的文件夹路径 +# 如果你使用Jellyfin等媒体管理工具,可以将规则改为 $censor/$actress/[$num] +save_dir = $actress/$num $title +# 影片、封面、nfo信息文件等的文件名将基于下面的规则来创建 +filename = $num +# 允许的最长文件路径(路径过长时将据此自动截短标题) +max_path_len = 180 +# 是否以字节数来计算文件路径长度(auto/yes/no, auto将自动根据输出路径的文件系统是本地还是远程来判断) +calc_path_len_by_byte = auto +# 路径中的$actress字段最多包含多少名女优? +max_actress_count = 10 +# nfo文件中的影片标题(即媒体管理工具中显示的标题) +nfo_title = $num $title +# 下面三个选项依次设置 已知有码/已知无码/不确定 这三种情况下 $censor 对应的文本(可以利用此变量将有码/无码影片整理到不同文件夹) +text_for_censored = 有码 +text_for_uncensored = 无码 +text_for_unknown_censorship = 打码情况未知 +# 下面这些项用来设置对应变量为空时的替代信息 +null_for_title = #未知标题 +null_for_actress = #未知女优 +null_for_serial = #未知系列 +null_for_director = #未知导演 +null_for_producer = #未知制作商 +null_for_publisher = #未知发行商 + +[Picture] +# 尽可能下载高清封面?(高清封面大小约 8-10 MiB,远大于普通封面,如果你的网络条件不佳,会降低整理速度) +use_big_cover = yes +# 启用图像识别裁剪海报 +use_ai_crop = no +# 要使用图像识别来裁剪的番号系列($label), \d表示纯数字番号(FC2和识别到的无码影片会自动使用图像识别裁剪) +use_ai_crop_labels = \d,ARA,SIRO,GANA,MIUM +# 要使用的图像识别引擎,详细配置见文档 https://github.com/Yuukiy/JavSP/wiki/AI-%7C-%E4%BA%BA%E8%84%B8%E8%AF%86%E5%88%AB +ai_engine = +# 百度人体分析应用的AppID(仅在图像识别引擎为baidu时需要) +aip_appid = +# 百度人体分析应用的API Key(仅在图像识别引擎为baidu时需要) +aip_api_key = +# 百度人体分析应用的Secret Key(仅在图像识别引擎为baidu时需要) +aip_secret_key = +# 在封面图上添加水印(标签),例如“字幕” +add_label_to_cover = no + +[Translate] +# 翻译引擎,可选: google, bing, baidu, claude(haiku), groq(llama 3.1-70b) (Google可以直接免费使用。留空表示禁用翻译功能) +# 进阶功能的文档 https://github.com/Yuukiy/JavSP/wiki/Translation-%7C-%E7%BF%BB%E8%AF%91 +engine = +# 是否翻译标题 +translate_title = yes +# 是否翻译剧情简介 +translate_plot = yes +# 百度翻译的APP ID和密钥 +baidu_appid = +baidu_key = +# 微软必应翻译(Azure 认知服务 → 翻译)的密钥 +bing_key = +# Claude的密钥 (使用haiku模型) +claude_key = +# Groq的密钥 (使用llama 3.1-70b模型) +groq_key = + +[NFO] +# 是否添加自定义分类(genre) +add_custom_genres = yes +# 要添加到自定义分类(genre)的字段,多个用英文逗号隔开,可以使用变量 +add_custom_genres_fields = $genre,$censor +# 是否添加自定义tag +add_custom_tags = yes +# 要添加到自定义tag的字段,多个用英文逗号隔开,可以使用变量 +add_custom_tags_fields = $genre,$censor +# 是否将分类(genre)添加到tag中 +add_genre_to_tag = yes + +[Other] +# 是否允许检查更新。如果允许,在有新版本时会显示提示信息和新版功能 +check_update = yes +# 是否允许检查到新版本时自动下载 +auto_update = yes +# 是否在刮削结束后自动退出软件 +auto_exit = false diff --git a/config.yml b/config.yml old mode 100644 new mode 100755 index 17547fbff..280a4d317 --- a/config.yml +++ b/config.yml @@ -1,198 +1,209 @@ -# vim:foldmethod=marker -################################ -scanner: - # 推测番号前忽略文件名中的特定字符串(忽略大小写,以英文分号;分隔) - # 大多数情况软件能够自动识别番号,只有当文件名中特定的部分导致番号识别错误时才需要更新此设置 - # 要忽略的正则表达式(如果你不熟悉正则表达式,请不要修改此配置,否则可能严重影响番号识别效果) - ignored_id_pattern: - - '(144|240|360|480|720|1080)[Pp]' - - '[24][Kk]' - - '\w+2048\.com' - - 'Carib(beancom)?' - - '[^a-z\d](f?hd|lt)[^a-z\d]' - # 整理哪个文件夹下的影片?(此项留空时将在运行时询问) - input_directory: null - # 哪些后缀的文件应当视为影片? - filename_extensions: [.3gp, .avi, .f4v, .flv, .iso, .m2ts, .m4v, .mkv, .mov, .mp4, .mpeg, .rm, .rmvb, .ts, .vob, .webm, .wmv, .strm, .mpg] - # 扫描影片文件时忽略指定的文件夹 - ignored_folder_name_pattern: ['^\.', '^#recycle$', '^#整理完成$', '^#不要扫描$'] - # 匹配番号时忽略小于指定大小的文件 - # 格式要求:https://docs.pydantic.dev/2.0/usage/types/bytesize/ - minimum_size: 232MiB - skip_nfo_dir: yes - manual: yes -################################ +# vim:foldmethod=marker +################################ +scanner: + # 推测番号前忽略文件名中的特定字符串(忽略大小写,以英文分号;分隔) + # 大多数情况软件能够自动识别番号,只有当文件名中特定的部分导致番号识别错误时才需要更新此设置 + # 要忽略的正则表达式(如果你不熟悉正则表达式,请不要修改此配置,否则可能严重影响番号识别效果) + ignored_id_pattern: #请手动清除重复的pattern + - '\!+' + - '\w+2048\.com' + - 'Carib(beancom)?' + - '[^a-z\d](f?hd|lt)[^a-z\d]' + - '144P' + - '240P' + - '360P' + - '480P' + - '720P' + - '1080P' + - '2K' + - '4K' + - '(144|240|360|480|720|1080)[Pp]' + - '[24][Kk]' + # 整理哪个文件夹下的影片?(此项留空时将在运行时询问) + input_directory: /Users/firewell/影视/AV + # 哪些后缀的文件应当视为影片? + filename_extensions: [.3gp, .avi, .f4v, .flv, .iso, .m2ts, .m4v, .mkv, .mov, .mp4, .mpeg, .rm, .rmvb, .ts, .vob, .webm, .wmv, .strm, .nrg, .divx] + # 扫描影片文件时忽略指定的文件夹 + ignored_folder_name_pattern: ['^\.', '^#recycle$', '^#整理完成$', '^不要扫描$'] + # 匹配番号时忽略小于指定大小的文件 + # 格式要求:https://docs.pydantic.dev/2.0/usage/types/bytesize/ + minimum_size: 232MiB + skip_nfo_dir: no + manual: false + +################################ network: # 设置代理服务器地址,支持 http, socks5/socks5h 代理,比如'http://127.0.0.1:1080' # null表示禁用代理 - proxy_server: null - # 各个站点的免代理地址。地址失效时软件会自动尝试获取新地址,你也可以手动设置 - proxy_free: - avsox: 'https://avsox.click' - javbus: 'https://www.seedmm.help' - javdb: 'https://javdb368.com' - javlib: 'https://www.y78k.com' - # 网络问题导致抓取数据失败时的重试次数,通常3次就差不多了 - retry: 3 - # https://en.wikipedia.org/wiki/ISO_8601#Durations - timeout: PT10S - -################################ -crawler: - # 要使用的爬虫列表(汇总数据时从前到后进行) - # airav avsox avwiki fanza fc2 fc2fan javbus javdb javlib javmenu jav321 mgstage prestige arzon arzon_iv - selection: - normal: [airav, avsox, javbus, javdb, javlib, jav321, mgstage, prestige] - fc2: [fc2, avsox, javdb, javmenu, fc2ppvdb] - cid: [fanza] - getchu: [dl_getchu] - gyutto: [gyutto] - # 爬虫至少要获取到哪些字段才可以视为抓取成功? - required_keys: [cover, title] - # 努力爬取更准确更丰富的信息(会略微增加部分站点的爬取耗时) - hardworking: true - # 使用网页番号作为最终番号(启用时会对番号大小写等进行更正) - respect_site_avid: true - # fc2fan已关站。如果你有镜像,请设置本地镜像文件夹的路径,此文件夹内要有类似'FC2-12345.html'的网页文件 - fc2fan_local_path: null - # 刮削一部电影后的等待时间(设置为0禁用此功能) - # https://en.wikipedia.org/wiki/ISO_8601#Durations - sleep_after_scraping: PT1S - # 是否使用javdb的封面(fallback/yes/no, 默认fallback: 如果能从别的站点获得封面则不用javdb的以避免水印) - use_javdb_cover: fallback - # 是否统一女优艺名。启用时会尝试将女优的多个艺名统一成一个 - normalize_actress_name: true - -################################ -# 配置整理时的命名规则 -# path_pattern, nfo_title_pattern和name_pattern中可以使用变量来引用影片的数据,支持的变量列表见下面的地址: -# https://github.com/Yuukiy/JavSP/wiki/NamingRule-%7C-%E5%91%BD%E5%90%8D%E8%A7%84%E5%88%99 -summarizer: - # 整理时是否移动文件: true-移动所有文件到新文件夹; false-数据保存到同级文件夹,不移动文件 - move_files: true - - # 路径相关的选项 - path: - # 存放影片、封面等文件的文件夹路径 - output_folder_pattern: '#整理完成/{actress}/[{num}] {title}' - # 影片、封面、nfo信息文件等的文件名将基于下面的规则来创建 - basename_pattern: '{num}' - # 允许的最长文件路径(路径过长时将据此自动截短标题) - length_maximum: 250 - # 是否以字节数来计算文件路径长度 - length_by_byte: true - # 路径中的{actress}字段最多包含多少名女优? - max_actress_count: 10 - # 是否用硬链接方式整理文件?硬链接可以节省空间,但不是所有文件系统都支持 - hard_link: false - - #标题处理 - title: - # 删除尾部可能存在的女优名 - remove_trailing_actor_name: true - - # 下面这些项用来设置对应变量为空时的替代信息 - default: - title: '#未知标题' - actress: '#未知女优' - series: '#未知系列' - director: '#未知导演' - producer: '#未知制作商' - publisher: '#未知发行商' - - # NFO文件生成相关的选项 - nfo: - # nfo文件的名称 - basename_pattern: "movie" - # nfo文件中的影片标题(即媒体管理工具中显示的标题) - title_pattern: '{num} {title}' - # 要添加到自定义分类的字段,空列表表示不添加 - custom_genres_fields: ['{genre}', '{censor}'] - # 要添加到自定义标签的字段,空列表表示不添加 - custom_tags_fields: ['{genre}', '{censor}'] - # 依次设置 已知无码/已知有码/不确定 这三种情况下 {censor} 对应的文本(可以利用此变量将有码/无码影片整理到不同文件夹) - censor_options_representation: ['无码', '有码', '打码情况未知'] - - cover: - # 封面文件的名称(不含拓展名),可以使用如`{title}`等字段 - basename_pattern: "poster" - # 尽可能下载高清封面?(高清封面大小约 8-10 MiB,远大于普通封面,如果你的网络条件不佳,会降低整理速度) - highres: true - # 在封面图上添加水印(标签),例如“字幕” - add_label: false - crop: - # 要使用图像识别来裁剪的番号系列需要匹配的正则表达式 - on_id_pattern: - - '^\d{6}[-_]\d{3}$' - - '^ARA' - - '^SIRO' - - '^GANA' - - '^MIUM' - - '^HHL' - # 要使用的图像识别引擎,详细配置见文档 https://github.com/Yuukiy/JavSP/wiki/AI-%7C-%E4%BA%BA%E8%84%B8%E8%AF%86%E5%88%AB - # NOTE: 此处无法直接对应,请参照注释手动填入 - engine: null # null表示禁用图像剪裁 - ## 使用Slimeface: {{{ - # engine: - # name: slimeface - ## }}} - - fanart: - # 横版封面文件的名称(不含拓展名),可以使用如`{title}`等字段 - basename_pattern: "fanart" - - extra_fanarts: - # 是否下载剧照? - enabled: true - # 间隔的两次封面爬取请求之间应该间隔多久 - scrap_interval: PT1.5S - -################################ -translator: - # 翻译引擎,可选: google, bing, baidu, claude(haiku), openai (Google可以直接免费使用。留空表示禁用翻译功能) - # 进阶功能的文档 https://github.com/Yuukiy/JavSP/wiki/Translation-%7C-%E7%BF%BB%E8%AF%91 - engine: null - ## 使用百度翻译: {{{ - # engine: - # name: baidu - # # 百度翻译的APP ID和密钥 - # app_id: '' - # api_key: '' - ## }}} - ## 使用必应翻译: {{{ - # engine: - # name: bing - # # 微软必应翻译(Azure 认知服务 → 翻译)的密钥 - # api_key: '' - ## }}} - ## 使用Claude翻译: {{{ - # engine: - # name: claude - # # Claude的密钥 (使用haiku模型) - # api_key: '' - ## }}} - ## 使用OpenAI翻译: {{{ - # engine: - # name: openai - # # OpenAI API(默认使用 Groq,可替换成任何兼容 OpenAI 的第三方 API) - # url: 'https://api.groq.com/openai/v1/chat/completions' - # api_key: '' - # # 要使用的模型(默认使用 Groq 的 llama-3.1-70b-versatile 模型,若使用 OpenAI 官方 API 的话一般模型为 gpt-3.5-turbo) - # model: llama-3.1-70b-versatile - ## }}} - - # 是否翻译各个字段 - fields: - # 是否翻译标题 - title: true - # 是否翻译剧情简介 - plot: true - -################################ -other: - # 是否在stdin/stdout进行交互 - interactive: true - # 是否允许检查更新。如果允许,在有新版本时会显示提示信息和新版功能 - check_update: true - # 是否允许检查到新版本时自动下载 - auto_update: false + proxy_server: null # 无代理模式(直连 proxy-free 域名) + # proxy_server: 'socks5://127.0.0.1:1080' # 启用代理时取消注释 + # 各个站点的免代理地址。地址失效时软件会自动尝试获取新地址,你也可以手动设置 + proxy_free: + avsox: 'https://avsox.click' + javdb: 'https://javdb572.com' + javbus: 'https://www.busdmm.shop' + javlib: 'https://www.v90f.com' + # 网络问题导致抓取数据失败时的重试次数,通常3次就差不多了 + retry: 3 + # https://en.wikipedia.org/wiki/ISO_8601#Durations + timeout: PT10S + +################################ +crawler: + # 要使用的爬虫列表(汇总数据时从前到后进行) + # airav avsox avwiki fanza fc2 fc2fan javbus javdb javlib javmenu jav321 mgstage prestige + selection: + normal: [javdb, airav, avsox, javbus, javlib, jav321, mgstage, prestige] + fc2: [javdb, fc2, avsox, javmenu] + cid: [fanza] + getchu: [dl_getchu] + gyutto: [gyutto] + # 爬虫至少要获取到哪些字段才可以视为抓取成功? + required_keys: [cover, title] + # 努力爬取更准确更丰富的信息(会略微增加部分站点的爬取耗时) + hardworking: true + # 使用网页番号作为最终番号(启用时会对番号大小写等进行更正) + respect_site_avid: true + # fc2fan已关站。如果你有镜像,请设置本地镜像文件夹的路径,此文件夹内要有类似'FC2-12345.html'的网页文件 + fc2fan_local_path: '' + # 刮削一部电影后的等待时间(设置为0禁用此功能) + # https://en.wikipedia.org/wiki/ISO_8601#Durations + sleep_after_scraping: PT1S + # 是否使用javdb的封面(fallback/yes/no, 默认fallback: 如果能从别的站点获得封面则不用javdb的以避免水印) + use_javdb_cover: fallback + # 是否统一女优艺名。启用时会尝试将女优的多个艺名统一成一个 + normalize_actress_name: true + +################################ +# 配置整理时的命名规则 +# path_pattern, nfo_title_pattern和name_pattern中可以使用变量来引用影片的数据,支持的变量列表见下面的地址: +# https://github.com/Yuukiy/JavSP/wiki/NamingRule-%7C-%E5%91%BD%E5%90%8D%E8%A7%84%E5%88%99 +summarizer: + # 整理时是否移动文件: true-移动所有文件到新文件夹; false-数据保存到同级文件夹,不移动文件 + move_files: false + + # 路径相关的选项 + path: + # 存放影片、封面等文件的文件夹路径 + output_folder_pattern: '#整理完成/{actress}/{num} {title}' + # 影片、封面、nfo信息文件等的文件名将基于下面的规则来创建 + basename_pattern: '{num}' + # 允许的最长文件路径(路径过长时将据此自动截短标题) + length_maximum: 250 + # 是否以字节数来计算文件路径长度 + length_by_byte: true + # 路径中的{actress}字段最多包含多少名女优? + max_actress_count: 10 + # 是否用硬链接方式整理文件?硬链接可以节省空间,但不是所有文件系统都支持 + hard_link: false + + #标题处理 + title: + # 删除尾部可能存在的女优名 + remove_trailing_actor_name: true + + # 下面这些项用来设置对应变量为空时的替代信息 + default: + title: '#未知标题' + actress: '#未知女优' + series: '#未知系列' + director: '#未知导演' + producer: '#未知制作商' + publisher: '#未知发行商' + + # NFO文件生成相关的选项 + nfo: + # nfo文件的名称 + basename_pattern: "movie" + # nfo文件中的影片标题(即媒体管理工具中显示的标题) + title_pattern: '{num} {title}' + # 要添加到自定义分类的字段,空列表表示不添加 + custom_genres_fields: ['{genre}', '{censor}'] + # 要添加到自定义标签的字段,空列表表示不添加 + custom_tags_fields: ['{genre}', '{censor}'] + # 依次设置 已知无码/已知有码/不确定 这三种情况下 $censor 对应的文本(可以利用此变量将有码/无码影片整理到不同文件夹) + censor_options_representation: ['无码', '有码', '打码情况未知'] + + cover: + # 封面文件的名称(不含拓展名),可以使用如`{title}`等字段 + basename_pattern: "poster" + # 尽可能下载高清封面?(高清封面大小约 8-10 MiB,远大于普通封面,如果你的网络条件不佳,会降低整理速度) + highres: true + # 在封面图上添加水印(标签),例如“字幕” + add_label: false + crop: + # 要使用图像识别来裁剪的番号系列需要匹配的正则表达式 + on_id_pattern: + - '^\d{6}[-_]\d{3}$' + - '^ARA' + - '^SIRO' + - '^GANA' + - '^MIUM' + # 要使用的图像识别引擎,详细配置见文档 https://github.com/Yuukiy/JavSP/wiki/AI-%7C-%E4%BA%BA%E8%84%B8%E8%AF%86%E5%88%AB + # NOTE: 此处无法直接对应,请参照注释手动填入 + engine: null #null表示禁用图像剪裁 + ## 使用Slimeface: {{{ + # engine: + # name: slimeface + ## }}} + + fanart: + # 横版封面文件的名称(不含拓展名),可以使用如`{title}`等字段 + basename_pattern: "fanart" + + extra_fanarts: + # 是否下载剧照? + enabled: false + # 间隔的两次封面爬取请求之间应该间隔多久 + scrap_interval: PT0S + +################################ +translator: + # NOTE: 此处无法直接对应,请参照注释手动填入 + # 翻译引擎,可选: google, bing, baidu, claude(haiku), openai (Google可以直接免费使用。留空表示禁用翻译功能) + # 进阶功能的文档 https://github.com/Yuukiy/JavSP/wiki/Translation-%7C-%E7%BF%BB%E8%AF%91 + engine: null + ## 使用百度翻译: {{{ + # engine: + # name: baidu + # # 百度翻译的APP ID和密钥 + # app_id: '' + # api_key: '' + ## }}} + ## 使用必应翻译: {{{ + # engine: + # name: bing + # # 微软必应翻译(Azure 认知服务 → 翻译)的密钥 + # api_key: '' + ## }}} + ## 使用Claude翻译: {{{ + # engine: + # name: claude + # # Claude的密钥 (使用haiku模型) + # api_key: '' + ## }}} + ## 使用OpenAI翻译: {{{ + # engine: + # name: openai + # # OpenAI API(默认使用 Groq,可替换成任何兼容 OpenAI 的第三方 API) + # url: 'https://api.groq.com/openai/v1/chat/completions' + # api_key: '' + # # 要使用的模型(默认使用 Groq 的 llama-3.1-70b-versatile 模型,若使用 OpenAI 官方 API 的话一般模型为 gpt-3.5-turbo) + # model: llama-3.1-70b-versatile + ## }}} + + # 是否翻译各个字段 + fields: + # 是否翻译标题 + title: true + # 是否翻译剧情简介 + plot: true + +################################ +other: + # 是否在stdin/stdout进行交互 + interactive: true + # 是否允许检查更新。如果允许,在有新版本时会显示提示信息和新版功能 + check_update: false + # 是否允许检查到新版本时自动下载 + auto_update: false \ No newline at end of file diff --git a/merge.conf b/merge.conf new file mode 100755 index 000000000..8327a6ce3 --- /dev/null +++ b/merge.conf @@ -0,0 +1,52 @@ +楓カレン(枫花恋) 楓カレン 楓可憐 田中レモン +楓ふうあ 楓富愛 +篠田ゆう 篠田優 +飯岡かなこ(饭冈) 飯岡かなこ 森沢かな 森沢かな(飯岡かなこ) 森澤佳奈(飯岡加奈子) 藤原遼子 饭冈加奈子 +奥田咲 奧田咲 +東條実澪 東条実澪 +涼森れむ(凉森玲梦) 涼森れむ 凉森 涼森玲夢 涼森 +三上悠亜 三上悠亞 三上 +深田えいみ(深田咏美) 深田えいみ 深田詠美 +田中ねね 田中なな実 +小島みなみ 小島南 小岛南 +おぐらななみ 小倉七海 +斎藤あみり 齋藤亞美里 斋藤 +五日市芽依 愛花あゆみ 愛花未滿 赤崎理奈 柏木あゆみ 相田亜由美 +長澤梓 長澤あずさ +愛弓りょう 愛弓涼 +愛音麻里亞 愛音まりあ +倉多真央 倉多まお +横宮七海 橫宮七海 +橘梨紗 橘メアリー +君島みお 君島美緒 +葵つかさ(葵司) 葵つかさ 葵司 +夢乃愛華 夢乃あいか +明里つむぎ 明里紬 +木下ひまり 木下彩芽 +橋本れいか 橋本有菜 +神宮寺ナオ 神宮寺奈緒 +水卜さくら 水卜櫻 +松岡すず 松岡玲 +松永さな 松永紗奈 +桃乃木かな 桃乃木香奈 +武藤彩香 武藤あやか 武藤绫香 +星宮こと 星宮一花 +梓ヒカリ 梓光莉 +美乃すずめ 美乃雀 +田中瞳 Hitomi(田中瞳) HITOMI +九十九メイ 九十九芽衣 +愛寶すず 愛宝すず +森ななこ 森奈奈子 +宝生リリー (芽森) 宝生リリー 寶生莉莉 一花琴音 芽森しずく 朝比奈るみな 朝日奈るみな 菊池凛 仲里絵里子 芽森雫 +吉高寧寧 吉高寧々 +Rio 柚木ティナ 柚木提娜 Rio Rio(柚木Tina) Rio(柚木ティナ) +穂高結花 穗高結花 +茉城麻美 茉城まみ +川崎ゆい 川崎真緒 朝倉すず 鳴海すず 中山亜矢 +羽田希 小林美沙 羽月のぞみ 今井なつみ 井上まさみ 上村加奈 希崎さおり 常盤優香 今井夏美 大島のぞみ 野添のぞみ +旬果 彩美旬果 あやみ旬果 +蒼井空 蒼井そら 苍井空 +菅野 菅野さゆき 菅野佐由紀 菅野松雪 +有栖花あか 凪ひかる 有栖花緋 汐世 +美谷朱里 美谷朱音 +波多野结衣 波多野結衣 diff --git a/merge_folders5.py b/merge_folders5.py new file mode 100644 index 000000000..d779005a6 --- /dev/null +++ b/merge_folders5.py @@ -0,0 +1,167 @@ +import os +import shutil +import argparse +import time +from tkinter import Tk +from tkinter.filedialog import askdirectory + + +def read_merge_conf(filename): + synonyms = {} + added_words = set() + + for enc in ('utf-8', 'gb18030'): + try: + with open(filename, 'r', encoding=enc) as file: + file.read() + break + except (UnicodeDecodeError, UnicodeError): + continue + else: + enc = 'utf-8' + + with open(filename, 'r', encoding=enc) as file: + for line_number, line in enumerate(file, start=1): + words = line.strip().split('\t') + if len(words) > 1: + primary_word = words[0] + for word in words: + if word in added_words: + raise ValueError(f"Duplicate word found: {word} on line {line_number}") + if word not in synonyms: + synonyms[word] = primary_word + added_words.add(word) + elif len(words) == 1 and words[0]: + print(f"Warning: Line '{line.strip()}' has no synonyms.") + + return synonyms + + +def fast_get_dir_size(path): + try: + total = 0 + for entry in os.scandir(path): + if entry.is_file(follow_symlinks=False): + total += entry.stat(follow_symlinks=False).st_size + elif entry.is_dir(follow_symlinks=False): + for root, dirs, files in os.walk(entry.path): + for f in files: + try: + total += os.path.getsize(os.path.join(root, f)) + except OSError: + pass + return total + except OSError: + return 0 + + +def rename_and_merge(directory, synonyms): + renamed = 0 + merged = 0 + + entries = os.listdir(directory) + dir_names = [e for e in entries if os.path.isdir(os.path.join(directory, e))] + + rename_plan = [] + for d in dir_names: + if d in synonyms: + target_name = synonyms[d] + if target_name != d: + rename_plan.append((d, target_name)) + + for old_name, new_name in rename_plan: + old_path = os.path.join(directory, old_name) + new_path = os.path.join(directory, new_name) + + if not os.path.exists(new_path): + os.rename(old_path, new_path) + print(f"Renamed: '{old_name}' -> '{new_name}'") + renamed += 1 + else: + print(f"Merging: '{old_name}' -> '{new_name}'") + for item in os.listdir(old_path): + s = os.path.join(old_path, item) + d = os.path.join(new_path, item) + if os.path.isdir(s): + shutil.copytree(s, d, dirs_exist_ok=True) + else: + if os.path.exists(d): + s_size = os.path.getsize(s) + d_size = os.path.getsize(d) + if s_size > d_size: + shutil.move(s, d) + else: + shutil.move(s, d) + shutil.rmtree(old_path) + merged += 1 + + print(f"\nRename: {renamed}, Merge: {merged}") + return renamed + merged + + +def remove_small_folders(directory, limit_mb): + limit_bytes = limit_mb * 1024 * 1024 + deleted = 0 + freed = 0 + + all_dirs = [] + for root, dirs, _ in os.walk(directory, topdown=False): + for d in dirs: + all_dirs.append(os.path.join(root, d)) + + for folder in all_dirs: + if not os.path.exists(folder): + continue + size = fast_get_dir_size(folder) + if size < limit_bytes: + size_mb = size / (1024 * 1024) + shutil.rmtree(folder) + print(f"Deleted ({size_mb:.1f}MB): {folder}") + freed += size + deleted += 1 + + print(f"\nDeleted {deleted} small folders, freed {freed / (1024*1024):.1f}MB") + return deleted + + +def select_directory(): + root = Tk() + root.withdraw() + directory = askdirectory(title="Select Directory") + if directory: + return directory + else: + print("No directory selected.") + exit(1) + + +if __name__ == "__main__": + parser = argparse.ArgumentParser(description="Rename/merge actress folders and remove small folders.") + parser.add_argument('-i', '--input', help="Directory to process.") + parser.add_argument('-s', '--size', type=int, default=200, help="Remove folders smaller than this size in MB (default: 200).") + args = parser.parse_args() + + directory_to_process = args.input or select_directory() + conf_file = "merge.conf" + start = time.time() + + print(f"Processing: {directory_to_process}") + print("=" * 60) + + try: + synonyms = read_merge_conf(conf_file) + rename_and_merge(directory_to_process, synonyms) + except Exception as e: + print(f"An error occurred during rename/merge: {e}") + + print("=" * 60) + print("Removing small folders...") + + try: + remove_small_folders(directory_to_process, args.size) + except Exception as e: + print(f"An error occurred while removing small folders: {e}") + + elapsed = time.time() - start + print("=" * 60) + print(f"Done in {elapsed:.1f}s") diff --git a/pyproject.toml b/pyproject.toml index a5e1b4d10..5fd1e83c4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,16 +28,13 @@ lxml = {extras = ["html-clean"], version = "^5.2.1"} confz = "^2.0.1" pydantic-extra-types = "^2.9.0" pendulum = "^3.0.0" -slimeface = "^2024.9.27" + [tool.poetry.scripts] javsp = "javsp.__main__:entry" server = "javsp.server:entry" -[[tool.poetry.source]] -name = "mirrors" -url = "https://pypi.tuna.tsinghua.edu.cn/simple/" -priority = "primary" + [tool.poetry.group.dev.dependencies] diff --git a/requirements-mac.txt b/requirements-mac.txt new file mode 100644 index 000000000..57070cbb6 --- /dev/null +++ b/requirements-mac.txt @@ -0,0 +1,3 @@ +requirements-mac.txt: macOS依赖说明 +# macOS 依赖(Homebrew) +# 详见 README.md 或 requirements.txt diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 000000000..4c8854872 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,77 @@ +appnope==0.1.4 +asttokens==3.0.0 +attrs==25.1.0 +backcall==0.2.0 +beautifulsoup4==4.13.3 +bleach==6.2.0 +certifi==2025.11.12 +charset-normalizer==3.4.1 +cloudscraper==1.2.71 +colorama==0.4.6 +decorator==5.2.1 +defusedxml==0.7.1 +docopt==0.6.2 +executing==2.2.0 +fastjsonschema==2.21.1 +greenlet==3.3.0 +h11==0.16.0 +idna==3.10 +ipython==8.12.3 +jedi==0.19.2 +Jinja2==3.1.6 +jsonschema==4.23.0 +jsonschema-specifications==2024.10.1 +jupyter_client==8.6.3 +jupyter_core==5.7.2 +jupyterlab_pygments==0.3.0 +lxml==6.0.2 +lxml_html_clean==0.4.3 +MarkupSafe==3.0.2 +matplotlib-inline==0.1.7 +mistune==3.1.2 +nbclient==0.10.2 +nbconvert==7.16.6 +nbformat==5.10.4 +outcome==1.3.0.post0 +packaging==24.2 +pandocfilters==1.5.1 +parso==0.8.4 +pexpect==4.9.0 +pickleshare==0.7.5 +pillow==12.0.0 +pipreqs==0.5.0 +platformdirs==4.3.6 +playwright==1.57.0 +pretty-errors==1.2.25 +prompt_toolkit==3.0.50 +ptyprocess==0.7.0 +pure_eval==0.2.3 +pyee==13.0.0 +Pygments==2.19.1 +pyparsing==3.3.1 +PySocks==1.7.1 +python-dateutil==2.9.0.post0 +pyzmq==26.2.1 +referencing==0.36.2 +requests==2.32.3 +requests-toolbelt==1.0.0 +rpds-py==0.23.1 +selenium==4.39.0 +six==1.17.0 +sniffio==1.3.1 +sortedcontainers==2.4.0 +soupsieve==2.6 +stack-data==0.6.3 +tinycss2==1.4.0 +tornado==6.4.2 +tqdm==4.67.1 +traitlets==5.14.3 +trio==0.32.0 +trio-websocket==0.12.2 +typing_extensions==4.15.0 +urllib3==2.6.2 +wcwidth==0.2.13 +webencodings==0.5.1 +websocket-client==1.9.0 +wsproto==1.3.2 +yarg==0.1.9