Mp3 Tag File (Built)

Hi @khalidhosain, the script is ready, you can access it from here. There is a version in Spanish and another in English, enjoy it!

@lxp I published a version of the script in English with the musical genres that you proposed, those of Opus, but knowing that musical genres vary a lot from person to person, I would like the script to allow the customization of musical genres, either by eliminating some of those present, as if adding something new.

Something like this...

01

02

That's fine, check out ScriptConfig and decide if you prefer a comma-separated or a multi-line entry.

Then fill it with the default value and read any changes made by the user at the beginning of your script. The rest will be the same.

Mind that there are two types of genre tags - ID3V1 with numerical value indicating a genre from a predefined set (some devices support only V1) and ID3V2 TCON which is a free text field (ID3 Tags).
I assume you are not considering Vorbis/APE?

Thank you very much @lxp, I will study that part of the manual.

True @, my goal is the IDE3V2 Tag.

I have managed to write the following lines, but I have two problems:

  1. The data has to be specified separated by commas, and not multi-line as I would like.

  2. The data specified in the script configuration is not what appears in the "Genre" field of the script in use.

Could you help me?

initData.config_desc = DOpus.Create.Map();
var genero = "Romántico, Balada, Bolero, Trova, Patrio, Son, Salsa, Guaracha, Montuno, Cumbia, Merengue, Tropical, Ranchera, Reggae, Reggaetón, Hip Hop, Pop, Rock, Tecno, Electrónico, Jazz, Infantil, Religioso, Instrumental, Vocal, Efecto sonoro, Inglés, Bossa Nova, Tropicália, Samba, Pagode, Lambada, MPB, Forró, Sertanejo, Sertanejo universitário, Axé, Funk, Gospel, Brega, Choro, Frevo, Baião, Piseiro, Pisadinha"; 
configName = "genero";
initData.config[configName] = genero;
initData.config_desc(configName) = "Modificar géneros musicales";

01

@WKen and @lxp I'm sorry to bother you, but I'm stuck in this situation and I don't know how to get out of it, could you help me relate this part of the code:

initData.config_desc = DOpus.Create.Map();
configName = "genero";
initData.config_desc(configName) = "Modificar géneros musicales";

var genero_pre = DOpus.NewVector();
genero_pre.push_back = "Jazz";
genero_pre.push_back = "Pop";

initData.config[configName] = genero_pre;

with:

dlg.Control("genero").label = item.metadata.audio.mp3genre; 

I replaced:
dlg.Control("genero").label = item.metadata.audio.mp3genre; com
dlg.Control("genero").label = genero_pre;
but it does not work.

What I'm trying to do is that the "genero" control (editable combobox) can be customized, that is, genres can be added or removed from the script's settings button. Thank you very much and sorry for inconvenience.

Complete code
// Mp3 Tag File
// (c) 2024 DASOTA
// Script para Directory Opus.

function OnInit(initData) {
    initData.name = "Mp3 Tag File";
    initData.version = "1.0";
    initData.copyright = "(c) 2024 DASOTA";
    initData.desc = "Editor Mp3 Tag de archivo único";
    initData.default_enable = true;
    initData.min_version = "13.0";

// Personalizar géneros musicales

	initData.config_desc = DOpus.Create.Map();
    configName = "genero";
	initData.config_desc(configName) = "Modificar géneros musicales";

     var genero_pre = DOpus.NewVector();
     genero_pre.push_back = "Romántico";
     genero_pre.push_back = "Balada";
     genero_pre.push_back = "Bolero";
     genero_pre.push_back = "Trova";
     genero_pre.push_back = "Patrio";
     genero_pre.push_back = "Son";
     genero_pre.push_back = "Salsa";
     genero_pre.push_back = "Guaracha";
     genero_pre.push_back = "Montuno";
     genero_pre.push_back = "Cumbia";
     genero_pre.push_back = "Merengue";
     genero_pre.push_back = "Tropical";
     genero_pre.push_back = "Ranchera";
     genero_pre.push_back = "Reggae";
     genero_pre.push_back = "Reggaetón";
     genero_pre.push_back = "Hip Hop";
     genero_pre.push_back = "Pop";
     genero_pre.push_back = "Rock";
     genero_pre.push_back = "Tecno";
     genero_pre.push_back = "Electrónico";
     genero_pre.push_back = "Jazz";
     genero_pre.push_back = "Infantil";
     genero_pre.push_back = "Religioso";
     genero_pre.push_back = "Instrumental";
     genero_pre.push_back = "Vocal";
     genero_pre.push_back = "Efecto sonoro";
     genero_pre.push_back = "Inglés";
     genero_pre.push_back = "Bossa Nova";
     genero_pre.push_back = "Tropicália";
     genero_pre.push_back = "Samba";
     genero_pre.push_back = "Pagode";
     genero_pre.push_back = "Lambada";
     genero_pre.push_back = "MPB";
     genero_pre.push_back = "Forró";
     genero_pre.push_back = "Sertanejo";
     genero_pre.push_back = "Sertanejo universitário";
     genero_pre.push_back = "Axé";
     genero_pre.push_back = "Funk";
     genero_pre.push_back = "Gospel";
     genero_pre.push_back = "Brega";
     genero_pre.push_back = "Choro";
     genero_pre.push_back = "Frevo";
     genero_pre.push_back = "Baião";
     genero_pre.push_back = "Piseiro";
     genero_pre.push_back = "Pisadinha";

	initData.config[configName] = genero_pre;
}

function OnAddCommands(addCmdData) {
    var cmd = addCmdData.AddCommand();
    cmd.name = "Mp3TagFile";
    cmd.method = "OnMp3TagFile";
    cmd.desc = "";
    cmd.label = "Mp3 Tag File";
    cmd.template = "Mp3TagFile";
    cmd.hide = false;
    cmd.icon = "script";
}

function OnMp3TagFile(scriptCmdData) {
    var dlg = scriptCmdData.func.Dlg();
    dlg.title = "Mp3 Tag File - Directory Opus";
    dlg.template = 'Mp3TagFile';
    dlg.detach = true;
    dlg.Create

    var tab = scriptCmdData.func.sourcetab;
    var item = tab.selected_files(0);

    dlg.Control("archivo").value = item.name_stem;
    dlg.Control("titulo").value = item.metadata.audio.mp3title;
    dlg.Control("artista").value = item.metadata.audio.mp3artist;
    dlg.Control("album").value = item.metadata.audio.mp3album;
    dlg.Control("anio").value = item.metadata.audio.mp3year;
    dlg.Control("pista").value = item.metadata.audio.mp3track;
    dlg.Control("genero").label = item.metadata.audio.mp3genre;
    dlg.Control("comentario").value = item.metadata.audio.mp3comment;
    dlg.Control("artista_album").value = item.metadata.audio.mp3albumartist;
    dlg.Control("compositor").value = item.metadata.audio.composers;
    dlg.Control("numero_disco").value = item.metadata.audio.mp3disc;

    if (item.metadata.audio.coverart > 0) {
        var tmpFile = DOpus.FSUtil().GetTempFile();
        tmpFile.Write(item.metadata.audio.coverart(0).data);
        tmpFile.Close();
        dlg.Control("caratula").label = Script.LoadImage(tmpFile);
    } else {
        dlg.Control("caratula").label = '';
    }

    dlg.Control("caratula").bg = "#FFFFFF";

    if (item.metadata.audio.coverart > 0) {
        var tmpFile = DOpus.FSUtil().GetTempFile();
        tmpFile.Write(item.metadata.audio.coverart(0).data);
        tmpFile.Close();
        var reso = item.metadata.audio.coverart(0);
        dlg.Control("resolucion").label = reso.width + " x " + reso.height + " x " + reso.depth + " (" + reso.size.fmt + ")";
    } else {
        dlg.Control("resolucion").label = '';
    }

    dlg.Control("resolucion").fg = "#000000";
    dlg.Control("resolucion").bg = "#FFFFFF";

    dlg.Show

    while (true) {
        msg = dlg.GetMsg();
        if (!msg.result) break;

        var sayGoodBye = false;

        var cmd = scriptCmdData.func.command;
        var coverart_usuario;


        if (msg.event == "click") {
            switch (msg.control) {

                case "btn_anadir_caratula":
                    coverart_usuario = dlg.open("Carátula");
                    dlg.Control("caratula").label = Script.LoadImage(coverart_usuario);
                    break;

                case "btn_eliminar_caratula":
                    dlg.Control("caratula").label = '';
                    dlg.Control("resolucion").label = '';
                    break;

                case "btn_eliminar_todo":
                    dlg.Control("titulo").value = '';
                    dlg.Control("artista").value = '';
                    dlg.Control("album").value = '';
                    dlg.Control("anio").value = '';
                    dlg.Control("pista").value = '';
                    dlg.Control("genero").label = '';
                    dlg.Control("comentario").value = '';
                    dlg.Control("artista_album").value = '';
                    dlg.Control("compositor").value = '';
                    dlg.Control("numero_disco").value = '';
                    dlg.Control("caratula").label = '';
                    dlg.Control("resolucion").label = '';
                    break;

                case "btn_guardar":
                    var cmdLine = 'SetAttr META' +
                        ' "title:' + dlg.Control('titulo').value + '"' +
                        ' "artist:' + dlg.Control('artista').value + '"' +
                        ' "album:' + dlg.Control('album').value + '"' +
                        ' "year:' + dlg.Control('anio').value + '"' +
                        ' "track:' + dlg.Control('pista').value + '"' +
                        ' "genre:' + dlg.Control('genero').value.name + '"' +
                        ' "comment:' + dlg.Control('comentario').value + '"' +
                        ' "albumartist:' + dlg.Control('artista_album').value + '"' +
                        ' "composers:' + dlg.Control('compositor').value + '"' +
                        ' "discnumber:' + dlg.Control('numero_disco').value + '"' +
                        ' "coverart:3:' + coverart_usuario + '"';

                    DOpus.Output(cmdLine);
                    cmd.RunCommand(cmdLine);
                    sayGoodBye = true;
                    break;

            }
        }
        if (sayGoodBye) break;
    }
}

==SCRIPT RESOURCES
<resources>
	<resource name="Mp3TagFile" type="dialog">
		<dialog fontsize="9" height="180" lang="esm" title="Mp3TagFile" width="332">
			<control halign="left" height="8" name="archivo_t" title="Archivo:" type="static" valign="top" width="48" x="8" y="10" />
			<control halign="left" height="12" name="archivo" readonly="yes" type="edit" width="148" x="58" y="8" />
			<control halign="left" height="8" name="titulo_t" title="Título:" type="static" valign="top" width="48" x="8" y="26" />
			<control halign="left" height="12" name="titulo" type="edit" width="148" x="58" y="25" />
			<control halign="left" height="8" name="artista_t" title="Artista:" type="static" valign="top" width="48" x="8" y="43" />
			<control halign="left" height="12" name="artista" type="edit" width="148" x="58" y="42" />
			<control halign="left" height="8" name="album_t" title="Álbum:" type="static" valign="top" width="48" x="8" y="60" />
			<control halign="left" height="12" name="album" type="edit" width="148" x="58" y="59" />
			<control halign="left" height="8" name="anio_t" title="Año:" type="static" valign="top" width="48" x="8" y="78" />
			<control halign="center" height="12" name="anio" number="yes" type="edit" updown="yes" val_min="1" width="50" x="58" y="76" />
			<control halign="left" height="8" name="pista_t" title="Pista:" type="static" valign="top" width="18" x="136" y="78" />
			<control halign="center" height="12" name="pista" number="yes" type="edit" updown="yes" val_min="1" width="30" x="157" y="76" />
			<control halign="left" height="8" name="genero_t" title="Género:" type="static" valign="top" width="48" x="8" y="94" />
			<control edit="yes" height="40" name="genero" sort="yes" type="combo" width="148" x="58" y="93">
				<contents>
					<item text="Montuno" />
					<item text="Cumbia" /> 
					<item text="Merengue" />
					<item text="Tropical" />
					<item text="Ranchera" />
					<item text="Reggae" />
					<item text="Reggaetón" />
					<item text="Hip Hop" />
					<item text="Pop" />
					<item text="Rock" />
					<item text="Tecno" />
					<item text="Electrónico" />
					<item text="Jazz" />
					<item text="Infantil" />
					<item text="Religioso" />
					<item text="Instrumental" />
					<item text="Vocal" />
					<item text="Efecto sonoro" />
					<item text="Inglés" />
					<item text="Bossa Nova" />
					<item text="Tropicália" />
					<item text="Samba" />
					<item text="Pagode" />
					<item text="Lambada" />
					<item text="MPB" />
					<item text="Forró" />
					<item text="Sertanejo" />
					<item text="Sertanejo universitário" />
					<item text="Axé" />
					<item text="Funk" />
					<item text="Gospel" />
					<item text="Brega" />
					<item text="Choro" />
					<item text="Frevo" />
					<item text="Baião" />
					<item text="Piseiro" />
					<item text="Pisadinha" />
				</contents>
			</control>
			<control halign="left" height="8" name="comentario_t" title="Comentario:" type="static" valign="top" width="48" x="8" y="111" />
			<control halign="left" height="12" name="comentario" type="edit" width="148" x="58" y="110" />
			<control halign="left" height="8" name="artista_album_t" title="Artista álbum:" type="static" valign="top" width="48" x="9" y="128" />
			<control halign="left" height="12" name="artista_album" type="edit" width="148" x="59" y="127" />
			<control halign="left" height="8" name="compositor_t" title="Compositor:" type="static" valign="top" width="48" x="8" y="145" />
			<control halign="left" height="12" name="compositor" type="edit" width="148" x="58" y="144" />
			<control halign="left" height="8" name="numero_disco_t" title="Número disco:" type="static" valign="top" width="48" x="8" y="162" />
			<control halign="left" height="12" name="numero_disco" type="edit" updown="yes" val_min="1" width="148" x="58" y="161" />
			<control halign="center" height="8" name="resolucion" type="static" valign="top" width="100" x="218" y="95" />
			<control height="122" name="caratula_t" title="Carátula" type="group" width="113" x="212" y="4" />
			<control halign="center" height="86" image="yes" name="caratula" type="static" valign="center" width="100" x="218" y="15" />
			<control height="14" name="btn_anadir_caratula" title="Añadir..." type="button" width="41" x="224" y="107" />
			<control height="14" name="btn_eliminar_caratula" title="Eliminar" type="button" width="41" x="272" y="107" />
			<control height="15" name="btn_eliminar_todo" title="Eliminar todo" type="button" width="76" x="229" y="135" />
			<control default="yes" height="15" name="btn_guardar" title="Guardar" type="button" width="76" x="229" y="155" />
		</dialog>
	</resource>
</resources>

Please, someone help me here, no matter how much I study, I don't know how to resolve this situation.

push_back is a method which you call with (...), but you're trying to assign to it.

If you do some basic javascript tutorials, this kind of mistake will become obvious to you.

Thanks @Leo for your advice, I'm going to try to plan my time better to see if I can study more, although really my only intention is to finish this script and return to my normal life, without any code.

Thanks to God and the help of all of you, I finished another script that I was very interested Resize Image

Now I'm going to study a lot about push_back, I should have done it before, but I didn't know it was a method.

I have no experience with this. I started learning Opus Scripting at the end of 2022 and browsed many scripts in forums for reference.
Here's what I know:

    dlg.Control("genero").label = item.metadata.audio.mp3genre;   // Defalut value
	for (var i = 0; i < Script.config[configName].count; i++)
	{
		dlg.Control("genero").AddItem(Script.config[configName][i]);   // Add each item to drop-down list
	}
Summary
// Mp3 Tag File
// (c) 2024 DASOTA
// Script para Directory Opus.

var configName = "genero";
function OnInit(initData) {
    initData.name = "Mp3 Tag File";
    initData.version = "1.0";
    initData.copyright = "(c) 2024 DASOTA";
    initData.desc = "Editor Mp3 Tag de archivo único";
    initData.default_enable = true;
    initData.min_version = "13.0";

// Personalizar géneros musicales

	initData.config_desc = DOpus.Create.Map();
	initData.config_desc(configName) = "Modificar géneros musicales";

     var genero_pre = DOpus.NewVector();
     genero_pre.push_back = "Romántico";
     genero_pre.push_back = "Balada";
     genero_pre.push_back = "Bolero";
     genero_pre.push_back = "Trova";
     genero_pre.push_back = "Patrio";
     genero_pre.push_back = "Son";
     genero_pre.push_back = "Salsa";
     genero_pre.push_back = "Guaracha";
     genero_pre.push_back = "Montuno";
     genero_pre.push_back = "Cumbia";
     genero_pre.push_back = "Merengue";
     genero_pre.push_back = "Tropical";
     genero_pre.push_back = "Ranchera";
     genero_pre.push_back = "Reggae";
     genero_pre.push_back = "Reggaetón";
     genero_pre.push_back = "Hip Hop";
     genero_pre.push_back = "Pop";
     genero_pre.push_back = "Rock";
     genero_pre.push_back = "Tecno";
     genero_pre.push_back = "Electrónico";
     genero_pre.push_back = "Jazz";
     genero_pre.push_back = "Infantil";
     genero_pre.push_back = "Religioso";
     genero_pre.push_back = "Instrumental";
     genero_pre.push_back = "Vocal";
     genero_pre.push_back = "Efecto sonoro";
     genero_pre.push_back = "Inglés";
     genero_pre.push_back = "Bossa Nova";
     genero_pre.push_back = "Tropicália";
     genero_pre.push_back = "Samba";
     genero_pre.push_back = "Pagode";
     genero_pre.push_back = "Lambada";
     genero_pre.push_back = "MPB";
     genero_pre.push_back = "Forró";
     genero_pre.push_back = "Sertanejo";
     genero_pre.push_back = "Sertanejo universitário";
     genero_pre.push_back = "Axé";
     genero_pre.push_back = "Funk";
     genero_pre.push_back = "Gospel";
     genero_pre.push_back = "Brega";
     genero_pre.push_back = "Choro";
     genero_pre.push_back = "Frevo";
     genero_pre.push_back = "Baião";
     genero_pre.push_back = "Piseiro";
     genero_pre.push_back = "Pisadinha";

	initData.config[configName] = genero_pre;
}

function OnAddCommands(addCmdData) {
    var cmd = addCmdData.AddCommand();
    cmd.name = "Mp3TagFile";
    cmd.method = "OnMp3TagFile";
    cmd.desc = "";
    cmd.label = "Mp3 Tag File";
    cmd.template = "Mp3TagFile";
    cmd.hide = false;
    cmd.icon = "script";
}

function OnMp3TagFile(scriptCmdData) {
    var dlg = scriptCmdData.func.Dlg();
    dlg.title = "Mp3 Tag File - Directory Opus";
    dlg.template = 'Mp3TagFile';
    dlg.detach = true;
    dlg.Create

    var tab = scriptCmdData.func.sourcetab;
    var item = tab.selected_files(0);

    dlg.Control("archivo").value = item.name_stem;
    dlg.Control("titulo").value = item.metadata.audio.mp3title;
    dlg.Control("artista").value = item.metadata.audio.mp3artist;
    dlg.Control("album").value = item.metadata.audio.mp3album;
    dlg.Control("anio").value = item.metadata.audio.mp3year;
    dlg.Control("pista").value = item.metadata.audio.mp3track;
    dlg.Control("genero").label = item.metadata.audio.mp3genre;   // Defalut value
	for (var i = 0; i < Script.config[configName].count; i++)
	{
		dlg.Control("genero").AddItem(Script.config[configName][i]);   // Add each item to drop-down list
	}
    dlg.Control("comentario").value = item.metadata.audio.mp3comment;
    dlg.Control("artista_album").value = item.metadata.audio.mp3albumartist;
    dlg.Control("compositor").value = item.metadata.audio.composers;
    dlg.Control("numero_disco").value = item.metadata.audio.mp3disc;

    if (item.metadata.audio.coverart > 0) {
        var tmpFile = DOpus.FSUtil().GetTempFile();
        tmpFile.Write(item.metadata.audio.coverart(0).data);
        tmpFile.Close();
        dlg.Control("caratula").label = Script.LoadImage(tmpFile);
    } else {
        dlg.Control("caratula").label = '';
    }

    dlg.Control("caratula").bg = "#FFFFFF";

    if (item.metadata.audio.coverart > 0) {
        var tmpFile = DOpus.FSUtil().GetTempFile();
        tmpFile.Write(item.metadata.audio.coverart(0).data);
        tmpFile.Close();
        var reso = item.metadata.audio.coverart(0);
        dlg.Control("resolucion").label = reso.width + " x " + reso.height + " x " + reso.depth + " (" + reso.size.fmt + ")";
    } else {
        dlg.Control("resolucion").label = '';
    }

    dlg.Control("resolucion").fg = "#000000";
    dlg.Control("resolucion").bg = "#FFFFFF";

    dlg.Show

    while (true) {
        msg = dlg.GetMsg();
        if (!msg.result) break;

        var sayGoodBye = false;

        var cmd = scriptCmdData.func.command;
        var coverart_usuario;


        if (msg.event == "click") {
            switch (msg.control) {

                case "btn_anadir_caratula":
                    coverart_usuario = dlg.open("Carátula");
                    dlg.Control("caratula").label = Script.LoadImage(coverart_usuario);
                    break;

                case "btn_eliminar_caratula":
                    dlg.Control("caratula").label = '';
                    dlg.Control("resolucion").label = '';
                    break;

                case "btn_eliminar_todo":
                    dlg.Control("titulo").value = '';
                    dlg.Control("artista").value = '';
                    dlg.Control("album").value = '';
                    dlg.Control("anio").value = '';
                    dlg.Control("pista").value = '';
                    dlg.Control("genero").label = '';
                    dlg.Control("comentario").value = '';
                    dlg.Control("artista_album").value = '';
                    dlg.Control("compositor").value = '';
                    dlg.Control("numero_disco").value = '';
                    dlg.Control("caratula").label = '';
                    dlg.Control("resolucion").label = '';
                    break;

                case "btn_guardar":
                    var cmdLine = 'SetAttr META' +
                        ' "title:' + dlg.Control('titulo').value + '"' +
                        ' "artist:' + dlg.Control('artista').value + '"' +
                        ' "album:' + dlg.Control('album').value + '"' +
                        ' "year:' + dlg.Control('anio').value + '"' +
                        ' "track:' + dlg.Control('pista').value + '"' +
                        ' "genre:' + dlg.Control('genero').value.name + '"' +
                        ' "comment:' + dlg.Control('comentario').value + '"' +
                        ' "albumartist:' + dlg.Control('artista_album').value + '"' +
                        ' "composers:' + dlg.Control('compositor').value + '"' +
                        ' "discnumber:' + dlg.Control('numero_disco').value + '"' +
                        ' "coverart:3:' + coverart_usuario + '"';

                    DOpus.Output(cmdLine);
                    cmd.RunCommand(cmdLine);
                    sayGoodBye = true;
                    break;

            }
        }
        if (sayGoodBye) break;
    }
}

==SCRIPT RESOURCES
<resources>
	<resource name="Mp3TagFile" type="dialog">
		<dialog fontsize="9" height="180" lang="esm" title="Mp3TagFile" width="332">
			<control halign="left" height="8" name="archivo_t" title="Archivo:" type="static" valign="top" width="48" x="8" y="10" />
			<control halign="left" height="12" name="archivo" readonly="yes" type="edit" width="148" x="58" y="8" />
			<control halign="left" height="8" name="titulo_t" title="Título:" type="static" valign="top" width="48" x="8" y="26" />
			<control halign="left" height="12" name="titulo" type="edit" width="148" x="58" y="25" />
			<control halign="left" height="8" name="artista_t" title="Artista:" type="static" valign="top" width="48" x="8" y="43" />
			<control halign="left" height="12" name="artista" type="edit" width="148" x="58" y="42" />
			<control halign="left" height="8" name="album_t" title="Álbum:" type="static" valign="top" width="48" x="8" y="60" />
			<control halign="left" height="12" name="album" type="edit" width="148" x="58" y="59" />
			<control halign="left" height="8" name="anio_t" title="Año:" type="static" valign="top" width="48" x="8" y="78" />
			<control halign="center" height="12" name="anio" number="yes" type="edit" updown="yes" val_min="1" width="50" x="58" y="76" />
			<control halign="left" height="8" name="pista_t" title="Pista:" type="static" valign="top" width="18" x="136" y="78" />
			<control halign="center" height="12" name="pista" number="yes" type="edit" updown="yes" val_min="1" width="30" x="157" y="76" />
			<control halign="left" height="8" name="genero_t" title="Género:" type="static" valign="top" width="48" x="8" y="94" />
			<control edit="yes" height="40" name="genero" sort="yes" type="combo" width="148" x="58" y="93" />
			<control halign="left" height="8" name="comentario_t" title="Comentario:" type="static" valign="top" width="48" x="8" y="111" />
			<control halign="left" height="12" name="comentario" type="edit" width="148" x="58" y="110" />
			<control halign="left" height="8" name="artista_album_t" title="Artista álbum:" type="static" valign="top" width="48" x="9" y="128" />
			<control halign="left" height="12" name="artista_album" type="edit" width="148" x="59" y="127" />
			<control halign="left" height="8" name="compositor_t" title="Compositor:" type="static" valign="top" width="48" x="8" y="145" />
			<control halign="left" height="12" name="compositor" type="edit" width="148" x="58" y="144" />
			<control halign="left" height="8" name="numero_disco_t" title="Número disco:" type="static" valign="top" width="48" x="8" y="162" />
			<control halign="left" height="12" name="numero_disco" type="edit" updown="yes" val_min="1" width="148" x="58" y="161" />
			<control halign="center" height="8" name="resolucion" type="static" valign="top" width="100" x="218" y="95" />
			<control height="122" name="caratula_t" title="Carátula" type="group" width="113" x="212" y="4" />
			<control halign="center" height="86" image="yes" name="caratula" type="static" valign="center" width="100" x="218" y="15" />
			<control height="14" name="btn_anadir_caratula" title="Añadir..." type="button" width="41" x="224" y="107" />
			<control height="14" name="btn_eliminar_caratula" title="Eliminar" type="button" width="41" x="272" y="107" />
			<control height="15" name="btn_eliminar_todo" title="Eliminar todo" type="button" width="76" x="229" y="135" />
			<control default="yes" height="15" name="btn_guardar" title="Guardar" type="button" width="76" x="229" y="155" />
		</dialog>
	</resource>
</resources>

1 Like

Perfect :pray: Perfect :clap: Perfect :star_struck:

I had three nights practically without sleep, studying the subject, inventing a thousand different things, but nothing resembles your solution, I really would never have solved this.

I thank you from the bottom of my heart, I will soon update the script, now it will be better, each person will be able to customize their list of genres, THANK YOU A THOUSAND TIMES. :smiling_face_with_three_hearts:

1 Like

Please let me know when you update the script.

Hi, I'm trying to add autocomplete to the "Gender" field, ignoring accents and upper/lower case. I've inserted the following code snippets, but it doesn't work. Does anyone have any ideas? Thank you very much.

Mp3 Tag File 1.5 (complete).js.txt (20.8 KB)

// Normalize text (remove accents and convert to lowercase)
function normalizeText(text) {
    if (!text) return '';
    return text.toString()
        .normalize("NFD").replace(/[\u0300-\u036f]/g, "") // Remove accents
        .toLowerCase(); // Convert to lowercase
}
    // Find gender matches
    function searchGenreMatches(searchText) {
        searchText = normalizeText(searchText);
        genreMatches.Clear();

        if (!searchText) {
            // If there is no text, show all genres
            for (var i = 0; i < genres.length; i++) {
                genreMatches.push_back(genres(i));
            }
            return;
        }

        for (var i = 0; i < genres.length; i++) {
            var genre = genres(i);
            if (normalizeText(genre).indexOf(searchText) !== -1) {
                genreMatches.push_back(genre);
            }
        }
    }

    // Update genre dropdown list
    function updateGenreDropdown() {
        var currentText = dlg.Control('genero').value.name || dlg.Control('genero').value;
        if (currentText === lastGenreSearch) return;

        lastGenreSearch = currentText;
        searchGenreMatches(currentText);

        dlg.Control('genero').redraw = false;
        dlg.Control('genero').RemoveItem(-1);

        if (genreMatches.length > 0) {
            for (var i = 0; i < genreMatches.length; i++) {
                dlg.Control('genero').AddItem(genreMatches(i));
            }
        } else {
            // If there are no matches, show the current text as the only option
            if (currentText) {
                dlg.Control('genero').AddItem(currentText);
            }
        }

        // Keep the text you are typing
        dlg.Control('genero').value = currentText;
        dlg.Control('genero').redraw = true;
    }

Reading the error log seems easier than writing a post, but to each his own.

Object doesn't support property or method 'normalize' (0x800a01b6)

Here I run the script and no errors are generated. And it's not a matter of taste, but of knowledge. I think when people ask for help here, it's because they've tried everything and haven't succeeded.

I redefined the function to ignore accents and capital letters, it continues to not generate an error for me, but it also continues to not autocomplete the gender field.

// Normalize text (remove accents and convert to lowercase)
function normalizeText(text) {
    if (!text) return '';

    // Manual mapping of accented characters to their unaccented equivalents
    var accents = {
        'á': 'a', 'é': 'e', 'í': 'i', 'ó': 'o', 'ú': 'u',
        'à': 'a', 'è': 'e', 'ì': 'i', 'ò': 'o', 'ù': 'u',
        'ä': 'a', 'ë': 'e', 'ï': 'i', 'ö': 'o', 'ü': 'u',
        'â': 'a', 'ê': 'e', 'î': 'i', 'ô': 'o', 'û': 'u',
        'ã': 'a', 'ñ': 'n', 'õ': 'o',
        'ç': 'c',
        'Á': 'A', 'É': 'E', 'Í': 'I', 'Ó': 'O', 'Ú': 'U',
        'À': 'A', 'È': 'E', 'Ì': 'I', 'Ò': 'O', 'Ù': 'U',
        'Ä': 'A', 'Ë': 'E', 'Ï': 'I', 'Ö': 'O', 'Ü': 'U',
        'Â': 'A', 'Ê': 'E', 'Î': 'I', 'Ô': 'O', 'Û': 'U',
        'Ã': 'A', 'Ñ': 'N', 'Õ': 'O',
        'Ç': 'C'
    };

    var str = text.toString();
    var result = '';

    for (var i = 0; i < str.length; i++) {
        var c = str.charAt(i);
        result += accents[c] || c;
    }

    return result.toLowerCase(); // Convert to lowercase
}

I'm not completely sure if this is what are you refering by "autocomplete" a combobox, but here's an example of a combobox who can works also as a filter.
It's very simplified, and explained the best I could, I'm not good for that, TBH. :smiley:
Feel free to adapt it to your needs.

// This is a script for Directory Opus.
// See https://www.gpsoft.com.au/endpoints/redirect.php?page=scripts for development information.

// Called by Directory Opus to initialize the script
function OnInit(initData) {
	initData.name = 'dlgtest';
	initData.version = '1.0';
	initData.desc = '';
	initData.default_enable = true;
	initData.min_version = '13.16';

}

// Called to add commands to Opus
function OnAddCommands(addCmdData) {
	var cmd = addCmdData.AddCommand();
	cmd.name = 'dlgtest';
	cmd.method = 'Ondlgtest';
	cmd.desc = '';
	cmd.label = 'dlgtest';
	cmd.template = '';
	cmd.hide = false;
	cmd.icon = 'script';
}
// Implement the dlgtest command
function Ondlgtest(scriptCmdData) {
	var dlg = scriptCmdData.func.Dlg();
	dlg.template = 'dialog1';
	dlg.title = 'combo test';
	dlg.Create();
	var dlg_combo = dlg.Control('combo');

	//iniciamos la variable de géneros. En este caso estoy creandola desde cero, en lugar de leerla de otro lado
	var generos = DOpus.NewVector('A Cappella', 'Abstract', 'Acid', 'Acoustic', 'Alternative', 'Ambient', 'Anime', 'Ballad', 'Baroque',
		'Blues', 'Breakbeat', 'Cabaret', 'Chorus', 'Christian', 'Classical', 'Comedy', 'Country', 'Cult', 'Dance', 'Disco', 'Dream', 'Electro',
		'Electronic', 'Folklore', 'Freestyle', 'Funk', 'Fusion', 'Game', 'Gospel', 'Gothic', 'Grunge', 'Hard Rock', 'Hardcore', 'Heavy Metal',
		'Hip-Hop', 'House', 'Humour', 'Industrial', 'Industro-Goth', 'Instrumental', 'Jazz', 'Latin', 'Meditative', 'Merengue', 'Metal',
		'Musical', 'Noise', 'Oldies', 'Opera', 'Other', 'Podcast', 'Pop', 'Pranks', 'Primus', 'Punk', 'Rap', 'Reggae', 'Retro', 'Rock',
		'Rock & Roll', 'Salsa', 'Samba', 'Sonata', 'Soul', 'Space', 'Tango', 'Techno', 'Trailer', 'Tribal', 'Vocal');
	//creamos un mapa de los generos con los diacriticos removidos
	var generos_map = DOpus.NewMap();
	//utilidad para quitar diacriticos
	var str_tools = DOpus.Create().StringTools();

	//removemos diacriticos y ponemos a minusculas antes del loop para agilizar el proceso
	for (var i = generos.length - 1; i >= 0; i--) {
		generos_map.Set(generos(i), str_tools.RemoveDiacritics(generos(i).toLowerCase()));
	}
	//creamos un enumerador para manejar el mapa
	var enum_gen = new Enumerator(generos_map);

	//llenamos el combobox llamando este metodo
	updateCombo();

	//seleccionamos el primer valor del combobox
	dlg_combo.value = 0;
	//iniciamos variables que vayamos a usar luego
	var msg;
	//mostramos el dialogo
	dlg.Show();

	while (true) {
		msg = dlg.GetMsg();
		if (!msg.result) break;

		if (msg.event === 'editchange' && msg.name === 'combo') {
			//creamos un timer para evitar multiples llamadas cuando el usuario escriba, mejorando el tiempo de respuesta y la usabilidad
			dlg.SetTimer(150, 'combo_timer');
		}
		else if (msg.event === 'timer' && msg.name === 'combo_timer') {
			//Quitamos el timer primero
			dlg.KillTimer('combo_timer');
			//llamamos a updateCombo con el contenido escrito
			updateCombo(dlg_combo.value.name);
		}
	}

	return;

	//actualiza el contenido del combobox, "autocompletando" según sea necesario
	//esta función está dentro la función principal, para que nos permita utilizar sus variables (el diálogo, etc)
	function updateCombo(combo_str) {
		//si se ha pasado texto, remover los diacriticos y ponerlo en minúsculas
		if (combo_str) var str = str_tools.RemoveDiacritics(combo_str.toLowerCase());
		//deshabilitamos el redibujado del combobox, para evitar artefactos visuales
		dlg_combo.redraw = false;
		//quitamos todos los valores del combobox
		dlg_combo.RemoveItem(-1);
		//movemos el mapa de generos a la primera posicion
		enum_gen.moveFirst();
		//iteramos el mapa
		var item;
		for (; !enum_gen.atEnd(); enum_gen.moveNext()) {
			item = enum_gen.item();
			//verificamos si el valor actual es candidato, usando el valor sin diacriticos
			//hay muchas formas de hacer esta seleccion, esta es la mas sencilla
			//si no hubiese contenido a filtrar, deja pasar todo
			if (!combo_str || generos_map(item).indexOf(str) !== -1)
				dlg_combo.AddItem(item);
		}
		//habilitamos el redibujado del combobox
		dlg_combo.redraw = true;
		if (!combo_str) return;
		//volvemos a llenar el valor anterior del combobox
		dlg_combo.label = combo_str;
		//movemos el caret de texto del combobox al final, para evitar que se entremezcle los caracteres
		dlg_combo.SelectRange(combo_str.length);

	}

}
==SCRIPT RESOURCES
<resources>
	<resource name="dialog1" type="dialog">
		<dialog height="46" lang="english" standard_buttons="ok" width="158">
			<control edit="yes" height="40" name="combo" sort="yes" type="combo" width="152" x="4" y="4" />
		</dialog>
	</resource>
</resources>

You can see that I'm using a Timer for manage better the filtering process. Also I'm "preprocessing" the text outside the loops to speed things up and using the current tools DOpus API scripting offers.
Is a very basic example, I hope you might find useful.
Note : You might want to look at DOpus.MusicGenres(), since it already contains a list of user-defined music genres.

1 Like

@errante, I'll take a good look at your code and try to adapt it to the script, thank you very, very much!!!