├── LICENSE ├── README.md ├── javsdt ├── Class │ ├── JavFile.py │ └── Settings.py ├── CreateIni.py ├── Functions │ ├── Baidu.py │ ├── Car.py │ ├── Genre.py │ ├── Picture.py │ ├── Process.py │ ├── Record.py │ ├── Requests │ │ ├── ArzonReq.py │ │ ├── Download.py │ │ ├── Jav321Req.py │ │ ├── JavbusReq.py │ │ ├── JavdbReq.py │ │ └── JavlibraryReq.py │ ├── Standard.py │ ├── Status.py │ ├── User.py │ └── XML.py ├── Jav321.py ├── JavbusWuma.py ├── JavbusYouma.py ├── JavdbFc2.py ├── StaticFiles │ ├── divulge.png │ ├── emby.ico │ ├── ini.ico │ ├── javbus.ico │ ├── javdb.ico │ ├── javlibrary.ico │ ├── javsdt.ico │ ├── subtitle.png │ ├── suren.ico │ └── update.ico ├── Update.py ├── divulge.png ├── emby_actors.py ├── javlibrary.py ├── subtitle.png ├── 【特征对照表】.xlsx └── 【素人车牌】.txt ├── requirements.txt ├── 检查更新.json └── 测试影片.zip /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 junerain123 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Adult Video Scraper for JAV 日本AV(JAV)刮削整理 2 | 原作者暂停更新和修复bug。开此分支用于维护javsdt并且继续开发,接受pull request,欢迎提交新功能和修复bug。 3 | 4 | 为了维护及开发新功能,欢迎各位加入TG群https://t.me/javsdtool 5 | 6 | Todo: 7 | - [ ] 由于缺少相关背景,需要有会使用Emby API的大佬帮助整合进入Emby。(参考Javscraper: https://github.com/JavScraper/Emby.Plugins.JavScraper 和jellyfin-plugin-avdc:https://github.com/xjasonlyu/jellyfin-plugin-avdc) 8 | 9 | ## 源码使用方法, 10 | 1、先安装[Python3](https://www.python.org/downloads/)和[Node.js](https://nodejs.org/zh-cn/download/)最新版本。 11 | 12 | 2、进入[Release](https://github.com/fanza1/Fake_javsdt/releases),下载最新版本源码 13 | 14 | 3、找到根目录下的requirements.txt,然后运行pip install安装执行环境 15 | ``` 16 | pip install -r .\requirements.txt 17 | pip install -U cfscrape 18 | ``` 19 | 4、然后进入javsdt目录执行CreateIni.py 20 | 21 | 5、接下来按需求执行如JavbusYouma.py 22 | 23 | ## 常见问题 24 | * 注意,执行方法可以打开cmd或PowerShell使用命令行如 25 | ``` 26 | pip install -r .\requirements.txt 27 | pip install -U cfscrape 28 | python CreateIni.py 29 | python JavbusYouma.py 30 | ``` 31 | 对于windows用户也可以直接双击运行,选择python执行 32 | 33 | * javlibrary免翻墙刮削出错建议更换网址,图书馆官方网址发布:http://www.javlibrary.com/cn/publictopic.php?id=13483 34 | 35 | ## Log 记录 36 | - [x] 21.6.7 fix JavBus图片刮削 37 | - [x] 21.2.25 fix JavBus有码无法刮削genre和tag 38 | - [x] 21.3.4 fix JavBus无码无法刮削genre和tag 39 | - [x] 21.4.2 fix javlibrary评分错乱的问题,移除作者瞎搞的评分算法,忠于图书馆的评分。修复由于cf造成的无法获取javlibrary网页的问题,但需要用户安装最新版node.js并且在命令行执行 40 | ``` 41 | pip install -U cfscrape 42 | ``` 43 | - [x] 21.4.2 new feature 增加避免小众高分影片霸榜的算法 44 | 45 | 46 | # 以下为原repo的Readme 47 | ## jav-standard-tool 简称javsdt 48 | ## 作者为生活所迫,已经跑路... 49 | 简介:收集影视元数据,并规范本地文件(夹)的格式,收集演员头像,为emby、kodi、jellyfin、极影派等影片管理软件铺路。 50 | 51 | 52 | ## 1、【一般用户】下载及群链接: 53 | 目前2021年2月7日更新的1.1.5版本 推荐使用win10 64位 54 | [从蓝奏云下载](https://junerain.lanzous.com/ivp8Plg6wza) 或者 [从github下载](https://github.com/javsdt/javsdt/releases/tag/V1.1.5) 55 | 56 | [前往下载演员头像](https://github.com/javsdt/javsdt/releases/tag/女优头像) 57 | 58 | [企鹅群](https://jq.qq.com/?_wv=1027&k=5CbWOpV)(需要付费1人民币扩群) 59 | [电报群](https://t.me/joinchat/PaHhgBaleu_qEgFy_NJlIA)(尽量进企鹅群,电报群真的没时间去了) 60 | 61 | ## 2、[使用说明(还没写完)](https://github.com/javsdt/javsdt/wiki) 62 | [旧版的使用说明从蓝奏云下载](https://www.lanzous.com/ib0qozg) 63 | 64 | ## 3、【其他开发者】运行环境: 65 | python3.7.6 发行版是pyinstaller打包的exe 66 | pip install requests==2.20.0(安装2.25.1报错ProxyError无法使用http代理) 67 | pip install Pillow 68 | pip install baidu-aip 69 | pip install pysocks 70 | pip install [cloudscraper](https://github.com/VeNoMouS/cloudscraper)(目前版本暂时不需要) 71 | pip install xlrd==1.2.0(安装2.2.1无法读取xlsx) 72 | 几个jav的py都是独立执行的,加了很多很多注释,希望能理解其中踩过的坑。 73 | 74 | ## 4、工作流程: 75 | (1)用户选择文件夹,遍历路径下的所有文件。 76 | (2)文件是jav,取车牌号,到javXXX网站搜索影片找到对应网页。 77 | (3)获取网页源码找出“标题”“导演”“发行日期”等信息和DVD封面url。 78 | (4)重命名影片文件。 79 | (5)重命名文件夹或建立独立文件夹。 80 | (6)保存信息写入nfo。 81 | (7)下载封面url作fanart.jpg,裁剪右半边作poster.jpg。 82 | (8)移动文件夹,完成归类。 83 | 84 | ## 5、目标效果: 85 | 效果图不放了 86 | !=[image](https://github.com/javsdt/images/blob/master/jav/javsdt/readme/%E7%9B%AE%E6%A0%87%E6%95%88%E6%9E%9C1.png?raw=false) 87 | !=[image](https://github.com/javsdt/images/blob/master/jav/javsdt/readme/%E7%9B%AE%E6%A0%87%E6%95%88%E6%9E%9C2.png?raw=false) 88 | !=[image](https://github.com/javsdt/images/blob/master/jav/javsdt/readme/%E7%9B%AE%E6%A0%87%E6%95%88%E6%9E%9C3.jpg?raw=false) 89 | 90 | ## 6、ini中的用户设置: 91 | ![image](https://github.com/javsdt/images/blob/master/jav/javsdt/readme/ini%E8%AE%BE%E7%BD%AE.PNG?raw=false) 92 | 93 | ## 7、其他说明: 94 | (1)不需要赞助; 95 | (2)允许对软件进行任何形式的转载; 96 | (3)代码及软件使用“MIT许可证”,他人可以修改代码、发布分支,允许闭源、商业化,但造成后果与本作者无关。 97 | -------------------------------------------------------------------------------- /javsdt/Class/JavFile.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from os import sep 3 | 4 | 5 | # 每一部jav的“结构体” 6 | class JavFile(object): 7 | def __init__(self, file_raw, root, car, episode, subtitle, num_current): 8 | self.name = file_raw # 文件名带文件扩展名 ABC-123.mp4 9 | self.root = root # 视频所在文件夹的路径 10 | self.car = car # 车牌 11 | self.episode = episode # 第几集 1 2 3集 12 | self.subtitle = subtitle # 字幕文件名 ABC-123.srt 13 | self.type = '.' + file_raw.split('.')[-1].lower() # 视频文件扩展名 .mp4 14 | self.subtitle_type = '.' + subtitle.split('.')[-1].lower() # 字幕扩展名 .srt 15 | self.number = num_current # 当前处理的视频在所有视频中的编号 16 | 17 | # 视频文件完整路径 18 | @property 19 | def path(self): 20 | return self.root + sep + self.name 21 | 22 | # 视频文件完整路径,但不带文件扩展名 23 | @property 24 | def name_no_ext(self): 25 | return self.name[:-len(self.type)] 26 | 27 | # 所在文件夹名称 28 | @property 29 | def folder(self): 30 | return self.root.split(sep)[-1] 31 | 32 | # 字幕文件完整路径 33 | @property 34 | def path_subtitle(self): 35 | return self.root + sep + self.subtitle -------------------------------------------------------------------------------- /javsdt/Class/Settings.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from configparser import RawConfigParser 3 | from os import system 4 | from os.path import exists 5 | from aip import AipBodyAnalysis 6 | 7 | 8 | # 设置 9 | class Settings(object): 10 | def __init__(self, av_type): 11 | config_settings = RawConfigParser() 12 | config_settings.read('【点我设置整理规则】.ini', encoding='utf-8-sig') 13 | ####################################################### nfo ################################################### 14 | # 是否 跳过已存在nfo的文件夹,不处理已有nfo的文件夹 15 | self.bool_skip = True if config_settings.get("收集nfo", "是否跳过已存在nfo的文件夹?") == '是' else False 16 | # 是否 收集nfo 17 | self.bool_nfo = True if config_settings.get("收集nfo", "是否收集nfo?") == '是' else False 18 | # 自定义 nfo中title的格式 19 | self._custom_nfo_title = config_settings.get("收集nfo", "nfo中title的格式") 20 | # 是否 去除 标题 末尾可能存在的演员姓名 21 | self.bool_strip_actors = True if config_settings.get("收集nfo", "是否去除标题末尾的演员姓名?") == '是' else False 22 | # 自定义 将系列、片商等元素作为特征,因为emby不会直接在影片介绍页面上显示片商,也不会读取系列set 23 | self._custom_genres = config_settings.get("收集nfo", "额外将以下元素添加到特征中") 24 | # ?是否将“系列”写入到特征中 25 | self.bool_write_series = True if '系列' in self._custom_genres else False 26 | # ?是否将“片商”写入到特征中 27 | self.bool_write_studio = True if '片商' in self._custom_genres else False 28 | # 是否 将特征保存到风格中 29 | self.bool_genre = True if config_settings.get("收集nfo", "是否将特征保存到genre?") == '是' else False 30 | # 是否 将 片商 作为特征 31 | self.bool_tag = True if config_settings.get("收集nfo", "是否将特征保存到tag?") == '是' else False 32 | ####################################################### 重命名 ################################################# 33 | # 是否 重命名 视频 34 | self.bool_rename_video = True if config_settings.get("重命名影片", "是否重命名影片?") == '是' else False 35 | # 自定义 重命名 视频 36 | self._custom_video = config_settings.get("重命名影片", "重命名影片的格式") 37 | # 是否 重命名视频所在文件夹,或者为它创建独立文件夹 38 | self._bool_rename_folder = True if config_settings.get("修改文件夹", "是否重命名或创建独立文件夹?") == '是' else False 39 | # 自定义 新的文件夹名 40 | self._custom_folder = config_settings.get("修改文件夹", "新文件夹的格式") 41 | ########################################################## 归类 ################################################ 42 | # 是否 归类jav 43 | self.bool_classify = True if config_settings.get("归类影片", "是否归类影片?") == '是' else False 44 | # 是否 针对“文件夹”归类jav,“否”即针对“文件” 45 | self.bool_classify_folder = True if config_settings.get("归类影片", "针对文件还是文件夹?") == '文件夹' else False 46 | # 自定义 路径 归类的jav放到哪 47 | self._custom_root = config_settings.get("归类影片", "归类的根目录") 48 | # 自定义 jav按什么类别标准来归类 49 | self._custom_classify_basis = config_settings.get("归类影片", "归类的标准") 50 | ######################################################## 图片 ################################################ 51 | # 是否 下载图片 52 | self.bool_jpg = True if config_settings.get("下载封面", "是否下载封面海报?") == '是' else False 53 | # 自定义 命名 大封面fanart 54 | self._custom_fanart = config_settings.get("下载封面", "DVD封面的格式") 55 | # 自定义 命名 小海报poster 56 | self._custom_poster = config_settings.get("下载封面", "海报的格式") 57 | # 是否 如果视频有“中字”,给poster的左上角加上“中文字幕”的斜杠 58 | self.bool_watermark_subtitle = True if config_settings.get("下载封面", "是否为海报加上中文字幕条幅?") == '是' else False 59 | # 是否 如果视频是“无码流出”,给poster的右上角加上“无码流出”的斜杠 60 | self.bool_watermark_divulge = True if config_settings.get("下载封面", "是否为海报加上无码流出条幅?") == '是' else False 61 | ###################################################### 字幕 ####################################################### 62 | # 是否 重命名用户已拥有的字幕 63 | self.bool_rename_subtitle = True if config_settings.get("字幕文件", "是否重命名已有的字幕文件?") == '是' else False 64 | ###################################################### kodi ####################################################### 65 | # 是否 收集演员头像 66 | self.bool_sculpture = True if config_settings.get("kodi专用", "是否收集演员头像?") == '是' else False 67 | # 是否 对于多cd的影片,kodi只需要一份图片和nfo 68 | self.bool_cd_only = True if config_settings.get("kodi专用", "是否对多cd只收集一份图片和nfo?") == '是' else False 69 | ###################################################### 代理 ######################################################## 70 | # 是否 使用局部代理 71 | self._bool_proxy = True if config_settings.get("局部代理", "是否使用局部代理?") == '是' else False 72 | # 是否 使用http代理,否 就是socks5 73 | self._bool_http = True if config_settings.get("局部代理", "http还是socks5?") == 'http' else False 74 | # 代理端口 75 | self._custom_proxy = config_settings.get("局部代理", "代理端口") 76 | # 是否 代理javlibrary 77 | self._bool_library_proxy = True if config_settings.get("局部代理", "是否代理javlibrary(有问题)?") == '是' else False 78 | # 是否 代理javbus,还有代理javbus上的图片cdnbus 79 | self._bool_bus_proxy = True if config_settings.get("局部代理", "是否代理javbus?") == '是' else False 80 | # 是否 代理javbus,还有代理javbus上的图片cdnbus 81 | self._bool_321_proxy = True if config_settings.get("局部代理", "是否代理jav321?") == '是' else False 82 | # 是否 代理javdb,还有代理javdb上的图片 83 | self._bool_db_proxy = True if config_settings.get("局部代理", "是否代理javdb?") == '是' else False 84 | # 是否 代理arzon 85 | self._bool_arzon_proxy = True if config_settings.get("局部代理", "是否代理arzon?") == '是' else False 86 | # 是否 代理dmm图片,javlibrary和javdb上的有码图片几乎都是直接引用dmm 87 | self._bool_dmm_proxy = True if config_settings.get("局部代理", "是否代理dmm图片?") == '是' else False 88 | #################################################### 原影片文件的性质 ################################################ 89 | # 自定义 无视的字母数字 去除影响搜索结果的字母数字 xhd1080、mm616、FHD-1080 90 | self._custom_surplus_words_youma_in_filename = config_settings.get("原影片文件的性质", "有码素人无视多余的字母数字") 91 | # 自定义 无视的字母数字 去除影响搜索结果的字母数字 full、tokyohot、 92 | self._custom_surplus_words_wuma_in_filename = config_settings.get("原影片文件的性质", "无码无视多余的字母数字") 93 | # 自定义 原影片性质 影片有中文,体现在视频名称中包含这些字符 94 | self._custom_subtitle_words_in_filename = config_settings.get("原影片文件的性质", "是否中字即文件名包含") 95 | # 自定义 是否中字 这个元素的表现形式 96 | self.custom_subtitle_expression = config_settings.get("原影片文件的性质", "是否中字的表现形式") 97 | # 自定义 原影片性质 影片是无码流出片,体现在视频名称中包含这些字符 98 | self._custom_divulge_words_in_filename = config_settings.get("原影片文件的性质", "是否流出即文件名包含") 99 | # 自定义 是否流出 这个元素的表现形式 100 | self.custom_divulge_expression = config_settings.get("原影片文件的性质", "是否流出的表现形式") 101 | # 自定义 原影片性质 有码 102 | self._av_type = config_settings.get("原影片文件的性质", av_type) 103 | ##################################################### 信息来源 ################################################## 104 | # 是否 收集javlibrary下方用户超过10个人点赞的评论 105 | self.bool_review = True if config_settings.get("信息来源", "是否用javlibrary整理影片时收集网友的热评?") == '是' else False 106 | # 是否 收集javlibrary下方用户超过10个人点赞的评论 107 | self.bool_bus_first = True if config_settings.get("信息来源", "是否用javlibrary整理影片时优先从javbus下载图片?") == '是' else False 108 | ################################################### 其他设置 #################################################### 109 | # 是否 使用简体中文 简介翻译的结果和jav特征会变成“简体”还是“繁体” 110 | self.bool_zh = True if config_settings.get("其他设置", "简繁中文?") == '简' else False 111 | # 网址 javlibrary 112 | self._url_library = config_settings.get("其他设置", "javlibrary网址") 113 | # 网址 javbus 114 | self._url_bus = config_settings.get("其他设置", "javbus网址") 115 | # 网址 javdb 116 | self._url_db = config_settings.get("其他设置", "javdb网址") 117 | # 自定义 文件类型 只有列举出的视频文件类型,才会被处理 118 | self._custom_file_type = config_settings.get("其他设置", "扫描文件类型") 119 | # 自定义 命名格式中“标题”的长度 windows只允许255字符,所以限制长度,但nfo中的标题是全部 120 | self.int_title_len = int(config_settings.get("其他设置", "重命名中的标题长度(50~150)")) 121 | ######################################## 百度翻译API #################################################### 122 | # 是否 需要简介 123 | self.bool_plot = True if config_settings.get("百度翻译API", "是否需要日语简介?") == '是' else False 124 | # 是否 把日语简介翻译为中文 125 | self.bool_tran = True if config_settings.get("百度翻译API", "是否翻译为中文?") == '是' else False 126 | # 账户 百度翻译api 127 | self._tran_id = config_settings.get("百度翻译API", "APP ID") 128 | self._tran_sk = config_settings.get("百度翻译API", "密钥") 129 | ######################################## 百度人体分析 #################################################### 130 | # 是否 需要准确定位人脸的poster 131 | self.bool_face = True if config_settings.get("百度人体分析", "是否需要准确定位人脸的poster?") == '是' else False 132 | # 账户 百度人体分析 133 | self._al_id = config_settings.get("百度人体分析", "appid") 134 | self._ai_ak = config_settings.get("百度人体分析", "api key") 135 | self._al_sk = config_settings.get("百度人体分析", "secret key") 136 | 137 | ########################################################################################################## 138 | # 是否 重命名视频所在文件夹,或者为它创建独立文件夹 139 | self.bool_rename_folder = self.judge_need_rename_folder() 140 | 141 | # ######################[收集nfo]##################################### 142 | # 命名nfo中title的格式 143 | def formula_name_nfo_title(self): 144 | # 给用户命名用的标题可能是删减的,nfo中的标题是完整标题 145 | return self._custom_nfo_title.replace('标题', '完整标题', 1).split('+') 146 | 147 | # 额外放入特征风格中的元素 148 | def list_extra_genre(self): 149 | list_extra_genres = self._custom_genres.split('、') if self._custom_genres else [] # 需要的额外特征 150 | list_extra_genres = [i for i in list_extra_genres if i != '系列' and i != '片商'] 151 | return list_extra_genres 152 | 153 | # #########################[重命名影片]############################## 154 | # 得到视频命名格式list 155 | def formula_rename_video(self): 156 | return self._custom_video.split('+') 157 | 158 | # #########################[修改文件夹]############################## 159 | # 是否需要重命名文件夹或者创建新的文件夹 160 | def judge_need_rename_folder(self): 161 | if self.bool_classify: # 如果需要归类 162 | if self.bool_classify_folder: # 并且是针对文件夹 163 | return True # 那么必须重命名文件夹或者创建新的文件夹 164 | else: 165 | return False # 否则不会操作新文件夹 166 | else: # 不需要归类 167 | if self._bool_rename_folder: # 但是用户本来就在ini中写了要重命名文件夹 168 | return True 169 | else: 170 | return False 171 | 172 | # 得到文件夹命名格式list 示例:['车牌', '【', '全部演员', '】'] 173 | def formula_rename_folder(self): 174 | return self._custom_folder.split('+') 175 | 176 | # #########################[归类影片]############################## 177 | # 功能:检查 归类根目录 的合法性 178 | # 参数:用户自定义的归类根目录,用户选择整理的文件夹路径 179 | # 返回:归类根目录路径 180 | # 辅助:os.sep,os.system 181 | def check_classify_root(self, root_choose, sep): 182 | if self.bool_classify: 183 | custom_root = self._custom_root.rstrip(sep) 184 | # 用户使用默认的“所选文件夹” 185 | if custom_root == '所选文件夹': 186 | return root_choose + sep + '归类完成' 187 | # 归类根目录 是 用户输入的路径c:\a,继续核实合法性 188 | else: 189 | # 用户输入的路径 不是 所选文件夹root_choose 190 | if custom_root != root_choose: 191 | if custom_root[:2] != root_choose[:2]: 192 | print('归类的根目录“', custom_root, '”和所选文件夹不在同一磁盘无法归类!请修正!') 193 | system('pause') 194 | if not exists(custom_root): 195 | print('归类的根目录“', custom_root, '”不存在!无法归类!请修正!') 196 | system('pause') 197 | return custom_root 198 | # 用户输入的路径 就是 所选文件夹root_choose 199 | else: 200 | return root_choose + sep + '归类完成' 201 | else: 202 | return '' 203 | 204 | # 归类标准 比如:影片类型\全部演员 205 | def custom_classify_basis(self): 206 | return self._custom_classify_basis 207 | 208 | # #########################[下载封面]############################## 209 | # 命名fanart的格式 210 | def formula_name_fanart(self): 211 | return self._custom_fanart.split('+') 212 | 213 | # 命名poster的格式 214 | def formula_name_poster(self): 215 | return self._custom_poster.split('+') 216 | 217 | # #########################[局部代理]############################## 218 | # 得到proxy 219 | def get_proxy(self): 220 | if self._bool_proxy and self._custom_proxy: 221 | if self._bool_http: 222 | proxies = {"http": "http://" + self._custom_proxy, 223 | "https": "https://" + self._custom_proxy} 224 | else: 225 | proxies = {"http": "socks5://" + self._custom_proxy, 226 | "https": "socks5://" + self._custom_proxy} 227 | proxy_library = proxies if self._bool_library_proxy else {} # 请求javlibrary时传递的参数 228 | proxy_bus = proxies if self._bool_bus_proxy else {} # 请求javbus时传递的参数 229 | proxy_321 = proxies if self._bool_321_proxy else {} # 请求jav321时传递的参数 230 | proxy_db = proxies if self._bool_db_proxy else {} # 请求javdb时传递的参数 231 | proxy_arzon = proxies if self._bool_arzon_proxy else {} # 请求arzon时传递的参数 232 | proxy_dmm = proxies if self._bool_dmm_proxy else {} # 请求dmm图片时传递的参数 233 | else: 234 | proxy_library = {} 235 | proxy_bus = {} 236 | proxy_321 = {} 237 | proxy_db = {} 238 | proxy_arzon = {} 239 | proxy_dmm = {} 240 | return proxy_library, proxy_bus, proxy_321, proxy_db, proxy_arzon, proxy_dmm 241 | 242 | # #########################[原影片文件的性质]############################## 243 | # 得到代表中字的文字list 244 | def list_subtitle_word_in_filename(self): 245 | return self._custom_subtitle_words_in_filename.upper().split('、') 246 | 247 | # 得到代表流出的文字list 248 | def list_divulge_word_in_filename(self): 249 | return self._custom_divulge_words_in_filename.upper().split('、') 250 | 251 | # 得到干扰车牌选择的文字list 252 | def list_surplus_word_in_filename(self, av_type): 253 | if av_type == '有码': 254 | return self._custom_surplus_words_youma_in_filename.upper().split('、') 255 | else: 256 | return self._custom_surplus_words_wuma_in_filename.upper().split('、') 257 | 258 | # 自定义有码、无码、素人、FC2的对应称谓 259 | def av_type(self): 260 | return self._av_type 261 | 262 | # #########################[信息来源]############################## 263 | 264 | # #########################[其他设置]############################## 265 | # javlibrary网址,是简体还是繁体 266 | def get_url_library(self): 267 | url_library = self._url_library 268 | if not url_library.endswith('/'): 269 | url_library += '/' 270 | return url_library + 'cn/' 271 | 272 | # javbus网址 273 | def get_url_bus(self): 274 | if not self._url_bus.endswith('/'): 275 | url_web_bus = self._url_bus + '/' 276 | else: 277 | url_web_bus = self._url_bus 278 | return url_web_bus 279 | 280 | # jav321网址 281 | def get_url_321(self): 282 | if self.bool_zh: 283 | url_search_321 = 'https://www.jav321.com/search' 284 | url_web_321 = 'https://www.jav321.com/' 285 | else: 286 | url_search_321 = 'https://tw.jav321.com/search' 287 | url_web_321 = 'https://tw.jav321.com/' 288 | return url_search_321, url_web_321 289 | 290 | # javdb网址 291 | def get_url_db(self): 292 | if not self._url_db.endswith('/'): 293 | url_db = self._url_db + '/' 294 | else: 295 | url_db = self._url_db 296 | return url_db 297 | 298 | # 得到扫描文件类型 299 | def tuple_video_type(self): 300 | return tuple(self._custom_file_type.upper().split('、')) 301 | 302 | # #########################[百度翻译API]############################## 303 | # 百度翻译的目标语言、翻译账户 304 | def get_translate_account(self): 305 | if self.bool_zh: 306 | to_language = 'zh' # 目标语言,zh是简体中文,cht是繁体中文 307 | else: 308 | to_language = 'cht' 309 | return to_language, self._tran_id, self._tran_sk 310 | 311 | # #########################[百度人体分析]############################## 312 | def start_body_analysis(self): 313 | if self.bool_face: 314 | return AipBodyAnalysis(self._al_id, self._ai_ak, self._al_sk) 315 | else: 316 | return None 317 | -------------------------------------------------------------------------------- /javsdt/CreateIni.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from os import system 3 | from configparser import RawConfigParser 4 | from traceback import format_exc 5 | 6 | try: 7 | print('>>正在重写ini文件...') 8 | config_settings = RawConfigParser() 9 | config_settings.add_section("收集nfo") 10 | config_settings.set("收集nfo", "是否跳过已存在nfo的文件夹?", "否") 11 | config_settings.set("收集nfo", "是否收集nfo?", "是") 12 | config_settings.set("收集nfo", "nfo中title的格式", "车牌+空格+标题") 13 | config_settings.set("收集nfo", "是否去除标题末尾的演员姓名?", "否") 14 | config_settings.set("收集nfo", "额外将以下元素添加到特征中", "系列、片商") 15 | config_settings.set("收集nfo", "是否将特征保存到genre?", "是") 16 | config_settings.set("收集nfo", "是否将特征保存到tag?", "是") 17 | config_settings.add_section("重命名影片") 18 | config_settings.set("重命名影片", "是否重命名影片?", "是") 19 | config_settings.set("重命名影片", "重命名影片的格式", "车牌+空格+标题") 20 | config_settings.add_section("修改文件夹") 21 | config_settings.set("修改文件夹", "是否重命名或创建独立文件夹?", "是") 22 | config_settings.set("修改文件夹", "新文件夹的格式", "【+全部演员+】+车牌") 23 | config_settings.add_section("归类影片") 24 | config_settings.set("归类影片", "是否归类影片?", "否") 25 | config_settings.set("归类影片", "针对文件还是文件夹?", "文件夹") 26 | config_settings.set("归类影片", "归类的根目录", "所选文件夹") 27 | config_settings.set("归类影片", "归类的标准", "影片类型\全部演员") 28 | config_settings.add_section("下载封面") 29 | config_settings.set("下载封面", "是否下载封面海报?", "是") 30 | config_settings.set("下载封面", "DVD封面的格式", "视频+-fanart.jpg") 31 | config_settings.set("下载封面", "海报的格式", "视频+-poster.jpg") 32 | config_settings.set("下载封面", "是否为海报加上中文字幕条幅?", "否") 33 | config_settings.set("下载封面", "是否为海报加上无码流出条幅?", "否") 34 | config_settings.add_section("字幕文件") 35 | config_settings.set("字幕文件", "是否重命名已有的字幕文件?", "是") 36 | config_settings.set("字幕文件", "是否跳过已有字幕的影片?", "是") 37 | config_settings.add_section("kodi专用") 38 | config_settings.set("kodi专用", "是否收集演员头像?", "否") 39 | config_settings.set("kodi专用", "是否对多cd只收集一份图片和nfo?", "否") 40 | config_settings.add_section("emby/jellyfin") 41 | config_settings.set("emby/jellyfin", "网址", "http://localhost:8096/") 42 | config_settings.set("emby/jellyfin", "API ID", "b55d950becc74bbebbf4698d995db826") 43 | config_settings.set("emby/jellyfin", "是否覆盖以前上传的头像?", "否") 44 | config_settings.add_section("局部代理") 45 | config_settings.set("局部代理", "是否使用局部代理?", "否") 46 | config_settings.set("局部代理", "http还是socks5?", "http") 47 | config_settings.set("局部代理", "代理端口", "127.0.0.1:10809") 48 | config_settings.set("局部代理", "是否代理javlibrary(有问题)?", "否") 49 | config_settings.set("局部代理", "是否代理javbus?", "否") 50 | config_settings.set("局部代理", "是否代理jav321?", "否") 51 | config_settings.set("局部代理", "是否代理javdb?", "否") 52 | config_settings.set("局部代理", "是否代理arzon?", "否") 53 | config_settings.set("局部代理", "是否代理dmm图片?", "否") 54 | # config_settings.set("其他设置", "是否将全部演员(多个)表现为“n人共演?", "否") 55 | config_settings.add_section("原影片文件的性质") 56 | config_settings.set("原影片文件的性质", "有码素人无视多余的字母数字", "xhd1080、mm616、fhd-1080") 57 | config_settings.set("原影片文件的性质", "无码无视多余的字母数字", "1080p、caribbean、carib、1pondo、1pon、fhd、all、tokyo-hot、tokyohot、3xplanet、full") 58 | config_settings.set("原影片文件的性质", "是否中字即文件名包含", "-C、_C、中字、中文字幕、字幕") 59 | config_settings.set("原影片文件的性质", "是否中字的表现形式", "㊥") 60 | config_settings.set("原影片文件的性质", "是否流出即文件名包含", "流出") 61 | config_settings.set("原影片文件的性质", "是否流出的表现形式", "无码流出") 62 | config_settings.set("原影片文件的性质", "有码", "有码") 63 | config_settings.set("原影片文件的性质", "无码", "无码") 64 | config_settings.set("原影片文件的性质", "素人", "素人") 65 | config_settings.set("原影片文件的性质", "FC2", "FC2") 66 | config_settings.add_section("信息来源") 67 | config_settings.set("信息来源", "是否用javlibrary整理影片时收集网友的热评?", "是") 68 | config_settings.set("信息来源", "是否用javlibrary整理影片时优先从javbus下载图片?", "否") 69 | config_settings.add_section("其他设置") 70 | config_settings.set("其他设置", "简繁中文?", "简") 71 | config_settings.set("其他设置", "javlibrary网址", "http://www.f50q.com/") 72 | config_settings.set("其他设置", "javbus网址", "https://www.buscdn.me") 73 | config_settings.set("其他设置", "javdb网址", "https://javdb6.com/") 74 | config_settings.set("其他设置", "扫描文件类型", "mp4、mkv、avi、wmv、iso、rmvb、flv、ts") 75 | config_settings.set("其他设置", "重命名中的标题长度(50~150)", "50") 76 | config_settings.add_section("百度翻译API") 77 | config_settings.set("百度翻译API", "是否需要日语简介?", "是") 78 | config_settings.set("百度翻译API", "是否翻译为中文?", "否") 79 | config_settings.set("百度翻译API", "app id", "") 80 | config_settings.set("百度翻译API", "密钥", "") 81 | config_settings.add_section("百度人体分析") 82 | config_settings.set("百度人体分析", "是否需要准确定位人脸的poster?", "否") 83 | config_settings.set("百度人体分析", "appid", "") 84 | config_settings.set("百度人体分析", "api key", "") 85 | config_settings.set("百度人体分析", "secret key", "") 86 | config_settings.write(open('【点我设置整理规则】.ini', "w", encoding='utf-8-sig')) 87 | print(' >“【点我设置整理规则】.ini”重写成功!') 88 | #################################################################################################################### 89 | config_actor = RawConfigParser() 90 | config_actor.add_section("缺失的演员头像") 91 | config_actor.set("缺失的演员头像", "演员姓名", "N(次数)") 92 | config_actor.add_section("说明") 93 | config_actor.set("说明", "上面的“演员姓名 = N(次数)”的表达式", "后面的N数字表示你有N部(次)影片都在找她的头像,可惜找不到") 94 | config_actor.set("说明", "你可以去保存一下她的头像jpg到“演员头像”文件夹", "以后就能保存她的头像到影片的文件夹了") 95 | config_actor.write(open('actors_for_kodi.ini', "w", encoding='utf-8-sig')) 96 | print(' >“actors_for_kodi.ini”重写成功!') 97 | system('pause') 98 | except: 99 | print(format_exc()) 100 | print('\n创建ini失败,解决上述问题后,重新打开exe创建ini!') 101 | system('pause') 102 | -------------------------------------------------------------------------------- /javsdt/Functions/Baidu.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import requests 3 | from os import system 4 | from time import sleep, time 5 | from hashlib import md5 6 | from json import loads 7 | # from traceback import format_exc 8 | 9 | 10 | # 功能:调用百度翻译API接口,翻译日语简介 11 | # 参数:百度翻译api账户api_id, api_key,需要翻译的内容word,目标语言to_lang 12 | # 返回:中文简介string 13 | # 辅助:os.system, hashlib.md5,time.time,requests.get,json.loads 14 | def translate(api_id, api_key, word, to_lang): 15 | for retry in range(10): 16 | # 把账户、翻译的内容、时间 混合md5加密,传给百度验证 17 | salt = str(time())[:10] 18 | final_sign = api_id + word + salt + api_key 19 | final_sign = md5(final_sign.encode("utf-8")).hexdigest() 20 | # 表单paramas 21 | paramas = { 22 | 'q': word, 23 | 'from': 'jp', 24 | 'to': to_lang, 25 | 'appid': '%s' % api_id, 26 | 'salt': '%s' % salt, 27 | 'sign': '%s' % final_sign 28 | } 29 | try: 30 | response = requests.get('http://api.fanyi.baidu.com/api/trans/vip/translate', params=paramas, timeout=(6, 7)) 31 | except: 32 | print(' >百度翻译拉闸了...重新翻译...') 33 | continue 34 | content = str(response.content, encoding="utf-8") 35 | # 百度返回为空 36 | if not content: 37 | print(' >百度翻译返回为空...重新翻译...') 38 | sleep(1) 39 | continue 40 | # 百度返回了dict json 41 | json_reads = loads(content) 42 | # print(json_reads) 43 | if 'error_code' in json_reads: # 返回错误码 44 | error_code = json_reads['error_code'] 45 | if error_code == '54003': 46 | print(' >请求百度翻译太快...技能冷却1秒...') 47 | sleep(1) 48 | elif error_code == '54005': 49 | print(' >发送了太多超长的简介给百度翻译...技能冷却3秒...') 50 | sleep(3) 51 | elif error_code == '52001': 52 | print(' >连接百度翻译超时...重新翻译...') 53 | elif error_code == '52002': 54 | print(' >百度翻译拉闸了...重新翻译...') 55 | elif error_code == '54003': 56 | print(' >使用过于频繁,百度翻译不想给你用了...') 57 | system('pause') 58 | elif error_code == '52003': 59 | print(' >请正确输入百度翻译API账号,请阅读【使用说明】!') 60 | print('>>javsdt已停止工作...') 61 | system('pause') 62 | elif error_code == '58003': 63 | print(' >你的百度翻译API账户被百度封禁了,请联系作者,告诉你解封办法!“') 64 | print('>>javsdt已停止工作...') 65 | system('pause') 66 | elif error_code == '90107': 67 | print(' >你的百度翻译API账户还未通过认证或者失效,请前往API控制台解决问题!“') 68 | print('>>javsdt已停止工作...') 69 | system('pause') 70 | else: 71 | print(' >百度翻译error_code!请截图联系作者!', error_code) 72 | continue 73 | else: # 返回正确信息 74 | return json_reads['trans_result'][0]['dst'] 75 | print(' >翻译简介失败...请截图联系作者...') 76 | return '【百度翻译出错】' + word 77 | 78 | 79 | -------------------------------------------------------------------------------- /javsdt/Functions/Car.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import re 3 | from os import system 4 | 5 | 6 | # 功能:发现用于javlibrary的有码车牌,因为T28-、ID比较特殊在 7 | # 参数:大写后的视频文件名file,素人车牌list_suren_car 示例:AVOP-127.MP4 ['LUXU', 'MIUM'] 8 | # 返回:发现的车牌 示例:AVOP-127 9 | # 辅助:re.search 10 | def find_car_library(file, list_suren_car): 11 | # car_pref 车牌前缀 ABP-,带横杠;car_suf,车牌后缀 123。 12 | # 先处理特例 T28 车牌 13 | if re.search(r'[^A-Z]?T28[-_ ]*\d\d+', file): 14 | car_pref = 'T-28' 15 | car_suf = re.search(r'T28[-_ ]*(\d\d+)', file).group(1) 16 | # 特例 ID 车牌,在javlibrary上,20ID-020是ID-20020 17 | elif re.search(r'[^\d]?\d\dID[-_ ]*\d\d+', file): 18 | carg = re.search(r'[^\d]?(\d\d)ID[-_ ]*(\d\d+)', file) 19 | car_pref = 'ID-' + carg.group(1) 20 | car_suf = carg.group(2) 21 | # 一般车牌 22 | elif re.search(r'[A-Z][A-Z]+[-_ ]*\d\d+', file): 23 | carg = re.search(r'([A-Z][A-Z]+)[-_ ]*(\d\d+)', file) 24 | car_pref = carg.group(1) 25 | # 如果是素人或无码车牌,不处理 26 | if car_pref in list_suren_car or car_pref in ['HEYZO', 'PONDO', 'CARIB', 'OKYOHOT']: 27 | return '' 28 | car_pref = car_pref + '-' 29 | car_suf = carg.group(2) 30 | else: 31 | return '' 32 | # 去掉太多的0,AVOP00127 => AVOP-127 33 | if len(car_suf) > 3: 34 | car_suf = car_suf[:-3].lstrip('0') + car_suf[-3:] 35 | return car_pref + car_suf 36 | 37 | 38 | # 功能:发现原视频文件名中用于javbus的有码车牌 39 | # 参数:大写后的视频文件名,素人车牌list_suren_car 示例:AVOP-127.MP4 ['LUXU', 'MIUM'] 40 | # 返回:发现的车牌 示例:AVOP-127 41 | # 辅助:re.search 42 | def find_car_bus(file, list_suren_car): 43 | # car_pref 车牌前缀 ABP-,带横杠;car_suf,车牌后缀 123。 44 | # 先处理特例 T28 车牌 45 | if re.search(r'[^A-Z]?T28[-_ ]*\d\d+', file): 46 | car_pref = 'T28-' 47 | car_suf = re.search(r'T28[-_ ]*(\d\d+)', file).group(1) 48 | # 以javbus上记录的20ID-020为标准 49 | elif re.search(r'[^\d]?\d\dID[-_ ]*\d\d+', file): 50 | carg = re.search(r'(\d\d)ID[-_ ]*(\d\d+)', file) 51 | car_pref = carg.group(1) + 'ID-' 52 | car_suf = carg.group(2) 53 | # 一般车牌 54 | elif re.search(r'[A-Z]+[-_ ]*\d\d+', file): 55 | carg = re.search(r'([A-Z]+)[-_ ]*(\d\d+)', file) 56 | car_pref = carg.group(1) 57 | if car_pref in list_suren_car or car_pref in ['HEYZO', 'PONDO', 'CARIB', 'OKYOHOT']: 58 | return '' 59 | car_pref = car_pref + '-' 60 | car_suf = carg.group(2) 61 | else: 62 | return '' 63 | # 去掉太多的0,avop00127 => avop-127 64 | if len(car_suf) > 3: 65 | car_suf = car_suf[:-3].lstrip('0') + car_suf[-3:] 66 | return car_pref + car_suf 67 | 68 | 69 | # 功能:发现原视频文件名中的无码车牌 70 | # 参数:被大写后的视频文件名,素人车牌list_suren_car 示例:ABC123ABC123.MP4 ['LUXU', 'MIUM'] 71 | # 返回:发现的车牌 示例:ABC123ABC123,只要是字母数字,全拿着 72 | # 辅助:re.search 73 | def find_car_wuma(file, list_suren_car): 74 | # N12345 75 | if re.search(r'[^A-Z]?N\d\d+', file): 76 | car_pref = 'N' 77 | car_suf = re.search(r'N(\d\d+)', file).group(1) 78 | # 123-12345 79 | elif re.search(r'\d+[-_ ]\d\d+', file): 80 | carg = re.search(r'(\d+)[-_ ](\d\d+)', file) 81 | car_pref = carg.group(1) + '-' 82 | car_suf = carg.group(2) 83 | # 只要是字母数字-_,全拿着 84 | elif re.search(r'[A-Z0-9]+[-_ ]?[A-Z0-9]+', file): 85 | carg = re.search(r'([A-Z0-9]+)([-_ ]*)([A-Z0-9]+)', file) 86 | car_pref = carg.group(1) 87 | # print(car_pref) 88 | if car_pref in list_suren_car: 89 | return '' 90 | car_pref = car_pref + carg.group(2) 91 | car_suf = carg.group(3) 92 | # 下面是处理和一般有码车牌差不多的无码车牌,拿到的往往是错误的,仅在1.0.4版本使用过,宁可不整理也不识别个错的 93 | # elif search(r'[A-Z]+[-_ ]?\d+', file): 94 | # carg = search(r'([A-Z]+)([-_ ]?)(\d+)', file) 95 | # car_pref = carg.group(1) 96 | # if car_pref in list_suren_car: 97 | # return '' 98 | # car_pref = car_pref + carg.group(2) 99 | # car_suf = carg.group(3) 100 | else: 101 | return '' 102 | # 无码就不去0了,去了0和不去0,可能是不同结果 103 | # if len(car_suf) > 3: 104 | # car_suf = car_suf[:-3].lstrip('0') + car_suf[-3:] 105 | return car_pref + car_suf 106 | 107 | 108 | # 功能:发现素人车牌,直接从已记录的list_suren_car中来对比 109 | # 参数:大写后的视频文件名,素人车牌list_suren_car 示例:LUXU-123.MP4 ['LUXU', 'MIUM'] 110 | # 返回:发现的车牌 示例:LUXU-123 111 | # 辅助:re.search 112 | def find_car_suren(file, list_suren_car): 113 | carg = re.search(r'([A-Z][A-Z]+)[-_ ]*(\d\d+)', file) # 匹配字幕车牌 114 | if str(carg) != 'None': 115 | car_pref = carg.group(1) 116 | # 如果用户把视频文件名指定为jav321上的网址,让该视频通过 117 | if car_pref not in list_suren_car and '三二一' not in file: 118 | return '' 119 | car_suf = carg.group(2) 120 | # 去掉太多的0,avop00127 121 | if len(car_suf) > 3: 122 | car_suf = car_suf[:-3].lstrip('0') + car_suf[-3:] 123 | return car_pref + '-' + car_suf 124 | else: 125 | return '' 126 | 127 | 128 | # 功能:得到素人车牌集合 129 | # 参数:无 130 | # 返回:素人车牌list 131 | # 辅助:无 132 | def list_suren_car(): 133 | try: 134 | with open('【素人车牌】.txt', 'r', encoding="utf-8") as f: 135 | list_suren_cars = list(f) 136 | except: 137 | print('【素人车牌】.txt读取失败!停止工作!') 138 | system('pause') 139 | list_suren_cars = [i.strip().upper() for i in list_suren_cars if i != '\n'] 140 | # print(list_suren_cars) 141 | return list_suren_cars 142 | -------------------------------------------------------------------------------- /javsdt/Functions/Genre.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import xlrd 3 | 4 | 5 | # 功能:得到优化的特征字典 6 | # 参数:用户在用哪个exe(对应sheet_name) ,简繁中文to_language 示例:Javbus有码 ,zh 7 | # 返回:优化的特征字典 8 | # 辅助:xlrd 9 | def better_dict_genre(sheet_name, to_language): 10 | dict = {} 11 | # 打开Excel文件 12 | excel = xlrd.open_workbook('【特征对照表】.xlsx',) 13 | # 定位excel中的某一sheet 14 | sheet = excel.sheet_by_name(sheet_name) 15 | row = sheet.nrows # 总行数 16 | for i in range(1, row): 17 | list_row = sheet.row_values(i) # i行的list 18 | if to_language == 'zh': 19 | dict[list_row[0]] = list_row[1] 20 | elif to_language == 'cht': 21 | dict[list_row[0]] = list_row[2] 22 | else: 23 | dict[list_row[0]] = list_row[1] 24 | # print(dict) 25 | return dict 26 | 27 | 28 | # print(better_dict_genre('Javlibrary', 'cht')) 29 | -------------------------------------------------------------------------------- /javsdt/Functions/Picture.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from os import system 3 | from PIL import Image 4 | 5 | 6 | # 功能:查看图片是否存在,能否打开,有没有损坏 7 | # 参数:图片路径path_picture 8 | # 返回:True 9 | # 辅助:Image.open 10 | def check_picture(path_picture): 11 | try: 12 | img = Image.open(path_picture) 13 | img.load() 14 | return True 15 | except (FileNotFoundError, OSError): 16 | # print('文件损坏') 17 | return False 18 | 19 | 20 | # 功能:调用百度AL人体分析,分析图片中的人体 21 | # 参数:图片路径,百度人体分析client 22 | # 返回:鼻子的x坐标 23 | # 辅助:os.system, cli.bodyAnalysis 24 | def image_cut(path, client): 25 | # if bool_face: # 启动人体分析的这两行代码在settings.py中 26 | # client = AipBodyAnalysis(al_id, ai_ak, al_sk) 27 | for retry in range(10): 28 | with open(path, 'rb') as fp: 29 | image = fp.read() 30 | try: 31 | result = client.bodyAnalysis(image) 32 | return int(result["person_info"][0]['body_parts']['nose']['x']) 33 | except: 34 | print(' >人体分析出现错误,请对照“人体分析错误表格”:', result) 35 | print(' >正在尝试重新人体检测...') 36 | continue 37 | print(' >人体分析无法使用...请先解决人体分析的问题,或截图联系作者...') 38 | system('pause') 39 | 40 | 41 | # 功能:裁剪有码的fanart封面作为poster,一般fanart是800*538,把右边的379*538裁剪下来 42 | # 参数:已下载的fanart路径,目标poster路径 43 | # 返回:无 44 | # 辅助:Image.open 45 | def crop_poster_youma(path_fanart, path_poster): 46 | img = Image.open(path_fanart) 47 | wf, hf = img.size # fanart的宽 高 48 | wide = int(hf / 1.42) # 理想中海报的宽(应该是379),应该是fanart的高/1.42,1.42来源于(538/379) 49 | # 如果fanart不是正常的800*576的横向图,而是非常“瘦”的图 50 | if wf < wide: 51 | poster = img.crop((0, 0, wf, int(wf * 1.42))) 52 | poster.save(path_poster, quality=95) # quality=95 是无损crop,如果不设置,默认75 53 | print(' >poster.jpg裁剪成功') 54 | else: 55 | x_left = wf - wide 56 | poster = img.crop((x_left, 0, wf, hf)) # poster在fanart的 左上角(x_left, 0),右下角(x_left + wide, hf) 57 | poster.save(path_poster, quality=95) # 坐标轴的Y轴是反的 58 | print(' >poster.jpg裁剪成功') 59 | 60 | 61 | # 功能:不使用人体分析,裁剪fanart封面作为poster,裁剪中间,或者裁剪右边 62 | # 参数:已下载的fanart路径,目标poster路径, 选择模式int_pattern(无码是裁剪fanart右边,FC2和素人是裁剪fanart中间) 63 | # 返回:无 64 | # 辅助:Image.open 65 | def crop_poster_default(path_fanart, path_poster, int_pattern): 66 | img = Image.open(path_fanart) 67 | wf, hf = img.size # fanart的宽 高 68 | wide = int(hf * 2 / 3) # 理想中海报的宽,应该是fanart的高的三分之二 69 | # 如果fanart特别“瘦”(宽不到高的三分之二),则以fanart现在的宽作为poster的宽,未来的高为宽的二分之三。 70 | if wf < wide: 71 | poster = img.crop((0, 0, wf, wf * 1.5)) 72 | poster.save(path_poster, quality=95) # quality=95 是无损crop,如果不设置,默认75 73 | print(' >poster.jpg裁剪成功') 74 | else: 75 | x_left = (wf - wide) / int_pattern # / 2,poster裁剪fanart中间;/ 1,poster裁剪fanart右边。 76 | # crop 77 | try: 78 | poster = img.crop((x_left, 0, x_left + wide, hf)) # poster在fanart的 左上角(x_left, 0),右下角(x_left + wide, hf) 79 | except: 80 | raise 81 | poster.save(path_poster, quality=95) 82 | print(' >poster.jpg裁剪成功') 83 | 84 | 85 | # 功能:使用人体分析,裁剪fanart封面作为poster,围绕鼻子坐标进行裁剪 86 | # 参数:已下载的fanart路径,目标poster路径, 百度人体分析client 87 | # 返回:无 88 | # 辅助:Image.open, image_cut() 89 | def crop_poster_baidu(path_fanart, path_poster, client): 90 | img = Image.open(path_fanart) 91 | wf, hf = img.size # fanart的宽 高 92 | wide = int(hf * 2 / 3) # 理想中海报的宽,应该是fanart的高的三分之二 93 | # 如果fanart特别“瘦”,宽不到高的三分之二。以fanart的宽作为poster的宽。 94 | if wf < wide: 95 | poster = img.crop((0, 0, wf, wf * 1.5)) 96 | poster.save(path_poster, quality=95) # quality=95 是无损crop,如果不设置,默认75 97 | print(' >poster.jpg裁剪成功') 98 | else: 99 | wide_half = wide / 2 100 | # 使用人体分析,得到鼻子x坐标 101 | x_nose = image_cut(path_fanart, client) # 鼻子的x坐标 102 | # 围绕鼻子进行裁剪,先来判断一下鼻子是不是太靠左或者太靠右 103 | if x_nose + wide_half > wf: # 鼻子 + 一半poster宽超出fanart右边 104 | x_left = wf - wide # 以右边为poster 105 | elif x_nose - wide_half < 0: # 鼻子 - 一半poster宽超出fanart左边 106 | x_left = 0 # 以左边为poster 107 | else: # 不会超出poster 108 | x_left = x_nose - wide_half # 以鼻子为中心向两边扩展 109 | # crop 110 | poster = img.crop((x_left, 0, x_left + wide, hf)) # poster在fanart的 左上角(x_left, 0),右下角(x_left + wide, hf), 111 | poster.save(path_poster, quality=95) # 坐标轴的Y轴是反的 112 | print(' >poster.jpg裁剪成功') 113 | 114 | 115 | # 功能:给poster的左上方加上“中文字幕”的红色条幅 116 | # 参数:poster路径 117 | # 返回:无 118 | # 辅助:Image.open 119 | def add_watermark_subtitle(path_poster): 120 | # 打开poster,“中文字幕”条幅的宽高是poster的宽的四分之一 121 | img_poster = Image.open(path_poster) 122 | scroll_wide = int(img_poster.height/4) 123 | # 打开“中文字幕”条幅,缩小到合适poster的尺寸 124 | watermark_subtitle = Image.open('StaticFiles/subtitle.png') 125 | watermark_subtitle = watermark_subtitle.resize((scroll_wide, scroll_wide), Image.ANTIALIAS) 126 | r, g, b, a = watermark_subtitle.split() # 获取颜色通道,保持png的透明性 127 | # 条幅在poster上摆放的位置。左上角(0,0) 128 | img_poster.paste(watermark_subtitle, (0, 0), mask=a) 129 | img_poster.save(path_poster, quality=95) 130 | print(' >poster加上中文字幕条幅') 131 | 132 | 133 | # 功能:给poster的右上方加上“无码流出”的红色条幅 134 | # 参数:poster路径 135 | # 返回:无 136 | # 辅助:Image.open 137 | def add_watermark_divulge(path_poster): 138 | # 打开poster,条幅的宽高是poster的宽的四分之一 139 | img_poster = Image.open(path_poster) 140 | w, h = img_poster.size 141 | scroll_wide = int(h/4) 142 | # 打开条幅,缩小到合适poster的尺寸 143 | watermark_divulge = Image.open('StaticFiles/divulge.png') 144 | watermark_divulge = watermark_divulge.resize((scroll_wide, scroll_wide), Image.ANTIALIAS) 145 | r, g, b, a = watermark_divulge.split() # 获取颜色通道,保持png的透明性 146 | # 条幅在poster上摆放的位置。左上角(x_left,0) 147 | x_left = w - scroll_wide 148 | img_poster.paste(watermark_divulge, (x_left, 0), mask=a) 149 | img_poster.save(path_poster, quality=95) 150 | print(' >poster加上无码流出红幅') -------------------------------------------------------------------------------- /javsdt/Functions/Process.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from os import sep 3 | from os.path import exists 4 | from xml.etree.ElementTree import parse, ParseError 5 | 6 | 7 | # 功能:完善用于命名的dict_data,如果用户自定义的各种命名公式中有dict_data未包含的元素,则添加进dict_data。 8 | # 将可能比较复杂的list_classify_basis按“+”“\”切割好,准备用于组装后面的归类路径。 9 | # 参数:用户自定义的各种命名公式list 10 | # 返回:存储命名信息的dict_data, 切割好的归类标准list_classify_basis 11 | # 辅助:os.sep 12 | def perfect_dict_data(list_extra_genres, list_rename_video, list_rename_folder, list_name_nfo_title, list_name_fanart, list_name_poster, custom_classify_basis, dict_data): 13 | for i in list_extra_genres: 14 | if i not in dict_data: 15 | dict_data[i] = i 16 | for i in list_rename_video: 17 | if i not in dict_data: 18 | dict_data[i] = i 19 | for i in list_rename_folder: 20 | if i not in dict_data: 21 | dict_data[i] = i 22 | for i in list_name_nfo_title: 23 | if i not in dict_data: 24 | dict_data[i] = i 25 | for i in list_name_fanart: 26 | if i not in dict_data: 27 | dict_data[i] = i 28 | for i in list_name_poster: 29 | if i not in dict_data: 30 | dict_data[i] = i 31 | list_classify_basis = [] 32 | for i in custom_classify_basis.split('\\'): 33 | for j in i.split('+'): 34 | if j not in dict_data: 35 | dict_data[j] = j 36 | list_classify_basis.append(j) 37 | list_classify_basis.append(sep) 38 | return dict_data, list_classify_basis 39 | 40 | 41 | # 功能:根据【原文件名】和《已存在的、之前整理的nfo》,判断当前jav是否有“中文字幕” 42 | # 参数:①当前jav所处文件夹路径root_jav ②jav文件名不带格式后缀name_no_extension, 43 | # ③如果【原文件名】包含list_subtitle_character中的元素即判断有“中文字幕”, 44 | # 返回:True 45 | # 辅助:os.path.exists,xml.etree.ElementTree.parse,xml.etree.ElementTree.ParseError 46 | def judge_exist_subtitle(root_jav, name_no_extension, list_subtitle_character): 47 | # 去除 '-CD' 和 '-CARIB'对 '-C'判断中字的影响 48 | name_no_extension = name_no_extension.upper().replace('-CD', '').replace('-CARIB', '') 49 | # 如果原文件名包含“-c、-C、中字”这些字符 50 | for i in list_subtitle_character: 51 | if i in name_no_extension: 52 | return True 53 | # 先前整理过的nfo中有 ‘中文字幕’这个Genre 54 | path_old_nfo = root_jav + sep + name_no_extension + '.nfo' 55 | if exists(path_old_nfo): 56 | try: 57 | tree = parse(path_old_nfo) 58 | except ParseError: # nfo可能损坏 59 | return False 60 | for child in tree.getroot(): 61 | if child.text == '中文字幕': 62 | return True 63 | return False 64 | 65 | 66 | # 功能:根据【原文件名】和《已存在的、之前整理的nfo》,判断当前jav是否有“无码流出” 67 | # 参数:①当前jav所处文件夹路径root_jav ②jav文件名不带格式后缀name_no_extension, 68 | # ③如果【原文件名】包含list_divulge_character中的元素即判断有“中文字幕”, 69 | # 返回:True 70 | # 辅助:os.path.exists,xml.etree.ElementTree.parse,xml.etree.ElementTree.ParseError 71 | def judge_exist_divulge(root_jav, name_no_extension, list_divulge_character): 72 | # 如果原文件名包含“-c、-C、中字”这些字符 73 | for i in list_divulge_character: 74 | if i in name_no_extension: 75 | return True 76 | # 先前整理过的nfo中有 ‘中文字幕’这个Genre 77 | path_old_nfo = root_jav + sep + name_no_extension + '.nfo' 78 | if exists(path_old_nfo): 79 | try: 80 | tree = parse(path_old_nfo) 81 | except ParseError: # nfo可能损坏 82 | return False 83 | for child in tree.getroot(): 84 | if child.text == '无码流出': 85 | return True 86 | return False -------------------------------------------------------------------------------- /javsdt/Functions/Record.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from time import strftime, localtime, time 3 | 4 | 5 | # 功能:记录整理的文件夹、整理的时间 6 | # 参数:错误信息 7 | # 返回:无 8 | # 辅助:os.strftime, os.localtime, os.time, 9 | def record_start(root_choose): 10 | msg = '已选择文件夹:' + root_choose + ' ' + strftime('%Y-%m-%d %H:%M:%S', localtime(time())) + '\n' 11 | txt = open('【可删除】失败记录.txt', 'a', encoding="utf-8") 12 | txt.write(msg) 13 | txt.close() 14 | txt = open('【可删除】警告信息.txt', 'a', encoding="utf-8") 15 | txt.write(msg) 16 | txt.close() 17 | txt = open('【可删除】新旧文件名清单.txt', 'a', encoding="utf-8") 18 | txt.write(msg) 19 | txt.close() 20 | 21 | 22 | # 功能:记录错误信息 23 | # 参数:错误信息 24 | # 返回:无 25 | # 辅助:无 26 | def record_fail(fail_msg): 27 | print(fail_msg, end='') 28 | txt = open('【可删除】失败记录.txt', 'a', encoding="utf-8") 29 | txt.write(fail_msg) 30 | txt.close() 31 | 32 | 33 | # 功能:记录警告信息 34 | # 参数:警告信息 35 | # 返回:无 36 | # 辅助:无 37 | def record_warn(warn_msg): 38 | txt = open('【可删除】警告信息.txt', 'a', encoding="utf-8") 39 | txt.write(warn_msg) 40 | txt.close() 41 | 42 | 43 | # 功能:记录旧文件名 44 | # 参数:新文件名,旧文件名 45 | # 返回:无 46 | # 辅助:无 47 | def record_video_old(name_new, name_old): 48 | txt = open('【可删除】新旧文件名清单.txt', 'a', encoding="utf-8") 49 | txt.write('<<<< ' + name_old + '\n') 50 | txt.write('>>>> ' + name_new + '\n') 51 | txt.close() -------------------------------------------------------------------------------- /javsdt/Functions/Requests/ArzonReq.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import requests, re 3 | from os import system 4 | # from traceback import format_exc 5 | 6 | 7 | # 功能:获取一个arzon_cookie 8 | # 参数:代理proxy 9 | # 返回:cookies 10 | def steal_arzon_cookies(proxy): 11 | print('\n正在尝试通过 https://www.arzon.jp 的成人验证...') 12 | for retry in range(10): 13 | try: # 当初费尽心机,想办法如何通过页面上的成人验证,结果在一个C#开发的jav爬虫项目,看到它请求以下网址,再跳转到arzon主页,所得到的的cookie即是合法的cookie 14 | if proxy: 15 | session = requests.Session() 16 | session.get('https://www.arzon.jp/index.php?action=adult_customer_agecheck&agecheck=1&redirect=https%3A%2F%2Fwww.arzon.jp%2F', proxies=proxy, timeout=(12, 7)) 17 | print('通过arzon的成人验证!\n') 18 | return session.cookies.get_dict() 19 | else: 20 | session = requests.Session() 21 | session.get('https://www.arzon.jp/index.php?action=adult_customer_agecheck&agecheck=1&redirect=https%3A%2F%2Fwww.arzon.jp%2F', timeout=(12, 7)) 22 | print('通过arzon的成人验证!\n') 23 | return session.cookies.get_dict() 24 | except requests.exceptions.ProxyError: 25 | # print(format_exc()) 26 | print(' >通过局部代理失败,重新尝试...') 27 | continue 28 | except: 29 | # print(format_exc()) 30 | print('通过失败,重新尝试...') 31 | continue 32 | print('>>请检查你的网络环境是否可以打开:https://www.arzon.jp/') 33 | system('pause') 34 | 35 | 36 | # 功能:搜索arzon,或请求arzon上jav所在网页 37 | # 参数:网址url,请求头部header/cookies,代理proxy 38 | # 返回:网页html 39 | def get_arzon_html(url, cookies, proxy): 40 | # print('代理:', proxy) 41 | for retry in range(10): 42 | try: 43 | if proxy: 44 | rqs = requests.get(url, cookies=cookies, proxies=proxy, timeout=(12, 7)) 45 | else: 46 | rqs = requests.get(url, cookies=cookies, timeout=(12, 7)) 47 | except requests.exceptions.ProxyError: 48 | # print(format_exc()) 49 | print(' >通过局部代理失败,重新尝试...') 50 | continue 51 | except: 52 | print(' >打开网页失败,重新尝试...') 53 | continue 54 | rqs.encoding = 'utf-8' 55 | rqs_content = rqs.text 56 | if re.search(r'arzon', rqs_content): 57 | return rqs_content 58 | else: 59 | print(' >打开网页失败,空返回...重新尝试...') 60 | continue 61 | print('>>请检查你的网络环境是否可以打开:', url) 62 | system('pause') 63 | 64 | 65 | # 功能:从arzon上查找简介 66 | # 参数:车牌car,cookies,proxy 67 | # 返回:简介,执行完成状态码,cookies 68 | def find_plot_arzon(car, cookies, proxy): 69 | for retry in range(2): 70 | url_search_arzon = 'https://www.arzon.jp/itemlist.html?t=&m=all&s=&q=' + car.replace('-', '') 71 | print(' >查找简介:', url_search_arzon) 72 | # 得到arzon的搜索结果页面 73 | html_search_arzon = get_arzon_html(url_search_arzon, cookies, proxy) 74 | #
获取简介:', url_on_arzon) 81 | # 打开arzon上每一个搜索结果的页面 82 | html_arzon = get_arzon_html(url_on_arzon, cookies, proxy) 83 | # 在该url_on_arzon网页上查找简介 84 | plotg = re.search(r'h2>作品紹介([\s\S]*?)', html_arzon) 85 | # 成功找到plot 86 | if str(plotg) != 'None': 87 | plot_br = plotg.group(1) 88 | plot = '' 89 | for line in plot_br.split('
'): 90 | line = line.strip() 91 | plot += line 92 | return plot, 0, cookies 93 | # 几个搜索结果查找完了,也没有找到简介 94 | return '【arzon有该影片,但找不到简介】', 1, cookies 95 | # 没有搜索结果 96 | else: 97 | # arzon返回的页面实际是18岁验证 98 | adultg = re.search(r'18歳未満', html_search_arzon) 99 | if str(adultg) != 'None': 100 | cookies = steal_arzon_cookies(proxy) 101 | continue 102 | # 不是成人验证,也没有简介 103 | else: 104 | return '【影片下架,暂无简介】', 2, cookies 105 | print('>>请检查你的网络环境是否可以通过成人验证:https://www.arzon.jp/') 106 | system('pause') 107 | return '', 3, cookies 108 | 109 | -------------------------------------------------------------------------------- /javsdt/Functions/Requests/Download.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import requests 3 | from PIL import Image 4 | 5 | 6 | # 下载图片,无返回 7 | # 参数:图片地址,存放路径,代理 8 | def download_pic(url, path, proxy): 9 | for retry in range(5): 10 | try: 11 | if proxy: 12 | r = requests.get(url, proxies=proxy, stream=True, timeout=(6, 10)) 13 | with open(path, 'wb') as pic: 14 | for chunk in r: 15 | pic.write(chunk) 16 | else: 17 | r = requests.get(url, stream=True, timeout=(6, 10)) 18 | with open(path, 'wb') as pic: 19 | for chunk in r: 20 | pic.write(chunk) 21 | except requests.exceptions.ProxyError: 22 | # print(format_exc()) 23 | print(' >通过局部代理失败,重新尝试...') 24 | continue 25 | except: 26 | # print(format_exc()) 27 | print(' >下载失败,重新下载...') 28 | continue 29 | # 如果下载的图片打不开,则重新下载 30 | try: 31 | img = Image.open(path) 32 | img.load() 33 | return 34 | except OSError: 35 | print(' >下载失败,重新下载....') 36 | continue 37 | raise Exception(' >下载多次,仍然失败!') -------------------------------------------------------------------------------- /javsdt/Functions/Requests/Jav321Req.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import re, requests 3 | from os import system 4 | # from traceback import format_exc 5 | 6 | 7 | # 用户指定jav321的网址后,请求jav所在网页,返回html 8 | def get_321_html(url, proxy): 9 | for retry in range(10): 10 | try: 11 | if proxy: 12 | rqs = requests.get(url, proxies=proxy, timeout=(6, 7)) 13 | else: 14 | rqs = requests.get(url, timeout=(6, 7)) 15 | except requests.exceptions.ProxyError: 16 | # print(format_exc()) 17 | print(' >通过局部代理失败...') 18 | continue 19 | except: 20 | print(' >打开网页失败,重新尝试...') 21 | continue 22 | rqs.encoding = 'utf-8' 23 | rqs_content = rqs.text 24 | if re.search(r'JAV321', rqs_content): 25 | return rqs_content 26 | else: 27 | print(' >打开网页失败,空返回...重新尝试...') 28 | continue 29 | print('>>请检查你的网络环境是否可以打开:', url) 30 | system('pause') 31 | 32 | 33 | # 向jav321 post车牌,得到jav所在网页,也可能是无结果的网页,返回html 34 | def post_321_html(url, data, proxy): 35 | for retry in range(10): 36 | try: 37 | if proxy: 38 | rqs = requests.post(url, data=data, proxies=proxy, timeout=(6, 7)) 39 | else: 40 | rqs = requests.post(url, data=data, timeout=(6, 7)) 41 | except requests.exceptions.ProxyError: 42 | # print(format_exc()) 43 | print(' >通过局部代理失败,重新尝试...') 44 | continue 45 | except: 46 | # print(format_exc()) 47 | print(' >打开网页失败,重新尝试...') 48 | continue 49 | rqs.encoding = 'utf-8' 50 | rqs_content = rqs.text 51 | if re.search(r'JAV321', rqs_content): 52 | return rqs_content 53 | else: 54 | print(' >打开网页失败,空返回...重新尝试...') 55 | continue 56 | print('>>请检查你的网络环境是否可以打开:', url) 57 | system('pause') 58 | 59 | -------------------------------------------------------------------------------- /javsdt/Functions/Requests/JavbusReq.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import re, os, requests 3 | # from traceback import format_exc 4 | 5 | # 功能:请求各大jav网站和arzon的网页 6 | # 参数:网址url,请求头部header/cookies,代理proxy 7 | # 返回:网页html,请求头部 8 | 9 | 10 | # 搜索javbus,或请求javbus上jav所在网页,返回html 11 | def get_bus_html(url, proxy): 12 | for retry in range(10): 13 | try: 14 | if proxy: # existmag=all为了 获得所有影片,而不是默认的有磁力的链接 15 | rqs = requests.get(url, proxies=proxy, timeout=(6, 7), headers={'Cookie': 'existmag=all'}) 16 | else: 17 | rqs = requests.get(url, timeout=(6, 7), headers={'Cookie': 'existmag=all'}) 18 | except requests.exceptions.ProxyError: 19 | # print(format_exc()) 20 | print(' >通过局部代理失败,重新尝试...') 21 | continue 22 | except: 23 | # print(format_exc()) 24 | print(' >打开网页失败,重新尝试...') 25 | continue 26 | rqs.encoding = 'utf-8' 27 | rqs_content = rqs.text 28 | if re.search(r'JavBus', rqs_content): 29 | return rqs_content 30 | else: 31 | print(' >打开网页失败,空返回...重新尝试...') 32 | continue 33 | print('>>请检查你的网络环境是否可以打开:', url) 34 | os.system('pause') 35 | 36 | 37 | # 去javbus搜寻系列、在javbus的封面链接 38 | # 返回:系列名称,图片链接,状态码 39 | def find_series_cover_bus(car, url_bus, proxy): 40 | # 需要这两个东西 41 | series = url_cover_bus = '' 42 | # 状态码,标记是否在javbus上搜索到多个结果(如果有多个结果,javlibrary整理时还是从javlibrary上下载封面) 43 | status = 0 # 0表示javbus是唯一的搜索结果 44 | # jav在javbus上的url,一般就是javbus网址+车牌 45 | url_on_bus = url_bus + car 46 | print(' >获取系列:', url_on_bus) 47 | # 获得影片在javbus上的网页 48 | html_bus = get_bus_html(url_on_bus, proxy) 49 | if not re.search(r'404 Page', html_bus): 50 | # DVD封面cover 51 | coverg = re.search(r'bigImage" href="(.+?)">', html_bus) 52 | if str(coverg) != 'None': 53 | url_cover_bus = coverg.group(1) 54 | # 系列:
悪質シロウトナンパ 55 | seriesg = re.search(r'系列: (.+?)', html_bus) 56 | if str(seriesg) != 'None': 57 | series = seriesg.group(1) 58 | # 这部jav在javbus的网址不简单 59 | else: 60 | # 还是老老实实去搜索 61 | url_search_bus = url_bus + 'search/' + car.replace('-', '') + '&type=1&parent=ce' 62 | print(' >搜索javbus:', url_search_bus) 63 | html_bus = get_bus_html(url_search_bus, proxy) 64 | # 搜索结果的网页,大部分情况一个结果,也有可能是多个结果的网页 65 | # 尝试找movie-box 66 | list_search_results = re.findall(r'movie-box" href="(.+?)">', html_bus) # 匹配处理“标题” 67 | if list_search_results: 68 | jav_pref = car.split('-')[0] # 匹配车牌的前缀字母 69 | jav_suf = car.split('-')[-1].lstrip('0') # 当前车牌的后缀数字 去除多余的0 70 | list_fit_results = [] # 存放,车牌符合的结果 71 | for i in list_search_results: 72 | url_end = i.split('/')[-1].upper() 73 | url_suf = re.search(r'[-_](\d+)', url_end).group(1).lstrip('0') # 匹配box上影片url,车牌的后缀数字,去除多余的0 74 | if jav_suf == url_suf: # 数字相同 75 | url_pref = re.search(r'([A-Z]+2?8?)', url_end).group(1).upper() # 匹配处理url所带车牌前面的字母“n” 76 | if jav_pref == url_pref: # 数字相同的基础下,字母也相同,即可能车牌相同 77 | list_fit_results.append(i) 78 | # 有结果 79 | if list_fit_results: 80 | # 有多个结果,发个状态码,警告一下用户 81 | if len(list_fit_results) > 1: 82 | status = 1 83 | # 默认用第一个搜索结果 84 | url_first_result = list_fit_results[0] 85 | print(' >获取系列:', url_first_result) 86 | html_bus = get_bus_html(url_first_result, proxy) 87 | # DVD封面cover 88 | coverg = re.search(r'bigImage" href="(.+?)">', html_bus) 89 | if str(coverg) != 'None': 90 | url_cover_bus = coverg.group(1) 91 | # 系列: 悪質シロウトナンパ 92 | seriesg = re.search(r'系列: (.+?)', html_bus) 93 | if str(seriesg) != 'None': 94 | series = seriesg.group(1) 95 | return series, url_cover_bus, status 96 | 97 | -------------------------------------------------------------------------------- /javsdt/Functions/Requests/JavdbReq.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import re, os, requests, time 3 | # from traceback import format_exc 4 | 5 | # 功能:请求各大jav网站和arzon的网页 6 | # 参数:网址url,请求头部header/cookies,代理proxy 7 | # 返回:网页html,请求头部 8 | 9 | 10 | #################################################### javdb ######################################################## 11 | # 搜索javdb,得到搜索结果网页,返回html。 12 | def get_search_db_html(url, cookies, proxy): 13 | for retry in range(1, 11): 14 | if retry % 4 == 0: 15 | print(' >睡眠5分钟...') 16 | time.sleep(300) 17 | try: 18 | if proxy: 19 | rqs = requests.get(url, cookies=cookies, proxies=proxy, timeout=(6, 7)) 20 | else: 21 | rqs = requests.get(url, cookies=cookies, timeout=(6, 7)) 22 | except requests.exceptions.ProxyError: 23 | # print(format_exc()) 24 | print(' >通过局部代理失败,重新尝试...') 25 | continue 26 | except: 27 | # print(format_exc()) 28 | print(' >打开网页失败,重新尝试...') 29 | continue 30 | rqs.encoding = 'utf-8' 31 | rqs_content = rqs.text 32 | if re.search(r'JavDB', rqs_content): 33 | if re.search(r'搜索結果', rqs_content): 34 | return rqs_content, cookies 35 | elif re.search(r'登入 | JavDB', rqs_content): 36 | cfduid = input('请输入cfduid:') 37 | jdb_session = input('请输入jdb_session:') 38 | cookies = { 39 | "__cfduid": cfduid, 40 | "_jdb_session": jdb_session, 41 | } 42 | else: 43 | print(' >睡眠5分钟...') 44 | time.sleep(300) 45 | continue 46 | else: 47 | print(' >打开网页失败,空返回...重新尝试...') 48 | continue 49 | print('>>请检查你的网络环境是否可以打开:', url) 50 | os.system('pause') 51 | 52 | 53 | # 请求jav在javdb上的网页,返回html 54 | def get_db_html(url, cookies, proxy): 55 | for retry in range(1, 11): 56 | if retry % 4 == 0: 57 | print(' >睡眠5分钟...') 58 | time.sleep(300) 59 | try: 60 | if proxy: 61 | rqs = requests.get(url, cookies=cookies, proxies=proxy, timeout=(6, 7)) 62 | else: 63 | rqs = requests.get(url, cookies=cookies, timeout=(6, 7)) 64 | except requests.exceptions.ProxyError: 65 | # print(format_exc()) 66 | print(' >通过局部代理失败,重新尝试...') 67 | continue 68 | except: 69 | # print(format_exc()) 70 | print(' >打开网页失败,重新尝试...') 71 | continue 72 | rqs.encoding = 'utf-8' 73 | rqs_content = rqs.text 74 | # print(rqs_content) 75 | if re.search(r'JavDB', rqs_content): 76 | if re.search(r'link rel="canonical"', rqs_content): 77 | return rqs_content, cookies 78 | elif re.search(r'登入 | JavDB', rqs_content): 79 | cfduid = input('请粘贴cfduid:') 80 | jdb_session = input('请粘贴jdb_session:') 81 | cookies = { 82 | "__cfduid": cfduid, 83 | "_jdb_session": jdb_session, 84 | } 85 | else: 86 | print(' >睡眠5分钟...') 87 | time.sleep(300) 88 | continue 89 | else: 90 | print(' >打开网页失败,空返回...重新尝试...') 91 | continue 92 | print('>>请检查你的网络环境是否可以打开:', url) 93 | os.system('pause') 94 | 95 | -------------------------------------------------------------------------------- /javsdt/Functions/Requests/JavlibraryReq.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import re, os, requests 3 | # from traceback import format_exc 4 | import cfscrape 5 | # 功能:请求各大jav网站和arzon的网页 6 | # 参数:网址url,请求头部header/cookies,代理proxy 7 | # 返回:网页html,请求头部 8 | 9 | 10 | #################################################### javlibrary ######################################################## 11 | # 搜索javlibrary,或请求javlibrary上jav所在网页,返回html 12 | def get_library_html(url, proxy): 13 | for retry in range(10): 14 | try: 15 | if proxy: 16 | rqs = requests.get(url, proxies=proxy, timeout=(6, 7)) 17 | else: 18 | rqs = requests.get(url, timeout=(6, 7)) 19 | except requests.exceptions.ProxyError: 20 | # print(format_exc()) 21 | print(' >通过局部代理失败,重新尝试...') 22 | continue 23 | except: 24 | # print(format_exc()) 25 | print(' >打开网页失败,重新尝试...') 26 | continue 27 | rqs.encoding = 'utf-8' 28 | rqs_content = rqs.text 29 | # print(rqs_content) 30 | if re.search(r'JAVLibrary', rqs_content): # 得到想要的网页,直接返回 31 | return rqs_content 32 | else: # 代理工具返回的错误信息 33 | print(' >打开网页失败,空返回...尝试cfscrape...') 34 | #print(url) 35 | scraper = cfscrape.create_scraper() 36 | web_data = scraper.get(url).text 37 | #print(web_data) 38 | if re.search(r'JAVLibrary', web_data): 39 | return web_data 40 | else: 41 | continue 42 | print('>>请检查你的网络环境是否可以打开:', url) 43 | os.system('pause') 44 | 45 | -------------------------------------------------------------------------------- /javsdt/Functions/Standard.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from os import sep, makedirs, rename, rmdir, listdir 3 | from os.path import exists 4 | from configparser import RawConfigParser 5 | from shutil import copyfile 6 | 7 | from Functions.Record import record_video_old, record_fail 8 | 9 | 10 | # 功能:1重命名视频 11 | # 参数:设置settings,重命名视频的命名公式list_name_video,命名信息dict_data,第几集str_cd,处理的影片jav,已失败次数num_fail,视频的相对路径path_relative 12 | # 返回:命名信息dict_data(dict_data['视频']可能改变)、处理的影片jav(文件名改变)、已发生的失败次数num_fail 13 | # 辅助:os.exists, os.rename, record_video_old, record_fail 14 | def rename_mp4(jav, num_fail, settings, dict_data, list_name_video, path_relative, str_cd): 15 | if settings.bool_rename_video: 16 | # 构造新文件名,不带文件类型后缀 17 | name_without_ext = '' 18 | for j in list_name_video: 19 | name_without_ext += dict_data[j] 20 | name_without_ext = name_without_ext.rstrip() + str_cd # 去除末尾空格,否则windows会自动删除空格,导致程序仍以为带空格 21 | path_new = jav.root + sep + name_without_ext + jav.type # 【临时变量】path_new 视频文件的新路径 22 | # 一般情况,不存在同名视频文件 23 | if not exists(path_new): 24 | rename(jav.path, path_new) 25 | record_video_old(jav.path, path_new) 26 | # 已存在目标文件,但就是现在的文件 27 | elif jav.path.upper() == path_new.upper(): 28 | try: 29 | rename(jav.path, path_new) 30 | # windows本地磁盘,“abc-123.mp4”重命名为“abc-123.mp4”或“ABC-123.mp4”没问题,但有用户反映,挂载的磁盘会报错“file exists error” 31 | except: 32 | num_fail += 1 33 | record_fail(' >第' + str(num_fail) + '个失败!请自行重命名大小写:' + path_relative + '\n') 34 | # 存在目标文件,不是现在的文件。 35 | else: 36 | num_fail += 1 37 | record_fail( 38 | ' >第' + str(num_fail) + '个失败!重命名影片失败,重复的影片,已经有相同文件名的视频了:' + path_new + '\n') 39 | raise FileExistsError # 【终止对该jav的整理】 40 | dict_data['视频'] = name_without_ext # 【更新】 dict_data['视频'] 41 | jav.name = name_without_ext + jav.type # 【更新】jav.name,重命名操作可能不成功,但之后的操作仍然围绕成功的jav.name来命名 42 | print(' >修改文件名' + str_cd + '完成') 43 | # 重命名字幕 44 | if jav.subtitle and settings.bool_rename_subtitle: 45 | subtitle_new = name_without_ext + jav.subtitle_type # 【临时变量】subtitle_new 46 | path_subtitle_new = jav.root + sep + subtitle_new # 【临时变量】path_subtitle_new 47 | if jav.path_subtitle != path_subtitle_new: 48 | rename(jav.path_subtitle, path_subtitle_new) 49 | jav.subtitle = subtitle_new # 【更新】 jav.subtitle 字幕完整文件名 50 | print(' >修改字幕名完成') 51 | return dict_data, jav, num_fail 52 | 53 | 54 | # 功能:2归类影片,只针对视频文件和字幕文件,无视它们当前所在文件夹 55 | # 参数:设置settings,归类的目标目录路径root_classify,归类标准组合公式list_classify_basis,命名信息dict_data,处理的影片jav,已失败次数num_fail 56 | # 返回:处理的影片jav(所在文件夹路径改变)、已失败次数num_fail 57 | # 辅助:os.exists, os.rename, os.makedirs, 58 | def classify_files(jav, num_fail, settings, dict_data, list_classify_basis, root_classify): 59 | # 如果需要归类,且不是针对文件夹来归类 60 | if settings.bool_classify and not settings.bool_classify_folder: 61 | # 移动的目标文件夹路径 62 | root_dest = root_classify + sep 63 | for j in list_classify_basis: 64 | root_dest += dict_data[j].rstrip() # 【临时变量】归类的目标文件夹路径 C:\Users\JuneRain\Desktop\测试文件夹\葵司\ 65 | # 还不存在该文件夹,新建 66 | if not exists(root_dest): 67 | makedirs(root_dest) 68 | path_new = root_dest + sep + jav.name # 【临时变量】新的影片路径 69 | # 目标文件夹没有相同的影片,防止用户已经有一个“avop-127.mp4”,现在又来一个 70 | if not exists(path_new): 71 | rename(jav.path, path_new) 72 | print(' >归类视频文件完成') 73 | # 移动字幕 74 | if jav.subtitle: 75 | path_subtitle_new = root_dest + sep + jav.subtitle # 【临时变量】新的字幕路径 76 | if jav.path_subtitle != path_subtitle_new: 77 | rename(jav.path_subtitle, path_subtitle_new) 78 | # 不再更新 jav.path_subtitle,下面不会再操作 字幕文件 79 | print(' >归类字幕文件完成') 80 | jav.root = root_dest # 【更新】jav.root 81 | else: 82 | num_fail += 1 83 | record_fail(' >第' + str(num_fail) + '个失败!归类失败,重复的影片,归类的目标文件夹已经存在相同的影片:' + path_new + '\n') 84 | raise FileExistsError # 【终止对该jav的整理】 85 | return jav, num_fail 86 | 87 | 88 | # 功能:3重命名文件夹【相同】如果已进行第2操作,第3操作不会进行,因为用户只需要归类视频文件,不需要管文件夹。 89 | # 参数:设置settings,重命名文件夹的公式list_name_folder,命名信息dict_data,是否是独立文件夹bool_separate_folder, 90 | # 处理的影片jav,该车牌总共多少cd num_all_episodes,已失败次数num_fail 91 | # 返回:处理的影片jav(所在文件夹路径改变)、已失败次数num_fail 92 | # 辅助:os.exists, os.rename, os.makedirs,record_fail 93 | def rename_folder(jav, num_fail, settings, dict_data, list_name_folder, bool_separate_folder, num_all_episodes): 94 | if settings.bool_rename_folder: 95 | # 构造 新文件夹名folder_new 96 | folder_new = '' 97 | for j in list_name_folder: 98 | folder_new += (dict_data[j]) 99 | folder_new = folder_new.rstrip(' .') # 【临时变量】新的所在文件夹。去除末尾空格和“.” 100 | # 是独立文件夹,才会重命名文件夹 101 | if bool_separate_folder: 102 | # 当前视频是该车牌的最后一集,他的兄弟姐妹已经处理完成,才会重命名它们的“家”。 103 | if jav.episode == num_all_episodes: 104 | list_root_now = jav.root.split(sep) 105 | del list_root_now[-1] 106 | root_new = sep.join(list_root_now) + sep + folder_new # 【临时变量】新的影片所在文件夹路径。 107 | # 想要重命名的目标影片文件夹不存在 108 | if not exists(root_new): 109 | rename(jav.root, root_new) 110 | jav.root = root_new # 【更新】jav.root 111 | # 目标影片文件夹存在,但就是现在的文件夹,即新旧相同 112 | elif jav.root == root_new: 113 | pass 114 | # 真的有一个同名的文件夹了 115 | else: 116 | num_fail += 1 117 | record_fail(' >第' + str(num_fail) + '个失败!重命名文件夹失败,已存在相同文件夹:' + root_new + '\n') 118 | raise FileExistsError # 【终止对该jav的整理】 119 | print(' >重命名文件夹完成') 120 | # 不是独立的文件夹,建立独立的文件夹 121 | else: 122 | path_separate_folder = jav.root + sep + folder_new # 【临时变量】需要创建的的影片所在文件夹。 123 | # 确认没有同名文件夹 124 | if not exists(path_separate_folder): 125 | makedirs(path_separate_folder) 126 | path_new = path_separate_folder + sep + jav.name # 【临时变量】新的影片路径 127 | # 如果这个文件夹是现成的,在它内部确认有没有“abc-123.mp4”。 128 | if not exists(path_new): 129 | rename(jav.path, path_new) 130 | # 移动字幕 131 | if jav.subtitle: 132 | path_subtitle_new = path_separate_folder + sep + jav.subtitle # 【临时变量】新的字幕路径 133 | rename(jav.path_subtitle, path_subtitle_new) 134 | # 下面不会操作 字幕文件 了,jav.path_subtitle不再更新 135 | print(' >移动字幕到独立文件夹') 136 | jav.root = path_separate_folder # 【更新】jav.root 137 | # 里面已有“avop-127.mp4”,这不是它的家。 138 | else: 139 | num_fail += 1 140 | record_fail(' >第' + str(num_fail) + '个失败!创建独立文件夹失败,已存在相同的视频文件:' + path_new + '\n') 141 | raise FileExistsError # 【终止对该jav的整理】 142 | return jav, num_fail 143 | 144 | 145 | # 功能:6为当前jav收集演员头像到“.actors”文件夹中 146 | # 参数:演员们 list_actors,jav当前所处文件夹的路径root_now 147 | # 返回:无 148 | # 辅助:os.path.exists,os.makedirs, configparser.RawConfigParser, shutil.copyfile 149 | def collect_sculpture(list_actors, root_now): 150 | for each_actor in list_actors: 151 | path_exist_actor = '演员头像' + sep + each_actor[0] + sep + each_actor # 事先准备好的演员头像路径 152 | if exists(path_exist_actor + '.jpg'): 153 | pic_type = '.jpg' 154 | elif exists(path_exist_actor + '.png'): 155 | pic_type = '.png' 156 | else: 157 | config_actor = RawConfigParser() 158 | config_actor.read('【缺失的演员头像统计For Kodi】.ini', encoding='utf-8-sig') 159 | try: 160 | each_actor_times = config_actor.get('缺失的演员头像', each_actor) 161 | config_actor.set("缺失的演员头像", each_actor, str(int(each_actor_times) + 1)) 162 | except: 163 | config_actor.set("缺失的演员头像", each_actor, '1') 164 | config_actor.write(open('【缺失的演员头像统计For Kodi】.ini', "w", encoding='utf-8-sig')) 165 | continue 166 | # 已经收录了这个演员头像 167 | root_dest_actor = root_now + sep + '.actors' + sep # 头像的目标文件夹 168 | if not exists(root_dest_actor): 169 | makedirs(root_dest_actor) 170 | # 复制一份到“.actors” 171 | copyfile(path_exist_actor + pic_type, root_dest_actor + each_actor + pic_type) 172 | print(' >演员头像收集完成:', each_actor) 173 | 174 | 175 | # 功能:7归类影片,针对文件夹(如果已进行第2操作,第7操作不会进行,因为用户只需要归类视频文件,不需要管文件夹) 176 | # 参数:设置settings,处理的影片jav,该车牌总共多少cd num_all_episodes,是否是独立文件夹bool_separate_folder, 177 | # 归类的目标目录路径root_classify,当前处理的文件夹路径root,命名信息dict_data 178 | # 返回:处理的影片jav(所在文件夹路径改变)、已失败次数num_fail 179 | # 辅助:os.exists, os.rename, os.makedirs, 180 | def classify_folder(jav, num_fail, settings, dict_data, list_classify_basis, root_classify, root, bool_separate_folder, num_all_episodes): 181 | if settings.bool_classify and settings.bool_classify_folder and jav.episode == num_all_episodes: # 需要移动文件夹,且,是该影片的最后一集 182 | # 用户选择的文件夹是一部影片的独立文件夹,为了避免在这个文件夹里又生成新的归类文件夹 183 | if bool_separate_folder and root_classify.startswith(root): 184 | print(' >无法归类,请选择该文件夹的上级文件夹作它的归类根目录') 185 | return num_fail 186 | # 归类放置的目标文件夹 187 | root_dest = root_classify + sep 188 | # 移动的目标文件夹 189 | for j in list_classify_basis: 190 | root_dest += dict_data[j].rstrip(' .') # 【临时变量】 文件夹移动的目标上级文件夹 C:\Users\JuneRain\Desktop\测试文件夹\1\葵司\ 191 | root_new = root_dest + sep + jav.folder # 【临时变量】 文件夹移动的目标路径 C:\Users\JuneRain\Desktop\测试文件夹\1\葵司\【葵司】AVOP-127\ 192 | # print(root_new) 193 | # 还不存在归类的目标文件夹 194 | if not exists(root_new): 195 | makedirs(root_new) 196 | # 把现在文件夹里的东西都搬过去 197 | jav_files = listdir(jav.root) 198 | for i in jav_files: 199 | rename(jav.root + sep + i, root_new + sep + i) 200 | # 删除“旧房子”,这是javsdt唯一的删除操作,而且os.rmdir只能删除空文件夹 201 | rmdir(jav.root) 202 | print(' >归类文件夹完成') 203 | # 用户已经有了这个文件夹,可能以前处理过同车牌的视频 204 | else: 205 | num_fail += 1 206 | record_fail(' >第' + str(num_fail) + '个失败!归类失败,归类的目标位置已存在相同文件夹:' + root_new + '\n') 207 | raise FileExistsError 208 | return num_fail 209 | -------------------------------------------------------------------------------- /javsdt/Functions/Status.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | from os import sep, system, walk 3 | from os.path import exists 4 | from shutil import copyfile 5 | 6 | 7 | # 功能:如果需要为kodi整理头像,则先检查“演员头像for kodi.ini”、“演员头像”文件夹是否存在 8 | # 参数:是否需要整理头像 9 | # 返回:无 10 | # 辅助:os.path.exists,os.system,shutil.copyfile 11 | def check_actors(bool_sculpture): 12 | if bool_sculpture: 13 | if not exists('演员头像'): 14 | print('\n“演员头像”文件夹丢失!请把它放进exe的文件夹中!\n') 15 | system('pause') 16 | if not exists('【缺失的演员头像统计For Kodi】.ini'): 17 | if exists('actors_for_kodi.ini'): 18 | copyfile('actors_for_kodi.ini', '【缺失的演员头像统计For Kodi】.ini') 19 | print('\n“【缺失的演员头像统计For Kodi】.ini”成功!') 20 | else: 21 | print('\n请打开“【ini】重新创建ini.exe”创建丢失的程序组件!') 22 | system('pause') 23 | 24 | 25 | # 功能:检查 归类根目录 是否存在,是不是和视频在同一个磁盘…… 26 | # 参数:用户自定义的归类根目录,用户选择整理的文件夹路径 27 | # 返回:归类根目录路径 28 | # 辅助:os.sep,os.system 29 | def check_classify_root(root_custom, root_choose): 30 | # 用户使用默认的“所选文件夹” 31 | if root_custom == '所选文件夹': 32 | return root_choose + sep + '归类完成' 33 | # 归类根目录 是 用户输入的路径c:\a,继续核实合法性 34 | else: 35 | root_custom = root_custom.rstrip(sep) 36 | # 用户输入的路径 不是 所选文件夹root_choose 37 | if root_custom != root_choose: 38 | if root_custom[:2] != root_choose[:2]: 39 | print('归类的根目录“', root_custom, '”和所选文件夹不在同一磁盘无法归类!请修正!') 40 | system('pause') 41 | if not exists(root_custom): 42 | print('归类的根目录“', root_custom, '”不存在!无法归类!请修正!') 43 | system('pause') 44 | return root_custom 45 | # 用户输入的路径 就是 所选文件夹root_choose 46 | else: 47 | return root_choose + sep + '归类完成' 48 | 49 | 50 | # 功能:所选文件夹总共有多少个视频文件 51 | # 参数:用户选择整理的文件夹路径root_choose,视频类型后缀集合tuple_video_type 52 | # 返回:无 53 | # 辅助:os.walk 54 | def count_num_videos(root_choose, tuple_video_type): 55 | num_videos = 0 56 | for root, dirs, files in walk(root_choose): 57 | for file_raw in files: 58 | file_temp = file_raw.upper() 59 | if file_temp.endswith(tuple_video_type) and not file_temp.startswith('.'): 60 | num_videos += 1 61 | return num_videos 62 | 63 | 64 | # 功能:判断当前一级文件夹是否含有nfo文件 65 | # 参数:这层文件夹下的文件们 66 | # 返回:True 67 | # 辅助:无 68 | def judge_exist_nfo(list_files): 69 | for file in list_files[::-1]: 70 | if file.endswith('.nfo'): 71 | return True 72 | return False 73 | 74 | 75 | # 功能:判断是否有除了“.actors”"extrafanrt”外的其他文件夹(如果有的话,说明当前文件夹不是jav的独立文件夹) 76 | # 参数:文件夹list 77 | # 返回:True 78 | # 辅助:无 79 | def judge_exist_extra_folders(list_folders): 80 | for folder in list_folders: 81 | if folder != '.actors' and folder != 'extrafanart': 82 | return True 83 | return False 84 | -------------------------------------------------------------------------------- /javsdt/Functions/User.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import sys 3 | from os import sep, system 4 | from os.path import exists, isdir 5 | from time import sleep 6 | from tkinter import filedialog, Tk, TclError 7 | 8 | 9 | # 功能:获取用户选取的文件夹路径 10 | # 参数:无 11 | # 返回:路径str 12 | # 辅助:tkinter.Tk,tkinter.filedialog,os.sep,sys 13 | def choose_directory(): 14 | # 用户:选择需要整理的文件夹 15 | print('请选择要整理的文件夹:', end='') 16 | for i in range(2): 17 | try: 18 | directory_root = Tk() 19 | directory_root.withdraw() 20 | path_work = filedialog.askdirectory() 21 | if path_work == '': 22 | print('你没有选择目录! 请重新选:') 23 | sleep(2) 24 | continue 25 | else: 26 | # askdirectory 获得是 正斜杠 路径C:/,所以下面要把 / 换成 反斜杠\ 27 | return path_work.replace('/', sep) 28 | except TclError: # 来自@BlueSkyBot 29 | try: 30 | path_work = input("请输入你需要整理的文件夹路径: ") 31 | except KeyboardInterrupt: 32 | sys.exit('输入终止,马上退出!') 33 | if not exists(path_work) or not isdir(path_work): 34 | print('\"{0}\" 不存在当前目录或者输入错误,请重新输入!'.format(path_work)) 35 | sleep(2) 36 | continue 37 | else: 38 | return path_work 39 | print('你可能不需要我了,请关闭我吧!') 40 | system('pause') -------------------------------------------------------------------------------- /javsdt/Functions/XML.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | 3 | 4 | # 功能:去除xml文档不允许的特殊字符 &<> 5 | # 参数:(文件名、简介、标题)str 6 | # 返回:str 7 | # 辅助:无 8 | def replace_xml(name): 9 | # 替换xml中的不允许的特殊字符 .replace('\'', ''').replace('\"', '"') 10 | # .replace('&', '&').replace('<', '<').replace('>', '>') nfo基于xml,xml中不允许这5个字符,但实际测试nfo只不允许左边3个 11 | return name.replace('&', '&').replace('<', '<').replace('>', '>')\ 12 | .replace('\n', '').replace('\t', '').replace('\r', '').rstrip() 13 | 14 | 15 | # 功能:去除xml文档和windows路径不允许的特殊字符 &<> \/:*?"<>| 16 | # 参数:(文件名、简介、标题)str 17 | # 返回:str 18 | # 辅助:无 19 | def replace_xml_win(name): 20 | # 替换windows路径不允许的特殊字符 \/:*?"<>| 21 | return name.replace('&', '&').replace('<', '<').replace('>', '>')\ 22 | .replace('\n', '').replace('\t', '').replace('\r', '')\ 23 | .replace("\\", "#").replace("/", "#").replace(":", ":").replace("*", "#")\ 24 | .replace("?", "?").replace("\"", "#").replace("|", "#").rstrip() 25 | -------------------------------------------------------------------------------- /javsdt/Jav321.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os 3 | import re 4 | from shutil import copyfile 5 | from traceback import format_exc 6 | 7 | from Class.JavFile import JavFile 8 | ######################################################################################################################## 9 | from Class.Settings import Settings 10 | from Functions.Baidu import translate 11 | from Functions.Car import find_car_suren, list_suren_car 12 | from Functions.Picture import add_watermark_divulge, crop_poster_default 13 | from Functions.Picture import check_picture, add_watermark_subtitle 14 | # ################################################## 不同 ########################################################## 15 | from Functions.Process import judge_exist_divulge 16 | from Functions.Process import judge_exist_subtitle 17 | from Functions.Process import perfect_dict_data 18 | from Functions.Record import record_start, record_fail 19 | from Functions.Requests.Download import download_pic 20 | from Functions.Requests.Jav321Req import get_321_html, post_321_html 21 | from Functions.Standard import rename_mp4, rename_folder, classify_files, classify_folder 22 | from Functions.Status import check_actors 23 | from Functions.Status import judge_exist_nfo, judge_exist_extra_folders, count_num_videos 24 | from Functions.User import choose_directory 25 | from Functions.XML import replace_xml, replace_xml_win 26 | 27 | 28 | # main开始 29 | print('1、请开启代理,建议美国节点,访问“https://www.jav321.com/”\n' 30 | '2、影片信息没有导演,没有演员头像,可能没有演员姓名\n' 31 | '3、只能整理列出车牌的素人影片\n' 32 | ' 如有素人车牌识别不出,请在ini中添加该车牌,或者告知作者\n') 33 | 34 | # 读取配置文件,这个ini文件用来给用户设置 35 | print('正在读取ini中的设置...', end='') 36 | try: 37 | settings = Settings('素人') 38 | except: 39 | settings = None 40 | print(format_exc()) 41 | print('\n无法读取ini文件,请修改它为正确格式,或者打开“【ini】重新创建ini.exe”创建全新的ini!') 42 | os.system('pause') 43 | print('\n读取ini文件成功!\n') 44 | 45 | # 路径分隔符:当前系统的路径分隔符 windows是“\”,linux和mac是“/” 46 | sep = os.sep 47 | 48 | # 检查头像:如果需要为kodi整理头像,先检查演员头像ini、头像文件夹是否存在。 49 | check_actors(settings.bool_sculpture) 50 | 51 | # 局部代理:哪些站点需要代理。 52 | proxy_library, proxy_bus, proxy_321, proxy_db, proxy_arzon, proxy_dmm = settings.get_proxy() 53 | # jav321网址 搜索网址 https://www.jav321.com/search https://www.jav321.com/ 54 | url_search_321, url_321 = settings.get_url_321() 55 | 56 | # 选择简繁中文以及百度翻译账户:需要简体中文还是繁体中文,影响影片特征和简介。 57 | to_language, tran_id, tran_sk = settings.get_translate_account() 58 | 59 | # 信息字典:存放影片信息,用于给用户自定义各种命名。 60 | dict_data = {'车牌': 'ABC-123', 61 | '车牌前缀': 'ABC', 62 | '标题': '素人标题', 63 | '完整标题': '完整素人标题', 64 | '导演': '素人导演', 65 | '片商': '素人片商', 66 | '评分': '0', 67 | '片长': '0', 68 | '系列': '素人系列', 69 | '发行年月日': '1970-01-01', '发行年份': '1970', '月': '01', '日': '01', 70 | '首个演员': '素人演员', '全部演员': '素人演员', 71 | '空格': ' ', 72 | '\\': sep, '/': sep, # 文件路径分隔符 73 | '是否中字': '', 74 | '是否流出': '', 75 | '影片类型': settings.av_type(), 76 | '视频': 'ABC-123', # 当前及未来的视频文件名,不带ext 77 | '原文件名': 'ABC-123', '原文件夹名': 'ABC-123', } 78 | 79 | # nfo中title的写法。 80 | list_name_nfo_title = settings.formula_name_nfo_title() 81 | # 额外将哪些元素放入特征中 82 | list_extra_genres = settings.list_extra_genre() 83 | # 重命名视频的格式 84 | list_name_video = settings.formula_rename_video() 85 | # 重命名文件夹的格式 86 | list_name_folder = settings.formula_rename_folder() 87 | 88 | # fanart的格式 89 | list_name_fanart = settings.formula_name_fanart() 90 | # poster的格式 91 | list_name_poster = settings.formula_name_poster() 92 | 93 | # 视频文件名包含哪些多余的字母数字,需要无视 94 | list_surplus_words_in_filename = settings.list_surplus_word_in_filename('素人') 95 | # 文件名包含哪些特殊含义的文字,判断是否中字 96 | list_subtitle_words_in_filename = settings.list_subtitle_word_in_filename() 97 | # 文件名包含哪些特殊含义的文字,判断是否是无码流出片 98 | list_divulge_words_in_filename = settings.list_divulge_word_in_filename() 99 | 100 | # 素人番号:得到事先设置的素人番号,让程序能跳过它们 101 | list_suren_cars = list_suren_car() 102 | 103 | # 需要扫描的文件的类型 104 | tuple_video_types = settings.tuple_video_type() 105 | 106 | # 完善dict_data,如果用户自定义了一些文字,不在元素中,需要将它们添加进dict_data;list_classify_basis,归类标准,归类目标文件夹的组成公式。 107 | dict_data, list_classify_basis = perfect_dict_data(list_extra_genres, list_name_video, list_name_folder, 108 | list_name_nfo_title, list_name_fanart, list_name_poster, 109 | settings.custom_classify_basis(), dict_data) 110 | 111 | # 用户输入“回车”就继续选择文件夹整理 112 | input_start_key = '' 113 | while input_start_key == '': 114 | # 用户:选择需要整理的文件夹 115 | print('请选择要整理的文件夹:', end='') 116 | root_choose = choose_directory() 117 | print(root_choose) 118 | # 日志:在txt中记录一下用户的这次操作,在某个时间选择了某个文件夹 119 | record_start(root_choose) 120 | # 归类:用户自定义的归类根目录,如果不需要归类则为空 121 | root_classify = settings.check_classify_root(root_choose, sep) 122 | # 计数:失败次数及进度 123 | num_fail = 0 # 已经或可能导致致命错误,比如整理未完成,同车牌有不同视频 124 | num_all_videos = count_num_videos(root_choose, tuple_video_types) # 所选文件夹总共有多少个视频文件 125 | num_current = 0 # 当前视频的编号 126 | print('...文件扫描开始...如果时间过长...请避开夜晚高峰期...\n') 127 | # root【当前根目录】 dirs【子文件夹】 files【文件】,root是str,后两个是list 128 | for root, dirs, files in os.walk(root_choose): 129 | # 什么文件都没有 130 | if not files: 131 | continue 132 | # 当前root是已归类的目录,无需处理 133 | if '归类完成' in root.replace(root_choose, ''): 134 | continue 135 | # 跳过已存在nfo的文件夹,判断这一层文件夹中有没有nfo 136 | if settings.bool_skip and judge_exist_nfo(files): 137 | continue 138 | # 对这一层文件夹进行评估,有多少视频,有多少同车牌视频,是不是独立文件夹 139 | list_jav_struct = [] # 存放:需要整理的jav的结构体 140 | dict_car_pref = {} # 存放:每一车牌的集数, 例如{'abp-123': 1, avop-789': 2}是指 abp-123只有一集,avop-789有cd1、cd2 141 | num_videos_include = 0 # 计数:当前文件夹中视频的数量,可能有视频不是jav 142 | dict_subtitle_files = {} # 存放:jav的字幕文件和车牌对应关系 {'c:\a\abc_123.srt': 'abc-123'} 143 | # 判断文件是不是字幕文件,放入dict_subtitle_files中 144 | for file_raw in files: 145 | file_temp = file_raw.upper() 146 | if file_temp.endswith(('.SRT', '.VTT', '.ASS', '.SSA', '.SUB', '.SMI',)): 147 | # 当前模式不处理FC2 148 | if 'FC2' in file_temp: 149 | continue 150 | # 去除用户设置的、干扰车牌的文字 151 | for word in list_surplus_words_in_filename: 152 | file_temp = file_temp.replace(word, '') 153 | # 得到字幕文件名中的车牌 154 | subtitle_car = find_car_suren(file_temp, list_suren_cars) 155 | # 将该字幕文件和其中的车牌对应到dict_subtitle_files中 156 | if subtitle_car: 157 | dict_subtitle_files[file_raw] = subtitle_car 158 | # print(dict_subtitle_files) 159 | # 判断文件是不是视频,放入list_jav_struct中 160 | for file_raw in files: 161 | file_temp = file_raw.upper() 162 | if file_temp.endswith(tuple_video_types) and not file_temp.startswith('.'): 163 | num_videos_include += 1 164 | num_current += 1 165 | if 'FC2' in file_temp: 166 | continue 167 | for word in list_surplus_words_in_filename: 168 | file_temp = file_temp.replace(word, '') 169 | # 得到视频中的车牌 170 | car = find_car_suren(file_temp, list_suren_cars) 171 | if car: 172 | try: 173 | dict_car_pref[car] += 1 # 已经有这个车牌了,加一集cd 174 | except KeyError: 175 | dict_car_pref[car] = 1 # 这个新车牌有了第一集 176 | # 这个车牌在dict_subtitle_files中,有它的字幕。 177 | if car in dict_subtitle_files.values(): 178 | subtitle_file = list(dict_subtitle_files.keys())[list(dict_subtitle_files.values()).index(car)] 179 | del dict_subtitle_files[subtitle_file] 180 | else: 181 | subtitle_file = '' 182 | # 将该jav的各种属性打包好,包括原文件名带扩展名、所在文件夹路径、第几集、所属字幕文件名 183 | jav_struct = JavFile(file_raw, root, car, dict_car_pref[car], subtitle_file, num_current) 184 | list_jav_struct.append(jav_struct) 185 | else: 186 | print('>>无法处理:', root.replace(root_choose, '') + sep + file_raw) 187 | 188 | # 判定影片所在文件夹是否是独立文件夹,独立文件夹是指该文件夹仅用来存放该影片,而不是大杂烩文件夹 189 | # 这一层文件夹下有jav 190 | if dict_car_pref: 191 | # 当前文件夹下,车牌不止一个;还有其他非jav视频;有其他文件夹,除了演员头像文件夹“.actors”和额外剧照文件夹“extrafanart”; 192 | if len(dict_car_pref) > 1 or num_videos_include > len(list_jav_struct) or judge_exist_extra_folders(dirs): 193 | bool_separate_folder = False # 不是独立的文件夹 194 | else: 195 | bool_separate_folder = True # 这一层文件夹是这部jav的独立文件夹 196 | else: 197 | continue 198 | 199 | # 开始处理每一部jav 200 | for jav in list_jav_struct: 201 | # 告诉用户进度 202 | print('>> [' + str(jav.number) + '/' + str(num_all_videos) + ']:', jav.name) 203 | print(' >发现车牌:', jav.car) 204 | 205 | # 判断是否有中字的特征,条件有三满足其一即可:1有外挂字幕 2文件名中含有“-C”之类的字眼 3旧的nfo中已经记录了它的中字特征 206 | if jav.subtitle: 207 | bool_subtitle = True # 判定成功 208 | dict_data['是否中字'] = settings.custom_subtitle_expression # '是否中字'这一命名元素被激活 209 | else: 210 | bool_subtitle = judge_exist_subtitle(root, jav.name_no_ext, list_subtitle_words_in_filename) 211 | dict_data['是否中字'] = settings.custom_subtitle_expression if bool_subtitle else '' 212 | # 判断是否是无码流出的作品,同理 213 | bool_divulge = judge_exist_divulge(root, jav.name_no_ext, list_divulge_words_in_filename) 214 | dict_data['是否流出'] = settings.custom_divulge_expression if bool_divulge else '' 215 | 216 | # 影片的相对于所选文件夹的路径,用于报错 217 | path_relative = sep + jav.path.replace(root_choose, '') 218 | 219 | # 获取nfo信息的jav321网页 220 | try: 221 | # 用户指定了网址,则直接得到jav所在网址 222 | if '图书馆' in jav.name: 223 | url_appointg = re.search(r'三二一(.+?)\.', jav.name) 224 | if str(url_appointg) != 'None': 225 | url_on_web = url_321 + 'video/' + url_appointg.group(1) 226 | print(' >获取信息:', url_on_web) 227 | html_web = get_321_html(url_on_web, proxy_321) 228 | # 尝试找标题,jav321上的标题不包含车牌,title_only表示单纯的标题 229 | titleg = re.search(r'

(.+?) ', html_web) # 匹配处理“标题” 230 | # 搜索结果就是AV的页面 231 | if str(titleg) != 'None': 232 | title_only = titleg.group(1) 233 | print(title_only) 234 | # 找不到标题,jav321找不到影片 235 | else: 236 | # print(html_web) 237 | num_fail += 1 238 | record_fail(' >第' + str(num_fail) + '个失败!你指定的jav321网址找不到影片:' + path_relative + '\n') 239 | continue # 【退出对该jav的整理】 240 | else: 241 | num_fail += 1 242 | record_fail(' >第' + str(num_fail) + '个失败!你指定的jav321网址有错误:' + path_relative + '\n') 243 | continue # 【退出对该jav的整理】 244 | # 用户没有指定网址,则去搜索 245 | else: 246 | # 得到jav321搜索网页html 247 | print(' >搜索车牌:', url_search_321) 248 | html_web = post_321_html(url_search_321, {'sn': jav.car}, proxy_321) 249 | # print(html_web) 250 | # 尝试找标题 251 | titleg = re.search(r'h3>(.+?) ', html_web) # 匹配处理“标题” 252 | # 找得到,搜索结果就是AV的页面 253 | if str(titleg) != 'None': 254 | title_only = titleg.group(1) 255 | # print(title_only) 256 | # 找不到标题,jav321找不到影片 257 | else: 258 | num_fail += 1 259 | record_fail(' >第' + str( 260 | num_fail) + '个失败!jav321找不到该车牌的信息:' + jav.car + ',' + path_relative + '\n') 261 | continue # 【退出对该jav的整理】 262 | 263 | # 去除xml文档和windows路径不允许的特殊字符 &<> \/:*?"<>| 264 | title_only = replace_xml_win(title_only) 265 | # 正则匹配 影片信息 开始! 266 | # 有大部分信息的html_web 267 | html_web = re.search(r'(h3>.+?)async', html_web).group(1) 268 | print(html_web) 269 | # 车牌 270 | dict_data['车牌'] = car = re.search(r'番.?: (.+?)
', html_web).group(1).upper() 271 | dict_data['车牌前缀'] = car.split('-')[0] 272 | # jav321上素人的title开头不是车牌 273 | title = car + ' ' + title_only 274 | # 给用户重命名用的标题是“短标题”,nfo中是“完整标题”,但用户在ini中只用写“标题” 275 | dict_data['完整标题'] = title_only 276 | # 处理影片的标题过长 277 | if len(title_only) > settings.int_title_len: 278 | dict_data['标题'] = title_only[:settings.int_title_len] 279 | else: 280 | dict_data['标题'] = title_only 281 | print(' >影片标题:', title) 282 | # DVD封面cover 283 | coverg = re.search(r'poster="(.+?)">: (\d\d\d\d-\d\d-\d\d)
', html_web) 304 | if str(premieredg) != 'None': 305 | dict_data['发行年月日'] = time_premiered = premieredg.group(1) 306 | dict_data['发行年份'] = time_premiered[0:4] 307 | dict_data['月'] = time_premiered[5:7] 308 | dict_data['日'] = time_premiered[8:10] 309 | else: 310 | dict_data['发行年月日'] = time_premiered = '1970-01-01' 311 | dict_data['发行年份'] = '1970' 312 | dict_data['月'] = '01' 313 | dict_data['日'] = '01' 314 | # 片长 150 分钟 315 | runtimeg = re.search(r'播放..: (\d+)', html_web) 316 | if str(runtimeg) != 'None': 317 | dict_data['片长'] = runtimeg.group(1) 318 | else: 319 | dict_data['片长'] = '0' 320 | # 片商: プレステージプレミアム(PRESTIGE PREMIUM) 321 | studiog = re.search(r'片商: (.+?)', html_web) 322 | if str(studiog) != 'None': 323 | dict_data['片商'] = studio = replace_xml_win(studiog.group(1)) 324 | else: 325 | dict_data['片商'] = '素人片商' 326 | studio = '' 327 | # 演员们 和 # 第一个演员 演员: 花音さん 21歳 床屋さん(家族経営)   328 | actorg = re.search(r'small>(.+?)
', html_web) 329 | if str(actorg) != 'None': 330 | actor_only = actorg.group(1) 331 | list_actor = actor_only.replace('/', ' ').split( 332 | ' ') # luxu-071 松波優 29歳 システムエンジニア 333 | list_actor = [i for i in list_actor if i] 334 | if len(list_actor) > 3: 335 | dict_data['首个演员'] = list_actor[1] + ' ' + list_actor[2] + ' ' + list_actor[3] 336 | elif len(list_actor) > 1: 337 | del list_actor[0] 338 | dict_data['首个演员'] = ' '.join(list_actor) 339 | else: 340 | dict_data['首个演员'] = '素人' 341 | dict_data['全部演员'] = dict_data['首个演员'] 342 | else: 343 | dict_data['首个演员'] = dict_data['全部演员'] = '素人' 344 | # 特点 345 | genres = re.findall(r'genre.+?">(.+?)', html_web) 346 | genres = [i for i in genres if i != '标签' and i != '標籤' and i != '素人'] # 这些特征 没有参考意义,为用户删去 347 | if bool_subtitle: # 有“中字“,加上特征”中文字幕” 348 | genres.append('中文字幕') 349 | if bool_divulge: # 是流出无码片,加上特征'无码流出' 350 | genres.append('无码流出') 351 | # print(genres) 352 | # 评分 353 | scoreg = re.search(r'评分: (\d\.\d)
', html_web) 354 | if str(scoreg) != 'None': 355 | float_score = float(scoreg.group(1)) 356 | float_score = (float_score - 2) * 10 / 3 357 | if float_score >= 0: 358 | score = '%.1f' % float_score 359 | else: 360 | score = '0' 361 | else: 362 | scoreg = re.search(r'"img/(\d\d)\.gif', html_web) 363 | if str(scoreg) != 'None': 364 | float_score = float(scoreg.group(1)) / 10 365 | float_score = (float_score - 2) * 10 / 3 366 | if float_score >= 0: 367 | score = '%.1f' % float_score 368 | else: 369 | score = '0' 370 | else: 371 | score = '0' 372 | dict_data['评分'] = score 373 | # 烂番茄评分 用上面的评分*10 374 | criticrating = str(float(score) * 10) 375 | ####################################################################### 376 | # 简介 377 | if settings.bool_nfo: 378 | plotg = re.search(r'md-12">([^<].+?)', html_web) 379 | if str(plotg) != 'None': 380 | plot = plotg.group(1) 381 | else: 382 | plot = '' 383 | plot = title_only + plot 384 | if settings.bool_tran: 385 | plot = translate(tran_id, tran_sk, plot, to_language) 386 | if plot.startswith('【百度'): 387 | num_fail += 1 388 | record_fail(' >第' + str(num_fail) + '个失败!翻译简介失败:' + path_relative + '\n') 389 | plot = replace_xml(plot) 390 | else: 391 | plot = '' 392 | # print(plot) 393 | ####################################################################### 394 | dict_data['视频'] = dict_data['原文件名'] = jav.name_no_ext # dict_data['视频'],先定义为原文件名,即将发生变化。 395 | dict_data['原文件夹名'] = jav.folder 396 | # 是CD1还是CDn? 397 | num_all_episodes = dict_car_pref[jav.car] # 该车牌总共多少集 398 | if num_all_episodes > 1: 399 | str_cd = '-cd' + str(jav.episode) 400 | else: 401 | str_cd = '' 402 | 403 | # 1重命名视频【相同】 404 | try: 405 | dict_data, jav, num_temp = rename_mp4(jav, num_fail, settings, dict_data, list_name_video, 406 | path_relative, str_cd) 407 | num_fail = num_temp 408 | except FileExistsError: 409 | num_fail += 1 410 | continue 411 | 412 | # 2 归类影片【相同】只针对视频文件和字幕文件。注意:第2操作和下面(第3操作+第7操作)互斥,只能执行第2操作或(第3操作+第7操作),归类影片是针对“文件”还是“文件夹”。 413 | try: 414 | jav, num_temp = classify_files(jav, num_fail, settings, dict_data, list_classify_basis, 415 | root_classify) 416 | num_fail = num_temp 417 | except FileExistsError: 418 | num_fail += 1 419 | continue 420 | 421 | # 3重命名文件夹【相同】如果是针对“文件”归类,这一步会被跳过。 因为用户只需要归类视频文件,不需要管文件夹。 422 | try: 423 | jav, num_temp = rename_folder(jav, num_fail, settings, dict_data, list_name_folder, 424 | bool_separate_folder, num_all_episodes) 425 | num_fail = num_temp 426 | except FileExistsError: 427 | num_fail += 1 428 | continue 429 | 430 | # 更新一下path_relative 431 | path_relative = sep + jav.path.replace(root_choose, '') # 影片的相对于所选文件夹的路径,用于报错 432 | 433 | # 4写入nfo【独特】 434 | if settings.bool_nfo: 435 | # 如果是为空地准备的nfo,不需要多cd 436 | if settings.bool_cd_only: 437 | path_nfo = jav.root + sep + jav.name_no_ext.replace(str_cd, '') + '.nfo' 438 | else: 439 | path_nfo = jav.root + sep + jav.name_no_ext + '.nfo' 440 | # nfo中tilte的写法 441 | title_in_nfo = '' 442 | for i in list_name_nfo_title: 443 | title_in_nfo += dict_data[i] 444 | # 开始写入nfo,这nfo格式是参考的kodi的nfo 445 | f = open(path_nfo, 'w', encoding="utf-8") 446 | f.write("\n" 447 | "\n" 448 | " " + plot + "\n" 449 | " " + title_in_nfo + "\n" 450 | " " + title + "\n" 451 | " " + score + "\n" 452 | " " + criticrating + "\n" 453 | " " + dict_data['发行年份'] + "\n" 454 | " NC-17\n" 455 | " NC-17\n" 456 | " JP\n" 457 | " " + time_premiered + "\n" 458 | " " + time_premiered + "\n" 459 | " " + dict_data['片长'] + "\n" 460 | " 日本\n" 461 | " " + studio + "\n" 462 | " " + car + "\n" 463 | " " + car + "\n") 464 | # 需要将特征写入genre 465 | if settings.bool_genre: 466 | for i in genres: 467 | f.write(" " + i + "\n") 468 | if settings.bool_write_studio and studio: 469 | f.write(" 片商:" + studio + "\n") 470 | if list_extra_genres: 471 | for i in list_extra_genres: 472 | f.write(" " + dict_data[i] + "\n") 473 | # 需要将特征写入tag 474 | if settings.bool_tag: 475 | for i in genres: 476 | f.write(" " + i + "\n") 477 | if settings.bool_write_studio and studio: 478 | f.write(" 片商:" + studio + "\n") 479 | if list_extra_genres: 480 | for i in list_extra_genres: 481 | f.write(" " + dict_data[i] + "\n") 482 | # 写入演员 483 | f.write( 484 | " \n " + dict_data['首个演员'] + "\n Actor\n \n") 485 | f.write("\n") 486 | f.close() 487 | print(' >nfo收集完成') 488 | 489 | # 5需要两张封面图片【独特】 490 | if settings.bool_jpg: 491 | # 下载海报的地址 cover 492 | # fanart和poster路径 493 | path_fanart = jav.root + sep 494 | path_poster = jav.root + sep 495 | for i in list_name_fanart: 496 | path_fanart += dict_data[i] 497 | for i in list_name_poster: 498 | path_poster += dict_data[i] 499 | # kodi只需要一份图片,图片路径唯一 500 | if settings.bool_cd_only: 501 | path_fanart = path_fanart.replace(str_cd, '') 502 | path_poster = path_poster.replace(str_cd, '') 503 | # emby需要多份,现在不是第一集,直接复制第一集的图片 504 | elif jav.episode != 1: 505 | try: 506 | copyfile(path_fanart.replace(str_cd, '-cd1'), path_fanart) 507 | print(' >fanart.jpg复制成功') 508 | copyfile(path_poster.replace(str_cd, '-cd1'), path_poster) 509 | print(' >poster.jpg复制成功') 510 | except FileNotFoundError: 511 | pass 512 | # kodi或者emby需要的第一份图片 513 | if check_picture(path_fanart): 514 | # print(' >已有fanart.jpg') 515 | pass 516 | else: 517 | # 下载封面 518 | print(' >从jav321下载封面:', url_cover) 519 | try: 520 | download_pic(url_cover, path_fanart, proxy_321) 521 | print(' >fanart.jpg下载成功') 522 | except: 523 | num_fail += 1 524 | record_fail(' >第' + str( 525 | num_fail) + '个失败!下载fanart.jpg失败:' + url_cover + ',' + path_relative + '\n') 526 | continue # 退出对该jav的整理 527 | # 下载海报 528 | if check_picture(path_poster): 529 | # print(' >已有poster.jpg') 530 | pass 531 | elif url_cover == url_poster: # 有些素人片,没有fanart和poster之分,只有一张接近正方形的图片 532 | # 裁剪生成 poster 533 | crop_poster_default(path_fanart, path_poster, 2) 534 | # 需要加上条纹 535 | if settings.bool_watermark_subtitle and bool_subtitle: 536 | add_watermark_subtitle(path_poster) 537 | if settings.bool_watermark_divulge and bool_divulge: 538 | add_watermark_divulge(path_poster) 539 | else: 540 | # 下载poster.jpg 541 | print(' >从jav321下载poster:', url_poster) 542 | try: 543 | download_pic(url_poster, path_poster, proxy_321) 544 | print(' >poster.jpg下载成功') 545 | # 需要加上条纹 546 | if settings.bool_watermark_subtitle and bool_subtitle: 547 | add_watermark_subtitle(path_poster) 548 | if settings.bool_watermark_divulge and bool_divulge: 549 | add_watermark_divulge(path_poster) 550 | except: 551 | num_fail += 1 552 | record_fail( 553 | ' >第' + str(num_fail) + '个失败!poster下载失败:' + url_poster + ',' + path_relative + '\n') 554 | continue 555 | 556 | # 6收集演员头像【相同】 557 | 558 | # 7归类影片,针对文件夹【相同】 559 | try: 560 | num_temp = classify_folder(jav, num_fail, settings, dict_data, list_classify_basis, root_classify, 561 | root, bool_separate_folder, num_all_episodes) 562 | num_fail = num_temp 563 | except FileExistsError: 564 | num_fail += 1 565 | continue 566 | 567 | except: 568 | num_fail += 1 569 | record_fail(' >第' + str( 570 | num_fail) + '个失败!发生错误,如一直在该影片报错请截图并联系作者:' + path_relative + '\n' + format_exc() + '\n') 571 | continue # 【退出对该jav的整理】 572 | 573 | # 完结撒花 574 | print('\n当前文件夹完成,', end='') 575 | if num_fail > 0: 576 | print('失败', num_fail, '个! ', root_choose, '\n') 577 | line = -1 578 | with open('【可删除】失败记录.txt', 'r', encoding="utf-8") as f: 579 | content = list(f) 580 | while 1: 581 | if content[line].startswith('已'): 582 | break 583 | line -= 1 584 | for i in range(line + 1, 0): 585 | print(content[i], end='') 586 | print('\n“【可删除】失败记录.txt”已记录错误\n') 587 | else: 588 | print(' “0”失败! ', root_choose, '\n') 589 | # os.system('pause') 590 | input_start_key = input('回车继续选择文件夹整理:') 591 | -------------------------------------------------------------------------------- /javsdt/JavbusWuma.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os, re 3 | from shutil import copyfile 4 | from traceback import format_exc 5 | ######################################################################################################################## 6 | from Class.Settings import Settings 7 | from Class.JavFile import JavFile 8 | from Functions.Status import judge_exist_nfo, judge_exist_extra_folders, count_num_videos 9 | from Functions.User import choose_directory 10 | from Functions.Record import record_start, record_fail 11 | from Functions.Process import perfect_dict_data 12 | from Functions.Standard import rename_mp4, rename_folder, classify_files, classify_folder 13 | from Functions.XML import replace_xml_win 14 | from Functions.Process import judge_exist_subtitle 15 | from Functions.Picture import check_picture, add_watermark_subtitle 16 | from Functions.Requests.Download import download_pic 17 | from Functions.Genre import better_dict_genre 18 | # ################################################## 不同 ########################################################## 19 | from Functions.Status import check_actors 20 | from Functions.Car import find_car_wuma, list_suren_car 21 | from Functions.Standard import collect_sculpture 22 | from Functions.Picture import crop_poster_baidu, crop_poster_default 23 | from Functions.Requests.JavbusReq import get_bus_html 24 | 25 | 26 | # main开始 27 | print('1、避开21:00-1:00,访问javbus很慢\n' 28 | '2、若一直连不上javbus,请在ini中更新防屏蔽网址\n' 29 | '3、找不到AV信息,请在javbus上确认,再修改本地视频文件名,如:\n' 30 | ' 1)多余的字母数字:[JAV] [Uncensored] HEYZO 2171 [1080p].mp4 => HEYZO 2171.mp4\n' 31 | ' 112314-742-carib-1080p.mp4 => 112314-742.mp4\n' 32 | ' Heyzo_HD_0733_full.mp4 => Heyzo_0733.mp4\n' 33 | ' 2)多余的横杠:sr-131.mp4 => sr131.mp4\n' 34 | ' 3)干扰车牌的分集:Heyzo_0733_01.mp4 => Heyzo_0733啊.mp4\n' 35 | ' Heyzo_0733_02.mp4 => Heyzo_0733吧.mp4\n') 36 | 37 | # 读取配置文件,这个ini文件用来给用户设置 38 | print('正在读取ini中的设置...', end='') 39 | try: 40 | settings = Settings('无码') 41 | except: 42 | settings = None 43 | print(format_exc()) 44 | print('\n无法读取ini文件,请修改它为正确格式,或者打开“【ini】重新创建ini.exe”创建全新的ini!') 45 | os.system('pause') 46 | print('\n读取ini文件成功!\n') 47 | 48 | # 路径分隔符:当前系统的路径分隔符 windows是“\”,linux和mac是“/” 49 | sep = os.sep 50 | 51 | # 检查头像:如果需要为kodi整理头像,先检查演员头像ini、头像文件夹是否存在。 52 | check_actors(settings.bool_sculpture) 53 | 54 | # 局部代理:哪些站点需要代理。 55 | proxy_library, proxy_bus, proxy_321, proxy_db, proxy_arzon, proxy_dmm = settings.get_proxy() 56 | 57 | # javbus网址 https://www.buscdn.work/ 58 | url_bus = settings.get_url_bus() 59 | 60 | # 选择简繁中文以及百度翻译账户:需要简体中文还是繁体中文,影响影片特征和简介。 61 | to_language, tran_id, tran_sk = settings.get_translate_account() 62 | 63 | # 信息字典:存放影片信息,用于给用户自定义各种命名。 64 | dict_data = {'车牌': 'CBA-123', 65 | '车牌前缀': 'CBA', 66 | '标题': '无码标题', 67 | '完整标题': '完整无码标题', 68 | '导演': '无码导演', 69 | '片商': '无码片商', 70 | '评分': '0', 71 | '片长': '0', 72 | '系列': '无码系列', 73 | '发行年月日': '1970-01-01', '发行年份': '1970', '月': '01', '日': '01', 74 | '首个演员': '无码演员', '全部演员': '无码演员', 75 | '空格': ' ', 76 | '\\': sep, '/': sep, # 文件路径分隔符 77 | '是否中字': '', 78 | '是否流出': '', 79 | '影片类型': settings.av_type(), 80 | '视频': 'CBA-123', # 当前及未来的视频文件名,不带ext 81 | '原文件名': 'CBA-123', '原文件夹名': 'CBA-123', } 82 | 83 | # nfo中title的写法。 84 | list_name_nfo_title = settings.formula_name_nfo_title() 85 | # 额外将哪些元素放入特征中 86 | list_extra_genres = settings.list_extra_genre() 87 | # 重命名视频的格式 88 | list_name_video = settings.formula_rename_video() 89 | # 重命名文件夹的格式 90 | list_name_folder = settings.formula_rename_folder() 91 | 92 | # fanart的格式 93 | list_name_fanart = settings.formula_name_fanart() 94 | # poster的格式 95 | list_name_poster = settings.formula_name_poster() 96 | 97 | # 视频文件名包含哪些多余的字母数字,需要无视 98 | list_surplus_words_in_filename = settings.list_surplus_word_in_filename('无码') 99 | # 文件名包含哪些特殊含义的文字,判断是否中字 100 | list_subtitle_words_in_filename = settings.list_subtitle_word_in_filename() 101 | 102 | # 素人番号:得到事先设置的素人番号,让程序能跳过它们 103 | list_suren_cars = list_suren_car() 104 | 105 | # 需要扫描的文件的类型 106 | tuple_video_types = settings.tuple_video_type() 107 | 108 | # 完善dict_data,如果用户自定义了一些文字,不在元素中,需要将它们添加进dict_data;list_classify_basis,归类标准,归类目标文件夹的组成公式。 109 | dict_data, list_classify_basis = perfect_dict_data(list_extra_genres, list_name_video, list_name_folder, 110 | list_name_nfo_title, list_name_fanart, list_name_poster, 111 | settings.custom_classify_basis(), dict_data) 112 | 113 | # 准备工作:使用人体分析,与百度al建立联系 114 | client = settings.start_body_analysis() 115 | 116 | # 优化特征的字典 117 | dict_genre = better_dict_genre('Javbus无码', to_language) 118 | 119 | # 用户输入“回车”就继续选择文件夹整理 120 | input_start_key = '' 121 | while input_start_key == '': 122 | # 用户:选择需要整理的文件夹 123 | print('请选择要整理的文件夹:', end='') 124 | root_choose = choose_directory() 125 | print(root_choose) 126 | # 日志:在txt中记录一下用户的这次操作,在某个时间选择了某个文件夹 127 | record_start(root_choose) 128 | # 归类:用户自定义的归类根目录,如果不需要归类则为空 129 | root_classify = settings.check_classify_root(root_choose, sep) 130 | # 计数:失败次数及进度 131 | num_fail = 0 # 已经或可能导致致命错误,比如整理未完成,同车牌有不同视频 132 | num_all_videos = count_num_videos(root_choose, tuple_video_types) # 所选文件夹总共有多少个视频文件 133 | num_current = 0 # 当前视频的编号 134 | print('...文件扫描开始...如果时间过长...请避开夜晚高峰期...\n') 135 | # root【当前根目录】 dirs【子文件夹】 files【文件】,root是str,后两个是list 136 | for root, dirs, files in os.walk(root_choose): 137 | # 什么文件都没有 138 | if not files: 139 | continue 140 | # 当前root是已归类的目录,无需处理 141 | if '归类完成' in root.replace(root_choose, ''): 142 | continue 143 | # 跳过已存在nfo的文件夹,判断这一层文件夹中有没有nfo 144 | if settings.bool_skip and judge_exist_nfo(files): 145 | continue 146 | # 对这一层文件夹进行评估,有多少视频,有多少同车牌视频,是不是独立文件夹 147 | list_jav_struct = [] # 存放:需要整理的jav的结构体 148 | dict_car_pref = {} # 存放:每一车牌的集数, 例如{'abp-123': 1, avop-789': 2}是指 abp-123只有一集,avop-789有cd1、cd2 149 | num_videos_include = 0 # 计数:当前文件夹中视频的数量,可能有视频不是jav 150 | dict_subtitle_files = {} # 存放:jav的字幕文件和车牌对应关系 {'c:\a\abc_123.srt': 'abc-123'} 151 | # 判断文件是不是字幕文件,放入dict_subtitle_files中 152 | for file_raw in files: 153 | file_temp = file_raw.upper() 154 | if file_temp.endswith(('.SRT', '.VTT', '.ASS', '.SSA', '.SUB', '.SMI',)): 155 | # 当前模式不处理FC2 156 | if 'FC2' in file_temp: 157 | continue 158 | # 去除用户设置的、干扰车牌的文字 159 | for word in list_surplus_words_in_filename: 160 | file_temp = file_temp.replace(word, '') 161 | # 得到字幕文件名中的车牌 162 | subtitle_car = find_car_wuma(file_temp, list_suren_cars) 163 | # 将该字幕文件和其中的车牌对应到dict_subtitle_files中 164 | if subtitle_car: 165 | dict_subtitle_files[file_raw] = subtitle_car 166 | # print(dict_subtitle_files) 167 | # 判断文件是不是视频,放入list_jav_struct中 168 | for file_raw in files: 169 | file_temp = file_raw.upper() 170 | if file_temp.endswith(tuple_video_types) and not file_temp.startswith('.'): 171 | num_videos_include += 1 172 | num_current += 1 173 | if 'FC2' in file_temp: 174 | continue 175 | for word in list_surplus_words_in_filename: 176 | file_temp = file_temp.replace(word, '') 177 | # 得到视频中的车牌 178 | car = find_car_wuma(file_temp, list_suren_cars) 179 | if car: 180 | try: 181 | dict_car_pref[car] += 1 # 已经有这个车牌了,加一集cd 182 | except KeyError: 183 | dict_car_pref[car] = 1 # 这个新车牌有了第一集 184 | # 这个车牌在dict_subtitle_files中,有它的字幕。 185 | if car in dict_subtitle_files.values(): 186 | subtitle_file = list(dict_subtitle_files.keys())[list(dict_subtitle_files.values()).index(car)] 187 | del dict_subtitle_files[subtitle_file] 188 | else: 189 | subtitle_file = '' 190 | # 将该jav的各种属性打包好,包括原文件名带扩展名、所在文件夹路径、第几集、所属字幕文件名 191 | jav_struct = JavFile(file_raw, root, car, dict_car_pref[car], subtitle_file, num_current) 192 | list_jav_struct.append(jav_struct) 193 | else: 194 | print('>>无法处理:', root.replace(root_choose, '') + sep + file_raw) 195 | 196 | # 判定影片所在文件夹是否是独立文件夹,独立文件夹是指该文件夹仅用来存放该影片,而不是大杂烩文件夹 197 | # 这一层文件夹下有jav 198 | if dict_car_pref: 199 | # 当前文件夹下,车牌不止一个;还有其他非jav视频;有其他文件夹,除了演员头像文件夹“.actors”和额外剧照文件夹“extrafanart”; 200 | if len(dict_car_pref) > 1 or num_videos_include > len(list_jav_struct) or judge_exist_extra_folders(dirs): 201 | bool_separate_folder = False # 不是独立的文件夹 202 | else: 203 | bool_separate_folder = True # 这一层文件夹是这部jav的独立文件夹 204 | else: 205 | continue 206 | 207 | # 开始处理每一部jav 208 | for jav in list_jav_struct: 209 | # 告诉用户进度 210 | print('>> [' + str(jav.number) + '/' + str(num_all_videos) + ']:', jav.name) 211 | print(' >发现车牌:', jav.car) 212 | 213 | # 判断是否有中字的特征,条件有三满足其一即可:1有外挂字幕 2文件名中含有“-C”之类的字眼 3旧的nfo中已经记录了它的中字特征 214 | if jav.subtitle: 215 | bool_subtitle = True # 判定成功 216 | dict_data['是否中字'] = settings.custom_subtitle_expression # '是否中字'这一命名元素被激活 217 | else: 218 | bool_subtitle = judge_exist_subtitle(root, jav.name_no_ext, list_subtitle_words_in_filename) 219 | dict_data['是否中字'] = settings.custom_subtitle_expression if bool_subtitle else '' 220 | 221 | # 影片的相对于所选文件夹的路径,用于报错 222 | path_relative = sep + jav.path.replace(root_choose, '') 223 | 224 | # 获取nfo信息的javbus网页 225 | try: 226 | # 用户指定了网址,则直接得到jav所在网址 227 | if '公交车' in jav.name: 228 | url_appointg = re.search(r'公交车(.+?)\.', jav.name) 229 | if str(url_appointg) != 'None': 230 | url_on_web = url_bus + url_appointg.group(1) 231 | else: 232 | num_fail += 1 233 | record_fail(' >第' + str(num_fail) + '个失败!你指定的javbus网址有错误:' + path_relative + '\n') 234 | continue # 【退出对该jav的整理】 235 | # 用户没有指定网址,则去搜索 236 | else: 237 | url_search_web = url_bus + 'uncensored/search/' + jav.car.replace('-', '%20').replace('_', 238 | '%20').replace( 239 | ' ', '%20') + '&type=&parent=uc' 240 | print(' >搜索车牌:', url_search_web) 241 | # 得到javbus搜索网页html 242 | html_web = get_bus_html(url_search_web, proxy_bus) 243 | # 尝试找movie-box 244 | list_search_results = re.findall(r'movie-box" href="(.+?)">', html_web) # 匹配处理“标题” 245 | if list_search_results: # 搜索结果页面只有一个box 246 | # print(list_search_results) 247 | # print(' >正在核查搜索结果...') 248 | jav_prefg = re.search(r'([A-Z0-9]+)[-_]?', jav.car) 249 | jav_pref = jav_prefg.group(1) if str(jav_prefg) != 'None' else '' 250 | jav_sufg = re.search(r'[^\d](\d\d+)', jav.car) 251 | jav_suf = jav_sufg.group(1).lstrip('0') if str(jav_sufg) != 'None' else '' 252 | # print(jav_pref, jav_suf) 253 | list_fit_results = [] # 存放,车牌符合的结果 254 | for i in list_search_results: 255 | url_end = i.split('/')[-1].upper() 256 | url_sufg = re.search(r'[^\d](\d\d+)', url_end) 257 | url_suf = url_sufg.group(1).lstrip('0') if str( 258 | url_sufg) != 'None' else '' # 匹配box上影片url,车牌的后缀数字,去除多余的0 259 | # print(url_end, url_suf) 260 | if jav_suf == url_suf: # 数字相同 261 | url_prefg = re.search(r'([A-Z0-9]+)[-_]?', url_end) 262 | url_pref = url_prefg.group(1).upper() if str(url_prefg) != 'None' else '' 263 | if jav_pref == url_pref: # 数字相同的基础下,字母也相同,即可能车牌相同 264 | list_fit_results.append(i) 265 | # 无码搜索的结果一个都匹配不上 266 | if not list_fit_results: 267 | num_fail += 1 268 | record_fail(' >第' + str( 269 | num_fail) + '个失败!javbus无码找不到该车牌的信息:' + jav.car + ',' + path_relative + '\n') 270 | continue # 【退出对该jav的整理】 271 | # 默认用第一个搜索结果 272 | url_on_web = list_fit_results[0] 273 | # print('最终链接:', url_on_web) 274 | # print('最终list:', list_fit_results) 275 | if len(list_fit_results) > 1: 276 | num_fail += 1 277 | record_fail(' >第' + str( 278 | num_fail) + '个警告!javbus搜索到同车牌的不同视频:' + jav.car + ',' + path_relative + '\n') 279 | # 找不到box 280 | else: 281 | num_fail += 1 282 | record_fail(' >第' + str( 283 | num_fail) + '个失败!javbus无码找不到该车牌的信息:' + jav.car + ',' + path_relative + '\n') 284 | continue # 【跳出对该jav的整理】 285 | # 经过上面的三种情况,可能找到了jav在bus上的网页链接url_on_web 286 | print(' >获取信息:', url_on_web) 287 | # 得到最终的jav所在网页 288 | html_web = get_bus_html(url_on_web, proxy_bus) 289 | 290 | # 开始匹配信息 291 | # 有大部分信息的html_web 292 | html_web = re.search(r'(h3>[\s\S]*?)磁力連結投稿', html_web, re.DOTALL).group(1) 293 | # 标题 294 | title = re.search(r'h3>(.+?) \/:*?"<>| 296 | title = replace_xml_win(title) 297 | print(' >影片标题:', title) 298 | # 正则匹配 影片信息 开始! 299 | # title的开头是车牌号,想要后面的纯标题 300 | car_titleg = re.search(r'(.+?) (.+)', title) 301 | # 车牌号 302 | dict_data['车牌'] = car = car_titleg.group(1) 303 | dict_data['车牌前缀'] = car.split('-')[0] 304 | # 给用户重命名用的标题是“短标题”,nfo中是“完整标题”,但用户在ini中只用写“标题” 305 | title_only = car_titleg.group(2) 306 | # DVD封面cover 307 | coverg = re.search(r'bigImage" href="(.+?)">', html_web) # 封面图片的正则对象 308 | if str(coverg) != 'None': 309 | url_cover = coverg.group(1) 310 | else: 311 | url_cover = '' 312 | # 发行日期 313 | premieredg = re.search(r'發行日期: (.+?)

', html_web) 314 | if str(premieredg) != 'None': 315 | dict_data['发行年月日'] = time_premiered = premieredg.group(1) 316 | dict_data['发行年份'] = time_premiered[0:4] 317 | dict_data['月'] = time_premiered[5:7] 318 | dict_data['日'] = time_premiered[8:10] 319 | else: 320 | dict_data['发行年月日'] = time_premiered = '1970-01-01' 321 | dict_data['发行年份'] = '1970' 322 | dict_data['月'] = '01' 323 | dict_data['日'] = '01' 324 | # 片长 150 分钟 325 | runtimeg = re.search(r'長度: (.+?)分鐘

', html_web) 326 | if str(runtimeg) != 'None': 327 | dict_data['片长'] = runtimeg.group(1) 328 | else: 329 | dict_data['片长'] = '0' 330 | # 导演 331 | directorg = re.search(r'導演: (.+?)<', html_web) 332 | if str(directorg) != 'None': 333 | dict_data['导演'] = replace_xml_win(directorg.group(1)) 334 | else: 335 | dict_data['导演'] = '无码导演' 336 | # 片商 制作商 337 | studiog = re.search(r'製作商: (.+?)', html_web) 338 | if str(studiog) != 'None': 339 | dict_data['片商'] = studio = replace_xml_win(studiog.group(1)) 340 | else: 341 | dict_data['片商'] = '有码片商' 342 | studio = '' 343 | # 系列: 悪質シロウトナンパ 344 | seriesg = re.search(r'系列: (.+?)', html_web) # 封面图片的正则对象 345 | if str(seriesg) != 'None': 346 | dict_data['系列'] = series = seriesg.group(1).replace(sep, '#') 347 | else: 348 | dict_data['系列'] = '无码系列' 349 | series = '' 350 | # 演员们 和 # 第一个演员 351 | actors = re.findall(r'star/.+?">', html_web) 352 | if actors: 353 | if len(actors) > 7: 354 | dict_data['全部演员'] = ' '.join(actors[:7]) 355 | else: 356 | dict_data['全部演员'] = ' '.join(actors) 357 | dict_data['首个演员'] = actors[0] 358 | # 有些用户需要删去 标题 末尾可能存在的 演员姓名 359 | if settings.bool_strip_actors and title_only.endswith(dict_data['全部演员']): 360 | title_only = title_only[:-len(dict_data['全部演员'])].rstrip() 361 | else: 362 | actors = ['有码演员'] 363 | dict_data['首个演员'] = dict_data['全部演员'] = '无码演员' 364 | # 处理影片的标题过长 365 | dict_data['完整标题'] = title_only 366 | if len(title_only) > settings.int_title_len: 367 | dict_data['标题'] = title_only[:settings.int_title_len] 368 | else: 369 | dict_data['标题'] = title_only 370 | # 特点 371 | genres = re.findall(r'genre">', html_web) 372 | if bool_subtitle: # 有“中字“,加上特征”中文字幕” 373 | genres.append('中文字幕') 374 | try: 375 | genres = [dict_genre[i] for i in genres if dict_genre[i] != '删除'] 376 | except KeyError as error: 377 | num_fail += 1 378 | record_fail(' >第' + str(num_fail) + '个失败!发现新的特征需要添加至【特征对照表】:' + str(error) + '\n') 379 | continue 380 | # print(genres) 381 | ####################################################################### 382 | dict_data['视频'] = dict_data['原文件名'] = jav.name_no_ext # dict_data['视频'],先定义为原文件名,即将发生变化。 383 | dict_data['原文件夹名'] = jav.folder 384 | # 是CD1还是CDn? 385 | num_all_episodes = dict_car_pref[jav.car] # 该车牌总共多少集 386 | if num_all_episodes > 1: 387 | str_cd = '-cd' + str(jav.episode) 388 | else: 389 | str_cd = '' 390 | 391 | # 1重命名视频【相同】 392 | try: 393 | dict_data, jav, num_temp = rename_mp4(jav, num_fail, settings, dict_data, list_name_video, 394 | path_relative, str_cd) 395 | num_fail = num_temp 396 | except FileExistsError: 397 | num_fail += 1 398 | continue 399 | 400 | # 2 归类影片【相同】只针对视频文件和字幕文件。注意:第2操作和下面(第3操作+第7操作)互斥,只能执行第2操作或(第3操作+第7操作),归类影片是针对“文件”还是“文件夹”。 401 | try: 402 | jav, num_temp = classify_files(jav, num_fail, settings, dict_data, list_classify_basis, 403 | root_classify) 404 | num_fail = num_temp 405 | except FileExistsError: 406 | num_fail += 1 407 | continue 408 | 409 | # 3重命名文件夹【相同】如果是针对“文件”归类,这一步会被跳过。 因为用户只需要归类视频文件,不需要管文件夹。 410 | try: 411 | jav, num_temp = rename_folder(jav, num_fail, settings, dict_data, list_name_folder, 412 | bool_separate_folder, num_all_episodes) 413 | num_fail = num_temp 414 | except FileExistsError: 415 | num_fail += 1 416 | continue 417 | 418 | # 更新一下path_relative 419 | path_relative = sep + jav.path.replace(root_choose, '') # 影片的相对于所选文件夹的路径,用于报错 420 | 421 | # 4写入nfo【独特】 422 | if settings.bool_nfo: 423 | if settings.bool_cd_only: 424 | path_nfo = jav.root + sep + jav.name_no_ext.replace(str_cd, '') + '.nfo' 425 | else: 426 | path_nfo = jav.root + sep + jav.name_no_ext + '.nfo' 427 | title_in_nfo = '' 428 | for i in list_name_nfo_title: 429 | title_in_nfo += dict_data[i] # nfo中tilte的写法 430 | # 开始写入nfo,这nfo格式是参考的kodi的nfo 431 | f = open(path_nfo, 'w', encoding="utf-8") 432 | f.write("\n" 433 | "\n" 434 | " " + title_in_nfo + "\n" 435 | " " + title + "\n" 436 | " " + dict_data['导演'] + "\n" 437 | " " + dict_data['发行年份'] + "\n" 438 | " NC-17\n" 439 | " NC-17\n" 440 | " JP\n" 441 | " " + time_premiered + "\n" 442 | " " + time_premiered + "\n" 443 | " " + dict_data['片长'] + "\n" 444 | " 日本\n" 445 | " " + studio + "\n" 446 | " " + car + "\n" 447 | " " + car + "\n" 448 | " " + series + "\n") # emby不管set系列,kodi可以 449 | # 需要将特征写入genre 450 | if settings.bool_genre: 451 | for i in genres: 452 | f.write(" " + i + "\n") 453 | if settings.bool_write_series and series: 454 | f.write(" 系列:" + series + "\n") 455 | if settings.bool_write_studio and studio: 456 | f.write(" 片商:" + studio + "\n") 457 | if list_extra_genres: 458 | for i in list_extra_genres: 459 | f.write(" " + dict_data[i] + "\n") 460 | # 需要将特征写入tag 461 | if settings.bool_tag: 462 | for i in genres: 463 | f.write(" " + i + "\n") 464 | if settings.bool_write_series and series: 465 | f.write(" 系列:" + series + "\n") 466 | if settings.bool_write_studio and studio: 467 | f.write(" 片商:" + studio + "\n") 468 | if list_extra_genres: 469 | for i in list_extra_genres: 470 | f.write(" " + dict_data[i] + "\n") 471 | # 写入演员 472 | for i in actors: 473 | f.write(" \n " + i + "\n Actor\n \n") 474 | f.write("\n") 475 | f.close() 476 | print(' >nfo收集完成') 477 | 478 | # 5需要两张封面图片【独特】 479 | if settings.bool_jpg: 480 | # fanart和poster路径 481 | path_fanart = jav.root + sep 482 | path_poster = jav.root + sep 483 | for i in list_name_fanart: 484 | path_fanart += dict_data[i] 485 | for i in list_name_poster: 486 | path_poster += dict_data[i] 487 | # print(path_fanart) 488 | # kodi只需要一份图片,图片路径唯一 489 | if settings.bool_cd_only: 490 | path_fanart = path_fanart.replace(str_cd, '') 491 | path_poster = path_poster.replace(str_cd, '') 492 | # emby需要多份,现在不是第一集,直接复制第一集的图片 493 | elif jav.episode != 1: 494 | try: 495 | copyfile(path_fanart.replace(str_cd, '-cd1'), path_fanart) 496 | print(' >fanart.jpg复制成功') 497 | copyfile(path_poster.replace(str_cd, '-cd1'), path_poster) 498 | print(' >poster.jpg复制成功') 499 | except FileNotFoundError: 500 | pass 501 | # kodi或者emby需要的第一份图片 502 | if check_picture(path_fanart): 503 | # print(' >已有fanart.jpg') 504 | pass 505 | else: 506 | # 下载封面 507 | print(' >从javbus下载封面:', url_cover) 508 | try: 509 | download_pic(url_cover, path_fanart, proxy_bus) 510 | print(' >fanart.jpg下载成功') 511 | except: 512 | num_fail += 1 513 | record_fail(' >第' + str( 514 | num_fail) + '个失败!下载fanart.jpg失败:' + url_cover + ',' + path_relative + '\n') 515 | continue # 退出对该jav的整理 516 | # 裁剪生成 poster 517 | if check_picture(path_poster): 518 | # print(' >已有poster.jpg') 519 | pass 520 | elif settings.bool_face: 521 | crop_poster_baidu(path_fanart, path_poster, client) 522 | # 需要加上条纹 523 | if settings.bool_watermark_subtitle and bool_subtitle: 524 | add_watermark_subtitle(path_poster) 525 | else: 526 | crop_poster_default(path_fanart, path_poster, 1) 527 | if settings.bool_watermark_subtitle and bool_subtitle: 528 | add_watermark_subtitle(path_poster) 529 | 530 | # 6收集演员头像【相同】 531 | if settings.bool_sculpture and jav.episode == 1: 532 | if actors[0] == '有码演员': 533 | print(' >未知演员,无法收集头像') 534 | else: 535 | collect_sculpture(actors, jav.root) 536 | 537 | # 7归类影片,针对文件夹【相同】 538 | try: 539 | num_temp = classify_folder(jav, num_fail, settings, dict_data, list_classify_basis, root_classify, 540 | root, bool_separate_folder, num_all_episodes) 541 | num_fail = num_temp 542 | except FileExistsError: 543 | num_fail += 1 544 | continue 545 | 546 | except: 547 | num_fail += 1 548 | record_fail(' >第' + str( 549 | num_fail) + '个失败!发生错误,如一直在该影片报错请截图并联系作者:' + path_relative + '\n' + format_exc() + '\n') 550 | continue # 【退出对该jav的整理】 551 | 552 | # 完结撒花 553 | print('\n当前文件夹完成,', end='') 554 | if num_fail > 0: 555 | print('失败', num_fail, '个! ', root_choose, '\n') 556 | line = -1 557 | with open('【可删除】失败记录.txt', 'r', encoding="utf-8") as f: 558 | content = list(f) 559 | while 1: 560 | if content[line].startswith('已'): 561 | break 562 | line -= 1 563 | for i in range(line + 1, 0): 564 | print(content[i], end='') 565 | print('\n“【可删除】失败记录.txt”已记录错误\n') 566 | else: 567 | print(' “0”失败! ', root_choose, '\n') 568 | # os.system('pause') 569 | input_start_key = input('回车继续选择文件夹整理:') 570 | -------------------------------------------------------------------------------- /javsdt/JavbusYouma.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os, re 3 | from shutil import copyfile 4 | from traceback import format_exc 5 | ######################################################################################################################## 6 | from Class.Settings import Settings 7 | from Class.JavFile import JavFile 8 | from Functions.Status import judge_exist_nfo, judge_exist_extra_folders, count_num_videos 9 | from Functions.User import choose_directory 10 | from Functions.Record import record_start, record_fail, record_warn 11 | from Functions.Process import perfect_dict_data 12 | from Functions.Standard import rename_mp4, rename_folder, classify_files, classify_folder 13 | from Functions.XML import replace_xml, replace_xml_win 14 | from Functions.Process import judge_exist_subtitle 15 | from Functions.Picture import check_picture, add_watermark_subtitle 16 | from Functions.Requests.Download import download_pic 17 | from Functions.Genre import better_dict_genre 18 | # ################################################## 不同 ########################################################## 19 | from Functions.Process import judge_exist_divulge 20 | from Functions.Status import check_actors 21 | from Functions.Car import find_car_bus, list_suren_car 22 | from Functions.Standard import collect_sculpture 23 | from Functions.Baidu import translate 24 | from Functions.Picture import add_watermark_divulge, crop_poster_youma 25 | from Functions.Requests.JavbusReq import get_bus_html 26 | from Functions.Requests.ArzonReq import steal_arzon_cookies, find_plot_arzon 27 | 28 | 29 | # main开始 30 | print('1、避开21:00-1:00,访问javbus和arzon很慢。\n' 31 | '2、若一直打不开javbus,请在ini中更新防屏蔽网址\n') 32 | 33 | # 读取配置文件,这个ini文件用来给用户设置 34 | print('正在读取ini中的设置...', end='') 35 | try: 36 | settings = Settings('有码') 37 | except: 38 | settings = None 39 | print(format_exc()) 40 | print('\n无法读取ini文件,请修改它为正确格式,或者打开“【ini】重新创建ini.exe”创建全新的ini!') 41 | os.system('pause') 42 | print('\n读取ini文件成功!\n') 43 | 44 | 45 | # 路径分隔符:当前系统的路径分隔符 windows是“\”,linux和mac是“/” 46 | sep = os.sep 47 | 48 | # 检查头像:如果需要为kodi整理头像,先检查演员头像ini、头像文件夹是否存在。 49 | check_actors(settings.bool_sculpture) 50 | 51 | # 局部代理:哪些站点需要代理。 52 | proxy_library, proxy_bus, proxy_321, proxy_db, proxy_arzon, proxy_dmm = settings.get_proxy() 53 | 54 | # arzon通行证:如果需要在nfo中写入日语简介,需要先获得合法的arzon网站的cookie,用于通过成人验证。 55 | cookie_arzon = steal_arzon_cookies(proxy_arzon) if settings.bool_plot and settings.bool_nfo else {} 56 | 57 | # javbus网址 https://www.buscdn.work/ 58 | url_bus = settings.get_url_bus() 59 | 60 | # 选择简繁中文以及百度翻译账户:需要简体中文还是繁体中文,影响影片特征和简介。 61 | to_language, tran_id, tran_sk = settings.get_translate_account() 62 | 63 | # 信息字典:存放影片信息,用于给用户自定义各种命名。 64 | dict_data = {'车牌': 'ABC-123', 65 | '车牌前缀': 'ABC', 66 | '标题': '有码标题', 67 | '完整标题': '完整有码标题', 68 | '导演': '有码导演', 69 | '片商': '有码片商', 70 | '评分': '0', 71 | '片长': '0', 72 | '系列': '有码系列', 73 | '发行年月日': '1970-01-01', '发行年份': '1970', '月': '01', '日': '01', 74 | '首个演员': '有码演员', '全部演员': '有码演员', 75 | '空格': ' ', 76 | '\\': sep, '/': sep, # 文件路径分隔符 77 | '是否中字': '', 78 | '是否流出': '', 79 | '影片类型': settings.av_type(), 80 | '视频': 'ABC-123', # 当前及未来的视频文件名,不带ext 81 | '原文件名': 'ABC-123', '原文件夹名': 'ABC-123', } 82 | 83 | # nfo中title的写法。 84 | list_name_nfo_title = settings.formula_name_nfo_title() 85 | # 额外将哪些元素放入特征中 86 | list_extra_genres = settings.list_extra_genre() 87 | # 重命名视频的格式 88 | list_name_video = settings.formula_rename_video() 89 | # 重命名文件夹的格式 90 | list_name_folder = settings.formula_rename_folder() 91 | 92 | # fanart的格式 93 | list_name_fanart = settings.formula_name_fanart() 94 | # poster的格式 95 | list_name_poster = settings.formula_name_poster() 96 | 97 | # 视频文件名包含哪些多余的字母数字,需要无视 98 | list_surplus_words_in_filename = settings.list_surplus_word_in_filename('有码') 99 | # 文件名包含哪些特殊含义的文字,判断是否中字 100 | list_subtitle_words_in_filename = settings.list_subtitle_word_in_filename() 101 | # 文件名包含哪些特殊含义的文字,判断是否是无码流出片 102 | list_divulge_words_in_filename = settings.list_divulge_word_in_filename() 103 | 104 | # 素人番号:得到事先设置的素人番号,让程序能跳过它们 105 | list_suren_cars = list_suren_car() 106 | 107 | # 需要扫描的文件的类型 108 | tuple_video_types = settings.tuple_video_type() 109 | 110 | # 完善dict_data,如果用户自定义了一些文字,不在元素中,需要将它们添加进dict_data;list_classify_basis,归类标准,归类目标文件夹的组成公式。 111 | dict_data, list_classify_basis = perfect_dict_data(list_extra_genres, list_name_video, list_name_folder, list_name_nfo_title, list_name_fanart, list_name_poster, settings.custom_classify_basis(), dict_data) 112 | 113 | # 优化特征的字典 114 | dict_genre = better_dict_genre('Javbus有码', to_language) 115 | 116 | # 用户输入“回车”就继续选择文件夹整理 117 | input_start_key = '' 118 | while input_start_key == '': 119 | # 用户:选择需要整理的文件夹 120 | print('请选择要整理的文件夹:', end='') 121 | root_choose = choose_directory() 122 | print(root_choose) 123 | # 日志:在txt中记录一下用户的这次操作,在某个时间选择了某个文件夹 124 | record_start(root_choose) 125 | # 归类:用户自定义的归类根目录,如果不需要归类则为空 126 | root_classify = settings.check_classify_root(root_choose, sep) 127 | # 计数:失败次数及进度 128 | num_fail = 0 # 已经或可能导致致命错误,比如整理未完成,同车牌有不同视频 129 | num_warn = 0 # 对整理结果不致命的问题,比如找不到简介 130 | num_all_videos = count_num_videos(root_choose, tuple_video_types) # 所选文件夹总共有多少个视频文件 131 | num_current = 0 # 当前视频的编号 132 | print('...文件扫描开始...如果时间过长...请避开夜晚高峰期...\n') 133 | # root【当前根目录】 dirs【子文件夹】 files【文件】,root是str,后两个是list 134 | for root, dirs, files in os.walk(root_choose): 135 | # 什么文件都没有 136 | if not files: 137 | continue 138 | # 当前root是已归类的目录,无需处理 139 | if '归类完成' in root.replace(root_choose, ''): 140 | continue 141 | # 跳过已存在nfo的文件夹,判断这一层文件夹中有没有nfo 142 | if settings.bool_skip and judge_exist_nfo(files): 143 | continue 144 | # 对这一层文件夹进行评估,有多少视频,有多少同车牌视频,是不是独立文件夹 145 | list_jav_struct = [] # 存放:需要整理的jav的结构体 146 | dict_car_pref = {} # 存放:每一车牌的集数, 例如{'abp-123': 1, avop-789': 2}是指 abp-123只有一集,avop-789有cd1、cd2 147 | num_videos_include = 0 # 计数:当前文件夹中视频的数量,可能有视频不是jav 148 | dict_subtitle_files = {} # 存放:jav的字幕文件和车牌对应关系 {'c:\a\abc_123.srt': 'abc-123'} 149 | # 判断文件是不是字幕文件,放入dict_subtitle_files中 150 | for file_raw in files: 151 | file_temp = file_raw.upper() 152 | if file_temp.endswith(('.SRT', '.VTT', '.ASS', '.SSA', '.SUB', '.SMI',)): 153 | # 当前模式不处理FC2 154 | if 'FC2' in file_temp: 155 | continue 156 | # 去除用户设置的、干扰车牌的文字 157 | for word in list_surplus_words_in_filename: 158 | file_temp = file_temp.replace(word, '') 159 | # 得到字幕文件名中的车牌 160 | subtitle_car = find_car_bus(file_temp, list_suren_cars) 161 | # 将该字幕文件和其中的车牌对应到dict_subtitle_files中 162 | if subtitle_car: 163 | dict_subtitle_files[file_raw] = subtitle_car 164 | # print(dict_subtitle_files) 165 | # 判断文件是不是视频,放入list_jav_struct中 166 | for file_raw in files: 167 | file_temp = file_raw.upper() 168 | if file_temp.endswith(tuple_video_types) and not file_temp.startswith('.'): 169 | num_videos_include += 1 170 | num_current += 1 171 | if 'FC2' in file_temp: 172 | continue 173 | for word in list_surplus_words_in_filename: 174 | file_temp = file_temp.replace(word, '') 175 | # 得到视频中的车牌 176 | car = find_car_bus(file_temp, list_suren_cars) 177 | if car: 178 | try: 179 | dict_car_pref[car] += 1 # 已经有这个车牌了,加一集cd 180 | except KeyError: 181 | dict_car_pref[car] = 1 # 这个新车牌有了第一集 182 | # 这个车牌在dict_subtitle_files中,有它的字幕。 183 | if car in dict_subtitle_files.values(): 184 | subtitle_file = list(dict_subtitle_files.keys())[list(dict_subtitle_files.values()).index(car)] 185 | del dict_subtitle_files[subtitle_file] 186 | else: 187 | subtitle_file = '' 188 | # 将该jav的各种属性打包好,包括原文件名带扩展名、所在文件夹路径、第几集、所属字幕文件名 189 | jav_struct = JavFile(file_raw, root, car, dict_car_pref[car], subtitle_file, num_current) 190 | list_jav_struct.append(jav_struct) 191 | else: 192 | print('>>无法处理:', root.replace(root_choose, '') + sep + file_raw) 193 | 194 | # 判定影片所在文件夹是否是独立文件夹,独立文件夹是指该文件夹仅用来存放该影片,而不是大杂烩文件夹 195 | # 这一层文件夹下有jav 196 | if dict_car_pref: 197 | # 当前文件夹下,车牌不止一个;还有其他非jav视频;有其他文件夹,除了演员头像文件夹“.actors”和额外剧照文件夹“extrafanart”; 198 | if len(dict_car_pref) > 1 or num_videos_include > len(list_jav_struct) or judge_exist_extra_folders(dirs): 199 | bool_separate_folder = False # 不是独立的文件夹 200 | else: 201 | bool_separate_folder = True # 这一层文件夹是这部jav的独立文件夹 202 | else: 203 | continue 204 | 205 | # 开始处理每一部jav 206 | for jav in list_jav_struct: 207 | # 告诉用户进度 208 | print('>> [' + str(jav.number) + '/' + str(num_all_videos) + ']:', jav.name) 209 | print(' >发现车牌:', jav.car) 210 | 211 | # 判断是否有中字的特征,条件有三满足其一即可:1有外挂字幕 2文件名中含有“-C”之类的字眼 3旧的nfo中已经记录了它的中字特征 212 | if jav.subtitle: 213 | bool_subtitle = True # 判定成功 214 | dict_data['是否中字'] = settings.custom_subtitle_expression # '是否中字'这一命名元素被激活 215 | else: 216 | bool_subtitle = judge_exist_subtitle(root, jav.name_no_ext, list_subtitle_words_in_filename) 217 | dict_data['是否中字'] = settings.custom_subtitle_expression if bool_subtitle else '' 218 | # 判断是否是无码流出的作品,同理 219 | bool_divulge = judge_exist_divulge(root, jav.name_no_ext, list_divulge_words_in_filename) 220 | dict_data['是否流出'] = settings.custom_divulge_expression if bool_divulge else '' 221 | 222 | # 影片的相对于所选文件夹的路径,用于报错 223 | path_relative = sep + jav.path.replace(root_choose, '') 224 | 225 | # 获取nfo信息的javbus网页 226 | try: 227 | # 用户指定了网址,则直接得到jav所在网址 228 | if '公交车' in jav.name: 229 | url_appointg = re.search(r'公交车(.+?)\.', jav.name) 230 | if str(url_appointg) != 'None': 231 | url_on_web = url_bus + url_appointg.group(1) 232 | else: 233 | num_fail += 1 234 | record_fail(' >第' + str(num_fail) + '个失败!你指定的javbus网址有错误:' + path_relative + '\n') 235 | continue # 【退出对该jav的整理】 236 | # 用户没有指定网址,则去搜索 237 | else: 238 | url_search_web = url_bus + 'search/' + jav.car + '&type=1&parent=ce' 239 | print(' >搜索车牌:', url_search_web) 240 | # 得到javbus搜索网页html 241 | html_web = get_bus_html(url_search_web, proxy_bus) 242 | # 尝试找movie-box 243 | list_search_results = re.findall(r'movie-box" href="(.+?)">', html_web) # 匹配处理“标题” 244 | if list_search_results: # 搜索页面有结果 245 | # print(list_search_results) 246 | # print(' >正在核查搜索结果...') 247 | jav_pref = jav.car.split('-')[0] # 匹配车牌的前缀字母 248 | jav_suf = jav.car.split('-')[-1].lstrip('0') # 当前车牌的后缀数字 去除多余的0 249 | list_fit_results = [] # 存放,车牌符合的结果 250 | for i in list_search_results: 251 | url_end = i.split('/')[-1].upper() 252 | url_suf = re.search(r'[-_](\d+)', url_end).group(1).lstrip('0') # 匹配box上影片url,车牌的后缀数字,去除多余的0 253 | if jav_suf == url_suf: # 数字相同 254 | url_pref = re.search(r'([A-Z0-9]+)[-_]', url_end).group(1).upper() # 匹配处理url所带车牌前面的字母“n” 255 | if jav_pref == url_pref: # 数字相同的基础下,字母也相同,即可能车牌相同 256 | list_fit_results.append(i) 257 | # 有码搜索的结果一个都匹配不上 258 | if not list_fit_results: 259 | num_fail += 1 260 | record_fail(' >第' + str( 261 | num_fail) + '个失败!javbus有码找不到该车牌的信息:' + jav.car + ',' + path_relative + '\n') 262 | continue # 【跳出对该jav的整理】 263 | # 默认用第一个搜索结果 264 | url_on_web = list_fit_results[0] 265 | if len(list_fit_results) > 1: 266 | num_fail += 1 267 | record_fail(' >第' + str( 268 | num_fail) + '个警告!javbus搜索到同车牌的不同视频:' + jav.car + ',' + path_relative + '\n') 269 | # 找不到box 270 | else: 271 | num_fail += 1 272 | record_fail(' >第' + str( 273 | num_fail) + '个失败!javbus有码找不到该车牌的信息:' + jav.car + ',' + path_relative + '\n') 274 | continue # 【跳出对该jav的整理】 275 | # 经过上面的三种情况,可能找到了jav在bus上的网页链接url_on_web 276 | print(' >获取信息:', url_on_web) 277 | # 得到最终的jav所在网页 278 | html_web = get_bus_html(url_on_web, proxy_bus) 279 | 280 | # 开始匹配信息 281 | # 有大部分信息的html_web 282 | html_web = re.search(r'(h3>[\s\S]*?)磁力連結投稿', html_web, re.DOTALL).group(1) 283 | # 标题 284 | title = re.search(r'h3>(.+?) \/:*?"<>| 286 | title = replace_xml_win(title) 287 | print(' >影片标题:', title) 288 | # 正则匹配 影片信息 开始! 289 | # title的开头是车牌号,想要后面的纯标题 290 | car_titleg = re.search(r'(.+?) (.+)', title) 291 | # 车牌号 292 | dict_data['车牌'] = car = car_titleg.group(1) 293 | dict_data['车牌前缀'] = car.split('-')[0] 294 | # 给用户重命名用的标题是“短标题”,nfo中是“完整标题”,但用户在ini中只用写“标题” 295 | title_only = car_titleg.group(2) 296 | # DVD封面cover 297 | coverg = re.search(r'bigImage" href="(.+?)">', html_web) # 封面图片的正则对象 298 | if str(coverg) != 'None': 299 | url_cover = url_bus + coverg.group(1) 300 | else: 301 | url_cover = '' 302 | # 发行日期 303 | premieredg = re.search(r'發行日期: (.+?)

', html_web) 304 | if str(premieredg) != 'None': 305 | dict_data['发行年月日'] = time_premiered = premieredg.group(1) 306 | dict_data['发行年份'] = time_premiered[0:4] 307 | dict_data['月'] = time_premiered[5:7] 308 | dict_data['日'] = time_premiered[8:10] 309 | else: 310 | dict_data['发行年月日'] = time_premiered = '1970-01-01' 311 | dict_data['发行年份'] = '1970' 312 | dict_data['月'] = '01' 313 | dict_data['日'] = '01' 314 | # 片长 150 分钟 315 | runtimeg = re.search(r'長度: (.+?)分鐘

', html_web) 316 | if str(runtimeg) != 'None': 317 | dict_data['片长'] = runtimeg.group(1) 318 | else: 319 | dict_data['片长'] = '0' 320 | # 导演 321 | directorg = re.search(r'導演: (.+?)<', html_web) 322 | if str(directorg) != 'None': 323 | dict_data['导演'] = replace_xml_win(directorg.group(1)) 324 | else: 325 | dict_data['导演'] = '有码导演' 326 | # 片商 制作商 327 | studiog = re.search(r'製作商: (.+?)', html_web) 328 | if str(studiog) != 'None': 329 | dict_data['片商'] = studio = replace_xml_win(studiog.group(1)) 330 | else: 331 | dict_data['片商'] = '有码片商' 332 | studio = '' 333 | # 系列: 悪質シロウトナンパ 334 | seriesg = re.search(r'系列: (.+?)', html_web) # 封面图片的正则对象 335 | if str(seriesg) != 'None': 336 | dict_data['系列'] = series = seriesg.group(1).replace(sep, '#') 337 | else: 338 | dict_data['系列'] = '有码系列' 339 | series = '' 340 | # 演员们 和 # 第一个演员 341 | actors = re.findall(r'star/.+?">', html_web) 342 | if actors: 343 | if len(actors) > 7: 344 | dict_data['全部演员'] = ' '.join(actors[:7]) 345 | else: 346 | dict_data['全部演员'] = ' '.join(actors) 347 | dict_data['首个演员'] = actors[0] 348 | # 有些用户需要删去 标题 末尾可能存在的 演员姓名 349 | if settings.bool_strip_actors and title_only.endswith(dict_data['全部演员']): 350 | title_only = title_only[:-len(dict_data['全部演员'])].rstrip() 351 | else: 352 | actors = ['有码演员'] 353 | dict_data['首个演员'] = dict_data['全部演员'] = '有码演员' 354 | # 处理影片的标题过长 355 | dict_data['完整标题'] = title_only 356 | if len(title_only) > settings.int_title_len: 357 | dict_data['标题'] = title_only[:settings.int_title_len] 358 | else: 359 | dict_data['标题'] = title_only 360 | # 特点 361 | genres = re.findall(r'genre">', html_web) 362 | if bool_subtitle: # 有“中字“,加上特征”中文字幕” 363 | genres.append('中文字幕') 364 | if bool_divulge: # 是流出无码片,加上特征'无码流出' 365 | genres.append('无码流出') 366 | try: 367 | genres = [dict_genre[i] for i in genres if dict_genre[i] != '删除'] 368 | except KeyError as error: 369 | num_fail += 1 370 | record_fail(' >第' + str(num_fail) + '个失败!发现新的特征需要添加至【特征对照表】:' + str(error) + '\n') 371 | continue 372 | # print(genres) 373 | # arzon的简介 ######################################################### 374 | # 去arzon找简介 375 | if settings.bool_nfo and settings.bool_plot and jav.episode == 1: 376 | plot, status_arzon, acook = find_plot_arzon(car, cookie_arzon, proxy_arzon) 377 | if status_arzon == 0: 378 | pass 379 | elif status_arzon == 1: 380 | num_warn += 1 381 | record_warn(' >第' + str(num_warn) + '个失败!找不到简介,尽管arzon上有搜索结果:' + path_relative + '\n') 382 | else: 383 | num_warn += 1 384 | record_warn(' >第' + str(num_warn) + '个失败!找不到简介,影片被arzon下架:' + path_relative + '\n') 385 | # 需要翻译简介 386 | if settings.bool_tran: 387 | plot = translate(tran_id, tran_sk, plot, to_language) 388 | if plot.startswith('【百度'): 389 | num_fail += 1 390 | record_fail(' >第' + str(num_fail) + '个失败!翻译简介失败:' + path_relative + '\n') 391 | # 去除xml文档不允许的特殊字符 &<> \/:*?"<>| 392 | plot = replace_xml(plot) 393 | # print(plot) 394 | else: 395 | plot = '' 396 | ####################################################################### 397 | dict_data['视频'] = dict_data['原文件名'] = jav.name_no_ext # dict_data['视频'],先定义为原文件名,即将发生变化。 398 | dict_data['原文件夹名'] = jav.folder 399 | # 是CD1还是CDn? 400 | num_all_episodes = dict_car_pref[jav.car] # 该车牌总共多少集 401 | if num_all_episodes > 1: 402 | str_cd = '-cd' + str(jav.episode) 403 | else: 404 | str_cd = '' 405 | 406 | # 1重命名视频【相同】 407 | try: 408 | dict_data, jav, num_temp = rename_mp4(jav, num_fail, settings, dict_data, list_name_video, 409 | path_relative, str_cd) 410 | num_fail = num_temp 411 | except FileExistsError: 412 | num_fail += 1 413 | continue 414 | 415 | # 2 归类影片【相同】只针对视频文件和字幕文件。注意:第2操作和下面(第3操作+第7操作)互斥,只能执行第2操作或(第3操作+第7操作),归类影片是针对“文件”还是“文件夹”。 416 | try: 417 | jav, num_temp = classify_files(jav, num_fail, settings, dict_data, list_classify_basis, 418 | root_classify) 419 | num_fail = num_temp 420 | except FileExistsError: 421 | num_fail += 1 422 | continue 423 | 424 | # 3重命名文件夹【相同】如果是针对“文件”归类,这一步会被跳过。 因为用户只需要归类视频文件,不需要管文件夹。 425 | try: 426 | jav, num_temp = rename_folder(jav, num_fail, settings, dict_data, list_name_folder, 427 | bool_separate_folder, num_all_episodes) 428 | num_fail = num_temp 429 | except FileExistsError: 430 | num_fail += 1 431 | continue 432 | 433 | # 更新一下path_relative 434 | path_relative = sep + jav.path.replace(root_choose, '') # 影片的相对于所选文件夹的路径,用于报错 435 | 436 | # 4写入nfo【独特】 437 | if settings.bool_nfo: 438 | if settings.bool_cd_only: 439 | path_nfo = jav.root + sep + jav.name_no_ext.replace(str_cd, '') + '.nfo' 440 | else: 441 | path_nfo = jav.root + sep + jav.name_no_ext + '.nfo' 442 | title_in_nfo = '' 443 | for i in list_name_nfo_title: 444 | title_in_nfo += dict_data[i] # nfo中tilte的写法 445 | # 开始写入nfo,这nfo格式是参考的kodi的nfo 446 | f = open(path_nfo, 'w', encoding="utf-8") 447 | f.write("\n" 448 | "\n" 449 | " " + plot + "\n" 450 | " " + title_in_nfo + "\n" 451 | " " + title + "\n" 452 | " " + dict_data['导演'] + "\n" 453 | " " + dict_data['发行年份'] + "\n" 454 | " NC-17\n" 455 | " NC-17\n" 456 | " JP\n" 457 | " " + time_premiered + "\n" 458 | " " + time_premiered + "\n" 459 | " " + dict_data['片长'] + "\n" 460 | " 日本\n" 461 | " " + studio + "\n" 462 | " " + car + "\n" 463 | " " + car + "\n" 464 | " " + series + "\n") # emby不管set系列,kodi可以 465 | # 需要将特征写入genre 466 | if settings.bool_genre: 467 | for i in genres: 468 | f.write(" " + i + "\n") 469 | if settings.bool_write_series and series: 470 | f.write(" 系列:" + series + "\n") 471 | if settings.bool_write_studio and studio: 472 | f.write(" 片商:" + studio + "\n") 473 | if list_extra_genres: 474 | for i in list_extra_genres: 475 | f.write(" " + dict_data[i] + "\n") 476 | # 需要将特征写入tag 477 | if settings.bool_tag: 478 | for i in genres: 479 | f.write(" " + i + "\n") 480 | if settings.bool_write_series and series: 481 | f.write(" 系列:" + series + "\n") 482 | if settings.bool_write_studio and studio: 483 | f.write(" 片商:" + studio + "\n") 484 | if list_extra_genres: 485 | for i in list_extra_genres: 486 | f.write(" " + dict_data[i] + "\n") 487 | # 写入演员 488 | for i in actors: 489 | f.write(" \n " + i + "\n Actor\n \n") 490 | f.write("\n") 491 | f.close() 492 | print(' >nfo收集完成') 493 | 494 | # 5需要两张封面图片【独特】 495 | if settings.bool_jpg: 496 | # fanart和poster路径 497 | path_fanart = jav.root + sep 498 | path_poster = jav.root + sep 499 | for i in list_name_fanart: 500 | path_fanart += dict_data[i] 501 | for i in list_name_poster: 502 | path_poster += dict_data[i] 503 | # print(path_fanart) 504 | # kodi只需要一份图片,图片路径唯一 505 | if settings.bool_cd_only: 506 | path_fanart = path_fanart.replace(str_cd, '') 507 | path_poster = path_poster.replace(str_cd, '') 508 | # emby需要多份,现在不是第一集,直接复制第一集的图片 509 | elif jav.episode != 1: 510 | try: 511 | copyfile(path_fanart.replace(str_cd, '-cd1'), path_fanart) 512 | print(' >fanart.jpg复制成功') 513 | copyfile(path_poster.replace(str_cd, '-cd1'), path_poster) 514 | print(' >poster.jpg复制成功') 515 | except FileNotFoundError: 516 | pass 517 | # kodi或者emby需要的第一份图片 518 | if check_picture(path_fanart): 519 | # print(' >已有fanart.jpg') 520 | pass 521 | else: 522 | # 下载封面 523 | print(' >从javbus下载封面:', url_cover) 524 | try: 525 | download_pic(url_cover, path_fanart, proxy_bus) 526 | print(' >fanart.jpg下载成功') 527 | except: 528 | num_fail += 1 529 | record_fail(' >第' + str( 530 | num_fail) + '个失败!下载fanart.jpg失败:' + url_cover + ',' + path_relative + '\n') 531 | continue # 退出对该jav的整理 532 | # 裁剪生成 poster 533 | if check_picture(path_poster): 534 | # print(' >已有poster.jpg') 535 | pass 536 | else: 537 | crop_poster_youma(path_fanart, path_poster) 538 | # 需要加上条纹 539 | if settings.bool_watermark_subtitle and bool_subtitle: 540 | add_watermark_subtitle(path_poster) 541 | if settings.bool_watermark_divulge and bool_divulge: 542 | add_watermark_divulge(path_poster) 543 | 544 | # 6收集演员头像【相同】 545 | if settings.bool_sculpture and jav.episode == 1: 546 | if actors[0] == '有码演员': 547 | print(' >未知演员,无法收集头像') 548 | else: 549 | collect_sculpture(actors, jav.root) 550 | 551 | # 7归类影片,针对文件夹【相同】 552 | try: 553 | num_temp = classify_folder(jav, num_fail, settings, dict_data, list_classify_basis, root_classify, 554 | root, bool_separate_folder, num_all_episodes) 555 | num_fail = num_temp 556 | except FileExistsError: 557 | num_fail += 1 558 | continue 559 | 560 | except: 561 | num_fail += 1 562 | record_fail(' >第' + str(num_fail) + '个失败!发生错误,如一直在该影片报错请截图并联系作者:' + path_relative + '\n' + format_exc() + '\n') 563 | continue # 【退出对该jav的整理】 564 | 565 | # 完结撒花 566 | print('\n当前文件夹完成,', end='') 567 | if num_fail > 0: 568 | print('失败', num_fail, '个! ', root_choose, '\n') 569 | line = -1 570 | with open('【可删除】失败记录.txt', 'r', encoding="utf-8") as f: 571 | content = list(f) 572 | while 1: 573 | if content[line].startswith('已'): 574 | break 575 | line -= 1 576 | for i in range(line+1, 0): 577 | print(content[i], end='') 578 | print('\n“【可删除】失败记录.txt”已记录错误\n') 579 | else: 580 | print(' “0”失败! ', root_choose, '\n') 581 | if num_warn > 0: 582 | print('“警告信息.txt”还记录了', num_warn, '个警告信息!\n') 583 | # os.system('pause') 584 | input_start_key = input('回车继续选择文件夹整理:') 585 | -------------------------------------------------------------------------------- /javsdt/JavdbFc2.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import os, re 3 | from shutil import copyfile 4 | from traceback import format_exc 5 | ######################################################################################################################## 6 | from Class.Settings import Settings 7 | from Class.JavFile import JavFile 8 | from Functions.Status import judge_exist_nfo, judge_exist_extra_folders, count_num_videos 9 | from Functions.User import choose_directory 10 | from Functions.Record import record_start, record_fail 11 | from Functions.Process import perfect_dict_data 12 | from Functions.Standard import rename_mp4, rename_folder, classify_files, classify_folder 13 | from Functions.XML import replace_xml_win 14 | from Functions.Process import judge_exist_subtitle 15 | from Functions.Picture import check_picture, add_watermark_subtitle 16 | from Functions.Requests.Download import download_pic 17 | from Functions.Genre import better_dict_genre 18 | # ################################################## 不同 ########################################################## 19 | from Functions.Process import judge_exist_divulge 20 | from Functions.Status import check_actors 21 | from Functions.Picture import add_watermark_divulge, crop_poster_baidu, crop_poster_default 22 | from Functions.Requests.JavdbReq import get_db_html, get_search_db_html 23 | 24 | 25 | # main开始 26 | print('1、若一直连不上javdb,请在ini中更新防屏蔽网址\n' 27 | '2、javdb限制搜索次数,5分钟只能搜索12次左右,然后睡眠5分钟,建议挂机整理!\n' 28 | ' 如果刚启动整理就睡眠,请检查当前网络环境能否访问javdb!\n' 29 | '4、整理FC2,fc2的信息非常非常少,大概只有个标题、卖家(片商)\n') 30 | 31 | # 读取配置文件,这个ini文件用来给用户设置 32 | print('正在读取ini中的设置...', end='') 33 | try: 34 | settings = Settings('fc2') 35 | except: 36 | settings = None 37 | print(format_exc()) 38 | print('\n无法读取ini文件,请修改它为正确格式,或者打开“【ini】重新创建ini.exe”创建全新的ini!') 39 | os.system('pause') 40 | print('\n读取ini文件成功!\n') 41 | 42 | 43 | print('进入javdb网站 => 点击主页上的FC2,登录账号 => 键盘按“F12”再按“F5” => 点击“fc2”,复制cfduid和jdb_session') 44 | print('需要看护值守,及时更新它们') 45 | cfduid = input('请粘贴cfduid:') 46 | jdb_session = input('请粘贴jdb_session:') 47 | cookies = { 48 | "__cfduid": cfduid, 49 | "_jdb_session": jdb_session, 50 | } 51 | 52 | # 路径分隔符:当前系统的路径分隔符 windows是“\”,linux和mac是“/” 53 | sep = os.sep 54 | 55 | # 检查头像:如果需要为kodi整理头像,先检查演员头像ini、头像文件夹是否存在。 56 | check_actors(settings.bool_sculpture) 57 | 58 | # 局部代理:哪些站点需要代理。 59 | proxy_library, proxy_bus, proxy_321, proxy_db, proxy_arzon, proxy_dmm = settings.get_proxy() 60 | 61 | # javdb网址: 62 | url_db = settings.get_url_db() 63 | 64 | # 选择简繁中文以及百度翻译账户:需要简体中文还是繁体中文,影响影片特征和简介。 65 | to_language, tran_id, tran_sk = settings.get_translate_account() 66 | 67 | # 信息字典:存放影片信息,用于给用户自定义各种命名。 68 | dict_data = {'车牌': 'FC2-123', 69 | '车牌前缀': 'FC2', 70 | '标题': 'FC2标题', 71 | '完整标题': '完整FC2标题', 72 | '导演': 'FC2导演', 73 | '片商': 'FC2片商', 74 | '评分': '0', 75 | '片长': '0', 76 | '系列': 'FC2系列', 77 | '发行年月日': '1970-01-01', '发行年份': '1970', '月': '01', '日': '01', 78 | '首个演员': 'FC2演员', '全部演员': 'FC2演员', 79 | '空格': ' ', 80 | '\\': sep, '/': sep, # 文件路径分隔符 81 | '是否中字': '', 82 | '是否流出': '', 83 | '影片类型': settings.av_type(), 84 | '视频': 'FC2-123', # 当前及未来的视频文件名,不带ext 85 | '原文件名': 'FC2-123', '原文件夹名': 'FC2-123', } 86 | 87 | # nfo中title的写法。 88 | list_name_nfo_title = settings.formula_name_nfo_title() 89 | # 额外将哪些元素放入特征中 90 | list_extra_genres = settings.list_extra_genre() 91 | # 重命名视频的格式 92 | list_name_video = settings.formula_rename_video() 93 | # 重命名文件夹的格式 94 | list_name_folder = settings.formula_rename_folder() 95 | 96 | # fanart的格式 97 | list_name_fanart = settings.formula_name_fanart() 98 | # poster的格式 99 | list_name_poster = settings.formula_name_poster() 100 | 101 | # 视频文件名包含哪些多余的字母数字,需要无视 102 | list_surplus_words_in_filename = settings.list_surplus_word_in_filename('有码') 103 | # 文件名包含哪些特殊含义的文字,判断是否中字 104 | list_subtitle_words_in_filename = settings.list_subtitle_word_in_filename() 105 | # 文件名包含哪些特殊含义的文字,判断是否是无码流出片 106 | list_divulge_words_in_filename = settings.list_divulge_word_in_filename() 107 | 108 | # 需要扫描的文件的类型 109 | tuple_video_types = settings.tuple_video_type() 110 | 111 | # 完善dict_data,如果用户自定义了一些文字,不在元素中,需要将它们添加进dict_data;list_classify_basis,归类标准,归类目标文件夹的组成公式。 112 | dict_data, list_classify_basis= perfect_dict_data(list_extra_genres, list_name_video, list_name_folder, list_name_nfo_title, list_name_fanart, list_name_poster, settings.custom_classify_basis(), dict_data) 113 | 114 | # 准备工作:使用人体分析,与百度al建立联系 115 | client = settings.start_body_analysis() 116 | 117 | # 优化特征的字典 118 | dict_genre = better_dict_genre('JavdbFc2', to_language) 119 | 120 | # 用户输入“回车”就继续选择文件夹整理 121 | input_start_key = '' 122 | while input_start_key == '': 123 | # 用户:选择需要整理的文件夹 124 | print('请选择要整理的文件夹:', end='') 125 | root_choose = choose_directory() 126 | print(root_choose) 127 | # 日志:在txt中记录一下用户的这次操作,在某个时间选择了某个文件夹 128 | record_start(root_choose) 129 | # 归类:用户自定义的归类根目录,如果不需要归类则为空 130 | root_classify = settings.check_classify_root(root_choose, sep) 131 | # 计数:失败次数及进度 132 | num_fail = 0 # 已经或可能导致致命错误,比如整理未完成,同车牌有不同视频 133 | num_all_videos = count_num_videos(root_choose, tuple_video_types) # 所选文件夹总共有多少个视频文件 134 | num_current = 0 # 当前视频的编号 135 | print('...文件扫描开始...如果时间过长...请避开夜晚高峰期...\n') 136 | # root【当前根目录】 dirs【子文件夹】 files【文件】,root是str,后两个是list 137 | for root, dirs, files in os.walk(root_choose): 138 | # 什么文件都没有 139 | if not files: 140 | continue 141 | # 当前root是已归类的目录,无需处理 142 | if '归类完成' in root.replace(root_choose, ''): 143 | continue 144 | # 跳过已存在nfo的文件夹,判断这一层文件夹中有没有nfo 145 | if settings.bool_skip and judge_exist_nfo(files): 146 | continue 147 | # 对这一层文件夹进行评估,有多少视频,有多少同车牌视频,是不是独立文件夹 148 | list_jav_struct = [] # 存放:需要整理的jav的结构体 149 | dict_car_pref = {} # 存放:每一车牌的集数, 例如{'abp-123': 1, avop-789': 2}是指 abp-123只有一集,avop-789有cd1、cd2 150 | num_videos_include = 0 # 计数:当前文件夹中视频的数量,可能有视频不是jav 151 | dict_subtitle_files = {} # 存放:jav的字幕文件和车牌对应关系 {'c:\a\abc_123.srt': 'abc-123'} 152 | # 判断文件是不是字幕文件,放入dict_subtitle_files中 153 | for file_raw in files: 154 | file_temp = file_raw.upper() 155 | if file_temp.endswith(('.SRT', '.VTT', '.ASS', '.SSA', '.SUB', '.SMI',)): 156 | # 仅处理fc2 157 | if 'FC2' not in file_temp: 158 | continue # 【跳出2】 159 | subtitle_carg = re.search(r'FC2[^\d]*(\d+)', file_temp) # 匹配字幕车牌 160 | if str(subtitle_carg) != 'None': 161 | subtitle_car = 'FC2-' + subtitle_carg.group(1) 162 | dict_subtitle_files[file_raw] = subtitle_car 163 | # print(dict_subtitle_files) 164 | # 判断文件是不是视频,放入list_jav_struct中 165 | for file_raw in files: 166 | file_temp = file_raw.upper() 167 | if file_temp.endswith(tuple_video_types) and not file_temp.startswith('.'): 168 | num_videos_include += 1 169 | num_current += 1 170 | # 仅处理fc2 171 | if 'FC2' not in file_temp: 172 | # print('>>无法处理:' + root.replace(root_choose, '') + sep + file_raw) 173 | continue # 【跳出2】 174 | video_numg = re.search(r'FC2[^\d]*(\d+)', file_temp) # 匹配视频车牌 175 | if str(video_numg) != 'None': 176 | car = 'FC2-' + video_numg.group(1) 177 | # 这个车牌有几集? 178 | try: 179 | dict_car_pref[car] += 1 # 已经有这个车牌了,加一集cd 180 | except KeyError: 181 | dict_car_pref[car] = 1 # 这个新车牌有了第一集 182 | # 这个车牌在dict_subtitle_files中,有它的字幕。 183 | if car in dict_subtitle_files.values(): 184 | subtitle_file = list(dict_subtitle_files.keys())[list(dict_subtitle_files.values()).index(car)] 185 | del dict_subtitle_files[subtitle_file] 186 | else: 187 | subtitle_file = '' 188 | # 将该jav的各种属性打包好,包括原文件名带扩展名、所在文件夹路径、第几集、所属字幕文件名 189 | jav_struct = JavFile(file_raw, root, car, dict_car_pref[car], subtitle_file, num_current) 190 | list_jav_struct.append(jav_struct) 191 | else: 192 | print('>>无法处理:', root.replace(root_choose, '') + sep + file_raw) 193 | 194 | # 判定影片所在文件夹是否是独立文件夹,独立文件夹是指该文件夹仅用来存放该影片,而不是大杂烩文件夹 195 | # 这一层文件夹下有jav 196 | if dict_car_pref: 197 | # 当前文件夹下,车牌不止一个;还有其他非jav视频;有其他文件夹,除了演员头像文件夹“.actors”和额外剧照文件夹“extrafanart”; 198 | if len(dict_car_pref) > 1 or num_videos_include > len(list_jav_struct) or judge_exist_extra_folders(dirs): 199 | bool_separate_folder = False # 不是独立的文件夹 200 | else: 201 | bool_separate_folder = True # 这一层文件夹是这部jav的独立文件夹 202 | else: 203 | continue 204 | 205 | # 开始处理每一部jav 206 | for jav in list_jav_struct: 207 | # 告诉用户进度 208 | print('>> [' + str(jav.number) + '/' + str(num_all_videos) + ']:', jav.name) 209 | print(' >发现车牌:', jav.car) 210 | 211 | # 判断是否有中字的特征,条件有三满足其一即可:1有外挂字幕 2文件名中含有“-C”之类的字眼 3旧的nfo中已经记录了它的中字特征 212 | if jav.subtitle: 213 | bool_subtitle = True # 判定成功 214 | dict_data['是否中字'] = settings.custom_subtitle_expression # '是否中字'这一命名元素被激活 215 | else: 216 | bool_subtitle = judge_exist_subtitle(root, jav.name_no_ext, list_subtitle_words_in_filename) 217 | dict_data['是否中字'] = settings.custom_subtitle_expression if bool_subtitle else '' 218 | # 判断是否是无码流出的作品,同理 219 | bool_divulge = judge_exist_divulge(root, jav.name_no_ext, list_divulge_words_in_filename) 220 | dict_data['是否流出'] = settings.custom_divulge_expression if bool_divulge else '' 221 | 222 | # 影片的相对于所选文件夹的路径,用于报错 223 | path_relative = sep + jav.path.replace(root_choose, '') 224 | 225 | # 获取nfo信息的javdb网页 226 | try: 227 | # 用户指定了网址,则直接得到jav所在网址 228 | if '图书馆' in jav.name: 229 | url_appointg = re.search(r'仓库(.+?)\.', jav.name) 230 | if str(url_appointg) != 'None': 231 | url_on_web = url_db + 'v/' + url_appointg.group(1) 232 | else: 233 | num_fail += 1 234 | record_fail(' >第' + str(num_fail) + '个失败!你指定的javdb网址有错误:' + path_relative + '\n') 235 | continue # 【退出对该jav的整理】 236 | # 用户没有指定网址,则去搜索 237 | else: 238 | url_search_web = url_db + 'search?q=' + jav.car + '&f=all' 239 | print(' >搜索车牌:', url_search_web) 240 | # 得到javdb搜索网页html 241 | html_web, cookies = get_search_db_html(url_search_web, cookies, proxy_db) 242 | # 尝试找movie-box 0链接 1车牌 243 | list_search_results = re.findall(r'href="/v/(.+?)" class="box" title=".+?"[\s\S]*?uid">(.+?)', html_web, re.DOTALL) # 匹配处理“标题” 244 | # print(list_search_results) 245 | if list_search_results: # 搜索结果页面只有一个box 246 | # print(list_search_results) 247 | # print(' >正在核查搜索结果...') 248 | jav_pref = jav.car.split('-')[0] # 匹配车牌的前缀字母 249 | jav_suf = jav.car.split('-')[-1].lstrip('0') # 当前车牌的后缀数字 去除多余的0 250 | list_fit_results = [] 251 | for i in list_search_results: 252 | url_num = i[1].upper() 253 | url_suf = re.search(r'(\d\d+)', url_num).group(1).lstrip('0') # 匹配box上影片url,车牌的后缀数字,去除多余的0 254 | # print('url后缀:', url_suf) 255 | if jav_suf == url_suf: # 数字相同 256 | url_pref = re.search(r'([A-Z]+2?)', url_num).group(1).upper() # 匹配处理url所带车牌前面的字母“n” 257 | # print('url前缀:', url_suf) 258 | if jav_pref == url_pref: # 数字相同的基础下,字母也相同,即可能车牌相同 259 | list_fit_results.append(i) 260 | else: 261 | continue # 【退出对该jav的整理】 262 | # 搜索结果一个都匹配不上 263 | if not list_fit_results: 264 | num_fail += 1 265 | record_fail(' >第' + str( 266 | num_fail) + '个失败!javdb找不到该车牌的信息:' + jav.car + ',' + path_relative + '\n') 267 | # print(html_web) 268 | continue # 【退出对该jav的整理】 269 | # 默认用第一个搜索结果 270 | url_on_web = url_db + 'v/' + list_fit_results[0][0] 271 | if len(list_fit_results) > 1: 272 | num_fail += 1 273 | record_fail(' >第' + str( 274 | num_fail) + '个警告!javdb搜索到同车牌的不同视频:' + jav.car + ',' + path_relative + '\n') 275 | # 找不到box 276 | else: 277 | num_fail += 1 278 | record_fail(' >第' + str( 279 | num_fail) + '个失败!javdb找不到该车牌的信息:' + jav.car + ',' + path_relative + '\n') 280 | continue # 【跳出对该jav的整理】 281 | # 经过上面的三种情况,可能找到了jav在web上的网页链接url_on_web 282 | print(' >获取信息:', url_on_web) 283 | # 得到最终的jav所在网页 284 | html_web, cookies = get_db_html(url_on_web, cookies, proxy_db) 285 | html = html_web 286 | # print(html_web) 287 | 288 | # 有大部分信息的html_web 289 | html_web = re.search(r'h2 class([\s\S]*?)想看', html_web, re.DOTALL).group(1) 290 | # print(html_web) 291 | # 标题 292 | title = re.search(r'strong>(.+?) \/:*?"<>| 294 | title = replace_xml_win(title) 295 | print(' >影片标题:', title) 296 | # title的开头是车牌号,想要后面的纯标题 297 | car_titleg = re.search(r'(.+?) (.+)', title) 298 | # 车牌号 299 | dict_data['车牌'] = car = car_titleg.group(1) 300 | # 给用户重命名用的标题是“短标题”,nfo中是“完整标题”,但用户在ini中只用写“标题” 301 | title_only = car_titleg.group(2) 302 | dict_data['完整标题'] = title_only 303 | # 处理影片的标题过长 304 | if len(title_only) > settings.int_title_len: 305 | dict_data['标题'] = title_only[:settings.int_title_len] 306 | else: 307 | dict_data['标题'] = title_only 308 | # DVD封面cover 309 | coverg = re.search(r'img src="(.+?)"', html_web) # 封面图片的正则对象 310 | if str(coverg) != 'None': 311 | url_cover = coverg.group(1) 312 | else: 313 | url_cover = '' 314 | # 发行日期 315 | premieredg = re.search(r'(\d\d\d\d-\d\d-\d\d)', html_web) 316 | if str(premieredg) != 'None': 317 | dict_data['发行年月日'] = time_premiered = premieredg.group(1) 318 | dict_data['发行年份'] = time_premiered[0:4] 319 | dict_data['月'] = time_premiered[5:7] 320 | dict_data['日'] = time_premiered[8:10] 321 | else: 322 | dict_data['发行年月日'] = time_premiered = '1970-01-01' 323 | dict_data['发行年份'] = '1970' 324 | dict_data['月'] = '01' 325 | dict_data['日'] = '01' 326 | # 片长 150 分钟 327 | runtimeg = re.search(r'value">(\d+) 分鍾<', html_web) 328 | if str(runtimeg) != 'None': 329 | dict_data['片长'] = runtimeg.group(1) 330 | else: 331 | dict_data['片长'] = '0' 332 | # 片商 制作商 333 | studiog = re.search(r'makers/.+?">(.+?)<', html_web) 334 | if str(studiog) != 'None': 335 | dict_data['片商'] = studio = replace_xml_win(studiog.group(1)) 336 | else: 337 | dict_data['片商'] = 'FC2卖家' 338 | studio = '' 339 | # 特点 340 | genres = re.findall(r'tags.+?">(.+?)', html_web) 341 | if bool_subtitle: # 有“中字“,加上特征”中文字幕” 342 | genres.append('中文字幕') 343 | try: 344 | genres = [dict_genre[i] for i in genres if dict_genre[i] != '删除'] 345 | except KeyError as error: 346 | num_fail += 1 347 | record_fail(' >第' + str(num_fail) + '个失败!发现新的特征需要添加至【特征对照表】:' + str(error) + '\n') 348 | continue 349 | # print(genres) 350 | ####################################################################### 351 | dict_data['视频'] = dict_data['原文件名'] = jav.name_no_ext # dict_data['视频'],先定义为原文件名,即将发生变化。 352 | dict_data['原文件夹名'] = jav.folder 353 | # 是CD1还是CDn? 354 | num_all_episodes = dict_car_pref[jav.car] # 该车牌总共多少集 355 | if num_all_episodes > 1: 356 | str_cd = '-cd' + str(jav.episode) 357 | else: 358 | str_cd = '' 359 | 360 | # 1重命名视频【相同】 361 | try: 362 | dict_data, jav, num_temp = rename_mp4(jav, num_fail, settings, dict_data, list_name_video, 363 | path_relative, str_cd) 364 | num_fail = num_temp 365 | except FileExistsError: 366 | num_fail += 1 367 | continue 368 | 369 | # 2 归类影片【相同】只针对视频文件和字幕文件。注意:第2操作和下面(第3操作+第7操作)互斥,只能执行第2操作或(第3操作+第7操作),归类影片是针对“文件”还是“文件夹”。 370 | try: 371 | jav, num_temp = classify_files(jav, num_fail, settings, dict_data, list_classify_basis, 372 | root_classify) 373 | num_fail = num_temp 374 | except FileExistsError: 375 | num_fail += 1 376 | continue 377 | 378 | # 3重命名文件夹【相同】如果是针对“文件”归类,这一步会被跳过。 因为用户只需要归类视频文件,不需要管文件夹。 379 | try: 380 | jav, num_temp = rename_folder(jav, num_fail, settings, dict_data, list_name_folder, 381 | bool_separate_folder, num_all_episodes) 382 | num_fail = num_temp 383 | except FileExistsError: 384 | num_fail += 1 385 | continue 386 | 387 | # 更新一下path_relative 388 | path_relative = sep + jav.path.replace(root_choose, '') # 影片的相对于所选文件夹的路径,用于报错 389 | 390 | # 4写入nfo【独特】 391 | if settings.bool_nfo: 392 | # 如果是为空地准备的nfo,不需要多cd 393 | if settings.bool_cd_only: 394 | path_nfo = jav.root + sep + jav.name_no_ext.replace(str_cd, '') + '.nfo' 395 | else: 396 | path_nfo = jav.root + sep + jav.name_no_ext + '.nfo' 397 | # nfo中tilte的写法 398 | title_in_nfo = '' 399 | for i in list_name_nfo_title: 400 | title_in_nfo += dict_data[i] 401 | # 开始写入nfo,这nfo格式是参考的kodi的nfo 402 | f = open(path_nfo, 'w', encoding="utf-8") 403 | f.write("\n" 404 | "\n" 405 | " " + title_in_nfo + "\n" 406 | " " + title + "\n" 407 | " " + dict_data['发行年份'] + "\n" 408 | " NC-17\n" 409 | " NC-17\n" 410 | " JP\n" 411 | " " + time_premiered + "\n" 412 | " " + time_premiered + "\n" 413 | " " + dict_data['片长'] + "\n" 414 | " 日本\n" 415 | " " + studio + "\n" 416 | " " + car + "\n" 417 | " " + car + "\n") 418 | # 需要将特征写入genre 419 | if settings.bool_genre: 420 | for i in genres: 421 | f.write(" " + i + "\n") 422 | if settings.bool_write_studio and studio: 423 | f.write(" 卖家:" + studio + "\n") 424 | if list_extra_genres: 425 | for i in list_extra_genres: 426 | f.write(" " + dict_data[i] + "\n") 427 | # 需要将特征写入tag 428 | if settings.bool_tag: 429 | for i in genres: 430 | f.write(" " + i + "\n") 431 | if settings.bool_write_studio and studio: 432 | f.write(" 卖家:" + studio + "\n") 433 | if list_extra_genres: 434 | for i in list_extra_genres: 435 | f.write(" " + dict_data[i] + "\n") 436 | # 写入演员 437 | f.write(" \n FC2演员\n Actor\n \n") 438 | f.write("\n") 439 | f.close() 440 | print(' >nfo收集完成') 441 | 442 | # 5需要两张封面图片【独特】 443 | if settings.bool_jpg: 444 | path_fanart = jav.root + sep 445 | path_poster = jav.root + sep 446 | for i in list_name_fanart: 447 | path_fanart += dict_data[i] 448 | for i in list_name_poster: 449 | path_poster += dict_data[i] 450 | # kodi只需要一份图片,图片路径唯一 451 | if settings.bool_cd_only: 452 | path_fanart = path_fanart.replace(str_cd, '') 453 | path_poster = path_poster.replace(str_cd, '') 454 | # emby需要多份,现在不是第一集,直接复制第一集的图片 455 | elif jav.episode != 1: 456 | try: 457 | copyfile(path_fanart.replace(str_cd, '-cd1'), path_fanart) 458 | print(' >fanart.jpg复制成功') 459 | copyfile(path_poster.replace(str_cd, '-cd1'), path_poster) 460 | print(' >poster.jpg复制成功') 461 | except FileNotFoundError: 462 | pass 463 | # kodi或者emby需要的第一份图片 464 | if check_picture(path_fanart): 465 | # print(' >已有fanart.jpg') 466 | pass 467 | else: 468 | # 下载封面 469 | print(' >从javdb下载封面:', url_cover) 470 | try: 471 | download_pic(url_cover, path_fanart, proxy_db) 472 | print(' >fanart.jpg下载成功') 473 | except: 474 | num_fail += 1 475 | record_fail(' >第' + str( 476 | num_fail) + '个失败!下载fanart.jpg失败:' + url_cover + ',' + path_relative + '\n') 477 | continue # 退出对该jav的整理 478 | # 裁剪生成 poster 479 | if check_picture(path_poster): 480 | # print(' >已有poster.jpg') 481 | pass 482 | elif settings.bool_face: 483 | crop_poster_baidu(path_fanart, path_poster, client) 484 | # 需要加上条纹 485 | if settings.bool_watermark_subtitle and bool_subtitle: 486 | add_watermark_subtitle(path_poster) 487 | if settings.bool_watermark_divulge and bool_divulge: 488 | add_watermark_divulge(path_poster) 489 | else: 490 | crop_poster_default(path_fanart, path_poster, 2) 491 | # 需要加上条纹 492 | if settings.bool_watermark_subtitle and bool_subtitle: 493 | add_watermark_subtitle(path_poster) 494 | if settings.bool_watermark_divulge and bool_divulge: 495 | add_watermark_divulge(path_poster) 496 | 497 | # 6收集演员头像【相同】 498 | 499 | # 7归类影片,针对文件夹【相同】 500 | try: 501 | num_temp = classify_folder(jav, num_fail, settings, dict_data, list_classify_basis, root_classify, 502 | root, bool_separate_folder, num_all_episodes) 503 | num_fail = num_temp 504 | except FileExistsError: 505 | num_fail += 1 506 | continue 507 | 508 | except: 509 | num_fail += 1 510 | record_fail(' >第' + str(num_fail) + '个失败!发生错误,如一直在该影片报错请截图并联系作者:' + path_relative + '\n' + format_exc() + '\n') 511 | continue # 【退出对该jav的整理】 512 | 513 | # 完结撒花 514 | print('\n当前文件夹完成,', end='') 515 | if num_fail > 0: 516 | print('失败', num_fail, '个! ', root_choose, '\n') 517 | line = -1 518 | with open('【可删除】失败记录.txt', 'r', encoding="utf-8") as f: 519 | content = list(f) 520 | while 1: 521 | if content[line].startswith('已'): 522 | break 523 | line -= 1 524 | for i in range(line+1, 0): 525 | print(content[i], end='') 526 | print('\n“【可删除】失败记录.txt”已记录错误\n') 527 | else: 528 | print(' “0”失败! ', root_choose, '\n') 529 | # os.system('pause') 530 | input_start_key = input('回车继续选择文件夹整理:') 531 | -------------------------------------------------------------------------------- /javsdt/StaticFiles/divulge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/divulge.png -------------------------------------------------------------------------------- /javsdt/StaticFiles/emby.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/emby.ico -------------------------------------------------------------------------------- /javsdt/StaticFiles/ini.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/ini.ico -------------------------------------------------------------------------------- /javsdt/StaticFiles/javbus.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/javbus.ico -------------------------------------------------------------------------------- /javsdt/StaticFiles/javdb.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/javdb.ico -------------------------------------------------------------------------------- /javsdt/StaticFiles/javlibrary.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/javlibrary.ico -------------------------------------------------------------------------------- /javsdt/StaticFiles/javsdt.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/javsdt.ico -------------------------------------------------------------------------------- /javsdt/StaticFiles/subtitle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/subtitle.png -------------------------------------------------------------------------------- /javsdt/StaticFiles/suren.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/suren.ico -------------------------------------------------------------------------------- /javsdt/StaticFiles/update.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/StaticFiles/update.ico -------------------------------------------------------------------------------- /javsdt/Update.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import requests, re, os 3 | 4 | now_version = '1.1.3' 5 | print('当前版本为:', now_version) 6 | print('正在检查更新...https://github.com/junerain123/javsdt/blob/master/%E6%A3%80%E6%9F%A5%E6%9B%B4%E6%96%B0.json') 7 | upd_url = 'https://github.com/junerain123/javsdt/blob/master/%E6%A3%80%E6%9F%A5%E6%9B%B4%E6%96%B0.json' 8 | try: 9 | rqs = requests.get(upd_url, timeout=20) 10 | except: 11 | print('连接github超时!请重新尝试!') 12 | os.system('pause') 13 | rqs.encoding = 'utf-8' 14 | html_github_update = rqs.text 15 | # print(html_github_update) 16 | version_g = re.search(r'version": "(.+?)<', html_github_update) 17 | new_version = version_g.group(1) 18 | download_g = re.search(r'lanzous.com/(.+?)<', html_github_update) 19 | new_download = 'https://www.lanzous.com/' + download_g.group(1) 20 | print('最新版本为:', new_version) 21 | if now_version != new_version: 22 | print('请下载最新的版本:', new_version, '!') 23 | print('下载链接为:', new_download, '!') 24 | else: 25 | print('你正在使用最新的版本,无需更新!') 26 | os.system('pause') 27 | -------------------------------------------------------------------------------- /javsdt/divulge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/divulge.png -------------------------------------------------------------------------------- /javsdt/emby_actors.py: -------------------------------------------------------------------------------- 1 | # -*- coding:utf-8 -*- 2 | import requests, os 3 | from configparser import RawConfigParser 4 | from base64 import b64encode 5 | from traceback import format_exc 6 | from json import loads 7 | from os.path import exists 8 | 9 | # 检查“演员头像”文件夹是否就绪 10 | if not exists('演员头像'): 11 | print('\n“演员头像”文件夹丢失!请把它放进exe的文件夹中!\n') 12 | os.system('pause') 13 | # 读取配置文件,这个ini文件用来给用户设置emby网址和api id 14 | print('正在读取ini中的设置...') 15 | config_settings = RawConfigParser() 16 | try: 17 | config_settings.read('【点我设置整理规则】.ini', encoding='utf-8-sig') 18 | url_emby = config_settings.get("emby/jellyfin", "网址") 19 | api_key = config_settings.get("emby/jellyfin", "api id") 20 | bool_replace = True if config_settings.get("emby/jellyfin", "是否覆盖以前上传的头像?") == '是' else False 21 | except: 22 | print(format_exc()) 23 | print('无法读取ini文件,请修改它为正确格式,或者打开“【ini】重新创建ini.exe”创建全新的ini!') 24 | os.system('pause') 25 | print('读取ini文件成功!\n') 26 | # 修正用户输入的emby网址,无论是不是带“/” 27 | if not url_emby.endswith('/'): 28 | url_emby += '/' 29 | # 成功的个数 30 | num_suc = 0 31 | num_fail = 0 32 | num_exist = 0 33 | sep = os.sep 34 | try: 35 | print('正在获取取emby中Persons清单...') 36 | # curl -X GET "http://localhost:8096/emby/Persons?api_key=3291434710e342089565ad05b6b2f499" -H "accept: application/json" 37 | # 得到所有“人员” emby api没有细分“演员”还是“导演”“编剧”等等 下面得到的是所有“有关人员” 38 | url_emby_persons = url_emby + 'emby/Persons?api_key=' + api_key # &PersonTypes=Actor 39 | try: 40 | rqs_emby = requests.get(url=url_emby_persons) 41 | except requests.exceptions.ConnectionError: 42 | print('无法访问emby服务端,请检查:', url_emby, '\n') 43 | os.system('pause') 44 | except: 45 | print(format_exc()) 46 | print('发生未知错误,请截图并联系作者:', url_emby, '\n') 47 | os.system('pause') 48 | # 401,无权访问 49 | if rqs_emby.status_code == 401: 50 | print('请检查api id是否正确!\n') 51 | os.system('pause') 52 | # print(rqs_emby.text) 53 | try: 54 | list_persons = loads(rqs_emby.text)['Items'] 55 | except: 56 | print(rqs_emby.text) 57 | print('发生错误!emby返回内容如上:') 58 | print('请截图并联系作者!') 59 | os.system('pause') 60 | num_persons = len(list_persons) 61 | print('当前有' + str(num_persons) + '个Person!\n') 62 | # os.system('pause') 63 | # 用户emby中的persons,在“演员头像”文件夹中,已有头像的,记录下来 64 | f_txt = open("已收录的人员清单.txt", 'w', encoding="utf-8") 65 | f_txt.close() 66 | f_txt = open("未收录的人员清单.txt", 'w', encoding="utf-8") 67 | f_txt.close() 68 | for dic_each_actor in list_persons: 69 | actor_name = dic_each_actor['Name'] 70 | # 头像jpg/png在“演员头像”中的路径 71 | actor_pic_path = '演员头像' + sep + actor_name[0] + sep + actor_name 72 | if exists(actor_pic_path + '.jpg'): 73 | actor_pic_path = actor_pic_path + '.jpg' 74 | header = {"Content-Type": 'image/jpeg', } 75 | elif exists(actor_pic_path + '.png'): 76 | actor_pic_path = actor_pic_path + '.png' 77 | header = {"Content-Type": 'image/png', } 78 | else: 79 | print('>>暂无头像:', actor_name) 80 | f_txt = open("未收录的人员清单.txt", 'a', encoding="utf-8") 81 | f_txt.write(actor_name + '\n') 82 | f_txt.close() 83 | num_fail += 1 84 | continue 85 | # emby有某个演员,“演员头像”文件夹也有这个演员的头像,记录一下 86 | f_txt = open("已收录的人员清单.txt", 'a', encoding="utf-8") 87 | f_txt.write(actor_name + '\n') 88 | f_txt.close() 89 | # emby有某个演员,已经有他的头像,不再进行下面“上传头像”的操作 90 | if dic_each_actor['ImageTags']: # emby已经收录头像 91 | num_exist += 1 92 | if not bool_replace: # 不需要覆盖已有头像 93 | continue # 那么不进行下面的上传操作 94 | f_pic = open(actor_pic_path, 'rb') # 二进制方式打开图文件 95 | b6_pic = b64encode(f_pic.read()) # 读取文件内容,转换为base64编码 96 | f_pic.close() 97 | url_post_img = url_emby + 'emby/Items/' + dic_each_actor['Id'] + '/Images/Primary?api_key=' + api_key 98 | requests.post(url=url_post_img, data=b6_pic, headers=header) 99 | print('>>设置成功:', actor_name) 100 | num_suc += 1 101 | 102 | print('\nemby/jellyfin拥有人员', num_persons, '个!') 103 | print('已有头像', num_exist, '个!') 104 | if bool_replace: 105 | print('当前模式:覆盖以前上传的头像') 106 | else: 107 | print('当前模式:跳过以前上传的头像') 108 | print('成功上传', num_suc, '个!') 109 | print('暂无头像', num_fail, '个!') 110 | print('已保存至“未收录的人员清单.txt”\n') 111 | os.system('pause') 112 | except: 113 | print(format_exc()) 114 | os.system('pause') 115 | 116 | 117 | -------------------------------------------------------------------------------- /javsdt/subtitle.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/subtitle.png -------------------------------------------------------------------------------- /javsdt/【特征对照表】.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/javsdt/【特征对照表】.xlsx -------------------------------------------------------------------------------- /javsdt/【素人车牌】.txt: -------------------------------------------------------------------------------- 1 | AID 2 | AKDL 3 | ARA 4 | BMH 5 | BNJC 6 | BZDC 7 | CUTE 8 | DCV 9 | EMOI 10 | ENDX 11 | ETQR 12 | ETVTM 13 | EVA 14 | EWDX 15 | EXMU 16 | EZD 17 | FAD 18 | FCTD 19 | GANA 20 | GAREA 21 | GAV 22 | GERBM 23 | GERK 24 | HAMENETS 25 | HEN 26 | HHH 27 | HMDN 28 | HOI 29 | HSAM 30 | HYPN 31 | IMDK 32 | INST 33 | ION 34 | JAC 35 | JKK 36 | JKZ 37 | JNT 38 | JOTK 39 | JTR 40 | KAGD 41 | KBVR 42 | KIRAY 43 | KITAIKE 44 | KJN 45 | KMTU 46 | KNB 47 | KSKO 48 | KURO 49 | LADY 50 | LAFBD 51 | LAS 52 | LOLI 53 | LUXU 54 | MAAN 55 | MAG 56 | MFC 57 | MGDN 58 | MISM 59 | MIUM 60 | MLA 61 | MMGH 62 | MMH 63 | MNTJ 64 | MTP 65 | MY 66 | NAMA 67 | NKR 68 | NNPJ 69 | NRPK 70 | NTK 71 | NTTR 72 | OBUT 73 | OKYH 74 | ONS 75 | ORE 76 | OREBMS 77 | OREC 78 | OREP 79 | ORERB 80 | ORETD 81 | ORETDP 82 | OREX 83 | OTIM 84 | PAPA 85 | PER 86 | PIZ 87 | PKJD 88 | RCTS 89 | REG 90 | REP 91 | SCP 92 | SCUTE 93 | SDGN 94 | SENN 95 | SGK 96 | SHOW 97 | SHYN 98 | SIMM 99 | SIRO 100 | SKIV 101 | SPCY 102 | SPOR 103 | SPRM 104 | SQB 105 | SRCN 106 | SRHO 107 | SRTD 108 | SSAN 109 | STKO 110 | SUKE 111 | SVMM 112 | SWEET 113 | SYBI 114 | TRUMG 115 | URF 116 | UTSU 117 | VOV 118 | YKMC 119 | YRTB 120 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | baidu-aip==2.2.18.0 2 | certifi==2020.12.5 3 | chardet==3.0.4 4 | idna==2.7 5 | Pillow==8.1.0 6 | PySocks==1.7.1 7 | requests==2.20.0 8 | urllib3==1.24.3 9 | xlrd==1.2.0 10 | -------------------------------------------------------------------------------- /检查更新.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "1.1.5", 3 | "download": "https://junerain.lanzous.com/ivp8Plg6wza" 4 | } 5 | -------------------------------------------------------------------------------- /测试影片.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/fanza1/AVScraper/615821cc93cf039fda4d0ed930a19dae6aa3451b/测试影片.zip --------------------------------------------------------------------------------