Skip to content

Commit b670818

Browse files
Add seek delay option
1 parent 4b8694b commit b670818

File tree

4 files changed

+81
-35
lines changed

4 files changed

+81
-35
lines changed

Ffmpeg.lua

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ local Promise = require('jls.lang.Promise')
44
local StringBuffer = require('jls.lang.StringBuffer')
55
local File = require('jls.io.File')
66
local strings = require('jls.util.strings')
7-
local TableList = require('jls.util.TableList')
7+
local List = require('jls.util.List')
88
local LocalDateTime = require('jls.util.LocalDateTime')
99

1010
local function getExecutableName(name)
@@ -51,6 +51,7 @@ return class.create(function(ffmpeg)
5151
self.ffprobePath = getExecutableName('ffprobe')
5252
end
5353
end
54+
self.seekDelayMs = options.seekDelay or 0
5455
end
5556

5657
function ffmpeg:check()
@@ -93,24 +94,30 @@ return class.create(function(ffmpeg)
9394
function ffmpeg:computeArguments(destFilename, destOptions, srcFilename, srcOptions, globalOptions)
9495
local args = {self.ffmpegPath, '-hide_banner'}
9596
if globalOptions then
96-
TableList.concat(args, globalOptions)
97+
List.concat(args, globalOptions)
9798
end
9899
if srcOptions then
99-
TableList.concat(args, srcOptions)
100+
List.concat(args, srcOptions)
100101
end
101102
if srcFilename then
102-
TableList.concat(args, '-i', srcFilename)
103+
if type(srcFilename) == 'table' then
104+
for _, filename in ipairs(srcFilename) do
105+
List.concat(args, '-i', filename)
106+
end
107+
else
108+
List.concat(args, '-i', srcFilename)
109+
end
103110
end
104111
if destOptions then
105-
TableList.concat(args, destOptions)
112+
List.concat(args, destOptions)
106113
end
107114
if destFilename then
108-
TableList.concat(args, '-y', destFilename)
115+
List.concat(args, '-y', destFilename)
109116
end
110117
return args
111118
end
112119

113-
function ffmpeg:createCommand(part, filename, options, seekDelay)
120+
function ffmpeg:createCommand(part, filename, options, parameters)
114121
--[[
115122
'-ss position (input/output)'
116123
When used as an input option (before -i), seeks in this input file to position.
@@ -132,25 +139,25 @@ return class.create(function(ffmpeg)
132139
local srcOptions = {}
133140
local destOptions = {}
134141
if part.from ~= nil then
135-
local delay = seekDelay or 0
142+
local delay = (parameters and parameters.seekDelay) or self.seekDelayMs or 0
136143
if (delay >= 0) and (delay < part.from) then
137-
TableList.concat(srcOptions, '-ss', formatTime(part.from - delay))
138-
TableList.concat(destOptions, '-ss', math.floor(delay))
144+
List.concat(srcOptions, '-ss', formatTime(part.from - delay))
145+
List.concat(destOptions, '-ss', math.floor(delay))
139146
elseif delay == -1 then
140-
TableList.concat(srcOptions, '-ss', formatTime(part.from))
147+
List.concat(srcOptions, '-ss', formatTime(part.from))
141148
else
142-
TableList.concat(destOptions, '-ss', formatTime(part.from))
149+
List.concat(destOptions, '-ss', formatTime(part.from))
143150
end
144151
end
145152
if part.to ~= nil then
146153
if part.from ~= nil then
147-
TableList.concat(destOptions, '-t', formatTime(part.to - part.from))
148-
--TableList.concat(destOptions, '-to', formatTime(part.to))
154+
List.concat(destOptions, '-t', formatTime(part.to - part.from))
155+
--List.concat(destOptions, '-to', formatTime(part.to))
149156
else
150-
TableList.concat(destOptions, '-t', formatTime(part.to))
157+
List.concat(destOptions, '-t', formatTime(part.to))
151158
end
152159
end
153-
TableList.concat(destOptions, options)
160+
List.concat(destOptions, options)
154161
local sourceFile = self.sources[part.sourceId]
155162
return self:computeArguments(filename, destOptions, sourceFile:getPath(), srcOptions)
156163
end
@@ -159,17 +166,30 @@ return class.create(function(ffmpeg)
159166
return File:new(self.cacheDir, filename)
160167
end
161168

162-
function ffmpeg:createCommands(filename, parts, destOptions, seekDelayMs)
169+
function ffmpeg:deleteTempFiles()
170+
local tmpFiles = self.cacheDir:listFiles(function(file)
171+
return file:isFile() and (file:getExtension() == 'tmp')
172+
end)
173+
for _, file in ipairs(tmpFiles) do
174+
file:delete()
175+
end
176+
end
177+
178+
function ffmpeg:createCommands(destFilename, parts, destOptions, parameters)
163179
local commands = {}
180+
local filename = destFilename
181+
if parameters.subtitles then
182+
filename = self:createTempFile('full.tmp'):getPath()
183+
end
164184
if #parts == 1 then
165-
table.insert(commands, self:createCommand(parts[1], filename, destOptions, seekDelayMs))
185+
table.insert(commands, self:createCommand(parts[1], filename, destOptions, parameters))
166186
elseif #parts > 1 then
167187
local concatScript = StringBuffer:new()
168188
concatScript:append('# fcut')
169189
for i, part in ipairs(parts) do
170190
local partName = 'part_'..tostring(i)..'.tmp'
171191
local outFilename = self:createTempFile(partName):getPath()
172-
table.insert(commands, self:createCommand(part, outFilename, destOptions, seekDelayMs))
192+
table.insert(commands, self:createCommand(part, outFilename, destOptions, parameters))
173193
local concatPartname = string.gsub(outFilename, '[\\+]', '/')
174194
--local concatPartname = partName -- to be safe
175195
concatScript:append('\nfile ', concatPartname)
@@ -178,6 +198,9 @@ return class.create(function(ffmpeg)
178198
concatFile:write(concatScript:toString());
179199
table.insert(commands, self:computeArguments(filename, {'-c', 'copy', '-map', '0'}, concatFile:getPath(), {'-f', 'concat', '-safe', '0'}))
180200
end
201+
if parameters.subtitles then
202+
table.insert(commands, self:computeArguments(destFilename, {'-c', 'copy', '-c:s', 'mov_text', '-map', '0'}, List.concat({}, filename, parameters.subtitles)))
203+
end
181204
return commands
182205
end
183206

@@ -217,9 +240,9 @@ return class.create(function(ffmpeg)
217240
local time = LocalDateTime:new():plusSeconds(sec or 0):toTimeString()
218241
local args = {self.ffmpegPath, '-hide_banner', '-v', '0', '-ss', time, '-i', sourceFile:getPath(), '-f', 'mjpeg', '-vcodec', 'mjpeg', '-vframes', '1', '-an'}
219242
if width and height then
220-
TableList.concat(args, '-s', tostring(width)..'x'..tostring(height))
243+
List.concat(args, '-s', tostring(width)..'x'..tostring(height))
221244
end
222-
TableList.concat(args, '-y', file:getPath())
245+
List.concat(args, '-y', file:getPath())
223246
return args
224247
end
225248

fcut.lua

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ local FileHttpHandler = require('jls.net.http.handler.FileHttpHandler')
1919
local RestHttpHandler = require('jls.net.http.handler.RestHttpHandler')
2020
local ZipFileHttpHandler = require('jls.net.http.handler.ZipFileHttpHandler')
2121
local TableHttpHandler = require('jls.net.http.handler.TableHttpHandler')
22-
local WebSocketUpgradeHandler = require('jls.net.http.ws').WebSocketUpgradeHandler
22+
local WebSocketUpgradeHandler = require('jls.net.http.WebSocket').UpgradeHandler
2323

2424
-- Project required modules
2525

@@ -87,6 +87,8 @@ local config = tables.createArgumentTable(arg, {
8787
emptyPath = 'config',
8888
schema = CONFIG_SCHEMA,
8989
aliases = {
90+
ffmpeg = 'ffmpeg.ffmpeg',
91+
ffprobe = 'ffmpeg.ffprobe',
9092
h = 'help',
9193
ll = 'loglevel',
9294
p = 'project',
@@ -133,7 +135,7 @@ end
133135
logger:info('Cache directory is '..cacheDir:getPath())
134136

135137
local ffmpeg = Ffmpeg:new(cacheDir)
136-
ffmpeg:configure(config)
138+
ffmpeg:configure(config.ffmpeg)
137139

138140
-- Application local functions
139141

@@ -218,6 +220,7 @@ local function endExport(exportContext, webSocket, exitCode)
218220
return webSocket:sendTextMessage('\n -- exit code '..tostring(exitCode)..' ------\n\n'):finally(function()
219221
logger:info('end export')
220222
Map.deleteValues(exportContexts, exportContext)
223+
ffmpeg:deleteTempFiles()
221224
return webSocket:close()
222225
end)
223226
end
@@ -292,6 +295,7 @@ local httpContexts = {
292295
end,
293296
['checkFFmpeg?method=POST'] = function(exchange)
294297
local status, reason = ffmpeg:check()
298+
-- TODO get ffmpeg version
295299
return {
296300
status = status,
297301
reason = reason,
@@ -351,7 +355,7 @@ local httpContexts = {
351355
return false
352356
end,
353357
['export(requestJson)?method=POST&Content-Type=application/json'] = function(exchange, parameters)
354-
local commands = ffmpeg:createCommands(parameters.filename, parameters.parts, parameters.options or {}, parameters.seekDelayMs)
358+
local commands = ffmpeg:createCommands(parameters.filename, parameters.parts, parameters.options or {}, parameters.parameters or {})
355359
local exportId = strings.formatInteger(system.currentTimeMillis(), 64)
356360
logger:info('export '..exportId..' '..tostring(#commands)..' command(s)')
357361
exportContexts[exportId] = {
@@ -386,7 +390,7 @@ local httpContexts = {
386390
if config.webview.disable then
387391
local httpServer = require('jls.net.http.HttpServer'):new()
388392
httpServer:bind(config.webview.address, config.webview.port):next(function()
389-
httpServer:createContexts(httpContexts)
393+
httpServer:addContexts(httpContexts)
390394
if config.webview.port == 0 then
391395
print('FCut HTTP Server available at http://localhost:'..tostring(select(2, httpServer:getAddress())))
392396
end
@@ -409,7 +413,7 @@ else
409413
url = url..'extensions/'..config.extension..'/'
410414
end
411415
require('jls.util.WebView').open(url, {
412-
title = 'Fast Cut (Preview)',
416+
title = 'Fast Cut',
413417
resizable = true,
414418
bind = true,
415419
width = config.webview.width,

fcutSchema.lua

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,13 +47,26 @@ return {
4747
maximum = 32,
4848
},
4949
ffmpeg = {
50-
title = 'The ffmpeg path',
51-
type = 'string',
52-
default = (require('jls.lang.system').isWindows() and 'ffmpeg\\ffmpeg.exe' or '/usr/bin/ffmpeg'),
53-
},
54-
ffprobe = {
55-
title = 'The ffprobe path, the default value is computed from the ffmpeg path',
56-
type = 'string',
50+
type = 'object',
51+
additionalProperties = false,
52+
properties = {
53+
ffmpeg = {
54+
title = 'The ffmpeg path',
55+
type = 'string',
56+
default = (require('jls.lang.system').isWindows() and 'ffmpeg\\ffmpeg.exe' or '/usr/bin/ffmpeg'),
57+
},
58+
ffprobe = {
59+
title = 'The ffprobe path, the default value is computed from the ffmpeg path',
60+
type = 'string',
61+
},
62+
seekDelay = {
63+
title = 'Seek delay in milli seconds',
64+
type = 'integer',
65+
default = 0,
66+
minimum = -2,
67+
maximum = 60000,
68+
},
69+
}
5770
},
5871
loglevel = {
5972
title = 'The log level',

htdocs/fcut.html

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
<html><head lang="en">
33
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
44
<meta charset="UTF-8">
5-
<title>Fast Cut (Preview)</title>
5+
<title>Fast Cut</title>
66
<link rel="stylesheet" href="fcut.css">
77
<link rel="stylesheet" href="page.css">
88
<link rel="stylesheet" href="FileChooser.css">
@@ -26,7 +26,7 @@
2626
<span class="bar-flex-row-content"></span>
2727
<button v-on:click="pages.navigateTo('help')"><i class="fas fa-question"></i></button>
2828
</div>
29-
<h2>Fast FFmpeg Cutter (Preview)</h2>
29+
<h2>Fast FFmpeg Cutter</h2>
3030
<div class="flow">
3131
<button v-on:click="addSources().then(function() { pages.navigateTo('preview'); })" class="home"><i class="far fa-file-video"></i> Select Videos</button>
3232
<i class="fas fa-chevron-right"></i>
@@ -245,6 +245,12 @@ <h2>Export</h2>
245245
<div class="input-bar">
246246
<label><input type="checkbox" v-model="exportMapAllStreams" /> Map all streams</label>
247247
</div>
248+
<!--
249+
<div class="input-bar">
250+
<label>Subtitles:</label>
251+
<button v-on:click="selectFiles(true, false, '.srt').then(function(filenames) { subtitles = filenames; })"><i class="fas fa-add"></i></button>
252+
</div>
253+
-->
248254
<br />
249255
<div class="bar-flex-row">
250256
<label>File:</label>

0 commit comments

Comments
 (0)