From fc13f887311bfc1aa54bf736c72dee31ba2816b3 Mon Sep 17 00:00:00 2001 From: Tan Peng Date: Wed, 25 Mar 2020 01:45:55 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96=E6=AD=A3=E5=88=99=E7=AD=89?= =?UTF-8?q?=EF=BC=8C=E4=BF=AE=E6=94=B9=E9=80=BB=E8=BE=91=EF=BC=8C=E9=81=BF?= =?UTF-8?q?=E5=85=8D=E8=A2=AB=E8=A6=86=E7=9B=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit to learn goupby learn pandas groupby groupby learn pandas groupby 优化正则提取番号和集数 待理解下载图片逻辑 还有剪裁+背景图逻辑 修改所有config[ 将整理生成nfo的代码 可缓存番号信息和缩略图和海报 可以识别番号后集数和尾部集数,赞不能分辨-C中文字幕片 改正一个错误 嵌套字典存储数据 整理函数 修正匹配时间正则 pipenv 添加依赖 修改优先取三位数字的规则:heyzo四位数除外 添加了依赖 和 有番号的优化 修改了啥 我也记不得了 --- .gitignore | 1 + .idea/.gitignore | 2 + .idea/AV_Data_Capture.iml | 8 + .idea/dictionaries/tanpengsccd.xml | 19 + .../inspectionProfiles/profiles_settings.xml | 6 + .idea/misc.xml | 7 + .idea/modules.xml | 8 + .idea/other.xml | 6 + .idea/vcs.xml | 6 + ADC_function.py | 263 ++- AV_Data_Capture.py | 578 ++++-- ConfigApp.py | 28 + LICENSE | 0 LearningNote/GroupbyDemo.py | 19 + LearningNote/PandasDemo.py | 38 + MediaServer.py | 28 + Metadate.py | 3 + PathNameProcessor.py | 115 ++ Pipfile | 19 + Pipfile.lock | 246 +++ README.md | 0 avsox.py => SiteSource/avsox.py | 229 +-- fanza.py => SiteSource/fanza.py | 458 ++--- fc2fans_club.py => SiteSource/fc2fans_club.py | 324 ++-- javbus.py => SiteSource/javbus.py | 277 +-- javdb.py => SiteSource/javdb.py | 246 +-- mgstage.py => SiteSource/mgstage.py | 216 +-- TestPathNFO.txt | 41 + TestPathSpecial.txt | 51 + TestPaths.txt | 50 + config.ini | 29 +- core.py | 1609 ++++++++++------- jav321.py | 73 - readme/._readme1.PNG | Bin 0 -> 4096 bytes readme/._readme2.PNG | Bin 0 -> 4096 bytes readme/._readme4.PNG | Bin 0 -> 4096 bytes readme/This is readms.md's images folder | 0 readme/flow_chart2.png | Bin readme/readme1.PNG | Bin readme/readme2.PNG | Bin readme/readme3.PNG | Bin readme/readme4.PNG | Bin readme/single.gif | Bin resource/This is readms.md's images folder | 1 + resource/flow_chart2.png | Bin 0 -> 103847 bytes resource/readme1.PNG | Bin 0 -> 1152 bytes resource/readme2.PNG | Bin 0 -> 3473 bytes resource/readme3.PNG | Bin 0 -> 1301 bytes resource/readme4.PNG | Bin 0 -> 16269 bytes resource/ruquirments.txt | 1 + resource/single.gif | Bin 0 -> 69859 bytes ruquirments.txt | 4 - test.py | 80 + update_check.json | 0 54 files changed, 3138 insertions(+), 1951 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/AV_Data_Capture.iml create mode 100644 .idea/dictionaries/tanpengsccd.xml create mode 100644 .idea/inspectionProfiles/profiles_settings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/other.xml create mode 100644 .idea/vcs.xml create mode 100755 ConfigApp.py mode change 100644 => 100755 LICENSE create mode 100644 LearningNote/GroupbyDemo.py create mode 100644 LearningNote/PandasDemo.py create mode 100644 MediaServer.py create mode 100644 Metadate.py create mode 100644 PathNameProcessor.py create mode 100644 Pipfile create mode 100644 Pipfile.lock mode change 100644 => 100755 README.md rename avsox.py => SiteSource/avsox.py (96%) mode change 100644 => 100755 rename fanza.py => SiteSource/fanza.py (97%) mode change 100644 => 100755 rename fc2fans_club.py => SiteSource/fc2fans_club.py (97%) rename javbus.py => SiteSource/javbus.py (95%) rename javdb.py => SiteSource/javdb.py (98%) rename mgstage.py => SiteSource/mgstage.py (98%) create mode 100644 TestPathNFO.txt create mode 100644 TestPathSpecial.txt create mode 100644 TestPaths.txt mode change 100644 => 100755 config.ini delete mode 100644 jav321.py create mode 100755 readme/._readme1.PNG create mode 100755 readme/._readme2.PNG create mode 100755 readme/._readme4.PNG mode change 100644 => 100755 readme/This is readms.md's images folder mode change 100644 => 100755 readme/flow_chart2.png mode change 100644 => 100755 readme/readme1.PNG mode change 100644 => 100755 readme/readme2.PNG mode change 100644 => 100755 readme/readme3.PNG mode change 100644 => 100755 readme/readme4.PNG mode change 100644 => 100755 readme/single.gif create mode 100755 resource/This is readms.md's images folder create mode 100755 resource/flow_chart2.png create mode 100755 resource/readme1.PNG create mode 100755 resource/readme2.PNG create mode 100755 resource/readme3.PNG create mode 100755 resource/readme4.PNG create mode 100755 resource/ruquirments.txt create mode 100755 resource/single.gif delete mode 100644 ruquirments.txt create mode 100755 test.py mode change 100644 => 100755 update_check.json diff --git a/.gitignore b/.gitignore index 894a44c..cdc48c0 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.DS_Store # Byte-compiled / optimized / DLL files __pycache__/ *.py[cod] diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..e7e9d11 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml diff --git a/.idea/AV_Data_Capture.iml b/.idea/AV_Data_Capture.iml new file mode 100644 index 0000000..21f057a --- /dev/null +++ b/.idea/AV_Data_Capture.iml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/dictionaries/tanpengsccd.xml b/.idea/dictionaries/tanpengsccd.xml new file mode 100644 index 0000000..d7229c1 --- /dev/null +++ b/.idea/dictionaries/tanpengsccd.xml @@ -0,0 +1,19 @@ + + + + avsox + emby + fanart + fanza + javbus + javdb + jellyfin + khtml + kodi + mgstage + plex + pondo + rmvb + + + \ No newline at end of file diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..a4410bf --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..9337de9 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/other.xml b/.idea/other.xml new file mode 100644 index 0000000..a708ec7 --- /dev/null +++ b/.idea/other.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/ADC_function.py b/ADC_function.py index 596a9ea..04708d6 100755 --- a/ADC_function.py +++ b/ADC_function.py @@ -1,136 +1,127 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import requests -from configparser import ConfigParser -import os -import re -import time -import sys -from lxml import etree -import sys -import io -# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) -# sys.setdefaultencoding('utf-8') - -config_file='config.ini' -config = ConfigParser() - -if os.path.exists(config_file): - try: - config.read(config_file, encoding='UTF-8') - except: - print('[-]Config.ini read failed! Please use the offical file!') -else: - print('[+]config.ini: not found, creating...',end='') - with open("config.ini", "wt", encoding='UTF-8') as code: - print("[common]", file=code) - print("main_mode = 1", file=code) - print("failed_output_folder = failed", file=code) - print("success_output_folder = JAV_output", file=code) - print("", file=code) - print("[proxy]",file=code) - print("proxy=127.0.0.1:1081",file=code) - print("timeout=10", file=code) - print("retry=3", file=code) - print("", file=code) - print("[Name_Rule]", file=code) - print("location_rule=actor+'/'+number",file=code) - print("naming_rule=number+'-'+title",file=code) - print("", file=code) - print("[update]",file=code) - print("update_check=1",file=code) - print("", file=code) - print("[media]", file=code) - print("media_warehouse=emby", file=code) - print("#emby plex kodi", file=code) - print("", file=code) - print("[escape]", file=code) - print("literals=\\", file=code) - print("", file=code) - print("[movie_location]", file=code) - print("path=", file=code) - print("", file=code) - print('.',end='') - time.sleep(2) - print('.') - print('[+]config.ini: created!') - print('[+]Please restart the program!') - time.sleep(4) - os._exit(0) - try: - config.read(config_file, encoding='UTF-8') - except: - print('[-]Config.ini read failed! Please use the offical file!') - -def get_network_settings(): - try: - proxy = config["proxy"]["proxy"] - timeout = int(config["proxy"]["timeout"]) - retry_count = int(config["proxy"]["retry"]) - assert timeout > 0 - assert retry_count > 0 - except: - raise ValueError("[-]Proxy config error! Please check the config.") - return proxy, timeout, retry_count - -def getDataState(json_data): # 元数据获取失败检测 - if json_data['title'] == '' or json_data['title'] == 'None' or json_data['title'] == 'null': - return 0 - else: - return 1 - -def ReadMediaWarehouse(): - return config['media']['media_warehouse'] - -def UpdateCheckSwitch(): - check=str(config['update']['update_check']) - if check == '1': - return '1' - elif check == '0': - return '0' - elif check == '': - return '0' - -def getXpathSingle(htmlcode,xpath): - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result1 = str(html.xpath(xpath)).strip(" ['']") - return result1 - -def get_html(url,cookies = None):#网页请求核心 - proxy, timeout, retry_count = get_network_settings() - i = 0 - while i < retry_count: - try: - if not proxy == '': - proxies = {"http": "http://" + proxy,"https": "https://" + proxy} - headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36'} - getweb = requests.get(str(url), headers=headers, timeout=timeout,proxies=proxies, cookies=cookies) - getweb.encoding = 'utf-8' - return getweb.text - else: - headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'} - getweb = requests.get(str(url), headers=headers, timeout=timeout, cookies=cookies) - getweb.encoding = 'utf-8' - return getweb.text - except: - i += 1 - print('[-]Connect retry '+str(i)+'/'+str(retry_count)) - print('[-]Connect Failed! Please check your Proxy or Network!') - - -def post_html(url: str, query: dict) -> requests.Response: - proxy, timeout, retry_count = get_network_settings() - - if proxy: - proxies = {"http": "http://" + proxy, "https": "https://" + proxy} - else: - proxies = {} - - for i in range(retry_count): - try: - result = requests.post(url, data=query, proxies=proxies) - return result - except requests.exceptions.ProxyError: - print("[-]Connect retry {}/{}".format(i+1, retry_count)) - print("[-]Connect Failed! Please check your Proxy or Network!") +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import requests +from configparser import ConfigParser +import os +import re +import time +import sys +from lxml import etree +import sys +import io +from ConfigApp import ConfigApp +# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) +# sys.setdefaultencoding('utf-8') + +# config_file='config.ini' +# config = ConfigParser() + +# if os.path.exists(config_file): +# try: +# config.read(config_file, encoding='UTF-8') +# except: +# print('[-]Config.ini read failed! Please use the offical file!') +# else: +# print('[+]config.ini: not found, creating...',end='') +# with open("config.ini", "wt", encoding='UTF-8') as code: +# print("[common]", file=code) +# print("main_mode = 1", file=code) +# print("failed_output_folder = failed", file=code) +# print("success_output_folder = JAV_output", file=code) +# print("", file=code) +# print("[proxy]",file=code) +# print("proxy=127.0.0.1:1081",file=code) +# print("timeout=10", file=code) +# print("retry=3", file=code) +# print("", file=code) +# print("[Name_Rule]", file=code) +# print("location_rule=actor+'/'+number",file=code) +# print("naming_rule=number+'-'+title",file=code) +# print("", file=code) +# print("[update]",file=code) +# print("update_check=1",file=code) +# print("", file=code) +# print("[media]", file=code) +# print("media_warehouse=emby", file=code) +# print("#emby plex kodi", file=code) +# print("", file=code) +# print("[escape]", file=code) +# print("literals=\\", file=code) +# print("", file=code) +# print("[movie_location]", file=code) +# print("path=", file=code) +# print("", file=code) +# print('.',end='') +# time.sleep(2) +# print('.') +# print('[+]config.ini: created!') +# print('[+]Please restart the program!') +# time.sleep(4) +# os._exit(0) +# try: +# config.read(config_file, encoding='UTF-8') +# except: +# print('[-]Config.ini read failed! Please use the offical file!') + +config = ConfigApp() + + +def get_network_settings(): + try: + proxy = config.proxy + timeout = int(config.timeout) + retry_count = int(config.retry) + assert timeout > 0 + assert retry_count > 0 + except: + raise ValueError("[-]Proxy config error! Please check the config.") + return proxy, timeout, retry_count + +def getDataState(json_data): # 元数据获取失败检测 + if json_data['title'] == '' or json_data['title'] == 'None' or json_data['title'] == 'null': + return 0 + else: + return 1 + +def ReadMediaWarehouse(): + return config.media_server + +def UpdateCheckSwitch(): + check=str(config.update_check) + if check == '1': + return '1' + elif check == '0': + return '0' + elif check == '': + return '0' + +def getXpathSingle(htmlcode,xpath): + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result1 = str(html.xpath(xpath)).strip(" ['']") + return result1 + +def get_html(url,cookies = None):#网页请求核心 + proxy, timeout, retry_count = get_network_settings() + i = 0 + print(url) + while i < retry_count: + try: + if not proxy == '': + proxies = {"http": proxy, "https": proxy} + headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/60.0.3100.0 Safari/537.36'} + getweb = requests.get(str(url), headers=headers, timeout=timeout, proxies=proxies, cookies=cookies) + getweb.encoding = 'utf-8' + return getweb.text + else: + headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'} + getweb = requests.get(str(url), headers=headers, timeout=timeout, cookies=cookies) + getweb.encoding = 'utf-8' + return getweb.text + except Exception as e: + print(e) + i += 1 + print('[-]Connect retry '+str(i)+'/'+str(retry_count)) + print('[-]Connect Failed! Please check your Proxy or Network!') + + diff --git a/AV_Data_Capture.py b/AV_Data_Capture.py index 547595e..1994529 100755 --- a/AV_Data_Capture.py +++ b/AV_Data_Capture.py @@ -1,162 +1,416 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -import glob -import os -import time -import re -from ADC_function import * -from core import * -import json -import shutil -from configparser import ConfigParser -import argparse - - -def UpdateCheck(version): - if UpdateCheckSwitch() == '1': - html2 = get_html('https://raw.githubusercontent.com/yoshiko2/AV_Data_Capture/master/update_check.json') - html = json.loads(str(html2)) - - if not version == html['version']: - print('[*] * New update ' + html['version'] + ' *') - print('[*] ↓ Download ↓') - print('[*] ' + html['download']) - print('[*]======================================================') - else: - print('[+]Update Check disabled!') - -def argparse_get_file(): - parser = argparse.ArgumentParser() - parser.add_argument("file", default='',nargs='?', help="Write the file path on here") - args = parser.parse_args() - if args.file == '': - return '' - else: - return args.file - -def movie_lists(escape_folder): - escape_folder = re.split('[,,]', escape_folder) - total = [] - file_type = ['.mp4', '.avi', '.rmvb', '.wmv', '.mov', '.mkv', '.flv', '.ts', '.webm', '.MP4', '.AVI', '.RMVB', '.WMV','.MOV', '.MKV', '.FLV', '.TS', '.WEBM', ] - file_root = os.getcwd() - for root, dirs, files in os.walk(file_root): - flag_escape = 0 - for folder in escape_folder: - if folder in root: - flag_escape = 1 - break - if flag_escape == 1: - continue - for f in files: - if os.path.splitext(f)[1] in file_type: - path = os.path.join(root, f) - path = path.replace(file_root, '.') - total.append(path) - return total - - -def CreatFailedFolder(failed_folder): - if not os.path.exists(failed_folder + '/'): # 新建failed文件夹 - try: - os.makedirs(failed_folder + '/') - except: - print("[-]failed!can not be make folder 'failed'\n[-](Please run as Administrator)") - os._exit(0) - - -def CEF(path): - try: - files = os.listdir(path) # 获取路径下的子文件(夹)列表 - for file in files: - os.removedirs(path + '/' + file) # 删除这个空文件夹 - print('[+]Deleting empty folder', path + '/' + file) - except: - a = '' - - -def getNumber(filepath,absolute_path = False): - if absolute_path == True: - filepath=filepath.replace('\\','/') - file_number = str(re.findall(r'(.+?)\.', str(re.search('([^<>/\\\\|:""\\*\\?]+)\\.\\w+$', filepath).group()))).strip("['']").replace('_', '-') - return file_number - if '-' in filepath or '_' in filepath: # 普通提取番号 主要处理包含减号-和_的番号 - filepath = filepath.replace("_", "-") - filepath.strip('22-sht.me').strip('-HD').strip('-hd') - filename = str(re.sub("\[\d{4}-\d{1,2}-\d{1,2}\] - ", "", filepath)) # 去除文件名中时间 - if 'FC2' or 'fc2' in filename: - filename = filename.replace('-PPV', '').replace('PPV-', '').replace('FC2PPV-','FC2-').replace('FC2PPV_','FC2-') - file_number = re.search(r'\w+-\w+', filename, re.A).group() - return file_number - else: # 提取不含减号-的番号,FANZA CID - try: - return str(re.findall(r'(.+?)\.', str(re.search('([^<>/\\\\|:""\\*\\?]+)\\.\\w+$', filepath).group()))).strip("['']").replace('_', '-') - except: - return re.search(r'(.+?)\.', filepath)[0] - - -if __name__ == '__main__': - version = '2.8.2' - config_file = 'config.ini' - config = ConfigParser() - config.read(config_file, encoding='UTF-8') - success_folder = config['common']['success_output_folder'] - failed_folder = config['common']['failed_output_folder'] # 失败输出目录 - escape_folder = config['escape']['folders'] # 多级目录刮削需要排除的目录 - print('[*]================== AV Data Capture ===================') - print('[*] Version ' + version) - print('[*]======================================================') - - UpdateCheck(version) - CreatFailedFolder(failed_folder) - os.chdir(os.getcwd()) - movie_list = movie_lists(escape_folder) - - #========== 野鸡番号拖动 ========== - number_argparse=argparse_get_file() - if not number_argparse == '': - print("[!]Making Data for [" + number_argparse + "], the number is [" + getNumber(number_argparse,absolute_path = True) + "]") - core_main(number_argparse, getNumber(number_argparse,absolute_path = True)) - print("[*]======================================================") - CEF(success_folder) - CEF(failed_folder) - print("[+]All finished!!!") - input("[+][+]Press enter key exit, you can check the error messge before you exit.") - os._exit(0) - # ========== 野鸡番号拖动 ========== - - count = 0 - count_all = str(len(movie_list)) - print('[+]Find', count_all, 'movies') - if config['common']['soft_link'] == '1': - print('[!] --- Soft link mode is ENABLE! ----') - for i in movie_list: # 遍历电影列表 交给core处理 - count = count + 1 - percentage = str(count / int(count_all) * 100)[:4] + '%' - print('[!] - ' + percentage + ' [' + str(count) + '/' + count_all + '] -') - # print("[!]Making Data for [" + i + "], the number is [" + getNumber(i) + "]") - # core_main(i, getNumber(i)) - # print("[*]======================================================") - try: - print("[!]Making Data for [" + i + "], the number is [" + getNumber(i) + "]") - core_main(i, getNumber(i)) - print("[*]======================================================") - except: # 番号提取异常 - print('[-]' + i + ' Cannot catch the number :') - if config['common']['soft_link'] == '1': - print('[-]Link', i, 'to failed folder') - os.symlink(i, str(os.getcwd()) + '/' + failed_folder + '/') - else: - try: - print('[-]Move ' + i + ' to failed folder') - shutil.move(i, str(os.getcwd()) + '/' + failed_folder + '/') - except FileExistsError: - print('[!]File exists in failed!') - except: - print('[+]skip') - continue - - CEF(success_folder) - CEF(failed_folder) - print("[+]All finished!!!") - input("[+][+]Press enter key exit, you can check the error messge before you exit.") +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- + +import glob +import os +import time +import fuckit +from tenacity import retry, stop_after_delay, wait_fixed +import json +import shutil +import itertools +import argparse +from pathlib import Path + +from core import * +from ConfigApp import ConfigApp +from PathNameProcessor import PathNameProcessor + +# TODO 封装聚合解耦:CORE +# TODO (学习)统一依赖管理工具 +# TODO 不同媒体服务器尽量兼容统一一种元数据 如nfo 海报等(emby,jellyfin,plex) +# TODO 字幕整理功能 文件夹中读取所有字幕 并提番号放入对应缓存文件夹中TEMP + +config = ConfigApp() + + +def safe_list_get(list_in, idx, default=None): + """ + 数组安全取值 + :param list_in: + :param idx: + :param default: + :return: + """ + try: + return list_in[idx] + except IndexError: + return default + + +def UpdateCheck(version): + if UpdateCheckSwitch() == '1': + html2 = get_html('https://raw.githubusercontent.com/yoshiko2/AV_Data_Capture/master/update_check.json') + html = json.loads(str(html2)) + + if not version == html['version']: + print('[*] * New update ' + html['version'] + ' *') + print('[*] ↓ Download ↓') + print('[*] ' + html['download']) + print('[*]======================================================') + else: + print('[+]Update Check disabled!') + + +def argparse_get_file(): + parser = argparse.ArgumentParser() + parser.add_argument("file", default='', nargs='?', help="Write the file path on here") + args = parser.parse_args() + if args.file == '': + return '' + else: + return args.file + + +def movie_lists(escape_folders): + escape_folders = re.split('[,,]', escape_folders) + total = [] + + for root, dirs, files in os.walk(config.search_folder): + if root in escape_folders: + continue + for file in files: + if re.search(PathNameProcessor.pattern_of_file_name_suffixes, file, re.IGNORECASE): + path = os.path.join(root, file) + total.append(path) + return total + + +# def CEF(path): +# try: +# files = os.listdir(path) # 获取路径下的子文件(夹)列表 +# for file in files: +# os.removedirs(path + '/' + file) # 删除这个空文件夹 +# print('[+]Deleting empty folder', path + '/' + file) +# except: +# a = '' +# + + +def get_numbers(paths): + """提取对应路径的番号+集数""" + + def get_number(filepath, absolute_path=False): + """ + 获取番号,集数 + :param filepath: + :param absolute_path: + :return: + """ + name = filepath.upper() # 转大写 + if absolute_path: + name = name.replace('\\', '/') + # 移除干扰字段 + name = PathNameProcessor.remove_distractions(name) + # 抽取 文件路径中可能存在的尾部集数,和抽取尾部集数的后的文件路径 + suffix_episode, name = PathNameProcessor.extract_suffix_episode(name) + # 抽取 文件路径中可能存在的 番号后跟随的集数 和 处理后番号 + episode_behind_code, code_number = PathNameProcessor.extract_code(name) + # 无番号 则设置空字符 + code_number = code_number if code_number else '' + # 优先取尾部集数,无则取番号后的集数(几率低),都无则为空字符 + episode = suffix_episode if suffix_episode else episode_behind_code if episode_behind_code else '' + + return code_number, episode + + maps = {} + for path in paths: + number, episode = get_number(path) + maps[path] = (number, episode) + + return maps + + +def create_folder(paths): + for path_to_make in paths: + if path_to_make: + try: + os.makedirs(path_to_make) + except FileExistsError as e: + # name = f'{folder=}'.split('=')[0].split('.')[-1] + print(path_to_make + " 已经存在") + pass + except Exception as exception: + print('! 创建文件夹 ' + path_to_make + ' 失败,文件夹路径错误或权限不够') + raise exception + else: + raise Exception('!创建的文件夹路径为空,请确认') + + +if __name__ == '__main__': + version = '2.8.2' + + print('[*]================== AV Data Capture ===================') + print('[*] Version ' + version) + print('[*]======================================================') + + # UpdateCheck(version) + + CreatFailedFolder(config.failed_folder) + os.chdir(os.getcwd()) + + # 创建文件夹 + create_folder([config.failed_folder, config.search_folder, config.temp_folder]) + + # temp 文件夹中infos放 番号json信息,pics中放图片信息 + path_infos = config.temp_folder + '/infos' + path_pics = config.temp_folder + '/pics' + + create_folder([path_infos, path_pics]) + + # 遍历搜索目录下所有视频的路径 + movie_list = movie_lists(config.escape_folder) + + # 以下是从文本中提取测试的数据 + # f = open('TestPathNFO.txt', 'r') + # f = open('TestPathSpecial.txt', 'r') + # movie_list = [line[:-1] for line in f.readlines()] + # f.close() + + # 获取 番号,集数,路径 的字典->list + code_ep_paths = [[codeEposode[0], codeEposode[1], path] for path, codeEposode in get_numbers(movie_list).items()] + [print(i) for i in code_ep_paths] + # 按番号分组片子列表(重点),用于寻找相同番号的片子 + ''' + 这里利用pandas分组 "https://pandas.pydata.org/pandas-docs/stable/user_guide/groupby.html" + + ''' + # # 设置打印时显示所有列 + # pd.set_option('display.max_columns', None) + # # 显示所有行 + # pd.set_option('display.max_rows', None) + # # 设置value的显示长度为100,默认为50 + # pd.set_option('max_colwidth', 30) + # # 创建框架 + # df = pd.DataFrame(code_ep_paths, columns=('code', 'ep', 'path')) + # # 以番号分组 + # groupedCode_code_ep_paths = df.groupby(['code']) + # # print(df.groupby(['code', 'ep']).describe().unstack()) + # grouped_code_ep = df.groupby(['code', 'ep'])['path'] + # + sorted_code_list = sorted(code_ep_paths, key=lambda code_ep_path: code_ep_path[0]) + group_code_list = itertools.groupby(sorted_code_list, key=lambda code_ep_path: code_ep_path[0]) + + + def group_code_list_to_dict(group_code_list): + data_dict = {} + for code, code_ep_path_group in group_code_list: + code_ep_path_list = list(code_ep_path_group) + eps_of_code = {} + group_ep_list = itertools.groupby(code_ep_path_list, key=lambda code_ep_path: code_ep_path[1]) + for ep, group_ep_group in group_ep_list: + group_ep_list = list(group_ep_group) + eps_of_code[ep] = [code_ep_path[2] for code_ep_path in group_ep_list] + data_dict[code] = eps_of_code + + return data_dict + + + def print_same_code_ep_path(data_dict_in): + for code_in in data_dict_in: + ep_path_list = data_dict_in[code_in] + if len(ep_path_list) > 1: + print('--' * 60) + print("|" + (code_in if code_in else 'unknown') + ":") + + # group_ep_list = itertools.groupby(code_ep_path_list.items(), key=lambda code_ep_path: code_ep_path[0]) + for ep in ep_path_list: + path_list = ep_path_list[ep] + print('--' * 12) + ep = ep if ep else ' ' + if len(path_list) == 1: + print('| 集数:' + ep + ' 文件: ' + path_list[0]) + else: + print('| 集数:' + ep + ' 文件: ') + for path in path_list: + print('| ' + path) + + else: + pass + + + # 分好组的数据 {code:{ep:[path]}} + data_dict_groupby_code_ep = group_code_list_to_dict(group_code_list) + + print('--' * 100) + print("找到影片数量:" + str(len(movie_list))) + print("合计番号数量:" + str(len(data_dict_groupby_code_ep)) + " (多个相同番号的影片只统计一个,不能识别的番号 都统一为'unknown')") + print('Warning:!!!! 以下为相同番号的电影明细') + print('◤' + '--' * 80) + print_same_code_ep_path(data_dict_groupby_code_ep) + print('◣' + '--' * 80) + + isContinue = input('任意键继续? N 退出 \n') + if isContinue.strip(' ') == "N": + exit(1) + + + # ========== 野鸡番号拖动 ========== + # number_argparse = argparse_get_file() + # if not number_argparse == '': + # print("[!]Making Data for [" + number_argparse + "], the number is [" + getNumber(number_argparse, + # absolute_path=True) + "]") + # nfo = core_main(number_argparse, getNumber(number_argparse, absolute_path=True)) + # print("[*]======================================================") + # CEF(config.success_folder) + # CEF(config.failed_folder) + # print("[+]All finished!!!") + # input("[+][+]Press enter key exit, you can check the error messge before you exit.") + # os._exit(0) + # ========== 野鸡番号拖动 ========== + + def download_code_infos(code_list, is_read_cache=True): + """ + 遍历按番号分组的集合,刮取番号信息并缓存 + + :param is_read_cache: 是否读取缓存数据 + :param code_list: + :return: {code:nfo} + """ + count_all_grouped = len(code_list) + count = 0 + code_info_dict = {} + + for code in code_list: + count = count + 1 + percentage = str(count / int(count_all_grouped) * 100)[:4] + '%' + print('[!] - ' + percentage + ' [' + str(count) + '/' + str(count_all_grouped) + '] -') + try: + print("[!]搜刮数据 [" + code + "]") + if code: + # 创建番号的文件夹 + file_path = path_infos + '/' + code + '.json' + nfo = {} + # 读取缓存信息,如果没有则联网搜刮 + + path = Path(file_path) + if is_read_cache and (path.exists() and path.is_file() and path.stat().st_size > 0): + print('找到缓存信息') + with open(file_path) as fp: + nfo = json.load(fp) + else: + + # 核心功能 - 联网抓取信息字典 + print('联网搜刮') + nfo = core_main(code) + print('正在写入', end='') + + # 把缓存信息写入缓存文件夹中,有时会设备占用而失败,重试即可 + @retry(stop=stop_after_delay(3), wait=wait_fixed(2)) + def read_file(): + with open(file_path, 'w') as fp: + json.dump(nfo, fp) + + read_file() + print('完成!') + # 将番号信息放入字典 + code_info_dict[code] = nfo + print("[*]======================================================") + + except Exception as e: # 番号的信息获取失败 + code_info_dict[code] = '' + print("找不到信息:" + code + ',Reason:' + str(e)) + + # if config.soft_link: + # print('[-]Link', file_path_name, 'to failed folder') + # os.symlink(file_path_name, config.failed_folder + '/') + # else: + # try: + # print('[-]Move ' + file_path_name + ' to failed folder:' + config.failed_folder) + # shutil.move(file_path_name, config.failed_folder + '/') + # except FileExistsError: + # print('[!]File exists in failed!') + # except: + # print('[+]skip') + continue + return code_info_dict + + + print('----------------------------------') + code_infos = download_code_infos(data_dict_groupby_code_ep) + print("----未找到番号数据的番号----") + print([print(code) for code in code_infos if code_infos[code] == '']) + print("-------------------------") + + + def download_images_of_nfos(code_info_dict): + """ + 遍历番号信息,下载番号电影的海报,图片 + :param code_info_dict: + :return: 无图片的信息的番号 + """ + + code_list_empty_image = [] + for code in code_info_dict: + nfo = code_info_dict[code] + if len(nfo.keys()) == 0: + code_list_empty_image.append(code) + continue + + code_pics_folder_to_save = path_pics + '/' + code + # 1 创建 番号文件夹 + os.makedirs(code_pics_folder_to_save, exist_ok=True) + # 下载缩略图 + if nfo['imagecut'] == 3: # 3 是缩略图 + path = Path(code_pics_folder_to_save + '/' + 'thumb.png') + if path.exists() and path.is_file() and path.stat().st_size > 0: + print(code + ':缩略图已有缓存') + else: + print(code + ':缩略图下载中...') + download_file(nfo['cover_small'], code_pics_folder_to_save, 'thumb.png') + print(code + ':缩略图下载完成') + # 下载海报 + path = Path(code_pics_folder_to_save + '/' + 'poster.png') + if path.exists() and path.is_file() and path.stat().st_size > 0: + print(code + ':海报已有缓存') + else: + print(code + ':海报下载中...') + download_file(nfo['cover'], code_pics_folder_to_save, 'poster.png') + print(code + ':海报下载完成') + return code_list_empty_image + + + + code_list_empty = download_images_of_nfos(code_infos) + print("----未找到集数的番号----") + print([print(code) for code in code_list_empty]) + print("------搜刮未找到集数的番号------") + code_infos_of_no_ep = download_code_infos(code_list_empty, is_read_cache=False) + print("----还是未找到番号数据的番号----") + print([print(code) for code in code_infos_of_no_ep if code_infos_of_no_ep[code] == '']) + print("----------------------") + # 开始操作 + # # 2 创建缩略图海报 + # if nfo['imagecut'] == 3: # 3 是缩略图 + # download_cover_file(nfo['cover_small'], code, code_pics_folder_to_save) + # # 3 创建图 + # download_image(nfo['cover'], code, code_pics_folder_to_save) + # # 4 剪裁 + # crop_image(nfo['imagecut'], code, code_pics_folder_to_save) + # # 5 背景图 + # copy_images_to_background_image(code, code_pics_folder_to_save) + # 6 创建 mame.nfo(不需要,需要时从infos中josn文件转为nfo文件) + # make_nfo_file(nfo, code, temp_path_to_save) + # 相同番号处理:按集数添加-CD[X];视频格式 and 大小 分; + # TODO 方式1 刮削:添加nfo,封面,内容截图等 + # 6 创建 mame.nfo(不需要,需要时从infos中josn文件转为nfo文件) + make_nfo_file(nfo, code, temp_path_to_save) + # TODO 方式2 整理:按规则移动影片,字幕 到 演员,发行商,有无🐎 等 + + # if config.program_mode == '1': + # if multi_part == 1: + # number += part # 这时number会被附加上CD1后缀 + # smallCoverCheck(path, number, imagecut, json_data['cover_small'], c_word, option, filepath, config.failed_folder) # 检查小封面 + # imageDownload(option, json_data['cover'], number, c_word, path, multi_part, filepath, config.failed_folder) # creatFoder会返回番号路径 + # cutImage(option, imagecut, path, number, c_word) # 裁剪图 + # copyRenameJpgToBackdrop(option, path, number, c_word) + # PrintFiles(option, path, c_word, json_data['naming_rule'], part, cn_sub, json_data, filepath, config.failed_folder, tag) # 打印文件 .nfo + # pasteFileToFolder(filepath, path, number, c_word) # 移动文件 + # # =======================================================================整理模式 + # elif config.program_mode == '2': + # pasteFileToFolder_mode2(filepath, path, multi_part, number, part, c_word) # 移动文件 + + # CEF(config.success_folder) + # CEF(config.failed_folder) + print("[+]All finished!!!") + input("[+][+]Press enter key exit, you can check the error message before you exit.") diff --git a/ConfigApp.py b/ConfigApp.py new file mode 100755 index 0000000..0d8c835 --- /dev/null +++ b/ConfigApp.py @@ -0,0 +1,28 @@ +from configparser import ConfigParser + +from MediaServer import MediaServer + + +class ConfigApp: + def __init__(self): + config_file = 'config.ini' + config = ConfigParser() + config.read(config_file, encoding='UTF-8') + self.success_folder = config['common']['success_output_folder'] + self.failed_folder = config['common']['failed_output_folder'] # 失败输出目录 + self.escape_folder = config['escape']['folders'] # 多级目录刮削需要排除的目录 + self.search_folder = config['common']['search_folder'] # 搜索路径 + self.temp_folder = config['common']['temp_folder'] # 临时资源路径 + self.soft_link = (config['common']['soft_link'] == 1) + # self.escape_literals = (config['escape']['literals'] == 1) + self.naming_rule = config['Name_Rule']['naming_rule'] + self.location_rule = config['Name_Rule']['location_rule'] + + self.proxy = config['proxy']['proxy'] + self.timeout = float(config['proxy']['timeout']) + self.retry = int(config['proxy']['retry']) + self.media_server = MediaServer[config['media']['media_warehouse']] + self.update_check = config['update']['update_check'] + self.debug_mode = config['debug_mode']['switch'] + + diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/LearningNote/GroupbyDemo.py b/LearningNote/GroupbyDemo.py new file mode 100644 index 0000000..558a415 --- /dev/null +++ b/LearningNote/GroupbyDemo.py @@ -0,0 +1,19 @@ +import pandas as pd +import numpy as np + +df = pd.DataFrame({'A': ['foo', 'bar', 'foo', 'bar', + 'foo', 'bar', 'foo', 'foo'], + 'B': ['one', 'one', 'two', 'three', + 'two', 'two', 'one', 'three'], + 'C': np.random.randn(8), + 'D': np.random.randn(8)}) + +print(df) +groupedA = df.groupby('A').describe() +groupedAB = df.groupby(['A', 'B'])['C'] +print('---'*18) +for a, b in groupedAB: + print('--'*18) + print(a) + print('-' * 18) + print(b) diff --git a/LearningNote/PandasDemo.py b/LearningNote/PandasDemo.py new file mode 100644 index 0000000..0ed8aad --- /dev/null +++ b/LearningNote/PandasDemo.py @@ -0,0 +1,38 @@ +import pandas as pd +import numpy as np + +''' +python数据处理三剑客之一pandas +https://pandas.pydata.org/pandas-docs/stable/user_guide +https://www.pypandas.cn/docs/getting_started/10min.html +''' + +dates = pd.date_range('20130101', periods=6) +df = pd.DataFrame(np.random.randn(6, 4), index=dates, columns=list('ABCD')) +print(dates) +print(df) + +df2 = pd.DataFrame({'A': 1., + 'B': pd.Timestamp('20130102'), + 'C': pd.Series(1, index=list(range(4)), dtype='float32'), + 'D': np.array([3] * 4, dtype='int32'), + 'E': pd.Categorical(["test", "train", "test", "train"]), + 'F': 'foo'}) +print(df2) +print(df2.dtypes) +print(df.head()) +print(df.tail(5)) +print(df.index) +print(df.columns) +df.describe() # 统计数据摘要 +df.T # index columns互转 +df.sort_index(axis=1, ascending=False) # 排序,axis=1 是columns,axis=1 是index +df.sort_values(by='B') # 按值排序 按B列中的值排序 + +# 切行 +df.A +df['A'] +# 切行 +df['20130102':'20130104'] +df[0:3] + diff --git a/MediaServer.py b/MediaServer.py new file mode 100644 index 0000000..52e1530 --- /dev/null +++ b/MediaServer.py @@ -0,0 +1,28 @@ +from enum import Enum, auto + + +class MediaServer(Enum): + EMBY = auto() + PLEX = auto() + KODI = auto() + + # media = EMBY + # + # def __init__(self, arg): + # self = [e for e in MediaServer if arg.upper() == self.name] + + def poster_name(self, name): + if self == MediaServer.EMBY: # 保存[name].png + return name + '.png' + elif self == MediaServer.KODI: # 保存[name]-poster.jpg + return name + '-poster.jpg' + elif self == MediaServer.PLEX: # 保存 poster.jpg + return 'poster.jpg' + + def image_name(self, name): + if self == MediaServer.EMBY: # name.jpg + return name + '.jpg' + elif self == MediaServer.KODI: # [name]-fanart.jpg + return name + '-fanart.jpg' + elif self == MediaServer.PLEX: # fanart.jpg + return 'fanart.jpg' diff --git a/Metadate.py b/Metadate.py new file mode 100644 index 0000000..9acf3c5 --- /dev/null +++ b/Metadate.py @@ -0,0 +1,3 @@ +from addict import Dict + +# class Metadata: diff --git a/PathNameProcessor.py b/PathNameProcessor.py new file mode 100644 index 0000000..fd87842 --- /dev/null +++ b/PathNameProcessor.py @@ -0,0 +1,115 @@ +import re + +import fuckit + + +class PathNameProcessor: + # 类变量 + pattern_of_file_name_suffixes = r'.(mov|mp4|avi|rmvb|wmv|mov|mkv|flv|ts|m2ts)$' + + # def __init__(self): + + @staticmethod + def remove_distractions(origin_name): + """移除干扰项""" + # 移除文件类型后缀 + origin_name = re.sub(PathNameProcessor.pattern_of_file_name_suffixes, '', origin_name, 0, re.IGNORECASE) + + # 处理包含减号-和_的番号'/-070409_621' + origin_name = re.sub(r'[-_~*# ]', "-", origin_name, 0) + + origin_name = re.sub(r'(Carib)(bean)?', '-', origin_name, 0, re.IGNORECASE) + origin_name = re.sub(r'(1pondo)', '-', origin_name, 0, re.IGNORECASE) + origin_name = re.sub(r'(tokyo)[-. ]?(hot)', '-', origin_name, 0, re.IGNORECASE) + origin_name = re.sub(r'Uncensored', '-', origin_name, 0, re.IGNORECASE) + origin_name = re.sub(r'JAV', '-', origin_name, 0, re.IGNORECASE) + # 移除干扰字段 + origin_name = origin_name.replace('22-sht.me', '-') + + # 去除文件名中时间 1970-2099年 月 日 + pattern_of_date = r'(?:-)(19[789]\d|20\d{2})(-?(0\d|1[012])-?(0[1-9]|[12]\d|3[01])?)?[-.]' + # 移除字母开头 清晰度相关度 字符 + pattern_of_resolution_alphas = r'(? NTTR-037 , SIVR-00008 -> SIVR-008 ,但是heyzo除外 + if "heyzo" not in name.lower(): + searched = re.search(r'([a-zA-Z]{2,})-(?:0*)(\d{3,})', name) + if searched: + name = '-'.join(searched.groups()) + + return episode, name + + @staticmethod + def extract_episode_behind_code(origin_name, code): + episode = None + + with fuckit: + # 零宽断言获取尾部字幕 剧集数 abc123 + result_dict = re.search(rf'(?<={code})-?((?P([A-Z](?![A-Z])))|(?P\d(?!\d)))', origin_name, + re.I).groupdict() + episode = result_dict['alpha'] or result_dict['num'] + return episode + + +def safe_list_get(list_in, idx, default): + try: + return list_in[idx] + except IndexError: + return default diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..cca1b93 --- /dev/null +++ b/Pipfile @@ -0,0 +1,19 @@ +[[source]] +name = "pypi" +url = "https://pypi.org/simple" +verify_ssl = true + +[dev-packages] + +[packages] +bs4 = "*" +tenacity = "*" +fuckit = "*" +requests = "*" +image = "*" +lazyxml = {editable = true,git = "https://github.com/waynedyck/lazyxml.git",ref = "python-3-conversion_wd1"} +lxml = "*" +pyquery = "*" + +[requires] +python_version = "3.8" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..1ca43ea --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,246 @@ +{ + "_meta": { + "hash": { + "sha256": "15bf3c6af3ec315358a0217481a13285f95fc742bb5db8a1f934e0d1c3d7d5e2" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.8" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "asgiref": { + "hashes": [ + "sha256:5ee950735509d04eb673bd7f7120f8fa1c9e2df495394992c73234d526907e17", + "sha256:7162a3cb30ab0609f1a4c95938fd73e8604f63bdba516a7f7d64b83ff09478f0" + ], + "markers": "python_version >= '3.5'", + "version": "==3.3.1" + }, + "beautifulsoup4": { + "hashes": [ + "sha256:4c98143716ef1cb40bf7f39a8e3eec8f8b009509e74904ba3a7b315431577e35", + "sha256:84729e322ad1d5b4d25f805bfa05b902dd96450f43842c4e99067d5e1369eb25", + "sha256:fff47e031e34ec82bf17e00da8f592fe7de69aeea38be00523c04623c04fb666" + ], + "version": "==4.9.3" + }, + "bs4": { + "hashes": [ + "sha256:36ecea1fd7cc5c0c6e4a1ff075df26d50da647b75376626cc186e2212886dd3a" + ], + "index": "pypi", + "version": "==0.0.1" + }, + "certifi": { + "hashes": [ + "sha256:1a4995114262bffbc2413b159f2a1a480c969de6e6eb13ee966d470af86af59c", + "sha256:719a74fb9e33b9bd44cc7f3a8d94bc35e4049deebe19ba7d8e108280cfd59830" + ], + "version": "==2020.12.5" + }, + "chardet": { + "hashes": [ + "sha256:0d6f53a15db4120f2b08c94f11e7d93d2c911ee118b6b30a04ec3ee8310179fa", + "sha256:f864054d66fd9118f2e67044ac8981a54775ec5b67aed0441892edb553d21da5" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==4.0.0" + }, + "cssselect": { + "hashes": [ + "sha256:f612ee47b749c877ebae5bb77035d8f4202c6ad0f0fc1271b3c18ad6c4468ecf", + "sha256:f95f8dedd925fd8f54edb3d2dfb44c190d9d18512377d3c1e2388d16126879bc" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.1.0" + }, + "django": { + "hashes": [ + "sha256:2d78425ba74c7a1a74b196058b261b9733a8570782f4e2828974777ccca7edf7", + "sha256:efa2ab96b33b20c2182db93147a0c3cd7769d418926f9e9f140a60dca7c64ca9" + ], + "markers": "python_version >= '3.6'", + "version": "==3.1.5" + }, + "fuckit": { + "hashes": [ + "sha256:059488e6aa2053da9db5eb5101e2498f608314da5118bf2385acb864568ccc25" + ], + "index": "pypi", + "version": "==4.8.1" + }, + "idna": { + "hashes": [ + "sha256:b307872f855b18632ce0c21c5e45be78c0ea7ae4c15c828c20788b26921eb3f6", + "sha256:b97d804b1e9b523befed77c48dacec60e6dcb0b5391d57af6a65a312a90648c0" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==2.10" + }, + "image": { + "hashes": [ + "sha256:baa2e09178277daa50f22fd6d1d51ec78f19c12688921cb9ab5808743f097126" + ], + "index": "pypi", + "version": "==1.5.33" + }, + "lazyxml": { + "editable": true, + "git": "https://github.com/waynedyck/lazyxml.git", + "ref": "f42ea4a4febf4c1e120b05d6ca9cef42556a75d5" + }, + "lxml": { + "hashes": [ + "sha256:0448576c148c129594d890265b1a83b9cd76fd1f0a6a04620753d9a6bcfd0a4d", + "sha256:127f76864468d6630e1b453d3ffbbd04b024c674f55cf0a30dc2595137892d37", + "sha256:1471cee35eba321827d7d53d104e7b8c593ea3ad376aa2df89533ce8e1b24a01", + "sha256:2363c35637d2d9d6f26f60a208819e7eafc4305ce39dc1d5005eccc4593331c2", + "sha256:2e5cc908fe43fe1aa299e58046ad66981131a66aea3129aac7770c37f590a644", + "sha256:2e6fd1b8acd005bd71e6c94f30c055594bbd0aa02ef51a22bbfa961ab63b2d75", + "sha256:366cb750140f221523fa062d641393092813b81e15d0e25d9f7c6025f910ee80", + "sha256:42ebca24ba2a21065fb546f3e6bd0c58c3fe9ac298f3a320147029a4850f51a2", + "sha256:4e751e77006da34643ab782e4a5cc21ea7b755551db202bc4d3a423b307db780", + "sha256:4fb85c447e288df535b17ebdebf0ec1cf3a3f1a8eba7e79169f4f37af43c6b98", + "sha256:50c348995b47b5a4e330362cf39fc503b4a43b14a91c34c83b955e1805c8e308", + "sha256:535332fe9d00c3cd455bd3dd7d4bacab86e2d564bdf7606079160fa6251caacf", + "sha256:535f067002b0fd1a4e5296a8f1bf88193080ff992a195e66964ef2a6cfec5388", + "sha256:5be4a2e212bb6aa045e37f7d48e3e1e4b6fd259882ed5a00786f82e8c37ce77d", + "sha256:60a20bfc3bd234d54d49c388950195d23a5583d4108e1a1d47c9eef8d8c042b3", + "sha256:648914abafe67f11be7d93c1a546068f8eff3c5fa938e1f94509e4a5d682b2d8", + "sha256:681d75e1a38a69f1e64ab82fe4b1ed3fd758717bed735fb9aeaa124143f051af", + "sha256:68a5d77e440df94011214b7db907ec8f19e439507a70c958f750c18d88f995d2", + "sha256:69a63f83e88138ab7642d8f61418cf3180a4d8cd13995df87725cb8b893e950e", + "sha256:6e4183800f16f3679076dfa8abf2db3083919d7e30764a069fb66b2b9eff9939", + "sha256:6fd8d5903c2e53f49e99359b063df27fdf7acb89a52b6a12494208bf61345a03", + "sha256:791394449e98243839fa822a637177dd42a95f4883ad3dec2a0ce6ac99fb0a9d", + "sha256:7a7669ff50f41225ca5d6ee0a1ec8413f3a0d8aa2b109f86d540887b7ec0d72a", + "sha256:7e9eac1e526386df7c70ef253b792a0a12dd86d833b1d329e038c7a235dfceb5", + "sha256:7ee8af0b9f7de635c61cdd5b8534b76c52cd03536f29f51151b377f76e214a1a", + "sha256:8246f30ca34dc712ab07e51dc34fea883c00b7ccb0e614651e49da2c49a30711", + "sha256:8c88b599e226994ad4db29d93bc149aa1aff3dc3a4355dd5757569ba78632bdf", + "sha256:923963e989ffbceaa210ac37afc9b906acebe945d2723e9679b643513837b089", + "sha256:94d55bd03d8671686e3f012577d9caa5421a07286dd351dfef64791cf7c6c505", + "sha256:97db258793d193c7b62d4e2586c6ed98d51086e93f9a3af2b2034af01450a74b", + "sha256:a9d6bc8642e2c67db33f1247a77c53476f3a166e09067c0474facb045756087f", + "sha256:cd11c7e8d21af997ee8079037fff88f16fda188a9776eb4b81c7e4c9c0a7d7fc", + "sha256:d8d3d4713f0c28bdc6c806a278d998546e8efc3498949e3ace6e117462ac0a5e", + "sha256:e0bfe9bb028974a481410432dbe1b182e8191d5d40382e5b8ff39cdd2e5c5931", + "sha256:f4822c0660c3754f1a41a655e37cb4dbbc9be3d35b125a37fab6f82d47674ebc", + "sha256:f83d281bb2a6217cd806f4cf0ddded436790e66f393e124dfe9731f6b3fb9afe", + "sha256:fc37870d6716b137e80d19241d0e2cff7a7643b925dfa49b4c8ebd1295eb506e" + ], + "index": "pypi", + "version": "==4.6.2" + }, + "pillow": { + "hashes": [ + "sha256:165c88bc9d8dba670110c689e3cc5c71dbe4bfb984ffa7cbebf1fac9554071d6", + "sha256:1d208e670abfeb41b6143537a681299ef86e92d2a3dac299d3cd6830d5c7bded", + "sha256:22d070ca2e60c99929ef274cfced04294d2368193e935c5d6febfd8b601bf865", + "sha256:2353834b2c49b95e1313fb34edf18fca4d57446675d05298bb694bca4b194174", + "sha256:39725acf2d2e9c17356e6835dccebe7a697db55f25a09207e38b835d5e1bc032", + "sha256:3de6b2ee4f78c6b3d89d184ade5d8fa68af0848f9b6b6da2b9ab7943ec46971a", + "sha256:47c0d93ee9c8b181f353dbead6530b26980fe4f5485aa18be8f1fd3c3cbc685e", + "sha256:5e2fe3bb2363b862671eba632537cd3a823847db4d98be95690b7e382f3d6378", + "sha256:604815c55fd92e735f9738f65dabf4edc3e79f88541c221d292faec1904a4b17", + "sha256:6c5275bd82711cd3dcd0af8ce0bb99113ae8911fc2952805f1d012de7d600a4c", + "sha256:731ca5aabe9085160cf68b2dbef95fc1991015bc0a3a6ea46a371ab88f3d0913", + "sha256:7612520e5e1a371d77e1d1ca3a3ee6227eef00d0a9cddb4ef7ecb0b7396eddf7", + "sha256:7916cbc94f1c6b1301ac04510d0881b9e9feb20ae34094d3615a8a7c3db0dcc0", + "sha256:81c3fa9a75d9f1afafdb916d5995633f319db09bd773cb56b8e39f1e98d90820", + "sha256:887668e792b7edbfb1d3c9d8b5d8c859269a0f0eba4dda562adb95500f60dbba", + "sha256:93a473b53cc6e0b3ce6bf51b1b95b7b1e7e6084be3a07e40f79b42e83503fbf2", + "sha256:96d4dc103d1a0fa6d47c6c55a47de5f5dafd5ef0114fa10c85a1fd8e0216284b", + "sha256:a3d3e086474ef12ef13d42e5f9b7bbf09d39cf6bd4940f982263d6954b13f6a9", + "sha256:b02a0b9f332086657852b1f7cb380f6a42403a6d9c42a4c34a561aa4530d5234", + "sha256:b09e10ec453de97f9a23a5aa5e30b334195e8d2ddd1ce76cc32e52ba63c8b31d", + "sha256:b6f00ad5ebe846cc91763b1d0c6d30a8042e02b2316e27b05de04fa6ec831ec5", + "sha256:bba80df38cfc17f490ec651c73bb37cd896bc2400cfba27d078c2135223c1206", + "sha256:c3d911614b008e8a576b8e5303e3db29224b455d3d66d1b2848ba6ca83f9ece9", + "sha256:ca20739e303254287138234485579b28cb0d524401f83d5129b5ff9d606cb0a8", + "sha256:cb192176b477d49b0a327b2a5a4979552b7a58cd42037034316b8018ac3ebb59", + "sha256:cdbbe7dff4a677fb555a54f9bc0450f2a21a93c5ba2b44e09e54fcb72d2bd13d", + "sha256:cf6e33d92b1526190a1de904df21663c46a456758c0424e4f947ae9aa6088bf7", + "sha256:d355502dce85ade85a2511b40b4c61a128902f246504f7de29bbeec1ae27933a", + "sha256:d673c4990acd016229a5c1c4ee8a9e6d8f481b27ade5fc3d95938697fa443ce0", + "sha256:dc577f4cfdda354db3ae37a572428a90ffdbe4e51eda7849bf442fb803f09c9b", + "sha256:dd9eef866c70d2cbbea1ae58134eaffda0d4bfea403025f4db6859724b18ab3d", + "sha256:f50e7a98b0453f39000619d845be8b06e611e56ee6e8186f7f60c3b1e2f0feae" + ], + "markers": "python_version >= '3.6'", + "version": "==8.1.0" + }, + "pyquery": { + "hashes": [ + "sha256:1fc33b7699455ed25c75282bc8f80ace1ac078b0dda5a933dacbd8b1c1f83963", + "sha256:a388eefb6bc4a55350de0316fbd97cda999ae669b6743ae5b99102ba54f5aa72" + ], + "index": "pypi", + "version": "==1.4.3" + }, + "pytz": { + "hashes": [ + "sha256:16962c5fb8db4a8f63a26646d8886e9d769b6c511543557bc84e9569fb9a9cb4", + "sha256:180befebb1927b16f6b57101720075a984c019ac16b1b7575673bea42c6c3da5" + ], + "version": "==2020.5" + }, + "requests": { + "hashes": [ + "sha256:27973dd4a904a4f13b263a19c866c13b92a39ed1c964655f025f3f8d3d75b804", + "sha256:c210084e36a42ae6b9219e00e48287def368a26d03a048ddad7bfee44f75871e" + ], + "index": "pypi", + "version": "==2.25.1" + }, + "six": { + "hashes": [ + "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259", + "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3'", + "version": "==1.15.0" + }, + "soupsieve": { + "hashes": [ + "sha256:4bb21a6ee4707bf43b61230e80740e71bfe56e55d1f1f50924b087bb2975c851", + "sha256:6dc52924dc0bc710a5d16794e6b3480b2c7c08b07729505feab2b2c16661ff6e" + ], + "markers": "python_version >= '3.0'", + "version": "==2.1" + }, + "sqlparse": { + "hashes": [ + "sha256:017cde379adbd6a1f15a61873f43e8274179378e95ef3fede90b5aa64d304ed0", + "sha256:0f91fd2e829c44362cbcfab3e9ae12e22badaa8a29ad5ff599f9ec109f0454e8" + ], + "markers": "python_version >= '3.5'", + "version": "==0.4.1" + }, + "tenacity": { + "hashes": [ + "sha256:baed357d9f35ec64264d8a4bbf004c35058fad8795c5b0d8a7dc77ecdcbb8f39", + "sha256:e14d191fb0a309b563904bbc336582efe2037de437e543b38da749769b544d7f" + ], + "index": "pypi", + "version": "==6.3.1" + }, + "urllib3": { + "hashes": [ + "sha256:19188f96923873c92ccb987120ec4acaa12f0461fa9ce5d3d0772bc965a39e08", + "sha256:d8ff90d979214d7b4f8ce956e80f4028fc6860e4431f731ea4a8c08f23f99473" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4' and python_version < '4'", + "version": "==1.26.2" + } + }, + "develop": {} +} diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/avsox.py b/SiteSource/avsox.py old mode 100644 new mode 100755 similarity index 96% rename from avsox.py rename to SiteSource/avsox.py index 67ee9bf..87ae401 --- a/avsox.py +++ b/SiteSource/avsox.py @@ -1,115 +1,116 @@ -import re -from lxml import etree -import json -from bs4 import BeautifulSoup -from ADC_function import * -# import sys -# import io -# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) - -def getActorPhoto(htmlcode): #//*[@id="star_qdt"]/li/a/img - soup = BeautifulSoup(htmlcode, 'lxml') - a = soup.find_all(attrs={'class': 'avatar-box'}) - d = {} - for i in a: - l = i.img['src'] - t = i.span.get_text() - p2 = {t: l} - d.update(p2) - return d -def getTitle(a): - try: - html = etree.fromstring(a, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[2]/h3/text()')).strip(" ['']") #[0] - return result.replace('/', '') - except: - return '' -def getActor(a): #//*[@id="center_column"]/div[2]/div[1]/div/table/tbody/tr[1]/td/text() - soup = BeautifulSoup(a, 'lxml') - a = soup.find_all(attrs={'class': 'avatar-box'}) - d = [] - for i in a: - d.append(i.span.get_text()) - return d -def getStudio(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//p[contains(text(),"制作商: ")]/following-sibling::p[1]/a/text()')).strip(" ['']").replace("', '",' ') - return result1 -def getRuntime(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//span[contains(text(),"长度:")]/../text()')).strip(" ['分钟']") - return result1 -def getLabel(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//p[contains(text(),"系列:")]/following-sibling::p[1]/a/text()')).strip(" ['']") - return result1 -def getNum(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//span[contains(text(),"识别码:")]/../span[2]/text()')).strip(" ['']") - return result1 -def getYear(release): - try: - result = str(re.search('\d{4}',release).group()) - return result - except: - return release -def getRelease(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//span[contains(text(),"发行时间:")]/../text()')).strip(" ['']") - return result1 -def getCover(htmlcode): - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[2]/div[1]/div[1]/a/img/@src')).strip(" ['']") - return result -def getCover_small(htmlcode): - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('//*[@id="waterfall"]/div/a/div[1]/img/@src')).strip(" ['']") - return result -def getTag(a): # 获取演员 - soup = BeautifulSoup(a, 'lxml') - a = soup.find_all(attrs={'class': 'genre'}) - d = [] - for i in a: - d.append(i.get_text()) - return d - -def main(number): - a = get_html('https://avsox.host/cn/search/' + number) - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//*[@id="waterfall"]/div/a/@href')).strip(" ['']") - if result1 == '' or result1 == 'null' or result1 == 'None': - a = get_html('https://avsox.host/cn/search/' + number.replace('-', '_')) - print(a) - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//*[@id="waterfall"]/div/a/@href')).strip(" ['']") - if result1 == '' or result1 == 'null' or result1 == 'None': - a = get_html('https://avsox.host/cn/search/' + number.replace('_', '')) - print(a) - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//*[@id="waterfall"]/div/a/@href')).strip(" ['']") - web = get_html(result1) - soup = BeautifulSoup(web, 'lxml') - info = str(soup.find(attrs={'class': 'row movie'})) - dic = { - 'actor': getActor(web), - 'title': getTitle(web).strip(getNum(web)), - 'studio': getStudio(info), - 'outline': '',# - 'runtime': getRuntime(info), - 'director': '', # - 'release': getRelease(info), - 'number': getNum(info), - 'cover': getCover(web), - 'cover_small': getCover_small(a), - 'imagecut': 3, - 'tag': getTag(web), - 'label': getLabel(info), - 'year': getYear(getRelease(info)), # str(re.search('\d{4}',getRelease(a)).group()), - 'actor_photo': getActorPhoto(web), - 'website': result1, - 'source': 'avsox.py', - } - js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') - return js - +import re +from lxml import etree +import json +from bs4 import BeautifulSoup +from ADC_function import * +# import sys +# import io +# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) + +def getActorPhoto(htmlcode): #//*[@id="star_qdt"]/li/a/img + soup = BeautifulSoup(htmlcode, 'lxml') + a = soup.find_all(attrs={'class': 'avatar-box'}) + d = {} + for i in a: + l = i.img['src'] + t = i.span.get_text() + p2 = {t: l} + d.update(p2) + return d +def getTitle(a): + try: + html = etree.fromstring(a, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[2]/h3/text()')).strip(" ['']") #[0] + return result.replace('/', '') + except: + return '' +def getActor(a): #//*[@id="center_column"]/div[2]/div[1]/div/table/tbody/tr[1]/td/text() + soup = BeautifulSoup(a, 'lxml') + a = soup.find_all(attrs={'class': 'avatar-box'}) + d = [] + for i in a: + d.append(i.span.get_text()) + return d +def getStudio(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//p[contains(text(),"制作商: ")]/following-sibling::p[1]/a/text()')).strip(" ['']").replace("', '",' ') + return result1 +def getRuntime(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//span[contains(text(),"长度:")]/../text()')).strip(" ['分钟']") + return result1 +def getLabel(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//p[contains(text(),"系列:")]/following-sibling::p[1]/a/text()')).strip(" ['']") + return result1 +def getNum(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//span[contains(text(),"识别码:")]/../span[2]/text()')).strip(" ['']") + return result1 +def getYear(release): + try: + result = str(re.search('\d{4}',release).group()) + return result + except: + return release +def getRelease(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//span[contains(text(),"发行时间:")]/../text()')).strip(" ['']") + return result1 +def getCover(htmlcode): + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[2]/div[1]/div[1]/a/img/@src')).strip(" ['']") + return result +def getCover_small(htmlcode): + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('//*[@id="waterfall"]/div/a/div[1]/img/@src')).strip(" ['']") + return result +def getTag(a): # 获取演员 + soup = BeautifulSoup(a, 'lxml') + a = soup.find_all(attrs={'class': 'genre'}) + d = [] + for i in a: + d.append(i.get_text()) + return d + +def main(number): + url = 'https://avsox.host/cn/search/' + number + a = get_html(url) + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//*[@id="waterfall"]/div/a/@href')).strip(" ['']") + if result1 == '' or result1 == 'null' or result1 == 'None': + a = get_html('https://avsox.host/cn/search/' + number.replace('-', '_')) + print(a) + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//*[@id="waterfall"]/div/a/@href')).strip(" ['']") + if result1 == '' or result1 == 'null' or result1 == 'None': + a = get_html('https://avsox.host/cn/search/' + number.replace('_', '')) + print(a) + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//*[@id="waterfall"]/div/a/@href')).strip(" ['']") + web = get_html(result1) + soup = BeautifulSoup(web, 'lxml') + info = str(soup.find(attrs={'class': 'row movie'})) + dic = { + 'actor': getActor(web), + 'title': getTitle(web).strip(getNum(web)), + 'studio': getStudio(info), + 'outline': '',# + 'runtime': getRuntime(info), + 'director': '', # + 'release': getRelease(info), + 'number': getNum(info), + 'cover': getCover(web), + 'cover_small': getCover_small(a), + 'imagecut': 3, + 'tag': getTag(web), + 'label': getLabel(info), + 'year': getYear(getRelease(info)), # str(re.search('\d{4}',getRelease(a)).group()), + 'actor_photo': getActorPhoto(web), + 'website': result1, + 'source': 'avsox.py', + } + js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') + return js + #print(main('012717_472')) \ No newline at end of file diff --git a/fanza.py b/SiteSource/fanza.py old mode 100644 new mode 100755 similarity index 97% rename from fanza.py rename to SiteSource/fanza.py index 87c8be0..72632dc --- a/fanza.py +++ b/SiteSource/fanza.py @@ -1,229 +1,229 @@ -#!/usr/bin/python3 -# -*- coding: utf-8 -*- -import json -import re - -from lxml import etree - -from ADC_function import * - -# import sys -# import io -# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) - - -def getTitle(text): - html = etree.fromstring(text, etree.HTMLParser()) - result = html.xpath('//*[@id="title"]/text()')[0] - return result - - -def getActor(text): - # //*[@id="center_column"]/div[2]/div[1]/div/table/tbody/tr[1]/td/text() - html = etree.fromstring(text, etree.HTMLParser()) - result = ( - str( - html.xpath( - "//td[contains(text(),'出演者')]/following-sibling::td/span/a/text()" - ) - ) - .strip(" ['']") - .replace("', '", ",") - ) - return result - - -def getStudio(text): - html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - try: - result = html.xpath( - "//td[contains(text(),'メーカー')]/following-sibling::td/a/text()" - )[0] - except: - result = html.xpath( - "//td[contains(text(),'メーカー')]/following-sibling::td/text()" - )[0] - return result - - -def getRuntime(text): - html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result = html.xpath("//td[contains(text(),'収録時間')]/following-sibling::td/text()")[0] - return re.search(r"\d+", str(result)).group() - - -def getLabel(text): - html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - try: - result = html.xpath( - "//td[contains(text(),'シリーズ:')]/following-sibling::td/a/text()" - )[0] - except: - result = html.xpath( - "//td[contains(text(),'シリーズ:')]/following-sibling::td/text()" - )[0] - return result - - -def getNum(text): - html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - try: - result = html.xpath( - "//td[contains(text(),'品番:')]/following-sibling::td/a/text()" - )[0] - except: - result = html.xpath( - "//td[contains(text(),'品番:')]/following-sibling::td/text()" - )[0] - return result - - -def getYear(getRelease): - try: - result = str(re.search(r"\d{4}", getRelease).group()) - return result - except: - return getRelease - - -def getRelease(text): - html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - try: - result = html.xpath( - "//td[contains(text(),'発売日:')]/following-sibling::td/a/text()" - )[0].lstrip("\n") - except: - result = html.xpath( - "//td[contains(text(),'発売日:')]/following-sibling::td/text()" - )[0].lstrip("\n") - return result - - -def getTag(text): - html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - try: - result = html.xpath( - "//td[contains(text(),'ジャンル:')]/following-sibling::td/a/text()" - ) - except: - result = html.xpath( - "//td[contains(text(),'ジャンル:')]/following-sibling::td/text()" - ) - return result - - -def getCover(text, number): - html = etree.fromstring(text, etree.HTMLParser()) - cover_number = number - try: - result = html.xpath('//*[@id="' + cover_number + '"]/@href')[0] - except: - # sometimes fanza modify _ to \u0005f for image id - if "_" in cover_number: - cover_number = cover_number.replace("_", r"\u005f") - try: - result = html.xpath('//*[@id="' + cover_number + '"]/@href')[0] - except: - # (TODO) handle more edge case - # print(html) - # raise exception here, same behavior as before - # people's major requirement is fetching the picture - raise ValueError("can not find image") - return result - - -def getDirector(text): - html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - try: - result = html.xpath( - "//td[contains(text(),'監督:')]/following-sibling::td/a/text()" - )[0] - except: - result = html.xpath( - "//td[contains(text(),'監督:')]/following-sibling::td/text()" - )[0] - return result - - -def getOutline(text): - html = etree.fromstring(text, etree.HTMLParser()) - try: - result = str(html.xpath("//div[@class='mg-b20 lh4']/text()")[0]).replace( - "\n", "" - ) - if result == "": - result = str(html.xpath("//div[@class='mg-b20 lh4']//p/text()")[0]).replace( - "\n", "" - ) - except: - # (TODO) handle more edge case - # print(html) - return "" - return result - - -def main(number): - # fanza allow letter + number + underscore, normalize the input here - # @note: I only find the usage of underscore as h_test123456789 - fanza_search_number = number - # AV_Data_Capture.py.getNumber() over format the input, restore the h_ prefix - if fanza_search_number.startswith("h-"): - fanza_search_number = fanza_search_number.replace("h-", "h_") - - fanza_search_number = re.sub(r"[^0-9a-zA-Z_]", "", fanza_search_number).lower() - - fanza_urls = [ - "https://www.dmm.co.jp/digital/videoa/-/detail/=/cid=", - "https://www.dmm.co.jp/mono/dvd/-/detail/=/cid=", - "https://www.dmm.co.jp/digital/anime/-/detail/=/cid=", - "https://www.dmm.co.jp/mono/anime/-/detail/=/cid=", - ] - chosen_url = "" - for url in fanza_urls: - chosen_url = url + fanza_search_number - htmlcode = get_html(chosen_url) - if "404 Not Found" not in htmlcode: - break - if "404 Not Found" in htmlcode: - return json.dumps({"title": "",}) - try: - # for some old page, the input number does not match the page - # for example, the url will be cid=test012 - # but the hinban on the page is test00012 - # so get the hinban first, and then pass it to following functions - fanza_hinban = getNum(htmlcode) - data = { - "title": getTitle(htmlcode).strip(getActor(htmlcode)), - "studio": getStudio(htmlcode), - "outline": getOutline(htmlcode), - "runtime": getRuntime(htmlcode), - "director": getDirector(htmlcode) if "anime" not in chosen_url else "", - "actor": getActor(htmlcode) if "anime" not in chosen_url else "", - "release": getRelease(htmlcode), - "number": fanza_hinban, - "cover": getCover(htmlcode, fanza_hinban), - "imagecut": 1, - "tag": getTag(htmlcode), - "label": getLabel(htmlcode), - "year": getYear( - getRelease(htmlcode) - ), # str(re.search('\d{4}',getRelease(a)).group()), - "actor_photo": "", - "website": chosen_url, - "source": "fanza.py", - } - except: - data = { - "title": "", - } - js = json.dumps( - data, ensure_ascii=False, sort_keys=True, indent=4, separators=(",", ":") - ) # .encode('UTF-8') - return js - - -if __name__ == "__main__": - # print(main("DV-1562")) - # input("[+][+]Press enter key exit, you can check the error messge before you exit.\n[+][+]按回车键结束,你可以在结束之前查看和错误信息。") - # print(main("ipx292")) - pass +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +import json +import re + +from lxml import etree + +from ADC_function import * + +# import sys +# import io +# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) + + +def getTitle(text): + html = etree.fromstring(text, etree.HTMLParser()) + result = html.xpath('//*[@id="title"]/text()')[0] + return result + + +def getActor(text): + # //*[@id="center_column"]/div[2]/div[1]/div/table/tbody/tr[1]/td/text() + html = etree.fromstring(text, etree.HTMLParser()) + result = ( + str( + html.xpath( + "//td[contains(text(),'出演者')]/following-sibling::td/span/a/text()" + ) + ) + .strip(" ['']") + .replace("', '", ",") + ) + return result + + +def getStudio(text): + html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + try: + result = html.xpath( + "//td[contains(text(),'メーカー')]/following-sibling::td/a/text()" + )[0] + except: + result = html.xpath( + "//td[contains(text(),'メーカー')]/following-sibling::td/text()" + )[0] + return result + + +def getRuntime(text): + html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result = html.xpath("//td[contains(text(),'収録時間')]/following-sibling::td/text()")[0] + return re.search(r"\d+", str(result)).group() + + +def getLabel(text): + html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + try: + result = html.xpath( + "//td[contains(text(),'シリーズ:')]/following-sibling::td/a/text()" + )[0] + except: + result = html.xpath( + "//td[contains(text(),'シリーズ:')]/following-sibling::td/text()" + )[0] + return result + + +def getNum(text): + html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + try: + result = html.xpath( + "//td[contains(text(),'品番:')]/following-sibling::td/a/text()" + )[0] + except: + result = html.xpath( + "//td[contains(text(),'品番:')]/following-sibling::td/text()" + )[0] + return result + + +def getYear(getRelease): + try: + result = str(re.search(r"\d{4}", getRelease).group()) + return result + except: + return getRelease + + +def getRelease(text): + html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + try: + result = html.xpath( + "//td[contains(text(),'発売日:')]/following-sibling::td/a/text()" + )[0].lstrip("\n") + except: + result = html.xpath( + "//td[contains(text(),'発売日:')]/following-sibling::td/text()" + )[0].lstrip("\n") + return result + + +def getTag(text): + html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + try: + result = html.xpath( + "//td[contains(text(),'ジャンル:')]/following-sibling::td/a/text()" + ) + except: + result = html.xpath( + "//td[contains(text(),'ジャンル:')]/following-sibling::td/text()" + ) + return result + + +def getCover(text, number): + html = etree.fromstring(text, etree.HTMLParser()) + cover_number = number + try: + result = html.xpath('//*[@id="' + cover_number + '"]/@href')[0] + except: + # sometimes fanza modify _ to \u0005f for image id + if "_" in cover_number: + cover_number = cover_number.replace("_", r"\u005f") + try: + result = html.xpath('//*[@id="' + cover_number + '"]/@href')[0] + except: + # (TODO) handle more edge case + # print(html) + # raise exception here, same behavior as before + # people's major requirement is fetching the picture + raise ValueError("can not find image") + return result + + +def getDirector(text): + html = etree.fromstring(text, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + try: + result = html.xpath( + "//td[contains(text(),'監督:')]/following-sibling::td/a/text()" + )[0] + except: + result = html.xpath( + "//td[contains(text(),'監督:')]/following-sibling::td/text()" + )[0] + return result + + +def getOutline(text): + html = etree.fromstring(text, etree.HTMLParser()) + try: + result = str(html.xpath("//div[@class='mg-b20 lh4']/text()")[0]).replace( + "\n", "" + ) + if result == "": + result = str(html.xpath("//div[@class='mg-b20 lh4']//p/text()")[0]).replace( + "\n", "" + ) + except: + # (TODO) handle more edge case + # print(html) + return "" + return result + + +def main(number): + # fanza allow letter + number + underscore, normalize the input here + # @note: I only find the usage of underscore as h_test123456789 + fanza_search_number = number + # AV_Data_Capture.py.getNumber() over format the input, restore the h_ prefix + if fanza_search_number.startswith("h-"): + fanza_search_number = fanza_search_number.replace("h-", "h_") + + fanza_search_number = re.sub(r"[^0-9a-zA-Z_]", "", fanza_search_number).lower() + + fanza_urls = [ + "https://www.dmm.co.jp/digital/videoa/-/detail/=/cid=", + "https://www.dmm.co.jp/mono/dvd/-/detail/=/cid=", + "https://www.dmm.co.jp/digital/anime/-/detail/=/cid=", + "https://www.dmm.co.jp/mono/anime/-/detail/=/cid=", + ] + chosen_url = "" + for url in fanza_urls: + chosen_url = url + fanza_search_number + htmlcode = get_html(chosen_url) + if "404 Not Found" not in htmlcode: + break + if "404 Not Found" in htmlcode: + return json.dumps({"title": "",}) + try: + # for some old page, the input number does not match the page + # for example, the url will be cid=test012 + # but the hinban on the page is test00012 + # so get the hinban first, and then pass it to following functions + fanza_hinban = getNum(htmlcode) + data = { + "title": getTitle(htmlcode).strip(getActor(htmlcode)), + "studio": getStudio(htmlcode), + "outline": getOutline(htmlcode), + "runtime": getRuntime(htmlcode), + "director": getDirector(htmlcode) if "anime" not in chosen_url else "", + "actor": getActor(htmlcode) if "anime" not in chosen_url else "", + "release": getRelease(htmlcode), + "number": fanza_hinban, + "cover": getCover(htmlcode, fanza_hinban), + "imagecut": 1, + "tag": getTag(htmlcode), + "label": getLabel(htmlcode), + "year": getYear( + getRelease(htmlcode) + ), # str(re.search('\d{4}',getRelease(a)).group()), + "actor_photo": "", + "website": chosen_url, + "source": "fanza.py", + } + except: + data = { + "title": "", + } + js = json.dumps( + data, ensure_ascii=False, sort_keys=True, indent=4, separators=(",", ":") + ) # .encode('UTF-8') + return js + + +if __name__ == "__main__": + # print(main("DV-1562")) + # input("[+][+]Press enter key exit, you can check the error messge before you exit.\n[+][+]按回车键结束,你可以在结束之前查看和错误信息。") + # print(main("ipx292")) + pass diff --git a/fc2fans_club.py b/SiteSource/fc2fans_club.py similarity index 97% rename from fc2fans_club.py rename to SiteSource/fc2fans_club.py index 3215e49..9dfeb24 100755 --- a/fc2fans_club.py +++ b/SiteSource/fc2fans_club.py @@ -1,162 +1,162 @@ -import re -from lxml import etree#need install -import json -import ADC_function -# import sys -# import io -# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) - -def getTitle(htmlcode): #获取厂商 - #print(htmlcode) - html = etree.fromstring(htmlcode,etree.HTMLParser()) - result = str(html.xpath('/html/body/div[2]/div/div[1]/h3/text()')).strip(" ['']") - result2 = str(re.sub('\D{2}2-\d+','',result)).replace(' ','',1) - #print(result2) - return result2 -def getActor(htmlcode): - try: - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[2]/div/div[1]/h5[5]/a/text()')).strip(" ['']") - return result - except: - return '' -def getStudio(htmlcode): #获取厂商 - html = etree.fromstring(htmlcode,etree.HTMLParser()) - result = str(html.xpath('/html/body/div[2]/div/div[1]/h5[3]/a[1]/text()')).strip(" ['']") - return result -def getNum(htmlcode): #获取番号 - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[1]/span[2]/text()')).strip(" ['']") - #print(result) - return result -def getRelease(htmlcode2): # - #a=ADC_function.get_html('http://adult.contents.fc2.com/article_search.php?id='+str(number).lstrip("FC2-").lstrip("fc2-").lstrip("fc2_").lstrip("fc2-")+'&utm_source=aff_php&utm_medium=source_code&utm_campaign=from_aff_php') - html=etree.fromstring(htmlcode2,etree.HTMLParser()) - result = str(html.xpath('//*[@id="container"]/div[1]/div/article/section[1]/div/div[2]/dl/dd[4]/text()')).strip(" ['']") - return result -def getCover(htmlcode,number,htmlcode2): #获取厂商 # - #a = ADC_function.get_html('http://adult.contents.fc2.com/article_search.php?id=' + str(number).lstrip("FC2-").lstrip("fc2-").lstrip("fc2_").lstrip("fc2-") + '&utm_source=aff_php&utm_medium=source_code&utm_campaign=from_aff_php') - html = etree.fromstring(htmlcode2, etree.HTMLParser()) - result = str(html.xpath('//*[@id="container"]/div[1]/div/article/section[1]/div/div[1]/a/img/@src')).strip(" ['']") - if result == '': - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result2 = str(html.xpath('//*[@id="slider"]/ul[1]/li[1]/img/@src')).strip(" ['']") - return 'https://fc2club.com' + result2 - return 'http:' + result -def getOutline(htmlcode2): #获取番号 # - html = etree.fromstring(htmlcode2, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[1]/div[2]/div[2]/div[1]/div/article/section[4]/p/text()')).strip(" ['']").replace("\\n",'',10000).replace("'",'',10000).replace(', ,','').strip(' ').replace('。,',',') - return result -def getTag(htmlcode): #获取番号 - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[2]/div/div[1]/h5[4]/a/text()')) - return result.strip(" ['']").replace("'",'').replace(' ','') -def getYear(release): - try: - result = re.search('\d{4}',release).group() - return result - except: - return '' - -def getTitle_fc2com(htmlcode): #获取厂商 - html = etree.fromstring(htmlcode,etree.HTMLParser()) - result = html.xpath('//*[@id="top"]/div[1]/section[1]/div/section/div[2]/h3/text()')[0] - return result -def getActor_fc2com(htmlcode): - try: - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = html.xpath('//*[@id="top"]/div[1]/section[1]/div/section/div[2]/ul/li[3]/a/text()')[0] - return result - except: - return '' -def getStudio_fc2com(htmlcode): #获取厂商 - try: - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('//*[@id="top"]/div[1]/section[1]/div/section/div[2]/ul/li[3]/a/text()')).strip(" ['']") - return result - except: - return '' -def getNum_fc2com(htmlcode): #获取番号 - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[1]/span[2]/text()')).strip(" ['']") - return result -def getRelease_fc2com(htmlcode2): # - html=etree.fromstring(htmlcode2,etree.HTMLParser()) - result = str(html.xpath('//*[@id="container"]/div[1]/div/article/section[1]/div/div[2]/dl/dd[4]/text()')).strip(" ['']") - return result -def getCover_fc2com(htmlcode2): #获取厂商 # - html = etree.fromstring(htmlcode2, etree.HTMLParser()) - result = str(html.xpath('//*[@id="top"]/div[1]/section[1]/div/section/div[1]/span/img/@src')).strip(" ['']") - return 'http:' + result -def getOutline_fc2com(htmlcode2): #获取番号 # - html = etree.fromstring(htmlcode2, etree.HTMLParser()) - result = str(html.xpath('/html/body/div/text()')).strip(" ['']").replace("\\n",'',10000).replace("'",'',10000).replace(', ,','').strip(' ').replace('。,',',') - return result -def getTag_fc2com(number): #获取番号 - htmlcode = str(bytes(ADC_function.get_html('http://adult.contents.fc2.com/api/v4/article/'+number+'/tag?'),'utf-8').decode('unicode-escape')) - result = re.findall('"tag":"(.*?)"', htmlcode) - return result -def getYear_fc2com(release): - try: - result = re.search('\d{4}',release).group() - return result - except: - return '' - -def main(number): - try: - htmlcode2 = ADC_function.get_html('https://adult.contents.fc2.com/article/'+number+'/') - htmlcode = ADC_function.get_html('https://fc2club.com//html/FC2-' + number + '.html') - actor = getActor(htmlcode) - if getActor(htmlcode) == '': - actor = 'FC2系列' - dic = { - 'title': getTitle(htmlcode), - 'studio': getStudio(htmlcode), - 'year': '',#str(re.search('\d{4}',getRelease(number)).group()), - 'outline': '',#getOutline(htmlcode2), - 'runtime': getYear(getRelease(htmlcode)), - 'director': getStudio(htmlcode), - 'actor': actor, - 'release': getRelease(number), - 'number': 'FC2-'+number, - 'label': '', - 'cover': getCover(htmlcode,number,htmlcode2), - 'imagecut': 0, - 'tag': getTag(htmlcode), - 'actor_photo':'', - 'website': 'https://fc2club.com//html/FC2-' + number + '.html', - 'source':'https://fc2club.com//html/FC2-' + number + '.html', - } - if dic['title'] == '': - htmlcode2 = ADC_function.get_html('https://adult.contents.fc2.com/article/' + number + '/',cookies={'wei6H':'1'}) - actor = getActor(htmlcode) - if getActor(htmlcode) == '': - actor = 'FC2系列' - dic = { - 'title': getTitle_fc2com(htmlcode2), - 'studio': getStudio_fc2com(htmlcode2), - 'year': '', # str(re.search('\d{4}',getRelease(number)).group()), - 'outline': getOutline_fc2com(htmlcode2), - 'runtime': getYear_fc2com(getRelease(htmlcode2)), - 'director': getStudio_fc2com(htmlcode2), - 'actor': actor, - 'release': getRelease_fc2com(number), - 'number': 'FC2-' + number, - 'cover': getCover_fc2com(htmlcode2), - 'imagecut': 0, - 'tag': getTag_fc2com(number), - 'label': '', - 'actor_photo': '', - 'website': 'http://adult.contents.fc2.com/article/' + number + '/', - 'source': 'http://adult.contents.fc2.com/article/' + number + '/', - } - except Exception as e: - # (TODO) better handle this - # print(e) - dic = {"title": ""} - js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'),)#.encode('UTF-8') - return js - - -#print(main('1252953')) +import re +from lxml import etree#need install +import json +import ADC_function +# import sys +# import io +# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) + +def getTitle(htmlcode): #获取厂商 + #print(htmlcode) + html = etree.fromstring(htmlcode,etree.HTMLParser()) + result = str(html.xpath('/html/body/div[2]/div/div[1]/h3/text()')).strip(" ['']") + result2 = str(re.sub('\D{2}2-\d+','',result)).replace(' ','',1) + #print(result2) + return result2 +def getActor(htmlcode): + try: + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[2]/div/div[1]/h5[5]/a/text()')).strip(" ['']") + return result + except: + return '' +def getStudio(htmlcode): #获取厂商 + html = etree.fromstring(htmlcode,etree.HTMLParser()) + result = str(html.xpath('/html/body/div[2]/div/div[1]/h5[3]/a[1]/text()')).strip(" ['']") + return result +def getNum(htmlcode): #获取番号 + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[1]/span[2]/text()')).strip(" ['']") + #print(result) + return result +def getRelease(htmlcode2): # + #a=ADC_function.get_html('http://adult.contents.fc2.com/article_search.php?id='+str(number).lstrip("FC2-").lstrip("fc2-").lstrip("fc2_").lstrip("fc2-")+'&utm_source=aff_php&utm_medium=source_code&utm_campaign=from_aff_php') + html=etree.fromstring(htmlcode2,etree.HTMLParser()) + result = str(html.xpath('//*[@id="container"]/div[1]/div/article/section[1]/div/div[2]/dl/dd[4]/text()')).strip(" ['']") + return result +def getCover(htmlcode,number,htmlcode2): #获取厂商 # + #a = ADC_function.get_html('http://adult.contents.fc2.com/article_search.php?id=' + str(number).lstrip("FC2-").lstrip("fc2-").lstrip("fc2_").lstrip("fc2-") + '&utm_source=aff_php&utm_medium=source_code&utm_campaign=from_aff_php') + html = etree.fromstring(htmlcode2, etree.HTMLParser()) + result = str(html.xpath('//*[@id="container"]/div[1]/div/article/section[1]/div/div[1]/a/img/@src')).strip(" ['']") + if result == '': + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result2 = str(html.xpath('//*[@id="slider"]/ul[1]/li[1]/img/@src')).strip(" ['']") + return 'https://fc2club.com' + result2 + return 'http:' + result +def getOutline(htmlcode2): #获取番号 # + html = etree.fromstring(htmlcode2, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[1]/div[2]/div[2]/div[1]/div/article/section[4]/p/text()')).strip(" ['']").replace("\\n",'',10000).replace("'",'',10000).replace(', ,','').strip(' ').replace('。,',',') + return result +def getTag(htmlcode): #获取番号 + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[2]/div/div[1]/h5[4]/a/text()')) + return result.strip(" ['']").replace("'",'').replace(' ','') +def getYear(release): + try: + result = re.search('\d{4}',release).group() + return result + except: + return '' + +def getTitle_fc2com(htmlcode): #获取厂商 + html = etree.fromstring(htmlcode,etree.HTMLParser()) + result = html.xpath('//*[@id="top"]/div[1]/section[1]/div/section/div[2]/h3/text()')[0] + return result +def getActor_fc2com(htmlcode): + try: + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = html.xpath('//*[@id="top"]/div[1]/section[1]/div/section/div[2]/ul/li[3]/a/text()')[0] + return result + except: + return '' +def getStudio_fc2com(htmlcode): #获取厂商 + try: + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('//*[@id="top"]/div[1]/section[1]/div/section/div[2]/ul/li[3]/a/text()')).strip(" ['']") + return result + except: + return '' +def getNum_fc2com(htmlcode): #获取番号 + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[1]/span[2]/text()')).strip(" ['']") + return result +def getRelease_fc2com(htmlcode2): # + html=etree.fromstring(htmlcode2,etree.HTMLParser()) + result = str(html.xpath('//*[@id="container"]/div[1]/div/article/section[1]/div/div[2]/dl/dd[4]/text()')).strip(" ['']") + return result +def getCover_fc2com(htmlcode2): #获取厂商 # + html = etree.fromstring(htmlcode2, etree.HTMLParser()) + result = str(html.xpath('//*[@id="top"]/div[1]/section[1]/div/section/div[1]/span/img/@src')).strip(" ['']") + return 'http:' + result +def getOutline_fc2com(htmlcode2): #获取番号 # + html = etree.fromstring(htmlcode2, etree.HTMLParser()) + result = str(html.xpath('/html/body/div/text()')).strip(" ['']").replace("\\n",'',10000).replace("'",'',10000).replace(', ,','').strip(' ').replace('。,',',') + return result +def getTag_fc2com(number): #获取番号 + htmlcode = str(bytes(ADC_function.get_html('http://adult.contents.fc2.com/api/v4/article/'+number+'/tag?'),'utf-8').decode('unicode-escape')) + result = re.findall('"tag":"(.*?)"', htmlcode) + return result +def getYear_fc2com(release): + try: + result = re.search('\d{4}',release).group() + return result + except: + return '' + +def main(number): + try: + htmlcode2 = ADC_function.get_html('https://adult.contents.fc2.com/article/'+number+'/') + htmlcode = ADC_function.get_html('https://fc2club.com//html/FC2-' + number + '.html') + actor = getActor(htmlcode) + if getActor(htmlcode) == '': + actor = 'FC2系列' + dic = { + 'title': getTitle(htmlcode), + 'studio': getStudio(htmlcode), + 'year': '',#str(re.search('\d{4}',getRelease(number)).group()), + 'outline': '',#getOutline(htmlcode2), + 'runtime': getYear(getRelease(htmlcode)), + 'director': getStudio(htmlcode), + 'actor': actor, + 'release': getRelease(number), + 'number': 'FC2-'+number, + 'label': '', + 'cover': getCover(htmlcode,number,htmlcode2), + 'imagecut': 0, + 'tag': getTag(htmlcode), + 'actor_photo':'', + 'website': 'https://fc2club.com//html/FC2-' + number + '.html', + 'source':'https://fc2club.com//html/FC2-' + number + '.html', + } + if dic['title'] == '': + htmlcode2 = ADC_function.get_html('https://adult.contents.fc2.com/article/' + number + '/',cookies={'wei6H':'1'}) + actor = getActor(htmlcode) + if getActor(htmlcode) == '': + actor = 'FC2系列' + dic = { + 'title': getTitle_fc2com(htmlcode2), + 'studio': getStudio_fc2com(htmlcode2), + 'year': '', # str(re.search('\d{4}',getRelease(number)).group()), + 'outline': getOutline_fc2com(htmlcode2), + 'runtime': getYear_fc2com(getRelease(htmlcode2)), + 'director': getStudio_fc2com(htmlcode2), + 'actor': actor, + 'release': getRelease_fc2com(number), + 'number': 'FC2-' + number, + 'cover': getCover_fc2com(htmlcode2), + 'imagecut': 0, + 'tag': getTag_fc2com(number), + 'label': '', + 'actor_photo': '', + 'website': 'http://adult.contents.fc2.com/article/' + number + '/', + 'source': 'http://adult.contents.fc2.com/article/' + number + '/', + } + except Exception as e: + # (TODO) better handle this + # print(e) + dic = {"title": ""} + js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'),)#.encode('UTF-8') + return js + + +#print(main('1252953')) diff --git a/javbus.py b/SiteSource/javbus.py similarity index 95% rename from javbus.py rename to SiteSource/javbus.py index aa18d2a..ea06ac4 100755 --- a/javbus.py +++ b/SiteSource/javbus.py @@ -1,138 +1,139 @@ -import re -from pyquery import PyQuery as pq#need install -from lxml import etree#need install -from bs4 import BeautifulSoup#need install -import json -from ADC_function import * - -def getActorPhoto(htmlcode): #//*[@id="star_qdt"]/li/a/img - soup = BeautifulSoup(htmlcode, 'lxml') - a = soup.find_all(attrs={'class': 'star-name'}) - d={} - for i in a: - l=i.a['href'] - t=i.get_text() - html = etree.fromstring(get_html(l), etree.HTMLParser()) - p=str(html.xpath('//*[@id="waterfall"]/div[1]/div/div[1]/img/@src')).strip(" ['']") - p2={t:p} - d.update(p2) - return d -def getTitle(htmlcode): #获取标题 - doc = pq(htmlcode) - title=str(doc('div.container h3').text()).replace(' ','-') - try: - title2 = re.sub('n\d+-','',title) - return title2 - except: - return title -def getStudio(htmlcode): #获取厂商 - html = etree.fromstring(htmlcode,etree.HTMLParser()) - result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[5]/a/text()')).strip(" ['']") - return result -def getYear(htmlcode): #获取年份 - html = etree.fromstring(htmlcode,etree.HTMLParser()) - result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[2]/text()')).strip(" ['']") - return result -def getCover(htmlcode): #获取封面链接 - doc = pq(htmlcode) - image = doc('a.bigImage') - return image.attr('href') -def getRelease(htmlcode): #获取出版日期 - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[2]/text()')).strip(" ['']") - return result -def getRuntime(htmlcode): #获取分钟 - soup = BeautifulSoup(htmlcode, 'lxml') - a = soup.find(text=re.compile('分鐘')) - return a -def getActor(htmlcode): #获取女优 - b=[] - soup=BeautifulSoup(htmlcode,'lxml') - a=soup.find_all(attrs={'class':'star-name'}) - for i in a: - b.append(i.get_text()) - return b -def getNum(htmlcode): #获取番号 - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[1]/span[2]/text()')).strip(" ['']") - return result -def getDirector(htmlcode): #获取导演 - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[4]/a/text()')).strip(" ['']") - return result -def getOutline(htmlcode): #获取演员 - doc = pq(htmlcode) - result = str(doc('tr td div.mg-b20.lh4 p.mg-b20').text()) - return result -def getSerise(htmlcode): - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[7]/a/text()')).strip(" ['']") - return result -def getTag(htmlcode): # 获取演员 - tag = [] - soup = BeautifulSoup(htmlcode, 'lxml') - a = soup.find_all(attrs={'class': 'genre'}) - for i in a: - if 'onmouseout' in str(i): - continue - tag.append(i.get_text()) - return tag - - -def main(number): - try: - htmlcode = get_html('https://www.javbus.com/' + number) - try: - dww_htmlcode = get_html("https://www.dmm.co.jp/mono/dvd/-/detail/=/cid=" + number.replace("-", '')) - except: - dww_htmlcode = '' - dic = { - 'title': str(re.sub('\w+-\d+-', '', getTitle(htmlcode))), - 'studio': getStudio(htmlcode), - 'year': str(re.search('\d{4}', getYear(htmlcode)).group()), - 'outline': getOutline(dww_htmlcode), - 'runtime': getRuntime(htmlcode), - 'director': getDirector(htmlcode), - 'actor': getActor(htmlcode), - 'release': getRelease(htmlcode), - 'number': getNum(htmlcode), - 'cover': getCover(htmlcode), - 'imagecut': 1, - 'tag': getTag(htmlcode), - 'label': getSerise(htmlcode), - 'actor_photo': getActorPhoto(htmlcode), - 'website': 'https://www.javbus.com/' + number, - 'source' : 'javbus.py', - } - js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') - return js - except: - return main_uncensored(number) - -def main_uncensored(number): - htmlcode = get_html('https://www.javbus.com/' + number) - dww_htmlcode = get_html("https://www.dmm.co.jp/mono/dvd/-/detail/=/cid=" + number.replace("-", '')) - if getTitle(htmlcode) == '': - htmlcode = get_html('https://www.javbus.com/' + number.replace('-','_')) - dww_htmlcode = get_html("https://www.dmm.co.jp/mono/dvd/-/detail/=/cid=" + number.replace("-", '')) - dic = { - 'title': str(re.sub('\w+-\d+-','',getTitle(htmlcode))).replace(getNum(htmlcode)+'-',''), - 'studio': getStudio(htmlcode), - 'year': getYear(htmlcode), - 'outline': getOutline(dww_htmlcode), - 'runtime': getRuntime(htmlcode), - 'director': getDirector(htmlcode), - 'actor': getActor(htmlcode), - 'release': getRelease(htmlcode), - 'number': getNum(htmlcode), - 'cover': getCover(htmlcode), - 'tag': getTag(htmlcode), - 'label': getSerise(htmlcode), - 'imagecut': 0, - 'actor_photo': '', - 'website': 'https://www.javbus.com/' + number, - 'source': 'javbus.py', - } - js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') - return js - +import re +from pyquery import PyQuery as pq#need install +from lxml import etree#need install +from bs4 import BeautifulSoup#need install +import json +from ADC_function import * + +def getActorPhoto(htmlcode): #//*[@id="star_qdt"]/li/a/img + soup = BeautifulSoup(htmlcode, 'lxml') + a = soup.find_all(attrs={'class': 'star-name'}) + d={} + for i in a: + l=i.a['href'] + t=i.get_text() + html = etree.fromstring(get_html(l), etree.HTMLParser()) + p=str(html.xpath('//*[@id="waterfall"]/div[1]/div/div[1]/img/@src')).strip(" ['']") + p2={t:p} + d.update(p2) + return d +def getTitle(htmlcode): #获取标题 + doc = pq(htmlcode) + title=str(doc('div.container h3').text()).replace(' ','-') + try: + title2 = re.sub('n\d+-','',title) + return title2 + except: + return title +def getStudio(htmlcode): #获取厂商 + html = etree.fromstring(htmlcode,etree.HTMLParser()) + result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[5]/a/text()')).strip(" ['']") + return result +def getYear(htmlcode): #获取年份 + html = etree.fromstring(htmlcode,etree.HTMLParser()) + result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[2]/text()')).strip(" ['']") + return result +def getCover(htmlcode): #获取封面链接 + doc = pq(htmlcode) + image = doc('a.bigImage') + return image.attr('href') +def getRelease(htmlcode): #获取出版日期 + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[2]/text()')).strip(" ['']") + return result +def getRuntime(htmlcode): #获取分钟 + soup = BeautifulSoup(htmlcode, 'lxml') + a = soup.find(text=re.compile('分鐘')) + return a +def getActor(htmlcode): #获取女优 + b=[] + soup=BeautifulSoup(htmlcode,'lxml') + a=soup.find_all(attrs={'class':'star-name'}) + for i in a: + b.append(i.get_text()) + return b +def getNum(htmlcode): #获取番号 + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[1]/span[2]/text()')).strip(" ['']") + return result +def getDirector(htmlcode): #获取导演 + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[4]/a/text()')).strip(" ['']") + return result +def getOutline(htmlcode): #获取演员 + doc = pq(htmlcode) + result = str(doc('tr td div.mg-b20.lh4 p.mg-b20').text()) + return result +def getSerise(htmlcode): + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('/html/body/div[5]/div[1]/div[2]/p[7]/a/text()')).strip(" ['']") + return result +def getTag(htmlcode): # 获取演员 + tag = [] + soup = BeautifulSoup(htmlcode, 'lxml') + a = soup.find_all(attrs={'class': 'genre'}) + for i in a: + if 'onmouseout' in str(i): + continue + tag.append(i.get_text()) + return tag + + +def main(number): + try: + htmlcode = get_html('https://www.javbus.com/' + number) + try: + dww_htmlcode = get_html("https://www.dmm.co.jp/mono/dvd/-/detail/=/cid=" + number.replace("-", '')) + except: + dww_htmlcode = '' + dic = { + 'title': str(re.sub('\w+-\d+-', '', getTitle(htmlcode))), + 'studio': getStudio(htmlcode), + 'year': str(re.search('\d{4}', getYear(htmlcode)).group()), + 'outline': getOutline(dww_htmlcode), + 'runtime': getRuntime(htmlcode), + 'director': getDirector(htmlcode), + 'actor': getActor(htmlcode), + 'release': getRelease(htmlcode), + 'number': getNum(htmlcode), + 'cover': getCover(htmlcode), + 'imagecut': 1, + 'tag': getTag(htmlcode), + 'label': getSerise(htmlcode), + 'actor_photo': getActorPhoto(htmlcode), + 'website': 'https://www.javbus.com/' + number, + 'source' : 'javbus.py', + } + js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') + return js + except: + return main_uncensored(number) + + +def main_uncensored(number): # 无码 + htmlcode = get_html('https://www.javbus.com/' + number) + dww_htmlcode = get_html("https://www.dmm.co.jp/mono/dvd/-/detail/=/cid=" + number.replace("-", '')) + if getTitle(htmlcode) == '': + htmlcode = get_html('https://www.javbus.com/' + number.replace('-','_')) + dww_htmlcode = get_html("https://www.dmm.co.jp/mono/dvd/-/detail/=/cid=" + number.replace("-", '')) + dic = { + 'title': str(re.sub('\w+-\d+-', '', getTitle(htmlcode))).replace(getNum(htmlcode)+'-', ''), + 'studio': getStudio(htmlcode), + 'year': getYear(htmlcode), + 'outline': getOutline(dww_htmlcode), + 'runtime': getRuntime(htmlcode), + 'director': getDirector(htmlcode), + 'actor': getActor(htmlcode), + 'release': getRelease(htmlcode), + 'number': getNum(htmlcode), + 'cover': getCover(htmlcode), + 'tag': getTag(htmlcode), + 'label': getSerise(htmlcode), + 'imagecut': 0, + 'actor_photo': '', + 'website': 'https://www.javbus.com/' + number, + 'source': 'javbus.py', + } + js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') + return js + diff --git a/javdb.py b/SiteSource/javdb.py similarity index 98% rename from javdb.py rename to SiteSource/javdb.py index 727c992..180602a 100755 --- a/javdb.py +++ b/SiteSource/javdb.py @@ -1,123 +1,123 @@ -import re -from lxml import etree -import json -from bs4 import BeautifulSoup -from ADC_function import * -# import sys -# import io -# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) - -def getTitle(a): - html = etree.fromstring(a, etree.HTMLParser()) - result = html.xpath("/html/body/section/div/h2/strong/text()")[0] - return result -def getActor(a): # //*[@id="center_column"]/div[2]/div[1]/div/table/tbody/tr[1]/td/text() - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//strong[contains(text(),"演員")]/../following-sibling::span/text()')).strip(" ['']") - result2 = str(html.xpath('//strong[contains(text(),"演員")]/../following-sibling::span/a/text()')).strip(" ['']") - return str(result1 + result2).strip('+').replace(",\\xa0", "").replace("'", "").replace(' ', '').replace(',,', '').lstrip(',').replace(',', ', ') -def getActorPhoto(actor): #//*[@id="star_qdt"]/li/a/img - a = actor.split(',') - d={} - for i in a: - p={i:''} - d.update(p) - return d -def getStudio(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//strong[contains(text(),"片商")]/../following-sibling::span/text()')).strip(" ['']") - result2 = str(html.xpath('//strong[contains(text(),"片商")]/../following-sibling::span/a/text()')).strip(" ['']") - return str(result1 + result2).strip('+').replace("', '", '').replace('"', '') -def getRuntime(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//strong[contains(text(),"時長")]/../following-sibling::span/text()')).strip(" ['']") - result2 = str(html.xpath('//strong[contains(text(),"時長")]/../following-sibling::span/a/text()')).strip(" ['']") - return str(result1 + result2).strip('+').rstrip('mi') -def getLabel(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//strong[contains(text(),"系列")]/../following-sibling::span/text()')).strip(" ['']") - result2 = str(html.xpath('//strong[contains(text(),"系列")]/../following-sibling::span/a/text()')).strip(" ['']") - return str(result1 + result2).strip('+').replace("', '", '').replace('"', '') -def getNum(a): - html = etree.fromstring(a, etree.HTMLParser()) - result1 = str(html.xpath('//strong[contains(text(),"番號")]/../following-sibling::span/text()')).strip(" ['']") - result2 = str(html.xpath('//strong[contains(text(),"番號")]/../following-sibling::span/a/text()')).strip(" ['']") - return str(result2 + result1).strip('+') -def getYear(getRelease): - try: - result = str(re.search('\d{4}', getRelease).group()) - return result - except: - return getRelease -def getRelease(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//strong[contains(text(),"時間")]/../following-sibling::span/text()')).strip(" ['']") - result2 = str(html.xpath('//strong[contains(text(),"時間")]/../following-sibling::span/a/text()')).strip(" ['']") - return str(result1 + result2).strip('+') -def getTag(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//strong[contains(text(),"类别")]/../following-sibling::span/text()')).strip(" ['']") - result2 = str(html.xpath('//strong[contains(text(),"类别")]/../following-sibling::span/a/text()')).strip(" ['']") - return str(result1 + result2).strip('+').replace(",\\xa0", "").replace("'", "").replace(' ', '').replace(',,', '').lstrip(',') -def getCover_small(a, index=0): - # same issue mentioned below, - # javdb sometime returns multiple results - # DO NOT just get the firt one, get the one with correct index number - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result = html.xpath("//div[@class='item-image fix-scale-cover']/img/@src")[index] - if not 'https' in result: - result = 'https:' + result - return result -def getCover(htmlcode): - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath("//div[@class='column column-video-cover']/a/img/@src")).strip(" ['']") - return result -def getDirector(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//strong[contains(text(),"導演")]/../following-sibling::span/text()')).strip(" ['']") - result2 = str(html.xpath('//strong[contains(text(),"導演")]/../following-sibling::span/a/text()')).strip(" ['']") - return str(result1 + result2).strip('+').replace("', '", '').replace('"', '') -def getOutline(htmlcode): - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('//*[@id="introduction"]/dd/p[1]/text()')).strip(" ['']") - return result -def main(number): - try: - number = number.upper() - query_result = get_html('https://javdb.com/search?q=' + number + '&f=all') - html = etree.fromstring(query_result, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - # javdb sometime returns multiple results, - # and the first elememt maybe not the one we are looking for - # iterate all candidates and find the match one - urls = html.xpath('//*[@id="videos"]/div/div/a/@href') - ids =html.xpath('//*[@id="videos"]/div/div/a/div[contains(@class, "uid")]/text()') - correct_url = urls[ids.index(number)] - detail_page = get_html('https://javdb.com' + correct_url) - dic = { - 'actor': getActor(detail_page), - 'title': getTitle(detail_page), - 'studio': getStudio(detail_page), - 'outline': getOutline(detail_page), - 'runtime': getRuntime(detail_page), - 'director': getDirector(detail_page), - 'release': getRelease(detail_page), - 'number': getNum(detail_page), - 'cover': getCover(detail_page), - 'cover_small': getCover_small(query_result, index=ids.index(number)), - 'imagecut': 3, - 'tag': getTag(detail_page), - 'label': getLabel(detail_page), - 'year': getYear(getRelease(detail_page)), # str(re.search('\d{4}',getRelease(a)).group()), - 'actor_photo': getActorPhoto(getActor(detail_page)), - 'website': 'https://javdb.com' + correct_url, - 'source': 'javdb.py', - } - except Exception as e: - # print(e) - dic = {"title": ""} - js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') - return js - -# main('DV-1562') -# input("[+][+]Press enter key exit, you can check the error messge before you exit.\n[+][+]按回车键结束,你可以在结束之前查看和错误信息。") -#print(main('ipx-292')) +import re +from lxml import etree +import json +from bs4 import BeautifulSoup +from ADC_function import * +# import sys +# import io +# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) + +def getTitle(a): + html = etree.fromstring(a, etree.HTMLParser()) + result = html.xpath("/html/body/section/div/h2/strong/text()")[0] + return result +def getActor(a): # //*[@id="center_column"]/div[2]/div[1]/div/table/tbody/tr[1]/td/text() + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//strong[contains(text(),"演員")]/../following-sibling::span/text()')).strip(" ['']") + result2 = str(html.xpath('//strong[contains(text(),"演員")]/../following-sibling::span/a/text()')).strip(" ['']") + return str(result1 + result2).strip('+').replace(",\\xa0", "").replace("'", "").replace(' ', '').replace(',,', '').lstrip(',').replace(',', ', ') +def getActorPhoto(actor): #//*[@id="star_qdt"]/li/a/img + a = actor.split(',') + d={} + for i in a: + p={i:''} + d.update(p) + return d +def getStudio(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//strong[contains(text(),"片商")]/../following-sibling::span/text()')).strip(" ['']") + result2 = str(html.xpath('//strong[contains(text(),"片商")]/../following-sibling::span/a/text()')).strip(" ['']") + return str(result1 + result2).strip('+').replace("', '", '').replace('"', '') +def getRuntime(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//strong[contains(text(),"時長")]/../following-sibling::span/text()')).strip(" ['']") + result2 = str(html.xpath('//strong[contains(text(),"時長")]/../following-sibling::span/a/text()')).strip(" ['']") + return str(result1 + result2).strip('+').rstrip('mi') +def getLabel(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//strong[contains(text(),"系列")]/../following-sibling::span/text()')).strip(" ['']") + result2 = str(html.xpath('//strong[contains(text(),"系列")]/../following-sibling::span/a/text()')).strip(" ['']") + return str(result1 + result2).strip('+').replace("', '", '').replace('"', '') +def getNum(a): + html = etree.fromstring(a, etree.HTMLParser()) + result1 = str(html.xpath('//strong[contains(text(),"番號")]/../following-sibling::span/text()')).strip(" ['']") + result2 = str(html.xpath('//strong[contains(text(),"番號")]/../following-sibling::span/a/text()')).strip(" ['']") + return str(result2 + result1).strip('+') +def getYear(getRelease): + try: + result = str(re.search('\d{4}', getRelease).group()) + return result + except: + return getRelease +def getRelease(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//strong[contains(text(),"時間")]/../following-sibling::span/text()')).strip(" ['']") + result2 = str(html.xpath('//strong[contains(text(),"時間")]/../following-sibling::span/a/text()')).strip(" ['']") + return str(result1 + result2).strip('+') +def getTag(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//strong[contains(text(),"类别")]/../following-sibling::span/text()')).strip(" ['']") + result2 = str(html.xpath('//strong[contains(text(),"类别")]/../following-sibling::span/a/text()')).strip(" ['']") + return str(result1 + result2).strip('+').replace(",\\xa0", "").replace("'", "").replace(' ', '').replace(',,', '').lstrip(',') +def getCover_small(a, index=0): + # same issue mentioned below, + # javdb sometime returns multiple results + # DO NOT just get the firt one, get the one with correct index number + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result = html.xpath("//div[@class='item-image fix-scale-cover']/img/@src")[index] + if not 'https' in result: + result = 'https:' + result + return result +def getCover(htmlcode): + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath("//div[@class='column column-video-cover']/a/img/@src")).strip(" ['']") + return result +def getDirector(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//strong[contains(text(),"導演")]/../following-sibling::span/text()')).strip(" ['']") + result2 = str(html.xpath('//strong[contains(text(),"導演")]/../following-sibling::span/a/text()')).strip(" ['']") + return str(result1 + result2).strip('+').replace("', '", '').replace('"', '') +def getOutline(htmlcode): + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('//*[@id="introduction"]/dd/p[1]/text()')).strip(" ['']") + return result +def main(number): + try: + number = number.upper() + query_result = get_html('https://javdb.com/search?q=' + number + '&f=all') + html = etree.fromstring(query_result, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + # javdb sometime returns multiple results, + # and the first elememt maybe not the one we are looking for + # iterate all candidates and find the match one + urls = html.xpath('//*[@id="videos"]/div/div/a/@href') + ids =html.xpath('//*[@id="videos"]/div/div/a/div[contains(@class, "uid")]/text()') + correct_url = urls[ids.index(number)] + detail_page = get_html('https://javdb.com' + correct_url) + dic = { + 'actor': getActor(detail_page), + 'title': getTitle(detail_page), + 'studio': getStudio(detail_page), + 'outline': getOutline(detail_page), + 'runtime': getRuntime(detail_page), + 'director': getDirector(detail_page), + 'release': getRelease(detail_page), + 'number': getNum(detail_page), + 'cover': getCover(detail_page), + 'cover_small': getCover_small(query_result, index=ids.index(number)), + 'imagecut': 3, + 'tag': getTag(detail_page), + 'label': getLabel(detail_page), + 'year': getYear(getRelease(detail_page)), # str(re.search('\d{4}',getRelease(a)).group()), + 'actor_photo': getActorPhoto(getActor(detail_page)), + 'website': 'https://javdb.com' + correct_url, + 'source': 'javdb.py', + } + except Exception as e: + # print(e) + dic = {"title": ""} + js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') + return js + +# main('DV-1562') +# input("[+][+]Press enter key exit, you can check the error messge before you exit.\n[+][+]按回车键结束,你可以在结束之前查看和错误信息。") +#print(main('ipx-292')) diff --git a/mgstage.py b/SiteSource/mgstage.py similarity index 98% rename from mgstage.py rename to SiteSource/mgstage.py index 8e358c9..d1a8e95 100755 --- a/mgstage.py +++ b/SiteSource/mgstage.py @@ -1,108 +1,108 @@ -import re -from lxml import etree -import json -from bs4 import BeautifulSoup -from ADC_function import * -# import sys -# import io -# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) - -def getTitle(a): - try: - html = etree.fromstring(a, etree.HTMLParser()) - result = str(html.xpath('//*[@id="center_column"]/div[1]/h1/text()')).strip(" ['']") - return result.replace('/', ',') - except: - return '' -def getActor(a): #//*[@id="center_column"]/div[2]/div[1]/div/table/tbody/tr[1]/td/text() - html = etree.fromstring(a, etree.HTMLParser()) #//table/tr[1]/td[1]/text() - result1=str(html.xpath('//th[contains(text(),"出演:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip('\\n') - result2=str(html.xpath('//th[contains(text(),"出演:")]/../td/text()')).strip(" ['']").strip('\\n ').strip('\\n') - return str(result1+result2).strip('+').replace("', '",'').replace('"','').replace('/',',') -def getStudio(a): - html = etree.fromstring(a, etree.HTMLParser()) #//table/tr[1]/td[1]/text() - result1=str(html.xpath('//th[contains(text(),"シリーズ:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip('\\n') - result2=str(html.xpath('//th[contains(text(),"シリーズ:")]/../td/text()')).strip(" ['']").strip('\\n ').strip('\\n') - return str(result1+result2).strip('+').replace("', '",'').replace('"','') -def getRuntime(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//th[contains(text(),"収録時間:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip('\\n') - result2 = str(html.xpath('//th[contains(text(),"収録時間:")]/../td/text()')).strip(" ['']").strip('\\n ').strip('\\n') - return str(result1 + result2).strip('+').rstrip('mi') -def getLabel(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//th[contains(text(),"シリーズ:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - result2 = str(html.xpath('//th[contains(text(),"シリーズ:")]/../td/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - return str(result1 + result2).strip('+').replace("', '",'').replace('"','') -def getNum(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//th[contains(text(),"品番:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - result2 = str(html.xpath('//th[contains(text(),"品番:")]/../td/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - return str(result1 + result2).strip('+') -def getYear(getRelease): - try: - result = str(re.search('\d{4}',getRelease).group()) - return result - except: - return getRelease -def getRelease(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//th[contains(text(),"配信開始日:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - result2 = str(html.xpath('//th[contains(text(),"配信開始日:")]/../td/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - return str(result1 + result2).strip('+') -def getTag(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//th[contains(text(),"ジャンル:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - result2 = str(html.xpath('//th[contains(text(),"ジャンル:")]/../td/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - return str(result1 + result2).strip('+').replace("', '\\n",",").replace("', '","").replace('"','') -def getCover(htmlcode): - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('//*[@id="center_column"]/div[1]/div[1]/div/div/h2/img/@src')).strip(" ['']") - # /html/body/div[2]/article[2]/div[1]/div[1]/div/div/h2/img/@src - return result -def getDirector(a): - html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() - result1 = str(html.xpath('//th[contains(text(),"シリーズ")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - result2 = str(html.xpath('//th[contains(text(),"シリーズ")]/../td/text()')).strip(" ['']").strip('\\n ').strip( - '\\n') - return str(result1 + result2).strip('+').replace("', '",'').replace('"','') -def getOutline(htmlcode): - html = etree.fromstring(htmlcode, etree.HTMLParser()) - result = str(html.xpath('//*[@id="introduction"]/dd/p[1]/text()')).strip(" ['']") - return result -def main(number2): - number=number2.upper() - htmlcode=str(get_html('https://www.mgstage.com/product/product_detail/'+str(number)+'/',cookies={'adc':'1'})) - soup = BeautifulSoup(htmlcode, 'lxml') - a = str(soup.find(attrs={'class': 'detail_data'})).replace('\n ','').replace(' ','').replace('\n ','').replace('\n ','') - dic = { - 'title': getTitle(htmlcode).replace("\\n",'').replace(' ',''), - 'studio': getStudio(a), - 'outline': getOutline(htmlcode), - 'runtime': getRuntime(a), - 'director': getDirector(a), - 'actor': getActor(a), - 'release': getRelease(a), - 'number': getNum(a), - 'cover': getCover(htmlcode), - 'imagecut': 0, - 'tag': getTag(a), - 'label':getLabel(a), - 'year': getYear(getRelease(a)), # str(re.search('\d{4}',getRelease(a)).group()), - 'actor_photo': '', - 'website':'https://www.mgstage.com/product/product_detail/'+str(number)+'/', - 'source': 'mgstage.py', - } - js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') - return js - -#print(main('SIRO-3607')) +import re +from lxml import etree +import json +from bs4 import BeautifulSoup +from ADC_function import * +# import sys +# import io +# sys.stdout = io.TextIOWrapper(sys.stdout.buffer, errors = 'replace', line_buffering = True) + +def getTitle(a): + try: + html = etree.fromstring(a, etree.HTMLParser()) + result = str(html.xpath('//*[@id="center_column"]/div[1]/h1/text()')).strip(" ['']") + return result.replace('/', ',') + except: + return '' +def getActor(a): #//*[@id="center_column"]/div[2]/div[1]/div/table/tbody/tr[1]/td/text() + html = etree.fromstring(a, etree.HTMLParser()) #//table/tr[1]/td[1]/text() + result1=str(html.xpath('//th[contains(text(),"出演:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip('\\n') + result2=str(html.xpath('//th[contains(text(),"出演:")]/../td/text()')).strip(" ['']").strip('\\n ').strip('\\n') + return str(result1+result2).strip('+').replace("', '",'').replace('"','').replace('/',',') +def getStudio(a): + html = etree.fromstring(a, etree.HTMLParser()) #//table/tr[1]/td[1]/text() + result1=str(html.xpath('//th[contains(text(),"シリーズ:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip('\\n') + result2=str(html.xpath('//th[contains(text(),"シリーズ:")]/../td/text()')).strip(" ['']").strip('\\n ').strip('\\n') + return str(result1+result2).strip('+').replace("', '",'').replace('"','') +def getRuntime(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//th[contains(text(),"収録時間:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip('\\n') + result2 = str(html.xpath('//th[contains(text(),"収録時間:")]/../td/text()')).strip(" ['']").strip('\\n ').strip('\\n') + return str(result1 + result2).strip('+').rstrip('mi') +def getLabel(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//th[contains(text(),"シリーズ:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + result2 = str(html.xpath('//th[contains(text(),"シリーズ:")]/../td/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + return str(result1 + result2).strip('+').replace("', '",'').replace('"','') +def getNum(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//th[contains(text(),"品番:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + result2 = str(html.xpath('//th[contains(text(),"品番:")]/../td/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + return str(result1 + result2).strip('+') +def getYear(getRelease): + try: + result = str(re.search('\d{4}',getRelease).group()) + return result + except: + return getRelease +def getRelease(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//th[contains(text(),"配信開始日:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + result2 = str(html.xpath('//th[contains(text(),"配信開始日:")]/../td/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + return str(result1 + result2).strip('+') +def getTag(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//th[contains(text(),"ジャンル:")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + result2 = str(html.xpath('//th[contains(text(),"ジャンル:")]/../td/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + return str(result1 + result2).strip('+').replace("', '\\n",",").replace("', '","").replace('"','') +def getCover(htmlcode): + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('//*[@id="center_column"]/div[1]/div[1]/div/div/h2/img/@src')).strip(" ['']") + # /html/body/div[2]/article[2]/div[1]/div[1]/div/div/h2/img/@src + return result +def getDirector(a): + html = etree.fromstring(a, etree.HTMLParser()) # //table/tr[1]/td[1]/text() + result1 = str(html.xpath('//th[contains(text(),"シリーズ")]/../td/a/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + result2 = str(html.xpath('//th[contains(text(),"シリーズ")]/../td/text()')).strip(" ['']").strip('\\n ').strip( + '\\n') + return str(result1 + result2).strip('+').replace("', '",'').replace('"','') +def getOutline(htmlcode): + html = etree.fromstring(htmlcode, etree.HTMLParser()) + result = str(html.xpath('//*[@id="introduction"]/dd/p[1]/text()')).strip(" ['']") + return result +def main(number2): + number=number2.upper() + htmlcode=str(get_html('https://www.mgstage.com/product/product_detail/'+str(number)+'/',cookies={'adc':'1'})) + soup = BeautifulSoup(htmlcode, 'lxml') + a = str(soup.find(attrs={'class': 'detail_data'})).replace('\n ','').replace(' ','').replace('\n ','').replace('\n ','') + dic = { + 'title': getTitle(htmlcode).replace("\\n",'').replace(' ',''), + 'studio': getStudio(a), + 'outline': getOutline(htmlcode), + 'runtime': getRuntime(a), + 'director': getDirector(a), + 'actor': getActor(a), + 'release': getRelease(a), + 'number': getNum(a), + 'cover': getCover(htmlcode), + 'imagecut': 0, + 'tag': getTag(a), + 'label':getLabel(a), + 'year': getYear(getRelease(a)), # str(re.search('\d{4}',getRelease(a)).group()), + 'actor_photo': '', + 'website':'https://www.mgstage.com/product/product_detail/'+str(number)+'/', + 'source': 'mgstage.py', + } + js = json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':'), ) # .encode('UTF-8') + return js + +#print(main('SIRO-3607')) diff --git a/TestPathNFO.txt b/TestPathNFO.txt new file mode 100644 index 0000000..399647c --- /dev/null +++ b/TestPathNFO.txt @@ -0,0 +1,41 @@ +/Volumes/Adult/Files/ノ瀬アメリ/Tokyo Hot N0646.avi +/Volumes/Adult/Files/ノ瀬アメリ/MKBD_S03-MaRieS.mp4 +/Volumes/192.168.2.100/Adult/Files/Aki Sasaki Megapack/HODV-21299.mkv +/Volumes/Adult/Files/[Tokyo-Hot] [n1180] 美人秘書3穴串刺奉仕残業 (中井綾香 Ayaka Nakai)/(Tokyo-Hot)(n1180)美人秘書3穴串刺奉仕残業 中井綾香.mp4 +/mcdv47.avi +/mcdv-47.avi +/mcdv-047.mp4 +/mcdv047.mp4 +/mcdv0047.mp4 +/1pondo-070409_621.mp4 +/Volumes/Adult/Files/Kirara Asuka (@明日花キララ) FHD Pack Vol#1(181222)@RUNBKK/No-Watermarked/HOBD00015.FHD2.wmv +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 1/720p/RBD-406_1.mp4 +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 1/720p/MDYD-664B.mp4 +/Volumes/Adult/Files/107NTTR-037A.mp4 +/Volumes/Adult/Files/Yua.Mikami-PML/SNIS-986 国民的アイドル アドレナリン大爆発!禁欲1ヶ月後の性欲剥き出し焦らされトランスFUCK 三上悠亜【桃花族】.mp4 +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 2/FHD/UPSM-109_2.mkv +/Volumes/Adult/Files/Kirara Asuka (@明日花キララ) FHD Pack Vol#2(181231)@RUNBKK/No-Watermarked/PPT003.SD3.wmv +/Volumes/Adult/Files/波多野结衣/THE波多野結衣 ぶっかけ50連発! CD1.wmv +/Volumes/Adult/Files/波多野结衣/欲しがり 後編 波多野結衣.wmv +/Volumes/Adult/Files/波多野结衣/欲しがり 前編 波多野結衣.wmv +/Volumes/Adult/Files/波多野结衣/加勒比 062212-055 夫の目の前で妻が ~元上司に縛られて~波多野結衣~.rmvb +/Volumes/Adult/Files/波多野结衣/022213-271-carib-whole_s.mp4 +/Volumes/Adult/Files/SKYHD-001~010/SKYHD-009_H265.mkv +/Volumes/Adult/Files/大桥步兵合集/LAFBD-41.LaForet.Girl.41.angel.and.devil.Miku.Ohashi.2015.Bluray.1080p.x264.ac3-MTeam.mkv +/Volumes/Adult/Files/大桥步兵合集/032015_161-caribpr-high.mp4 +/Volumes/Adult/Files/桃谷绘里香(桃谷エリカ) 所有作品集合/118ppt00016hhb2.mkv +/Volumes/Adult/Files/tia/soe935C.HD.wmv +/Volumes/Adult/Files/SKYHD-011~020/SKYHD-020_H265.mkv +/Volumes/Adult/Files/RION(りおん).Utsunomiya.Shion.宇都宮しをん(うつのみやしをん)/VR/sivr00008_E.mp4 +/Volumes/Adult/Files/RION(りおん).Utsunomiya.Shion.宇都宮しをん(うつのみやしをん)/DMM.Video/onsd00899hhb3.mp4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/SHKD-744 営業課長の湿ったパンスト 里美ゆりあ.mp4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/ABP-627 裏・鈴村あいり-鈴村あいりのオトナの激情SEX4本番 鈴村あいり.MP4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/20 ABP-408 上原瑞穂/上原瑞穂 ABP-408 无码流出片段/[ThZu.Cc]20150909164411.m2ts +/Volumes/Adult/Files/Caribbean-101717-520-HD/100917-515/100917-515-carib-1080p.mp4 +/Volumes/Adult/Files/ノ瀬アメリ/20081105栗栖エリカ - Sky Angel Blue 10 天舞超絕美少女天使降臨(skyhd010)(中文字幕).avi +/Volumes/Adult/Files/ノ瀬アメリ/一ノ瀬アメリ~加勒比 VERY SEXY.wmv +/Volumes/Adult/Files/ノ瀬アメリ/20101202一ノ瀬アメリ - 東京ブルドック05(inu006).avi +/Volumes/Adult/Files/ノ瀬アメリ/Sky Angel Vol 80 - CD2.mp4 +/Volumes/Adult/Files/Mika Sumire すみれ美香/Caribbean-091818-755.mp4 +/Volumes/Adult/Files/Takizawa Rola/[HD]abp-031C.wmv +/Volumes/Adult/Files/Takizawa Rola/ABP-013HDA.wmv \ No newline at end of file diff --git a/TestPathSpecial.txt b/TestPathSpecial.txt new file mode 100644 index 0000000..cc22544 --- /dev/null +++ b/TestPathSpecial.txt @@ -0,0 +1,51 @@ +/Volumes/192.168.2.100/Adult/Files/Aki Sasaki Megapack/HODV-21222.mkv +/Volumes/Adult/Files/ノ瀬アメリ/Tokyo Hot N0646.avi +/Volumes/Adult/Files/ノ瀬アメリ/MKBD_S03-MaRieS.mp4 +/Volumes/192.168.2.100/Adult/Files/RION(りおん).Utsunomiya.Shion.宇都宮しをん(うつのみやしをん)/DMM.Video/onsd00899hhb3.mp4 +/Volumes/192.168.2.100/Adult/Files/Rating Top 30 JAV pack/IPTD-999-1 彼女の姉貴とイケナイ関係 Rio.wmv +/Volumes/192.168.2.100/Adult/Files/Rating Top 30 JAV pack/IPTD-999A 彼女の姉貴とイケナイ関係 Rio.wmv +/Volumes/192.168.2.100/Adult/Files/Rating Top 30 JAV pack/IPTD-999-A 彼女の姉貴とイケナイ関係 Rio.wmv +/Volumes/192.168.2.100/Adult/Files/Rating Top 30 JAV pack/IPTD-999-C 彼女の姉貴とイケナイ関係 Rio.wmv +/Volumes/192.168.2.100/Adult/Files/Rating Top 30 JAV pack/IPTD-999-B 彼女の姉貴とイケナイ関係 Rio.wmv +/Volumes/192.168.2.100/Adult/Files/tia/soe935C.HD.wmv +/Volumes/192.168.2.100/Adult/Files/tia/soe935B.HD.wmv +/Volumes/192.168.2.100/Adult/Files/tia/soe935A.HD.wmv +/Volumes/192.168.2.100/Adult/Files/tia/soe935D.HD.wmv +/Volumes/Adult/Files/大桥步兵合集/LAFBD-41.LaForet.Girl.41.angel.and.devil.Miku.Ohashi.2015.Bluray.1080p.x264.ac3-MTeam.mkv +/Volumes/Adult/Files/[Tokyo-Hot] [n1180] 美人秘書3穴串刺奉仕残業 (中井綾香 Ayaka Nakai)/(Tokyo-Hot)(n1180)美人秘書3穴串刺奉仕残業 中井綾香.mp4 +/mcdv47.avi +/mcdv-47.avi +/mcdv-047.mp4 +/mcdv047.mp4 +/mcdv0047.mp4 +/1pondo-070409_621.mp4 +/Volumes/Adult/Files/Kirara Asuka (@明日花キララ) FHD Pack Vol#1(181222)@RUNBKK/No-Watermarked/HOBD00015.FHD2.wmv +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 1/720p/RBD-406_1.mp4 +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 1/720p/MDYD-664B.mp4 +/Volumes/Adult/Files/107NTTR-037A.mp4 +/Volumes/Adult/Files/Yua.Mikami-PML/SNIS-986 国民的アイドル アドレナリン大爆発!禁欲1ヶ月後の性欲剥き出し焦らされトランスFUCK 三上悠亜【桃花族】.mp4 +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 2/FHD/UPSM-109_2.mkv +/Volumes/Adult/Files/Kirara Asuka (@明日花キララ) FHD Pack Vol#2(181231)@RUNBKK/No-Watermarked/PPT003.SD3.wmv +/Volumes/Adult/Files/波多野结衣/THE波多野結衣 ぶっかけ50連発! CD1.wmv +/Volumes/Adult/Files/波多野结衣/欲しがり 後編 波多野結衣.wmv +/Volumes/Adult/Files/波多野结衣/欲しがり 前編 波多野結衣.wmv +/Volumes/Adult/Files/波多野结衣/加勒比 062212-055 夫の目の前で妻が ~元上司に縛られて~波多野結衣~.rmvb +/Volumes/Adult/Files/波多野结衣/022213-271-carib-whole_s.mp4 +/Volumes/Adult/Files/SKYHD-001~010/SKYHD-009_H265.mkv +/Volumes/Adult/Files/大桥步兵合集/LAFBD-41.LaForet.Girl.41.angel.and.devil.Miku.Ohashi.2015.Bluray.1080p.x264.ac3-MTeam.mkv +/Volumes/Adult/Files/大桥步兵合集/032015_161-caribpr-high.mp4 +/Volumes/Adult/Files/桃谷绘里香(桃谷エリカ) 所有作品集合/118ppt00016hhb2.mkv +/Volumes/Adult/Files/SKYHD-011~020/SKYHD-020_H265.mkv +/Volumes/Adult/Files/RION(りおん).Utsunomiya.Shion.宇都宮しをん(うつのみやしをん)/VR/sivr00008_E.mp4 +/Volumes/Adult/Files/RION(りおん).Utsunomiya.Shion.宇都宮しをん(うつのみやしをん)/DMM.Video/onsd00899hhb3.mp4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/SHKD-744 営業課長の湿ったパンスト 里美ゆりあ.mp4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/ABP-627 裏・鈴村あいり-鈴村あいりのオトナの激情SEX4本番 鈴村あいり.MP4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/20 ABP-408 上原瑞穂/上原瑞穂 ABP-408 无码流出片段/[ThZu.Cc]20150909164411.m2ts +/Volumes/Adult/Files/Caribbean-101717-520-HD/100917-515/100917-515-carib-1080p.mp4 +/Volumes/Adult/Files/ノ瀬アメリ/20081105栗栖エリカ - Sky Angel Blue 10 天舞超絕美少女天使降臨(skyhd010)(中文字幕).avi +/Volumes/Adult/Files/ノ瀬アメリ/一ノ瀬アメリ~加勒比 VERY SEXY.wmv +/Volumes/Adult/Files/ノ瀬アメリ/20101202一ノ瀬アメリ - 東京ブルドック05(inu006).avi +/Volumes/Adult/Files/ノ瀬アメリ/Sky Angel Vol 80 - CD2.mp4 +/Volumes/Adult/Files/Mika Sumire すみれ美香/Caribbean-091818-755.mp4 +/Volumes/Adult/Files/Takizawa Rola/[HD]abp-031C.wmv +/Volumes/Adult/Files/Takizawa Rola/ABP-013HDA.wmv \ No newline at end of file diff --git a/TestPaths.txt b/TestPaths.txt new file mode 100644 index 0000000..93ecbe0 --- /dev/null +++ b/TestPaths.txt @@ -0,0 +1,50 @@ +/Volumes/Adult/Files/Kirara Asuka (@明日花キララ) FHD Pack Vol#1(181222)@RUNBKK/No-Watermarked/HOBD00015.FHD2.wmv +/1pondo-070409_621.mp4 +/Volumes/Adult/Files/107NTTR-037.mp4 +/Volumes/Adult/Files/107NTTR-037A.mp4 +/Volumes/Adult/Files/Yua.Mikami-PML/TEK-097 ふたりは無敵.wmv +/Volumes/Adult/Files/Yua.Mikami-PML/SNIS-986 国民的アイドル アドレナリン大爆発!禁欲1ヶ月後の性欲剥き出し焦らされトランスFUCK 三上悠亜【桃花族】.mp4 +/Volumes/Adult/Files/Yua.Mikami-PML/SSNI-030 三上悠亜ファン感謝祭 国民的アイドル×一般ユーザー20人‘ガチファンとSEX解禁’ハメまくりスペシャル【桃花族】.mp4 +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 2/FHD/MIDD-893A.mkv +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 2/FHD/UPSM-109_2.mkv +/Volumes/Adult/Files/Kirara Asuka (@明日花キララ) FHD Pack Vol#2(181231)@RUNBKK/No-Watermarked/PPT003.SD3.wmv +/Volumes/Adult/Files/波多野结衣/THE波多野結衣 ぶっかけ50連発! CD1.wmv +/Volumes/Adult/Files/波多野结衣/欲しがり 後編 波多野結衣.wmv +/Volumes/Adult/Files/波多野结衣/欲しがり 前編 波多野結衣.wmv +/Volumes/Adult/Files/波多野结衣/加勒比 062212-055 夫の目の前で妻が ~元上司に縛られて~波多野結衣~.rmvb +/Volumes/Adult/Files/波多野结衣/022213-271-carib-whole_s.mp4 +/Volumes/Adult/Files/桜木凛 Rin Sakuragi FHD Collection Pack Vol/BBI-183.wmv +/Volumes/Adult/Files/NOP-019 芭蕾教室 水嶋あずみ/NOP019B.HD.wmv +/Volumes/Adult/Files/一ノ瀬アメリ part2/栗栖エリカ/20081105栗栖エリカ - Sky Angel Blue 10 天舞超絕美少女天使降臨(skyhd010)(中文字幕).avi +/Volumes/Adult/Files/一ノ瀬アメリ part2/Max Girls/Max Girls 24(xv804)伊東遥,Rio,小沢アリス,葉月しおり,一ノ瀬アメリ,ひなた結衣,藤崎りお.avi +/Volumes/Adult/Files/一ノ瀬アメリ part2/ノ瀬アメリAmeri Ichinose/20091127一ノ瀬アメリ - 一見面就做愛(xv801).avi +/Volumes/Adult/Files/Aki Sasaki Megapack/MSTG-003.mkv +/Volumes/Adult/Files/SKYHD-001~010/SKYHD-009_H265.mkv +/Volumes/Adult/Files/大桥步兵合集/LAFBD-41.LaForet.Girl.41.angel.and.devil.Miku.Ohashi.2015.Bluray.1080p.x264.ac3-MTeam.mkv +/Volumes/Adult/Files/大桥步兵合集/032015_161-caribpr-high.mp4 +/Volumes/Adult/Files/桃谷绘里香(桃谷エリカ) 所有作品集合/(PRESTIGE)(ABP-171)彼女のお姉さんは、誘惑ヤリたがり娘。桃谷エリカ.wmv +/Volumes/Adult/Files/桃谷绘里香(桃谷エリカ) 所有作品集合/(PRESTIGE)(ABP-145)濃密な接吻と欲情ベロキス性交 04 桃谷エリカ.wmv +/Volumes/Adult/Files/桃谷绘里香(桃谷エリカ) 所有作品集合/118ppt00016hhb2.mkv +/Volumes/Adult/Files/tia/soe935C.HD.wmv +/Volumes/Adult/Files/SKYHD-011~020/SKYHD-020_H265.mkv +/Volumes/Adult/Files/sakumomo1203-PML/IDBD-795 ももに夢中 2018年日本人にもっとも愛された女優桜空ももPREMIUM BOX8時間BEST.mp4 +/Volumes/Adult/Files/sakumomo1203-PML/IDBD-768 Gカップグラビアアイドル桜空もも初ベスト 原石 2【桃花族】.mp4 +/Volumes/Adult/Files/RION(りおん).Utsunomiya.Shion.宇都宮しをん(うつのみやしをん)/VR/sivr00008_E.mp4 +/Volumes/Adult/Files/RION(りおん).Utsunomiya.Shion.宇都宮しをん(うつのみやしをん)/DMM.Video/onsd00899hhb3.mp4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/SHKD-744 営業課長の湿ったパンスト 里美ゆりあ.mp4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/ABP-627 裏・鈴村あいり-鈴村あいりのオトナの激情SEX4本番 鈴村あいり.MP4 +/Volumes/Adult/Files/Rating Top 30 JAV pack/20 ABP-408 上原瑞穂/上原瑞穂 ABP-408 无码流出片段/[ThZu.Cc]20150909164411.m2ts +/Volumes/Adult/Files/Caribbean-101717-520-HD/100917-515/100917-515-carib-1080p.mp4 +/Volumes/Adult/Files/Kirara Asuka (@明日花キララ) FHD Pack Vol#3(190119)@RUNBKK/No-Watermarked/SOE976.FHD3.wmv +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 1/720p/RBD-406_1.mp4 +/Volumes/Adult/Files/(1.18TB) Julia movie pack collection Part 1/720p/MDYD-664B.mp4 +/Volumes/Adult/Files/ノ瀬アメリ/20081105栗栖エリカ - Sky Angel Blue 10 天舞超絕美少女天使降臨(skyhd010)(中文字幕).avi +/Volumes/Adult/Files/ノ瀬アメリ/一ノ瀬アメリ~加勒比 VERY SEXY.wmv +/Volumes/Adult/Files/ノ瀬アメリ/20101202一ノ瀬アメリ - 東京ブルドック05(inu006).avi +/Volumes/Adult/Files/ノ瀬アメリ/Sky Angel Vol 80 - CD2.mp4 +/Volumes/Adult/Files/ノ瀬アメリ/20100226一ノ瀬アメリ - OL Style 制服(xv827).avi +/Volumes/Adult/Files/Mika Sumire すみれ美香/Caribbean-091818-755.mp4 +/Volumes/Adult/Files/[Tokyo-Hot] [n1180] 美人秘書3穴串刺奉仕残業 (中井綾香 Ayaka Nakai)/(Tokyo-Hot)(n1180)美人秘書3穴串刺奉仕残業 中井綾香.mp4 +/Volumes/Adult/Files/Takizawa Rola/[HD]abp-031C.wmv +/Volumes/Adult/Files/Takizawa Rola/ABP-013HDA.wmv +/Volumes/Adult/Files/Uncensored Mosaic Removal Megapack/ADN-017(Asami Ogawa).mp4 \ No newline at end of file diff --git a/config.ini b/config.ini old mode 100644 new mode 100755 index f2ac60d..a017bab --- a/config.ini +++ b/config.ini @@ -1,28 +1,35 @@ [common] -main_mode=1 -failed_output_folder=failed -success_output_folder=JAV_output +main_mode=2 +# 路径均为绝对路径,不要写入" '等符号 +search_folder= /Volumes/192.168.2.100/Adult/AVTest +# 如果failed_output_folder 为空,抓取不到相关信息的视频将不回移动 +failed_output_folder= /Volumes/192.168.2.100/Adult/UnknownStars +success_output_folder= /Volumes/192.168.2.100/Adult/Files +#临时资源存储路径,比如xxx.nfo 海报图 +temp_folder= /Volumes/192.168.2.100/Adult/temp +# 如果是远程挂载的盘符,建议不开启创建软连接:软连接链接的是绝对路径,远程NAS上的路径和本地挂载的路径一般不同。 soft_link=0 [proxy] -proxy=127.0.0.1:1080 -timeout=10 -retry=3 +#例子为socks代理配置,可以 =后留空 +proxy= socks5h://127.0.0.1:1081 +timeout= 10 +retry= 5 [Name_Rule] -location_rule=actor+'/'+number -naming_rule=number+'-'+title +location_rule= actor+'/'+number +naming_rule= number+'-'+title [update] update_check=1 [media] -media_warehouse=emby #emby or plex or kodi ,emby=jellyfin +media_warehouse=EMBY [escape] literals=\() -folders=failed,JAV_output +folders=/Volumes/Adult/UnknownStars,/Volumes/Adult/Stars [debug_mode] -switch=1 \ No newline at end of file +switch=1 diff --git a/core.py b/core.py index 5b47d6b..23e1237 100755 --- a/core.py +++ b/core.py @@ -1,691 +1,918 @@ -# -*- coding: utf-8 -*- - -import re -import os -import os.path -import shutil -from PIL import Image -import time -import json -from ADC_function import * -from configparser import ConfigParser -import argparse -# =========website======== -import fc2fans_club -import mgstage -import avsox -import javbus -import javdb -import fanza -import jav321 -import requests - - -# =====================本地文件处理=========================== - -def escapePath(path, Config): # Remove escape literals - escapeLiterals = Config['escape']['literals'] - backslash = '\\' - for literal in escapeLiterals: - path = path.replace(backslash + literal, '') - return path - - -def moveFailedFolder(filepath, failed_folder): - print('[-]Move to Failed output folder') - shutil.move(filepath, str(os.getcwd()) + '/' + failed_folder + '/') - return - - -def CreatFailedFolder(failed_folder): - if not os.path.exists(failed_folder + '/'): # 新建failed文件夹 - try: - os.makedirs(failed_folder + '/') - except: - print("[-]failed!can not be make Failed output folder\n[-](Please run as Administrator)") - return - - -def getDataFromJSON(file_number, filepath, failed_folder): # 从JSON返回元数据 - """ - iterate through all services and fetch the data - """ - - func_mapping = { - "avsox": avsox.main, - "fc2": fc2fans_club.main, - "fanza": fanza.main, - "javdb": javdb.main, - "javbus": javbus.main, - "mgstage": mgstage.main, - "jav321": jav321.main, - } - - # default fetch order list, from the begining to the end - sources = ["javbus", "javdb", "fanza", "mgstage", "fc2", "avsox", "jav321"] - - # if the input file name matches centain rules, - # move some web service to the begining of the list - if re.match(r"^\d{5,}", file_number) or ( - "HEYZO" in file_number or "heyzo" in file_number or "Heyzo" in file_number - ): - sources.insert(0, sources.pop(sources.index("avsox"))) - elif re.match(r"\d+\D+", file_number) or ( - "siro" in file_number or "SIRO" in file_number or "Siro" in file_number - ): - sources.insert(0, sources.pop(sources.index("mgstage"))) - elif "fc2" in file_number or "FC2" in file_number: - sources.insert(0, sources.pop(sources.index("fc2"))) - - for source in sources: - json_data = json.loads(func_mapping[source](file_number)) - # if any service return a valid return, break - if getDataState(json_data) != 0: - break - - # ================================================网站规则添加结束================================================ - - title = json_data['title'] - actor_list = str(json_data['actor']).strip("[ ]").replace("'", '').split(',') # 字符串转列表 - release = json_data['release'] - number = json_data['number'] - studio = json_data['studio'] - source = json_data['source'] - runtime = json_data['runtime'] - outline = json_data['runtime'] - label = json_data['label'] - year = json_data['year'] - try: - cover_small = json_data['cover_small'] - except: - cover_small = '' - imagecut = json_data['imagecut'] - tag = str(json_data['tag']).strip("[ ]").replace("'", '').replace(" ", '').split(',') # 字符串转列表 @ - actor = str(actor_list).strip("[ ]").replace("'", '').replace(" ", '') - - - if title == '' or number == '': - print('[-]Movie Data not found!') - moveFailedFolder(filepath, failed_folder) - return - - # if imagecut == '3': - # DownloadFileWithFilename() - - # ====================处理异常字符====================== #\/:*?"<>| - title = title.replace('\\', '') - title = title.replace('/', '') - title = title.replace(':', '') - title = title.replace('*', '') - title = title.replace('?', '') - title = title.replace('"', '') - title = title.replace('<', '') - title = title.replace('>', '') - title = title.replace('|', '') - release = release.replace('/', '-') - tmpArr = cover_small.split(',') - if len(tmpArr) > 0: - cover_small = tmpArr[0].strip('\"').strip('\'') - # ====================处理异常字符 END================== #\/:*?"<>| - - naming_rule = eval(config['Name_Rule']['naming_rule']) - location_rule = eval(config['Name_Rule']['location_rule']) - - # 返回处理后的json_data - json_data['title'] = title - json_data['actor'] = actor - json_data['release'] = release - json_data['cover_small'] = cover_small - json_data['tag'] = tag - json_data['naming_rule'] = naming_rule - json_data['location_rule'] = location_rule - json_data['year'] = year - return json_data - - -def get_info(json_data): # 返回json里的数据 - title = json_data['title'] - studio = json_data['studio'] - year = json_data['year'] - outline = json_data['outline'] - runtime = json_data['runtime'] - director = json_data['director'] - actor_photo = json_data['actor_photo'] - release = json_data['release'] - number = json_data['number'] - cover = json_data['cover'] - website = json_data['website'] - return title, studio, year, outline, runtime, director, actor_photo, release, number, cover, website - - -def smallCoverCheck(path, number, imagecut, cover_small, c_word, option, Config, filepath, failed_folder): - if imagecut == 3: - if option == 'emby': - DownloadFileWithFilename(cover_small, '1.jpg', path, Config, filepath, failed_folder) - try: - img = Image.open(path + '/1.jpg') - except Exception: - img = Image.open('1.jpg') - w = img.width - h = img.height - img.save(path + '/' + number + c_word + '.png') - time.sleep(1) - os.remove(path + '/1.jpg') - if option == 'kodi': - DownloadFileWithFilename(cover_small, '1.jpg', path, Config, filepath, failed_folder) - try: - img = Image.open(path + '/1.jpg') - except Exception: - img = Image.open('1.jpg') - w = img.width - h = img.height - img.save(path + '/' + number + c_word + '-poster.jpg') - time.sleep(1) - os.remove(path + '/1.jpg') - if option == 'plex': - DownloadFileWithFilename(cover_small, '1.jpg', path, Config, filepath, failed_folder) - try: - img = Image.open(path + '/1.jpg') - except Exception: - img = Image.open('1.jpg') - w = img.width - h = img.height - img.save(path + '/poster.jpg') - os.remove(path + '/1.jpg') - - -def creatFolder(success_folder, location_rule, json_data, Config): # 创建文件夹 - title, studio, year, outline, runtime, director, actor_photo, release, number, cover, website = get_info(json_data) - if len(location_rule) > 240: # 新建成功输出文件夹 - path = success_folder + '/' + location_rule.replace("'actor'", "'manypeople'", 3).replace("actor", - "'manypeople'", - 3) # path为影片+元数据所在目录 - else: - path = success_folder + '/' + location_rule - # print(path) - if not os.path.exists(path): - path = escapePath(path, Config) - try: - os.makedirs(path) - except: - path = success_folder + '/' + location_rule.replace('/[' + number + ']-' + title, "/number") - path = escapePath(path, Config) - os.makedirs(path) - return path - - -# =====================资源下载部分=========================== -def DownloadFileWithFilename(url, filename, path, Config, filepath, failed_folder): # path = examle:photo , video.in the Project Folder! - proxy, timeout, retry_count = get_network_settings() - i = 0 - - while i < retry_count: - try: - if not proxy == '': - if not os.path.exists(path): - os.makedirs(path) - headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'} - r = requests.get(url, headers=headers, timeout=timeout, - proxies={"http": "http://" + str(proxy), "https": "https://" + str(proxy)}) - if r == '': - print('[-]Movie Data not found!') - return - with open(str(path) + "/" + filename, "wb") as code: - code.write(r.content) - return - else: - if not os.path.exists(path): - os.makedirs(path) - headers = { - 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'} - r = requests.get(url, timeout=timeout, headers=headers) - if r == '': - print('[-]Movie Data not found!') - return - with open(str(path) + "/" + filename, "wb") as code: - code.write(r.content) - return - except requests.exceptions.RequestException: - i += 1 - print('[-]Image Download : Connect retry ' + str(i) + '/' + str(retry_count)) - except requests.exceptions.ConnectionError: - i += 1 - print('[-]Image Download : Connect retry ' + str(i) + '/' + str(retry_count)) - except requests.exceptions.ProxyError: - i += 1 - print('[-]Image Download : Connect retry ' + str(i) + '/' + str(retry_count)) - except requests.exceptions.ConnectTimeout: - i += 1 - print('[-]Image Download : Connect retry ' + str(i) + '/' + str(retry_count)) - print('[-]Connect Failed! Please check your Proxy or Network!') - moveFailedFolder(filepath, failed_folder) - return - - -def imageDownload(option, cover, number, c_word, path, multi_part, Config, filepath, failed_folder): # 封面是否下载成功,否则移动到failed - if option == 'emby': - if DownloadFileWithFilename(cover, number + c_word + '.jpg', path, Config, filepath, failed_folder) == 'failed': - moveFailedFolder(filepath, failed_folder) - return - DownloadFileWithFilename(cover, number + c_word + '.jpg', path, Config, filepath, failed_folder) - if not os.path.getsize(path + '/' + number + c_word + '.jpg') == 0: - print('[+]Image Downloaded!', path + '/' + number + c_word + '.jpg') - return - i = 1 - while i <= int(config['proxy']['retry']): - if os.path.getsize(path + '/' + number + c_word + '.jpg') == 0: - print('[!]Image Download Failed! Trying again. [' + config['proxy']['retry'] + '/3]') - DownloadFileWithFilename(cover, number + c_word + '.jpg', path, Config, filepath, failed_folder) - i = i + 1 - continue - else: - break - if multi_part == 1: - old_name = os.path.join(path, number + c_word + '.jpg') - new_name = os.path.join(path, number + c_word + '.jpg') - os.rename(old_name, new_name) - print('[+]Image Downloaded!', path + '/' + number + c_word + '.jpg') - else: - print('[+]Image Downloaded!', path + '/' + number + c_word + '.jpg') - elif option == 'plex': - if DownloadFileWithFilename(cover, 'fanart.jpg', path, Config, filepath, failed_folder) == 'failed': - moveFailedFolder(filepath, failed_folder) - return - DownloadFileWithFilename(cover, 'fanart.jpg', path, Config, filepath, failed_folder) - if not os.path.getsize(path + '/fanart.jpg') == 0: - print('[+]Image Downloaded!', path + '/fanart.jpg') - return - i = 1 - while i <= int(config['proxy']['retry']): - if os.path.getsize(path + '/fanart.jpg') == 0: - print('[!]Image Download Failed! Trying again. [' + config['proxy']['retry'] + '/3]') - DownloadFileWithFilename(cover, 'fanart.jpg', path, Config, filepath, failed_folder) - i = i + 1 - continue - else: - break - if not os.path.getsize(path + '/' + number + c_word + '.jpg') == 0: - print('[!]Image Download Failed! Trying again.') - DownloadFileWithFilename(cover, number + c_word + '.jpg', path, Config, filepath, failed_folder) - print('[+]Image Downloaded!', path + '/fanart.jpg') - elif option == 'kodi': - if DownloadFileWithFilename(cover, number + c_word + '-fanart.jpg', path, Config, filepath, failed_folder) == 'failed': - moveFailedFolder(filepath, failed_folder) - return - DownloadFileWithFilename(cover, number + c_word + '-fanart.jpg', path, Config, filepath, failed_folder) - if not os.path.getsize(path + '/' + number + c_word + '-fanart.jpg') == 0: - print('[+]Image Downloaded!', path + '/' + number + c_word + '-fanart.jpg') - return - i = 1 - while i <= int(config['proxy']['retry']): - if os.path.getsize(path + '/' + number + c_word + '-fanart.jpg') == 0: - print('[!]Image Download Failed! Trying again. [' + config['proxy']['retry'] + '/3]') - DownloadFileWithFilename(cover, number + c_word + '-fanart.jpg', path, Config, filepath, failed_folder) - i = i + 1 - continue - else: - break - print('[+]Image Downloaded!', path + '/' + number + c_word + '-fanart.jpg') - - -def PrintFiles(option, path, c_word, naming_rule, part, cn_sub, json_data, filepath, failed_folder, tag): - title, studio, year, outline, runtime, director, actor_photo, release, number, cover, website = get_info(json_data) - try: - if not os.path.exists(path): - os.makedirs(path) - if option == 'plex': - with open(path + "/" + number + c_word + ".nfo", "wt", encoding='UTF-8') as code: - print('', file=code) - print("", file=code) - print(" " + naming_rule + part + "", file=code) - print(" ", file=code) - print(" ", file=code) - print(" " + studio + "+", file=code) - print(" " + year + "", file=code) - print(" " + outline + "", file=code) - print(" " + outline + "", file=code) - print(" " + str(runtime).replace(" ", "") + "", file=code) - print(" " + director + "", file=code) - print(" poster.jpg", file=code) - print(" thumb.png", file=code) - print(" fanart.jpg", file=code) - try: - for key, value in actor_photo.items(): - print(" ", file=code) - print(" " + key + "", file=code) - if not value == '': # or actor_photo == []: - print(" " + value + "", file=code) - print(" ", file=code) - except: - aaaa = '' - print(" " + studio + "", file=code) - print(" ", file=code) - if cn_sub == '1': - print(" 中文字幕", file=code) - try: - for i in str(json_data['tag']).strip("[ ]").replace("'", '').replace(" ", '').split(','): - print(" " + i + "", file=code) - except: - aaaaa = '' - try: - for i in str(json_data['tag']).strip("[ ]").replace("'", '').replace(" ", '').split(','): - print(" " + i + "", file=code) - except: - aaaaaaaa = '' - if cn_sub == '1': - print(" 中文字幕", file=code) - print(" " + number + "", file=code) - print(" " + release + "", file=code) - print(" " + cover + "", file=code) - print(" " + website + "", file=code) - print("", file=code) - print("[+]Writeed! " + path + "/" + number + ".nfo") - elif option == 'emby': - with open(path + "/" + number + c_word + ".nfo", "wt", encoding='UTF-8') as code: - print('', file=code) - print("", file=code) - print(" " + naming_rule + part + "", file=code) - print(" ", file=code) - print(" ", file=code) - print(" " + studio + "+", file=code) - print(" " + year + "", file=code) - print(" " + outline + "", file=code) - print(" " + outline + "", file=code) - print(" " + str(runtime).replace(" ", "") + "", file=code) - print(" " + director + "", file=code) - print(" " + number + c_word + ".png", file=code) - print(" " + number + c_word + ".png", file=code) - print(" " + number + c_word + '.jpg' + "", file=code) - try: - for key, value in actor_photo.items(): - print(" ", file=code) - print(" " + key + "", file=code) - if not value == '': # or actor_photo == []: - print(" " + value + "", file=code) - print(" ", file=code) - except: - aaaa = '' - print(" " + studio + "", file=code) - print(" ", file=code) - if cn_sub == '1': - print(" 中文字幕", file=code) - try: - for i in tag: - print(" " + i + "", file=code) - except: - aaaaa = '' - try: - for i in tag: - print(" " + i + "", file=code) - except: - aaaaaaaa = '' - if cn_sub == '1': - print(" 中文字幕", file=code) - print(" " + number + "", file=code) - print(" " + release + "", file=code) - print(" " + cover + "", file=code) - print(" " + website + "", file=code) - print("", file=code) - print("[+]Writeed! " + path + "/" + number + c_word + ".nfo") - elif option == 'kodi': - with open(path + "/" + number + c_word + ".nfo", "wt", encoding='UTF-8') as code: - print('', file=code) - print("", file=code) - print(" " + naming_rule + part + "", file=code) - print(" ", file=code) - print(" ", file=code) - print(" " + studio + "+", file=code) - print(" " + year + "", file=code) - print(" " + outline + "", file=code) - print(" " + outline + "", file=code) - print(" " + str(runtime).replace(" ", "") + "", file=code) - print(" " + director + "", file=code) - print(" " + number + c_word + "-poster.jpg", file=code) - print(" " + number + c_word + '-fanart.jpg' + "", file=code) - try: - for key, value in actor_photo.items(): - print(" ", file=code) - print(" " + key + "", file=code) - if not value == '': # or actor_photo == []: - print(" " + value + "", file=code) - print(" ", file=code) - except: - aaaa = '' - print(" " + studio + "", file=code) - print(" ", file=code) - if cn_sub == '1': - print(" 中文字幕", file=code) - try: - for i in tag: - print(" " + i + "", file=code) - except: - aaaaa = '' - try: - for i in tag: - print(" " + i + "", file=code) - except: - aaaaaaaa = '' - if cn_sub == '1': - print(" 中文字幕", file=code) - print(" " + number + "", file=code) - print(" " + release + "", file=code) - print(" " + cover + "", file=code) - print(" " + website + "", file=code) - print("", file=code) - print("[+]Writeed! " + path + "/" + number + c_word + ".nfo") - except IOError as e: - print("[-]Write Failed!") - print(e) - moveFailedFolder(filepath, failed_folder) - return - except Exception as e1: - print(e1) - print("[-]Write Failed!") - moveFailedFolder(filepath, failed_folder) - return - - -def cutImage(option, imagecut, path, number, c_word): - if option == 'plex': - if imagecut == 1: - try: - img = Image.open(path + '/fanart.jpg') - imgSize = img.size - w = img.width - h = img.height - img2 = img.crop((w / 1.9, 0, w, h)) - img2.save(path + '/poster.jpg') - except: - print('[-]Cover cut failed!') - elif imagecut == 0: - img = Image.open(path + '/fanart.jpg') - w = img.width - h = img.height - img.save(path + '/poster.jpg') - elif option == 'emby': - if imagecut == 1: - try: - img = Image.open(path + '/' + number + c_word + '.jpg') - imgSize = img.size - w = img.width - h = img.height - img2 = img.crop((w / 1.9, 0, w, h)) - img2.save(path + '/' + number + c_word + '.png') - except: - print('[-]Cover cut failed!') - elif imagecut == 0: - img = Image.open(path + '/' + number + c_word + '.jpg') - w = img.width - h = img.height - img.save(path + '/' + number + c_word + '.png') - elif option == 'kodi': - if imagecut == 1: - try: - img = Image.open(path + '/' + number + c_word + '-fanart.jpg') - imgSize = img.size - w = img.width - h = img.height - img2 = img.crop((w / 1.9, 0, w, h)) - img2.save(path + '/' + number + c_word + '-poster.jpg') - except: - print('[-]Cover cut failed!') - elif imagecut == 0: - img = Image.open(path + '/' + number + c_word + '-fanart.jpg') - w = img.width - h = img.height - try: - img = img.convert('RGB') - img.save(path + '/' + number + c_word + '-poster.jpg') - except: - img = img.convert('RGB') - img.save(path + '/' + number + c_word + '-poster.jpg') - - -def pasteFileToFolder(filepath, path, number, c_word): # 文件路径,番号,后缀,要移动至的位置 - houzhui = str(re.search('[.](AVI|RMVB|WMV|MOV|MP4|MKV|FLV|TS|WEBM|avi|rmvb|wmv|mov|mp4|mkv|flv|ts|webm)$', filepath).group()) - try: - if config['common']['soft_link'] == '1': # 如果soft_link=1 使用软链接 - os.symlink(filepath, path + '/' + number + c_word + houzhui) - else: - os.rename(filepath, path + '/' + number + c_word + houzhui) - if os.path.exists(os.getcwd() + '/' + number + c_word + '.srt'): # 字幕移动 - os.rename(os.getcwd() + '/' + number + c_word + '.srt', path + '/' + number + c_word + '.srt') - print('[+]Sub moved!') - elif os.path.exists(os.getcwd() + '/' + number + c_word + '.ssa'): - os.rename(os.getcwd() + '/' + number + c_word + '.ssa', path + '/' + number + c_word + '.ssa') - print('[+]Sub moved!') - elif os.path.exists(os.getcwd() + '/' + number + c_word + '.sub'): - os.rename(os.getcwd() + '/' + number + c_word + '.sub', path + '/' + number + c_word + '.sub') - print('[+]Sub moved!') - except FileExistsError: - print('[-]File Exists! Please check your movie!') - print('[-]move to the root folder of the program.') - return - except PermissionError: - print('[-]Error! Please run as administrator!') - return - - -def pasteFileToFolder_mode2(filepath, path, multi_part, number, part, c_word): # 文件路径,番号,后缀,要移动至的位置 - if multi_part == 1: - number += part # 这时number会被附加上CD1后缀 - houzhui = str(re.search('[.](AVI|RMVB|WMV|MOV|MP4|MKV|FLV|TS|WEBM|avi|rmvb|wmv|mov|mp4|mkv|flv|ts|webm)$', filepath).group()) - try: - if config['common']['soft_link'] == '1': - os.symlink(filepath, path + '/' + number + part + c_word + houzhui) - else: - os.rename(filepath, path + '/' + number + part + c_word + houzhui) - if os.path.exists(number + '.srt'): # 字幕移动 - os.rename(number + part + c_word + '.srt', path + '/' + number + part + c_word + '.srt') - print('[+]Sub moved!') - elif os.path.exists(number + part + c_word + '.ass'): - os.rename(number + part + c_word + '.ass', path + '/' + number + part + c_word + '.ass') - print('[+]Sub moved!') - elif os.path.exists(number + part + c_word + '.sub'): - os.rename(number + part + c_word + '.sub', path + '/' + number + part + c_word + '.sub') - print('[+]Sub moved!') - print('[!]Success') - except FileExistsError: - print('[-]File Exists! Please check your movie!') - print('[-]move to the root folder of the program.') - return - except PermissionError: - print('[-]Error! Please run as administrator!') - return - - -def copyRenameJpgToBackdrop(option, path, number, c_word): - if option == 'plex': - shutil.copy(path + '/fanart.jpg', path + '/Backdrop.jpg') - shutil.copy(path + '/poster.jpg', path + '/thumb.png') - if option == 'emby': - shutil.copy(path + '/' + number + c_word + '.jpg', path + '/Backdrop.jpg') - if option == 'kodi': - shutil.copy(path + '/' + number + c_word + '-fanart.jpg', path + '/Backdrop.jpg') - - -def get_part(filepath, failed_folder): - try: - if re.search('-CD\d+', filepath): - return re.findall('-CD\d+', filepath)[0] - if re.search('-cd\d+', filepath): - return re.findall('-cd\d+', filepath)[0] - except: - print("[-]failed!Please rename the filename again!") - moveFailedFolder(filepath, failed_folder) - return - - -def debug_mode(json_data): - try: - if config['debug_mode']['switch'] == '1': - print('[+] ---Debug info---') - for i, v in json_data.items(): - if i == 'outline': - print('[+] -', i, ' :', len(v), 'characters') - continue - if i == 'actor_photo' or i == 'year': - continue - print('[+] -', "%-11s" % i, ':', v) - print('[+] ---Debug info---') - except: - aaa = '' - - -def core_main(file_path, number_th): - # =======================================================================初始化所需变量 - multi_part = 0 - part = '' - c_word = '' - option = '' - cn_sub = '' - config_file = 'config.ini' - Config = ConfigParser() - Config.read(config_file, encoding='UTF-8') - try: - option = ReadMediaWarehouse() - except: - print('[-]Config media_warehouse read failed!') - program_mode = Config['common']['main_mode'] # 运行模式 - failed_folder = Config['common']['failed_output_folder'] # 失败输出目录 - success_folder = Config['common']['success_output_folder'] # 成功输出目录 - filepath = file_path # 影片的路径 - number = number_th - json_data = getDataFromJSON(number, filepath, failed_folder) # 定义番号 - if json_data["number"] != number: - # fix issue #119 - # the root cause is we normalize the search id - # PrintFiles() will use the normalized id from website, - # but pasteFileToFolder() still use the input raw search id - # so the solution is: use the normalized search id - number = json_data["number"] - imagecut = json_data['imagecut'] - tag = json_data['tag'] - # =======================================================================判断-C,-CD后缀 - if '-CD' in filepath or '-cd' in filepath: - multi_part = 1 - part = get_part(filepath, failed_folder) - if '-c.' in filepath or '-C.' in filepath or '中文' in filepath or '字幕' in filepath: - cn_sub = '1' - c_word = '-C' # 中文字幕影片后缀 - - CreatFailedFolder(failed_folder) # 创建输出失败目录 - debug_mode(json_data) # 调试模式检测 - path = creatFolder(success_folder, json_data['location_rule'], json_data, Config) # 创建文件夹 - # =======================================================================刮削模式 - if program_mode == '1': - if multi_part == 1: - number += part # 这时number会被附加上CD1后缀 - smallCoverCheck(path, number, imagecut, json_data['cover_small'], c_word, option, Config, filepath, failed_folder) # 检查小封面 - imageDownload(option, json_data['cover'], number, c_word, path, multi_part, Config, filepath, failed_folder) # creatFoder会返回番号路径 - cutImage(option, imagecut, path, number, c_word) # 裁剪图 - copyRenameJpgToBackdrop(option, path, number, c_word) - PrintFiles(option, path, c_word, json_data['naming_rule'], part, cn_sub, json_data, filepath, failed_folder, tag) # 打印文件 - pasteFileToFolder(filepath, path, number, c_word) # 移动文件 - # =======================================================================整理模式 - elif program_mode == '2': - pasteFileToFolder_mode2(filepath, path, multi_part, number, part, c_word) # 移动文件 +# -*- coding: utf-8 -*- + +import os.path +import shutil +from PIL import Image +import json +from ADC_function import * +from MediaServer import * +from AV_Data_Capture import config +import lazyxml +# =========website======== +from SiteSource import avsox, javdb, fc2fans_club, javbus, fanza, mgstage +import requests +from enum import Enum, auto + + +# =====================本地文件处理=========================== + +def escapePath(path, escapeLiterals): # Remove escape literals + # escapeLiterals = Config['escape']['literals'] + backslash = '\\' + for literal in escapeLiterals: + path = path.replace(backslash + literal, '') + return path + + +def moveFailedFolder(filepath, failed_folder): + if failed_folder.strip() == '': + print('[+]Failed output folder is Empty') + else: + print('[-]Move to Failed output folder') + shutil.move(filepath, failed_folder) + return + + +def CreatFailedFolder(failed_folder): + if not os.path.exists(failed_folder + '/'): # 新建failed文件夹 + try: + os.makedirs(failed_folder + '/') + except: + print("[-]failed!can not be make Failed output folder\n[-](Please run as Administrator)") + return + + # 根据番号获取字典数据 + + +class SiteSource(Enum): + AVSOX = auto() + FC2 = auto() + FANZA = auto() + JAVDB = auto() + JAVBUS = auto() + MGSTAGE = auto() + + +def getDataFromJSON(file_number): # 从JSON返回元数据 + """ + iterate through all services and fetch the data + """ + + func_mapping = { + "avsox": avsox.main, + "fc2": fc2fans_club.main, + "fanza": fanza.main, + "javdb": javdb.main, + "javbus": javbus.main, + "mgstage": mgstage.main, + } + + # default fetch order list, from the begining to the end + sources = ["javbus", "javdb", "fanza", "mgstage", "fc2", "avsox"] + + # if the input file name matches centain rules, + # move some web service to the begining of the list + if re.match(r"^\d{5,}", file_number) or re.match(r'heyzo', file_number, re.IGNORECASE): + sources.insert(0, sources.pop(sources.index("avsox"))) + elif re.match(r"\d+\D+", file_number) or re.match(r'siro', file_number, re.IGNORECASE): + sources.insert(0, sources.pop(sources.index("mgstage"))) + sources.insert(0, sources.pop(sources.index("fanza"))) + elif re.match(r'fc2', file_number, re.IGNORECASE): + sources.insert(0, sources.pop(sources.index("fc2"))) + + for source in sources: + json_data = json.loads(func_mapping[source](file_number)) + # if any service return a valid return, break + if getDataState(json_data) != 0: + break + + # ================================================网站规则添加结束================================================ + + title = json_data['title'] + actor_list = str(json_data['actor']).strip("[ ]").replace("'", '').split(',') # 字符串转列表 + release = json_data['release'] + number = json_data['number'] + studio = json_data['studio'] + source = json_data['source'] + runtime = json_data['runtime'] + outline = json_data['runtime'] + label = json_data['label'] + year = json_data['year'] + try: + cover_small = json_data['cover_small'] + except: + cover_small = '' + + imagecut = json_data['imagecut'] + tag = str(json_data['tag']).strip("[ ]").replace("'", '').replace(" ", '').split(',') # 字符串转列表 @ + actor = str(actor_list).strip("[ ]").replace("'", '').replace(" ", '') + + if title == '' or number == '': + raise Exception('[-]Movie Data not found!') + + # if imagecut == '3': + # DownloadFileWithFilename() + + # ====================处理异常字符====================== #\/:*?"<>| + title = re.sub(r'[#\\/:*?"<>|\]]', '', title, 0, re.IGNORECASE) + release = release.replace('/', '-') + tmpArr = cover_small.split(',') + if len(tmpArr) > 0: + cover_small = tmpArr[0].strip('\"').strip('\'') + # ====================处理异常字符 END================== #\/:*?"<>| + + naming_rule = eval(config.naming_rule) + location_rule = eval(config.location_rule) + + # 返回处理后的json_data + json_data['title'] = title + json_data['actor'] = actor + json_data['release'] = release + json_data['cover_small'] = cover_small + json_data['tag'] = tag + json_data['naming_rule'] = naming_rule + json_data['location_rule'] = location_rule + json_data['year'] = year + return json_data + + +def get_info(json_data): # 返回json里的数据 + title = json_data['title'] + studio = json_data['studio'] + year = json_data['year'] + outline = json_data['outline'] + runtime = json_data['runtime'] + director = json_data['director'] + actor_photo = json_data['actor_photo'] + release = json_data['release'] + number = json_data['number'] + cover = json_data['cover'] + website = json_data['website'] + return title, studio, year, outline, runtime, director, actor_photo, release, number, cover, website + + +def download_cover_file(url, name, folder_path): + """ + download small cover + :param url: url + :param name: name same as movie's name without ext + :param folder_path: dir to save + :return: + """ + filename = config.media_server.poster_name(name) + DownloadFileWithFilename(url, filename, folder_path) + + +def smallCoverCheck(path, number, imagecut, cover_small, c_word, option, filepath, failed_folder): + if imagecut == 3: + if option == 'emby': + DownloadFileWithFilename(cover_small, '1.jpg', path, filepath, failed_folder) + try: + img = Image.open(path + '/1.jpg') + except Exception: + img = Image.open('1.jpg') + w = img.width + h = img.height + img.save(path + '/' + number + c_word + '.png') + time.sleep(1) + os.remove(path + '/1.jpg') + if option == 'kodi': + DownloadFileWithFilename(cover_small, '1.jpg', path, filepath, failed_folder) + try: + img = Image.open(path + '/1.jpg') + except Exception: + img = Image.open('1.jpg') + w = img.width + h = img.height + img.save(path + '/' + number + c_word + '-poster.jpg') + time.sleep(1) + os.remove(path + '/1.jpg') + if option == 'plex': + DownloadFileWithFilename(cover_small, '1.jpg', path, filepath, failed_folder) + try: + img = Image.open(path + '/1.jpg') + except Exception: + img = Image.open('1.jpg') + w = img.width + h = img.height + img.save(path + '/poster.jpg') + os.remove(path + '/1.jpg') + + +def creatFolder(success_folder, location_rule, json_data, escapeLiterals): # 创建文件夹 + title, studio, year, outline, runtime, director, actor_photo, release, number, cover, website = get_info(json_data) + if len(location_rule) > 240: # 新建成功输出文件夹 + path = success_folder + '/' + location_rule.replace("'actor'", "'manypeople'", 3).replace("actor", + "'manypeople'", + 3) # path为影片+元数据所在目录 + else: + path = success_folder + '/' + location_rule + # print(path) + if not os.path.exists(path): + path = escapePath(path, escapeLiterals) + try: + os.makedirs(path) + except: + path = success_folder + '/' + location_rule.replace('/[' + number + ']-' + title, "/number") + path = escapePath(path, escapeLiterals) + os.makedirs(path) + return path + + +# =====================资源下载部分=========================== +def download_file(url, folder, name_with_ext): + """ + download file + :param url: source url + :param name_with_ext: full name like 'mike.jpg' + :param folder: folder path + :return: full path if downloaded file like '/Users/proj/AV_Data_Capture/mike.jpg' + """ + proxy_dict = {"http": str(config.proxy), "https": str(config.proxy)} if config.proxy else None + i = 0 + while i < config.retry: + try: + if not os.path.exists(folder): + os.makedirs(folder) + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'} + r = requests.get(url, headers=headers, timeout=config.timeout, proxies=proxy_dict) + if r == '': + print('[-]Movie Data not found!') + return + with open(str(folder) + "/" + name_with_ext, "wb") as code: + code.write(r.content) + return str(folder) + "/" + name_with_ext + except requests.exceptions.RequestException: + i += 1 + print('[-]Image Download : Connect retry ' + str(i) + '/' + str(config.retry)) + except requests.exceptions.ConnectionError: + i += 1 + print('[-]Image Download : Connect retry ' + str(i) + '/' + str(config.retry)) + except requests.exceptions.ProxyError: + i += 1 + print('[-]Image Download : Connect retry ' + str(i) + '/' + str(config.retry)) + except requests.exceptions.ConnectTimeout: + i += 1 + print('[-]Image Download : Connect retry ' + str(i) + '/' + str(config.retry)) + + +def DownloadFileWithFilename(url, filename, path): # path = examle:photo , video.in the Project Folder! + proxy, timeout, retry_count = get_network_settings() + i = 0 + proxy_dict = {"http": str(config.proxy), "https": str(config.proxy)} if proxy else None + while i < retry_count: + try: + if not os.path.exists(path): + os.makedirs(path) + headers = { + 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/68.0.3440.106 Safari/537.36'} + r = requests.get(url, headers=headers, timeout=timeout, + proxies=proxy_dict) + if r == '': + print('[-]Movie Data not found!') + return + with open(str(path) + "/" + filename, "wb") as code: + code.write(r.content) + return + except requests.exceptions.RequestException: + i += 1 + print('[-]Image Download : Connect retry ' + str(i) + '/' + str(retry_count)) + except requests.exceptions.ConnectionError: + i += 1 + print('[-]Image Download : Connect retry ' + str(i) + '/' + str(retry_count)) + except requests.exceptions.ProxyError: + i += 1 + print('[-]Image Download : Connect retry ' + str(i) + '/' + str(retry_count)) + except requests.exceptions.ConnectTimeout: + i += 1 + print('[-]Image Download : Connect retry ' + str(i) + '/' + str(retry_count)) + print('[-]Connect Failed! Please check your Proxy or Network!') + # moveFailedFolder(filepath, failed_folder) + return + + +def download_image(url, name, folder): + """ + download img + :param url: source + :param name: name + :param folder: folder to save + :return: + """ + name_with_ext = config.media_server.image_name(name) + download_file(url, folder, name_with_ext) + + +def imageDownload(option, cover, number, c_word, path, multi_part, filepath, failed_folder): # 封面是否下载成功,否则移动到failed + if option == 'emby': # name.jpg + if DownloadFileWithFilename(cover, number + c_word + '.jpg', path, filepath, failed_folder) == 'failed': + moveFailedFolder(filepath, failed_folder) + return + DownloadFileWithFilename(cover, number + c_word + '.jpg', path, filepath, failed_folder) + if not os.path.getsize(path + '/' + number + c_word + '.jpg') == 0: + print('[+]Image Downloaded!', path + '/' + number + c_word + '.jpg') + return + i = 1 + while i <= int(config.retry): + if os.path.getsize(path + '/' + number + c_word + '.jpg') == 0: + print('[!]Image Download Failed! Trying again. [' + config.retry + '/3]') + DownloadFileWithFilename(cover, number + c_word + '.jpg', path, filepath, failed_folder) + i = i + 1 + continue + else: + break + if multi_part == 1: + old_name = os.path.join(path, number + c_word + '.jpg') + new_name = os.path.join(path, number + c_word + '.jpg') + os.rename(old_name, new_name) + print('[+]Image Downloaded!', path + '/' + number + c_word + '.jpg') + else: + print('[+]Image Downloaded!', path + '/' + number + c_word + '.jpg') + elif option == 'plex': # fanart.jpg + if DownloadFileWithFilename(cover, 'fanart.jpg', path, filepath, failed_folder) == 'failed': + moveFailedFolder(filepath, failed_folder) + return + DownloadFileWithFilename(cover, 'fanart.jpg', path, filepath, failed_folder) + if not os.path.getsize(path + '/fanart.jpg') == 0: + print('[+]Image Downloaded!', path + '/fanart.jpg') + return + i = 1 + while i <= int(config.retry): + if os.path.getsize(path + '/fanart.jpg') == 0: + print('[!]Image Download Failed! Trying again. [' + config.retry + '/3]') + DownloadFileWithFilename(cover, 'fanart.jpg', path, filepath, failed_folder) + i = i + 1 + continue + else: + break + if not os.path.getsize(path + '/' + number + c_word + '.jpg') == 0: + print('[!]Image Download Failed! Trying again.') + DownloadFileWithFilename(cover, number + c_word + '.jpg', path, filepath, failed_folder) + print('[+]Image Downloaded!', path + '/fanart.jpg') + elif option == 'kodi': # [name]-fanart.jpg + if DownloadFileWithFilename(cover, number + c_word + '-fanart.jpg', path, filepath, failed_folder) == 'failed': + moveFailedFolder(filepath, failed_folder) + return + DownloadFileWithFilename(cover, number + c_word + '-fanart.jpg', path, filepath, failed_folder) + if not os.path.getsize(path + '/' + number + c_word + '-fanart.jpg') == 0: + print('[+]Image Downloaded!', path + '/' + number + c_word + '-fanart.jpg') + return + i = 1 + while i <= int(config.retry): + if os.path.getsize(path + '/' + number + c_word + '-fanart.jpg') == 0: + print('[!]Image Download Failed! Trying again. [' + config.retry + '/3]') + DownloadFileWithFilename(cover, number + c_word + '-fanart.jpg', path, filepath, failed_folder) + i = i + 1 + continue + else: + break + print('[+]Image Downloaded!', path + '/' + number + c_word + '-fanart.jpg') + + +def make_nfo_file(nfo, nfo_name, folder_path): + """ + make xxx.nfo in folder + :param nfo_name: name + :param nfo: nfo dict + :param folder_path: where to create file, default temp_folder + :return: + """ + title, studio, year, outline, runtime, director, actor_photo, release, number, cover, website = get_info(nfo) + naming_rule = nfo['naming_rule'] + tag = nfo['tag'] + + path = folder_path + c_word = '' + cn_sub = '' + part = '' + # path_file = path + "/" + number + c_word + ".nfo", "wt" + path_file = path + "/" + nfo_name + c_word + ".nfo" + lazyxml.dump + try: + if not os.path.exists(path): + os.makedirs(path) + if config.media_server == MediaServer.PLEX: + with open(path_file, "wt", encoding='UTF-8') as code: + print('', file=code) + print("", file=code) + print(" " + naming_rule + part + "", file=code) + print(" ", file=code) + print(" ", file=code) + print(" " + studio + "+", file=code) + print(" " + year + "", file=code) + print(" " + outline + "", file=code) + print(" " + outline + "", file=code) + print(" " + str(runtime).replace(" ", "") + "", file=code) + print(" " + director + "", file=code) + print(" poster.jpg", file=code) + print(" thumb.png", file=code) + print(" fanart.jpg", file=code) + try: + for key, value in actor_photo.items(): + print(" ", file=code) + print(" " + key + "", file=code) + if not value == '': # or actor_photo == []: + print(" " + value + "", file=code) + print(" ", file=code) + except: + aaaa = '' + print(" " + studio + "", file=code) + print(" ", file=code) + if cn_sub == '1': + print(" 中文字幕", file=code) + try: + for i in str(tag).strip("[ ]").replace("'", '').replace(" ", '').split(','): + print(" " + i + "", file=code) + except: + aaaaa = '' + try: + for i in str(tag).strip("[ ]").replace("'", '').replace(" ", '').split(','): + print(" " + i + "", file=code) + except: + aaaaaaaa = '' + if cn_sub == '1': + print(" 中文字幕", file=code) + print(" " + number + "", file=code) + print(" " + release + "", file=code) + print(" " + cover + "", file=code) + print(" " + website + "", file=code) + print("", file=code) + print("[+]Writeed! " + path + "/" + number + ".nfo") + elif config.media_server == MediaServer.EMBY: + with open(path_file, "wt", encoding='UTF-8') as code: + print('', file=code) + print("", file=code) + print(" " + naming_rule + part + "", file=code) + print(" ", file=code) + print(" ", file=code) + print(" " + studio + "+", file=code) + print(" " + year + "", file=code) + print(" " + outline + "", file=code) + print(" " + outline + "", file=code) + print(" " + str(runtime).replace(" ", "") + "", file=code) + print(" " + director + "", file=code) + print(" " + number + c_word + ".png", file=code) + print(" " + number + c_word + ".png", file=code) + print(" " + number + c_word + '.jpg' + "", file=code) + try: + for key, value in actor_photo.items(): + print(" ", file=code) + print(" " + key + "", file=code) + if not value == '': # or actor_photo == []: + print(" " + value + "", file=code) + print(" ", file=code) + except: + aaaa = '' + print(" " + studio + "", file=code) + print(" ", file=code) + if cn_sub == '1': + print(" 中文字幕", file=code) + try: + for i in tag: + print(" " + i + "", file=code) + except: + aaaaa = '' + try: + for i in tag: + print(" " + i + "", file=code) + except: + aaaaaaaa = '' + if cn_sub == '1': + print(" 中文字幕", file=code) + print(" " + number + "", file=code) + print(" " + release + "", file=code) + print(" " + cover + "", file=code) + print(" " + website + "", file=code) + print("", file=code) + print("[+]Writeed! " + path + "/" + number + c_word + ".nfo") + elif config.media_server == MediaServer.KODI: + with open(path_file, "wt", encoding='UTF-8') as code: + print('', file=code) + print("", file=code) + print(" " + naming_rule + part + "", file=code) + print(" ", file=code) + print(" ", file=code) + print(" " + studio + "+", file=code) + print(" " + year + "", file=code) + print(" " + outline + "", file=code) + print(" " + outline + "", file=code) + print(" " + str(runtime).replace(" ", "") + "", file=code) + print(" " + director + "", file=code) + print(" " + number + c_word + "-poster.jpg", file=code) + print(" " + number + c_word + '-fanart.jpg' + "", file=code) + try: + for key, value in actor_photo.items(): + print(" ", file=code) + print(" " + key + "", file=code) + if not value == '': # or actor_photo == []: + print(" " + value + "", file=code) + print(" ", file=code) + except: + aaaa = '' + print(" " + studio + "", file=code) + print(" ", file=code) + if cn_sub == '1': + print(" 中文字幕", file=code) + try: + for i in tag: + print(" " + i + "", file=code) + except: + aaaaa = '' + try: + for i in tag: + print(" " + i + "", file=code) + except: + aaaaaaaa = '' + if cn_sub == '1': + print(" 中文字幕", file=code) + print(" " + number + "", file=code) + print(" " + release + "", file=code) + print(" " + cover + "", file=code) + print(" " + website + "", file=code) + print("", file=code) + print("[+]Writeed! " + path + "/" + number + c_word + ".nfo") + except IOError as e: + print("[-]Write Failed! :" + e) + # print(e) + # moveFailedFolder(filepath, failed_folder) + return + except Exception as e: + print("[-]Write Failed! :" + e) + # moveFailedFolder(filepath, failed_folder) + return + + +def PrintFiles(option, path, c_word, naming_rule, part, cn_sub, json_data, filepath, failed_folder, tag): + title, studio, year, outline, runtime, director, actor_photo, release, number, cover, website = get_info(json_data) + try: + if not os.path.exists(path): + os.makedirs(path) + if option == 'plex': + with open(path + "/" + number + c_word + ".nfo", "wt", encoding='UTF-8') as code: + print('', file=code) + print("", file=code) + print(" " + naming_rule + part + "", file=code) + print(" ", file=code) + print(" ", file=code) + print(" " + studio + "+", file=code) + print(" " + year + "", file=code) + print(" " + outline + "", file=code) + print(" " + outline + "", file=code) + print(" " + str(runtime).replace(" ", "") + "", file=code) + print(" " + director + "", file=code) + print(" poster.jpg", file=code) + print(" thumb.png", file=code) + print(" fanart.jpg", file=code) + try: + for key, value in actor_photo.items(): + print(" ", file=code) + print(" " + key + "", file=code) + if not value == '': # or actor_photo == []: + print(" " + value + "", file=code) + print(" ", file=code) + except: + aaaa = '' + print(" " + studio + "", file=code) + print(" ", file=code) + if cn_sub == '1': + print(" 中文字幕", file=code) + try: + for i in str(json_data['tag']).strip("[ ]").replace("'", '').replace(" ", '').split(','): + print(" " + i + "", file=code) + except: + aaaaa = '' + try: + for i in str(json_data['tag']).strip("[ ]").replace("'", '').replace(" ", '').split(','): + print(" " + i + "", file=code) + except: + aaaaaaaa = '' + if cn_sub == '1': + print(" 中文字幕", file=code) + print(" " + number + "", file=code) + print(" " + release + "", file=code) + print(" " + cover + "", file=code) + print(" " + website + "", file=code) + print("", file=code) + print("[+]Writeed! " + path + "/" + number + ".nfo") + elif option == 'emby': + with open(path + "/" + number + c_word + ".nfo", "wt", encoding='UTF-8') as code: + print('', file=code) + print("", file=code) + print(" " + naming_rule + part + "", file=code) + print(" ", file=code) + print(" ", file=code) + print(" " + studio + "+", file=code) + print(" " + year + "", file=code) + print(" " + outline + "", file=code) + print(" " + outline + "", file=code) + print(" " + str(runtime).replace(" ", "") + "", file=code) + print(" " + director + "", file=code) + print(" " + number + c_word + ".png", file=code) + print(" " + number + c_word + ".png", file=code) + print(" " + number + c_word + '.jpg' + "", file=code) + try: + for key, value in actor_photo.items(): + print(" ", file=code) + print(" " + key + "", file=code) + if not value == '': # or actor_photo == []: + print(" " + value + "", file=code) + print(" ", file=code) + except: + aaaa = '' + print(" " + studio + "", file=code) + print(" ", file=code) + if cn_sub == '1': + print(" 中文字幕", file=code) + try: + for i in tag: + print(" " + i + "", file=code) + except: + aaaaa = '' + try: + for i in tag: + print(" " + i + "", file=code) + except: + aaaaaaaa = '' + if cn_sub == '1': + print(" 中文字幕", file=code) + print(" " + number + "", file=code) + print(" " + release + "", file=code) + print(" " + cover + "", file=code) + print(" " + website + "", file=code) + print("", file=code) + print("[+]Writeed! " + path + "/" + number + c_word + ".nfo") + elif option == 'kodi': + with open(path + "/" + number + c_word + ".nfo", "wt", encoding='UTF-8') as code: + print('', file=code) + print("", file=code) + print(" " + naming_rule + part + "", file=code) + print(" ", file=code) + print(" ", file=code) + print(" " + studio + "+", file=code) + print(" " + year + "", file=code) + print(" " + outline + "", file=code) + print(" " + outline + "", file=code) + print(" " + str(runtime).replace(" ", "") + "", file=code) + print(" " + director + "", file=code) + print(" " + number + c_word + "-poster.jpg", file=code) + print(" " + number + c_word + '-fanart.jpg' + "", file=code) + try: + for key, value in actor_photo.items(): + print(" ", file=code) + print(" " + key + "", file=code) + if not value == '': # or actor_photo == []: + print(" " + value + "", file=code) + print(" ", file=code) + except: + aaaa = '' + print(" " + studio + "", file=code) + print(" ", file=code) + if cn_sub == '1': + print(" 中文字幕", file=code) + try: + for i in tag: + print(" " + i + "", file=code) + except: + aaaaa = '' + try: + for i in tag: + print(" " + i + "", file=code) + except: + aaaaaaaa = '' + if cn_sub == '1': + print(" 中文字幕", file=code) + print(" " + number + "", file=code) + print(" " + release + "", file=code) + print(" " + cover + "", file=code) + print(" " + website + "", file=code) + print("", file=code) + print("[+]Writeed! " + path + "/" + number + c_word + ".nfo") + except IOError as e: + print("[-]Write Failed!") + print(e) + moveFailedFolder(filepath, failed_folder) + return + except Exception as e1: + print(e1) + print("[-]Write Failed!") + moveFailedFolder(filepath, failed_folder) + return + + +def crop_image(crop_style, name, path): + try: + origin_image = Image.open(path + '/' + config.media_server.image_name(name)) + if crop_style == 1: + cropped_image = origin_image.crop((origin_image.width / 1.9, 0, origin_image.width, origin_image.height)) + else: + cropped_image = origin_image + cropped_image.save(path + '/' + config.media_server.poster_name(name)) + + except Exception as e: + print('[-]Cover cut failed:' + e) + + +def cutImage(option, imagecut, path, number, c_word): + if option == 'plex': + if imagecut == 1: # 截取右侧封面 fanart.jpg 截取为poster.jpg + try: + img = Image.open(path + '/fanart.jpg') + imgSize = img.size + w = img.width + h = img.height + img2 = img.crop((w / 1.9, 0, w, h)) + img2.save(path + '/poster.jpg') + except: + print('[-]Cover cut failed!') + elif imagecut == 0: # 改名 fanart.jpg ->poster.jpg + img = Image.open(path + '/fanart.jpg') + w = img.width + h = img.height + img.save(path + '/poster.jpg') + elif option == 'emby': + if imagecut == 1: # 截取右侧封面 [name].jpg 截取为 [name].jpg + try: + img = Image.open(path + '/' + number + c_word + '.jpg') + imgSize = img.size + w = img.width + h = img.height + img2 = img.crop((w / 1.9, 0, w, h)) + img2.save(path + '/' + number + c_word + '.png') + except: + print('[-]Cover cut failed!') + elif imagecut == 0: # [name].jpg -> [name].png + img = Image.open(path + '/' + number + c_word + '.jpg') + img.save(path + '/' + number + c_word + '.png') + elif option == 'kodi': + if imagecut == 1: # 截取右侧封面 [name]-fanart.jpg 截取为 [name]-poster.jpg + try: + img = Image.open(path + '/' + number + c_word + '-fanart.jpg') + w = img.width + h = img.height + img2 = img.crop((w / 1.9, 0, w, h)) + img2.save(path + '/' + number + c_word + '-poster.jpg') + except: + print('[-]Cover cut failed!') + elif imagecut == 0: # [name]-fanart.jpg 截取为 [name]-poster.jpg + img = Image.open(path + '/' + number + c_word + '-fanart.jpg') + + try: + img = img.convert('RGB') + img.save(path + '/' + number + c_word + '-poster.jpg') + except: + img = img.convert('RGB') + img.save(path + '/' + number + c_word + '-poster.jpg') + + +def pasteFileToFolder(filepath, path, number, c_word): # 文件路径,番号,后缀,要移动至的位置 + houzhui = str(re.search('[.](avi|rmvb|wmv|mov|mp4|mkv|flv|ts|webm)$', filepath, re.IGNORECASE).group()) + try: + if config.soft_link == '1': # 如果soft_link=1 使用软链接 + os.symlink(filepath, path + '/' + number + c_word + houzhui) + else: + os.rename(filepath, path + '/' + number + c_word + houzhui) + if os.path.exists(config.search_folder + '/' + number + c_word + '.srt'): # 字幕移动 + os.rename(config.search_folder + '/' + number + c_word + '.srt', path + '/' + number + c_word + '.srt') + print('[+]Sub moved!') + elif os.path.exists(config.search_folder + '/' + number + c_word + '.ssa'): + os.rename(os.getcwd() + '/' + number + c_word + '.ssa', path + '/' + number + c_word + '.ssa') + print('[+]Sub moved!') + elif os.path.exists(config.search_folder + '/' + number + c_word + '.sub'): + os.rename(os.getcwd() + '/' + number + c_word + '.sub', path + '/' + number + c_word + '.sub') + print('[+]Sub moved!') + except FileExistsError: + print('[-]File Exists! Please check your movie!') + print('[-]move to the root folder of the program.') + return + except PermissionError: + print('[-]Error! Please run as administrator!') + return + + +def pasteFileToFolder_mode2(filepath, path, multi_part, number, part, c_word): # 文件路径,番号,后缀,要移动至的位置 + if multi_part == 1: + number += part # 这时number会被附加上CD1后缀 + houzhui = str(re.search('[.](avi|rmvb|wmv|mov|mp4|mkv|flv|ts|webm)$', filepath, re.IGNORECASE).group()) + try: + if config.soft_link== '1': + os.symlink(filepath, path + '/' + number + part + c_word + houzhui) + else: + os.rename(filepath, path + '/' + number + part + c_word + houzhui) + if os.path.exists(number + '.srt'): # 字幕移动 + os.rename(number + part + c_word + '.srt', path + '/' + number + part + c_word + '.srt') + print('[+]Sub moved!') + elif os.path.exists(number + part + c_word + '.ass'): + os.rename(number + part + c_word + '.ass', path + '/' + number + part + c_word + '.ass') + print('[+]Sub moved!') + elif os.path.exists(number + part + c_word + '.sub'): + os.rename(number + part + c_word + '.sub', path + '/' + number + part + c_word + '.sub') + print('[+]Sub moved!') + print('[!]Success') + except FileExistsError: + print('[-]File Exists! Please check your movie!') + print('[-]move to the root folder of the program.') + return + except PermissionError: + print('[-]Error! Please run as administrator!') + return + + +def copy_images_to_background_image(name, path): + shutil.copy(path + "/" + config.media_server.image_name(name), path + "/Backdrop.jpg") + if config.media_server == MediaServer.PLEX: + shutil.copy(path + "/" + config.media_server.poster_name(name), path + '/thumb.png') + + +def copyRenameJpgToBackdrop(option, path, number, c_word): + if option == 'plex': + shutil.copy(path + '/fanart.jpg', path + '/Backdrop.jpg') + shutil.copy(path + '/poster.jpg', path + '/thumb.png') + if option == 'emby': + shutil.copy(path + '/' + number + c_word + '.jpg', path + '/Backdrop.jpg') + if option == 'kodi': + shutil.copy(path + '/' + number + c_word + '-fanart.jpg', path + '/Backdrop.jpg') + + +def get_part(filepath, failed_folder): + try: + if re.search('-CD\d+', filepath): + return re.findall('-CD\d+', filepath)[0] + if re.search('-cd\d+', filepath): + return re.findall('-cd\d+', filepath)[0] + except: + print("[-]failed!Please rename the filename again!") + moveFailedFolder(filepath, failed_folder) + return + + +def debug_mode(json_data): + try: + if config.debug_mode == '1': + print('[+] ---Debug info---') + for i, v in json_data.items(): + if i == 'outline': + print('[+] -', i, ' :', len(v), 'characters') + continue + if i == 'actor_photo' or i == 'year': + continue + print('[+] -', "%-11s" % i, ':', v) + print('[+] ---Debug info---') + except: + aaa = '' + + +def core_main(number_th): + # =======================================================================初始化所需变量 + multi_part = 0 + part = '' + c_word = '' + option = '' + cn_sub = '' + + # filepath = file_path # 影片的路径 + number = number_th + + json_data = getDataFromJSON(number) # 定义番号 + + # if json_data.get('number') != number: + # fix issue #119 + # the root cause is we normalize the search id + # PrintFiles() will use the normalized id from website, + # but pasteFileToFolder() still use the input raw search id + # so the solution is: use the normalized search id + # number = json_data["number"] + # imagecut = json_data['imagecut'] + # tag = json_data['tag'] + # =======================================================================判断-C,-CD后缀 + # if '-CD' in filepath or '-cd' in filepath: + # multi_part = 1 + # part = get_part(filepath, config.failed_folder) + + # if '-c.' in filepath or '-C.' in filepath or '中文' in filepath or '字幕' in filepath: + # cn_sub = '1' + # c_word = '-C' # 中文字幕影片后缀 + + # CreatFailedFolder(config.failed_folder) # 创建输出失败目录 + # debug_mode(json_data) # 调试模式检测 + return json_data + # path = creatFolder(config.success_folder, json_data['location_rule'], json_data, config.escape_literals) # 创建文件夹 + # =======================================================================刮削模式 + # if config.program_mode == '1': + # if multi_part == 1: + # number += part # 这时number会被附加上CD1后缀 + # smallCoverCheck(path, number, imagecut, json_data['cover_small'], c_word, option, filepath, config.failed_folder) # 检查小封面 + # imageDownload(option, json_data['cover'], number, c_word, path, multi_part, filepath, config.failed_folder) # creatFoder会返回番号路径 + # cutImage(option, imagecut, path, number, c_word) # 裁剪图 + # copyRenameJpgToBackdrop(option, path, number, c_word) + # PrintFiles(option, path, c_word, json_data['naming_rule'], part, cn_sub, json_data, filepath, config.failed_folder, tag) # 打印文件 .nfo + # pasteFileToFolder(filepath, path, number, c_word) # 移动文件 + # # =======================================================================整理模式 + # elif config.program_mode == '2': + # pasteFileToFolder_mode2(filepath, path, multi_part, number, part, c_word) # 移动文件 diff --git a/jav321.py b/jav321.py deleted file mode 100644 index 1259553..0000000 --- a/jav321.py +++ /dev/null @@ -1,73 +0,0 @@ -import json -from bs4 import BeautifulSoup -from lxml import html -from ADC_function import post_html - - -def main(number: str) -> json: - result = post_html(url="https://www.jav321.com/search", query={"sn": number}) - soup = BeautifulSoup(result.text, "html.parser") - lx = html.fromstring(str(soup)) - - if "/video/" in result.url: - data = parse_info(soup=soup) - dic = { - "title": get_title(lx=lx), - "studio": "", - "year": data["release"][:4], - "outline": get_outline(lx=lx), - "director": "", - "cover": get_cover(lx=lx), - "imagecut": 1, - "actor_photo": "", - "website": result.url, - "source": "jav321.py", - **data, - } - else: - dic = {} - - return json.dumps(dic, ensure_ascii=False, sort_keys=True, indent=4, separators=(',', ':')) - - -def get_title(lx: html.HtmlElement) -> str: - return lx.xpath("/html/body/div[2]/div[1]/div[1]/div[1]/h3/text()")[0].strip() - - -def parse_info(soup: BeautifulSoup) -> dict: - data = str(soup.select_one("div.row > div.col-md-9")).split("
") - - return { - "actor": get_anchor_info(h=data[0]), - "label": get_anchor_info(h=data[1]), - "tag": get_anchor_info(h=data[2]), - "number": get_text_info(h=data[3]), - "release": get_text_info(h=data[4]), - "runtime": get_text_info(h=data[5]), - } - - -def get_anchor_info(h: str) -> str: - result = [] - - data = BeautifulSoup(h, "html.parser").find_all("a", href=True) - for d in data: - result.append(d.text) - - return ",".join(result) - - -def get_text_info(h: str) -> str: - return h.split(": ")[1] - - -def get_cover(lx: html.HtmlElement) -> str: - return lx.xpath("/html/body/div[2]/div[2]/div[1]/p/a/img/@src")[0] - - -def get_outline(lx: html.HtmlElement) -> str: - return lx.xpath("/html/body/div[2]/div[1]/div[1]/div[2]/div[3]/div/text()")[0] - - -if __name__ == "__main__": - print(main("wmc-002")) diff --git a/readme/._readme1.PNG b/readme/._readme1.PNG new file mode 100755 index 0000000000000000000000000000000000000000..0ea2059cc5eb95b2595a3f25a7252724b035198c GIT binary patch literal 4096 zcmZQz6=P>$Vqox1Ojhs@R)|o50+1L3ClDJkFz{^v(m+1nBL)UWIUt(=a103vVqjo@ z1JS`S1E|~*O$#HC4;2?p&d=3LEGWoH)hjGbEK1BP$;?Y-Ffgz%v^GsOH!?OiNws#) zD9X=GwRSOewls5dHrF*aadgr(F*UZ-wJ literal 0 HcmV?d00001 diff --git a/readme/._readme2.PNG b/readme/._readme2.PNG new file mode 100755 index 0000000000000000000000000000000000000000..388f7978a16e1398924128033eb71106355b11ec GIT binary patch literal 4096 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 Ie4@Vp2dmi7y8r+H literal 0 HcmV?d00001 diff --git a/resource/readme1.PNG b/resource/readme1.PNG new file mode 100755 index 0000000000000000000000000000000000000000..b3d0a21a3abe1c9142edc3e3c4716b417bdca77c GIT binary patch literal 1152 zcmV-`1b_R9P)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1Qkg{K~!i%?V8_9 z6j2n%_qmAK-EnL*dh(%%pjTu672g+n)S?oMAPRg5rZB>4!G8E7BQ&U`sI17W)CBF< z)>cSqCYhR9^g}(}d+yxXduC=w4RdvwFC5ODb9d&Rb3gaqofRVxswfm`90Uk${ad&r zi$nxu{K0d_Vo$OV%e;qZW(0lg*)YUv@>Cr1-?!N%$u2X2C3MUUp z=_N=m>7`VVTp`H9i-3$h9kyGn{c6N+0c;%kh+cawV*1Si(A)20o6D?eLJL(m5s=#O zC%Xj@{WxK_Sp3(l-4cpDdV$_Z-9~TjJBJpka3Ub1pC_qb5CEmuzRg1P!x%eu-{7?> zv#!N%twp6F8HqpyEdf^64x@tlq{L?u{+<6x%Nvt|oc>C@_GcV@bV2M?ZFUBtV-uj? zeU8U#yiFXh=7Bb}wW%yv31vdkNd_#*rim-4FQ=@PK+vlRa=BwtkYA=8(|)tvH}``< zPC+3&*tWV>>D2DJC%c&*-i#n9X&W-{U{ikvHgyew5X{ zyN|@=eagMOKOd~c`joMPL8^HMU8^-pDX}Gw=@kJPzcb`m?D0zk6=&atDmZC+M-TdP z26D&AG#qJcu%DvHm6#*nI4{Tr&MuNbao?A33@J~4{)x-=$#H$mGuBFhT+WzY5s)R9 zdK}Z!eH7gFeu#DULP^^_65oF2x<5n|FG5W`4VEB+akARfNoB{8<=mqd?|z|KvN$n*&o&pQ4onnM|rR zEOE?5L_r?DoHO<|$^ivg{ize^3#x!!wD#mW0aj21_VysQk2k~l;)4K&+$+lcE> zRaFJGwY88+rJ%dJ8^*`QUx8L#AxIPf8EXA11X&~sK^BR^f4(Xb3wLBu2;eU%$;-Vn SEGYW`0000z8D0l0Exa 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 literal 0 HcmV?d00001 diff --git a/resource/readme3.PNG b/resource/readme3.PNG new file mode 100755 index 0000000000000000000000000000000000000000..81e05cd31fa4023eb4a44a221de4e3904173d50b GIT binary patch literal 1301 zcmV+w1?u{VP)Px#1ZP1_K>z@;j|==^1poj532;bRa{vGi!vFvd!vV){sAK>D1gc3yK~#8N?U=tV z1wj;t?-S@1dc8!U^8`u>jY_LmDM&OPKr1?}g3=>swOU$hU2o{_W`7iMF^wjO_>~t?LFWuSMSvQ$XlI{BX z`g6qxtXo@K>#wl4x7W8ID3KI7#kIM)*%zVUo<3hxxk96Oy^Y`lFLIiPJ}Yu!yu7^p zC2*magnuCn1z+&V+P8iR`tZZQkbL`%+il-LUOcrnQg}UJUv*a+6Y^_& zdpn!I5>+DS$|h{)$AF~T#MUHQL9>OegZhK(`QhPVY7KroKR@@^i;Ih?F$HOeEqkUC zH#axk(a}+Nd3iaiD2a{sJU%{7Ce8{+u+Gk{iL-_Isl9-+j}qv5KBo?3g=C$|psf1ItgTuoLW*%rQL^v;{(dhW1diB=0}0d%#})wKbM7Z0ef}q84s7O%gjkFf`N6?K zcXD#_tyqW95>sNJ`b<$8_cg$k9N2n-AvQe9uSGd7jg{_)P$hD2+&|kxdQqiF1*~MF zBuxJdV{HxL_4TzoJw5G<^6u{Lvv)Scjb9xS56?7&f^Ybu`09RreZ6mb3HYXQ=9ZAn z6!_vrjz77duYxiC%4=n8OgR&I%=uXw&-qWRh^=B;YlHalf?%y)zRIJ;=h$M$?4If$ zN>bz)(#FyBpU*YHi`+Ju6D z)B#clCcrBzEB$|qbHNr`uRXvXR<>k^dGbO2X>UE-Vzl#z*s7)VZ*unbBFBFvu|?#p zu?10TrE_Di<_j%fTWO6cKKMUdIG=n=jdO4R4C0xWtsu7cPD!8}L!uQMBoP!<$h}cwc5}x2N8r$97o%$MUjJ@=aH~!9TwN>LcFt6pm>+J1C zt}nTuPl;D-bq3#t_+A745ku{tBUc+^XpJf0uZ@fMO@h?87cAIXq4^ki8?>7E+jYJ^ zOJf$!gMULw{#^f0NC2}6@-LARx%x>m$+iW^yxK2$E5xx>XkwlM|L+ioTCx8IA@U$t zaDG(e_RD13JOvpd4}voP{!1PNL75PF5G*($@*r4nLgYcP&~)89j57SxF%2$500000 LNkvXXu0mjf|Br?; literal 0 HcmV?d00001 diff --git a/resource/readme4.PNG b/resource/readme4.PNG new file mode 100755 index 0000000000000000000000000000000000000000..26a2cf4bed50aafb63abb97d97852d1124764b4a GIT binary patch literal 16269 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 PJ(#q(f>@Qv&%pl%4+5fe literal 0 HcmV?d00001 diff --git a/resource/ruquirments.txt b/resource/ruquirments.txt new file mode 100755 index 0000000..97951df --- /dev/null +++ b/resource/ruquirments.txt @@ -0,0 +1 @@ +pipenv install -rlxml bs4 pillow pyquery \ No newline at end of file diff --git a/resource/single.gif b/resource/single.gif new file mode 100755 index 0000000000000000000000000000000000000000..4b9c37175251f878e236cffde9dc1baf367ba813 GIT binary patch literal 69859 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 literal 0 HcmV?d00001 diff --git a/ruquirments.txt b/ruquirments.txt deleted file mode 100644 index aa091a0..0000000 --- a/ruquirments.txt +++ /dev/null @@ -1,4 +0,0 @@ -lxml -bs4 -pillow -pyquery \ No newline at end of file diff --git a/test.py b/test.py new file mode 100755 index 0000000..5ebb19e --- /dev/null +++ b/test.py @@ -0,0 +1,80 @@ +import os +import re +from itertools import groupby + +import fuckit as fuckit +import pandas as pd +from tenacity import retry, stop_after_delay, wait_fixed + + +def go(): + a = [1, 2, 3, 4, 5, 6] + # [print(x) for x in a] + # [print(x) for x in a] + a1 = groupby(a, key=lambda k: (k / 2)) + for i in a1: + print(i) + for i in a1: + print(i) + + +class TryDo: + def __init__(self, func, times=3): + self.tries = times + self.func = func + + def __iter__(self): + self.currentTry = 1 + return self + + def __next__(self): + if self.currentTry > self.tries: + raise StopIteration(False) + else: + self.currentTry += 1 + self.func() + raise StopIteration(True) + + # def do(self): + + +@retry(stop=stop_after_delay(3), wait=wait_fixed(2)) +def stop_after_10_s(): + print("Stopping after 10 seconds") + raise Exception + + +# f = iter( TryDo(do_something, 5)) + +# stop_after_10_s() +def errorfunc(): + raise Exception + + +def okfunc(): + print("ok") + + +# with fuckit: +# errorfunc() +# okfunc() +# re.match() + +r = re.search(r'(?<=999)-?((?P([A-Z](?![A-Z])))|(?P\d(?!\d)))', "IPTD-999-B-彼女の姉貴とイケナイ関係-RIO", re.I) +# +print(r.groupdict()) +print(r.groupdict()['alpha']) +print(r.group(2)) +import re + +line = "Cats are smarter than dogs" +matchObj = re.search(r'(?<=a)(.*) are (.*?) .*', line, re.M | re.I) +if matchObj: + print("matchObj.group() : ", matchObj.group()) + print("matchObj.group(1) : ", matchObj.group(1)) + print("matchObj.group(2) : ", matchObj.group(2)) +else: + print("No match!!") + +# print(r[-1]) +# print(newList) diff --git a/update_check.json b/update_check.json old mode 100644 new mode 100755