+#!/usr/bin/perl -w
+use strict;
+use 5.010;
+
+package KTmusic;
+
+# package global music data
+# $music->{$id}->{titles} is array of album titles
+# $music->{$id}->{albums} is hash of album information hashes
+my $music;
+
+# initialize KTmusic module (create $music)
+# in: $config
+# out: true or undef
+sub init
+{
+ my $c;
+
+ unless($c = shift) {
+ say "Error: ".(caller 0)[3].": no config";
+ return;
+ }
+
+ # include every menu entry with type='music'
+ foreach my $entry (@{$c->{menu}->{entry}}) {
+ next unless ($entry->{id} eq 'music');
+
+ # fetch all album folders
+ my $basepath = $c->{dirs}->{content} . $entry->{folder};
+ unless(opendir(ALBUMS, $basepath)) {
+ say "Error: ".(caller 0)[3].
+ ": can't open $basepath: $!";
+ return;
+ }
+ my @folders = readdir(ALBUMS);
+ closedir(ALBUMS);
+
+ # read and parse $folder/info.xml
+ foreach my $folder(sort @folders) {
+ next if $folder =~ /^\.+$/;
+
+ # parse album information file
+ my $infofile = "$basepath/$folder/info.xml";
+ my $album = eval { XML::Simple::XMLin(
+ $infofile, KeyAttr => []) };
+ if($@) {
+ say "Error: ".(caller 0)[3].": $infofile: $@";
+ next;
+ }
+
+ # TODO: check data structures more thorough
+ unless($album->{albumtitle}) {
+ say "Error: ".(caller 0)[3].
+ "$infofile contains no album title";
+ next;
+ }
+
+ # fill in album title, folder and all other data
+ my $title = $album->{albumtitle};
+ push (@{$music->{$entry->{id}}->{titles}}, $title);
+ $music->{$entry->{id}}->{albums}->{$title} = $album;
+ $music->{$entry->{id}}->{albums}->{$title}->{path} =
+ $folder;
+ }
+ }
+}
+
+# check for music area overview or album detail page
+# in: $config, $entry (menu entry from $config), $templates
+# out: nothing
+sub print_content
+{
+ my $c;
+ my $entry;
+ my $templates;
+
+ unless($c = shift) {
+ say "Error: ".(caller 0)[3].": no config";
+ return;
+ }
+
+ unless($entry = shift) {
+ say "Error: ".(caller 0)[3].": no entry";
+ return;
+ }
+
+ unless($templates = shift) {
+ say "Error: ".(caller 0)[3].": no templates";
+ return;
+ }
+
+ # check for 'albumid' parameter in QUERY_STRING
+ if($ENV{QUERY_STRING}) {
+ foreach(split(/;/, $ENV{QUERY_STRING})) {
+ (my $param, my $val) = split(/=/, $_);
+ next unless ($param =~ /^albumid$/);
+
+ # found -> print album detail page
+ &KTmusic::print_albumdetails(
+ $c,
+ $entry,
+ $templates,
+ $val,
+ );
+ return;
+ }
+ }
+
+ # not found -> print album overview
+ &KTmusic::print_overview(
+ $c,
+ $entry,
+ $templates,
+ );
+}
+
+# print album overview
+# in: $config, $entry (menu entry from $config), $templates
+# out: nothing
+sub print_overview
+{
+ my $c;
+ my $entry;
+ my $templates;
+
+ unless($c = shift) {
+ say "Error: ".(caller 0)[3].": no config";
+ return;
+ }
+
+ unless($entry = shift) {
+ say "Error: ".(caller 0)[3].": no entry";
+ return;
+ }
+
+ unless($templates = shift) {
+ say "Error: ".(caller 0)[3].": no templates";
+ return;
+ }
+
+ # set global variables
+ $templates->{music_overview}->param(
+ PAGETITLE => &KTmain::get_string($c, 'music_contents'),
+ SCRIPT => $ENV{SCRIPT_NAME},
+ LANGUAGE => &KTmain::get_language($c),
+ MENUID => $entry->{id},
+ MENUTITLE => &KTmain::get_string($c, $entry->{id}),
+ TRACKS => &KTmain::get_string($c, 'music_tracks'),
+ ARTIST => &KTmain::get_string($c, 'music_artist'),
+ LICENSE => &KTmain::get_string($c, 'license'),
+ );
+
+ # set per-album variables
+ my @albums = @{$music->{$entry->{id}}->{titles}};
+ my @loopdata;
+ for my $index (0 .. $#albums) {
+ my %albumdata;
+ my $albuminfo =
+ $music->{$entry->{id}}->{albums}->{$albums[$index]};
+
+ $albumdata{ALBUMID} = $index;
+ $albumdata{ALBUMTITLE} = $albums[$index];
+ $albumdata{ALBUMCOVER} = $c->{dirs}->{webcontent} .
+ $entry->{folder} .
+ $albuminfo->{path} .
+ "/cover.jpg";
+ $albumdata{TRACKCOUNT} = $albuminfo->{albumtrackcount};
+ $albumdata{ARTISTNAME} = $albuminfo->{albumartist};
+ $albumdata{LICENSETEXT} = $albuminfo->{albumlicense};
+
+ # fill in license data
+ (my $url, my $img) =
+ &KTmain::get_license($c, $albuminfo->{albumlicense});
+ if(($url) && ($img)) {
+ $albumdata{LICENSEURL} = $url;
+ $albumdata{LICENSETEXT} = "<img src=\"$img\">";
+ } else {
+ $albumdata{LICENSEURL} = $c->{dirs}->{webcontent} .
+ $entry->{folder} .
+ $albuminfo->{path} . "/" .
+ $albuminfo->{albumlicense};
+ $albumdata{LICENSETEXT} =
+ &KTmain::get_string($c, 'license');
+ }
+
+ push(@loopdata, \%albumdata);
+ }
+ $templates->{music_overview}->param(ALBUMLOOP => \@loopdata);
+
+ print $templates->{music_overview}->output;
+}
+
+# print album details
+# in: $config, $entry, $templates, $albumid
+# ($albumid: index in $music->{id]->{titles} array)
+# out: nothing
+sub print_albumdetails
+{
+ my $c;
+ my $entry;
+ my $templates;
+ my $albumid;
+
+ unless($c = shift) {
+ say "Error: ".(caller 0)[3].": no config";
+ return;
+ }
+
+ unless($entry = shift) {
+ say "Error: ".(caller 0)[3].": no entry";
+ return;
+ }
+
+ unless($templates = shift) {
+ say "Error: ".(caller 0)[3].": no templates";
+ return;
+ }
+
+ unless(defined($albumid = shift)) {
+ say "Error: ".(caller 0)[3].": no albumid";
+ return;
+ }
+
+ my $albumtitle = $music->{$entry->{id}}->{titles}->[$albumid];
+ my $albuminfo = $music->{$entry->{id}}->{albums}->{$albumtitle};
+
+ # set global variables
+ $templates->{music_details}->param(
+ # general
+ PAGETITLE => &KTmain::get_string($c, 'music_details'),
+ SCRIPT => $ENV{SCRIPT_NAME},
+ LANGUAGE => &KTmain::get_language($c),
+ MENUID => $entry->{id},
+
+ # translations
+ MENUTITLE => &KTmain::get_string($c, $entry->{id}),
+ TRACKS => &KTmain::get_string($c, 'music_tracks'),
+ ARTIST => &KTmain::get_string($c, 'music_artist'),
+ LICENSE => &KTmain::get_string($c, 'license'),
+ TRACK => &KTmain::get_string($c, 'music_track'),
+ TITLE => &KTmain::get_string($c, 'music_title'),
+ LENGTH => &KTmain::get_string($c, 'music_length'),
+ DOWNLOAD => &KTmain::get_string($c, 'music_download'),
+ DOWNLOADTEXT => &KTmain::get_string($c, 'music_downloadtext'),
+ FETCHALBUM => &KTmain::get_string($c, 'music_fetchalbum'),
+
+ # album specific
+ ALBUMID => $albumid,
+ ALBUMTITLE => $albumtitle,
+ TRACKCOUNT => $albuminfo->{albumtrackcount},
+ ARTISTNAME => $albuminfo->{albumartist},
+ LICENSETEXT => $albuminfo->{albumlicense},
+ ALBUMCOVER => $c->{dirs}->{webcontent} .
+ $entry->{folder} .
+ $albuminfo->{path} .
+ "/cover.jpg",
+ # TODO: dynamically create zipfile
+ FETCHALBUMLINK => "javascript:alert(\'not implemented\')",
+ );
+
+ # fill in license data
+ (my $url, my $img) =
+ &KTmain::get_license($c, $albuminfo->{albumlicense});
+ if(($url) && ($img)) {
+ $templates->{music_details}->param(
+ LICENSEURL => $url,
+ LICENSETEXT => "<img src=\"$img\">",
+ );
+ } else {
+ $templates->{music_details}->param(
+ LICENSEURL => $c->{dirs}->{webcontent} .
+ $entry->{folder} .
+ $albuminfo->{path} . "/" .
+ $albuminfo->{albumlicense},
+ LICENSETEXT =>
+ &KTmain::get_string($c, 'license'),
+ );
+ }
+
+ # set track-specific variables
+ my @loopdata;
+ foreach my $track (@{$albuminfo->{song}}) {
+ my %trackdata;
+
+ $trackdata{TRACKTITLE} = $track->{title},
+ $trackdata{TRACKLENGTH} = &get_time($track->{playtime}),
+ $trackdata{TRACKPATH} = $c->{dirs}->{webcontent} .
+ $entry->{folder} .
+ $albuminfo->{path} . '/' .
+ $track->{filename},
+
+ push(@loopdata, \%trackdata);
+ }
+ $templates->{music_details}->param(TRACKLOOP => \@loopdata);
+
+ print $templates->{music_details}->output;
+}
+
+# turn number of seconds into human-readable format m:ss or h:mm:ss
+# in: $num_seconds
+# out: string or undef
+sub get_time
+{
+ my $seconds;
+ my $output;
+
+ unless($seconds = shift) {
+ say "Error: ".(caller 0)[3].": no seconds";
+ return;
+ }
+
+ (my $sec, my $min, my $hour) = gmtime($seconds);
+ if($hour) {
+ $output = sprintf("%u:%02u:%02u", $hour, $min, $sec);
+ } else {
+ $output = sprintf("%u:%02u", $min, $sec);
+ }
+ return $output;
+}
+
+1;