修复歌词原文和译文交错排列时会出现不对应的问题
This commit is contained in:
parent
452db2f7d2
commit
f92ddb245b
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -87,6 +87,6 @@ fabric.properties
|
|||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
*.bat
|
||||
/example/*.srt
|
||||
/example/*.lrc
|
||||
/temp
|
||||
|
|
3
build.bat
Normal file
3
build.bat
Normal file
|
@ -0,0 +1,3 @@
|
|||
echo off
|
||||
@REM forceposix 表示在windows上参数也为linux风格,即以“-”开头
|
||||
go build -tags="forceposix" -ldflags "-s -w" -o lts.exe .
|
16
example/example.txt
Normal file
16
example/example.txt
Normal file
|
@ -0,0 +1,16 @@
|
|||
encoding=utf-8
|
||||
|
||||
example#1 从网易云上下载歌词
|
||||
lts -i 1903635166 传说的世界.srt
|
||||
|
||||
example#2 从qq音乐上下载歌词
|
||||
lts -i 003eKeNV0t8IVi -s qq "bad guy.srt"
|
||||
|
||||
example#3 从网易云上下载歌词,不解析
|
||||
lts -i 1903635166 -d 传说的世界.lrc
|
||||
|
||||
example#4 解析已有的lrc文件
|
||||
lts -I 传说的世界.lrc 传说的世界2.srt
|
||||
|
||||
example#5 设置mode为2,原文在上,译文在下
|
||||
lts -i 003eKeNV0t8IVi -s qq -m 2 "bad guy2.srt"
|
66
lts.go
66
lts.go
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"os"
|
||||
"regexp"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
|
@ -44,7 +45,7 @@ type Option struct {
|
|||
|
||||
const (
|
||||
// VERSION 当前版本
|
||||
VERSION = `"0.1.0" (build 2022.02.18)`
|
||||
VERSION = `"0.1.1" (build 2022.03.05)`
|
||||
)
|
||||
|
||||
var (
|
||||
|
@ -68,7 +69,7 @@ func main() {
|
|||
opt.Output = args[0]
|
||||
}
|
||||
|
||||
//获取歌词
|
||||
//获取歌词,lyric为原文歌词,tranLyric为译文歌词
|
||||
var lyric, tranLyric string
|
||||
if opt.Id != "" {
|
||||
if opt.Source != "163" {
|
||||
|
@ -107,7 +108,7 @@ func main() {
|
|||
fmt.Println("Error: 请指定需要转换的歌词。")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//原文和译文作为两条歌词流信息分开保存,但最终生成的srt文件会同时包含两个信息
|
||||
lyricSRT, tranLyricSRT := Lrc2Srt(lyric), Lrc2Srt(tranLyric)
|
||||
SaveSRT(lyricSRT, tranLyricSRT, opt.Output)
|
||||
}
|
||||
|
@ -117,6 +118,8 @@ func SaveSRT(srt *SRT, tranSrt *SRT, name string) {
|
|||
if tranSrt == nil {
|
||||
//没有译文时,用一个空的对象的代替,减少nil判断
|
||||
tranSrt = &SRT{Content: make([]*SRTContent, 0)}
|
||||
//因为没有译文,所以mode选项无效,设为2之后,后面不用做多余判断
|
||||
opt.Mode = 2
|
||||
}
|
||||
//处理结果文件的文件名
|
||||
if name == "" {
|
||||
|
@ -138,40 +141,45 @@ func SaveSRT(srt *SRT, tranSrt *SRT, name string) {
|
|||
name += ".srt"
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
||||
file, err := os.Create(name)
|
||||
if err != nil {
|
||||
fmt.Printf("保存结果失败:%v\n", err)
|
||||
fmt.Printf("创建结果文件失败:%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
writer := bufio.NewWriter(file)
|
||||
defer func(file *os.File) {
|
||||
_ = file.Close()
|
||||
}(file)
|
||||
//6KB的缓存区,大部分歌词生成的SRT文件均在4-6kb左右
|
||||
writer := bufio.NewWriterSize(file, 1024*6)
|
||||
|
||||
/*
|
||||
原文和译文歌词的排列方式,因为原文歌词中可能包含一些非歌词信息,
|
||||
例如作词者,作曲者等,而在译文歌词中却可能不包含这些
|
||||
*/
|
||||
//srt的序号
|
||||
index := 1
|
||||
switch opt.Mode {
|
||||
case 1:
|
||||
//译文和原文交错排列
|
||||
_len, _lent, size := len(srt.Content), len(tranSrt.Content), 0
|
||||
if _len > _lent {
|
||||
size = 2 * _len
|
||||
} else {
|
||||
size = 2 * _lent
|
||||
//将两个歌词合并成一个新的数组
|
||||
size := len(srt.Content) + len(tranSrt.Content)
|
||||
temp := make([]*SRTContent, size, size)
|
||||
i := 0
|
||||
for _, v := range srt.Content {
|
||||
temp[i] = v
|
||||
i++
|
||||
}
|
||||
//临时缓冲区,偶数索引存原文,奇数存译文
|
||||
buf := make([]*SRTContent, size, size)
|
||||
for i, item := range srt.Content {
|
||||
buf[2*i] = item
|
||||
for _, v := range tranSrt.Content {
|
||||
temp[i] = v
|
||||
i++
|
||||
}
|
||||
for i, item := range tranSrt.Content {
|
||||
buf[2*i+1] = item
|
||||
}
|
||||
|
||||
//按开始时间进行排序,使用SliceStable确保一句歌词的原文在译文之前
|
||||
sort.SliceStable(temp, func(i, j int) bool {
|
||||
return temp[i].Start < temp[j].Start
|
||||
})
|
||||
//写入文件
|
||||
for _, item := range buf {
|
||||
if item != nil {
|
||||
item.Index = index
|
||||
_, _ = writer.WriteString(item.String())
|
||||
index++
|
||||
}
|
||||
for i, v := range temp {
|
||||
v.Index = i + 1
|
||||
_, _ = writer.WriteString(v.String())
|
||||
}
|
||||
case 2:
|
||||
//原文在上,译文在下
|
||||
|
@ -202,9 +210,9 @@ func SaveSRT(srt *SRT, tranSrt *SRT, name string) {
|
|||
err = writer.Flush()
|
||||
if err != nil {
|
||||
fmt.Printf("保存结果失败:%v\n", err)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
fmt.Printf("转换文件完成,保存结果为:%s\n", name)
|
||||
}
|
||||
fmt.Printf("转换文件完成,保存结果为:%s\n", name)
|
||||
}
|
||||
|
||||
// Lrc2Srt 将原始个LRC字符串歌词解析SRT对象
|
||||
|
|
25
lts_test.go
Normal file
25
lts_test.go
Normal file
|
@ -0,0 +1,25 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func BenchmarkSRTContent_String(b *testing.B) {
|
||||
srt := SRTContent{
|
||||
Index: 10,
|
||||
Start: 100,
|
||||
End: 200,
|
||||
Text: "言语 不起作用,想看到 具体行动",
|
||||
}
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_ = srt.String()
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkLrc2Srt(b *testing.B) {
|
||||
id := "28891491"
|
||||
lyric, lyricT := Get163Lyric(id)
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, _ = Lrc2Srt(lyric), Lrc2Srt(lyricT)
|
||||
}
|
||||
}
|
17
qqlyric.go
17
qqlyric.go
|
@ -9,12 +9,19 @@ import (
|
|||
"os"
|
||||
)
|
||||
|
||||
/**
|
||||
从QQ音乐上获取歌词
|
||||
*/
|
||||
|
||||
// QQLyric qq音乐获取歌词接口返回的数据结构
|
||||
type QQLyric struct {
|
||||
RetCode int `json:"retcode"`
|
||||
Code int `json:"code"`
|
||||
SubCode int `json:"subcode"`
|
||||
Lyric string `json:"lyric"`
|
||||
Trans string `json:"trans"`
|
||||
RetCode int `json:"retcode"`
|
||||
Code int `json:"code"`
|
||||
SubCode int `json:"subcode"`
|
||||
//Lyric 原文歌词
|
||||
Lyric string `json:"lyric"`
|
||||
//Trans 译文歌词
|
||||
Trans string `json:"trans"`
|
||||
}
|
||||
|
||||
func GetQQLyric(id string) (lyric, tLyric string) {
|
||||
|
|
12
util.go
12
util.go
|
@ -36,7 +36,9 @@ func ReadFile(name string) string {
|
|||
fmt.Printf("打开文件失败:%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
defer func(file *os.File) {
|
||||
_ = file.Close()
|
||||
}(file)
|
||||
data, err := io.ReadAll(file)
|
||||
if err != nil {
|
||||
fmt.Printf("读取文件失败:%v\n", err)
|
||||
|
@ -46,12 +48,14 @@ func ReadFile(name string) string {
|
|||
}
|
||||
|
||||
func WriteFile(name string, data string) {
|
||||
file, err := os.OpenFile(name, os.O_WRONLY|os.O_CREATE, os.ModePerm)
|
||||
file, err := os.Create(name)
|
||||
if err != nil {
|
||||
fmt.Printf("保存文件失败:%v\n", err)
|
||||
fmt.Printf("创建结果文件失败:%v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer file.Close()
|
||||
defer func(file *os.File) {
|
||||
_ = file.Close()
|
||||
}(file)
|
||||
nw, err := file.WriteString(data)
|
||||
if err != nil || nw < len(data) {
|
||||
fmt.Printf("保存文件失败:%v\n", err)
|
||||
|
|
80
util_test.go
Normal file
80
util_test.go
Normal file
|
@ -0,0 +1,80 @@
|
|||
package main
|
||||
|
||||
import "testing"
|
||||
|
||||
func TestMillisecond2Time(t *testing.T) {
|
||||
type args struct {
|
||||
millisecond int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantH int
|
||||
wantM int
|
||||
wantS int
|
||||
wantMs int
|
||||
}{
|
||||
{"ms_0", args{0}, 0, 0, 0, 0},
|
||||
{"ms_100", args{100}, 0, 0, 0, 100},
|
||||
{"ms_1000", args{1000}, 0, 0, 1, 0},
|
||||
{"ms_1100", args{1100}, 0, 0, 1, 100},
|
||||
{"ms_60000", args{60000}, 0, 1, 0, 0},
|
||||
{"ms_3600000", args{3600000}, 1, 0, 0, 0},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotH, gotM, gotS, gotMs := Millisecond2Time(tt.args.millisecond)
|
||||
if gotH != tt.wantH {
|
||||
t.Errorf("Millisecond2Time() gotH = %v, want %v", gotH, tt.wantH)
|
||||
}
|
||||
if gotM != tt.wantM {
|
||||
t.Errorf("Millisecond2Time() gotM = %v, want %v", gotM, tt.wantM)
|
||||
}
|
||||
if gotS != tt.wantS {
|
||||
t.Errorf("Millisecond2Time() gotS = %v, want %v", gotS, tt.wantS)
|
||||
}
|
||||
if gotMs != tt.wantMs {
|
||||
t.Errorf("Millisecond2Time() gotMs = %v, want %v", gotMs, tt.wantMs)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTime2Millisecond(t *testing.T) {
|
||||
type args struct {
|
||||
m int
|
||||
s int
|
||||
ms int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
want int
|
||||
}{
|
||||
{"0:0.0", args{0, 0, 0}, 0},
|
||||
{"0:0.1", args{0, 0, 1}, 1},
|
||||
{"0:0.999", args{0, 0, 999}, 999},
|
||||
{"0:1.0", args{0, 1, 0}, 1000},
|
||||
{"0:1.999", args{0, 1, 999}, 1999},
|
||||
{"1:0.0", args{1, 0, 0}, 60000},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := Time2Millisecond(tt.args.m, tt.args.s, tt.args.ms); got != tt.want {
|
||||
t.Errorf("Time2Millisecond() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkTime2Millisecond(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Time2Millisecond(999, 999, 999)
|
||||
}
|
||||
}
|
||||
|
||||
func BenchmarkMillisecond2Time(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
Millisecond2Time(9999999999)
|
||||
}
|
||||
}
|
Loading…
Reference in a new issue