Marco Nie - 2020年9月 you are the company you keep... 2020-09-30T11:52:07+08:00 Typecho https://blog.niekun.net/feed/atom/2020/09/ <![CDATA[powershell 脚本实现系统消息提醒]]> https://blog.niekun.net/archives/1782.html 2020-09-30T11:52:07+08:00 2020-09-30T11:52:07+08:00 admin https://niekun.net https://mcpmag.com/articles/2017/09/07/creating-a-balloon-tip-notification-using-powershell.aspx

]]>
<![CDATA[echo 命令的使用]]> https://blog.niekun.net/archives/1781.html 2020-09-30T09:23:00+08:00 2020-09-30T09:23:00+08:00 admin https://niekun.net echo 是常用的终端命令,常用来输出字符串。配合命令的 option 选项可以实现多种输出方式。

语法结构:

echo [option(s)] [string(s)]

常规使用

输出字符串:

$ echo i have a dream
i have a dream

输出变量:

$ x=10
$ echo $x
10

输出目录下所有文件名:

$ echo *
Business Card Template - PiXimperfect.psd Telegram Desktop Video desktop.ini echo.txt test1.txt test2.txt

输出特定名称的文件名:

$ echo *.txt
echo.txt test1.txt test2.txt

将字符串输出到文件:

$ echo 'abc' > test.txt
$ cat test.txt
abc

使用 -e 选项识别字符串内的转义符 \

\b 表示清除字符间空格

$ echo -e 'a \bb \bc'
abc

\n 表示换行

$ echo -e 'a\nb\nc'
a
b
c

\t 表示添加制表符

$ echo -e 'a\tb\tc'
a       b       c

可以结合 \n \t 使用:

$ echo -e '\ta\n\tb\n\tc'
        a
        b
        c

\v 表示添加垂直方向制表符

$ echo -e 'a\vb\vc'
a
 b
  c

\r 表示丢弃前面的字符串

$ echo -e 'a\rbc'
bc

\c 表示丢弃后面的字符串

$ echo -e 'a\cbc'
aroot@localhost$

以上就是 echo 命令的简单使用方法。

]]>
<![CDATA[powershell 脚本运行问题处理]]> https://blog.niekun.net/archives/1777.html 2020-09-28T12:53:00+08:00 2020-09-28T12:53:00+08:00 admin https://niekun.net 最近在学习 powershell 脚本中发现如果当前目录不在 ps1 脚本所在目录的话脚本不能正常执行,研究了下如何解决。

PS C:\Users\Marco Nie> "C:\Users\Marco Nie\Application\aria2\download-complete.ps1"
C:\Users\Marco Nie\Application\aria2\download-complete.ps1

如以上,我想执行脚本,但是只是返回了路径。

解决方法是在脚本路径前加上程序调用符 & ,表示后面的是可执行文件。

 &"C:\Users\Marco Nie\Application\aria2\download-complete.ps1"

以上方式就可以正常执行脚本了。

还有就是脚本路径上如果有空格的话,需要使用引号将路径包围起来,这样就可以了。

参考链接:
http://www.splaybow.com/post/powershell-run-command-with-space.html

]]>
<![CDATA[macOS 下命令行控制显示系统通知]]> https://blog.niekun.net/archives/1773.html 2020-09-26T21:42:00+08:00 2020-09-26T21:42:00+08:00 admin https://niekun.net 在使用 aira2 的 rpc 下载功能时,默认没有下载完成提示,所以需要手动打开 webui 进行查看。

关于 aria2 的使用参考我的教程:https://blog.niekun.net/archives/1199.html

注意到配置文件里有一个:on-download-complete 选项,可以在下载完成后执行脚本,具体解释参考官方网页

可以利用这一点,编写一个脚本来触发系统通知,这样就可以知道文件下载完成了。

on-download-complete

在 aria2 下载完成后,执行 on-download-complete 脚本时会自动传递三个参数:

  • GID GID is an ID of a download which aria2c uses to identify a particular download
  • 下载文件个数
  • 文件路径

示例:

$ cat hook.sh
#!/bin/sh
echo "Called with [$1] [$2] [$3]"
$ aria2c --on-download-complete hook.sh http://example.org/file.iso
Called with [1] [1] [/path/to/file.iso]

osascript

这里使用 AppleScript 来编写简单的系统通知,使用 osascript 可以在 terminal 终端执行 AppleScript 脚本。

语法结构:

osascript [-l language] [-i] [-s flags] [-e statement | programfile] [argument ...]

这里主要使用 -e选项 来执行 AppleScript 脚本。

显示通知

显示一个简单的通知很简单:

osascript -e 'display notification "hello world!"'

引号内的就是纯 AppleScript 脚本,很简单。

显示带标题的通知

osascript -e 'display notification "hello world!" with title "This is the title"'

显示带主副标题的通知

osascript -e 'display notification "hello world!" with title "Greeting" subtitle "More text"'

显示带声音提醒的通知

系统内置的声音音频在:/System/Library/Sounds 目录:

$ ls /System/Library/Sounds
Basso.aiff    Frog.aiff    Hero.aiff    Pop.aiff    Submarine.aiff
Blow.aiff    Funk.aiff    Morse.aiff    Purr.aiff    Tink.aiff
Bottle.aiff    Glass.aiff    Ping.aiff    Sosumi.aif

Pop.aiff 这个声音就是默认的提示音。

osascript -e 'display notification "hello world!" with title "Greeting" subtitle "More text" sound name "Pop.aiff"'

显示需要点击确认的提示框

osascript -e 'display alert "Hello World!" message "longer text can be added in the message field."'

音频提示

除了显示系统通知外,也可以用音频提示,使用 say 命令来完成。

osascript -e 'say "Hello World!"'

提示信息将不显示而是语音播报出来。

aria2 下载完成提示脚本

download-complete.sh

#!/bin/sh
osascript -e 'display notification "download complete" sound name "Pop.aiff"'

参考连接:
https://code-maven.com/display-notification-from-the-mac-command-line

]]>
<![CDATA[Failed to parse PID from file /opt/nginx/logs/nginx.pid 处理]]> https://blog.niekun.net/archives/1763.html 2020-09-22T16:27:00+08:00 2020-09-22T16:27:00+08:00 admin https://niekun.net 今天在使用 journalctl 查看 nginx 日志时看到在每次启动服务后会出现一条错误信息:

$ journalctl -u nginx
...
nginx.service: Failed to parse PID from file /opt/nginx/logs/nginx.pid: Invalid argument
...

查找了下原因,可能是 nginx 在启动时创建 nginx.pid 文件前 systemd 就在请求这个文件,所以出错了。

解决办法就是题前手动创建 systemd 需要的文件:

mkdir /etc/systemd/system/nginx.service.d
printf "[Service]\nExecStartPost=/bin/sleep 0.1\n" > /etc/systemd/system/nginx.service.d/override.conf
systemctl daemon-reload

以上处理就可以解决问题。

参考链接:
https://bugs.launchpad.net/ubuntu/+source/nginx/+bug/1581864

]]>
<![CDATA[从源码编译安装 python]]> https://blog.niekun.net/archives/1758.html 2020-09-21T16:35:00+08:00 2020-09-21T16:35:00+08:00 admin https://niekun.net 从源码编译程序的好处是可以使用最新版本,下面介绍如何在 Linux 下编译安装 python 和 pip 环境。

下载源码包

python 官网:https://www.python.org/

当前最新版是 3.8.5,在这个页面找到地址:https://www.python.org/downloads/release/python-385/

1.jpg

下载 tgz 压缩包到本地并解压:

cd /tmp
wget https://www.python.org/ftp/python/3.8.5/Python-3.8.5.tgz
tar xvf Python-3.8.5.tgz

环境安装

编译需要安装一些依赖:

apt install libffi-dev libgdbm-dev libsqlite3-dev libssl-dev zlib1g-dev

编译

python 源码使用标准 GNU 编译系统,详细说明参考:https://blog.niekun.net/archives/883.html

将 python 安装到 /opt 目录,先创建文件夹:

mkdir /opt/python3.8.5

然后配置 configure:

cd /tmp/Python-3.8.5

./configure \
--prefix=/opt/python3.8.5 \
--enable-optimizations \

没有错误提示的话就开始编译和安装:

make
make install

安装完成后测试执行:

/opt/python3.8.5/bin/python3 --version

返回版本信息则安装完成。

下面将可执行文件加入系统路径,创建软连接:

ln -s /opt/python3.8.5/bin/python3 /usr/bin/python

测试运行:

python --version

安装 pip

源码编译安装的 python 不自带 pip,需要自己安装,可以使用 get-pip.py 脚本来安装。

官网:https://pip.pypa.io/en/stable/installing/

下载脚本到本地:

curl https://bootstrap.pypa.io/get-pip.py -o get-pip.py

使用刚才安装的 python 执行脚本:

/opt/python3.8.5/bin/python3 get-pip.py

pip 的安装路径是 /opt/python3.8.5/bin/,测试命令:

/opt/python3.8.5/bin/pip3 --version

返回版本信息则安装完成。

添加软连接到系统路径:

ln -s /opt/python3.8.5/bin/pip3 /usr/bin/pip

测试命令:

pip --version

参考链接

https://docs.rstudio.com/resources/install-python-source/

]]>
<![CDATA[awk 命令的用法]]> https://blog.niekun.net/archives/1757.html 2020-09-18T16:20:00+08:00 2020-09-18T16:20:00+08:00 admin https://niekun.net awk 是常用的 Linux 文本操作命令和脚本语言。用来按行提取和处理文本内容,也可以执行简单的逻辑处理。

比如我们有一个 txt 文件:

ab.c 123 e.rt 456
oh.g 324 b.na 756
si.d 156 o.ui 452

执行命令:

$ awk '{print $1}' test.txt
ab.c
oh.g
si.d

可以看到返回结果为每一行的第一个字符串。默认以空格作为分隔符。

$1 为每行第一个字符串,$2 为每行第二个字符串,以此类推。$0 为整个文本。

可以同时输出多个内容:

$ awk '{print $1, $2}' test.txt
ab.c 123
oh.g 324
si.d 156

内部集成的参数

FS 区域分割符

awk 默认使用空格来分割字符串,也可以自己定义分割符:

$ awk 'FS = "." {print $1, $2}' test.txt
ab c 123 e
oh g 324 b
si d 156 o

这时候,abc 123 e 分别是一个整体。

还有一种写法使用 -F 表示,要写在引号外部:

$ awk -F. '{print $1, $2}' test.txt

NF 每行字符串个数

$NF 来表示每行最后一个串:

$ awk '{print $NF}' test.txt
456
756
452

用 NF 来判断每行字符串格个数:只输出有 4 个字符串的所在行的内容

$ awk 'NF == 4 {print $1, $2}' test.txt
ab.c 123
oh.g 324
si.d 156

NR 当前行号

NR 记录当前行的行号:

$ awk '{print NR}' test.txt
1
2
3
$ awk '{print NR, $0}' test.txt
1 ab.c 123 e.rt 456
2 oh.g 324 b.na 756
3 si.d 156 o.ui 452

OFS 定义输出串分割符

字符串输出是可以自定义分割符号:

$ awk 'OFS="/" {print $1, $2}' test.txt
ab.c/123
oh.g/324
si.d/156

BEGIN 和 END 规则

BEGIN 规则是在 awk 读取输入文本前执行的指令,END 规则是在 awk 输出完字符串后执行的指令。

$ awk 'BEGIN {print "begin process"} {print $0} END {print "end process"}' test.txt
begin process
ab.c 123 e.rt 456
oh.g 324 b.na 756
si.d 156 o.ui 452
end process

判断模块

可以使用常用的判断来过滤输出结果:

设置第4个字符串数字需要大于等于500:

$ awk '$4 >=500 {print $0}' test.txt
oh.g 324 b.na 756

设置行内必须包含字符串 ab:

$ awk '/ab/ {print $0}' test.txt
ab.c 123 e.rt 456

设置行开始必须包含字符串 ab:

$ awk '/^ab/ {print $0}' test.txt
ab.c 123 e.rt 456

awk script 脚本

如果命令很复杂,可以建立一个脚本来单独执行。

建立文件:test.awk

#!/usr/bin/awk -f

BEGIN {
  # set the input and output field separators
  FS=":"
  OFS=":"
  # zero the accounts counter
  accounts=0
}
{
  # set field 2 to nothing
  $2=""
  # print the entire line
  print $0
  # count another account
  accounts++
}
END {
  # print the results
  print accounts " accounts.\n"
}

可执行权限:

chmod +x test.awk

执行:

./test.awk /etc/passwd

windows

windows 下也可以使用 awk,下载安装 Gawk 即可。

官网:http://gnuwin32.sourceforge.net/packages/gawk.htm

下载对应安装包,安装后将 awk 可执行文件的路径加入系统 PATH 即可使用,参考:https://blog.niekun.net/archives/413.html

参考链接

https://www.howtogeek.com/562941/how-to-use-the-awk-command-on-linux/
http://linuxcommand.org/lc3_adv_awk.php

]]>
<![CDATA[${1%str} 在 shell 脚本的用法]]> https://blog.niekun.net/archives/1754.html 2020-09-18T15:25:00+08:00 2020-09-18T15:25:00+08:00 admin https://niekun.net 执行 shell 脚本时经常会有传入参数,如:

./test.sh abcdef abc.bbb

以上的命令使用了两个传入参数,abcdef,abc.bbb

在脚本里使用时,$1 就表示第一个参数,$2 就表示第二个参数:

var1 = $1
var2 = $2

在脚本中有一种用法,如:${1%def}jjj
他的意思就是将 $1 最后的字符 def 替换为 jjj

newstr1 = ${1%def}jjj

newstr1 的值就是 abcjjj

newstr2 = ${2%.bbb}.ccc

newstr2 的值就是 abc.ccc

]]>
<![CDATA[使用 subprocess.check_output 执行cmd命令并返回结果到字符串]]> https://blog.niekun.net/archives/1753.html 2020-09-18T15:13:51+08:00 2020-09-18T15:13:51+08:00 admin https://niekun.net 语法:

subprocess.check_output(args, *, stdin=None, stderr=None, shell=False, universal_newlines=False)

执行cmd命令并返回结果到字符串。

用法:

import subprocess

output = check_output(["cat", "/etc/hostname"]).strip()
print(output)

以上脚本会执行 cat /etc/hostname 命令然后将结果赋值给 output 变量。
strip() 可以将 string 的前后空格去掉。

]]>
<![CDATA[FFmpeg 简单用法]]> https://blog.niekun.net/archives/1752.html 2020-09-18T15:03:12+08:00 2020-09-18T15:03:12+08:00 admin https://niekun.net FFmpeg 是视频处理最常用的开源软件。

它功能强大,用途广泛,大量用于视频网站和商业软件(比如 Youtube 和 iTunes),也是许多音频和视频格式的标准编码/解码实现。

官方网站:https://www.ffmpeg.org/

安装

最简单的方法就是用包管理工具如:apt 安装:

apt update
apt install ffmpeg

或者也可以从源码安装,可以参考我之前的教程:https://blog.niekun.net/archives/891.html

常用指令

查看 ffmpeg 版本:

ffmpeg -version

查看支持的编码格式:如 h.264, h.265

ffmpeg -codecs

查看支持的容器:如 mp4, mp3, mkv

ffmpeg -formats

查看已安装的编码器:如 libx264, libx265, libvpx, aac

ffmpeg -encoders

使用格式

FFmpeg 的命令行参数非常多,可以分成五个部分。

ffmpeg {1} {2} -i {3} {4} {5}

上面命令中,五个部分的参数依次如下:

全局参数
输入文件参数
输入文件
输出文件参数
输出文件

参数太多的时候,为了便于查看,ffmpeg 命令可以写成多行:

$ ffmpeg \
[全局参数] \
[输入文件参数] \
-i [输入文件] \
[输出文件参数] \
[输出文件]

下面是一个例子:

ffmpeg \
-y \ # 全局参数
-c:a libfdk_aac -c:v libx264 \ # 输入文件参数
-i input.mp4 \ # 输入文件
-c:v libvpx-vp9 -c:a libvorbis \ # 输出文件参数
output.webm # 输出文件

上面的命令将 mp4 文件转成 webm 文件,这两个都是容器格式。输入的 mp4 文件的音频编码格式是 aac,视频编码格式是 H.264;输出的 webm 文件的视频编码格式是 VP9,音频格式是 Vorbis。

如果不指明编码格式,FFmpeg 会自己判断输入文件的编码。一般可以省略输入文件参数。

常用命令参数

-c:指定编码器
-c copy:直接复制,不经过重新编码(这样比较快)
-c:v:指定视频编码器
-c:a:指定音频编码器
-i:指定输入文件
-an:去除音频流
-vn: 去除视频流
-preset:指定输出的视频质量,会影响文件的生成速度,有以下几个可用的值 ultrafast, superfast, veryfast, faster, fast, medium, slow, slower, veryslow。
-y:不经过确认,输出时直接覆盖同名文件。

常规使用方法

查看元数据信息,如时长,比特率等:

ffmpeg -i test.mp4

输出的信息较多,可以通过 -hide_banner 只显示媒体文件信息:

ffmpeg -i test.mp4 -hide_banner

转码,如 avi to h.264:

ffmpeg -i test.avi -c:v libx264 test.mp4

转换容器:

ffmpeg -i test.mp4 -c copy test.webm

转换容器不需要转码,所以直接 copy 即可。

转换码率,转换成固定码率:

ffmpeg -i test.mp4 -b:v 500k test_out.mp4

转换码率,转换成一个码率范围:

ffmpeg -i test.mp4 -minrate 964K -maxrate 3856K -bufsize 2000K test_out.mp4

改变分辨率:转换成 480p

ffmpeg \
-i input.mp4 \
-vf scale=480:-1 \
output.mp4

视频中提取音频:

ffmpeg \
-i input.mp4 \
-vn -c:a copy \
output.aac

上面例子中,-vn 表示去掉视频,-c:a copy 表示不改变音频编码,直接拷贝。

视频截图:下面的例子是从指定时间开始,连续对1秒钟的视频进行截图

ffmpeg \
-y \
-i input.mp4 \
-ss 00:01:24 -t 00:00:01 \
output_%3d.jpg

%3d 在 shell 里表示至少输出3个字符空间的数字:

% means "Print a variable here"
3 means "use at least 3 spaces to display, padding as needed"
d means "The variable will be an integer"

如果只需要截一张图,可以指定只截取一帧。

$ ffmpeg \
-ss 01:23:45 \
-i input \
-vframes 1 -q:v 2 \
output.jpg

上面例子中,-vframes 1 指定只截取一帧,-q:v 2 表示输出的图片质量,一般是1到5之间(1 为质量最高)。

裁剪:
裁剪(cutting)指的是,截取原始视频里面的一个片段,输出为一个新视频。可以指定开始时间(start)和持续时间(duration),也可以指定结束时间(end)。

$ ffmpeg -ss [start] -i [input] -t [duration] -c copy [output]
$ ffmpeg -ss [start] -i [input] -to [end] -c copy [output]

下面是实际的例子。

# 从1分50秒开始截取10.5秒
ffmpeg -ss 00:01:50 -i test.mp4 -t 10.5 -c copy out.mp4

# 从25秒开始截取10秒
ffmpeg -ss 25 -i test.mp4 -to 10 -c copy out.mp4
ffmpeg -i test.mp4 -ss 25 -to 10 -c copy out.mp4

上面例子中,-c copy 表示不改变音频和视频的编码格式,直接拷贝,这样会快很多。

高级用法

压缩视频内容到指定容量大小

使用的技术主要是 ffmpeg 的 2 pass 方法和 ffprobe 得到码率和时长信息。

bash脚本:

#!/bin/bash

target_video_size_MB="$2"
origin_duration_s=$(ffprobe -v error -show_streams -select_streams a "$1" | grep -Po "(?<=^duration\=)\d*\.\d*")
origin_audio_bitrate_kbit_s=$(ffprobe -v error -pretty -show_streams -select_streams a "$1" | grep -Po "(?<=^bit_rate\=)\d*\.\d*")
target_audio_bitrate_kbit_s=$origin_audio_bitrate_kbit_s # TODO for now, make audio bitrate the same
target_video_bitrate_kbit_s=$(\
    awk \
    -v size="$target_video_size_MB" \
    -v duration="$origin_duration_s" \
    -v audio_rate="$target_audio_bitrate_kbit_s" \
    'BEGIN { print  ( ( size * 8192.0 ) / ( 1.048576 * duration ) - audio_rate ) }')

ffmpeg \
    -y \
    -i "$1" \
    -c:v libx264 \
    -b:v "$target_video_bitrate_kbit_s"k \
    -pass 1 \
    -an \
    -f mp4 \
    /dev/null \
&& \
ffmpeg \
    -i "$1" \
    -c:v libx264 \
    -b:v "$target_video_bitrate_kbit_s"k \
    -pass 2 \
    -c:a aac \
    -b:a "$target_audio_bitrate_kbit_s"k \
    "${1%.*}-$2mB.mp4"

使用方法:压缩视频到 50 MB 大小

./script.sh test.mp4 50

切割视频到指定时长的多个视频

使用的技术主要是 python,ffprobe 得到视频时长,然后计算需要切割为几个视频。
python 脚本:

#!/usr/bin/env python

import csv
import subprocess
import math
import json
import os
import shlex
from optparse import OptionParser


def split_by_manifest(filename, manifest, vcodec="copy", acodec="copy",
                      extra="", **kwargs):

    if not os.path.exists(manifest):
        print("File does not exist: %s" % manifest)
        raise SystemExit

    with open(manifest) as manifest_file:
        manifest_type = manifest.split(".")[-1]
        if manifest_type == "json":
            config = json.load(manifest_file)
        elif manifest_type == "csv":
            config = csv.DictReader(manifest_file)
        else:
            print("Format not supported. File must be a csv or json file")
            raise SystemExit

        split_cmd = ["ffmpeg", "-i", filename, "-vcodec", vcodec,
                     "-acodec", acodec, "-y"] + shlex.split(extra)
        try:
            fileext = filename.split(".")[-1]
        except IndexError as e:
            raise IndexError("No . in filename. Error: " + str(e))
        for video_config in config:
            split_str = ""
            split_args = []
            try:
                split_start = video_config["start_time"]
                split_length = video_config.get("end_time", None)
                if not split_length:
                    split_length = video_config["length"]
                filebase = video_config["rename_to"]
                if fileext in filebase:
                    filebase = ".".join(filebase.split(".")[:-1])

                split_args += ["-ss", str(split_start), "-t",
                               str(split_length), filebase + "." + fileext]
                print("########################################################")
                print("About to run: "+" ".join(split_cmd+split_args))
                print("########################################################")
                subprocess.check_output(split_cmd+split_args)
            except KeyError as e:
                print("############# Incorrect format ##############")
                if manifest_type == "json":
                    print("The format of each json array should be:")
                    print("{start_time: <int>, length: <int>, rename_to: <string>}")
                elif manifest_type == "csv":
                    print("start_time,length,rename_to should be the first line ")
                    print("in the csv file.")
                print("#############################################")
                print(e)
                raise SystemExit


def get_video_length(filename):

    output = subprocess.check_output(("ffprobe", "-v", "error", "-show_entries",
                                      "format=duration", "-of", "default=noprint_wrappers=1:nokey=1", filename)).strip()
    video_length = int(float(output))
    print("Video length in seconds: "+str(video_length))

    return video_length


def ceildiv(a, b):
    return int(math.ceil(a / float(b)))


def split_by_seconds(filename, split_length, vcodec="copy", acodec="copy",
                     extra="", video_length=None, **kwargs):
    if split_length and split_length <= 0:
        print("Split length can't be 0")
        raise SystemExit

    if not video_length:
        video_length = get_video_length(filename)
    split_count = ceildiv(video_length, split_length)
    if(split_count == 1):
        print("Video length is less then the target split length.")
        raise SystemExit

    split_cmd = ["ffmpeg", "-i", filename, "-vcodec",
                 vcodec, "-acodec", acodec] + shlex.split(extra)
    try:
        filebase = ".".join(filename.split(".")[:-1])
        fileext = filename.split(".")[-1]
    except IndexError as e:
        raise IndexError("No . in filename. Error: " + str(e))
    for n in range(0, split_count):
        split_args = []
        if n == 0:
            split_start = 0
        else:
            split_start = split_length * n

        split_args += ["-ss", str(split_start), "-t", str(split_length),
                       filebase + "-" + str(n+1) + "-of-" +
                       str(split_count) + "." + fileext]
        print("About to run: "+" ".join(split_cmd+split_args))
        subprocess.check_output(split_cmd+split_args)


def main():
    parser = OptionParser()

    parser.add_option("-f", "--file",
                      dest="filename",
                      help="File to split, for example sample.avi",
                      type="string",
                      action="store"
                      )
    parser.add_option("-s", "--split-size",
                      dest="split_length",
                      help="Split or chunk size in seconds, for example 10",
                      type="int",
                      action="store"
                      )
    parser.add_option("-c", "--split-chunks",
                      dest="split_chunks",
                      help="Number of chunks to split to",
                      type="int",
                      action="store"
                      )
    parser.add_option("-S", "--split-filesize",
                      dest="split_filesize",
                      help="Split or chunk size in bytes (approximate)",
                      type="int",
                      action="store"
                      )
    parser.add_option("--filesize-factor",
                      dest="filesize_factor",
                      help="with --split-filesize, use this factor in time to"
                      " size heuristics [default: %default]",
                      type="float",
                      action="store",
                      default=0.95
                      )
    parser.add_option("--chunk-strategy",
                      dest="chunk_strategy",
                      help="with --split-filesize, allocate chunks according to"
                      " given strategy (eager or even)",
                      type="choice",
                      action="store",
                      choices=['eager', 'even'],
                      default='eager'
                      )
    parser.add_option("-m", "--manifest",
                      dest="manifest",
                      help="Split video based on a json manifest file. ",
                      type="string",
                      action="store"
                      )
    parser.add_option("-v", "--vcodec",
                      dest="vcodec",
                      help="Video codec to use. ",
                      type="string",
                      default="copy",
                      action="store"
                      )
    parser.add_option("-a", "--acodec",
                      dest="acodec",
                      help="Audio codec to use. ",
                      type="string",
                      default="copy",
                      action="store"
                      )
    parser.add_option("-e", "--extra",
                      dest="extra",
                      help="Extra options for ffmpeg, e.g. '-e -threads 8'. ",
                      type="string",
                      default="",
                      action="store"
                      )
    (options, args) = parser.parse_args()

    def bailout():
        parser.print_help()
        raise SystemExit

    if not options.filename:
        bailout()

    if options.manifest:
        split_by_manifest(**(options.__dict__))
    else:
        video_length = None
        if not options.split_length:
            video_length = get_video_length(options.filename)
            file_size = os.stat(options.filename).st_size
            split_filesize = None
            if options.split_filesize:
                split_filesize = int(
                    options.split_filesize * options.filesize_factor)
            if split_filesize and options.chunk_strategy == 'even':
                options.split_chunks = ceildiv(file_size, split_filesize)
            if options.split_chunks:
                options.split_length = ceildiv(
                    video_length, options.split_chunks)
            if not options.split_length and split_filesize:
                options.split_length = int(
                    split_filesize / float(file_size) * video_length)
        if not options.split_length:
            bailout()
        split_by_seconds(video_length=video_length, **(options.__dict__))


if __name__ == '__main__':
    main()

使用方法:将视频切割为单个视频100秒

./split.py -f test.mp4 -s 100

ffprobe 使用

ffprobe 可以用来得到视频信息。

视频时长:秒

ffprobe -v error -show_entries format=duration -of default=noprint_wrappers=1:nokey=1 input.mp4

视频码率:bit

ffprobe -v error -show_entries format=bit_rate -of default=noprint_wrappers=1:nokey=1 input.mp4

参考链接

http://www.ruanyifeng.com/blog/2020/01/ffmpeg.html
https://stackoverflow.com/questions/29082422/ffmpeg-video-compression-specific-file-size
https://github.com/c0decracker/video-splitter
https://trac.ffmpeg.org/wiki/FFprobeTips

]]>