Skip to content

Commit

Permalink
Merge pull request #43 from Gahara-Editor/fix/duration-load-on-first-…
Browse files Browse the repository at this point in the history
…insert

fix: get video duration from ffmpeg
  • Loading branch information
k1nho authored Jun 12, 2024
2 parents 2c180ac + 1dcfe2c commit c3a62c3
Show file tree
Hide file tree
Showing 13 changed files with 336 additions and 60 deletions.
12 changes: 11 additions & 1 deletion app.go
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,17 @@ func (a *App) ReadProjectWorkspace() ([]Video, error) {
if !video.IsValidExtension(filepath.Ext(project.Name())) {
continue
}
projectFiles = append(projectFiles, Video{Name: strings.Split(project.Name(), ".")[0], Extension: filepath.Ext(project.Name()), FilePath: a.config.ProjectDir})

duration, err := getVideoDuration(video.ProcessingOpts{
Filename: strings.Split(project.Name(), ".")[0],
VideoFormat: filepath.Ext(project.Name()),
InputPath: a.config.ProjectDir,
})
if err != nil {
wruntime.LogError(a.ctx, fmt.Sprintf("could not check video duration: %s", err.Error()))
continue
}
projectFiles = append(projectFiles, Video{Name: strings.Split(project.Name(), ".")[0], Extension: filepath.Ext(project.Name()), FilePath: a.config.ProjectDir, Duration: duration})
}
}

Expand Down
15 changes: 15 additions & 0 deletions ffmpegbuilder/ffmpegbuilder.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,8 @@ type OutputParams struct {
// MovFlags: -movflags in ffmpeg, mov, mp4, and ismv support fragmentation. The metadata about all packets is stored in one location,
// but it can be moved at the start for better playback (+faststart)
MovFlags string
// NullOutput: -f null - in ffmpeg (used to check info only)
NullOutput string
}

func NewDefaultFFmpegBuilder() *FFmpegBuilder {
Expand Down Expand Up @@ -113,11 +115,21 @@ func (f *FFmpegBuilder) WithStatsPeriod(statsPeriod string) *FFmpegBuilder {
return f
}

func (f *FFmpegBuilder) WithVerbose(verbose string) *FFmpegBuilder {
f.PreInputParams.VerboseMode = ""
return f
}

func (f *FFmpegBuilder) WithCodec(codec string) *FFmpegBuilder {
f.OutputParams.Codec = codec
return f
}

func (f *FFmpegBuilder) WithNullOutput() *FFmpegBuilder {
f.OutputParams.NullOutput = "-f null -"
return f
}

func (f *FFmpegBuilder) WithVideoCodec(videoCodec string) *FFmpegBuilder {
f.OutputParams.VideoCodec = videoCodec
return f
Expand Down Expand Up @@ -258,6 +270,9 @@ func (f *FFmpegBuilder) BuildQuery() (string, error) {
}

// Append output parameters
if f.OutputParams.NullOutput != "" {
cmd.WriteString(fmt.Sprintf("%s ", f.OutputParams.NullOutput))
}
if f.OutputParams.Duration != 0 {
cmd.WriteString(fmt.Sprintf("-t %.4f ", f.OutputParams.Duration))
}
Expand Down
23 changes: 20 additions & 3 deletions ffmpegbuilder/query.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,39 @@ import (
"github.com/k1nho/gahara/internal/video"
)

func CheckVideoDuration(userOpts video.ProcessingOpts) (string, error) {
input := GetFullInputPath(userOpts)

query, err := NewDefaultFFmpegBuilder().WithInputs(input).
WithNullOutput().WithVerbose("").BuildQuery()
if err != nil {
return "", err
}
return query, nil
}

// CreateProxyFileQuery: creates a proxy file for a video
func CreateProxyFileQuery(userOpts video.ProcessingOpts, format string) (string, error) {
input := GetFullInputPath(userOpts)
userOpts.VideoFormat = format
output := GetFullOutputPath(userOpts)

query, err := NewDefaultFFmpegBuilder().WithInputs(input).WithCodec("copy").
WithOutputs(output).BuildQuery()
querybuilder := NewDefaultFFmpegBuilder().WithInputs(input).WithCodec("copy").
WithOutputs(output)

if err := querybuilder.validateProxyFileCreationQuery(); err != nil {
return "", err
}

query, err := querybuilder.BuildQuery()
if err != nil {
return "", err
}

return query, nil
}

// GenerateThumbnailQuery: generate a thumbnail from a video
// CreateThumbnailQuery: generates a thumbnail taking the 1 frame of a video
func CreateThumbnailQuery(userOpts video.ProcessingOpts, format string) (string, error) {
input := GetFullInputPath(userOpts)
userOpts.VideoFormat = format
Expand Down
14 changes: 14 additions & 0 deletions ffmpegbuilder/validate.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,3 +38,17 @@ func (f *FFmpegBuilder) validateLosslessCutQuery() error {
}
return nil
}

func (f *FFmpegBuilder) validateProxyFileCreationQuery() error {
if len(f.Inputs) != 1 {
return fmt.Errorf("no input stream(s) provided")
}
if len(f.Outputs) != 1 {
return fmt.Errorf("no output stream(s) provided")
}

if f.OutputParams.Codec != "copy" {
return fmt.Errorf("codec must be copy. No re-encoding needed for proxy")
}
return nil
}
36 changes: 33 additions & 3 deletions frontend/src/App.svelte
Original file line number Diff line number Diff line change
@@ -1,20 +1,50 @@
<script lang="ts">
import VideoLayout from "./VideoLayout.svelte";
import MainMenuLayout from "./MainMenuLayout.svelte";
import { exportOptionsStore, router } from "./stores";
import {
exportOptionsStore,
router,
videoFiles,
videoStore,
projectName,
} from "./stores";
import ExportMenuLayout from "./ExportMenuLayout.svelte";
import { EventsOff, EventsOn } from "../wailsjs/runtime/runtime";
import {
EventsOff,
EventsOn,
WindowSetTitle,
} from "../wailsjs/runtime/runtime";
import { ResetTimeline } from "../wailsjs/go/main/App";
import { onDestroy } from "svelte";
const { route, setRoute } = router;
const { isProcessingVid } = exportOptionsStore;
const { resetVideo } = videoStore;
const { resetVideoFiles } = videoFiles;
function cleanupProjectWorkspace() {
resetVideoFiles();
resetVideo();
ResetTimeline();
WindowSetTitle("Gahara");
}
EventsOn("evt_change_route", (to: string) => {
switch ($route) {
case "video":
switch (to) {
case "main":
cleanupProjectWorkspace();
break;
case "export":
WindowSetTitle(`Export - ${$projectName}`);
}
setRoute(to);
break;
case "export":
if (!$isProcessingVid) setRoute(to);
if (!$isProcessingVid) {
WindowSetTitle($projectName);
setRoute(to);
}
}
});
Expand Down
38 changes: 19 additions & 19 deletions frontend/src/VideoLayout.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
FilePicker,
ReadProjectWorkspace,
LoadTimeline,
LoadProjectFiles,
SaveTimeline,
SaveProjectFiles,
ResetTimeline,
DeleteRIDReferences,
DeleteProjectFile,
Expand Down Expand Up @@ -51,8 +53,6 @@
pipelineMessages,
videoFilesError,
removeVideoFile,
addPipelineMsg,
removePipelineMsg,
addVideos,
setVideoFilesError,
resetVideoFiles,
Expand All @@ -61,17 +61,24 @@
const { actionMessage, setActionMsg } = toolingStore;
onMount(() => {
loadProjectFiles();
loadTimeline();
if ($videoFiles.length <= 0) loadProjectFiles();
if ($trackStore.length <= 0) loadTimeline();
EnableVideoMenus();
});
function loadProjectFiles() {
if ($videoFiles.length === 0) {
ReadProjectWorkspace()
.then((files) => addVideos(files))
.catch(() => setVideoFilesError("No files in this project"));
}
LoadProjectFiles()
.then((files) => {
addVideos(files);
})
.catch(() => {
ReadProjectWorkspace()
.then((files) => {
addVideos(files);
SaveProjectFiles(files).then().catch(console.log);
})
.catch(() => setVideoFilesError("No files in this project"));
});
}
function loadTimeline() {
Expand Down Expand Up @@ -103,6 +110,7 @@
await SaveTimeline();
removeRIDReferencesFromTrack(0, rid);
removeVideoFile(video.name);
await SaveProjectFiles($videoFiles);
} catch (err) {
setVideoFilesError(err);
}
Expand All @@ -111,26 +119,18 @@
EventsOn("evt_proxy_file_created", (video: main.Video) => {
setVideoFilesError("");
addVideos([video]);
removePipelineMsg();
SaveProjectFiles($videoFiles).then().catch(console.log);
});
EventsOn("evt_proxy_error_msg", (msg: string) => {
setVideoFilesError(msg);
});
EventsOn("evt_proxy_pipeline_msg", (msg: string) => {
addPipelineMsg(msg);
});
EventsOn("evt_upload_file", () => {
selectFile();
});
onDestroy(() => {
if ($route === "main") SetDefaultAppMenu();
EventsOff(
"evt_proxy_file_created",
"evt_error_msg",
"evt_proxy_pipeline_msg",
"evt_upload_file",
);
EventsOff("evt_proxy_file_created", "evt_error_msg", "evt_upload_file");
});
</script>

Expand Down
8 changes: 2 additions & 6 deletions frontend/src/components/SearchList.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,7 @@
} = toolingStore;
const { addVideoToTrack } = trackStore;
const { searchFiles } = videoFiles;
const { source, setVideoSrc, setCurrentTime, getDuration, viewVideo } =
videoStore;
const { source, setVideoSrc, setCurrentTime, viewVideo } = videoStore;
let searchTerm = "";
let searchIdx = -1;
Expand Down Expand Up @@ -44,14 +43,11 @@
if (searchIdx >= 0 && searchIdx < searchList.length) {
viewVideo(searchList[searchIdx]);
const videoDuration = getDuration();
if (videoDuration === 0) return;
InsertInterval(
$source,
searchList[searchIdx].name,
0,
videoDuration,
searchList[searchIdx].duration,
$videoNodePos,
)
.then((tVideo) => {
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/lib/dnd.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { draggedVideo, trackStore, videoStore, toolingStore } from "../stores";
import type { video, main } from "../../wailsjs/go/models";
import type { main } from "../../wailsjs/go/models";
import { InsertInterval } from "../../wailsjs/go/main/App";

export function draggable(node: HTMLDivElement, data: main.Video) {
Expand Down Expand Up @@ -72,7 +72,7 @@ export function dropzone(node: HTMLDivElement, opts) {
videoID,
draggedVideo.value().name,
0,
videoStore.getDuration(),
draggedVideo.value().duration,
0,
)
.then((tVideo) => {
Expand Down
4 changes: 4 additions & 0 deletions frontend/wailsjs/go/main/App.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export function GetTrackDuration():Promise<number>;

export function InsertInterval(arg1:string,arg2:string,arg3:number,arg4:number,arg5:number):Promise<video.VideoNode>;

export function LoadProjectFiles():Promise<Array<main.Video>>;

export function LoadTimeline():Promise<video.Timeline>;

export function OpenFile(arg1:string):Promise<void>;
Expand All @@ -50,6 +52,8 @@ export function RenameVideoNode(arg1:number,arg2:string):Promise<void>;

export function ResetTimeline():Promise<void>;

export function SaveProjectFiles(arg1:Array<main.Video>):Promise<void>;

export function SaveTimeline():Promise<void>;

export function SetDefaultAppMenu():Promise<void>;
Expand Down
8 changes: 8 additions & 0 deletions frontend/wailsjs/go/main/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ export function InsertInterval(arg1, arg2, arg3, arg4, arg5) {
return window['go']['main']['App']['InsertInterval'](arg1, arg2, arg3, arg4, arg5);
}

export function LoadProjectFiles() {
return window['go']['main']['App']['LoadProjectFiles']();
}

export function LoadTimeline() {
return window['go']['main']['App']['LoadTimeline']();
}
Expand Down Expand Up @@ -94,6 +98,10 @@ export function ResetTimeline() {
return window['go']['main']['App']['ResetTimeline']();
}

export function SaveProjectFiles(arg1) {
return window['go']['main']['App']['SaveProjectFiles'](arg1);
}

export function SaveTimeline() {
return window['go']['main']['App']['SaveTimeline']();
}
Expand Down
4 changes: 4 additions & 0 deletions frontend/wailsjs/go/models.ts
Original file line number Diff line number Diff line change
@@ -1,19 +1,23 @@
export namespace main {

export class Video {
id: string;
name: string;
extension: string;
filepath: string;
duration: number;

static createFrom(source: any = {}) {
return new Video(source);
}

constructor(source: any = {}) {
if ('string' === typeof source) source = JSON.parse(source);
this.id = source["id"];
this.name = source["name"];
this.extension = source["extension"];
this.filepath = source["filepath"];
this.duration = source["duration"];
}
}

Expand Down
Loading

0 comments on commit c3a62c3

Please sign in to comment.