You are not logged in.
Hi,
I am trying to use PascalScript to serialize duplicates, while also renaming the first duplicate file. Consider the following filename:
SPCYCLIC Beats 130 IDMModular Drums.wav
I want all duplicates to be renamed as well as the first file.
So if there are five files with the same name as above, I want the following result:
SPCYCLIC Beats 130 IDMModular Drums 1.wav
SPCYCLIC Beats 130 IDMModular Drums 2.wav
SPCYCLIC Beats 130 IDMModular Drums 3.wav
SPCYCLIC Beats 130 IDMModular Drums 4.wav
SPCYCLIC Beats 130 IDMModular Drums 5.wav
This is the script I'm using:
var
Files: TWideStringArray;
procedure Add(const S: WideString);
begin
SetLength(Files, Length(Files)+1);
Files[Length(Files)-1] := S;
end;
function Exists(const S: WideString): Boolean;
var I: Integer;
begin
Result := False;
for I:=0 to Length(Files)-1 do
if WideSameText(Files[I], S) then
begin Result := True; Break; end;
end;
var
NewFileName: WideString;
Counter: Integer;
begin
Counter := 2;
NewFileName := FileName;
while Exists(NewFileName) do
begin
NewFileName :=
WideExtractBaseName(FileName) +
' ' + IntToStr(Counter) +
WideExtractFileExt(FileName);
Counter := Counter + 1;
end;
FileName := NewFileName;
Add(FileName);
end.
It partially works, but it doesn't rename the first file. This is because "while Exists(NewFileName)" will always evaluate to false for the first member of a duplicate set.
How can I accomplish my goal here? Is this even possible?
Edit: I just wanted to specify that only files in a set of duplicates should be renamed. If the file doesn't have existing duplicates, it should not be renamed at all. So the default state is no serialization suffix. All examples on the wiki either only rename the duplicate files, or rename all files. Hopefully this is clear enough.
Any help at all would be greatly appreciated.
Last edited by visusys (2023-01-31 04:54)
Offline
Just wanted to add that I found the solution from another forum post (Thanks to SafetyCar).
Here's the final script for my use:
var
NamesDB: TStringsArray;
NamesTXT: WideString;
I, Count: Integer;
Initialized, EmptyVars: Boolean;
Files: TStringsArray;
NewFileName: WideString;
Counter: Integer;
procedure Add(const S: WideString);
begin
SetLength(Files, Length(Files)+1);
Files[Length(Files)-1] := S;
end;
function Exists(const S: WideString): Boolean;
var I: Integer;
begin
Result := False;
for I:=0 to Length(Files)-1 do
if WideSameText(Files[i], S) then
begin Result := True; Break; end;
end;
begin
NamesTXT := WideGetTempPath + '\~temp_vars_names.txt';
FileName := ReplaceRegEx(WideExtractBaseName(FileName), ' *(\(\d+\)|-\d+)$', '', False, False) + WideExtractFileExt(FileName);
If not Initialized then
begin
If not WideFileExists(NamesTXT) then FileWriteContent(NamesTXT, '');
If FileReadContent(NamesTXT)='' then EmptyVars:=True else EmptyVars:=False;
end;
If EmptyVars then
begin
FileAppendContent(NamesTXT, FileName + #13#10);
FileName := ':: Please do the Preview again ::';
end
else
begin
If not Initialized then
begin
NamesDB := WideSplitString(FileReadContent(NamesTXT), #13#10);
WideDeleteFile(NamesTXT);
end;
Count:=0
For I:=0 to length(NamesDB)-1 do
begin
If WideSameText(FileName, NamesDB[i]) then Count := Count + 1;
end;
If Count>1 then
begin
Counter := 1;
NewFileName := FileName;
If not Exists(FileName) then Add(FileName);
while Exists(NewFileName) do
begin
NewFileName :=
WideExtractBaseName(FileName) +
' ' + IntToStr(Counter) +
WideExtractFileExt(FileName);
Counter := Counter + 1;
end;
FileName := NewFileName;
Add(FileName);
end;
end;
Initialized := True;
end.
Offline
You might get rid of the need for two runs as well as the usage of an external file.
If you make use of GetMarkedFiles you'll get all full qualified filenames. By calling GetMarkedFiles you'll have dismissed the path parts.
And: if you hold the filenames as WideStrings you should store them in a TWideStringArray
Just an untested scratch:
var
NamesDB: TWideStringArray;
I, Count: Integer;
Initialized: Boolean;
Files: TWideStringArray;
NewFileName: WideString;
Counter: Integer;
procedure Add(const S: WideString);
begin
SetLength(Files, Length(Files)+1);
Files[Length(Files)-1] := S;
end;
function Exists(const S: WideString): Boolean;
var
I: Integer;
begin
Result := False;
for I:=0 to Length(Files)-1 do
if WideSameText(Files[i], S) then
begin
Result := True;
Break;
end;
end;
begin
If not Initialized then
begin
NamesDB := GetMarkedFiles();
For I:=0 to length(NamesDB)-1 do
begin
NamesDB[i] := WideExtractFileName(NamesDB[i]);
end;
Initialized := True;
end;
FileName := ReplaceRegEx(WideExtractBaseName(FileName), ' *(\(\d+\)|-\d+)$', '', False, False) + WideExtractFileExt(FileName);
Count:=0
For I:=0 to length(NamesDB)-1 do
begin
If WideSameText(FileName, NamesDB[i]) then Count := Count + 1;
end;
If Count > 1 then
begin
Counter := 1;
NewFileName := FileName;
If not Exists(FileName) then Add(FileName);
while Exists(NewFileName) do
begin
NewFileName :=
WideExtractBaseName(FileName) +
' ' + IntToStr(Counter) +
WideExtractFileExt(FileName);
Counter := Counter + 1;
end;
FileName := NewFileName;
Add(FileName);
end;
end.
Some more notes:
You are assuming incoming filename with number at the end which you are cutting in advance. But the numbers are either in brackets ' (001)' or following space and dash ' -02'
Seeking won't work in NamesDB if you want these cut off to lead to some grouping. So 'abc (01)' and 'abc (02)' will both be changed to 'abc' but no group is recognized.
To resolve this NamesDB has to be cleaned with the same replacement.
Offline
While very clever, the problem with your approach is that I'm doing this serialization after a lot of filename manipulation. The names on disk will be different than the "current" new name. Sometimes during the normal processing of this preset, it generates a duplicate filename when there was none before. So I'm not too keen on going down the GetMarkedFiles rabbit hole, and this is also the reason why I posted about adding two new functions to ReNamer here.
There is one option I want to explore, but I'm a bit unsure if it will work. I'm wondering if I can split this into two PascalScript rules:
Rule 1: Create a temporary file and fill it with all of the current filenames.
Rule 2: Read the temporary file and perform serialization, then delete the file.
This might let me avoid having to preview twice. But I'm not sure if the temporary file is created exactly when Rule 1 is executed and before Rule 2 is executed. I.E. Will Rule 2 really be able to read the file during the preview process?
You are assuming incoming filename with number at the end which you are cutting in advance. But the numbers are either in brackets ' (001)' or following space and dash ' -02'
I'm not quite sure I follow what you're saying here, but I am not operating on files that have brackets or "space and dash" formatting. I am just adding a number (serializing) exactly duplicate filenames.
Thanks for all your suggestions!
Offline
You are assuming incoming filename with number at the end which you are cutting in advance. But the numbers are either in brackets ' (001)' or following space and dash ' -02'
I'm not quite sure I follow what you're saying here, but I am not operating on files that have brackets or "space and dash" formatting. I am just adding a number (serializing) exactly duplicate filenames.
I just was wondering why this is done:
FileName := ReplaceRegEx(WideExtractBaseName(FileName), ' *(\(\d+\)|-\d+)$', '', False, False) + WideExtractFileExt(FileName);
As I read the regexp there are digits in brackets or digits following a dash - and in front of this there might be none, one or some spaces ...
Offline
I didn't know that these filenames have been changed already before building groups. In that case it seems impossible to forecast how the next filename wil llook like.
So two runs might be the only possible approach. Instead of using a file you could store the information (like array of names) inside the GlobalVars ...
Offline