In this project, we are opening MP3 files using the component 3delite.hu =- ID3v2 Library, and adding the MP3 files metadata to a ListView.
What I would like to accomplish.
When selecting multiple items in the listview, the edit fields will show <multi>
, meaning that multiple items have been selected. If you try and edit anything and save it, this will result in the files getting the wrong data sent to them.
What I would like to ask is this.
How can you select multiple items, edit certain areas, and have it update each file individually?
Below is the code for this project.
LoadIt.pas
unit LoadIt;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, ComCtrls, StdCtrls, ID3v1Library, ID3v2Library, UITypes, StrUtils;
type
TForm1 = class(TForm)
SidePanel: TPanel;
MainPanel: TPanel;
ListView1: TListView;
OpenDialog1: TOpenDialog;
strTitle: TEdit;
strArtist: TEdit;
Label1: TLabel;
Label2: TLabel;
strAlbum: TEdit;
Label3: TLabel;
strTrack: TEdit;
Label5: TLabel;
TopPanel: TPanel;
Button8: TButton;
Edit1: TEdit;
Label4: TLabel;
strPath: TEdit;
saveButton: TButton;
Memo1: TMemo;
SelectedLabel: TLabel;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure Button8Click(Sender: TObject);
procedure ListView1SelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
procedure saveButtonClick(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
ID3v1Tag: TID3v1Tag = nil;
ID3v2Tag: TID3v2Tag = nil;
CurrentAPICIndex: Integer = - 1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
Column: TListColumn;
begin
TopPanel.Align := alTop;
SidePanel.Align := alLeft;
MainPanel.Align := alClient;
ListView1.Align := alClient;
ID3v1Tag := TID3v1Tag.Create;
ID3v2Tag := TID3v2Tag.Create;
Column := ListView1.Columns.Add;
Column.Caption := 'Track ID';
Column.Alignment := taLeftJustify;
Column.Width := 60;
Column := ListView1.Columns.Add;
Column.Caption := 'Artist';
Column.Alignment := taLeftJustify;
Column.Width := 200;
Column := ListView1.Columns.Add;
Column.Caption := 'Album';
Column.Alignment := taLeftJustify;
Column.Width := 200;
Column := ListView1.Columns.Add;
Column.Caption := 'Title';
Column.Alignment := taLeftJustify;
Column.Width := 200;
Column := ListView1.Columns.Add;
Column.Caption := 'File ALocation';
Column.Alignment := taLeftJustify;
Column.Width := 0;
end;
procedure AddFileToTree(ListView: TListView; FileName: String);
var
SR: TSearchRec;
ListItem: TListItem;
begin
// FileSize := 0;
if (FindFirst(FileName, faAnyFile, SR) = 0) then
begin
// FileSize := SR.Size;
// FileDate := FileDateToDateTime(SR.Time);
ListItem := ListView.Items.Add;
// get the MP3 File name
// Track Number
ListItem.Caption := ID3v2Tag.GetUnicodeText('TRCK'); //0
// get the Artist
ListItem.SubItems.Add(ID3v2Tag.GetUnicodeText('TPE1')); // 1
// get the Album
ListItem.SubItems.Add(ID3v2Tag.GetUnicodeText('TALB')); // 2
// get the Song title
ListItem.SubItems.Add(ID3v2Tag.GetUnicodeText('TIT2')); // 3
// get the path to the MP3 file
ListItem.SubItems.Add(FileName); // 4
// First we get the MP3 File name
ListItem.SubItems.Add(extractfilename(FileName)); //5
end;
end;
// This section was coded by: Remy Lebeau
// https://stackoverflow.com/a/78172902/2031172
procedure TForm1.ListView1SelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
var
ls: TListItem;
sTrack, sArtist, sSong, sAlbum, sPath: string;
procedure CheckForMulti(var selectedStr: string; const value: string);
begin
if selectedStr = '' then
selectedStr := value
else if selectedStr <> value then
selectedStr := '<multi>';
end;
begin
ls := ListView1.Selected;
while Assigned(ls) do
begin
CheckForMulti(sTrack, ls.Caption);
CheckForMulti(sArtist, ls.SubItems[0]);
CheckForMulti(sSong, ls.SubItems[1]);
CheckForMulti(sAlbum, ls.SubItems[2]);
CheckForMulti(sPath, ls.SubItems[3]);
ls := ListView1.GetNextItem(ls, sdAll, [isSelected]);
end;
strTrack.Text := sTrack;
strArtist.Text := sArtist;
strTitle.Text := sSong;
strAlbum.Text := sAlbum;
strPath.Text := sPath;
end;
procedure TForm1.saveButtonClick(Sender: TObject);
var
ErrorCode, i: Integer;
LanguageID: TLanguageID;
FileName : string;
ListItem: TListItem;
getString : string;
LoadIt : integer;
begin
if ID3v2Tag.MajorVersion = 2 then begin
if MessageDlg('The tag in the file is an ID3v2.2 tag, if you click ''Yes'' it will be saved as an ID3v2.3 or ID3v2.4 tag. Do you want to save the tag?', mtConfirmation, [mbYes, mbCancel], 0) <> mrYes then begin
Exit;
end;
end;
ID3v2Tag.SetUnicodeText('TPE1', strArtist.Text);
ID3v2Tag.SetUnicodeText('TALB', strAlbum.Text);
ID3v2Tag.SetUnicodeText('TIT2', strTitle.Text);
ID3v2Tag.SetUnicodeText('TRCK', strTrack.Text);
ErrorCode := ID3v2Tag.SaveToFile(strPath.text);
// ErrorCode := ID3v2Tag.SaveToFile(strPath.Text{$IFDEF DEBUG}, False{$ENDIF});
if ErrorCode <> ID3V2LIBRARY_SUCCESS then begin
MessageDlg('Error saving ID3v2 tag, error code: ' + IntToStr(ErrorCode) + #13#10 + ID3v2TagErrorCode2String(ErrorCode), mtError, [mbOk], 0);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
Item: TListItem;
test: System.String;
begin
memo1.Lines.Clear;
Item := ListView1.Selected;
while Item <> nil do
begin
// From Remy Lebeau - https://stackoverflow.com/a/14189378/2031172
Memo1.SelStart := Memo1.GetTextLen;
Memo1.SelLength := 0;
//memo1.lines.Add(Item.SubItems[3]);
// Added in this to created a comma delemited list.
// This should make it easier to iterate through.
Memo1.SelText := Item.SubItems[3] + ',';
Item := ListView1.GetNextItem(Item, sdAll, [isSelected]);
end;
// Remove the last character (comma) in the string.
// Mike Jablonski - https://stackoverflow.com/a/29102436/2031172
test := Memo1.Text;
test := Copy(test,1,length(test)-1);
Memo1.Text := test;
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Memo1.Clear;
end;
procedure TForm1.Button8Click(Sender: TObject);
var
i: integer;
LoadIt: integer;
begin
ListView1.Clear;
if OpenDialog1.execute then
for i := 0 to OpenDialog1.Files.Count - 1 do
if SameText(ExtractFileExt(OpenDialog1.Files[i]), '.mp3') then
begin
// Bug fix here. Changing from the original TEdit to the OpenDialog.Files[i] fixed song titles in list.
LoadIt := ID3v2Tag.LoadFromFile(OpenDialog1.Files[i]);
AddFileToTree(ListView1, OpenDialog1.Files[i]);
end;
end;
end.
LoadIt.dfm
object Form1: TForm1
Left = 0
Top = 0
Caption = 'Form1'
ClientHeight = 375
ClientWidth = 688
Color = clBtnFace
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
OnCreate = FormCreate
TextHeight = 13
object SidePanel: TPanel
Left = -4
Top = 128
Width = 189
Height = 252
TabOrder = 0
object Label1: TLabel
Left = 4
Top = 40
Width = 38
Height = 19
Caption = 'Artist'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
end
object Label2: TLabel
Left = 4
Top = 95
Width = 31
Height = 19
Caption = 'Title'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
end
object Label3: TLabel
Left = 4
Top = 68
Width = 47
Height = 19
Caption = 'Album'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
end
object Label5: TLabel
Left = 4
Top = 120
Width = 56
Height = 19
Caption = 'Track #'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
end
object strTitle: TEdit
Left = 61
Top = 93
Width = 121
Height = 21
TabOrder = 0
Text = 'Song Title'
end
object strArtist: TEdit
Left = 61
Top = 39
Width = 121
Height = 21
TabOrder = 1
Text = 'Artist Name'
end
object strAlbum: TEdit
Left = 61
Top = 66
Width = 121
Height = 21
TabOrder = 2
Text = 'Artist Name'
end
object strTrack: TEdit
Left = 61
Top = 120
Width = 121
Height = 21
TabOrder = 3
Text = 'Artist Name'
end
object saveButton: TButton
Left = 21
Top = 156
Width = 59
Height = 22
Caption = 'Save...'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
TabOrder = 4
OnClick = saveButtonClick
end
end
object MainPanel: TPanel
Left = 200
Top = 130
Width = 441
Height = 201
TabOrder = 1
object ListView1: TListView
Left = 104
Top = 27
Width = 200
Height = 150
Columns = <>
MultiSelect = True
RowSelect = True
SortType = stText
TabOrder = 0
ViewStyle = vsReport
OnSelectItem = ListView1SelectItem
end
end
object TopPanel: TPanel
Left = 8
Top = 8
Width = 649
Height = 114
TabOrder = 2
object Label4: TLabel
Left = 352
Top = 15
Width = 31
Height = 19
Caption = 'Path'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -16
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
end
object SelectedLabel: TLabel
Left = 81
Top = 45
Width = 89
Height = 13
Caption = 'All Selected Paths.'
end
object Button8: TButton
Left = 9
Top = 12
Width = 59
Height = 22
Caption = 'Select...'
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
TabOrder = 0
OnClick = Button8Click
end
object Edit1: TEdit
Left = 74
Top = 13
Width = 265
Height = 21
Font.Charset = DEFAULT_CHARSET
Font.Color = clWindowText
Font.Height = -11
Font.Name = 'Tahoma'
Font.Style = []
ParentFont = False
TabOrder = 1
end
object strPath: TEdit
Left = 389
Top = 12
Width = 244
Height = 21
TabOrder = 2
Text = 'strPath'
end
object Memo1: TMemo
Left = 74
Top = 64
Width = 505
Height = 41
ScrollBars = ssVertical
TabOrder = 3
end
object Button1: TButton
Left = 192
Top = 40
Width = 91
Height = 25
Caption = 'Get All Selected'
TabOrder = 4
OnClick = Button1Click
end
object Button2: TButton
Left = 308
Top = 40
Width = 75
Height = 25
Caption = 'Clear Paths'
TabOrder = 5
OnClick = Button2Click
end
end
object OpenDialog1: TOpenDialog
Filter = 'MP3|*.mp3'
Options = [ofHideReadOnly, ofAllowMultiSelect, ofEnableSizing]
Left = 598
Top = 324
end
end
Project1.dpr
program Project1;
uses
Forms,
LoadIt in 'LoadIt.pas' {Form1};
{$R *.res}
begin
Application.Initialize;
Application.MainFormOnTaskbar := True;
Application.CreateForm(TForm1, Form1);
Application.Run;
end.
For what you are attempting, you need to keep track of the changes being made (ie, have a look at the TEdit.Modified
property) and then loop through the selected items saving those changes to each individual file as needed.
Also, AddFileToTree()
is leaking the TSearchRec
resources as it is not calling FindClose()
. But, since you are not actually using the TSearchRec
for anything useful, you should just use FileExists()
instead. Or better, just load the file unconditionally and then check whether ID3v2Tag.LoadFromFile()
succeeded or failed.
Try something more like this:
unit LoadIt;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, ExtCtrls, ComCtrls, StdCtrls, ID3v1Library, ID3v2Library, UITypes, StrUtils;
type
TForm1 = class(TForm)
SidePanel: TPanel;
MainPanel: TPanel;
ListView1: TListView;
OpenDialog1: TOpenDialog;
strTitle: TEdit;
strArtist: TEdit;
Label1: TLabel;
Label2: TLabel;
strAlbum: TEdit;
Label3: TLabel;
strTrack: TEdit;
Label5: TLabel;
TopPanel: TPanel;
Button8: TButton;
Edit1: TEdit;
Label4: TLabel;
strPath: TEdit;
saveButton: TButton;
Memo1: TMemo;
SelectedLabel: TLabel;
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
procedure FormDeestroy(Sender: TObject);
procedure Button8Click(Sender: TObject);
procedure ListView1SelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
procedure saveButtonClick(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Button2Click(Sender: TObject);
private
{ Private declarations }
ID3v1Tag: TID3v1Tag;
ID3v2Tag: TID3v2Tag;
procedure AddFileToTree(FileName: String);
public
{ Public declarations }
end;
var
Form1: TForm1;
CurrentAPICIndex: Integer = - 1;
implementation
{$R *.dfm}
procedure TForm1.FormCreate(Sender: TObject);
var
Column: TListColumn;
begin
ID3v1Tag := TID3v1Tag.Create;
ID3v2Tag := TID3v2Tag.Create;
TopPanel.Align := alTop;
SidePanel.Align := alLeft;
MainPanel.Align := alClient;
ListView1.Align := alClient;
Column := ListView1.Columns.Add;
Column.Caption := 'Track ID';
Column.Alignment := taLeftJustify;
Column.Width := 60;
Column := ListView1.Columns.Add;
Column.Caption := 'Artist';
Column.Alignment := taLeftJustify;
Column.Width := 200;
Column := ListView1.Columns.Add;
Column.Caption := 'Album';
Column.Alignment := taLeftJustify;
Column.Width := 200;
Column := ListView1.Columns.Add;
Column.Caption := 'Title';
Column.Alignment := taLeftJustify;
Column.Width := 200;
Column := ListView1.Columns.Add;
Column.Caption := 'File Allocation';
Column.Alignment := taLeftJustify;
Column.Width := 0;
end;
procedure TForm1.FormDestroy(Sender: TObject);
begin
ID3v1Tag.Free;
ID3v2Tag.Free;
end;
procedure TForm1.AddFileToTree(FileName: String);
var
ErrorCode: integer;
ListItem: TListItem;
begin
ErrorCode := ID3v2Tag.LoadFromFile(FileName);
if ErrorCode <> ID3V2LIBRARY_SUCCESS then begin
MessageDlg('Error loading ID3v2 tag from file: ' + FileName + #13#10 + 'Error code: ' + IntToStr(ErrorCode) + #13#10 + ID3v2TagErrorCode2String(ErrorCode), mtError, [mbOk], 0);
Exit;
end;
ListItem := ListView1.Items.Add;
// get the MP3 File name
// Track Number
ListItem.Caption := ID3v2Tag.GetUnicodeText('TRCK'); //0
// get the Artist
ListItem.SubItems.Add(ID3v2Tag.GetUnicodeText('TPE1')); // 1
// get the Album
ListItem.SubItems.Add(ID3v2Tag.GetUnicodeText('TALB')); // 2
// get the Song title
ListItem.SubItems.Add(ID3v2Tag.GetUnicodeText('TIT2')); // 3
// get the path to the MP3 file
ListItem.SubItems.Add(FileName);// 4
// First we get the MP3 File name
ListItem.SubItems.Add(ExtractFileName(FileName)); //5
end;
procedure TForm1.ListView1SelectItem(Sender: TObject; Item: TListItem;
Selected: Boolean);
var
ListItem: TListItem;
sTrack, sArtist, sSong, sAlbum, sPath: string;
procedure CheckForMulti(var selectedStr: string; const value: string);
begin
if selectedStr = '' then
selectedStr := value
else if selectedStr <> value then
selectedStr := '<multi>';
end;
begin
ListItem := ListView1.Selected;
while ListItem <> nil do
begin
CheckForMulti(sTrack, ListItem.Caption);
CheckForMulti(sArtist, ListItem.SubItems[0]);
CheckForMulti(sSong, ListItem.SubItems[1]);
CheckForMulti(sAlbum, ListItem.SubItems[2]);
CheckForMulti(sPath, ListItem.SubItems[3]);
ListItem := ListView1.GetNextItem(ListItem, sdAll, [isSelected]);
end;
if not strTrack.Modified then begin
strTrack.Text := sTrack;
strTrack.Modified := False;
end;
if not strArtist.Modified then begin
strArtist.Text := sArtist;
strArtist.Modified := False;
end;
if not strTitle.Modified then begin
strTitle.Text := sSong;
strTitle.Modified := False;
end;
if not strAlbum.Modified then begin
strAlbum.Text := sAlbum;
strAlbum.Modified := False;
end;
strPath.Text := sPath;
end;
procedure TForm1.saveButtonClick(Sender: TObject);
var
FileName : string;
ListItem: TListItem;
ErrorCode: Integer;
begin
if (not strTrack.Modified) and
(not strArtist.Modified) and
(not strTitle.Modified) and
(not strAlbum.Modified) then
begin
MessageDlg('No changes to save', mtInformation, [mbOk], 0);
Exit;
end;
if ID3v2Tag.MajorVersion = 2 then begin
if MessageDlg('The tag in the file is an ID3v2.2 tag, if you click ''Yes'' it will be saved as an ID3v2.3 or ID3v2.4 tag. Do you want to save the tag?', mtConfirmation, [mbYes, mbCancel], 0) <> mrYes then begin
Exit;
end;
end;
ListItem := ListView1.Selected;
while ListItem <> nil do
begin
if strTrack.Modified then
sTrack := strTrack.Text
else
sTrack := ListItem.Caption;
if strArtist.Modified then
sArtist := strArtist.Text
else
sArtist := ListItem.SubItems[0];
if strTitle.Modified then
sSong := strTitle.Text
else
sSong := ListItem.SubItems[1];
if strAlbum.Modified then
sAlbum := strAlbum.Text
else
sAlbum := ListItem.SubItems[2];
ID3v2Tag.SetUnicodeText('TPE1', sArtist);
ID3v2Tag.SetUnicodeText('TALB', sAlbum);
ID3v2Tag.SetUnicodeText('TIT2', sSong);
ID3v2Tag.SetUnicodeText('TRCK', sTrack);
FileName := ListItem.SubItems[3];
ErrorCode := ID3v2Tag.SaveToFile(FileName);
// ErrorCode := ID3v2Tag.SaveToFile(FileName{$IFDEF DEBUG}, False{$ENDIF});
if ErrorCode <> ID3V2LIBRARY_SUCCESS then begin
MessageDlg('Error saving ID3v2 tag, error code: ' + IntToStr(ErrorCode) + #13#10 + ID3v2TagErrorCode2String(ErrorCode), mtError, [mbOk], 0);
end;
ListItem := ListView1.GetNextItem(ListItem, sdAll, [isSelected]);
end;
strTrack.Modified := False;
strArtist.Modified := False;
strTitle.Modified := False;
strAlbum.Modified := False;
end;
procedure TForm1.Button1Click(Sender: TObject);
var
ListItem: TListItem;
test: String;
begin
test := '';
ListItem := ListView1.Selected;
if ListItem <> nil then
begin
do
test := test + ListItem.SubItems[3] + ',';
ListItem := ListView1.GetNextItem(ListItem, sdAll, [isSelected]);
until ListItem = nil;
// Remove the last character (comma) in the string.
SetLength(test, Length(test)-1);
end;
Memo1.Text := test;
{ alternatively:
SL := TStringList.Create;
try
ListItem := ListView1.Selected;
while ListItem <> nil do
begin
SL.Add(ListItem.SubItems[3]);
ListItem := ListView1.GetNextItem(ListItem, sdAll, [isSelected]);
end;
Memo1.Text := SL.CommaText;
finally
SL.Free;
end;
}
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
Memo1.Clear;
end;
procedure TForm1.Button8Click(Sender: TObject);
var
i: integer;
FileName: string;
begin
ListView1.Clear;
if OpenDialog1.Execute then
begin
for i := 0 to OpenDialog1.Files.Count - 1 do
begin
FileName := OpenDialog1.Files[i];
if SameText(ExtractFileExt(FileName), '.mp3') then
AddFileToTree(FileName);
end;
end;
end;
end.