package com.robonobo.core.service; import java.util.*; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import com.robonobo.common.exceptions.SeekInnerCalmException; import com.robonobo.core.api.TransferSpeed; import com.robonobo.core.api.TransferSpeedListener; import com.robonobo.core.api.model.*; import com.robonobo.core.api.model.Track.PlaybackStatus; import com.robonobo.mina.external.MinaControl; /** First point of call to get information about a track, whether we're sharing it, downloading it or it just exists in * the cloud. * * Get shares and downloads from here rather than Share/Download service as they will then include info on whether * they're playing, and their transfer speeds * * @author macavity */ public class TrackService extends AbstractService implements TransferSpeedListener { Log log = LogFactory.getLog(getClass()); private ShareService share; private DownloadService download; private StreamService streams; private PlaybackService playback; private EventService event; private MinaControl mina; protected Map<String, TransferSpeed> transferSpeeds = null; private String currentPlayingStreamId = null; private boolean allSharesStarted; private boolean started = false; public TrackService() { addHardDependency("core.shares"); addHardDependency("core.downloads"); } public String getName() { return "Track Service"; } public String getProvides() { return "core.tracks"; } @Override public void startup() throws Exception { share = rbnb.getShareService(); download = rbnb.getDownloadService(); streams = rbnb.getStreamService(); playback = rbnb.getPlaybackService(); event = rbnb.getEventService(); mina = rbnb.getMina(); event.addTransferSpeedListener(this); started = true; // Do this here rather than in [share|download]service as we // start after them and we need to be present to fire allTracksLoaded download.startAllDownloads(); share.startAllShares(); } @Override public void newTransferSpeeds(Map<String, TransferSpeed> speedsByStream, Map<String, TransferSpeed> speedsByNode) { // We fire the updated event for all streams that were in the // previous set of speeds and this set, so they get reset to 0 Set<String> changedStreamIds = new HashSet<String>(); synchronized (this) { if (transferSpeeds != null) changedStreamIds.addAll(transferSpeeds.keySet()); transferSpeeds = speedsByStream; changedStreamIds.addAll(transferSpeeds.keySet()); } if (changedStreamIds.size() > 0) event.fireTracksUpdated(changedStreamIds); } @Override public void shutdown() throws Exception { } /** Don't hang onto the object you get returned from here - implement TrackListener, and look it up every time * instead. That way we keep ram usage down and you always have the correct status (tracks start off as CloudTracks, * then become DownloadingTracks, then SharedTracks, plus they get played, then stopped, etc) */ public Track getTrack(String streamId) { // If we haven't started yet, just wait til we have while (!started) { try { Thread.sleep(100); } catch (InterruptedException e) { throw new SeekInnerCalmException(e); } } Track t; // Are we sharing this track? t = share.getShare(streamId); if (t == null) { // Are we downloading this track? t = download.getDownload(streamId); if (t == null) { // Nope - it's in the cloud Stream s = streams.getKnownStream(streamId); if (s == null) return null; t = new CloudTrack(s, mina.numSources(streamId)); } } // Set playback status (might be already there if downloading, if necessary override it) String playingSid = playback.getCurrentStreamId(); if (playingSid != null && playingSid.equals(streamId)) { switch (playback.getStatus()) { case Buffering: // fall through case Starting: t.setPlaybackStatus(PlaybackStatus.Starting); break; case Playing: t.setPlaybackStatus(PlaybackStatus.Playing); break; case Paused: t.setPlaybackStatus(PlaybackStatus.Paused); break; } } // Set transfer speeds synchronized (this) { if (transferSpeeds != null && transferSpeeds.containsKey(streamId)) { TransferSpeed ts = transferSpeeds.get(streamId); t.setRates(ts.getDownload(), ts.getUpload()); } } return t; } public void notifyPlayingTrackChange(String newPlayingStreamId) { if (currentPlayingStreamId != null) event.fireTrackUpdated(currentPlayingStreamId); if (newPlayingStreamId != null) event.fireTrackUpdated(newPlayingStreamId); currentPlayingStreamId = newPlayingStreamId; } public boolean haveAllSharesStarted() { return allSharesStarted; } public void setAllSharesStarted(boolean allSharesStarted) { this.allSharesStarted = allSharesStarted; } }