[Tinypy] Tips & docs on Avidemux .py scripting

Started by butterw, January 17, 2021, 01:18:41 PM

Previous topic - Next topic

butterw

February 17, 2021, 04:22:17 PM #45 Last Edit: March 08, 2021, 09:28:00 AM by butterw
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

butterw

February 17, 2021, 08:41:02 PM #46 Last Edit: February 19, 2021, 08:05:04 AM by butterw
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).



 

butterw

February 19, 2021, 08:35:25 AM #47 Last Edit: February 19, 2021, 08:40:32 AM by butterw
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


 





 

butterw

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")




butterw

March 03, 2021, 08:55:28 PM #50 Last Edit: March 04, 2021, 08:36:29 AM by butterw
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

butterw

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










eumagga0x2a

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).

butterw

March 08, 2021, 06:01:37 PM #53 Last Edit: March 08, 2021, 06:03:53 PM by butterw
I've come accross this page: https://fossies.org/linux/avidemux/avidemux_plugins/ADM_scriptEngines/qtScript/src/html/_dialog_8admjs-example.html



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.

 

EggCess

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?

eumagga0x2a

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.

EggCess

Hmm, ok, I'll think about it and poke around for a bit. Thanks for the answer! :)

butterw

April 05, 2021, 11:55:22 AM #57 Last Edit: April 05, 2021, 11:57:15 AM by butterw
# 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