吾爱破解 - 52pojie.cn

 找回密码
 注册[Register]

QQ登录

只需一步,快速开始

查看: 3144|回复: 62
收起左侧

[Windows] 【原创】故事分析器v1.0---一键拆文

  [复制链接]
宋长雨 发表于 2025-2-26 15:07
本帖最后由 宋长雨 于 2025-2-26 18:00 编辑

小说拆文,自己用的,不墨迹,调用的是硅基流动 deepseek 8b的api,有BuG自己找问题,别找我,我用着没毛病。应该很多人在找这种东西吧,趁着api模型免费赶紧用。虽然可能这个目前一直免费,但是,天佑不测风云。。。

自己测试。
111.png

测试结果:一本900章左右的小说,大概要拆1个半小时

微信截图_20250226143028.png

网盘链接:
百度网盘:链接: https://pan.baidu.com/s/1iP73WOZgsolQnQ1n9zDSrg?pwd=52pj 提取码: 52pj

移动云盘:链接: https://caiyun.139.com/m/i?2jQXiy2UeXVp7  提取码:reio  

免费评分

参与人数 6吾爱币 +6 热心值 +5 收起 理由
zwjtr93 + 1 + 1 我很赞同!
dogox + 1 + 1 我很赞同!
Doublevv + 1 + 1 热心回复!
一大夹子 + 1 谢谢@Thanks!
nndyky + 1 + 1 鼓励转贴优秀软件安全工具和文档!
wanfon + 1 + 1 热心回复!

查看全部评分

本帖被以下淘专辑推荐:

发帖前要善用论坛搜索功能,那里可能会有你要找的答案或者已经有人发布过相同内容了,请勿重复发帖。

 楼主| 宋长雨 发表于 2025-2-27 15:04
Doublevv 发表于 2025-2-27 14:12
挺有意思的应用。
能分享完整源码吗?准备收录到高质量Python源码资源淘专辑中 https://www.52pojie.cn/fo ...

[Python] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
import re
import time
import aiohttp
import asyncio
import tkinter as tk
import threading
from tkinter import ttk, filedialog, scrolledtext, messagebox
from typing import List, Tuple
 
 
class ConfigWindow(tk.Tk):
    def __init__(self):
        super().__init__()
        self.title("DeepSeek 故事分析器")
        self.geometry("720x700")
        self.protocol("WM_DELETE_WINDOW", self.on_close)
 
        # 配置参数初始化
        self.api_key = tk.StringVar(value="请输入硅基流动中的API密钥!")
        self.input_file = tk.StringVar()
        self.output_dir = tk.StringVar()
        self.concurrency = tk.IntVar(value=3)
        self.prompt_template = tk.StringVar(
            value="请以简洁的纯文本格式输出故事八要素,包含:身份、目的、行动、核心问题、阻力、结局、正能量和负能量,不要修饰。"
        )
        self.content_length = tk.IntVar(value=2000)
        self.temperature = tk.DoubleVar(value=0.3)
        self.connect_timeout = tk.IntVar(value=35)
        self.total_timeout = tk.IntVar(value=160)
 
        self.processing = False
        self.processor = None
        self.create_widgets()
        self.setup_log_area()
 
    def create_widgets(self):
        row = 0
 
        # 文件选择部分
        ttk.Label(self, text="输入文件:").grid(row=row, column=0, sticky="w", padx=10, pady=5)
        ttk.Entry(self, textvariable=self.input_file, width=50).grid(row=row, column=1)
        ttk.Button(self, text="浏览...", command=self.select_input_file).grid(row=row, column=2)
        row += 1
 
        ttk.Label(self, text="输出目录:").grid(row=row, column=0, sticky="w", padx=10, pady=5)
        ttk.Entry(self, textvariable=self.output_dir, width=50).grid(row=row, column=1)
        ttk.Button(self, text="浏览...", command=self.select_output_dir).grid(row=row, column=2)
        row += 1
 
        # API配置
        ttk.Label(self, text="API密钥:").grid(row=row, column=0, sticky="w", padx=10, pady=5)
        ttk.Entry(self, textvariable=self.api_key, width=50).grid(row=row, column=1, columnspan=2)
        row += 1
 
        # 并发数
        ttk.Label(self, text="并发数 (1-5):").grid(row=row, column=0, sticky="w", padx=10, pady=5)
        ttk.Spinbox(self, from_=1, to=5, textvariable=self.concurrency, width=5).grid(row=row, column=1, sticky="w")
        row += 1
 
        # 内容截取长度
        ttk.Label(self, text="每章截取长度:").grid(row=row, column=0, sticky="w", padx=10, pady=5)
        ttk.Spinbox(self, from_=100, to=5000, increment=100, textvariable=self.content_length, width=8).grid(row=row,
                                                                                                             column=1,
                                                                                                             sticky="w")
        row += 1
 
        # 温度值
        ttk.Label(self, text="温度值 (0.1-1):").grid(row=row, column=0, sticky="w", padx=10, pady=5)
        ttk.Spinbox(self, from_=0.1, to=1.0, increment=0.1, format="%.1f",
                    textvariable=self.temperature, width=5).grid(row=row, column=1, sticky="w")
        row += 1
 
        # 超时设置
        ttk.Label(self, text="超时设置(秒):").grid(row=row, column=0, sticky="w", padx=10, pady=5)
        ttk.Frame(self).grid(row=row, column=1, sticky="w")
        ttk.Label(self, text="连接").grid(row=row, column=1, sticky="w")
        ttk.Spinbox(self, from_=10, to=300, textvariable=self.connect_timeout, width=5).grid(row=row, column=2,
                                                                                             sticky="w", padx=5)
        ttk.Label(self, text="总超时").grid(row=row, column=3, sticky="w")
        ttk.Spinbox(self, from_=30, to=600, textvariable=self.total_timeout, width=5).grid(row=row, column=4,
                                                                                           sticky="w")
        row += 1
 
        # 提示模板
        ttk.Label(self, text="提示模板:").grid(row=row, column=0, sticky="nw", padx=10, pady=5)
        self.prompt_editor = scrolledtext.ScrolledText(self, width=70, height=8, wrap=tk.WORD)
        self.prompt_editor.insert("1.0", self.prompt_template.get())
        self.prompt_editor.grid(row=row, column=1, columnspan=4, pady=5, sticky="ew")
        row += 1
 
        # 控制按钮
        self.btn_frame = ttk.Frame(self)
        self.btn_frame.grid(row=row, column=1, pady=10, sticky="e")
        self.start_btn = ttk.Button(self.btn_frame, text="开始处理", command=self.toggle_processing)
        self.start_btn.pack(side="left", padx=5)
        row += 1
 
    def setup_log_area(self):
        self.log_area = scrolledtext.ScrolledText(self, width=85, height=12, state="disabled")
        self.log_area.grid(row=10, column=0, columnspan=5, padx=10, pady=5, sticky="nsew")
        self.grid_rowconfigure(10, weight=1)
 
    def select_input_file(self):
        filepath = filedialog.askopenfilename(filetypes=[("文本文件", "*.txt")])
        if filepath:
            self.input_file.set(filepath)
 
    def select_output_dir(self):
        dirpath = filedialog.askdirectory()
        if dirpath:
            self.output_dir.set(dirpath)
 
    def toggle_processing(self):
        if not self.processing:
            self.start_processing()
        else:
            self.cancel_processing()
 
    def start_processing(self):
        if not all([self.input_file.get(), self.output_dir.get(), self.api_key.get()]):
            messagebox.showerror("错误", "请填写所有必填字段!")
            return
 
        try:
            if not (0.1 <= self.temperature.get() <= 1.0):
                raise ValueError("温度值必须在0.1到1.0之间")
            if self.content_length.get() < 100:
                raise ValueError("截取长度至少100字符")
        except Exception as e:
            messagebox.showerror("参数错误", str(e))
            return
 
        self.processing = True
        self.start_btn.config(text="取消处理")
        self.log_area.config(state="normal")
        self.log_area.delete("1.0", tk.END)
        self.log_area.config(state="disabled")
 
        self.processor = TurboProcessor(
            api_key=self.api_key.get(),
            input_file=self.input_file.get(),
            output_dir=self.output_dir.get(),
            concurrency=self.concurrency.get(),
            prompt_template=self.prompt_editor.get("1.0", tk.END).strip(),
            content_length=self.content_length.get(),
            temperature=self.temperature.get(),
            timeout=(self.connect_timeout.get(), self.total_timeout.get()),
            log_callback=self.update_log
        )
 
        self.thread = threading.Thread(target=self.run_async_task, daemon=True)
        self.thread.start()
 
    def cancel_processing(self):
        if self.processor:
            self.processor.cancel()
        self.processing = False
        self.start_btn.config(text="开始处理")
        self.update_log("处理已中止")
 
    def run_async_task(self):
        try:
            loop = asyncio.new_event_loop()
            asyncio.set_event_loop(loop)
            loop.run_until_complete(self.processor.run())
        except Exception as e:
            self.update_log(f"错误: {str(e)}")
        finally:
            self.after(0, lambda: self.cancel_processing())
 
    def update_log(self, message: str):
        self.log_area.config(state="normal")
        self.log_area.insert(tk.END, message + "\n")
        self.log_area.see(tk.END)
        self.log_area.config(state="disabled")
 
    def on_close(self):
        if self.processing:
            if messagebox.askokcancel("退出", "处理正在进行中,确定要退出吗?"):
                self.cancel_processing()
                self.destroy()
        else:
            self.destroy()
 
 
class TurboProcessor:
    def __init__(self, api_key, input_file, output_dir, concurrency,
                 prompt_template, content_length, temperature, timeout, log_callback):
        self.api_key = api_key
        self.input_file = input_file
        self.output_file = f"{output_dir}/result_{int(time.time())}.txt"
        self.concurrency = concurrency
        self.prompt_template = f"{prompt_template} 内容:{{content}}"
        self.content_length = content_length
        self.temperature = temperature
        self.timeout = timeout
        self.log_callback = log_callback
 
        self.sem = asyncio.Semaphore(concurrency)
        self.cancelled = False
        self.tasks = []
        self.chapters = []
        self.results = []
        self.total_chapters = 0
        self.start_time = 0.0
        self.success = 0
        self.failed = 0
 
    def cancel(self):
        self.cancelled = True
        for task in self.tasks:
            task.cancel()
 
    def _log(self, message: str):
        if self.log_callback:
            self.log_callback(message)
 
    async def process_chapter(self, chapter_info: Tuple[int, str]) -> str:
        if self.cancelled:
            return None
 
        index, content = chapter_info
        current = f"{index}/{self.total_chapters}"
 
        payload = {
            "model": "deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
            "messages": [{
                "role": "user",
                "content": self.prompt_template.format(content=content[:self.content_length])
            }],
            "temperature": self.temperature
        }
 
        async with self.sem:
            for attempt in range(5 + 1):
                try:
                    async with aiohttp.ClientSession(
                            headers={"Authorization": f"Bearer {self.api_key}"},
                            timeout=aiohttp.ClientTimeout(*self.timeout)
                    ) as session:
 
                        if self.cancelled:
                            return None
 
                        if attempt == 0:
                            self._log(f"[章节 {current}] 开始处理")
                        else:
                            self._log(f"[章节 {current}] 第{attempt}次重试")
 
                        start = time.monotonic()
                        async with session.post(
                                "https://api.siliconflow.cn/v1/chat/completions",
                                json=payload
                        ) as resp:
 
                            if resp.status == 200:
                                data = await resp.json()
                                elapsed = time.monotonic() - start
                                self._log(f"[章节 {current}] 成功(耗时 {elapsed:.1f}s)")
                                text = data['choices'][0]['message']['content'].strip()
                                return re.sub(r'\n{3,}', '\n', text)
 
                            elif resp.status == 429:
                                backoff = min(2 ** attempt, 30)
                                self._log(f"[章节 {current}] 频率限制(等待{backoff}s)")
                                await asyncio.sleep(backoff)
 
                            else:
                                self._log(f"[章节 {current}] HTTP错误 {resp.status}")
                                break
 
                except (asyncio.CancelledError, KeyboardInterrupt):
                    raise
                except Exception as e:
                    backoff = min(2 ** attempt, 30)
                    self._log(f"[章节 {current}] 错误: {type(e).__name__}({attempt + 1}/5次)")
                    await asyncio.sleep(backoff)
 
            self._log(f"[章节 {current}] 处理失败")
            return None
 
    async def pipeline(self):
        try:
            # 读取文件
            try:
                with open(self.input_file, 'r', encoding='utf-8') as f:
                    text = f.read()
            except UnicodeDecodeError:
                with open(self.input_file, 'r', encoding='gbk') as f:
                    text = f.read()
 
            # 分割章节
            raw_chapters = re.findall(r'(第[^章]+章[\s\S]*?)(?=第[^章]+章|$)', text)
            self.total_chapters = len(raw_chapters)
            self.chapters = list(enumerate(raw_chapters, start=1))
 
            self._log(f"开始处理 {self.total_chapters} 个章节...")
 
            # 创建任务
            self.tasks = [asyncio.create_task(self.process_chapter((idx, ch)))
                          for idx, ch in self.chapters]
            self.results = await asyncio.gather(*self.tasks, return_exceptions=True)
 
            # 写入结果
            with open(self.output_file, 'w', encoding='utf-8') as f:
                for (idx, _), res in zip(self.chapters, self.results):
                    if res and not isinstance(res, Exception):
                        f.write(f"=== 第{idx}章 ===\n{res}\n\n")
                        self.success += 1
                    else:
                        f.write(f"=== 第{idx}章处理失败 ===\n\n")
                        self.failed += 1
 
            # 最终报告
            total_time = time.monotonic() - self.start_time
            self._log("\n处理完成!\n"
                      f"成功: {self.success}\n"
                      f"失败: {self.failed}\n"
                      f"总耗时: {total_time:.1f}s\n"
                      f"输出文件: {self.output_file}")
 
        except asyncio.CancelledError:
            self._log("用户中止处理流程")
        except Exception as e:
            self._log(f"处理出错: {str(e)}")
 
    async def run(self):
        self.start_time = time.monotonic()
        await self.pipeline()
 
 
if __name__ == "__main__":
    window = ConfigWindow()
    window.mainloop()
 楼主| 宋长雨 发表于 2025-2-26 17:52
核心源码放出来
[Python] 纯文本查看 复制代码
001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import re
import time
import aiohttp  # 改用异步请求库
import asyncio
from typing import List, Tuple
 
# ============== 配置区 ==============
API_KEY = "API密钥"
INPUT_FILE = "要拆的小说"
OUTPUT_FILE = "结果"
CONCURRENCY = 3  # 并发请求数(根据API限制调整)
MAX_RETRY = 5  # 智能重试次数
TIMEOUT = (35, 160# 连接35s/总160s超时
 
 
# ====================================
 
class TurboProcessor:
    def __init__(self):
        self.log = []
        self.success = 0
        self.failed = 0
        self.sem = asyncio.Semaphore(CONCURRENCY)  # 信号量控制并发
        self.chapters: List[Tuple[int, str]] = []
        self.results = []
 
    def _log(self, msg: str):
        """轻量级日志记录"""
        entry = f"[{time.strftime('%H:%M:%S')}] {msg}"
        print(entry)
        self.log.append(entry)
 
    async def process_chapter(self, index: int, content: str) -> str:
        """异步处理核心"""
        payload = {
            "model": "deepseek-ai/DeepSeek-R1-Distill-Llama-8B",
            "messages": [{
                "role": "user",
                "content": f"请以简洁的纯文本格式输出故事八要素,包含八要素:身份、目的、行动、核心问题、阻力、结局、正能量和负能量,不要任何修饰。将以下内容精简至100字八要素:{content[:2000]}"
            }],
            "temperature": 0.3
        }
 
        async with self.sem:
            for attempt in range(MAX_RETRY):
                try:
                    async with aiohttp.ClientSession(
                            headers={"Authorization": f"Bearer {API_KEY}"},
                            timeout=aiohttp.ClientTimeout(*TIMEOUT)
                    ) as session:
 
                        start = time.monotonic()
                        async with session.post(
                                "https://api.siliconflow.cn/v1/chat/completions",
                                json=payload
                        ) as resp:
 
                            if resp.status == 200:
                                data = await resp.json()
                                elapsed = time.monotonic() - start
                                self._log(f"Ch{index} &#10003; ({elapsed:.1f}s)")
                                return re.sub(r'\n{3,}', '\n', data['choices'][0]['message']['content'].strip())
 
                            elif resp.status == 429:
                                backoff = min(2 ** attempt, 10)
                                self._log(f"Ch{index} &#9889; 过载(等待{backoff}s)")
                                await asyncio.sleep(backoff)
                                continue
 
                            else:
                                self._log(f"Ch{index} &#10007; HTTP {resp.status}")
                                break
 
                except (aiohttp.ClientError, asyncio.TimeoutError) as e:
                    backoff = min(2 ** attempt, 15)
                    self._log(f"Ch{index} &#9889; {type(e).__name__}(重试 {attempt + 1}/{MAX_RETRY})")
                    await asyncio.sleep(backoff)
 
            self._log(f"Ch{index} &#10007; 最终失败")
            return None
 
    async def pipeline(self):
        """异步处理管线"""
        # 读取文件
        try:
            with open(INPUT_FILE, 'r', encoding='utf-8') as f:
                text = f.read()
        except UnicodeDecodeError:
            with open(INPUT_FILE, 'r', encoding='gbk') as f:
                text = f.read()
 
        # 分割章节
        self.chapters = list(enumerate(re.findall(
            r'(第[^章]+章[\s\S]*?)(?=第[^章]+章|$)', text
        ), start=1))
 
        # 启动异步任务
        tasks = [self.process_chapter(idx, cont) for idx, cont in self.chapters]
        self.results = await asyncio.gather(*tasks)
 
        # 写入结果
        with open(OUTPUT_FILE, 'w', encoding='utf-8') as f:
            for (idx, _), res in zip(self.chapters, self.results):
                if res:
                    f.write(f"第{idx}章:\n{res}\n\n")
                    self.success += 1
                else:
                    f.write(f"第{idx}章处理失败\n\n")
                    self.failed += 1
 
        # 性能报告
        total_time = time.monotonic() - self.start_time
        self._log(
            f"处理完成 | 成功:{self.success} 失败:{self.failed} | 总耗时:{total_time:.1f}s | 平均速度:{len(self.chapters) / total_time:.2f}章/秒")
 
    async def run(self):
        self.start_time = time.monotonic()
        await self.pipeline()
 
 
if __name__ == "__main__":
    processor = TurboProcessor()
    asyncio.run(processor.run())
歆风 发表于 2025-2-26 18:24
liangp1979 发表于 2025-2-26 18:31
这个猛啊,多谢分享
dj102007 发表于 2025-2-26 18:34

这个猛啊,多谢分享
hhjjrrtt 发表于 2025-2-26 19:55
这个有点意思,谢谢分享
qinlao123 发表于 2025-2-26 20:11
试一下。谢谢
LUYAJUN 发表于 2025-2-26 20:18
有没有能倒过来的工具,先自己例每个章节的提纲,它按题纲写出几万字的小说出来
halou 发表于 2025-2-26 20:32
大佬NB plus!
自在小鱼 发表于 2025-2-26 20:51
这个有点意思,谢谢分享
您需要登录后才可以回帖 登录 | 注册[Register]

本版积分规则

返回列表

RSS订阅|小黑屋|处罚记录|联系我们|吾爱破解 - LCG - LSG ( 京ICP备16042023号 | 京公网安备 11010502030087号 )

GMT+8, 2025-4-1 05:52

Powered by Discuz!

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表