[Slim-Checkins] r12809 - in /trunk/server/Slim: Formats/RemoteStream.pm Plugin/Pandora/ProtocolHandler.pm Plugin/RhapsodyDirect/ProtocolHandler.pm Plugin/RhapsodyDirect/RPDS.pm Plugin/RhapsodyDirect/strings.txt Utils/Misc.pm
andy at svn.slimdevices.com
andy at svn.slimdevices.com
Thu Aug 30 21:09:38 PDT 2007
Author: andy
Date: Thu Aug 30 21:09:38 2007
New Revision: 12809
URL: http://svn.slimdevices.com?rev=12809&view=rev
Log:
Sync improvements for Pandora and Rhapsody Direct
Modified:
trunk/server/Slim/Formats/RemoteStream.pm
trunk/server/Slim/Plugin/Pandora/ProtocolHandler.pm
trunk/server/Slim/Plugin/RhapsodyDirect/ProtocolHandler.pm
trunk/server/Slim/Plugin/RhapsodyDirect/RPDS.pm
trunk/server/Slim/Plugin/RhapsodyDirect/strings.txt
trunk/server/Slim/Utils/Misc.pm
Modified: trunk/server/Slim/Formats/RemoteStream.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Formats/RemoteStream.pm?rev=12809&r1=12808&r2=12809&view=diff
==============================================================================
--- trunk/server/Slim/Formats/RemoteStream.pm (original)
+++ trunk/server/Slim/Formats/RemoteStream.pm Thu Aug 30 21:09:38 2007
@@ -129,8 +129,9 @@
my $class = ref $self;
my $request = $self->requestString($args->{'client'}, $url, $post);
- ${*$self}{'client'} = $args->{'client'};
- ${*$self}{'create'} = $args->{'create'};
+ ${*$self}{'client'} = $args->{'client'};
+ ${*$self}{'create'} = $args->{'create'};
+ ${*$self}{'bitrate'} = $args->{'bitrate'};
$log->info("Request: $request");
Modified: trunk/server/Slim/Plugin/Pandora/ProtocolHandler.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Plugin/Pandora/ProtocolHandler.pm?rev=12809&r1=12808&r2=12809&view=diff
==============================================================================
--- trunk/server/Slim/Plugin/Pandora/ProtocolHandler.pm (original)
+++ trunk/server/Slim/Plugin/Pandora/ProtocolHandler.pm Thu Aug 30 21:09:38 2007
@@ -24,23 +24,24 @@
my $args = shift;
my $client = $args->{client};
+ my $url = $args->{url};
+
my $track = $client->pluginData('currentTrack') || {};
+
+ $log->debug( 'Remote streaming Pandora track: ' . $track->{audioUrl} );
return unless $track->{audioUrl};
my $sock = $class->SUPER::new( {
- url => $track->{audioUrl},
- client => $client
+ url => $track->{audioUrl},
+ client => $client,
+ bitrate => 128_000,
} ) || return;
${*$sock}{contentType} = 'audio/mpeg';
-
- # XXX: Need some way to get the track length for remote streaming mode
-
+
# XXX: Time counter is not right, it starts from 0:00 as soon as next track
- # begins streaming
-
- # XXX: Sync not working yet (players will play different tracks)
+ # begins streaming (slimp3/SB1 only)
return $sock;
}
@@ -153,21 +154,23 @@
my $track = eval { from_json( $http->content ) };
+ if ( $@ || $track->{error} ) {
+ # We didn't get the next track to play
+ $log->warn( 'Pandora error getting next track: ' . ( $@ || $track->{error} ) );
+
+ my $url = Slim::Player::Playlist::url($client);
+
+ setCurrentTitle( $client, $url, $client->string('PLUGIN_PANDORA_NO_TRACKS') );
+
+ $client->update();
+
+ Slim::Player::Source::playmode( $client, 'stop' );
+
+ return;
+ }
+
if ( $log->is_debug ) {
- $log->debug( "Got Pandora track: " . Data::Dump::dump($track) );
- }
-
- if ( $track->{error} ) {
- # We didn't get the next track to play
- my $url = Slim::Player::Playlist::url($client);
-
- setCurrentTitle( $client, $url, $client->string('PLUGIN_PANDORA_NO_TRACKS') );
-
- $client->update();
-
- Slim::Player::Source::playmode( $client, 'stop' );
-
- return;
+ $log->debug( 'Got Pandora track: ' . Data::Dump::dump($track) );
}
# Watch for playlist commands
@@ -213,6 +216,19 @@
sub onDecoderUnderrun {
my ( $class, $client, $nextURL, $callback ) = @_;
+ # Special handling needed when synced
+ if ( Slim::Player::Sync::isSynced($client) ) {
+ if ( !Slim::Player::Sync::isMaster($client) ) {
+ # Only the master needs to fetch next track info
+ $log->debug('Letting sync master fetch next Pandora track');
+ return;
+ }
+ else {
+ # XXX: Source does not call skipahead when synced for some reason
+ # Need to restart playback here?
+ }
+ }
+
# Flag that we don't want any buffering messages while loading the next track,
$client->pluginData( showBuffering => 0 );
@@ -258,7 +274,7 @@
my $url = shift;
my @headers = @_;
- my $bitrate = 128000;
+ my $bitrate = 128_000;
my $contentType = 'mp3';
# Clear previous duration, since we're using the same URL for all tracks
@@ -358,6 +374,13 @@
sub canDirectStream {
my ( $class, $client, $url ) = @_;
+ # We need to check with the base class (HTTP) to see if we
+ # are synced or if the user has set mp3StreamingMethod
+ my $base = $class->SUPER::canDirectStream( $client, $url );
+ if ( !$base ) {
+ return 0;
+ }
+
my $track = $client->pluginData('currentTrack') || {};
return $track->{audioUrl} || 0;
Modified: trunk/server/Slim/Plugin/RhapsodyDirect/ProtocolHandler.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Plugin/RhapsodyDirect/ProtocolHandler.pm?rev=12809&r1=12808&r2=12809&view=diff
==============================================================================
--- trunk/server/Slim/Plugin/RhapsodyDirect/ProtocolHandler.pm (original)
+++ trunk/server/Slim/Plugin/RhapsodyDirect/ProtocolHandler.pm Thu Aug 30 21:09:38 2007
@@ -123,22 +123,6 @@
# XXX: When hitting play while currently listening to another Rhapsody track,
# no logging is performed
-
- # Clear any previous outstanding rpds queries
- cancel_rpds($client);
-
- # Always get a new playback session
- $log->debug("Requesting new playback session...");
-
- # Update the 'Connecting...' text
- $client->suppressStatus(1);
- displayStatus( $client, $url, 'PLUGIN_RHAPSODY_DIRECT_GETTING_TRACK_INFO', 30 );
-
- # Clear old radio data if any
- $client->pluginData( radioTrack => 0 );
-
- # Display buffering info on loading the next track
- $client->pluginData( showBuffering => 1 );
# Get login info from SN if we don't already have it
my $account = $client->pluginData('account');
@@ -169,6 +153,53 @@
return;
}
+ $log->debug("Ending any previous playback session");
+
+ my @clients;
+
+ if ( Slim::Player::Sync::isSynced($client) ) {
+ # if synced, send this packet to all slave players
+ my $master = Slim::Player::Sync::masterOrSelf($client);
+ push @clients, $master, @{ $master->slaves };
+ }
+ else {
+ push @clients, $client;
+ }
+
+ for my $client ( @clients ) {
+ # Clear any previous outstanding rpds queries
+ cancel_rpds($client);
+
+ rpds( $client, {
+ data => pack( 'c', 6 ),
+ callback => \&getPlaybackSession,
+ onError => sub {
+ getPlaybackSession( $client, undef, $url, $callback );
+ },
+ passthrough => [ $url, $callback ],
+ } );
+ }
+}
+
+sub getPlaybackSession {
+ my ( $client, $data, $url, $callback ) = @_;
+
+ # Always get a new playback session
+ $log->debug( $client->id, ' Requesting new playback session...');
+
+ # Update the 'Connecting...' text
+ $client->suppressStatus(1);
+ displayStatus( $client, $url, 'PLUGIN_RHAPSODY_DIRECT_GETTING_TRACK_INFO', 30 );
+
+ # Clear old radio data if any
+ $client->pluginData( radioTrack => 0 );
+
+ # Display buffering info on loading the next track
+ $client->pluginData( showBuffering => 1 );
+
+ # Get login info
+ my $account = $client->pluginData('account');
+
my $packet = pack 'cC/a*C/a*C/a*C/a*',
2,
encode_entities( $account->{username}->[0] ),
@@ -176,6 +207,8 @@
encode_entities( decode_base64( $account->{password}->[0] ) ),
$account->{clientType};
+ # When synced, all players will make this request to get a new playback session
+
rpds( $client, {
data => $packet,
callback => \&gotPlaybackSession,
@@ -225,6 +258,12 @@
$log->debug("Radio mode: Next track is $url");
}
else {
+
+ if ( Slim::Player::Sync::isSynced($client) && !Slim::Player::Sync::isMaster($client) ) {
+ $log->debug('Radio mode: Letting master get next track');
+ return;
+ }
+
# Get the next track and call us back
$log->debug('Radio mode: Getting next track...');
@@ -242,13 +281,18 @@
my ($trackId) = $url =~ /(Tra\.[^.]+)/;
# Get metadata for normal tracks
- getTrackMetadata( $client, {
- trackId => $trackId,
- callback => \&gotTrackMetadata,
- passthrough => [ $client ],
- } );
+ # If synced, only master should do this
+
+ if ( !Slim::Player::Sync::isSynced($client) || Slim::Player::Sync::isMaster($client) ) {
+ getTrackMetadata( $client, {
+ trackId => $trackId,
+ callback => \&gotTrackMetadata,
+ passthrough => [ $client ],
+ } );
+ }
# Get the track URL via the player
+ # When synced, all players will do this to initialize themselves for playback
rpds( $client, {
data => pack( 'cC/a*', 3, $trackId ),
callback => \&gotTrackInfo,
@@ -359,18 +403,22 @@
onError => sub {
# We don't really care if the logging call fails,
# so allow onError to work like the normal callback
- getNextTrackInfo( $client, undef, $nextURL, $callback );
+ getNextTrackInfo( $client, undef, $nextURL, $callback, 'all' );
},
- passthrough => [ $nextURL, $callback ],
+ passthrough => [ $nextURL, $callback, 'all' ],
} );
}
else {
- getNextTrackInfo( $client, undef, $nextURL, $callback );
+ getNextTrackInfo( $client, undef, $nextURL, $callback, 'all' );
}
}
sub getNextTrackInfo {
- my ( $client, undef, $nextURL, $callback ) = @_;
+ my ( $client, undef, $nextURL, $callback, $requestMode ) = @_;
+
+ # requestMode is used when synced to indicate whether only this client
+ # or all clients need to send RPDS 3 packets
+ $requestMode ||= 'client';
# Radio mode, get next track ID
if ( my ($stationId) = $nextURL =~ m{rhapd://(.+)\.rdr} ) {
@@ -381,13 +429,19 @@
$log->debug("Radio mode: Next track is $nextURL");
}
else {
+
+ if ( Slim::Player::Sync::isSynced($client) && !Slim::Player::Sync::isMaster($client) ) {
+ $log->debug('Radio mode: Letting master get next track');
+ return;
+ }
+
# Get the next track and call us back
$log->debug("Radio mode: Getting info about next track ($nextURL)...");
getNextRadioTrack( $client, {
stationId => $stationId,
callback => \&getNextTrackInfo,
- passthrough => [ $client, undef, $nextURL, $callback ],
+ passthrough => [ $client, undef, $nextURL, $callback, 'all' ],
} );
return;
}
@@ -397,18 +451,34 @@
my ($trackId) = $nextURL =~ /(Tra\.[^.]+)/;
# Get metadata for normal tracks
- getTrackMetadata( $client, {
- trackId => $trackId,
- callback => \&gotTrackMetadata,
- passthrough => [ $client ],
- } );
-
- rpds( $client, {
- data => pack( 'cC/a*', 3, $trackId ),
- callback => \&gotTrackInfo,
- onError => \&gotTrackError,
- passthrough => [ $nextURL, $callback ],
- } );
+ # If synced, only the master should do this
+ if ( !Slim::Player::Sync::isSynced($client) || Slim::Player::Sync::isMaster($client) ) {
+ getTrackMetadata( $client, {
+ trackId => $trackId,
+ callback => \&gotTrackMetadata,
+ passthrough => [ $client ],
+ } );
+ }
+
+ my @clients;
+
+ if ( Slim::Player::Sync::isSynced($client) && $requestMode eq 'all' ) {
+ # if synced and requestMode is all, send this packet to all slave players
+ my $master = Slim::Player::Sync::masterOrSelf($client);
+ push @clients, $master, @{ $master->slaves };
+ }
+ else {
+ push @clients, $client;
+ }
+
+ for my $client ( @clients ) {
+ rpds( $client, {
+ data => pack( 'cC/a*', 3, $trackId ),
+ callback => \&gotTrackInfo,
+ onError => \&gotTrackError,
+ passthrough => [ $nextURL, $callback ],
+ } );
+ }
}
# On an underrun, restart radio or skip to next track
@@ -659,15 +729,18 @@
# When done, callback to Scanner, which will continue on to playback
# This is a callback to Source::decoderUnderrun if we are loading the next track
- my $dns = Slim::Networking::Async->new;
- $dns->open( {
- Host => URI->new($mediaUrl)->host,
- Timeout => 3, # Default timeout of 10 is too long,
- # by the time it fails player will underrun and stop
- onDNS => $callback,
- onError => $callback, # even if it errors, keep going
- passthrough => [],
- } );
+ # If synced, only the master calls back
+ if ( !Slim::Player::Sync::isSynced($client) || Slim::Player::Sync::isMaster($client) ) {
+ my $dns = Slim::Networking::Async->new;
+ $dns->open( {
+ Host => URI->new($mediaUrl)->host,
+ Timeout => 3, # Default timeout of 10 is too long,
+ # by the time it fails player will underrun and stop
+ onDNS => $callback,
+ onError => $callback, # even if it errors, keep going
+ passthrough => [],
+ } );
+ }
# Watch for stop commands for logging purposes
Slim::Control::Request::subscribe(
@@ -718,7 +791,8 @@
handleError( $error, $client );
}
else {
- Slim::Player::Source::jumpto( $client, '+1' );
+ $client->execute([ 'playlist', 'jump', '+1' ]);
+ #Slim::Player::Source::jumpto( $client, '+1' );
}
}
@@ -786,17 +860,29 @@
$data = pack( 'cC/a*', 4, $songtime );
}
- # Call endPlaybackSession when stopping
- rpds( $client, {
- data => $data,
- callback => \&endPlaybackSession,
- onError => sub {
- # We don't really care if the logging call fails,
- # so allow onError to work like the normal callback
- endPlaybackSession( $client );
- },
- passthrough => [],
- } );
+ my @clients;
+
+ if ( Slim::Player::Sync::isSynced($client) ) {
+ # if synced, send this packet to all slave players
+ my $master = Slim::Player::Sync::masterOrSelf($client);
+ push @clients, $master, @{ $master->slaves };
+ }
+ else {
+ push @clients, $client;
+ }
+
+ for my $client ( @clients ) {
+ rpds( $client, {
+ data => $data,
+ callback => \&endPlaybackSession,
+ onError => sub {
+ # We don't really care if the logging call fails,
+ # so allow onError to work like the normal callback
+ endPlaybackSession( $client );
+ },
+ passthrough => [],
+ } );
+ }
}
}
}
@@ -807,7 +893,7 @@
rpds( $client, {
data => pack( 'c', 6 ),
callback => sub {},
- onError => sub {}, # doesn't matter if this one fails
+ onError => sub {},
passthrough => [],
} );
}
Modified: trunk/server/Slim/Plugin/RhapsodyDirect/RPDS.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Plugin/RhapsodyDirect/RPDS.pm?rev=12809&r1=12808&r2=12809&view=diff
==============================================================================
--- trunk/server/Slim/Plugin/RhapsodyDirect/RPDS.pm (original)
+++ trunk/server/Slim/Plugin/RhapsodyDirect/RPDS.pm Thu Aug 30 21:09:38 2007
@@ -9,6 +9,7 @@
use Exporter::Lite;
use HTML::Entities qw(encode_entities);
use MIME::Base64 qw(decode_base64);
+use Scalar::Util qw(blessed);
use Slim::Utils::Log;
use Slim::Utils::Misc;
@@ -35,6 +36,8 @@
Slim::Utils::Timers::killTimers( $client, \&rpds_timeout );
Slim::Utils::Timers::killTimers( $client, \&rpds_resend );
+
+ return unless blessed($client);
# Save callback info for rpds_handler
$rpds_args->{$client} = $args;
@@ -125,6 +128,7 @@
if ( $ENV{SLIM_SERVICE} ) {
logError( $client, 'RPDS_EA_FAILED' );
}
+ $log->debug('RPDS: getEA failed');
return;
}
@@ -151,9 +155,6 @@
my $error = $client->string('PLUGIN_RHAPSODY_DIRECT_INVALID_SESSION');
- # Stop the player
- Slim::Player::Source::playmode( $client, 'stop' );
-
# Track session errors so we don't get in a loop
my $sessionErrors = $client->pluginData('sessionErrors') || 0;
$sessionErrors++;
@@ -163,6 +164,9 @@
if ( $sessionErrors > 1 ) {
# On the second error, give up
$log->debug("Giving up after multiple invalid session errors");
+
+ # Stop the player
+ Slim::Player::Source::playmode( $client, 'stop' );
handleError( $error, $client );
@@ -170,6 +174,16 @@
}
$client->pluginData( sessionErrors => $sessionErrors );
+
+ # Retry if command was 3 to get track info
+ if ( $sent_cmd eq '3' ) {
+ $log->debug( $client->id, ' Getting a new session and retrying' );
+ retry_new_session( $client, $rpds );
+ return;
+ }
+
+ # Stop the player
+ Slim::Player::Source::playmode( $client, 'stop' );
my $restart = sub {
# Clear radio data if any, so we always get a new radio track
@@ -201,57 +215,22 @@
elsif ( $got_cmd eq '-2' ) {
# Player indicates it needs a new session
+ # Ignore if command was 6 to end a session
+ if ( $sent_cmd eq '6' ) {
+ my $cb = $rpds->{onError} || sub {};
+ $cb->();
+ return;
+ }
+
$log->warn( $client->id . " Received RPDS -2, player needs a new session");
if ( $ENV{SLIM_SERVICE} ) {
logError( $client, 'RPDS_NO_SESSION' );
}
-
- my $account = $client->pluginData('account');
-
- if ( !$account ) {
- my $accountURL = Slim::Networking::SqueezeNetwork->url( '/api/rhapsody/account' );
-
- my $http = Slim::Networking::SqueezeNetwork->new(
- \&Slim::Plugin::RhapsodyDirect::ProtocolHandler::gotAccount,
- \&Slim::Plugin::RhapsodyDirect::ProtocolHandler::gotAccountError,
- {
- client => $client,
- cb => sub {
- # reset the rpds and try again
- $rpds_args->{$client} = $rpds;
- $data_ref = pack 'c', '-2';
- rpds_handler( $client, \$data_ref );
- },
- ecb => sub {
- my $error = shift;
- $error = $client->string('PLUGIN_RHAPSODY_DIRECT_ERROR_ACCOUNT') . ": $error";
- handleError( $error, $client );
- },
- },
- );
-
- $log->debug("Getting Rhapsody account from SqueezeNetwork");
-
- $http->get( $accountURL );
-
- return;
- }
-
- my $packet = pack 'cC/a*C/a*C/a*C/a*',
- 2,
- encode_entities( $account->{username}->[0] ),
- $account->{cobrandId},
- encode_entities( decode_base64( $account->{password}->[0] ) ),
- $account->{clientType};
-
- rpds( $client, {
- data => $packet,
- callback => \&rpds_resend,
- onError => \&handleError,
- passthrough => [ $rpds ],
- } );
-
+
+ # Get a new session and retry the previous rpds command
+ retry_new_session( $client, $rpds );
+
return;
}
elsif ( $got_cmd eq '-3' ) {
@@ -292,7 +271,7 @@
}
if ( !$rpds || $got_cmd ne $sent_cmd ) {
- $log->warn( $client->id . ' Received unrequested or old RPDS packet, ignoring' );
+ $log->warn( $client->id . " Ignoring unrequested or old RPDS packet (got $got_cmd, expected $sent_cmd)" );
if ( $ENV{SLIM_SERVICE} ) {
logError( $client, 'RPDS_OLD', "got $got_cmd, ignoring" );
@@ -317,7 +296,9 @@
sub rpds_resend {
my ( $client, undef, $rpds ) = @_;
- $log->warn( $client->id . ' Re-sending RPDS packet');
+ if ( $log->is_debug ) {
+ $log->debug( $client->id . ' Re-sending RPDS packet: ' . Data::Dump::dump( $rpds->{data} ) );
+ }
rpds( $client, {
data => $rpds->{data},
@@ -327,5 +308,56 @@
} );
}
+sub retry_new_session {
+ my ( $client, $rpds ) = @_;
+
+ my $account = $client->pluginData('account');
+
+ if ( !$account ) {
+ my $accountURL = Slim::Networking::SqueezeNetwork->url( '/api/rhapsody/account' );
+
+ my $http = Slim::Networking::SqueezeNetwork->new(
+ \&Slim::Plugin::RhapsodyDirect::ProtocolHandler::gotAccount,
+ \&Slim::Plugin::RhapsodyDirect::ProtocolHandler::gotAccountError,
+ {
+ client => $client,
+ cb => sub {
+ # try again
+ retry_new_session( $client, $rpds );
+ },
+ ecb => sub {
+ my $error = shift;
+ $error = $client->string('PLUGIN_RHAPSODY_DIRECT_ERROR_ACCOUNT') . ": $error";
+ handleError( $error, $client );
+ },
+ },
+ );
+
+ $log->debug("Getting Rhapsody account from SqueezeNetwork");
+
+ $http->get( $accountURL );
+
+ return;
+ }
+
+ if ( $log->is_debug ) {
+ $log->debug( $client->id, ' Getting a new session and then retrying ' . Data::Dump::dump( $rpds->{data} ) );
+ }
+
+ my $packet = pack 'cC/a*C/a*C/a*C/a*',
+ 2,
+ encode_entities( $account->{username}->[0] ),
+ $account->{cobrandId},
+ encode_entities( decode_base64( $account->{password}->[0] ) ),
+ $account->{clientType};
+
+ rpds( $client, {
+ data => $packet,
+ callback => \&rpds_resend,
+ onError => \&handleError,
+ passthrough => [ $rpds ],
+ } );
+}
+
1;
Modified: trunk/server/Slim/Plugin/RhapsodyDirect/strings.txt
URL: http://svn.slimdevices.com/trunk/server/Slim/Plugin/RhapsodyDirect/strings.txt?rev=12809&r1=12808&r2=12809&view=diff
==============================================================================
--- trunk/server/Slim/Plugin/RhapsodyDirect/strings.txt (original)
+++ trunk/server/Slim/Plugin/RhapsodyDirect/strings.txt Thu Aug 30 21:09:38 2007
@@ -99,4 +99,7 @@
PLUGIN_RHAPSODY_DIRECT_ERROR_ACCOUNT
DE Konnte über das SqueezeNetwork keine Account-Informationen finden.
- EN Unable to retrieve account details from SqueezeNetwork.
+ EN Unable to retrieve account details from SqueezeNetwork.
+
+PLUGIN_RHAPSODY_DIRECT_INVALID_SESSION
+ EN Invalid playback session.
Modified: trunk/server/Slim/Utils/Misc.pm
URL: http://svn.slimdevices.com/trunk/server/Slim/Utils/Misc.pm?rev=12809&r1=12808&r2=12809&view=diff
==============================================================================
--- trunk/server/Slim/Utils/Misc.pm (original)
+++ trunk/server/Slim/Utils/Misc.pm Thu Aug 30 21:09:38 2007
@@ -855,7 +855,7 @@
opendir(DIR, $dirname) || do {
- logError("opendir on [$dirname] failed: $!");
+ $log->debug("opendir on [$dirname] failed: $!");
return @diritems;
};
More information about the checkins
mailing list