雨中1999

alt text
tag :

((reverse:1999):1.1),very awa, masterpiece, best quality,highres, absurdres, (1girl:1.05), (solo:1.05), 3d_background, vertin_(reverse:1999), (rain:1.2), white_gloves, tilted_headwear, arm_up, street, shop, straight-on, white_eyes, overcast, (close-up:1.2), bokeh, backlighting, lens_flare, portrait, hat_over_eyes, (sidelighting:1.1), light_particles,
((reverse:1999):1.1),very awa, masterpiece, best quality,highres, absurdres, (1girl:1.05), (solo:1.05), 3d_background, vertin_(reverse:1999), (rain:1.2), white_gloves, tilted_headwear, arm_up, street, shop, straight-on, white_eyes, overcast, (close-up:1.2), bokeh, backlighting, lens_flare, portrait, hat_over_eyes, (sidelighting:1.1), light_particles,

光影人物

alt text
tag:

masterpiece,best quality,ultra-detailed CG,1girl,solo,
{{blonde hair}} cascading over arms, {{eyes closed}} in serene repose,
head resting on folded hands, {{open book}} centered before her,
left page: “Nemophila” text + illustration of {{girl in orange dress}} amidst blue flowers,
right page: “Sunflower” text + {{sunflower diagram}} + {{girl in pale gown}} in golden field,
background: floor-to-ceiling bookshelf with leather-bound tomes, {{potted monstera}} in corner,
warm palette: golden yellows (hair/sunflower), burnt umbers (wood/text), sage greens (plant),
{{dramatic lighting}}: sunbeam spotlighting book pages, dust motes in air,
textured details: paper fibers visible, fabric wrinkles on sleeves, wood grain on desk,
book’s gilt-edged pages reflecting light, pressed flower bookmark peeking from spine,
{{shallow depth of field}}: foreground book sharp, background shelves softly blurred,
nostalgic library atmosphere, vellum-like skin texture, velvet cushion under elbows

光影玫瑰

alt text

masterpiece, best quality, ultra-detailed CG,
{{upright open book}}, book propped vertically,
left page: “Golden Rose” text + {{golden rose botanical drawing}} + subtle ink sketch of petals curling outward,
right page: “Blue Rose” text + {{blue rose botanical drawing}} + delicate illustration of dew-covered petals,
{{sepia-toned ink}} and delicate watercolor textures on pages,
sunlight shining through book pages, subtle glow, warm soft lighting,
background: vintage wooden bookshelf, filled with leather-bound books,
{{potted monstera}} with split leaves to the side, antique brass desk lamp, feather quill and inkwell,
warm palette: golden yellows (golden rose), cool blues (blue rose), burnt umber (wood, ink), sage greens (plant), soft cream (pages),
{{dust motes floating}}, soft atmospheric haze, nostalgic library ambiance,
book has {{gilt edges}}, {{pressed flower bookmark}} peeking subtly,
velvet cushion beneath, fabric folds on nearby cloth,
shallow depth of field: foreground sharp, background softly blurred,
anime-style, soft gradient shading, pastel tones, watercolor and pencil sketch effects,
3::glittering light particles, ethereal golden light sparkles, light scattering, volumetric rays, painted bokeh, floating particles, dreamlike golden particles::

古老异蛇

alt text

1.5::tarot_(medium),ornate border::,1.2::art nouveau::,fantasy,2::maya (culture),chichen itza,el_castillo,quetzalcoatl_(mythology)::,giant snake,giant snake with feathered wings,aztec,mesoamerican_architecture,pyramid_(structure),plant,1girl,bishoujo,1.5::sharp teeth::,open mouth,hair intakes,1.3::hair over one eye::,dark green hair,orange eyes,slit pupils,wavy hair,long hair,1.2::twintails::,small breasts,hair ornament,feather trim,1.5::mayan clothes::,jewelry,armlet,white see-through clothes,skirt,gold trim,bare legs,full body,1.3::invisible chair::

魔法卡牌

alt text

tarot (medium), star (symbol), star,hexagram,crystal in lantern,blue fire,candle,candlestand,crow,purple butterfly,classical style decoration,decoration,blue rose,magic,fantasy,1girl,witch hat with gold pattern,suit collar,frills,white layered dress,dark blue over skirt,puffy long sleeves,short over long sleeves,decorative adornments,capelet,two-sided fabric,ascot,black pantyhose,sitting,crossed ankles,on crescent,holding kerosene lamp,holding tarot card

梦幻书籍

alt text

Masterpiece, best quality, ultra-detailed CG, fantasy illustration, anime-style, watercolor and pencil sketch effects, classical style decoration, warm ambient lighting, soft pastel gradient shading, dark academia aesthetic, shallow depth of field,​​Background left:​​ Open grimoire on velvet cloth with three-dimensional holographic magic circle projection rising from pages, glowing edge highlights, sepia-toned ink sketches, winged sprite bookmarks, floating petals and parchment textures ​​Background right:​​ Blue rose and dried roses entwined around gilded candle stand, enchanted thorn vines emitting bioluminescent glow, ivy strands curling across aged wood, forest moss at base ​​Mid-ground center:​​ Celestial orrery mechanism replacing carpet, gear structure revealing rotating nebula diagrams, magical particles forming dynamic vortex ​​Upper background:​​ Twilight gradient sky, dreamy volumetric rays, drifting star chart fragments instead of calligraphy symbols ​​Bottom foreground:​​ Velvet cushion with molten gold metallic drips, golden fringe, scattered petals, reflective gold dust ​​Rightmost background:​​ Antique bookshelf with leather-bound tomes, liquid gold and mithril vapor seeping from pages, softly glowing gemstones embedded among relics ​​Ambient:​​ 3::manifested knowledge sprites formed by light particles, ethereal golden light composing string theory geometries, painted bokeh, volumetric rays scattering through scene, floating magical particles, charred parchment ash floating::highly detailed​​Color palette:​​ Warm gold tones (chalice/candle), deep indigo blue (blue rose/background), sage green (foliage), dusty rose (dried roses), soft cream (pages), burnt umber (wood), nebula purple (orrery), mithril gray (metal vapor), soft light gradients and atmospheric haze

连接大数据中台数据库

  • SAP抽取到GP数仓中

  • 大数据平台位置
    alt text

  • 扫描域
    alt text

  • DBO
    alt text

  • 同构数据表

  • 第一步同步表结构
    alt text

数据中台映射管理

alt text

24小时留言板

核心效果如图所示

alt text

代码解析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
# TodoController 代码解析

## 整体架构
这是一个基于Spring WebFlux的响应式控制器,结合Redis发布\订阅机制实现实时更新的待办事项系统。关键组件包括:
- **TodoRepo**:数据访问层接口
- **ReactiveRedisTemplate**:Redis响应式操作模板
- **ChannelTopic**:Redis消息通道
- **Sinks.Many**:Project Reactor的事件分发器

## 核心功能解析

### 1. Redis发布\订阅初始化
```java
public TodoController(TodoRepo repo, ReactiveRedisTemplate<String, String> redisTemplate) {
this.repo = repo;
this.redisTemplate = redisTemplate;

redisTemplate.listenTo(todoTopic)
.map(msg -> msg.getMessage())
.filter(message -> !StringUtils.isEmpty(message))
.subscribe(eventSink::tryEmitNext);
}
  • 功能:控制器启动时自动订阅Redis频道
  • 处理流程
    1. 监听todo_events频道
    2. 提取消息内容
    3. 过滤空消息
    4. 将消息推送到事件流(Sinks)

2. 事件广播机制

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
private void broadcastEvent(String eventType, Todo todo) {
String messageId = "global_" + todo.getId() + "_" + System.currentTimeMillis();
String payload = "";

\\ 事件类型处理
switch (eventType) {
case "create":
case "update":
payload = String.format("id:%s\nevent:%s\ndata:%s\n\n",
messageId, eventType, renderItem(todo));
break;
case "delete":
payload = String.format("id:%s\nevent:delete\ndata:%d\n\n",
messageId, todo.getId());
break;
}

\\ Redis发布
if (StringUtils.hasLength(payload)) {
redisTemplate.convertAndSend(todoTopic.getTopic(), payload).subscribe();
}
}
  • 事件格式:符合SSE规范,包含:
    • id:全局唯一事件ID(含时间戳)
    • event:事件类型(create\update\delete)
    • data:有效载荷
  • 安全设计:广播前检查消息有效性

3. SSE数据流接口

1
2
3
4
5
6
7
8
9
10
11
@GetMapping(produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> list() {
Flux<String> initialFlux = repo.findAllByOrderByIdDesc()
.map(todo ->
String.format("id:init_%d\nevent:create\ndata:%s\n\n",
todo.getId(), renderItem(todo))
);

return Flux.merge(initialFlux, eventSink.asFlux())
.filter(payload -> !StringUtils.isEmpty(payload));
}
  • 实现策略
    1. 加载初始数据并转换为SSE格式(init前缀)
    2. 合并实时事件流(Redis订阅)
    3. 过滤空消息
  • SSE规范
    • id: [event_id]
    • event: [event_type]
    • data: [payload]
    • 消息以双换行结束

4. CRUD操作实现

创建待办事项

1
2
3
4
5
6
@PostMapping
public Mono<String> create(@ModelAttribute Form form) {
return repo.save(new Todo(null, HtmlUtils.htmlEscape(form.getContent())))
.doOnSuccess(todo -> broadcastEvent("create", todo))
.thenReturn("");
}
  • 安全特性:HTML内容转义防御XSS
  • 响应策略:空字符串实现PRG(Post-Redirect-Get)模式

删除待办事项

1
2
3
4
5
6
7
@PostMapping(path = "\{id}\delete", produces = MediaType.TEXT_HTML_VALUE)
public Mono<String> delete(@PathVariable Long id) {
return repo.findById(id)
.doOnSuccess(todo -> broadcastEvent("delete", todo))
.then(repo.deleteById(id))
.thenReturn("");
}
  • 关键流程
    1. 先查询再删除(确保广播正确的todo数据)
    2. 广播删除事件
    3. 执行删除操作

5. 视图渲染引擎

查看模式渲染

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
private String renderItem(Todo t) {
return """
<li id="todo-%d" class="message-item">
<div class="message-content">
%s
<div class="message-extra">
<div class="message-time"><i class="far fa-clock"><\i> %s<\div>
<\div>
<\div>
<div class="message-actions">
<button class="delete-btn" onclick="handleDelete(%d)">
<i class="fas fa-trash-alt"><\i>
<\button>
<\div>
<\li>
""";
}
  • 元素结构
    • 唯一ID绑定:todo-{id}
    • 内容显示区
    • 时间戳(当前服务器时间)
    • 删除按钮(绑定JavaScript操作)

编辑模式渲染(未使用)

1
2
3
private String renderItemEditing(Todo t) {
\\ 包含表单和按钮
}
  • 潜在扩展点:提供编辑功能时可复用

6. 安全机制

1
2
3
4
5
6
7
\\ 输入转义
HtmlUtils.htmlEscape(form.getContent())

\\ 输出转义
private String escapeHtml(String s) {
return HtmlUtils.htmlEscape(s);
}
  • 双保险策略:输入输出双重转义防御XSS攻击
  • 规范实践:使用Spring内置HtmlUtils工具类

设计亮点

  1. 响应式架构

    • Flux\Mono异步处理
    • 背压支持(onBackpressureBuffer)
  2. 实时广播系统

    • Redis发布\订阅
    • 多级事件分发(Sinks->Flux)
  3. 客户端驱动更新

    • HTML片段直接注入页面
    • 无JavaScript框架依赖
  4. 优雅降级策略

    • 事件ID前缀区分初始化\实时事件
    • 空消息过滤保证稳定性
  5. 扩展性设计

    • 事件类型支持(create\update\delete)
    • 预留编辑模式渲染器

优化建议

  1. 时间戳改进

    1
    2
    3
    4
    5
    \\ 当前使用服务器当前时间
    LocalTime.now().format(DateTimeFormatter.ofPattern("HH:mm:ss"))

    \\ 建议:使用Todo创建时间
    t.getCreatedAt().format(DateTimeFormatter.ofPattern("HH:mm:ss"))
  2. 事件完整性保证

    1
    2
    \\ 增加错误处理
    .doOnError(e -> log.error("Event broadcast failed", e))
  3. 集群支持增强

    1
    2
    3
    4
    5
    \\ 当前事件ID格式
    "global_" + todo.getId() + "_" + System.currentTimeMillis()

    \\ 建议增加实例标识
    "node1_" + todo.getId() + "_" + System.currentTimeMillis()
  4. 编辑功能集成
    可启用预留的renderItemEditing方法实现编辑功能

  5. 自动清理机制
    增加定时任务清理24小时前的待办事项

该系统实现了基于响应式编程和Redis发布\订阅的高效实时应用,兼顾了性能和安全,代码结构清晰且扩展性强,是实时应用开发的优秀范例。

1
2
##GITHUb仓库
https:\\github.com\qingyun201908\HTMXANDSpringWebflux

alt text

AI处理漫画转视频

第一步 从漫画PDF文件读取图片
第二部 图片信息剪裁
第三步 OCR识别处理图片,获取漫画对应的文本信息
第四步 运用阿里云通义大模型千文处理提取的文本信息更符合文本语言
第五步 运用FishVideo大模型将文本信息转变为对应的语音
第六步 图片转视频处理将图片与语音结合并添加转场
第七步 合并视频段

从PDF文件读取图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import fitz  # PyMuPDF
import os

def extract_images_from_pdf(pdf_path, output_folder):
"""
从PDF文件中提取所有图片并按顺序保存到指定文件夹

参数:
pdf_path (str): PDF文件路径
output_folder (str): 图片保存目录
"""
# 创建输出目录
os.makedirs(output_folder, exist_ok=True)

# 打开PDF文件
doc = fitz.open(pdf_path)

# 初始化图片计数器
image_count = 0

# 遍历每一页
for page_num in range(len(doc)):
page = doc.load_page(page_num)

# 获取当前页所有图片列表
image_list = page.get_images(full=True)

# 遍历当前页的图片
for image_index, img in enumerate(image_list, start=1):
xref = img[0] # 获取图片xref
base_image = doc.extract_image(xref) # 提取图片信息
image_data = base_image["image"] # 获取图片二进制数据
ext = base_image["ext"] # 获取图片扩展名

# 生成图片文件名
image_filename = f"image_{image_count + 1}.{ext}"
image_path = os.path.join(output_folder, image_filename)

# 保存图片
with open(image_path, "wb") as img_file:
img_file.write(image_data)

image_count += 1

print(f"成功提取 {image_count} 张图片到目录: {output_folder}")

if __name__ == "__main__":
# 配置参数(根据实际情况修改)
pdf_file = "D:\\BaiduNetdiskDownload\\一-人-之-下第051-100话.pdf" # PDF文件路径

# 自动生成输出文件夹路径(PDF文件名 + _images)
base_name = os.path.splitext(os.path.basename(pdf_file))[0] # 去除扩展名
folder_name = f"{base_name}_images" # 生成文件夹名称
output_dir = os.path.join(os.path.dirname(pdf_file), folder_name) # 完整输出路径

# 执行提取
extract_images_from_pdf(pdf_file, output_dir)

代码解析

这段代码的主要功能是从指定的PDF文件中提取所有图片,并将这些图片按顺序保存到指定的文件夹中。下面是代码的详细分析:

导入库

1
2
import fitz  # PyMuPDF
import os
  • fitz是PyMuPDF库的一个模块,用于处理PDF文件,包括读取和提取内容。
  • os模块用于文件和目录的操作。

提取函数

1
def extract_images_from_pdf(pdf_path, output_folder):
  • 定义一个名为extract_images_from_pdf的函数,接受两个参数:pdf_path表示PDF文件的路径,output_folder表示保存提取图片的文件夹。
创建输出目录
1
os.makedirs(output_folder, exist_ok=True)
  • 使用os.makedirs创建保存图片的目录,exist_ok=True表示如果目录已存在则不抛出异常。
打开PDF文件
1
doc = fitz.open(pdf_path)
  • 使用fitz.open打开PDF文件,并将其赋值给doc对象。
初始化计数器
1
image_count = 0
  • 初始化image_count变量,用于统计提取的图片数量。
遍历每一页
1
2
for page_num in range(len(doc)):
page = doc.load_page(page_num)
  • 使用for循环遍历PDF的每一页,doc.load_page(page_num)加载当前页。
获取和处理图片
1
image_list = page.get_images(full=True)
  • 获取当前页的所有图片,并将其存储在image_list中。
1
2
3
4
5
for image_index, img in enumerate(image_list, start=1):
xref = img[0] # 获取图片xref
base_image = doc.extract_image(xref) # 提取图片信息
image_data = base_image["image"] # 获取图片二进制数据
ext = base_image["ext"] # 获取图片扩展名
  • 对于每个图片,提取图片的引用(xref),然后获取其详细信息,包括二进制数据和扩展名。
保存图片
1
2
3
4
5
6
7
image_filename = f"image_{image_count + 1}.{ext}"
image_path = os.path.join(output_folder, image_filename)

with open(image_path, "wb") as img_file:
img_file.write(image_data)

image_count += 1
  • 生成图片文件名,构造完整的文件路径,并使用with open将图片数据写入文件。
提示成功信息
1
print(f"成功提取 {image_count} 张图片到目录: {output_folder}")
  • 在控制台输出提取成功的图片数量及其保存路径。

主程序块

1
if __name__ == "__main__":
  • 仅当该脚本作为主程序运行时,以下代码才会执行。
配置参数
1
pdf_file = "D:\\BaiduNetdiskDownload\\一-人-之-下第051-100话.pdf"  # PDF文件路径
  • 设置要提取图片的PDF文件路径。
自动生成输出文件夹路径
1
2
3
base_name = os.path.splitext(os.path.basename(pdf_file))[0]
folder_name = f"{base_name}_images"
output_dir = os.path.join(os.path.dirname(pdf_file), folder_name)
  • 自动生成输出文件夹的名称,格式为PDF文件名_images
执行提取
1
extract_images_from_pdf(pdf_file, output_dir)
  • 调用提取函数,开始提取图片。

Markdown 文件内容

1
2
3
4
5
6
7
8
9
10
11
# 从PDF提取图片的Python脚本

## 概述
该脚本用于从指定的PDF文件中提取所有图片并将其按顺序保存到指定的文件夹中。使用了`fitz`库(PyMuPDF)来处理PDF文件。

## 代码解析

### 导入库
```python
import fitz # PyMuPDF
import os

提取函数

1
def extract_images_from_pdf(pdf_path, output_folder):
  • 参数:
    • pdf_path: PDF文件的路径。
    • output_folder: 存储提取图片的文件夹路径。

创建输出目录

1
os.makedirs(output_folder, exist_ok=True)

打开PDF文件

1
doc = fitz.open(pdf_path)

初始化计数器

1
image_count = 0

遍历每一页

1
2
for page_num in range(len(doc)):
page = doc.load_page(page_num)

获取和处理图片

1
image_list = page.get_images(full=True)

保存图片

1
2
3
4
5
6
7
image_filename = f"image_{image_count + 1}.{ext}"
image_path = os.path.join(output_folder, image_filename)

with open(image_path, "wb") as img_file:
img_file.write(image_data)

image_count += 1

提示成功信息

1
print(f"成功提取 {image_count} 张图片到目录: {output_folder}")

主程序块

1
if __name__ == "__main__":
  • 配置PDF文件路径和输出文件夹。

使用说明

  1. 安装PyMuPDF库:pip install PyMuPDF
  2. 修改pdf_file变量为要提取的PDF文件路径。
  3. 运行脚本,提取的图片将存储在指定的文件夹中。

注意事项

  • 确保PDF文件路径正确。
  • 输出文件夹将自动创建,若已存在则会被忽略。

图片信息剪裁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
from PIL import Image
import os

def batch_crop_images(input_folder, output_folder, top, bottom):
"""
批量裁剪图片(仅修改下边界,保持左右宽度不变)
:param input_folder: 输入文件夹路径
:param output_folder: 输出文件夹路径
:param top: 裁剪区域上边界
:param bottom: 裁剪区域下边界
"""
# 确保输出文件夹存在
if not os.path.exists(output_folder):
os.makedirs(output_folder)

# 遍历输入文件夹中的所有文件
for filename in os.listdir(input_folder):
if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
image_path = os.path.join(input_folder, filename)
with Image.open(image_path) as img:
width, height = img.size
# 保持左右不变(left=0, right=图片宽度)
# 动态调整下边界,确保不超过图片高度
adjusted_bottom = min(bottom, height)
# 裁剪区域:左=0, 上=top, 右=width, 下=adjusted_bottom
cropped_img = img.crop((0, top, width, adjusted_bottom))
output_path = os.path.join(output_folder, filename)
cropped_img.save(output_path)
print(f"裁剪并保存: {output_path}")

# 示例调用
input_folder = "D:\\BaiduNetdiskDownload\\051-100_images"
output_folder = "D:\\BaiduNetdiskDownload\\051-100_images2"
top, bottom = 0, 1225 # 仅设置上边界和下边界

batch_crop_images(input_folder, output_folder, top, bottom)

代码解析

该代码的主要功能是批量裁剪指定文件夹中的图片,保持左右宽度不变,只调整上下边界。裁剪后的图片将保存到指定的输出文件夹中。以下是代码的详细分析:

导入库

1
2
from PIL import Image
import os
  • PIL(Python Imaging Library): 用于图像处理,主要用于打开、操作和保存图像文件。
  • os: 用于与操作系统交互,进行文件和目录操作。

批量裁剪函数

1
def batch_crop_images(input_folder, output_folder, top, bottom):
  • 该函数接受四个参数:
    • input_folder: 输入文件夹路径,包含待裁剪的图片。
    • output_folder: 输出文件夹路径,保存裁剪后的图片。
    • top: 裁剪区域的上边界。
    • bottom: 裁剪区域的下边界。
确保输出文件夹存在
1
2
if not os.path.exists(output_folder):
os.makedirs(output_folder)
  • 检查输出文件夹是否存在,如果不存在则创建该文件夹。
遍历输入文件夹中的所有文件
1
2
for filename in os.listdir(input_folder):
if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):
  • 遍历输入文件夹中的所有文件,并筛选出支持的图片格式(.png, .jpg, .jpeg, .bmp, .gif)。
打开和裁剪图片
1
2
3
image_path = os.path.join(input_folder, filename)
with Image.open(image_path) as img:
width, height = img.size
  • 构造每个图片的完整路径,使用Image.open()打开图片,并获取其宽度和高度。
动态调整裁剪区域
1
adjusted_bottom = min(bottom, height)
  • 确保下边界不超过图片的实际高度,避免裁剪超出图片范围。
裁剪并保存图片
1
2
3
4
cropped_img = img.crop((0, top, width, adjusted_bottom))
output_path = os.path.join(output_folder, filename)
cropped_img.save(output_path)
print(f"裁剪并保存: {output_path}")
  • 使用crop()方法进行裁剪,裁剪区域为左=0,上=top,右=width,下=adjusted_bottom。裁剪后的图片保存到输出路径,并打印保存消息。

示例调用

1
2
3
4
5
input_folder = "D:\\BaiduNetdiskDownload\\051-100_images"
output_folder = "D:\\BaiduNetdiskDownload\\051-100_images2"
top, bottom = 0, 1225 # 仅设置上边界和下边界

batch_crop_images(input_folder, output_folder, top, bottom)
  • 指定输入文件夹、输出文件夹及裁剪的上下边界,然后调用batch_crop_images函数进行批量裁剪操作。

Markdown 文件内容

1
2
3
4
5
6
7
8
9
10
11
# 批量裁剪图片脚本

## 概述
该脚本用于批量裁剪指定文件夹中的图片,裁剪区域的上下边界可由用户自定义,保持左右宽度不变。裁剪后的图片将保存到指定的输出文件夹中。

## 代码解析

### 导入库
```python
from PIL import Image
import os
  • PIL: 用于图像处理。
  • os: 用于文件和目录操作。

批量裁剪函数

1
def batch_crop_images(input_folder, output_folder, top, bottom):
  • 函数接受输入文件夹路径、输出文件夹路径、上边界和下边界。

确保输出文件夹存在

1
2
if not os.path.exists(output_folder):
os.makedirs(output_folder)

遍历输入文件夹中的所有文件

1
2
for filename in os.listdir(input_folder):
if filename.lower().endswith(('.png', '.jpg', '.jpeg', '.bmp', '.gif')):

打开和裁剪图片

1
2
3
image_path = os.path.join(input_folder, filename)
with Image.open(image_path) as img:
width, height = img.size

动态调整裁剪区域

1
adjusted_bottom = min(bottom, height)

裁剪并保存图片

1
2
3
4
cropped_img = img.crop((0, top, width, adjusted_bottom))
output_path = os.path.join(output_folder, filename)
cropped_img.save(output_path)
print(f"裁剪并保存: {output_path}")

示例调用

1
2
3
4
5
input_folder = "D:\\BaiduNetdiskDownload\\051-100_images"
output_folder = "D:\\BaiduNetdiskDownload\\051-100_images2"
top, bottom = 0, 1225 # 仅设置上边界和下边界

batch_crop_images(input_folder, output_folder, top, bottom)

使用说明

  1. 确保已安装Pillow库(可以使用pip install Pillow)。
  2. 指定输入文件夹和输出文件夹路径。
  3. 调整上边界和下边界参数。
  4. 运行脚本,裁剪后的图片将保存到输出文件夹中。

注意事项

  • 确保输入文件夹中存在支持的图片格式。
  • 裁剪区域的上边界和下边界应确保合理,以避免裁剪超出图片范围。

OCR识别处理图片,获取漫画对应的文本信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
import easyocr
import cv2
import os
import torch
from gc import collect # 内存回收

# ------------------- 配置区域 -------------------
input_folder = r'D:\BaiduNetdiskDownload\051-100_images2'
languages = ['ch_sim', 'en']
supported_exts = ['.jpg', '.jpeg', '.png']
# ----------------------------------------------

# GTX 1050Ti 优化参数
config = {
'batch_size': 4, # 显存有限,建议4-6
'workers': 2, # 避免过多线程竞争
'fp16': False, # 1050Ti不支持Tensor Core,关闭半精度
'model_load': 'balanced' # 显存优化模式
}

def merge_paragraphs(results):
"""轻量级段落合并算法"""
if not results:
return []

# 按Y坐标排序
sorted_results = sorted(results, key=lambda x: x[0][0][1])

# 合并逻辑(简化版)
paragraphs = []
current_para = []
prev_bottom = -100 # 初始间距

for box, text, _ in sorted_results:
top = box[0][1]
# 行间距超过50像素视为新段落(根据漫画排版调整)
if (top - prev_bottom) > 50:
if current_para:
paragraphs.append(' '.join(current_para))
current_para = []
current_para.append(text.strip())
prev_bottom = box[3][1] # 更新底部坐标

if current_para:
paragraphs.append(' '.join(current_para))

return paragraphs

def process_image(reader, image_path, output_path):
"""处理单张图片并管理显存"""
try:
# 读取图片
img = cv2.imread(image_path)
if img is None:
raise ValueError("图片读取失败")

# 显存优化策略
torch.cuda.empty_cache()

# 执行OCR
results = reader.readtext(
img,
batch_size=config['batch_size'],
workers=config['workers'],
detail=1, # 必须为1才能获取坐标
paragraph=False # 禁用内置段落合并(使用自定义算法)
)

# 合并段落
paragraphs = merge_paragraphs(results)

# 保存结果
with open(output_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(paragraphs))

return True
except Exception as e:
print(f"处理失败: {str(e)}")
return False
finally:
collect() # 强制回收内存

if __name__ == "__main__":
# 验证CUDA
if not torch.cuda.is_available():
raise RuntimeError("CUDA不可用,请检查驱动")

# 初始化Reader(显存优化配置)
reader = easyocr.Reader(
lang_list=languages,
gpu=True,
detector=True,
recognizer=True,
model_storage_directory=None,
download_enabled=False,
user_network_directory=None
)

# 遍历处理
total = len([f for f in os.listdir(input_folder) if any(f.endswith(ext) for ext in supported_exts)])
processed = 0

for filename in os.listdir(input_folder):
if not any(filename.lower().endswith(ext) for ext in supported_exts):
continue

image_path = os.path.join(input_folder, filename)
output_path = os.path.join(
input_folder,
f"{os.path.splitext(filename)[0]}.txt"
)

print(f"处理中 [{processed+1}\{total}]: {filename[:20]}...", end='', flush=True)

if process_image(reader, image_path, output_path):
print(" ✓")
processed += 1
else:
print(" ✗")

print(f"\n完成!成功处理 {processed}\{total} 张图片")

代码解析

该代码的主要功能是使用EasyOCR库从指定文件夹中的图片中提取文本,并将提取的文本保存到相应的文本文件中。使用了OpenCV库来读取图像,并且通过PyTorch对GPU进行优化。以下是代码的详细分析:

导入库

1
2
3
4
5
import easyocr
import cv2
import os
import torch
from gc import collect # 内存回收
  • easyocr: 用于图像文本识别(OCR)。
  • cv2: OpenCV库,用于图像处理。
  • os: 用于文件和目录操作。
  • torch: PyTorch库,用于深度学习相关操作。
  • collect: 从gc模块中导入,用于强制内存回收。

配置区域

1
2
3
input_folder = r'D:\BaiduNetdiskDownload\051-100_images2'
languages = ['ch_sim', 'en']
supported_exts = ['.jpg', '.jpeg', '.png']
  • input_folder: 输入图片的文件夹路径。
  • languages: 指定OCR识别的语言,这里包括简体中文和英语。
  • supported_exts: 支持的图片扩展名列表。

GPU优化参数

1
2
3
4
5
6
config = {
'batch_size': 4,
'workers': 2,
'fp16': False,
'model_load': 'balanced'
}
  • batch_size: 设置为4以适应显存限制。
  • workers: 工作线程数,避免过多线程竞争。
  • fp16: 由于GTX 1050Ti不支持Tensor Core,设置为False以关闭半精度。
  • model_load: 使用平衡模式优化显存使用。

段落合并函数

1
def merge_paragraphs(results):
  • 该函数负责将OCR识别的文本结果进行段落合并,以便更好地处理文本输出。
合并逻辑
  • 按Y坐标排序,并根据行间距(超过50像素视为新段落)将文本合并。

图像处理函数

1
def process_image(reader, image_path, output_path):
  • 处理单张图片并管理显存,读取图片,执行OCR,合并段落并保存结果。
读取和检查图片
1
2
3
img = cv2.imread(image_path)
if img is None:
raise ValueError("图片读取失败")
执行OCR
1
2
3
4
5
6
7
results = reader.readtext(
img,
batch_size=config['batch_size'],
workers=config['workers'],
detail=1,
paragraph=False
)
  • 使用EasyOCR的readtext方法提取文本,设置详细级别为1以获取坐标。
保存结果
1
2
with open(output_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(paragraphs))
内存回收
1
2
finally:
collect() # 强制回收内存

主程序块

1
if __name__ == "__main__":
  • 确保该代码块仅在脚本作为主程序运行时执行。
验证CUDA
1
2
if not torch.cuda.is_available():
raise RuntimeError("CUDA不可用,请检查驱动")
  • 检查CUDA是否可用,以确保可以使用GPU进行加速。
初始化Reader
1
2
3
4
5
6
7
8
9
reader = easyocr.Reader(
lang_list=languages,
gpu=True,
detector=True,
recognizer=True,
model_storage_directory=None,
download_enabled=False,
user_network_directory=None
)
  • 初始化EasyOCR的Reader对象,配置使用GPU。
遍历处理图片
1
total = len([f for f in os.listdir(input_folder) if any(f.endswith(ext) for ext in supported_exts)])
  • 统计输入文件夹中支持的图片数量。
处理每张图片
1
for filename in os.listdir(input_folder):
  • 遍历输入文件夹中的每个文件,检查扩展名并调用process_image处理。
打印处理状态
1
print(f"处理中 [{processed+1}\{total}]: {filename[:20]}...", end='', flush=True)
  • 输出当前处理进度。
1
print(f"\n完成!成功处理 {processed}\{total} 张图片")
  • 最后输出处理结果。

Markdown 文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 使用EasyOCR从图片中提取文本的Python脚本

## 概述
该脚本通过EasyOCR库从指定文件夹中的图片中提取文本,并将提取的文本保存到相应的文本文件中。使用OpenCV库读取图像,并通过PyTorch对GPU进行优化。

## 代码解析

### 导入库
```python
import easyocr
import cv2
import os
import torch
from gc import collect # 内存回收

配置区域

1
2
3
input_folder = r'D:\BaiduNetdiskDownload\051-100_images2'
languages = ['ch_sim', 'en']
supported_exts = ['.jpg', '.jpeg', '.png']

GPU优化参数

1
2
3
4
5
6
config = {
'batch_size': 4,
'workers': 2,
'fp16': False,
'model_load': 'balanced'
}

段落合并函数

1
def merge_paragraphs(results):
  • 合并OCR结果中的段落,以提高可读性。

图像处理函数

1
def process_image(reader, image_path, output_path):
  • 读取图片,执行OCR,合并段落并保存结果。

主程序块

1
if __name__ == "__main__":
  • 检查CUDA是否可用,并初始化EasyOCR的Reader对象。

遍历处理图片

1
total = len([f for f in os.listdir(input_folder) if any(f.endswith(ext) for ext in supported_exts)])
  • 统计待处理的图片数量,逐一处理并输出处理进度。

使用说明

  1. 安装必要的库:pip install easyocr opencv-python torch
  2. 修改input_folder为要处理的图片文件夹路径。
  3. 运行脚本,提取的文本将保存为相应的.txt文件。

注意事项

  • 确保安装了支持CUDA的PyTorch版本。
  • 确保输入文件夹中存在支持的图片格式。

运用阿里云通义千文处理提取的文本信息更符合文本语言

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
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
import os
import glob
import datetime
from openai import OpenAI
import openpyxl

# 初始化OpenAI客户端
client = OpenAI(
api_key="",
base_url="https:\\dashscope.aliyuncs.com\compatible-mode\v1",
)

# 初始化Excel文件
excel_file = "D:\\text_processing_log.xlsx"
if os.path.exists(excel_file):
wb = openpyxl.load_workbook(excel_file)
else:
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "处理记录"
if ws.max_row == 1:
ws.append(["类型", "内容", "文件路径", "处理时间"])

def save_to_excel(question, answer, file_path):
"""保存到Excel文件"""
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
ws.append(["问题", question, file_path, timestamp])
ws.append(["回答", answer, file_path, timestamp])
wb.save(excel_file)

def print_dialog(question, answer, max_width=80):
"""在CMD界面美观打印对话"""
sep_line = '─' * max_width
print(f"\n{sep_line}")
print(" 问题 ".center(max_width, '░'))
print(f"{question}\n")
print(" 回答 ".center(max_width, '░'))
print(f"{answer}")
print(sep_line)

def log_error(log_file, message, error=None):
"""统一记录错误日志"""
timestamp = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
with open(log_file, 'a', encoding='utf-8') as f:
log_entry = f"[{timestamp}] {message}"
if error:
log_entry += f"\n错误详情: {str(error)}"
log_entry += "\n" + "-"*50 + "\n"
f.write(log_entry)

def process_file(file_path, log_file):
"""处理单个文件"""
try:
if os.path.getsize(file_path) == 0:
print(f"跳过空文件: {file_path}")
return

with open(file_path, 'r+', encoding='utf-8') as f:
original_lines = f.readlines()

if not any(line.strip() for line in original_lines):
print(f"跳过内容为空的文件: {file_path}")
return

f.seek(0)
f.truncate()

for line_num, line in enumerate(original_lines, 1):
try:
if not line.strip():
f.write(line)
continue

original_text = line.strip()

completion = client.chat.completions.create(
model="qwen-plus",
messages=[{
'role': 'user',
'content': f'''原本句子中存在错词,不常见词语。可以修改词语和文字不采用偏僻词,不涵盖英语词汇,请严格按正常语序重组句子,只返回修改后的句子,不要任何解释。需要修改的句子:
{original_text}'''
}]
)

modified = completion.choices[0].message.content
f.write(modified + line[len(line.rstrip('\n\r')):])

# 显示对话并保存
print_dialog(original_text, modified)
save_to_excel(original_text, modified, file_path)

except Exception as e:
error_msg = f"文件: {file_path}{line_num}行处理失败 - 保留原内容"
print(f"\n! 处理异常: {error_msg}")
log_error(log_file, error_msg, e)
f.write(line)

except Exception as e:
error_msg = f"文件处理失败: {file_path}"
print(f"\n! 严重错误: {error_msg}")
log_error(log_file, error_msg, e)

def process_folder(folder_path):
"""处理整个文件夹"""
log_file = os.path.join(folder_path, "processing_errors.log")

with open(log_file, 'w', encoding='utf-8') as f:
f.write("文本处理异常日志\n")
f.write(f"开始时间: {datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S')}\n")
f.write("="*60 + "\n")

txt_files = glob.glob(os.path.join(folder_path, '**', '*.txt'), recursive=True)

if not txt_files:
print(f"在 {folder_path} 中未找到txt文件")
return

print(f"开始批量处理,共发现 {len(txt_files)} 个文本文件")
print(f"处理记录将保存至: {os.path.abspath(excel_file)}")
print("="*60)

for i, file_path in enumerate(txt_files, 1):
print(f"\n正在处理文件 ({i}\{len(txt_files)}):{os.path.basename(file_path)}")
process_file(file_path, log_file)

print("\n" + "="*60)
print(f"处理完成!错误日志已保存至: {log_file}")
print(f"共处理 {len(txt_files)*2} 条记录到Excel文件")

if __name__ == "__main__":
try:
process_folder('D:\\BaiduNetdiskDownload\\001-050_images')
finally:
wb.close()

代码解析

该代码的主要功能是批量处理指定文件夹中的文本文件,使用OpenAI的API对文本内容进行处理,然后将处理的记录保存到Excel文件中,并记录错误日志。以下是代码的详细分析:

导入库

1
2
3
4
5
import os
import glob
import datetime
from openai import OpenAI
import openpyxl
  • os: 用于文件和目录操作。
  • glob: 用于查找符合特定规则的文件路径名。
  • datetime: 用于处理日期和时间。
  • OpenAI: 用于与OpenAI API交互。
  • openpyxl: 用于处理Excel文件。

初始化OpenAI客户端

1
2
3
4
client = OpenAI(
api_key="",
base_url="https:\\dashscope.aliyuncs.com\compatible-mode\v1",
)
  • 初始化OpenAI API客户端,需提供API密钥。

初始化Excel文件

1
2
3
4
5
6
7
8
9
excel_file = "D:\\text_processing_log.xlsx"
if os.path.exists(excel_file):
wb = openpyxl.load_workbook(excel_file)
else:
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "处理记录"
if ws.max_row == 1:
ws.append(["类型", "内容", "文件路径", "处理时间"])
  • 检查Excel文件是否存在,若不存在则创建新的Excel工作簿,并设置工作表标题和表头。

保存数据到Excel

1
def save_to_excel(question, answer, file_path):
  • 该函数接受问题、答案和文件路径,并将它们写入Excel文件中,同时记录当前时间。

打印对话

1
def print_dialog(question, answer, max_width=80):
  • 在命令行窗口美观地打印问题和答案,使用分隔线以增强可读性。

记录错误日志

1
def log_error(log_file, message, error=None):
  • 记录错误信息到日志文件中,格式化输出时间和错误详情。

处理单个文件

1
def process_file(file_path, log_file):
  • 该函数负责读取文本文件,处理每一行文本,并调用OpenAI的API进行文本修改。
检查文件内容
1
2
3
if os.path.getsize(file_path) == 0:
print(f"跳过空文件: {file_path}")
return
  • 跳过空文件和内容为空的文件。
读取和处理文本
1
2
with open(file_path, 'r+', encoding='utf-8') as f:
original_lines = f.readlines()
  • 读取文件所有行,并逐行处理。
调用OpenAI API
1
2
3
4
5
6
7
8
completion = client.chat.completions.create(
model="qwen-plus",
messages=[{
'role': 'user',
'content': f'''原本句子中存在错词,不常见词语。可以修改词语和文字不采用偏僻词,不涵盖英语词汇,请严格按正常语序重组句子,只返回修改后的句子,不要任何解释。需要修改的句子:
{original_text}'''
}]
)
  • 使用OpenAI的API进行文本修改,API模型为qwen-plus
保存修改后的文本
1
f.write(modified + line[len(line.rstrip('\n\r')):])
  • 将修改后的文本写回文件,同时保留行末的换行符。

处理整个文件夹

1
def process_folder(folder_path):
  • 遍历指定文件夹中的所有文本文件,并调用process_file进行处理。
日志记录
1
log_file = os.path.join(folder_path, "processing_errors.log")
  • 创建一个日志文件来记录处理过程中出现的错误信息。
查找文本文件
1
txt_files = glob.glob(os.path.join(folder_path, '**', '*.txt'), recursive=True)
  • 使用glob模块查找指定文件夹下的所有文本文件。

主程序

1
if __name__ == "__main__":
  • 该部分保证代码在作为主程序运行时执行文件夹处理函数。

Markdown 文件内容

1
2
3
4
5
6
7
8
9
10
11
12
13
14
# 文本处理脚本

## 概述
该脚本用于批量处理指定文件夹中的文本文件,使用OpenAI的API对文本内容进行处理,并将处理记录保存到Excel文件中,同时记录错误日志。

## 代码解析

### 导入库
```python
import os
import glob
import datetime
from openai import OpenAI
import openpyxl

初始化OpenAI客户端

1
2
3
4
client = OpenAI(
api_key="",
base_url="https:\\dashscope.aliyuncs.com\compatible-mode\v1",
)

初始化Excel文件

1
2
3
4
5
6
7
8
9
excel_file = "D:\\text_processing_log.xlsx"
if os.path.exists(excel_file):
wb = openpyxl.load_workbook(excel_file)
else:
wb = openpyxl.Workbook()
ws = wb.active
ws.title = "处理记录"
if ws.max_row == 1:
ws.append(["类型", "内容", "文件路径", "处理时间"])

保存数据到Excel

1
def save_to_excel(question, answer, file_path):
  • 保存处理记录到Excel文件。

打印对话

1
def print_dialog(question, answer, max_width=80):
  • 在命令行窗口美观地打印问题和答案。

记录错误日志

1
def log_error(log_file, message, error=None):
  • 记录错误信息到日志文件中。

处理单个文件

1
def process_file(file_path, log_file):
  • 读取文本文件,处理每一行文本,并调用OpenAI的API进行文本修改。

处理整个文件夹

1
def process_folder(folder_path):
  • 遍历指定文件夹中的所有文本文件,并调用process_file进行处理。

使用说明

  1. 确保安装了必要的库:pip install openai openpyxl
  2. 填写OpenAI API密钥。
  3. 修改文件夹路径以处理文本文件。
  4. 运行脚本,处理记录将保存到Excel文件中,错误日志将保存在文件夹中。

注意事项

  • 确保OpenAI API服务可用。
  • 检查文件夹路径和文件权限。

运用FishVideo大模型将文本信息转变为对应的语音

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import os
import logging
from fish_audio_sdk import Session, TTSRequest, ReferenceAudio

# 配置日志记录异常
logging.basicConfig(
filename='tts_errors.log',
level=logging.ERROR,
format='%(asctime)s - %(levelname)s - %(message)s'
)

def process_directory(target_dir: str):
"""处理指定目录下的文本文件

Args:
target_dir: 包含文本文件和生成音频文件的目录
"""
session = Session("")

processed = 0
skipped = 0
errors = 0

for filename in os.listdir(target_dir):
if not filename.endswith(".txt"):
continue

file_path = os.path.join(target_dir, filename)
base_name = os.path.splitext(filename)[0]
audio_path = os.path.join(target_dir, f"{base_name}.mp3")

try:
# 读取文本内容
with open(file_path, "r", encoding="utf-8") as f:
text = f.read().strip()

# 跳过空文件
if not text:
logging.info(f"跳过空文件: {filename}")
skipped += 1
continue

# 生成语音文件
with open(audio_path, "wb") as f:
for chunk in session.tts(TTSRequest(
reference_id="7f92f8afb8ec43bf81429cc1c9199cb1",
text=text
)):
f.write(chunk)

print(f"生成成功: {filename} -> {os.path.basename(audio_path)}")
processed += 1

except Exception as e:
logging.error(f"文件 {filename} 处理失败: {str(e)}", exc_info=True)
print(f"错误: {filename} 转换失败,详见日志")
errors += 1

# 输出统计报告
print(f"\n处理完成: 成功 {processed} 个 | 跳过 {skipped} 个 | 失败 {errors} 个")

if __name__ == "__main__":
# 使用示例:处理当前目录下的文件
process_directory(target_dir="D:\\BaiduNetdiskDownload\\001-050_images")

代码解析

该代码的主要功能是处理指定目录下的文本文件,利用fish_audio_sdk库将文本转换为语音,并生成相应的音频文件。以下是代码的详细分析:

导入库

1
2
3
import os
import logging
from fish_audio_sdk import Session, TTSRequest, ReferenceAudio
  • os: 用于文件和目录操作。
  • logging: 用于记录日志,方便后续的错误追踪和调试。
  • fish_audio_sdk: 语音合成SDK,主要用于将文本转换为语音。

配置日志

1
2
3
4
5
logging.basicConfig(
filename='tts_errors.log',
level=logging.ERROR,
format='%(asctime)s - %(levelname)s - %(message)s'
)
  • 配置日志记录,日志将被写入tts_errors.log文件,记录错误信息及时间戳。

处理目录函数

1
def process_directory(target_dir: str):
  • 该函数接受一个参数target_dir,表示包含文本文件和生成音频文件的目录。
初始化变量
1
2
3
4
session = Session("")
processed = 0
skipped = 0
errors = 0
  • 创建一个Session对象以进行语音合成。
  • 初始化计数器:processed用于记录成功处理的文件数量,skipped用于记录跳过的文件数量,errors用于记录处理失败的文件数量。
遍历文件
1
2
3
for filename in os.listdir(target_dir):
if not filename.endswith(".txt"):
continue
  • 遍历指定目录下的文件,筛选出扩展名为.txt的文件。
处理每个文件
1
2
3
file_path = os.path.join(target_dir, filename)
base_name = os.path.splitext(filename)[0]
audio_path = os.path.join(target_dir, f"{base_name}.mp3")
  • 拼接文件路径和音频文件的保存路径。
读取文本内容
1
2
with open(file_path, "r", encoding="utf-8") as f:
text = f.read().strip()
  • 读取文本文件内容,并去除首尾空格。
跳过空文件
1
2
3
4
if not text:
logging.info(f"跳过空文件: {filename}")
skipped += 1
continue
  • 如果文件内容为空,则记录日志并跳过处理。
生成语音文件
1
2
3
4
5
6
with open(audio_path, "wb") as f:
for chunk in session.tts(TTSRequest(
reference_id="7f92f8afb8ec43bf81429cc1c9199cb1",
text=text
)):
f.write(chunk)
  • 调用session.tts生成语音文件并写入到指定的路径。TTSRequest中包含了文本和一个参考ID。
成功处理
1
2
print(f"生成成功: {filename} -> {os.path.basename(audio_path)}")
processed += 1
  • 打印生成成功的消息,并增加成功处理计数。
错误处理
1
2
3
4
except Exception as e:
logging.error(f"文件 {filename} 处理失败: {str(e)}", exc_info=True)
print(f"错误: {filename} 转换失败,详见日志")
errors += 1
  • 捕获异常,记录错误并增加失败计数。
输出统计报告
1
print(f"\n处理完成: 成功 {processed} 个 | 跳过 {skipped} 个 | 失败 {errors} 个")
  • 在处理完成后输出统计报告。

主程序

1
2
if __name__ == "__main__":
process_directory(target_dir="D:\\BaiduNetdiskDownload\\001-050_images")
  • 主程序入口,调用process_directory处理指定目录下的文本文件。

Markdown 文件内容

1
2
3
4
5
6
7
8
9
10
11
12
# 文本转语音处理脚本

## 概述
该脚本用于处理指定目录下的文本文件,通过`fish_audio_sdk`将文本转换为语音,并生成对应的音频文件。处理过程中会记录成功、跳过和失败的文件数量,并将错误日志保存到文件中。

## 代码解析

### 导入库
```python
import os
import logging
from fish_audio_sdk import Session, TTSRequest, ReferenceAudio

配置日志

1
2
3
4
5
logging.basicConfig(
filename='tts_errors.log',
level=logging.ERROR,
format='%(asctime)s - %(levelname)s - %(message)s'
)
  • 配置日志记录,记录错误信息到tts_errors.log文件中。

处理目录函数

1
def process_directory(target_dir: str):
  • 处理指定目录下的文本文件,并生成语音文件。

初始化变量

1
2
3
4
session = Session("")
processed = 0
skipped = 0
errors = 0

遍历文件

1
2
3
for filename in os.listdir(target_dir):
if not filename.endswith(".txt"):
continue
  • 只处理扩展名为.txt的文件。

处理每个文件

1
2
3
file_path = os.path.join(target_dir, filename)
base_name = os.path.splitext(filename)[0]
audio_path = os.path.join(target_dir, f"{base_name}.mp3")
  • 生成文本文件的路径和对应的音频文件路径。

读取文本内容

1
2
with open(file_path, "r", encoding="utf-8") as f:
text = f.read().strip()

跳过空文件

1
2
3
4
if not text:
logging.info(f"跳过空文件: {filename}")
skipped += 1
continue

生成语音文件

1
2
3
4
5
6
with open(audio_path, "wb") as f:
for chunk in session.tts(TTSRequest(
reference_id="7f92f8afb8ec43bf81429cc1c9199cb1",
text=text
)):
f.write(chunk)

输出统计报告

1
print(f"\n处理完成: 成功 {processed} 个 | 跳过 {skipped} 个 | 失败 {errors} 个")

使用说明

  1. 确保已安装fish_audio_sdk库。
  2. 将要处理的文本文件放入指定目录。
  3. 运行脚本,生成的音频文件将与文本文件保存在同一目录中。

注意事项

  • 确保fish_audio_sdk的配置正确,API密钥和参考ID有效。
  • 检查文件权限,确保可以读取文本文件和写入音频文件。

图片转视频处理将图片与语音结合并添加转场

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
import os
import glob
import subprocess

# 支持的图片和音频扩展名
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
AUDIO_EXTENSIONS = ['.mp3', '.wav', '.ogg']

def find_audio_file(image_path):
"""查找与图片同名的音频文件"""
base_name = os.path.splitext(image_path)[0]
for ext in AUDIO_EXTENSIONS:
audio_path = f"{base_name}{ext}"
if os.path.exists(audio_path):
return audio_path
return None

def create_video(image_path, output_folder):
"""使用 FFmpeg 创建视频"""
base_name = os.path.splitext(os.path.basename(image_path))[0]
output_path = os.path.join(output_folder, f"{base_name}.mp4")

cmd = [
'ffmpeg',
'-y',
'-loop', '1',
'-i', image_path,
]

if audio_path := find_audio_file(image_path):
cmd += ['-i', audio_path]
else:
cmd += [
'-f', 'lavfi',
'-i', 'anullsrc=cl=stereo:r=44100',
'-t', '3'
]

cmd += [
'-vf',
'scale=trunc(iw\2)*2:trunc(ih\2)*2,fps=24,format=yuv420p', # 关键修改点
'-c:v', 'libx264',
'-c:a', 'aac',
'-shortest',
'-movflags', '+faststart',
output_path
]

try:
subprocess.run(cmd, check=True, capture_output=True, text=True)
return True
except subprocess.CalledProcessError as e:
print(f"FFmpeg 错误: {e.stderr}")
return False

def process_folder(folder_path):
"""处理整个文件夹"""
# 收集所有图片文件
image_files = []
for ext in IMAGE_EXTENSIONS:
image_files.extend(glob.glob(os.path.join(folder_path, '*' + ext)))

# 创建输出目录
output_folder = os.path.join(folder_path, "output_videos")
os.makedirs(output_folder, exist_ok=True)

# 处理每个图片文件
for img_file in image_files:
print(f"正在处理: {os.path.basename(img_file)}")
if create_video(img_file, output_folder):
print(f"成功创建: {os.path.basename(img_file)}.mp4")
else:
print(f"处理失败: {os.path.basename(img_file)}")
print("-" * 50)

if __name__ == "__main__":
target_folder = "D:\\BaiduNetdiskDownload\\001-050_images"

if not os.path.exists(target_folder):
print(f"错误: 目录不存在 - {target_folder}")
else:
process_folder(target_folder)
print("全部处理完成!")

代码解析

该代码的主要功能是使用FFmpeg将指定文件夹中的图片转换为视频,并在视频中添加与图片同名的音频文件(如果存在)。以下是代码的详细分析:

导入库

1
2
3
import os
import glob
import subprocess
  • os: 用于文件和目录操作。
  • glob: 用于查找符合特定规则的文件路径名。
  • subprocess: 用于执行外部命令,这里用来调用FFmpeg。

支持的文件扩展名

1
2
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
AUDIO_EXTENSIONS = ['.mp3', '.wav', '.ogg']
  • 定义支持的图像和音频文件扩展名。

查找音频文件

1
2
3
4
5
6
7
8
def find_audio_file(image_path):
"""查找与图片同名的音频文件"""
base_name = os.path.splitext(image_path)[0]
for ext in AUDIO_EXTENSIONS:
audio_path = f"{base_name}{ext}"
if os.path.exists(audio_path):
return audio_path
return None
  • 该函数接收图片路径,查找是否存在与之同名的音频文件,并返回找到的音频文件路径,如果未找到则返回None

创建视频

1
2
3
4
def create_video(image_path, output_folder):
"""使用 FFmpeg 创建视频"""
base_name = os.path.splitext(os.path.basename(image_path))[0]
output_path = os.path.join(output_folder, f"{base_name}.mp4")
  • 函数负责使用FFmpeg将图片转换为视频。
构建FFmpeg命令
1
2
3
4
5
6
cmd = [
'ffmpeg',
'-y',
'-loop', '1',
'-i', image_path,
]
  • 使用-loop 1选项使图片循环显示。
添加音频
1
2
3
4
5
6
7
8
if audio_path := find_audio_file(image_path):
cmd += ['-i', audio_path]
else:
cmd += [
'-f', 'lavfi',
'-i', 'anullsrc=cl=stereo:r=44100',
'-t', '3'
]
  • 如果找到与图片同名的音频文件,则将其添加到FFmpeg命令中;否则,使用虚拟音频源(无声)生成长度为3秒的音频。
设定视频输出参数
1
2
3
4
5
6
7
8
9
cmd += [
'-vf',
'scale=trunc(iw\2)*2:trunc(ih\2)*2,fps=24,format=yuv420p',
'-c:v', 'libx264',
'-c:a', 'aac',
'-shortest',
'-movflags', '+faststart',
output_path
]
  • 设置视频过滤器,确保宽高是偶数,并设置每秒帧数(fps)为24。视频编码器为libx264,音频编码器为aac
运行FFmpeg命令
1
2
3
4
5
6
try:
subprocess.run(cmd, check=True, capture_output=True, text=True)
return True
except subprocess.CalledProcessError as e:
print(f"FFmpeg 错误: {e.stderr}")
return False
  • 执行命令并处理可能出现的错误。

处理文件夹

1
2
def process_folder(folder_path):
"""处理整个文件夹"""
  • 该函数遍历指定目录,处理所有支持的图片文件。
收集图片文件
1
2
3
image_files = []
for ext in IMAGE_EXTENSIONS:
image_files.extend(glob.glob(os.path.join(folder_path, '*' + ext)))
  • 使用glob查找所有支持的图片文件。
创建输出目录
1
2
output_folder = os.path.join(folder_path, "output_videos")
os.makedirs(output_folder, exist_ok=True)
  • 创建一个名为output_videos的文件夹,用于保存生成的视频文件。
处理每个图片文件
1
2
3
4
5
6
7
for img_file in image_files:
print(f"正在处理: {os.path.basename(img_file)}")
if create_video(img_file, output_folder):
print(f"成功创建: {os.path.basename(img_file)}.mp4")
else:
print(f"处理失败: {os.path.basename(img_file)}")
print("-" * 50)
  • 对每个图片文件调用create_video函数,并报告处理结果。

主程序

1
2
3
4
5
6
7
8
if __name__ == "__main__":
target_folder = "D:\\BaiduNetdiskDownload\\001-050_images"

if not os.path.exists(target_folder):
print(f"错误: 目录不存在 - {target_folder}")
else:
process_folder(target_folder)
print("全部处理完成!")
  • 主程序入口,检查目标文件夹是否存在,并调用process_folder处理图像。

Markdown 文件内容

1
2
3
4
5
6
7
8
9
10
11
12
# 图片转视频处理脚本

## 概述
该脚本用于将指定文件夹中的图片文件转换为视频,并在视频中添加与图片同名的音频文件(如果存在)。生成的视频文件将保存到一个单独的输出目录中。

## 代码解析

### 导入库
```python
import os
import glob
import subprocess

支持的文件扩展名

1
2
IMAGE_EXTENSIONS = ['.jpg', '.jpeg', '.png', '.bmp']
AUDIO_EXTENSIONS = ['.mp3', '.wav', '.ogg']

查找音频文件

1
def find_audio_file(image_path):
  • 查找与图片同名的音频文件。

创建视频

1
def create_video(image_path, output_folder):
  • 使用FFmpeg将图片转换为视频。

构建FFmpeg命令

1
2
3
4
5
6
cmd = [
'ffmpeg',
'-y',
'-loop', '1',
'-i', image_path,
]

添加音频

1
2
3
4
5
6
7
8
if audio_path := find_audio_file(image_path):
cmd += ['-i', audio_path]
else:
cmd += [
'-f', 'lavfi',
'-i', 'anullsrc=cl=stereo:r=44100',
'-t', '3'
]

设置视频输出参数

1
2
3
4
5
6
7
8
9
cmd += [
'-vf',
'scale=trunc(iw\2)*2:trunc(ih\2)*2,fps=24,format=yuv420p',
'-c:v', 'libx264',
'-c:a', 'aac',
'-shortest',
'-movflags', '+faststart',
output_path
]

处理文件夹

1
def process_folder(folder_path):
  • 处理指定目录中的所有图片文件,并生成视频。

主程序

1
if __name__ == "__main__":
  • 检查目标目录并执行处理。

使用说明

  1. 确保已安装FFmpeg并将其路径添加到系统环境变量。
  2. 将要处理的图片文件放入指定目录。
  3. 运行脚本,生成的视频文件将保存到output_videos子目录中。

注意事项

  • 确保文件夹路径和文件权限正确。
  • 处理时请检查FFmpeg的输出信息以获取错误提示。

合并视频段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import os
import subprocess

def merge_videos(folder_path, output_file):
# 获取所有视频文件
video_files = [f for f in os.listdir(folder_path) if f.endswith(('.mp4', '.mkv', '.avi', '.mov'))]
video_files.sort() # 根据名称排序,确保顺序正确

# 生成 FFmpeg 需要的输入列表文件
list_file = os.path.join(folder_path, 'file_list.txt')
with open(list_file, 'w', encoding='utf-8') as f:
for video in video_files:
f.write(f"file '{os.path.join(folder_path, video)}'\n")

# FFmpeg 合并视频
ffmpeg_cmd = f'ffmpeg -f concat -safe 0 -i "{list_file}" -c copy "{output_file}"'
subprocess.run(ffmpeg_cmd, shell=True, check=True)

# 清理文件
os.remove(list_file)
print(f"合并完成:{output_file}")

# 使用示例
merge_videos("D:\\BaiduNetdiskDownload\\001-050_images\\output_videos", "output.mp4")

下面是对您提供的Python代码的详细解析,以及生成的Markdown文件内容。

代码解析

导入库

1
2
import os
import subprocess
  • os: 用于文件和目录操作。
  • subprocess: 用于执行外部命令,这里用来调用FFmpeg。

合并视频函数

1
def merge_videos(folder_path, output_file):
  • 该函数接受两个参数:一个是包含视频文件的文件夹路径,另一个是合并后输出视频文件的名称。
获取视频文件
1
2
video_files = [f for f in os.listdir(folder_path) if f.endswith(('.mp4', '.mkv', '.avi', '.mov'))]
video_files.sort() # 根据名称排序,确保顺序正确
  • 获取指定文件夹中的所有视频文件,筛选出扩展名为.mp4.mkv.avi.mov的视频文件,并将其按名称排序,以确保合并顺序正确。
生成FFmpeg输入列表文件
1
2
3
4
list_file = os.path.join(folder_path, 'file_list.txt')
with open(list_file, 'w', encoding='utf-8') as f:
for video in video_files:
f.write(f"file '{os.path.join(folder_path, video)}'\n")
  • 创建一个名为file_list.txt的文本文件,FFmpeg将使用这个文件来读取需要合并的视频文件路径。每行包含一个视频文件的路径。
执行FFmpeg命令
1
2
ffmpeg_cmd = f'ffmpeg -f concat -safe 0 -i "{list_file}" -c copy "{output_file}"'
subprocess.run(ffmpeg_cmd, shell=True, check=True)
  • 构建FFmpeg命令,使用-f concat选项来合并文件,并使用-c copy选项以快速地复制视频流而不进行重新编码。通过subprocess.run执行命令。
清理临时文件
1
2
os.remove(list_file)
print(f"合并完成:{output_file}")
  • 合并完成后删除临时的file_list.txt文件,并打印合并完成的消息。

使用示例

1
merge_videos("D:\\BaiduNetdiskDownload\\001-050_images\\output_videos", "output.mp4")
  • 调用merge_videos函数,传入包含视频文件的文件夹路径和输出文件名。

Markdown 文件内容

1
2
3
4
5
6
7
8
9
10
11
# 视频合并脚本

## 概述
该脚本用于将指定文件夹中的多个视频文件合并为一个视频,使用FFmpeg工具来完成合并操作。

## 代码解析

### 导入库
```python
import os
import subprocess
  • os: 用于文件和目录操作。
  • subprocess: 用于执行外部命令,这里用来调用FFmpeg。

合并视频函数

1
def merge_videos(folder_path, output_file):
  • 该函数接受两个参数:包含视频文件的文件夹路径和合并后输出视频文件的名称。

获取视频文件

1
2
video_files = [f for f in os.listdir(folder_path) if f.endswith(('.mp4', '.mkv', '.avi', '.mov'))]
video_files.sort() # 根据名称排序,确保顺序正确

生成FFmpeg输入列表文件

1
2
3
4
list_file = os.path.join(folder_path, 'file_list.txt')
with open(list_file, 'w', encoding='utf-8') as f:
for video in video_files:
f.write(f"file '{os.path.join(folder_path, video)}'\n")

执行FFmpeg命令

1
2
ffmpeg_cmd = f'ffmpeg -f concat -safe 0 -i "{list_file}" -c copy "{output_file}"'
subprocess.run(ffmpeg_cmd, shell=True, check=True)

清理临时文件

1
2
os.remove(list_file)
print(f"合并完成:{output_file}")

使用说明

  1. 确保已安装FFmpeg并将其路径添加到系统环境变量。
  2. 将要合并的视频文件放入指定文件夹。
  3. 运行脚本,将多个视频合并为一个视频文件。

注意事项

  • 确保所有视频文件格式相同,以避免合并时出现问题。
  • 处理时请检查FFmpeg的输出信息以获取可能的错误提示。

仓库代码
AIDrow

alt text

本仓库为女巫模组的汉化版本,原模组仓库地址:https:\github.com\gygrazok\witchmod

女巫(The Witch)

杀戮尖塔(Slay The Spire) 的全新角色模组

女巫模组为游戏增添了超过 75 张新卡牌和 4 个全新遗物,围绕 诅咒(Curses) 展开独特的玩法体验。她的起始遗物 黑猫(Black Cat) 每场战斗开始时会在抽牌堆中加入一张临时随机诅咒牌,但每当你抽到诅咒牌时可获得 1 点额外能量。这一机制打破了诅咒牌的传统负面印象,令其在女巫的卡组中可能成为战略优势。

与铁卫(Ironclad)或寂静猎手(Silent)相比,女巫在游戏初期可能略显脆弱,玩法策略需要一定的探索和练习。希望玩家们能够分享宝贵的意见和建议,以帮助进一步平衡和优化她的游戏体验。

卡牌详细列表请参见 此处

核心机制

  • 循环(Recurrent):打出后不会进入弃牌堆,而是直接洗回抽牌堆,增强卡组的循环效率。
  • 净化(Cleanse):部分特殊诅咒牌可在战斗中完成特定目标后被“净化”,解锁其隐藏效果,但该效果仅限于当前战斗。

新增状态效果

  • 腐烂(Rot):类似中毒,每回合对敌方造成伤害,且与中毒不同,腐烂会在每回合自动 增加 1 点。
  • 衰弱(Decrepit):使受影响生物承受额外伤害,额外伤害数值取决于衰弱层数,每回合减少 1 层。

安装指南

本模组基于 FruityMod-StS 的安装指南整理,简明易懂,便于快速上手。

前置要求

  • Java 8(JRE)— 仅支持 Java 8,Java 9 兼容性问题尚在处理。
  • BaseMod v3.3.0+(下载地址
  • ModTheSpire v3.2.0+(下载地址

安装步骤

  1. 如果已安装 ModTheSpire,可跳至步骤 5。
  2. 此处 下载 ModTheSpire.jar
  3. ModTheSpire.jar 移动至 杀戮尖塔 目录,路径示例:
    C:\Program Files (x86)\Steam\steamapps\common\SlayTheSpire
  4. 在游戏目录中创建 mods 文件夹:
    C:\Program Files (x86)\Steam\steamapps\common\SlayTheSpire\mods
  5. 此处 下载 BaseMod.jar
  6. BaseMod.jar 放入 mods 文件夹
  7. 此处 下载 WitchMod.jar
  8. WitchMod.jar 同样放入 mods 文件夹
  9. 双击 ModTheSpire.jar 启动游戏
  10. 在模组选择界面勾选 BaseModWitchMod,点击 Play 开始游戏

鸣谢

  • 感谢 杀戮尖塔 官方团队,为我们提供了出色的游戏和模组支持。
  • 感谢 BaseMod 的开发者 test447 及所有贡献者。
  • 感谢 ModTheSpire 的开发者 kiooeht 及贡献者。
  • 感谢所有提供 Bug 反馈、建议与平衡调整意见的玩家社区成员!

Github地址

Github
alt text

PS制作手机壁纸和电脑壁纸

1. 思绪来源

找到了一位B站UP,分享了有关于灰原哀的动态壁纸。自身( •̀ ω •́ )也是名侦探柯南的爱好者,在此基础上,萌生了制作壁纸的想法。便在B站上搜寻有关于壁纸制作的教学。找到了一位壁纸分享者的教程镜湾湾在此基础上学习些许

2. 前期准备

软件:
由于电脑是18年产物,故使用2018年版本的PS
素材:
个人是在pixiv中手动寻找素材,在github中有提供自动寻找的工具,但是个人使用不太理想。
自动化工具()自动化
部分素材如下:
alt text
alt text
alt text

3. 教学绘制过程

可去看对应的UP主视频

4. 成品如下

alt text
alt text
alt text
alt text
alt text
alt text
alt text
alt text
alt text

5.附带产物

帮忙做的高考志愿报名
alt text

自动化 Markdown 图片上传到 GitHub

1. 代码介绍

本代码用于自动扫描指定目录下的 Markdown 文章,提取其中的本地图片,并上传到 GitHub 仓库,最后替换文章中的图片路径,实现图片的托管和访问加速。

2. 主要功能

  • 读取本地配置文件,加载 GitHub Token
  • 扫描 Markdown 文章,查找本地图片
  • 判断图片是否已处理,避免重复上传
  • 将图片上传到 GitHub 指定仓库和分支
  • 更新 Markdown 文章中的图片链接
  • 记录处理过的文件哈希值,避免重复处理

3. 代码解析

3.1 配置文件管理

代码首先定义了 CONFIG_FILE 路径 (~\.image_upload_config.json),用于存储 GitHub Token。然后 load_github_token() 函数用于读取该文件,并获取 GITHUB_TOKEN

1
2
3
4
5
6
7
8
9
10
11
12
13
CONFIG_FILE = Path.home() \ ".image_upload_config.json"

def load_github_token():
try:
with open(CONFIG_FILE, 'r') as f:
config = json.load(f)
token = config.get("GITHUB_TOKEN")
if not token:
raise ValueError("配置文件中缺少GITHUB_TOKEN字段")
return token
except FileNotFoundError:
print(f"错误:配置文件 {CONFIG_FILE} 不存在。请创建该文件并包含GITHUB_TOKEN字段。")
exit(1)

3.2 全局配置项

定义了 CONFIG 字典,包含 GitHub 仓库信息、文章目录、允许的图片格式等。

1
2
3
4
5
6
7
8
9
CONFIG = {
"GITHUB_TOKEN": load_github_token(),
"REPO": "qingyun201908\qingyun201908.github.io",
"BRANCH": "images",
"POSTS_DIR": Path(r"D:\\2025Blog\\my-blog\\source\\_posts"),
"ALLOWED_EXTENSIONS": {'.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'},
"HASH_STORE": Path.home() \ ".image_upload_processed",
"LOCAL_IMAGES_DIR": None,
}

3.3 文件处理管理

3.3.1 FileProcessor

  • 负责初始化 GitHub 连接
  • 读取和存储已处理的文件哈希值
  • 遍历 Markdown 目录,逐个处理文章
1
2
3
4
5
class FileProcessor:
def __init__(self):
self.processed = self.load_processed()
self.repo = self.init_github()
print(f"成功连接仓库: {self.repo.full_name}\n")

3.3.2 计算文件哈希值

用于检查文章是否修改过,避免重复处理。

1
2
3
4
5
6
def calculate_hash(self, filepath):
hasher = hashlib.sha256()
with open(filepath, 'rb') as f:
while chunk := f.read(8192):
hasher.update(chunk)
return hasher.hexdigest()

3.4 文章处理

3.4.1 ArticleProcessor

  • 解析 Markdown 文件,查找图片链接
  • 处理本地图片(检查路径、上传到 GitHub)
  • 替换 Markdown 文章中的本地图片路径
1
2
3
4
5
def process_images(self):
matches = re.findall("!\[.*?\]\((.*?)\)", self.content)
print(f"发现 {len(matches)} 个图片引用")
for local_path in matches:
self.process_single_image(local_path)

3.4.2 上传图片到 GitHub

  • 先检查 GitHub 仓库中是否已存在相同文件
  • 如果文件已存在且内容一致,则直接复用 URL
  • 否则更新或创建新的图片文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
def upload_image(self, image_path):
image_name = image_path.name
remote_path = f"images\{self.article_folder}\{image_name}"
with open(image_path, 'rb') as f:
content = f.read()
try:
existing = self.manager.repo.get_contents(remote_path, CONFIG["BRANCH"])
if existing.decoded_content == content:
print(f"图片已存在,跳过上传: {remote_path}")
return f"{CONFIG['BASE_URL']}{remote_path}"
self.manager.repo.update_file(
path=remote_path,
message=f"Update image: {remote_path}",
content=content,
sha=existing.sha,
branch=CONFIG["BRANCH"]
)
except Exception:
self.manager.repo.create_file(
path=remote_path,
message=f"Add image: {remote_path}",
content=content,
branch=CONFIG["BRANCH"]
)
return f"{CONFIG['BASE_URL']}{remote_path}"

3.5 运行主程序

1
2
3
4
5
6
7
if __name__ == "__main__":
CONFIG["POSTS_DIR"] = Path(CONFIG["POSTS_DIR"])
CONFIG["LOCAL_IMAGES_DIR"] = CONFIG["POSTS_DIR"].parent \ "images"
CONFIG["BASE_URL"] = f"https:\\raw.githubusercontent.com\{CONFIG['REPO']}\{CONFIG['BRANCH']}\"

processor = FileProcessor()
processor.process_directory()

4. 运行方式

  1. ~\.image_upload_config.json 中配置 GitHub Token:
    1
    2
    3
    {
    "GITHUB_TOKEN": "your_github_personal_access_token"
    }
  2. 修改 CONFIG 变量,指定 GitHub 仓库和本地 Markdown 目录。
  3. 运行 Python 脚本:
    1
    python script.py

5. 总结

本代码通过解析 Markdown 文件,自动上传本地图片到 GitHub,并更新文章内容,使得图片可以通过 GitHub 进行托管,适用于 Hexo 或 Jekyll 博客的自动化部署。

alt text

路与己

张之维:脚在你身上长着,走不走,走哪条路,走什么样的路,做不做人,做什么样的人,亦是如此。

夏禾:太多时候,你我的模样是别人决定的。

张之维:是啊!想走的路不好走,想做人不好做。都说是身不由己,那不是废话嘛,己不由心,身又岂能由己。

写在生活之外的往事

一:为什么不想考研

没有能力,没有热情,很多时候都不知道自己想去做什么,只是浑浑噩噩的过日子。

二:对不起的人

于父母有愧,二老对我没太大要求,只是渴望我有更好的发展,不被人欺负,不受气

三:担心害怕的事情

害怕对不起父母,但是自己又不愿意去承担,假如亲近之人生病,我又该怎么办?没钱,没能力。

四:对待社会

不怎么喜欢、社会千奇百怪,光怪陆离,就像在地狱一样。

五:为什么写这些?

我没抑郁症,也没想不开,只是记录下,想着一些事情,就在这里留下!

六:想着依年为单位去完成一些东西

时间不等人,落叶于枫树飘零,但不知以年为单位的任务,是否能带来改变

0%