lastfmLink = 'http://www.last.fm/music/'.
urlencode($this->artist).'/_/'.urlencode($this->title);
$this->albumLastfmLink = ($this->album) ? 'http://www.last.fm/music/'.
urlencode($this->artist).'/'.
urlencode($this->album) : '';
$this->hash = $this->songHash();
if (!$this->id)
$this->id = $this->registerSong();
if (!$this->albumId)
$this->albumId = ($this->album) ? $this->registerAlbum() : 0;
}
/*
* songHash
*
* calculate a uniquely identifying hash for a artist/title/length combo
*
*/
private function songHash()
{
return md5($this->artist.$this->title.$this->length);
}
public function registerPlay($user)
{
if (!$this->id) die('no id to register');
Util::registerUserIp($user, $_SERVER['REMOTE_ADDR']);
$sql = "
INSERT INTO plays
SET song_id = ".$this->id.",
album_id = ".$this->albumId.",
user = '".mysql_real_escape_string($user)."'";
mysql_query($sql) or Util::log($sql.mysql_error());
return mysql_insert_id();
}
/*
* registerSong
*
* makes sure the song is registered in the database
*
* @return song id
*/
private function registerSong()
{
$songId = $this->songId();
if ($songId)
return $songId;
else
return $this->addSong();
}
/*
* registerAlbum
*
* makes sure the album is registered in the database
*
* @return album id
*/
private function registerAlbum()
{
$albumId = $this->albumId();
if ($albumId)
return $albumId;
else
return $this->addAlbum();
}
/*
* addAlbum
*
* adds the album to the database
*
*/
private function addAlbum()
{
$sql = "
INSERT INTO albums
SET artist = '".mysql_real_escape_string($this->artist)."',
title = '".mysql_real_escape_string($this->album)."',
created = NOW()";
mysql_query($sql) or Util::log($sql.mysql_error());
return mysql_insert_id();
}
/*
* addSong
*
* adds the song to the database
*
*/
private function addSong()
{
$sql = "
INSERT INTO songs
SET artist = '".mysql_real_escape_string($this->artist)."',
title = '".mysql_real_escape_string($this->title)."',
length = ".mysql_real_escape_string($this->length).",
songhash = '".mysql_real_escape_string($this->hash)."',
created = NOW()";
mysql_query($sql) or Util::log($sql.mysql_error());
return mysql_insert_id();
}
/* songId - returns the id of a song based on the song hash
* if song is not found, returns 0
*/
private function songId()
{
$sql = "
SELECT id FROM songs
WHERE songhash = '".mysql_real_escape_string($this->hash)."'";
$result = mysql_query($sql) or die($sql.mysql_error());
$row = mysql_fetch_assoc($result);
return ($row['id']) ? $row['id'] : 0;
}
// albumId() - returns the id of an album based on the album
private function albumId()
{
$sql = "
SELECT id
FROM albums
WHERE title = '".mysql_real_escape_string($this->album)."'
AND artist = '".mysql_real_escape_string($this->artist)."'";
$result = mysql_query($sql) or Util::log($sql.mysql_error());
$row = mysql_fetch_assoc($result);
return ($row['id']) ? $row['id'] : 0;
}
}
Class User
{
public $user;
public $lastTracks = array();
public $lastNotes = array();
public $numPlayed;
public function __construct($user, $numTracks = 15, $numNotes = 10,
$tag = '')
{
$this->user = $user;
if ($numTracks > 0) $this->getLastTracks($numTracks);
if ($numNotes > 0) $this->getLastNotes($numNotes, $tag);
}
public function numPlayed()
{
$sql = "
SELECT COUNT(*) as numPlayed
FROM plays
WHERE user = '".mysql_real_escape_string($this->user)."'";
$result = mysql_query($sql) or Util::log($sql.mysql_error());
$row = mysql_fetch_assoc($result);
$this->numPlayed = $row['numPlayed'];
return $this->numPlayed;
}
public function getLastNotes($numNotes, $tag = '', $lastId = 0)
{
$tagSql = ($tag) ? "AND tags LIKE '%$tag%'\n" : '';
$lastIdSql = ($lastId) ? "AND notes.id < $lastId\n" : '';
$sql = "
SELECT notes.id as id, description, tags, songs.title as title, songs.artist as artist,
notes.created, notes.user as user, songs.id as songId
FROM songs, notes
WHERE songs.id = song_id
AND user = '".mysql_real_escape_string($this->user)."'
$tagSql
$lastIdSql
ORDER BY notes.updated DESC
LIMIT 0, $numNotes";
$result = mysql_query($sql) or Util::log($sql.mysql_error());
while ($row = mysql_fetch_assoc($result))
{
$note = new Note();
$note->id = $row['id'];
$note->description = $row['description'];
$note->song->id = $row['songId'];
$note->song->title = $row['title'];
$note->song->artist = $row['artist'];
$note->song->initialize();
$note->user = $row['user'];
$note->tags = $row['tags'];
$this->lastNotes[] = $note;
}
}
private function getLastTracks($numTracks)
{
$sql = "
SELECT songs.title as title, songs.artist as artist, plays.time as time,
albums.title as album, songs.length as length, songs.id as songid,
albums.id as albumid
FROM songs, albums, plays
WHERE song_id = songs.id
AND album_id = albums.id
AND plays.user = '".mysql_real_escape_string($this->user)."'
ORDER BY plays.id DESC
LIMIT $numTracks";
$result = mysql_query($sql) or Util::log($sql.mysql_error());
while ($row = mysql_fetch_assoc($result))
{
$song = new Song();
$song->artist = $row['artist'];
$song->title = $row['title'];
$song->album = $row['album'];
$song->albumId = $row['albumid'];
$song->id = $row['songid'];
$song->initialize();
$this->lastTracks[] = array('time' => $row['time'],
'song' => $song);
}
}
}
Class Note
{
public $description;
public $tags;
public $user;
public $id;
public $topTags;
public function __construct()
{
$this->song = new Song();
}
//find data about the note from the old databunk
public function populateNoteData()
{
if (!is_numeric($this->song->id)) die('numeric song id needed');
$sql = "
SELECT notes.id as id, notes.description as description, notes.tags as tags,
songs.artist as artist, songs.title as title, songs.id as song_id
FROM notes, songs
WHERE notes.song_id = songs.id
AND user = '".mysql_real_escape_string($this->user)."'
AND song_id = {$this->song->id}";
$result = mysql_query($sql) or Util::log($sql.mysql_error());
$note = mysql_fetch_assoc($result);
if ($note)
{
$this->description = $note['description'];
$this->tags = $note['tags'];
$this->id = $note['id'];
$this->song->id = $note['song_id'];
$this->song->title = $note['title'];
$this->song->artist = $note['artist'];
$this->song->initialize();
return true;
}
else //can't find note? well we can still populate with artist info
{
$sql = "
SELECT artist, title
FROM songs
WHERE id = {$this->song->id}";
$result = mysql_query($sql) or Util::log($sql.mysql_error());
$song = mysql_fetch_assoc($result);
$this->song->title = $song['title'];
$this->song->artist = $song['artist'];
return false;
}
}
public function fetchTopTags()
{
$xml = simplexml_load_file('http://ws.audioscrobbler.com/1.0/artist/'.
urlencode($this->song->artist).'/toptags.xml');
foreach ($xml->tag as $tag)
{
$this->topTags .= strtolower(ereg_replace('[\ ]','_',$tag->name)).' ';
}
}
//a nice html view of the note
public function renderHtml()
{
return ($this->tags or $this->description) ? "
" : '';
}
private function linkTags()
{
$tagString = '';
foreach (explode(' ',$this->tags) as $tag)
{
$tagString .= "".
htmlspecialchars($tag, ENT_NOQUOTES, 'UTF-8').
" ";
}
return $tagString;
}
// a nice xml view of the note
public function renderRss()
{
return ($this->tags or $this->description) ? "
{$this->id}".htmlspecialchars($this->song->artist.' - '.$this->song->title,
ENT_NOQUOTES, 'UTF-8')."
".htmlspecialchars(ereg_replace("\n"," ",
$this->description), ENT_NOQUOTES,
'UTF-8').
"".htmlspecialchars($this->tags, ENT_NOQUOTES, 'UTF-8').
"" : '';
}
public function update()
{
if (!is_numeric($this->song->id)) die('song id must be numerical');
if (!$this->id)
{
$sql = "
INSERT INTO notes
SET song_id = {$this->song->id}, created = NOW(),
description = '".mysql_real_escape_string($this->description)."',
tags = '".mysql_real_escape_string($this->tags)."',
user = '".mysql_real_escape_string($this->user)."'";
mysql_query($sql) or Util::log($sql.mysql_error());
$this->id = mysql_insert_id();
}
else
{
$sql = "
UPDATE notes
SET description = '".mysql_real_escape_string($this->description)."',
tags = '".mysql_real_escape_string($this->tags)."',
updated = NOW()
WHERE song_id = {$this->song->id}
AND user = '".mysql_real_escape_string($this->user)."'";
mysql_query($sql) or Util::log($sql.mysql_error());
}
}
}
/* simple utilities **/
Class Util
{
public static function alphabetize($in_string)
{
$words = explode(' ',$in_string);
asort($words);
return implode(' ',$words);
}
public static function stringForJavascript($in_string)
{
$str = ereg_replace("[\r\n]", " \\n\\\n", $in_string);
$str = ereg_replace('"', '\\"', $str);
return $str;
}
/* log - generic database based log function
*
* @param: string message
*
*/
public static function log($message)
{
$sql = "
INSERT INTO log
SET message = '".mysql_real_escape_string($message)."'";
mysql_query($sql) or die($sql.mysql_error());
}
/* sendToHost
* ~~~~~~~~~~
* Params:
* $host - Just the hostname. No http:// or
/path/to/file.html portions
* $method - get or post, case-insensitive
* $path - The /path/to/file.html part
* $data - The query string, without initial question mark
* $useragent - If true, 'MFM' will be sent as
the User-Agent (optional)
*
* Examples:
* sendToHost('www.google.com','get','/search','q=php_imlib');
* sendToHost('www.example.com','post','/some_script.cgi',
* 'param=First+Param&second=Second+param');
*/
public static function sendToHost($host, $method, $path, $data)
{
// Supply a default method of GET if the one passed was empty
if (empty($method))
{
$method = 'GET';
}
$method = strtoupper($method);
$fp = fsockopen($host, 80);
if ($method == 'GET')
{
$path .= '?' . $data;
}
fputs($fp, "$method $path HTTP/1.1\r\n");
fputs($fp, "Host: $host\r\n");
fputs($fp,"Content-type: application/x-www-form-urlencoded\r\n");
fputs($fp, "Content-length: " . strlen($data) . "\r\n");
fputs($fp, "User-Agent: MFM\r\n");
fputs($fp, "Connection: close\r\n\r\n");
if ($method == 'POST')
{
fputs($fp, $data);
}
while (!feof($fp))
{
$buf .= fgets($fp,128);
}
fclose($fp);
return $buf;
}
public static function registerUserIp($user, $ip)
{
$sql = "
SELECT COUNT(*) as ipRegistered
FROM ips
WHERE username = '".mysql_real_escape_string($user)."'
AND ip = INET_ATON('".mysql_real_escape_string($ip)."')";
$result = mysql_query($sql) or Util::log($sql.mysql_error());
$row = mysql_fetch_assoc($result);
if ($row['ipRegistered'] == 0)
{
$sql = "
INSERT INTO ips
SET username = '".mysql_real_escape_string($user)."',
ip = INET_ATON('".mysql_real_escape_string($ip)."')";
mysql_query($sql) or Util::log($sql.mysql_error());
}
}
}
/* a visitor is someone who comes around to say hi */
Class Visitor
{
public $ip;
public $editDomains;
public function __construct($ip)
{
$this->ip = $ip;
$this->registerEditDomains();
}
private function registerEditDomains()
{
$sql = "
SELECT username as name
FROM ips
WHERE ip = INET_ATON('".mysql_real_escape_string($this->ip)."')";
$result = mysql_query($sql) or Util::log($sql.mysql_error());
while ($row = mysql_fetch_assoc($result))
{
$this->editDomains[] = $row['name'];
}
}
}
/* renders various types of content */
Class Renderer
{
public $content;
//renders track content as html
public function lastTracksHtml()
{
foreach ($this->content as $track)
{
echo <<♪ lastfmLink}'>{$track['song']->artist} - {$track['song']->title}
EOTRACK;
if ($track['song']->album)
echo <<albumLastfmLink}'>
{$track['song']->album}»
id});'>
tag this song
EOALBUM;
}
}
public function lastTracksRss()
{
foreach ($this->content as $track)
{
$safeTrackTitle = htmlspecialchars($track['song']->artist.' - '.$track['song']->title,
ENT_NOQUOTES, 'UTF-8');
$safeAlbumTitle = htmlspecialchars($track['song']->album, ENT_NOQUOTES,'UTF-8');
$safeAlbumLink = htmlspecialchars($track['song']->albumLastfmLink,ENT_QUOTES,'UTF-8');
$safeDescription = ($track['song']->album) ?
"Off the album $safeAlbumTitle" :
'';
$safeTrackLink = htmlspecialchars($track['song']->lastfmLink, ENT_NOQUOTES, 'UTF-8');
echo <<{$track['time']}{$safeTrackTitle}{$safeDescription}
{$safeTrackLink}
$safeAlbumTitle
EOTRACK;
}
}
//renders notes content as html
public function lastNotesHtml()
{
foreach ($this->content as $note)
{
echo $note->renderHtml();
}
}
public function lastNotesRss()
{
foreach ($this->content as $note)
{
echo $note->renderRss();
}
}
public static function renderRssHeader($in_title)
{
$safeTitle = htmlspecialchars($in_title, ENT_NOQUOTES, 'UTF-8');
header('Content-Type: text/xml');
echo <<{$safeTitle}
EORSSHEAD;
}
public static function renderHtmlHeader()
{
echo <<musika
EOHEAD;
}
public static function renderHtmlFooter()
{
echo <<
EOFOOT;
}
public static function renderRssFooter()
{
echo <<
EORSSFOOT;
}
private function renderStyle()
{
}
public static function renderNotesRssForUser($username, $count, $tag)
{
if (!is_numeric($count)) die('count must be a number');
$notesRenderer = new Renderer();
$user = new User($username, 0, $count, $tag);
$notesRenderer->content = $user->lastNotes;
Renderer::renderRssHeader('Last tagged tracks for '.$username);
$notesRenderer->lastNotesRss();
Renderer::renderRssFooter();
}
public static function renderTracksRssForUser($username, $count)
{
if (!is_numeric($count)) die('count must be a number');
$tracksRenderer = new Renderer();
$user = new User($username, $count, 0);
$tracksRenderer->content = $user->lastTracks;
Renderer::renderRssHeader('Last played tracks for '.$username);
$tracksRenderer->lastTracksRss();
Renderer::renderRssFooter();
}
public static function renderLatestNotesForUser($username, $tag,
$lastNoteId)
{
$renderer = new Renderer();
$user = new User($username, 0, 0);
$user->getLastNotes(15, $tag, $lastNoteId);
$renderer->content = $user->lastNotes;
$renderer->lastNotesHtml();
}
public static function renderLatestTracksForUser($username)
{
$tracksRenderer = new Renderer();
$user = new User($username);
$tracksRenderer->content = $user->lastTracks;
$tracksRenderer->lastTracksHtml();
}
public static function renderHtmlForUser($username, $tag = '')
{
$tracksRenderer = new Renderer();
$notesRenderer = new Renderer();
$user = new User($username, 10, 10, $tag);
$visitor = new Visitor($_SERVER['REMOTE_ADDR']);
$tracksRenderer->content = $user->lastTracks;
$notesRenderer->content = $user->lastNotes;
$isEditable = in_array($username,$visitor->editDomains);
// begin rendering page
Renderer::renderHtmlHeader();
Renderer::renderStyle();
echo "
";
Renderer::renderHtmlFooter();
}
}
//a web page, with a title and some content
Class Page
{
protected $title;
protected $content;
public function render()
{
echo <<{$this->title}
EOHEAD;
echo $this->content;
echo <<
EOFOOT;
}
}
Class Homepage extends Page
{
public function __construct()
{
$this->title = "musika - it's my music";
$this->content = <<Musika - It's my music
Musika is a proxy for last.fm -
it offers a different web interface and rss feeds that put you in control of your own
music.