Ero-Lab部分游戏资源下载脚本

很久沒更新的遊戲了 tps://nidg.bhjsj8.com/new_patch/files/index.txt.852550fed47ae553edf7e3b66c935678

ttps://nidg.bhjsj8.com/new_patch/files/assets_resources_honly_baseassets_uiprefabs_simpleprefab_img_hcg_e001_01_hcg1_2.7fa8c079dd7

ttps://www.sbkjl.com/game_list.html

ttps://www.sbkjl.com/game.html?id=1

櫻境和這款除了加密其實差不多 (紳士冒險沒有加密)

nutaku版叫 Lusty Odyssey 已經收了

绅士冒险半个月前抓的。具体资源清单hash的逻辑没仔细看。

下载清单
import os
import requests
import urllib3
from concurrent.futures import ThreadPoolExecutor
import time

# 忽略 InsecureRequestWarning 警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

# 下载单个文件的函数
def download_file(link, download_dir):
    # 从链接中提取文件名(保留原始文件名)
    path_part = link.split("?")[0].split("/")
    file_name = path_part[-1] if path_part[-1] else f"file_{hash(link) % 1000000}.bin"
    
    file_path = os.path.join(download_dir, file_name)

    headers = {
        "Accept": "*/*",
        "Accept-Encoding": "deflate, gzip",
        "User-Agent": "UnityPlayer/2020.3.41f1 (UnityWebRequest/1.0, libcurl/7.84.0-DEV)",
        "X-Unity-Version": "2020.3.41f1",
        "Host": "nidg.bhjsj8.com"
    }

    try:
        # 禁用 SSL 证书验证
        response = requests.get(link, headers=headers, verify=False)
        response.raise_for_status()

        # 确定响应内容类型
        content_type = response.headers.get('Content-Type', '')
        
        # 根据内容类型选择写入模式
        if 'json' in content_type or 'text' in content_type:
            # 文本内容
            with open(file_path, 'w', encoding='utf-8') as file:
                file.write(response.text)
            print(f"下载完成: {file_name} (文本模式)")
        else:
            # 二进制内容
            with open(file_path, 'wb') as file:
                file.write(response.content)
            print(f"下载完成: {file_name} (二进制模式)")
            
        # 打印文件信息
        file_size = os.path.getsize(file_path)
        print(f"文件大小: {file_size} 字节")
        print(f"保存路径: {os.path.abspath(file_path)}")

    except requests.RequestException as e:
        print(f"请求过程中出现错误: {e}")
    except Exception as e:
        print(f"下载过程中出现未知错误: {e}")

# 主下载函数
def main_download():
    # 生成时间戳
    ts = int(time.time())
    # 下载链接
    base_link = "https://nidg.bhjsj8.com/new_patch/files/index.txt.c47c53a61a6b650788724579ebab4da9"
    download_link = [f"{base_link}?ts={ts}"]
    
    # 确保下载目录存在
    download_dir = os.getcwd()
    os.makedirs(download_dir, exist_ok=True)
    
    print(f"下载目录: {os.path.abspath(download_dir)}")
    
    # 使用线程池进行下载
    with ThreadPoolExecutor(max_workers=2) as executor:
        print(f"开始下载: {download_link}")
        executor.map(lambda link: download_file(link, download_dir), download_link)

    print("\n🎉 文件下载完成!")

if __name__ == "__main__":
    # 执行下载操作
    main_download()
    
    # 等待用户查看结果
    input("按 Enter 键退出...")
提取url
import os
import json

def process_index_file():
    # 读取同目录下的 index.txt 文件
    input_file_path = os.path.join(os.getcwd(), 'index.txt')
    output_file_path = os.path.join(os.getcwd(), 'output.txt')
    
    try:
        with open(input_file_path, 'r', encoding='utf-8') as f:
            content = f.read()
            data = json.loads(content)
    except FileNotFoundError:
        print(f"错误: 找不到 {input_file_path} 文件")
        return
    except json.JSONDecodeError:
        print(f"错误: {input_file_path} 文件不是有效的 JSON 格式")
        return
    except Exception as e:
        print(f"错误: 读取文件时发生未知错误: {e}")
        return
    
    # 基础 URL
    base_url = "https://nidg.bhjsj8.com/new_patch/files/"
    
    # 用于存储结果的列表
    result_urls = []
    
    # 遍历 JSON 数据中的每个键值对
    for key, value in data.items():
        # 确保值是列表且至少有两个元素
        if isinstance(value, list) and len(value) >= 2:
            # 拼接文件名和哈希值(直接使用键名,不做前缀过滤)
            filename = f"{key}.{value[1]}"
            # 拼接完整 URL
            full_url = f"{base_url}{filename}"
            result_urls.append(full_url)
    
    # 将结果写入 output.txt 文件
    try:
        with open(output_file_path, 'w', encoding='utf-8') as f:
            # 每行写入一个 URL
            for url in result_urls:
                f.write(url + '\n')
        print(f"成功生成 {output_file_path} 文件,共写入 {len(result_urls)} 个 URL")
    except Exception as e:
        print(f"错误: 写入文件时发生未知错误: {e}")

if __name__ == "__main__":
    process_index_file()
    input("按 Enter 键退出...")
下载
import requests
import os
import time
from concurrent.futures import ThreadPoolExecutor
import urllib3
from datetime import datetime

# 禁用 InsecureRequestWarning 警告
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)

def get_simplified_timestamp():
    now = datetime.now()
    return now.strftime("%y%m%d%H%M%S")

def download_file(link, download_dir, total_files, index, digit_count, success_files, failed_files):
    # 提取文件名和路径
    path_parts = link.split("/")
    file_name = path_parts[-1].split("?")[0]
    relative_path = "/".join(path_parts[3:-1])  # 假设前三个部分是协议和域名,可根据实际情况调整
    local_dir = os.path.join(download_dir, relative_path)
    local_path = os.path.join(local_dir, file_name)

    max_retries = 3  # 最大重试次数
    retry_delay = 5  # 重试延迟时间(秒)

    # 创建本地目录
    os.makedirs(local_dir, exist_ok=True)

    for attempt in range(max_retries):
        try:
            # 检查本地文件是否存在且大小相同(忽略证书验证)
            if os.path.exists(local_path):
                local_size = os.path.getsize(local_path)
                headers = requests.head(link, allow_redirects=True, timeout=10, verify=False).headers
                remote_size = int(headers.get('Content-Length', 0))
                if local_size == remote_size and remote_size != 0:
                    print(f"[跳过] {total_files}/{index:0{digit_count}d} 已存在且大小一致: {local_path}")
                    success_files.append(file_name)
                    return

            response = requests.get(link, stream=True, timeout=10, verify=False)
            response.raise_for_status()

            with open(local_path, 'wb') as file:
                for chunk in response.iter_content(chunk_size=8192):
                    if chunk:
                        file.write(chunk)

            if os.path.exists(local_path):
                final_size = os.path.getsize(local_path)
                if final_size > 0:
                    print(f"[成功] {total_files}/{index:0{digit_count}d} 下载完成: {local_path}")
                    success_files.append(file_name)
                    return
                else:
                    raise Exception("下载后文件大小为0")
        except (requests.RequestException, Exception) as e:
            if attempt < max_retries - 1:
                print(f"[尝试 {attempt + 1}/{max_retries}] {total_files}/{index:0{digit_count}d} 下载失败: {str(e)}, 重试中...")
                time.sleep(retry_delay)
            else:
                print(f"[失败] {total_files}/{index:0{digit_count}d} 下载失败: {str(e)}")
                failed_files.append(file_name)


def count_files_in_directory(directory):
    file_count = 0
    for root, dirs, files in os.walk(directory):
        file_count += len(files)
    return file_count

def main_download():
    file_path = "output.txt"
    download_dir = os.path.join(os.getcwd(), "com.superhgame.rpg.emma")

    if not os.path.exists(file_path):
        print("❌ 未找到下载列表文件")
        return

    with open(file_path, "r", encoding="utf-8") as f:
        download_links = [line.strip() for line in f if line.strip()]

    if not download_links:
        print("❌ 下载列表中没有有效链接")
        return

    total_files = len(download_links)
    digit_count = len(str(total_files))
    os.makedirs(download_dir, exist_ok=True)

    success_files = []
    failed_files = []

    print(f"✅ 准备下载 {total_files} 个文件,保存到 {download_dir}")
    print("-" * 60)

    with ThreadPoolExecutor(max_workers=32) as executor:
        futures = []
        for index, link in enumerate(download_links, start=1):
            futures.append(executor.submit(
                download_file,
                link, download_dir, total_files, index, digit_count,
                success_files, failed_files
            ))

        for future in futures:
            future.result()

    print("\n" + "=" * 60)
    print(f"📦 下载完成 | 成功: {len(success_files)} | 失败: {len(failed_files)}")
    if failed_files:
        print("\n❌ 以下文件下载失败(请手动检查):")
        for fname in failed_files:
            print(f" - {fname}")
    else:
        print("\n🎉 所有文件均下载成功!")

    # 计算目标文件夹中的实际文件数
    actual_file_count = count_files_in_directory(download_dir)
    print(f"\n📁 目标文件夹中的实际文件数: {actual_file_count}")
    print(f"📋 下载列表中的文件数: {total_files}")

    if actual_file_count == total_files:
        print("✅ 实际文件数与下载数相同。")
    else:
        print("❌ 实际文件数与下载数不同,请检查。")

    # 等待用户输入,阻止程序立即退出
    input("按回车键退出...")


if __name__ == "__main__":
    try:
        import requests
    except ImportError:
        print("❌ 缺少requests库,请先安装:pip install requests")
        exit(1)

    main_download()
1 个赞

感谢
不过我的意思是我需要游戏本体的安装包,不仅是为了研究逻辑还有一点就是这种游戏一般都有离线(APK自带)资源,比如星陨的H060(使徒),最近在验证已有资源的完整性发现了一些APK里有但是没收集到的资源

工口.R18 成人遊戲免費線上玩

感谢大佬

这个是绅士冒险游戏本体的安装包https://resfile.lixincsb.com/apk/1697525935357.apk

import os

import requests
import json


def test():
    index_url = "https://nidg.bhjsj8.com/new_patch/files/index.txt.623f677d60bab9e181b00a26f8bf2d77"

    try:
        response = requests.get(index_url, timeout=10)
        response.raise_for_status()
        data = json.loads(response.text)
        base_url = "https://nidg.bhjsj8.com/new_patch/files/{filename}.{hash}"
        with open("ssmx.txt", "w", encoding="utf-8") as outfile:
            for filename, values in data.items():
                if isinstance(values, list) and len(values) >= 2:
                    file_hash = values[1]
                    download_url = base_url.format(filename=filename, hash=file_hash)
                    outfile.write(f"{download_url}\n   out={filename}\n")
        print(f"共处理 {len(data)} 个资源条目。")
    except requests.exceptions.RequestException as e:
        print(f"下载资源清单失败: {e}")
    except json.JSONDecodeError as e:
        print(f"解析JSON数据失败: {e}")
    except Exception as e:
        print(f"发生未知错误: {e}")


if __name__ == "__main__":
    test()
    os.system("aria2c -i ssmx.txt -j 32 --check-certificate=false --dir=resdownload/绅士冒险")

试试看行不行

1 个赞

这也是NS做的游戏应该风格太像了logo也是
怪了
没有找到
b004_01.png这个资源

1 个赞

我自己猜 這款是NS主要員工的前東家的遊戲

(這個前東家有很多CG互用 水球社 桃色旅團等等 CG都很多一樣的 水球社最近也收了)

2025 07 02 23:00(UTC +8)
V1.29更新加入 禁域战姬 支持
加入清除生成的所有url txt
感谢 yjzyl9008 & Airtnp的支持

本来想直接等E服新游的算了估计还要一段时间先更吧。。。

3 个赞

關於 [童話邊境] 檔案名稱這部分,我還是很懶得改
叫GPT寫了一份
同時可以修改所有檔案名稱補0
又可以修正ATLAS內的PNG名稱補0

:white_check_mark: 功能總覽:

  1. 遞迴處理所有子資料夾
  2. .png, .json, .atlas 三種檔案:
  • Hero3.pngHero03.png
  • Walk.pngWalk01.png
  • Jump15.png → 不修改
  1. 修改 .atlas 檔案 第二行檔名 也套用同樣補零規則!
import os
import re

def fix_filename(name, ext):
    match = re.match(r'^(.*?)(\d+)?$', name)
    if not match:
        return None

    base = match.group(1)
    digits = match.group(2)

    if digits is None:
        return f"{base}01{ext}"
    elif len(digits) == 1:
        return f"{base}0{digits}{ext}"
    else:
        return None  # 兩位數以上不動

def rename_files_and_fix_atlas(root_dir):
    for dirpath, _, filenames in os.walk(root_dir):
        for filename in filenames:
            name, ext = os.path.splitext(filename)
            if ext.lower() not in ['.png', '.json', '.atlas']:
                continue

            old_path = os.path.join(dirpath, filename)
            new_filename = fix_filename(name, ext)

            # 改檔名
            if new_filename and new_filename != filename:
                new_path = os.path.join(dirpath, new_filename)
                if not os.path.exists(new_path):
                    os.rename(old_path, new_path)
                    print(f"✅ 檔名修正:{filename} ➜ {new_filename}")
                    # 若是 atlas 被改名,也要順便改內容(第二行)
                    if ext.lower() == '.atlas':
                        fix_atlas_second_line(new_path)
                else:
                    print(f"⚠ 目標檔案已存在:{new_filename},略過")
            else:
                # 如果是 atlas 而檔名沒改,也要檢查第二行
                if ext.lower() == '.atlas':
                    fix_atlas_second_line(old_path)

def fix_atlas_second_line(file_path):
    try:
        with open(file_path, 'r', encoding='utf-8') as f:
            lines = f.readlines()

        if len(lines) < 2:
            print(f"⏩ {os.path.basename(file_path)} 少於兩行,略過第二行處理")
            return

        line2 = lines[1].strip()
        name, ext = os.path.splitext(line2)
        if ext.lower() != '.png':
            print(f"⏩ {file_path} 第二行不是 PNG 檔名,略過")
            return

        new_line2 = fix_filename(name, ext)
        if new_line2 and new_line2 != line2:
            lines[1] = new_line2 + '\n'
            with open(file_path, 'w', encoding='utf-8') as f:
                f.writelines(lines)
            print(f"📝 {os.path.basename(file_path)} 第二行修正為:{new_line2}")
        else:
            print(f"⏩ {os.path.basename(file_path)} 第二行無需更改")
    except Exception as e:
        print(f"❌ 錯誤處理 atlas 第二行:{file_path},錯誤:{e}")

# ▶ 請修改這個資料夾路徑為你的目標資料夾
rename_files_and_fix_atlas("Spine-merge")

2025 07 08 4:00 (UTC + 8)
1.29.2更新
1.29.2 添加对 星陨计划 WebGL版的支持
不建议使用WebGL作为资源获取的主要途径,安卓版相对WebGL图像更加清晰,WebGL的更新相对超前,稍微领先安卓版清单几个星期仅供解包超前预告使用!!!
参考网页Ark Re:Code Wiki
添加启动时自动检测本体更新功能
由于 红尘问仙封测 目前已关闭封测入口apk链接改变该版本将动态获取apk地址
红尘问仙封测已从 其他 选项卡移动至EroLab
由于支持的游戏越来越多现在对EroLab选项卡采用自动分页每页12个游戏
首富2改名为首富2/梦琪领导
修复了更新bug无法自动开启浏览器和获取url请手动从http://156.226.180.251:715/resdownloader.exe 下载更新sorry…

2 个赞

关于分页的问题是否可以用下拉框来解决?

最新版本下载禁欲战姬遇到报错:

version.json 下载地址: Android/9A188D7BF0BF119DE38B57A44DF3A917.ab
Exception in thread Thread-2:
Traceback (most recent call last):
  File "threading.py", line 950, in _bootstrap_inner
  File "threading.py", line 888, in run
  File "resdownloader.py", line 3713, in custom_resource_download
  File "resdownloader.py", line 3582, in jyzjdownloader
  File "resdownloader.py", line 3557, in main
  File "requests\api.py", line 73, in get
  File "requests\api.py", line 59, in request
  File "requests\sessions.py", line 575, in request
  File "requests\sessions.py", line 484, in prepare_request
  File "requests\models.py", line 367, in prepare
  File "requests\models.py", line 438, in prepare_url
requests.exceptions.MissingSchema: Invalid URL 'Android/9A188D7BF0BF119DE38B57A44DF3A917.ab': No scheme supplied. Perhaps you meant https://Android/9A188D7BF0BF119DE38B57A44DF3A917.ab?

能分享下源码吗?这个 exe 看不到源码没法自己修

等2025 07 09的更新明天停服更新应该是改了服务器地址

2 个赞

这个和你前面发的脚本有什么改动吗?我用前面的 Python 代码能下载

不明白你在说什么,我没有发过禁域战姬的下载脚本,你想知道为什么无法下载我只告诉你目前这个游戏的配置url中将AssetBundlePath的值移除了是空的所以拼接后变成了

而且我也说了今天停服更新有新角色你就不能等更新完了再试试?

不好意思是我搞混了,我用了樱境的脚本,当成禁域了

已发布更新当前禁域战姬版本为1.02

更新后出bug了