月度归档: 2023 年 10 月

给Kaggle上个终端

借助 code-server 和 ngrok 给 Kaggle 上个在线版的终端,这样修改文件就很方便。

Notebook:

!curl -fsSL https://code-server.dev/install.sh | sh
!pip install pyngrok
!ngrok config add-authtoken ?authtoken_here?
# run the cell then stop it if you want to generate the password
# there is no password for default
# !code-server 
!sed -i.bak 's/auth: password/auth: none/' ~/.config/code-server/config.yaml
# !cat /root/.config/code-server/config.yaml | grep password

import subprocess
def iframe_thread():
    p = subprocess.Popen(["code-server"], stdout=subprocess.PIPE)

from threading import Thread
Thread(target=iframe_thread, daemon=True).start()

!ngrok http --domain=?your_ngrok_domain? 8080

Kaggle 好像不允许直接 ssh 连接,在线终端应该没事?

缺少 so 文件直接删除 conda 里的即可,系统一般是正常的,conda 比较残废罢了。

可能用蚁剑管理也可以,不过暂时 cs 够用了,先凑合一下。

(更新)一个简单的多线程消费者示例

更新:修改了一些容易卡锁的细节

一条简单的笔记。

需求:我有一批获取的 IP,要一一验证可用性。

一个个验证太慢了,需要使用多线程。
ChatGPT 给出的方法是,先用//分割文本,然后每个线程处理相等数量的 IP。但是,这个方法预分配了所有条目,总有线程特别慢,迟迟难以收尾。

之前写了一个简单的多消费者模型。先把文本全部读进列表,增加一个行计数器。每个线程一次只分配一条数据,在取任何数据前,先将计数器+1,代表对应行已被分配。处理完了再分配下一条数据。
不过,代码很奇怪,我决定用 queue 再重写一下。

以下是一个修改后的通用实现,附带一个简单的进度条。

from os import _exit
from time import sleep
from queue import Queue
from threading import Thread
from signal import signal, SIGINT
from dataclasses import dataclass
from time import time, strftime, gmtime

def consume(queue):
    while not queue.empty():
        item = queue.get()

        ''' do something here '''
        sleep(0.1)
        ''' do something here '''

        queue.task_done()

@dataclass
class QProgress:
    ''' a wget-like progress bar '''
    queue: Queue
    qsize: int = 0 # queue.qsize() by default
    width: int = 50 # characters of progress bar

    alive = True

    def interrupt(self):
        self.alive = False

    def show(self):
        data_size = max(self.queue.qsize(), self.qsize)
        start_time, showing_count= time(), 0
        while self.alive and showing_count < data_size:
            showing_count = data_size-self.queue.qsize()
            per = showing_count/data_size
            print(''.join([f"\r{('%.2f'%(per*100)).rjust(6,' ')}%[",
                f"{'='*int(per*self.width-1)}>".ljust(self.width,' '),
                f"] {showing_count}/{data_size} ",
                strftime('%H:%M:%S', gmtime(time()-start_time))]),
                end='', flush=True)

        if self.alive:
            print('', 'Done', sep='\n')

if __name__ == "__main__":

    queue = Queue()

    # 假定一些已生产的数据
    data_size = 6661
    [queue.put(i) for i in range(data_size)]

    # 进度条,启动!
    qprogress= QProgress(queue)
    Thread(target=qprogress.show, daemon=True).start()

    # 消费者们,启动!
    num_consumers = 661

    threads = [Thread(target=consume, args=(queue,), daemon=True)
        for _ in range(num_consumers)]
    [t.start() for t in threads]

    # 配合 daemon 响应 Ctrl+C
    def signal_handler(signal, frame):
        qprogress.interrupt()
        print('', 'Interrupted', sep='\n')
        _exit(0)

    signal(SIGINT,signal_handler)

    while any(t.is_alive() for t in threads):
        sleep(0)

一种基于终端输出的数据传输方式

哦,就是 base64 嘛。


最近 scaleway 服务很不稳定,终有一天它失联了。
根据以前的经验,一般是机器重启,需要去网页 TTY 重置下网络。

登录后,scaleway 问我,新的网络上线,更快更好,要不要一键 auto migrant?
一手贱,然后就双向失联了。具体表现为什么都 ping 不通,没有任何公网地址,连 apt update 也无法运行,通过 scw 分配 IP 也救不活。
此时 sacleway 后台弹出马后炮提示:您的套餐迁移时如果分配了静态 IP,需要先升级 blablabla,否则就会丢失网络连接。怎么救,没说。

这台服务器主要运行了这个博客,尽管文章在本地都有备份,但还是想着先抢救一下数据库。
我使用的是 sqlite 数据库,就一个/wp-content/database/.ht.sqlite文件。

抢救一下

网络彻底失联的情况下,我和这台机器唯一的交互手段就只有后台提供的网页版的 TTY。于是,只能靠屏幕输出了。

具体而言的话:

f=".ht.sqlite";
zip -9 -q f "$f";
base64 -w0 f.zip;echo "";

将输出保存到文件,从另一台机器

base64 -d save.txt > f.zip

进一步压缩

之前是针对空白系统的抢救办法。由于屏幕的输出空间很有限,结合一些工具的话,可以进一步缩减体积。

apt-get install sqlite3 p7zip-full python3-pip
pip install base2048
f=".ht.sqlite";
sqlite3 $f 'VACUUM;';
:<<eof
- or dump to sql -
sqlite3 $f .dump > dump.sql; f="dump.sql"; 
eof
7z a -bso0 -mx -myx -mmt=off -ms=on -mtm=off -mtc=off -mta=off -m0=LZMA:d=384m:fb=273:lc=4 -mmc=1000000000 f $f;
python3 -c "print(__import__('base2048').encode(open('f.7z','rb').read()))";

等待 TTY 狂暴输出。

这块截图大约是 10KB 的数据

其实还有更壮观的 base65536,输出的都是形似汉字的字符,但复制时可能会造成数据错乱。另一方面,base2048 屏幕输出占一个 ASCII 码的宽度,能容纳 2 个 ASCII 码的数据,而 base65536 占 2 个 ASCII 码的宽度,能容纳 3 个 ASCII 码的数据,压缩比提升不大且异常卡顿,实测还是 base2048 好。

恢复数据:将 TTY 打印的内容放入 2048.txt,然后恢复到 2048.7z。
其中明文数据大约是解码数据的 1.8 倍。

import base2048

with open('2048.txt', 'r',encoding='utf-8') as file:
    encoded_data = file.read()

decoded_data = base2048.decode(encoded_data)

with open('2048.7z', 'wb') as output_file:
    output_file.write(decoded_data)

更多可能

终端还可以展示带颜色的文字,用以进一步扩充符号数。如果能够显示图片,则可以结合一些隐写方法,将数据压缩到 RGB 值内。又或者结合屏幕刷新 + 录像,以类似二维码的原理定位和容错,并提供自动化的可能。

这样即使不需要数据线和网络,也能把电脑文件复制到手机上(当然首先要能在宿主上执行程序,听上去像是 007 会干的事)。
这些想法没什么太多应用场景,就只是想想。

Stable Diffusion inpaint扩图半成功

PS 的 AI 试用到期,于是尝试使用 SD 平替。

使用 inpaint only+lama 进行扩图。预览时还好好的,最后一步失败。
填充内容全部变成横条,原因未知。

如果先把原图本地扩大,再通过 SD 涂抹空白区域进行填充,倒是可以正常运行。
以下是 SD 生成的效果图。

搜了一些教程,发现教程里的 UI 在局部填充的地方和我的略有区别,但不清楚具体影响。

这是一只猫

最近收到个推送《为什么英短猫弃养率这么高》,文章说 70% 的人后悔养英短,接着细数了英短多条罪状。

我也有一只蓝白,是朋友送的。

精神的时候她长这样。

没睡醒的时候她长这样。

有人说英短心眼特小,报复心特强,我家的猫好像从来不记仇。

还有人说英短发情期会喵喵叫。我想所有猫都会这样。
不过我家的猫即使是发情期也不会随地大小便。
后来给她做了绝育,除了求猫条,平时都是哑巴了。

至于英短贪吃过于肥胖的问题,我是更加没遇到。我的猫明明一直好好吃饭,但就是长不胖,让我很苦恼。

文章最后说英短掉毛厉害。我想短毛猫都掉毛厉害,美短也是一样的。
比起长毛猫打理费劲,无毛猫洗澡麻烦,短毛猫已经很不错了。

我每天和猫贴贴的时候就会给她梳梳毛,也不是很麻烦。

这只猫不算很好看,但性格真的棒。
她很少咬人或抓人,也从来不会把东西弄在地上,还可以随便揉肚子。
猫的性格应该是随爸遗传的,这些都没有专门训练过。

2022 年封控前的一段时间,我压力非常大,是这只猫陪我度过了那段抑郁的日子。

她是一只小天使。她还有一个很霸气的名字,但现在已经退化成咪咪了(笑

我的看法:7z为什么不如RAR流行?

2014 年,有人在知乎提了个问题:为什么直到现在 RAR 仍然比 7z 更流行? 这个问题陆陆续续一直有人回答,直到今天,RAR 在商业上仍然比 7z 更加流行。

这个问题有很多角度,每个时期的答案也非常不同。这个时代版本的答案是,用户只需要一个打包工具,根本不关心压缩率,7z 最核心的竞争力荡然无存。

我有一个更简洁的回答,即 7z 是面向后端开发的,而不是消费者。
它虽然诞生自 Windows 平台,但它使用的是 Linux 的那套方法论,这就是它难以普及的根本原因。

Linux 的设计哲学是,所有工具都专心做自己的事,然而专一的同时,对商业化非常不友好。
7z 的功能是丑陋的,它拥有 Linux 软件的通病,即:开箱即用的默认配置不合理,无法第一时间满足用户关注的需求。明明它可以做到,但它会把一大堆参数丢给用户。比如,不看说明,有多少人知道 2021 年前 7z 要加入 cu 参数才能保证 zip 不乱码?
如果一定要类比的话,7z 可能是压缩解压界的 ffmpeg,相信没人会问出 为什么 ffplay 不如快播流行 这种鬼话。

相比 tar.gz,7z 还是做到了打包压缩一体化,但这仅仅是做到了不反人类,还远远不够。RAR 支持一步解压 tar.gz、支持分卷、支持注释、支持恢复记录、支持添加到 Email、支持记住密码、扫描病毒等等。很多功能功能是过时的、臃肿的,但是可以看出来,真正优秀的商业软件是如何在每个时代,去迎合用户的需求。反观 7z,只是能用,但不好用。

话又说回来,7z.exe 是不好用 ,但 7z.dll 又不同。一些软件经过 7z.dll 套壳后问世,功能界面几乎完全复刻 WinRAR,然后就好用了。这进一步说明,7z 是面向开发者和后端设计的。
很多软件爱好者会说,这些套壳软件是带广告的流氓软件,有些甚至是强行安装的。但市场已经证明,用户不在乎广告。RAR 弹一个试用过期,和这些软件弹一个广告,对普通用户来说有什么区别呢?用户只是想要一个解压软件,一个不需要说明书就能用好的软件,一个不太反人类、至少压缩 zip 文件名不会乱码的软件。如果一个用户的知识水平达到了认识 7z,他大概率会选择用 RAR,或者某些不带广告的 7z 套壳。

可悲的是,如今,7z 连后端的流行度也难保。尽管企业仍有压缩的需求,但已经不如早期那么极端。而且企业大部分的压缩需求源于流量昂贵,数据更多是为了传输后立刻解包的,而不是为了冷备份。能够顺序读并解压的 zip、效率更高的 zstd,又或者是 webp/webm 这种为浏览器而生的格式,才是贴近时代的解决方案。

我可能还是会用 7z 的命令行做一些极限压缩,或许只是因为好玩,但也仅限于小体积的打包。7z 只能用于个人分享,反正解压它不是难事,但是商业交付,7z 绝对不是什么好选项。不想和用户啰嗦的话,zip 是唯一正解。
最后,做商业产品,一定不要学 7z。

给网站加上“原生”的文件直链(踩坑)

最近存在一些文件分享需求,我希望它符合以下要求:

  1. 链接长期有效,即使文件失效再补也能保持原链接
  2. 不要出现过度限速问题
  3. 不要存在我的服务器上,我的磁盘很有限
  4. 不要暴露任何与文件本身无关的信息

一些尝试

首先我尝试把文件存在 Github 的 release 上,因为 release 不限制带宽,也是官方推荐的分发方式。不过,release 的文件链接会包括上传者和 repo 名,我不希望它出现。

Github release 的链接和 raw 不同,会 302 到 objects 子域,无法直接 rewrite。我试图用 CF workers 代理这部分流量,发现在 workers.dev 下可以工作,但绑定自定义域名后就会出现 52x 错误。后来我在 CF workers 社区中找到了相关讨论,这是个历史遗留问题,答案就是 CF 的限制(或 Bug?)
如果再试图用 worker A 通过域名访问 worker B 来绕过,会显示两者无法连通。
回头看目前基于 CF 的 Github 代理,就能发现大家都是通过加一层外部 proxy 绕过这个限制。

也就是说想要自定义域名代理 Github release 文件的话,无论如何都必须引入 CF 外的服务。

换个方案

在服务器上 fetch() 并传递给 CF 自然是可行的,不过事已至此,我决定换 AList 挂载网盘。

最终我选了 mega 网盘。挂载这个网盘只需要用户名和密码,不涉及 token、私有 API、Cookie,不需考虑过期更新的问题,也没有限速问题,非常符合开头提到的要求。

具体而言,我将 down 子域 指向我的 AList 服务器并反代,然后将直链中的 /[d|p]/files/<挂载目录> 通过 Transform Rules 去掉。

根据 CF 的文档,缓存单文件最大 100M,Page Rule 中设置最长可缓存 14 天,除了耗费一点回源流量,基本没什么消耗。

最终效果:https://down.liedown.win/Tinify-mod.zip