<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>幻梦の大图书馆</title><description>就用这里放一些笔记吧</description><link>https://www.yumehinata.com/</link><language>zh_CN</language><item><title>【笔记】记一次小米路由器固件解包</title><link>https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%B0%8F%E7%B1%B3%E8%B7%AF%E7%94%B1%E5%99%A8%E5%9B%BA%E4%BB%B6%E8%A7%A3%E5%8C%85/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E8%AE%B0%E4%B8%80%E6%AC%A1%E5%B0%8F%E7%B1%B3%E8%B7%AF%E7%94%B1%E5%99%A8%E5%9B%BA%E4%BB%B6%E8%A7%A3%E5%8C%85/</guid><description>当前的小米路由器固件是没有进行加密的，并且可以轻松的通过官网获取到（这一点比h3c不知道宽容到哪里去了。固件只能通过当时购买的店铺客服获取，这是哪个人才想出来的）。这次的解包以miwifi_rc01_firmware_5a1e1_1.1.56.bin为例</description><pubDate>Tue, 14 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;工具准备：&lt;/h2&gt;
&lt;p&gt;幻梦是在Debian13上完成的解包操作，以下的笔记仅针对Debian系统环境。&lt;/p&gt;
&lt;p&gt;这次解包的为 miwifi_rc01_firmware_5a1e1_1.1.56.bin&lt;/p&gt;
&lt;p&gt;安装 SquashFS 和 Python3&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo apt install squashfs-tools
sudo apt install python3 python3-pip pipx git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;安装 ubi_reader&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;pipx install git+https://github.com/jrspruitt/ubi_reader.git
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;下载ROM：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.miwifi.com/miwifi_download.html&quot;&gt;miwifi.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;可以下载到最新的 rom 文件。&lt;/p&gt;
&lt;h2&gt;解包流程：&lt;/h2&gt;
&lt;h3&gt;1.查看 UBI 信息&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ubireader_display_info miwifi_rc01_firmware_5a1e1_1.1.56.bin
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;UBI_File Warning: end_offset - start_offset length is not block aligned, could mean missing data.       
UBI File                                                                                                
---------------------                                                                                   
        Min I/O: 2048                                                                                   
        LEB Size: 126976                                                                                
        PEB Size: 131072                                                                                
        Total Block Count: 294                                                                          
        Data Block Count: 292                                                                           
        Layout Block Count: 2                                                                           
        Internal Volume Block Count: 0                                                                  
        Unknown Block Count: 0                                                                          
        First UBI PEB Number: 1                                                                         
                                                                                                        
        Image: 1955263782                                                                               
        ---------------------                                                                           
                Image Sequence Num: 1955263782                                                          
                Volume Name:kernel                                                                      
                Volume Name:ubi_rootfs                                                                  
                PEB Range: 2 - 293                                                                      
                                                                                                        
                Volume: kernel                                                                          
                ---------------------                                                                   
                        Vol ID: 0                                                                       
                        Name: kernel                                                                    
                        Block Count: 42                                                                 
                                                                                                        
                        Volume Record                                                                   
                        ---------------------                                                           
                                alignment: 1                                                            
                                crc: &apos;0xceb18f81&apos;                                                       
                                data_pad: 0                                                             
                                errors: &apos;&apos;                                                              
                                flags: 0                                                                
                                name: &apos;kernel&apos;                                                          
                                name_len: 6                                                             
                                padding: &apos;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&apos;
                                rec_index: 0                                                            
                                reserved_pebs: 83                                                       
                                upd_marker: 0                                                           
                                vol_type: &apos;static&apos;                                                      
                                                                                                        
                                                                                                        
                Volume: ubi_rootfs                                                                      
                ---------------------                                                                   
                        Vol ID: 1                                                                       
                        Name: ubi_rootfs                                                                
                        Block Count: 250                                                                
                                                                                                        
                        Volume Record                                                                   
                        ---------------------                                                           
                                alignment: 1                                                            
                                crc: &apos;0xc84aa00d&apos;                                                       
                                data_pad: 0                                                             
                                errors: &apos;&apos;                                                              
                                flags: 0                                                                
                                name: &apos;ubi_rootfs&apos;                                                      
                                name_len: 10                                                            
                                padding: &apos;\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00&apos;
                                rec_index: 1                                                            
                                reserved_pebs: 250                                                      
                                upd_marker: 0                                                           
                                vol_type: &apos;dynamic&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可以看到有&lt;code&gt;Volume Name: kernel&lt;/code&gt;与&lt;code&gt;Volume Name: ubi_rootfs&lt;/code&gt;确认是标准UBI&lt;/p&gt;
&lt;h3&gt;2.提取 UBI Volume&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;ubireader_extract_images miwifi_rc01_firmware_5a1e1_1.1.56.bin
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;UBI_File Warning: end_offset - start_offset length is not block aligned, could mean missing data.
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;可能会输出这样的警告，但是先不管他，&lt;code&gt;ls&lt;/code&gt;看一下是不是生成了&lt;code&gt;ubifs-root&lt;/code&gt;目录&lt;/p&gt;
&lt;p&gt;一般目录的路径是这样的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;ubifs-root/
└── miwifi_rc01_firmware_5a1e1_1.1.56.bin/
    ├── img-1955263782_vol-kernel.ubifs
    └── img-1955263782_vol-ubi_rootfs.ubifs
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;他这个&lt;code&gt;.bin&lt;/code&gt;结尾的真的是一个文件夹，是可以打开的。&lt;/p&gt;
&lt;h3&gt;3.确认 RootFS 类型&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;file ubifs-root/miwifi_rc01_firmware_5a1e1_1.1.56.bin/img-1955263782_vol-ubi_rootfs.ubifs
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;ubifs-root/miwifi_rc01_firmware_5a1e1_1.1.56.bin/img-1955263782_vol-ubi_rootfs.ubifs: 
Squashfs filesystem, little endian, 
version 4.0, 
xz compressed, 
31643602 bytes, 
5765 inodes, 
blocksize: 262144 bytes, 
created: Wed May 14 07:57:33 2025
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;说明这个 Volume 是一个完整的 SquashFS&lt;/p&gt;
&lt;h3&gt;4.解包 RootFS&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;unsquashfs \
-d rootfs \
ubifs-root/miwifi_rc01_firmware_5a1e1_1.1.56.bin/img-1955263782_vol-ubi_rootfs.ubifs
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;Parallel unsquashfs: Using 4 processors                                                                 
5433 inodes (5062 blocks) to write                                                                      
                                                                                                        
                                                                                                        
create_inode: could not create character device rootfs/dev/console, because you&apos;re not superuser!       
[==================================================================================| ] 10494/10495  99% 
                                                                                                        
created 4877 files                                                                                      
created 332 directories                                                                                 
created 555 symlinks                                                                                    
created 0 devices                                                                                       
created 0 fifos                                                                                         
created 0 sockets                                                                                       
created 0 hardlinks
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;当运行命令后输出以上内容，并且生成一个&lt;code&gt;rootfs&lt;/code&gt;目录，目录里就是我们提取到的文件了。&lt;/p&gt;
</content:encoded></item><item><title>【教程】从DecapCMS迁移到SveltiaCMS</title><link>https://www.yumehinata.com/posts/%E6%95%99%E7%A8%8B%E4%BB%8Edecapcms%E8%BF%81%E7%A7%BB%E5%88%B0sveltiacms/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E6%95%99%E7%A8%8B%E4%BB%8Edecapcms%E8%BF%81%E7%A7%BB%E5%88%B0sveltiacms/</guid><description>如果幻梦没有见过SveltiaCMS，那么大概能忍受DecapCMS。但是现在幻梦看见了，就再也忍受不了。</description><pubDate>Fri, 10 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;封面图：https://www.pixiv.net/artworks/113004958&lt;/p&gt;
&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;如Sveltia CMS简介里说的一样，Decap CMS大抵已经死了。难看的UI、缺少的功能、奇怪的Bug、无用的文档、聊胜于无的更新，看起来没有必要再待在这条千疮百孔的船上了。&lt;/p&gt;
&lt;p&gt;幸运的是Sveltia很好的兼容了Decap，所以几乎不需要修改之前的Decap配置内容，而且也不是非要用Sveltia的OAuth 客户端，这都大幅度降低了我们从Decap迁移的难度。&lt;/p&gt;
&lt;h2&gt;简单配置下文件&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://www.yumehinata.com/posts/%E5%9F%BA%E4%BA%8Eedgeone-pages%E9%83%A8%E7%BD%B2astro%E5%8D%9A%E5%AE%A2decapcms/#%E7%AC%AC%E4%BA%8C%E6%AD%A5%E9%83%A8%E7%BD%B2decapcms&quot;&gt;【笔记】基于Edgeone Pages部署Fuwari(Astro)博客+DecapCMS&lt;/a&gt;前文我们准备了&lt;code&gt;src/pages/admin.html&lt;/code&gt;这个Decap专用的admin页面，现在不需要了，就把它删掉。&lt;/p&gt;
&lt;p&gt;我们重新在&lt;code&gt;/public/admin/&lt;/code&gt;新建一个&lt;code&gt;index.html&lt;/code&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;

&amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot; /&amp;gt;
    &amp;lt;meta name=&quot;robots&quot; content=&quot;noindex&quot; /&amp;gt;
    &amp;lt;title&amp;gt;Sveltia CMS&amp;lt;/title&amp;gt;
&amp;lt;/head&amp;gt;

&amp;lt;body&amp;gt;
    &amp;lt;script src=&quot;https://fastly.jsdelivr.net/npm/@sveltia/cms/dist/sveltia-cms.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&amp;lt;/body&amp;gt;

&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Sveltia的config.yml配置文件如下&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;logo_url: Sveltia的logo位置

media_folder: &quot;/src/content/posts/images&quot; # 文件将被存储在仓库中的位置

collections:
    - name: &quot;terminal&quot; # 用于路由，例如，/admin/collections/blog
    label: &quot;paper&quot; # 在 UI 中使用
    folder: &quot;src/content/posts&quot; # 存储文档的文件夹路径
    create: true # 允许用户在此集合中创建新文档
    fields: # 每个文档的字段，通常在 front matter 中
            - { label: &quot;标题&quot;, name: &quot;title&quot;, widget: &quot;string&quot; }
            - { label: &quot;发布日期&quot;, name: &quot;published&quot;, widget: &quot;datetime&quot;, type: &quot;date&quot; }
            - { label: &quot;描述&quot;, name: &quot;description&quot;, widget: &quot;text&quot;, required: false }
            - { label: &quot;封面图片&quot;, name: &quot;image&quot;, widget: &quot;image&quot;, default: &quot;https://rdimg.yumehinata.com/random-wallpaper&quot;}
            - { label: &quot;标签&quot;, name: &quot;tags&quot;, widget: &quot;list&quot;, required: false }
            - { label: &quot;分类&quot;, name: &quot;category&quot;, widget: &quot;string&quot;, required: false }
            - { label: &quot;草稿状态&quot;, name: &quot;draft&quot;, widget: &quot;boolean&quot;, default: false }
            - { label: &quot;正文&quot;, name: &quot;body&quot;, widget: &quot;richtext&quot; }
    media_folder: &quot;/src/content/posts/images/{{slug}}&quot;
    public_folder: &quot;./images/{{slug}}&quot; # 上传媒体文件的 src 属性

i18n:
  structure: multiple_folders
  locales: [&quot;zh-cn&quot;, &quot;en&quot;]
  default_locale: &quot;zh-cn&quot;
backend:
  name: github
  repo: &quot;用户/仓库&quot;
  branch: &quot;main&quot;
  api_root: &quot;https://api.github.com&quot;
  site_domain: &quot;https://博客域名&quot;
  base_url: &quot;https://OAuth客户端域名&quot;
  auth_endpoint: &quot;auth&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;这里就要说Sveltia的优势了。之前的Decap教程提到过，Fuwari的特殊功能导致了我们在编辑器中必须写&lt;code&gt;./images&lt;/code&gt;这样的相对路径，但是&lt;code&gt;public_folder&lt;/code&gt;是不允许写这种相对路径的（Sveltia的全局里也是不允许写的），但是&lt;code&gt;collections:&lt;/code&gt;下的属性就不要紧了。所以我们现在全局里写一个&lt;code&gt;media_folder&lt;/code&gt;，再在&lt;code&gt;collections&lt;/code&gt;中补上&lt;code&gt;public_folder&lt;/code&gt;、&lt;code&gt;media_folder&lt;/code&gt;。&lt;strong&gt;当然实际需求请按自己的来&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;而且这还顺带解决了我们替换编辑器的需求，这样&lt;a href=&quot;https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0fuwari%E4%B8%BB%E9%A2%98%E4%B8%8B%E7%9A%84decapcms%E7%BC%96%E8%BE%91%E5%99%A8%E6%9B%BF%E6%8D%A2/&quot;&gt;【笔记】Fuwari主题下的DecapCMS编辑器替换&lt;/a&gt;也没有用了，Sveltia的编辑器可比Decap好用和美观多了。&lt;/p&gt;
&lt;p&gt;另外，我们之前的教程被html文件中加载的yml文件的格式搞得头疼，现在Sveltia就不用担心这种奇怪的问题了（不知道比Decap高到哪里去了）。&lt;/p&gt;
&lt;p&gt;下面的第三方 OAuth 客户端相关配置，还是可以参考之前的笔记内容。使用Edgeone Pages的大家还是可以用幻梦之前做好的客户端&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;YumeHinata/dacap-cms-edgeone-pages&quot;}&lt;/p&gt;
&lt;h2&gt;展示：&lt;/h2&gt;
&lt;p&gt;这篇新的文章就是在新的SveltiaCMS完成的编辑，体验非常良好。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/QQ20260710-210412.png&quot; alt=&quot;文章编辑页&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/QQ_1783688700435.png&quot; alt=&quot;登录页面&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/QQ20260710-210632.png&quot; alt=&quot;文章页，通过日期排序&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/QQ20260710-210757.png&quot; alt=&quot;媒体文件页&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/QQ20260710-210933.png&quot; alt=&quot;黑夜模式与小语种支持，但是目前只支持英语与日语&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/%E3%80%90%E6%95%99%E7%A8%8B%E3%80%91%E4%BB%8Edecapcms%E8%BF%81%E7%A7%BB%E5%88%B0sveltiacms/QQ20260710-220151.png&quot; alt=&quot;快捷键&quot; /&gt;&lt;/p&gt;
&lt;p&gt;比Decap美观太多了&lt;/p&gt;
</content:encoded></item><item><title>【笔记】解决Fuwari在EdgeonePages部署时无法正常使用Pagefind的问题</title><link>https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E8%A7%A3%E5%86%B3fuwari%E5%9C%A8edgeonepages%E9%83%A8%E7%BD%B2%E6%97%B6%E6%97%A0%E6%B3%95%E6%AD%A3%E5%B8%B8%E4%BD%BF%E7%94%A8pagefind%E7%9A%84%E9%97%AE%E9%A2%98/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E8%A7%A3%E5%86%B3fuwari%E5%9C%A8edgeonepages%E9%83%A8%E7%BD%B2%E6%97%B6%E6%97%A0%E6%B3%95%E6%AD%A3%E5%B8%B8%E4%BD%BF%E7%94%A8pagefind%E7%9A%84%E9%97%AE%E9%A2%98/</guid><description>这是只会在edgeone出现的特有输出路径问题，如果是pagefind不执行请查看https://github.com/saicaca/fuwari/pull/706</description><pubDate>Sat, 04 Jul 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;封面图：https://www.pixiv.net/artworks/146641763&lt;/p&gt;
&lt;h2&gt;序：&lt;/h2&gt;
&lt;p&gt;这次就不说前言了，因为根本没什么好注意的。其实fuwari博客的搜索功能从部署完一开始就是坏的，之前幻梦一直不高兴修（本来以为是作者没有把功能实现，想试试有没有更好的搜索方案的，结果发现是原来自带的功能坏了），查了下issues发现可能是pagefind的问题&lt;a href=&quot;https://github.com/saicaca/fuwari/issues/529&quot;&gt;https://github.com/saicaca/fuwari/issues/529&lt;/a&gt;。再F12看一下，果然pagefind.js加载是报错的。这个时候就发现&lt;a href=&quot;https://github.com/saicaca/fuwari/pull/706&quot;&gt;https://github.com/saicaca/fuwari/pull/706&lt;/a&gt; 提过修复的办法。试下来发现不对啊，怎么还是没有生成，赶紧看log……这log里不是有pagefind的输出吗？&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260704-134704.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;什么时候有的呢，查了下记录原来幻梦的命令根本就没错，其实从部署那天开始pagefind就是好的。那文件去哪了？之前的**&lt;a href=&quot;https://www.yumehinata.com/posts/%E6%95%99%E7%A8%8B%E5%88%A9%E7%94%A8ga4%E7%BB%9F%E8%AE%A1%E5%AE%9E%E7%8E%B0%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2%E9%98%85%E8%AF%BB%E7%BB%9F%E8%AE%A1/&quot;&gt;【教程】利用GA4统计实现静态博客阅读统计&lt;/a&gt;** 遇到过输出结果与可见文件不相符的问题，这次可能又是Edgeone的锅，于是就有了下面的这个修改。&lt;/p&gt;
&lt;h2&gt;订正：&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;astro build  &amp;amp;&amp;amp; pagefind --site dist&lt;/code&gt;改为&lt;code&gt;astro build  &amp;amp;&amp;amp; pagefind --site .edgeone/assets&lt;/code&gt;即可。&lt;/p&gt;
&lt;h2&gt;碎碎念：&lt;/h2&gt;
&lt;p&gt;修改方法极其简单，但是先确定你的pagefind是否正常的运行了，如果没有运行先看看上面&lt;strong&gt;序&lt;/strong&gt;里提到的两个issues，先让pagefind运行起来，后面再用这个针对EdgeonePages的解决方案（EdgeonePages截止发稿时，这个问题百分百能复现，任何插件只要是输出到&lt;code&gt;dist&lt;/code&gt;，又没有被Edgeone识别移动到&lt;code&gt;.edgeone/assets/&lt;/code&gt;那就一定会出问题）。&lt;/p&gt;
&lt;p&gt;&lt;s&gt;pagefind除了&lt;code&gt;--site dist&lt;/code&gt;这个参数外，还有&lt;code&gt;--output-path&lt;/code&gt;参数，能指定输出到对应目录。那么Edgeone能够正常被访问的目录不是&lt;code&gt;dist&lt;/code&gt;而是&lt;code&gt;.edgeone/assets/&lt;/code&gt;。所以我们只需要把原本的&lt;code&gt;pagefind --site dist&lt;/code&gt;修改为&lt;code&gt;pagefind --site dist --output-path .edgeone/assets/pagefind&lt;/code&gt;一切问题就都解决了。&lt;/s&gt;&lt;/p&gt;
&lt;p&gt;其实还没解决，这样改的跳转url会多出一个client（多亏了edgeone奇怪的设定），直接让pagefind扫描build完成的&lt;code&gt;.edgeone/assets/&lt;/code&gt;好了，这样连&lt;code&gt;--output-path&lt;/code&gt;也省了。&lt;/p&gt;
</content:encoded></item><item><title>【游戏】为卡拉彼丘特调的赛睿声呐EQ文件</title><link>https://www.yumehinata.com/posts/%E6%B8%B8%E6%88%8F%E4%B8%BA%E5%8D%A1%E6%8B%89%E5%BD%BC%E4%B8%98%E7%89%B9%E8%B0%83%E7%9A%84%E8%B5%9B%E7%9D%BF%E5%A3%B0%E5%91%90eq%E6%96%87%E4%BB%B6/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E6%B8%B8%E6%88%8F%E4%B8%BA%E5%8D%A1%E6%8B%89%E5%BD%BC%E4%B8%98%E7%89%B9%E8%B0%83%E7%9A%84%E8%B5%9B%E7%9D%BF%E5%A3%B0%E5%91%90eq%E6%96%87%E4%BB%B6/</guid><description>这个是幻梦自用的卡丘特调赛睿声呐配置，防止哪天失传了（卡丘不会死在文件失传前吧）</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;分享链接：&lt;/h2&gt;
&lt;p&gt;需要注意的是幻梦使用的耳机是赛睿Nova7p，不同品牌型号耳机可能存在默认eq不同的问题，所以这个配置不一定是最适合你的，只是提供一种可以有效放大弦化下飘飞、贴墙、走路，与正常走路脚步声的参考。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.steelseries.com/deeplink/gg/sonar/config/v1/import?url=aHR0cHM6Ly9jb21tdW5pdHktY29uZmlncy5zdGVlbHNlcmllc2Nkbi5jb20vdjEvZDg4NzVjNGEtZDE2ZC00NDA3LWI2N2QtMjZhOGQ0Yjg4MWZk&quot;&gt;https://www.steelseries.com/deeplink/gg/sonar/config/v1/import?url=aHR0cHM6Ly9jb21tdW5pdHktY29uZmlncy5zdGVlbHNlcmllc2Nkbi5jb20vdjEvZDg4NzVjNGEtZDE2ZC00NDA3LWI2N2QtMjZhOGQ0Yjg4MWZk&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个链接应该可以直接获取到配置，不过为了防止分享失效下面会给出详细的设置内容。&lt;/p&gt;
&lt;h2&gt;详细设置：&lt;/h2&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260620-230834.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/%E6%9C%AA%E6%A0%87%E9%A2%98-1.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;以上就是均衡器的详细参数。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260620-232959.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;开启空间音效-耳机模式，关闭音量增强与智能音量，距离拉满&lt;/p&gt;
</content:encoded></item><item><title>【笔记】为Edgeone Pages部署的astro博客添加indexnow插件</title><link>https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E4%B8%BAedgeone-pages%E9%83%A8%E7%BD%B2%E7%9A%84astro%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0indexnow%E6%8F%92%E4%BB%B6/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E4%B8%BAedgeone-pages%E9%83%A8%E7%BD%B2%E7%9A%84astro%E5%8D%9A%E5%AE%A2%E6%B7%BB%E5%8A%A0indexnow%E6%8F%92%E4%BB%B6/</guid><description>indexnow可以加快搜索引擎（bing和雅虎可以，google不行）收录页面的速度，astro目前有astro-indexnow插件可以在编译时像indexnow提交新增的页面。</description><pubDate>Sat, 20 Jun 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;封面图：https://www.pixiv.net/artworks/145910364&lt;/p&gt;
&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;indexnow每次最多提交10000个url，提交indexnow后的爬取也会消耗额度。因此astro-indexnow插件为我们提供了.astro-indexnow-cache.json缓存，避免重复提交indexnow。但是这个缓存文件在Tencent Edgeone pages上有点水土不服，幻梦的这篇笔记主要就解决了这个问题。&lt;/p&gt;
&lt;h2&gt;第一步：安装IndexNow&lt;/h2&gt;
&lt;p&gt;::github{repo=&quot;velohost/astro-indexnow&quot;}&lt;/p&gt;
&lt;p&gt;首先在astro博客本地的文件夹中安装astro-indexnow&lt;/p&gt;
&lt;p&gt;&lt;code&gt;npm install astro-indexnow&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;astro-indexnow相关配置会自动的写入package.json、package-lock.json中。&lt;/p&gt;
&lt;p&gt;插件安装后需要在&lt;code&gt;astro.config.mjs&lt;/code&gt;中进行配置，在配置前我们先前往&lt;a href=&quot;https://www.bing.com/indexnow/getstarted&quot;&gt;https://www.bing.com/indexnow/getstarted&lt;/a&gt; 获取API密钥和文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260620-150404.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;把密钥的文件放到静态博客的&lt;code&gt;/public&lt;/code&gt;目录下，并打开&lt;code&gt;astro.config.mjs&lt;/code&gt;在合适位置添加以下内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;import { defineConfig } from &quot;astro/config&quot;; //这个不用加，在这附近加入下面这一行
import indexnow from &quot;astro-indexnow&quot;;

export default defineConfig({  //这个和下面这个都不用添加，找到这个代码块
  site: &quot;https://example.com&quot;,  //这个不用额外加，一般不会存在没有配置站点域名的情况
  integrations: [  //还是不用加这个，找到integrations这个数组，添加下面indexnow这个函数
    indexnow({
      key: process.env.INDEXNOW_KEY,  //这里可以用环境变量，但是反正都要在public里放一个txt文件的，直接写明文也不要紧
      cacheDir: &quot;./public/&quot;,  //这里是.astro-indexnow-cache.json的目录，笔记默认按照这个位置不建议盲目修改。
      enabled: false, //indexnow插件可以通过这个参数开启，但是我们先不启用插件
    }),
  ],
});
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;第二步：为Edgeone Pages解决.astro-indexnow-cache.json持久化&lt;/h2&gt;
&lt;p&gt;indexnow提供的&lt;code&gt;.astro-indexnow-cache.json&lt;/code&gt;只有在Git(CI/CD)进行部署或者在本地进行部署时才能有效果（edgeone可没法持久保留这个文件，也没办法操作github仓库）。但是如果我们将&lt;code&gt;.astro-indexnow-cache.json&lt;/code&gt;文件放到public中，那么我们就能够通过url直接访问到&lt;code&gt;.astro-indexnow-cache.json&lt;/code&gt;，在推送发生后&lt;code&gt;astro build&lt;/code&gt;前我们可以再通过命令把&lt;code&gt;.astro-indexnow-cache.json&lt;/code&gt;下载到一个文件夹里，提供indexnow插件调用不就能够变相的完成持久化了吗（这里为了方便就直接下载到public目录下，因此前文的配置也是指向了public目录）。&lt;/p&gt;
&lt;p&gt;到这里看似解决了所有问题，但是还有点瑕疵。edgeone在实际编译页面时indexnow插件运行后的新&lt;code&gt;.astro-indexnow-cache.json&lt;/code&gt;不会被生成到&lt;code&gt;public&lt;/code&gt;目录里，通过一些方法最后幻梦知道是生成在了&lt;code&gt;.edgeone/assets/&lt;/code&gt;目录下，我们只需要再通过&lt;code&gt;cp ./public/.astro-indexnow-cache.json .edgeone/assets/&lt;/code&gt;把缓存文件替换即可完成闭环。&lt;/p&gt;
&lt;p&gt;因此我们的&lt;code&gt;package.json&lt;/code&gt;需要进一步修改。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &quot;scripts&quot;: {  //找到这个scripts，下面有一个build（这个不用新增）

    &quot;build&quot;: &quot;astro build &amp;amp;&amp;amp; pagefind --site dist&quot;,  //一般长这个模样，我们稍作修改。如下：(这一行的build记得删掉，只留下修改完成的)
    &quot;build&quot;: &quot;(curl -fs https://example.com/.astro-indexnow-cache.json -o ./public/.astro-indexnow-cache.json || echo &apos;{}&apos; &amp;gt; ./public/.astro-indexnow-cache.json) &amp;amp;&amp;amp; astro build &amp;amp;&amp;amp; cp ./public/.astro-indexnow-cache.json .edgeone/assets/ &amp;amp;&amp;amp; pagefind --site dist&quot;,
  },

&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;strong&gt;上方的域名改为你博客的域名&lt;/strong&gt;即可。&lt;/p&gt;
&lt;p&gt;完成这些后，我们先进行一次提交（需要注意的是，此时我们&lt;strong&gt;没有启用&lt;/strong&gt;indexnow插件，因为我们需要把密钥的txt文件先上传上去，保证能够正常访问到这个txt文件后再启用插件）&lt;/p&gt;
&lt;p&gt;当编译完成后，我们尝试访问&lt;code&gt;https://example.com/&amp;lt;YOUR_KEY&amp;gt;.txt&lt;/code&gt;检查是否能够正常打开。若能正常打开我们可以尝试运行以下命令测试自己的你的api能否正常使用。（以下命令在Windows powershell运行测试）&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12

$body = @{
    host        = &quot;你的博客域名，不要带https://这些&quot;
    key         = &quot;你获取到的密钥，确保和刚刚上传的文件一致&quot;
    keyLocation = &quot;https://example.com/你的密钥.txt ，你刚刚访问成功的密钥文本url&quot;
    urlList     = @(&quot;https://www.example.com/，提交验证的url，最好就是你的博客站主页url&quot;)
} | ConvertTo-Json -Compress

Invoke-RestMethod -Uri &quot;https://www.bing.com/indexnow&quot; -Method Post -ContentType &quot;application/json; charset=utf-8&quot; -Body $body
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;如果没有任何报错，就是一切正常，可以进入下一步，如果报错403，那就要重新生成一个密钥和密钥文件了（也有可能是爬虫被拦截了，但是这个概率很小），如果报错422那就是超量使用了，一样换一个密钥和文件。&lt;/p&gt;
&lt;p&gt;一切测试正常了，我们回到&lt;code&gt;astro.config.mjs&lt;/code&gt;，把刚刚的&lt;code&gt;enabled: false,&lt;/code&gt;改为&lt;code&gt;enabled: true,&lt;/code&gt;启用astro-indexnow。&lt;/p&gt;
&lt;h2&gt;检查indexnow的提交：&lt;/h2&gt;
&lt;p&gt;当提交成功后我们可以到&lt;a href=&quot;https://www.bing.com/webmasters&quot;&gt;https://www.bing.com/webmasters&lt;/a&gt; 登录绑定自己的网站，查看indexnow的提交记录。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260620-160504.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>【教程】利用GA4统计实现静态博客阅读统计</title><link>https://www.yumehinata.com/posts/%E6%95%99%E7%A8%8B%E5%88%A9%E7%94%A8ga4%E7%BB%9F%E8%AE%A1%E5%AE%9E%E7%8E%B0%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2%E9%98%85%E8%AF%BB%E7%BB%9F%E8%AE%A1/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E6%95%99%E7%A8%8B%E5%88%A9%E7%94%A8ga4%E7%BB%9F%E8%AE%A1%E5%AE%9E%E7%8E%B0%E9%9D%99%E6%80%81%E5%8D%9A%E5%AE%A2%E9%98%85%E8%AF%BB%E7%BB%9F%E8%AE%A1/</guid><description>静态博客很多人会选用umami来实现阅读量的统计，但是这就不够serverless了，也不方便将白嫖进行到底，所以就有了现在这个方案，当然这个方案也是有缺点的。</description><pubDate>Sun, 31 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;封面图：https://www.pixiv.net/artworks/145105485&lt;/p&gt;
&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;Umami无疑是最适合个人静态博客的自建统计系统了，但是你需要一个服务器来部署Umami。那么有没有一个不需要自己准备服务器就能使用的统计工具呢？有的，Google Analytics正是我们需要的。不过GA4的缺点就是&lt;strong&gt;可能会被用户过滤&lt;/strong&gt;，&lt;strong&gt;统计结果不够及时&lt;/strong&gt;，以及&lt;strong&gt;谷歌服务本身在当前环境下的不稳定性&lt;/strong&gt;。幸好幻梦是不在意这些问题的，如果要选择这个方案最好也衡量一下自己是否也不在意。&lt;/p&gt;
&lt;h2&gt;第一步：使用Analytics统计&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://marketingplatform.google.com/about/analytics/?hl=zh-CN&quot;&gt;https://marketingplatform.google.com/about/analytics/?hl=zh-CN&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在首页开始，注册一个Analytics账号。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-193722.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-193911.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-194053.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-194205.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点击创建，进入下一个环节，选择网站。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-194601.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;google会给你一串代码，我们放到需要统计的网页中&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-194833.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;完成后点击测试，当有数据时会是以下的显示&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-195315.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在设置中可以看到&lt;strong&gt;媒体资源ID&lt;/strong&gt;，记录下来&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-203119.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二步：调用Google Analytics Data API&lt;/h2&gt;
&lt;h3&gt;进入Google cloud console&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://console.cloud.google.com/&quot;&gt;https://console.cloud.google.com/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;搜索&lt;code&gt;Google Analytics Data API&lt;/code&gt;点击进入&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-200931.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-201219.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-201423.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这边的URI是&lt;code&gt;https://developers.google.com/oauthplayground&lt;/code&gt;&lt;strong&gt;一定要添加并填写这个&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;点击创建得到ID与密钥，&lt;strong&gt;做好保存&lt;/strong&gt;等会要用。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-201909.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;还是这个页面下，目标对象中创建一个测试用户，不然下面这一步验证不了&lt;/p&gt;
&lt;h3&gt;进入Google OAuth 2.0 Playground&lt;/h3&gt;
&lt;p&gt;&lt;a href=&quot;https://developers.google.com/oauthplayground/&quot;&gt;https://developers.google.com/oauthplayground/&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;在设置中勾选复选框，填入你的id与密钥&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-202341.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在右侧的输入框里填入&lt;code&gt;https://www.googleapis.com/auth/analytics.readonly&lt;/code&gt;点击 Authorize APIs，完成登录验证，将会跳转进入Step 2: Exchange authorization code for tokens&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-202651.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里点击进行兑换，我们把得到的&lt;strong&gt;Refresh token保留下来&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;第三步：使用github同步数据&lt;/h2&gt;
&lt;p&gt;::github{repo=&quot;YumeHinata/GA4-Click-Statistics&quot;}&lt;/p&gt;
&lt;p&gt;可以fork幻梦创建的这个仓库来完成获取GA4的数据并保存到仓库的&lt;code&gt;data/views.json&lt;/code&gt;中&lt;/p&gt;
&lt;p&gt;使用前需要在刚刚fork的仓库中设置一些内容&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260531-203910.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们需要新建变量&lt;code&gt;GA_CLIENT_ID&lt;/code&gt;、&lt;code&gt;GA_CLIENT_SECRET&lt;/code&gt;、&lt;code&gt;GA_PROPERTY_ID&lt;/code&gt;、&lt;code&gt;GA_REFRESH_TOKEN&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;填写完毕后进入action运行一次&lt;/p&gt;
&lt;p&gt;运行成功后就可以去前端调用了&lt;/p&gt;
&lt;h2&gt;杂项：Fuwari的适配&lt;/h2&gt;
&lt;p&gt;Fuwari首先就是文章卡片中需要显示阅读量&lt;/p&gt;
&lt;p&gt;在&lt;code&gt;src-components-PostCard.astro&lt;/code&gt;中&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;const hasCover = image !== undefined &amp;amp;&amp;amp; image !== null &amp;amp;&amp;amp; image !== &quot;&quot;;

const coverWidth = &quot;28%&quot;;

const { remarkPluginFrontmatter } = await entry.render();
//找到以上内容，加入下面这条
const cleanUrl = url.endsWith(&quot;/&quot;) &amp;amp;&amp;amp; url !== &quot;/&quot; ? url.slice(0, -1) : url; // 用于匹配 views.json 中的路径，确保末尾没有斜杠（除非是根路径）
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;div
  class=&quot;text-sm text-black/30 dark:text-white/30 flex gap-4 transition&quot;
&amp;gt;
    &amp;lt;div&amp;gt;
      {remarkPluginFrontmatter.words}
      {
        &quot; &quot; +
          i18n(
            remarkPluginFrontmatter.words === 1
              ? I18nKey.wordCount
              : I18nKey.wordsCount,
          )
      }
      &amp;lt;/div&amp;gt;
      &amp;lt;div&amp;gt;|&amp;lt;/div&amp;gt;
      &amp;lt;div&amp;gt;
      {remarkPluginFrontmatter.minutes}
      {
        &quot; &quot; +
          i18n(
            remarkPluginFrontmatter.minutes === 1
              ? I18nKey.minuteCount
              : I18nKey.minutesCount,
            )
      }
      &amp;lt;/div&amp;gt;
//找到以上内容加入以下的代码
      &amp;lt;div&amp;gt;|&amp;lt;/div&amp;gt;
      &amp;lt;div
        class=&quot;data-astro-cid-iyiqi2so postcard-view-count&quot;
        data-path={cleanUrl}
        style=&quot;--coverWidth: 28%;&quot;
      &amp;gt;
      &amp;lt;/div&amp;gt;
&amp;lt;/div&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;script&amp;gt;
    async function initCardViews() {
        const targets = document.querySelectorAll(&quot;.postcard-view-count&quot;);
        if (targets.length === 0) return;

        try {
            // 只请求 1 次原始简洁的 json 字典
            const res = await fetch(
                &quot;https://fastly.jsdelivr.net/gh/你的github账号/你刚刚建立的仓库@main/data/views.json&quot;,
            );//这里要改成你仓库的地址
            if (!res.ok) return;
            const viewsMap = await res.json();

            // 批量匹配并填入对应卡片的阅读量
            targets.forEach((el) =&amp;gt; {
                const path = el.getAttribute(&quot;data-path&quot;);
                if (path &amp;amp;&amp;amp; viewsMap[path] !== undefined) {
                    el.textContent = viewsMap[path] + &quot; 次阅读&quot;;
                } else {
                    el.textContent = &quot;0 次阅读&quot;;
                }
            });
        } catch (e) {
            console.error(&quot;Failed to fetch GA4 views:&quot;, e);
        }
    }

    // 页面首次加载时运行
    initCardViews();
    // 适配 Fuwari 主题的视图切换动画 (View Transitions)
    document.addEventListener(&quot;astro:after-swap&quot;, initCardViews);
&amp;lt;/script&amp;gt;
//在末尾的style标签以上添加以上代码
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;以上就基本完成了所有工作&lt;/p&gt;
</content:encoded></item><item><title>【笔记】干掉搜狗输入法的流氓组件</title><link>https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E5%B9%B2%E6%8E%89%E6%90%9C%E7%8B%97%E8%BE%93%E5%85%A5%E6%B3%95%E7%9A%84%E6%B5%81%E6%B0%93%E7%BB%84%E4%BB%B6/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E5%B9%B2%E6%8E%89%E6%90%9C%E7%8B%97%E8%BE%93%E5%85%A5%E6%B3%95%E7%9A%84%E6%B5%81%E6%B0%93%E7%BB%84%E4%BB%B6/</guid><description>搜狗输入法并非无可替代，百度、微信、讯飞能完全替代他的一抓一大把。但是搜狗输入法的词库和皮肤生态可能也使你舍不得放弃这个毒瘤，只能想办法删掉那些垃圾捏着鼻子继续用。</description><pubDate>Sat, 30 May 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;封面图:https://www.pixiv.net/artworks/145252557&lt;/p&gt;
&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;幻梦基本上是对搜狗输入法忍无可忍了，之前直接把搜狗整个卸掉换了微软输入法和小狼毫。后两者确实很干净、轻量，可是微软输入法的词库问题，小狼毫的美化问题（对于正常使用几乎无感）确实又影响日常的体验。还是得把搜狗输入法从垃圾桶里捞出来，不过得想办法清洗干净。&lt;/p&gt;
&lt;h2&gt;第一步：阻止搜狗输入法使用网络&lt;/h2&gt;
&lt;p&gt;安装完搜狗输入法，我们先安装一些需要联网的内容，例如：皮肤。然后让我们看看这个github项目&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;yongxin-ms/DisableSogouNetwork&quot;}&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/yongxin-ms/DisableSogouNetwork/archive/refs/heads/master.zip&quot;&gt;https://github.com/yongxin-ms/DisableSogouNetwork/archive/refs/heads/master.zip&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;这个项目是通过Windows自带的防火墙规则阻止搜狗输入法的入站与出站，下载这个压缩包后可根据readme的说明使用。&lt;/p&gt;
&lt;h2&gt;第二步：调整输入法设置&lt;/h2&gt;
&lt;p&gt;以下这些都是广告开关，通通点击关闭。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260530-184926.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260530-185213.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260530-185359.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第三步：删掉垃圾&lt;/h2&gt;
&lt;p&gt;搜狗输入法的安装路径&lt;code&gt;SogouInput\Components&lt;/code&gt;里面都是一些额外的组件，&lt;strong&gt;但是并不是每一个都需要删掉&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/qq20260530-190130.png&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们只需要&lt;strong&gt;保留以上的组件&lt;/strong&gt;即可，其他文件夹全部删除。遇到删除不掉的，进入Windows的安全启动尝试删除，如果还是删不掉可以尝试关闭进程后重命名，重启后再删除。&lt;/p&gt;
&lt;h2&gt;第四步：该死的壁纸功能&lt;/h2&gt;
&lt;p&gt;搜狗输入法的更换壁纸与电脑主题是通过appx注册的，所以通常手段删不掉。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Get-AppxPackage | Where-Object {$_.Name -like &quot;*wallpaper*&quot;} | Select Name, PackageFullName
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行以上命令查看wallpaper扩展是否存在，如果存在运行以下命令。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;Get-AppxPackage -AllUsers *WallpaperChangeMenu* | Remove-AppxPackage -AllUsers
&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;扩展阅读：&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://zhuanlan.zhihu.com/p/5569093059&quot;&gt;https://zhuanlan.zhihu.com/p/5569093059&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;http://opinion.people.com.cn/n1/2021/0616/c431649-32132196.html&quot;&gt;http://opinion.people.com.cn/n1/2021/0616/c431649-32132196.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://www.landian.news/archives/99829.html&quot;&gt;https://www.landian.news/archives/99829.html&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>【教程】WinPE环境下黑群晖引导硬盘制作</title><link>https://www.yumehinata.com/posts/%E6%95%99%E7%A8%8Bwinpe%E7%8E%AF%E5%A2%83%E4%B8%8B%E9%BB%91%E7%BE%A4%E6%99%96%E5%BC%95%E5%AF%BC%E7%A1%AC%E7%9B%98%E5%88%B6%E4%BD%9C/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E6%95%99%E7%A8%8Bwinpe%E7%8E%AF%E5%A2%83%E4%B8%8B%E9%BB%91%E7%BE%A4%E6%99%96%E5%BC%95%E5%AF%BC%E7%A1%AC%E7%9B%98%E5%88%B6%E4%BD%9C/</guid><description>通常黑群晖会选择U盘来作为引导盘，这样可以剩下一个硬盘位置。毕竟itx版型无论sata和pcie接口都是极其宝贵的，但是U盘总有缺陷（断头台、写入慢、容易坏）。所以也许有人也会需要一个在硬盘中安装引导的方法。</description><pubDate>Thu, 09 Apr 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言&lt;/h2&gt;
&lt;p&gt;通常黑群晖会选择U盘来作为引导盘，这样可以剩下一个硬盘位置。毕竟itx版型无论sata和pcie接口都是极其宝贵的，但是U盘总有缺陷（断头台、写入慢、容易坏）。所以也许有人会需要一个在硬盘中安装引导的方法。这篇内容比较水，主要也是留一个给幻梦自己看的笔记（部分PE系统过于精简，写入工具都很难选择使用）。&lt;/p&gt;
&lt;p&gt;这篇文章只有写入引导的步骤，引导的配置工作请查看RR引导的官网或移步其他内容（b站直接搜“RR引导”，例如这个：BV1ESkpBiEH5）&lt;/p&gt;
&lt;h2&gt;选择硬盘&lt;/h2&gt;
&lt;p&gt;本来不想提这个的，但是想了想还是写了。&lt;/p&gt;
&lt;p&gt;我们制作的这块引导盘，只有引导功能。不承担后续群晖系统的安装（群晖系统会被安装在你日后每一块使用的硬盘里，引导盘是不会被群晖系统使用到），所以推荐选一个小一点不容易坏的SSD硬盘，例如：傲腾M10（&lt;strong&gt;傲腾硬盘存在兼容性问题&lt;/strong&gt;，先看自己的主板和CPU是否兼容，这里仅做介绍）。&lt;/p&gt;
&lt;h2&gt;安装WinPE&lt;/h2&gt;
&lt;p&gt;想要给一块硬盘写入镜像首先得有一个系统和软件。如果你有硬盘坞可以跳过这一步，直接插入你需要写入镜像的硬盘然后写入RR引导的镜像文件。如果没有先找一个U盘或者移动硬盘来制作PE&lt;/p&gt;
&lt;p&gt;制作PE的第一步。&lt;strong&gt;确定你的U盘或者别的什么介质里没有你要的东西了，或者已经做好备份&lt;/strong&gt;。看清楚加粗内容，请务必确认，数据无价。&lt;/p&gt;
&lt;p&gt;我们可以选择的WinPE有很多，幻梦这边使用的是微PE，也就是Wepe。&lt;a href=&quot;https://www.wepe.com.cn/download.html&quot;&gt;https://www.wepe.com.cn/download.html&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;2.3与1.3版本幻梦的主机都可以运行，各位请按需选择。&lt;/p&gt;
&lt;p&gt;下载完PE工具箱后，双击启动。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E6%95%99%E7%A8%8B_winpe%E7%8E%AF%E5%A2%83%E4%B8%8B%E9%BB%91%E7%BE%A4%E6%99%96%E5%BC%95%E5%AF%BC%E7%A1%AC%E7%9B%98%E5%88%B6%E4%BD%9C/QQ20260409-093445.png&quot; alt=&quot;QQ20260409-093445.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们是在U盘制作WinPE，选择下面第一个U盘图案的图标。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E6%95%99%E7%A8%8B_winpe%E7%8E%AF%E5%A2%83%E4%B8%8B%E9%BB%91%E7%BE%A4%E6%99%96%E5%BC%95%E5%AF%BC%E7%A1%AC%E7%9B%98%E5%88%B6%E4%BD%9C/QQ20260409-093720.png&quot; alt=&quot;QQ20260409-093720.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选择你刚刚&lt;strong&gt;已经备份好或确认无需保留数据&lt;/strong&gt;的U盘，点击立刻安装。&lt;/p&gt;
&lt;p&gt;等待安装完成后进入下一步。&lt;/p&gt;
&lt;h2&gt;准备引导与写入工具&lt;/h2&gt;
&lt;p&gt;RR引导的发布地址：&lt;a href=&quot;https://github.com/RROrg/rr/releases&quot;&gt;https://github.com/RROrg/rr/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;RR引导的官网：&lt;a href=&quot;https://rrorg.cn/categories/evaluation&quot;&gt;https://rrorg.cn/categories/evaluation&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;写入工具 Roadkil&apos;s Disk Image：&lt;a href=&quot;https://roadkil.net/program.php?ProgramID=12&amp;amp;Action=NewOSID&amp;amp;DownloadVersion=12&amp;amp;Installer=NO&quot;&gt;https://roadkil.net/program.php?ProgramID=12&amp;amp;Action=NewOSID&amp;amp;DownloadVersion=12&amp;amp;Installer=NO&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;重点就是这个写入工具。其他的像是rufus，幻梦基本都试过了。PE上不是启动不了就是读不到盘，就这个好用了。&lt;/p&gt;
&lt;p&gt;下载好这些工具后，通通扔到刚刚制作好的PE U盘上，完成后安全弹出并拔出U盘。&lt;/p&gt;
&lt;h2&gt;写入引导&lt;/h2&gt;
&lt;p&gt;将拔出的U盘插到黑群晖主机上。设定好启动项，使开机U盘启动。&lt;/p&gt;
&lt;p&gt;进入WinPE。我们直接打开Roadkil&apos;s Disk Image。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E6%95%99%E7%A8%8B_winpe%E7%8E%AF%E5%A2%83%E4%B8%8B%E9%BB%91%E7%BE%A4%E6%99%96%E5%BC%95%E5%AF%BC%E7%A1%AC%E7%9B%98%E5%88%B6%E4%BD%9C/QQ20260409-095717.png&quot; alt=&quot;QQ20260409-095717.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们先选择正确的需要安装引导的硬盘位置，然后选择RR引导的img镜像文件（记得提前解压RR引导的压缩包，github发布页下载的一般是一个压缩包，img在压缩包里）&lt;/p&gt;
&lt;p&gt;选择完成后点击“Start”&lt;/p&gt;
&lt;p&gt;等待镜像写入完成后，关机拔出U盘，设置启动项为刚刚安装的引导盘。&lt;/p&gt;
&lt;p&gt;进入RR引导。&lt;/p&gt;
&lt;h2&gt;结束&lt;/h2&gt;
&lt;p&gt;到这一步引导盘的制作已经完成了，详细的配置请查阅其他资料，幻梦这边就不做更多的教程了。&lt;/p&gt;
</content:encoded></item><item><title>【笔记】在Ubuntu系统下进行安装黑苹果的准备工作，并安装黑苹果</title><link>https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/</guid><description>估计不会有几个人有这种需求的。哪有放着好好的linux不用，装黑苹果的。</description><pubDate>Sun, 01 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题记：&lt;/h2&gt;
&lt;p&gt;这大概是一种极其小众的需求吧，结果被幻梦遇上了。之前应该有提到过，幻梦的动态博客halo部署在一台nuc小主机上，总受周知nuc在这个时代成为软路由圣体之前其实是安装黑苹果的首选项，尤其是nuc5中的NUC5i5RYK。因为这台机器搭载了一个可能是除了苹果以外只有intel在自家主机上有使用过的5代i5处理器，5250u。现在动态博客不用了，小主机也就闲置下来了，于是在黑苹果生命的末期来体验一下，圆上幻梦一个折腾黑苹果的梦。&lt;/p&gt;
&lt;h2&gt;警告：&lt;/h2&gt;
&lt;p&gt;只是部分一定要在被安装的主机上进行的工作会在ubuntu上完成，剩下非必要环节放在Windows上完成（例如：烧录到U盘）与MacOS完成（例如：usb映射），另外这篇笔记里还存在一些未解决的问题，仅供参考。如果你在安装环节遇到一些问题，且项目的issues里没有提供解决方案的话，请善用Ai工具来完成检索和解答。此外目前由于5250u的核显为hd6000，因此Mac OS15应该是5250u最后支持的一个版本，但是我们不选15而是选择12作为体验。&lt;/p&gt;
&lt;h2&gt;安装Hardware-Sniffer&lt;/h2&gt;
&lt;p&gt;通常Hardware-Sniffer是OpCore-Simplify自带的一个模块，但是由于我们是linux环境所以得特意安装一个linux支持的。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 新建一个目录然后克隆项目
git clone https://github.com/lzhoang2801/Hardware-Sniffer.git

# 进入目录运行HardwareSniffer.py
python3 HardwareSniffer.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;进入后，选H输出Report.json&lt;/p&gt;
&lt;p&gt;回车返回，选A输出ACPI&lt;/p&gt;
&lt;p&gt;现在&lt;code&gt;Hardware-Sniffer&lt;/code&gt;文件夹下会有一个&lt;code&gt;SysReport&lt;/code&gt;，&lt;code&gt;SysReport&lt;/code&gt;下有一个&lt;code&gt;Report.json&lt;/code&gt;和&lt;code&gt;ACPI&lt;/code&gt;文件夹，把这两个复制到一回会克隆好的OpCore-Simplify文件夹里。&lt;/p&gt;
&lt;h2&gt;安装OpCore-Simplify&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;# 新建一个目录然后克隆
git clone https://github.com/lzhoang2801/OpCore-Simplify.git

# 把刚刚复制的json和文件夹丢到项目里
# 运行OpCore-Simplify.py
python3 OpCore-Simplify.py
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;选择1，输入&lt;code&gt;Report.json&lt;/code&gt;的路径。nuc5可能会出现以下报错&lt;/p&gt;
&lt;h3&gt;nuc5提取时遇到的问题及解决方法&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;Validation report for: /home/yume/opcore/OpCore-Simplify/Report.json

Hardware report is not valid! Please check the errors and warnings below.

Errors (7):
    1. Root.Monitor.ICD2400.Connector Type: Value &apos;HDMI-A&apos; does not match pattern &apos;^(VGA|DVI|HDMI|LVDS|DP|eDP|Internal|Uninitialized)$&apos;
    2. Root.Monitor.ICD2400: Missing required key &apos;Connector Type&apos;
    3. Root.System Devices.PNP0C0B.ACPI Path: Value &apos;\_TZ_.FAN3&apos; does not match pattern &apos;^[\\]?_SB(\.[A-Z0-9_]+)+$&apos;
    4. Root.System Devices.PNP0C0B_#1.ACPI Path: Value &apos;\_TZ_.FAN1&apos; does not match pattern &apos;^[\\]?_SB(\.[A-Z0-9_]+)+$&apos;
    5. Root.System Devices.PNP0C0B_#2.ACPI Path: Value &apos;\_TZ_.FAN4&apos; does not match pattern &apos;^[\\]?_SB(\.[A-Z0-9_]+)+$&apos;
    6. Root.System Devices.PNP0C0B_#3.ACPI Path: Value &apos;\_TZ_.FAN2&apos; does not match pattern &apos;^[\\]?_SB(\.[A-Z0-9_]+)+$&apos;
    7. Root.System Devices.PNP0C0B_#4.ACPI Path: Value &apos;\_TZ_.FAN0&apos; does not match pattern &apos;^[\\]?_SB(\.[A-Z0-9_]+)+$&apos;

Warnings (4):
    1. Root.BIOS: Unknown key &apos;Above 4G Decoding&apos;
    2. Root.GPU.Intel Corporation HD Graphics 6000: Unknown key &apos;Bus Type&apos;
    3. Root.Sound.Realtek ALC283: Unknown key &apos;PCI Path&apos;
    4. Root.Sound.Realtek ALC283: Unknown key &apos;ACPI Path&apos;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们根据提示进行修改即可，唯一需要关注的点是风扇的ACPI 路径。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 安装acpica-tool
sudo apt install acpica-tools -y

# 导出ACPI log
sudo acpidump &amp;gt; acpi.log

#提取dat
acpixtract -a acpi.log

# 反编译dsdt
iasl -d dsdt.dat
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们打开&lt;code&gt;dsdt.dsl&lt;/code&gt;文件，检索关键词&lt;code&gt;fan&lt;/code&gt;。会发现以下内容&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;If ((Arg0 == 0x03))
        {
            If ((Zero == ACTT))
            {
                If ((ECON == One))
                {
                    \_SB.PCI0.LPCB.H_EC.ECWT (Zero, RefOf (\_SB.PCI0.LPCB.H_EC.CFAN))
                }
            }
        }
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;\\_SB.PCI0.LPCB.H_EC&lt;/code&gt; 替换掉所有&lt;code&gt;\_TZ_.FAN&lt;/code&gt;相关内容，保存。&lt;/p&gt;
&lt;h3&gt;导入Report.json后&lt;/h3&gt;
&lt;p&gt;接着根据提示我们导入ACPI文件，之前我们已经把ACPI文件夹复制到了OpCore-Simplify里，可以直接输入&lt;code&gt;ACPI&lt;/code&gt;回车，下一步，如果没有导入的输入&lt;code&gt;ACPI&lt;/code&gt;文件夹的绝对路径。&lt;/p&gt;
&lt;h3&gt;导出EFI文件&lt;/h3&gt;
&lt;p&gt;前面根据自己需求选完选项后，会回到脚本主页（不清楚的选项就默认，MacOS的版本能最新就最新）。选择6导出EFI。&lt;/p&gt;
&lt;p&gt;EFI会被导出到&lt;code&gt;/tmp/&lt;/code&gt;路径下，这个时候我们不要点任何按钮进行下一步，立刻通过其他工具将EFI复制到其他稳定的路径里。如果不小心点击了下一步，恭喜你重新导入Report.json把选项重新选一遍把，临时文件夹关闭后即刻删除。&lt;/p&gt;
&lt;h2&gt;下载MacOS12镜像&lt;/h2&gt;
&lt;p&gt;先安装&lt;code&gt;acidanthera/OpenCorePkg&lt;/code&gt;工具&lt;/p&gt;
&lt;p&gt;还是在一个合适的位置克隆项目&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# 克隆项目
git clone https://github.com/acidanthera/OpenCorePkg.git

# 进入工具目录
cd ./OpenCorePkg/Utilities/macrecovery

# 拉取15版本镜像
python3 macrecovery.py -b Mac-E43C1C25D4880AD6 -m 00000000000000000 download
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;把包含镜像的文件夹&lt;code&gt;com.apple.recovery.boot&lt;/code&gt;、此前的&lt;strong&gt;EFI文件夹&lt;/strong&gt;，保存到一台可以制作启动u盘的系统上。幻梦这边还是使用了另一台Windows来完成这两步的工作。&lt;/p&gt;
&lt;h2&gt;制作启动U盘&lt;/h2&gt;
&lt;p&gt;下载一个Rufus来制作启动盘&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/pbatard/rufus/releases&quot;&gt;https://github.com/pbatard/rufus/releases&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/QQ20260223-183117.png&quot; alt=&quot;QQ20260223-183117.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;千万要注意&lt;/strong&gt;，此操作需要&lt;strong&gt;格式化u盘&lt;/strong&gt;，请确保u盘里已经&lt;strong&gt;做好备份&lt;/strong&gt;或内部无再需要的文件。&lt;/p&gt;
&lt;p&gt;初始化u盘后将&lt;code&gt;EFI&lt;/code&gt;、&lt;code&gt;com.apple.recovery.boot&lt;/code&gt;文件夹拖入盘内。&lt;/p&gt;
&lt;p&gt;启动U盘制作完成，安全弹出后插在准备安装U盘的主机上，进入bios选择u盘启动。&lt;/p&gt;
&lt;h2&gt;进入启动盘&lt;/h2&gt;
&lt;p&gt;进入引导后选择第一个选项运行。此时会出现黑屏，这是正常情况，等一段时间会出现跑码，此时说明成功进入安装环节。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/IMG_20260223_184736.jpg&quot; alt=&quot;IMG_20260223_184736.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选择对应的语言
&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/IMG_20260223_184758.jpg&quot; alt=&quot;IMG_20260223_184758.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选择磁盘工具对硬盘进行操作
&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/IMG_20260223_185153.jpg&quot; alt=&quot;IMG_20260223_185153.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点进去先别急着操作，想清楚&lt;strong&gt;自己有几块硬盘&lt;/strong&gt;；&lt;strong&gt;需要在哪一块硬盘上安装系统&lt;/strong&gt;；&lt;strong&gt;需要抹除的硬盘是否还有重要资料未保存&lt;/strong&gt;；&lt;strong&gt;是否真的需要抹除硬盘&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;想清楚上面的内容再进行后面的操作&lt;/p&gt;
&lt;p&gt;幻梦这边是单硬盘，空间也不够只能选择抹除掉原来的ubuntu来安装MacOS
&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/IMG_20260223_185403.jpg&quot; alt=&quot;IMG_20260223_185403.jpg&quot; /&gt;&lt;/p&gt;
&lt;p&gt;抹除硬盘后选择正确的硬盘进行安装（Mac OS 12名称应该为Monterey，幻梦第一次就安装错了，这是最后检查的机会，不然一会只能重装了）&lt;/p&gt;
&lt;p&gt;后面就等待进度条完成，过程中可能会反复重启几次。&lt;/p&gt;
&lt;p&gt;最后进入设置界面完成设置。&lt;/p&gt;
&lt;h2&gt;生成UTBMap.kext文件&lt;/h2&gt;
&lt;p&gt;现在我们应该顺利进入到MacOS系统了，可能还存在一些小问题或者卡顿的大问题，如果出现卡顿问题先跳转到下面疑难杂症里面尝试解决，为了保证阅读到流畅性还是先写如何导入。&lt;/p&gt;
&lt;p&gt;这一步，取下刚刚带启动EFI的u盘，把u盘内的文件进行备份。备份完成后我们装一个Windows PE系统到U盘里，制作winPE是必须要做的，因为我们等会替换EFI还是要用winPE的。&lt;/p&gt;
&lt;p&gt;（这步生成USB Map主要是力求完美的操作，之前自动化生产的EFI通常已经能驱动usb设备了，真不想做可以不做这一步）&lt;/p&gt;
&lt;p&gt;制作PE盘，我们挑一个USB3.0的U盘。这个很重要，一定要是USB3.0的，不然之后这个USB端口在Mac下就只有2.0的速度了。&lt;/p&gt;
&lt;p&gt;PE系统很多，请自行选择。幻梦选了老牌的&lt;a href=&quot;https://www.wepe.com.cn/download.html&quot;&gt;微PE&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;做好PE后下载&lt;a href=&quot;https://github.com/USBToolBox/tool/releases&quot;&gt;https://github.com/USBToolBox/tool/releases&lt;/a&gt;，下载windows.zip文件复制到PE里等会要用到。&lt;/p&gt;
&lt;p&gt;刚刚备份的，尤其是EFI也复制进去。&lt;/p&gt;
&lt;p&gt;然后，在准备一个USB3.0的设备，推荐是U盘，之后识别端口需要用到，u盘还能方便我们制作新的config.plist。&lt;/p&gt;
&lt;p&gt;进入pe后解压zip并进入文件夹，双击Windows.exe启动工具，启动不了的可以试试在文件夹位置打开cmd然后&lt;code&gt;./Windows.exe&lt;/code&gt;尝试打开&lt;/p&gt;
&lt;p&gt;选择D.  Discover Ports&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/QQ20260228-230823.png&quot; alt=&quot;QQ20260228-230823.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;掏出我们刚刚另外准备好的usb3.0设备，依次测试每个usb接口。&lt;strong&gt;注意：工具5秒进行一次识别，确认工具刷新，识别成功后，再试下一个&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;全部完成后，B退出。到第一页后选择S.  Select Ports and Build Kext&lt;/p&gt;
&lt;p&gt;确认识别到的USB口数量正确，选择K，进行生成。&lt;/p&gt;
&lt;p&gt;生成完成在这个工具的目录下会多一个&lt;code&gt;UTBMap.kext&lt;/code&gt;我们把这个复制到&lt;code&gt;/EFI/OC/Kexts&lt;/code&gt;下,并且&lt;strong&gt;删掉&lt;/strong&gt;原有的&lt;code&gt;UTBdefault.kext&lt;/code&gt;，我们现在要想办法更新&lt;code&gt;config.plist&lt;/code&gt;。第一种办法，把整个EFI文件夹复制到一个新的U盘里备用；第二种方法，关机拔出U盘进行下一步&lt;/p&gt;
&lt;p&gt;把U盘插到一个Windows系统的主机上，我们需要使用到一个工具&lt;a href=&quot;https://github.com/corpnewt/ProperTree&quot;&gt;https://github.com/corpnewt/ProperTree&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;拉取以后运行&lt;code&gt;ProperTree.bat&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE_2026-02-28_232337.png&quot; alt=&quot;屏幕截图 2026-02-28 232337.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们导入刚刚&lt;code&gt;/EFI/OC&lt;/code&gt;下的&lt;code&gt;config.plist&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;然后我们再选择OC snapshot&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/%E5%B1%8F%E5%B9%95%E6%88%AA%E5%9B%BE_2026-02-28_232454.png&quot; alt=&quot;屏幕截图 2026-02-28 232454.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;ctrl+s保存，保存完后退出取下U盘，我们在来到winPE完成后续操作。&lt;/p&gt;
&lt;h2&gt;导入EFI&lt;/h2&gt;
&lt;p&gt;进入PE后我们使用自带的磁盘精灵工具，左侧找到一个名为ESP的卷，卷标为EFI，通常情况下这个卷里是空的我们选到文件预览，直接把我们刚刚完成修改后的整个EFI文件夹丢到里面。&lt;/p&gt;
&lt;p&gt;由于我们是单系统，直接重新启动在bios中将硬盘设置为第一启动项完成我们的所有工作。&lt;/p&gt;
&lt;h2&gt;疑难杂症&lt;/h2&gt;
&lt;p&gt;幻梦目前遇到的最严重的问题是与核显输出相关，暂时也无法完全解决。&lt;/p&gt;
&lt;h3&gt;系统很卡&lt;/h3&gt;
&lt;p&gt;MacOS12对于5250u和他的hd6000来说有压力，但是绝对不会出现画面撕裂、锯齿边，动画掉的只有1帧的问题的。出现这种问题一般就是显存分配的问题了。&lt;/p&gt;
&lt;p&gt;我们点击左上角的&lt;code&gt;苹果图标-关于本机-系统报告&lt;/code&gt;左侧&lt;code&gt;硬件-图形卡/显示器-VRAM&lt;/code&gt;出现这种情况时显存分配可能只有14M，5250U的HD6000核显正常有1536MB的显存，如果过少就说明出现了问题。&lt;/p&gt;
&lt;p&gt;我们打开&lt;code&gt;config.plist&lt;/code&gt;检查&lt;code&gt;Nvram&lt;/code&gt;项&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_%E5%9C%A8ubuntu%E7%B3%BB%E7%BB%9F%E4%B8%8B%E4%BD%BF%E7%94%A8opcore-simplify/QQ20260228-234649.png&quot; alt=&quot;QQ20260228-234649.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意看这个&lt;code&gt;-igfxvesa&lt;/code&gt;如果你有这个问题，那么他就是罪魁祸首，直接删掉即可。但是删掉会带来下一个问题，hdmi接口无法输出1080p60hz的视频信号。但是我们至少先要系统能正常用再追求画面。&lt;/p&gt;
&lt;h3&gt;显示器分辨率低&lt;/h3&gt;
&lt;p&gt;幻梦使用的是一个2k分辨率的显示器，2560*1440分辨率。我们去掉上面的参数以后，hdmi接口输出的时钟频率被限制，导致信号出现问题。幻梦暂时没有找到完美解决方案，但是我们可以缓解一下。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://github.com/xzhih/one-key-hidpi&quot;&gt;https://github.com/xzhih/one-key-hidpi&lt;/a&gt;，这个工具可以帮我们解锁hidpi功能。&lt;/p&gt;
&lt;p&gt;要用这个工具我们要先关闭&lt;code&gt;SIP&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;重启进入引导，这里我们选择&lt;code&gt;Recovery&lt;/code&gt;，选择语言，顶部导航栏选择&lt;code&gt;实用工具-终端&lt;/code&gt;输入以下指令&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;csrutil disable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;关闭SIP，关闭后重启进入系统拉取工具。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;git clone https://github.com/xzhih/one-key-hidpi.git
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行&lt;code&gt;hidpi.command&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;根据你的需求进行选择，作为nuc通常我们选择imac+自己显示器的分辨率，&lt;strong&gt;笔记本用户装黑苹果的别照抄&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;完成设置后重启&lt;/p&gt;
&lt;p&gt;重启后进入系统，默认就是你选择的显示器分辨率，但是实际上输出的依旧是1080p不过不再是50hz而是正常的60hz了。但是你应该会感觉还是有点糊，我们进入系统选择&lt;code&gt;系统偏好设置-显示器-缩放-显示所有分辨率&lt;/code&gt;选择带&lt;strong&gt;HiDPI&lt;/strong&gt;的一个分辨率。不过这只能改善一点你的使用体验，并没有真的解决掉时钟频率受限的核心问题。如果你有好的办法，能发到评论区吗，拜托了。&lt;/p&gt;
&lt;p&gt;对了，不要忘记重新开启&lt;code&gt;SIP&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;老位置输入&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;csrutil enable
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;SIP还是能够增加一点点安全性的，虽然是黑果。&lt;/p&gt;
&lt;h2&gt;最后&lt;/h2&gt;
&lt;p&gt;享受你的黑苹果吧，目前这个还不是完美黑苹果系统，不过至少能够正常使用了，这个笔记到这里也就结束啦。&lt;/p&gt;
</content:encoded></item><item><title>【笔记】Fuwari主题下的DecapCMS编辑器替换</title><link>https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0fuwari%E4%B8%BB%E9%A2%98%E4%B8%8B%E7%9A%84decapcms%E7%BC%96%E8%BE%91%E5%99%A8%E6%9B%BF%E6%8D%A2/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E7%AC%94%E8%AE%B0fuwari%E4%B8%BB%E9%A2%98%E4%B8%8B%E7%9A%84decapcms%E7%BC%96%E8%BE%91%E5%99%A8%E6%9B%BF%E6%8D%A2/</guid><description>Fuwari的资源优化功能对编辑器的影响太大了，直到现在也无法十全十美</description><pubDate>Sun, 08 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;还是老样子前言开篇，先展示一下成果&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;./images/_%E7%AC%94%E8%AE%B0_fuwari%E4%B8%BB%E9%A2%98%E4%B8%8B%E7%9A%84decapcms%E7%BC%96%E8%BE%91%E5%99%A8%E6%9B%BF%E6%8D%A2/QQ20260208-162153.png&quot; alt=&quot;QQ20260208-162153.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;我们看图说话吧。当然幻梦在编辑这篇文档时是看不到图片的，你们都能看到，而这就是第一个问题。Fuwari导致的我们必须相对文档“./”来选择图片，这东西只有Fuwari看得懂其他没有任何一个工具能看懂，现在也解决不掉预览的问题。当然，其实有一个缺德一点的办法，直接用jsdelivr这种cdn去加载github仓库里的图片，不再使用相对路径，不过这其实就是滥用了，jsdelivr本体就是被滥用导致备案挂掉的。属于理论上可行，但是过于缺德，不推荐。&lt;/p&gt;
&lt;p&gt;然后就是右上角的这个提示了，目前看起来很好，实际上代码的实现很不优雅。DecapCMS是有一个提示功能的，但是官方没有把方法暴露出来，无法调用（Decap基于React框架，为了安全没有暴露很多方法，其中提示功能是基于Toastify库完成的）。我们就只能模拟一下Toastify了&lt;/p&gt;
&lt;p&gt;接下来是编辑器，这个编辑器的超链接按钮是一个残废的状态（快捷键好使但是按钮残废，是否触发全看运气），编辑器使用的是vditor。&lt;/p&gt;
&lt;h2&gt;工作代码：&lt;/h2&gt;
&lt;p&gt;其实也没什么好看的了，最后偷懒让Ai改的错，已经变成Qwen模型的样子了。不过也不考虑后期维护了，就这样吧。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;# public\admin\vditor-plugin\index.js
(function () {
  &apos;use strict&apos;;
  // 防止重复加载
  if (window.decapCmsVditorPlugin) return;

  // 创建一个模拟Toastify的通知函数
  function showToast(message, type = &apos;info&apos;, duration = 5000) {
    // 检查是否已有Toastify容器，如果没有则创建
    let container = document.querySelector(&apos;.Toastify&apos;);
    if (!container) {
      container = document.createElement(&apos;div&apos;);
      container.className = &apos;Toastify&apos;;
      document.body.appendChild(container);
    }

    // 创建toast元素
    const toast = document.createElement(&apos;div&apos;);
    toast.className = `vditor-toast Toastify__toast Toastify__toast--${type}`;

    // 添加内容
    const body = document.createElement(&apos;div&apos;);
    body.className = &apos;Toastify__toast-body&apos;;
    body.innerHTML = `&amp;lt;div&amp;gt;${message.replace(/\n/g, &apos;&amp;lt;br&amp;gt;&apos;)}&amp;lt;/div&amp;gt;`;

    toast.appendChild(body);

    // 添加关闭按钮
    const closeButton = document.createElement(&apos;button&apos;);
    closeButton.className = &apos;Toastify__close-button&apos;;
    closeButton.innerHTML = &apos;×&apos;;

    closeButton.onclick = () =&amp;gt; {
      if (container.contains(toast)) {
        toast.classList.add(&apos;fade-out&apos;);
        setTimeout(() =&amp;gt; {
          if (container.contains(toast)) {
            container.removeChild(toast);
          }
        }, 300);
      }
    };

    toast.appendChild(closeButton);

    container.appendChild(toast);

    // 自动移除toast
    const timer = setTimeout(() =&amp;gt; {
      if (container.contains(toast)) {
        toast.classList.add(&apos;fade-out&apos;);
        setTimeout(() =&amp;gt; {
          if (container.contains(toast)) {
            container.removeChild(toast);
          }
        }, 300);
      }
    }, duration);
  }

  // 测试通知功能的方法
  function testNotifications() {
    showToast(&apos;测试成功通知&apos;, &apos;success&apos;, 10000);
    showToast(&apos;测试错误通知&apos;, &apos;error&apos;, 10000);
    showToast(&apos;测试警告通知&apos;, &apos;warning&apos;, 10000);
    showToast(&apos;测试信息通知&apos;, &apos;info&apos;, 10000);
  }

  // 将测试函数暴露到全局，方便调试
  window.testVditorNotifications = testNotifications;

  function init() {
    if (!window.createClass || !window.h) {
      setTimeout(init, 100);
      return;
    }
    registerPlugin();
  }

  // 合并媒体文件到主分支的函数
  async function mergeMediaToMain() {
    // 检查媒体分支是否存在
    const token = ImageUploadManager.getToken();
    const { repoOwner, repoName, branch: mainBranch, mediaBranch } = ImageUploadManager.config;

    try {
      // 检查媒体分支是否存在
      const branchCheckUrl = `https://api.github.com/repos/${repoOwner}/${repoName}/branches/${mediaBranch}`;
      const branchCheckRes = await fetch(branchCheckUrl, {
        headers: { Authorization: `token ${token}` }
      });

      if (branchCheckRes.status === 404) {
        // 媒体分支不存在，无需合并
        console.log(&apos;媒体分支不存在，无需合并&apos;);
        return;
      } else if (!branchCheckRes.ok) {
        const errorData = await branchCheckRes.text();
        console.error(`检查分支状态失败: ${branchCheckRes.status}, ${errorData}`);
        return;
      }

      // 如果媒体分支存在，则执行合并
      console.log(&apos;开始合并媒体分支到主分支...&apos;);

      const mergeRes = await fetch(`https://api.github.com/repos/${repoOwner}/${repoName}/merges`, {
        method: &apos;POST&apos;,
        headers: {
          Authorization: `token ${token}`,
          &apos;Content-Type&apos;: &apos;application/json&apos;
        },
        body: JSON.stringify({
          base: mainBranch,
          head: mediaBranch,
          commit_message: &apos;[Auto] Merge media assets to main&apos;
        })
      });

      if (mergeRes.ok) {
        console.log(&apos;成功将媒体分支合并到主分支&apos;);
        showToast(&apos;✅ 成功将媒体分支合并到主分支&apos;, &apos;success&apos;);

        // 合并成功后删除媒体分支
        try {
          const deleteUrl = `https://api.github.com/repos/${repoOwner}/${repoName}/git/refs/heads/${mediaBranch}`;

          const deleteRes = await fetch(deleteUrl, {
            method: &apos;DELETE&apos;,
            headers: {
              Authorization: `token ${token}`
            }
          });

          if (deleteRes.ok) {
            console.log(&apos;成功删除媒体分支&apos;);
            showToast(&apos;🗑️ 成功删除媒体分支&apos;, &apos;info&apos;);
          } else {
            const errorData = await deleteRes.text();
            console.error(&apos;删除分支失败:&apos;, deleteRes.status, errorData);
          }
        } catch (error) {
          console.error(&apos;删除媒体分支失败:&apos;, error);
          // 不抛出错误，因为合并已经成功
        }

        // 清空已上传但未提交的记录
        ImageUploadManager.uploadedButUncommitted.clear();
      } else if (mergeRes.status === 409) {
        // 合并冲突，可能需要手动处理
        console.warn(&apos;媒体分支与主分支存在冲突，无法自动合并&apos;);
        showToast(&apos;⚠️ 媒体分支与主分支存在冲突，无法自动合并&apos;, &apos;warning&apos;, 5000);
      } else {
        const errorText = await mergeRes.text();
        console.error(`合并失败: ${mergeRes.status}`, errorText);
        showToast(`❌ 合并失败: ${mergeRes.status}`, &apos;error&apos;, 5000);
      }
    } catch (error) {
      console.error(&apos;合并媒体分支时出错:&apos;, error);
      showToast(`❌ 合并媒体分支时出错: ${error.message}`, &apos;error&apos;, 5000);
    }
  }

  // Slug工具类，用于处理slug相关的功能
  const SlugUtils = {
    // 从URL中提取slug
    extractSlugFromUrl() {
      const url = window.location.href;
      try {
        // 提取URL中的slug部分，例如从 https://www.yumehinata.com/admin#/collections/terminal/entries/slug
        const match = url.match(/\/entries\/([^\/\?#]+)/);
        if (match &amp;amp;&amp;amp; match[1]) {
          const decodedSlug = decodeURIComponent(match[1]); // 解码URL编码的slug
          // 检查是否是有效的slug格式（不是特殊占位符或无法解码的内容）
          if (decodedSlug &amp;amp;&amp;amp; decodedSlug !== &apos;new&apos; &amp;amp;&amp;amp; decodedSlug !== &apos;create&apos; &amp;amp;&amp;amp; decodedSlug !== &apos;default&apos;) {
            return decodedSlug;
          }
        }
      } catch (e) {
        console.error(&apos;无法从URL中提取slug:&apos;, e);
      }
      return null; // 返回null表示无法获取有效slug
    },

    // 获取当前文档的slug，如果无法获取则抛出错误
    getCurrentSlug() {
      const slug = this.extractSlugFromUrl();

      if (!slug) {
        // 使用自定义通知而不是直接抛出错误（虽然仍需抛出以中断流程）
        // 但在抛出前显示通知
        showToast(&apos;无法确定文章标识符，请先保存文章标题后再上传图片&apos;, &apos;error&apos;, 4000);
        throw new Error(&apos;无法确定文章标识符，请先保存文章标题后再上传图片&apos;);
      }
      // 保留字母、数字、中文、连字符、下划线，替换其他特殊字符为下划线
      return slug.replace(/[^a-zA-Z0-9\u4e00-\u9fa5_-]/g, &apos;_&apos;);
    }
  };

  // 全局变量用于存储待提交的图片
  let pendingMediaFiles = [];

  const ImageUploadManager = {
    pendingImages: [],
    isUploading: false,
    uploadedButUncommitted: new Set(),  // 初始化为Set对象

    config: {
      repoOwner: &apos;YumeHinata&apos;,
      repoName: &apos;AstroBlog&apos;,
      branch: &apos;main&apos;,
      mediaFolder: &apos;src/content/posts&apos;,
      mediaBranch: &apos;cms/media-assets&apos;  // 添加媒体分支配置
    },

    commitConfig: {
      authorName: &apos;Decap CMS Editor&apos;,
      authorEmail: &apos;editor@yumehinata.com&apos;,
      commitPrefix: &apos;[Media] Upload: &apos;
    },

    getToken() {
      try {
        const userData = JSON.parse(localStorage.getItem(&apos;decap-cms-user&apos;));
        if (!userData?.token) throw new Error(&apos;请先登录Decap CMS&apos;);
        return userData.token;
      } catch (e) {
        throw new Error(&apos;认证失败: &apos; + e.message);
      }
    },

    // 使用SlugUtils来获取slug
    extractSlugFromUrl: SlugUtils.extractSlugFromUrl,

    getCurrentSlug: SlugUtils.getCurrentSlug,

    calculatePaths(filename) {
      const mediaFolder = this.config.mediaFolder.replace(/^\//, &apos;&apos;);

      // 获取当前文档的slug
      const slug = this.getCurrentSlug();

      // 保留字母、数字、中文、连字符、下划线和点，替换其他特殊字符为下划线
      const safeFilename = filename.replace(/[^a-zA-Z0-9\u4e00-\u9fa5._-]/g, &apos;_&apos;);

      // 在post目录下的images子目录中创建以slug命名的子目录存放图片
      const pathInRepo = `${mediaFolder}/images/${slug}/${safeFilename}`;
      const markdownPath = `./images/${slug}/${safeFilename}`;
      return { pathInRepo, markdownPath };
    },

    addImages(files) {
      const newImages = Array.from(files).map(file =&amp;gt; ({
        file,
        previewUrl: URL.createObjectURL(file),
        name: file.name,
        size: file.size,
        id: Date.now() + Math.random()
      }));

      this.pendingImages.push(...newImages);
      return newImages;
    },

    cleanupPreviews() {
      this.pendingImages.forEach(img =&amp;gt; URL.revokeObjectURL(img.previewUrl));
      this.pendingImages = [];
    },

    // 上传所有图片并返回markdown字符串
    async uploadAll(vditorInstance) {
      if (this.pendingImages.length === 0) throw new Error(&apos;没有图片需要上传&apos;);
      if (this.isUploading) throw new Error(&apos;上传正在进行中&apos;);

      this.isUploading = true;

      // 在开始上传前验证能否获取到slug
      try {
        this.calculatePaths(&apos;test.jpg&apos;); // 只是为了验证能否成功计算路径
      } catch (error) {
        console.error(&quot;路径计算失败:&quot;, error);
        throw new Error(error.message);
      }

      const token = this.getToken();
      const { repoOwner, repoName, mediaBranch = &apos;cms/media-assets&apos; } = this.config; // 设置默认值
      const commitCfg = this.commitConfig;

      const results = { success: 0, errors: [], markdowns: [] };

      try {
        // 确保媒体分支存在
        await this.ensureMediaBranchExists(token, repoOwner, repoName, mediaBranch);

        for (const img of this.pendingImages) {
          try {
            const { pathInRepo, markdownPath } = this.calculatePaths(img.name);

            // 检查文件是否已存在
            const checkUrl = `https://api.github.com/repos/${repoOwner}/${repoName}/contents/${pathInRepo}?ref=${mediaBranch}`;
            const checkRes = await fetch(checkUrl, {
              headers: { Authorization: `token ${token}` }
            });

            if (checkRes.ok) {
              // 文件已存在，直接使用现有文件
              results.success++;
              results.markdowns.push(`![${img.name}](${markdownPath})`);

              // 添加到已上传集合，但不实际上传
              this.uploadedButUncommitted.add(pathInRepo);

              // 释放预览URL
              URL.revokeObjectURL(img.previewUrl);
              continue; // 跳过上传步骤
            }

            // 文件不存在，执行上传
            const content = await this.fileToBase64(img.file);

            // 上传到媒体分支 - 现在commitMediaFile会自己处理文件存在性检查
            await this.commitMediaFile(token, repoOwner, repoName, pathInRepo, content, mediaBranch, img.name, commitCfg);

            results.success++;
            results.markdowns.push(`![${img.name}](${markdownPath})`);

            this.uploadedButUncommitted.add(pathInRepo);

            // 释放预览URL
            URL.revokeObjectURL(img.previewUrl);
          } catch (error) {
            console.error(&quot;处理图片出错:&quot;, img.name, error);
            results.errors.push(`${img.name}: ${error.message}`);
          }
        }

        // 即使有部分失败，也将成功上传的图片插入到编辑器中
        if (results.success &amp;gt; 0 &amp;amp;&amp;amp; results.markdowns.length &amp;gt; 0 &amp;amp;&amp;amp; vditorInstance) {
          vditorInstance.insertValue(&apos;\n&apos; + results.markdowns.join(&apos;\n&apos;) + &apos;\n&apos;);
        }

        if (results.errors.length &amp;gt; 0) {
          console.error(&apos;以下图片上传失败:&apos;, results.errors);

          // 使用自定义的Toastify样式通知
          showToast(`部分图片上传失败:\n${results.errors.join(&apos;\n&apos;)}\n\n但已成功上传的图片已插入编辑器。`, &apos;error&apos;, 5000);
        } else if (results.success &amp;gt; 0) {
          // 使用自定义的Toastify样式通知
          showToast(`✅ 成功处理 ${results.success} 张图片到媒体库`, &apos;success&apos;);
        }

        this.pendingImages = [];
        return results;
      } finally {
        this.isUploading = false;
      }
    },


    // 提交单个媒体文件到GitHub
    async commitMediaFile(token, owner, repo, path, content, branch, filename, commitCfg) {
      // 首先检查文件是否已存在
      const checkUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${path}?ref=${branch}`;
      let sha = null;

      try {
        const checkRes = await fetch(checkUrl, {
          headers: { Authorization: `token ${token}` }
        });

        if (checkRes.ok) {
          // 文件已存在，获取SHA
          const fileInfo = await checkRes.json();
          sha = fileInfo.sha;
        }
        // 如果文件不存在，checkRes.status 会是 404，我们将继续创建新文件
      } catch (error) {
        console.error(`检查文件是否存在时出错: ${error.message}`);
        // 如果检查失败，继续上传新文件
      }

      // 构建API URL和请求体
      const url = `https://api.github.com/repos/${owner}/${repo}/contents/${path}`;
      const body = {
        message: `${commitCfg.commitPrefix}${filename}`,
        content: content, // 确保内容是base64编码的
        branch: branch,
        committer: { name: commitCfg.authorName, email: commitCfg.authorEmail },
        author: { name: commitCfg.authorName, email: commitCfg.authorEmail }
      };

      // 只有当文件确实存在（有SHA）时才添加sha字段
      if (sha) {
        body.sha = sha;
      }

      const res = await fetch(url, {
        method: &apos;PUT&apos;,
        headers: {
          Authorization: `token ${token}`,
          &apos;Content-Type&apos;: &apos;application/json&apos;
        },
        body: JSON.stringify(body)
      });

      if (!res.ok) {
        const errorData = await res.text();
        console.error(`GitHub API错误详情: ${errorData}`);
        console.error(`请求URL: ${url}`);
        console.error(`路径原始值: ${path}`);
        console.error(`请求体: ${JSON.stringify({ message: body.message, contentLength: content.length, branch: body.branch }, null, 2)}`);
        throw new Error(`GitHub API错误: ${res.status}, ${errorData}`);
      }

      // 成功上传后返回数据
      return await res.json();
    },

    // 确保媒体分支存在，如果不存在则从主分支创建
    async ensureMediaBranchExists(token, repoOwner, repoName, mediaBranch) {
      // 检查媒体分支是否存在
      const branchCheckUrl = `https://api.github.com/repos/${repoOwner}/${repoName}/branches/${mediaBranch}`;
      const branchCheckRes = await fetch(branchCheckUrl, {
        headers: { Authorization: `token ${token}` }
      });

      if (branchCheckRes.ok) {
        // 分支已存在
        return;
      } else if (branchCheckRes.status === 404) {
        // 分支不存在，需要创建

        // 获取主分支信息
        const mainBranchName = this.config.branch;
        const mainBranchUrl = `https://api.github.com/repos/${repoOwner}/${repoName}/git/refs/heads/${mainBranchName}`;
        const mainBranchRes = await fetch(mainBranchUrl, {
          headers: { Authorization: `token ${token}` }
        });

        if (!mainBranchRes.ok) {
          throw new Error(`无法获取主分支信息: ${mainBranchRes.status}`);
        }

        const mainBranchData = await mainBranchRes.json();
        const mainBranchSha = mainBranchData.object.sha;

        // 创建媒体分支
        const createBranchUrl = `https://api.github.com/repos/${repoOwner}/${repoName}/git/refs`;
        const createRes = await fetch(createBranchUrl, {
          method: &apos;POST&apos;,
          headers: {
            Authorization: `token ${token}`,
            &apos;Content-Type&apos;: &apos;application/json&apos;
          },
          body: JSON.stringify({
            ref: `refs/heads/${mediaBranch}`,
            sha: mainBranchSha
          })
        });

        if (!createRes.ok) {
          const errorData = await createRes.text();
          throw new Error(`创建分支失败: ${createRes.status}, ${errorData}`);
        }
      } else {
        const errorData = await branchCheckRes.text();
        throw new Error(`检查分支状态失败: ${branchCheckRes.status}, ${errorData}`);
      }
    },

    // 获取当前内容所在分支
    getCurrentContentBranch() {
      if (window.CMS?.localBackend) {
        return this.config.branch;
      }

      if (window.CMS?.activeEntry) {
        return this.config.branch;
      }

      return this.config.branch;
    },

    // 恢复checkFileExists函数，尽管目前在上传流程中不需要使用它，但保留该函数以备将来使用
    async checkFileExists(token, owner, repo, path, branch = null) {
      // 对路径进行URL编码以用于API请求
      const encodedPath = encodeURIComponent(path).replace(/\//g, &apos;%2F&apos;);
      let url = `https://api.github.com/repos/${owner}/${repo}/contents/${encodedPath}`;
      if (branch) {
        url += `?ref=${branch}`;
      }

      const res = await fetch(url, { headers: { Authorization: `token ${token}` } });
      return res.status === 200 ? await res.json() : null;
    },

    async fileToBase64(file) {
      return new Promise((resolve, reject) =&amp;gt; {
        const reader = new FileReader();
        reader.onload = () =&amp;gt; resolve(reader.result.split(&apos;,&apos;)[1]);
        reader.onerror = reject;
        reader.readAsDataURL(file);
      });
    }
  };

  const VditorControl = createClass({
    getInitialState() {
      return {
        value: this.props.value || &apos;&apos;,
        showUploadPanel: false,
        uploadStatus: null
      };
    },

    componentDidMount() {
      this.initVditor();
      this.pathCheckInterval = setInterval(() =&amp;gt; this.checkDocPath(), 2000);
    },

    // 在props中提供的控件方法中处理提交前逻辑
    control: {
      // 这个方法将在外部调用，当需要提交内容时
      async persist(entry) {
        if (pendingMediaFiles.length &amp;gt; 0) {
          try {
            // 获取token
            const token = ImageUploadManager.getToken();
            const { repoOwner, repoName, mediaBranch } = ImageUploadManager.config; // 使用媒体分支
            const contentBranch = ImageUploadManager.getCurrentContentBranch();

            // 提交所有待处理的媒体文件到媒体分支
            for (const mediaFile of pendingMediaFiles) {
              try {
                await this.commitMediaFile(
                  token,
                  repoOwner,
                  repoName,
                  mediaFile.path,
                  mediaFile.content,
                  mediaBranch, // 使用媒体分支
                  mediaFile.filename,
                  ImageUploadManager.commitConfig,
                  mediaFile.sha
                );
              } catch (error) {
                console.error(&apos;提交媒体文件失败:&apos;, error);
              }
            }

            // 清空待提交列表
            pendingMediaFiles = [];
          } catch (error) {
            console.error(&apos;处理媒体文件时出错:&apos;, error);
          }
        }
      }
    },


    componentWillUnmount() {
      // 清除定时器
      clearInterval(this.pathCheckInterval);

      // 由于我们使用全局hashchange监听器，不需要在组件中移除
      if (this.vditor) this.vditor.destroy();
      ImageUploadManager.cleanupPreviews();
    },

    checkDocPath() {
      const newPath = this.getCurrentDocPath();
      if (newPath !== this.state.currentDocPath) {
        this.setState({ currentDocPath: newPath });
      }
    },

    getCurrentDocPath() {
      if (window.CMS?.activeEntry?.path) {
        // 解码路径中的URL编码字符
        return decodeURIComponent(window.CMS.activeEntry.path);
      }
      if (this.props.entry?.path) {
        return decodeURIComponent(this.props.entry.path);
      }

      // 尝试从URL中获取当前文档路径
      try {
        const slug = SlugUtils.extractSlugFromUrl();
        if (slug) {
          return `src/content/posts/${slug}.md`;
        }
      } catch (e) {
        console.error(&apos;无法从URL中确定文档路径:&apos;, e);
      }

      return &apos;无法确定当前文档路径&apos;;
    },

    initVditor() {
      try {
        this.vditor = new Vditor(this.props.forID, {
          height: 500,
          value: this.state.value,
          mode: &apos;ir&apos;,
          cache: { enable: false },
          toolbar: this.getToolbarConfig(),
          input: (value) =&amp;gt; {
            this.setState({ value });
            this.props.onChange(value);
          }
        });

        window.vditorInstance = this.vditor;
      } catch (e) {
        console.error(&apos;Vditor初始化失败:&apos;, e);
      }
    },

    getToolbarConfig() {
      const baseTools = [
        &apos;emoji&apos;, &apos;headings&apos;, &apos;bold&apos;, &apos;italic&apos;, &apos;strike&apos;, &apos;link&apos;, &apos;quote&apos;, &apos;code&apos;, &apos;inline-code&apos;,
        &apos;insert-before&apos;, &apos;insert-after&apos;, &apos;|&apos;, &apos;list&apos;, &apos;ordered-list&apos;, &apos;check&apos;, &apos;outdent&apos;, &apos;indent&apos;,
        &apos;|&apos;, &apos;table&apos;, &apos;|&apos;, &apos;undo&apos;, &apos;redo&apos;, &apos;|&apos;
      ];

      const uploadButton = {
        name: &apos;image-upload&apos;,
        tip: &apos;上传图片到GitHub&apos;,
        className: &apos;toolbar__image-upload&apos;,
        icon: &apos;&amp;lt;svg viewBox=&quot;0 0 1024 1024&quot; width=&quot;16&quot; height=&quot;16&quot;&amp;gt;&amp;lt;path d=&quot;M959.9 774.4c0 70.4-57.6 128-128 128H192c-70.4 0-128-57.6-128-128V249.6c0-70.4 57.6-128 128-128h640c70.4 0 128 57.6 128 128v524.8z&quot; fill=&quot;#FF8A00&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;path d=&quot;M825.6 300.8c0 57.6-44.8 102.4-102.4 102.4s-102.4-44.8-102.4-102.4 44.8-102.4 102.4-102.4 102.4 44.8 102.4 102.4zM710.4 556.8l-108.8-108.8-185.6 185.6-108.8-108.8L128 697.6v76.8c0 70.4 57.6 128 128 128h640c70.4 0 128-57.6 128-128v-76.8L710.4 556.8z&quot; fill=&quot;#FFFFFF&quot;&amp;gt;&amp;lt;/path&amp;gt;&amp;lt;/svg&amp;gt;&apos;,
        click: () =&amp;gt; this.setState({ showUploadPanel: true })
      };

      const remainingTools = [
        &apos;edit-mode&apos;, &apos;content-theme&apos;, &apos;code-theme&apos;, &apos;export&apos;, &apos;outline&apos;, &apos;preview&apos;, &apos;devtools&apos;, &apos;info&apos;, &apos;help&apos;, &apos;br&apos;
      ];

      return [...baseTools, uploadButton, &apos;|&apos;, ...remainingTools];
    },

    handleFileSelect(event) {
      const files = event.target.files;
      if (!files.length) return;

      ImageUploadManager.addImages(files);
      this.setState({
        uploadStatus: `已暂存 ${files.length} 张图片，共 ${ImageUploadManager.pendingImages.length} 张待上传`
      });

      event.target.value = &apos;&apos;;
    },

    async handleUpload() {
      const pendingCount = ImageUploadManager.pendingImages.length;

      if (pendingCount === 0) {
        this.setState({ uploadStatus: &apos;请先选择图片&apos; });
        return;
      }

      this.setState({
        uploadStatus: &apos;上传中...&apos;,
        showUploadPanel: false
      });

      try {
        const result = await ImageUploadManager.uploadAll(this.vditor);

        if (result.success &amp;gt; 0) {
          this.setState({
            uploadStatus: `✅ 上传完成！成功 ${result.success}/${pendingCount} 张`
          });
          setTimeout(() =&amp;gt; this.setState({ uploadStatus: null }), 5000);

          // 显示成功通知
          showToast(`✅ 成功上传 ${result.success} 张图片`, &apos;success&apos;);
        } else {
          this.setState({
            uploadStatus: &apos;上传失败，请查看控制台&apos;,
            showUploadPanel: true
          });

          // 显示失败通知
          showToast(&apos;❌ 图片上传失败，请重试&apos;, &apos;error&apos;);
        }

        if (result.errors.length &amp;gt; 0) {
          console.error(&apos;上传错误:&apos;, result.errors);
        }
      } catch (error) {
        console.error(&apos;上传过程出错:&apos;, error); // 保留错误信息到控制台
        this.setState({
          uploadStatus: `错误: ${error.message}`,
          showUploadPanel: true
        });

        // 显示错误通知
        showToast(`❌ 上传出错: ${error.message}`, &apos;error&apos;, 5000);
      }
    },

    handleClear() {
      ImageUploadManager.cleanupPreviews();
      this.setState({
        uploadStatus: &apos;已清空暂存图片&apos;,
        showUploadPanel: false
      });

      // 显示清空成功的通知
      showToast(&apos;🗑️ 已清空暂存图片&apos;, &apos;info&apos;);
    },

    render() {
      const h = window.h;
      const { showUploadPanel, uploadStatus } = this.state;
      const pendingImages = ImageUploadManager.pendingImages;

      return h(&apos;div&apos;, { className: &apos;vditor-full-container&apos; }, [
        h(&apos;div&apos;, {
          key: &apos;editor&apos;,
          id: this.props.forID,
          className: &apos;vditor-editor&apos;
        }),

        showUploadPanel &amp;amp;&amp;amp; this.renderUploadPanel(h, pendingImages, uploadStatus),

        !showUploadPanel &amp;amp;&amp;amp; pendingImages.length &amp;gt; 0 &amp;amp;&amp;amp; h(&apos;div&apos;, {
          key: &apos;upload-hint&apos;,
          className: &apos;upload-hint&apos;
        }, `📷 ${pendingImages.length} 张图片待上传，点击管理`)
      ]);
    },

    renderUploadPanel(h, pendingImages, uploadStatus) {
      const currentDocPath = this.getCurrentDocPath();

      return h(&apos;div&apos;, {
        key: &apos;upload-panel&apos;,
        className: &apos;vditor-upload-panel&apos;
      }, [
        h(&apos;h4&apos;, {}, &apos;📁 图片上传到GitHub&apos;),

        currentDocPath &amp;amp;&amp;amp; h(&apos;div&apos;, { className: &apos;doc-path&apos; }, `文档路径: ${currentDocPath}`),

        h(&apos;div&apos;, { className: &apos;file-input-container&apos; }, [
          h(&apos;input&apos;, {
            type: &apos;file&apos;,
            accept: &apos;image/*&apos;,
            multiple: true,
            onChange: this.handleFileSelect,
            className: &apos;file-input&apos;
          }),
          h(&apos;div&apos;, { className: &apos;file-hint&apos; }, &apos;支持多选，图片将暂存在浏览器中&apos;)
        ]),

        pendingImages.length &amp;gt; 0 &amp;amp;&amp;amp; this.renderPreviewArea(h, pendingImages),

        uploadStatus &amp;amp;&amp;amp; h(&apos;div&apos;, {
          className: `upload-status ${this.getStatusClass(uploadStatus)}`
        }, uploadStatus),

        this.renderActionButtons(h, pendingImages)
      ]);
    },

    renderPreviewArea(h, pendingImages) {
      return h(&apos;div&apos;, {
        key: &apos;preview-area&apos;,
        className: &apos;preview-area&apos;
      }, [
        h(&apos;div&apos;, { className: &apos;preview-label&apos; }, `已选择 ${pendingImages.length} 张图片:`),
        ...pendingImages.map((img, idx) =&amp;gt; h(&apos;div&apos;, {
          key: idx,
          className: &apos;preview-item&apos;
        }, [
          h(&apos;img&apos;, {
            src: img.previewUrl,
            className: &apos;preview-image&apos;
          }),
          h(&apos;div&apos;, { className: &apos;preview-name&apos; }, img.name)
        ]))
      ]);
    },

    renderActionButtons(h, pendingImages) {
      const isUploading = ImageUploadManager.isUploading;

      return h(&apos;div&apos;, { className: &apos;button-container&apos; }, [
        h(&apos;button&apos;, {
          onClick: this.handleUpload,
          disabled: pendingImages.length === 0 || isUploading,
          className: `primary-button ${pendingImages.length === 0 ? &apos;disabled&apos; : &apos;&apos;}`
        }, isUploading ? &apos;上传中...&apos; : &apos;🚀 开始上传&apos;),

        h(&apos;button&apos;, {
          onClick: this.handleClear,
          className: &apos;secondary-button&apos;
        }, &apos;清空&apos;),

        h(&apos;button&apos;, {
          onClick: () =&amp;gt; this.setState({ showUploadPanel: false }),
          className: &apos;secondary-button&apos;
        }, &apos;关闭&apos;)
      ]);
    },

    getStatusClass(status) {
      if (status.includes(&apos;✅&apos;)) return &apos;success&apos;;
      if (status.includes(&apos;❌&apos;) || status.includes(&apos;错误&apos;)) return &apos;error&apos;;
      return &apos;warning&apos;;
    }
  });

  const VditorPreview = createClass({
    render() {
      const h = window.h;
      const value = this.props.value || &apos;&apos;;

      return h(&apos;div&apos;, {
        className: &apos;vditor-preview&apos;
      }, value || &apos;(无内容)&apos;);
    }
  });

  function registerPlugin() {
    if (!window.CMS?.registerWidget || typeof Vditor === &apos;undefined&apos;) {
      setTimeout(registerPlugin, 100);
      return;
    }

    try {
      // 注册widget，添加数据持久化方法
      const widget = {
        control: VditorControl,
        preview: VditorPreview,
        // 添加一个方法用于在提交前处理媒体文件
        beforeSubmit: async function (entry) {
          if (window.vditorInstance &amp;amp;&amp;amp; window.vditorInstance.getValue) {
            // 更新entry中的内容
            entry.set(window.vditorInstance.getValue());
          }

          // 处理待提交的媒体文件
          if (pendingMediaFiles.length &amp;gt; 0) {
            try {
              // 获取token
              const token = ImageUploadManager.getToken();
              const { repoOwner, repoName, mediaBranch } = ImageUploadManager.config; // 使用媒体分支
              const contentBranch = ImageUploadManager.getCurrentContentBranch();

              // 提交所有待处理的媒体文件到媒体分支
              for (const mediaFile of pendingMediaFiles) {
                try {
                  await VditorControl.prototype.commitMediaFile.call(
                    { commitMediaFile: VditorControl.prototype.commitMediaFile }, // 为调用提供上下文
                    token,
                    repoOwner,
                    repoName,
                    mediaFile.path,
                    mediaFile.content,
                    mediaBranch, // 使用媒体分支
                    mediaFile.filename,
                    ImageUploadManager.commitConfig,
                    mediaFile.sha
                  );
                } catch (error) {
                  console.error(&apos;提交媒体文件失败:&apos;, error);
                }
              }

              // 清空待提交列表
              pendingMediaFiles = [];
            } catch (error) {
              console.error(&apos;处理媒体文件时出错:&apos;, error);
            }
          }
        }
      };

      window.CMS.registerWidget(&apos;vditor&apos;, widget.control, widget.preview);

      // 添加beforeSubmit处理器到全局，供CMS调用
      if (window.CMS_EVENTS) {
        window.CMS_EVENTS.beforeSubmit = widget.beforeSubmit;
      } else {
        window.CMS_EVENTS = { beforeSubmit: widget.beforeSubmit };
      }

      // 使用正确的事件注册方式注册发布事件监听器
      if (window.CMS &amp;amp;&amp;amp; typeof window.CMS.registerEventListener === &apos;function&apos;) {
        window.CMS.registerEventListener({
          name: &apos;prePublish&apos;,
          handler: async (obj) =&amp;gt; {
            console.log(&apos;检测到prePublish事件，即将发布文章&apos;);
            // 发布前合并媒体分支到主分支
            await mergeMediaToMain();
          }
        });
      }

      window.decapCmsVditorPlugin = {
        version: &apos;4.7&apos;,
        hasUpload: true,
        manager: ImageUploadManager
      };

      console.log(&apos;✅ Vditor插件已注册&apos;);
    } catch (e) {
      console.error(&apos;插件注册失败:&apos;, e);
    }
  }

  if (typeof Vditor !== &apos;undefined&apos;) {
    init();
  } else {
    const checkVditor = () =&amp;gt; {
      typeof Vditor !== &apos;undefined&apos; ? init() : setTimeout(checkVditor, 100);
    };
    checkVditor();
  }
})();
&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;/* public\admin\vditor-plugin\style.css */
/* Vditor 编辑器样式 */
.toolbar__image-upload {
  cursor: pointer;
}

.toolbar__image-upload:hover {
  opacity: 0.8;
}

/* 上传面板样式 */
.vditor-upload-panel {
  position: fixed;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  background: white;
  border: 1px solid #ddd;
  border-radius: 8px;
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
  z-index: 10000;
  padding: 20px;
  min-width: 400px;
  max-width: 600px;
}

.upload-hint {
  margin: 15px 0;
  color: #666;
  line-height: 1.5;
}

.file-input-container {
  margin: 15px 0;
}

.file-hint {
  margin: 10px 0;
  color: #888;
  font-size: 0.9em;
}

.doc-path {
  margin: 10px 0;
  padding: 8px;
  background-color: #f5f5f5;
  border-radius: 4px;
  font-family: monospace;
  word-break: break-all;
}

.preview-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px;
  border-bottom: 1px solid #f0f0f0;
}

.preview-item:last-child {
  border-bottom: none;
}

.preview-info {
  display: flex;
  align-items: center;
}

.preview-image {
  width: 40px;
  height: 40px;
  object-fit: cover;
  margin-right: 10px;
  border-radius: 4px;
}

.preview-name {
  font-size: 14px;
  color: #333;
}

.preview-item {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px;
  border-bottom: 1px solid #f0f0f0;
}

.preview-item:last-child {
  border-bottom: none;
}

.preview-image {
  width: 40px;
  height: 40px;
  object-fit: cover;
  margin-right: 10px;
  border-radius: 4px;
}

.preview-name {
  font-size: 14px;
  color: #333;
}

.preview-area {
  margin-top: 20px;
  max-height: 300px;
  overflow-y: auto;
  border: 1px solid #eee;
  border-radius: 4px;
  padding: 10px;
}

.button-container {
  margin-top: 20px;
  display: flex;
  gap: 10px;
  justify-content: flex-end;
}

.primary-button {
  background-color: #007cba;
  color: white;
  border: none;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.primary-button:hover {
  background-color: #005a87;
}

.primary-button.disabled {
  background-color: #cccccc;
  cursor: not-allowed;
}

.secondary-button {
  background-color: #f0f0f0;
  color: #333;
  border: 1px solid #ccc;
  padding: 10px 20px;
  border-radius: 4px;
  cursor: pointer;
  font-size: 14px;
}

.secondary-button:hover {
  background-color: #e0e0e0;
}

.upload-status {
  margin-top: 15px;
  padding: 10px;
  border-radius: 4px;
  text-align: center;
}

.upload-status.success {
  background-color: #d4edda;
  color: #155724;
  border: 1px solid #c3e6cb;
}

.upload-status.error {
  background-color: #f8d7da;
  color: #721c24;
  border: 1px solid #f5c6cb;
}

.upload-status.loading {
  background-color: #d1ecf1;
  color: #0c5460;
  border: 1px solid #bee5eb;
}

/* 加载指示器 */
.loading-indicator {
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 3px solid rgba(255,255,255,.3);
  border-radius: 50%;
  border-top-color: #fff;
  animation: spin 1s ease-in-out infinite;
}

@keyframes spin {
  to { transform: rotate(360deg); }
}

/* Toast通知样式 */
.Toastify {
  position: fixed;
  top: 80px;
  right: 20px;
  z-index: 10000;
  min-width: 300px;
  max-width: 400px;
}

.vditor-toast {
  position: relative;
  min-height: 64px;
  box-sizing: border-box;
  margin-bottom: 10px;
  padding: 12px 16px;
  border-radius: 6px;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
  display: flex;
  justify-content: space-between;
  align-items: center;
  max-height: 120px;
  overflow: hidden;
  font-family: sans-serif;
  cursor: default;
  direction: ltr;
  background: #fff;
  color: #fff;
  animation: slideInRight 0.3s ease-out;
}

.vditor-toast.fade-out {
  animation: fadeOut 0.3s ease-out;
}

.Toastify__toast--success {
  background: #07bc0c;
}

.Toastify__toast--error {
  background: #e74c3c;
}

.Toastify__toast--warning {
  background: #f1c40f;
  color: #333;
}

.Toastify__toast--info {
  background: #3498db;
}

.Toastify__toast-body {
  flex: 1;
  word-break: break-word;
}

.Toastify__close-button {
  background: transparent;
  border: none;
  color: inherit;
  font-size: 20px;
  cursor: pointer;
  padding: 0;
  margin-left: 10px;
  width: 20px;
  height: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}

@keyframes slideInRight {
  from {
    transform: translateX(100%);
    opacity: 0;
  }
  to {
    transform: translateX(0);
    opacity: 1;
  }
}

@keyframes fadeOut {
  from {
    opacity: 1;
  }
  to {
    opacity: 0;
  }
}

&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;使用说明：&lt;/h2&gt;
&lt;p&gt;这种垃圾代码就不传github污染copilot了，不过姑且写个使用说明给自己看吧。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;{ label: &quot;正文&quot;, name: &quot;body&quot;, widget: &quot;vditor&quot; }&lt;/code&gt;config.yml里做出修改&lt;code&gt;widget&lt;/code&gt;不再是&lt;code&gt;markdown&lt;/code&gt;，应该为&lt;code&gt;vditor&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;在admin.html &lt;code&gt;head&lt;/code&gt;中引入以下样式表：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;/admin/vditor/index.css&quot; /&amp;gt;
  &amp;lt;link rel=&quot;stylesheet&quot; href=&quot;/admin/vditor-plugin/style.css&quot; /&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;body&lt;/code&gt;中按顺序引入以下脚本：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;  &amp;lt;script src=&quot;/admin/vditor/index.min.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script src=&quot;https://unpkg.com/decap-cms@^3.1.2/dist/decap-cms.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;script src=&quot;/admin/vditor-plugin/index.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;需要注意目前幻梦是固定了vditor编辑器的版本在本地，其使用中也可以替换为vditor官方的cdn版本。&lt;/p&gt;
</content:encoded></item><item><title>【笔记】基于Edgeone Pages部署Fuwari(Astro)博客+DecapCMS</title><link>https://www.yumehinata.com/posts/%E5%9F%BA%E4%BA%8Eedgeone-pages%E9%83%A8%E7%BD%B2astro%E5%8D%9A%E5%AE%A2decapcms/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/%E5%9F%BA%E4%BA%8Eedgeone-pages%E9%83%A8%E7%BD%B2astro%E5%8D%9A%E5%AE%A2decapcms/</guid><description>astro博客部署其实并不复杂，复杂的主要是decap的适配工作</description><pubDate>Sat, 31 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;当你看到这篇博客的时候，幻梦博客的迁移应该已经进入尾声了。先说为什么要迁移。&lt;/p&gt;
&lt;p&gt;几天前，当幻梦临睡时偶然发现博客上不去了，报了一个422的错误。通常这是证书过期又或是协议错误的表现，但是这天幻梦并没有对网站进行任何设置，访问下百度试试，好吧还是报错。由于是全局魔法，幻梦首先把目标放在了魔法上。关掉魔法，刷新还是报错；重启路由、光猫、交换机还是报错；又开启魔法，报错消失了，但是手机上还是无法正常访问博客；再关掉魔法手机上能访问了，电脑端也能访问了，但是公网环境手机依旧不能访问博客。&lt;/p&gt;
&lt;p&gt;emmmmm，这就很奇怪了。正当幻梦打算重启服务器的时候，突然博客访问又恢复了正常。好吧看着已经到一点的份上还是别折腾自己了。&lt;/p&gt;
&lt;p&gt;第二天炸裂的就来了。短信提醒“因为移动网络波动如有问题请联系……”是致歉短信。好吧“家里云”隔三差五出问题好像并不是很好用，该跑路了。&lt;/p&gt;
&lt;p&gt;于是你就看到这个博客从halo迁移到了Edgeone Pages，变成了一个静态站。&lt;/p&gt;
&lt;h2&gt;第一步：部署Astro&lt;/h2&gt;
&lt;p&gt;这是这个流程里最简单的一步了，首先拉取一个你喜欢的Astro主题或者原版Astro。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;比如幻梦选择的是Fuwari主题&lt;code&gt;pnpm create fuwari@latest&lt;/code&gt;通过这个命令即可完成部署。原版的话可以看这里&lt;code&gt;pnpm create astro@latest&lt;/code&gt;通过这个命令可以拉取原版的Astro。&lt;/li&gt;
&lt;li&gt;因为我们需要部署在Edgeone上所以我们需要添加一些支持&lt;a href=&quot;https://docs.astro.build/zh-cn/guides/deploy/edgeone-pages/&quot;&gt;https://docs.astro.build/zh-cn/guides/deploy/edgeone-pages/&lt;/a&gt;这是astro官方文档的建议，我们需要前往Edgeone查看更详细的文档。&lt;a href=&quot;https://pages.edgeone.ai/document/framework-astro#b1042e3a-c859-4565-be83-e84faa5ed3d9&quot;&gt;https://pages.edgeone.ai/document/framework-astro&lt;/a&gt;。
&lt;ol&gt;
&lt;li&gt;我们第一步先安装适配器&lt;code&gt;npm install @edgeone/astro&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在&lt;code&gt;astro.config.mjs&lt;/code&gt;中加入以下内容。&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;import { defineConfig } from &quot;astro/config&quot;;
import edgeoneAdapter from &quot;@edgeone/astro&quot;;

export default defineConfig({
  adapter: edgeoneAdapter(),
});
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;接下来我们正常向github提交我们的博客项目，然后进入Edgeone Pages控制台&lt;a href=&quot;https://console.cloud.tencent.com/edgeone/pages&quot;&gt;https://console.cloud.tencent.com/edgeone/pages&lt;/a&gt;
&lt;img src=&quot;./images/qq20260131-185934.png&quot; alt=&quot;&quot; /&gt;
选择导入github仓库，选择我们刚刚上传的博客仓库，这样当我们每进行一次提交就可以让Edgeone Pages自动进行部署。&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;第二步：部署DecapCMS&lt;/h2&gt;
&lt;p&gt;这一步其实就会复杂一点。因为不同主题的博客配置其实会有差异，这里一定要先看你主题的示例文档。astro官方文档&lt;a href=&quot;https://docs.astro.build/zh-cn/guides/cms/decap-cms/#%E5%AE%89%E8%A3%85-decapcms&quot;&gt;https://docs.astro.build/zh-cn/guides/cms/decap-cms/#%E5%AE%89%E8%A3%85-decapcms&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;先在博客中安装DecapCMS。&lt;code&gt;pnpm add decap-cms-app&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;在&lt;code&gt;src/pages/&lt;/code&gt;路径下，新建&lt;code&gt;admin.html&lt;/code&gt;文件。如果你也是Fuwari主题可以直接抄配置文件。&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;!doctype html&amp;gt;
&amp;lt;html lang=&quot;zh_CN&quot;&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset=&quot;utf-8&quot; /&amp;gt;
    &amp;lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; /&amp;gt;
    &amp;lt;meta name=&quot;robots&quot; content=&quot;noindex&quot; /&amp;gt;
    &amp;lt;!-- 由于Edgeone Pages无法通过text/yaml格式加载yml文件所以我们必须通过raw.githubusercontent.com，或fastly.jsdelivr.net完成引入 --&amp;gt;
    &amp;lt;link href=&quot;https://raw.githubusercontent.com/你的github用户名/你的博客仓库/refs/heads/main/public/admin/config.yml&quot; type=&quot;text/yaml&quot; rel=&quot;cms-config-url&quot; /&amp;gt;
    &amp;lt;title&amp;gt;内容管理器&amp;lt;/title&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;script src=&quot;https://unpkg.com/decap-cms@^3.1.2/dist/decap-cms.js&quot;&amp;gt;&amp;lt;/script&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;在&lt;code&gt;public/admin/&lt;/code&gt;路径下，新建&lt;code&gt;config.yml&lt;/code&gt;文件，进入编辑&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;collections:
  - name: &quot;terminal&quot; # 用于路由，例如，/admin/collections/blog
    label: &quot;paper&quot; # 在 UI 中使用
    folder: &quot;src/content/posts&quot; # 存储文档的文件夹路径
    create: true # 允许用户在此集合中创建新文档
    fields: # 每个文档的字段，通常在 front matter 中
      - { label: &quot;标题&quot;, name: &quot;title&quot;, widget: &quot;string&quot; }
      - { label: &quot;发布日期&quot;, name: &quot;published&quot;, widget: &quot;datetime&quot;, format: &quot;YYYY-MM-DD&quot; }
      - { label: &quot;描述&quot;, name: &quot;description&quot;, widget: &quot;text&quot;, required: false }
      - { label: &quot;封面图片&quot;, name: &quot;image&quot;, widget: &quot;image&quot;, default: &quot;https://rdimg.yumehinata.com/random-wallpaper&quot;}
      - { label: &quot;标签&quot;, name: &quot;tags&quot;, widget: &quot;list&quot;, required: false }
      - { label: &quot;分类&quot;, name: &quot;category&quot;, widget: &quot;string&quot;, required: false }
      - { label: &quot;草稿状态&quot;, name: &quot;draft&quot;, widget: &quot;boolean&quot;, default: false }
      - { label: &quot;正文&quot;, name: &quot;body&quot;, widget: &quot;markdown&quot; }
media_folder: &quot;/src/content/posts/images&quot; # 文件将被存储在仓库中的位置
# public_folder: &quot;./images&quot; # 上传媒体文件的 src 属性
backend:
  name: github
  repo: &quot;你的github用户名/博客仓库&quot;
  branch: &quot;main&quot; # 选择对应的分支，默认是main分支或者master分支
  api_root: &quot;https://api.github.com&quot;
  site_url: &quot;你站点的url&quot; # 例如https://example.com
  base_url: &quot;你站点的url&quot;
  auth_endpoint: &quot;auth&quot; # 后续使用幻梦提供的外部 OAuth 客户端可以保持不变
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;外部 OAuth 客户端
先看官方推荐的，虽然Edgeone没法直接用但是可以自己修改着部署用&lt;a href=&quot;https://decapcms.org/docs/external-oauth-clients/&quot;&gt;https://decapcms.org/docs/external-oauth-clients/&lt;/a&gt;。&lt;/p&gt;
&lt;p&gt;然后是幻梦毛遂自荐的修改项目：&lt;/p&gt;
&lt;p&gt;::github{repo=&quot;YumeHinata/dacap-cms-edgeone-pages&quot;}&lt;/p&gt;
&lt;p&gt;请看项目的Readme.md文件完成部署。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;疑难杂症&lt;/h2&gt;
&lt;p&gt;与其说疑难杂症更应该说是未解决的问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Fuwari会对图片资源进行优化，所以你必须使用&lt;code&gt;./xxxx/xxxx.jpg&lt;/code&gt;或者&lt;code&gt;./xxx.png&lt;/code&gt;这样相对路径，绝对不要用&lt;code&gt;/src/ports/images&lt;/code&gt;或&lt;code&gt;src/posts/images&lt;/code&gt;这样的路径，或者就使用fastly、github raw、jsdelivr的绝对路径。&lt;/li&gt;
&lt;li&gt;yaml和yml在edgeone直接引用一定会出问题，包括json文件也有可能遭遇这个问题，所以你只能用fastly、github raw、jsdelivr，但是需要注意，fastly和jsdelivr具有缓存时间，并不是你实时上传的内容，而jsdelivr与github raw在部分地区被污染无法正常使用。&lt;/li&gt;
&lt;li&gt;DecapCMS的配置文件必须得写&lt;code&gt;posts/images&lt;/code&gt;相对路径（相对于文件根目录），所以实际在你复制资源的路径时他给你的会是&lt;code&gt;posts/images/xxxx.jpg&lt;/code&gt;这样的格式，这会导致第一条提到的问题，这大概是Fuwari特有的问题，请自行修改为&lt;code&gt;./images/xxxx.jpg&lt;/code&gt;使用（Fuwari的编译时，根目录应该为文档文件所在目录为根目录）或自己修改Decap的代码来一步到位实现。&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>【思路】CS2服务端自动检查并更新脚本（Linux+Docker环境）</title><link>https://www.yumehinata.com/posts/sxoqoisr/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/sxoqoisr/</guid><pubDate>Fri, 02 Jan 2026 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;首先看标题这个是linux+docker环境，幻梦的steamcmd和cs2服务端都是分别跑在容器里的。如果不是这个环境的没法直接使用脚本，但是只看思路是足够的。&lt;/p&gt;
&lt;p&gt;然后这个脚本理论上应该适配所有V社游戏的服务器，但是幻梦暂时没有对本文的脚本进行完整的测试，所以只算提供一个思路而不是教程。&lt;/p&gt;
&lt;p&gt;CS2之前其实是有自动更新插件的，但是作者已经快1年没更新了。issue里其他人fork的版本也不怎么好用，迫于实际需求幻梦就只能自己写一个了。&lt;/p&gt;
&lt;p&gt;为什么是shell脚本而不是一个插件？很简单，幻梦没有看完CSS插件的开发文档，同时也没兴趣不断维护一个随时可能失效的插件，幻梦的需求是功能简单、稳定能用。有什么能稳的过shell和Valve官方提供的api呢？&lt;/p&gt;
&lt;h2&gt;第一步：通过steamcmd获取最新的buildID&lt;/h2&gt;
&lt;p&gt;完成更新一共分四步。获取官方最新的buildID，获取本地的buildID，对比两者，不一致则进行更新。&lt;/p&gt;
&lt;p&gt;获取buildID的命令是&lt;code&gt;steamcmd +login anonymous +app_status 730 +quit&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;docker环境获取的脚本则是，注意更改容器名称再运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec [steamcmd容器名] \
  /home/steam/steamcmd/steamcmd.sh \
  +login anonymous \
  +app_info_print 730 \
  +quit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正常情况下steamcmd会返回一个json，里面对我们有用的只有&lt;code&gt;branches&lt;/code&gt;-&lt;code&gt;public&lt;/code&gt;-&lt;code&gt;buildid&lt;/code&gt;。其他的幻梦下文里省略掉了&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&quot;branches&quot;{
        &quot;public&quot;{
                &quot;buildid&quot;               &quot;21248816&quot;
                &quot;timeupdated&quot;           &quot;1766101800&quot;
        }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;接下来我们上docker的提取脚本&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash
set -euo pipefail

CONTAINER=&quot;steamcmd&quot;
STEAMCMD=&quot;/home/steam/steamcmd/steamcmd.sh&quot;
APPID=730

#执行steamcmd并获取输出
OUTPUT=$(docker exec &quot;$CONTAINER&quot; \
  &quot;$STEAMCMD&quot; \
  +login anonymous \
  +app_info_print &quot;$APPID&quot; \
  +quit 2&amp;gt;/dev/null)

#提取public分支的buildid
BUILDID=$(echo &quot;$OUTPUT&quot; | awk &apos;
  /&quot;branches&quot;/ {in_branches=1}
  in_branches &amp;amp;&amp;amp; /&quot;public&quot;/ {in_public=1}
  in_public &amp;amp;&amp;amp; /&quot;buildid&quot;/ {
    gsub(/&quot;/,&quot;&quot;)
    print $2
    exit
  }&apos;)

if [[ -z &quot;$BUILDID&quot; ]]; then
  echo &quot;ERROR: failed to extract buildid&quot; &amp;gt;&amp;amp;2
  exit 1
fi

echo &quot;$BUILDID&quot;
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正常情况下这个脚本应该输出&lt;code&gt;21248816&lt;/code&gt; ，或者类似的id&lt;/p&gt;
&lt;h2&gt;第二步：获取服务端buildID&lt;/h2&gt;
&lt;p&gt;服务端的buildID在&lt;code&gt;appmanifest_730.acf&lt;/code&gt; 这个文件中，通常他在docker里的路径如下&lt;code&gt;/home/steam/cs2-dedicated/steamapps/appmanifest_730.acf&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;docker环境的我们通过以下指令获取，注意修改容器名称再运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;docker exec [修改容器名称] \
  awk -F&apos;&quot;&apos; &apos;/&quot;buildid&quot;/ {print $4}&apos; \
  /home/steam/cs2-dedicated/steamapps/appmanifest_730.acf
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;现在得到结果是&lt;code&gt;21248816&lt;/code&gt; 说明无需更新&lt;/p&gt;
&lt;h2&gt;第三步：检查更新的完整脚本&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;#!/usr/bin/env bash
#请在此处进行配置
# SteamCMD 容器
STEAMCMD_CONTAINER=&quot;steamcmd&quot;
STEAMCMD_BIN=&quot;/home/steam/steamcmd/steamcmd.sh&quot;

# CS2 服务器容器
CS2_CONTAINER=&quot;CS2-normal&quot; #容器名字
CS2_APP_MANIFEST=&quot;/home/steam/cs2-dedicated/steamapps/appmanifest_730.acf&quot; #acf文件位置

# Steam AppID，cs2为730，csgo为740，请自行查阅
APPID=730

# SteamCMD 超时
STEAMCMD_TIMEOUT=60

#更新运行脚本，根据实际情况进行选择
# UPDATE_HANDLER=&quot;docker restart CS2-huifang&quot;
# UPDATE_HANDLER=&quot;/volume2/docker/CS2/update_cs2.sh&quot;
# UPDATE_HANDLER=&quot;docker exec CS2-huifang ./update.sh&quot;
UPDATE_HANDLER=&quot;docker restart CS2-normal&quot;

#log输出函数
log() {
  echo &quot;[$(date &apos;+%Y-%m-%d %H:%M:%S&apos;)] $*&quot;
}

#更新脚本函数
run_update_handler() {
  log &quot;Executing update handler:&quot;
  log &quot;&amp;gt;&amp;gt;&amp;gt; $UPDATE_HANDLER&quot;
  set +e
  eval &quot;$UPDATE_HANDLER&quot;
  RC=$?
  set -e
  if [[ $RC -ne 0 ]]; then
    log &quot;ERROR: Update handler failed with code $RC&quot;
    exit 11
  fi
}

log &quot;CS2 update check started&quot; #检查脚本开始

#检查steamcmd最新的id
log &quot;Querying remote buildid...&quot;
set +e
STEAMCMD_OUTPUT=$(
  docker exec &quot;$STEAMCMD_CONTAINER&quot; \
    timeout &quot;$STEAMCMD_TIMEOUT&quot; \
    &quot;$STEAMCMD_BIN&quot; \
      +login anonymous \
      +app_info_print &quot;$APPID&quot; \
      +quit 2&amp;gt;&amp;amp;1
)
STEAMCMD_RC=$?
set -e
REMOTE_BUILDID=$(echo &quot;$STEAMCMD_OUTPUT&quot; | awk -F&apos;&quot;&apos; &apos;/&quot;buildid&quot;/ {print $4; exit}&apos;)
if [[ -z &quot;$REMOTE_BUILDID&quot; ]]; then
  log &quot;ERROR: Failed to retrieve remote buildid&quot;
  exit 2
fi
log &quot;Remote buildid: $REMOTE_BUILDID&quot; #输出线上最新的构建id

#检查本地的构建id
log &quot;Reading local buildid...&quot;

LOCAL_BUILDID=$(
  docker exec &quot;$CS2_CONTAINER&quot; \
    awk -F&apos;&quot;&apos; &apos;/&quot;buildid&quot;/ {print $4}&apos; &quot;$CS2_APP_MANIFEST&quot; #配置的acf位置变量
)

if [[ -z &quot;$LOCAL_BUILDID&quot; ]]; then
  log &quot;ERROR: Failed to retrieve local buildid&quot;
  exit 3
fi
log &quot;Local buildid : $LOCAL_BUILDID&quot;

#对比本地与steamcmd的版本是否存在差别
if [[ &quot;$LOCAL_BUILDID&quot; == &quot;$REMOTE_BUILDID&quot; ]]; then
  log &quot;RESULT: CS2 server is up to date.&quot;
  exit 0
fi
log &quot;RESULT: UPDATE REQUIRED!!!&quot; 
run_update_handler #调用更新

log &quot;Update handler finished&quot;
exit 10
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;首先我放出脚本，请注意阅读注释，对一些变量进行配置。&lt;/p&gt;
&lt;h2&gt;第四步：执行更新命令&lt;/h2&gt;
&lt;p&gt;需要注意到上面的脚本提供了&lt;code&gt;UPDATE_HANDLER&lt;/code&gt; 这个变量。其中的字符串会作为命令被运行，因为考虑到可能你不是重启容器即可完成更新的情况，可按自身需求进行修改。&lt;/p&gt;
&lt;h2&gt;结尾：&lt;/h2&gt;
&lt;p&gt;最后我们只需要添加一个计划任务来定期执行这个脚本。脚本周期运行即可完成自动检查，并理论上在有新的更新时自动完成更新。不过截止到发稿时，由于并没有新的更新，所以这个脚本并没有完成全部的测试。&lt;/p&gt;
</content:encoded></item><item><title>【笔记】群晖计划任务自动更新SSL证书，已通过Certimate工作流将证书上传到群晖服务器</title><link>https://www.yumehinata.com/posts/diekxchw/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/diekxchw/</guid><pubDate>Sat, 20 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;幻梦为了给博客的域名自动续签SSL证书并且自动更新CDN的SSL证书到最新，所以安装了一个Certimate来进行续签和向提供商部署证书。也正因为已经有了Certimate进行续签工作，所以幻梦极度反感再在群晖上额外部署一个acme.sh。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&lt;em&gt;注意看标题&lt;/em&gt;&lt;/strong&gt;，后文会&lt;strong&gt;省略&lt;/strong&gt;Certimate的安装和工作流如何申请、并部署到CDN提供商的部分，&lt;strong&gt;只讲&lt;/strong&gt;怎么向个人的群晖服务器上传证书。如果想直接抄群晖自动计划的脚本请手动跳转。&lt;/p&gt;
&lt;h2&gt;第一步：部署证书到群晖&lt;/h2&gt;
&lt;p&gt;直入主题我们已经申请好了证书，增加一步节点&lt;strong&gt;部署证书到……&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-025858.png?sign=q5Yf6wYfbUj2BxtUhJns8Drm-iV2t5UIXi0p4ZC2MpM=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;待部署证书选择刚刚申请的证书&lt;/p&gt;
&lt;p&gt;部署目标选择远程主机SSH&lt;/p&gt;
&lt;p&gt;主机提供商授权选择已有的或者新建&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-030136.png?sign=36Pct800jPZZxMrOO8TMDnSrxOIpvjQfumpeQDHq6jk=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;名称自己选择记得住的&lt;/p&gt;
&lt;p&gt;服务器地址，有DDNS的写DDNS的域名，在内网的写内网ip，有固定公网的写公网ip&lt;/p&gt;
&lt;p&gt;服务器端口号同理，看你的群晖设定的端口&lt;/p&gt;
&lt;p&gt;认证方式开启了群晖的SSH功能后，即可通过群晖的用户名与密码进行SSH登录&lt;/p&gt;
&lt;p&gt;用户名与密码就填写群晖登录用的用户名与密码&lt;/p&gt;
&lt;p&gt;完成后提交&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-030531.png?sign=zvmcNiJnfeEzK7x1NFKN_X8CHDhLHIWPIckjI9PmBzA=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意：一定要打开回退使用SCP，除非你开启了群晖的SFTP功能&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文件格式就PEM&lt;/p&gt;
&lt;p&gt;后面的路径，可以先通过scp或者ssh，查看自己想要把证书保存到群晖上挂载的哪一个硬盘的哪一个文件夹&lt;/p&gt;
&lt;p&gt;需要注意一点群晖作为个人存储服务器通常需要保证较高的安全性，所以不建议非必要情况下使用root权限&lt;/p&gt;
&lt;p&gt;没有root权限的情况下，除了挂载的硬盘外，其余位置基本上无法访问。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#生成server.pem
awk &apos;
/BEGIN CERTIFICATE/ {i++}
i==1 {print}
&apos; [你选择保存的服务器证书文件路径]/fullchain.pem &amp;gt; [你选择保存的服务器证书文件路径]/server.pem
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;在后置命令中插入以下代码，记得编辑修改占位的内容&lt;/p&gt;
&lt;p&gt;完成后&lt;strong&gt;保存更改，选择发布，点击运行&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;进入仪表板，查看刚刚运行的工作流是否成功&lt;/p&gt;
&lt;p&gt;成功后我们选择的保存位置会得到以下几个文件&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-031737.png?sign=_HwuMA82VwXkByd5xZF2E6Jl-MDzfOYYEcWgo2Kwwp4=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意观察server.pem文件的大小一定不能是0kb&lt;/p&gt;
&lt;p&gt;到此第一步结束&lt;/p&gt;
&lt;h2&gt;第二步：自动计划&lt;/h2&gt;
&lt;p&gt;首先讲群晖原本提供了一套官方的api，但是那个api过于的繁琐了。我们直接修改默认证书文件夹里的文件，并在修改后重启web服务完成证书的更新。&lt;/p&gt;
&lt;p&gt;我们要修改文件的文件夹会在&lt;code&gt;/usr/syno/etc/certificate/_archive/&lt;/code&gt; 路径下&lt;/p&gt;
&lt;p&gt;我们先通过ssh连接到群晖服务器，进行sudo提权&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo -i
#输入你登录的账户密码，进入root账户
ls /usr/syno/etc/certificate/_archive
#查看_archive文件夹下有还有哪些文件或文件夹
#该路径必须要root权限才可以访问，请不要跳过sudo -i
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;正常情况他会输出四个目录&lt;code&gt;DEFAULT&lt;/code&gt;、&lt;code&gt;INFO&lt;/code&gt;、&lt;code&gt;SERVICES&lt;/code&gt; 和一个没有明确意义字符的文件夹&lt;/p&gt;
&lt;p&gt;我们要找的就是那个看起来像乱码的文件夹，先记住他的名字，等会要用到&lt;/p&gt;
&lt;p&gt;进入群晖的&lt;strong&gt;控制面板&lt;/strong&gt;-选择&lt;strong&gt;任务计划&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-033229.png?sign=Ee9vlFu7KHwDEwj8s2M19zTGgN-ma0a4pVbR0MT06z8=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选择&lt;strong&gt;新增-计划的任务-用户定义的脚步&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-033415.png?sign=UOXw-vvW3dSQJnXkTVnxtih-6BX1c_Y_PSD_eaBoREw=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;任务名称选择一个自己记得住的英文名&lt;/p&gt;
&lt;p&gt;用户账户一定要是root账户，否则无法编辑文件夹里的文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-033552.png?sign=pj2-WXdFISmIfoZyAw6cIfDC-nl6zPcW9qUakk3os_c=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选择的每月重复日期要和Certimate续签的日期一致&lt;/p&gt;
&lt;p&gt;开始时间一定要晚于Certimate续签完成后的时间&lt;/p&gt;
&lt;p&gt;如果之后出现无法保存的情况，先勾选在以下天重运行-每天，然后再切换回在以下日期运行&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;#cert_id为刚刚记下的文件夹名字
CERT_ID=[记得替换成你自己的文件夹名字]
CERT_DIR=/usr/syno/etc/certificate/_archive/$CERT_ID

#下面是刚刚存放证书的路径，记得改成自己的。文件名默认你们和幻梦的一致了
sudo cp [改成自己存放证书的路径]/server.pem    $CERT_DIR/cert.pem
sudo cp [改成自己存放证书的路径]/fullchain.pem $CERT_DIR/fullchain.pem
sudo cp [改成自己存放证书的路径]/privkey.pem   $CERT_DIR/privkey.pem

#设定权限
sudo chown root:root $CERT_DIR/*
sudo chmod 600 $CERT_DIR/*

#重新启动服务
sudo synosystemctl restart nginx
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;修改后复制进去&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-034256.png?sign=sz5qaI8lF5aYKU2-wwEhrWhfH3oRP9d8bsXysd4XUrY=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;根据个人需要自行选择是否发送邮件给自己提醒&lt;/p&gt;
&lt;p&gt;如果需要发邮件的记得在&lt;strong&gt;控制面板-通知设置-电子邮件-发件人&lt;/strong&gt;中添加&lt;/p&gt;
&lt;p&gt;完成后&lt;strong&gt;确定&lt;/strong&gt;保存&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-034533.png?sign=UzaENE0PoHNiHkI3tnpBCkz3pQzqStkMoTNAH_c4UYQ=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;勾选刚刚保存的任务，点击应用完成计划的启动&lt;/p&gt;
&lt;p&gt;也可以选中刚刚保存的计划，点击运行，查看运行效果&lt;/p&gt;
&lt;p&gt;或者进入ssh终端&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;sudo -i
#输入密码
cd /usr/syno/etc/certificate/_archive/[你存放证书的默认文件夹]
#记得替换成你的路径
ls -l
#输出所有文件的修改日期
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251220-035056.png?sign=BB9aPI7DWGMU-qnzfn480lkluLphnwWbH0UnG_5gto4=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;确认文件为近期修改即可&lt;/p&gt;
&lt;h2&gt;END：&lt;/h2&gt;
&lt;p&gt;就这么简单，祝大家玩得愉快&lt;/p&gt;
</content:encoded></item><item><title>【笔记】CS2服务器利用stun或ipv6公网连接，通过webhook让kook机器人将改变的ip与端口发送至频道</title><link>https://www.yumehinata.com/posts/t0vshpco/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/t0vshpco/</guid><pubDate>Sun, 02 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;重大消息，我们的v社终于是支持ipv6域名解析和通过ipv6地址的方式连接到服务器了。不知道什么时候支持的，幻梦也不记得在更新日志里面有提到相关内容，且用且珍惜。（这种好事应该不会撤回吧）&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;记得之前讲过stun方式连接非常复杂吗？因为实在搞不定不规则变化的端口与ip，如果能在端口或者ip变化时能够发送消息就好了。没错lucky是支持webhook的，但是webhook也得有接收端嘛，而且数据持久化必须得依赖一台服务器，那么就存在一个自建的成本了。有没有不需要自建又可以发送消息的服务呢？消息机器人、消息机器人早已不罕见了，qq官方都已经支持了消息机器人，而幻梦通常与好友联机的平台则是kook，正好kook也有官方的机器人支持。&lt;/p&gt;
&lt;h2&gt;创建一个机器人&lt;/h2&gt;
&lt;p&gt;kook开发者平台内创建一个应用。&lt;a href=&quot;https://developer.kookapp.cn/app/index&quot;&gt;https://developer.kookapp.cn/app/index&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251101-235428_1.png?sign=vbjwgJhYPBN4inX4aKbke3bmMgr8Rz_DnACjm9kRR7A=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;在邀请链接里做如下设置&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251101-235648.png?sign=5O1vmObZreXCFBC0_X3nlx4KWZv2MZgME5T_BLr9etM=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251101-235721_1.png?sign=oiOeRUtupEAfi3jOSv3Y_htChWGBmmAsxQIT3RdjsT8=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;通过邀请链接将机器人添加到需要的服务器内&lt;/p&gt;
&lt;h2&gt;消息模板&lt;/h2&gt;
&lt;p&gt;这里就要痛骂kook开发文档了，提供了KMarkdown和卡片消息的功能，但是如果我们直接看文档中的频道消息相关接口的内容你会发现完全无法使用卡片消息，以及KMarkdown会存在各种奇怪bug。两者正确的打开方式是通过机器人的模板管理功能，但是开发者文档里从来没提到模板这一项内容。&lt;/p&gt;
&lt;p&gt;首先是编辑一个自己需要的消息模板&lt;/p&gt;
&lt;p&gt;官方提供了卡片编辑器与markdown编辑器&lt;a href=&quot;https://www.kookapp.cn/tools/message-builder.html#/kmarkdown&quot;&gt;https://www.kookapp.cn/tools/message-builder.html#/kmarkdown&lt;/a&gt;。进入后按需自行切换即可&lt;/p&gt;
&lt;p&gt;设计完消息内容后，点击复制代码&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251102-000523.png?sign=5OA-AmF1tawBRUoi-JfYJRIJJgcYy9EvlU1PztUdvkY=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进入模板管理-新建模板-填写内容&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251102-000725_1.png?sign=QrwRxw3oVpjI6KOugVLOsXfY0UTANL4Oi3HOYU5iH-c=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;模板标题按自己习惯填写不重要&lt;/p&gt;
&lt;p&gt;模板类型暂时只支持twig&lt;/p&gt;
&lt;p&gt;消息类型刚刚选择卡片消息的这里选&lt;strong&gt;卡片消息json&lt;/strong&gt;，kmarkdown的选kmarkdown&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;测试数据里面不要填内容&lt;/strong&gt;，测试完他也不会自己删掉的&lt;/p&gt;
&lt;p&gt;把刚刚复制的代码填到模板内容里&lt;/p&gt;
&lt;p&gt;twig语法我们这里不做介绍，可自行点击官方提供的twig文档查看。&lt;/p&gt;
&lt;p&gt;我们这里只需要twig的变量功能&lt;code&gt;{{content}}&lt;/code&gt; 这对于用过vue开发的人应该已经非常熟悉了吧。&lt;/p&gt;
&lt;p&gt;在需要变量的地方填入这个，就像这样&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251102-001335_1.png?sign=BOmqe9S5mg8LPFwPCnwlQd2SKiuHPzsOknVyBzwQZSU=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里的&lt;code&gt;data.ip&lt;/code&gt; 是要根据之后你webhook请求中的content来决定的，但是&lt;code&gt;data&lt;/code&gt;这个是全局变量不需要调整的。开发者文档里又没提，还是看生成示例数据得到的。&lt;/p&gt;
&lt;p&gt;设置完成后点击提交&lt;/p&gt;
&lt;h2&gt;Lucky中设置webhook&lt;/h2&gt;
&lt;p&gt;在正常配置完stun的相关部分后，我们打开webhook开关。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20251102-002111.png?sign=uV3FZolx6ixkXAyam5ATig2vUEG7CxBm5P9-gOggtTA=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;接口地址是&lt;code&gt;https://www.kookapp.cn/api/v3/message/create&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;请求头是两部分&lt;code&gt;Authorization: Bot&lt;/code&gt; (这里确实有一个空格)&lt;code&gt;你的机器人token令牌&lt;/code&gt; token去机器人中找，连接方式下有一个Token点击复制。&lt;/p&gt;
&lt;p&gt;最后这一行是&lt;code&gt;Authorization: Bot 123123123&lt;/code&gt; 这样一个效果&lt;/p&gt;
&lt;p&gt;请求体：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{&quot;type&quot;: 10,
&quot;target_id&quot;:&quot;&quot;,
&quot;guild_id&quot;:&quot;&quot;,
&quot;content&quot;:&quot;{\&quot;ip\&quot;:\&quot;#{ipAddr}\&quot;}&quot;,
&quot;template_id&quot;:
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;type&lt;/code&gt; 只有两个值9和10，9是kmarkdown或者普通消息，10是卡片消息&lt;/p&gt;
&lt;p&gt;&lt;code&gt;target_id&lt;/code&gt; 是频道的id，服务器内的频道右键复制id&lt;/p&gt;
&lt;p&gt;&lt;code&gt;guild_id&lt;/code&gt; 是服务器id，服务器右键复制id。开发者文档里又没写，只有测试报错才说要写guild_id&lt;/p&gt;
&lt;p&gt;&lt;code&gt;content&lt;/code&gt; 是我们需要传入的内容，可以简单粗暴的理解为上文的&lt;code&gt;data&lt;/code&gt; 全局变量等于&lt;code&gt;content&lt;/code&gt; ，这里可以注意到我们的&lt;code&gt;content&lt;/code&gt;用了转义字符，因为&lt;code&gt;content&lt;/code&gt;只允许字符串，但是实际上最后又可以被转换成对象。&lt;/p&gt;
&lt;p&gt;之前幻梦设置的是&lt;code&gt;{{data.ip}}&lt;/code&gt; 这里传的就自然是&lt;code&gt;ip&lt;/code&gt;这个变量和他的值了&lt;/p&gt;
&lt;p&gt;&lt;code&gt;template_id&lt;/code&gt; 是刚刚创建的模板的id，去模板管理中复制即可&lt;/p&gt;
&lt;h2&gt;结尾&lt;/h2&gt;
&lt;p&gt;这个其实没什么困难的，如果有详细的开发者文档的话。可是他没有，并且还留了一堆的坑。&lt;/p&gt;
&lt;p&gt;webhook中的内容填完了，点击测试发送一个消息看看是否正确。正确的点击修改进行保存。&lt;/p&gt;
&lt;p&gt;祝大家玩得愉快&lt;/p&gt;
</content:encoded></item><item><title>【笔记】通过shizuku+installerX安装应用程序</title><link>https://www.yumehinata.com/posts/89nhqrzi/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/89nhqrzi/</guid><pubDate>Tue, 21 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;本来不想写这个内容的，但是最近某些厂商越来越不安分了，于是收集整理了一些&lt;strong&gt;无需root&lt;/strong&gt;干掉或者替代自带的&lt;strong&gt;应用包管理组件&lt;/strong&gt;的方法。至于为什么不root，或者不买能root的手机？大概是因为手机上有了一些如果root了可能会导致财产损失的应用，有这种应用的懂的都懂，尽量避免root或者越狱吧，毕竟就算是开源也顶不住供应链投毒。&lt;/p&gt;
&lt;h2&gt;shizuku+installerX+np/mt文件管理器&lt;/h2&gt;
&lt;p&gt;shizuku的版本其实还挺多的，不考虑未在github开源的分支。没有特别需求的推荐原版的&lt;a href=&quot;https://github.com/RikkaApps/Shizuku/releases&quot;&gt;RikkaApps/Shizuku&lt;/a&gt;（目前原版疑似加入了无root自启动功能？）；希望能无root通过无线adb来开机自启动的推荐这个分支&lt;a href=&quot;https://github.com/pixincreate/Shizuku/releases&quot;&gt;pixincreate/Shizuku&lt;/a&gt;；幻梦在用的是另一位作者在以上进行二改的版本&lt;a href=&quot;https://github.com/yangFenTuoZi/Shizuku&quot;&gt;yangFenTuoZi/Shizuku&lt;/a&gt;，这里只是贴出来不做推荐。顺带一提现在很多作者的releases里都不是最新的安装包，得去actions里翻一下最新的文件。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/Screenshot_2025-10-21-02-36-39-810_moe.shizuku.privileged.api.jpg?sign=E4bvziUD2rVCPq-mcAzh-blCNzSz2HN4_bbDqu1b3fk=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;shizuku的设置其实没什么好说的，就这个开机启动（无线调试）勾上然后根据提示启动服务就好了。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/Screenshot_2025-10-21-02-39-46-149_com.miui.securitycenter.jpg?sign=o-7BNvoeNmcZ3AqmZGKRQtojjz64aYCCgruFYEDpDOs=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/Screenshot_2025-10-21-02-39-37-850_com.miui.securitycenter.jpg?sign=oOro1VTEDSsGkpUyF_88CzqUb494AwXis2hYSlV014Q=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;shizuku外面的设置其实比较重要，耗电记得无限制，自启动记得打开，有些情况可能shizuku没开通知权限也要记得开起来。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/Screenshot_2025-10-21-02-42-36-835_com.android.settings.jpg?sign=Y76lUt-2SBoW6RY-NUr13jl9JRG8QaCM3CWD-rfI45E=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;开发者选项里除了在shizuku启动时会提示开启的选项，还要记得开启&lt;strong&gt;usb安装、usb调试（安全设置）、停用adb授权超时功能&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;到这里shizuku的部分暂告段落，接下来安装installerX。这里就要提一点，幻梦选installerX是因为这个软件长得好看，除了这个以外还有很多安装器可以选择的。&lt;/p&gt;
&lt;p&gt;原版&lt;a href=&quot;https://github.com/iamr0s/InstallerX&quot;&gt;iamr0s/InstallerX&lt;/a&gt;在23年作者就停止更新归档了。不过，目前在a16版本下依旧能正常工作。在幻梦写这篇笔记的时候发现github有了一个新的分支&lt;a href=&quot;https://github.com/wxxsfxyzm/InstallerX-Revived&quot;&gt;wxxsfxyzm/InstallerX-Revived&lt;/a&gt;，值得关注后续维护状态。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/Screenshot_2025-10-21-02-50-53-453_com.rosan.installer.x.jpg?sign=XPTU3SyIivxMKMLLlPOqjNITKUmTf9axFCjkDAzxboQ=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/Screenshot_2025-10-21-02-51-45-067_com.rosan.installer.x.jpg?sign=CDRoUd1d-v7v6uWl0LxJOrSDzO6_BXGTmEFoWUXyj30=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;installerX内如此设置，设置完成记得右下角点击保存。&lt;/p&gt;
&lt;p&gt;另外锁定为默认安装器记得点一下，虽然基本上就是一个心里安慰的作用。&lt;/p&gt;
&lt;p&gt;还有记得在shizuku里授权installerX。&lt;/p&gt;
&lt;p&gt;到这里其实就结束了，标题里面为什么还有一个文件管理器？因为在非root的情况下无法将系统自带的文件管理器默认包管理组件换成installerX，上一步的锁定为默认安装器主要是针对比如&lt;strong&gt;应用宝、taptap、第三方浏览器&lt;/strong&gt;默认调用installerX。mt管理器在有shizuku授权的情况下，其实无需installerX即可完成应用安装（np管理器不行）。&lt;/p&gt;
&lt;h2&gt;Dhizuku+installerX&lt;/h2&gt;
&lt;p&gt;installerX还有一个优势是兼容Dhizuku授权，Dhizuku的优势比shizuku主要在于开机启动不会掉权限（因为Dhizuku依赖的是设备管理员权限而非ADB），但是这个方案不做推荐，因为会影响应用双开，而且使用过应用双开的用户会出现各种各样奇怪的错误。&lt;/p&gt;
&lt;h2&gt;CTS兼容性测试套件&lt;/h2&gt;
&lt;p&gt;这个方案依旧不做推荐。CTS能够把厂商的包装管理器换成安卓原版安装器，也就能把许多系统定制的功能变成安卓原版，效果类似于关闭miui/hyperos优化，但是效果更强且不可逆（连miui优化开关效果都可逆了，这个效果不可逆知道后果有多严重了吧）。链接还是贴在这里了&lt;a href=&quot;https://source.android.google.cn/docs/compatibility/cts/downloads?hl=zh-cn&quot;&gt;https://source.android.google.cn/docs/compatibility/cts/downloads?hl=zh-cn&lt;/a&gt;，毕竟不是每一个人都喜欢厂商的定制系统，很多人还是喜欢原生安卓的，&lt;strong&gt;但是用了CTS出现问题别再去给厂商反馈问题了&lt;/strong&gt;。&lt;/p&gt;
</content:encoded></item><item><title>【笔记】解决开启openclash后steam下载速度异常</title><link>https://www.yumehinata.com/posts/kavvzzeq/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/kavvzzeq/</guid><pubDate>Sat, 27 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;其实没什么复杂的，直接看这个&lt;a href=&quot;https://github.com/coolsnowwolf/lede/issues/7188&quot;&gt;阅读原文&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;- DOMAIN-SUFFIX,steamcontent.com,DIRECT
- DOMAIN-SUFFIX,steamstatic.com,DIRECT
- DOMAIN-SUFFIX,steamserver.net,DIRECT
- DOMAIN-SUFFIX,test.steampowered.com,DIRECT
##- DOMAIN-SUFFIX,api.steampowered.com,DIRECT 默认注释掉了。当前网络环境api子域名已被污染，直连可能会导致登录时出现通讯失败
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;基本上把这些加上就可以解决下载速度的问题。&lt;/p&gt;
&lt;p&gt;找不到写哪的。覆写设置-&amp;gt;规则设置-&amp;gt;自定义规则（勾选上）&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20250927-004359.png?sign=tkpjuOM4F_k5oMbS22aOJ9_bAmb3kTjqmjlYf7JF808=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;填写在这里&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/QQ20250927-004538.png?sign=aIiJ6V88EvufDTTcVXFBdX27RP7KaySNrFC5Rwp7Fb0=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
</content:encoded></item><item><title>【笔记】头像绘制实战？反正把颜色上了，阴影画了</title><link>https://www.yumehinata.com/posts/allzmh1j/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/allzmh1j/</guid><pubDate>Mon, 15 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;上一篇里幻梦粗草稿，它有一些严重的问题。比如头发的位置，脸的形状，缺少发饰，头饰结构缺少。&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/%E8%8D%89%E7%A8%BF3.png?sign=B0aVcSY5bcLWp9vWrvh0bQ3kXjTnmQj1xnOBlJ03M-E=:0&quot; alt=&quot;草稿3.png&quot; /&gt;&lt;/p&gt;
&lt;p&gt;首先，右侧下方的头发不应该凭空生出，上方应该显示出下方头发的信息，亦或是完全遮挡导致缺少信息。&lt;/p&gt;
&lt;p&gt;然后左侧这是一个头略微向左转动的视角。根据透视关系左侧应该会小一些，右侧大一些。而这张图没有显示出这样的透视关系。&lt;/p&gt;
&lt;p&gt;同时角色的头像左侧倾斜，其余头发都有向左倾斜符合引力的趋势。而刘海缺少了这样的趋势。&lt;/p&gt;
&lt;p&gt;脸还是老问题，有点胖了，接着改。瓜子脸就往尖锐的趋势画，符合三庭五眼和自己理解的美型。&lt;/p&gt;
&lt;p&gt;应该有的发卡没有画。&lt;/p&gt;
&lt;p&gt;根据透视关系发箍的部分也缺失了(这个视角下头发不应该遮挡大部分发箍)&lt;/p&gt;
&lt;p&gt;锁骨结构错误，锁骨应该粗略的分为三段的结构，上图只有两段。&lt;/p&gt;
&lt;p&gt;内衣的结构存在问题。&lt;/p&gt;
&lt;p&gt;视角只是略低于平视，眼睛不应该出现很大的弧形的透视形状。&lt;/p&gt;
&lt;p&gt;所以略微改了一下&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/%E5%A4%B4%E5%83%8F%E8%8D%89%E7%A8%BF.png?sign=0IJQhARXOaSCuGQUCKMYJ6H44L617rP4jH_Q-lSU0YY=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这个版本其实是我最满意的一个草稿。但是脖子有一点问题，发箍与头发衔接处还存在问题，耳朵结构存在问题(幸好耳朵不用露出来)&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/%E5%A4%B4%E5%83%8F%E8%8D%89%E7%A8%BF3.png?sign=gRuK68WVVwJRzEbGr_ae5flX2yDOzV-605NDuHYT_Eg=:0&quot; alt=&quot;&quot; /&gt;修改了脖子的问题，未解决眼睛以及留海与发箍的连接问题&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/%E7%BA%BF%E7%A8%BF.png?sign=Alh2lIBn-7TwPKvFzkCESO7FpHN2dWI5yIX_Dy3RipM=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;修改了袖子的外观，修改了眼睛，修改了头发的样式（不过这个头发不如前面那个灵动），解决了内衣的边缘低于正常角度的问题，修复了锁骨的位置及形状&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/%E5%9B%BA%E6%9C%89%E8%89%B21.png?sign=7qfIKjak65LkgKdfcNOoe8FZbPcVKahpVoY-tl2S3fc=:0&quot; alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;简单上色&lt;/p&gt;
&lt;p&gt;&lt;img src=&quot;https://openlist.yumehinata.com/d///Tianyicloud/blog/%E9%98%B4%E5%BD%B11.png?sign=acqId5jsQ_qoYYKdm-L6F98w0VPQc0EuWDO4kSJMsdI=:0&quot; alt=&quot;&quot; /&gt;加上一些阴影，不过这个阴影还是有问题，等着后面和老师沟通再做修改了&lt;/p&gt;
</content:encoded></item><item><title>【教程】iStoreOS基于docker部署在线订阅转换服务（subconverter）</title><link>https://www.yumehinata.com/posts/uo4kyybz/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/uo4kyybz/</guid><pubDate>Mon, 08 Sep 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;有很多人对订阅转换服务有心里洁癖。当然这是对的，确实有部分转换服务存在偷链接、偷节点的问题，并且在目前的环境下，越来越多的在线转换服务都在失效的边缘。因此在局域网环境下部署一个不会失效的在线订阅转换服务，会成为越来越多人的选择。同时幻梦也不建议将自建的转换服务开放给他人使用，以免引起不必要的麻烦。&lt;/p&gt;
&lt;p&gt;为什么使用subconverter？目前subconverter支持的协议数量较多，并且符合openclash调用在线订阅转换api的规范。subconverter就是自然而然的选择。&lt;/p&gt;
&lt;p&gt;什么是subconverter？本篇文章中的subconverter，指的是&lt;a href=&quot;https://github.com/tindy2013/subconverter&quot;&gt;tindy2013/subconverter&lt;/a&gt;这个开源项目。介绍就不做了，直接去github看readme吧。&lt;/p&gt;
&lt;h2&gt;第一步：拉取镜像&lt;/h2&gt;
&lt;p&gt;需要明确，幻梦这次使用的subconverter，作者tindy2013已经在DockerHub发布了这个项目的镜像，我们可以直接拉取使用，无需使用自行制作的镜像。DockerHub的链接我就放在这里了，&lt;a href=&quot;https://hub.docker.com/r/tindy2013/subconverter&quot;&gt;查看原文&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;**提示：**tindy2013/subconverter，目前不支持Vless节点。如需vless支持，请使用&lt;a href=&quot;https://github.com/asdlokj1qpi233/subconverter&quot;&gt;asdlokj1qpi233/subconverter&lt;/a&gt;，DockerHub链接请&lt;a href=&quot;https://hub.docker.com/r/asdlokj1qpi23/subconverter&quot;&gt;查看原文&lt;/a&gt;。两者的使用方法一致，只需在拉取镜像这一步替换为&lt;code&gt;asdlokj1qpi23/subconverter&lt;/code&gt; （&lt;strong&gt;复制这个，用户名github与DockerHub中并不一致&lt;/strong&gt;）。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;直接输入&lt;code&gt;tindy2013/subconverter&lt;/code&gt; 或&lt;code&gt;asdlokj1qpi23/subconverter&lt;/code&gt;然后点击&lt;strong&gt;拉取&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;拉取完成后可以在下方看到出现了一个这样的新的镜像文件&lt;/p&gt;
&lt;p&gt;如果拉取时出现网络问题。请开启魔法，或者安装并使用Kspeeder&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第二步：添加并启动一个容器&lt;/h2&gt;
&lt;p&gt;从https://github.com/tindy2013/subconverter/blob/master/README-docker.md，可得到docker-compose&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;---
version: &apos;3&apos;
services:
  subconverter:
    image: tindy2013/subconverter:latest
    container_name: subconverter
    ports:
      - &quot;15051:25500&quot;
    restart: always
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们在iStoreOS的创建docker容器中做出如下编辑&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点击&lt;strong&gt;提交&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;左侧&lt;strong&gt;打钩&lt;/strong&gt;，点击&lt;strong&gt;启动&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;启动后，我们可以得到以上的地址和端口号。我们直接访问他&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;如果出现以上的内容证明服务部署成功&lt;/p&gt;
&lt;p&gt;另外如果直接访问路由器的ip+端口，应该会显示拒绝访问。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第三步：测试并使用&lt;/h2&gt;
&lt;p&gt;将我们刚刚部署的订阅转换服务的ip+端口按格式&lt;code&gt;IP地址:端口号/sub&lt;/code&gt;填入对应的位置&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;正常调用运行日志中将会出现类似以下内容&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;注意以上三个框内的内容，分别是成功调用了转换服务的api，成功完成转换并下载，转换后的配置文件正确。&lt;/p&gt;
&lt;h2&gt;第四步：结束&lt;/h2&gt;
&lt;p&gt;到这里容器的部署工作已经完成了，这篇博客其实主要就是介绍&lt;a href=&quot;https://github.com/tindy2013/subconverter&quot;&gt;tindy2013/subconverter&lt;/a&gt;这个项目。记得给你使用的项目仓库点一个star&lt;/p&gt;
&lt;h2&gt;疑难杂症：&lt;/h2&gt;
&lt;p&gt;本来幻梦不想写这一块的，但是幻梦在使用中确实遇到了这些问题，顺便帮大家解决一下。&lt;/p&gt;
&lt;p&gt;首先是vless节点的支持问题，这个没什么好说的换&lt;code&gt;asdlokj1qpi23/subconverter&lt;/code&gt; 就可以解决了。&lt;/p&gt;
&lt;p&gt;然后是比较麻烦的一点，subconvertor会有&lt;code&gt;SubConverter-Request&lt;/code&gt;和&lt;code&gt;SubConverter-Version&lt;/code&gt;的请求头。部分机场为了防止节点或者订阅链接泄漏，只要检测到这两个请求头就会给你返回空的值，这样我们就会收到一个报错。又是经典的“我为了你好”问题，开发者可能是为了方便机场亦或是使用者的适配，结果机场反过来检测这两个值给你ban了（那确实是适配了）。详细的issue看这两个&lt;/p&gt;
&lt;p&gt;【1】&lt;a href=&quot;https://github.com/tindy2013/subconverter/issues/573#issuecomment-1436012060&quot;&gt;https://github.com/tindy2013/subconverter/issues/573#issuecomment-1436012060&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;【2】&lt;a href=&quot;https://github.com/asdlokj1qpi233/subconverter/issues/7&quot;&gt;https://github.com/asdlokj1qpi233/subconverter/issues/7&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;问题还是要解决，其中一个issue中提供了一个去掉请求头的分支。但是已经两年没更新了，也不支持vless和更多新协议。那就只剩两个办法，自己修改编译源码，或者在订阅链接上做文章。&lt;/p&gt;
&lt;p&gt;幻梦果断选第二种，还是反代。openresty反代订阅链接的域名或者ip，而且是局域网内使用自然无需域名和https的需求。我们只需要增加一条&lt;code&gt;more_clear_input_headers &apos;SubConverter-Request&apos; &apos;SubConverter-Version&apos;;&lt;/code&gt; 一切就大功告成。&lt;/p&gt;
</content:encoded></item><item><title>【白嫖】使用Lark建立邮箱服务</title><link>https://www.yumehinata.com/posts/oyeiedxp/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/oyeiedxp/</guid><pubDate>Sun, 24 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：&lt;/h2&gt;
&lt;p&gt;这个博客网站其实一直没有一个消息通知的服务（虽然这个博客今后肯定不会开放注册的，但是评论功能还是可以有的。为了方便幻梦收到大家的评论消息，以及防止幻梦自己忘记密码。所以一个拥有一个邮件服务就迫在眉睫了）。&lt;/p&gt;
&lt;p&gt;这里就有两种解决办法，自己搭一个和用公共的服务。由于这个台可怜的小主机过于羸弱的性能，幻梦果断选择公共服务。这个时候又有几种方案，outlook、gmail、163、qq数不胜数的选项摆在面前。但是这些幻梦都不想用（主要是不想在这些平台上注册多个账号，最好能通过企业化的管理进行使用）。有人可能要说微软outlook其实支持这种企业管理，是的但是这是之前。之前的e5账户或者e3账户其实送了这么个服务（虽然没什么人用，都是冲着office365去的，而且容易被当做垃圾邮件），可是现在就算是e5账号也是订阅制了，白嫖退散。&lt;/p&gt;
&lt;p&gt;然后幻梦在逛LinuxDo论坛的时候看到了这样一个帖子&lt;a href=&quot;https://linux.do/t/topic/162064/1&quot;&gt;&lt;strong&gt;《分享一个几乎0成本自建邮箱服务器的办法》&lt;/strong&gt;&lt;/a&gt;**。**这就完美符合需求了，而且飞书作为与钉钉、企业微信齐名的企业通讯工具，工具的使用效率一定也很不错。&lt;/p&gt;
&lt;h2&gt;第一步：幻梦已经决定了，就用你来做邮件服务&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;进入Lark官网。&lt;a href=&quot;https://www.larksuite.com/ja_jp&quot;&gt;https://www.larksuite.com/ja_jp&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;注册（目前Lark需要全局代理才能正常注册）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;现在的注册方式已经不像是原文的可以注册组织，只能通过谷歌或者别的邮箱进行注册。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;注册登入后，选择企业法人（不要选个人用户）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进入下一步&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;第二步：邮箱域名管理&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;进入管理后台&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;域名管理中添加域名&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这一步很关键，看清楚设置。完成后&lt;strong&gt;立即验证&lt;/strong&gt;，完成验证进入下一步&lt;/p&gt;
&lt;h2&gt;第三步：设置公共邮箱&lt;/h2&gt;
&lt;p&gt;这一步就开始和原文不同了&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;设置完成后点击&lt;strong&gt;创建&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这里面有我们需要的smtp服务的所有信息。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;结束&lt;/h2&gt;
&lt;p&gt;到这里就完成了，把地址、端口、密码，填到你需要的地方就可以了。&lt;/p&gt;
</content:encoded></item><item><title>【笔记】记一次头像绘制实战1</title><link>https://www.yumehinata.com/posts/8frytnte/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/8frytnte/</guid><pubDate>Sun, 24 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;题记？&lt;/h2&gt;
&lt;p&gt;幻梦大概是第一个画了一张图然后做了一次笔记的家伙（还是以这种形式）。另外这张图也绝非终稿，后面肯定还要请教老师进行修改的，所以说这种记录一定有后续。另外这个不是给大家的教程，这是幻梦的&lt;strong&gt;笔记&lt;/strong&gt;，别照着学啊（应该也不会有人把这个当正确教程吧）。&lt;/p&gt;
&lt;h2&gt;1.初稿&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;很显然这张图有大问题，是个人都能看出为什么脸这么大。&lt;/p&gt;
&lt;p&gt;当然除了这个还有其他问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;这其实是一个仰视角的机位，仰视角怎么会看到这么多头顶的装饰？（&lt;strong&gt;透视问题，记得仔细分析&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;手画的什么东西，没找任何参考最后画出这个鬼样子。（&lt;strong&gt;尽量别画手，等单独练个几十张手的画再去搞&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;衣服其实也有大问题，不要把一些应该有明显区别的线连到一起（&lt;strong&gt;刻意的造成视角差&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;为什么脸大？因为头发画的不对，一切大小关系都是通过对比得到的（&lt;strong&gt;头发越蓬松，脸越小&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;别总惦记着头发要紧贴着头，这是画图不是建模，怎么好看怎么来（&lt;strong&gt;背后的头发，侧脸的情况下就看着往后拉，显得脸小就行&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;心夏这件是冲锋衣，宽松褶皱没画出来（&lt;strong&gt;宽松的衣服有褶皱&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;2.自主修改1&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这张其实依旧有问题，只是问题小了很多。首先脸成功的小了一些，但是实际上最后效果没小多少。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;左侧头发片的角度导致线条与脸相近，造成视角差显得脸大。（&lt;strong&gt;头发还得往外移&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;上面这个问题同时还是视角差的问题（&lt;strong&gt;不是同一个结构的物体的线条就画开一点，角度、距离做出区分&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;背后的头发还是太近，再挪&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;正面的小刘海，仔细观察应该是从发箍里钻出来的，高度应该高于两侧的斜刘海。（&lt;strong&gt;透视问题&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;3.自主修改2&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;脸稍微正常了一点，但是仔细看还是有问题。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;脸部被手挡住，但是挡的位置刚好是转折处（&lt;strong&gt;又是视角问题，显得脸大了&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;左边的袖子还是不够宽松&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;前刘海依旧不够高&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;正面的头发位置有点乱（线条的走向最好一致，右侧那个v字形有点不对）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;右侧的头发细节画的太多了&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;衣服的线条不够笔直，显得衣服褶皱了&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;鼻子画的太大了&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;嘴巴显得不太开心？&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;嘴的位置也不太合理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;右上角的头发应该尽量的连在一起效果更好。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;和眼眶相比，眼瞳的大小显得小了&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;线条不够流畅（&lt;strong&gt;能一笔到位的，一笔到位&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;接上一条，这是幻梦自己的画，不再是完全的临摹，线条弯曲就按照自己的自然习惯来画，和线稿不完全贴在一起也不不要紧（&lt;strong&gt;线条要自然，画的要放松&lt;/strong&gt;）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;4.自主修改3&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这其实已经是通过幻梦个人审美和扣细节能做到的最大程度了，接下来只能让老师帮忙了。&lt;/p&gt;
</content:encoded></item><item><title>【开箱即用】基于Edgeone Pages Functions的随机图API</title><link>https://www.yumehinata.com/posts/xnptbucx/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/xnptbucx/</guid><pubDate>Sat, 16 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;Edgeone Pages Functions&lt;/h2&gt;
&lt;p&gt;目前的Pages Functions是基于边缘函数实现，边缘函数目前有使用量限制，而Pages Functions目前处于测试阶段所以并不限制使用。所以先部署一个随机图api玩玩。&lt;/p&gt;
&lt;h2&gt;第一步：准备好文件&lt;/h2&gt;
&lt;p&gt;先下载我们需要的文件，我再github写好了一个能够开箱即用的项目。&lt;a href=&quot;https://github.com/YumeHinata/random-wallpaper&quot;&gt;https://github.com/YumeHinata/random-wallpaper&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;直接下载zip文件，根据需求做出修改。&lt;/p&gt;
&lt;p&gt;首先如果你不像我一样反代了pixiv来做随机图片，而是用了比较常见的CF R2或者微博图床等方式，请直接删掉zip中的pixiv.js，并且删除&lt;code&gt;/functions/index.js&lt;/code&gt;第215行&lt;code&gt;&amp;lt;script src=&quot;../pixiv.js&quot;&amp;gt;&amp;lt;/script&amp;gt;&lt;/code&gt; 。这样就不会出现一些意料之外的报错。&lt;/p&gt;
&lt;p&gt;其次如果你看了我之前的反代pixiv的教程并且也想用来做随机图片api，那你就有福了，这个项目可以开箱即用。&lt;/p&gt;
&lt;p&gt;在做好处理后还是打包成一个zip文件，等待后续上传部署。&lt;/p&gt;
&lt;h2&gt;第二步：在Pages中创建项目&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;选择创建项目-直接上传&lt;/p&gt;
&lt;p&gt;把我们准备好的zip文件拖进去上传&lt;/p&gt;
&lt;p&gt;项目名称根据自己需要来命名&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;点击开始部署&lt;/p&gt;
&lt;p&gt;部署完成后会弹窗提示你部署成功&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;此时直接打开页面会是这个样子&lt;/p&gt;
&lt;p&gt;我们需要根据readme.md的提示来完成配置&lt;/p&gt;
&lt;h2&gt;第三步：添加环境变量&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;URL_LIST&lt;/code&gt;的值应该为一个URL链接，链接到一个txt文本文件，文本文件内每行写一个图片URL链接。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;INDEX_TITLE&lt;/code&gt;的值为一个字符串，用来控制首页的title标题，在这里填入你想要的网页标题。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;OVERVIEW_HTML&lt;/code&gt;的值应该为html格式，这里填入你对这个随机图片api的概述。例如：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&amp;lt;p&amp;gt;此API提供来自Pixiv社区的随机图片，所有图片版权归原作者所有。&amp;lt;/p&amp;gt;
&amp;lt;p&amp;gt;API仅提供图片展示服务，请勿用于商业用途。&amp;lt;/p&amp;gt;
&lt;/code&gt;&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;TOKEN_TYPE&lt;/code&gt;的值可以选择&lt;code&gt;0&lt;/code&gt;、&lt;code&gt;A&lt;/code&gt;、&lt;code&gt;D&lt;/code&gt;。0代表无需添加token鉴权功能，A、D对应这个edgeone的token鉴权A鉴权与D鉴权。需要注意的是鉴权的设置中时间格式应该为十进制（时间戳），鉴权加密串参数名称统一为&lt;code&gt;token&lt;/code&gt;，鉴权时间戳参数名称为&lt;code&gt;t&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;SECRET_KEY&lt;/code&gt;的值请填入&lt;code&gt;主鉴权密钥&lt;/code&gt;。如果你不使用token鉴权值填写&lt;code&gt;0&lt;/code&gt;。&lt;/p&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;写完这些，重新再部署一遍。&lt;/p&gt;
&lt;h2&gt;第四步：构建部署&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;其实没什么可讲的，点击左侧的&lt;code&gt;构建部署&lt;/code&gt;-&lt;code&gt;新建部署&lt;/code&gt;，后面和第二步的顺序一样再重新部署一遍。&lt;/p&gt;
&lt;p&gt;部署完以后把前一个删掉，就像这样。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第五步：查看效果，并绑定域名&lt;/h2&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样就很顺利的完成了。&lt;/p&gt;
&lt;p&gt;接下去就绑定一个自己的域名。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;项目设置，添加自定义域名，自动生成免费证书。&lt;/p&gt;
&lt;p&gt;就结束了。&lt;/p&gt;
&lt;p&gt;同时欢迎大家来用这个&lt;code&gt;https://rdimg.yumehinata.com/random-wallpaper&lt;/code&gt; 的随机图片api。&lt;/p&gt;
</content:encoded></item><item><title>【白嫖】Edgeone反代Pixiv</title><link>https://www.yumehinata.com/posts/my4jqevf/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/my4jqevf/</guid><pubDate>Sat, 09 Aug 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;警告：不要跳过这部分内容&lt;/h2&gt;
&lt;p&gt;反代不属于自己的网站本身具有一定的法律风险（不管是版权问题，亦是钓鱼网站嫌疑），各大运营商也是被举报就严打的态度（有些臭名昭著的公司尤其喜欢打小报告）。所以要使用这个方案就&lt;strong&gt;不要跳过&lt;/strong&gt;【鉴权】这一步，这不仅&lt;strong&gt;是对你的保护&lt;/strong&gt;，也是对大家&lt;strong&gt;能长久使用这个方案的保证&lt;/strong&gt;。另外&lt;strong&gt;做好&lt;/strong&gt;域名被edgeone&lt;strong&gt;封禁&lt;/strong&gt;的准备，请&lt;strong&gt;知悉&lt;/strong&gt;这个方案是&lt;strong&gt;有风险的&lt;/strong&gt;，要彻底无风险就用上一篇文章的方法。如果很不幸，你被封禁了，&lt;strong&gt;&lt;em&gt;这和幻梦一点关系没有&lt;/em&gt;&lt;/strong&gt;，因为你清楚这个&lt;strong&gt;风险&lt;/strong&gt;和&lt;strong&gt;后果&lt;/strong&gt;，&lt;strong&gt;&lt;em&gt;不想承担风险同时无力承受后果&lt;/em&gt;&lt;strong&gt;的&lt;/strong&gt;请关掉&lt;/strong&gt;这篇教程。&lt;/p&gt;
&lt;h2&gt;前言：为什么又换到edgeone&lt;/h2&gt;
&lt;p&gt;看了上一篇文章的应该知道，Nginx反代已经实现了功能，同时文章也提到“因为一些原因”我们不通过魔法无法完成这个工作，而魔法就不那么稳定了，于是幻梦把目光投向了腾讯的edgeone。&lt;/p&gt;
&lt;p&gt;目前腾讯凭借edgeone这个功能，可以说已经是是仅次于cloudflare的大善人。而且edgeone作为cdn的功能已经远远超越了现在的cloudflare要依靠ns的cdn功能，甚至超过了cloudflare partner时期（edgeone支持ipv6回源，cloudflare做得到吗？），cloudflare除了打不死其实已经没有什么优势了。更重要的是相较于其他厂商的cdn，edgeone除了能被白嫖外，比较多的自定义规则也是我们能够实现反代的核心。&lt;/p&gt;
&lt;h2&gt;第一步：默认你已经拥有了免费版edgeone&lt;/h2&gt;
&lt;p&gt;幻梦这边默认你已经有了免费版的edgeone套餐，并且完成了域名的绑定，并且能够熟练使用各家厂商的dns解析服务。文章将专注于edgeone的配置。&lt;/p&gt;
&lt;p&gt;首先来到“域名服务-域名管理”，添加上反代域名。如图所示&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;源站域名为&lt;code&gt;i.pximg.net&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;协议选择&lt;code&gt;https&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;回源host头选择使用&lt;code&gt;源站域名&lt;/code&gt; （这步的重要性类似于上一篇文章的SNI回源的设置）&lt;/p&gt;
&lt;p&gt;点击&lt;code&gt;完成&lt;/code&gt;进行保存&lt;/p&gt;
&lt;h2&gt;第二步：设置站点加速的全局配置&lt;/h2&gt;
&lt;p&gt;“站点加速”，按照下图进行设置。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这边每项不做过多解释，个人需求可以更具页面上的文档进行调整。&lt;/p&gt;
&lt;h2&gt;第三步：规则引擎设置header Referer&lt;/h2&gt;
&lt;p&gt;还是更具上一篇文章的经验，要设置一个referer才能解决403错误的问题。按下图设置&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;判断host等于加速域名，执行下面的操作&lt;/p&gt;
&lt;p&gt;修改http回源请求头，这一步相当于&lt;code&gt;proxy_set_header Referer &quot;https://www.pixiv.net/&quot;;&lt;/code&gt;&lt;/p&gt;
&lt;p&gt;于是选择设置-自定义-&lt;code&gt;Referer&lt;/code&gt; -&lt;code&gt;https://www.pixiv.net/&lt;/code&gt; （这两个值直接复制，自己写容易错）&lt;/p&gt;
&lt;p&gt;点击右上角的保存并发布&lt;/p&gt;
&lt;h2&gt;第四步：设置鉴权&lt;/h2&gt;
&lt;p&gt;因为edgeone的功能非常夸张，所以这步&lt;strong&gt;不要跳过&lt;/strong&gt;，**不要跳过，不要跳过。**不要害的自己以后没得玩和大家以后也没得玩。&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这是一个最简单的鉴权方式，之后我们再通过另一个域名来访问反代这个域名实现套娃（为什么要套娃？那我问你，我反代我自己的域名和Pixiv有什么关系，你怎么证明我反代的域名反代了pixiv的网站，毕竟你又没法直接打开）。或者你使用下文中的另一种鉴权方式（比较复杂的一个方式，还是建议两个都做。同时反代pixiv的域名和后面反代反代的域名最好不要是同一个域名，做好反代pixiv的域名被封然后丢掉这个域名的心理准备）。听起来有点绕，幻梦希望你能看懂。&lt;/p&gt;
&lt;p&gt;这个头部其实可以自己想设置成什么就设置成什么，自己记住就行，后面还要用的。&lt;/p&gt;
&lt;p&gt;不满足条件就返回一个错误网页，这个自己看着设置。&lt;/p&gt;
&lt;p&gt;保存并发布&lt;/p&gt;
&lt;h2&gt;第五步：配置一个套娃反代来通过鉴权&lt;/h2&gt;
&lt;p&gt;这个和第一步很像，直接照抄，不多解释了&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后设置通过鉴权，站点加速-规则引擎-创建规则&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样就能通过鉴权，保存并发布&lt;/p&gt;
&lt;h2&gt;第六步：给套娃反代域名添加一个鉴权（部分操作可替换第四步）&lt;/h2&gt;
&lt;p&gt;鉴权的方式其实很多，除了上面添加一个请求头进行鉴权最经典的就是Referer鉴权，pixiv的网站就是通过这个来阻止我们反代的（然后添加一个Referer就绕过了，但是爱举报的大公司们是不会去额外添加Referer的，至少目前不会）&lt;/p&gt;
&lt;p&gt;另一种就是通过密钥和时间戳的验证，这个显然更安全（这个是真正的防盗链，一次性链接超时就不可用）&lt;/p&gt;
&lt;p&gt;因为幻梦的需求是最后做一个公益的随机图API的同时，给幻梦自己的网站上使用，所以两个鉴权方式幻梦都上了。请看下图&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;上文就说了，这一步的鉴权和前面的可以相互替换，但是还是建议准备两个域名。如果一个被封了不会影响后面套娃域名的安全（最好一个国际站，一个国内站，做好两个账号）&lt;/p&gt;
&lt;h2&gt;第七步：做一下节点缓存，提高访问速度&lt;/h2&gt;
&lt;p&gt;看图设置吧&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;这样就完成了所有工作&lt;/p&gt;
&lt;h2&gt;结束&lt;/h2&gt;
&lt;p&gt;看到这里幻梦再次提醒，&lt;strong&gt;一定要做鉴权&lt;/strong&gt;，&lt;strong&gt;一定要做鉴权，一定要做鉴权&lt;/strong&gt;。还有做了鉴权也不是万无一失的，&lt;strong&gt;做好被封禁的心理准备，做好被封禁的心理准备，做好被封禁的心理准备&lt;/strong&gt;。自己清楚后果，不想承担后果的，删掉刚刚的所有步骤，当一切无事发生，爱打小报告的没这么快发现你。最后如果很不幸，你被封禁了，和幻梦一点关系没有，因为你清楚这个后果，并且选择了无视风险。&lt;/p&gt;
</content:encoded></item><item><title>【笔记】记1panel面板Nginx反代pixiv</title><link>https://www.yumehinata.com/posts/89xub6gn/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/89xub6gn/</guid><pubDate>Fri, 11 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;h2&gt;前言：为什么选择Nginx？&lt;/h2&gt;
&lt;p&gt;目前，现有的方案中最流行的是Cloudflare Worker反代pixiv的i.pximg.net。这个方法是很好，但是woker的请求还是有限的（虽然每日10w次的请求个人使用其实绰绰有余了），而且容易出现pixiv找上门然后被封号的问题，因此把这种重要的资源掌握在自己手里还是非常有必要的。然后为什么是nginx？因为好用，而且也有现成的方案。其中应用最早且最流行的是“Mashiro”白猫大佬的nginx反代pixiv工具，但是本篇nginx反代和白猫大佬的反代项目无关，这里只是给大家讲一个现状以及市面上的好用案例。&lt;/p&gt;
&lt;h2&gt;第一步：准备一个能够顺利连上pixiv的服务器&lt;/h2&gt;
&lt;p&gt;这步看起来像是废话但是却非常关键。最好的办法就是服务器肉身离境，或者就使用“魔法”，亦或是使用白猫大佬的项目然后再想办法修改nginx的config。“幻梦”这边就直接使用魔法了。&lt;/p&gt;
&lt;h2&gt;第二步：进入你的1panel面板&lt;/h2&gt;
&lt;p&gt;标题已经写了，这个是使用1panel面板部署nginx反代，我都看到宝塔面板的反代教程了（虽然那个教程是全局反代，而且因为pixiv的升级已经过期了）我们1panel什么时候才能站起来。&lt;/p&gt;
&lt;p&gt;确定应用商店中安装好了Openresty&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;在网站中创建一个反代服务，主域名后面可以更具需求添加端口号如：&lt;code&gt;example.com:8848&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;协议选&lt;code&gt;https&lt;/code&gt; ，代理地址为&lt;code&gt;i.pximg.net&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确定保存&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;为你的反代域名申请一个ssl证书&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;主域名填刚刚反代服务写的主域名，如&lt;code&gt;example.com&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;acme账户省事就选Let&apos;s Encrypt&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;验证方式选择dns，dns账号更具你的dns解析服务商提供的API进行填写&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;勾选自动续签&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;勾选跳过DNS校验&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确认，等待完成证书完成申请&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;网站选项卡点击刚刚部署的网站&lt;code&gt;example.com&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;选择HTTPS，启用https。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;ssl选项选择已有证书，acme账户选择刚刚申请的Let&apos;s Encrypt账户或者别的账户&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;证书选择刚刚申请的证书&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保存&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;继续选择反向代理&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;点击编辑&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;开启缓存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;开启回源SNI&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;proxy_ssl_name&lt;/code&gt; 填写&lt;code&gt;i.pximg.net&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;后端代理地址选择&lt;code&gt;https&lt;/code&gt; 填写&lt;code&gt;i.pximg.net&lt;/code&gt; （默认情况下已填好），后端域名填写&lt;code&gt;i.pximg.net&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;确定&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;先不要走，还是反向代理的选项内，点击源文&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;增加一条&lt;code&gt;proxy_set_header Referer &quot;https://www.pixiv.net/&quot;;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;最后源文大致如下&lt;/p&gt;
&lt;p&gt;location ^~ / {
proxy_pass https://i.pximg.net;
proxy_set_header Host i.pximg.net;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header REMOTE-HOST $remote_addr;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $http_connection;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header Referer &quot;https://www.pixiv.net/&quot;;
proxy_http_version 1.1;
add_header X-Cache $upstream_cache_status;
proxy_ssl_server_name on;
proxy_ssl_name i.pximg.net;
if ( $uri ~* &quot;.(gif|png|jpg|css|js|woff|woff2)$&quot; ) {
expires 1m;
}
proxy_ignore_headers Set-Cookie Cache-Control expires;
proxy_cache proxy_cache_panel;
proxy_cache_key $host$uri$is_args$args;
proxy_cache_valid 200 304 301 302 10m;
add_header Strict-Transport-Security &quot;max-age=31536000&quot;;
}&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;到这里还没有结束，1.0版本的1panel有一个bug，非默认443端口情况下在配置文件内不会在端口号后自动添加ssl。此时访问必定是打不开的。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;点击配置文件&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;在&lt;code&gt;listen 端口号&lt;/code&gt; 后面添加&lt;code&gt;ssl&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;保存并重载&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;h2&gt;第三步：验证你的服务&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;访问你的域名如：&lt;code&gt;example.com:8848&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果出现&lt;code&gt;imgaz.pixiv.net&lt;/code&gt; 的文字基本上就说明大功告成了。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;来随便找一张图试试吧。在你的域名后添加&lt;code&gt;/img-original/img/2025/07/10/12/00/03/132525075_p0.jpg&lt;/code&gt; 如&lt;code&gt;https://example.com:8848/img-original/img/2025/07/10/12/00/03/132525075_p0.jpg&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果顺利打开就说明没有问题，打开很慢或者打不开，请检查能否顺利访问&lt;code&gt;pixiv.net&lt;/code&gt; 。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果出现421错误，请检查反向代理中是否开启SNI回源，以及&lt;code&gt;proxy_ssl_name&lt;/code&gt;是否填写正确。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;如果出现403错误，请检查反向代理源文中是否添加&lt;code&gt;proxy_set_header Referer &quot;https://www.pixiv.net/&quot;;&lt;/code&gt; 并且顺利保存重载。&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;成果展示&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://pximg.yumehinata.com/img-master/img/2025/07/10/12/00/03/132525075_p0_master1200.jpg&quot;&gt;&lt;img src=&quot;https://pximg.yumehinata.com/img-master/img/2025/07/10/12/00/03/132525075_p0_master1200.jpg&quot; alt=&quot;&quot; /&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;之后想要不被其他人使用可以开启防盗链功能，顺便还可以套一个cdn。&lt;/p&gt;
&lt;p&gt;接下来怎么用就看各位的创意了。&lt;/p&gt;
</content:encoded></item><item><title>搭建CS2社区服【满十、训练、管理员插件】群晖docker环境</title><link>https://www.yumehinata.com/posts/cemgrevk/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/cemgrevk/</guid><pubDate>Fri, 13 Jun 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;11月2日临时更新文章：目前cs2已支持ipv6解析，暂未测试是否支持srv记录。有nat1的朋友们想知道如何用Lucky的webhook功能发送到kook频道的可以看这个文章&lt;/strong&gt;&lt;a href=&quot;https://www.yumehinata.com/archives/T0vSHpCO&quot;&gt;&lt;strong&gt;https://www.yumehinata.com/archives/T0vSHpCO&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;前言：提示看到这篇笔记的读者，~~截止发稿时cs2无法完成ipv6解析，不用费心做SRV、TXT记录亦或是ipv6的ddns，敬爱的v社至今无法对上述dns记录进行解析。~~另外301重定向仅支持http协议，而cs2是udp协议也不用想了。老老实实用内网穿透工具吧，或者你有办法解决实时更新Stun穿透会变化的端口和地址（当然stun是完全可以用的，用起来麻烦罢了）。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;一、docker拉取镜像&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;注册表中搜索&quot;steamcmd&quot;，下载cm2network/steamcmd这个镜像&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;注册表中搜索“cs2”，下载cm2network/cs镜像&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;两个都选latest，等下载完进行下一步&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;二、安装steamcmd&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;进入“映像”选项&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;双击刚刚下载的steamcmd镜像&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;网络用默认的bridge就行-&amp;gt;下一步&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;容器名称改成自己觉得好记好看的-&amp;gt;使用高权限执行容器（防止出现奇怪报错）-&amp;gt;启用自动更新启动-&amp;gt;下一步-&amp;gt;下一步-&amp;gt;完成&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;docker容器中双击容器进入-&amp;gt;终端机（终端机输入常出现问题建议换个工具进容器）|使用putty之类ssh工具，&lt;code&gt;sudo -i&lt;/code&gt;获取root权限，执行&lt;code&gt;docker ps&lt;/code&gt;命令，找到steamcmd的容器id，执行&lt;code&gt;docker exec -it 容器id /bin/bash&lt;/code&gt; 命令（记得替换）进入&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;执行&lt;code&gt;./steamcmd.sh&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;等待下载完成出现&lt;code&gt;Steam&amp;gt;&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;执行&lt;code&gt;login steam账号&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;输入密码&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（有手机令牌需要进行验证）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;（未来重启steamcmd需要再输入账号登录）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;三、安装cs2客户端&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;docker文件夹中新建一个&lt;code&gt;cs2&lt;/code&gt;文件夹之后存文件用（都用群晖了有条件打开samba的记得打开samba，之后管理文件也方便）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进入“映像”选项&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;双击刚刚下载的&lt;code&gt;cs2&lt;/code&gt;镜像&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;网络用默认的bridge-&amp;gt;下一步&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;改容器名-&amp;gt;使用高权限执行容器-&amp;gt;启用自动更新启动-&amp;gt;下一步&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;端口设置本地端口就和容器端口一致（除非你的端口被占用了，当然除非你有公网的ipv4不然内网穿透还是要变，要不要改、怎么改就看自己了）-&amp;gt;下一步&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;添加文件夹-&amp;gt;找到刚刚建好的&lt;code&gt;docker/cs2&lt;/code&gt;文件夹，选择-&amp;gt;装载目录为&lt;code&gt;/home/steam/cs2-dedicated/&lt;/code&gt; -&amp;gt;下一步-&amp;gt;完成&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进“终端机”看cs2的下载进度，等待下载完成（上一步的文件夹千万不能错）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;出现&lt;code&gt;[STARTUP] {0.000} activated session on GC&lt;/code&gt; 说明服务成功上线，进行下一步&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;停止容器&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;code&gt;cs2&lt;/code&gt;容器-&amp;gt;右键&lt;code&gt;编辑&lt;/code&gt;-&amp;gt;高级设置-&amp;gt;（删除以下变量），这个界面先别管也别保存（因为还有东西没填也保存不了）&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;pre&gt;&lt;code&gt;CS2_RCON_PORT
CS2_RCONPW
CS2_PW
CS2_GAMEALIAS
CS2_CFG_URL
CS2_ADDITIONAL_ARGS
TV_PW
TV_RELAY_PW
&lt;/code&gt;&lt;/pre&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;申请一个srcds-token-&amp;gt;进入&lt;a href=&quot;https://steamcommunity.com/dev/managegameservers&quot;&gt;https://steamcommunity.com/dev/managegameservers&lt;/a&gt;-&amp;gt;&lt;code&gt;appid&lt;/code&gt;填730-&amp;gt;创建&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;复制刚刚创建的登录令牌-&amp;gt;进入刚刚高级设置的变量-&amp;gt;找到&lt;code&gt;SRCDS_TOKEN&lt;/code&gt; -&amp;gt;值粘贴刚刚复制的令牌-&amp;gt;保存-&amp;gt;保存&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进入&lt;code&gt;/docker/cs2/game/csgo/cfg&lt;/code&gt; 文件夹找到&lt;code&gt;server.cfg&lt;/code&gt;（有开samba的就直接windows文件夹里打开编辑）编辑&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;找刚刚上面删过的变量，把这个cfg文件中的这些参数后面的值&lt;code&gt;changeme&lt;/code&gt;修改成空串，保存替换&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;运行容器，出现&lt;code&gt;[STARTUP] {0.000} activated session on GC&lt;/code&gt; 说明成功启动&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;游戏控制台输入&lt;code&gt;connect 服务器ip:27015&lt;/code&gt;（记得替换，刚刚没改端口就是27015，改了就改成自己的）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;能够顺利进入就没问题了，下一步&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;h2&gt;四、安装插件核心&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;目前需要的两个核心为css和metamod，未来也许sourcemod会重装上阵？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://docs.cssharp.dev/docs/guides/getting-started.html&quot;&gt;**【扩展阅读】**&lt;strong&gt;CounterStrikeSharp官方文档&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;下载metamod-&amp;gt;&lt;a href=&quot;https://www.metamodsource.net/downloads.php/?branch=master&quot;&gt;https://www.metamodsource.net/downloads.php/?branch=master&lt;/a&gt;-&amp;gt;群晖当然选linux下载&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解压下载的文件，最后得到一个&lt;code&gt;addons&lt;/code&gt; 文件夹，复制这个文件夹-&amp;gt;&lt;code&gt;cs2/game/csgo/&lt;/code&gt; 粘贴&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;进入&lt;code&gt;cs2/game/csgo/&lt;/code&gt; 找到&lt;code&gt;gameinfo.gi&lt;/code&gt; 编辑，在&lt;code&gt;Game_LowViolence csgo_lv&lt;/code&gt; 下换行添加一行&lt;code&gt;Game csgo/addons/metamod&lt;/code&gt; （对比一下其他的格式）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下载css-&amp;gt;&lt;a href=&quot;https://github.com/roflmuffin/CounterStrikeSharp/releases&quot;&gt;https://github.com/roflmuffin/CounterStrikeSharp/releases&lt;/a&gt;-&amp;gt;就下载最新的，记得下载带“with-runtime”的linux版本&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;解压下载的文件，最后得到一个&lt;code&gt;addons&lt;/code&gt; 文件夹，还是复制这个文件夹-&amp;gt;&lt;code&gt;cs2/game/csgo/&lt;/code&gt; 粘贴&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;重启cs2容器-&amp;gt;等待启动完成（进终端机看，等信息输出基本上不动了，基本上就启动完了）-&amp;gt;终端机中输入&lt;code&gt;meta list&lt;/code&gt; 如果输出&lt;/p&gt;
&lt;p&gt;meta list
Listing 1 plugin:
[01] CounterStrikeSharp (0.1.0) by Roflmuffin&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;输出以上内容就证明安装成功，有更多问题看扩展阅读，官方文档能解决大部分问题，解决不了的问题进issue去看有没有人遇到类似的&lt;/p&gt;
&lt;h2&gt;五、安装管理员插件&lt;/h2&gt;
&lt;p&gt;管理员插件有很多，但是目前最推荐的是&lt;strong&gt;CS2-SimpleAdmin:&lt;/strong&gt;&lt;a href=&quot;https://github.com/daffyyyy/CS2-SimpleAdmin&quot;&gt;&lt;strong&gt;https://github.com/daffyyyy/CS2-SimpleAdmin&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://cs2-simpleadmin.daffyy.dev/getting-started/quickstart&quot;&gt;&lt;strong&gt;【扩展阅读】CS2-SimpleAdmin官方wiki&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;先看上面github的readme，一定一定要看完，之后高级使用看wiki，扩展阅读就是&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;先下载安装依赖，&lt;a href=&quot;https://github.com/NickFox007/PlayerSettingsCS2&quot;&gt;PlayerSettings&lt;/a&gt;、&lt;a href=&quot;https://github.com/NickFox007/AnyBaseLibCS2&quot;&gt;AnyBaseLibCS2&lt;/a&gt;、&lt;a href=&quot;https://github.com/NickFox007/MenuManagerCS2&quot;&gt;MenuManagerCS2&lt;/a&gt;。下载完就解压都会得到&lt;code&gt;addons&lt;/code&gt; 文件夹，全都复制到&lt;code&gt;cs2/game/csgo/&lt;/code&gt; 粘贴。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;下载CS2-SimpleAdmin，去&lt;a href=&quot;https://github.com/daffyyyy/CS2-SimpleAdmin/releases&quot;&gt;releases&lt;/a&gt;下载最新的，下载完解压得到&lt;code&gt;addons&lt;/code&gt; 文件夹，依旧粘贴到&lt;code&gt;cs2/game/csgo/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;**关键来了，这步很重要。**进入&lt;code&gt;cs2\game\csgo\addons\counterstrikesharp\configs&lt;/code&gt; 文件夹，找到&lt;code&gt;admins.example.json&lt;/code&gt; ，复制、重命名为&lt;code&gt;admins.json&lt;/code&gt; ，编辑这个新的文件。注意看以下的内容，默认是下面这些内容，我加了些注释。&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://help.steampowered.com/zh-cn/faqs/view/2816-BE67-5B69-0FEC&quot;&gt;&lt;strong&gt;【扩展阅读】SteamID、Steam 帐户名称、合并帐户以及删除帐户&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;{
&quot;Erikj&quot;: {                              //用户的名字，字符串记得写在引号内，写个简单好记的英文，别写中文，虽然json对象可以有中文但是也别写
&quot;identity&quot;: &quot;76561197960265731&quot;,      //用户的steam64id，这个有steam官方的文档，看文档。文档放在上面扩展阅读
&quot;immunity&quot;: 100,                      //可以不改
&quot;flags&quot;: [                            //这里面的数组是对应的用户权限，如果你不想给root权限，那就要单独给他设置权限，可以有的类型看之前的css官方文档-Admin Framework-Defining Admins-Standard Permissions
&quot;@css/custom-flag-1&quot;,               //建议服主就给自己一个root权限后面进服务器后用插件添加管理，改成@css/root
&quot;@css/custom-flag-2&quot;                //类型不是root就要按需添加，当然可以有好几个，数组中的值记得用逗号隔开，服主是root就删掉多余的记得删掉上面那个逗号
],
&quot;groups&quot;: [
&quot;#css/admin&quot;                         //管理员分组，一般个人玩的不设置，默认就好
],
&quot;command_overrides&quot;: {
&quot;css_plugins&quot;: true,
&quot;css&quot;: false                         //是服主的用户就把这边改成true
}
},
&quot;Another erikj&quot;: {                       //这一行开始就和上面的用户没关系了，只是告诉你万一有同名用户怎么办。别管，反正你只要知道用户名和steam用户名没啥关系，这边爱写啥写啥。
&quot;identity&quot;: &quot;STEAM_0:1:1&quot;,             //steam32id，但是实际填入不起作用，干脆就用steam64id，获取方便经过检验也好用
&quot;flags&quot;: [&quot;@mycustomplugin/admin&quot;]     //简化写法，懒人就用下面这种用户写法好了，完事给root权限@css/root
}
}&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;最后得到以下的内容（写自己的别复制）：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;{
  &quot;Erikj&quot;: {
    &quot;identity&quot;: &quot;7656********88471&quot;,
    &quot;immunity&quot;: 100,
    &quot;flags&quot;: [
      &quot;@css/root&quot;
    ],
    &quot;groups&quot;: [
      &quot;#css/admin&quot;
    ],
    &quot;command_overrides&quot;: {
      &quot;css_plugins&quot;: true,
      &quot;css&quot;: true
    }
  }
}
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;重启cs2容器，进游戏，聊天框输入&lt;code&gt;!admin&lt;/code&gt; 屏幕中间下方有弹出一个小提示窗口里面选项选择就说明成功配置了管理员权限，没有成功会在聊天框输出内容，出现问题看是不是写错了最后文档和issue解决问题&lt;/p&gt;
&lt;h2&gt;六、安装满十插件&lt;/h2&gt;
&lt;p&gt;目前最好的满十插件是MatchZy:&lt;a href=&quot;https://github.com/shobhit-pathak/MatchZy&quot;&gt;https://github.com/shobhit-pathak/MatchZy&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;https://shobhit-pathak.github.io/MatchZy/&quot;&gt;&lt;strong&gt;【扩展阅读】MatchZy官方文档&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;下载releases中最新的然后解压，得到&lt;code&gt;addons、cfg&lt;/code&gt; 文件夹，依旧复制到&lt;code&gt;cs2/game/csgo/&lt;/code&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;这个插件基本上不用设置，虽然他有另一套管理员系统，但是他同时还兼容css的管理员（刚刚已经设置过的）&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;需要动的配置去看上面的官方文档这边不做赘述&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;另外记得在&lt;code&gt;cs2\game\csgo\addons\counterstrikesharp\configs&lt;/code&gt; 找到&lt;code&gt;core.cfg&lt;/code&gt; 文件，编辑。&lt;code&gt;&quot;ServerLanguage&quot;&lt;/code&gt;从&lt;code&gt;en&lt;/code&gt;改为&lt;code&gt;zh-Hans&lt;/code&gt; 切换为简体中文，上述插件已完成简体中文支持&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置完成重启cs2容器&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
</content:encoded></item><item><title>【教程】GenP3.5激活后还需要做的工作</title><link>https://www.yumehinata.com/posts/hctnjw4g/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/hctnjw4g/</guid><pubDate>Tue, 22 Apr 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;本文不提供激活破解的任何教程，只讲如何关闭一些不想在使用中看到的东西。&lt;/p&gt;
&lt;p&gt;此外该教程只是对“&lt;a href=&quot;https://www.reddit.com/r/GenP/wiki/redditgenpguides/&quot;&gt;https://www.reddit.com/r/GenP/wiki/redditgenpguides/&lt;/a&gt;”中的一部分信息进行提炼，并非原创。教程中的指令与代码均为转载。&lt;/p&gt;
&lt;p&gt;另外，使用这个教程中的步骤会导致应用的网络连接错误，如果刚需那些联网的AI功能等的，这个教程并不合适。&lt;/p&gt;
&lt;p&gt;最后，请支持开发者与公司的劳动成果，不要滥用技术，此技术仅为学习。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;1.一些不希望自启动的进程：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;通常我们不希望以下的可执行文件自动启动&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;C:\Program Files (x86)\Adobe\Adobe Sync\CoreSync\CoreSync.exe

C:\Program Files\Adobe\Adobe Creative Cloud Experience\CCXProcess.exe

C:\Program Files (x86)\Common Files\Adobe\Adobe Desktop Common\ADS\Adobe Desktop Service.exe（更新CreativeCloud时至少恢复这个）

C:\Program Files\Common Files\Adobe\Creative Cloud Libraries\CCLibrary.exe

C:\Program Files\Adobe\Acrobat DC\Acrobat\AdobeCollabSync.exe（如安装Acrobat DC会有这个目录和文件，没有就不用）
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;我们需要对这些可执行文件后缀从.exe改为.exe.bak来做一个备份。当需要更新CreativeCloud时需要恢复“Adobe Desktop Service.exe”，如果还是遇到问题恢复上面所有备份的文件，更新完后再执行这一步关掉他们。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2.一些烦人的弹窗：&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;在使用过程中时常会出现一些妨碍我们工作，他们不可移动，不可关闭。此时我们需要再hosts中重定向一些连接来屏蔽这些弹窗广告，或者运行一下GenP3.5中的脚本工具。&lt;/p&gt;
&lt;p&gt;AGS是我们尤其不希望看到的。不过工具提供了卸载它的快捷按钮，只需要管理员方式运行GenP3.5--&amp;gt;Pop-upTools--&amp;gt;remove AGS&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;然后是另一些弹窗，他们需要hosts去阻断链接。&lt;/p&gt;
&lt;p&gt;我们选择使用“教程”中的方案2，“UpdateHostsFile”。&lt;strong&gt;但是需要注意的，这是一个自动化脚本存在风险，请在知晓此点后选择是否使用。&lt;/strong&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;*执行之前先把GenP3.5放到“C:\GenP.v3.5.0-CGP\”这个位置，确保“C:\GenP.v3.5.0-CGP\GenP 3.5.0.exe”路径存在。然后请使用PowerShell执行以下的代码。
$scriptDir = &quot;C:\Windows\System32\drivers\etc&quot;
$scriptPath = &quot;$scriptDir\UpdateHostsFile.ps1&quot;
if (Get-ScheduledTask -TaskName &quot;UpdateHostsFile&quot; -ErrorAction SilentlyContinue) { Unregister-ScheduledTask -TaskName &quot;UpdateHostsFile&quot; -Confirm:$false }
if (-not (Test-Path -Path $scriptDir)) { New-Item -ItemType Directory -Path $scriptDir -Force }
Set-Content -Path $scriptPath -Value @&apos;
$logDirectory = &quot;C:\Windows\System32\drivers\etc&quot;
$logFile = &quot;$logDirectory\UpdateHostsFile.log&quot;
if (-not (Test-Path -Path $logDirectory)) { New-Item -ItemType Directory -Path $logDirectory -Force }
Set-Content -Path $logFile -Value $null
function Write-Log {
    param ([string]$message)
    $timestamp = Get-Date -Format &quot;yyyy-MM-dd HH:mm:ss&quot;
    $logEntry = &quot;$timestamp - $message&quot;
    Add-Content -Path $logFile -Value $logEntry
}
function Test-IsAdmin {
    if (-not ([Security.Principal.WindowsPrincipal] [Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole([Security.Principal.WindowsBuiltInRole]::Administrator)) {
        Write-Log &quot;This script requires administrative privileges. Please run PowerShell as Administrator.&quot;
        Write-Output &quot;This script requires administrative privileges. Please run PowerShell as Administrator.&quot;
        exit
    }
}
function SafeFileOperation {
    param (
        [string]$Action,
        [string]$Source,
        [string]$Destination
    )
    try {
        switch ($Action) {
            &quot;Copy&quot; { Copy-Item -Path $Source -Destination $Destination -Force }
            &quot;Rename&quot; { Rename-Item -Path $Source -NewName $Destination -Force }
        }
        Write-Log &quot;$Action operation successful: $Source -&amp;gt; $Destination&quot;
    } catch {
        Write-Log &quot;Failed to $Action file: Please ensure the source file exists and you have the necessary permissions.&quot;
    }
}
Test-IsAdmin
Write-Log &quot;Script execution started.&quot;
$hostsFile = &quot;C:\Windows\System32\drivers\etc\hosts&quot;
$backupCurrent = &quot;C:\Windows\System32\drivers\etc\hosts.bak&quot;
$plainBackup = &quot;C:\Windows\System32\drivers\etc\hosts.plain&quot;
$tempFile = &quot;$scriptDir\hosts_temp&quot;
$primaryGenPPath = &quot;C:\GenP.v3.5.0-CGP\GenP 3.5.0.exe&quot;
if (-not (Test-Path $hostsFile)) {
    Write-Log &quot;The hosts file does not exist at the specified path: $hostsFile&quot;
    exit
}
if (-not (Test-Path $primaryGenPPath)) {
    Write-Log &quot;The specified GenP executable does not exist at: $primaryGenPPath&quot;
    exit
}
if ((Get-Item $hostsFile).Attributes -band [System.IO.FileAttributes]::ReadOnly) {
    Write-Log &quot;The hosts file is currently read-only. Attempting to remove read-only attribute.&quot;
    try {
        Set-ItemProperty -Path $hostsFile -Name IsReadOnly -Value $false
        Write-Log &quot;Removed read-only attribute from the hosts file.&quot;
    } catch {
        Write-Log &quot;Could not change the read-only status of the hosts file. Please check permissions.&quot;
    }
}
$currentContent = Get-Content -Path $hostsFile -Raw -ErrorAction SilentlyContinue
if (-not (Test-Path $backupCurrent) -or ($currentContent -ne (Get-Content -Path $backupCurrent -Raw -ErrorAction SilentlyContinue))) {
    Write-Log &quot;Creating a new backup of the hosts file.&quot;
    SafeFileOperation -Action &quot;Copy&quot; -Source $hostsFile -Destination $backupCurrent
} else {
    Write-Log &quot;Hosts file has not changed since the last backup. Skipping backup.&quot;
}
$hostsContent = Get-Content -Path $hostsFile -Raw -ErrorAction SilentlyContinue
Write-Log &quot;Hosts file content retrieved.&quot;
$maxRetries = 3
$retryDelay = 2
$newBlocklist = @()
Write-Log &quot;Attempting to execute GenP.exe for blocklist.&quot;
try {
    Start-Process -FilePath $primaryGenPPath -ArgumentList &quot;-popup&quot; -NoNewWindow -Wait
    Write-Log &quot;GenP.exe executed successfully.&quot;
    $hostsContent = Get-Content -Path $hostsFile -Raw
    if ($hostsContent -match &quot;^0\.0\.0\.0&quot;) {
        Write-Log &quot;GenP.exe output detected and valid.&quot;
        $newBlocklist = $hostsContent -split &quot;`n&quot; | Where-Object { $_.Trim() -match &apos;^0\.0\.0\.0&apos; }
    } else {
        Write-Log &quot;GenP.exe output missing or invalid.&quot;
        throw &quot;Invalid GenP.exe output.&quot;
    }
} catch {
    Write-Log &quot;Failed to run GenP to fetch the blocklist; please ensure the executable is present and try again.&quot;
}
if (-not $newBlocklist.Count) {
    Write-Log &quot;Attempting to retrieve blocklist from fallback URLs.&quot;
    $encodedUrls = @(
        &quot;aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2lnbmFjaW9jYXN0cm8vYS1kb3ZlLWlzLWR1bUIvcmVmcy9oZWFkcy9tYWluL2xpc3QudHh0&quot;, 
        &quot;aHR0cHM6Ly9yYXcuZ2l0aHVidXNlcmNvbnRlbnQuY29tL2lnbmFjaW9jYXN0cm8vYS1kb3ZlLWlzLWR1bUIvcmVmcy9oZWFkcy9tYWluL3dpbmhvc3RzLnR4dA==&quot;
    )
    foreach ($encodedUrl in $encodedUrls) {
        $decodedUrl = [System.Text.Encoding]::UTF8.GetString([Convert]::FromBase64String($encodedUrl))
        Write-Log &quot;Trying to access fallback URL.&quot;
        for ($attempt = 1; $attempt -le $maxRetries; $attempt++) {
            try {
                Write-Log &quot;Retrieving blocklist content from fallback URL.&quot;
                $response = Invoke-WebRequest -Uri $decodedUrl -Method Get -ErrorAction Stop
                if ($response.Content) {
                    Write-Log &quot;Successfully retrieved content from fallback URL on attempt $attempt.&quot;
                    $newBlocklist += $response.Content -split &quot;`n&quot; | Where-Object { $_ -match &quot;^0\.0\.0\.0|^# Last update:&quot; }
                    break
                }
            } catch {
                Write-Log &quot;Failed to connect to the provided URL; please check internet connectivity or the URL itself.&quot;
                if ($attempt -lt $maxRetries) {
                    Write-Log &quot;Retrying in $retryDelay seconds.&quot;
                    Start-Sleep -Seconds $retryDelay
                }
            }
        }
        if ($newBlocklist.Count -gt 0) { break }
    }
}
if ($newBlocklist.Count -eq 0) {
    Write-Log &quot;No valid blocklist entries retrieved from all sources. Attempting to restore hosts file from hosts.plain.&quot;
    if (Test-Path $plainBackup) {
        Write-Log &quot;Restoring hosts file from hosts.plain file.&quot;
        $plainContent = Get-Content -Path $plainBackup -Raw -ErrorAction SilentlyContinue
        if (-not [string]::IsNullOrWhiteSpace($plainContent)) {
            Write-Log &quot;Restoring hosts file.&quot;
            Set-Content -Path $hostsFile -Value $plainContent -NoNewline
            Write-Log &quot;Successfully restored hosts file from hosts.plain.&quot;
        } else {
            Write-Log &quot;hosts.plain is empty or invalid; unable to restore hosts file.&quot;
        }
    } else {
        Write-Log &quot;hosts.plain file does not exist; cannot restore hosts file.&quot;
        Write-Log &quot;Restoring hosts file from backup.&quot;
        SafeFileOperation -Action &quot;Copy&quot; -Source $backupCurrent -Destination $hostsFile
    }
    Write-Log &quot;Exiting script.&quot;
    exit
}
Write-Log &quot;Total valid blocklist entries pulled: $($newBlocklist.Count)&quot;
$blocklistHeader = &quot;# START - Adobe Blocklist&quot;
$blocklistFooter = &quot;# END - Adobe Blocklist&quot;
$lastUpdateComment = &quot;&quot;
foreach ($line in $newBlocklist) {
    if ($line.Trim().StartsWith(&quot;# Last update:&quot;)) {
        $lastUpdateComment = &quot;`n$($line.Trim())&quot;
    }
}
$filteredBlocklist = $newBlocklist | Where-Object { -not ($_.Trim().StartsWith(&quot;# Last update:&quot;)) -and $_.Trim() -ne &quot;&quot; }
$finalContent = &quot;&quot;
if (Test-Path $plainBackup) {
    $plainContent = Get-Content -Path $plainBackup -Raw -ErrorAction SilentlyContinue
    if (-not [string]::IsNullOrWhiteSpace($plainContent)) {
        Write-Log &quot;Including content from hosts.plain at the top of the hosts file.&quot;
        $finalContent += $plainContent.Trim() + &quot;`n&quot;
    } else {
        Write-Log &quot;The file hosts.plain is empty; it will not be included.&quot;
    }
}
$finalContent += &quot;$blocklistHeader$lastUpdateComment`n$($filteredBlocklist -join &quot;`n&quot;)`n$blocklistFooter&quot;.Trim()
$finalContent = $finalContent -replace &quot;`r?`n`r?`n&quot;, &quot;`n&quot;
for ($attempt = 1; $attempt -le $maxRetries; $attempt++) {
    try {
        Start-Sleep -Seconds 1
        Write-Log &quot;Attempting to write to $tempFile, Attempt #$attempt&quot;
        if ((Get-Item $hostsFile).Attributes -band [System.IO.FileAttributes]::ReadOnly) {
            Write-Log &quot;The hosts file is currently read-only. Attempting to remove read-only attribute.&quot;
            Set-ItemProperty -Path $hostsFile -Name IsReadOnly -Value $false
            Write-Log &quot;Removed read-only attribute from hosts file.&quot;
        }
        Set-Content -Path $tempFile -Value $finalContent -NoNewline
        Write-Log &quot;Temporary file created successfully.&quot;
        SafeFileOperation -Action &quot;Copy&quot; -Source $tempFile -Destination $hostsFile
        Write-Log &quot;Hosts file successfully updated from temporary file.&quot;
        break
    } catch {
        Write-Log &quot;An error occurred while updating the hosts file. Ensure you have sufficient permissions.&quot;
        if ($errorMsg -like &quot;*denied*&quot;) {
            Write-Log &quot;Access to $hostsFile is denied. Retrying in $retryDelay seconds...&quot;
            Start-Sleep -Seconds $retryDelay
        } else {
            Write-Log &quot;An unexpected error occurred: $errorMsg&quot;
            break
        }
    }
}
Remove-Item -Path $tempFile -ErrorAction SilentlyContinue
Write-Log &quot;Script execution completed successfully.&quot;
&apos;@
if (Test-Path $scriptPath) {
    Write-Output &quot;Script file successfully created at $scriptPath.&quot;
} else {
    Write-Output &quot;Script file creation failed. Please check permissions or paths.&quot;
}
$action = New-ScheduledTaskAction -Execute &quot;powershell.exe&quot; -Argument &quot;-NoProfile -ExecutionPolicy Bypass -File `&quot;$scriptPath`&quot;&quot;
$trigger = New-ScheduledTaskTrigger -Once -At (Get-Date) -RepetitionInterval (New-TimeSpan -Hours 3) -RepetitionDuration (New-TimeSpan -Days 3650)
$settings = New-ScheduledTaskSettingsSet -AllowStartIfOnBatteries -DontStopIfGoingOnBatteries -StartWhenAvailable -MultipleInstances IgnoreNew -ExecutionTimeLimit (New-TimeSpan -Minutes 2)
try {
    Register-ScheduledTask -TaskName &quot;UpdateHostsFile&quot; -Action $action -Trigger $trigger -Settings $settings -User &quot;SYSTEM&quot; -RunLevel Highest
    Write-Output &quot;Scheduled task &apos;UpdateHostsFile&apos; created successfully.&quot;
} catch {
    Write-Output &quot;Failed to create scheduled task.&quot;
}
Start-Sleep -Seconds 5
try {
    Start-ScheduledTask -TaskName &quot;UpdateHostsFile&quot;
    Write-Output &quot;Scheduled task &apos;UpdateHostsFile&apos; started successfully.&quot;
} catch {
    Write-Output &quot;Failed to start scheduled task.&quot;
}
Start-Process -FilePath &quot;powershell.exe&quot; -ArgumentList &quot;-Command exit&quot;
Stop-Process -Id $PID -Force
exit
&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;运行完这个脚本后打开任务计划程序--&amp;gt;任务计划程序库--&amp;gt;UpdateHostFile--&amp;gt;运行&lt;/p&gt;
&lt;p&gt;&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;p&gt;检查一下hosts文件内是否发生了变化。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3.文末&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这只是幻梦本人在使用过程中的操作步骤，需要更详细的帮助请使用文章头部留下的链接，看完后足够解决大部分问题。&lt;/p&gt;
&lt;p&gt;&lt;a href=&quot;/upload/GenP.v3.5.0-CGP.zip&quot;&gt;GenP3.5&lt;/a&gt;&lt;/p&gt;
</content:encoded></item><item><title>【非原创】openclash设置项，降低延迟提高访问速度。</title><link>https://www.yumehinata.com/posts/mvnqmpuf/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/mvnqmpuf/</guid><pubDate>Thu, 13 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;此方案非“幻梦”原创，原教程来自于恩山无线“悟空“大佬的方案，&lt;a href=&quot;https://www.right.com.cn/forum/forum.php?mod=viewthread&amp;amp;tid=8360227&quot;&gt;原文链接&lt;/a&gt;。接下来的演示使用的版本为istoreOS22.03，clashLuci为0.46.079。&lt;strong&gt;请注意&lt;/strong&gt;接下来的配置应用路由的工作模式为&lt;strong&gt;主路由&lt;/strong&gt;,旁路由模式请勿照抄。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;网络--接口--lan--编辑--高级设置--使用自定义的DNS服务器：223.5.5.5(此dns为阿里云dns，可根据个人需要修改，&lt;strong&gt;不建议&lt;/strong&gt;设置为谷歌或cf的dns)--保存--保存并应用&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;服务--openclash--插件设置--模式设置--切换页面到Fake-IP模式--（更改运行模式即可，原文的meta内核在0.46.079无需要额外选择）--保存配置&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;插件设置--流量控制--（此处无法填写大陆域名DNS服务器地址，但是不要紧第一步已经设置了一个国内dns服务；勾选仅允许内网后，WAN口名称进“网络--接口--wan”看一下名字然后设置，lan接口名称项不动，保持“禁用”）--保存配置&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;插件设置--dns设置--（此处无禁止dnsmasq缓存dns的选项，不设置也行。插件设置--调试日志--点击生成，可以查看是否启用）--保存配置&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;覆写设置--常规设置--（github地址修改，浙江地区cdn.jsdelivr.net完全可用，所以改这个也行）--保存配置&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;覆写设置--dns设置--（fake-ip-filter勾选）--保存配置&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;覆写设置--meta设置--（按图设置即可）--保存配置&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;覆写设置--开发者选项--（第29行附近，看图位置添加一条配置。配置项为：ruby_edit &quot;$CONFIG_FILE&quot; &quot;[&apos;experimental&apos;]&quot; &quot;{&apos;sniff-tls-sni&apos;=&amp;gt;false}&quot;）--保存配置&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;覆写设置--（下拉）设置自定义上游 DNS 服务器--（nameServer、FallBack、Default-NameServer均需设置）--（去掉所有勾选的内容）--&lt;strong&gt;分别&lt;/strong&gt;保存配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置完上述点击&lt;strong&gt;应用配置&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;插件设置--版本更新--使用cdn一键检查更新&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置订阅--（转换服务地址、自定义模板地址见原文。&lt;strong&gt;重点：跳过证书验证--启用&lt;/strong&gt;，原文没有勾选可能导致部分节点无法使用）--保存配置&lt;img alt=&quot;&quot; /&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置订阅--（下拉）更新配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;配置订阅--自动订阅勾选--保存配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;插件设置--外部控制--切换Dashboard版本、切换Yacd版本、更新metacubexd版本、更新zashboard版本--保存配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;插件设置--第三方规则订阅--勾选自动更新--检查并更新--保存配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;插件设置--GEO数据库订阅--勾选全部自动更新选项--保存配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;插件设置--大陆白名单订阅--勾选自动更新--保存配置&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;设置完上述点击&lt;strong&gt;应用配置&lt;/strong&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;以上完成了基础的修改，如无特殊需求请勿随意改动。对订阅模板和转换服务不放心的可以自建。&lt;/p&gt;
&lt;p&gt;订阅转换服务地址：https://apiurl.v1.mk/sub&lt;/p&gt;
&lt;p&gt;自定义模板地址：https://raw.githubusercontent.com/WukongMaster/demo/main/wukong.ini&lt;/p&gt;
</content:encoded></item><item><title>无需Gapps解决googlePlayStore三件套安装</title><link>https://www.yumehinata.com/posts/yirjpfvn/</link><guid isPermaLink="true">https://www.yumehinata.com/posts/yirjpfvn/</guid><pubDate>Tue, 17 Dec 2024 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;strong&gt;参考文献&lt;/strong&gt;：《&lt;strong&gt;Android 8.0 安装Google服务&lt;/strong&gt;》&lt;a href=&quot;https://blog.csdn.net/baobei0112/article/details/90210409&quot;&gt;https://blog.csdn.net/baobei0112/article/details/90210409&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;所需软件&lt;strong&gt;下载地址&lt;/strong&gt;为：&lt;a href=&quot;https://blog.csdn.net/baobei0112/article/details/90210409&quot;&gt;https://www.apkmirror.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;注意事项&lt;/strong&gt;：尽量围绕gfs和GooglePlayServices的发布时间选择安装包。GFS选择需要注意安卓版本，GooglePlayService选择需要注意安卓版本、处理器指令集、屏幕分辨率支持。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第一步&lt;/strong&gt;，安装googleAccountManger。包名：com.google.android.gsf.login。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第二步&lt;/strong&gt;，安装googleServicesFramework（GFS）。包名：com.google.android.gsf（注意小版本号，如8.1和8.0为两个不同安卓版本对应不同安装包）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第三步&lt;/strong&gt;，安装googlePlayService。包名：com.google.android.gms。（无脑选arm64+nodpi版本，不确定对应包的安卓版本选择和GFS相近发布时间的新包）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第四步&lt;/strong&gt;，安装googlePlayStore。包名：com.android.vending。（无脑下载taptap，进附件管理装palyStore）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第五步&lt;/strong&gt;，Enjoy（按道理到这步就结束了，但是参考文献提到了GMScore，笔者在mix上安装时无需安装此应用即可正常启动play商店，疑似非❀系手机无需此步操作）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;额外注意1&lt;/strong&gt;：google登录需要手机启动魔法工具，笔者路由的shellclash对手机应用的兼容性存在较大问题。（不启动卡加载是很正常的现象）&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;额外注意2&lt;/strong&gt;：play商店无法直接找到应用请前往网页端查询，网页端地址：&lt;a href=&quot;https://play.google.com/&quot;&gt;https://play.google.com/&lt;/a&gt;。无法直接安装请多准备不同版本的安卓机，可以先在低版本安卓机上完成一次安装，再切换到高版本安卓机，playStore-&amp;gt;头像-&amp;gt;管理应用和设备-&amp;gt;管理-&amp;gt;此设备（切换至完成安装的低版本设备或未安装选项）-&amp;gt;点开应用-&amp;gt;安装。或者mt工具箱提取暴力解决问题。&lt;/p&gt;
</content:encoded></item></channel></rss>