血糖有点高吃什么食物好| 怀孕血糖高有什么症状| 腿抽筋什么原因引起的| kcal是什么单位| 腿脚浮肿是什么原因引起的| 怀孕甲减对孩子有什么影响| 拔了牙吃什么消炎药| 背部爱出汗是什么原因| 四不像是指什么动物| 痔疮痒痒的是什么原因| 甲状腺偏高有什么影响| 下面痒用什么药效果好| 我炸了是什么意思| 赢字五行属什么| 新手摆地摊卖什么好| 百年好合是什么意思| 高手过招下一句是什么| 变应原皮内试验是测什么的| 荨麻疹涂什么药膏| 飞的第一笔是什么| 头晕吃什么药效果好| 结肠炎是什么原因引起的| 奎宁现在叫什么药| 中出是什么意识| 肺结节吃什么水果好| 事物指的是什么| 胃胀消化不好吃什么药| 肠粉是用什么粉做的| 桫椤是什么植物| hb是什么意思医学| 246是什么意思| 淋巴结肿大是什么引起的| 能戒烟的男人什么性格| 胃痉挛有什么症状表现| 海军蓝是什么颜色| 尿道疼是什么原因| 吃五谷杂粮有什么好处| 中性粒细胞低说明什么| 格桑花的花语是什么| 乌龙茶是什么茶| 石榴木命是什么意思| 一月20号是什么星座| 玥字属于五行属什么| 狗狗拉肚子吃什么药| 退行性变是什么意思| 亚麻籽油和胡麻油有什么区别| 主治医师是什么级别| 宫颈囊肿多发是什么意思| 黑曜石属于五行属什么| 日本兵为什么不怕死| 养胃是什么意思| 看正月初一是什么生肖| 肠梗阻有什么症状| 双喜临门是什么生肖| 五月二十六是什么星座| 心悸失眠是什么症状| 从小一起长大的姐妹叫什么| 化妆棉是干什么用的| 618是什么日子| 吃什么药不能喝酒| 想一出是一出什么意思| 乙肝第二针最晚什么时候打| 什么人入什么| 椎间盘突出是什么意思| 下腹部胀是什么原因| 黑桃a是什么酒| 蛇为什么怕雄黄| 脾肾阳虚吃什么中成药最好| 姓蓝的是什么民族| 十二生肖代表什么花| hcg是什么激素| 胰腺炎吃什么好| 巨蟹男和什么座最配| 胆囊胆固醇结晶是什么| 肝主筋的筋是指什么| 黄体破裂有什么症状| 3个火读什么| bodywash是什么意思| 军长是什么级别| 妾是什么意思| 早上八点到九点属于什么时辰| 犀牛吃什么食物| 第一磨牙什么时候换| 什么面什么刀| 什么样的女孩容易招鬼| 植物油是什么| 什么不得| 月经褐色量少是什么原因| 季昌明是什么级别| 动漫是什么意思| 肾囊肿用什么药| 下次闰六月是什么时候| 吃柿子有什么好处和坏处| 贪吃的动物是什么生肖| 浪是什么意思| 答非所问是什么意思| 猪的五行属什么| 为什么生气会胃疼| 肝囊肿饮食要注意什么| 血脂四项包括什么| 怀孕初期需要注意些什么| 十月二十五是什么星座| 盗汗和自汗有什么区别| 嘴巴经常长溃疡是什么原因| 合肥为什么叫合肥| 三花聚顶是什么修为| 同位素是什么| 周瑜为什么打黄盖| 什么的蚜虫| 浆果是什么| 拔罐红色是什么原因| 什么什么一笑| 胎盘低置需要注意什么| 鸡蛋不能和什么一起吃| 两个火念什么| 鼠女和什么生肖最配| 胎儿畸形是什么原因造成的| 什么的母鸡| 爸爸的哥哥的老婆叫什么| 手机电池为什么会鼓包| 女人梦见蛇预示着什么| 椰子煲鸡汤放什么材料| 机缘是什么意思| 腿肿是什么原因引起的怎么办| 三个十念什么| 晚上十一点半是什么时辰| 为什么腋窝老是出汗| 茶鱼是什么鱼| 甘薯和红薯有什么区别| 什么药一吃就哑巴了| 鬼打墙什么意思| 梦见捉蛇是什么意思| 猫鼻支什么症状| balea是什么牌子| 吃避孕药有什么副作用| 泻立停又叫什么名字| 1月22号是什么星座| 吃什么能阻止性早熟| 茁壮的什么| 篮子房是什么意思| 为什么要冬病夏治| 土命缺什么| apart是什么意思| 肝胆湿热吃什么中成药最好| 仙女下凡是什么生肖| 11是什么生肖| 什么是出轨| 什么矿泉水比较好| 尿液有隐血是什么情况| 化疗期间吃什么| 九四年属什么生肖| 无蔗糖是什么意思| 木辛读什么| 眼压低是什么原因| 八月出生的是什么星座| 为什么会得麦粒肿| 为人是什么意思| 什么是风湿热| 暗渡陈仓是什么生肖| 血小板低会引发什么病| 五代十国是什么意思| 省油的灯是什么意思| 猪跟什么生肖配对最好| 霸王别姬是什么生肖| 精神病是什么意思| 女排精神是什么| 夏至要吃什么| 眼睛飞蚊症吃什么药| 周瑜是什么样的人| 血糖高吃什么饭| trx是什么| 一个山一个脊念什么| merry是什么意思| 颅内出血有什么症状| 抽血化验能查出什么| 房性心动过速是什么意思| 阴囊瘙痒用什么药最好| 7月14什么星座| 有志什么成| 眉毛附近长痘痘是什么原因| 芭比q是什么意思| 除湿气吃什么好| 粘鞋子用什么胶水最好| 西游记什么时候写的| 中秋节送什么好| 孙子兵法是什么生肖| 口腔异味挂什么科| 迷恋一个人说明什么| 19年是什么年| henry是什么意思| 牙龈红肿吃什么药| 高沫是什么茶| 贫血严重会得什么病| 眼睛痒是什么原因引起的| 舌头中间疼是什么原因| 七月七日是什么节日| 尿发黄什么原因| 秦始皇原名叫什么| naco是什么牌子| 吃什么对睡眠好| 代偿期和失代偿期是什么意思| 鹿鞭泡酒有什么功效| vcr是什么意思| 鸡血藤手镯有什么功效| 脑白质变性是什么病| 风什么浪什么| 左甲状腺是什么病| 小什么名字好听| 羊水透声欠佳什么意思| 妥协是什么意思| 饱的偏旁叫什么| 月经量少吃什么排淤血| 男鼠配什么生肖最好| 痛风是什么病| 紧急避孕药有什么危害| 梦见女尸是什么预兆| 安慰什么意思| 蝉的幼虫叫什么| 六冲是什么意思| 胆碱酯酶高是什么原因| 大作是什么意思| 做造影是什么意思| 什么牌子的充电宝好| 尿床去医院挂什么科| 炒米泡水喝有什么功效| 7月7日是什么纪念日| 肝硬化是什么意思| 肾阴虚火旺吃什么药| 5月10号是什么星座| 珍珠状丘疹有什么危害| 什么叫腺样体肥大| 白露节气的含义是什么| 中东是什么意思| 双脚麻木是什么病的前兆| 每天坚持黄瓜敷脸有什么效果| 什么样的空气| 麻椒和花椒有什么区别| 蝼蛄是什么动物| 弓形虫是什么| 醍醐灌顶什么意思| 工种是什么意思| 痛经是什么原因引起的| 结石有什么症状| 2018属什么生肖| 左下腹部是什么器官| 防弹衣为什么能防弹| 亚铁是什么| 李隆基是李世民的什么人| 10.1是什么星座| 辣椒炒什么好吃| 脚后跟痒是什么原因| 莲藕不能和什么一起吃| 颜色什么| 内痔疮吃什么药好得快| 血糖用什么字母表示| 把子肉是什么肉| 妈妈是什么| 糗事是什么意思| 鼻子流清水是什么原因| 瘘管是什么病| 遗忘的遗是什么意思| 礽是什么意思| 豆浆和什么搭配最好| 一九七二年属什么生肖| 百度
Skip to content

Commit 0e34d39

Browse files
committed
feat: implement multitrack
1 parent 14ba244 commit 0e34d39

37 files changed

+1835
-814
lines changed

?app.go

Lines changed: 38 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import (
1313

1414
"runtime"
1515

16+
"github.com/k1nho/gahara/internal/timeline"
1617
"github.com/k1nho/gahara/internal/video"
1718
"github.com/wailsapp/wails/v2/pkg/menu"
1819
"github.com/wailsapp/wails/v2/pkg/menu/keys"
@@ -37,15 +38,17 @@ type App struct {
3738
ctx context.Context
3839
// config: gahara configuration
3940
config Config
40-
// Timeline: the project timeline
41-
Timeline video.Timeline `json:"timeline"`
41+
// Timeline: the timeline of the project (NODE_VIDEO0, NODE_AUDIO, NODE_PLACEHOLDER, etc)
42+
Timeline timeline.Timeline
4243
// FFmpegPath: the configured ffmpeg on build
4344
FFmpegPath string
45+
// TrackDuration: the duration of the track being encoded
46+
TrackDuration int
4447
}
4548

4649
// NewApp creates a new App application struct
4750
func NewApp() *App {
48-
return &App{Timeline: video.NewTimeline()}
51+
return &App{Timeline: timeline.NewTimeline()}
4952
}
5053

5154
// startup is called when the app starts. The context is saved
@@ -99,6 +102,14 @@ func (a *App) FilePicker() error {
99102

100103
}
101104

105+
func (a *App) AddTrack() {
106+
a.Timeline.AddTrack()
107+
}
108+
109+
func (a *App) RemoveTrack(tid int) error {
110+
return a.Timeline.RemoveTrack(tid)
111+
}
112+
102113
func (a *App) OpenFile(filepath string) error {
103114
var cmd *exec.Cmd
104115
switch runtime.GOOS {
@@ -316,7 +327,7 @@ func (a *App) EnableVideoMenus() {
316327
timelineMenu.AddText("Save Timeline", keys.CmdOrCtrl("s"), func(cd *menu.CallbackData) {
317328
err := a.SaveTimeline()
318329
if err != nil {
319-
wruntime.LogError(a.ctx, "could not save timeline")
330+
wruntime.LogError(a.ctx, "could not save video")
320331
}
321332
wruntime.EventsEmit(a.ctx, video.EVT_SAVED_TIMELINE, "-- SAVED --")
322333
})
@@ -342,7 +353,7 @@ func (a *App) EnableVideoMenus() {
342353
})
343354
timelineMenu.AddText("Toggle Vim Mode", keys.CmdOrCtrl("i"), func(cd *menu.CallbackData) {
344355
wruntime.EventsEmit(a.ctx, video.EVT_TOGGLE_VIM_MODE)
345-
wruntime.EventsEmit(a.ctx, video.EVT_TRACK_MOVE, 0)
356+
wruntime.EventsEmit(a.ctx, video.EVT_CLIP_MOVE, 0)
346357
})
347358
vimCommandsMenu := timelineMenu.AddSubmenu("Vim Commands")
348359
vimCommandsMenu.AddText("Normal Mode", keys.Key("i"), func(cd *menu.CallbackData) {
@@ -366,28 +377,42 @@ func (a *App) EnableVideoMenus() {
366377
vimCommandsMenu.AddText("Execute Edit", keys.Key("enter"), func(cd *menu.CallbackData) {
367378
wruntime.EventsEmit(a.ctx, video.EVT_EXECUTE_EDIT)
368379
})
369-
vimCommandsMenu.AddText("Move Track Left", keys.Key("h"), func(cd *menu.CallbackData) {
380+
vimCommandsMenu.AddText("Move Down Track", keys.Key("j"), func(cd *menu.CallbackData) {
381+
wruntime.EventsEmit(a.ctx, video.EVT_TRACK_MOVE, 1)
382+
})
383+
vimCommandsMenu.AddText("Move Up Track", keys.Key("k"), func(cd *menu.CallbackData) {
370384
wruntime.EventsEmit(a.ctx, video.EVT_TRACK_MOVE, -1)
371385
})
386+
vimCommandsMenu.AddText("Move Track Left", keys.Key("h"), func(cd *menu.CallbackData) {
387+
wruntime.EventsEmit(a.ctx, video.EVT_CLIP_MOVE, -1)
388+
})
372389
vimCommandsMenu.AddText("Move Track Right", keys.Key("l"), func(cd *menu.CallbackData) {
373-
wruntime.EventsEmit(a.ctx, video.EVT_TRACK_MOVE, 1)
390+
wruntime.EventsEmit(a.ctx, video.EVT_CLIP_MOVE, 1)
374391
})
375392
vimCommandsMenu.AddText("Move to Beginning of Track", keys.Key("0"), func(cd *menu.CallbackData) {
376-
wruntime.EventsEmit(a.ctx, video.EVT_TRACK_MOVE, -len(a.Timeline.VideoNodes))
393+
wruntime.EventsEmit(a.ctx, video.EVT_CLIP_MOVE, -len(a.Timeline.Nodes[0]))
377394
})
378395
vimCommandsMenu.AddText("Move to End of Track", keys.Key("$"), func(cd *menu.CallbackData) {
379-
wruntime.EventsEmit(a.ctx, video.EVT_TRACK_MOVE, len(a.Timeline.VideoNodes))
396+
wruntime.EventsEmit(a.ctx, video.EVT_CLIP_MOVE, len(a.Timeline.Nodes[0]))
380397
})
381398
vimCommandsMenu.AddText("Zoom In Timeline", keys.Shift("+"), func(cd *menu.CallbackData) {
382399
wruntime.EventsEmit(a.ctx, video.EVT_ZOOM_TIMELINE, "in")
383400
})
384401
vimCommandsMenu.AddText("Zoom Out Timeline", keys.Shift("-"), func(cd *menu.CallbackData) {
385402
wruntime.EventsEmit(a.ctx, video.EVT_ZOOM_TIMELINE, "out")
386403
})
404+
405+
vimCommandsMenu.AddText("Add Timeline Track", keys.Shift("t"), func(cd *menu.CallbackData) {
406+
wruntime.EventsEmit(a.ctx, video.EVT_ADD_TRACK)
407+
})
408+
vimCommandsMenu.AddText("Remove Timeline Track", keys.Shift("d"), func(cd *menu.CallbackData) {
409+
wruntime.EventsEmit(a.ctx, video.EVT_REMOVE_TRACK)
410+
})
411+
387412
vimCommandsMenu.AddText("Save Timeline", keys.Shift("w"), func(cd *menu.CallbackData) {
388413
err := a.SaveTimeline()
389414
if err != nil {
390-
wruntime.LogError(a.ctx, "could not save timeline")
415+
wruntime.LogError(a.ctx, "could not save video")
391416
}
392417
wruntime.EventsEmit(a.ctx, video.EVT_SAVED_TIMELINE, "-- SAVED --")
393418
})
@@ -397,6 +422,9 @@ func (a *App) EnableVideoMenus() {
397422
vimCommandsMenu.AddText("Search Timeline Clip", keys.Shift("f"), func(cd *menu.CallbackData) {
398423
wruntime.EventsEmit(a.ctx, video.EVT_SEARCH_TIMELINE_CLIP)
399424
})
425+
vimCommandsMenu.AddText("Insert Placeholder Clip", keys.Shift("p"), func(cd *menu.CallbackData) {
426+
wruntime.EventsEmit(a.ctx, video.EVT_SEARCH_PLACEHOLDER)
427+
})
400428

401429
appMenu := a.AppMenu()
402430
appMenu.Items = append(appMenu.Items, &menu.MenuItem{

?ffmpegbuilder/ffmpegbuilder.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -205,12 +205,12 @@ func (f *FFmpegBuilder) ConcatFilter(videoNodes []video.VideoNode) (string, erro
205205

206206
concatQuery.WriteString("\"")
207207
for i, videoNode := range videoNodes {
208-
if pos, ok := ridToPos[videoNode.RID]; ok {
209-
concatQuery.WriteString(fmt.Sprintf("[%d:v]trim=start=%.4f:end=%.4f,setpts=PTS-STARTPTS,scale=%s[v%d];", pos, videoNode.Start, videoNode.End, f.FilterGraphParams.Scale, i))
208+
if pos, ok := ridToPos[videoNode.VideoRID]; ok {
209+
concatQuery.WriteString(fmt.Sprintf("[%d:v]trim=start=%.4f:end=%.4f,setpts=PTS-STARTPTS,scale=%s[v%d];", pos, videoNode.VideoStart, videoNode.VideoEnd, f.FilterGraphParams.Scale, i))
210210
continue
211211
}
212-
ridToPos[videoNode.RID] = pos
213-
concatQuery.WriteString(fmt.Sprintf("[%d:v]trim=start=%.4f:end=%.4f,setpts=PTS-STARTPTS,scale=%s[v%d];", pos, videoNode.Start, videoNode.End, f.FilterGraphParams.Scale, i))
212+
ridToPos[videoNode.VideoRID] = pos
213+
concatQuery.WriteString(fmt.Sprintf("[%d:v]trim=start=%.4f:end=%.4f,setpts=PTS-STARTPTS,scale=%s[v%d];", pos, videoNode.VideoStart, videoNode.VideoEnd, f.FilterGraphParams.Scale, i))
214214
pos += 1
215215
}
216216

?ffmpegbuilder/ffmpegbuilder_test.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,17 @@ import (
44
"fmt"
55
"testing"
66

7+
"github.com/k1nho/gahara/internal/timeline"
78
"github.com/k1nho/gahara/internal/video"
89
)
910

10-
func mockTl() *video.Timeline {
11-
return &video.Timeline{VideoNodes: []video.VideoNode{
12-
{RID: "root1", ID: "1", Name: "input1", Start: 20.1, End: 25.2, LosslessExport: true},
13-
{RID: "root1", ID: "2", Name: "input2", Start: 1.12, End: 10.2, LosslessExport: true},
14-
{RID: "root2", ID: "3", Name: "input3", Start: 12.2, End: 21.2, LosslessExport: true},
15-
{RID: "root3", ID: "4", Name: "input4", Start: 69.112, End: 80.23, LosslessExport: true},
16-
}}
11+
func mockTl() *timeline.Timeline {
12+
return &timeline.Timeline{Nodes: [][]timeline.TimelineNode{{
13+
&video.VideoNode{VideoRID: "root1", VideoID: "1", VideoName: "input1", VideoStart: 20.1, VideoEnd: 25.2, LosslessExport: true},
14+
&video.VideoNode{VideoRID: "root1", VideoID: "2", VideoName: "input2", VideoStart: 1.12, VideoEnd: 10.2, LosslessExport: true},
15+
&video.VideoNode{VideoRID: "root2", VideoID: "3", VideoName: "input3", VideoStart: 12.2, VideoEnd: 21.2, LosslessExport: true},
16+
&video.VideoNode{VideoRID: "root3", VideoID: "4", VideoName: "input4", VideoStart: 69.112, VideoEnd: 80.23, LosslessExport: true},
17+
}}}
1718

1819
}
1920

@@ -65,8 +66,8 @@ func TestFFmpegBuilder(t *testing.T) {
6566

6667
t.Run("concat filter query", func(t *testing.T) {
6768
expectedQuery := "ffmpeg -hide_banner -v quiet -stats_period 5s -progress pipe:2 -i \"root1\" -i \"root2\" -i \"root3\" -filter_complex \"[0:v]trim=start=20.1000:end=25.2000,setpts=PTS-STARTPTS,scale=1920x1080[v0];[0:v]trim=start=1.1200:end=10.2000,setpts=PTS-STARTPTS,scale=1920x1080[v1];[1:v]trim=start=12.2000:end=21.2000,setpts=PTS-STARTPTS,scale=1920x1080[v2];[2:v]trim=start=69.1120:end=80.2300,setpts=PTS-STARTPTS,scale=1920x1080[v3];[v0][v1][v2][v3]concat=n=4:v=1:a=0[out]\" -map \"[out]\" -c:v libx264 -crf 18 -preset medium \"outputpath/myvideo.mp4\" "
68-
69-
query, err := MergeClipsQuery("ffmpeg", mockTl().VideoNodes, video.ProcessingOpts{
69+
videoNodes := ExtractVideoNodes(mockTl().Nodes[0])
70+
query, err := MergeClipsQuery("ffmpeg", videoNodes, video.ProcessingOpts{
7071
Resolution: "1920x1080",
7172
Codec: "libx264",
7273
CRF: "18",
@@ -85,14 +86,14 @@ func TestFFmpegBuilder(t *testing.T) {
8586
})
8687

8788
t.Run("lossless cut query", func(t *testing.T) {
88-
videoNode := video.VideoNode{RID: "root1", Name: "myvideo", Start: 22.2300, End: 28.4321, ID: "1", LosslessExport: true}
89-
duration := videoNode.End - videoNode.Start
89+
videoNode := video.VideoNode{VideoRID: "root1", VideoName: "myvideo", VideoStart: 22.2300, VideoEnd: 28.4321, VideoID: "1", LosslessExport: true}
90+
duration := videoNode.VideoEnd - videoNode.VideoStart
9091

9192
expectedQuery := fmt.Sprintf("ffmpeg -hide_banner -v quiet -stats_period 5s -progress pipe:2 -ss 22.2300 -i \"root1\" -t %.4f -avoid_negative_ts make_zero -c copy -movflags '+faststart' \"outputpath/myvideo.mp4\" ", duration)
9293

9394
query, err := LosslessCutQuery("ffmpeg", videoNode, video.ProcessingOpts{
9495
OutputPath: "outputpath",
95-
Filename: videoNode.Name,
96+
Filename: videoNode.VideoName,
9697
VideoFormat: ".mp4",
9798
})
9899

?ffmpegbuilder/query.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,10 +78,10 @@ func MergeClipsQuery(FFmpegPath string, videoNodes []video.VideoNode, userOpts v
7878
// LosslessCutQuery: returns the query string to make a lossless cut of a video node
7979
func LosslessCutQuery(FFmpegPath string, videoNode video.VideoNode, userOpts video.ProcessingOpts) (string, error) {
8080
// overwrite filename, if it was passed by default lossy opts
81-
userOpts.Filename = videoNode.Name
81+
userOpts.Filename = videoNode.VideoName
8282

83-
querybuilder := NewDefaultFFmpegBuilder(FFmpegPath).WithInputs(videoNode.RID).WithInputStartTime(videoNode.Start).
84-
WithOutputDuration(videoNode.End - videoNode.Start).WithCodec("copy").WithAvoidNegativeTS("make_zero").
83+
querybuilder := NewDefaultFFmpegBuilder(FFmpegPath).WithInputs(videoNode.VideoRID).WithInputStartTime(videoNode.VideoStart).
84+
WithOutputDuration(videoNode.VideoEnd - videoNode.VideoStart).WithCodec("copy").WithAvoidNegativeTS("make_zero").
8585
WithMovFlags("+faststart").WithOutputs(GetFullOutputPath(userOpts))
8686

8787
if err := querybuilder.validateLosslessCutQuery(); err != nil {

?ffmpegbuilder/utils.go

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,16 @@ package ffmpegbuilder
33
import (
44
"path"
55

6+
"github.com/k1nho/gahara/internal/audio"
7+
"github.com/k1nho/gahara/internal/timeline"
68
"github.com/k1nho/gahara/internal/video"
79
)
810

911
// extractInputs: extract input params from video nodes
1012
func ExtractInputs(videoNodes []video.VideoNode) []string {
1113
var inputs []string
1214
for _, videoNode := range videoNodes {
13-
inputs = append(inputs, videoNode.RID)
15+
inputs = append(inputs, videoNode.VideoRID)
1416
}
1517
return inputs
1618
}
@@ -24,3 +26,27 @@ func GetFullOutputPath(opts video.ProcessingOpts) string {
2426
func GetFullInputPath(opts video.ProcessingOpts) string {
2527
return path.Join(opts.InputPath, opts.Filename+opts.VideoFormat)
2628
}
29+
30+
func ExtractVideoNodes(track []timeline.TimelineNode) []video.VideoNode {
31+
videoNodes := make([]video.VideoNode, 0)
32+
for _, node := range track {
33+
if node.Type() == video.NODE_VIDEO {
34+
if videoNode, ok := node.(*video.VideoNode); ok {
35+
videoNodes = append(videoNodes, *videoNode)
36+
}
37+
}
38+
}
39+
return videoNodes
40+
}
41+
42+
func ExtractAudioNodes(track []timeline.TimelineNode) []audio.AudioNode {
43+
audioNodes := make([]audio.AudioNode, 0)
44+
for _, node := range track {
45+
if node.Type() == audio.NODE_AUDIO {
46+
if audioNode, ok := node.(*audio.AudioNode); ok {
47+
audioNodes = append(audioNodes, *audioNode)
48+
}
49+
}
50+
}
51+
return audioNodes
52+
}

?frontend/src/App.svelte

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
videoFiles,
88
videoStore,
99
projectName,
10+
trackStore,
1011
} from "./stores";
1112
import ExportMenuLayout from "./ExportMenuLayout.svelte";
1213
import {
@@ -18,6 +19,7 @@
1819
import { onDestroy } from "svelte";
1920
const { route, setRoute } = router;
2021
const { isProcessingVid } = exportOptionsStore;
22+
const { resetTrackStore } = trackStore;
2123
const { resetVideo } = videoStore;
2224
const { resetVideoFiles } = videoFiles;
2325
@@ -30,6 +32,7 @@
3032
} catch (err) {
3133
console.log(err);
3234
}
35+
resetTrackStore();
3336
WindowSetTitle("Gahara");
3437
}
3538

?frontend/src/ExportMenuLayout.svelte

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
import { EventsOff, EventsOn } from "../wailsjs/runtime/runtime";
1515
import type { video } from "wailsjs/go/models";
1616
import { formatSecondsToHMS } from "./lib/utils";
17+
import { isNodeVideo } from "./lib/timeline";
1718
const { setRoute } = router;
1819
const {
1920
filename,
@@ -52,7 +53,13 @@
5253
if ($projectName) setFilename($projectName.replace(/\s+/g, ""));
5354
GetTimeline()
5455
.then((timeline) => {
55-
videoNodes = [...timeline.video_nodes];
56+
let tmpVideoNodes: video.VideoNode[] = [];
57+
timeline.timeline.forEach((track) => {
58+
track.forEach((node) => {
59+
if (isNodeVideo(node)) tmpVideoNodes.push(node);
60+
});
61+
});
62+
videoNodes = [...tmpVideoNodes];
5663
})
5764
.catch(() => console.log("could not load timeline"));
5865
EnableExportMenus();
@@ -84,7 +91,7 @@
8491
}
8592
8693
function handleCheck(pos: number) {
87-
ToggleLossless(pos);
94+
ToggleLossless(0, pos);
8895
}
8996
9097
function handleExport() {

?frontend/src/MainMenuLayout.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@
129129
class="border-solid border-indigo-300 border-2 rounded-md focus:border-indigo-500 focus:outline-none"
130130
/>
131131
<button
132-
class="rounded-full bg-indigo-500 font-semibold text-white px-4 py-1.5 hover:bg-indigo-700 transition ease-in-out duration-200 self-center"
132+
class="rounded-lg border-white border-2 bg-indigo-500 font-semibold text-white px-4 py-1.5 hover:bg-indigo-700 transition ease-in-out duration-200 self-center"
133133
on:click={() => createProject()}>Start</button
134134
>
135135
</div>

0 commit comments

Comments
?(0)
醋精是什么 吃完饭就打嗝是什么原因 慷他人之慨什么意思 格林巴利综合症是什么 双肾钙化灶是什么意思
牙齿脱矿是什么意思 boss是什么意思 冬天用什么沐浴露好 ras医学上是什么意思 覆盆子是什么东西
鬼针草长什么样 什么叫造影 国庆节是什么时候 塔罗牌能算什么 应接不暇的暇是什么意思
泡脚用什么东西泡最好 宣府是现在的什么地方 血精和精囊炎吃什么药 临床药学在医院干什么 一什么蜻蜓
妇科支原体感染吃什么药hcv8jop1ns9r.cn 百合什么意思hcv9jop3ns8r.cn 梦到吃梨是什么意思hcv9jop0ns1r.cn 虫咬性皮炎用什么药hcv7jop9ns7r.cn 头昏脑涨是什么原因helloaicloud.com
vinegar是什么意思hcv7jop5ns4r.cn 歼是什么意思hcv9jop5ns6r.cn 人为什么会困hcv9jop7ns0r.cn 限期使用日期是什么意思hcv8jop5ns2r.cn 什么样的孙悟空hcv9jop0ns1r.cn
与五行属什么hcv8jop7ns7r.cn 灵芝长什么样子图片hcv8jop0ns4r.cn 叶酸什么牌子好hcv8jop9ns9r.cn 六味地黄丸有什么副作用zhongyiyatai.com 什么东西只进不出hcv7jop6ns8r.cn
博美犬吃什么狗粮最好hcv7jop7ns0r.cn abcd是什么意思hcv9jop5ns3r.cn 纵欲是什么意思xinmaowt.com 老师家访的目的是什么520myf.com 今年56岁属什么生肖hcv7jop6ns3r.cn
百度