阅读视图

发现新文章,点击刷新页面。
🔲 ☆

电信 IPTV 内网融合教程:在局域网任意设备观看 IPTV

背景

传统的 IPTV 需要使用运营商提供的 IPTV Box 连接电视才能观看,这种方案有几个缺点:

  • 客厅如果只有一个网口就得在上网和 IPTV 之间二选一
  • 每次看 IPTV 必须切换视频信号到 IPTV Box,稍微麻烦一些
  • 必须忍受 IPTV Box 的 UI/UX
  • 客厅电视没有空闲的 HDMI 端口可用(已经连接了多个设备,例如游戏机、其他电视盒子)

如果你想解决这些问题。网络上通常有很多术语,令人眼花缭乱,例如 IPTV 组播转单播、IPTV 单线复用、光猫超级管理员密码之类的概念。

本文经过实际测试摸索,找到一条相对简单高效的方案,大概只需要10分钟时间即可配置完毕。

为什么要开 IPTV

经常听到一种说法:“现在都没人看电视了”,在抖音之类的短视频大行其道,人手一个智能手机的现状下,确实,现在包括很多老年人在内一年都开不了几次电视。

那为什么要折腾开通 IPTV 呢?从我的角度来说主要有以下几点:

  1. 画质:IPTV 码率相对较高,1080p(H264) 一般在 8 Mbps 左右,4K(HEVC 50fps HDR) 一般在 20-30Mbps 左右,显著高于国内大部分流媒体平台
  2. 延迟:IPTV 走电信专用内网,直播延迟相对较低,尤其是组播协议
  3. 直播:各种体育赛事、大型年度直播之类的直接看 CCTV 或者对应频道是最省事的

对我来说主要观看的是 CCTV 5、CCTV 16 这类体育频道,虽然有广告,但是相对来说明显画质和延迟要比国内流媒体平台好不少,并且不用每家都开会员。

相比腾讯优酷爱奇艺之类的体育会员动辄几十块一个月,IPTV 一个月 10 块钱,对于轻度用户来说性价比可以说很好了。

前置条件

  • 已开通 IPTV 服务,如果你已经在用融合宽带套餐,每个月加 10 块钱就可以
  • 光猫和路由器放在一起,可以从光猫接两根网线到路由器
  • 光猫有单独标识的 ITV 端口
  • 局域网内有可以安装 docker 镜像的设备(例如 NAS)或者你有其他办法可以在局域网内部署 https://github.com/stackia/rtp2httpd
  • OpenWrt 软路由或者其他支持自定义 dhcp client hostname 和 option 的路由器

前置测试

我的光猫是在安装宽带时就让电信工作人员设置了桥接模式加路由器拨号,除此以外没有改过网络接口相关设置。也就说你不需要超级管理员账密。

将光猫 ITV 口接一根网线连到电脑,在电脑网络设置里手动设置此连接的 IP(指定为 192.168.1.1xx)具体 IP 段一般和光猫背面展示的控制台地址一致。本文后面以此 IP 段示例。
网关指定为 192.168.1.1

然后使用 VLC 播放器播放组播地址,可以从以下项目找具体地址:
https://github.com/FHZDCJ/Zhejiang_Telecom_IPTV/blob/main/Zhejiang_Multicast/Zhejiang_Telecom_IPTV.m3u

格式:rtp://233.xxx.xxx.xxx:5140,如果可以正常播放组播源,则继续后面的步骤。

在执行此测试的时候建议关闭 Wi-Fi 和 surge 之类的工具。确保所有请求直接通过网线发给光猫的 ITV 口。

组播转单播

组播转单播架构图

如果前面的测试通过,说明你当前的网络条件可以正常播放组播源信号。

接下来为了实现在局域网内任意设备观看 IPTV,需要将组播信号(通常是 rtp 协议)转为单播信号(http 协议)。

首先需要在局域网内正常部署 https://github.com/stackia/rtp2httpd 服务。
我是直接在 NAS 上通过 Docker Compose 部署的,配置如下:

services:
  rtp2httpd:
    image: ghcr.io/stackia/rtp2httpd:latest
    container_name: rtp2httpd
    network_mode: host
    command: --noconfig --verbose 2 --listen 5140 --maxclients 20

待你可以正常访问 http://NAS-IP:5140/status 之后再进行下一步。

将光猫 ITV 口连接到路由器的第二个 WAN 口(第一个 WAN 口用于正常上网),这一步的主要目的是实现在正常上网的这个 LAN(局域网) 里面通过 HTTP 单播源观看 IPTV。

我用的是 Unifi 路由器,直接在控制台新建一个 WAN 口,关联刚才连接 ITV 口的端口,同时勾选 IGMP proxy 开关(这个很关键),选择静态 ip。

IGMP Proxy 和 ITV WAN 口配置

此时在 VLC 里打开 rtp2httpd 格式的单播信号源地址:

http://192.168.10.20:5140/rtp/239.xxx.xxx.xxx:5140

这里 192.168.10.20 是我的 NAS 地址,里面用 docker 部署了rtp2httpd(host 网络模式,默认绑定在 5140 端口),239.xxx.xxx.xxx 是具体 IPTV 频道的组播地址。

正常情况下这里就可以正常观看了。

最后,为了方便切换所有可观看的频道,可以将这里的 m3u 文件进行修改:
https://github.com/FHZDCJ/Zhejiang_Telecom_IPTV/blob/main/Zhejiang_Multicast/Zhejiang_Telecom_IPTV_ONLINE_LOGO.m3u

替换里面的链接为 rtp2httpd 格式,以下是替换后的示例:

#EXTM3U
#EXTINF:-1 tvg-id="CCTV1" tvg-name="CCTV1" group-title="央视",CCTV1综合
http://192.168.10.20:5140/rtp/233.xxx.xxx.xxx:5140
#EXTINF:-1 tvg-id="CCTV2" tvg-name="CCTV2" group-title="央视",CCTV2财经
http://192.168.10.20:5140/rtp/233.xxx.xxx.xxx:5140

将修改后的 m3u 文件导入到 APTV 之类的软件即可在 Apple TV、Mac、iPhone 等局域网设备观看 IPTV 频道。

原始单播信号

对于大部分用户来说,如果组播信号用起来感觉没啥问题,就不推荐折腾单播信号了,毕竟首次设置和后续维护成本还是显著增加的。并且单播的延迟和性能在高峰期可能不如组播稳定,这是它们的技术原理决定的。不存在单播就一定优于组播。

组播最大的问题是如果配置不当,可能对局域网性能造成影响,例如在观看 IPTV 的时候会给局域网内所有设备端口进行 “泛洪” 发送无意义流量包。使用运营商提供的原始单播信号可以避免这个问题。

所谓原始单播信号是指直接在局域网任意设备上使用下面这种格式的 URL 即可观看 IPTV 频道:

rtsp://115.xxx.xxx.xxx/PLTV/88888913/224/3221228078/xxx.smil

对于杭州电信来说,想要访问运营商提供的单播信号源,需要通过和 IPTV 盒子一样的认证才可以。

杭州电信的 IPTV 盒子使用 IPoE 认证,打开 IPTV 盒子的网络设置可以发现,正常通过认证的情况下可以获取到 10 开头的 IP 地址和网关地址。

要想认证通过拿到合法的 IP 和网关信息,IPTV 盒子会作为一个 DHCP Client 向运营商发送 DHCP 报文,并在报文里携带两个关键信息:

  1. hostname (option 12) 这个值是盒子的STB ID,具体格式见下方。
  2. vendorid (option 60) 这个值是盒子内部使用 IPoE 账密 + 其他逻辑计算生成后发送的,需要抓包获取

IPoE (IP over Ethernet) 是一种直接在以太网(Ethernet)上承载 IP 数据的网络接入技术,它替代了传统的 PPPoE,通过 DHCP 动态获取 IP 地址并利用用户的物理信息(如 MAC 地址、VLAN ID)进行认证。

获取 hostname

STB ID

记下盒子上的 STBID,它就是 dhcp 报文里的 option 12(hostname)
示例值:00108325090207200000B1FDF1E258FD

获取 vendorid (option 60)

这一步核心原理是抓取 IPTV 盒子开机后发送的 DHCP 报文,里面包含了盒子内部计算好的 vendorid。

抓包方式有很多种,简单的一种是直接将盒子用网线(交叉线)连接到电脑网口,电脑启动 Wireshark 捕获对应的网口,待盒子通电开机后,在 Wireshark 顶部搜索框输入 dhcp 过滤出所有 dhcp 报文。

找到对应报文找到并展开 Option: (60) Vendor class identifier,选中里面的 Vendor class identifier:,右键 copy as hex stream。

复制到的内容应当是这种格式:00001f39d35c06c5560f91e4f652a438f7bd9ad33a5972fb06e75f6f45cb4fdfb190045a51153196b05f5a7b5d5b6f699b5d5b4956975b4900000000300e89ed9c786007ee110

将其保存起来。

Wireshark DHCP 报文抓包界面

注意 DHCP 报文里的 Client MAC address 要和 IPTV 盒子的 MAC 地址匹配,避免被电脑自己发出的报文干扰。

配置主路由网络接口

我的光猫 ITV 口和主路由 UDM Port 4 用网线连接。确保 Port 4 没被分配给 WAN 口。

ITV 网口确保没被分配给 WAN 口

新建 IPTV VLAN

id 设置为 100,命名为 IPTV, 避免和其他 vlan id 混淆:

新建 vlan 100

给 UDM Port 4 绑定 VLAN 100:

UMD Port 4 配置

设置 OpenWrt 对应的交换机端口

我的 OpenWrt 是一个树莓派,我将其插入了客厅 USW 交换机的 Port 6,需要正确配置此端口:

Native VLAN:选择局域网主 VLAN
Tagged VLAN Management:custom
Tagged VLANs:选择前面新增的 VLAN 100

OpenWrt 对应的交换机端口设置

配置 OpenWrt 软路由

由于我家里的 UDM 路由器不支持设置 DHCP Client 的 hostname,所以找来找去还是找一台设备刷个 OpenWrt 最简单,我是找了家里吃灰的树莓派 4B 刷了最新稳定版 OpenWrt。

修改网络接口

新增 一个 device,一个 interface:

vim /etc/config/network
config device
	option type '8021q'
	option ifname 'eth0'
	option vid '100'
	option name 'eth0.100'

config interface 'IPTV_WAN'
	option proto 'dhcp'
	option device 'eth0.100'
	option metric '20'
	option hostname '这里替换为前面拼接获取的hostname'
	option macaddr '这里替换为iptv盒子的完整mac地址'
	option sendopts '60:这里替换为前面抓包拿到的vendorid'
	option defaultroute '0'

修改 lan 的 IP 地址:

vim /etc/config/network

将 interface 'lan' 改为静态 IP,指定正确的局域网 IP 和网关地址。

config device
	option name 'br-lan'
	option type 'bridge'
	list ports 'eth0'

config interface 'lan'
	option device 'br-lan'
	option proto 'static'
	option ipaddr '主路由器局域网IP地址(没被使用的)'
	option netmask '255.255.255.0'
	option ip6assign '60'
	option gateway '主路由器网关地址'
	option metric '20'

修改 dhcp.sh 脚本

vim /lib/netifd/proto/dhcp.sh

原版:会导致 DHCP 报文发送两个 option 60


proto_run_command "$config" udhcpc \
		-p /var/run/udhcpc-$iface.pid \
		-s /lib/netifd/dhcp.script \
		-f -t 0 -i "$iface" \
		${ipaddr:+-r $ipaddr} \
		${hostname:+-x "hostname:$hostname"} \
		${vendorid:+-V "$vendorid"} \
		$clientid $defaultreqopts $broadcast $release $dhcpopts

修改后的版本:去除了 ${vendorid:+-V "$vendorid"} 这一行,增加了 -V '' \ 这行

	proto_run_command "$config" udhcpc \
		-p /var/run/udhcpc-$iface.pid \
		-s /lib/netifd/dhcp.script \
		-f -t 0 -i "$iface" \
		${ipaddr:+-r ${ipaddr/\/*/}} \
		${hostname:+-x "hostname:$hostname"} \
		-V '' \
		$clientid $defaultreqopts $broadcast $norelease $dhcpopts

自动设置静态路由

如果要正常在局域网内访问单播地址,就需要将单播服务器的 IP 设置静态路由让其可以正常走 IPTV-WAN 出去,最终走到光猫 ITV 口。

用下面的脚本自动化获取 IPTV_WAN 口的 网关 地址,然后添加静态路由。

所以后续如果要添加新的静态路由,则需要编辑这个脚本里的 TARGET_NET 变量。

vim /etc/hotplug.d/iface/99-iptv-route
#!/bin/sh
set -u

[ "${INTERFACE:-}" = "IPTV_WAN" ] || exit 0

# 需要接管的单播 IP 段列表,将其替换为你自行抓包或者网络上找到的单播服务器IP
TARGET_NETS="
115.233.40.0/21
220.191.136.0/24
"

if [ "${ACTION:-}" = "ifup" ]; then
    GW="$(ifstatus "$INTERFACE" | jsonfilter -e '@.inactive.route[@.target="0.0.0.0"].nexthop')"

    if [ -z "$GW" ]; then
        GW="$(ifstatus "$INTERFACE" | jsonfilter -e '@.data.dhcpserver')"
    fi

    if [ -n "$GW" ]; then
        for NET in $TARGET_NETS; do
            ip route replace "$NET" via "$GW" dev "$DEVICE"
            logger -t IPTV_Route "Fixed: Replaced DHCP route for $NET via $GW"
        done
    else
        logger -t IPTV_Route "Warning: Gateway not found, skip route fix"
    fi
fi

主路由静态 IP 设置

为了确保在局域网内访问单播服务器时,流量走光猫的 ITV 口出去,需要在主路由上指定静态 IP 路由表,实现局域网内任意设备访问单播服务器 IP -> 静态路由到 OpenWrt -> 走 IPTV_WAN -> 光猫 ITV 口。一旦路由策略不对,导致流量走到常规公网口去了,就会出现单播无法访问的情况。

UDM 静态IP策略

验证

以上配置全部调整好之后,重启网络以验证。注意待你重启后电脑就无法直接通过网线访问 OpenWrt 了,因为前面已经改成了主路由局域网内的 static 静态 IP,你需要将 OpenWrt 设备接入到主路由的 lan 口或者下面的交换机上面。再用你指定的静态 IP 重新访问。

重启 OpenWrt 网络:

/etc/init.d/network restart

检查 IPTV_WAN 口是否正常获取到 IP:

ifstatus IPTV_WAN

正常情况应当获取到 10 开头的 ipv4 地址。

{
  "up": true,
  "pending": false,
  "available": true,
  "autostart": true,
  "dynamic": false,
  "uptime": 67031,
  "l3_device": "eth0.100",
  "proto": "dhcp",
  "device": "eth0.100",
  "metric": 20,
  "dns_metric": 0,
  "delegation": true,
  "ipv4-address": [
    {
      "address": "10.xxx.xxx.xxx",
      "mask": 19
    }
  ],
  "dns-server": ["202.101.172.35", "202.101.172.46"],
  "inactive": {
    "route": [
      {
        "target": "0.0.0.0",
        "mask": 0,
        "nexthop": "10.xxx.xxx.1",
        "source": "10.xxx.xxx.xxx/32"
      }
    ]
  },
  "data": {
    "dhcpserver": "10.xxx.xxx.1",
    "leasetime": 2400
  }
}

查看路由表是否生效:

ip route show

正常应当展示 /etc/hotplug.d/iface/99-iptv-route 脚本里指定的 IP 段,例如:

default via 192.168.10.1 dev br-lan  metric 20
10.xxx.xxx.0/19 dev eth0.100 scope link  metric 20
115.xxx.xxx.0/21 via 10.xxx.xxx.1 dev eth0.100
192.168.10.0/24 dev br-lan scope link  metric 20
220.xxx.xxx.0/24 via 10.xxx.xxx.1 dev eth0.100

via 后面的 10.xxx.xxx.1 对应前面的 dhcpserver。

到这里正常情况下就大功告成了,可以在局域网内直接播放原始单播信号源。

关于 OpenWrt 支持组播信号源

在本文最前面我们没有使用 OpenWrt 也实现了正常播放组播信号,但是如果你已经有一台 OpenWrt 了,并且按照本文前面的内容进行了单播相关的设置,此时如果想同时支持组播,需要声明防火墙规则:

vi /etc/config/firewall
# --- IPTV 专用规则 ---
# 允许 UDP 组播视频流量进入,否则有信号无画面
config rule
	option name 'Allow-IPTV-UDP'
	option src 'wan'
	option proto 'udp'
	option dest_ip '224.0.0.0/4'
	option target 'ACCEPT'

关于 IGMP Snooping

这个开关打开之后可能会引发一些额外的问题导致组播不可用或者卡顿。如果你没法解决则关掉最省事。

IGMP Snooping 配置开关

前面新建 WAN 口的时候我们开启了 IGMP Proxy,IPTV 信号成功进入了局域网(LAN),但此时如果你没有开启(或设备不支持)IGMP Snooping 就会发生“泛洪机制”。

IGMP Snooping 泛洪对比图

为什么会发生泛洪?

IGMPProxy 的工作(L3): 路由器把 WAN 口进来的 IPTV 视频流(比如 20Mbps 的高清流)转发到了 LAN 口。

交换机/LAN 的困惑(L2): 到了局域网这一层,普通的二层交换机(或者路由器内部集成的交换芯片)如果不运行 IGMP Snooping,它就看不懂这个组播包。

默认行为: 对于看不懂的组播包,交换机的默认处理方式是把它当成广播(Broadcast)。它会想:“我不知道谁要这个包,为了保险起见,我发给所有人吧!”

下图是局域网开启 IGMP Snooping 之前和之后的对比,注意当我在 Mac Mini 上用 APTV 观看 IPTV 频道时,Apple TV 和 Flex 摄像头的端口流量。

开启 IGMP Snooping 前后对比

可以清晰看到,虽然我只在 Mac Mini 上观看 IPTV,但是流量被发送到了其他局域网端口(这里是 Apple TV + Flex 摄像头)。这会造成带宽和性能浪费。

IGMP Snooping 就是用来解决这个问题的。
IGMP Snooping(窥探) 是二层交换机(或桥接接口)的一个功能。它的做法: “偷听” IGMP Proxy 和机顶盒之间的对话。效果:

它听到机顶盒说:“我要看 CCTV-5”。

它就在心里记下:“只有 LAN Port 2 需要这个组播流”。

当视频流过来时,它只把数据发给 Port 2,其他端口(Port 1, 3, Wi-Fi)完全收不到这个数据。有效避免了垃圾流量产生。

OpenWrt 优化 IGMP Snooping 的副作用

IGMP Snooping 开启后如果出现无法播放或者几分钟后自动断开无信号,可以尝试将 OpenWrt 内核以及对应的 interface(在本文就是 eth.100)降低到 IGMPv2 版本。

内核层面指定使用 IGMPv2:

cat <<EOF >> /etc/sysctl.conf
# 强制 IGMP v2
net.ipv4.conf.all.force_igmp_version=2
net.ipv4.conf.eth0.100.force_igmp_version=2
EOF
sysctl -p

interface 层面也需要指定 igmp 版本:

vim /etc/config/network

找到 interface IPTV_WAN,增加一行 `option igmpversion '2':

config interface 'IPTV_WAN'
	option proto 'dhcp'
	option device 'eth0.100'
	option metric '20'
	option hostname 'xxxxxx'
	option macaddr 'b1:xxxxxx'
	option sendopts '60:xxxx'
	option defaultroute '0'
	option igmpversion '2'

不开启 IGMP Snooping 也能解决 “泛洪”

在将 OpenWrt 作为旁路由接入我的主网交换机时,我单独划分了一个 VLAN 100 给 IPTV 流量使用。其他家庭设备不会接入这个 VLAN。
所以呢,我们完全可以在 VLAN 层面切断 “泛洪”,具体做法是,在其他连接局域网设备的 LAN 口上只接受一个 Main VLAN(家里主局域网),其他 VLAN 流量一律屏蔽即可解决 “泛洪” 的问题。

路由器到 AP 的端口设置为自定义 VLAN,以确保访客和 IoT 设备专用 Wi-Fi 可以正常使用。
UDM -> AP VLAN 设置

其他连接 NAS 和局域网设备的端口一律设置为 Block all,这样 IPTV 的组播流量由于有 vlan id = 100 的标记,就不会影响到其他 VLAN 端口。

VLAN Block all

本文网络拓扑参考

组播转单播

如果你只是组播转单播,这个架构就足够了

组播转单播网络拓扑图

原始单播

为了正常访问原始单播信号源,网络架构变的复杂了很多。下面这个架构同时支持原始单播和组播。

引入了 VLAN + Trunk:核心功能是通过单条网线/端口承载和传输来自多个 VLAN 的流量,通过 VLAN 标记(如802.1Q)区分不同 VLAN 的帧。

同时支持原始单播 + 组播的网络架构图

总结

本文提供了两种方案,其中组播方案唯一的物理链路修改是光猫到路由器多了一根网线,客厅网口和 HDMI 线完全没有任何调整。
原始单播方案引入了额外的 OpenWrt 旁路由,整体网络配置方案较为复杂。

两种方案都可以实现在任意局域网设备例如 Apple TV 上观看 IPTV 节目,整体具备低延迟高稳定性。

折腾下来可以明显感知到 IPTV 在技术和电信资源层面明显是要比互联网流媒体占很大优势的,可惜在内容方面惨不忍睹,白白浪费这些电信资源。

踩坑需要耗费不少时间,如果此文对你有帮助,欢迎通过 /buyMeCoffee 请我喝杯咖啡。

相关参考资源

感谢诸多在互联网上公开分享自己折腾历程的人。

要获得最佳阅读体验,请访问原文 https://baiyun.me/zhejiang-hangzhou-telecom-iptv
🔲 ☆

浙江杭州电信 IPTV 内网融合教程:在局域网任意设备观看 IPTV

背景

传统的 IPTV 需要使用运营商提供的 IPTV Box 连接电视才能观看,这种方案有几个缺点:

  • 客厅如果只有一个网口就得在上网和 IPTV 之间二选一
  • 每次看 IPTV 必须切换视频信号到 IPTV Box,稍微麻烦一些
  • 必须忍受 IPTV Box 的 UI/UX
  • 客厅电视没有空闲的 HDMI 端口可用(已经连接了多个设备,例如游戏机、其他电视盒子)

如果你想解决这些问题。网络上通常有很多术语,令人眼花缭乱,例如 IPTV 组播转单播、IPTV 单线复用、光猫超级管理员密码之类的概念。

本文经过实际测试摸索,找到一条相对简单高效的方案,大概只需要10分钟时间即可配置完毕。

前置条件

  • 联系运营商开通 IPTV 服务,一般如果你已经在用融合宽带套餐,每个月加 10 块钱就可以
  • 光猫为桥接模式(路由器拨号)
  • 光猫和路由器放在一起,可以从光猫接两根线到路由器
  • 路由器支持多 WAN 口模式
  • 光猫有单独标识的 ITV 端口
  • 局域网内有可以安装 docker 镜像的设备(例如 NAS)或者你有其他办法可以在局域网内部署 https://github.com/stackia/rtp2httpd

前置测试

将光猫 ITV 口接一根网线连到电脑,在电脑网络设置里手动设置此连接的 IP(指定为 192.168.1.1xx)具体 IP 段一般和光猫背面展示的控制台地址一致。本文后面以此 IP 段示例。
网关指定为 192.168.1.1

然后使用 VLC 播放器播放组播地址,可以从以下项目找具体地址:
https://github.com/FHZDCJ/Zhejiang_Telecom_IPTV/blob/main/Zhejiang_Multicast/Zhejiang_Telecom_IPTV.m3u

格式:rtp://233.xxx.xxx.xxx:5140,如果可以正常播放组播源,则继续后面的步骤。

在执行此测试的时候建议关闭 Wi-Fi 和 surge 之类的工具。确保所有请求直接通过网线发给光猫的 ITV 口。

组播转单播

如果前面的测试通过,说明你当前的网络条件可以正常播放组播源信号。

接下来为了实现在局域网内任意设备观看 IPTV,需要将组播信号(通常是 rtp 协议)转为单播信号(http 协议)。

首先需要在局域网内正常部署 https://github.com/stackia/rtp2httpd 服务。
我是直接在 NAS 上通过 Docker Compose 部署的,配置如下:

services:
  rtp2httpd:
    image: ghcr.io/stackia/rtp2httpd:latest
    container_name: rtp2httpd
    network_mode: host
    command: --noconfig --verbose 2 --listen 5140 --maxclients 20

待你可以正常访问 http://NAS-IP:5140/status 之后再进行下一步。

将光猫 ITV 口连接到路由器的第二个 WAN 口(第一个 WAN 口用于正常上网),这一步的主要目的是实现在正常上网的这个 LAN(局域网) 里面通过 HTTP 单播源观看 IPTV。

我用的是 Unifi 路由器,直接在控制台新建一个 WAN 口,关联刚才连接 ITV 口的端口,同时勾选 IGMP proxy 开关(这个很关键),选择静态 ip。

此时在 VLC 里打开 rtp2httpd 格式的单播信号源地址:

http://192.168.10.20:5140/rtp/239.xxx.xxx.xxx:5140

这里 192.168.10.20 是我的 NAS 地址,里面用 docker 部署了rtp2httpd(host 网络模式,默认绑定在 5140 端口),239.xxx.xxx.xxx 是具体 IPTV 频道的组播地址。

正常情况下这里就可以正常观看了。

最后,为了方便切换所有可观看的频道,可以将这里的 m3u 文件进行修改:
https://github.com/FHZDCJ/Zhejiang_Telecom_IPTV/blob/main/Zhejiang_Multicast/Zhejiang_Telecom_IPTV_ONLINE_LOGO.m3u

替换里面的链接为 rtp2httpd 格式,以下是替换后的示例:

#EXTM3U
#EXTINF:-1 tvg-id="CCTV1" tvg-name="CCTV1" group-title="央视",CCTV1综合
http://192.168.10.20:5140/rtp/233.xxx.xxx.xxx:5140
#EXTINF:-1 tvg-id="CCTV2" tvg-name="CCTV2" group-title="央视",CCTV2财经
http://192.168.10.20:5140/rtp/233.xxx.xxx.xxx:5140

将修改后的 m3u 文件导入到 APTV 之类的软件即可在 Apple TV、Mac、iPhone 等局域网设备观看 IPTV 频道。

总结

通过本文方案,唯一的物理链路修改是光猫到路由器多了一根网线,客厅网口和 HDMI 线完全没有任何调整,实现了在我现有的 Apple TV 设备上观看 IPTV 节目。整体具备低延迟高稳定性。

踩坑需要耗费不少时间,如果此文对你有帮助,欢迎通过 /buyMeCoffee 请我喝杯咖啡。

要获得最佳阅读体验,请访问原文 https://baiyun.me/zhejiang-hangzhou-iptv
🔲 ⭐

RIME 鼠须管输入法简明使用教程

工作中经常见到大量同事花名被打错的场景,本文分享了我近几年在用的输入法,完美解决了这个问题。

本文适合有以下需求的读者:

  • 很在意输入法隐私,不想让第三方知道自己输入了什么
  • 很在意打字效率和准确性
  • 经常要输入一些自定义词组

在接触到 RIME 之前,我用过很多款中文输入法,从最早的搜狗、到后来的百度、必应Bing输入法(后来改名叫微软拼音输入法)。其中 Windows 上最满意的是必应输入法,无奈 Mac 平台用不了。后来 Mac 上一直使用了好多年百度输入法,打字词库总是缺少一种称手的感觉,又找不到好的代替。

另外考虑到这类国内厂商的输入法基本毫无隐私和安全可言,它们都是免费产品,如果不是为了获取用户数据,那他们开发和持续维护这些输入法的目的是什么呢?

🐭 RIME 鼠须管

RIME

Rime(中州韵)是一个开源的输入法引擎,鼠须管是基于 Rime 构建的一款开源输入法,面向 macOS 平台。

我很早就听说过这款输入法的大名,也遇到很多人推荐它,但是被它繁杂的配置劝退了。最近,看到有人做了一套配置集合,整合了很多常用的功能,配合官方的配置管理工具让使用门槛一下子降低了好多。

接下来是一个简明指南,可以帮你快速安装和配置好一个开箱即用的输入法。

继续阅读需要的一些前置要求:

  1. 了解基本的 linux/unix 命令,例如 cat、vim 用于在命令行查看和编辑文件
  2. 了解 git 和 brew 这些工具,用于安装必要的程序

安装

# 回到家目录,可选
cd ~

# 安装 plum,这是 Rime 官方的配置管理工具
git clone --depth=1 https://github.com/rime/plum
cd plum

# 安装 Rime 配置:雾凇拼音
bash rime-install iDvel/rime-ice:others/recipes/full

# 安装 鼠须管 Squirrel,安装之后需要登出系统重新登入才能添加输入法
brew install --cask squirrel

下面是雾凇拼音提供的功能概览图

雾凇拼音功能概览图

修改个人配置

鼠须管和默认配置安装好之后,其实已经可以正常使用了。本节的示例教你如何正确的做一些个性化配置。

RIME 设置

执行 vim ~/Library/Rime/default.custom.yaml,填入以下配置,下面的配置主要做了两个事情:

  • 输入法方案只保留了全拼(schema: rime_ice),删除了默认启用的双拼方案
  • 开启了 ⇧Shift 键切换英文
# Rime Patch settings
# encoding: utf-8

patch:
  # 方案列表
  schema_list:
    - schema: rime_ice

  # 中西文切换
  #
  # 【good_old_caps_lock】 CapsLock 切换到大写或切换中英。
  # (macOS 偏好设置的优先级更高,如果勾选【使用大写锁定键切换“ABC”输入法】则始终会切换输入法)
  #
  # 切换中英:
  # 不同的选项表示:打字打到一半时按下了 CapsLock、Shift、Control 后:
  # commit_code  上屏原始的编码,然后切换到英文
  # commit_text  上屏拼出的词句,然后切换到英文
  # clear        清除未上屏内容,然后切换到英文
  # inline_ascii 无输入时,切换中英;有输入时,切换到临时英文模式,按回车上屏后回到中文状态
  # noop         屏蔽快捷键,不切换中英,但不要屏蔽 CapsLock
  ascii_composer:
    good_old_caps_lock: true  # true | false
    switch_key:
      Caps_Lock: commit_code   # commit_code | commit_text | clear
      Shift_L: commit_code     # commit_code | commit_text | inline_ascii | clear | noop
      Shift_R: commit_code     # commit_code | commit_text | inline_ascii | clear | noop
      Control_L: noop          # commit_code | commit_text | inline_ascii | clear | noop
      Control_R: noop          # commit_code | commit_text | inline_ascii | clear | noop

鼠须管设置

鼠须管有很多内置的主题,你可以直接将他们的名字填到下面配置的 color_schema 字段即可,我这里没有使用内置主题,而是从网络上找了两款接近 macOS 原生输入法的主题。

执行 vim ~/Library/Rime/squirrel.custom.yaml,填入以下配置:

# Squirrel Patch settings
# encoding: utf-8
#
# 内置皮肤展示: https://github.com/NavisLab/rime-pifu
# 鼠须管配置指南: https://github.com/LEOYoon-Tsaw/Rime_collections/blob/master/鼠鬚管介面配置指南.md
# 鼠须管作者写的图形化的皮肤设计器: https://github.com/LEOYoon-Tsaw/Squirrel-Designer

patch:
  style:
    color_scheme: macos_light       # 将皮肤名称输入在此处
    color_scheme_dark: macos_dark   # 暗色模式下的皮肤名称

  # 皮肤列表
  preset_color_schemes:
    macos_light:
      name: "MacOS 浅色/MacOS Light"
      author: 小码哥
      font_face: "PingFangSC"          # 字体及大小
      font_point: 16
      label_font_face: "PingFangSC"    # 序号字体及大小
      label_font_point: 12
      comment_font_face: "PingFangSC"  # 注字体及大小
      comment_font_point: 16
      candidate_format: "%c\u2005%@\u2005" # 编号 %c 和候选词 %@ 前后的空间
      candidate_list_layout: linear   # 候选排布:层叠 stacked | 行 linear
      text_orientation: horizontal    # 行文向: 横 horizontal | 纵 vertical
      inline_preedit: true            # 拼音位于: 候选框 false | 行内 true
      translucency: false             # 磨砂: false | true
      mutual_exclusive: false         # 色不叠加: false | true
      border_height: 1                # 外边框 高
      border_width: 1                 # 外边框 宽
      corner_radius: 5                # 外边框 圆角半径
      hilited_corner_radius: 5       # 选中框 圆角半径
      surrounding_extra_expansion: 0 # 候选项背景相对大小?
      shadow_size: 0                 # 阴影大小
      line_spacing: 5                # 行间距
      base_offset: 0                 # 字基高
      alpha: 1                       # 透明度,0~1
      spacing: 10                    # 拼音与候选项之间的距离 (inline_preedit: false)
      color_space: srgb                       # 色彩空间: srgb | display_p3
      back_color: 0xFFFFFF                    # 底色
      hilited_candidate_back_color: 0xD75A00  # 选中底色
      label_color: 0x999999                   # 序号颜色
      hilited_candidate_label_color: 0xFFFFFF # 选中序号颜色
      candidate_text_color: 0x3c3c3c          # 文字颜色
      hilited_candidate_text_color: 0xFFFFFF  # 选中文字颜色
      comment_text_color: 0x999999            # 注颜色
      hilited_comment_text_color: 0xFFFFFF    # 选中注颜色
      text_color: 0x424242                    # 拼音颜色 (inline_preedit: false)
      hilited_text_color: 0xFFFFFF            # 选中拼音颜色 (inline_preedit: false)
      candidate_back_color: 0xe9e9ea          # 候选项底色
      # preedit_back_color:                   # 拼音底色 (inline_preedit: false)
      hilited_back_color: 0xD75A00            # 选中拼音底色 (inline_preedit: false)
      border_color: 0xFFFFFF                  # 外边框颜色

    macos_dark:
      name: "MacOS 深色/MacOS Dark"
      author: 小码哥
      font_face: "PingFangSC"          # 字体及大小
      font_point: 16
      label_font_face: "PingFangSC"    # 序号字体及大小
      label_font_point: 12
      comment_font_face: "PingFangSC"  # 注字体及大小
      comment_font_point: 16
      candidate_format: "%c\u2005%@\u2005" # 编号 %c 和候选词 %@ 前后的空间
      candidate_list_layout: linear   # 候选排布:层叠 stacked | 行 linear
      text_orientation: horizontal    # 行文向: 横 horizontal | 纵 vertical
      inline_preedit: true            # 拼音位于: 候选框 false | 行内 true
      translucency: false             # 磨砂: false | true
      mutual_exclusive: false         # 色不叠加: false | true
      border_height: 1                # 外边框 高
      border_width: 1                 # 外边框 宽
      corner_radius: 5                # 外边框 圆角半径
      hilited_corner_radius: 5       # 选中框 圆角半径
      surrounding_extra_expansion: 0 # 候选项背景相对大小?
      shadow_size: 0                 # 阴影大小
      line_spacing: 5                # 行间距
      base_offset: 0                 # 字基高
      alpha: 1                       # 透明度,0~1
      spacing: 10                    # 拼音与候选项之间的距离 (inline_preedit: false)
      color_space: srgb                       # 色彩空间: srgb | display_p3
      back_color: 0x1f1e2d                  # 底色
      hilited_candidate_back_color: 0xD75A00  # 选中底色
      label_color: 0x999999                   # 序号颜色
      hilited_candidate_label_color: 0xFFFFFF # 选中序号颜色
      candidate_text_color: 0xe9e9ea          # 文字颜色
      hilited_candidate_text_color: 0xFFFFFF  # 选中文字颜色
      comment_text_color: 0x999999            # 注颜色
      hilited_comment_text_color: 0x999999    # 选中注颜色
      text_color: 0x808080                    # 拼音颜色 (inline_preedit: false)
      hilited_text_color: 0xFFFFFF            # 选中拼音颜色 (inline_preedit: false)
      candidate_back_color: 0xe9e9ea          # 候选项底色
      # preedit_back_color:                   # 拼音底色 (inline_preedit: false)
      hilited_back_color: 0xD75A00            # 选中拼音底色 (inline_preedit: false)
      border_color: 0x050505                  # 外边框颜色

    wechat_light:
      name: "微信浅色/Wechat Light"
      author: 小码哥
      font_face: "PingFangSC"          # 字体及大小
      font_point: 16
      label_font_face: "PingFangSC"    # 序号字体及大小
      label_font_point: 13
      comment_font_face: "PingFangSC"  # 注字体及大小
      comment_font_point: 16
      candidate_format: "%c\u2005%@\u2005" # 编号 %c 和候选词 %@ 前后的空间
      candidate_list_layout: linear   # 候选排布:层叠 stacked | 行 linear
      text_orientation: horizontal    # 行文向: 横 horizontal | 纵 vertical
      inline_preedit: true            # 拼音位于: 候选框 false | 行内 true
      translucency: false             # 磨砂: false | true
      mutual_exclusive: false         # 色不叠加: false | true
      border_height: 1                # 外边框 高
      border_width: 1                 # 外边框 宽
      corner_radius: 5                # 外边框 圆角半径
      hilited_corner_radius: 5       # 选中框 圆角半径
      surrounding_extra_expansion: 0 # 候选项背景相对大小?
      shadow_size: 0                 # 阴影大小
      line_spacing: 5                # 行间距
      base_offset: 0                 # 字基高
      alpha: 1                       # 透明度,0~1
      spacing: 10                    # 拼音与候选项之间的距离 (inline_preedit: false)
      color_space: srgb                       # 色彩空间: srgb | display_p3
      back_color: 0xFFFFFF                    # 底色
      hilited_candidate_back_color: 0x79af22  # 选中底色
      label_color: 0x999999                   # 序号颜色
      hilited_candidate_label_color: 0xFFFFFF # 选中序号颜色
      candidate_text_color: 0x3c3c3c          # 文字颜色
      hilited_candidate_text_color: 0xFFFFFF  # 选中文字颜色
      comment_text_color: 0x999999            # 注颜色
      hilited_comment_text_color: 0x999999    # 选中注颜色
      text_color: 0x424242                    # 拼音颜色 (inline_preedit: false)
      hilited_text_color: 0x999999            # 选中拼音颜色 (inline_preedit: false)
      candidate_back_color: 0xe9e9ea          # 候选项底色
      # preedit_back_color:                   # 拼音底色 (inline_preedit: false)
      hilited_back_color: 0x79af22            # 选中拼音底色 (inline_preedit: false)
      border_color: 0xFFFFFF                  # 外边框颜色

    wechat_dark:
      name: "微信深色/Wechat Dark"
      author: 小码哥
      font_face: "PingFangSC"          # 字体及大小
      font_point: 16
      label_font_face: "PingFangSC"    # 序号字体及大小
      label_font_point: 13
      comment_font_face: "PingFangSC"  # 注字体及大小
      comment_font_point: 16
      candidate_format: "%c\u2005%@\u2005" # 编号 %c 和候选词 %@ 前后的空间
      candidate_list_layout: linear   # 候选排布:层叠 stacked | 行 linear
      text_orientation: horizontal    # 行文向: 横 horizontal | 纵 vertical
      inline_preedit: true            # 拼音位于: 候选框 false | 行内 true
      translucency: false             # 磨砂: false | true
      mutual_exclusive: false         # 色不叠加: false | true
      border_height: 1                # 外边框 高
      border_width: 1                 # 外边框 宽
      corner_radius: 5                # 外边框 圆角半径
      hilited_corner_radius: 5       # 选中框 圆角半径
      surrounding_extra_expansion: 0 # 候选项背景相对大小?
      shadow_size: 0                 # 阴影大小
      line_spacing: 5                # 行间距
      base_offset: 0                 # 字基高
      alpha: 1                       # 透明度,0~1
      spacing: 10                    # 拼音与候选项之间的距离 (inline_preedit: false)
      color_space: srgb                       # 色彩空间: srgb | display_p3
      back_color: 0x151515                    # 底色
      hilited_candidate_back_color: 0x79af22  # 选中底色
      label_color: 0x999999                   # 序号颜色
      hilited_candidate_label_color: 0xFFFFFF # 选中序号颜色
      candidate_text_color: 0xbbbbbb          # 文字颜色
      hilited_candidate_text_color: 0xFFFFFF  # 选中文字颜色
      comment_text_color: 0x999999            # 注颜色
      hilited_comment_text_color: 0xFFFFFF    # 选中注颜色
      text_color: 0xbbbbbb                    # 拼音颜色 (inline_preedit: false)
      hilited_text_color: 0x999999            # 选中拼音颜色 (inline_preedit: false)
      candidate_back_color: 0xbbbbbb          # 候选项底色
      # preedit_back_color:                   # 拼音底色 (inline_preedit: false)
      hilited_back_color: 0x79af22            # 选中拼音底色 (inline_preedit: false)
      border_color: 0x292929                  # 外边框颜色

拷贝完之后,右上角输入法切换到 Squirrel 点击 Deploy 让配置生效。
Deploy配置

上面我用的配置文件都是 *.custom.yaml 结尾,这种模式称之为打补丁模式,主要是为了避免修改原配置文件,这样以后输入法配置升级就很容易了。

设置同步功能

使用鼠须管自带的用户数据同步功能配合 iCloud 之类的网盘,可以轻松将你的输入法配置和词库跨设备同步,对于多台设备,鼠须管会自动合并用户词库。

  1. 编辑 installation.yaml 文件
vim ~/Library/Rime/installation.yaml
  1. 修改内容参考如下:
# 本机的 ID 标志,默认是一串 UUID。生成的文件夹是这个名字,可以改成更好识别的名称
installation_id: "mbp16"
# 同步的路径,默认如没有设置此属性,则是在当前配置目录的 `sync/` 文件夹
sync_dir: "/Users/YOUR_NAME/Library/Mobile Documents/com~apple~CloudDocs/RimeSync"
  1. 修改好之后,右上角输入法切换到 Squirrel 点击 Sync user data 即可完成数据同步,之后改动配置后需要再次重复此操作。目前官方没有提供自动触发同步的功能,因为在同步用户数据期间,输入法会处于不可用状态。

RIME 的同步功能是 配置单向同步 + 用户词库双向同步,举个例子:A 设备开启同步功能后,B 设备并不会自动获得相同的配置,这两个设备只会自动合并用户词库(也就是你的自造词)。

RIME 同步功能图解

设置自动同步

如果你觉得经常手动同步用户数据比较麻烦,也可以配合 sleepwatcher 在电脑休眠和唤醒的时候自动触发同步,具体做法如下:

brew install sleepwatcher
touch ~/.sleep
chmod +x ~/.sleep

编辑 ~/.sleep 文件,填入以下内容:

#!/usr/bin/env bash

# 触发鼠须管同步用户数据
/Library/Input\ Methods/Squirrel.app/Contents/MacOS/Squirrel --sync

配置好之后,重启 sleepwatcher

brew services restart sleepwatcher

这时候可以休眠电脑等10秒再唤醒,检查 sync_dir 里面的 rime_ice.userdb.txt 文件修改时间是否变了。如果不生效,请检查 ~/.sleep 脚本是否正确,可以手动执行看下效果,以及重启 sleepwatcher.

上面的 ~/.sleep 脚本是在电脑休眠之前执行,根据你的需要还可以定义一个 ~/.wakeup 脚本在唤醒后执行。

配置更新

前面我们用的预设配置是 雾凇拼音 这个项目提供的,后续可以通过下面的命令更新配置以获得新功能和 Bugfix。

更新雾凇拼音:所有配置和词库(更新前建议先备份 ~/Library/Rime 目录,更新后所有非 .custom.yaml 结尾的配置文件会被覆盖)

# 先回到 plum 安装目录,如果你将 plum 安装在了其他目录,这里需要修改
cd ~/plum
bash rime-install iDvel/rime-ice:others/recipes/full

更新雾凇拼音:所有词库文件

cd ~/plum
bash rime-install iDvel/rime-ice:others/recipes/all_dicts

如果你用下来感觉挺好的没啥问题,那建议只更新词库就行了。

对于自定义的补丁配置文件,尤其是 *.custom.yaml 和自定义字典,建议将其备份到自己的 git 仓库保存,防止配置意外被覆盖或丢失。

使用技巧

RIME 有些很有用的使用技巧新手刚开始不仔细研究可能发现不了,这里我罗列一些非常好用的技巧。

删除不想要的自造词

打字久了很容易在不经意间产生一些错误的自造词,这时候如果不处理就很容易在后续的输入过程中继续使用这些错误的自造词。

要删除不想要的自造词,你可以使用方向键选中要删除的候选词,再按下 Fn+Shift+Delete 即可删除。

手册原文:只能夠從用戶詞典中刪除詞組。用於碼表中原有的詞組時,只會取消其調頻效果。

使用 Tab 键在拼音之间切换光标

当你打了一段话,发现前面的拼音打错了,或者想单独修改前面的某个字,这时候不用狂按退格键,你可以直接按 Tab 键或 Shift + Tab 在拼音中前后切换光标到对应的拼音。

Tab键切换光标示例

进阶说明

按照上面的步骤操作下来你已经入门了,这时候如果你想让 RIME 输入法更顺手更符合你的习惯偏好就需要了解进阶概念了。

修改配置

如果你想调整一些自定义的设置,可以先查看以下两个文件,可以对整体配置有个概念:

  1. cat ~/Library/Rime/default.yaml 这是 Rime 输入法引擎的默认配置文件,大部分跟输入有关的核心配置都在这里
# Rime default settings
# encoding: utf-8
#
# 小狼毫似乎不支持 Control+Shift 开头的快捷键,可自行修改成别的。
# 鼠须管在 Sublime Text、Telegram 等个别软件中也无法使用 Control+Shift+数字 的快捷键,可暂时用方案选单切换。

config_version: '2023-03-03'

# 方案列表
schema_list:
  - schema: rime_ice
  - schema: double_pinyin
  - schema: double_pinyin_flypy

# 菜单
menu:
  page_size: 5  # 候选词个数
  # alternative_select_labels: [ ①, ②, ③, ④, ⑤, ⑥, ⑦, ⑧, ⑨, ⑩ ]  # 修改候选项标签
  # alternative_select_keys: ASDFGHJKL  # 如编码字符占用数字键,则需另设选字键

# 方案选单相关
switcher:
  caption: 「方案选单」
  hotkeys:
    # - F4
    # - Control+grave
    # - Alt+grave
    - Control+Shift+grave
  save_options:  # 开关记忆,从方案选单(而非快捷键)切换时会记住的选项,需要记忆的开关不能设定 reset
    - ascii_punct
    - traditionalization
    - emoji
  fold_options: true            # 呼出时是否折叠,多方案时建议折叠 true ,一个方案建议展开 false
  abbreviate_options: true      # 折叠时是否缩写选项
  option_list_separator: ' / '  # 折叠时的选项分隔符

# 中西文切换
#
# 【good_old_caps_lock】 CapsLock 切换到大写或切换中英。
# (macOS 偏好设置的优先级更高,如果勾选【使用大写锁定键切换“ABC”输入法】则始终会切换输入法)
#
# 切换中英:
# 不同的选项表示:打字打到一半时按下了 CapsLock、Shift、Control 后: 
# commit_code  上屏原始的编码,然后切换到英文
# commit_text  上屏拼出的词句,然后切换到英文
# clear        清除未上屏内容,然后切换到英文
# inline_ascii 无输入时,切换中英;有输入时,切换到临时英文模式,按回车上屏后回到中文状态
# noop         屏蔽快捷键,不切换中英,但不要屏蔽 CapsLock
ascii_composer:
  good_old_caps_lock: true  # true | false
  switch_key:
    Caps_Lock: clear  # commit_code | commit_text | clear
    Shift_L: noop     # commit_code | commit_text | inline_ascii | clear | noop
    Shift_R: noop     # commit_code | commit_text | inline_ascii | clear | noop
    Control_L: noop   # commit_code | commit_text | inline_ascii | clear | noop
    Control_R: noop   # commit_code | commit_text | inline_ascii | clear | noop


# punctuator 和 recognizer 由具体方案指定了
punctuator:
  full_shape:
  half_shape:
  symbols:
recognizer:
  patterns:


# 快捷键
key_binder:
  # 以词定字(上屏当前词句的第一个或最后一个字)
  select_first_character:
  select_last_character: "grave"

  bindings:
    # Tab/Shift+Tab 切换光标至下/上一个拼音
    - { when: composing, accept: Shift+Tab, send: Shift+Left }
    - { when: composing, accept: Tab, send: Shift+Right }
    # Tab/Shift+Tab 翻页
    # - { when: has_menu, accept: Shift+Tab, send: Page_Up }
    # - { when: has_menu, accept: Tab, send: Page_Down }

    # 翻页 - =
    - { when: has_menu, accept: minus, send: Page_Up }
    - { when: has_menu, accept: equal, send: Page_Down }

    # 翻页 , .
    # 需要额外注释掉方案中 recognizer/patterns 下的 url_2 选项(这个会覆盖掉句号的行为)
    # - { when: paging, accept: comma, send: Page_Up }
    # - { when: has_menu, accept: period, send: Page_Down }

    # 翻页 [ ]
    # - { when: paging, accept: bracketleft, send: Page_Up }
    # - { when: has_menu, accept: bracketright, send: Page_Down }

    # numbered_mode_switch:
    # - { when: always, accept: Control+Shift+1, select: .next }               # 在最近的两个方案之间切换
    # - { when: always, accept: Control+Shift+2, toggle: ascii_mode }          # 切换中英
    - { when: always, accept: Control+Shift+3, toggle: ascii_punct }           # 切换中英标点
    - { when: always, accept: "Control+Shift+4", toggle: traditionalization }  # 切换简繁
    # - { when: always, accept: Control+Shift+5, toggle: full_shape }          # 切换全半角

# key_binder 按键速查 https://github.com/LEOYoon-Tsaw/Rime_collections/blob/master/Rime_description.md

  1. cat ~/Library/Rime/squirrel.yaml 这是鼠须管输入法的配置,包含主题配色之类的设置
# Squirrel settings
# encoding: utf-8
#
# 内置皮肤展示: https://github.com/NavisLab/rime-pifu
# 鼠须管配置指南: https://github.com/LEOYoon-Tsaw/Rime_collections/blob/master/鼠鬚管介面配置指南.md
# 鼠须管作者写的图形化的皮肤设计器: https://github.com/LEOYoon-Tsaw/Squirrel-Designer

config_version: '2023-02-27'

# options: last | default | _custom_
# last: the last used latin keyboard layout
# default: US (ABC) keyboard layout
# _custom_: keyboard layout of your choice, e.g. 'com.apple.keylayout.USExtended' or simply 'USExtended'
keyboard_layout: default

# for veteran chord-typist
chord_duration: 0.1  # seconds

# options: always | never | appropriate
show_notifications_when: appropriate

# ascii_mode、inline、no_inline、vim_mode 等等设定,可参考 /Library/Input Methods/Squirrel.app/Contents/SharedSupport/squirrel.yaml
app_options:
  # com.apple.Spotlight:
  #   ascii_mode: true    # 开启默认英文
  # com.microsoft.VSCode:
  #   ascii_mode: false   # 关闭默认英文

style:
  # 选择皮肤,亮色与暗色主题
  color_scheme: purity_of_form_custom
  color_scheme_dark: purity_of_form_custom

  # 预设选项:(可被皮肤覆盖;如果皮肤没写,则默认使用这些属性。)
  text_orientation: horizontal  # horizontal | vertical
  inline_preedit: true
  corner_radius: 10
  hilited_corner_radius: 0
  border_height: 0
  border_width: 0
  line_spacing: 5
  spacing: 10
  #candidate_format: '%c. %@'
  #base_offset: 6
  font_face: 'Lucida Grande'
  font_point: 21
  #label_font_face: 'Lucida Grande'
  label_font_point: 18
  #comment_font_face: 'Lucida Grande'
  comment_font_point: 18


# 皮肤列表
preset_color_schemes:
  # 对 purity_of_form 略微调整颜色,让色彩更柔和点,补全其他选项和注释
  purity_of_form_custom:
    name: "純粹的形式/Purity of Form Custom"
    author: 雨過之後、佛振
    font_face: ""                   # 字体及大小
    font_point: 18
    label_font_face: "Helvetica"    # 序号字体及大小
    label_font_point: 12
    comment_font_face: "Helvetica"  # 注字体及大小
    comment_font_point: 16
    candidate_list_layout: stacked  # 候选排布:层叠 stacked | 行 linear
    text_orientation: horizontal    # 行文向: 横 horizontal | 纵 vertical
    inline_preedit: true            # 拼音位于: 候选框 false | 行内 true
    translucency: false             # 磨砂: false | true
    mutual_exclusive: false         # 色不叠加: false | true
    border_height: 0               # 外边框 高
    border_width: 0                # 外边框 宽
    corner_radius: 10              # 外边框 圆角半径
    hilited_corner_radius: 0       # 选中框 圆角半径
    surrounding_extra_expansion: 0 # 候选项背景相对大小?
    shadow_size: 0                 # 阴影大小
    line_spacing: 5                # 行间距
    base_offset: 0                 # 字基高
    alpha: 1                       # 透明度,0~1
    spacing: 10                    # 拼音与候选项之间的距离 (inline_preedit: false)
    color_space: srgb                       # 色彩空间: srgb | display_p3
    back_color: 0x545554                    # 底色
    hilited_candidate_back_color: 0xE3E3E3  # 选中底色
    label_color: 0xBBBBBB                   # 序号颜色
    hilited_candidate_label_color: 0x4C4C4C # 选中序号颜色
    candidate_text_color: 0xEEEEEE          # 文字颜色
    hilited_candidate_text_color: 0x000000  # 选中文字颜色
    comment_text_color: 0x808080            # 注颜色
    hilited_comment_text_color: 0x808080    # 选中注颜色
    text_color: 0x808080                    # 拼音颜色 (inline_preedit: false)
    hilited_text_color: 0xEEEEEE            # 选中拼音颜色 (inline_preedit: false)
    # candidate_back_color:                 # 候选项底色
    # preedit_back_color:                   # 拼音底色 (inline_preedit: false)
    # hilited_back_color:                   # 选中拼音底色 (inline_preedit: false)
    # border_color:                         # 外边框颜色

碰到你想修改的配置项,有两种修改方案:

  1. 直接修改 default.yaml,这种方式的缺点是后续你更新雾凇拼音配置的时候会被覆盖掉
  2. patch 打补丁,本文前面用到的 *.custom.yaml 文件就是使用这种方案,想用 patch 的方式修改 default.yaml,需要新建一个 default.custom.yaml 文件用来覆盖 default.yaml 中的配置项,具体格式可以参考本文开头的示例。

修改配置文件后记得重新 Deploy。

自定义短语

  • cat ~/Library/Rime/custom_phrase.txt 查看现有短语和配置说明

自定义字典

对于拼音输入法来说,不管是全拼还是双拼,都面临一个问题:重码。相同的拼音对应多个发音类似的词语,如果第一屏候选词没出现用户想要的词,就会严重影响输入效率。

一款输入法好不好用,在很大程度上是看这款输入法的字典好不好用,字典不是越大越多越好,而是要契合用户的使用场景,越大的字典重码率通常会越高,越容易让用户感觉首屏候选词经常出现不了自己想要的词。

根据自己的日常使用场景来挂载对应的词库,并在平时逐步定制积累自定义词库是一个不错的选择。

本文使用的雾凇拼音已经默认做了很多输入优化和字典优化,如果你使用一段时间后想实现更好的打字效果,制作你的自定义字典是个不错的选择(当然你多次重复输入一个词语也会自动造词的)

举个例子,一些大公司喜欢让员工起花名,随着时间推移员工人数飞速增长,起两个字的中文花名就是个很困难的事情,所以经常能看到许多新员工的花名奇奇怪怪,生僻字和谐音很常见。这时候你用拼音输入法打同事的花名就非常麻烦和难受。针对这种情况就可以用自定义字典很好的解决,后续输入同事的花名效率就会非常高。

要制作自定义字典,请先切换到 Rime 的用户配置目录:cd ~/Library/Rime

先查看雾凇拼音的默认字典配置:cat rime_ice.dict.yaml

# Rime dictionary
# encoding: utf-8

---
name: rime_ice
version: "2023-03-04"
import_tables:
  - cn_dicts/8105     # 字表
  # - cn_dicts/41448  # 大字表(按需启用)
  - cn_dicts/base     # 基础词库
  - cn_dicts/sogou    # 搜狗流行词
  - cn_dicts/ext      # 扩展词库
  - cn_dicts/tencent  # 腾讯词向量(大词库,部署时间较长)
  - cn_dicts/others   # 一些杂项

  # 建议把扩展词库放到下面,有重复词条时,最上面的权重生效
  # 下面两个是我自己加的自定义字典
  - cn_dicts/names      # 自定义人名词库
  - en_dicts/my_en_ext  # 扩充英文词语
...

编辑自定义字典:vim cn_dicts/names.dict.yaml,这里的 names.dict.yaml 要改成你自己的字典名称。

内容是这样的:

---
name: names
version: "1"
sort: by_weight
...

维恩 wei en 100
步鲁斯 bu lu si 100

这里需要格外注意字典内容的缩进,正确的示例如下,用 Tab 分割不同的列,用空格分割拼音:

拼音	pin yin 1234
拼音<Tab>pin<Space>yin<Tab>1234

最好不要直接复制这段配置(制表符被转换为空格了),RIME 解析字典要依赖制表符识别不同的列,在编辑字典文件的时候记得先将编辑器缩进模式设置为 Tab 模式,建议先查看雾凇拼音的文档:编写词库,如果字典里的词没出现在首屏,可以增加权重,例如:1000000,默认建议先设置 100 就可以了。

一些缺点

  • 目前没有好的图形界面来修改配置和字典,不过对程序员来说问题不大,我建议将自定义的部分维护在 Git 仓库里,再配合一个自定义的更新脚本,实现一键备份、更新
  • 配置错误不会明确提示,例如字典格式不对、语法不对、引用了不存在的文件都不会明确提示你,如果你发现改配置后重新 Deploy 不生效,大概率是配置出错了,例如字典里没有用 Tab 分割。这种情况可以检查配置,或者手工去查看 Rime 日志文件(位于 $TMPDIR/rime.squirrel)

总结

通过几分钟时间你可以快速配置出一个好用、安全、隐私、轻量的输入法,它可以充分按照你的习惯和需求去定制,而且 RIME 有一个不错的用户手册可供你参考。如果你在意以上这些特点,那 RIME 可能是目前最好的选择。虽然它并不是完美的,也有一些小毛病,就看你怎么取舍了。

如果你想在 iOS 上也使用和 Mac 相同的词库,可以试一试 iOS 上的一款 Rime 实现:仓输入法。我自己实测下来直接将 Mac 端 Rime 配置导入到 iOS 端的仓输入法可以正常使用,不过得手动同步有些低效。

参考

要获得最佳阅读体验,请访问原文 https://baiyun.me/rime-simple-tutorial
🔲 ⭐

WEB 性能优化:如何让你的网站页面秒开(方法论)

本文适合于专业和普通用户

让用户实际感知到你的网站使用起来非常快是很重要的。性能优化的方法和细节非常多,且不同的网站形态需要对症下药。本文不会一个个细节讲下来,而是会授人以渔,告诉你方法论,让你自己能发现性能问题,知道如何发现和解决性能问题。

性能优化的几个关键点

  • 页面加载速度
  • 页面交互性能

发现和解决性能问题

不管是专业开发者还是普通用户,我都推荐你使用 Google 提供的 PageSpeed Insights 来直观的发现性能问题以及对应的解决方案。

输入你的网站地址就可以看到在移动端和电脑端的性能分数以及对应的优化建议

PageSpeed Insights 性能测试截图

PageSpeed Insights 性能审查建议

点击对应的结果展开详细信息就可以看到问题的详细描述,以及每一项后面都有一个「了解更多」可以让你更详细的了解这个问题的原因和解决方案。

PageSpeed Insights 详细问题描述和了解更多

可以把 PageSpeed Insights 这个工具当做你优化网站性能的顶级良师益友。

一些高性价比的技巧

  • 给 img 标签增加 loading="lazy" 属性可以原生实现图片懒加载效果
  • 使用 css content-visibility: hidden; 属性可以优化浏览器渲染性能
    • 例如切换 Tab 后,当前不可见的 Tab 内容就可以用这个属性从而在保持状态的同时让浏览器跳过不必要的渲染
要获得最佳阅读体验,请访问原文 https://baiyun.me/web-performance
❌