Files
lazarus-ccr/applications/cactusjukebox/source/mediacol.pas

1346 lines
37 KiB
ObjectPascal
Raw Normal View History

{
Mediacollection Object for Cactus Jukebox
written by Sebastian Kraft, <c> 2006-2008
Contact the author at: sebastian_kraft@gmx.de
This Software is published under the GPL
}
Unit mediacol;
{$mode objfpc}{$H+}
Interface
Uses
Classes, SysUtils,
//Tagreader:
WMAfile, OggVorbis, FLACfile, mp3file, debug, LCLProc, lconvencoding, guesstag;
Type
// PMediaCollectionClass = ^TMediaCollectionClass;
TSrchType = ( FTrackSrch_Artist, FTrackSrch_ArtistAlbum, FAlbumSrch, FArtistSrch, FAllArtist );
TMediaType = (MTAudioFile, MTStream, MTCDAudio);
TPathFmt = ( FRelative, FDirect );
TMediaCollectionClass = Class;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
{ TMediaFileClass }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TMediaFileClass = Class
Private
FTitle, FAlbum, FArtist: string;
FStreamUrl: string;
FMediaType: TMediaType;
function StrToUTF8(s:string): string;
Procedure read_tag_ogg;
Procedure read_tag_flac;
Procedure read_tag_wma;
Procedure read_tag_wave;
Procedure read_tag_mp3;
procedure write_tag_mp3;
Procedure SetArtist(aValue: String);
Procedure SetAlbum(aValue: String);
Procedure SetTitle(aValue: String);
Procedure setStreamUrl(aValue: String);
Public
Path: String;
CoverPath: ansistring;
Collection: TMediaCollectionClass;
Comment: ansistring;
GenreID: Byte;
Year: string[4];
Track: string[10]; // ####/####
Filetype: string[5];
Size: int64;
ID, Bitrate, Samplerate, Playlength, Action: longint;
Playtime: string;
index: integer;
constructor create(filepath:String; ParentCollection: TMediaCollectionClass);
constructor create(ParentCollection: TMediaCollectionClass);
destructor destroy;
Procedure Write_Tag;
Procedure Read_Tag;
Procedure assign(SourceObject: TMediaFileClass);
Function PathNameFromTag(var strFormat: string): Boolean;
Function PathNameFromTag_dryrun(var strFormat: string): string;
Function FullPathNameFromTag_dryrun(var strFormat: string): string;
Function move2path(strFilePath: string): Boolean;
Function LibraryPath(): string;
function GetCoverFile: string;
property Artist: string read FArtist write SetArtist;
property Album: string read FAlbum write SetAlbum;
property Title: string read FTitle write SetTitle;
property StreamUrl: string read FStreamUrl write SetStreamUrl;
property MediaType: TMediaType read FMEdiaType;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
{ TMediaCollectionClass }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
TMediaCollectionClass = Class(Tlist)
Private
FSorted, FEnumerated, FAutoEnum: Boolean;
FSrchAscending: Boolean;
FSrchPos: Integer;
FSrchArtist, FSrchAlbum: String;
FSrchType: TSrchType;
Function GetItems(index: integer): TMediaFileClass;
Procedure Recursive_AddDir(dir: String);
Procedure SetAutoEnum(Const AValue: boolean);
Procedure SetSorted(Const AValue: boolean);
Public
syncronize: Procedure (dir: String) Of object;
Savepath: string;
DirList: TStringList;
PathFmt: TPathFmt;
property Items[index: integer]: TMediaFileClass read GetItems;
property sorted: boolean read FSorted write SetSorted;
// when Sorted is set true, add/insert always adds at right position
// on changing state from false to true whole collection is getting sorted once
property AutoEnum: boolean read FAutoEnum write SetAutoEnum;
property enumerated: boolean read FEnumerated;
constructor create;
destructor destroy;
Procedure Assign(SourceCol:TMediaCollectionClass);
Function LoadFromFile(path: String): boolean;
Function SaveToFile(path: String): boolean;
Function SaveToFile: boolean;
Procedure clear;
Procedure insert(path: String; atInd: integer);
Function add(path: String): integer;
Function add(MedFileObj: TMediaFileClass): integer;
Procedure add_directory(dir: String);
Procedure remove(ind: integer);
Procedure move(dest, target: integer);
Function ItemCount: integer;
Procedure enumerate;
Procedure enumerate(StartFrom: integer);
Function getTracks(artist: String): integer;
Function getTracks(artist: String; StartFrom: integer): integer;
Function getTracks(artist, album: String): integer;
Function getTracks(artist, album: String; StartFrom: integer): integer;
Function getAlbums(artist: String): TStringList;
Function getAlbums(artist: String; StartFrom: integer): TStringList;
Function getNext: integer;
Function getArtists: integer;
Function getNextArtist: integer;
Function getIndexByPath(path: String): integer;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Var MediaCollection, PlayerCol : TMediaCollectionClass;
implementation
Uses functions, config;
{$i cactus_const.inc}
{ TMediaCollectionClass }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.GetItems(index: integer): TMediaFileClass;
Begin
if (index>=0) and (index < Count) then
Result := (TMediaFileClass(Inherited Items [Index]));
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.Recursive_AddDir(dir: String);
Var mp3search,dirsearch: TSearchrec;
tmps: string;
fHandle: file Of byte;
Begin
dir := IncludeTrailingPathDelimiter(dir);
writeln('scanning through: '+dir);
syncronize(dir);
If FindFirst(dir+'*.*',faAnyFile,mp3search)=0 Then
Begin
Repeat
Begin
tmps := lowercase(ExtractFileExt(mp3search.name));
syncronize(dir);
If (tmps='.mp3') Or (tmps='.wav') Or (tmps='.ogg') Or (tmps='.wma')
Or (tmps='.flac') Or (tmps='.fla') Then
Begin
// Files with bad filenames may suddenly vanish from samba
// mounts when accessed. This will fiter them out.
system.assign(fHandle, dir+mp3search.name);
{$I-}
reset(fHandle);
close(fHandle);
{$I+}
if IOResult = 0
then
add(dir+mp3search.name);
End;
End;
Until FindNext(mp3search)<>0;
End;
Findclose(mp3search);
If Findfirst(dir+'*',faanyfile,dirsearch)=0 Then
Begin
syncronize(dir);
Repeat
Begin
If (dirsearch.attr And FaDirectory)=FaDirectory Then
Begin
If (dirsearch.name<>'..') And (dirsearch.name<>'.') Then
Recursive_AddDir(IncludeTrailingPathDelimiter(dir+dirsearch.name));
End;
End;
Until FindNext(dirsearch)<>0;
End;
Findclose(dirsearch);
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.SetAutoEnum(Const AValue: boolean);
Begin
FAutoEnum := AValue;
enumerate;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.SetSorted(Const AValue: boolean);
Begin
Fsorted := AValue;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
constructor TMediaCollectionClass.create;
Begin
Inherited create;
FSorted := true;
DirList := TStringList.Create;
PathFmt := FDirect;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
destructor TMediaCollectionClass.destroy;
Begin
End;
Procedure TMediaCollectionClass.Assign(SourceCol: TMediaCollectionClass);
Begin
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.LoadFromFile(path: String): boolean;
Var i: integer;
linecount: longint;
lfile: textfile;
RPath, tmps: String;
NumEntries: Integer;
MedFileObj: TMediaFileClass;
sortState: boolean;
tmpstring: string;
Begin
savepath := path;
sortState := FSorted;
linecount:=0;
system.assign(lfile,path);
{$i-}
reset(lfile);
{$i+}
if IoResult<>0 then begin
result := false;
writeln('unable to open file ', path);
exit;
end;
try
readln(lfile, tmps);
readln(lfile, tmps);
readln(lfile, tmps);
NumEntries := StrToInt(tmps);
writeln(NumEntries);
readln(lfile, tmps);
If tmps[length(tmps)]=';' Then System.Delete(tmps, length(tmps), 1);
i := pos(';', tmps);
While i<>0 Do
Begin
DirList.Add(IncludeTrailingPathDelimiter(copy(tmps, 1, i-1)));
system.delete(tmps, 1, i);
i := pos(';', tmps);
End;
DirList.Add(tmps);
If PathFmt = FRelative Then RPath := IncludeTrailingPathDelimiter(DirList[0])
Else RPath := '';
readln(lfile);
linecount:=5;
fsorted := false;
For i:= 0 To NumEntries-1 Do
Begin
MedFileObj := TMediaFileClass.create(self);
MedFileObj.action := ANOTHING;
readln(lfile, MedFileObj.path);
inc(linecount);
If PathFmt = FRelative Then MedFileObj.Path := RPath+MedFileObj.Path;
readln(lfile, MedFileObj.id);
inc(linecount);
readln(lfile, tmpstring);
MedFileObj.artist:=tmpstring;
inc(linecount);
readln(lfile, tmpstring);
MedFileObj.album:=tmpstring;
inc(linecount);
readln(lfile, tmpstring);
MedFileObj.title:=tmpstring;
inc(linecount);
readln(lfile, MedFileObj.year);
inc(linecount);
readln(lfile, MedFileObj.comment);
inc(linecount);
readln(lfile, MedFileObj.track);
inc(linecount);
readln(lfile, MedFileObj.size);
inc(linecount);
readln(lfile, MedFileObj.filetype);
inc(linecount);
readln(lfile, MedFileObj.bitrate);
inc(linecount);
readln(lfile, MedFileObj.samplerate);
inc(linecount);
readln(lfile, MedFileObj.playlength);
inc(linecount);
readln(lfile, MedFileObj.GenreID);
inc(linecount);
readln(lfile, MedFileObj.playtime);
inc(linecount);
add(MedFileObj);
End;
fsorted := sortState;
AutoEnum := true;
close(lfile);
writeln('library sucessfully loaded');
result := true;
Except
close(lfile);
fsorted := sortState;
writeln('lib seems corupted');
write('exception at entry ');
writeln(i);
write('line: ');writeln(linecount);
result := false;
End;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.SaveToFile(path: String): boolean;
Var lfile: textfile;
i: integer;
tmps: string;
Begin
savepath := path;
writeln('saving library to -> '+path);
Try
system.assign(lfile,path);
rewrite(lfile);
writeln(lfile,
'#####This config file is created by Cactus Jukebox. NEVER(!!) edit by hand!!!####')
;
writeln(lfile,'++++Config++++');
writeln(lfile, ItemCount);
For i:= 0 To DirList.Count-1 Do
tmps := tmps+DirList.Strings[i]+';';
writeln(lfile, tmps);
writeln(lfile,'++++Files++++');
tmps := '';
For i:= 0 To ItemCount-1 Do
Begin
If PathFmt = FDirect Then
tmps := items[i].Path
Else
Begin
tmps := copy(items[i].path, length(DirList[0]), length(items[i].path) - length(
DirList[0])+1);
If tmps[1]=DirectorySeparator Then system.Delete(tmps, 1, 1);
End;
//writeln(tmps);
writeln(lfile,tmps);
writeln(lfile,items[i].id);
writeln(lfile,items[i].artist);
writeln(lfile, items[i].album);
writeln(lfile, items[i].title);
writeln(lfile, items[i].year);
writeln(lfile, items[i].comment);
writeln(lfile, items[i].track);
writeln(lfile,items[i].size);
writeln(lfile,items[i].filetype);
writeln(lfile,items[i].bitrate);
writeln(lfile,items[i].samplerate);
writeln(lfile,items[i].playlength);
writeln(lfile,items[i].GenreID);
writeln(lfile,items[i].playtime);
End;
close(lfile);
write('written ');
write(i);
write(' of ');
writeln(ItemCount);
Except
writeln('error writing library to disk: check permissions!');
write('written ');
write(i);
write(' of ');
writeln(ItemCount);
End;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.SaveToFile: boolean;
Begin
result := SaveToFile(Savepath);
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.clear;
var AutoEnumStatus: boolean;
Begin
AutoEnumStatus:=AutoEnum;
AutoEnum:=false;
While count>0 Do
remove(0);
AutoEnum:=AutoEnumStatus;
DirList.Clear;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.insert(path: String; atInd: integer);
Var i: integer;
Begin
inherited Insert(atInd, TMediaFileClass.create(path, self));
items[atInd].index := atInd;
items[atInd].Collection := self;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.add(path: String): integer;
Var i,z: integer;
MedFileObj: TMediaFileClass;
SortedState: boolean;
Begin
i := 0;
SortedState := FSorted;
FSorted := false;
MedFileObj := TMediaFileClass.create(path, self);
If (SortedState) Then
Begin
If (MedFileObj.Artist<>'') and (ItemCount>0) Then
Begin
While (i<ItemCount) And (CompareText(items[i].Artist, MedFileObj.Artist)<0)
Do
inc(i);
While (i<=ItemCount-1) And (CompareText(items[i].Artist, MedFileObj.Artist)=0) And
(CompareText(items[i].Title, MedFileObj.Title)<0)
Do
inc(i);
End
Else i := 0;
inherited Insert(i, MedFileObj);
If AutoEnum Then enumerate(i)
Else FEnumerated := false;
End
Else
Begin
i := Inherited Add(MedFileObj);
items[i].index := i;
End;
items[i].Collection := self;
result := i;
FSorted := SortedState;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.add(MedFileObj: TMediaFileClass): integer;
Var i: integer;
SortedState: boolean;
Begin
SortedState := FSorted;
FSorted := false;
i := 0;
If SortedState Then
Begin
try
If MedFileObj.Artist<>'' Then
Begin
While (i<ItemCount) And (CompareText(items[i].Artist, MedFileObj.Artist)<0)
Do
inc(i);
While (i<=ItemCount-1) And (CompareText(items[i].Artist, MedFileObj.Artist)=0) And
(CompareText(items[i].Title, MedFileObj.Title)<0)
Do
inc(i);
End;
inherited Insert(i, MedFileObj);
If AutoEnum Then enumerate(i)
Else FEnumerated := false;
except
writeln('exception sorting object in library');
writeln(MedFileObj.Path);
end;
End
Else
Begin
i := Inherited Add(MedFileObj);
items[i].index := i;
End;
items[i].Collection := self;
result := i;
FSorted := SortedState;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.add_directory(dir: String);
Begin
DirList.Add(dir);
Recursive_AddDir(dir);
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.remove(ind: integer);
Var i: integer;
Begin
If (ind>=0) And (ind < Count) Then
Begin
Items[ind].free;
inherited Delete(ind);
dec(FSrchPos);
{ If (FSrchPos<=ind) Then dec(FSrchPos);
If (FSrchPos>ind) And (FSrchAscending) Then dec(FSrchPos);}
If AutoEnum Then enumerate(ind)
Else FEnumerated := false;
End;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.move(dest, target: integer);
Begin
//TODO: Test move operation for all FsrchPos cases
inherited move(dest, target);
// if (FSrchPos>=dest) and (FSrchPos>target) then inc(FSrchPos);
If (FSrchPos>=dest) And (FSrchPos<target) Then dec(FSrchPos);
// if (FSrchPos>ind) and (FSrchAscending) then dec(FSrchPos);
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.ItemCount: integer;
Begin
Result := Inherited Count;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.enumerate;
Begin
enumerate(0);
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaCollectionClass.enumerate(StartFrom: integer);
Var i: integer;
Begin
For i:=StartFrom To ItemCount-1 Do
items[i].index := i;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.GetTracks(artist: String): integer;
Begin
result := getTracks(artist, 0);
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.getTracks(artist, album: String): integer;
Begin
result := getTracks(artist, album, 0);
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.GetTracks(artist: String; StartFrom: integer): integer;
Var i: integer;
Begin
FSrchType := FTrackSrch_Artist;
artist := lowercase(artist);
FSrchArtist := artist;
If StartFrom<>0 Then
Begin
If (sorted=false) Then
StartFrom := 0
Else
Begin
i := AnsiCompareText(Items[StartFrom].Artist, artist);
If (i=0) Or (i>0) Then FSrchAscending := true
Else FSrchAscending := false;
End;
End;
i := StartFrom;
If (i<>0) And (FSrchAscending) Then
Begin
While (lowercase(Items[i].Artist)<>artist) And (i>=0) Do
dec(i);
While (lowercase(Items[i].Artist)=artist) And (i>0) Do
dec(i);
inc(i);
FSrchPos := i;
End
Else
Begin
While (i<Count) And (lowercase(Items[i].Artist)<>artist) Do
inc(i);
If i<>Count Then FSrchPos := i
Else FSrchPos := -1;
End;
Result := FSrchPos;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.getTracks(artist, album: String;
StartFrom: integer): integer;
Var i: integer;
Begin
album:=LowerCase(album);
artist:=LowerCase(artist);
FSrchArtist := artist;
FSrchAlbum := album;
i := getTracks(artist, StartFrom);
FSrchType := FTrackSrch_ArtistAlbum;
While lowercase(items[i].Album)<>album Do
inc(i);
FSrchPos := i;
Result := i;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.getAlbums(artist: String): TStringList;
Begin
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.getAlbums(artist: String; StartFrom: integer
): TStringList;
Var tmplist : TStringList;
i: integer;
Begin
artist := lowercase(artist);
If StartFrom<>0 Then
Begin
If (sorted=false) Then
StartFrom := 0
Else
Begin
i := AnsiCompareText(Items[StartFrom].Artist, artist);
If (i=0) Or (i>0) Then FSrchAscending := true
Else FSrchAscending := false;
End;
End;
i := StartFrom;
If (i<>0) And (FSrchAscending) Then
Begin
While (lowercase(Items[i].Artist)<>artist) And (i>=0) Do
dec(i);
While (lowercase(Items[i].Artist)=artist) And (i>0) Do
dec(i);
inc(i);
End
Else
Begin
While (i<count) And (lowercase(Items[i].Artist)<>artist) Do
inc(i);
If i=Count Then i := -1;
End;
tmplist := TStringList.Create;
tmplist.Sorted := true;
tmplist.CaseSensitive := false;
tmplist.Duplicates := dupIgnore;
If (i>=0) And (i<Count) Then
Begin
While (i<Count) And (lowercase(Items[i].Artist)=artist) Do
Begin
If Items[i].Album<>'' Then tmplist.AddObject(items[i].Album, Items[i])
Else tmplist.AddObject('Unknown', Items[i]);
inc(i);
End;
Result := tmplist;
End;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.getArtists: integer;
Begin
FSrchPos := 0;
FSrchArtist := lowercase(Items[0].Artist);
result := FSrchPos;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.getNextArtist: integer;
Var i: integer;
Begin
i := FSrchPos;
Repeat
inc(i)
Until (i>=Count) Or (lowercase(items[i].Artist)<>FSrchArtist);
If i<Count Then
Begin
FSrchArtist := lowercase(Items[i].Artist);
FSrchPos := i;
Result := i;
End
Else result := -1;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.GetNext: integer;
Var i: integer;
Begin
Case FSrchType Of
FTrackSrch_Artist:
Begin
Repeat
inc(FSrchPos)
Until (FSrchPos>=ItemCount) Or (lowercase(Items[FSrchPos].Artist)=
FSrchArtist);
If (FSrchPos<>ItemCount) And (lowercase(Items[FSrchPos].Artist)=
FSrchArtist)Then
result := FSrchPos
Else
result := -1;
exit;
End;
FTrackSrch_ArtistAlbum:
Begin
Repeat
inc(FSrchPos)
Until (FSrchPos>=ItemCount) Or ((lowercase(Items[FSrchPos].Album)=
FSrchAlbum) And (lowercase(Items[FSrchPos].Artist)=
FSrchArtist));
If (FSrchPos<>ItemCount) And ((lowercase(Items[FSrchPos].Album)=
FSrchAlbum) And (lowercase(Items[FSrchPos].Artist)=FSrchArtist)
) Then
result := FSrchPos
Else
result := -1;
exit;
End;
End;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaCollectionClass.GetIndexByPath(path: String): integer;
Var i: integer;
Begin
i := ItemCount;
Repeat
dec(i)
Until (i<0) Or (items[i].Path=path);
result := i;
End;
function TMediaFileClass.StrToUTF8(s: string): string;
var
w: Word;
begin
if length(s)>2 then begin
W := PWord(@S[1])^;
if (W=$FFFE) or (W=$FEFF) then begin
s := copy(s, 3, length(s)-2);
if (W=$FFFE) then
result := UCS2BEToUTF8(s)
else
result := UCS2LEToUTF8(s);
exit;
end
end;
if FindInvalidUTF8Character(pchar(s), Length(s), false)>=0 then
result := ISO_8859_1ToUTF8(s)
else
result := S;
end;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
{ TMediaFileClass }
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// ogg-Support changed by monta
Procedure TMediaFileClass.read_tag_ogg;
Var OGGFile: TOggVorbis;
Begin
OGGFile := TOggVorbis.Create;
try
if FileExists(Path) then
begin
writeln(path);
OGGFile.ReadFromFile(Path);
artist := UTF8Encode(OGGFile.Artist);
title := UTF8Encode(OGGFile.Title);
album := UTF8Encode(OGGFile.Album);
Bitrate := OGGFile.BitRate;
Year := OGGFile.Date;
Samplerate := OGGFile.SampleRate;
Comment := OGGFile.Comment;
Track := IntToStr(OGGFile.Track);
Playlength := round(OGGFile.Duration);
Playtime := SecondsToFmtStr(Playlength);
GenreID := StrToIntDef(OGGFile.Genre, 0);
end;
finally
OGGFile.Free;
end;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// flac-Support added by monta
Procedure TMediaFileClass.read_tag_flac;
Var FLACFile: TFLACfile;
Begin
FLACfile := TFLACfile.Create;
try
if FileExists(Path) then
begin
FLACFile.ReadFromFile(Path);
artist := UTF8Encode(FLACFile.Artist);
title := UTF8Encode(FLACFile.Title);
album := UTF8Encode(FLACFile.Album);
Bitrate := FLACFile.BitRate;
Year := FLACFile.Year;
Samplerate := FLACFile.SampleRate;
Comment := FLACFile.Comment;
Track := FLACFile.TrackString;
Playlength := round(FLACFile.Duration);
Playtime := SecondsToFmtStr(Playlength);
GenreID := StrToIntDef(FLACFile.Genre, 0);
end;
finally
FLACFile.Free;
end;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
// wma-Support added by monta
Procedure TMediaFileClass.read_tag_wma;
Var WMAFile: TWMAfile;
Begin
WMAFile := TWMAfile.Create;
try
if FileExists(Path) then
begin
WMAFile.ReadFromFile(Path);
artist := UTF8Encode(WMAFile.Artist);
title := UTF8Encode(WMAFile.Title);
album := UTF8Encode(WMAFile.Album);
Bitrate := WMAFile.BitRate;
Year := WMAFile.Year;
Samplerate := WMAFile.SampleRate;
Comment := WMAFile.Comment;
Track := IntToStr(WMAFile.Track);
Playlength := round(WMAFile.Duration);
Playtime := SecondsToFmtStr(Playlength);
GenreID := StrToIntDef(WMAFile.Genre, 0);
end;
finally
WMAFile.Free;
end;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaFileClass.read_tag_wave;
Var li: cardinal;
b: byte;
z: integer;
mp3filehandle: longint;
tmps: string;
Begin
Try
mp3filehandle := fileopen(path, fmOpenRead);
fileseek(mp3filehandle,8,fsfrombeginning);
b := 0;
Repeat
Begin
inc(b);
fileread(mp3filehandle,li,4);
End;
Until (li=$20746D66) Or (b=15);
fileread(mp3filehandle,li,4);
fileread(mp3filehandle,li,4);
fileread(mp3filehandle,li,4);
samplerate := li;
fileread(mp3filehandle,li,4);
bitrate := (li Div 1024)*8;
playlength := size Div li;
playtime := SecondsToFmtStr(Playlength);
Except
writeln('**EXCEPTION reading wave file '+path+'**');
End;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
procedure TMediaFileClass.read_tag_mp3;
Var MP3File: TMP3File;
Begin
MP3File := TMP3File.Create;
try
if FileExists(Path) then
begin
// writeln(path);
MP3File.ReadTag(Path);
artist := StrToUTF8(MP3File.Artist);
title := StrToUTF8(MP3File.Title);
album := StrToUTF8(MP3File.Album);
Bitrate := MP3File.BitRate;
Year := StrToUTF8(MP3File.Year);
Samplerate := MP3File.SampleRate;
Comment := StrToUTF8(MP3File.Comment);
Track := StrToUTF8(MP3File.Track);
Playlength := round(MP3File.Playlength);
Playtime := SecondsToFmtStr(Playlength);
GenreID := (MP3File.GenreID);
end;
finally
MP3File.Free;
end;
end;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
procedure TMediaFileClass.write_tag_mp3;
var MP3File: TMP3File;
begin
MP3File:=TMP3File.create;
MP3File.Artist:=UTF8ToISO_8859_1(FArtist);
MP3File.Album:=UTF8ToISO_8859_1(FAlbum);
MP3File.Title:=UTF8ToISO_8859_1(FTitle);
MP3File.Track:= Track;
MP3File.Comment:=UTF8ToISO_8859_1(Comment);
MP3File.Year:= Year;
MP3File.GenreID:=(GenreID);
MP3File.WriteTag(Path);
MP3File.Free;
end;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaFileClass.Read_Tag;
var TagFromName: TTagFromFilename;
Begin
If filetype='.wav' Then read_tag_wave;
If filetype='.ogg' Then read_tag_ogg;
If filetype='.mp3' Then read_tag_mp3;
If filetype='.wma' Then read_tag_wma;
If (filetype='.flac') or (filetype='.fla') Then read_tag_flac;
if (Artist='') and CactusConfig.GuessTag then begin
TagFromName:=TTagFromFilename.create;
TagFromName.ReadTag(Path);
if TagFromName.Artist<>'' then Artist:=TagFromName.Artist+'*';
if (Title='') and (TagFromName.Artist<>'') then Title:=TagFromName.Title+'*';
if (Track='') and (TagFromName.Track<>'') then Track:=TagFromName.Track+'*';
TagFromName.Free;
end;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaFileClass.SetArtist(aValue: String);
Var i, start: integer;
Begin
i := index;
start := index;
FArtist := aValue;
If (Collection<>nil) and Collection.sorted Then
Begin
If (i<Collection.Count-1) And (CompareText(FArtist, Collection.Items[i+1].Artist)>0) Then
Begin
inc(i);
While (i<=Collection.Count-1) And (compareText(FArtist, Collection.Items[i].Artist)>0)
Do
Begin
inc(i);
End;
While (i<=Collection.Count-1) And (compareText(FTitle, Collection.Items[i].Title)>0)
And (CompareText(FArtist, Collection.Items[i].Artist)=0) Do
Begin
inc(i);
End;
Collection.Move(index, i-1);
If Collection.AutoEnum Then Collection.enumerate(start);
End;
If (i>0) And (CompareText(FArtist, Collection.Items[i-1].Artist)<0) Then
Begin
dec(i);
While (i>=0) And (compareText(FArtist, Collection.Items[i].Artist)<0) Do
Begin
dec(i);
End;
While ((i>=0) And (compareText(FTitle, Collection.Items[i].Title)<0))
And (CompareText(FArtist, Collection.Items[i].Artist)=0) Do
Begin
dec(i);
End;
Collection.Move(index, i+1);
If Collection.AutoEnum Then Collection.enumerate;
End;
End;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaFileClass.SetAlbum(aValue: String);
Begin
FAlbum := aValue;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaFileClass.SetTitle(aValue: String);
Var i, start: integer;
Begin
FTitle := aValue;
i := index;
start := index;
If (Collection<>nil) and Collection.sorted Then
Begin
writeln(i);
If (i<Collection.Count-1) And (CompareText(FTitle, Collection.Items[i+1].Title)>0)
And (CompareText(FArtist, Collection.Items[i+1].Artist)=0) Then
Begin
inc(i);
While ((i<=Collection.Count-1) And (compareText(FTitle, Collection.Items[i].Title)>0))
And (CompareText(FArtist, Collection.Items[i].Artist)=0) Do
Begin
inc(i);
End;
Collection.Move(index, i-1);
If Collection.AutoEnum Then Collection.enumerate(start);
End;
If (i>0) And (CompareText(FTitle, Collection.Items[i-1].Title)<0)
And (CompareText(FArtist, Collection.Items[i-1].Artist)=0) Then
Begin
dec(i);
While ((i>=0) And (compareText(FTitle, Collection.Items[i].Title)<0))
And (compareText(FArtist, Collection.Items[i].Artist)=0) Do
Begin
dec(i);
End;
Collection.Move(index, i+1);
If Collection.AutoEnum Then Collection.enumerate;
End;
End;
End;
Procedure TMediaFileClass.setStreamUrl(aValue: String);
Begin
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
constructor TMediaFileClass.create(filepath: String; ParentCollection: TMediaCollectionClass);
Var tmpfile: file Of byte;
Begin
Collection := ParentCollection;
path := filepath;
action := ANOTHING;
If pos(URLID, filepath)=0 Then FMediaType := MTStream
Else FMediaType := MTAudioFile;
Filemode := 0;
try
system.assign(tmpfile, path);
//Open file temporaly to get some information about it
reset(tmpfile);
size := filesize(tmpfile);
//get filesize
ID := crc32(path);
// calc unique file ID
filetype := lowercase(ExtractFileExt(filepath));
close(tmpfile);
except debugoutln('ERROR reading file '+filepath,2);
end;
Filemode := 2;
read_tag;
//finally read tag information
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
constructor TMediaFileClass.create(ParentCollection: TMediaCollectionClass);
Begin
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
destructor TMediaFileClass.destroy;
Begin
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaFileClass.Write_Tag;
begin
if Filetype='.mp3' then write_tag_mp3;
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Procedure TMediaFileClass.assign(SourceObject: TMediaFileClass);
Begin
End;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaFileClass.move2path(strFilePath: string): Boolean;
var
i: integer;
strSrc, strDest, strTmp: string;
begin
// did the filename change at all?
strSrc := Path;
strDest := strFilePath;
if strSrc = strDest then
begin
Result := true;
exit;
end;
// has the folder changed?
strSrc := ExtractFilePath(Path);
strDest := ExtractFilePath(strFilePath);
if strSrc <> strDest then
if NOT DirectoryExists(strDest) then
ForceDirectories(strDest);
// does the target file alredy existe?
strDest := strFilePath;
if FileExists(strDest) then
begin
while strDest[Length(strDest)-1] <> '.' do
strDest := Copy(strDest, 1, Length(strDest)-1);
strDest := Copy(strDest, 1, Length(strDest)-2);
i := 2;
repeat
begin
strTmp := '(' + IntToStr(i) + ')' + Filetype;
i += 1;
end
until NOT FileExists(strDest + strTmp)
end;
strDest += strTmp;
// move the file
strSrc := Path;
RenameFile(strSrc, strDest);
// remove old folder and folders above if empty
strSrc := ExtractFilePath(Path);
while DirectoryIsEmpty(strSrc) do
begin
RemoveDir(strSrc);
i := LastDelimiter(PathDelim,ExcludeTrailingPathDelimiter(strSrc));
Delete(strSrc, i, Length(strSrc)-i+1);
end;
result := true; // FIXME write error detection needed
if result then
Path := strDest;
end;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaFileClass.PathNameFromTag_dryrun(var strFormat: string): string;
var
strArtist, strAlbum, strTitle, strTrack, strYear: string;
strLeft, strRight, strMid: string;
chrLetter: char;
intPos, intPos2: integer;
bNonEmpty: Boolean;
begin
// format string could be '%a/%a - %b - %n - %t'
// if existant, replace unwanted chars in tags
strArtist := MakeValidFilename(Artist);
strAlbum := MakeValidFilename(Album);
strTitle := MakeValidFilename(Title);
strTrack := MakeValidFilename(Track);
strYear := MakeValidFilename(Year);
result := strFormat;
// result := '%a/%a - %b%? - ?%n - %t%? in ?%y';
while (Pos('%?', result) >0) and (Pos('?%', result) >0) do
begin
intPos := Pos('%?', result);
intPos2 := Pos('?%', result);
if length(result) < intPos2+2 then break;
if intPos2 < intPos then break; // FIXME could be more elegant
strLeft := Copy(result, 1, intPos -1);
strRight := Copy(result, intPos2 +3, Length(result) - intPos2 +3);
strMid := Copy (result, intPos +2, Length(result) - intPos -1 -(Length(result) - intPos2) -1);
chrLetter := result[intPos2 +2];
bNonEmpty := false;
case chrLetter of
'a': if strArtist <> '' then bNonEmpty := true;
'b': if strAlbum <> '' then bNonEmpty := true;
't': if strTitle <> '' then bNonEmpty := true;
'n': if strTrack <> '' then bNonEmpty := true;
'y': if strYear <> '' then bNonEmpty := true;
end;
if bNonEmpty then
result := strLeft + strMid + '%' + chrLetter + strRight
else
result := strLeft + strRight;
end;
result := StringReplace(result, '%a', strArtist, [rfReplaceAll, rfIgnoreCase]);
result := StringReplace(result, '%b', strAlbum, [rfReplaceAll, rfIgnoreCase]);
result := StringReplace(result, '%t', strTitle, [rfReplaceAll, rfIgnoreCase]);
result := StringReplace(result, '%n', strTrack, [rfReplaceAll, rfIgnoreCase]);
result := StringReplace(result, '%y', strYear, [rfReplaceAll, rfIgnoreCase]);
result += FileType;
end;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaFileClass.FullPathNameFromTag_dryrun(var strFormat: string): string;
begin
result := LibraryPath() + PathNameFromTag_dryrun(strFormat);
end;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaFileClass.PathNameFromTag(var strFormat: string): Boolean;
begin
result := move2path(FullPathNameFromTag_dryrun(strFormat));
end;
//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Function TMediaFileClass.LibraryPath(): string;
var
i: integer;
begin
result := '';
For i:= 0 To MediaCollection.dirlist.Count-1 Do
if Pos(Collection.dirlist[i], Path) > 0 then
begin
result := IncludeTrailingPathDelimiter(Collection.dirlist[i]);
break;
end;
end;
function TMediaFileClass.GetCoverFile: string;
begin
result := Artist+'_'+album+'.jpeg'
end;
End.