News:

--

Main Menu

Navigating Segments in Tinypy

Started by TheTooleMan, July 23, 2022, 03:08:02 PM

Previous topic - Next topic

TheTooleMan

I've attempted this post half a dozen times but received the wrath of the overzealous spam detector. I will make another attempt now.

I record a two-hour block of cartoons with commercials each week. I load the recording into Avidemux and manually delete the commercials. This creates multiple segments in the same video.

My goal is to navigate through the collection of segments. For each segment, I want the procedure to set markerA and markerB, encode the video, and save an MKV file.

I am running into problems with the references to the properties of the segments. I don't understand the syntax shown in butterw's post 45 of the thread "[Tinypy] Tips & docs on Avidemux .py scripting."

more...

TheTooleMan

#1
My pasted code is rejected, so I will describe my attempts and errors instead.

Tinypy doesn't seem to be strongly typed, but apparently I should declare the type of variables used for the values of segmentIdx and the three "get" functions for segments. Please shed some light on these questions:

Can the Segments code posted in post 45 of the Tips and Docs post be pasted into my script and used as-is?

What type is pts?

Is there somewhere I can learn more about Tinypy syntax? (I asked this before, more or less.)

Here is the error produced by my script. The "len () of unsized object" seems to refer to segmentIDX or the value returned by getsegmentduration.

You cannot view this attachment.

Thanks for the help

butterw

Tinypy uses python syntax. You don't declare types of variables.

pts is an integer (in micro-seconds).






 

TheTooleMan

If I could paste my code into a message and not trigger the spam filter, you might see what I'm doing wrong. How can I do that?

eumagga0x2a

Please send me PM and I'll post it tomorrow for you in this topic. pts in Avidemux scripting is technically a double floating point value, but internally (in the C++ part), it is an unsigned 64-bit integer.

eumagga0x2a

Quote from: TheTooleMan on July 23, 2022, 03:08:02 PMI record a two-hour block of cartoons with commercials each week. I load the recording into Avidemux and manually delete the commercials.

Why? If there are no deletions within each cartoon, you could just save each one directly or, if you re-encode, use Ctrl+U to queue for later batch processing with Avidemux Jobs GUI.

TheTooleMan

Quote from: eumagga0x2a on July 25, 2022, 11:20:24 PM
Quote from: TheTooleMan on July 23, 2022, 03:08:02 PMI record a two-hour block of cartoons with commercials each week. I load the recording into Avidemux and manually delete the commercials.

Why? If there are no deletions within each cartoon, you could just save each one directly or, if you re-encode, use Ctrl+U to queue for later batch processing with Avidemux Jobs GUI.
Because a) a batch script saves me the time of waiting for each segment to be processed before moving to the next; b) thanks for the tip about the Jobs option; c) I don't re-encode; d) I want to learn the syntax and make this work, if possible; e) I'm not wasting any more time explaining myself to you. You either get it or you don't.

eumagga0x2a

Quote from: TheTooleMan on July 26, 2022, 02:05:14 PMc) I don't re-encode

I meant as the final stage. Performing editing steps and exporting segments in copy mode may complicate the task, triggering cut point checks and eventually making the actual start of the output differ from the provided selection start. Luckily, we don't need to take this complication into account here.

Below a project script which can either serve as a template for a fully-automated processing or be run directly as-is after editing steps have been completed. It will output each segment as a separate video to a target directory selected by the user.

The script has been tested on Linux only. If it doesn't work on Windows, please replace "/" in the argument for adm.save() with "\\".

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

adm = Avidemux()
ed=Editor()
gui=Gui()

# Populate the part below based on the project script
# exported once editing is finished.
# Audio-related commands may be completely omitted if
# there are no special wishes like track language etc.
#
# Alternatively, load a video, perform editing, select
# encoder etc, then run this script.
# ---------------------------------------------------
# if not adm.loadVideo("/path/to/video"):
#    raise("Cannot load /path/to/video")
#adm.audioClearTracks()
#adm.setSourceTrackLanguage(0,"eng")
#if adm.audioTotalTracksCount() <= 0:
#    raise("Cannot add audio track 0, total tracks: " + str(adm.audioTotalTracksCount()))
#adm.audioAddTrack(0)
#adm.audioCodec(0, "copy")
#adm.audioSetShift(0, 0, 0)
#adm.clearSegments()
#adm.addSegment(0, 17304033, 368451423)
#adm.addSegment(0, 427030022, 463262800)
#adm.addSegment(0, 1050285989, 443860078)
#adm.addSegment(0, 1639324433, 362645623)
#adm.addSegment(0, 2185019589, 416582833)
#adm.addSegment(0, 2657341433, 392925867)
#adm.addSegment(0, 3180330567, 406038966)
#adm.addSegment(0, 3640990767, 450733622)
#adm.addSegment(0, 4162027956, 438438000)
#adm.addSegment(0, 4820585856, 475007866)
#adm.addSegment(0, 5435850500, 446796356)
#adm.addSegment(0, 6110891533, 454287167)
#adm.addSegment(0, 6695542267, 481264122)

#adm.videoCodec("x265", "useAdvancedConfiguration=True", "general.params=AQ=21", "general.poolThreads=99", "general.frameThreads=0", "general.output_bit_depth=0", "general.preset=medium", "general.tuning=animation", "general.profile=main"
#    , "level=-1", "vui.sar_idc=1", "vui.sar_height=1", "vui.sar_width=1", "vui.color_primaries=1", "vui.transfer_characteristics=1", "vui.matrix_coeffs=1", "MaxRefFrames=5", "MinIdr=60", "MaxIdr=600", "i_scenecut_threshold=40"
#    , "MaxBFrame=5", "i_bframe_adaptive=2", "i_bframe_bias=0", "i_bframe_pyramid=1", "b_deblocking_filter=True", "b_open_gop=False", "interlaced_mode=0", "constrained_intra=False", "b_intra=True", "lookahead=40"
#    , "weighted_pred=2", "weighted_bipred=True", "rect_inter=False", "amp_inter=False", "limit_modes=False", "cb_chroma_offset=0", "cr_chroma_offset=0", "me_method=3", "me_range=32", "subpel_refine=6", "limit_refs=3"
#    , "rd_level=1", "psy_rd=0.400000", "rdoq_level=0", "psy_rdoq=0.000000", "fast_pskip=True", "dct_decimate=True", "noise_reduction_intra=0", "noise_reduction_inter=0", "strong_intra_smoothing=False", "ratecontrol.rc_method=0"
#    , "ratecontrol.qp_constant=0", "ratecontrol.qp_step=4", "ratecontrol.bitrate=0", "ratecontrol.vbv_max_bitrate=0", "ratecontrol.vbv_buffer_size=0", "ratecontrol.vbv_buffer_init=1", "ratecontrol.ip_factor=1.400000"
#    , "ratecontrol.pb_factor=1.300000", "ratecontrol.aq_mode=2", "ratecontrol.aq_strength=0.600000", "ratecontrol.cu_tree=True", "ratecontrol.strict_cbr=False")
# ---------------------------------------------------
# Replace muxer name and configuration below if desired so.
# In this case, please adjust the filename extension (ext).
adm.setContainer("MKV", "forceAspectRatio=False", "displayWidth=1280", "displayAspectRatio=2", "addColourInfo=False", "colMatrixCoeff=2", "colRange=0", "colTransfer=2", "colPrimaries=2")
ext = "mkv"

Counter = 0
mark1 = 0
mark2 = 0

nbSeg = ed.nbSegments()

if not nbSeg:
    gui.displayError("Error", "No video loaded, nothing to do, bye")
    return

if nbSeg > 100:
    gui.displayInfo("Warning", "Maximum of 100 segments supported, but got " + str(nbSeg))
    nbSeg = 100

filename = ed.getRefVideoName(0)
if filename is None:
    gui.displayError("Error", "Cannot obtain reference video filename")
    return

filename = (splitext(filename))[0]
filename = basename(filename)

if filename is None:
    gui.displayError("Error", "Cannot obtain input basename")
    return

# Replace gui.dirSelect() below with output directory path for unattended operation.
outdir = gui.dirSelect("Select output folder")
if outdir is None:
    gui.displayInfo("Warning", "No output folder selected, cannot proceed")
    return

leadingZero = ""

for segmentIdx in range(nbSeg):
    mark2 += ed.getDurationForSegment(segmentIdx)
    if segmentIdx < 10:
        leadingZero = "0"
    adm.markerA = mark1
    adm.markerB = mark2
    Counter += adm.save(outdir + "/" + filename + "-segment-" + leadingZero + str(segmentIdx) + "." + ext)
    mark1 = mark2

gui.displayInfo("Finished", str(Counter) + " files out of " + str(nbSeg) + " segments converted")

tripunto

tanks eumagga0x2a

Thanks to your script, I've been able to perfect mine.

My script has several improvements that may be useful to other users.

1. It supports multiple files.
2. It automatically obtains video names and lists the cuts.
3. (Optional) You can define a unique name to list the cuts.


#PY  <- Needed to identify #
# Create by tripunto v0.1.3 (2025.04.19)

### Start new process
app=Avidemux(); ed=Editor(); gui=Gui(); app.clearSegments(); app.clearVideoFilters()

### Output file name and directory
out_folder = gui.dirSelect("Select the destination directory") # pop up a dialog asking for a destination directory
out_segment_name = "_cut"
out_ext = "mp4"
out_filename = "" # Edit to rename the cuts in the output files

### Replace lines 15 through 60 with the script from the saved project file.

#adm=Avidemux()
#if not adm.loadVideo("/path/to/file1.mp4"):
#    raise("Cannot load /path/to/file1.mp4")
#if not adm.loadVideo("/path/to/file2.mp4"):
#    raise("Cannot load /path/to/file2.mp4")
#if not adm.loadVideo("/path/to/file3.mp4"):
#    raise("Cannot load /path/to/file3.mp4")
#adm.clearSegments()
#adm.addSegment(1, 294583333, 69833333)
#adm.addSegment(0, 35166666, 14066667)
#adm.addSegment(0, 21100000, 14066666)
#adm.addSegment(1, 217375000, 77124999)
#adm.addSegment(2, 428957550, 54822222)
#adm.addSegment(2, 162024216, 38933334)
#adm.markerA = 175091665
#adm.markerB = 760580558
#adm.setHDRConfig(1, 1, 1, 1, 0)
#adm.videoCodec("x264", "useAdvancedConfiguration=True", "general.params=AQ=25", "general.threads=0", "general.preset=ultrafast", "general.tuning=none", "general.profile=baseline", "general.fast_decode=False", "general.zero_latency=False"
#, "general.fast_first_pass=True", "general.blueray_compatibility=False", "general.fake_interlaced=False", "level=-1", "vui.sar_height=1", "vui.sar_width=1", "vui.overscan=0", "vui.vidformat=5", "vui.fullrange=False"
#, "vui.colorprim=2", "vui.transfer=2", "vui.colmatrix=2", "vui.chroma_loc=0", "MaxRefFrames=3", "MinIdr=25", "MaxIdr=250", "i_scenecut_threshold=40", "intra_refresh=False", "MaxBFrame=3", "i_bframe_adaptive=1"
#, "i_bframe_bias=0", "i_bframe_pyramid=2", "b_deblocking_filter=True", "i_deblocking_filter_alphac0=0", "i_deblocking_filter_beta=0", "cabac=True", "interlaced=False", "constrained_intra=False", "tff=True"
#, "fake_interlaced=False", "analyze.b_8x8=True", "analyze.b_i4x4=True", "analyze.b_i8x8=True", "analyze.b_p8x8=True", "analyze.b_p16x16=False", "analyze.b_b16x16=False", "analyze.weighted_pred=2", "analyze.weighted_bipred=True"
#, "analyze.direct_mv_pred=1", "analyze.chroma_offset=0", "analyze.me_method=1", "analyze.me_range=16", "analyze.mv_range=-1", "analyze.mv_range_thread=-1", "analyze.subpel_refine=7", "analyze.chroma_me=True"
#, "analyze.mixed_references=True", "analyze.trellis=1", "analyze.psy_rd=1.000000", "analyze.psy_trellis=0.000000", "analyze.fast_pskip=True", "analyze.dct_decimate=True", "analyze.noise_reduction=0", "analyze.psy=True"
#, "analyze.intra_luma=11", "analyze.inter_luma=21", "ratecontrol.rc_method=0", "ratecontrol.qp_constant=0", "ratecontrol.qp_min=10", "ratecontrol.qp_max=51", "ratecontrol.qp_step=4", "ratecontrol.bitrate=0"
#, "ratecontrol.rate_tolerance=1.000000", "ratecontrol.vbv_max_bitrate=0", "ratecontrol.vbv_buffer_size=0", "ratecontrol.vbv_buffer_init=1", "ratecontrol.ip_factor=1.400000", "ratecontrol.pb_factor=1.300000"
#, "ratecontrol.aq_mode=1", "ratecontrol.aq_strength=1.000000", "ratecontrol.mb_tree=True", "ratecontrol.lookahead=40")
#adm.addVideoFilter("resampleFps", "mode=0", "newFpsDen=1000", "newFpsNum=25000", "interpolation=0")
#adm.addVideoFilter("crop", "top=0", "bottom=0", "left=32", "right=32", "ar_select=0")
#adm.addVideoFilter("swscale", "width=1280", "height=720", "algo=2", "sourceAR=0", "targetAR=0", "lockAR=True", "roundup=0")
#adm.audioClearTracks()
#adm.setSourceTrackLanguage(0,"und")
#if adm.audioTotalTracksCount() <= 0:
#    raise("Cannot add audio track 0, total tracks: " + str(adm.audioTotalTracksCount()))
#adm.audioAddTrack(0)
#adm.audioCodec(0, "LavAAC", "bitrate=96")
#adm.audioSetMixer(0, "MONO");
#adm.audioSetResample(0, 44100)
#adm.audioSetDrc2(0, 0, 1, 0.001, 0.2, 1, 2, -12)
#adm.audioSetEq(0, 0, 0, 0, 0, 880, 5000)
#adm.audioSetChannelGains(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
#adm.audioSetChannelDelays(0, 0, 0, 0, 0, 0, 0, 0, 0, 0)
#adm.audioSetChannelRemap(0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 8)
#adm.audioSetShift(0, 0, 0)
#adm.audioSetNormalize2(0, 1, 10, 0)
#adm.setContainer("MP4", "muxerType=0", "optimize=1", "forceAspectRatio=False", "aspectRatio=1", "displayWidth=1280", "rotation=0", "clockfreq=0")

#####################################################################
### Start the process of splitting segments into individual files ###
#####################################################################

nbSeg = ed.nbSegments()
if not nbSeg:
    gui.displayError("Error", "No video loaded, nothing to do, bye")
    return

if nbSeg > 100:
    gui.displayInfo("Warning", "Maximum of 100 segments supported, but got " + str(nbSeg))
    nbSeg = 100

Counter = 0
mark2 = 0
video = {}

for i in range(nbSeg):
leadingZero = ""
file_name = ""
mark1 = mark2
file_id = ed.getRefIdxForSegment(i)
file_path = ed.getRefVideoName(file_id)

if file_path is None:
gui.displayError("Error", "Cannot obtain reference video file_path")
return

### Select auto 'file_name' from video file if 'out_filename' is not defined
if not out_filename:
file_name = (splitext(file_path))[0]
file_name = basename(file_name)
if file_name is None:
gui.displayError("Error", "Cannot obtain input basename, include the files and segments from saved project")
raise("ERROR: Cannot obtain input basename, include the files and segments from saved project")
return
if file_id not in video:
out_cut_number = 1
else:
out_cut_number = video[file_id]+1
video[file_id] = out_cut_number
if out_cut_number < 10:
leadingZero = "0"

### Select 'file_name' for 'out_filename' variable
else:
file_name = out_filename
out_cut_number = str(i+1)
if i < 10:
leadingZero = "0"

### Select segment to file cut process
mark2 += ed.getDurationForSegment(i)
print("Segment start:",mark1,"Segment end:",mark2)

app.markerA = mark1
app.markerB = mark2

### Save to individual file from segment marks
output_file = out_folder + "/" + file_name + out_segment_name + leadingZero + str(out_cut_number) + "." + out_ext
Counter += app.save(output_file)

gui.displayInfo("Finished", str(Counter) + " files out of " + str(nbSeg) + " segments converted")

I'd like to make some final adjustments so I don't have to edit the script again.

1. It defines the 'out_filename' variable using a text input window.
2. It imports the project file, but I'm getting errors with 'import gui.fileReadSelect("Select input file")'
.
It would be great if I could request that data from the script!