Can't call method "title" on undefined title in Perl script
ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
Can't call method "title" on undefined title in Perl script
I'm writing a short Perl script to rename a directory full of MP3 files and re-write some of their ID3 tags using the MP3::Tag module, but I'm having some trouble with it. Whenever I try to run it, I get this error:
Code:
[scuzzy@slackdell /home/scuzzy/music/young_jeezy/unknown_disc]$ ~/fixid3.pl
Will every file in this directory have the same album and artist header (y/n)? y
What is the title of the album? Thug Motivation 101
Who is the artist? Young Jeezy
Filename: track_01.mp3
What is the track name? Go Crazy - Remix
Can't call method "title" on an undefined value at /home/scuzzy/fixid3.pl line 51, <STDIN> line 4.
This is the Perl script I'm running:
Code:
#!/usr/bin/perl
# fixid3.pl - Perl script to write MP3 ID3 tags to files
# Fields are title, ARTIST, ALBUM, YEAR, COMMENT, GENRE
# load the ID3 module
use MP3::Tag;
# get the filesname(s)
# We'll just use all files in the directory:
@filenames = <*.mp3>; # This glob gets all mp3 files in current dir
print "Will every file in this directory have the same ";
print "album and artist header (y/n)? ";
chomp ( $same_album_artist = <STDIN> );
if ( $same_album_artist == "y" ) {
print "What is the title of the album? ";
chomp ( $album = <STDIN> );
print "\n";
print "Who is the artist? ";
chomp ( $artist = <STDIN> );
print "\n";
foreach (@filenames) { # since the album and artist are the same, just change song titles
print "Filename: $_\n";
$file = $_;
&fix_id3($album, $artist, $file);
}
} elsif ($same_album_artist == "n") {
foreach (@filenames) { # ask album and artist on each song
print "Filename: $_\n";
$file = $_;
print "What is the title of the album? ";
chomp ( $album = <STDIN> );
print "\n";
print "Who is the artist? ";
chomp ( $artist = <STDIN> );
print "\n";
&fix_id3($album, $artist, $file);
}
} else {
die "Unrecognized entry.\n";
}
sub fix_id3 {
# Here's where the magic is done
$album = $_[0];
$artist = $_[1];
$file = $_[2];
print "What is the track name? ";
chomp ( $title = <STDIN> );
$mp3 = MP3::Tag->new($file); # Creates new object
$mp3->{ID3v1}->title("$title"); # adds element title
$mp3->{ID3v1}->artist("$artist"); # adds element artist
$mp3->{ID3v1}->album("$album"); # adds element album
$mp3->{ID3v1}->write_tag(); # writes the tags
$mp3->close(); # close the file
# we'll also rename the files
$oldname = "\"$file\"";
$newname = "\"$artist - ${title}.mp3\"";
rename $oldname, $newname;
print "\n";
}
Is there anything I can do to figure out why this is detecting an undefined value, even though everything should be properly defined?
Edit: I have just tried it with and without the quotes on these lines (in red) and yielded the same results:
Quote:
$mp3->{ID3v1}->title("$title"); # adds element title
$mp3->{ID3v1}->artist("$artist"); # adds element artist
$mp3->{ID3v1}->album("$album"); # adds element album
The error you're getting means that you are trying to call your methods (title being the first one) on an undefined object. I don't think you can access ID3v1 methods like that. You need to use the get_tags() method first to fetch all the tags the mp3 object has access to and then call it directly via the MP3::Tag::ID3v1
I think it will work if you add this after your new() call:
It's still not working, but now I at least have a clue why. This is the new segment of code:
Code:
$mp3 = MP3::Tag->new($file); # Creates new object
$mp3 -> get_tags();
unless (exists $mp3->{ID3v1}) {
die "Unable to read ID3 tags from file.\n";
}
$id3v1 = $mp3 -> {ID3V1};
$id3v1 -> title( "$title" ); # adds element title
These are the errors from the console:
Code:
[scuzzy@slackdell /home/scuzzy/music/young_jeezy/unknown_disc]$ ~/fixid3.pl
Will every file in this directory have the same album and artist header (y/n)? y
What is the title of the album? Thug Motivation 101
Who is the artist? Young Jeezy
Filename: track_01.mp3
What is the track name? Go Crazy - Remix
Unable to read ID3 tags from file.
But what could be causing it to be unable to read the ID3 tags? Perhaps because they do not yet exist? If that's the case, then why can I not write them (to make them exist)?
print "What is the track name? ";
chomp ( $title = <STDIN> );
$mp3 = MP3::Tag->new($file); # Creates new object
##ADDED DEBUG
$tags = [];
$tags = $mp3->get_tags;
print Dumper($tags);
##END DEBUG
$mp3 -> get_tags();
unless (exists $mp3->{ID3v1}) {
die "Unable to read ID3 tags from file.\n";
}
And this is the output:
Code:
[scuzzy@slackdell /home/scuzzy/music/young_jeezy/unknown_disc]$ ~/fixid3.pl
Will every file in this directory have the same album and artist header (y/n)? y
What is the title of the album? Thug Motivation 101
Who is the artist? Young Jeezy
Filename: track_01.mp3
What is the track name? Go Crazy - Remix
$VAR1 = 2;
Unable to read ID3 tags from file.
[scuzzy@slackdell /home/scuzzy/music/young_jeezy/unknown_disc]$
Sorry I didn't realize get_tags just returns a list of strings.
Pleasse try the code snippet below and look at my comments for an explanation.
Code:
# wrap the object constructor call in an eval to
# make sure we catch any weird errors and object was
# created ok
eval {
$mp3 = MP3::Tag->new($file); # Creates new object
};
# make sure object was created ok
if ($@) {
die qq{Error crating MP3 object, $@};
}
print qq{ MP3 Object created ok\n.};
# fetch list of tags
@tags = $mp3->get_tags();
print qq{tags\n}, Dumper(\@tags);
# no need to call get_tags again ...
# make sure ID3v1 is one ofthe choices on the list
# also make sure you have a little v not ID3V1 ... I
# know that's silly but I saw it in one off your earlier
# posts.
unless ( exists $mp3->{ID3v1} ) {
die "Unable to read ID3 tags from file.\n";
}
$id3v1 = $mp3->{ID3v1};
# ready to set element title ... the double quotes should
# really be omitted since you are passing a variable not a
# straight string ... but it may not make a diff
$id3v1->title("$title"); # adds element title
Pardon me for any syntax errors, I don't have a computer I can test this on. Let me know how it works out.
Still not working... I'm pretty sure I understand most of the code, and it looks as though it should function (when the Dump is removed that is...). This is the current fix_id3 subroutine:
Code:
sub fix_id3 {
# Here's where the magic is done
$album = $_[0];
$artist = $_[1];
$file = $_[2];
print "What is the track name? ";
chomp ( $title = <STDIN> );
# wrap the object constructor call in an eval to
# make sure we catch any weird errors and object was
# created ok
eval {
$mp3 = MP3::Tag->new($file); # Creates new object
};
# make sure object was created ok
if ($@) {
die qq{Error crating MP3 object, $@};
}
print qq{ MP3 Object created ok.\n};
# fetch list of tags
@tags = $mp3->get_tags();
print qq{tags\n}, Dumper(\@tags);
unless ( exists $mp3->{ID3v1} ) {
die "Unable to read ID3 tags from file.\n";
}
$id3v1 = $mp3->{ID3v1};
# ready to set element title ... the double quotes should
# really be omitted since you are passing a variable not a
# straight string ... but it may not make a diff
$id3v1->title("$title"); # adds element title
$id3v1 -> artist( "$artist" ); # adds element artist
$id3v1 -> album( "$album" ); # adds element album
$id3v1 -> write_tag(); # writes the tags
$mp3->close(); # close the file
# we'll also rename the files
$oldname = "$file";
$newname = "$artist_-_${title}.mp3";
rename $oldname, $newname;
print "\n";
}
And this is the output from the console:
Code:
[scuzzy@slackdell /home/scuzzy/music/young_jeezy/unknown_disc]$ ~/fixid3.pl
Will every file in this directory have the same album and artist header (y/n)? y
What is the title of the album? Thug Motivation 101
Who is the artist? Young Jeezy
Filename: track_01.mp3
What is the track name? Go Crazy - Remix
MP3 Object created ok
.tags
$VAR1 = [
'ParseData',
'LastResort'
];
Unable to read ID3 tags from file.
[scuzzy@slackdell /home/scuzzy/music/young_jeezy/unknown_disc]$
Although, I don't understand the use of 'qq' - is that kind of like 'qw'?
qq is double quotes. The difference is that qq will interpret any variables for you unlike q.
For some reason the file you're using only has 'ParseData' and 'LastResort' available.
That's why any references to $mp3->{ID3v1} are undefined.
I copied the script and ran it on my computer with one of my mp3 files and it worked.
This is the output:
Code:
(host: waldemar) (user: waldemar) (time= 21:09:06)
> ~ $ ./test.pl
Will every file in this directory have the same album and artist header (y/n)? yWhat is the title of the album? foo
Who is the artist? bar
Filename: bignic.mp3
What is the track name? baz
MP3 Object created ok
.tags
$VAR1 = [
'ParseData',
'ID3v2',
'ID3v1',
'LastResort'
];
done!
I didn't change anything else so the only difference is the file being used. I don't know much about mp3's so I don't know why only those 2 tags are limited for your mp3-object-file. Maybe you can try and find out why your mp3 file doesn't have ID3v1? I looked over the perldoc briefly and it looks like ID3v1 and ID3v2 are the only ones that support reading and writing. You might be able to just create those tags.
From the perldoc
Code:
# create a new tag
$mp3->new_tag("ID3v2");
$mp3->{ID3v2}->add_frame("TALB", "Album title");
$mp3->{ID3v2}->write_tag
I cannot thank you enough for you help And for once, I understand what was not working. Here is the fully working code:
Code:
#!/usr/bin/perl
# fixid3.pl - Perl script to write MP3 ID3 tags to files
# Fields are title, ARTIST, ALBUM, YEAR, COMMENT, GENRE
# load the ID3 module
use MP3::Tag;
# get the filename(s)
# We'll just use all files in the directory:
@filenames = <*.mp3>; # This glob gets all mp3 files in current dir
print "Will every file in this directory have the same ";
print "album and artist header (y/n)? ";
chomp ( $same_album_artist = <STDIN> );
if ( $same_album_artist == "y" ) {
print "What is the title of the album? ";
chomp ( $album = <STDIN> );
print "\n";
print "Who is the artist? ";
chomp ( $artist = <STDIN> );
print "\n";
foreach (@filenames) { # since the album and artist are the same, just change song titles
print "Filename: $_\n";
$file = $_;
&fix_id3($album, $artist, $file);
}
} elsif ($same_album_artist == "n") {
foreach (@filenames) { # ask album and artist on each song
print "Filename: $_\n";
$file = $_;
print "What is the title of the album? ";
chomp ( $album = <STDIN> );
print "\n";
print "Who is the artist? ";
chomp ( $artist = <STDIN> );
print "\n";
&fix_id3($album, $artist, $file);
}
} else {
die "Unrecognized entry.\n";
}
sub fix_id3 {
# Here's where the magic is done
$album = $_[0];
$artist = $_[1];
$file = $_[2];
print "What is the track name? ";
chomp ( $title = <STDIN> );
# wrap the object constructor call in an eval to
# make sure we catch any weird errors and object was
# created ok
eval {
$mp3 = MP3::Tag->new($file); # Creates new object
};
# Make sure the object was created OK
if ($@) {
die qq{Error creating MP3 object, $@}; # if not, die and tell us why
}
unless ( exists $mp3->{ID3v1} ) { # assure ID3v1 is a valid option
$mp3 -> new_tag("ID3v1");
$mp3 -> {ID3v1}->write_tag; # otherwise, create it
}
$mp3 -> {ID3v1} -> title("$title"); # adds element title
$mp3 -> {ID3v1} -> artist("$artist"); # adds element artist
$mp3 -> {ID3v1} -> album("$album"); # adds element album
$mp3 -> {ID3v1} -> write_tag(); # writes the tags
$mp3 -> close(); # close the file
# we'll also rename the files
$oldname = "$file";
$newname = "${artist} - ${title}.mp3";
rename $oldname, $newname;
print "\n";
}
I think the problem was that all along the MP3 I was manipulating did not have any ID3 information - it simply needed the tags written
BTW: The extra variables $id3v1 and @tags were not needed - I've removed them. The code above works exactly as planned
As this is a learning exercise, I've been slowly adding more and more functionality to the script. Right now, I'm trying to add the functionality to verify the ID3's before rewriting them - but the function isn't running. This is the code:
Code:
#!/usr/bin/perl
# fixid3.pl - Perl script to verify and write MP3 ID3 tags to files
# Fields are TITLE, ARTIST, ALBUM, GENRE
# sub-routine definitions:
# &get_id3: retrieves ID3 information from user
# &show_id3: shows ID3 information to user and verifies accuracy
# &fix_id3: sets ID3 tags based on user input
# load the ID3 module
use MP3::Tag;
# get the filename(s)
# We'll just use all files in the directory:
@filenames = <*.mp3>; # This glob gets all mp3 files in current dir
print "Will every file in this directory have the same ";
print "album and artist header (y/n)? ";
chomp ( $same_album_artist = <STDIN> );
print "Would you like to verify the ID3 information for each processed file? ";
chomp ( $verify = <STDIN> );
if ( $verify == "y" ) {
$same_album_artist = "n"; # force this to no so it reveals and checks the information
}
if ( $same_album_artist == "y" ) {
foreach (@filenames) {
print "Filename: $_\n";
$file = $_;
my ( $artist , $album , $genre ) = &get_id3(); # get ID3 information from the user
&fix_id3($album, $artist, $genre, $file);
}
} elsif ($same_album_artist == "n") {
foreach (@filenames) {
print "Filename: $_\n";
$file = $_;
if ( ($verify == "y") && !(&show_id3($file)) ) {
my ( $artist , $album , $genre ) = &get_id3();
&fix_id3($album, $artist, $genre, $file);
}
if ( $verify == "n" ) {
my ( $artist , $album , $genre ) = &get_id3();
&fix_id3($album, $artist, $genre, $file);
}
}
} else {
die "Unrecognized entry.\n";
}
sub get_id3 {
print "What is the title of the album? ";
chomp ( $album = <STDIN> ); # album name
print "\n";
print "Who is the artist? ";
chomp ( $artist = <STDIN> ); # artist name
print "\n";
print "What is the Genre? ";
chomp ( $genre = <STDIN> ); # music genre
print "\n";
@id3_tags = ($artist, $album, $genre); # load input into array for return
return @id3_tags; # return array
}
sub show_id3 {
$file = $_[0];
eval {
$mp3 = MP3::Tag->new($file); # Creates new object
};
# Make sure the object was created OK
if ($@) {
die qq{Error creating MP3 object, $@}; # if not, die and tell us why
}
unless ( exists $mp3->{ID3v1} ) { # assure ID3 tags exist
print "No ID3 Information!\n"; # If no, exit sub with FALSE
return 0;
}
print "Current ID3 Information:\n";
print "Title: ";
print $mp3 -> {ID3v1} -> title(); # retrieve title
print "\n";
print "Artist: ";
print $mp3 -> {ID3v1} -> artist(); # retrieve artist
print "\n";
print "Album: ";
print $mp3 -> {ID3v1} -> album(); # retrieve album
print "\n";
print "Genre: ";
print $mp3 -> {ID3v1} -> genre(); # retrieve genre
print "\n";
print "Is this information correct (y/n)? ";
chomp ( $correct = <STDIN> );
if ($correct == "y") {
return 1; # return true - information correct, skip file
} elsif ($correct == "n") {
return 0; # return false and rewrite data
} else {
die qq{Incorrect Input!\n};
}
print "\n";
}
sub fix_id3 {
# Here's where the magic is done
$album = $_[0];
$artist = $_[1];
$genre = $_[2];
$file = $_[3];
print "What is the track name? ";
chomp ( $title = <STDIN> );
# wrap the object constructor call in an eval to
# make sure we catch any weird errors and object was
# created ok
eval {
$mp3 = MP3::Tag->new($file); # Creates new object
};
# Make sure the object was created OK
if ($@) {
die qq{Error creating MP3 object, $@}; # if not, die and tell us why
}
unless ( exists $mp3->{ID3v1} ) { # assure ID3v1 is a valid option
$mp3 -> new_tag("ID3v1");
$mp3 -> {ID3v1}->write_tag; # otherwise, create it
}
$mp3 -> {ID3v1} -> title("$title"); # adds element title
$mp3 -> {ID3v1} -> artist("$artist"); # adds element artist
$mp3 -> {ID3v1} -> album("$album"); # adds element album
$mp3 -> {ID3v1} -> genre("$genre"); # adds element genre
$mp3 -> {ID3v1} -> write_tag(); # writes the tags
$mp3 -> close(); # close the file
# we'll also rename the files
$oldname = "$file";
$newname = "${artist} - ${title}.mp3";
rename $oldname, $newname;
print "\n";
}
The function that isn't running (even if the appropriate options are entered) is the &show_id3 function. I'm not able to see anything I'm doing incorrect, so could someone please give this a look-over for me?
One thing I just noticed is that you're using "==" for your string comparison.
The comparison operator for strings is "eq" and "==" is for numeric values only. That may be causing the issue.
This is a great thread...I'm beginning to think that this issue is deeper than just a file without tags...I've been fighting the "Can't callmethod " " on undefined value..." error for the past few hours now and can't seem to get a handle on the issue. I actually created two files...one simply edits meta tags and the other takes the name of the actually mp3 and parses it into a foldername THEN edits the meta tag. My goal is to write a program that takes a directory full of uniformly formatted mp3s and parses the names into uniformly named folders and also edits the meta tag of each mp3 using the data collected from parsing the filename:
Here is the metatag part (the part relevant to this thread)
I included the lines that I commented out to demonstrate what I'd eventually like to do...this code WORKED exactly how I want it to on ONE mp3 file...when I insert the code with EXACTLY the same syntax into the larger code I get that weird error!!!
I'm writing a short Perl script to rename a directory full of MP3 files and re-write some of their ID3 tags using the MP3::Tag module, but I'm having some trouble with it. Whenever I try to run it, I get this error:
Code:
[scuzzy@slackdell /home/scuzzy/music/young_jeezy/unknown_disc]$ ~/fixid3.pl
Will every file in this directory have the same album and artist header (y/n)? y
What is the title of the album? Thug Motivation 101
Who is the artist? Young Jeezy
Filename: track_01.mp3
What is the track name? Go Crazy - Remix
Can't call method "title" on an undefined value at /home/scuzzy/fixid3.pl line 51, <STDIN> line 4.
This is the Perl script I'm running:
Code:
#!/usr/bin/perl
# fixid3.pl - Perl script to write MP3 ID3 tags to files
# Fields are title, ARTIST, ALBUM, YEAR, COMMENT, GENRE
# load the ID3 module
use MP3::Tag;
# get the filesname(s)
# We'll just use all files in the directory:
@filenames = <*.mp3>; # This glob gets all mp3 files in current dir
print "Will every file in this directory have the same ";
print "album and artist header (y/n)? ";
chomp ( $same_album_artist = <STDIN> );
if ( $same_album_artist == "y" ) {
print "What is the title of the album? ";
chomp ( $album = <STDIN> );
print "\n";
print "Who is the artist? ";
chomp ( $artist = <STDIN> );
print "\n";
foreach (@filenames) { # since the album and artist are the same, just change song titles
print "Filename: $_\n";
$file = $_;
&fix_id3($album, $artist, $file);
}
} elsif ($same_album_artist == "n") {
foreach (@filenames) { # ask album and artist on each song
print "Filename: $_\n";
$file = $_;
print "What is the title of the album? ";
chomp ( $album = <STDIN> );
print "\n";
print "Who is the artist? ";
chomp ( $artist = <STDIN> );
print "\n";
&fix_id3($album, $artist, $file);
}
} else {
die "Unrecognized entry.\n";
}
sub fix_id3 {
# Here's where the magic is done
$album = $_[0];
$artist = $_[1];
$file = $_[2];
print "What is the track name? ";
chomp ( $title = <STDIN> );
$mp3 = MP3::Tag->new($file); # Creates new object
$mp3->{ID3v1}->title("$title"); # adds element title
$mp3->{ID3v1}->artist("$artist"); # adds element artist
$mp3->{ID3v1}->album("$album"); # adds element album
$mp3->{ID3v1}->write_tag(); # writes the tags
$mp3->close(); # close the file
# we'll also rename the files
$oldname = "\"$file\"";
$newname = "\"$artist - ${title}.mp3\"";
rename $oldname, $newname;
print "\n";
}
Is there anything I can do to figure out why this is detecting an undefined value, even though everything should be properly defined?
Edit: I have just tried it with and without the quotes on these lines (in red) and yielded the same results:
Just don't waste your and our time trying to debug Perl scripts not having
Code:
use strict;
use warnings;
in the very beginning. First put the the above pragmas, achieve compilation without errors and run without warnings, then debug proper.
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.