Avidemux Forum

Participate => Documentation & Tips => Topic started by: butterw on January 17, 2021, 01:18:41 PM

Title: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 17, 2021, 01:18:41 PM
Avidemux Scripting Tips

Avidemux uses python syntax Tinypy scripts (.py) for its project files and custom user scripts (which can be added to the Auto and Custom menus):
https://www.avidemux.org/admWiki/doku.php?id=using:tinypy
Tinypy can be used for batch processing, to apply an output settings template on the loaded video, or to add commands to the custom menu in Avidemux.

The simplest way to get a working a .py script is just to save your current project (File > Project Script > Save As Project...)
It is then possible to edit/modify the python script as desired.
The script can then be run with  (File > Project Script > Run Project...)

In this thread I'll post a few tips that could be useful to first-time users and that are not mentionned in the previous link.
Please posts your own tips, examples, or links to relevant threads.

Thread index:
#Some functions  (https://avidemux.org/smif/index.php?msg=90763)
#File Management  (https://avidemux.org/smif/index.php?msg=90774)
#Segments  (https://avidemux.org/smif/index.php?msg=91098)
#Saved project script files (.py) (https://avidemux.org/smif/index.php?msg=91788)
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 17, 2021, 01:49:31 PM
Using the Interactive Shell:
To test a script it is easier (and safer) to first try with the interactive shell (Tools > Scripting Shell) where you can execute blocks of code.
- to display the value of x you need to use print(x) or for more elegant output: print("x: ", x) and Enter a blank line after.
- or it could be shortened by defining a shorter custom print function pp:
def pp(*args): print("  ", *args) - if you evaluate an undefined variable or function name XXX, you will get an Exception KeyError: XXX
- The shell variables are persistent until you close Avidemux (this is not the case for variables in scripts).
- ! In Python, indentation is syntax. Incorrect indentation or mixing tabs and spaces will cause ("tokenize") errors.
- ! Tinypy only supports a very small subset of Python. Many built-in functions missing: type, isinstance, dir, etc.
- no string manipulation beyond (+, s.replace("ab", "cde")) is possible !!!, len(s) and the in operator work
- tuples are converted to lists
- functions do not support named parameters 
- 1e3 evals to 1 !!!
- import math works , it's also possible to write your functions in a separate file (but it's simpler to just copy/paste your code in your source file)
- The list of all available functions can be obtained from the shell, by typing help(), Classname.help() (ex: Editor.help())
- ! Python bindings are not yet available for many Avidemux menu commands.
- ! With some bindings the preview and markers will not update in the GUI until you close the shell.
- a basic dialog box (with checkbox, combo-box, integer spin-box, File Select) can be displayed for user input and info.
 

Display a popup info message:
gui = Gui()
## !!! Affected by Preferences > Message Level: if set to Error Only, use displayError instead of displayInfo
# gui.displayInfo("Append", "Done") # Displays a popup message
gui.displayError("Append", "almost Done..."+"\n"+"fully Done")
! You need to ensure the title and the message to display in the popup are strings.
msg = str(2/3) # "0.666667"
gui.displayError("Test", msg)

A simple script loading a video, the useful info is in # comments:
#PY  <- Needed to identify #
# !!! No very long lines: line cannot exceed 200 chars: split it and add "\" at the end of each split line.

## Open file tmp1.mp4 (fname must be correct !!!) and displays a message
fname = "B:/Videos/out/tmp1.mp4" #a str variable, on Windows use / OR  filepaths will need to be escaped : "B:\\Videos\\out\\tmp1.mp4"
adm = Avidemux()
if not adm.loadVideo(fname):
    raise("Cannot load " + fname) #Exception displayed in msgbox popup
w = adm.getWidth() #the width of loaded video
h = adm.getHeight()
print(" res:", w, "*", h) #prints to admlog.txt (Help > Advanced > Open Application Log)

The loaded video replaces the existing video, the A,B markers are set to the first and the last frame (end) of the video respectively. Current Position is set to the first frame. DefaultSettings.py file is loaded.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 17, 2021, 05:00:07 PM
!!! New functions have been added in v2.7.7 (in bold)
Attempting to use the new functions in earlier versions of Avidemux will fail with TinyPy:Exception Key Error:xxx, where xxx is the new command.
Note: filepath designates a str, ex: "B:/Videos/out/tmp1.mp4"

# Loading/Appending/Saving
adm.loadVideo(filepath)
adm.appendVideo(filepath) #Multiple source files !
adm.clearSegments() #n=0, end=0
adm.addSegment(source_int, start_pts, duration) #source_int=0 if there is only one source file
ed.nbSegments() #0 if no video is loaded, n>1 if the source has multiple segments
! input can be a project, edits generate multiple segments even if there is only one source video file.
ed.nbVideos() #the number of source video files. Appending the same video multiple times counts as multiple source videos
ed.getVideoDuration()   ##Current end, pts after last frame. real video duration is: end-firstframe
adm.save(filepath) # overwrites, 0 if it couldn't save.
adm.saveAudio(int_idx, filepath) #Saves the raw audio (ex: .aac) with the corresponding track index.   
! What is saved depends on A, B Marker values. To save to the previous frame, set markerB to ptsB-1   
! Values of end and current position should be coherent when saving.

# Video File Properties
adm.getWidth(); adm.getHeight()
adm.getFps1000() # average frame rate multiplied by 1000, so a 25fps video will return 25000
adm.getVideoCodec() #ex: H264
adm.audioTotalTracksCount()

# Audio
adm.audioClearTracks()  ##Disable all Audio tracks (Mute)
adm.audioTracksCount() ##Current number of Audio tracks
adm.audioAddExternal(filepath) ## Load an external Audio track

# Get Position timestamps (Pts in micro-seconds)
ed.getCurrentPts()
ed.getPrevKFramePts(-1) #previous Keyframe based on current position, returns -1 on the first KeyFrame
ed.getNextKFramePts(-1) #next Keyframe based on current position, -1 on/after last KeyFrame
## or (pts) #previous/next Keyframe based on pts value.

# Seeking << updates
adm.seekFrame(n) #seeks with n frames offset, ex: 1, 10, -1  updates the GUI. Returns 0 if the requested seek is limited by the first/last video frame.
adm.seekKeyFrame(int) #ex: -1 prev, +1 next, +10 Keyframes #updates the preview from interactive shell, ! always returns None 
## (0): no-op if current frame is a keyframe, otherwise seeks to previous keyframe.
adm.setCurrentPts(pts) #expects exact pts, seeks to the last keyframe before the given time otherwise. pts must be in [0, end] range, returns 0 otherwise.

# Get and Set the A, B selection markers
adm.markerA #default value: 0
adm.markerB #default value: end

# Saving images:
adm.saveJpeg(filepath); adm.savePng(filepath); adm.saveBmp(filepath)

# Video Codec, Filters
adm.videoCodec("Copy") #no re-encoding Copy Mode
adm.videoCodec("x264") ##set video codec (codec parameters will be set to Avidemux defaults, crf=20. The full x264 codec configuration is a long list of parameters -> for a more compact approach set a Profile), returns 1 if parameters were applied.
adm.videoCodecSetProfile("x264", "iPhone") #set video codec + the settings of profile iPhone.json, returns 1 if the profile could be loaded
adm.clearVideoFilters()  #removes all video filters
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 17, 2021, 10:35:20 PM
The following sample script will try to cut currently loaded video in approximately equal chunks in copy mode with a single audio track, files are named 00.mp4 up to 99.mp4, the muxer configuration is not included:

adm = Avidemux()
ed = Editor()
gui = Gui()
# If no video is loaded, we will crash. The check below doesn't work.
if 0 == ed.nbSegments():
    gui.displayError("No video loaded!")
    return
# video
adm.videoCodec("Copy")
# audio
tracks = adm.audioTracksCount()
if tracks > 0:
    adm.audioClearTracks()
    adm.setSourceTrackLanguage(0,"Unknown")
    adm.audioAddTrack(0)
    adm.audioCodec(0, "copy")
# choose output directory
outdir = gui.dirSelect("Select the target folder")
if outdir is None:
    gui.displayError("No output folder selected, bye")
    return
# choose chunk duration (1 to 10 minutes)
spin = DFInteger("Chunk duration in minutes:",1,10)
spin.value = 2
dialog = DialogFactory("Split video in chunks")
dialog.addControl(spin)
if 1 != dialog.show():
    return
# set some variables
chunk = spin.value
chunk *= 60        # chunk duration in seconds
chunk *= 1000000  # chunk duration in microseconds (us)
duration = ed.getVideoDuration() # video duration in us
start = 0
end = chunk
sep = "/"
ext = ".mp4"
count = 1
zero = "0"
# loop!
while True:
    adm.markerA = start
    if end > duration:
        end = duration
    adm.markerB = end
    if count > 9:
        zero = ""
    outfile = outdir + sep + zero + str(count) + ext
    if 1 != adm.save(outfile):
        gui.displayError("Error saving "+outfile)
        return
    if end >= duration:
        break
    start += chunk
    end += chunk
    count += 1
    if count > 99:
        gui.displayError("Number of chunks exceeds max = 99")
        return
# done
gui.displayInfo("Everything done","")
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 18, 2021, 09:52:33 AM
File Management

# Directory and file selection dialogs. They return filepaths, or None if cancel was chosen
folder = gui.dirSelect("Select folder")
filepath = gui.fileReadSelect("Select input file")
filepath = ed.getRefVideoName(0)  #filepath of the first loaded file
gui.fileWriteSelect("Select output file") # If the file already exists, you'll be prompted to overwrite it
gui.fileWriteSelectEx("Select output file", "mp4") # same as gui.fileWriteSelect if extension is "": no filter is applied
gui.fileReadSelectEx("Select input file", "mp4")

# Filepath
lst=get_folder_content("/home/foo/videos", "avi") # returns a list of filepaths, None if no files match or the folder isn't found
- get_file_size(filepath) #in bytes. A float equal to 2**32-1 is returned if the filepath isn't found
- dirname(filepath); basename(filepath)
- splitext(filepath_with_ext) #returns a list of two elements [root, ext]
filepath is split into root + extension. The period between root and extension is dropped.

# Examples
fpath = "b:/Videos/file1.mp4"
dirname(fpath) #"b:/Videos"
bname = basename(fpath) #"file1.mp4"
#dirname/basename functions return None if the filepath isn't found
if fpath is not None: ext=splitext(fpath)[1] #"mp4"
splitext(fpath)[0] #"b:/Videos/file1"
splitext(bname)[0] #"file1"
fname_root, ext = splitext(fpath)

#ex: fileWriteSelect(); fileWriteSelect("Title"); fileWriteSelect(ext="mp4"); fileWriteSelect("Title", "mp4")
def fileWriteSelect(title="Please Select output file", ext=None):
# this custom function uses the container extension from the GUI by default in the fileWriteSelect Dialog
if ext is None: ext=adm.getOutputExtension()
return gui.fileWriteSelectEx(title, ext)

#ex: filelist = get_folder_content("b:/Videos", "mp4"); totalFilesize(filelist) #117.456000
def totalFilesize(filelist):
# returns total Filesize in MiBytes (1e6 Bytes)
# filelist: a list of filepath strings
out = 0.0
for fpath in filelist:
out+= get_file_size(fpath)/1000000.0
return out
 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 19, 2021, 10:20:35 AM
Some basic code blocks

# import math
# math.pow(10, 6) ##1e6
# math.sqrt(16.5)
adm=Avidemux(); ed=Editor(); gui=Gui()
sec = 1000*1000

#shorter pretty print for interactive shell
def pp(*args): print("  ", *args)   
def pt(*args): #print times in s instead of micro-seconds
res=""
for elt in args: res+= "  " + str(elt*0.000001)
print(res)
def gd(): return ed.getVideoDuration() #get end
def gt(): return ed.getCurrentPts()
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 19, 2021, 08:24:31 PM
Quote from: butterw on January 18, 2021, 09:52:33 AM# Seeking
adm.seekKeyFrame(int) ##ex: -1, +1, +10 Keyframes #updates the preview from interactive shell
ed.nextFrame()

# Saving images:
adm.saveJpeg(filepath); adm.savePng(filepath); adm.saveBmp(filepath)

It is worth noting that ed.nextFrame() contrary to adm.seekKeyFrame doesn't update the preview buffer adm.saveJpeg and the other two methods use as source. This means that while it is possible to export each N-th keyframe as an image, doing the same for each N-th frame is not possible.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 20, 2021, 12:18:43 PM
Quote from: eumagga0x2a on January 17, 2021, 10:35:20 PM# choose output directory
outdir = gui.dirSelect("Select the target folder")
if outdir is None:
    gui.displayError("No output folder selected, bye")
    return

- gui.dirSelect("Select the target folder") generates an exception if Cancel is chosen (at least on Windows).
- Also gui.displayError requires 2 string parameters.

The correct code (to display the error message) is as follows:
try:
    outdir=gui.dirSelect("Select the target folder")
except:
    gui.displayError("No output folder selected", "bye");
    return


I'm running a script from the GUI. Is there a way to get the name and path of the loaded video needed for adm.save(filepath) ?
The closest I get is opening the filesave dialog with gui.fileWriteSelect("Output Filename"), but the extension is not auto-selected.
ex:
- loaded: "B:/videos/myvideo.mp4"
- gui.fileWriteSelect default value: B:/videos/myvideo.*

out=gui.dirSelect("Dir") opens the input dir by default.

Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 21, 2021, 12:33:22 AM
Quote from: butterw on January 20, 2021, 12:18:43 PM- gui.dirSelect("Select the target folder") generates an exception if Cancel is chosen (at least on Windows).

It shouldn't. Does your Avidemux build include this commit (https://github.com/mean00/avidemux2/commit/6e6746c0a837b581fafd57fa0811ece94a4fb6c3)?

Quote from: butterw on January 20, 2021, 12:18:43 PM- Also gui.displayError requires 2 string parameters.

Oops, indeed. Thank you for pointing out. A fixed version below, this time I really tried it :-D

The check for video not loaded actually works now.

adm = Avidemux()
ed = Editor()
gui = Gui()
if 0 == ed.nbSegments():
    gui.displayError("Error","No video loaded!")
    return
# video
adm.videoCodec("Copy")
# audio
tracks = adm.audioTracksCount()
if tracks > 0:
    adm.audioClearTracks()
    adm.setSourceTrackLanguage(0,"Unknown")
    adm.audioAddTrack(0)
    adm.audioCodec(0, "copy")
# choose output directory
outdir = gui.dirSelect("Select the target folder")
if outdir is None:
    gui.displayError("Error","No output folder selected, bye")
    return
# choose chunk duration (1 to 10 minutes)
spin = DFInteger("Chunk duration in minutes:",1,10)
spin.value = 2
dialog = DialogFactory("Split video in chunks")
dialog.addControl(spin)
if 1 != dialog.show():
    return
# set some variables
chunk = spin.value
chunk *= 60        # chunk duration in seconds
chunk *= 1000000  # chunk duration in microseconds (us)
duration = ed.getVideoDuration() # video duration in us
start = 0
end = chunk
sep = "/"
ext = ".mp4"
count = 1
zero = "0"
# loop!
while True:
    adm.markerA = start
    if end > duration:
        end = duration
    adm.markerB = end
    if count > 9:
        zero = ""
    outfile = outdir + sep + zero + str(count) + ext
    if 1 != adm.save(outfile):
        gui.displayError("Error","Error saving "+outfile)
        return
    if end >= duration:
        break
    start += chunk
    end += chunk
    count += 1
    if count > 99:
        gui.displayError("Error","Number of chunks exceeds max = 99")
        return
# done
gui.displayInfo("Everything done","")

Quote from: butterw on January 20, 2021, 12:18:43 PMI'm running a script from the GUI. Is there a way to get the name and path of the loaded video needed for adm.save (//adm.save)(filepath) ?

No, I don't think so. The full path can be obtained in Avidemux using admCoreUtils::getLastReadFile(std::string &fullpath), but it is not wired into Python scripting interface.

Quote from: butterw on January 20, 2021, 12:18:43 PMThe closest I get is opening the filesave dialog with gui.fileWriteSelect("Output Filename"), but the extension is not auto-selected.

Regarding the file dialog, gluing fileWriteSelect via pyFileSelWrite to GUI_FileSelReadExtension is on my todo list (probably post-release).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 21, 2021, 12:33:31 PM
Quote from: eumagga0x2a on January 21, 2021, 12:33:22 AM
Quote from: butterw on January 20, 2021, 12:18:43 PM- gui.dirSelect("Select the target folder") generates an exception if Cancel is chosen (at least on Windows).

It shouldn't. Does your Avidemux build include this commit (https://github.com/mean00/avidemux2/commit/6e6746c0a837b581fafd57fa0811ece94a4fb6c3)?
Indeed, it's fixed in the latest dev build.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 23, 2021, 04:27:19 PM
Split scripts

- eumagga0x2a export script saves in ex:2min chunks.
- A script could split in a chosen number of parts on the same principle.

For custom splits using the GUI preview, it's possible to split in up to 4 parts by using the current position and the Markers as split points.
! To ensure there is no overlap between parts (with Copy mode Save) markerA needs to be set on a keyframe. Marker B needs to be set on the frame before the keyframe.

ed.getPrevKFramePts(pts) has recently been added in dev version (v2.7.7-29 Jan 2021).
This could for example be used to check whether a position timestamp is a keyframe:
pts = ed.getCurrentPts()
if pts == ed.getPrevKFramePts(pts+1): print("current frame is a keyframe")

ed.getCurrentFlags()
returns 16 for I-FRM (2)
16384 for B
0 for P
-1 for ?, there is no frame


I've written a simple script to allow the user to split on Markers (set on keyframes in the GUI) without overlap between parts.
My problem is that I need to know the timestamp of the frame before the keyframe to be able to split without overlapping the marker B frame.
It seems currently the only way to do this would be to iterate ed.nextFrame() from the previous keyframe ? 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 26, 2021, 09:55:47 AM
I see some new methods have just been added for seeking.

I'm assuming it is preferable to avoid seeking when possible and just look-up Pts ?
If it's easy to do I would suggest adding:
getNextPts(n=1) #get the Pts of a next (or previous) nth Frame
 





Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 26, 2021, 02:05:14 PM
The main addition is seekFrame(int N) in Avidemux() which allows to display a frame N frames from the current (so that seekFrame(0) is no-op).

adm = Avidemux()
adm.seekFrame(-1)

displays the previous frame.

The main change is getCurrentPts() in Editor() returning the PTS of the displayed picture, not the maximum PTS output by the decoder.

Therefore please don't use nextFrame() in Editor() unless you just need to test that decoding succeeds. Current PTS returned by getCurrentPts() is not updated on nextFrame(). Please use adm.seekFrame(1) instead.

A minor change is seekKeyFrame(int N) counting from the closest earlier keyframe if the current position is not at a keyframe. This means, adm.seekKeyFrame(0) is no-op when the current picture is a keyframe, will seek to the previous keyframe if not.

Quote from: butterw on January 26, 2021, 09:55:47 AMI'm assuming it is preferable to avoid seeking when possible and just look-up Pts ?

This would be preferable by far, but very unreliable before the entire video has been decoded. This is also the reason why 2.5.x frame-based approach turned out to be not viable with modern codecs.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 26, 2021, 02:19:24 PM
Quote from: butterw on January 23, 2021, 04:27:19 PMed.getPrevKFramePts(pts) has recently been added in dev version.
If I understand correctly, this could for example be used to check whether a position timestamp is a keyframe:
pts = ed.getCurrentPts()
if pts == ed.getPrevKFramePts(pts+1): print("current frame is a keyframe")

Such workarounds should become unnecessary once getCurrentFrameFlags() (https://github.com/mean00/avidemux2/blob/d8751a0eff32e010d88a1cec6e194938caf7e3c5/avidemux/common/ADM_editor/include/IEditor.h#L48) gets wired to the Python interface.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 26, 2021, 06:35:01 PM
Done (https://github.com/mean00/avidemux2/commit/f2c854aabf77f1a4123626f0a4e9484b028f0bdf).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 27, 2021, 12:42:30 AM
Quote from: eumagga0x2a on January 21, 2021, 12:33:22 AM
QuoteThe closest I get is opening the filesave dialog with gui.fileWriteSelect("Output Filename"), but the extension is not auto-selected.

Regarding the file dialog, gluing fileWriteSelect via pyFileSelWrite to GUI_FileSelReadExtension is on my todo list (probably post-release).

Already happened (https://github.com/mean00/avidemux2/commit/e1756d398bd9269db1f1f2bd0afcfa9cfcf1b20d), albeit not verbatim, of course (I mentioned GUI_FileSelReadExtension by error, it serves a different purpose).

Please use fileReadSelectEx / fileWriteSelectEx functions in pyGui to open such file dialogs. If the first argument is an empty string, a default caption is used. Only a single extension (e.g. "mkv") is supported.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 27, 2021, 09:51:29 AM
The new method is called with gui.fileWriteSelectEx("title", "mkv") ? 
Does the save dialog use the extension of the output container by default ? That's the most important thing IMO.

- to mux to a different container you have to explicitly set the container with ex: adm.setContainer("MKV") or via GUI
- there is currently no way of knowing the container set in the GUI.
- there is currently no way of knowing the path/extension of the input file.
- there is currently no way of splitting extension from filename

For my split script, I prefix the output save name based on the filename (including extension) chosen in the save dialog:
save name = p1_filename
if filename already exists, there is a overwrite warning (! filename will not be overwritten, but p1_filename will be overwritten silently if it does exists).

Knowing the path name would be useful to be able to bypass the gui.fileWriteSelect (single output filename path selector dialog). Often with a script (ex: ffmpeg command-line) you just need to save  prefixed (or postfixed) filenames to an output dir.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 27, 2021, 11:34:13 AM
Quote from: butterw on January 27, 2021, 09:51:29 AMThe new method is called with gui.fileWriteSelectEx("title", "mkv") ?

This is correct.

Quote from: butterw on January 27, 2021, 09:51:29 AMDoes the save dialog use the extension of the output container by default ? That's the most important thing IMO.

No. File dialog minds its business, uses whatever the caller passes to it and doesn't ask impolite questions about the caller's affairs ;D

Quote from: butterw on January 27, 2021, 09:51:29 AM- to mux to a different container you have to explicitly set the container with ex: adm.setContainer("MKV") or via GUI

Yes, sure.

Quote from: butterw on January 27, 2021, 09:51:29 AM- there is currently no way of knowing the container set in the GUI.

True, ADM_Composer::getCurrentMuxer() needs to be wired into tinyPy.

Quote from: butterw on January 27, 2021, 09:51:29 AM- there is currently no way of knowing the path/extension of the input file.

If you use gui.fileReadSelect with or without Ex, you have got the full path. Once the file is loaded, there is no (safe) way. Handling of input is encapsulated in demuxers, the "file" can be a frame server or a large set of files, logically treated by demuxer as one single file.

Quote from: butterw on January 27, 2021, 09:51:29 AM- there is currently no way of splitting extension from filename

Indeed, not in the subset of Python implemented by tinyPy. Easily accomplished with external tools on command line.

Quote from: butterw on January 27, 2021, 09:51:29 AMif filename already exists, there is a overwrite warning (! filename will not be overwritten, but p1_filename will be overwritten silently if it does exists).

The overwrite warning is triggered by file dialog (probably not on macOS, it does its own thing), if you alter the filename behind its back, how should it catch it??
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 27, 2021, 01:23:00 PM
Quote from: eumagga0x2a on January 27, 2021, 11:34:13 AM
Quote from: butterw on January 27, 2021, 09:51:29 AM- there is currently no way of knowing the container set in the GUI.
True, ADM_Composer::getCurrentMuxer() needs to be wired into tinyPy.

Got it.
But it's not clear why 2 methods are needed. Why not just replace the existing gui.fileWriteSelect with something like gui.fileWriteSelectEx(title="", ext=None) ?

Quote from: eumagga0x2a on January 27, 2021, 11:34:13 AMOnce the file is loaded, there is no (safe) way. Handling of input is encapsulated in demuxers, the "file" can be a frame server or a large set of files, logically treated by demuxer as one single file.
If there was a way to access it, the filename as displayed by the GUI would be useful (without extension, with filepath).

Quote from: eumagga0x2a on January 27, 2021, 11:34:13 AMThe overwrite warning is triggered by file dialog (probably not on macOS, it does its own thing), if you alter the filename behind its back, how should it catch it??
Likely not worth worrying about as it just complicates things, but the following may be possible:
- Disabling the overwrite warning by passing an extra parameter to the write dialog.
- Handling the overwrite warning from python using get_folder_content("/home/foo/videos","mkv")
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 28, 2021, 12:41:21 AM
Quote from: butterw on January 27, 2021, 01:23:00 PMWhy not just replace the existing gui.fileWriteSelect with something like gui.fileWriteSelectEx(title="", ext=None) ?

For the same reason we have audioSetNormalize(int,int,int) and audioSetNormalize2(int,int,int,int): in order to avoid breaking existing scripts. However, you have the point as the impact would be really minimal this time as fileWriteSelect / fileReadSelect is not used in scripts Avidemux auto-generates.

Quote from: butterw on January 27, 2021, 01:23:00 PM- Disabling the overwrite warning by passing an extra parameter to the write dialog.

I actually remembered it wrong, QFileDialog::DontConfirmOverwrite is always set in the file dialog (Avidemux tries to handle collisions on its own), except of on macOS where this flag is useless (if you ignore the warning Avidemux shows, your way gets blocked by one more dialog probably from the OS anyway). I'll need to recheck.

Quote from: butterw on January 27, 2021, 01:23:00 PM- Handling the overwrite warning from python using get_folder_content("/home/foo/videos","mkv")

This was an idea I wanted to suggest. Unsure whether it would be sufficient, however.

Quote from: eumagga0x2a on January 27, 2021, 11:34:13 AMADM_Composer::getCurrentMuxer() needs to be wired into tinyPy.

Done (https://github.com/mean00/avidemux2/commit/ad97816bad47dc554d7278a0359d3de6fb6ef5e7).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 28, 2021, 10:34:06 AM
If I understand correctly:
- adm.getOutputExtension() #New: returns the extension of the current container, ex="mkv"
- adm.setContainer("mkv")  #sets the container to mkv

- gui.fileWriteSelectEx("title") #The new method makes sense for backwards compat if it sets the extension to the current container by default as opposed to the current gui.fileWriteSelect("title").
The new method would almost always be used rather than the old one.

From the python user code side it only saves one line of code, but it is simpler to use:
gui.fileWriteSelectEx("title")

vs
ext = adm.getOutputExtension()
gui.fileWriteSelectEx("title", ext)
 
Regarding the fileWriteSelect Overwrite warning:  it gets triggered on Windows.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 28, 2021, 01:24:25 PM
Quote from: butterw on January 28, 2021, 10:34:06 AMThe new method makes sense for backwards compat if it sets the extension to the current container by default

You are right, backward compatibility is not that important here. Tying the filter to muxer is not viable as it would break the dialog for all other purposes.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 28, 2021, 02:36:00 PM
Just noticed that the default name is the same as the input using gui.fileWriteSelect.
If it's possible to get it without opening gui.fileWriteSelect dialog, it would be useful in scripts to set the save name directly to something meaningful.

I am assuming default function parameter values and variable parameter numbers are possible in bindings ?
If not it can be done in Python with a wrapper.
 
- gui.fileWriteSelect("title") #current, no extension selected, default filename: video.*

- fileWriteSelectEx("title") #extension tied to the current muxer (ex:mkv), default filename: video.mkv
most frequently used, avoids using .mp4 extension with an mkv file

- fileWriteSelectEx("title", "jpg") # default filename: video.jpg
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 28, 2021, 02:58:37 PM
Quote from: butterw on January 28, 2021, 02:36:00 PMJust noticed that the default name is the same as the input using gui.fileWriteSelect.

The file dialog obtains the default filename from the list of last files in Avidemux, it also checks whether the directory of that last file exists and if it does, it checks for filename collision. I doubt that this needs to be exposed via Python interface. So the only way to know which file is loaded, is

file = gui.fileWriteSelectEx("title","extension") # if extension is "", no filter is applied
if file is None: # dialog cancelled
    return
# now do whatever you want with the full path stored in string "file"

I still need to change the API for fileReadSelect / fileWriteSelect.

After some deliberation I am going to keep both variants, else people who switch between versions will have a hard time.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on January 31, 2021, 04:12:35 PM
I've noticed the following issue on Windows:
- When run project or recent project is used, there is a file lock on the opened script, which stays in place until Avidemux is closed.
- This can be troublesome to edit/test scripts (saves fail),
- Closing the file doesn't help.
 
- The same issue does not occur when the script is called from the custom menu (but the menu only refreshes on app startup).
 


Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on January 31, 2021, 06:17:06 PM
Good catch, should be fixed now (https://github.com/mean00/avidemux2/commit/462df43c1287a3b2bf2a3b28c9770a7d4bad54bc), thank you for your report.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 01, 2021, 01:24:45 PM
adm.getOutputExtension() is causing an immediate crash on v2.7.7dev 29/01/2021 VsWin64, on Win10 from Shell.
admlog.txt is not updated.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 01, 2021, 03:09:23 PM
Oops, fixed (https://github.com/mean00/avidemux2/commit/06d5d52c8eeef6e46a0cb4a92d41cad1303a6c09). Thank you very much.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 01, 2021, 04:01:37 PM
- There isn't currently any display in the GUI of the current number of open video files (can be >1 via appends) https://avidemux.org/smif/index.php?msg=90806

If possible, it would be useful to have a python binding to access this info (nbSegments changes when edits are made).
 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 01, 2021, 04:16:23 PM
If the same file is appended to itself, the number of reference videos will be 2 and the number of open files (I mean videos) will be 2 but it will be still one and the same video. I don't think such a function would be of much use.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 01, 2021, 04:27:34 PM
Actually, the existing dumpRefVideo() funciton in pyEditor breaks the tinyPy shell when called. Need to look into that.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 01, 2021, 06:00:49 PM
Quote from: eumagga0x2a on February 01, 2021, 04:27:34 PMActually, the existing dumpRefVideo() funciton in pyEditor breaks the tinyPy shell when called. Need to look into that.

From my testing, there are a number of ed functions that don't seem to do much useful or raise exceptions. What are the dump functions used for ?


Quote from: eumagga0x2a on February 01, 2021, 04:16:23 PMIf the same file is appended to itself, the number of reference videos will be 2 and the number of open files (I mean videos) will be 2 but it will be still one and the same video. I don't think such a function would be of much use.

It doesn't seem illogical to me that appending a video on itself should result in a count of 2 video sources. If it was the desired result, the user could have copy/pasted the video and avoided this.

The problem with appending files (or loading a project involving appended files) is that currently no information on the current status is provided by Avidemux. There is no way of knowing what is going on other than saving and analysing the project file (which requires expertise and may also be unpractical if the project involves a large number of files).

From what I understand the video count is available in Avidemux, but there is no plan to use it currently in the GUI. Exposing getVideoCount() (or whatever it should be named) in python would allow custom menu scripts to use this information.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 02, 2021, 10:06:53 AM
flist = get_folder_content("B:/pics", "jpg")
is causing an immediate crash if the folder contains >=200 .jpg files.

Trying to append with TinyPy when no video is loaded results in  an error message "Assert failed..." then crash.py. A further test should be done to avoid this situation and instead display a clear error message and avoid crash.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 02, 2021, 08:19:40 PM
Quote from: butterw on February 02, 2021, 10:06:53 AMTrying to append with TinyPy when no video is loaded results in  an error message "Assert failed..." then crash

Totally avoidable, fixed by [editor] Open video instead of crashing when appendFile is called and no video is loaded yet (https://github.com/mean00/avidemux2/commit/731a4d22b03685fd5b6d0686b16ab4aaf920c099).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 03, 2021, 12:00:53 AM
Quote from: butterw on February 01, 2021, 06:00:49 PMFrom my testing, there are a number of ed functions that don't seem to do much useful or raise exceptions. What are the dump functions used for ?

These functions can be useful for debugging, like hexDumpFrame which helped a lot during the work on solving issues with stream copy mode for AnnexB type H.264 and HEVC streams. None raise exceptions when used in the right way (i.e. with a video loaded), but evaluating uninitialized memory is indeed bad, should not occur after [editor] Let caller of ADM_Composer::getVideoPtsDts know when returned values are invalid (https://github.com/mean00/avidemux2/commit/93d54d70fc2e2b9336aa75bf0c2f20f3ca16bd78).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 03, 2021, 09:39:20 AM
What does Dump mean ? if it extracts some data, what does it do with it ?


With regard to adding Python bindings, how is it done ?

ex: adm.getOutputExtension()
Get the default filename extension for the current muxer, ex: "mkv"
https://github.com/mean00/avidemux2/commit/ad97816bad47dc554d7278a0359d3de6fb6ef5e7

In avidemux_plugins/ADM_scriptEngines/tinyPy/src
- ADM_pyAvidemux.h:
char *pyGetContainerEx(IEditor *editor);
- ADM_pyAvidemux.cpp: write the C++ binding function, which calls editor->getCurrentMuxer()

add the python binding to binding/adm.admPyClass:
/* METHOD */ str pyGetContainerEx:getOutputExtension(void)

ADM_pyAvidemux.cpp is autogenerated ?


 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 03, 2021, 01:16:09 PM
Quote from: butterw on February 03, 2021, 09:39:20 AMWhat does Dump mean ? if it extracts some data, what does it do with it ?

The functions print human-readable debug information to stdout, i.e. to the terminal on Linux or macOS or to admlog.txt on Windows.

Quote from: butterw on February 03, 2021, 09:39:20 AMADM_pyAvidemux.cpp is autogenerated ?

adm_gen.cpp is autogenerated by the Perl script cmake/admPyClass.pl (this is also the reason for a huge diff as the order of autogenerated functions is random) from binding/adm.admPyClass file.

I can only encourage you to install Linux (Fedora might provide the best experience IMVHO) if you want to stay in touch with current development, not limited to Avidemux. Linux provides not only a completely different, superior level of comfort (IMHO) but it also lets you easily generate your own builds for Windows.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 03, 2021, 03:30:00 PM
With the addition of a binding for int getVideoCount(void) I think the 2.7.7 TinyPy bindings would be complete at a basic level.

More advanced features could be added in the future (full access to segments was available in the Qtscript bindings for instance, and clipboard access could be interesting), which could then be used to improve / add custom commands to the GUI.

The scripts featured in the Auto Menu probably should be looked at before this release. Some might now be obsolete (vcd, svcd ?) and could be moved out.


Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 11, 2021, 03:44:48 PM
The old scripts in the Auto menu seem to be DVD converters scripts. I've never used any of them, but they can be useful as examples. vcd and svcd.py are likely obsolete. I'm not sure what check24fps.py does.


 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 12, 2021, 11:52:51 PM
Quote from: butterw on February 11, 2021, 03:44:48 PMI'm not sure what check24fps.py (//check24fps.py) does.

It looks like it probed time increment in order to classify fps. I'm not sure it is relevant now. Maybe just to showcase the usage of displayInfo().
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 14, 2021, 10:02:08 AM
Quote from: eumagga0x2a on February 12, 2021, 11:52:51 PM
Quote from: butterw on February 11, 2021, 03:44:48 PMI'm not sure what check24fps.py (//check24fps.py) does.

It looks like it probed time increment in order to classify fps. I'm not sure it is relevant now. Maybe just to showcase the usage of displayInfo().

The thing is if the preference message level is set to error only, displayInfo doesn't even show up in the GUI.

- I would suggest removing obsolete or scripts of dubious use from the Auto menu, as a first step, a shorter list would better showcase genuinely useful scripts (and currently if you don't want these script in the Auto menu, you have to manually delete them each time you install).
- All the conversion scripts basically do the same thing, so one of these would showcase this use case just as effectively.
- Once the TinyPy interface is done, it will be possible to add more useful scripts (A directory loader script comes to mind).
I see you have added some functions in TinyPy to access segments and splitext, but I am still missing an editor->getVideoCount() binding.



 

 

 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 14, 2021, 12:03:24 PM
Quote from: butterw on February 14, 2021, 10:02:08 AMThe thing is if the preference message level is set to error only, displayInfo doesn't even show up in the GUI.

My UX paradigm is not to keep users from shooting themselves in the foot at any cost. Users will always win.

Quote from: butterw on February 14, 2021, 10:02:08 AM- I would suggest removing obsolete or scripts of dubious use from the Auto menu, as a first step, a shorter list would better showcase genuinely useful scripts (and currently if you don't want these script in the Auto menu, you have to manually delete them each time you install).

Sure, later.

Quote from: butterw on February 14, 2021, 10:02:08 AM- Once the TinyPy interface is done, it will be possible to add more useful scripts (A directory loader script comes to mind).

Agreed, probably later. The interface is more or less done.

Quote from: butterw on February 14, 2021, 10:02:08 AMI see you have added some functions in TinyPy to access segments and splitext, but I am still missing an editor->getVideoCount() binding.

Surely an issue of comfort, but for now it should be possible to iterate over segments calling getRefIdxForSegment and taking the max.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 14, 2021, 03:14:10 PM
Quote from: eumagga0x2a on February 14, 2021, 12:03:24 PM
Quote from: butterw on February 14, 2021, 10:02:08 AM- Once the TinyPy interface is done, it will be possible to add more useful scripts (A directory loader script comes to mind).

Agreed, probably later. The interface is more or less done.

OK.
Having a release version will allow to write scripts with the requirement of this version. Having a clean version number would be good when the interface is complete (ex: Avidemux 2.8 ).

Quote from: eumagga0x2a on February 14, 2021, 12:03:24 PM
Quote from: butterw on February 14, 2021, 10:02:08 AMI see you have added some functions in TinyPy to access segments and splitext, but I am still missing an editor->getVideoCount() binding.

Surely an issue of comfort, but for now it should be possible to iterate over segments calling getRefIdxForSegment and taking the max.

There can be very many segments, in which case this approach seems unefficient.

Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on February 14, 2021, 06:16:18 PM
Quote from: butterw on February 14, 2021, 03:14:10 PM
Quote from: eumagga0x2afor now it should be possible to iterate over segments calling getRefIdxForSegment and taking the max.

There can be very many segments, in which case this approach seems unefficient.

The inefficiency is not a concern, my suggestion simply won't work reliably as existing segments don't necessarily reference the video which was loaded last to get the highest id. I've added ADM_Composer::getVideoCount (https://github.com/mean00/avidemux2/commit/348f53bd560ef782fb19fbc2ae42e9b57dc822ad) to the interface now.

Quote from: butterw on February 14, 2021, 03:14:10 PMHaving a release version will allow to write scripts with the requirement of this version. Having a clean version number would be good when the interface is complete (ex: Avidemux 2.8 ).

It is not like an ABI of a library where all methods must match. An old script won't include a query of the interface version and will probably run nevertheless. A new script may include one and thus fail on an older Avidemux build which doesn't implement this method. However, if existing methods change their semantics, API versioning, tied to actual interface changes, not to Avidemux version, would be handy indeed.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 14, 2021, 08:19:39 PM
As I understand it there have been no changes in TinyPy semantics: a script written 5 years ago is expected to run fine in the next release of Avidemux. However, a number of new functions have been added and scripts which use them will not be compatible with earlier versions of Avidemux (they will fail with TinyPy:Exception Key Error:xxx where xxx is the new unknown command).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 17, 2021, 04:22:17 PM
Segments are the result of loading/appending and edit operations.
adm.addSegment(int refVideoIdx, pts, duration)
The info about the sequence of segments can be accessed with segmentIdx < ed.nbSegments()

- Every loaded video has an integer index (refVideoIdx) to reference it in the segments list and is counted in ed.nbVideos().
Videos loaded multiple times will have the same path.
Videos (end_pts, filepath) >= unique_Videos
Segments (Videos_Idx, video_start_offset, duration) >= used_Videos

So with Avidemux v2.7.7 and some TinyPy code, it is possible to know:
- the duration and number of segments (nbSegments) of the current edit
- the total number of video files loaded (nbVideos)
- the number of videos files used, whether they are unique, their duration, filepath, filesize
- the bitrate of a source video: filesize/duration

get Segment/RefVideo info methods added in v2.7.7:
- Segments:
int  ed.getRefIdxForSegment(int segmentIdx)
pts  ed.getTimeOffsetForSegment(int segmentIdx)
pts  ed.getDurationForSegment(int segmentIdx)

- RefVideo:
int ed.nbVideos()
pts ed.getRefVideoDuration(int refVideoIdx)
filepath    ed.getRefVideoName(int refVideoIdx) #check value is not None
! int, pts: check that the return value is not negative
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 17, 2021, 08:41:02 PM
To jump to the 1st keyframe of segment_1:
e1 = ed.getDurationForSegment(0)
k1 = ed.getNextKFramePts(e1-1) #if segment_1 has an offset, e1 can directly be the keyframe we are looking for.
adm.setCurrentPts(k1)

This could be used for a goToNextSegment() seek/jump function, which is useful for editing.

EDIT: You need to cumulate segments pts values durations to make this work. This could also be used to determine the segment corresponding to the current position (and associated video file, path, filesize).



 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on February 19, 2021, 08:35:25 AM
Thinking about TinyPy bindings which could be useful for next-gen Avidemux (v2.8 ?):
- a binding to copy a str to system clipboard
- More pts markers (at least markers C, D) and make available a storage variable.
One issue with TinyPy scripts is that values are not persistent accross runs: Run script A, determine some values. These values are not then available for script B or script A if it is run again in the same GUI session.
The way around this currently is to first define variables (or as in the following example an empty object) in the interactive shell:
class Vars: pass
vars = Vars()
You can now store anything in vars from scripts in a persistent fashion, ex: vars.x = 100


 





 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on March 01, 2021, 08:27:50 PM
I've encountered the following issue with adm.saveAudio: the return value is always 1 even if the operation failed (should be 0 ?).
[A_saveAudioCommon] 20:22:18-484 Cannot open file for writing

out = adm.saveAudio(0, "out.aac")


Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on March 01, 2021, 10:44:44 PM
Thanks, should be fixed by [gui_save] Truthfully relay the result of saving audio to caller, show error message on failure in copy mode too (https://github.com/mean00/avidemux2/commit/d2d8f1cacd71be5eb30d2483aa58729759c6fbb9).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on March 03, 2021, 08:55:28 PM
The following script adds a black filter section based on the position of markers A and B. B isn't blacked out. The black filter can be used multiple times.

black_A[B.py:
# adds A[B black filter (re-encode req)

adm=Avidemux(); ed=Editor(); gui=Gui()
if not ed.nbSegments():
gui.displayError("black_A-[B", "No video loaded !")
return
adm.addVideoFilter("black", "startBlack="+str(adm.markerA/1000), "endBlack="+str(adm.markerB/1000)) #startBlack is int_ms

EDIT: All cut operations should be performed before you apply partial filters (filters which are applied to only part of the video) as the time values are hardcoded when the videoFilter is added and this causes the position of the filter to shift when an edit before the position of the filter is made.
To avoid this issue: clear filters and apply the filter again. A manually written project script could alternatively use marker variables instead of hardcoded values (copy-paste the last line of black_A[B.py for a filter with dynamic marker-based position). 

To clear/delete the filter chain:
adm.clearVideoFilters() #All filters are removed
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on March 07, 2021, 05:14:29 PM
TinyPy seek

Using the new v2.7.7 functions, I have done a custom time (backward, forward) keyframe-seek script: https://github.com/butterw/bShaders/tree/master/test_LimitedRange/Avidemux/Settings/Custom

to Seek to first frame: adm.setCurrentPts(0)
- It's not clear to me how to seek to last frame via script currently, seek to the last keyframe then seek frame by frame ?
https://github.com/mean00/avidemux2/blob/a565ebc435827ec11edfd58b751e188bfb5c02da/avidemux/common/gui_navigate.cpp









Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on March 07, 2021, 06:47:20 PM
Quote from: butterw on March 07, 2021, 05:14:29 PM- It's not clear to me how to seek to last frame via script currently, seek to the last keyframe then seek frame by frame ?

Exactly, this is basically what GUI_infiniteForward(uint64_t) does, with one extra trick or hack (if seek to the last keyframe fails, it seeks to the previous one and tries to advance from that point before giving up).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on March 08, 2021, 06:01:37 PM
I've come accross this page: https://fossies.org/linux/avidemux/avidemux_plugins/ADM_scriptEngines/qtScript/src/html/_dialog_8admjs-example.html

(https://fossies.org/linux/avidemux/avidemux_plugins/ADM_scriptEngines/qtScript/src/html/Dialog.jpg)

which suggests further widgets beyond what is currently available in Tinypy are possible: 
lineEdit, doubleSpinBox, slider.

Having tried out some Tinypy parameter GUIs, one of the issues is the lack of a text label component, which complicates layout. A toggle widget without the checkbox would be useful.

 
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: EggCess on March 20, 2021, 10:29:56 PM
Hi, new here but a big fan of the project.

I've recently written myself a few small tinypy scripts for batch-muxing and -converting stuff I recorded with x265. Huge timesaver, works like a charm, so thanks for your amazing work!

Small question though: As far as I understand from looking at the sources (https://github.com/mean00/avidemux2/tree/master/avidemux_plugins/ADM_scriptEngines/tinyPy/src), it's currently not possible to add a job to the Avidemux job queue/list via script, correct? Just making sure I don't overlook something obvious :)

I'd love to be able to have a script create jobs for the avidemux_jobs list instead of having an adm.save() call in the script directly. That would allow for more fine-grained control over e.g., aborting jobs while they're running, seeing how many jobs there are left, etc.
In other words, I'd like to delegate control of starting a job from the script to the job queue, while keeping the possibility to specify each job's details with the tinypy scripting interface.

I can do some stuff by showing dialogs or printing debug info during or between jobs, etc. ... but the job queue kind of already has some of those features, so I'd like to use it instead, if at all possible.

Is there a way to achieve that?
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on March 20, 2021, 11:24:07 PM
A simple answer: no, this is not possible, but it should be possible to replicate all the interfaces Jobs GUI is using to run Avidemux e.g. from external Python interpreter the way you want.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: EggCess on March 21, 2021, 03:31:32 PM
Hmm, ok, I'll think about it and poke around for a bit. Thanks for the answer! :)
Title: [Tinypy] directly cut to AB Selection
Post by: butterw on April 05, 2021, 11:55:22 AM
# directly cut to [AB[ Selection (video must be single segment), the alternative is to save (selection) and load the file
adm=Avidemux(); ed=Editor(); gui=Gui()
if ed.nbSegments() is not 1: print("nbSegments must be one!"); return
c_pts = ed.getCurrentPts()
adm.clearSegments()
adm.addSegment(0, adm.markerA, adm.markerB-adm.markerA)

end = ed.getVideoDuration()
if c_pts>adm.markerB: adm.setCurrentPts(end)
elif c_pts>adm.markerA: adm.setCurrentPts(c_pts-adm.markerA)
else: adm.setCurrentPts(0)
adm.markerA=0
adm.markerB=end
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on April 23, 2021, 07:13:21 PM
# clearVideoFilters.py

adm=Avidemux()
adm.clearVideoFilters()
Title: Contents of a saved project script file (.py)
Post by: butterw on April 29, 2021, 08:01:02 AM
Contents of a saved project script file (.py):
0) Header

#PY  <- Needed to identify #
#--automatically built--

adm = Avidemux()

1) Load/append Input Video files from filepath, ex: "B:/Videos/vid.mkv"
- Segments: optional unless there are edits/cuts to the input files
- (AB Markers: optional)
2) Convert
# video
- Output Video Codec (ex: "copy", "mp4")
- Video Filters with parameters(ex: crop, denoise, resize)
...filters are not applied in copy mode
...some filters can be made "partial" (in this case they apply only to part of the video)
- Audio Tracks (Avidemux default is to keep all source audio tracks) 
# audio
...Output Audio Codec (ex: "copy")
...Output Audio Shift
# format
- Output Container (ex: "mp4", "mkv")

A saved project script can easily be modified:
- to set a specific filter chain configuration / with filter parameters
- to batch process files (ex: load all mkv files from the input directory, process them and save the result)
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on May 08, 2021, 06:59:10 PM
Would it be possible to add a save_project(filepath) Tinypy binding ?

Some edits performed by user scripts (ex: vThumb.py my automatic video thumbnail generator) can't be undone, so it would be good to be able to autosave the current project before starting the edit perfomed by the script.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on May 10, 2021, 01:10:54 PM
With current TinyPy bindings there is no UI widget to enter a floating point value (https://avidemux.org/smif/index.php?msg=91317) (only integers).
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on May 13, 2021, 11:14:02 AM
Attempting to show a DialogFactory without any control widgets causes an Avidemux Crash:

dlgConfirm = DialogFactory("Please Confirm")
# display = DFMenu("test"); dlgConfirm.addControl(display)
if not dlgConfirm.show(): return
Title: [Tinypy] confirm dialog before Batch
Post by: butterw on May 14, 2021, 01:25:39 PM
(https://files.videohelp.com/u/295418/adm-confirm_batch.png)

ext="mp4"

adm=Avidemux(); gui=Gui()
# -------- select input directory --------
inputFolder = gui.dirSelect("Select " +ext+ " source folder")
# inputFolder = gui.fileReadSelectEx("Select input file", ext)

if inputFolder is None:
    gui.displayError(header_str, "No source folder selected"); return
# inputFolder = dirname(inputFolder)


# -------- get filelist with extension --------
filelist = get_folder_content(inputFolder, ext)
if filelist is None:
    gui.displayError(header_str, "No "+ ext +" files found in: "+ inputFolder); return

# -------- confirm dialog before batch --------
dlgConfirm = DialogFactory(inputFolder)
label_str = "Batch process " +str(len(filelist))+ " (" +ext+ ") Files in folder ?"
label = DFToggle(label_str); label.value=True; dlgConfirm.addControl(label)
fnames = DFMenu("in:")
for fname_in in filelist:
    fnames.addItem(basename(fname_in))
dlgConfirm.addControl(fnames)
if not dlgConfirm.show(): return


Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on May 14, 2021, 02:21:03 PM
Quote from: butterw on May 13, 2021, 11:14:02 AMAttempting to show a DialogFactory without any control widgets causes an Avidemux Crash

The number of controls being zero should trigger an assert failure either in qt4DiaFactoryPrepare or in qt4DiaFactoryTabsPrepare. The easiest way to catch this may be not very helpful for the user as no dialog will be shown at all and the method will just return false. Of course, it can be modified in a way that a dialog box with just OK and Cancel buttons will be shown. Might match the expectations better.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on May 14, 2021, 05:23:24 PM
Either the user forgot to add his widget or he just needed a confirmation dialog with title.

I've noticed "_" doesn't display in the widget label, only in the dialog title and the Menu widget entry, which is troublesome because it is commonly used in filenames.


Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: eumagga0x2a on May 14, 2021, 06:38:06 PM
Underscore in titles is reinterpreted as a keyboard accelerator. This was done so to ensure compatibility with GTK. The design won't work with arbitrary strings in dialog element constructors.

I think that the real problem is that diaElemReadOnlyText is not exposed to scripting.
Title: Re: [Tinypy] Tips & docs on Avidemux .py scripting
Post by: butterw on May 15, 2021, 09:12:35 AM
! get_folder_content(folder, "") crashes Avidemux


here's my wrapper version which can handle multiple extensions:
get_folder_content2(folder, "mp4")
get_folder_content2(folder, ["mp4", "mkv"])
get_folder_content2(folder)


def get_folder_content2(folder, ext=["mp4","mkv","webm","mov","avi","ts","m2ts","vob","mpg","wmv"]):
"""returns a filepath list
ext: "mp4", ["mp4", "mkv"],
by default get_folder_content2(folder): All video files
"""
filelist=[]
if not ext: raise("get_folder_content2: ext is None or ''")
try:
ext.replace("ab", "cd")
if ext: filelist = get_folder_content(folder, ext)
except TypeError:
for elt in ext:
flist = get_folder_content(folder, elt)
if flist: filelist.extend(flist)
return filelist