#998 使用 ffmpeg 从视频中提取与合并音频信号

2024-11-28
# 提取音频
ffmpeg -i 夏洛的网_中文.mp4 -q:a 0 -map a 夏洛的网_中文.mp3

# 合并音频
ffmpeg -i 夏洛的网_英语.mp4 -i 夏洛的网_中文.mp3 -map 0:v -map 1:a -map 0:a -c:v copy -c:a copy 夏洛的网.mp4

# 给不同音轨取个名字
ffmpeg -i 夏洛的网2.mp4 -map 0 -c copy -metadata:s:a:0 title="中文" -metadata:s:a:1 title="English" 夏洛的网3.mp4

# 添加字幕(总是报错,没有成功)
ffmpeg -i 夏洛的网3.mp4 -i 夏洛的网_中文.srt -i 夏洛的网_英文.srt -map 0 -map 1 -map 2 -c:v copy -c:a copy -c:s srt \
-metadata:s:s:0 language=chi -metadata:s:s:0 title="中文" \
-metadata:s:s:1 language=eng -metadata:s:s:1 title="English" \
夏洛的网4.mp4

虽然字幕没有成功,但是播放器会自动加载,也还好。
PS:字幕其实也是播放器自动在线匹配,然后我选择保存在本地的。

#996 什么是“企业 Linux”

2024-09-17

我看了阮一峰推荐的内容中有篇文章讲什么是“企业 Linux”(后面会附原文和中文翻译)。
RHEL 名字叫红帽企业 Linux,SLE 也是 SUSE Linux Enterprise 的缩写,我也一直好奇,这个“企业”到底是什么意思?

#995 Mac 目录结构

2024-09-16
-> % tree -dL2 /
/
├── Applications
│   ├── Another Redis Desktop Manager.app
│   ├── Apifox.app
│   ├── Beyond Compare.app
│   ├── CopyQ.app
│   ├── DBeaver.app
│   ├── DingTalk.app
│   ├── GitButler.app
│   ├── Install macOS Monterey.app
│   ├── Microsoft Edge.app
│   ├── Nutstore.app
│   ├── OpenVPN Connect
│   ├── OpenVPN Connect.app -> /Applications/OpenVPN Connect/OpenVPN Connect.app
│   ├── Safari.app -> ../System/Cryptexes/App/System/Applications/Safari.app
│   ├── Utilities
│   ├── WeChat.app
│   ├── Wireshark.app
│   ├── iTerm.app
│   ├── wpsoffice.app
│   ├── 优酷.app
│   └── 企业微信.app
├── Library
│   ├── Apple
│   ├── Application Support
│   ├── Audio
│   ├── Bluetooth
│   ├── Bundles
│   ├── Caches
│   ├── Catacomb
│   ├── ColorPickers
│   ├── ColorSync
│   ├── Components
│   ├── Compositions
│   ├── Contextual Menu Items
│   ├── CoreAnalytics
│   ├── CoreMediaIO
│   ├── Developer
│   ├── DirectoryServices
│   ├── Documentation
│   ├── DriverExtensions
│   ├── Extensions
│   ├── Filesystems
│   ├── Fonts
│   ├── Frameworks
│   ├── GPUBundles
│   ├── Google
│   ├── Graphics
│   ├── Image Capture
│   ├── Input Methods
│   ├── InstallerSandboxes
│   ├── Internet Plug-Ins
│   ├── Java
│   ├── KernelCollections
│   ├── Keyboard Layouts
│   ├── Keychains
│   ├── LaunchAgents
│   ├── LaunchDaemons
│   ├── Logs
│   ├── Modem Scripts
│   ├── OSAnalytics
│   ├── OpenDirectory
│   ├── Perl
│   ├── PreferencePanes
│   ├── Preferences
│   ├── Printers
│   ├── PrivilegedHelperTools
│   ├── QuickLook
│   ├── Receipts
│   ├── Ruby
│   ├── Sandbox
│   ├── Screen Savers
│   ├── ScriptingAdditions
│   ├── Scripts
│   ├── Security
│   ├── Speech
│   ├── Spotlight
│   ├── StagedDriverExtensions
│   ├── StagedExtensions
│   ├── StartupItems
│   ├── SystemExtensions
│   ├── SystemMigration
│   ├── SystemProfiler
│   ├── Trial
│   ├── Updates
│   ├── User Pictures
│   ├── User Template
│   ├── Video
│   └── WebServer
├── System
│   ├── Applications
│   ├── Cryptexes
│   ├── Developer
│   ├── DriverKit
│   ├── Library
│   ├── Volumes
│   └── iOSSupport
├── Users
│   ├── Guest
│   ├── Shared
│   └── adm
├── Volumes
│   └── 未命名 -> /
├── bin
├── cores
├── dev
│   └── fd
├── etc -> private/etc
├── home -> /System/Volumes/Data/home
├── opt
├── private
│   ├── etc
│   ├── tmp
│   └── var
├── sbin
├── tmp -> private/tmp
├── usr
│   ├── bin
│   ├── lib
│   ├── libexec
│   ├── local
│   ├── sbin
│   ├── share
│   └── standalone
└── var -> private/var

128 directories

Linux 目录结构

/home -> /System/Volumes/Data/home
/usr
/bin
/sbin
/etc  -> private/etc
/var  -> private/var
/opt
/tmp  -> private/tmp
/dev

应用目录

/Applications/
/System/Applications/
/System/Volumes/Data/Applications/

-> % find / -maxdepth 5 -type d -name "*.app" > /tmp/a
-> % awk -F '/' '{path="";for(i=1;i<NF;i++){path=path $i "/"};c[path]++}END{for(i in c){printf "%5d  %s\n",c[i],i}}' /tmp/a | sort -nr 
  107  /System/Library/CoreServices/
   39  /System/Applications/
   19  /System/Applications/Utilities/
   18  /System/Library/Input Methods/
   18  /Applications/
    5  /Users/adm/Downloads/
    4  /Library/Image Capture/Devices/
    2  /System/Library/Services/
    2  /Library/Input Methods/
    2  /Applications/OpenVPN Connect/
    1  /usr/libexec/
    1  /System/Library/Classroom/
    1  /Applications/Nutstore.app/Contents/
-> % type code
code is /usr/local/bin/code
-> % ll /usr/local/bin/code
lrwxr-xr-x@ 1 adm  admin   167B  9 20 21:40 /usr/local/bin/code -> /private/var/folders/lh/vyr5k29j1cgbmd4b_lhpxq340000gn/T/AppTranslocation/F14FB3FE-0B4C-4F60-81FA-7AD83514E78D/d/Visual Studio Code.app/Contents/Resources/app/bin/code
-> % echo $TMPDIR
/var/folders/lh/vyr5k29j1cgbmd4b_lhpxq340000gn/T/

#994 大武汉旅游年卡 - 景区列表

2024-09-15
景点 城市 位置
电影院 武汉 - 光谷里
武昌泛悦奥特莱斯
江夏罗马春天
汉口北
黄鹤楼公园 武汉 武昌区 司门口
黄鹤楼落梅轩 武汉 武昌区 司门口
杜莎夫人蜡像馆 武汉 武昌区 楚河汉街
东湖落雁景区 武汉 东湖 -
东湖飞鸟世界 武汉 东湖 -
东湖游船 武汉 东湖 -
龙泉山 武汉 江夏区 -
武汉植物园 武汉 武昌区 鲁磨路
长春观 武汉 武昌区 大东门
九峰山森林公园 武汉 洪山区 -
禧汤生活馆 武汉 洪山区 珞珈山
武汉园博园(武汉自然博物馆) 武汉 硚口区 -
张公山寨 武汉 青山区 -
木兰天池 武汉 黄陂区 -
木兰草原 武汉 黄陂区 -
木兰云雾山 武汉 黄陂区 -
木兰山 武汉 黄陂区 -
花海乐园 武汉 黄陂区 -
大余湾 武汉 黄陂区 -
锦里沟 武汉 黄陂区 -
清凉寨 武汉 黄陂区 -
姚家山 武汉 黄陂区 -
九真山 武汉 蔡甸区 -
九真桃源 武汉 蔡甸区 -
金龙水寨 武汉 蔡甸区 -
紫微都市田园 武汉 新洲区 -
凤娃古寨 武汉 新洲区 -
花朝河湾 武汉 新洲区 -
香草伊甸园 武汉 新洲区 -
-------------------- - - -
熙凤水乡 孝感 - -
楚珍园 孝感 应城 -
金卉庄园 孝感 孝南区 -
天紫湖 孝感 孝南区 -
盛世闻樱景区 孝感 安陆 -
白兆山风景区 孝感 安陆 -
-------------------- - - -
三里畈温泉 黄冈 罗田县 -
罗田薄刀峰 黄冈 罗田县 -
黄石河峡谷隧道漂流 黄冈 罗田县 -
帝王湖 黄冈 红安县
-------------------- - - -
瑶池温泉 咸宁 咸安区 -
山湖温泉 咸宁 嘉鱼县 -

#993 Golang text/template 的用法

2024-09-09

基础示例

package main

import (
    "fmt"
    "os"
    "strings"
    "text/template"
    "time"
)

const TEMPLTE = `{{ .Subject }}
Time: {{ .Time.Format "2006-01-02 15:04:05" }}
Source: {{ .Source }}

{{ .Body }}`

func main() {
    tmpl01, _ := template.New("tmpl01").Parse("你好,{{ . }}")
    tmpl01.Execute(os.Stdout, "世界")

    fmt.Println()
    fmt.Println(strings.Repeat("=", 80))
    fmt.Println()

    type Message struct {
        Subject string
        Time    time.Time
        Source  string
        Body    string
    }

    // http://www.xinhuanet.com/politics/2020-02/08/c_1125546135.htm
    subject := "国家监察委员会调查组已抵达武汉"
    timeobj, _ := time.Parse("2006-01-02 15:04:05", "2020-02-08 13:49:38")
    source := "新华社“新华视点”微博"
    content := "中央纪委国家监委网站8日消息,国家监察委员会调查组已抵达武汉。经中央批准,国家监察委员会派出调查组赴湖北省武汉市,就群众反映的涉及李文亮医生的有关问题作全面调查。"

    tmpl02, _ := template.New("tmpl02").Parse(TEMPLTE)
    tmpl02.Execute(os.Stdout, Message{subject, timeobj, source, content})
}

语法说明

  1. 双花括号
{{ . }}  // 输出当前变量
{{ .Name }} // 输出当前变量的 Name 字段

{{/* 注释 */}}
{{- /* 注释(去掉前后空格与换行) */ -}}

{{ if ... }} A {{ end }}
{{ if ... }} A {{ else }} B {{ end }}
{{ if ... }} A {{ else if ... }} B {{ else }} C {{ end }}

{{ range ... }} A {{ end }}
{{ range ... }} A {{ else }} B {{ end }}
{{ range . -}} {{ . }} {{ end -}}
{{ range $key, $val := . -}} ... {{ end -}}

#991 Extensible Log Format

2024-08-25

https://en.wikipedia.org/wiki/Extended_Log_Format

相比 Common Log Format (通用日志格式),ELF 是名副其实的可拓展:在头部声明了版本,以及字段。
例如:

#Version: 1.0
#Date: 12-Jan-1996 00:00:00
#Fields: time cs-method cs-uri
00:34:23 GET /foo/bar.html
12:21:16 GET /foo/bar.html
12:45:52 GET /foo/bar.html
12:57:34 GET /foo/bar.html
  • Version: <integer>.<integer>
    The version of the extended log file format used. This draft defines version 1.0.
  • Fields: [<specifier>...]
    Specifies the fields recorded in the log.
  • Software: string
    Identifies the software which generated the log.
  • Start-Date: <date> <time>
    The date and time at which the log was started.
  • End-Date: <date> <time>
    The date and time at which the log was finished.
  • Date: <date> <time>
    The date and time at which the entry was added.
  • Remark: <text>
    Comment information. Data recorded in this field should be ignored by analysis tools.

然后这个字段的声明又有一套规则,可以参考

#990 知识分享

2024-08-23

隐性知识是危险的

隐性知识又称为"部落知识",指的是有些知识没有文档,只掌握在团队成员的头脑里面。

如果你想掌握这些知识,只有去询问团队成员。

隐形知识的优点是,省去了文档成本,而且询问相关成员比自己阅读文档更快,当然前提是那位成员能够快速响应。

隐形知识的缺点是,一旦团队扩大规模,它就会失败。对于掌握知识的团队成员来说,回答问题所占用的时间是一个拖累,影响了生产力,也拖慢了团队的开发速度。

另一方面,随着团队规模的扩大和知识变得更加分散,你自己阅读文档和观看视频讲座,会比向他人寻求帮助更快速和方便。

所以,团队越是大,就越要避免"隐形知识",所有知识尽量文档化,让团队成员能够方便地查阅。

团队知识分享是一个很重要的事情。

  1. 周会分享
  2. 专题分享/讨论会
  3. 文档维护制度

#989 Common Log Format (通用日志格式)

2024-08-12
  1. NCSA HTTPd (Apache HTTP Server 前身) 定义的一个标准 Web 服务器日志格式。
  2. 格式:host ident authuser date request status bytes
    例如:127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
  3. 如果哪一个字段没有值,就用 - 代替。
import re
from datetime import datetime

RE_CLF = re.compile(r'(\S+) (\S+) (\S+) \[(.*?)\] "(.*?)" (\d{3}) (\d+|-)')

def parse_clf(log_line):
    match = RE_CLF.match(log_line)
    if not match:
        raise ValueError('Log line does not match CLF format')

    ip_address = match.group(1)
    identity = match.group(2)
    user = match.group(3)
    time_str = match.group(4)
    request_line = match.group(5)
    status_code = int(match.group(6))
    size = match.group(7)

    time_format = '%d/%b/%Y:%H:%M:%S %z'
    timestamp = datetime.strptime(time_str, time_format)

    size = int(size) if size != '-' else None

    return {
        'host': ip_address,
        'ident': identity,
        'authuser': user,
        'date': timestamp,
        'request': request_line,
        'status': status_code,
        'bytes': size,
    }

log_example = '127.0.0.1 user-identifier frank [10/Oct/2000:13:55:36 -0700] 'GET /apache_pb.gif HTTP/1.0' 200 2326'
parsed_log = parse_clf(log_example)
print(parsed_log)
# {'host': '127.0.0.1', 'ident': 'user-identifier', 'authuser': 'frank',
#  'date': datetime.datetime(2000, 10, 10, 13, 55, 36, tzinfo=datetime.timezone(datetime.timedelta(days=-1, seconds=61200))),
#  'request': 'GET /apache_pb.gif HTTP/1.0', 'status': 200, 'bytes': 2326}