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) ? "
edit

{$this->song->artist} - {$this->song->title}

".ereg_replace("\n","
",$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 "

musika / $username

"; //render tracks echo "

".$user->numPlayed()." Played RSS

"; $tracksRenderer->lastTracksHtml(); echo "
"; //render notes echo "

$tag Tagged Tracks RSS

cancel
"; $notesRenderer->lastNotesHtml(); echo "

↓ More

"; 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.

Sample page: alexbosworth's tracks

Downloads

Musika Plugin for OSX + iTunes src

EOHEAD; } }