<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
    <channel>
        <title>xfangfang</title>
        <atom:link href="/feed.xml" rel="self" type="application/rss+xml"/>
        <link>https://xfangfang.github.io/</link>
        <description>xfangfang&apos;s blog of sharing programming developments</description>
        <pubDate>Sat, 13 Sep 2025 11:48:49 +0000</pubDate>
        
        <item>
            <title>如何下载Github Actions中的编译结果</title>
            <link>/036</link>
            <guid isPermaLink="true">/036</guid>
            <description>&lt;p&gt;如果你不会在Github Actions下载编译结果，你可能需要先读读这个内容。&lt;/p&gt;

&lt;h1 id=&quot;第一步注册-github-账号&quot;&gt;第一步：注册 Github 账号&lt;/h1&gt;

&lt;p&gt;这一步在 &lt;a href=&quot;https://github.com&quot;&gt;Github 首页&lt;/a&gt; 注册即可，你也可以点击 &lt;a href=&quot;https://github.com/signup?ref_cta=Sign+up&amp;amp;ref_loc=header+logged+out&amp;amp;ref_page=%2F&amp;amp;source=header-home&quot;&gt;这里&lt;/a&gt; 直达注册页面。&lt;/p&gt;

&lt;h1 id=&quot;第二步找到你要下载的构建结果&quot;&gt;第二步：找到你要下载的构建结果&lt;/h1&gt;

&lt;p&gt;比如 这里展示了一个链接：&lt;a href=&quot;https://github.com/xfangfang/wiliwili/actions/workflows/build.yaml&quot;&gt;https://github.com/xfangfang/wiliwili/actions/workflows/build.yaml&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;点进去之后长这样：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-12-02-036/image-20221202152930795.png&quot; alt=&quot;image-20221202152930795&quot; /&gt;&lt;/p&gt;

&lt;p&gt;你只需要关注右侧的列表就可以了，也就是：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-12-02-036/image-20221202153018051.png&quot; alt=&quot;image-20221202153018051&quot; /&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;strong&gt;代码变更&lt;/strong&gt; 的简介。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;在这个列表中，最上面的是最新的编译结果，一般来说点击最上面绿色的链接即可。&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;第三步下载&quot;&gt;第三步，下载&lt;/h1&gt;

&lt;p&gt;点击上一步说的编译结果后，我们进入了这样一个页面：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-12-02-036/image-20221202153628552.png&quot; alt=&quot;image-20221202153628552&quot; /&gt;&lt;/p&gt;

&lt;p&gt;如果你想了解编译的细节，可以点击这个页面上那些小绿点来检查编译过程中产生的log，&lt;strong&gt;因为一切都是运行在云端且所有编译步骤可查，所以你大可以放心软件的安全性（至少我写的可以放心）&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;如果你只是想下载编译的结果，请滑到页面底部：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-12-02-036/image-20221202153846814.png&quot; alt=&quot;image-20221202153846814&quot; /&gt;&lt;/p&gt;

&lt;p&gt;在底部，罗列了这一次编译的最终结果，按照你的需要点击下载即可。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;⚠️ 请注意如果你没有登录Github账号，在这里是无法点击下载的。&lt;/strong&gt;&lt;/p&gt;

&lt;h1 id=&quot;总结反馈&quot;&gt;总结反馈&lt;/h1&gt;

&lt;p&gt;使用Github Actions可以让开发者的每一次代码修改都能自动编译出测试版，虽然下载的步骤略微繁琐一些，不能直接点击下载，但是毕竟是免费的还能要求什么呢？&lt;/p&gt;

&lt;p&gt;我的博客支持 Github 账号登录评论，如果你有任何问题欢迎点击下方评论栏登录你的 Github 账号来给我留言，你的每条留言我都会收到邮件提醒，所以最好一次性将要说的内容总结完毕，&lt;strong&gt;在发布前站在旁白的角度思考一下留言的内容是否可以让别人看懂&lt;/strong&gt;，感谢。&lt;/p&gt;

</description>
            <pubDate>Fri, 02 Dec 2022 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>如何缓解网络喷子带来的负面情绪</title>
            <link>/034</link>
            <guid isPermaLink="true">/034</guid>
            <description>&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;那个时候火起来了一个软件名字叫做 ”Soul“，大概是可以和一个陌生人语音对话几分钟的软件。通过这个软件我见识到了各式各样的人。知道了原来国内还存在“专科生”，我之前一直认为专科已经不存在了，因为当时我记着高考的时候我们班级只有最后一名去了二本的学校，那个女生经常被老师点名起来回答问题的时候一言不发，天真的我据此认为应该不会再有更差的成绩了。&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;然后就到了营销号大力推广5G的时候，虽然不是具体的从业者，但是作为一个程序员我还是可以意识到“5G手术”、“5G汽车”等等宣传根本不靠谱。于是我本着科教兴国的想法深入评论区，希望可以通过自己的努力让更多人也意识到5G短时间意义不大这件事。于是得到了：”没想到985教出来的学生如此鼠目寸光“、”敢不敢把你成绩单拿出来“，为什么能从5G聊到我的学历呢？因为这些被洗脑的人会从头到脚从任何一个可以抓住的细节攻击我。时至今日我用的5G手机为了省电还是只开4G，曾经的宣传又有哪样实现了？而努力科普的我获得了什么呢？何苦来哉。&lt;/p&gt;

&lt;p&gt;这样的事情层出不穷，诸位看官此时此刻可能正一方面同意我的想法，一方面又在某个领域做着相似的网络暴力。甚至我也可能正在做同样的事，于是为了让自己显得更聪明一点，闭嘴是最好的结果。&lt;/p&gt;

&lt;p&gt;为什么今天突然想起来长篇大论呢？&lt;/p&gt;

&lt;p&gt;原因是我在空闲时候一直在做开源项目，从Switch、PSV到Kindle，我制作或移植了一些程序可以运行在越狱后的这些设备上。大多数评论都是在感激作者的为爱发电，但总有那一小撮杠精可以找到各种理由发出让人不舒服的评论，比如毫无缘由地说我是”游戏机的二道贩子目的是抬高二手游戏机价格”。遇到这种丝毫逻辑都没有的评论，我甚至连反驳都找不到切入点。&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;

</description>
            <pubDate>Mon, 03 Oct 2022 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>在越狱的Kindle上自由玩耍</title>
            <link>/031</link>
            <guid isPermaLink="true">/031</guid>
            <description>&lt;p&gt;女朋友给我买了新的Kindle，于是终于可以甩开膀子自由玩耍我的老Kindle了！&lt;/p&gt;

&lt;h2 id=&quot;kindle-越狱&quot;&gt;Kindle 越狱&lt;/h2&gt;

&lt;p&gt;大力感谢越狱作者：&lt;a href=&quot;https://www.mobileread.com/forums/showthread.php?t=346037&quot;&gt;&lt;strong&gt;WatchThis - Software Jailbreak for any Kindle &amp;lt;= 5.14.2&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;以及书伴网站的中文教程：&lt;a href=&quot;https://bookfere.com/post/970.html&quot;&gt;Kindle 通用越狱教程：适用固件版本 5.12.2.2~5.14.2&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;关于一个Kindle漏洞的详细发现过程：&lt;a href=&quot;https://medium.com/realmodelabs/kindledrip-from-your-kindles-email-address-to-using-your-credit-card-bb93dbfb2a08&quot;&gt;&lt;strong&gt;KindleDrip —&lt;/strong&gt; From Your Kindle’s Email Address to Using Your Credit Card&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;推荐阅读最后一个文章，作者详细描述了一个发现漏洞、利用漏洞的一般流程，适合我们这些没啥技术力的程序员没事看看跟着沸腾。最终作者因此获得了亚马逊&lt;em&gt;18K$&lt;/em&gt; 的奖励，也有人据此制作了越狱。&lt;/p&gt;

&lt;h2 id=&quot;在越狱的kindle上开发&quot;&gt;在越狱的Kindle上开发&lt;/h2&gt;

&lt;p&gt;不论是软件越狱还是硬件越狱，破解了我们的kindle后，我们就可以下载一些网友们自制的程序了。&lt;/p&gt;

&lt;p&gt;和大火的psv与switch破解不同，kindle这边越狱虽然发展了十多年了但是并没有太多的自制软件，大部分的自制软件都有着近十年的历史，我想主要原因是Kindle这菜鸡性能也没什么好做的，大部分越狱的朋友都是冲着自定义锁屏页面去的，再加上kindle系统本身依托于linux，即使固件再三升级也不怎么影响系统底层。&lt;/p&gt;

&lt;p&gt;所以这一份十年前(2012)写的kindle开发教程，在今天依旧不过期：&lt;a href=&quot;https://www.mobileread.com/forums/showthread.php?t=189372&quot;&gt;&lt;strong&gt;here’s how to compile a gtk native app for the kindle touch&lt;/strong&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3 id=&quot;交叉编译&quot;&gt;交叉编译&lt;/h3&gt;

&lt;p&gt;其实过程异常简单，只需要配置好arm32位交叉编译链然后把系统中预装好的库拷贝出来准备使用即可。编译出来的程序甚至都能运行在树莓派上。&lt;/p&gt;

&lt;p&gt;为了避免手动编译交叉编译工具链，以及保持系统环境比较干净这里我做了一个简单的docker镜像来在我的mac上进行编译，Kindle上的glibc库比较老所以我们也需要用旧版本的gcc来编译，Dockerfile在此，大家也可以直接拉取我做好的镜像：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;xfangfang/gcc-4.7-arm-linux-gnueabi&lt;/code&gt;&lt;/p&gt;

&lt;div class=&quot;language-dockerfile highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;FROM&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; ubuntu:trusty-20191217&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;RUN &lt;/span&gt;apt update &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  apt &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; gcc-4.7-arm-linux-gnueabi libgtk2.0-dev &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;cd&lt;/span&gt; /usr/bin &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; arm-linux-gnueabi-gcc-4.7 arm-linux-gnueabi-gcc &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; arm-linux-gnueabi-cpp-4.7 arm-linux-gnueabi-cpp &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; arm-linux-gnueabi-gcc-ar-4.7 arm-linux-gnueabi-gcc-ar &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; arm-linux-gnueabi-gcc-nm-4.7 arm-linux-gnueabi-gcc-nm &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; arm-linux-gnueabi-gcc-ranlib-4.7 arm-linux-gnueabi-gcc-ranlib &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\
&lt;/span&gt;  &lt;span class=&quot;nb&quot;&gt;ln&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; arm-linux-gnueabi-gcov-4.7 arm-linux-gnueabi-gcov

&lt;span class=&quot;k&quot;&gt;WORKDIR&lt;/span&gt;&lt;span class=&quot;s&quot;&gt; /src&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;来一份hello-world吧&quot;&gt;来一份hello world吧&lt;/h3&gt;

&lt;p&gt;编译hello world比较简单，代码就不提供了，我们直接编译即可：&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 启动容器&lt;/span&gt;
docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;:/src xfangfang/gcc-4.7-arm-linux-gnueabi /bin/bash
&lt;span class=&quot;c&quot;&gt;# 容器内执行编译&lt;/span&gt;
arm-linux-gnueabi-gcc-4.7 hello_world.c &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; hello_world
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;开发到这完全没有神秘感，编译出来的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hello_world&lt;/code&gt; 拷贝到kindle上，使用自制软件：Kterm 来执行即可。&lt;/p&gt;

&lt;h3 id=&quot;那么怎么开发gui呢&quot;&gt;那么怎么开发GUI呢？&lt;/h3&gt;

&lt;p&gt;kindle系统包中提供了GTK2的运行环境，虽然我之前做过一个gnome上的状态栏插件的gtk4.0适配，但完全是照葫芦画瓢。感觉gtk软件的一大问题就是不同大版本的gtk不兼容，我对gtk这种api不兼容的做法表示不理解，尤其是当你发现很多常用的api变动只是换了个名字的时候。（当然可能只是我对gtk的理解过于浅薄了）&lt;/p&gt;

&lt;p&gt;Kindle上可以直接使用GTK2.0来开发，有一点小小的不同，就是窗口标题需要按照要求来设定，不然软件会不显示，要求如下：&lt;a href=&quot;https://wiki.mobileread.com/wiki/Running_X_apps_on_Kindle_Touch&quot;&gt;Running_X_apps_on_Kindle_Touch&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;这里我们使用那份十年前的教程中提供的代码即可正常运行：&lt;/p&gt;

&lt;div class=&quot;language-c highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;cp&quot;&gt;#include&lt;/span&gt; &lt;span class=&quot;cpf&quot;&gt;&amp;lt;gtk/gtk.h&amp;gt;&lt;/span&gt;&lt;span class=&quot;cp&quot;&gt;
&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GtkWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpointer&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gtk_main_quit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gboolean&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;delete_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GtkWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GdkEvent&lt;/span&gt;  &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpointer&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g_print&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;delete event occurred&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\n&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;c1&quot;&gt;// we do want to quit&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;k&quot;&gt;static&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GtkWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;widget&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gpointer&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;data&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gtk_main_quit&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;main&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;int&lt;/span&gt;   &lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;char&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[]&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;GtkWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;GtkWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;gtk_init&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argc&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;amp;&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;argv&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;window&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gtk_window_new&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GTK_WINDOW_TOPLEVEL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g_signal_connect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;delete-event&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G_CALLBACK&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delete_event&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g_signal_connect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;destroy&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G_CALLBACK&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;destroy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gtk_container_set_border_width&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GTK_CONTAINER&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;GtkWidget&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_box_gamepad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;v_box_gamepad&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gtk_hbox_new&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gtk_container_add&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GTK_CONTAINER&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;v_box_gamepad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;


    &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;gtk_button_new_with_label&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;Hello World&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;g_signal_connect&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;clicked&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;G_CALLBACK&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;hello&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;NULL&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;// gtk_container_add (GTK_CONTAINER (window), button);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gtk_box_pack_start&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;GTK_BOX&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;v_box_gamepad&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;button&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;TRUE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;FALSE&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;gtk_window_set_title&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;GTK_WINDOW&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;L:A_N:application_ID:test&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;gtk_widget_show_all&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;window&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;);&lt;/span&gt;

    &lt;span class=&quot;n&quot;&gt;gtk_main&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;();&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;

&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;在编译之前，我们还需要拷贝系统中关于gtk相关的动态库到我们的电脑上，这里为了省事可以全部考过来，拷贝时候比较消耗时间，所以大家要耐心等待。&lt;/p&gt;

&lt;p&gt;越狱之后我安装了&lt;a href=&quot;https://github.com/koreader/koreader&quot;&gt;koreader&lt;/a&gt;，其中自带了ssh服务，所以这里我使用ssh直接拷贝了系统库，读者也可以使用Kterm手动备份 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/usr/lib&lt;/code&gt; 目录到 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;/mnt/us/&lt;/code&gt; 下（这个目录是usb连接时的根目录），并通过usb复制文件到你的电脑上。&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# Copy /usr/lib to this project&lt;/span&gt;
ssh &lt;span class=&quot;nt&quot;&gt;-p&lt;/span&gt; &amp;lt;your_kindle_ssh_port&amp;gt; root@&amp;lt;your_kindle_ip&amp;gt; &lt;span class=&quot;s2&quot;&gt;&quot;cd /usr &amp;amp;&amp;amp; tar -cpvzf /mnt/us/libs.tar.gz lib&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# It&apos;s a Upper &quot;P&quot; in &quot;scp -P ...&quot;&lt;/span&gt;
scp &lt;span class=&quot;nt&quot;&gt;-P&lt;/span&gt; &amp;lt;your_kindle_ssh_port&amp;gt; root@&amp;lt;your_kindle_ip&amp;gt;:/mnt/us/libs.tar.gz &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;
&lt;span class=&quot;nb&quot;&gt;tar&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-xzvf&lt;/span&gt; libs.tar.gz
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后直接进行编译即可&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 启动容器&lt;/span&gt;
docker run &lt;span class=&quot;nt&quot;&gt;-it&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;:/src xfangfang/gcc-4.7-arm-linux-gnueabi /bin/bash
&lt;span class=&quot;c&quot;&gt;# 容器内执行编译&lt;/span&gt;
arm-linux-gnueabi-gcc-4.7 hello_world_gui.c &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; hello_world_gui &lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt;pkg-config gtk+-2.0 &lt;span class=&quot;nt&quot;&gt;--cflags&lt;/span&gt;&lt;span class=&quot;sb&quot;&gt;`&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt;/usr/arm-linux-gnueabi/lib &lt;span class=&quot;nt&quot;&gt;-L&lt;/span&gt;/src/lib &lt;span class=&quot;nt&quot;&gt;-lgtk-x11-2&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lgdk-x11-2&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lXrender&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lXinerama&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lXext&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lgdk_pixbuf-2&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lpangocairo-1&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lXdamage&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lXfixes&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-latk-1&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lcairo&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lpixman-1&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lpng16&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lxcb-shm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lxcb-render&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lX11&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lxcb&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lXau&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lXdmcp&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lgio-2&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lpangoft2-1&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lpango-1&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lfontconfig&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lfreetype&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lz&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lexpat&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lgobject-2&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lffi&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-lgmodule-2&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lgthread-2&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lglib-2&lt;/span&gt;.0 &lt;span class=&quot;nt&quot;&gt;-lstdc&lt;/span&gt;++
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;⚠️：前面docker镜像中的gtk库头文件版本为：2.24，但是kindle提供的gtk动态库为2.10，所以要注意不要不小心使用了高版本的api。&lt;/p&gt;

&lt;h2 id=&quot;移植实战&quot;&gt;移植实战&lt;/h2&gt;

&lt;p&gt;简单移植了一个最初于1999年开发的俄罗斯方块，在github找到了一个适配了GTK2与GTK3的版本，简单调整了一下窗口大小与标题编译即可。&lt;/p&gt;

&lt;p&gt;Gtihub repo: &lt;a href=&quot;https://github.com/xfangfang/gtktetris_kindle&quot;&gt;gtktetris_kindle&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-05-06-031/gtktetris.jpg&quot; alt=&quot;gtktetris&quot; /&gt;&lt;/p&gt;
</description>
            <pubDate>Fri, 06 May 2022 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>如何制作一个144ppi且超小的png图片</title>
            <link>/030</link>
            <guid isPermaLink="true">/030</guid>
            <description>&lt;p&gt;当你在将png图片转换为base64文本且需要保持144ppi时（一般的压缩工具只会导出72ppi的图片），你可能会需要这篇文章来尽可能降低你的图片大小。&lt;/p&gt;

&lt;h2 id=&quot;前情&quot;&gt;前情&lt;/h2&gt;

&lt;p&gt;最近（有相当一段时间了）买了一个米家的智能插排，插孔是支持分控的，想用来搭配电脑实现 20%-80% 电量自动充电循环拯救一下被 “建议维修” 的mbp，最终结论是没啥用，不过过程很有趣，这里选取其中一部分分享一下折腾经验。&lt;/p&gt;

&lt;p&gt;我的目的是用电脑来根据电量自动控制这个插排，但是电脑上并没有可以用于自动控制的米家客户端（或许m1的mac可以，但我不是），所以折腾着用python实现了一个简单的控制脚本，搭配xbar可以快速完成一个状态栏插件。&lt;/p&gt;

&lt;p&gt;本来用文字做状态栏菜单的标题就可以了，但是追求完美的小方需要一个完美的米家图标显示在自己的状态栏；xbar支持在脚本中插入一个&lt;strong&gt;144ppi的base64编码图片&lt;/strong&gt;作为状态栏图标，本来随便做一个就可以了，但是同样追求完美的小方想要同时兼顾观感体验与图片大小，因此有了这一期的图文，&lt;strong&gt;如何做一个超小又不影响观感且分辨率是144ppi的png图片&lt;/strong&gt;。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-02-24-030/image-20220224164135064.png&quot; alt=&quot;image-20220224164135064&quot; style=&quot;zoom:50%;&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;操作过程&quot;&gt;操作过程&lt;/h2&gt;

&lt;h3 id=&quot;1-制作图片&quot;&gt;1. 制作图片&lt;/h3&gt;

&lt;p&gt;第一步很简单，我们用PS做一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;36x36&lt;/code&gt;大小的米家图片，导出来。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-02-24-030/image-20220224163219517.png&quot; alt=&quot;image-20220224163219517&quot; style=&quot;zoom:50%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;有三种方式导出图片，&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;快速导出&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;导出为...&lt;/code&gt;、&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;储存为Web所用格式&lt;/code&gt;，以我的经验看最后一种方式导出的图片可以调整到最小，调整的方式有（png8格式、四色、不嵌入颜色配置文件、减少色彩种类）&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-02-24-030/image-20220224165701543.png&quot; alt=&quot;image-20220224165701543&quot; style=&quot;zoom:25%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;三种方式导出的图片大小：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-02-24-030/image-20220224170350391.png&quot; alt=&quot;image-20220224170350391&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;2-删除无用字段&quot;&gt;2. 删除无用字段&lt;/h3&gt;

&lt;p&gt;你以为已经调整到最小了嘛？我们使用&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;hex friend&lt;/code&gt;打开刚刚生成的图片，看到其中的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;tExtSoftware Adobe ImageReady&lt;/code&gt;字样了嘛，这是tExt字段，用来储存额外的信息，把它删掉即可，注意这个字段其实是由四部分组成：前四个字节表示数据长度、tExt表示数据块类型、可变长度的数据内容、四个字节的CRC（cyclic redundancy check）校验。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-02-24-030/image-20220224182447584.png&quot; alt=&quot;image-20220224182447584&quot; /&gt;&lt;/p&gt;

&lt;p&gt;删掉之后，我们的图片大小从 358字节下降到了321字节。（我们也可以通过一些网站来解决，比如：https://tinypng.com）&lt;/p&gt;

&lt;h3 id=&quot;3-修改分辨率&quot;&gt;3. 修改分辨率&lt;/h3&gt;

&lt;p&gt;难道这就结束了？NO！&lt;/p&gt;

&lt;p&gt;PS导出的图片ppi为72，但是我们为了能让图片正常显示在状态栏需要144ppi，该怎么办呢？这时插入我们的神奇数据头到这张图片中。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;00000009 70485973 00001625 00001625 01495224 F0
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-02-24-030/image-20220225044646816.png&quot; alt=&quot;image-20220225044646816&quot; /&gt;&lt;/p&gt;

&lt;p&gt;这个数据头就是用来配置自定义分辨率的，具体的含义大家可以根据png文件头的说明去分析，这里简单说一下，这段二进制串中关键的数据如下：&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;00001625 00001625 01
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;前面两个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;1625&lt;/code&gt;表示分别在长与宽方向1单位长度的像素数量，后面的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;01&lt;/code&gt;表示这里的1单位长度为1米，因为我们需要的是144ppi，1米约等于39.37英寸，简单做个乘法就是：144*39.37≈5669，转换为16进制就是 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;0x1625&lt;/code&gt;。&lt;/p&gt;

&lt;h3 id=&quot;4-转换base64&quot;&gt;4. 转换base64&lt;/h3&gt;

&lt;p&gt;最后在网上找个网站转换把png图转成base64字符串：https://c.runoob.com/front-end/59/&lt;/p&gt;

&lt;h2 id=&quot;最终结果&quot;&gt;最终结果&lt;/h2&gt;

&lt;p&gt;当然是还可以压的更小，但是再小放在macOS状态栏看就开始不清晰了。&lt;/p&gt;

&lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkBAMAAAATLoWrAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAFVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAASAQCkAAAAB3RSTlMB/mWiL80ZQxWwKAAAALRJREFUKM+lkk0KwjAQhQd/sn/W7CW2rlPxAJHgvi0eQKr3P4PRPuyYLqT4bfL4SMJjGPmDlXOHTwrvsABsKYm+BRqqxFEkIuFHha6CVuSXKmskilKpnamALhitRGJ3ky/FqvYkmTI8qchURZ+r/fR7zFRso6pyprxFpqr3ecOI7RDWQBhSDZsFOQOe7zZUS8CGVKEGGirTAoVzySAIuYBsxj4tlZrbfTBXUUQugebxWp85PAFLWhtKWfFu3QAAAABJRU5ErkJggg==
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACQAAAAkBAMAAAATLoWrAAAACXBIWXMAABYlAAAWJQFJUiTwAAAAFVBMVEUAAAAAAAAAAAAAAAAAAAAAAAAAAAASAQCkAAAAB3RSTlMB/mWiL80ZQxWwKAAAALRJREFUKM+lkk0KwjAQhQd/sn/W7CW2rlPxAJHgvi0eQKr3P4PRPuyYLqT4bfL4SMJjGPmDlXOHTwrvsABsKYm+BRqqxFEkIuFHha6CVuSXKmskilKpnamALhitRGJ3ky/FqvYkmTI8qchURZ+r/fR7zFRso6pyprxFpqr3ecOI7RDWQBhSDZsFOQOe7zZUS8CGVKEGGirTAoVzySAIuYBsxj4tlZrbfTBXUUQugebxWp85PAFLWhtKWfFu3QAAAABJRU5ErkJggg==&quot; alt=&quot;image&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;参考资料&quot;&gt;参考资料&lt;/h2&gt;

&lt;p&gt;png文件格式介绍1：https://www.jianshu.com/p/e4a7eb127d53&lt;/p&gt;

&lt;p&gt;png文件格式介绍2：https://blog.csdn.net/destiny1507/article/details/82942141&lt;/p&gt;

&lt;p&gt;图片转base64：https://c.runoob.com/front-end/59/&lt;/p&gt;

&lt;p&gt;缩小图片：https://tinypng.com&lt;/p&gt;
</description>
            <pubDate>Thu, 24 Feb 2022 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>分析Bilibili客户端的“哔哩必连”协议</title>
            <link>/028</link>
            <guid isPermaLink="true">/028</guid>
            <description>&lt;h2 id=&quot;前情提要&quot;&gt;前情提要&lt;/h2&gt;

&lt;p&gt;一直很希望有一个可以在电脑上接收DLNA投屏的软件，找了一圈最后找到了kodi，但是kodi不是很适合日常使用，因为他要求使用时软件全屏前置，这和我的需求完全不相同，我想要的是一个专注DLNA投屏，使用时可以悬浮小窗或者全屏播放，日常还可以隐藏在状态栏的简单轻量级的应用。&lt;/p&gt;

&lt;p&gt;机缘偶合之下，看到了一篇DLNA协议的介绍，感觉用python实现一遍应该在我的能力范围之内，所以开始了尝试性的开发。后来程序不断完善分别在不同平台打包分发，变成了一个&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;PC端全平台的DLNA视频接收器&lt;/code&gt; —— Macast。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;说句题外话：如果让我重新来过一次，我可能会选择用go来开发，虽然相比python对我来说go写起来会慢一些，但是无论是软件大小还是多平台打包分发的能力都会更有优势，使用python会面临两个严重问题，那就是启动速度过慢与多平台打包会遭遇兼容问题（尤其是linux上很耗时间）。&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;再后来因为国内众多的视频软件开始仅仅对本家的TV客户端开放高清视频投放的权限，导致我日常用起来总是看不到高清的视频。所以微微重构了一下软件，让他支持通过插件的形式切换不同的协议，开发者仅仅关注协议实现的本身即可快速的实现一个对应平台的投屏接收器，这里的&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;哔哩必连&lt;/code&gt;就是 Macast 对协议插件的第一次尝试。&lt;/p&gt;

&lt;p&gt;&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;哔哩必连&lt;/code&gt;是B站于2021年推出的专属投屏协议，他支持高清视频投屏与弹幕投屏，这正是我需要的内容，所以我开始了对这个协议的分析。&lt;/p&gt;

&lt;h2 id=&quot;如何抓包&quot;&gt;如何抓包&lt;/h2&gt;

&lt;p&gt;首先要讲一讲如何抓取协议数据，这个部分我要说的其实并不是唯一解，大家可以用各种自己喜欢的方式抓取数据。但是如果你暂时还没有头绪，或许可以尝试一下我说的办法。&lt;/p&gt;

&lt;p&gt;首先我们需要两个智能设备，设备中必须有一个运行安卓系统，我们在其上安装&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;B站TV版&lt;/code&gt;作为接收投屏的一端，另外一个设备安装支持&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;哔哩必连&lt;/code&gt;的B站客户端。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;软件的下载地址：https://app.bilibili.com&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;这里我使用了一个运行着openwrt的路由器搭配wireshark进行抓包，在路由器上运行tcpdump（虽然叫tcpdump但是udp数据也是可以抓取的），通过ssh实时传输抓包数据到电脑并输入到wireshark中，就能实现实时抓取路由器下任意一个设备的数据包（不过这种方式对有加密的数据貌似就无能为力了）。&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 一条命令抓取实时数据&lt;/span&gt;
ssh root@192.168.1.1 &lt;span class=&quot;s1&quot;&gt;&apos;tcpdump -i wlan1 host 192.168.1.223 -s 0 -l -w -&apos;&lt;/span&gt; | /Applications/Wireshark.app/Contents/MacOS/Wireshark &lt;span class=&quot;nt&quot;&gt;-k&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-i&lt;/span&gt; -
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;简单介绍一下上面shell的含义，首先通过ssh在路由器(192.168.1.1)上执行命令，命令内容为使用tcpdump抓包，tcpdump具体的指令含义大家可以查看相关的文档，这里我就不赘述了。然后通过管道将数据传输给wireshark实时显示出来。&lt;/p&gt;

&lt;p&gt;要说明的是Fiddler和Charles这样的抓包工具在抓http的数据时候比较有用，但是我们要分析的数据不仅仅是http协议，还可能有其他应用层协议，不仅仅是tcp也可能是udp，所以wireshark在这种需求下非常有用。&lt;/p&gt;

&lt;p&gt;除了在路由器上通过tcpdump抓包，还可以将TV端应用安装在安卓模拟器上，抓取模拟器的数据，不过这个方法对我来说用起来不如直接在路由器上抓包方便。&lt;/p&gt;

&lt;h2 id=&quot;分析数据&quot;&gt;分析数据&lt;/h2&gt;

&lt;p&gt;先指明我的两个设备的ip地址，TV端地址：192.168.1.223，客户端地址：192.168.1.156&lt;/p&gt;

&lt;p&gt;简单插一嘴，DLNA服务流程大致如下：服务向SSDP注册 -&amp;gt; 客户端通过SSDP发现服务 -&amp;gt; 客户端访问主体服务的xml描述 -&amp;gt; 客户端与主体服务交互。&lt;/p&gt;

&lt;p&gt;因为我们知道B站的小电视也可以接受标准DLNA投屏，所以我们对协议的分析也分别从这三个内容入手——SSDP、XML描述、协议交互数据&lt;/p&gt;

&lt;h3 id=&quot;ssdp协议&quot;&gt;SSDP协议&lt;/h3&gt;

&lt;p&gt;SSDP是DLNA协议中的服务发现协议，它使用UDP组播，发送HTTP Header格式的数据内容。&lt;/p&gt;

&lt;p&gt;DLNA协议会在组播（239.255.255.250:1900）中发送类似如下的数据，用于通知局域网中的设备某地址存在可用的DLNA服务，客户端收到之后，就会访问Location中指定的描述文件地址，查看具体的服务内容：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-01-17-028/image-20220117122521043.png&quot; alt=&quot;image-20220117122521043&quot; style=&quot;zoom: 33%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;其中NT与USN用于区分设备类型，一般来说DLNA Renderer会发送如下设备类型到组播。（也就是一次组播发送多次内容，使用下边的列表中的设备类型替换上图中的USN与NT即可）&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;
  &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;uuid:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usn&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;::upnp:rootdevice&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;uuid:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usn&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;uuid:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usn&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;::urn:schemas-upnp-org:device:MediaRenderer:1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;uuid:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usn&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;::urn:schemas-upnp-org:service:RenderingControl:1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;uuid:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usn&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;::urn:schemas-upnp-org:service:ConnectionManager:1&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
  &lt;span class=&quot;sa&quot;&gt;f&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;uuid:&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;usn&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;::urn:schemas-upnp-org:service:AVTransport:1&apos;&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;通过分析数据，我们看到组播中收到了一条与众不同的内容：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-01-17-028/image-20220117124343548.png&quot; alt=&quot;image-20220117124343548&quot; style=&quot;zoom: 33%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;很容易就分析到在SSDP这一部分 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;f&apos;uuid:{usn}::urn:schemas-upnp-org:service:NirvanaControl:3&apos;&lt;/code&gt;是B站专属的服务内容，并且其采用了与DLNA协议指定格式不同的uuid格式，由于这里服务名称为&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;NirvanaControl&lt;/code&gt;所以后文中用NVA（nirvana）协议指代“哔哩必连”。（这里NVA实际是来自B站安卓客户端内部对nirvana的简写）&lt;/p&gt;

&lt;p&gt;看来SSDP部分B站仅仅是增加了一个专属的服务组播内容与微调（UUID的微调），我们将DLNA的SSDP部分也做微调即可。&lt;/p&gt;

&lt;p&gt;非常简单吧，已经完成了1/3。&lt;/p&gt;

&lt;h3 id=&quot;xml描述&quot;&gt;XML描述&lt;/h3&gt;

&lt;p&gt;DLNA使用XML来描述服务包含的功能与具体的接口定义等等，客户端在获取到服务的XML描述后就可以根据其定义的具体内容来访问对应的服务，早期NVA协议是通过扩展DLNA的XML描述的办法来实现定制功能的。（这里关于服务的XML描述记录于SSDP组播内容的Location字段。）&lt;/p&gt;

&lt;p&gt;下一步我们尝试在电脑上访问SSDP header中Location指定的xml描述文件，看看和NVA协议标准DLNA有何不同。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;root&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:dlna=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-dlna-org:device-1-0&quot;&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-upnp-org:device-1-0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;specVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;major&amp;gt;&lt;/span&gt;1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/major&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;minor&amp;gt;&lt;/span&gt;0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/minor&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/specVersion&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;device&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;deviceType&amp;gt;&lt;/span&gt;urn:schemas-upnp-org:device:MediaRenderer:1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/deviceType&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;UDN&amp;gt;&lt;/span&gt;uuid:XY0000...&lt;span class=&quot;nt&quot;&gt;&amp;lt;/UDN&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;friendlyName&amp;gt;&lt;/span&gt;我的小电视&lt;span class=&quot;nt&quot;&gt;&amp;lt;/friendlyName&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;manufacturer&amp;gt;&lt;/span&gt;Bilibili Inc.&lt;span class=&quot;nt&quot;&gt;&amp;lt;/manufacturer&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;manufacturerURL&amp;gt;&lt;/span&gt;https://bilibili.com/&lt;span class=&quot;nt&quot;&gt;&amp;lt;/manufacturerURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;modelDescription&amp;gt;&lt;/span&gt;云视听小电视&lt;span class=&quot;nt&quot;&gt;&amp;lt;/modelDescription&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;modelName&amp;gt;&lt;/span&gt;16s&lt;span class=&quot;nt&quot;&gt;&amp;lt;/modelName&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;modelNumber&amp;gt;&lt;/span&gt;1024&lt;span class=&quot;nt&quot;&gt;&amp;lt;/modelNumber&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;modelURL&amp;gt;&lt;/span&gt;https://app.bilibili.com/&lt;span class=&quot;nt&quot;&gt;&amp;lt;/modelURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serialNumber&amp;gt;&lt;/span&gt;1024&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serialNumber&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;X_brandName&amp;gt;&lt;/span&gt;Meizu&lt;span class=&quot;nt&quot;&gt;&amp;lt;/X_brandName&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;hostVersion&amp;gt;&lt;/span&gt;25&lt;span class=&quot;nt&quot;&gt;&amp;lt;/hostVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;ottVersion&amp;gt;&lt;/span&gt;104600&lt;span class=&quot;nt&quot;&gt;&amp;lt;/ottVersion&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;channelName&amp;gt;&lt;/span&gt;master&lt;span class=&quot;nt&quot;&gt;&amp;lt;/channelName&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;capability&amp;gt;&lt;/span&gt;254&lt;span class=&quot;nt&quot;&gt;&amp;lt;/capability&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;dlna:X_DLNADOC&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:dlna=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-dlna-org:device-1-0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;DMR-1.50&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dlna:X_DLNADOC&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;dlna:X_DLNACAP&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;xmlns:dlna=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;urn:schemas-dlna-org:device-1-0&quot;&lt;/span&gt;&lt;span class=&quot;nt&quot;&gt;&amp;gt;&lt;/span&gt;playcontainer-1-0&lt;span class=&quot;nt&quot;&gt;&amp;lt;/dlna:X_DLNACAP&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceList&amp;gt;&lt;/span&gt;...&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceList&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/device&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/root&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;可以注意到，除了uuid的格式不同外，NVA协议还在device的元素中添加了一些用于标识兼容性的内容，比如：capability，ottVersion等内容，后来经过测试如果不指定这些值会导致在某些版本的客户端上无法实现高清投屏。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;补充：关于xml device中各个元素的值&lt;/p&gt;

  &lt;table&gt;
    &lt;thead&gt;
      &lt;tr&gt;
        &lt;th&gt;元素&lt;/th&gt;
        &lt;th&gt;值&lt;/th&gt;
      &lt;/tr&gt;
    &lt;/thead&gt;
    &lt;tbody&gt;
      &lt;tr&gt;
        &lt;td&gt;hostVersion&lt;/td&gt;
        &lt;td&gt;电视的Android 版本&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;X_brandName&lt;/td&gt;
        &lt;td&gt;电视品牌名&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;modelName&lt;/td&gt;
        &lt;td&gt;电视型号名&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;ottVersion&lt;/td&gt;
        &lt;td&gt;未知含义：104600/104501&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;channelName&lt;/td&gt;
        &lt;td&gt;未知/master 电视/xiaomi&lt;/td&gt;
      &lt;/tr&gt;
      &lt;tr&gt;
        &lt;td&gt;capability&lt;/td&gt;
        &lt;td&gt;有弹幕：254 / 有弹幕+4K：1&lt;/td&gt;
      &lt;/tr&gt;
    &lt;/tbody&gt;
  &lt;/table&gt;
&lt;/blockquote&gt;

&lt;p&gt;friendlyName、manufacturer等内容也在客户端的判定范围内，如果不严格的指定这些值，会导致客户端将其对待为普通的DLNA renderer。&lt;/p&gt;

&lt;p&gt;现在看起来区别还是很小的，让我们继续看一看 被折叠的 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;serviceList&lt;/code&gt;部分（为方便阅读简化了一下，不影响实际功能）。&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceList&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceType&amp;gt;&lt;/span&gt;urn:schemas-upnp-org:service:AVTransport:1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceType&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceId&amp;gt;&lt;/span&gt;urn:upnp-org:serviceId:AVTransport&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;controlURL&amp;gt;&lt;/span&gt;AVTransport/action&lt;span class=&quot;nt&quot;&gt;&amp;lt;/controlURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;eventSubURL&amp;gt;&lt;/span&gt;AVTransport/event&lt;span class=&quot;nt&quot;&gt;&amp;lt;/eventSubURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;SCPDURL&amp;gt;&lt;/span&gt;dlna/AVTransport.xml&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SCPDURL&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceType&amp;gt;&lt;/span&gt;urn:schemas-upnp-org:service:RenderingControl:1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceType&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceId&amp;gt;&lt;/span&gt;urn:upnp-org:serviceId:RenderingControl&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;controlURL&amp;gt;&lt;/span&gt;RenderingControl/action&lt;span class=&quot;nt&quot;&gt;&amp;lt;/controlURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;eventSubURL&amp;gt;&lt;/span&gt;RenderingControl/event&lt;span class=&quot;nt&quot;&gt;&amp;lt;/eventSubURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;SCPDURL&amp;gt;&lt;/span&gt;dlna/RenderingControl.xml&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SCPDURL&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceType&amp;gt;&lt;/span&gt;urn:schemas-upnp-org:service:ConnectionManager:1&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceType&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceId&amp;gt;&lt;/span&gt;urn:upnp-org:serviceId:ConnectionManager&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;controlURL&amp;gt;&lt;/span&gt;ConnectionManager/action&lt;span class=&quot;nt&quot;&gt;&amp;lt;/controlURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;eventSubURL&amp;gt;&lt;/span&gt;ConnectionManager/event&lt;span class=&quot;nt&quot;&gt;&amp;lt;/eventSubURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;SCPDURL&amp;gt;&lt;/span&gt;dlna/ConnectionManager.xml&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SCPDURL&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;service&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceType&amp;gt;&lt;/span&gt;urn:app-bilibili-com:service:NirvanaControl:3&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceType&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;serviceId&amp;gt;&lt;/span&gt;urn:app-bilibili-com:serviceId:NirvanaControl&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceId&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;controlURL&amp;gt;&lt;/span&gt;NirvanaControl/action&lt;span class=&quot;nt&quot;&gt;&amp;lt;/controlURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;eventSubURL&amp;gt;&lt;/span&gt;NirvanaControl/event&lt;span class=&quot;nt&quot;&gt;&amp;lt;/eventSubURL&amp;gt;&lt;/span&gt;
  &lt;span class=&quot;nt&quot;&gt;&amp;lt;SCPDURL&amp;gt;&lt;/span&gt;dlna/NirvanaControl.xml&lt;span class=&quot;nt&quot;&gt;&amp;lt;/SCPDURL&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/service&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/serviceList&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;一般的DLNA投屏接受端会实现三个服务：AVTransport、RenderingControl、ConnectionManager，分别负责播放控制(播放暂停等主要功能)、播放器控制(比如音量调节等功能)和协议相关的信息服务。&lt;/p&gt;

&lt;p&gt;其中每一项服务都包含了 服务ID（serviceId，标识服务）、控制地址（controlURL，客户端向这个地址POST数据）、订阅地址（eventSubURL，客户端可以选择订阅某个服务，这样在电视端有信息更新时会主动向客户端发送数据）、服务信息描述地址（SCPDURL，一个XML文件 描述了一个服务提供具体哪些功能）。&lt;/p&gt;

&lt;p&gt;可以看到，在这里NVA协议新增了一项服务：NirvanaControl （对应上一节SSDP新增的内容）&lt;/p&gt;

&lt;p&gt;进一步访问 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;{host}/dlna/NirvanaControl.xml&lt;/code&gt; 可看到里面提供了如下方法：&lt;/p&gt;

&lt;div class=&quot;language-xml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nt&quot;&gt;&amp;lt;actionList&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;action&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;name&amp;gt;&lt;/span&gt;GetAppInfo&lt;span class=&quot;nt&quot;&gt;&amp;lt;/name&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;argumentList&amp;gt;&lt;/span&gt;...&lt;span class=&quot;nt&quot;&gt;&amp;lt;/argumentList&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/action&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;action&amp;gt;&lt;/span&gt;
...
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/action&amp;gt;&lt;/span&gt;
&lt;span class=&quot;nt&quot;&gt;&amp;lt;/actionList&amp;gt;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;后来通过对数据包的分析，发现这个列表中的内容应该是早期NVA协议实现的时候会用到的，当时采用的扩展DLNA协议的方式将定制化的功能说明放入XML中，但是现在的NVA几乎不使用这个XML描述中的访问了，所以我们可以直接忽视里面的内容，重新实现协议的时候只要保留这个XML描述即可，无需实现里面的所有功能。&lt;/p&gt;

&lt;p&gt;也就是说，xml部分的内容也不多，重新实现的时候几乎只需要复制粘贴即可。&lt;/p&gt;

&lt;h3 id=&quot;协议交互数据&quot;&gt;协议交互数据&lt;/h3&gt;

&lt;p&gt;如果是纯粹的DLNA，设备间交互是采用互相POST数据的办法进行的（接口通过上面提到的XML描述获取），NVA协议在此基础上还拓展了新的交互方式，采用了类似websocket的TCP长连接与客户端交互。&lt;/p&gt;

&lt;p&gt;我们使用wireshark抓取TV端数据，然后再通过wireshark的过滤指令&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;ip.src == 192.168.1.156 or ip.dst == 192.168.1.156&lt;/code&gt;在wireshark内将两个设备间的数据过滤出来。&lt;/p&gt;

&lt;p&gt;通过分析数据可以看到两个设备间交互的数据均为TCP协议的数据，除了常规DLNA xml相关的访问之外，还有一些看似是乱码的TCP数据。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-01-17-028/image-20220117122011018.png&quot; alt=&quot;image-20220117122011018&quot; style=&quot;zoom:25%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;我们随便选择一条，右键跟随TCP流，这样我们就能够获取到单个连接的所有收发数据。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-01-17-028/image-20220117142317334.png&quot; alt=&quot;image-20220117142317334&quot; style=&quot;zoom: 25%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;咱们先分析这条TCP链接的第一个交互数据，其中红色表示客户端发送的内容，蓝色是TV端返回的数据，可以看到其与HTTP/1.0的区别仅仅是将协议名改成了NVA/1.0，并且设置了 Keep-Alive 说明后面还会有其他数据等待接受或发送。&lt;/p&gt;

&lt;p&gt;如果感觉分析的过程有点啰嗦，可以直接看后面我整理凝练的协议说明。&lt;/p&gt;

&lt;h5 id=&quot;1-解密发送指令的结构&quot;&gt;1. 解密发送指令的结构&lt;/h5&gt;

&lt;p&gt;前面说到NVA协议是一个类似websocket的TCP长连接协议，那么上图中展示的这一步就相当于wensocket中的HTTP upgrade，让两个节点建立起了长连接，后面就开始命令数据的传送：&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2022-01-17-028/image-20220117151447334.png&quot; alt=&quot;image-20220117151447334&quot; style=&quot;zoom:25%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;似乎大部分都是明文，这让人安心了不少。先看客户端发送的两个数据（上图红色）：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;000000ED  e0 02 00 00 00 01 01 07  43 6f 6d 6d 61 6e 64 09   ........ Command.
000000FD  47 65 74 56 6f 6c 75 6d  65                        GetVolum e

00000106  e0 03 00 00 00 02 01 07  43 6f 6d 6d 61 6e 64 04   ........ Command.
00000116  50 6c 61 79 00 00 01 72  7b 22 61 75 74 6f 4e 65   Play...r {&quot;autoNe
...
00000286  36 35 34 30 30 31 30 30  22 7d                     65400100 &quot;}

&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;我们很容易看出来的是Command与GetVolume之间的一个字节是 0x09，9刚好是GetVolume这个命令语句的长度，而第二条数据中，Command后面的字节为0x04，也刚好是Play这个单词的长度，这意味着在Command后面跟随的一个字节表示了后面命令名称的长度。&lt;/p&gt;

&lt;p&gt;经过计数，同理， Play 后面的四个字节，也正是其后json字符串的长度。以此类推Command前一个字节内容都为0x07，是这个Command单词本身的长度。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;所以我们现在对于Command相关的指令，得到了如下信息：一条命令可以分为两部分或三部分，第一个部分均为 Command，第二部分为命令的名称，第三部分为可选的json字符串。其中前两个部分开头由一个字节表示指令的长度，第三部分开头由四个字节表示json字符串的长度。&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;下面只剩前七个字节需要分析了，为方便找规律，我们现在只关注前16个字节。&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;000000ED  e0 02 00 00 00 01 01 07  43 6f 6d 6d 61 6e 64 09   ........ Command.
00000106  e0 03 00 00 00 02 01 07  43 6f 6d 6d 61 6e 64 04   ........ Command.
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;可以看到 第一个字节始终为 0xe0，第七个字节始终为 0x01，这两个暂时没有变化所以分析不出来。（后来分析发现这两个值在所有指令中都始终不变，我简单将0xe0理解为指令的标识，0x01猜测可能表示的是指令的版本）&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;先看第二个字节，其分别为 0x02和0x03，正好对应了指令参数的数量，第一条有两个参数：Command和Play，第二条有三个参数：Command、GetVolume和json字符串&lt;/strong&gt; （肯定不是一下就分析出来的，是在看过了很多指令之后总结的规律）&lt;/p&gt;

&lt;p&gt;然后看第三个字节开始的四个字节，第一条指令为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;00 00 00 01&lt;/code&gt;，第二条指令为 &lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;00 00 00 02&lt;/code&gt; 再看看后面发送的指令，以此为 03、04，所以我们可以知道其代表的是指令的序列号。&lt;/p&gt;

&lt;h5 id=&quot;2-如何发送指令的返回值&quot;&gt;2. 如何发送指令的返回值&lt;/h5&gt;

&lt;p&gt;在客户端发送了两条指令后，TV回复了四段数据&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;    000000FF  e4 00 00 00 00 01                                  ......
    
    00000105  c0 01 00 00 00 01 00 00  00 0d 7b 22 76 6f 6c 75   ........ ..{&quot;volu
    00000115  6d 65 22 3a 33 33 7d                               me&quot;:33}
    
    0000011C  c0 00 00 00 00 02                                  ......
    
    00000122  e0 03 00 00 00 02 01 07  43 6f 6d 6d 61 6e 64 0a   ........ Command.
    00000132  4f 6e 50 72 6f 67 72 65  73 73 00 00 00 1d 7b 22   OnProgre ss....{&quot;
    00000142  64 75 72 61 74 69 6f 6e  22 3a 39 31 2c 22 70 6f   duration &quot;:91,&quot;po
    00000152  73 69 74 69 6f 6e 22 3a  31 34 7d                  sition&quot;: 14}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;紧接着，客户端又回复了如下的内容&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;    00000290  c0 00 00 00 00 02                                  ......
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;咱们先分析TV端发送的内容，首先发了六个字节0xe4开头的数据，这个暂时含义不明，咱们之后分析。&lt;/p&gt;

&lt;p&gt;然后发送了 c0 开头的数据，可以看到其后有volume字样的明文，所以不难猜测，这是对前面 GetVolume的返回值。&lt;/p&gt;

&lt;p&gt;由前面分析的经验不难得到：&lt;strong&gt;0xc0代表回复指令，其后一个字节表示参数的数量（为0或1），在之后四个字节表示回复指令的序号（与自身发送指令的序号无关），若参数数量标识为1，则再之后的四个字节表示参数的长度，最后参数长度个字节表示返回的json字符串。&lt;/strong&gt;&lt;/p&gt;

&lt;h5 id=&quot;3-发送ping&quot;&gt;3. 发送ping&lt;/h5&gt;

&lt;p&gt;前面出现了一条：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;e4 00 00 00 00 01 &lt;/code&gt; 咱们一直无法分析出内容来，后面发现，每隔一秒，TV端都会向客户端发送类似的数据，所以推测出：此指令负责定时ping一下客户端，验证客户端还在线。&lt;/p&gt;

&lt;p&gt;ping指令由0xe4开头，后面跟随 0x00 表示没有参数，再之后的四个字节表示当前的指令序号，由1起单调递增。&lt;/p&gt;

&lt;h2 id=&quot;nva协议整理&quot;&gt;NVA协议整理&lt;/h2&gt;

&lt;p&gt;以下协议内容为个人整理，仅供学习参考，不得用于非法用途。&lt;/p&gt;

&lt;h3 id=&quot;协议头&quot;&gt;协议头&lt;/h3&gt;

&lt;h5 id=&quot;1-客户端建立连接时发送&quot;&gt;1. 客户端建立连接时发送&lt;/h5&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;err&quot;&gt;SETUP /projection NVA/1.0
Session: {session}
NvaVersion: 1
Connection: Keep-Alive
UUID: {device-UUID}
User-Agent: Linux/3.0.0 UPnP/1.0 Platinum/1.0.5.13
Host: 192.168.1.223:9958
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;客户端device-UUID：同一设备唯一不变。&lt;/p&gt;

&lt;p&gt;格式：Y + 35字节[0-1A-Z]，例如：Y12345ABCDE12345ABCDE12345ABCDE12345&lt;/p&gt;

&lt;p&gt;session：可使用python自带模块 &lt;em&gt;uuid&lt;/em&gt; 生成，例如：b94eb921-882c-ba2b-8ecd-f456769987de&lt;/p&gt;

&lt;p&gt;Host：想要连接的TV端IP地址与端口（端口貌似一直是9958）&lt;/p&gt;

&lt;h5 id=&quot;2-tv端响应建立连接&quot;&gt;2. TV端响应建立连接&lt;/h5&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;err&quot;&gt;NVA/1.0 200 OK
NvaVersion: 1
Session: {session}
Connection: Keep-Alive
UUID: {device-UUID}
Date: Sun, 02 Jan 2022 03:58:00 GMT
Content-Length: 0
Server: Linux/3.0.0, UPnP/1.0, Platinum/1.0.5.13
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;TV端device-UUID：唯一不变：XY + 35字节[0-1A-Z]，例如：XY12345ABCDE12345ABCDE12345ABCDE12345&lt;/p&gt;

&lt;p&gt;session：为客户端指定的session&lt;/p&gt;

&lt;h5 id=&quot;3-客户端断线重连时发送&quot;&gt;3. 客户端断线重连时发送&lt;/h5&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;err&quot;&gt;RESTORE /projection NVA/1.0
Session: {session}
NvaVersion: 1
Connection: Keep-Alive
UUID: {device-UUID}
User-Agent: Linux/3.0.0 UPnP/1.0 Platinum/1.0.5.13
Host: 192.168.1.223:9958
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;参数定义不变，仅仅是将方法名改为了 RESTORE&lt;/p&gt;

&lt;h5 id=&quot;4-tv端响应断线重连&quot;&gt;4. TV端响应断线重连&lt;/h5&gt;

&lt;div class=&quot;language-http highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;err&quot;&gt;NVA/1.0 200 OK
NvaVersion: 1
Session: {session}
Connection: Keep-Alive
UUID: {device-UUID}
Date: Sun, 02 Jan 2022 03:58:00 GMT
Content-Length: 0
Server: Linux/3.0.0, UPnP/1.0, Platinum/1.0.5.13
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;参数定义不变，内容与响应建立连接相同。&lt;/p&gt;

&lt;p&gt;但是TV端会根据客户端发来的设备ID与session查找历史连接，如果找到session相同的历史连接，则需要通过后续交互发送当前的播放状态，包括进度、播放状态（暂停或播放等）、弹幕状态等等内容&lt;/p&gt;

&lt;p&gt;如果找到了相同的设备ID但是session不同，则需要删除之前的连接。&lt;/p&gt;

&lt;h3 id=&quot;命令结构&quot;&gt;命令结构&lt;/h3&gt;

&lt;p&gt;在建立起连接后，客户端与TV端直接通过TCP长连接互相发送数据。&lt;/p&gt;

&lt;h5 id=&quot;1-指令&quot;&gt;1. 指令&lt;/h5&gt;

&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;# 例1
000000ED  e0 02 00 00 00 01 01 07  43 6f 6d 6d 61 6e 64 09   ........ Command.
000000FD  47 65 74 56 6f 6c 75 6d  65                        GetVolum e
# 例2
00000106  e0 03 00 00 00 02 01 07  43 6f 6d 6d 61 6e 64 04   ........ Command.
00000116  50 6c 61 79 00 00 01 72  7b 22 61 75 74 6f 4e 65   Play...r {&quot;autoNe
...
00000286  36 35 34 30 30 31 30 30  22 7d                     65400100 &quot;}
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;0xe0代表发送指令，其后一个字节表示参数的数量（为2或3），在之后的四个字节表示发送指令的序号（此序号由1起单调递增），再一个字节始终为0x01（有可能表示协议版本为v1），最后参数部分可以分为两部分或三部分，第一个部分均为 Command，第二部分为命令的名称，第三部分为可选的json字符串。其中前两个部分开头由一个字节表示指令的长度，第三部分开头由四个字节表示json字符串的长度。&lt;/p&gt;

&lt;h5 id=&quot;2回复指令&quot;&gt;2.回复指令&lt;/h5&gt;

&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;# 例1
c0 01 00 00 00 01 00 00  00 0d 7b 22 76 6f 6c 75   ........ ..{&quot;volu
6d 65 22 3a 33 33 7d                               me&quot;:33}
# 例2
c0 00 00 00 00 02
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;0xc0代表回复指令，其后一个字节表示参数的数量（为0或1），在之后四个字节表示回复指令的序号（与自身发送指令的序号无关），若参数数量标识为1，则再之后的四个字节表示参数的长度，最后参数长度个字节表示返回的json字符串。&lt;/p&gt;

&lt;h5 id=&quot;3ping&quot;&gt;3.ping&lt;/h5&gt;

&lt;pre&gt;&lt;code class=&quot;language-assembly&quot;&gt;# 例1
e4 00 00 00 00 01
# 例2
e4 00 00 00 00 02
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;TV端每隔一秒发送ping，ping指令由0xe4开头，后面跟随 0x00 表示没有参数，再之后的四个字节表示当前的指令序号，由1起单调递增。&lt;/p&gt;

&lt;h3 id=&quot;客户端命令定义&quot;&gt;客户端命令定义&lt;/h3&gt;

&lt;h5 id=&quot;0-一键登录&quot;&gt;0. 一键登录&lt;/h5&gt;

&lt;p&gt;暂未抓取&lt;/p&gt;

&lt;h5 id=&quot;1-gettvinfo获取电视信息此指令仅由安卓客户端调用ios客户端不会调用&quot;&gt;1. GetTVInfo/获取电视信息，此指令仅由安卓客户端调用，ios客户端不会调用&lt;/h5&gt;

&lt;p&gt;参数：无&lt;/p&gt;

&lt;p&gt;返回值示例：暂未抓取&lt;/p&gt;

&lt;h5 id=&quot;2-getvolume获取音量&quot;&gt;2. GetVolume/获取音量&lt;/h5&gt;

&lt;p&gt;参数：无&lt;/p&gt;

&lt;p&gt;返回值示例：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;volume&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h5 id=&quot;3-setvolume设置音量&quot;&gt;3. SetVolume/设置音量&lt;/h5&gt;

&lt;p&gt;参数：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;volume&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;30&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;4-pause暂停&quot;&gt;4. Pause/暂停&lt;/h5&gt;

&lt;p&gt;参数：无&lt;/p&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;5-resume继续播放&quot;&gt;5. Resume/继续播放&lt;/h5&gt;

&lt;p&gt;参数：无&lt;/p&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;6-senddanmaku发送弹幕&quot;&gt;6. SendDanmaku/发送弹幕&lt;/h5&gt;

&lt;p&gt;参数：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;size&apos;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;size&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可选值:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;18&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;25&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;36&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;mRemoteDmId&apos;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;-1382000000&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;弹幕的ID&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;content&apos;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;hello&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;弹幕内容&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;action&apos;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;type&apos;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:滚动字幕&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;5&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:上侧中央字幕&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;4&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;:下侧中央字幕&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
 &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;&apos;color&apos;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16777215&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;十进制数字表示颜色，转为&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;16&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;进制字符串分别表示&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;RGB&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;弹幕需要在TV端登录账号后才能发送&lt;/p&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;7-switchdanmaku是否显示弹幕&quot;&gt;7. SwitchDanmaku/是否显示弹幕&lt;/h5&gt;

&lt;p&gt;参数：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;open&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;true&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;或&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;open&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;false&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;8-switchspeed倍速&quot;&gt;8. SwitchSpeed/倍速&lt;/h5&gt;

&lt;p&gt;参数示例：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;speed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;1.5&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;9-switchqn切换清晰度&quot;&gt;9. SwitchQn/切换清晰度&lt;/h5&gt;

&lt;p&gt;参数示例：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;120&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;10-stop停止播放&quot;&gt;10. Stop/停止播放&lt;/h5&gt;

&lt;p&gt;参数：无&lt;/p&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;11-seek变更进度单位秒&quot;&gt;11. Seek/变更进度（单位：秒）&lt;/h5&gt;

&lt;p&gt;参数：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;seekTs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;12&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;12-play&quot;&gt;12. Play&lt;/h5&gt;

&lt;p&gt;参数：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;epid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;season_id&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;oid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;epid不为&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;时为epid，为&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;err&quot;&gt;时为aid&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content_type&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;未知含义&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;seekTs&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;播放起始位置&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;access_key&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可选，可用于获取会员高清视频或登录用户账号&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;current_qn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;112&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可选，当前清晰度&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;desire_qn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;120&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可选，用户选择的清晰度&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;danmakuSwitchSave&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可选，切换视频时是否保持弹幕开启状态&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;userDesireSpeed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;#&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;可选，播放速度&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h5 id=&quot;13-playurl设定播放url此指令仅由ios-hd版本客户端调用&quot;&gt;13. PlayUrl/设定播放url，此指令仅由ios HD版本客户端调用&lt;/h5&gt;

&lt;p&gt;参数：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;url&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;​	通过读取url的nva_ext参数并解析json，获取视频的相关信息&lt;/p&gt;

&lt;p&gt;nva_ext：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;ver&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;content&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;param&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;​	param参数定义与Play指令的参数定义相同&lt;/p&gt;

&lt;p&gt;返回值：无&lt;/p&gt;

&lt;h3 id=&quot;客户端命令定义-1&quot;&gt;客户端命令定义&lt;/h3&gt;

&lt;h5 id=&quot;1-onprogress进度条改变单位秒&quot;&gt;1. OnProgress/进度条改变（单位：秒）&lt;/h5&gt;

&lt;p&gt;发送数据：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;duration&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;duration&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;position&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;position&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;期待返回：无&lt;/p&gt;

&lt;h5 id=&quot;2-ondanmakuswitch打开关闭弹幕&quot;&gt;2. OnDanmakuSwitch/打开关闭弹幕&lt;/h5&gt;

&lt;p&gt;发送数据：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;open&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;kc&quot;&gt;true&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;期待返回：无&lt;/p&gt;

&lt;h5 id=&quot;3-onepisodeswitch切换播放视频&quot;&gt;3. OnEpisodeSwitch/切换播放视频&lt;/h5&gt;

&lt;p&gt;发送数据：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;playItem&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;aid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;self.aid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;cid&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;self.cid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;contentType&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;self.content_type&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;epId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;self.epid&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;seasonId&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;self.season_id&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;},&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;qnDesc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;self.qn&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;title&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;self.title&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;期待返回：无&lt;/p&gt;

&lt;h5 id=&quot;4-onqnswitch切换清晰度&quot;&gt;4. OnQnSwitch/切换清晰度&lt;/h5&gt;

&lt;p&gt;发送数据：当前为空时返回如下数据，否则应该返回正确的清晰度数据，数据可由 https://api.bilibili.com/x/tv/playurl 接口与获取播放链接时一并获取。&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
	&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;curQn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;supportQnList&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;description&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;displayDesc&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;needLogin&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;needVip&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;False&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;quality&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
      &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;superscript&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;&quot;&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
    &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
  &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;userDesireQn&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;期待返回：无&lt;/p&gt;

&lt;h5 id=&quot;5-speedchanged播放速度改变&quot;&gt;5. SpeedChanged/播放速度改变&lt;/h5&gt;

&lt;p&gt;发送数据：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;currSpeed&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;current_speed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;supportSpeedList&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;err&quot;&gt;support_speed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;​	默认的倍速支持列表：[&lt;strong&gt;0.5&lt;/strong&gt;, &lt;strong&gt;0.75&lt;/strong&gt;, 1, &lt;strong&gt;1.25&lt;/strong&gt;, &lt;strong&gt;1.5&lt;/strong&gt;, 2]&lt;/p&gt;

&lt;p&gt;期待返回：无&lt;/p&gt;

&lt;h5 id=&quot;6-onplaystate播放状态改变&quot;&gt;6. OnPlayState/播放状态改变&lt;/h5&gt;

&lt;p&gt;发送数据：&lt;/p&gt;

&lt;div class=&quot;language-json highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;p&quot;&gt;{&lt;/span&gt;&lt;span class=&quot;nl&quot;&gt;&quot;playState&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;&lt;span class=&quot;w&quot;&gt; &lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;}&lt;/span&gt;&lt;span class=&quot;w&quot;&gt;
&lt;/span&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;3 加载中
4 播放中
5 暂停
6 媒体播放结束 end of file
7 停止&lt;/p&gt;

&lt;p&gt;期待返回：无&lt;/p&gt;

&lt;h3 id=&quot;协议行为&quot;&gt;协议行为&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;协议支持多台设备同时连接，当播放的状态改变时，会将当前播放的状态广播给所有连接的设备。&lt;/li&gt;
  &lt;li&gt;发送指令的序号是针对每个连接分别设置的，从1算起单调递增，发送指令和ping都会使其增加&lt;/li&gt;
  &lt;li&gt;当断线重连时，发送指令的序号重新设置为1&lt;/li&gt;
  &lt;li&gt;断线重连时，需要发送当前的播放状态&lt;/li&gt;
  &lt;li&gt;前面说的无返回值不是不返回，而是返回值参数为空&lt;/li&gt;
  &lt;li&gt;待补充&lt;/li&gt;
&lt;/ul&gt;

</description>
            <pubDate>Mon, 17 Jan 2022 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>iOS15快捷指令自动化执行小米插座</title>
            <link>/027</link>
            <guid isPermaLink="true">/027</guid>
            <description>&lt;h2 id=&quot;前情&quot;&gt;前情&lt;/h2&gt;

&lt;p&gt;米家智能场景在iOS上搭配快捷指令中的自动化非常好用，可以大大的方便生活，但是貌似在iOS15上，关闭米家指令的运行时显示后，快捷指令并没有正常的调用米家的智能场景。
现象是：毫无反应！&lt;/p&gt;

&lt;p&gt;我之前的解决办法是：用go逆向了米家的接口，跑在路由器上，这样快捷指令可以通过调用http post控制米家设备，但是最近发现了一个更简单的方法～&lt;/p&gt;

&lt;h2 id=&quot;先说结论&quot;&gt;先说结论：&lt;/h2&gt;

&lt;p&gt;关闭米家的运行时显示，然后在后面加“等待1秒” 可以解决&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;../assets/img/posts/2021-12-12-027/mihome.png&quot; alt=&quot;mihome&quot; style=&quot;zoom:20%;&quot; /&gt;&lt;/p&gt;

&lt;p&gt;手把手教学：&lt;/p&gt;

&lt;ol&gt;
  &lt;li&gt;在米家中新建需要的智能场景并添加到siri&lt;/li&gt;
  &lt;li&gt;快捷指令中新建“自动化”，并注意&lt;strong&gt;关闭&lt;/strong&gt;自动化中的“&lt;strong&gt;运行前询问&lt;/strong&gt;”&lt;/li&gt;
  &lt;li&gt;编辑自动化的快捷指令，选择米家app，添加需要的指令&lt;/li&gt;
  &lt;li&gt;在上面的米家指令中，&lt;strong&gt;关闭选项“运行时显示”，&lt;/strong&gt;并在其后添加“&lt;strong&gt;等待1秒&lt;/strong&gt;”&lt;/li&gt;
&lt;/ol&gt;

&lt;h2 id=&quot;推理过程&quot;&gt;推理过程：&lt;/h2&gt;

&lt;p&gt;升级 iOS15后，米家的自动化运行就出了问题，体现为无法自动执行米家智能场景。&lt;/p&gt;

&lt;p&gt;一直以为是iOS15对权限的限制增强了，所以就没从指令的角度上寻找解决方案，而是特别费劲（对我来说）的逆向了一下米家api完成自动控制需求，刚刚看到有人说：“&lt;strong&gt;在米家指令后添加文本朗读可以解决&lt;/strong&gt;”，感觉非常神奇，难道这真的是 iOS15引入的 bug?&lt;/p&gt;

&lt;p&gt;不过还是思考到了原因，大概猜测是在iOS15下，如果某条指令（暂时叫他M）关闭了“运行时显示”，那么运行时会同时执行M和M之后的指令。且在M之后的指令结束时，无论M指令是否运行完毕都得停止运行（相当于ios15 把这样的指令改成 daemon线程）&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;strong&gt;根据我们上面的分析，我们在其后添加别的耗时长的指令也可以有相似的效果，比如：等待1秒&lt;/strong&gt;&lt;/p&gt;
</description>
            <pubDate>Sun, 12 Dec 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>第一次安卓反编译体验——魅族手环H1 精简去登录离线版</title>
            <link>/026</link>
            <guid isPermaLink="true">/026</guid>
            <description>&lt;blockquote&gt;
  &lt;p&gt;魅族手环无法登陆可以说是魅族这个公司的正常操作，魅族很多产品貌似都是外包出去的，发布之后几乎没有更新，我买的魅族pop一代和魅族手环都是如此。 ——被耍猴多次的煤油&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;魅族手环的app很久都没有更新了，安装在现在的手机上发热严重几乎不可用，所以前一阵子我就换成了小米手环（非常讽刺，当时是用更贵的价格支持的魅族）。昨天拿出来魅族手环一看，好家伙app已经登录不上去了。询问了官方客服，客服表明已经停产两年，服务器数据已经清空关闭了。&lt;/p&gt;

&lt;p&gt;于是我们只能自力更生！&lt;/p&gt;

&lt;h3 id=&quot;安卓反编译流程&quot;&gt;安卓反编译流程&lt;/h3&gt;

&lt;p&gt;我们首先要有一份APK，然后通过apktool将他分别反编译为java版本和smali版本。java是用来看源码理思路的，smali是我们可以修改重新编译的，两个版本的代码一一对应，比如有一个MainActivity.java文件就有一个MainActivity.smali文件对应，其中的类、方法等内容也几乎是对应的。&lt;/p&gt;

&lt;p&gt;我们看java代码是比较容易理解程序的逻辑流程的，但java代码几乎没有有效快捷的方法去重新编译，所以我们修改的时候只能先阅读java代码，理清要修改的java代码内容，再通过二者的对应关系修改smali代码。&lt;/p&gt;

&lt;p&gt;针对smali简单科普一下：apk中代码被储存为dex格式，也就是安卓虚拟机的字节码格式，而smali就相当于是dex对应的汇编语言。虽然我们可能不太了解smali，但是不了解也没关系，这对精简来说影响不大。&lt;/p&gt;

&lt;h3 id=&quot;0x00-反编译&quot;&gt;0x00. 反编译&lt;/h3&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 在命令行中先写好环境变量（windows下可能有些不同）&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;input&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/Users/balabala/Downloads/meizushouhuan.apk
&lt;span class=&quot;nv&quot;&gt;output&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/Users/balabala/Downloads/meizushouhuan
&lt;span class=&quot;nv&quot;&gt;output_java&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/Users/balabala/Downloads/meizushouhuan_java
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;我们这一步需要两个软件 apktool、dex2jar和JD-gui，大家可以自行下载&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 反编译为smali项目&lt;/span&gt;
apktool d &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$input&lt;/span&gt;

&lt;span class=&quot;c&quot;&gt;# 反编译为dex项目&lt;/span&gt;
apktool d &lt;span class=&quot;nt&quot;&gt;-s&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-f&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output_java&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$input&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 再将dex转换为jar文件&lt;/span&gt;
sh d2j-dex2jar.sh &lt;span class=&quot;nv&quot;&gt;$output_java&lt;/span&gt;/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;.dex
&lt;span class=&quot;c&quot;&gt;# 打开JD-gui导入jar查看源码&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# 或者下载jadx直接从apk反编译到源码（不过貌似没有前面的效果好）&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;下一步就是阅读java源码整理思路，实际大部分应用都会做“加壳”处理，轻则让我们不能很好的阅读理解代码，重则反编译失败。好在魅族手环并没有这样的操作，一切代码以近乎源码的程度向我们开放。&lt;/p&gt;

&lt;h3 id=&quot;0x01-修改代码10手动写入用户数据&quot;&gt;0x01. 修改代码1.0——手动写入用户数据&lt;/h3&gt;

&lt;p&gt;修改的过程只能说是具体情况具体分析，简单的修改可以是替换res文件夹下的语言文件（也就是我们听过的汉化），也可以修改layout文件夹下的界面布局文件，改改样式改改内容，这些都不涉及代码层面的修改，所以难度不大。&lt;/p&gt;

&lt;p&gt;但是魅族手环这个情况涉及到了需要去除登录的问题，只是简单的跳转到首页没有作用，因为软件几乎每个操作都要读取修改数据库中的用户数据，思考再三我准备在第一次进入软件时模仿注册成功的行为向数据库中写入新用户的数据信息，这样就可以欺骗程序无需跳转到登录页面。&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;下列对代码的分析都是基于魅族手环app进行的，如果换一个app的话不能直接生搬硬套！&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;经过翻阅代码，发现程序在打开时会运行“gotoNext方法”，读取数据库判断存不存在本地用户，如果不存在本地用户则跳转到&lt;strong&gt;登录页面（LoginActivity）&lt;/strong&gt;进行登录，然后重复运行类似的“gotoNext方法”，这时会监测到用户存在但是基本数据（年龄、身高……）为空，所以跳转到&lt;strong&gt;基本数据填写页面（RegistInfoActivity）&lt;/strong&gt;，填写好数据后再次运行“gotoNext方法”，这回一切条件都满足了，程序进入主页。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt; &lt;span class=&quot;c1&quot;&gt;// 原跳转函数示例&lt;/span&gt;
    &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gotoNext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DBUserApi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getLoginUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;startActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Intent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;LoginActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;booleanValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;startActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Intent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegistInfoActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;startActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Intent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;所以我们可以在 不存在本地用户时（user == null），写入本地用户信息，然后直接跳转到&lt;strong&gt;基本数据填写页面（RegistInfoActivity）&lt;/strong&gt;&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;// 修改后的代码 &lt;/span&gt;
   &lt;span class=&quot;kd&quot;&gt;private&lt;/span&gt; &lt;span class=&quot;kt&quot;&gt;void&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;gotoNext&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
        &lt;span class=&quot;nc&quot;&gt;User&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;DBUserApi&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getLoginUser&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
        &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;==&lt;/span&gt; &lt;span class=&quot;kc&quot;&gt;null&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 写入本地用户信息&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;LoginInfoServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;saveLoginInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;13000000000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;13000000000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
            &lt;span class=&quot;nc&quot;&gt;UserInfoServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;saveUserInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;13000000000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Logindata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
            &lt;span class=&quot;c1&quot;&gt;// 跳转到数据填写页面&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;startActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Intent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegistInfoActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;if&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;user&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;getIsEmpty&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;().&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;booleanValue&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;())&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;startActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Intent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;RegistInfoActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;else&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;{&lt;/span&gt;
            &lt;span class=&quot;n&quot;&gt;startActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;new&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Intent&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;MainActivity&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;class&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;));&lt;/span&gt;
        &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
    &lt;span class=&quot;o&quot;&gt;}&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;其中添加数据的这两行代码如下。在程序调用 &lt;strong&gt;DBUserApi.getLoginUser(this)&lt;/strong&gt; 获取登录的用户时，会先从SharePreferences中获取用户id，根据此id再在数据库中查询。所以我们要分别在SharePreferences中和数据库中写入用户信息。再次提示上述言论只针对魅族手环app，其他app的登录逻辑可不一定是这样，要大家根据代码认真分析。&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;// 向SharePreferences写入用户id和密码&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;LoginInfoServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;saveLoginInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;13000000000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;13000000000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;);&lt;/span&gt;
&lt;span class=&quot;c1&quot;&gt;// 向sql数据库中写入用户id和空白的用户详细数据(Logindata：年龄、身高...)&lt;/span&gt;
&lt;span class=&quot;nc&quot;&gt;UserInfoServer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;).&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;saveUserInfo&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;k&quot;&gt;this&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;13000000000&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Logindata&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;());&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里&lt;strong&gt;saveLoginInfo&lt;/strong&gt;传入了两个参数：用户id和密码，都是我随机设定的，原本在登录时，用户密码参数是传入了一个空字符串的，因为在app开发中，我们是不应该明文储存用户密码的。但是这里我并没有传入空值。主要是考虑到smali代码的缘故。&lt;/p&gt;

&lt;p&gt;下面就是前面两行java代码对应的smali代码：&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-smali&quot;&gt;    const-string v2, &quot;13000000000&quot;
    new-instance v1, Lcom/meizu/smart/wristband/models/database/servers/LoginInfoServer;
    invoke-direct {v1, p0}, Lcom/meizu/smart/wristband/models/database/servers/LoginInfoServer;-&amp;gt;&amp;lt;init&amp;gt;(Landroid/content/Context;)V
    invoke-virtual {v1, v2, v2}, Lcom/meizu/smart/wristband/models/database/servers/LoginInfoServer;-&amp;gt;saveLoginInfo(Ljava/lang/String;Ljava/lang/String;)V

    new-instance v1, Lcom/meizu/smart/wristband/models/database/servers/UserInfoServer;
    invoke-direct {v1, p0}, Lcom/meizu/smart/wristband/models/database/servers/UserInfoServer;-&amp;gt;&amp;lt;init&amp;gt;(Landroid/content/Context;)V
    new-instance v0, Lcom/meizu/smart/wristband/models/newwork/response/Logindata;
    invoke-direct {v0}, Lcom/meizu/smart/wristband/models/newwork/response/Logindata;-&amp;gt;&amp;lt;init&amp;gt;()V
    invoke-virtual {v1, p0, v2, v0}, Lcom/meizu/smart/wristband/models/database/servers/UserInfoServer;-&amp;gt;saveUserInfo(Landroid/content/Context;Ljava/lang/String;Lcom/meizu/smart/wristband/models/newwork/response/Logindata;)Z
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;这里你可能需要稍微了解一下smali才行，稍等你一段时间去百度看看……&lt;/p&gt;

&lt;p&gt;分析上面的代码，都是三段式的，从左到右依次是（我自己的话总结出来的，不是专业术语）：指令的类型、涉及到的寄存器或参数、类或方法&lt;/p&gt;

&lt;p&gt;最左侧的指令的类型，有：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;类型&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;const-string&lt;/td&gt;
      &lt;td&gt;指定一个字符串&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;new-instance&lt;/td&gt;
      &lt;td&gt;指定一个类的实例对象&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;invoke-*&lt;/td&gt;
      &lt;td&gt;调用方法&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;中间表示涉及到的变量，v* 表示寄存器（这里简单理解成变量），p*表示这条语句所处方法的参数，这里p0表示this。&lt;/p&gt;

&lt;p&gt;于是：&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt;smali代码&lt;/th&gt;
      &lt;th&gt;含义&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;const-string v2, “13000000000”&lt;/td&gt;
      &lt;td&gt;v2储存着一个字符串“13000000000”&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;new-instance v1, Lcom/……/LoginInfoServer;&lt;/td&gt;
      &lt;td&gt;v1储存着LoginInfoServer类的实例&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;invoke-direct {v1, p0}, Lcom/…/LoginInfoServer;-&amp;gt;&lt;init&gt;(Landroid/content/Context;)V&lt;/init&gt;&lt;/td&gt;
      &lt;td&gt;对v1初始化，传入类型为Context的参数（这里因为是在activity中调用的，所以activity本身的this就是Context，于是传入p0）&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;invoke-virtual {v1, v2, v2}, Lcom/…/LoginInfoServer;-&amp;gt;saveLoginInfo(Ljava/lang/String;Ljava/lang/String;)V&lt;/td&gt;
      &lt;td&gt;v1调用saveLoginInfo，并传入两个字符串作为参数（这里解释了为啥id密码都写成同样的内容，为了偷懒少写一行代码，当然也可以说是合理复用寄存器优化程序）&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;第二句java代码以此类推，我就不展开描述了。后面修改跳转的activity路径就更简单了，相信你可以自己完成～&lt;/p&gt;

&lt;h3 id=&quot;0x02-修改代码20假装网络访问成功&quot;&gt;0x02. 修改代码2.0——假装网络访问成功&lt;/h3&gt;

&lt;p&gt;到这里我们已经可以进入应用并成功同步时间&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://pic2.zhimg.com/v2-a8a67ee53ea405fd45c15f0523cf0741_b.png&quot; alt=&quot;img&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;于是找到代码中访问网络的部分（models/network/NetWorkApi1:executeCommon），在访问网络之前，直接返回了一个字符串。这里我还担心返回值会不会同样影响到程序的运行，比如后面有对字符串的json的转换之类的，所以传了一个“{}”避免出错。不过好在这个程序并没有对返回值做检查，只要有返回就不影响后面代码的运行。&lt;/p&gt;

&lt;p&gt;魅族手环app大量的运用了rxjava的链式调用，所以我们需要返回的其实是 Observable&lt;String&gt; 类型的内容。&lt;/String&gt;&lt;/p&gt;

&lt;div class=&quot;language-java highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;// 直接返回一个字符串&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;return&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Observable&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;na&quot;&gt;just&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;{}&quot;&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;对应的samli代码&lt;/p&gt;

&lt;pre&gt;&lt;code class=&quot;language-smali&quot;&gt;    const-string v0, &quot;{}&quot;
    invoke-static {v0}, Lrx/Observable;-&amp;gt;just(Ljava/lang/Object;)Lrx/Observable;
    move-result-object v0
    return-object v0
&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;但如果他对返回的内容也做了检查的话，估计就要换个思路改或者做更巧妙的修改了。&lt;/p&gt;

&lt;h3 id=&quot;0x03-解决发热&quot;&gt;0x03. 解决发热&lt;/h3&gt;

&lt;p&gt;然后又回到了老问题，只要运行这个程序手机就发热，看cpu占用达到了18%-20%，这种情况可以推测出是某个线程死循环导致了的单核满载进而发热。&lt;/p&gt;

&lt;p&gt;使用Android Studio自带的Android Profiler可以用来监测手机上某个被debug的应用的状态数据，用它来监测CPU并在手机开始发热时录制几秒。在录制的信息中看到了”com.mob.*“字样的内容，Mob是安卓中一个应用广泛的第三方库，经常被用来增加社交应用分享功能。&lt;/p&gt;

&lt;p&gt;推测可能是由于应用太久未更新，老版本的Mob库不适合新的安卓版本，或老版本的网络接口被取消导致的。考虑到分享功能没啥必要，所以直接去Application文件中找到初始化的代码删除就解决了。&lt;/p&gt;

&lt;p&gt;感慨一下，如果当初知道改这东西这么容易，没准就不会轻易投向小米了。&lt;/p&gt;

&lt;h3 id=&quot;0x04-收尾&quot;&gt;0x04. 收尾&lt;/h3&gt;

&lt;p&gt;最后就是修改修改layout文件，把用不到的功能直接从前端隐藏，注意这里不能删除，因为代码中往往会有前端组件的ID引用，如果直接删除掉会导致程序报错，所以我们隐藏即可（或者设置长或宽为0dp）。&lt;/p&gt;

&lt;h3 id=&quot;0x05-重新编译&quot;&gt;0x05. 重新编译&lt;/h3&gt;

&lt;p&gt;在编译前，我们要准备好签名文件，大家在android studio中直接生成一个新的即可，准备好签名文件路径、别名和密码。&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 环境变量&lt;/span&gt;
&lt;span class=&quot;nv&quot;&gt;keystroe&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;/Users/balabala/meizuband.jks
&lt;span class=&quot;nb&quot;&gt;alias&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;band
&lt;span class=&quot;nv&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;nicaicaiwodemimashishenmo
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;然后再次使用apktool进行编译，使用jarsigner来给应用签名，使用adb直接安装到手机上&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c&quot;&gt;# 编译、签名与安装&lt;/span&gt;
apktool b &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt;/dist/unsigned.apk
&lt;span class=&quot;nb&quot;&gt;echo&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$password&lt;/span&gt; | jarsigner &lt;span class=&quot;nt&quot;&gt;-verbose&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-keystore&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$keystroe&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-signedjar&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt;/dist/signed.apk &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt;/dist/unsigned.apk &lt;span class=&quot;nv&quot;&gt;$alias&lt;/span&gt;
adb &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$output&lt;/span&gt;/dist/signed.apk
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;&lt;img src=&quot;https://pic4.zhimg.com/v2-69a68f1912d8fa77c97ee5116641890b_b.png&quot; alt=&quot;img&quot; /&gt;&lt;/p&gt;

&lt;p&gt;写这种程序竖着用屏幕提升效率真是大大滴&lt;/p&gt;
</description>
            <pubDate>Sun, 12 Dec 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>NTFS Mounter Plugin for Xbar</title>
            <link>/025</link>
            <guid isPermaLink="true">/025</guid>
            <description>&lt;p&gt;&lt;strong&gt;This plugin is only tested with MacOS 12.0.1&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;https://xfangfang.github.io/assets/img/ntfs_mounter.png&quot; alt=&quot;NTFS Mounter&quot; /&gt;&lt;/p&gt;

&lt;h2 id=&quot;how-to-use&quot;&gt;How to use&lt;/h2&gt;

&lt;ol&gt;
  &lt;li&gt;download &lt;a href=&quot;https://xbarapp.com/&quot;&gt;Xbar&lt;/a&gt;&lt;/li&gt;
  &lt;li&gt;Find NTFS Mounter under System category and click install&lt;/li&gt;
  &lt;li&gt;Plugin the USB disk to your Mac&lt;/li&gt;
  &lt;li&gt;Click the &lt;strong&gt;Refresh MenuItem&lt;/strong&gt; to refresh the disk list&lt;/li&gt;
  &lt;li&gt;Click the disk displayed in black or white text to mount in read-write mode&lt;/li&gt;
  &lt;li&gt;The green disk indicates that it has been mounted in read-write mode. Clicking it will unmount the disk&lt;/li&gt;
  &lt;li&gt;Click &lt;strong&gt;Open Folder MenuItem&lt;/strong&gt; to open disk folder&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The text after the disk name indicates:  free capacity / whole capacity&lt;/p&gt;

&lt;h2 id=&quot;using-this-script-from-terminal&quot;&gt;Using this script from terminal&lt;/h2&gt;

&lt;div class=&quot;language-bash highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;bash ntfs.1d.sh
bash ntfs.1d.sh mount disk2s1 Kingston
bash ntfs.1d.sh mount disk2s1 Kingston system_password
bash ntfs.1d.sh umount disk2s1
bash ntfs.1d.sh open &lt;span class=&quot;c&quot;&gt;# open disk folder&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;
</description>
            <pubDate>Tue, 23 Nov 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>Macast Linux 打包说明</title>
            <link>/024</link>
            <guid isPermaLink="true">/024</guid>
            <description>&lt;h2 id=&quot;构建&quot;&gt;构建&lt;/h2&gt;

&lt;h3 id=&quot;1-安装依赖&quot;&gt;1 安装依赖&lt;/h3&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;nb&quot;&gt;sudo &lt;/span&gt;apt &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-y&lt;/span&gt; gettext &lt;span class=&quot;c&quot;&gt;# 用于构建多语言文件&lt;/span&gt;
pip &lt;span class=&quot;nb&quot;&gt;install&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-r&lt;/span&gt; requirements/common.txt
pip &lt;span class=&quot;nb&quot;&gt;install &lt;/span&gt;pyinstaller
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;2-构建多语言文件&quot;&gt;2 构建多语言文件&lt;/h3&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;file &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;i18n/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
	msgfmt &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;/LC_MESSAGES/macast.mo &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;/LC_MESSAGES/macast.po&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt;&lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;done&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;3-构建macast二进制文件&quot;&gt;3 构建macast二进制文件&lt;/h3&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;pyinstaller &lt;span class=&quot;nt&quot;&gt;--noconfirm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-F&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-w&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--additional-hooks-dir&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;.&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--add-data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;macast/.version:.&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--add-data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;macast/xml/*:macast/xml&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--add-data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;i18n/zh_CN/LC_MESSAGES/*.mo:i18n/zh_CN/LC_MESSAGES&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--add-data&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;&quot;macast/assets/*.png:assets&quot;&lt;/span&gt; &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--exclude-module&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;tkinter &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;nt&quot;&gt;--exclude-module&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;pyqt &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            Macast.py

&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h2 id=&quot;注意事项&quot;&gt;注意事项&lt;/h2&gt;

&lt;h3 id=&quot;减小二进制文件体积移除不需要的依赖&quot;&gt;减小二进制文件体积(移除不需要的依赖)&lt;/h3&gt;

&lt;p&gt;使用pyinstaller打包有时候会把没有用到的包也打包进二进制文件中，这会导致打包之后的文件较大。有两个解决办法：&lt;/p&gt;

&lt;p&gt;1 推荐创建新的python虚拟环境或者使用全新的系统环境安装macast依赖的pip包。&lt;/p&gt;

&lt;p&gt;2 把明确不需要的包排除在外，比如Macast没有使用tkinter或pyqt，那么可以在构建时指明&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;--exclude-module=tkinter --exclude-module=pyqt&lt;/code&gt;&lt;/p&gt;

&lt;h3 id=&quot;pyinstaller对非主流cpu架构的支持&quot;&gt;pyinstaller对非主流cpu架构的支持&lt;/h3&gt;

&lt;p&gt;注意到昨天pyinstall的develop分支合并了一个修复非主流cpu架构的bug：https://github.com/pyinstaller/pyinstaller/pull/6306&lt;/p&gt;

&lt;p&gt;希望这有助于解决某些问题（似乎需要手动安装pyinstaller develop的最新分支）&lt;/p&gt;

&lt;h2 id=&quot;附x86-docker构建镜像&quot;&gt;附：X86 docker构建镜像&lt;/h2&gt;

&lt;p&gt;提供了一个预装pyinstaller和部分依赖的docker镜像，可以用于跨系统快速构建Macast Linux二进制文件&lt;/p&gt;

&lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;git clone https://github.com/xfangfang/Macast.git &lt;span class=&quot;o&quot;&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;cd &lt;/span&gt;Macast
&lt;span class=&quot;k&quot;&gt;for &lt;/span&gt;file &lt;span class=&quot;k&quot;&gt;in &lt;/span&gt;i18n/&lt;span class=&quot;k&quot;&gt;*&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;do &lt;/span&gt;msgfmt &lt;span class=&quot;nt&quot;&gt;-o&lt;/span&gt; &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;/LC_MESSAGES/macast.mo &lt;span class=&quot;nv&quot;&gt;$file&lt;/span&gt;/LC_MESSAGES/macast.po&lt;span class=&quot;p&quot;&gt;;&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;done
&lt;/span&gt;docker run &lt;span class=&quot;nt&quot;&gt;--rm&lt;/span&gt; &lt;span class=&quot;nt&quot;&gt;-v&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;$(&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;pwd&lt;/span&gt;&lt;span class=&quot;si&quot;&gt;)&lt;/span&gt;&lt;span class=&quot;s2&quot;&gt;:/src/&quot;&lt;/span&gt; xfangfang/build-macast &lt;span class=&quot;se&quot;&gt;\&lt;/span&gt;
            &lt;span class=&quot;s1&quot;&gt;&apos;pip install -r requirements/linux.txt &amp;amp;&amp;amp; \
            pyinstaller --noconfirm -F -w \
            --additional-hooks-dir=. \
            --add-data=&quot;macast/.version:.&quot; \
            --add-data=&quot;macast/xml/*:macast/xml&quot; \
            --add-data=&quot;i18n/zh_CN/LC_MESSAGES/*.mo:i18n/zh_CN/LC_MESSAGES&quot; \
            --add-data=&quot;macast/assets/*.png:assets&quot; \
            --exclude-module=tkinter \
            --exclude-module=pyqt \
            Macast.py&apos;&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
            <pubDate>Tue, 26 Oct 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>M1版本Macbook机器学习究竟有多强</title>
            <link>/021</link>
            <guid isPermaLink="true">/021</guid>
            <description>&lt;p&gt;开篇提醒：本文系无机云评测。&lt;/p&gt;

&lt;h3 id=&quot;前情提要&quot;&gt;前情提要&lt;/h3&gt;

&lt;p&gt;今天在知乎上看到了 &lt;a href=&quot;https://zhuanlan.zhihu.com/p/350955566&quot;&gt;Apple Silicon M1 机器学习性能简单测试&lt;/a&gt;，突然沸腾了（沸腾体警告⚠️）。文章中用一段cnn训练mnist的代码在几台机器上分别运行，以每batch运行时间为评测标准，横向对比。正好我也把手头的几个电脑加入这个对比中。&lt;/p&gt;

&lt;h3 id=&quot;运行结果&quot;&gt;运行结果&lt;/h3&gt;

&lt;p&gt;把文章中的结果和我跑出来的结果以及github上看到的结果总结之后放在一起，我最感兴趣的数据就是我的mbp的GPU对比m1，即使他是一个不十分准确的对比也让我对m1有了个粗略的认识。&lt;/p&gt;

&lt;table&gt;
  &lt;thead&gt;
    &lt;tr&gt;
      &lt;th&gt; &lt;/th&gt;
      &lt;th&gt;CPU&lt;/th&gt;
      &lt;th&gt;GPU&lt;/th&gt;
    &lt;/tr&gt;
  &lt;/thead&gt;
  &lt;tbody&gt;
    &lt;tr&gt;
      &lt;td&gt;Macbook Air 2020 M1&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;24s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Macbook Pro 2020 M1&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
      &lt;td&gt;23s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;MacMini 2020 M1&lt;/td&gt;
      &lt;td&gt;16s&lt;/td&gt;
      &lt;td&gt;22s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;Macbook Pro 13 2015（i5 5257u ?）&lt;/td&gt;
      &lt;td&gt;310s ?&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;🧑‍💻 xfangfang’s MacBook Air 13 2015（i5 5250u）&lt;/td&gt;
      &lt;td&gt;85s&lt;/td&gt;
      &lt;td&gt; &lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;🧑‍💻 xfangfang’s Macbook Pro 15 2018（i7 8750h; RadeonPro555x）&lt;/td&gt;
      &lt;td&gt;45s&lt;/td&gt;
      &lt;td&gt;27s&lt;/td&gt;
    &lt;/tr&gt;
    &lt;tr&gt;
      &lt;td&gt;🧑‍💻 xfangfang’s PC（Ryzen5 3600; 2070super）&lt;/td&gt;
      &lt;td&gt;22s&lt;/td&gt;
      &lt;td&gt;2s&lt;/td&gt;
    &lt;/tr&gt;
  &lt;/tbody&gt;
&lt;/table&gt;

&lt;p&gt;说的有些迟到，但是好像ARM的时代真的来了。手上这台18款mbp的CPU发热巨大，日常使用60度，动不动就跑到100度，CPU跑这个卷积更是风扇满转，不过切到GPU去运行倒是完全不热。日常的高温让我爱不起来，看了数据甚至有点想入手下一代ARM处理器的Macbook。&lt;/p&gt;

&lt;p&gt;数据粗粗浏览下载，有的机型前后差距很大，应该是tensorflow正在对M1不断优化，ARM真的是未来可期了啊。&lt;/p&gt;

&lt;h3 id=&quot;附评测脚本&quot;&gt;附：评测脚本&lt;/h3&gt;

&lt;p&gt;github评测结果: https://github.com/apple/tensorflow_macos/issues/25&lt;/p&gt;

&lt;p&gt;为了可以用mbp18的GPU，稍微修改了一些，应该和github中的脚本跑起来差不多。&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# 运行在macbook上 使用plaidml keras开启AMD GPU支持
# pip install plaidml-keras
# terminal 运行 plaidml-setup 选择对应的显卡
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;plaidml.keras&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;plaidml&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;install_backend&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;KERAS_BACKEND&quot;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;plaidml.keras.backend&quot;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;keras&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# 通用脚本 CPU
&lt;/span&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;os&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;os&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;environ&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;CUDA_VISIBLE_DEVICES&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&apos;-1&apos;&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;tensorflow&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tensorflow&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;c1&quot;&gt;# 通用脚本 GPU
&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpus&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;experimental&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;list_physical_devices&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;GPU&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;assert&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;len&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;&amp;gt;&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;&quot;Not enough GPU hardware devices available&quot;&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;tf&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;config&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;experimental&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;set_memory_growth&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;gpus&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;bp&quot;&gt;True&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;上面三段代码片段，分别和下面的脚本组合，用来在不同设备上运行&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;time&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;numpy&lt;/span&gt; &lt;span class=&quot;k&quot;&gt;as&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;
&lt;span class=&quot;kn&quot;&gt;from&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;datetime&lt;/span&gt; &lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;mnist&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;datasets&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;mnist&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;train_images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;train_labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test_images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;test_labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;mnist&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;load_data&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;train_images&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expand_dims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;train_images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;255.0&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;test_images&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;np&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;expand_dims&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;test_images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;axis&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;/&lt;/span&gt; &lt;span class=&quot;mf&quot;&gt;255.0&lt;/span&gt;

&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;models&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Sequential&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;([&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conv2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;32&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;relu&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;input_shape&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;28&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;1&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Conv2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;64&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;kernel_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;3&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;relu&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;MaxPooling2D&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;pool_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;2&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Flatten&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;relu&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;layers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Dense&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;activation&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;softmax&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;])&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;compile&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;loss&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;sparse_categorical_crossentropy&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;optimizer&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;keras&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;optimizers&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;Adam&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;mf&quot;&gt;0.001&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;),&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;metrics&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;accuracy&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;start&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;model&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;fit&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;train_images&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;train_labels&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;epochs&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;10&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt;
    &lt;span class=&quot;n&quot;&gt;batch_size&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;128&lt;/span&gt;
&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;time&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;()&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;start&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;nb&quot;&gt;str&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;timedelta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;seconds&lt;/span&gt;&lt;span class=&quot;o&quot;&gt;=&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;delta&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;print&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&apos;Elapsed Time: {}&apos;&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;nb&quot;&gt;format&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;elapsed&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;))&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
            <pubDate>Fri, 14 May 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>Clash配置文件</title>
            <link>/020</link>
            <guid isPermaLink="true">/020</guid>
            <description>&lt;h3 id=&quot;主要配置文件&quot;&gt;主要配置文件&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7890&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;socks-port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7891&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;redir-port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;7892&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;allow-lan&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;bind-address&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;*&apos;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;rule&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;log-level&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;info&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;external-controller&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;127.0.0.1:9090&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;proxies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;first&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;trojan&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;first.com&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;first.com&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skip-cert-verify&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;second&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;trojan&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;server&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;second.com&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;port&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;443&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;password&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;second.com&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;skip-cert-verify&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;secret&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;&quot;&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;proxy-groups&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;lb&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;load-balance&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;proxies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;first&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;second&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://www.gstatic.com/generate_204&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;na&quot;&gt;name&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s2&quot;&gt;&quot;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;auto&quot;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;type&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;url-test&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;proxies&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;first&lt;/span&gt;
      &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;second&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# tolerance: 150&lt;/span&gt;
    &lt;span class=&quot;c1&quot;&gt;# lazy: true&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;url&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s1&quot;&gt;&apos;&lt;/span&gt;&lt;span class=&quot;s&quot;&gt;http://www.gstatic.com/generate_204&apos;&lt;/span&gt;
    &lt;span class=&quot;na&quot;&gt;interval&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;m&quot;&gt;300&lt;/span&gt;
&lt;span class=&quot;na&quot;&gt;rules&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;MATCH,auto&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;h3 id=&quot;dns配置文件&quot;&gt;dns配置文件&lt;/h3&gt;

&lt;div class=&quot;language-yaml highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
5
6
7
8
9
10
11
12
13
14
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;na&quot;&gt;dns&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enable&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;true&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;ipv6&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;no&quot;&gt;false&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;listen&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;0.0.0.0:53&lt;/span&gt;
  &lt;span class=&quot;c1&quot;&gt;#enhanced-mode: fake-ip&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;enhanced-mode&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;redir-host&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;nameserver&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;114.114.114.114&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;223.5.5.5&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tls://dns.rubyfish.cn:853&lt;/span&gt;
  &lt;span class=&quot;na&quot;&gt;fallback&lt;/span&gt;&lt;span class=&quot;pi&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;114.114.114.114&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;tls://dns.rubyfish.cn:853&lt;/span&gt;
    &lt;span class=&quot;pi&quot;&gt;-&lt;/span&gt; &lt;span class=&quot;s&quot;&gt;8.8.8.8&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
            <pubDate>Wed, 12 May 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>xbar 插件之 we_sing（全民K歌）</title>
            <link>/019</link>
            <guid isPermaLink="true">/019</guid>
            <description>&lt;h3 id=&quot;简介&quot;&gt;简介&lt;/h3&gt;

&lt;p&gt;xbar是一款允许用户定制化macos状态栏的应用，用户可以使用xbar快速的构建一款简单的状态栏应用，他的github介绍是这样的。&lt;/p&gt;

&lt;blockquote&gt;
  &lt;p&gt;xbar (the BitBar reboot) lets you put the output from any script/program in your macOS menu bar.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;而这款全名K歌的插件支持在你的mac上无需登录即可循环播放你喜欢的声音。&lt;/p&gt;

&lt;p&gt;&lt;img src=&quot;/assets/img/posts/019/we_sing.png&quot; alt=&quot;screenshot&quot; /&gt;&lt;/p&gt;

&lt;h3 id=&quot;配置说明&quot;&gt;配置说明&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;下载全民K歌插件，放置在xbar的插件目录下（默认的插件目录是：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt; /Users/fang/Library/Application\ Support/xbar/plugins/&lt;/code&gt;）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;打开全民K歌，打开你要收听的歌手主页，并点击分享，并复制分享链接。&lt;/li&gt;
  &lt;li&gt;链接的格式大概是这样的：&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;http://kg.qq.com/node/personal?uid=1234567812345678&amp;amp;balabala&lt;/code&gt;，复制从&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;uid=&lt;/code&gt;到&lt;code class=&quot;language-plaintext highlighter-rouge&quot;&gt;&amp;amp;&lt;/code&gt;中的16个字符，这就是歌手独一无二的uid&lt;/li&gt;
  &lt;li&gt;打开xbar中we_sing插件的管理页面，将uid填入到id的配置项目中，点击左上角刷新&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;相关链接&quot;&gt;相关链接&lt;/h3&gt;

&lt;p&gt;xbar github链接：&lt;a href=&quot;https://github.com/matryer/xbar&quot;&gt;xbar&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;全民K歌插件链接：&lt;a href=&quot;https://github.com/xfangfang/studyPython/blob/master/we_sing.1d.py&quot;&gt;we_sing.1d.py&lt;/a&gt;&lt;/p&gt;
</description>
            <pubDate>Thu, 08 Apr 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>Python 犯错实录</title>
            <link>/018</link>
            <guid isPermaLink="true">/018</guid>
            <description>&lt;h3 id=&quot;表达式级联比较&quot;&gt;表达式“级联”比较&lt;/h3&gt;

&lt;p&gt;python的表达式比较与c不完全相同&lt;/p&gt;

&lt;p&gt;据个例子：&lt;/p&gt;

&lt;p&gt;-1 &amp;gt; -2 &amp;gt; 0&lt;/p&gt;

&lt;p&gt;python 答案为 False， c 答案为 1（True）&lt;/p&gt;

&lt;p&gt;经过测试，形如 a ? b ? c ? d ? … z 的表达式，（？为 比较符号 如 ==， &amp;lt;=， &amp;gt;= 等）&lt;/p&gt;

&lt;h5 id=&quot;python-比较的逻辑&quot;&gt;python 比较的逻辑：&lt;/h5&gt;

&lt;ol&gt;
  &lt;li&gt;判断 a ? b&lt;/li&gt;
  &lt;li&gt;如果是False 那此表达式的值为False，运算结束。&lt;/li&gt;
  &lt;li&gt;如果是True， 那么顺次判断下两个值（ 也就是 b ? c） ，跳转到2&lt;/li&gt;
  &lt;li&gt;以此类推， a ? b，b ? c，c ? d … 直到最后一个 如果此时还为True，那么这个表达式的值为True&lt;/li&gt;
&lt;/ol&gt;

&lt;h5 id=&quot;cc-比较的逻辑&quot;&gt;c/c++ 比较的逻辑&lt;/h5&gt;

&lt;ol&gt;
  &lt;li&gt;判断 a ? b&lt;/li&gt;
  &lt;li&gt;如果是False 那么 判断 0 ? c ，如果是 True 那末判断 1 ? c&lt;/li&gt;
  &lt;li&gt;以此类推 不断将刚刚比较的值还为 1 或 0 ，然后与余下的表达式比较直到结束&lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;函数的默认参数&quot;&gt;函数的默认参数&lt;/h3&gt;

&lt;p&gt;小心默认参数并不是深拷贝的问题。&lt;/p&gt;

&lt;p&gt;如：&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

&lt;p&gt;这里所有的Board实例都共享了同一个_board值，因为在默认参数中的赋值同样是引用赋值。&lt;/p&gt;

&lt;p&gt;如果想正常使用需要改为深拷贝：&lt;/p&gt;

&lt;div class=&quot;language-python highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
4
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;&lt;span class=&quot;kn&quot;&gt;import&lt;/span&gt; &lt;span class=&quot;nn&quot;&gt;copy&lt;/span&gt;
&lt;span class=&quot;k&quot;&gt;class&lt;/span&gt; &lt;span class=&quot;nc&quot;&gt;Board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;:&lt;/span&gt;
    &lt;span class=&quot;k&quot;&gt;def&lt;/span&gt; &lt;span class=&quot;nf&quot;&gt;__init__&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;],&lt;/span&gt; &lt;span class=&quot;p&quot;&gt;[&lt;/span&gt;&lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;,&lt;/span&gt; &lt;span class=&quot;mi&quot;&gt;0&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;]]):&lt;/span&gt;
        &lt;span class=&quot;bp&quot;&gt;self&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;_board&lt;/span&gt; &lt;span class=&quot;o&quot;&gt;=&lt;/span&gt; &lt;span class=&quot;n&quot;&gt;copy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;.&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;deepcopy&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;(&lt;/span&gt;&lt;span class=&quot;n&quot;&gt;board&lt;/span&gt;&lt;span class=&quot;p&quot;&gt;)&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;

</description>
            <pubDate>Wed, 17 Mar 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>Linux 常用指令</title>
            <link>/017</link>
            <guid isPermaLink="true">/017</guid>
            <description>&lt;h3 id=&quot;conda&quot;&gt;conda&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;If you’d prefer that conda’s base environment not be activated on startup, set the auto_activate_base parameter to false:
    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;conda config &lt;span class=&quot;nt&quot;&gt;--set&lt;/span&gt; auto_activate_base &lt;span class=&quot;nb&quot;&gt;false&lt;/span&gt;
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;jupyter&quot;&gt;jupyter&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;This will show you the URLs of running servers with their tokens, which you can copy and paste into your browser.&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;jupyter notebook list
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;jupyter server password&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;docker + tensorflow-gpu + jupyterlab&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;docker run -d –gpus all -p 8888:8888 -p 6006-6009:6006-6009 –restart=always –name=tensorflow -v /home/fang:/tf/fang tensorflow/tensorflow:latest-gpu-jupyter&lt;/li&gt;
      &lt;li&gt;docker exec -it tensorflow pip config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple&lt;/li&gt;
      &lt;li&gt;docker exec -it tensorflow pip install jupyterlab&lt;/li&gt;
      &lt;li&gt;docker exec -it tensorflow jupyter server password&lt;/li&gt;
      &lt;li&gt;cd /var/lib/docker/containers/&lt;strong&gt;your container id&lt;/strong&gt;&lt;/li&gt;
      &lt;li&gt;docker stop tensorflow &amp;amp;&amp;amp; sed -i ‘s/jupyter notebook/jupyter lab/g’  config.v2.json&lt;/li&gt;
      &lt;li&gt;sudo systemctl restart docker&lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;notebook config&lt;/p&gt;

    &lt;ol&gt;
      &lt;li&gt;
        &lt;p&gt;c.ServerApp.token = ‘xfangfang’&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;c.ServerApp.quit_button = False&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;c.ServerApp.terminado_settings = {&apos;shell_command&apos;: [&apos;/bin/bash&apos;]}
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;        &lt;/div&gt;
      &lt;/li&gt;
    &lt;/ol&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;docker&quot;&gt;docker&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;docker run –rm –gpus all nvidia/cuda:11.0-base nvidia-smi&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;docker run -d --gpus all -p 8888:8888 --restart=always --name=tensorflow -v /home/fang:/tf/fang tensorflow/tensorflow:latest-gpu-jupyter
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;docker exec -it tensorflow python - c “import requests; requests.get(‘http://baidu.com’)”&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;docker commit tensorflow xfangfang/tensorflow-gpu-jupyter:v0.1
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;shell&quot;&gt;shell&lt;/h3&gt;

&lt;ol&gt;
  &lt;li&gt;
    &lt;p&gt;Shows CPU freq&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
3
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;watch &lt;span class=&quot;nt&quot;&gt;-n&lt;/span&gt; 0 &lt;span class=&quot;s2&quot;&gt;&quot;cat /proc/cpuinfo | grep -i mhz&quot;&lt;/span&gt;
&lt;span class=&quot;c&quot;&gt;# or&lt;/span&gt;
cpupower &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; all frequency-info
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;设置所有CPU为性能模式：&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;cpupower &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; all frequency-set &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; performance
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;设置所有CPU为节能模式：&lt;/p&gt;

    &lt;div class=&quot;language-shell highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;cpupower &lt;span class=&quot;nt&quot;&gt;-c&lt;/span&gt; all frequency-set &lt;span class=&quot;nt&quot;&gt;-g&lt;/span&gt; powersave
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;nvme smart-log /dev/nvme0&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_governor&lt;/p&gt;
  &lt;/li&gt;
&lt;/ol&gt;

&lt;h3 id=&quot;node&quot;&gt;node&lt;/h3&gt;

&lt;p&gt;apt install npm&lt;/p&gt;

&lt;p&gt;npm config set registry http://registry.npm.taobao.org/&lt;/p&gt;

&lt;p&gt;npm install n -g&lt;/p&gt;

&lt;p&gt;n lts&lt;/p&gt;

</description>
            <pubDate>Wed, 17 Mar 2021 00:00:00 +0000</pubDate>
        </item>
        
        <item>
            <title>我的ubuntu20.04软件配置</title>
            <link>/016</link>
            <guid isPermaLink="true">/016</guid>
            <description>&lt;p&gt;精选ubuntu应用，下次换电脑时候快速回归手感！&lt;/p&gt;

&lt;h3 id=&quot;可以直接安装的软件&quot;&gt;可以直接安装的软件：&lt;/h3&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;官网下载&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;chrome&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;网易云音乐
    &lt;ul&gt;
      &lt;li&gt;百度网盘&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;smartgit&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;应用商店下载&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;
        &lt;p&gt;vscode&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;android studio&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;filezilla （ftp client）&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;typora （markdown编辑器）&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;postman&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;SimulIDE (just for fun)&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;tint2&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;GitKraken （免费计划不支持私有或自建仓库）&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;Git Cola (也可以 apt-get install git-cola 对我来说非常难用)&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;cheat (命令行提示)&lt;/p&gt;
      &lt;/li&gt;
      &lt;li&gt;
        &lt;p&gt;gnome-tweak-tool https://extensions.gnome.org/# （可以简单改键位 切换cmd和ctrl）&lt;/p&gt;
      &lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;命令行安装&lt;/p&gt;

    &lt;ul&gt;
      &lt;li&gt;vim&lt;/li&gt;
      &lt;li&gt;mpv&lt;/li&gt;
      &lt;li&gt;&lt;a href=&quot;https://obsproject.com/wiki/install-instructions#linux&quot;&gt;obs&lt;/a&gt;&lt;/li&gt;
    &lt;/ul&gt;
  &lt;/li&gt;
&lt;/ul&gt;

&lt;h3 id=&quot;deepin-wine-软件&quot;&gt;deepin-wine 软件&lt;/h3&gt;

&lt;p&gt;https://gitee.com/wszqkzqk/deepin-wine-for-ubuntu&lt;/p&gt;

&lt;p&gt;迅雷极速版（wine版本的迅雷可以加速，deepin原生迅雷无加速功能）&lt;/p&gt;

&lt;h3 id=&quot;gnome-shell-插件&quot;&gt;gnome shell 插件&lt;/h3&gt;

&lt;p&gt;（首先需要在chrome安装插件gnome shell integration）&lt;/p&gt;

&lt;ul&gt;
  &lt;li&gt;
    &lt;p&gt;argos 自定义状态栏&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;freon 温度显示 （可以用argos实现）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Handy Scripts2 自定义脚本（可以用argos实现）&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;GSConnect 连接手机&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;Topicons Plus 优化wine图标显示&lt;/p&gt;
  &lt;/li&gt;
  &lt;li&gt;
    &lt;p&gt;system-monitor:（使用下面脚本安装，网站上的版本对ubuntu20.04支持有点问题）&lt;/p&gt;

    &lt;div class=&quot;language-plaintext highlighter-rouge&quot;&gt;&lt;div class=&quot;highlight&quot;&gt;&lt;pre class=&quot;highlight&quot;&gt;&lt;code&gt;&lt;table class=&quot;rouge-table&quot;&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td class=&quot;rouge-gutter gl&quot;&gt;&lt;pre class=&quot;lineno&quot;&gt;1
2
&lt;/pre&gt;&lt;/td&gt;&lt;td class=&quot;rouge-code&quot;&gt;&lt;pre&gt;  sudo apt-get -y install gnome-shell-extension-system-monitor
  killall -SIGQUIT gnome-shell  # restarts gnome
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;    &lt;/div&gt;
  &lt;/li&gt;
&lt;/ul&gt;

</description>
            <pubDate>Wed, 10 Mar 2021 00:00:00 +0000</pubDate>
        </item>
        
    </channel>
</rss>