1<?php
2
3/**
4 * Class representing an audio-CD with CDDB information
5 *
6 * @author Keith Palmer <Keith@UglySlug.com>
7 * @category Net
8 * @package Net_CDDB
9 * @license http://www.opensource.org/licenses/bsd-license.php BSD License
10 */
11
12/**
13 * Require CDDB class, we need the constants
14 */
15require_once 'Net/CDDB.php';
16
17/**
18 * Require the CDDB Track class, so we can assign tracks to discs...
19 */
20require_once 'Net/CDDB/Track.php';
21
22/**
23 * Class representing an audio-CD with CDDB information
24 *
25 * This class has methods to access all of the information a CDDB server has
26 * available about a specific audio CD.
27 *
28 * @package Net_CDDB
29 */
30class Net_CDDB_Disc
31{
32	/**
33	 * Array of tracks for the disc, {@link Net_CDDB_Track} objects
34	 *
35	 * @var array
36	 * @access protected
37	 */
38	var $_tracks;
39
40	/**
41	 * Artist name
42	 *
43	 * @var string
44	 * @access protected
45	 */
46	var $_artist;
47
48	/**
49	 * Disc/audio CD title
50	 *
51	 * @var string
52	 * @access protected
53	 */
54	var $_title;
55
56	/**
57	 * 8-char discid field
58	 *
59	 * @var string
60	 * @access protected
61	 */
62	var $_discid;
63
64	/**
65	 * CDDB category string (the directory the disc record is stored in)
66	 *
67	 * @var string
68	 * @access protected
69	 */
70	var $_category;
71
72	/**
73	 * CDDB genre string
74	 *
75	 * @var string
76	 * @access protected
77	 */
78	var $_genre;
79
80	/**
81	 * Year audio CD was published
82	 *
83	 * @var integer
84	 * @access protected
85	 */
86	var $_year;
87
88	/**
89	 * Length in seconds of the audio-CD
90	 *
91	 * @var integer
92	 * @access protected
93	 */
94	var $_length;
95
96	/**
97	 * Playorder of tracks ( optional CDDB attribute )
98	 *
99	 * @var string
100	 * @access protected
101	 */
102	var $_playorder;
103
104	/**
105	 * CDDB revision attribute
106	 *
107	 * @var integer
108	 * @access protected
109	 */
110	var $_revision;
111
112	/**
113	 * The program which submitted this record
114	 *
115	 * @var string
116	 * @access protected
117	 */
118	var $_submitted_via;
119
120	/**
121	 * The program which processing the record submission
122	 *
123	 * @var string
124	 * @access protected
125	 */
126	var $_processed_by;
127
128	/**
129	 * Revision id if we've edited the record
130	 *
131	 * @var integer
132	 * @access protected
133	 */
134	var $_this_revision;
135
136	/**
137	 * Construct a new Net_CDDB_Disc object
138	 *
139	 * The Net_CDDB_Disc actually supports two differnet methods of constructing
140	 * a Net_CDDB_Disc object. The first is to provide all of the parameters.
141	 * An alternative constructor allows passing an associative array as the
142	 * first *and only* parameter with the following keys:
143	 * 	- discid, 8-char discid
144	 * 	- dartist, string artist name
145	 * 	- dtitle, string title of the cd
146	 * 	- dgenre, string genre of the cd
147	 * 	- dyear, integer year the cd was published
148	 * 	- tracks, array of either {@link Net_CDDB_Track} objects or of arrays containing keys to construct a {@link Net_CDDB_Track} object with
149	 * 	- dlength, length of disc (in seconds)
150	 * 	- revision, CDDB record revision # of disc
151	 * 	- playorder, string describing custom track play order
152	 *
153	 * @access public
154	 * @todo Make sure documentation about field names is correct and complete
155	 *
156	 * @param string $arr_or_discid *See note above!*
157	 * @param string $dartist
158	 * @param string $dtitle
159	 * @param string $category
160	 * @param string $dgenre
161	 * @param integer $dyear
162	 * @param array $tracks An array of {@link Net_CDDB_Track} objects or an array of arrays to construct {@link Net_CDDB_Track} objects with
163	 * @param integer $dlength
164	 * @param integer $revision The CDDB revision from the CDDB record
165	 * @param string $playorder Optional play-order of the tracks
166	 */
167	function Net_CDDB_Disc($arr_or_discid, $dartist = '', $dtitle = '', $category = '', $dgenre = '', $dyear = '', $tracks = array(), $dlength = 0, $revision = 0, $playorder = '', $submitted_via = '', $processed_by = '')
168	{
169		if (is_array($arr_or_discid)) // If the first parameter is an array, we'll treat this as an associative array constructor
170		{
171			$defaults = array(
172				'discid' => '',
173				'dartist' => '',
174				'dtitle' => '',
175				'category' => '',
176				'dgenre' => '',
177				'dyear' => '',
178				'tracks' => array(),
179				//'extras' => array(),
180				//'offsets' => array(),
181				'dlength' => 0,
182				'revision' => 0,
183				'submitted_via' => '',
184				'processed_by' => '',
185				'playorder' => ''
186				);
187
188			$arr_or_discid = array_merge($defaults, $arr_or_discid); // Ensure all required variables are set
189			$arr_or_discid['arr_or_discid'] = $arr_or_discid['discid'];
190
191			extract($arr_or_discid); // Extract into local scope so we can assign next
192		}
193
194		$this->_discid = substr(str_pad($arr_or_discid, 8), 0, 8);
195		$this->_artist = '' . $dartist;
196		$this->_title = '' . $dtitle;
197		$this->_category = $category;
198		$this->_genre = '' . $dgenre;
199		$this->_year = (int) $dyear;
200		$this->_length = (int) $dlength;
201		$this->_revision = (int) $revision;
202		$this->_playorder = $playorder;
203		$this->_submitted_via = $submitted_via;
204		$this->_processed_by = $processed_by;
205
206		// Assign the tracks
207		foreach ($tracks as $track) {
208			if (is_array($track)) {
209				$this->_tracks[] = new Net_CDDB_Track($track);
210			} else if (is_a($track, 'Net_CDDB_Track')) {
211				$this->_tracks[] = $track;
212			}
213		}
214
215		// Check the first track, if the first track doesn't have a length set,
216		//	we'll go ahead and calculate lengths for all of the tracks
217		/*$count = count($this->_tracks);
218		if ($count and !$this->_tracks[0]->getLength()) {
219			$start = $this->_tracks[0]->getOffset(); // Initial disc offset
220
221			for ($i = 1; $i < $count; $i++) {
222				$end = $this->_tracks[$i]->getOffset();
223				$this->_tracks[$i - 1]->setLength(round(($end - $start) / 75)); // Set track offsets (seconds get rounded)
224				$start = $this->_tracks[$i]->getOffset();
225			}
226
227			// Set the final track length
228			$this->_tracks[$count - 1]->setLength($this->_length - round($start / 75));
229		}*/
230
231		$this->_this_revision = -1;
232	}
233
234	/**
235	 * Get the 8-char discid for the disc
236	 *
237	 * @access public
238	 *
239	 * @return string
240	 */
241	function getDiscId()
242	{
243		return $this->_discid;
244	}
245
246	/**
247	 * Get the artist for the audio CD
248	 *
249	 * @access public
250	 *
251	 * @return string
252	 */
253	function getArtist()
254	{
255		return $this->_artist;
256	}
257
258	/**
259	 * Get the title of this disc
260	 *
261	 * @access public
262	 *
263	 * @return string
264	 */
265	function getTitle()
266	{
267		return $this->_title;
268	}
269
270	/**
271	 * Get the category this disc is stored in (directory on CDDB server)
272	 *
273	 * CDDB servers usually maintain about 11 different categories for discs
274	 * which (very roughly) correspond to a few different genres of music. The
275	 * category is needed for disc submission and CDDB reads. There is also a
276	 * free-form 'genre' field stored with each disc record which should be used
277	 * for a more accurate disc genre description.
278	 *
279	 * @see Net_CDDB_Disc::getGenre()
280	 *
281	 * @access public
282	 *
283	 * @return string
284	 */
285	function getCategory()
286	{
287		return $this->_category;
288	}
289
290	/**
291	 * Get the genre this disc belongs to
292	 *
293	 * @access public
294	 *
295	 * @return string
296	 */
297	function getGenre()
298	{
299		return $this->_genre;
300	}
301
302	/**
303	 * Get the number of tracks the CD contains
304	 *
305	 * @access public
306	 *
307	 * @return integer
308	 */
309	function numTracks()
310	{
311		return count($this->_tracks);
312	}
313
314	/**
315	 * Get the track title for a specific track number ( track #s start at 0 )
316	 *
317	 * <code>
318	 * print("The title of the first song is: ");
319	 * $disc->getTrackTitle(0);
320	 * </code>
321	 *
322	 * @access public
323	 * @see Net_CDDB_Disc::getTrack()
324	 * @see Net_CDDB_Track
325	 *
326	 * @param integer $track_num
327	 * @return string
328	 */
329	function getTrackTitle($track_num)
330	{
331		if ($track = $this->getTrack($track_num)) {
332			return $track->getTitle();
333		} else {
334			return null;
335		}
336	}
337
338	/**
339	 * Get the track offset for a specific track
340	 *
341	 * @access public
342	 * @see Net_CDDB_Disc::getTrack()
343	 * @see Net_CDDB_Track
344	 *
345	 * @param integer $track_num
346	 * @return integer
347	 */
348	function getTrackOffset($track_num)
349	{
350		if ($track = $this->getTrack($track_num)) {
351			return $track->getOffset();
352		} else {
353			return null;
354		}
355	}
356
357	/**
358	 * Get the track length for the given track number
359	 *
360	 * @access public
361	 * @see Net_CDDB_Disc::getTrack()
362	 * @see Net_CDDB_Track
363	 *
364	 * @param integer $track_num
365	 * @param boolean $formatted
366	 * @return mixed
367	 */
368	function getTrackLength($track_num, $formatted = false)
369	{
370		if ($track = $this->getTrack($track_num)) {
371			return $track->getLength($formatted);
372		}
373		return null;
374	}
375
376	/**
377	 * Get the extra data for the given track number
378	 *
379	 * @access public
380	 * @see Net_CDDB_Disc::getTrack()
381	 *
382	 * @param integer $track_num
383	 * @return string
384	 */
385	function getTrackExtraData($track_num)
386	{
387		if ($track = $this->getTrack($track_num)) {
388			return $track->getExtraData();
389		}
390		return null;
391	}
392
393	/**
394	 * Get the track artist for the given track number
395	 *
396	 * @access public
397	 * @see Net_CDDB_Disc::getTrack()
398	 * @param integer $track_num
399	 * @return string
400	 */
401	function getTrackArtist($track_num)
402	{
403		if ($track = $this->getTrack($track_num)) {
404			return $track->getArtist();
405		}
406		return null;
407	}
408
409	/**
410	 * Get the {@link Net_CDDB_Track} object representing the given track number
411	 *
412	 * @access public
413	 *
414	 * @param integer $track_num
415	 * @return Net_CDDB_Track
416	 */
417	function getTrack($track_num)
418	{
419		if (isset($this->_tracks[$track_num])) {
420			return $this->_tracks[$track_num];
421		}
422		return null;
423	}
424
425	/**
426	 * Retrieve the length of this disc in seconds
427	 *
428	 * @access public
429	 * @param bool $formatted Whether or not to return in string format: HH:MM:SS ( defaults to returning an integer number of seconds in length )
430	 * @return integer
431	 */
432	function getDiscLength($formatted = false)
433	{
434		if ($formatted) {
435			$hours = floor($this->_length / (60 * 60));
436			$minutes = floor(($this->_length / 60) % 60);
437			$seconds = $this->_length % 60;
438
439			return sprintf('%02d', $hours) . ':' . sprintf('%02d', $minutes) . ':' . sprintf('%02d', $seconds);
440		} else {
441			return $this->_length;
442		}
443	}
444
445	/**
446	 * Retrieve the year this disc was published
447	 *
448	 * @access public
449	 * @return integer
450	 */
451	function getDiscYear()
452	{
453		return $this->_year;
454	}
455
456	/**
457	 * Get the record revision number
458	 *
459	 * @access public
460	 * @return integer
461	 */
462	function getRevision()
463	{
464		return $this->_revision;
465	}
466
467	/**
468	 * Get the name of the program which processed the disc
469	 *
470	 * @access public
471	 * @return string
472	 */
473	function getProcessedBy()
474	{
475		return $this->_processed_by;
476	}
477
478	/**
479	 * Get the name of the program that submitted the disc record
480	 *
481	 * @access public
482	 * @return string
483	 */
484	function getSubmittedVia()
485	{
486		return $this->_submitted_via;
487	}
488
489	/**
490	 * Get any extra data associated with the disc
491	 *
492	 * @todo Finish supporting this
493	 * @access public
494	 * @return string
495	 */
496	function getDiscExtraData()
497	{
498		return '';
499	}
500
501	/**
502	 * Get the playorder of the disc
503	 *
504	 * @todo Make sure this actually works
505	 * @access public
506	 * @return string
507	 */
508	function getDiscPlayorder()
509	{
510		return $this->_playorder;
511	}
512
513	/**
514	 * Return a string representation of this CDDB Disc ( the CDDB file format )
515	 *
516	 * @todo Make EXTD data field work
517	 * @todo Probably should return an *exact* copy of already submitted record (submitted by, processed by are optoinal)
518	 *
519	 * @access public
520	 *
521	 * @return string
522	 */
523	function toString()
524	{
525		$str = "# xcmd\r\n";
526		$str .= "#\r\n";
527		$str .= "# Track frame offsets:\r\n";
528
529		foreach ($this->_tracks as $track) {
530			$str .= '#    ' . $track->getOffset() . "\r\n";
531		}
532
533		//foreach ($this->_offsets as $key => $offset) {
534		//	$str .= '#    ' . $offset . "\r\n";
535		//}
536
537		$str .= "#\r\n";
538		$str .= '# Disc length: ' . $this->_length . " seconds\r\n";
539		$str .= "#\r\n";
540		$str .= "# Revision: " . $this->_revision . "\r\n";
541		$str .= "# Submitted via: " . $this->_submitted_via . "\r\n";
542		$str .= "# Processed by: " . $this->_processed_by . "\r\n";
543		$str .= "#\r\n";
544		$str .= 'DISCID=' . $this->_discid . "\r\n";
545		$str .= 'DTITLE=' . $this->_artist . ' / ' . $this->_title . "\r\n";
546		$str .= 'DYEAR=' . $this->_year . "\r\n";
547		$str .= 'DGENRE=' . $this->_genre . "\r\n";
548
549		foreach ($this->_tracks as $key => $track) {
550			if ($track->getArtist() != $this->_artist) {
551				$str .= 'TTITLE' . $key . '=' . $track->getArtist() . ' / ' . $track->getTitle() . "\r\n";
552			} else {
553				$str .= 'TTITLE' . $key . '=' . $track->getTitle() . "\r\n";
554			}
555		}
556
557		$str .= 'EXTD=' . "\r\n";
558
559		foreach ($this->_tracks as $key => $track) {
560			$str .= 'EXTT' . $key . '=' . $track->getExtraData() . "\r\n";
561		}
562
563		$str .= 'PLAYORDER=' . $this->_playorder . "\r\n";
564
565		return trim($str);
566	}
567}
568
569?>