forked from NMAAHC/nmaahcmm
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmkvnote
executable file
·447 lines (380 loc) · 24.1 KB
/
mkvnote
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
#!/bin/bash
# The shebang above specifies that this script should be run with the Bash shell.
# It tells the system that the script should be interpreted using Bash.
# Declaration of an array named MKV_TAG_SET_JPC to hold tag names specific to the JPC profile.
# These tags are metadata fields that describe various aspects of a video file.
MKV_TAG_SET_JPC=(
"COLLECTION" # The collection to which the video belongs.
"TITLE" # The title of the video, likely matching the archive's system (e.g., ASpace).
"CATALOG_NUMBER" # Unique identifier or catalog number for the video, often the filename.
"DESCRIPTION" # Brief description of the video's content.
"DATE_DIGITIZED" # The date when the video was digitized, in yyyy-mm-dd format.
"ENCODER_SETTINGS" # Details about the encoding process, including hardware and software used.
"ENCODED_BY" # Information about the entity or person who encoded the video.
"ORIGINAL_MEDIA_TYPE" # The original format of the media before digitization.
"DATE_TAGGED" # The date when these tags were applied or updated.
"TERMS_OF_USE" # Legal or usage terms associated with the video.
"_TECHNICAL_NOTES" # Any technical notes related to the video or digitization process.
"_ORIGINAL_FPS" # The original frames per second for motion picture film, if applicable.
)
# Declaration of an array named MKV_TAG_SET_NMAAHC to hold tag names specific to the NMAAHC profile.
# This set includes all tags from the JPC profile with an additional custom tag `_TAGTAG`.
MKV_TAG_SET_NMAAHC=(
"COLLECTION" # (Same as above) The collection to which the video belongs.
"TITLE" # (Same as above) The title of the video, likely matching the archive's system.
"CATALOG_NUMBER" # (Same as above) Unique identifier or catalog number for the video.
"DESCRIPTION" # (Same as above) Brief description of the video's content.
"DATE_DIGITIZED" # (Same as above) The date when the video was digitized.
"ENCODER_SETTINGS" # (Same as above) Details about the encoding process.
"ENCODED_BY" # (Same as above) Information about the entity or person who encoded the video.
"ORIGINAL_MEDIA_TYPE" # (Same as above) The original format of the media before digitization.
"DATE_TAGGED" # (Same as above) The date when these tags were applied or updated.
"TERMS_OF_USE" # (Same as above) Legal or usage terms associated with the video.
"_TECHNICAL_NOTES" # (Same as above) Technical notes related to the video.
"_ORIGINAL_FPS" # (Same as above) The original frames per second for the film.
"_TAGTAG" # Additional custom tag unique to NMAAHC, indicating extra metadata.
)
# Setting up color and style variables using `tput`.
# These variables define text appearance in the script’s output (bold, color, etc.).
BOLD=$(tput bold) # Sets text style to bold for emphasis.
RESET=$(tput sgr0) # Resets text style to the default, removing any bold or color.
GB=$(tput setaf 38) # Sets the text color to greenish-blue for regular output.
ERROR=$(tput setaf 1) # Sets the text color to red for error messages.
# Definition of the `_usage` function to provide usage information for the script.
# This function outputs a detailed help message when the script is run incorrectly or with `-h`.
_usage(){
cat <<EOF
$(basename "${0}")
This application will embed key:value pairs ("tags") into a Matroska file.
Usage: $(basename "${0}") [-p|--profile <profile>] /path/to/file
Tag keys are pre-defined in sets: JPC or NMAAHC
JPC:
"COLLECTION"
"TITLE"
"CATALOG_NUMBER"
"DESCRIPTION"
"DATE_DIGITIZED"
"ENCODER_SETTINGS"
"ENCODED_BY"
"ORIGINAL_MEDIA_TYPE"
"DATE_TAGGED"
"TERMS_OF_USE"
"_TECHNICAL_NOTES"
"_ORIGINAL_FPS"
NMAAHC:
"COLLECTION"
"TITLE"
"CATALOG_NUMBER"
"DESCRIPTION"
"DATE_DIGITIZED"
"ENCODER_SETTINGS"
"ENCODED_BY"
"ORIGINAL_MEDIA_TYPE"
"DATE_TAGGED"
"TERMS_OF_USE"
"_TECHNICAL_NOTES"
"_ORIGINAL_FPS"
"_TAGTAG"
Tags beginning with an underscore are not part of the official set of Matroska tags as defined here:
https://www.matroska.org/technical/tagging.html
The script needs swiftdialog to run. It can be installed here:
https://github.com/swiftDialog/swiftDialog/releases
Calling the script will bring up a dialog window in which to enter values for the tag set you have chosen.
The script can also be run in batch mode by feeding it a .csv
filename director rating
test.mkv dave pg
test2.mkv blake no
test3.mkv immy g
The filename should be the full path of the file.
Usage: $(basename "${0}") mycsv.csv
Tag definitions for JPC:
Tag Example
COLLECTION The name of the collection that the content or object is from ; ex. Johnson Publishing Company Archive / Pearl Bowser Collection
TITLE Should be the same as ASpace ; ex. Ebony/Jet Showcase #1001 / A Pinch of Soul
CATALOG_NUMBER Often the file name ; ex. JPC_AV_12345 / 2012.79.2.54.1a
DESCRIPTION Please be brief! Ok to leave blank! ex. In the 1980s a young journalist moves to New York City / Broadcast of the 1978 American Black Achievement Awards
DATE_DIGITIZED yyyy-mm-dd ; ex. 2024-10-10
ENCODER_SETTINGS Source VTR: model name, serial number, video signal type (Composite, SDI, etc.), audio signal type (analog balanced, analog unbalanced, embedded, etc.) ;
TBC/Framesync: model name, serial number, video signal type, audio signal type ;
ADC: model name, serial number, video signal type, audio signal type (if audio is embedded at this point simply say "embedded") ;
Capture Device: model name, serial number, data connection type (Thunderbolt/PCIe/SATA/etc) ;
Computer: model name, serial number, computer os version, capture software (including version), encoding software (ffmpeg version not required)
ENCODED_BY Entity, name, country ; ex. Smithsonian NMAAHC, James Smithson, US (use ISO 3166-1 alpha-2 codes)
ORIGINAL_MEDIA_TYPE Format (from PB Core), manufacturer, model ; ex. U-matic, Sony, KSP-30 (don't guess or estimate the manufacturer or model - the format is plenty sufficient if the others are ambiguous)
DATE_TAGGED yyyy-mm-dd, this is the date the mkv tags were embedded or updated ; ex. 2024-10-18
TERMS_OF_USE Not definitive, nor authoritative ; ex. Some or all of this video maybe subject to copyright or other intellectual property rights. Proper usage is the responsibility of the user.
_TECHNICAL_NOTES Free text for capturing anything concerning the inspection, conservation, digitization of the tape. ex.
_ORIGINAL_FPS For motion picture film, the frames per second at which the film was meant to be projected. most common - 16, 18, 24 ; ex. 24
EOF
exit
}
# The function ends with the `exit` command, ensuring that the script stops after displaying usage info.
# Initialize variables to store the selected profile and corresponding tag set.
SELECTED_PROFILE="" # This variable will hold the name of the profile selected by the user (either 'jpc' or 'nmaahc').
SELECTED_TAG_SET=() # This array will store the tags associated with the selected profile.
# Start processing command-line options using `getopts`.
# The options are `-p` for selecting a profile and `-h` for displaying help.
OPTIND=1 # Initialize the option index to 1, the first command-line argument.
while getopts ":hp:" opt ; do
case "${opt}" in
p) SELECTED_PROFILE="${OPTARG}" ;; # If the `-p` option is provided, set the selected profile to the argument passed with `-p`.
h) _usage ;; # If the `-h` option is provided, call the `_usage` function to display usage information and exit.
:) echo "Option -${OPTARG} requires an argument" ; _usage ;; # Handle the case where an option requires an argument but none is provided.
*) echo "bad option -${OPTARG}" ; _usage ;; # Handle any invalid options by displaying an error message and calling `_usage`.
esac
done
shift "$((OPTIND-1))" # Shift the positional parameters so that `$1` points to the first non-option argument.
# Check if the script was run without any arguments after processing options.
# If no arguments are provided, the `_usage` function is called to display usage information and the script exits.
if [[ "${#}" -eq 0 ]]; then
_usage
fi
# Assign the first argument (after options) to `INPUT_FILE`, which represents the path to the file to be tagged.
INPUT_FILE="${1}"
# Extract the file extension from `INPUT_FILE` to determine the file type.
EXTENSION="${INPUT_FILE##*.}"
# Determine the selected profile and set the corresponding tag set.
if [[ "${SELECTED_PROFILE}" == "jpc" ]] ; then
SELECTED_TAG_SET=("${MKV_TAG_SET_JPC[@]}") # If the profile is 'jpc', use the `MKV_TAG_SET_JPC` array.
elif [[ "${SELECTED_PROFILE}" == "nmaahc" ]] ; then
SELECTED_TAG_SET=("${MKV_TAG_SET_NMAAHC[@]}") # If the profile is 'nmaahc', use the `MKV_TAG_SET_NMAAHC` array.
else
unset SELECTED_PROFILE # If an invalid profile is specified or none is provided, unset the profile variable.
# Display a message indicating the invalid profile selection and prompt the user to select a profile interactively.
echo -e "${BOLD}${ERROR}\nNote: Invalid profile or no profile specified with -p. You will be prompted to select a profile.${RESET}"
fi
# Function `_maketemp` creates a temporary file and checks for success.
# This function is used to generate a temporary file that will be used for storing intermediate data, such as XML drafts.
_maketemp(){
mktemp -q -t "$(basename "${0}")" # Creates a temporary file with a name based on the script's name.
if [ "${?}" -ne 0 ]; then # Checks if the `mktemp` command failed (non-zero exit status).
# If the temporary file creation fails, an error message is displayed, and the script exits.
echo "${0}: ${ERROR}Can't create temp file, exiting...${RESET}"
exit 1
fi
}
# Check if the input file has a `.csv` extension, indicating a batch mode operation.
if [[ "${EXTENSION}" == "csv" ]] ; then
# If the input file is a CSV, create a temporary XML file for storing converted data.
CSV_XML="$(_maketemp).xml"
# Convert the CSV to XML format using `csvprintf` and redirect the output to the temporary XML file.
csvprintf -X -f "${INPUT_FILE}" > "${CSV_XML}"
# Convert the CSV to markdown format using `csvtomd`, then replace newline characters with `<br/>` for HTML formatting.
CSV_TABLE="$(csvtomd "${INPUT_FILE}" | tr '\n' '<br/>')"
# Extract the list of XML element names (tags) from the first row of the CSV.
ELEMENT_LIST="$(xmlstarlet sel -t -m "/csv/row[1]/*" -v "name()" -n "${CSV_XML}")"
FILENAME_FOUND=0 # Initialize a flag to check if the 'filename' column is present in the CSV.
echo "Examining the csv: ${CSV_XML}."
# Iterate through the list of XML elements (column names) from the CSV.
while read ELEMENT_VALUE ; do
echo "Found ${ELEMENT_VALUE}." # Output the name of each element found.
ADDITIONAL_COLUMNS+="_${ELEMENT_VALUE}" # Append each element name to `ADDITIONAL_COLUMNS` with an underscore prefix.
if [[ "${ELEMENT_VALUE}" == "filename" ]] ; then
FILENAME_FOUND=1 # If the element name is 'filename', set the flag to indicate its presence.
fi
done < <(echo "${ELEMENT_LIST}")
# If the 'filename' column is not found in the CSV, display an error message and exit the script.
if [[ "${FILENAME_FOUND}" = "0" ]] ; then
echo "${BOLD}${ERROR}Error, $(basename "${INPUT_FILE}") does not contain a column called 'filename'. Exiting.${RESET}"
exit
fi
MISSING_FILE=0 # Initialize a flag to track missing files listed in the CSV.
# Iterate through the 'filename' column values in the CSV to check if the corresponding files exist.
while read FILE_IN_CSV ; do
if [[ ! -f "${FILE_IN_CSV}" ]] ; then
# If a file listed in the CSV is not found, display an error message and set the missing file flag.
echo "${BOLD}${ERROR}Error, $(basename "${FILE_IN_CSV}") is referenced in the csv but not found.${RESET}"
MISSING_FILE=1
MISSING_FILE_LIST+="${FILE_IN_CSV} " # Append the missing file name to the list of missing files.
fi
done < <(xmlstarlet sel -t -m "/csv/row/filename" -v . -n "${CSV_XML}")
# If any files are missing, display a summary of the missing files and exit the script.
if [[ "${MISSING_FILE}" != "0" ]] ; then
echo "${BOLD}${ERROR}Error, Some files in the csv, such as ${MISSING_FILE_LIST}, are not found. Exiting.${RESET}"
exit
else
# If all files are found, display a success message and proceed with the tagging process.
echo "${BOLD}Nice. All files from the csv are found. Let's tag.${RESET}"
fi
# The script continues to process the CSV for tagging if all checks pass.
# Additional options and functionality related to tagging via CSV could be added here, as indicated by the comments.
#### Dave's note ####
# give an option to force ahead despite missing files
# add a -d option to provide a parent directory to the files referenced in the csv filenames
# ./mkvnote -d /over/here mkv.csv
# -d the parent directory of the file's referenced in the csv
# -D search from here down for the file and complain is not 1 is found
# need an option to get the existing tags out to a csv, query all mkv and then compile all tags and build a csv
# ensure that an empty column removes the tag in the csv.
# put choosing of directory and upload of csv into swift
# default csv outputs from swiftdialog to desktop
# deal with multiline values and single quotes
# make sure the error log from camera card rewrapping is self explanatory, like the h264 and aac messages
# Create a temporary XML file using the `_maketemp` function.
# This file will be used to draft the XML structure needed for embedding tags into each MKV file.
XML_DRAFT="$(_maketemp).xml"
# Begin processing each file listed in the 'filename' column of the CSV.
while read FILE_IN_CSV ; do
# Initialize the XML draft for the current file by creating a basic structure with root `<Tags>` and `<Tag>` elements.
echo "<Tags><Tag></Tag></Tags>" > "${XML_DRAFT}"
# Loop through each tag line associated with the current file in the CSV.
while read TAG_LINE ; do
# Extract the tag name by splitting the line at the first colon and trimming any trailing spaces.
TAG_NAME="$(echo "${TAG_LINE}" | cut -d ":" -f1 | sed 's/ $//g')"
# Extract the tag value by splitting the line at the first colon and trimming any leading spaces.
TAG_VALUE="$(echo "${TAG_LINE}" | cut -d ":" -f2- | sed 's/^ //g')"
# If the tag value is not empty, proceed to add it to the XML draft.
if [[ -n "${TAG_VALUE// /}" ]] ; then
# Use `xmlstarlet` to add the tag name and value to the XML draft.
# The command edits the XML structure, ensuring each tag is uniquely added under a `<Simple>` element.
xml ed --omit-decl --inplace \
--subnode "/Tags/Tag[not(Simple/Name='${TAG_NAME}')]" --type elem -n "Simple" \
--subnode "/Tags/Tag/Simple[not(Name)]" --type elem -n "Name" -v "${TAG_NAME}" \
--subnode "/Tags/Tag/Simple[not(String)]" --type elem -n "String" -v "${TAG_VALUE}" \
"${XML_DRAFT}"
fi
done < <(xmlstarlet sel -t -m "/csv/row[filename='${FILE_IN_CSV}']/*" -v "name()" -o ":" -v "." -n "${CSV_XML}")
# The inner loop reads each tag line corresponding to the current file and updates the XML draft with valid tags.
# Display a message indicating that the current file is being tagged, showing the file name.
echo "${BOLD}Tagging ${FILE_IN_CSV} with: ${RESET}"
# Display the tags being applied to the current file by selecting relevant tags from the XML draft.
xmlstarlet sel -t -m "/csv/row[filename='${FILE_IN_CSV}']/*[not(filename)]" -v "name()" -o ":" -v "." -n "${CSV_XML}"
echo # Add an empty line for better readability in the output.
# Use `mkvpropedit` to embed the prepared XML metadata into the current MKV file.
# The `--tags` option specifies the XML draft file containing the tags, and `global` applies them to the entire MKV file.
mkvpropedit --tags "global:${XML_DRAFT}" "${FILE_IN_CSV}"
# End the outer loop, moving on to the next file listed in the CSV.
done < <(xmlstarlet sel -t -m "/csv/row/filename" -v . -n "${CSV_XML}")
# Exit the script after processing all files listed in the CSV.
exit
DIALOG_FORM="$(_maketemp).xml"
cat << EOF> "${DIALOG_FORM}"
{
"title" : "NMAAHC MKV Tagging for ${INPUT_FILE_NAME}",
"ontop" : 1,
"moveable" : 1,
"width" : "75%",
"height" : "75%",
"background" : "color=#800080",
"message" : "These tags semantically are intended to describe the file as a whole and not intended to specifically refer to a particular track or attachment or other sort of piece of the file. Empty tags will be ignored. If you edit exisitng tags here they will be overwritten when saved.\n\n${CSV_TABLE}",
"infobox" : "### Aditional Info\n\n#### To upload your key:value tag data in .csv \n - choose a .csv file to upload \n - choose a directory where the .mkv you wish to tag are \n - click the 'File Style' button on the bottom left \n - be cool \n#### To tag file via this gui \n - click the 'Tag-On' button on the bottom left \n - follow the instructions",
"button1text" : "Tag-On!!",
"button2text" : "Cancel"
}
EOF
DIALOG_RESULT="$(dialog --jsonfile "${DIALOG_FORM}")"
echo $DIALOG_RESULT
exit
fi
# Display available tag options and prompt for selection if no profile was selected
if [ -z "${SELECTED_PROFILE}" ]; then
echo -e "${GB}\nAvailable tag profiles and their respective tags:${RESET}"
echo -e "${BOLD}\n1) JPC profile tags:${RESET}"
for TAG in "${MKV_TAG_SET_JPC[@]}"; do
echo -e " - $TAG"
done
echo -e "${BOLD}\n2) NMAAHC profile tags:${RESET}"
for TAG in "${MKV_TAG_SET_NMAAHC[@]}"; do
echo -e " - $TAG"
done
while true; do
echo -e "${GB}${BOLD}\nYou must select a tag profile."
echo -e "Press 1 for JPC, 2 for NMAAHC, or 0 to exit.\n"
read -p "Enter your choice: ${RESET}" choice
case "$choice" in
1)
SELECTED_TAG_SET=("${MKV_TAG_SET_JPC[@]}")
SELECTED_PROFILE="jpc"
break # Exit the loop after selection
;;
2)
SELECTED_TAG_SET=("${MKV_TAG_SET_NMAAHC[@]}")
SELECTED_PROFILE="nmaahc"
break # Exit the loop after selection
;;
0)
echo -e "${ERROR}\nExiting...${RESET}"
exit 0
;;
*)
echo "Invalid choice. Please try again."
;;
esac
done
fi
# After ensuring a profile is selected, the script proceeds to handle the .mkv file provided by the user.
# A placeholder for a function call to check if the provided file is indeed an MKV file. This is an important validation step to ensure the script operates on valid input.
#todo _check_if_mkv "${INPUT_FILE}"
INPUT_FILE_NAME="$(basename "${INPUT_FILE}")" # Extracts the file name from the MKV file path.
# Creates a temporary text file to hold the tags and a temporary XML file for drafting the MKV metadata. The tag template will be used for user input, while the XML draft will be used for embedding metadata into the MKV file.
EXISTING_TAGS="$(_maketemp).txt"
XML_DRAFT="$(_maketemp).xml"
mkvextract tags "${INPUT_FILE}" | xmlstarlet sel -t -m "/Tags/Tag[Targets='' or not(Targets) or (Targets/TargetTypeValue='50' and not(Targets/TrackUID))]/Simple" -v "Name" -o "=" -v "String" -n >> "${EXISTING_TAGS}"
IS_FIRST="Y"
while IFS="=" read -r EXISTING_KEY EXISTING_VALUE ; do
if [[ ! ${DIALOG_LIST[*]} =~ "\"${EXISTING_KEY}\"" ]] ; then
if [[ ! "${IS_FIRST}" = "Y" ]] ; then
DIALOG_LIST+=(",
")
fi
IS_FIRST="N"
DIALOG_LIST+=("{\"title\" : \"${EXISTING_KEY}\", \"value\" : \"${EXISTING_VALUE}\", \"prompt\" : \"Existing tag\"}")
fi
done < "${EXISTING_TAGS}"
for PROFILE_TAG in "${SELECTED_TAG_SET[@]}" ; do
if [[ "$(grep -c "^${PROFILE_TAG}=" "${EXISTING_TAGS}")" = "0" && ! ${DIALOG_LIST[*]} =~ "\"${PROFILE_TAG}\"" ]] ; then
if [[ ! "${IS_FIRST}" = "Y" ]] ; then
DIALOG_LIST+=(",
")
fi
IS_FIRST="N"
DIALOG_LIST+=("{\"title\" : \"${PROFILE_TAG}\", \"prompt\" : \"A ${SELECTED_PROFILE} profile tag\"}")
fi
done
DIALOG_FORM="$(_maketemp).xml"
cat << EOF > "${DIALOG_FORM}"
{
"title" : "NMAAHC MKV Tagging for ${INPUT_FILE_NAME}",
"ontop" : 1,
"moveable" : 1,
"width" : "75%",
"height" : "75%",
"background" : "color=#800080",
"message" : "These tags semantically are intended to describe the file as a whole and not intended to specifically refer to a particular track or attachment or other sort of piece of the file. Empty tags will be ignored. If you edit exisitng tags here they will be overwritten when saved.",
"infobox" : "### Aditional Info\n\n#### To upload your key:value tag data in .csv \n - choose a .csv file to upload \n - choose a directory where the .mkv you wish to tag are \n - click the 'File Style' button on the bottom left \n - be cool \n#### To tag file via this gui \n - click the 'Tag-On' button on the bottom left \n - follow the instructions",
"textfield" : [
${DIALOG_LIST[@]}
],
"button1text" : "Tag-On!!",
"button2text" : "Cancel"
}
EOF
DIALOG_RESULT="$(dialog --jsonfile "${DIALOG_FORM}")"
echo "$DIALOG_RESULT"
if [[ "${DIALOG_RESULT}" == "JSON import failed" ]] ; then
echo "Error loading the form with ${INPUT_FILE}"
exit
elif [[ -z "${DIALOG_RESULT}" ]] ; then
echo "cancelled"
exit
else
# Starts the XML draft for MKV metadata embedding with a root Tags element.
echo "<Tags><Tag></Tag></Tags>" > "${XML_DRAFT}"
# Reads the edited tag template line by line, extracting tag names and values entered by the user.
# Only lines that conform to the expected format (tag_name=[tag_value]) are processed.
while read TAG_LINE ; do
# Extracts the tag name by splitting the line at the colon and taking the first part.
TAG_NAME="$(echo "${TAG_LINE}" | cut -d ":" -f1 | sed 's/ $//g')"
# Extracts the tag value by splitting the line at the colon and taking the second part.
TAG_VALUE="$(echo "${TAG_LINE}" | cut -d ":" -f2- | sed 's/^ //g')"
# If the tag value is not empty, the tag is added to the XML draft for embedding.
# The xmlstarlet tool is used to edit the XML structure, ensuring each tag is uniquely added.
if [[ -n "${TAG_VALUE// /}" ]] ; then
xml ed --omit-decl --inplace --subnode "/Tags/Tag[not(Simple/Name='${TAG_NAME}')]" --type elem -n "Simple" --subnode "/Tags/Tag/Simple[not(Name)]" --type elem -n "Name" -v "${TAG_NAME}" --subnode "/Tags/Tag/Simple[not(String)]" --type elem -n "String" -v "${TAG_VALUE}" "${XML_DRAFT}"
fi
done < <(echo "${DIALOG_RESULT}" | grep -v " : $" | sed 's/* : / : /g')
# Uses mkvpropedit to embed the prepared XML metadata into the MKV file, effectively tagging the file.
mkvpropedit --tags "global:${XML_DRAFT}" "${INPUT_FILE}"
# A fun message to indicate the completion of the tagging process, potentially using the cowsay program.
cowsay "Cool. Done. Enjoy. Tag-on. ${INPUT_FILE}.${RESET}"
fi