Mat's Playground

Music Browser

Description

The Music Browser uses the MySQL database created by the music.dbIndex script. It allows you to sort your music by different criteria and to stream it. Simply point your browser to the site and click on the item you want to listen to. The script will then create a .m3u playlist which can then be loaded into your favorite music player.

The music will then be streamed from the server.

The script has the ability to display album covers on the album info page. Therefore, all you have to do is to copy a file cover.(png,jpg,gif) into the album's directory. The script will look for such a file and -if available- display it

Here are some screenshots (click for larger images)
all albums sorted by genre all songs an album info page

Note1: I've tested it only in a LAN with 2 PCs, so I don't know what happens if the same file is accessed simultaneously by different computers. It should work though :)

Note2: XMMS does not support seeking in streams. This is not a bug in my script, but in the player. Use AlsaPlayer instead

Note3: music.cgi will only stream the mp3 files. The helper script stream.ogg will stream ogg files.

Required tcl extensions

Download

Source code

Hide line numbers Expand lines
   1#!/bin/sh
   2# the next line restarts using tclsh \
   3exec tclsh "$0" "$@"
   4
   5
   6#
   7# required packages:
   8#   base64 (tcllib)
   9#   mysqltcl
  10#
  11
  12
  13# in test_mode, the created m3u file is displayed, not passed to the player
  14set test_mode no
  15
  16# should the playcount be displayed/used
  17set use_playcount yes
  18
  19# how many items should be displayed in the "most played"-boxes
  20set playcount_nr 5
  21
  22
  23# display a randomly selected album?
  24set use_random_album yes
  25
  26# keep track of last_played date and display it?
  27set use_last_played yes
  28
  29# colors used for tables
  30set global_first_color  "#FFFFFF"    ;# every odd row
  31set global_second_color "#E9E9E9"    ;# every even row
  32set global_hilite_color "#B0FFF6"    ;# one given row
  33
  34
  35catch {
  36    lappend auto_path /usr/local/lib
  37
  38    # load base64 extension
  39    package require base64
  40
  41    # load mysql extension
  42    package require mysqltcl
  43
  44    # mysql settings
  45    set config(host) "localhost"
  46    set config(user) "tcl"
  47    set config(pass) "tcl"
  48    set config(base) "music"
  49
  50    # table names
  51    set config(albums)  "albums"
  52    set config(artists) "artists"
  53    set config(songs)   "songs"
  54
  55
  56
  57
  58    # display all environment variables
  59    proc show_env {} {
  60        global env
  61        puts "<pre>"
  62        foreach i [parray env] {puts $i}
  63        puts "</pre>"
  64    }
  65
  66
  67    # connect to the database
  68    proc db_connect {} {
  69        global config db
  70        catch {set db [mysqlconnect -host $config(host) -user $config(user) -pass $config(pass) -db $config(base)]} sql_result
  71
  72        # if connection was successful, we'll get a variable db
  73        if {![info exists db]} then {
  74            header
  75            puts $sql_result
  76            footer
  77
  78            unset sql_result
  79            exit
  80        }
  81        unset sql_result
  82
  83        # check whether all tables exist
  84        set listOfTables [mysqlinfo $db tables]
  85
  86        foreach i [list $config(albums) $config(artists) $config(songs)] {
  87            if {[lsearch $listOfTables $i] == "-1"} then {
  88                header
  89                puts "table \"$i\" missing"
  90                footer
  91                unset listOfTables
  92                exit
  93            }
  94        }
  95    }
  96
  97
  98    # returns a masked &
  99    proc & {} {
 100        return "&amp;"
 101    }
 102
 103    # randomizes a list
 104    proc lrandomize {list} {
 105        # first create a element-random value pair list
 106        set l {}
 107        foreach e $list {
 108            lappend l [list $e [expr {rand()}]]
 109        }
 110
 111        # then extract randomized elements
 112        set list {}
 113        foreach e [lsort -index 1 $l] {
 114            lappend list [lindex $e 0]
 115        }
 116        return $list
 117    }
 118
 119    # converts seconds into 'nice' time values
 120    proc transform_secs {in} {
 121
 122        set m [expr $in / 60]
 123        set s [expr $in % 60]
 124
 125        if {$m < 10} then {set m "0$m"}
 126        if {$s < 10} then {set s "0$s"}
 127
 128        # hrs, if necessary
 129        if {$m > 59} then {
 130            set h [expr $m / 60]
 131            set m [expr $m % 60]
 132            if {$h < 10} then {set h "0$h"}
 133            if {$m < 10} then {set m "0$m"}
 134            set m "$h:$m"
 135        }
 136
 137        return "$m:$s"
 138    }
 139
 140    # takes a list of filenames, and creates a playlist
 141    # tracks is a list of {songID filename title artist length} items
 142    proc send_playlist {tracks} {
 143        global env test_mode
 144
 145        if {! $test_mode} then {
 146            puts "Content-type: audio/x-mpegurl"
 147            puts "Content-Disposition: attachment; filename=playlist.m3u\n"
 148        } else {
 149            header
 150            puts "<pre>"
 151        }
 152
 153        puts "#EXTM3U"
 154        foreach i $tracks {
 155            set id     [lindex $i 0]
 156            set file   [lindex $i 1]
 157            set title  [lindex $i 2]
 158            set artist [lindex $i 3]
 159            set length [lindex $i 4]
 160
 161
 162            #
 163            # mp3 files will be streamed from inside music.cgi, ogg files don't like that, so they'll be
 164            # streamed by the helper script stream.ogg
 165            #
 166            if {[file extension $file] == ".ogg"} then {
 167                set link "http://$env(SERVER_NAME)/cgi-bin/stream.ogg?$id"
 168            } else {
 169                set link "http://$env(SERVER_NAME)$env(SCRIPT_NAME)?stream=$id"
 170            }
 171
 172
 173            #
 174            # if the file is requested from the server host, return the true filename
 175            #
 176            if {$env(SERVER_ADDR) == $env(REMOTE_ADDR)} then {
 177                set link $file
 178            }
 179
 180            puts "#EXTINF:$length,$artist - $title"
 181            puts "$link"
 182
 183            unset i id file title artist length link
 184        }
 185        unset tracks
 186
 187
 188        if {$test_mode} then {
 189            footer
 190        }
 191
 192    }
 193
 194
 195    # the header of all pages
 196    proc header {} {
 197        # show the browser what's coming :)
 198        puts "Content-type: text/html\n"
 199        puts "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\" \"http://www.w3.org/TR/REC-html40/loose.dtd\">"
 200        puts "<html>"
 201        puts "  <head>"
 202        puts "    <META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html;charset=utf-8\">"
 203        puts "    <title>Music Browser</title>"
 204        puts "    <script type=\"text/javascript\">"
 205        puts "    <!--"
 206        puts "      function toggle(el) {"
 207        puts "        var target = document.getElementById(el);"
 208        puts "        if (target.style.visibility == \"hidden\")"
 209        puts "        {"
 210        puts "          target.style.visibility = \"visible\";"
 211        puts "          target.style.height = \"auto\";"
 212        puts "        } else {"
 213        puts "          target.style.visibility = \"hidden\";"
 214        puts "          target.style.height = \"0px\";"
 215        puts "        }"
 216        puts "      }"
 217        puts "    //-->"
 218        puts "    </script>"
 219        puts "    <style type=\"text/css\">"
 220        puts "    <!--"
 221        puts "      div.box {"
 222        puts "        background-color: white;"
 223        puts "        margin: 4px;"
 224        puts "        border: 1px solid #8CACBB;"
 225        puts "      }"
 226        puts ""
 227        puts "      div.box-title {"
 228        puts "        background-color: #C7D0D9;"
 229        puts "        padding-left: 0px;"
 230        puts "        padding-right: 0px;"
 231        puts "        border-bottom: 1px solid #8CACBB;"
 232        puts "        text-align: center;"
 233        puts "        font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;"
 234        puts "        font-size: 10px;"
 235        puts "        color: #555575;"
 236        puts "      }"
 237        puts ""
 238        puts "      div.box-data {"
 239        puts "        padding-left: 5px;"
 240        puts "        padding-right: 5px;"
 241        puts "        padding-bottom: 4px;"
 242        puts "      }"
 243        puts ""
 244        puts "      td.box-data-contents {"
 245        puts "        font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;"
 246        puts "        font-size: 10px;"
 247        puts "        color: black;"
 248        puts "        text-align: left;"
 249        puts "      }"
 250        puts ""
 251        puts "      a.box-data-link {"
 252        puts "        font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;"
 253        puts "        font-size: 10px;"
 254        puts "        text-decoration: none;"
 255        puts "        color: #436976;"
 256        puts "      }"
 257        puts ""
 258        puts "      a.box-menu-link {"
 259        puts "        font-family: Verdana, Geneva, Arial, Helvetica, sans-serif;"
 260        puts "        font-size: 10px;"
 261        puts "        text-decoration: none;"
 262        puts "        color: #555575;"
 263        puts "      }"
 264        puts "    -->"
 265        puts "    </style>"
 266
 267        puts "  </head>"
 268        puts "  <body bgcolor=\"#FFFFFF\" text=\"#000000\" link=\"#0000FF\" alink=\"#0000FF\" vlink=\"#0000FF\">\n"
 269
 270        # the 'nav-bar'
 271        navibar
 272    }
 273
 274
 275    # returns a link
 276    proc makeLink {href desc {title ""} {class ""}} {
 277        if {$class == ""} then {set class "box-data-link"}
 278
 279        if {$title == ""} then {
 280            switch $desc {
 281                "play|all" {set title "play album"}
 282                "rand"     {set title "play album in random order"}
 283                "info"     {set title "show album info"}
 284                "select"   {set title "play selected songs"}
 285            }
 286        }
 287
 288
 289        return "<a class=\"$class\" href=\"$href\" title=\"$title\">$desc</a>"
 290    }
 291
 292
 293    # navi-bar
 294    proc navibar {} {
 295        global env cmd val
 296
 297        set albumLink [makeLink "$env(SCRIPT_NAME)?sort=album" "album" "show all albums sorted by title"]
 298        set genreLink [makeLink "$env(SCRIPT_NAME)?sort=genre" "genre" "show all albums sorted by genre"]
 299        set songLink  [makeLink "$env(SCRIPT_NAME)?sort=song"  "song"  "show all songs sorted by title"]
 300        set yearLink  [makeLink "$env(SCRIPT_NAME)?sort=year"  "year"  "show all albums sorted by year"]
 301
 302        if {$cmd == "sort"} then {
 303            switch -glob $val {
 304                "album*" {set albumLink "album"}
 305                "genre"  {set genreLink "genre"}
 306                "song*"  {set songLink  "song"}
 307                "year"   {set yearLink  "year"}
 308            }
 309        }
 310
 311        puts "    <table border=\"0\" align=\"center\">"
 312        puts "      <tr>"
 313        puts "        <td>"
 314        puts "          <div class=\"box\">"
 315        puts "            <div class=\"box-title\">Menu</div>"
 316        puts "            <div class=\"box-data\">"
 317        puts "              <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\">"
 318        puts "                <tr>"
 319        puts "                  <td class=\"box-data-contents\">$albumLink</td>"
 320        puts "                  <td class=\"box-data-contents\">&nbsp;$genreLink</td>"
 321        puts "                  <td class=\"box-data-contents\">&nbsp;$songLink</td>"
 322        puts "                  <td class=\"box-data-contents\">&nbsp;$yearLink</td>"
 323        puts "                </tr>"
 324        puts "              </table>"
 325        puts "            </div>"
 326        puts "          </div>"
 327        puts "        </td>"
 328        puts "      </tr>"
 329        puts "    </table>\n"
 330
 331        unset albumLink genreLink songLink yearLink
 332    }
 333
 334    # last_played album
 335    proc last_played {} {
 336        global db env config
 337
 338        puts "          <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
 339        puts "            <tr>"
 340        puts "              <td>"
 341        puts "                <div class=\"box\">"
 342        puts "                  <div class=\"box-title\">Last Played Album</div>"
 343        puts "                  <div class=\"box-data\">"
 344        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"2\">"
 345
 346        foreach i [mysqlsel $db "SELECT albumID, artist, title, last_played
 347                                        FROM $config(albums)
 348                                        ORDER BY last_played DESC, artist, title
 349                                        LIMIT 1" -list] {
 350
 351            set albumID     [lindex $i 0]
 352            set artistName  [lindex $i 1]
 353            set title       [lindex $i 2]
 354
 355            set infoLink [makeLink "$env(SCRIPT_NAME)?info=album[&]id=[set albumID]" "info"]
 356
 357            puts "                      <tr>"
 358            puts "                        <td class=\"box-data-contents\">$artistName - $title</td>"
 359            puts "                        <td class=\"box-data-contents\" valign=\"top\">&nbsp;$infoLink</td>"
 360            puts "                      </tr>"
 361
 362            unset i albumID artistName title infoLink
 363        }
 364
 365        puts "                    </table>"
 366        puts "                  </div>"
 367        puts "                </div>"
 368        puts "              </td>"
 369        puts "            </tr>"
 370        puts "          </table>"
 371
 372    }
 373
 374    # displays a box with a randomly selected album
 375    proc random_album {} {
 376        global db env config
 377
 378        puts "          <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
 379        puts "            <tr>"
 380        puts "              <td>"
 381        puts "                <div class=\"box\">"
 382        puts "                  <div class=\"box-title\">Random Album</div>"
 383        puts "                  <div class=\"box-data\">"
 384        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"2\">"
 385
 386        foreach i [mysqlsel $db "SELECT albumID, artist, title
 387                                        FROM $config(albums)
 388                                        ORDER BY rand()
 389                                        LIMIT 1" -list] {
 390
 391            set albumID     [lindex $i 0]
 392            set artistName  [lindex $i 1]
 393            set title       [lindex $i 2]
 394
 395            set infoLink [makeLink "$env(SCRIPT_NAME)?info=album[&]id=[set albumID]" "info"]
 396
 397            puts "                      <tr>"
 398            puts "                        <td class=\"box-data-contents\">$artistName - $title</td>"
 399            puts "                        <td class=\"box-data-contents\" valign=\"top\">&nbsp;$infoLink</td>"
 400            puts "                      </tr>"
 401
 402            unset i albumID artistName title infoLink
 403        }
 404
 405        puts "                    </table>"
 406        puts "                  </div>"
 407        puts "                </div>"
 408        puts "              </td>"
 409        puts "            </tr>"
 410        puts "          </table>"
 411
 412    }
 413
 414    # most played albums/songs
 415    proc most_played {} {
 416        global db env config playcount_nr
 417
 418        foreach type {albums songs} {
 419
 420            if {$type == "albums"} then {
 421                set db_to_use $config(albums)
 422            } else {
 423                set db_to_use $config(songs)
 424            }
 425
 426
 427            if {[mysqlsel $db "select count(*) from $db_to_use where playcount <> '0'" -list] == 0} then {
 428                unset type
 429                continue
 430            }
 431
 432            puts "          <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
 433            puts "            <tr>"
 434            puts "              <td>"
 435            puts "                <div class=\"box\">"
 436            puts "                  <div class=\"box-title\">Most Played [string totitle $type]</div>"
 437            puts "                  <div class=\"box-data\">"
 438            puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"2\">"
 439
 440            switch $type {
 441
 442                "albums" {
 443                    set list [mysqlsel $db "SELECT albumID, artist, title, playcount
 444                                                   FROM $db_to_use WHERE playcount <> '0'
 445                                                   ORDER BY playcount DESC, artist, title
 446                                                   LIMIT $playcount_nr" -list]
 447                }
 448                "songs" {
 449                    set list [mysqlsel $db "SELECT albumID, $config(artists).name AS artist, title, playcount, songID
 450                                                   FROM $db_to_use, $config(artists)
 451                                                   WHERE playcount <> '0' AND $config(songs).artistID = $config(artists).artistID
 452                                                   ORDER BY playcount DESC, artist, title
 453                                                   LIMIT $playcount_nr;" -list]
 454                }
 455            }
 456
 457            foreach i $list {
 458                set albumID    [lindex $i 0]
 459                set artistName [lindex $i 1]
 460                set title      [lindex $i 2]
 461                set playcount  [lindex $i 3]
 462
 463
 464                if {$type == "albums"} then {
 465                    set infoLink [makeLink "$env(SCRIPT_NAME)?info=album[&]id=[set albumID]" "info"]
 466                } else {
 467                    set songID [lindex $i 4]
 468                    set infoLink [makeLink "$env(SCRIPT_NAME)?info=album[&]id=[set albumID][&]highlight=[set songID]" "info"]
 469                    unset songID
 470                }
 471
 472                puts "                      <tr>"
 473                puts "                        <td class=\"box-data-contents\" valign=\"top\" style=\"text-align:right\">$playcount</td>"
 474                puts "                        <td class=\"box-data-contents\">$artistName - $title</td>"
 475                puts "                        <td class=\"box-data-contents\" valign=\"top\">&nbsp;$infoLink</td>"
 476                puts "                      </tr>"
 477
 478                unset i albumID artistName title playcount infoLink
 479            }
 480            unset list
 481
 482            puts "                    </table>"
 483            puts "                  </div>"
 484            puts "                </div>"
 485            puts "              </td>"
 486            puts "            </tr>"
 487            puts "          </table>"
 488
 489            if {$type == "albums"} then {
 490                # display the least played albums
 491                least_played
 492            }
 493
 494            unset type db_to_use
 495        }
 496    }
 497
 498    # least played albums
 499    proc least_played {} {
 500        global db env config playcount_nr
 501
 502        puts "          <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
 503        puts "            <tr>"
 504        puts "              <td>"
 505        puts "                <div class=\"box\">"
 506        puts "                  <div class=\"box-title\">Least Played Albums</div>"
 507        puts "                  <div class=\"box-data\">"
 508        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"2\">"
 509
 510        foreach i [mysqlsel $db "SELECT albumID, artist, title, playcount
 511                                        FROM $config(albums)
 512                                        ORDER BY playcount, artist, title
 513                                        LIMIT $playcount_nr" -list] {
 514
 515            set albumID    [lindex $i 0]
 516            set artistName [lindex $i 1]
 517            set title      [lindex $i 2]
 518            set playcount  [lindex $i 3]
 519
 520            set infoLink [makeLink "$env(SCRIPT_NAME)?info=album[&]id=[set albumID]" "info"]
 521
 522            puts "                      <tr>"
 523            puts "                        <td class=\"box-data-contents\" valign=\"top\" style=\"text-align:right\">$playcount</td>"
 524            puts "                        <td class=\"box-data-contents\">$artistName - $title</td>"
 525            puts "                        <td class=\"box-data-contents\" valign=\"top\">&nbsp;$infoLink</td>"
 526            puts "                      </tr>"
 527
 528            unset i albumID artistName title playcount infoLink
 529        }
 530
 531        puts "                    </table>"
 532        puts "                  </div>"
 533        puts "                </div>"
 534        puts "              </td>"
 535        puts "            </tr>"
 536        puts "          </table>"
 537    }
 538
 539
 540    # the footer of all pages
 541    proc footer {} {
 542        puts "\n  </body>"
 543        puts "</html>"
 544    }
 545
 546    set query $env(QUERY_STRING)
 547    if {$query == ""} then {
 548        puts "Location: $env(SCRIPT_NAME)?sort=genre\n\n"
 549    }
 550
 551    # a query is of the form 'command=value', here it is separated
 552    set cmd [lindex [split $query "="] 0]
 553    set val [join [lrange [split $query "="] 1 end] "="]
 554
 555
 556    switch $cmd {
 557
 558        "sort" {
 559        #
 560        # display the listings of the available media
 561        #
 562
 563            # connect to database
 564            db_connect
 565
 566            # emit the header
 567            header
 568
 569            # show the lists
 570            switch -glob $val {
 571                "album*" {
 572                    #
 573                    # sort albums by title
 574                    #
 575
 576                    # val = "album&by=title" or val = "album"
 577
 578                    # sortby = {'title','artist','length'}
 579                    set sortby [lindex [split [lindex [split $val "&"] end] "="] end]
 580                    if {$sortby == "album"} then {set sortby "title"}
 581
 582                    puts "    <table border=\"0\" align=\"center\">"
 583                    puts "      <tr>"
 584                    puts "        <td>\n"
 585                    puts "          <div class=\"box\">"
 586                    puts "            <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">"
 587                    puts "              <tr>"
 588
 589                    set titleLink  [makeLink "$env(SCRIPT_NAME)?sort=album[&]by=title"  "Title"  "sort list by album titles" "box-menu-link"]
 590                    set artistLink [makeLink "$env(SCRIPT_NAME)?sort=album[&]by=artist" "Artist" "sort list by artists"      "box-menu-link"]
 591                    set lengthLink [makeLink "$env(SCRIPT_NAME)?sort=album[&]by=length" "Length" "sort list by length"       "box-menu-link"]
 592
 593                    puts "                <td><div class=\"box-title\" style=\"text-align:left\">$titleLink&nbsp;</div></td>"
 594                    puts "                <td><div class=\"box-title\" style=\"text-align:left\">$artistLink&nbsp;</div></td>"
 595                    puts "                <td><div class=\"box-title\" style=\"text-align:right\">$lengthLink</div></td>"
 596                    puts "              </tr>"
 597
 598                    unset titleLink artistLink lengthLink
 599
 600                    switch $sortby {
 601                        "artist" {set sqlOrderClause "ORDER BY artist, title"}
 602                        "length" {set sqlOrderClause "ORDER BY length, artist, title"}
 603                        default  {set sqlOrderClause "ORDER BY title, artist"}
 604                    }
 605
 606                    # display them
 607                    foreach album [mysqlsel $db "SELECT $config(albums).albumID,
 608                                                        $config(artists).artistID  AS artistID,
 609                                                        $config(albums).artist     AS artist,
 610                                                        $config(albums).title      AS title,
 611                                                        sum($config(songs).length) AS length
 612
 613                                                            FROM $config(albums), $config(artists), $config(songs)
 614                                                            WHERE artist = $config(artists).name
 615                                                                AND $config(albums).albumID = $config(songs).albumID
 616                                                            GROUP BY $config(albums).albumID
 617                                                            $sqlOrderClause" -list] {
 618
 619                        set albumID  [lindex $album 0]
 620                        set artistID [lindex $album 1]
 621                        set artist   [lindex $album 2]
 622                        set title    [lindex $album 3]
 623                        set length   [lindex $album 4]
 624
 625                        set titleLink  [makeLink "$env(SCRIPT_NAME)?info=album[&]id=$albumID"        $title  "show album info"]
 626                        set artistLink [makeLink "$env(SCRIPT_NAME)?info=artist[&]id=[set artistID]" $artist "show all albums by this artist"]
 627
 628                        puts "              <tr>"
 629                        puts "                <td valign=\"top\" class=\"box-data-contents\" nowrap>$titleLink&nbsp;</td>"
 630                        puts "                <td valign=\"top\" class=\"box-data-contents\" nowrap>$artistLink&nbsp;</td>"
 631                        puts "                <td valign=\"top\" class=\"box-data-contents\" style=\"text-align:right\">[transform_secs $length]</td>"
 632                        puts "              </tr>"
 633
 634                        unset album albumID artistID artist title titleLink artistLink
 635                    }
 636                    unset sqlOrderClause
 637
 638                    puts "            </table>"
 639                    puts "          </div>\n"
 640                    puts "        </td>"
 641                    puts "      </tr>"
 642                    puts "    </table>"
 643                    unset sortby
 644                }
 645
 646
 647                "genre" {
 648                    #
 649                    # sort albums by genre
 650                    #
 651
 652                    puts "    <table border=\"0\" align=\"center\">"
 653                    puts "      <tr>"
 654
 655                    puts "        <td width=\"1\" valign=\"top\">"
 656                    puts "          <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
 657                    puts "            <tr>"
 658                    puts "              <td>"
 659                    puts "                <div class=\"box\">"
 660                    puts "                  <div class=\"box-title\">Statistics</div>"
 661                    puts "                  <div class=\"box-data\">"
 662
 663                    set son [mysqlsel $db "SELECT count(*) FROM $config(songs)"    -list]
 664                    set art [mysqlsel $db "SELECT count(*) FROM $config(artists)"  -list]
 665                    set alb [mysqlsel $db "SELECT count(*) FROM $config(albums)"   -list]
 666                    set run [mysqlsel $db "SELECT sum(length) FROM $config(songs)" -list]
 667
 668                    # if the db is empty, $run will be "{{}}"
 669                    if {[string is integer $run]} then {
 670                        set run [transform_secs $run]
 671                    } else {
 672                        set run 0
 673                    }
 674
 675                    puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\">"
 676                    puts "                      <tr>"
 677                    puts "                        <td class=\"box-data-contents\">Albums:&nbsp;</td>"
 678                    puts "                        <td class=\"box-data-contents\" style=\"text-align:right\">$alb</td>"
 679                    puts "                      </tr>"
 680                    puts "                      <tr>"
 681                    puts "                        <td class=\"box-data-contents\">Artists:&nbsp;</td>"
 682                    puts "                        <td class=\"box-data-contents\" style=\"text-align:right\">$art</td>"
 683                    puts "                      </tr>"
 684                    puts "                      <tr>"
 685                    puts "                        <td class=\"box-data-contents\">Songs:&nbsp;</td>"
 686                    puts "                        <td class=\"box-data-contents\" style=\"text-align:right\">$son</td>"
 687                    puts "                      </tr>"
 688                    puts "                      <tr>"
 689                    puts "                        <td class=\"box-data-contents\">Runtime:&nbsp;</td>"
 690                    puts "                        <td class=\"box-data-contents\" style=\"text-align:right\">$run</td>"
 691                    puts "                      </tr>"
 692                    puts "                    </table>"
 693
 694                    unset alb art son run
 695
 696                    puts "                  </div>"
 697                    puts "                </div>"
 698                    puts "              </td>"
 699                    puts "            </tr>"
 700                    puts "          </table>"
 701                    puts "        </td>\n"
 702
 703
 704                    puts "        <td valign=\"top\">\n"
 705
 706                    foreach genre [mysqlsel $db "SELECT DISTINCT genre
 707                                                                 FROM $config(albums)
 708                                                                 ORDER BY genre" -flatlist] {
 709
 710                        # html id tags don't allow spaces in them, exchange with underscores
 711                        regsub -all " " $genre "_" genreID
 712
 713                        set genreLink [makeLink "javascript:toggle('$genreID')" $genre "click to display genre $genre" "box-menu-link"]
 714
 715                        puts "          <div class=\"box\">"
 716                        puts "            <div class=\"box-title\">$genreLink</div>"
 717                        puts "            <div class=\"box-data\" id=\"$genreID\" style=\"visibility:hidden; height: 0px\">"
 718                        puts "              <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
 719
 720                        foreach album [mysqlsel $db "SELECT artist, title, albumID
 721                                                            FROM $config(albums)
 722                                                            WHERE genre = '$genre'
 723                                                            ORDER BY artist, title" -list] {
 724
 725                            set albumArtist [lindex $album 0]
 726                            set albumTitle  [lindex $album 1]
 727                            set albumID     [lindex $album 2]
 728
 729                            set playLink [makeLink "$env(SCRIPT_NAME)?play=album[&]id=[set albumID]"                "play"]&nbsp;
 730                            set randLink [makeLink "$env(SCRIPT_NAME)?play=album[&]id=[set albumID][&]style=random" "rand"]&nbsp;
 731                            set infoLink [makeLink "$env(SCRIPT_NAME)?info=album[&]id=[set albumID]"                "info"]
 732
 733                            # song collections will only have the randLink
 734                            if {$genre == "Other"} then {set playLink ""}
 735
 736                            set panel "[set playLink][set randLink][set infoLink]"
 737
 738                            puts "                <tr>"
 739                            puts "                  <td class=\"box-data-contents\" valign=\"top\" align=\"center\" width=\"20\"></td>"
 740                            puts "                  <td class=\"box-data-contents\">$albumArtist - $albumTitle<br>$panel</td>"
 741                            puts "                </tr>"
 742
 743                            unset album albumArtist albumTitle albumID playLink randLink infoLink panel
 744                        }
 745
 746                        puts "              </table>"
 747                        puts "            </div>"
 748                        puts "          </div>\n"
 749
 750                        unset genre genreID genreLink
 751                    }
 752
 753                    puts "        </td>\n"
 754
 755                    if {$use_playcount || $use_last_played || $use_random_album} then {
 756                        puts "        <td valign=\"top\">"
 757
 758                        if {$use_last_played} then {
 759                            last_played
 760                        }
 761
 762                        if {$use_random_album} then {
 763                            random_album
 764                        }
 765
 766                        if {$use_playcount} then {
 767                            most_played
 768                        }
 769
 770                        puts "        </td>\n"
 771                    }
 772
 773                    puts "      </tr>"
 774                    puts "    </table>"
 775
 776                }
 777
 778                "song*" {
 779                    #
 780                    # sort by song-title
 781                    #
 782
 783                    # val = "song&by=title" or val = "song"
 784
 785                    # sortby = {'title','artist','album','length'}
 786                    set sortby [lindex [split [lindex [split $val "&"] end] "="] end]
 787                    if {$sortby == "song"} then {set sortby "title"}
 788
 789                    set seleLink [makeLink "javascript:document.forms\[0\].submit();" "selection" "play selected songs"]
 790
 791                    puts "    <table border=\"0\" align=\"center\">"
 792                    puts "      <tr>\n"
 793
 794                    puts "        <td width=\"1\" valign=\"top\">"
 795                    puts "          <table border=\"0\">"
 796                    puts "            <tr>"
 797                    puts "              <td>"
 798                    puts "                <div class=\"box\">"
 799                    puts "                  <div class=\"box-title\">Play</div>"
 800                    puts "                  <div class=\"box-data\">"
 801                    puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\">"
 802                    puts "                      <tr>"
 803                    puts "                        <td class=\"box-data-contents\">$seleLink</td>"
 804                    puts "                      </tr>"
 805                    puts "                    </table>"
 806                    puts "                  </div>"
 807                    puts "                </div>"
 808                    puts "              </td>"
 809                    puts "            </tr>"
 810                    puts "          </table>"
 811                    puts "        </td>\n"
 812
 813                    puts "        <td align=\"center\" valign=\"top\">"
 814                    puts "          <table border=\"0\">"
 815                    puts "            <tr>"
 816                    puts "              <td>"
 817                    puts "                <form action=\"$env(SCRIPT_NAME)?play=selection\" method=\"post\">"
 818                    puts "                  <div class=\"box\">"
 819                    puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">"
 820                    puts "                      <tr>"
 821
 822                    set titleLink  [makeLink "$env(SCRIPT_NAME)?sort=song[&]by=title"  "Title"  "sort list by song titles" "box-menu-link"]
 823                    set artistLink [makeLink "$env(SCRIPT_NAME)?sort=song[&]by=artist" "Artist" "sort list by artists"     "box-menu-link"]
 824                    set albumLink  [makeLink "$env(SCRIPT_NAME)?sort=song[&]by=album"  "Album"  "sort list by album names" "box-menu-link"]
 825                    set lengthLink [makeLink "$env(SCRIPT_NAME)?sort=song[&]by=length" "Length" "sort list by length"      "box-menu-link"]
 826
 827                    puts "                        <td><div class=\"box-title\" style=\"text-align:left\">&nbsp;</div></td>"
 828                    puts "                        <td><div class=\"box-title\" style=\"text-align:left\">$titleLink&nbsp;</div></td>"
 829                    puts "                        <td><div class=\"box-title\" style=\"text-align:left\">$artistLink&nbsp;</div></td>"
 830                    puts "                        <td><div class=\"box-title\" style=\"text-align:left\">$albumLink&nbsp;</div></td>"
 831                    puts "                        <td><div class=\"box-title\" style=\"text-align:right\">$lengthLink</div></td>"
 832                    puts "                      </tr>"
 833
 834                    unset seleLink titleLink artistLink albumLink lengthLink
 835
 836                    switch $sortby {
 837                        "artist" {set sqlOrderClause "ORDER BY artist, album, song"}
 838                        "album"  {set sqlOrderClause "ORDER BY album, song, artist"}
 839                        "length" {set sqlOrderClause "ORDER BY $config(songs).length DESC, song, artist, album"}
 840                        default  {set sqlOrderClause "ORDER BY song, artist, album"}
 841                    }
 842
 843                    # display them
 844                    set wholeLength 0
 845                    set row_count 0
 846                    foreach song [mysqlsel $db "SELECT $config(songs).songID, $config(songs).albumID, $config(songs).artistID, $config(songs).length,
 847                                                       $config(songs).title AS song, $config(artists).name AS artist, $config(albums).title as album,
 848                                                       $config(albums).genre as genre
 849                                                       FROM $config(songs), $config(artists), $config(albums)
 850                                                       WHERE $config(artists).artistID = $config(songs).artistID
 851                                                             AND $config(songs).albumID = $config(albums).albumID
 852                                                       $sqlOrderClause" -list] {
 853
 854                        set songID   [lindex $song 0]
 855                        set albumID  [lindex $song 1]
 856                        set artistID [lindex $song 2]
 857                        set length   [lindex $song 3]
 858                        set title    [lindex $song 4]
 859
 860                        set artist   [lindex $song 5]
 861                        set album    [lindex $song 6]
 862                        set genre    [lindex $song 7]
 863
 864                        if {$genre == "Opera"} then {
 865                            unset song title length albumID songID artistID artist album genre
 866                            continue
 867                        }
 868
 869                        # set background color
 870                        incr row_count
 871                        if {[expr $row_count % 2] != "0"} then {
 872                            set row_color $global_first_color
 873                        } else {
 874                            set row_color $global_second_color
 875                        }
 876
 877                        set songLink   [makeLink "$env(SCRIPT_NAME)?play=song[&]id=$songID"          $title  "play song"]
 878                        set albumLink  [makeLink "$env(SCRIPT_NAME)?info=album[&]id=$albumID"        $album  "show album info"]
 879                        set artistLink [makeLink "$env(SCRIPT_NAME)?info=artist[&]id=[set artistID]" $artist "show all songs by this artist"]
 880
 881                        set wholeLength [expr $wholeLength + $length]
 882                        puts "                      <tr>"
 883                        puts "                        <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color\"><input type=\"checkbox\" name=\"$songID\"></td>"
 884                        puts "                        <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color\">$songLink&nbsp;</td>"
 885                        puts "                        <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color\">$artistLink&nbsp;</td>"
 886                        puts "                        <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color\">$albumLink&nbsp;</td>"
 887                        puts "                        <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color; text-align:right\">[transform_secs $length]</td>"
 888                        puts "                      </tr>"
 889
 890                        unset song title length albumID songID artistID artist album songLink albumLink artistLink genre row_color
 891                    }
 892                    unset sqlOrderClause row_count
 893
 894                    puts "                    </table>"
 895                    puts "                  </div>"
 896                    puts "                </form>"
 897                    puts "              </td>"
 898                    puts "            </tr>"
 899                    puts "          </table>"
 900                    puts "        </td>\n"
 901
 902                    puts "        <td width=\"1\" valign=\"top\">"
 903                    puts "          <table border=\"0\">"
 904                    puts "            <tr>"
 905                    puts "              <td>"
 906                    puts "                <div class=\"box\">"
 907                    puts "                  <div class=\"box-title\">Runtime</div>"
 908                    puts "                  <div class=\"box-data\">"
 909                    puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\">"
 910                    puts "                      <tr>"
 911                    puts "                        <td class=\"box-data-contents\">[transform_secs $wholeLength]</td>"
 912                    puts "                      </tr>"
 913                    puts "                    </table>"
 914                    puts "                  </div>"
 915                    puts "                </div>"
 916                    puts "              </td>"
 917                    puts "            </tr>"
 918                    puts "          </table>"
 919                    puts "        </td>\n"
 920
 921                    puts "      </tr>"
 922                    puts "    </table>"
 923                    unset wholeLength sortby
 924                }
 925
 926                "year" {
 927                    #
 928                    # sort by year
 929                    #
 930                    puts "    <table border=\"0\" align=\"center\">"
 931                    puts "      <tr>"
 932                    puts "        <td align=\"center\">\n"
 933
 934                    foreach year [mysqlsel $db "SELECT DISTINCT year FROM $config(albums) ORDER BY year DESC" -list] {
 935                        if {$year == 0} then {unset year; continue}
 936
 937                        puts "          <table>"
 938                        puts "            <tr>"
 939                        puts "              <td>"
 940                        puts "                <div class=\"box\">"
 941                        puts "                  <div class=\"box-title\">$year</div>"
 942                        puts "                  <div class=\"box-data\">"
 943                        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
 944
 945                        foreach album [mysqlsel $db "SELECT albumID, artist, title
 946                                                            FROM $config(albums)
 947                                                            WHERE year = '$year'
 948                                                            ORDER BY artist, title" -list] {
 949
 950                            set albumID      [lindex $album 0]
 951                            set albumArtist  [lindex $album 1]
 952                            set albumTitle   [lindex $album 2]
 953
 954                            set infoLink [makeLink "$env(SCRIPT_NAME)?info=album[&]id=[set albumID]" "info"]
 955
 956                            puts "                      <tr>"
 957                            puts "                        <td class=\"box-data-contents\">$albumArtist - $albumTitle</td>"
 958                            puts "                        <td class=\"box-data-contents\" valign=\"top\">&nbsp;$infoLink</td>"
 959                            puts "                      </tr>"
 960
 961                            unset album albumArtist albumTitle albumID infoLink
 962                        }
 963
 964                        puts "                    </table>"
 965                        puts "                  </div>"
 966                        puts "                </div>"
 967                        puts "              </td>"
 968                        puts "            </tr>"
 969                        puts "          </table>\n"
 970
 971                        puts "          <p></p>\n"
 972                        unset year
 973                    }
 974
 975                    puts "        </td>"
 976                    puts "      </tr>"
 977                    puts "    </table>"
 978                }
 979
 980                default {
 981                    puts "Don't know how to sort by \"$val\""
 982                }
 983            }
 984            # disconnect from the database
 985            mysqlclose $db
 986            unset db
 987
 988            # emit the footer
 989            footer
 990        }
 991
 992
 993        "info" {
 994        #
 995        # info pages
 996        #
 997
 998            # connect to database
 999            db_connect
1000
1001            # the header
1002            header
1003
1004            # val == "album&id=130"
1005            set val [split $val "&"]
1006
1007            set type   [lindex $val 0]
1008            set id     [lindex [split [lindex $val 1] "="] 1]
1009
1010            switch $type {
1011
1012                "album" {
1013                    #
1014                    # display album info
1015                    #
1016
1017                    # if val has a highlight argument (like "album&id=130&highlight=1547"), the highlight value will contain the
1018                    # songID, if no such argument is given, it will be empty
1019                    set songID_to_highlight [lindex [split [lindex $val 2] "="] 1]
1020
1021                    # receive information from database
1022                    set albumID $id
1023                    set list [mysqlsel $db "SELECT artist, title, genre, year, playcount FROM $config(albums) WHERE albumID = '$albumID'" -flatlist]
1024
1025                    if {$list == ""} then {
1026                        # the specified ID doesn't exist
1027
1028                        puts "no album with ID \"$albumID\""
1029                        unset albumID list
1030                    } else {
1031
1032                        set albumArtist [lindex $list 0]
1033                        set albumTitle  [lindex $list 1]
1034                        set albumGenre  [lindex $list 2]
1035                        set albumYear   [lindex $list 3]
1036                        set albumPlayed [lindex $list 4]
1037                        set albumLength "0"
1038                        unset list
1039
1040
1041                        # if we have a compilation, display just the artist's name ('various')
1042                        # otherwise display a link to the artist's info page
1043
1044                        if {$albumArtist == "Various"} then {
1045                            set albumArtistLink "$albumArtist"
1046                        } else {
1047                            set artistID [mysqlsel $db "SELECT DISTINCT artistID FROM $config(songs) WHERE albumID = '$albumID'" -list]
1048                            set albumArtistLink [makeLink "$env(SCRIPT_NAME)?info=artist[&]id=[set artistID]" $albumArtist "show all songs by this artist"]
1049
1050                            unset artistID
1051                        }
1052
1053
1054                        # print first part of web page
1055                        puts "    <table border=\"0\" width=\"100%\">"
1056                        puts "      <tr>\n"
1057
1058                        # print album info
1059                        puts "        <td valign=\"top\" width=\"33%\" align=\"right\">"
1060                        puts "          <table border=\"0\">"
1061                        puts "            <tr>"
1062                        puts "              <td>"
1063                        puts "                <div class=\"box\">"
1064                        puts "                  <div class=\"box-title\">Info</div>"
1065                        puts "                  <div class=\"box-data\">"
1066                        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
1067                        puts "                      <tr>"
1068                        puts "                        <td class=\"box-data-contents\">Artist:</td>"
1069                        puts "                        <td class=\"box-data-contents\">$albumArtistLink</td>"
1070                        puts "                      </tr>"
1071                        puts "                      <tr>"
1072                        puts "                        <td class=\"box-data-contents\" valign=\"top\">Album:&nbsp;</td>"
1073                        puts "                        <td class=\"box-data-contents\">$albumTitle</td>"
1074                        puts "                      </tr>"
1075                        puts "                      <tr>"
1076                        puts "                        <td class=\"box-data-contents\">Genre:</td>"
1077                        puts "                        <td class=\"box-data-contents\">$albumGenre</td>"
1078                        puts "                      </tr>"
1079
1080                        if {$albumYear > 0} then {
1081                            puts "                      <tr>"
1082                            puts "                        <td class=\"box-data-contents\">Year:</td>"
1083                            puts "                        <td class=\"box-data-contents\">$albumYear</td>"
1084                            puts "                      </tr>"
1085                        }
1086
1087                        if {$use_playcount && $albumPlayed > 0} then {
1088                            puts "                      <tr>"
1089                            puts "                        <td class=\"box-data-contents\">played:</td>"
1090                            puts "                        <td class=\"box-data-contents\">$albumPlayed</td>"
1091                            puts "                      </tr>"
1092                        }
1093
1094                        puts "                    </table>"
1095                        puts "                  </div>"
1096                        puts "                </div>"
1097                        puts "              </td>"
1098                        puts "            </tr>"
1099                        puts "          </table>"
1100
1101                        # print play links
1102                        set playLink [makeLink "$env(SCRIPT_NAME)?play=album[&]id=[set albumID]"                "all" "play album"]
1103                        set randLink [makeLink "$env(SCRIPT_NAME)?play=album[&]id=[set albumID][&]style=random" "rand"]
1104                        set seleLink [makeLink "javascript:document.forms\[0\].submit();"                       "select"]
1105
1106                        puts "          <table border=\"0\">"
1107                        puts "            <tr>"
1108                        puts "              <td>"
1109                        puts "                <div class=\"box\">"
1110                        puts "                  <div class=\"box-title\">Play</div>"
1111                        puts "                  <div class=\"box-data\">"
1112                        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\">"
1113                        puts "                      <tr>"
1114                        if {$albumGenre != "Other"} then {
1115                            puts "                        <td class=\"box-data-contents\">$playLink&nbsp;</td>"
1116                        }
1117                        puts "                        <td class=\"box-data-contents\">$randLink&nbsp;</td>"
1118                        puts "                        <td class=\"box-data-contents\">$seleLink</td>"
1119                        puts "                      </tr>"
1120                        puts "                    </table>"
1121                        puts "                  </div>"
1122                        puts "                </div>"
1123                        puts "              </td>"
1124                        puts "            </tr>"
1125                        puts "          </table>"
1126
1127                        puts "        </td>\n"
1128
1129                        # print list of songs
1130
1131                        puts "        <td valign=\"top\" width=\"34%\" align=\"center\">"
1132                        puts "          <table border=\"0\">"
1133                        puts "            <tr>"
1134                        puts "              <td>"
1135                        puts "                <form action=\"$env(SCRIPT_NAME)?play=selection\" method=\"post\">"
1136                        puts "                  <div class=\"box\">"
1137                        puts "                    <div class=\"box-title\">Songs</div>"
1138                        puts "                    <div class=\"box-data\">"
1139                        puts "                      <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
1140
1141                        if {$albumGenre == "Other"} then {
1142                            # sort alphabtically
1143                            set sqlOrderClause "artist, song"
1144                        } else {
1145                            # sort by tracknr
1146                            set sqlOrderClause "$config(songs).tracknr"
1147                        }
1148
1149                        # now emit the songs
1150                        set row_count 0
1151                        foreach song [mysqlsel $db "SELECT $config(artists).name AS artist, $config(songs).title AS song, $config(songs).songID,
1152                                                           $config(songs).length, $config(songs).tracknr
1153                                                           FROM $config(songs), $config(artists)
1154                                                           WHERE albumID = '$albumID' AND $config(songs).artistID = $config(artists).artistID
1155                                                           ORDER BY $sqlOrderClause" -list] {
1156
1157                            set songArtist [lindex $song 0]
1158                            set songTitle  [lindex $song 1]
1159                            set songID     [lindex $song 2]
1160                            set songLength [lindex $song 3]
1161
1162                            # set background color
1163                            incr row_count
1164                            if {[expr $row_count % 2] != "0"} then {
1165                                set row_color $global_first_color
1166                            } else {
1167                                set row_color $global_second_color
1168                            }
1169
1170                            if {$songID == $songID_to_highlight} then {
1171                                set row_color $global_hilite_color
1172                            }
1173
1174                            # calculate complete runtime of the album
1175                            set albumLength [expr $albumLength + $songLength]
1176
1177                            # format length of song
1178                            set songLength [transform_secs $songLength]
1179
1180                            if {$songArtist != $albumArtist} then {set songTitle "$songArtist - $songTitle"}
1181
1182                            set songLink [makeLink "$env(SCRIPT_NAME)?play=song[&]id=$songID" "<font color=\"#000000\">$songTitle</font>" "play song"]
1183
1184                            puts "                        <tr>"
1185                            puts "                          <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color\"><input type=\"checkbox\" name=\"$songID\"></td>"
1186                            puts "                          <td class=\"box-data-contents\" style=\"background-color: $row_color\">$songLink</td>"
1187                            puts "                          <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color; text-align: right\">&nbsp;$songLength</td>"
1188                            puts "                        </tr>"
1189                            unset song songArtist songTitle songLength songID songLink row_color
1190                        }
1191                        unset sqlOrderClause row_count
1192
1193                        puts "                        <tr>"
1194                        puts "                          <td colspan=\"2\"></td>"
1195                        puts "                          <td class=\"box-data-contents\" colspan=\"1\" valign=\"top\" style=\"text-align:right\">[transform_secs $albumLength]</td>"
1196                        puts "                        </tr>"
1197                        puts "                      </table>"
1198
1199                        puts "                    </div>"
1200                        puts "                  </div>"
1201                        puts "                </form>"
1202                        puts "              </td>"
1203                        puts "            </tr>"
1204                        puts "          </table>"
1205                        puts "        </td>\n"
1206
1207                        # print cover
1208                        puts "        <td valign=\"top\" width=\"33%\" align=\"left\">"
1209                        puts "          <table border=\"0\">"
1210                        puts "            <tr>"
1211                        puts "              <td>"
1212                        puts "                <div class=\"box\">"
1213                        puts "                  <div class=\"box-title\">Cover</div>"
1214                        puts "                  <div class=\"box-data\" style=\"padding-left: 0px; padding-right: 0px; padding-bottom: 0px\">"
1215                        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\">"
1216                        puts "                      <tr>"
1217                        puts "                        <td class=\"box-data-contents\"><img src=\"$env(SCRIPT_NAME)?cover=$albumID\" title=\"$albumArtist - $albumTitle\" alt=\"$albumArtist - $albumTitle\"></td>"
1218                        puts "                      </tr>"
1219                        puts "                    </table>"
1220                        puts "                  </div>"
1221                        puts "                </div>"
1222                        puts "              </td>"
1223                        puts "            </tr>"
1224                        puts "          </table>"
1225                        puts "        </td>\n"
1226
1227                        # print rest of web page
1228                        puts "      </tr>"
1229                        puts "    </table>"
1230
1231                        unset seleLink playLink randLink
1232                        unset albumID albumArtist albumArtistLink albumTitle albumGenre albumYear albumPlayed albumLength
1233                    }
1234                    unset songID_to_highlight
1235                }
1236
1237                "artist" {
1238                    #
1239                    # display artist info
1240                    #
1241
1242                    # receive artist name
1243                    set artistID $id
1244                    set artistName [lindex [mysqlsel $db "SELECT name FROM $config(artists) WHERE artistID = '$artistID'" -flatlist] 0]
1245
1246                    if {$artistName == ""} then {
1247                        # the specified ID doesn't exist
1248
1249                        puts "no artist with ID \"$artistID\""
1250                        unset artistID artistName
1251                    } else {
1252
1253                        # print first part of web page
1254                        puts "    <table border=\"0\" width=\"100%\">"
1255                        puts "      <tr>\n"
1256
1257                        # print play links
1258
1259                        set playLink [makeLink "$env(SCRIPT_NAME)?play=artist[&]id=[set artistID]"                "all"   "play all songs sorted by album and tracknr"]
1260                        set sortLink [makeLink "$env(SCRIPT_NAME)?play=artist[&]id=[set artistID][&]style=sorted" "sort"  "play all songs sorted by title"]
1261                        set randLink [makeLink "$env(SCRIPT_NAME)?play=artist[&]id=[set artistID][&]style=random" "rand"  "play all songs in random order"]
1262                        set seleLink [makeLink "javascript:document.forms\[0\].submit();"                         "select"]
1263
1264
1265
1266                        puts "        <td width=\"33%\" valign=\"top\" align=\"right\">"
1267                        puts "          <table border=\"0\">"
1268                        puts "            <tr>"
1269                        puts "              <td>"
1270                        puts "                <div class=\"box\">"
1271                        puts "                  <div class=\"box-title\">Play</div>"
1272                        puts "                  <div class=\"box-data\">"
1273                        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\">"
1274                        puts "                      <tr>"
1275                        puts "                        <td class=\"box-data-contents\">$playLink&nbsp;</td>"
1276                        puts "                        <td class=\"box-data-contents\">$sortLink&nbsp;</td>"
1277                        puts "                        <td class=\"box-data-contents\">$randLink&nbsp;</td>"
1278                        puts "                        <td class=\"box-data-contents\">$seleLink</td>"
1279                        puts "                      </tr>"
1280                        puts "                    </table>"
1281                        puts "                  </div>"
1282                        puts "                </div>"
1283                        puts "              </td>"
1284                        puts "            </tr>"
1285                        puts "          </table>"
1286                        puts "        </td>\n"
1287
1288
1289                        # print songs/albums
1290                        puts "        <td width=\"34%\" valign=\"top\" align=\"center\">"
1291                        puts "          <table border=\"0\">"
1292                        puts "            <tr>"
1293                        puts "              <td>"
1294                        puts "                <form action=\"$env(SCRIPT_NAME)?play=selection\" method=\"post\">"
1295
1296                        set artistLength 0
1297                        set artistNrSongs 0
1298                        set artistNrAlbums 0
1299
1300                        # all albums from this artist
1301                        foreach album [mysqlsel $db "SELECT DISTINCT $config(songs).albumID, $config(albums).title AS title
1302                                                                     FROM $config(songs), $config(albums)
1303                                                                     WHERE artistID = '$artistID' AND $config(songs).albumID = $config(albums).albumID
1304                                                                     ORDER BY title" -list] {
1305
1306                            set albumID [lindex $album 0]
1307                            set albumTitle [lindex $album 1]
1308
1309                            set albumLink [makeLink "$env(SCRIPT_NAME)?info=album[&]id=$albumID" $albumTitle "show album info" "box-menu-link"]
1310
1311                            puts "                  <div class=\"box\">"
1312                            puts "                    <div class=\"box-title\">$albumLink</div>"
1313                            puts "                    <div class=\"box-data\">"
1314                            puts "                      <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" width=\"100%\">"
1315
1316                            # each song of those albums (of this artist)
1317                            set row_count 0
1318                            foreach song [mysqlsel $db "SELECT tracknr, title, length, songID
1319                                                               FROM $config(songs)
1320                                                               WHERE artistID = '$artistID' AND albumID = '$albumID'
1321                                                               ORDER BY tracknr" -list] {
1322
1323                                set trackNr    [lindex $song 0]
1324                                set songTitle  [lindex $song 1]
1325                                set songLength [lindex $song 2]
1326                                set songID     [lindex $song 3]
1327
1328                                # set background color
1329                                incr row_count
1330                                if {[expr $row_count % 2] != "0"} then {
1331                                    set row_color $global_first_color
1332                                } else {
1333                                    set row_color $global_second_color
1334                                }
1335
1336                                # calculate complete runtime of this artist
1337                                set artistLength [expr $artistLength + $songLength]
1338
1339                                # format length of song
1340                                set songLength [transform_secs $songLength]
1341
1342                                set songLink [makeLink "$env(SCRIPT_NAME)?play=song[&]id=$songID" "<font color=\"#000000\">$songTitle</font>" "play song"]
1343
1344                                puts "                        <tr>"
1345                                puts "                          <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color\" width=\"1\"><input type=\"checkbox\" name=\"$songID\"></td>"
1346                                puts "                          <td class=\"box-data-contents\" style=\"background-color: $row_color\">$songLink</td>"
1347                                puts "                          <td valign=\"top\" class=\"box-data-contents\" style=\"background-color: $row_color; text-align:right\">&nbsp;$songLength</td>"
1348                                puts "                        </tr>"
1349
1350                                set artistNrSongs [expr $artistNrSongs + 1]
1351
1352                                unset song trackNr songTitle songLength songID songLink row_color
1353                            }
1354                            unset row_count
1355
1356                            puts "                      </table>"
1357                            puts "                    </div>"
1358                            puts "                  </div>"
1359                            puts "                  <p></p>"
1360
1361                            set artistNrAlbums [expr $artistNrAlbums + 1]
1362
1363                            unset album albumID albumTitle albumLink
1364                        }
1365
1366                        puts "                </form>"
1367                        puts "              </td>"
1368                        puts "            </tr>"
1369                        puts "          </table>"
1370                        puts "        </td>\n"
1371
1372                        # stats
1373                        puts "        <td width=\"33%\" valign=\"top\" align=\"left\">"
1374                        puts "          <table border=\"0\">"
1375                        puts "            <tr>"
1376                        puts "              <td>"
1377                        puts "                <div class=\"box\">"
1378                        puts "                  <div class=\"box-title\">$artistName</div>"
1379                        puts "                  <div class=\"box-data\">"
1380                        puts "                    <table border=\"0\" cellpadding=\"0\" cellspacing=\"0\" align=\"center\">"
1381                        puts "                      <tr>"
1382                        puts "                        <td class=\"box-data-contents\">Nr of Songs&nbsp;</td>"
1383                        puts "                        <td class=\"box-data-contents\">$artistNrSongs</td>"
1384                        puts "                      </tr>"
1385                        puts "                      <tr>"
1386                        puts "                        <td class=\"box-data-contents\">Nr of Albums:&nbsp;</td>"
1387                        puts "                        <td class=\"box-data-contents\">$artistNrAlbums</td>"
1388                        puts "                      </tr>"
1389                        puts "                      <tr>"
1390                        puts "                        <td class=\"box-data-contents\">Total Runtime:&nbsp;</td>"
1391                        puts "                        <td class=\"box-data-contents\">[transform_secs $artistLength]</td>"
1392                        puts "                      </tr>"
1393                        puts "                    </table>"
1394                        puts "                  </div>"
1395                        puts "                </div>"
1396                        puts "              </td>"
1397                        puts "            </tr>"
1398                        puts "          </table>"
1399                        puts "        </td>\n"
1400
1401                        # print rest of web page
1402                        puts "      </tr>"
1403                        puts "    </table>"
1404
1405
1406                        unset artistID artistName
1407                        unset seleLink playLink sortLink randLink artistLength artistNrSongs artistNrAlbums
1408                    }
1409                }
1410
1411                default {
1412                    puts "Don't have information of type \"$type\""
1413                }
1414
1415            }
1416
1417            # disconnect from the database
1418            mysqlclose $db
1419            unset db
1420
1421            # emit the footer
1422            footer
1423
1424            unset type id
1425        }
1426
1427
1428        "cover" {
1429        #
1430        # returns the image for this album
1431        #
1432
1433            # connect to database
1434            db_connect
1435
1436            # gets fs path for the album
1437            set path [mysqlsel $db "SELECT directory FROM $config(albums) WHERE albumID = '$val'" -list]
1438
1439            # check whether there's an cover for that album
1440            set coverReturned no
1441            foreach ext {png jpg jpeg gif} {
1442                set file [file join $path "cover.$ext"]
1443
1444                if {[file exists $file] && [file readable $file] && !$coverReturned} then {
1445                    #
1446                    # cover was found, now display it
1447                    #
1448
1449                    if {$ext == "jpg"} then {set ext "jpeg"}
1450
1451                    # read image
1452                    set img [open $file r]
1453
1454                    # emit image
1455
1456                    puts "Content-type: image/[set ext]\n"
1457                    fconfigure $img   -translation binary -encoding binary
1458                    fconfigure stdout -translation binary -encoding binary
1459                    fcopy $img stdout
1460
1461                    # close image
1462                    close $img
1463                    unset img
1464
1465                    # prevent further covers from being emitted
1466                    set coverReturned yes
1467
1468                    unset file ext
1469                    break
1470                }
1471                unset file ext
1472            }
1473            unset path
1474
1475
1476            if {!$coverReturned} then {
1477                # no cover was returned -> no one was available -> return a standard image
1478
1479                # empty image
1480                # set img "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABmJLR0QA/wD/AP+gvaeTAAAACXBIWXMAAAsSAAALEgHS3X78AAAADUlEQVR42mNgYGBgAAAABQABeqhXUAAAAABJRU5ErkJggg=="
1481                # puts "Content-type: image/png\n"
1482
1483                # 'no image available' image
1484                set img "R0lGODlhWgBaALMAAMrKynJycqOjo4qKirq6utnZ2ZaWlvX19ebm5v///wAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAABaAFoAAAT/MMlJq7046827/2AojmRpnmiqrmzrvnAsz3Rt33iu73zv/8CgcEgsGo/IpHLJbDqf0KjUiKhar9isdsvtYl3esHjcBSOQVTP63Eof3Sx4Ua6iD+0ofFBv4v/8JHQIBQcSBWwXgzGAI3QEAQQSkBmPi4grjgEBZ5MJAAQAFIpVn4MEbAcEpxIIoG6fBRaMIpkGBgmTj7oTlQaQAwECAgGFwY+EAQaPtwQDj6Gil3XSjwgBAJAH157EEr3MAQkFAYSE1qDdBAbaBAUCAxWzIZkJzZDjsfje4bb14fgHDAwYpipcPQPjBti6FU0NhUraJuUy6C9Bv0r4IEKyJgDbrW0A/6BNkAeCHrdIHrftswhOHDlrCicBEKaOmy+RrKSlkHeA0LxYrAyEMiBAQs9C8XTmURrjUTCkGUh+kIqDagerNrBu0Doy1lELXycc4sA1KtMPGMlZ0Dehk4ayGPAUAHXga5pWqxTpw3tm3CdonWAlOlvCji6FuCI5Ozcs1b+XkBon3JXLmcqGbXRWOTAsgTBrVWIdS3tINDm/nuFlu/dOFuFAOj36OgkPwYCB5Ejbxl1AX6V7ART2S+qQwkRJkwTcwjb3sbvlp7cJTIwrVEjXxScME9ht2BnZ10iDB5AQWCiZyS6PfN1I6di4hO3YCUs885rsROAOvm//DXta/+2gHzx2/uF3R4AlIZjDgEmR4eCDZWQG4YQUKjjFhRhmqOGGHHbo4YcghijiiCSWaOKJKKao4oostujiizCyGAEAOw=="
1485                puts "Content-type: image/gif\n"
1486
1487                puts -nonewline [::base64::decode $img]
1488
1489                unset img
1490            }
1491
1492            unset coverReturned
1493
1494            # disconnect from the database
1495            mysqlclose $db
1496            unset db
1497
1498        }
1499
1500
1501        "play" {
1502        #
1503        # a album/song was chosen, create a playlist und redirect the user to it
1504        #
1505
1506            # val == "album&id=130&style=sorted"
1507
1508            set val [split $val "&"]                         ;# {album id=130 style=sorted}
1509
1510            set type  [lindex $val 0]                        ;# album
1511            set id    [lindex [split [lindex $val 1] "="] 1] ;# 130
1512            set style [lindex [split [lindex $val 2] "="] 1] ;# sorted
1513
1514
1515            #
1516            # check whether style is allowed
1517            #
1518
1519            switch $type {
1520
1521                "album" {
1522                    if {$style != "random" && $style != ""} then {
1523                        header
1524                        puts "style \"$style\" not allowed here"
1525                        footer
1526                        break
1527                    }
1528
1529                }
1530
1531                "artist" {
1532                    if {$style != "random" && $style != "sorted" && $style != ""} then {
1533                        header
1534                        puts "style \"$style\" not allowed here"
1535                        footer
1536                        break
1537                    }
1538                }
1539
1540                default {
1541                    if {$style != ""} then {
1542                        header
1543                        puts "style \"$style\" not allowed here"
1544                        footer
1545                        break
1546                    }
1547                }
1548            }
1549
1550            # connect to database
1551            db_connect
1552
1553            switch $type {
1554
1555                "song" {
1556                    # a single song was chosen
1557
1558                    set list [mysqlsel $db "SELECT $config(songs).songID, $config(artists).name, $config(songs).title, $config(songs).length,
1559                                                   $config(songs).file
1560                                                   FROM $config(songs), $config(artists)
1561                                                   WHERE songID = '$id' AND $config(songs).artistID = $config(artists).artistID" -flatlist]
1562
1563                    if {$list == ""} then {
1564                        header
1565                        puts "no song with ID \"$id\""
1566                        footer
1567                    } else {
1568
1569                        set songID [lindex $list 0]
1570                        set artist [lindex $list 1]
1571                        set title  [lindex $list 2]
1572                        set length [lindex $list 3]
1573                        set file   [lindex $list 4]
1574
1575                        send_playlist [list [list $songID $file $title $artist $length]]
1576
1577                        # increase playcount
1578                        if {$use_playcount && !$test_mode} then {
1579                            set pc [mysqlsel $db "SELECT playcount FROM $config(songs) WHERE songID = '$songID'" -list]
1580                            incr pc
1581                            mysqlexec $db "UPDATE $config(songs) SET playcount='$pc' WHERE songID = '$songID'"
1582                            unset pc
1583                        }
1584
1585                        unset songID artist title length file
1586
1587                    }
1588
1589                    unset list
1590                }
1591
1592                "album" {
1593                    # an album was chosen
1594
1595                    if {[mysqlsel $db "SELECT albumID FROM $config(albums) WHERE albumID = '$id'"] == 0} then {
1596                        # album does not exist
1597                        header
1598                        puts "no album with ID \"$id\""
1599                        footer
1600                    } else {
1601
1602                        # get all songs
1603                        set listOut [mysqlsel $db "SELECT $config(songs).songID, $config(songs).file, $config(songs).title, $config(artists).name,
1604                                                          $config(songs).length
1605                                                          FROM $config(songs), $config(artists)
1606                                                          WHERE $config(songs).albumID = '$id' AND $config(artists).artistID = $config(songs).artistID
1607                                                          ORDER BY $config(songs).tracknr" -list]
1608
1609                        # randomize list?
1610                        if {$style == "random"} then {set listOut [lrandomize $listOut]}
1611
1612                        # emit playlist
1613                        send_playlist $listOut
1614                        unset listOut
1615
1616                        # increase playcount
1617                        if {$use_playcount && !$test_mode} then {
1618                            set pc [mysqlsel $db "SELECT playcount FROM $config(albums) WHERE albumID = '$id'" -list]
1619                            incr pc
1620                            mysqlexec $db "UPDATE $config(albums) SET playcount='$pc' WHERE albumID = '$id'"
1621                            unset pc
1622                        }
1623
1624                        # update last_played value
1625                        if {$use_last_played && !$test_mode} then {
1626                            set last_played [clock format [clock seconds] -format "%Y-%m-%d %T"]
1627                            mysqlexec $db "UPDATE $config(albums) SET last_played='$last_played' WHERE albumID = '$id'"
1628                            unset last_played
1629                        }
1630
1631
1632                    }
1633                }
1634
1635                "artist" {
1636                    # play all songs of an artist
1637
1638                    if {[mysqlsel $db "SELECT artistID FROM $config(songs) WHERE artistID = '$id'"] == 0} then {
1639                        # artist does not exist
1640                        header
1641                        puts "no artist with ID \"$id\""
1642                        footer
1643                    } else {
1644
1645                        if {$style == "sorted"} then {
1646                            # sort all songs by title
1647                            set listOut [mysqlsel $db "SELECT $config(songs).songID, $config(songs).file, $config(songs).title, $config(artists).name,
1648                                                              $config(songs).length
1649                                                              FROM $config(songs), $config(artists)
1650                                                              WHERE $config(songs).artistID = '$id'
1651                                                                    AND $config(songs).artistID = $config(artists).artistID
1652                                                              ORDER BY $config(songs).title" -list]
1653                        } else {
1654                            # sort by albumName, then by tracknr
1655                            set listOut [mysqlsel $db "SELECT $config(songs).songID, $config(songs).file, $config(songs).title, $config(artists).name,
1656                                                              $config(songs).length
1657                                                              FROM $config(songs), $config(artists), $config(albums)
1658                                                              WHERE $config(songs).artistID = '$id'
1659                                                                    AND $config(songs).artistID = $config(artists).artistID
1660                                                                    AND $config(songs).albumID = $config(albums).albumID
1661                                                              ORDER BY $config(albums).title, $config(songs).tracknr" -list]
1662                        }
1663
1664                        # randomize list?
1665                        if {$style == "random"} then {set listOut [lrandomize $listOut]}
1666
1667                        # emit playlist
1668                        send_playlist $listOut
1669
1670                        unset listOut
1671                    }
1672                }
1673
1674                "selection" {
1675                    # plays a selection of songs
1676
1677                    if {![info exists env(CONTENT_LENGTH)] || $env(CONTENT_LENGTH) == 0} then {
1678                        header
1679                        puts "no selection was made"
1680                        footer
1681                    } else {
1682                        # the songs were sent via the POST method
1683                        # thus the list has to be read from standard input
1684                        set songs [read stdin $env(CONTENT_LENGTH)]             ;# songs == 1210=on&1214=on&1225=on
1685                        regsub -all "=on" $songs "" songs                       ;# songs == 1210&1214&1225
1686
1687                        # create playlist
1688                        set songList {}
1689                        foreach songID [split $songs "&"] {
1690                            lappend songList [mysqlsel $db "SELECT $config(songs).songID, $config(songs).file, $config(songs).title,
1691                                                                   $config(artists).name, $config(songs).length
1692                                                                   FROM $config(songs), $config(artists)
1693                                                                   WHERE songID = '$songID'
1694                                                                         AND $config(songs).artistID = $config(artists).artistID" -flatlist]
1695                            unset songID
1696                        }
1697                        unset songs
1698
1699
1700                        # redirect to playlist
1701                        send_playlist $songList
1702
1703                        unset songList
1704                    }
1705                }
1706
1707                default {
1708                    header
1709                    puts "Don't know how to play \"$type\""
1710                    footer
1711                }
1712            }
1713
1714            # disconnect from the database
1715            mysqlclose $db
1716            unset db
1717
1718            unset type id style
1719        }
1720
1721
1722        "stream" {
1723        #
1724        # stream the requested file
1725        #
1726
1727        #
1728        # the functionality to stream an ogg vorbis audio file is here, but somehow, it isn't liked by the audio
1729        # programs, so only mp3 files will arrive here, waiting to be streamed
1730        #
1731
1732            # connect to database
1733            db_connect
1734
1735            # get file name
1736            set file [mysqlsel $db "select file from $config(songs) where songID = '$val'" -list]
1737
1738            # disconnect from the database
1739            mysqlclose $db
1740
1741            # send mime type
1742            if {[file extension $file] == ".mp3"} then {
1743                puts "Content-Type: application/octet-stream"
1744            } else {
1745                puts "Content-Type: application/x-ogg"
1746            }
1747
1748            # send file name
1749            puts "Content-Disposition: attachment; filename=[file tail $file]"
1750
1751            # send file size
1752            puts "Content-Length: [file size $file]\n"
1753
1754            # send file
1755            set fd [open $file r]
1756
1757            fconfigure $fd -translation binary -encoding binary
1758            fconfigure stdout -translation binary -encoding binary
1759
1760            fcopy $fd stdout
1761
1762            close $fd
1763
1764            unset file fd
1765
1766        }
1767
1768
1769        default {
1770            header
1771            puts "Unknown command \"$cmd\""
1772            footer
1773        }
1774    }
1775
1776    unset query cmd val
1777} err
1778if {$err != ""} then {puts "Content-Type: text/html\n\n<hr>\n<h1>Script Error</h1>\n<pre>\n$errorInfo\n</pre>\n<hr>"}
1779
1780
1781array unset config
1782unset test_mode use_playcount playcount_nr use_random_album use_last_played err
1783unset global_first_color global_second_color global_hilite_color
1784
1785exit 0