From 58105feca825a6a44a77217aca7f735014ef1bea Mon Sep 17 00:00:00 2001 From: Michael Herger Date: Sat, 30 Jan 2021 07:41:35 +0100 Subject: [PATCH] Remove MOG plugin - they've been shut down more than half a decade ago. --- Changelog7.html | 2 +- Slim/Player/Squeezebox2.pm | 193 +++-- .../HTML/EN/plugins/MOG/html/images/icon.png | Bin 59426 -> 0 bytes Slim/Plugin/MOG/Plugin.pm | 125 ---- Slim/Plugin/MOG/ProtocolHandler.pm | 705 ------------------ Slim/Plugin/MOG/install.xml | 18 - Slim/Plugin/MOG/strings.txt | 32 - 7 files changed, 95 insertions(+), 980 deletions(-) delete mode 100644 Slim/Plugin/MOG/HTML/EN/plugins/MOG/html/images/icon.png delete mode 100644 Slim/Plugin/MOG/Plugin.pm delete mode 100644 Slim/Plugin/MOG/ProtocolHandler.pm delete mode 100644 Slim/Plugin/MOG/install.xml delete mode 100644 Slim/Plugin/MOG/strings.txt diff --git a/Changelog7.html b/Changelog7.html index 6d5f4d9e1d4..07dc080f14d 100644 --- a/Changelog7.html +++ b/Changelog7.html @@ -3,7 +3,7 @@

Version 7.7.7

  • Server Changes:

  • diff --git a/Slim/Player/Squeezebox2.pm b/Slim/Player/Squeezebox2.pm index 3e3d9560472..3f0ef78ef42 100644 --- a/Slim/Player/Squeezebox2.pm +++ b/Slim/Player/Squeezebox2.pm @@ -114,7 +114,7 @@ sub model { sub modelName { my $client = shift; - + if ($client->model(1) eq 'squeezebox3') { return 'Squeezebox Classic'; } @@ -125,20 +125,20 @@ sub modelName { # in order of preference based on whether we're connected via wired or wireless... sub formats { my $client = shift; - + return qw(wma ogg flc aif pcm mp3); } sub statHandler { my ($client, $code, $jiffies, $error_code) = @_; - + if ($code eq 'STMc') { $client->streamStartTimestamp($jiffies); } else { return if ! defined($client->streamStartTimestamp()); } - - + + if ($code eq 'STMd') { $client->readyToStream(1); $client->controller()->playerReadyToStream($client); @@ -167,20 +167,20 @@ sub statHandler { $client->controller()->playerEndOfStream($client); } else { $client->controller->playerStatusHeartbeat($client); - } - + } + } - + # The original Squeezebox2 firmware supported a fairly narrow volume range # below unity gain - 129 levels on a linear scale represented by a 1.7 # fixed point number (no sign, 1 integer, 7 fractional bits). # From FW 22 onwards, volume is sent as a 16.16 value (no sign, 16 integer, # 16 fractional bits), significantly increasing our fractional range. -# Rather than test for the firmware level, we send both values in the +# Rather than test for the firmware level, we send both values in the # volume message. -# We thought about sending a dB scale volume to the client, but decided -# against it. Sending a fixed point multiplier allows us to change +# We thought about sending a dB scale volume to the client, but decided +# against it. Sending a fixed point multiplier allows us to change # the mapping of UI volume settings to gain as we want, without being # constrained by any scale other than that of the fixed point range allowed # by the client. @@ -189,16 +189,16 @@ sub statHandler { # we only have 129 levels to work with now, and within 100 range, # that's pretty tight. # this table is optimized for 40 steps (like we have in the current player UI. -my @volume_map = ( -0, 1, 1, 1, 2, 2, 2, 3, 3, 4, -5, 5, 6, 6, 7, 8, 9, 9, 10, 11, -12, 13, 14, 15, 16, 16, 17, 18, 19, 20, -22, 23, 24, 25, 26, 27, 28, 29, 30, 32, -33, 34, 35, 37, 38, 39, 40, 42, 43, 44, -46, 47, 48, 50, 51, 53, 54, 56, 57, 59, -60, 61, 63, 65, 66, 68, 69, 71, 72, 74, -75, 77, 79, 80, 82, 84, 85, 87, 89, 90, -92, 94, 96, 97, 99, 101, 103, 104, 106, 108, 110, +my @volume_map = ( +0, 1, 1, 1, 2, 2, 2, 3, 3, 4, +5, 5, 6, 6, 7, 8, 9, 9, 10, 11, +12, 13, 14, 15, 16, 16, 17, 18, 19, 20, +22, 23, 24, 25, 26, 27, 28, 29, 30, 32, +33, 34, 35, 37, 38, 39, 40, 42, 43, 44, +46, 47, 48, 50, 51, 53, 54, 56, 57, 59, +60, 61, 63, 65, 66, 68, 69, 71, 72, 74, +75, 77, 79, 80, 82, 84, 85, 87, 89, 90, +92, 94, 96, 97, 99, 101, 103, 104, 106, 108, 110, 112, 113, 115, 117, 119, 121, 123, 125, 127, 128 ); @@ -209,7 +209,7 @@ sub dBToFixed { # Map a floating point dB value to a 16.16 fixed point value to # send as a new style volume to SB2 (FW 22+). my $floatmult = 10 ** ($db/20); - + # use 8 bits of accuracy for dB values greater than -30dB to avoid rounding errors if ($db >= -30 && $db <= 0) { return int($floatmult * (1 << 8) + 0.5) * (1 << 8); @@ -221,7 +221,7 @@ sub dBToFixed { sub getVolumeParameters { # A negative stepPoint ensures that the alternate (low level) ramp never kicks in. - my $params = + my $params = { totalVolumeRange => -50, # dB stepPoint => -1, # Number of steps, up from the bottom, where a 2nd volume ramp kicks in. @@ -238,15 +238,15 @@ sub getVolume my $stepdB = $volume_parameters->{totalVolumeRange} * $volume_parameters->{stepFraction}; my $maxVolumedB = (defined $volume_parameters->{maximumVolume}) ? $volume_parameters->{maximumVolume} : 0; - - # Equation for a line: + + # Equation for a line: # y = mx+b - # y1 = mx1+b, y2 = mx2+b. + # y1 = mx1+b, y2 = mx2+b. # y2-y1 = m(x2 - x1) # y2 = m(x2 - x1) + y1 my $slope_high = ($maxVolumedB-$stepdB)/(100-$stepPoint) ; my $slope_low = ($stepdB-$totalVolumeRange)/($stepPoint-0); - + my $x2 = $volume; my $m = undef; my $x1 = undef; @@ -263,7 +263,7 @@ sub getVolume my $y2 = $m * ($x2 - $x1) + $y1; # print "$m, ($x1, $y1), ($x2, $y2)\n"; return $y2; - + } sub volume { @@ -275,7 +275,7 @@ sub volume { if (defined($newvolume)) { # Old style volume: my $oldGain = $volume_map[int($volume)]; - + my $newGain; # Negative volume = muting if ($volume <= 0) { @@ -285,12 +285,12 @@ sub volume { my $db = $client->getVolume($volume, $client->getVolumeParameters()); $newGain = $client->dBToFixed($db); } - + my $dvc = $prefs->client($client)->get('digitalVolumeControl'); if ( !defined $dvc ) { $dvc = $Slim::Player::Player::defaultPrefs->{digitalVolumeControl}; } - + my $preamp = 255 - int( 2 * ( $prefs->client($client)->get('preampVolumeControl') || 0 ) ); my $data; @@ -329,7 +329,7 @@ sub upgradeFirmware { $client->showBriefly( { 'line' => [ $client->string( 'FIRMWARE_MISSING' ), $client->string( 'FIRMWARE_MISSING_DESC' ) ] }, - { + { 'block' => 1, 'scroll' => 1, 'firstline' => 1, @@ -342,17 +342,17 @@ sub upgradeFirmware { return(0); } - + if (-f $file2 && !-f $file) { $file = $file2; } - + $client->stop(); main::INFOLOG && $log->info("Using new update mechanism: $file"); - + $client->isUpgrading(1); - + # Notify about firmware upgrade starting Slim::Control::Request::notifyFromArray( $client, [ 'firmware_upgrade' ] ); @@ -415,13 +415,13 @@ sub stop { sub songElapsedSeconds { my $client = shift; - + return 0 if $client->isStopped() || defined $_[0]; my ($jiffies, $elapsedMilliseconds, $elapsedSeconds) = Slim::Networking::Slimproto::getPlayPointData($client); return 0 unless $elapsedMilliseconds || $elapsedSeconds; - + # Use milliseconds for the song-elapsed-time if has not suffered truncation my $songElapsed; if (defined $elapsedMilliseconds) { @@ -432,12 +432,12 @@ sub songElapsedSeconds { } else { $songElapsed = $elapsedSeconds; } - + if ($client->isPlaying(1)) { my $timeDiff = Time::HiRes::time() - $client->jiffiesToTimestamp($jiffies); $songElapsed += $timeDiff if ($timeDiff > 0); } - + return $songElapsed; } @@ -465,7 +465,7 @@ sub directHeaders { my $controller = $client->controller()->songStreamController(); my $handler = $controller ? $controller->protocolHandler() : undef; - + if ($handler && $handler->can('handlesStreamHeaders')) { if ($handler->handlesStreamHeaders($client, $headers)) { @@ -478,16 +478,16 @@ sub directHeaders { my $url = $controller->streamUrl(); my $songHandler = $controller->songProtocolHandler(); - + # We involve the protocol handler in the header parsing process. - # The current iteration of the firmware only knows about HTTP - # headers. Specifically, it returns headers after finding a + # The current iteration of the firmware only knows about HTTP + # headers. Specifically, it returns headers after finding a # CRLF pair in the stream. In the future, we could tell the firmware - # to return a specific number of bytes or look for a specific + # to return a specific number of bytes or look for a specific # byte sequence and make this less HTTP specific. For now, we only # support this type of direct streaming for HTTP-esque protocols. - # Trim embedded nulls + # Trim embedded nulls $headers =~ s/[\0]*$//; $headers =~ s/\r/\n/g; @@ -496,9 +496,9 @@ sub directHeaders { my @headers = split "\n", $headers; chomp(@headers); - + my $response = shift @headers; - + if (!$response || $response !~ m/ (\d\d\d)/) { $directlog->warn("Invalid response code ($response) from remote stream $url"); @@ -506,19 +506,19 @@ sub directHeaders { $client->failedDirectStream($response); } else { - + my $status_line = $response; $response = $1; - + if (($response < 200) || $response > 399) { $directlog->warn("Invalid response code ($response) from remote stream $url"); if ($songHandler && $songHandler->can("handleDirectError")) { - + # bug 10407 - make sure ready to stream again $client->readyToStream(1); - + $songHandler->handleDirectError($client, $url, $response, $status_line); } else { @@ -542,7 +542,7 @@ sub directHeaders { if ($songHandler && $songHandler->can("parseDirectHeaders")) { # Could use a hash ref for header parameters main::INFOLOG && $directlog->info("Calling $songHandler ::parseDirectHeaders"); - ($title, $bitrate, $metaint, $redir, $contentType, $length, $body) + ($title, $bitrate, $metaint, $redir, $contentType, $length, $body) = $songHandler->parseDirectHeaders($client, $controller->song()->currentTrack(), @headers); } elsif ($handler->can("parseDirectHeaders")) { # Could use a hash ref for header parameters @@ -553,34 +553,34 @@ sub directHeaders { $controller->song()->isLive($length ? 0 : 1) if !$redir; # XXX maybe should (also) check $song->scanData()->{$url}->{metadata}->{info}->{broadcast} # for WMA streams here. - + # update bitrate, content-type title for this URL... Slim::Music::Info::setContentType($url, $contentType) if $contentType; Slim::Music::Info::setBitrate($url, $bitrate) if $bitrate; - + # Always prefer the title returned in the headers of a radio station if ( $title ) { main::INFOLOG && $directlog->is_info && $directlog->info( "Setting new title for $url, $title" ); Slim::Music::Info::setCurrentTitle( $url, $title ); - + # Bug 7979, Only update the database title if this item doesn't already have a title my $curTitle = Slim::Music::Info::title($url); if ( !$curTitle || $curTitle =~ /^(?:http|mms)/ ) { Slim::Music::Info::setTitle( $url, $title ); } } - + # Bitrate may have been set in Scanner by reading the mp3 stream if ( !$bitrate ) { $bitrate = Slim::Music::Info::getBitrate( $url ); } - + # WMA handles duration based on metadata if ( $contentType ne 'wma' ) { - + # See if we have an existing track object with duration info for this stream. if ( my $secs = Slim::Music::Info::getDuration($url) ) { - + # Display progress bar $client->streamingProgressBar( { 'url' => $redirects->{ $url } || $url, @@ -588,7 +588,7 @@ sub directHeaders { } ); } else { - + if ( $bitrate && $length && $bitrate > 0 && $length > 0 && !$client->shouldLoop($length) ) { # if we know the bitrate and length of a stream, display a progress bar if ( $bitrate < 1000 ) { @@ -612,19 +612,14 @@ sub directHeaders { if ($redir) { main::INFOLOG && $directlog->info("Redirecting to: $redir" . (defined($controller->song->seekdata()) ? ' with seekdata' : '')); - + # Store the old URL so we can update its bitrate/content-type/etc - $redirects->{ $redir } = $url; - - # For any track-based services that use redirects (e.g. MOG Australia) this stop will - # cause each track to get cut off at the end. It doesn't appear to be necessary but - # just in case I'll leave it here commented out. -Andy - # $client->stop(); + $redirects->{ $redir } = $url; $controller->song->streamUrl($redir); $client->play({ - 'paused' => ($client->isSynced(1)), - 'format' => ($client->master())->streamformat(), + 'paused' => ($client->isSynced(1)), + 'format' => ($client->master())->streamformat(), 'url' => $redir, 'controller' => $controller, 'seekdata' => $controller->song->seekdata(), @@ -640,7 +635,7 @@ sub directHeaders { $client->sendFrame('body', \(pack('N', $length))); } elsif ($client->contentTypeSupported($contentType)) { - + # If we redirected (Live365), update the original URL with the metadata from the real URL if ( my $oldURL = delete $redirects->{ $url } ) { @@ -648,7 +643,7 @@ sub directHeaders { Slim::Music::Info::setContentType( $oldURL, $contentType ) if $contentType; Slim::Music::Info::setBitrate( $oldURL, $bitrate ) if $bitrate; - + # carry the original title forward to the new URL my $title = Slim::Music::Info::title( $oldURL ); Slim::Music::Info::setTitle( $url, $title ) if $title; @@ -657,12 +652,12 @@ sub directHeaders { main::INFOLOG && $directlog->is_info && $directlog->info("Beginning direct stream!"); my $loop = $client->shouldLoop($length); - + # Some looping sounds are too short and mess up the buffer secs # This will let quickstart avoid buffering these tracks too long if ( $loop ) { Slim::Music::Info::setDuration( $url, 0 ); - + main::INFOLOG && $directlog->info('Using infinite loop mode'); } @@ -675,7 +670,7 @@ sub directHeaders { $client->failedDirectStream(); } - + # Bug 6482, refresh the cached Track object in the client playlist from the database # so it picks up any changed data such as title, bitrate, etc Slim::Player::Playlist::refreshTrack( $client, $url ); @@ -685,21 +680,21 @@ sub directHeaders { sub sendContCommand { my ($client, $metaint, $loop, @guids) = @_; - + $metaint ||= 0; - + if ( main::DEBUGLOG ) { my $log = logger('player.streaming.direct'); $log->is_debug && $log->debug("Sending cont frame: metaint $metaint, loop $loop"); } - + $client->sendFrame('cont', \(pack('NCnC*', $metaint, $loop, scalar @guids, @guids))); } sub directBodyFrame { my $client = shift; my $body = shift; - + my $isInfo = $directlog->is_info; my $controller = $client->controller()->songStreamController(); @@ -711,7 +706,7 @@ sub directBodyFrame { my $done = 0; $isInfo && $directlog->info("Got some body from the player, length " . length($body)); - + if (length($body)) { $isInfo && $directlog->info("Saving away that body message until we get an empty body"); @@ -733,7 +728,7 @@ sub directBodyFrame { $isInfo && $directlog->info("directBodyFrame: Saving to temp file: " . $fh->filename); } - + $client->directBody->write( $body, length($body) ); } @@ -749,7 +744,7 @@ sub directBodyFrame { if ($done) { if ( defined $client->directBody ) { - + # seek back to the front of the file seek $client->directBody, 0, 0; @@ -765,8 +760,8 @@ sub directBodyFrame { else { @items = Slim::Formats::Playlists->parseList( $url, $client->directBody ); } - - if ( scalar @items ) { + + if ( scalar @items ) { Slim::Player::Source::explodeSong($client, \@items); Slim::Player::Source::playmode($client, 'play'); @@ -801,7 +796,7 @@ sub directMetadata { if ( $handler->can('parseMetadata') ) { $handler->parseMetadata( $client, $controller->song(), $metadata ); } - + # new song, so reset counters $client->songBytes(0); } @@ -809,7 +804,7 @@ sub directMetadata { sub failedDirectStream { my $client = shift; my $error = shift; - + # bug 10407 - make sure ready to stream again $client->readyToStream(1); @@ -821,7 +816,7 @@ sub failedDirectStream { $directlog->warn("Oh, well failed to do a direct stream for: $url: ", ($error || '')); $client->directBody(undef); - + $client->controller()->playerStreamingFailed($client, $error || 'PROBLEM_CONNECTING'); } @@ -830,13 +825,13 @@ sub failedDirectStream { sub shouldLoop { my $client = shift; my $audio_size = shift; - + # Ask the client if the track is small enough for this return 0 unless ( $audio_size && $client->canLoop($audio_size) ); - + # Check with the protocol handler my $url = Slim::Player::Playlist::url($client); - + if ( Slim::Music::Info::isRemoteURL($url) ) { my $handler = Slim::Player::ProtocolHandlers->handlerForURL($url); if ( $handler && $handler->can('shouldLoop') ) { @@ -870,7 +865,7 @@ sub canDoReplayGain { } -sub audio_outputs_enable { +sub audio_outputs_enable { my $client = shift; my $enabled = shift; @@ -938,13 +933,13 @@ sub setPlayerSetting { my $value = shift; return unless defined $value; - + my $isInfo = $prefslog->is_info; $isInfo && $prefslog->info("Setting pref: [$pref] to [$value]"); my $currpref = $pref_settings->{$pref}; - + my $status = $client->pendingPrefChanges()->{$pref} || 0; # Only send a setd packet to the player if it is stopped and we are not @@ -953,7 +948,7 @@ sub setPlayerSetting { my $data = pack('C'.$currpref->{pack}, $currpref->{firmwareid}, $value); $client->sendFrame('setd', \$data); - + # We are now waiting for a response to this setd call $client->pendingPrefChanges()->{$pref} = SETD_WAITING; } @@ -971,7 +966,7 @@ sub setPlayerSetting { sub playerSettingsFrame { my $client = shift; my $data_ref = shift; - + my $isInfo = main::INFOLOG && $prefslog->is_info; my $id = unpack('C', $$data_ref); @@ -981,7 +976,7 @@ sub playerSettingsFrame { if ($currpref->{'firmwareid'} != $id) { next; } - + # We've received a response, so remove waiting status from this pref $client->pendingPrefChanges()->{$pref} ||= 0; $client->pendingPrefChanges()->{$pref} &= ~SETD_WAITING; @@ -1003,7 +998,7 @@ sub playerSettingsFrame { } } else { - utf8::decode($value) if $pref eq 'playername'; + utf8::decode($value) if $pref eq 'playername'; $prefs->client($client)->set( $pref, $value ); } } @@ -1067,7 +1062,7 @@ sub playPoint { sub startAt { my ($client, $at) = @_; - + main::DEBUGLOG && $synclog->is_debug && $synclog->debug( $client->id, ' startAt: ' . int(($at - $client->jiffiesEpoch()) * 1000) ); $client->stream( 'u', { 'interval' => int(($at - $client->jiffiesEpoch()) * 1000) } ); @@ -1076,7 +1071,7 @@ sub startAt { sub resume { my ($client, $at) = @_; - + $client->stream('u', ($at ? { 'interval' => int(($at - $client->jiffiesEpoch()) * 1000) } : undef)); $client->SUPER::resume(); return 1; diff --git a/Slim/Plugin/MOG/HTML/EN/plugins/MOG/html/images/icon.png b/Slim/Plugin/MOG/HTML/EN/plugins/MOG/html/images/icon.png deleted file mode 100644 index a722e70616b3632130d3d8850ed2eeae666952e4..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 59426 zcmYg%2Rzm7`~T-02glwcBgYD5M6&k?*^;d6vL$=Z>|`gKkgSk|ki9}e_TFUg&F?wUf7@9R1d8fppzxHPyB1Q94H%4tCm9Q-dFLSljc9D4jc zhag;8Ygt(hC0SXNqtmOG)^;x-$YUboiMwvX38lpB$qOPKnX%o9WV=9!iMa&XMXdLc zI36;6Lrf~w9ukhLLry`GuK2VqXc$*9BYYv7y~a{HGF(6EsiDv|!&E_)uh-7j*2U;W z{c8H^%?Z6IN-5HXQ4h8T6lw(f=iIECUI0!6&i(_)F)4neyZo*sTK z<&u}MvRB}79f!GDNIU0mf`4e-c?O~4pgAVeRXkX%7SzS9-9-*{QA6cVDlKrKLhO-w!3Be)R+hlzhC>4zC`-6>hg~pB{HHt8(sVAu~ zS$XgQc}>zE8u$I(xdq0B7P|@=Qz{PPmpDsI>B(u#WQwa8D!=g|h~{jd`C3Rac6DWG ze&yw=-OgeA$7|c&S4P>3jv8#IQn*6q5@F+svK{rUs?||zq4PG2XY{}2(c>+NR zjb|}Z_h3+8a^4~Y)vr+-eomxP=|Dn|Tz25ya%pV*X7Z9I!t`eP`X(y4DVCfRK1-7r zu?*Hy03B`)^?T`C+D#&3h^lf7nrxEPdE#%)yg~%4bEJlW8;(?1Ce)cN)b*h_2NvlQL zPo1Ai4to~iX%{~wDmK#b{q*Q*rN&|*&R@#n;X?YSb%g?7ervgF#c1?v8t48rz6v@w zC-@o~@+do0F<0BXy5m+fBV`BQi#=VL5hopt4Nlt+s%8dX&o))uFfVA`c)V4f$T}Cj zEO{CCGGpa+AB?c0Vs2M>JpJ)+#@__Ly}2;BB)KX^Y)W62`jrY!cuoAAFxzt4qTJe; z*l~5tkjmi9I1;fJIdR4LA@D=o`ZhjtJMM2NaI%4|9*G{_TwH z3@`}Z=SK>}-m>ErGT-fuHD9y+U|ln?`)2+#NsrZ!xHhZY0i|5FnmgNJmWvNPs3cyN zyrMHJ;h%XiWh-WCX3aO5*Ou45^pm46(KhwvkAZJPF1@{7oKq!ZCEc6vgoo2@GfjVv z*H2zXhvdtrw?B>&i0XVl@!qEg^S$c(ladcQ5;~qbM>?Y=N+X6FoE!Ed&+Ig4pQYia ziKO*>|6H0@+UAyKKTEzq9>X!narDGtf_L+B6L&Lp6KhNO*z}n7*#5YmzL$=aeniwo z)W^xiwb#wgv3?_Op1-xQt*~|dnI)}dj&sJrL#j{8u|1^8Zpls+~zEI^(qwRj)D!wCqxO<|z@Va*~qadxIh{ z&*-Azwt8jyoy?K@_Fa>026GKP-X2*EV19KPiHFYz2A}Ha|(9h zH4N6bQmD>+$vN)w6>OSQ88yC>6K zOz)vytdCI02h!xPlFPv;84Vt4_2mKwReG}hH)$d2@;^UDf7Gm~u6ZmLzmlPobSyM2 z>MMJ06{`Nowc`Pfy4F3Tkpw1Nlo^JVV^J>yR*OziG)_;RRUYffRBWbt&zCVALE$Nw%>l5vS+$>%Sp z<&SDs7M`gY5BnT>+D}Z(Jg)w=wV}9?x8YE7qSsjVxWPtx_)q9AK`HHC^fIln{o@&z z950)rcYWXbe)VBIG<~>b(EGh)az95P_t-1L|9OwjRcUec&+$7GBaPGU-Uo!i^c12q zJBge1Rrfre**3`g9yttd2TjM&n9=@dT=AQ(QE*pp&y4ZT_r^Y!-`TKnd1W_M*4X$J z$6iYy`nIBo<8oTStBJI&RQD=J{kw!M(#y?kv!8st7d6&$ez#7w_RXgFS05oa6g}?^ ziZr^GaOK)(i{~`#ti8?6pf{pd_*s49x5K+=?%Me<=2>QT2D|v)*-}u#Vu3|LW`>%i z_}`Q%&l8o)6x-*T&l@ad`!gk}T((uJjZeA{W>#aK@76mWGM$)BvQ{gXihmJT@N>MH zS1dDlwejMlXX}~R7neiT4bg#}A^&rmreWvN(^2l{S)L6$9h-~Ikt;;7S-SQ#k26h zX{WfbOr(>!lcD~%r}*Bd%hKPIzB@HLKV~}{7*g0TrLHv&2F}ep3;w8|NUZu?U-F#1 zIk*TAW+efv63a|kK@Pe>|C`-VkN~dWI4bHnLl7Pr`VS0BO{WGIv0ao@<*_HRVR)is zxNG$i5QKu1+++_j(nz`9H?dLp}$BlCz)S`p5|6wLl0l6}Ia*gk6d<9DyR z2KUmspFjP7xi?mA|NFipZ;&3QLqb9>Gnm~g3x|=)bfy22%GWCNy!H!d@E%p_ds3uz z@%-Msq{qX;7n|#zGq3St;)hZTFe0-vwJnl`mC8x-E)aY7HrF$0%S(ajaWOPwWLY zWduu5aU0fH;NL=?Utcvg#5dCOrKqq)eiRTCbgwTiR_?i+?O7m?;s`={?~*9qT~Jk1)7nZM*v9#M0X1edlOUvvx&Wm()1v-(e;UVbhujn+lh!yMZ;j=Y+6 zNu){;OXn|Myij6>DM{BxMBoHkU2>j2;8cnw?7FH(Kpq!c)ky_v88+W0x1x>8p0)ag zZON@v6Jz1*)?#D(gLOG}3z5zdknQ?PY^+wFNVu(O-yJTspZq>O(dge8+z>|o?8DA! zEM!K)&cmZFM54$(sH&=p!`aF>r*4E@Bk8--jLlGTpBlf^p}OvU_74U?KVt&|SON_& z{yR9^tLATh95ojh{{A=2JKf9sIYB`|wiy{2`o|-Iipb_i`>BQx;v53sFitKl<;9Hn z&(#pY#2ZQJLT~+}#s@#PO^5NW@nJ0tJ&{k6oSX55c42j*3E@K3K(u zdls(nVEVmhSG>OS8un>(b5mLH)l5A_B$Bw7ag=c+K&}>Z;hn>iCr`*v^C$cRym6u3 zPRVP{;hCA41xF!$gtUed#NZbmfVygYLw?GPI4FT%OU)3iISazjN1sRa$2-{fPQb{H zdTznZKK3zUEx)P%`sVt=EnjQ=YArX$?PllZx=b7*$v&|xh29|BCTWPDk*8&5eu}#Q z>$fgv7&wJ3g}~3Cva4$zYQ|j!+!joC!fUYo^YOPQ{D|Rmqn8ZOZhrQ5GK9EbaHjWG zQ~P~ugbCG0x@$>*s;RW-j<1Oes9_ujyF`RiX1ctxx!M>hE&jXiB8uD%gL|c@+(BIQ zUM0%pl$LJYsCK-0eB{mYZz7_`L9A>&M9?L48~)9n*NAPzmNQgQEFTk`5~h_HI{xk7*_atyE660x6lHMO_cxd=Z^C+9TMB6g2&R+6>}K5vL} zG>bDmdAC$I?<1g#-~7|};#wK^_$2tW>lN<#wIb~7tsysq&q4&-_1Sja&TnTt#V5EC zqT)s@sF(2j4<9aLT)(YtDI1yn`}eO+<@g9p<7{ZN+g7rX17>Awd$Hs4K=K6>Zx_E- zQ-pMAY!rUAe%@k(IqX71E}*Np?B}^92MpE3bH(LL(DX&8nCIm9*a+_>fr)8FffL(*5Sc5(<;{NQ#Xuw+pY4et(9!{A>o2T^4n$Q5XHq8ki0#aVy~y9kuO}Mg zRcV-xuEH?3bFLMP8jmMzZK&>lH)8N(sbMm3g!G@S@ygYU#>-`%;|r;s7m*0h z@gJhMW7wGPNsX=j!n%wL+oRg7_B?pmH#Ro*0Mc&uxd1p`Lv(r2dsPwS_f=8$>mn9q zjC^4HGd0SZe6s=AJvP2Vdk#hNQ-$-?8q94n*sF3~7}QZzRFtKbB~i+pZ1!b6$;`Qi z1;v?{o}Nz5a(1PCaDsgug!+6be^{BlqrMS?Ra0w#B16i08D-T8o*oJ1D3uE2=;_m=hC~FhG87d89=1dT#yF5beijFPK~=#|0c!gB)f#exvc*s;sYM)v zd6;{)E%O5DI2ilmWNw?#fnwg?Kx~cychekp=2!}Is7_;r4mjc0iUD#LS?=Fnvy&2& z6QB>1*!8Lv=3lf&R+_~%Ov{Z@fKL^^q|hzl2u&Aq+;75>`}<6;o~K($4S(XMfv8Om zlA+_E+pjBmh|=|uLMcP=bOvJ1`}a{=Si5IKQj+^us%Zk&9i5#RL5+so*tus<68yyi z{duu*o%UlLP9kqQesj^Lr#nV<*W*2+kSCA8L-h_}$Oiebw9Orj8sG4QLLSo&?oy;0 zdU|J9DGw}NNas$zh6HuFVzeU=tWetyLw0@HE`ZgYB4&km^+6Xf|LdkKtE;#me=)Ts zWlXc!wE#giR1Id3fv}L$yd3NC&KqTjY51`9ii;KY_F>bj2^6&WL|y#o$oS^`ndl_~ zlk^hr{jE7F#+{jld#CFx47)c&S=V{z>jhbvdtMExCUZQ=J(vR3l9VR>f6cvwBj(;C z)$R!C&C9V%jH+V6!jAv{ugP_;iN_!0`C(OJ!DBCfzhnK z9X0~yvW<<6tX&T=*ZH8Et4Nq8%!4xzMG@XCgH7^D%YZLbFfuYo3IMAmzyB8Mx;<0}? zZEV!BDBS8$ z%<}YF*;s47T}JQ);{_90I40YJbSst(i2=H6vf_2gzup772whTQln^m1E9>m6q*NCh zd#rsW|2?Tg>_n9YQ-+}%(VhC-!F889R_u#Y@-s%sfgpd*o%|3ADPmOKT#J%+mi!Di zEQ02z<79l-8CelQL}uOfcGlY?riNEvcT#%)Wk(I*atMdtnLnv5!)>EGCoh_gKRgcD z!St3K8&i(iCOGt0ioWsGRk@#ZmPvKObpW{73%*+}BGZ7~d9GHv&-#vTUp>p1yAY|r z@l={+4D=uLxzX_R7x-n&os9R9J(OaLf$NWgEvDkIe9GgMOBY-7qN;mOClU%BQdAlQ zt7+Q(qOF{-bB8Y*+DPdFUFV_&XHG%5EGOy0TOzP`ALC%+AP*TKhiY1t^8od~{(x*0 z_r08yyS&y)xVdVQyv4CkcJDL{<1*zoUb!w3i|tmrm19e*lKR(e+8C(GF+xg_lsLzB z&Mz`MOq(F_2cq7H=daCC3ZN*qu@;!I-T(rO;d9uRjL0AiD2OlY2^J=+c5qIs1Ns`n_pPF9K_mMS_>3xY;2g-lLWPfYRy>$ z)Vg(U_Gq5Vs&K(q=nwa9Qvdv$5OZd)9R3Dzg}|K4pOkrgM>ulzfFF-#jbC zgAAH;t(&g09sN>T@3j0&$oW-ZEL07oRd(b0y0yUIZ08{nR8>{gh!M*cafnTOsGxQn zC6LcUUE_QY8ZCOTl`>dyH6@aevZdm#EXVH(ecZcHIPhb?*R)aAcx@@buHlr5f~dnn zLnT-zBwu5e%gKGn%%t2-!(U0cdG^I{+)5zLoo`M(_gY@_koQUf{(BF8^SK0?E5PL5 zZEdMzXXxq43a<7YsP?R={Z1JK$(=V!odi*holJ;71M+9z-3@YbauRWvsrSSq6@;37 z;t&X8I~JC?t?F5w)V>wfHp`|ONO7i7-NB5%;35R?I%*m>_TS2akmP1ijcI&JWBTv@ z_rG6MKIv1*QnM$lzADUA%iz|THF*q&RR6mw6wbpiDJ@Vz)@{mu=s z-U6us-@B^B46w1WnL%0m+f^`_o$suzAsx;elar|gcUN6uLVtFD&i7}o_y72i#Y6F! zUBLhkWrRQ(@}aI&zrv9uCQ%5_2BVAJ-CgY1MC7qo!^vZq@@;)92L}g4HcMLo*dZK~ z6cx(Q$Ml)sGuZD*eG2fqEA~pn?r~JRT$V$B=B9-1=AL+AeL=pBu#>;NO4#6^>nTLw zx$d2W!02V>_m>xeH@{X^Ryr~>GYw2|;joinh;`jxMpdB#0RTvzpdV!OogvAD9dnARVn6@BE9*!*8p?WokqTPNt?vl%}=oAServgj4n z*Rs~y#zS8n>7={GgC{Mu4(0vrgX;b_7qd48k&~Mzn{M9B7uAZ?ZND=_og0kGR({sb zuIiddj@*O?ZGVJaIpWBPAa1yH?1K$>NQE5~adGC%yGoUPvVx4ao)^6iTf_}#>AN3$ zCOIWc%#PB4U)tTzp4;=_7j@t6-CA3_54?<9XM<~NR?ku2nG?F3MlNGkQJtg^b*O54#3%(&t>~1pqiG;Y5i}^@z{A-4<%~;_;oQ~`iM5aWg(buxrT<_N=)tdLO zN?Z8SmOPTm4!@A%h2mBYurEZ@zdwEsYhHj1xu3BHC@=9#sS;~Y#q&nw_HC)N&Z#fK zSEG`ZKQ;&V^GXo$`AQ&)%Bhg2vc8hqkn!ghiRKBi--~m8G}&1KR*=-xcYA|O;CVgn zrG{TWg`*0|cxPaKQSCmI=oheTmb+iRe7S%56rbs3u+_m08GTJB2F8v2UQK_j@>hXJ+qo;G>r;xOXC!52 zt~uwuvnNbk9;@o`b{KI_wj|Z64#^OA<9gv^jY49OeyyYg^U+WtJGc^rb*hl*G(sV zm0~c#KrW1z%f;gp+SQlUI5IVbBm`P)CsmwoH-=5;J+XCxv4>-g?T%8x2{6KQ!su8{tWv$@ogW7Y*DCgFQ1!$&PjFys5+ zTeV%RWUK0YFiE2=f`HW^6w)Z}N}(uNgpJ;(a=B%diAuu1rY$t*O;`B30EZCHTvP~N zR+QoCrINy~YSk*)kkaJJT)&_Jw-2||`~lnZW=TC3R5}?B1}S81J*PcT-(Z9q5{722 zYIo1TLojg9I}t%MQm0%U-uKCr$?K6im}b0n$Wxj5mR08e?p4VnKjZ6@1J?iF1+BWP z>uuGUsPE6yp7(>WMwSFMT4ES0E|wkq#NNfQ1Qn zhxBKuPzP&Jb3gK~&?cx+PF8{almeRpzQMqa$6;2Fm9e^ZfY}n#ugvXp?T9la8F&07 zyITVW!@5~#? zzIPzIe=BP>DoDBL6^+k3r+Mdavf?EJ$gHHQj`)3m#y0XNam}tO*f3c`zGe|(GFbSi zrfByacw8R1LyMq2w-4cKy-lVL=b-%Hm@FOyx6jZ@%PMhlJ6~u;jdPoehli0*IG*t) zI)2(%cZHNY#nOdQq0ShDP2dti8eWz{y)jjCc;Uo&x(j$_Tqlj1s0!hF_e(Dqtcm4hy`Zzy`Ju6FbfzEk%s@{O z?b(eJ4Qu48n{dYB8ke0sn~s*;>P)LA(Jz5CUO~aI+FIfB36uLxR{UCj1k?KNr}dTV za+XhSISSG3dNpX=O>(T8CO;h)P@I&VkWoaQF$Sn3gTb9;aL5)v?~^a+w4hfGZw zl9Q8XXJM*kaY;#-`C2$hk1?f*+1Id{u$HdVi5=%wDMVnnut8 zso*@tc&depvKO3u&OA?G@;Ri`b$2s+^*AcEI@p@Pv$RKV*(r+Y+(zEl9O*U0B1w*Dz zPNZnN`w&Q$?Ai%j6595M! ztu-VJZVnh2(40^MYX@5d{juUt#7|62%q}UxSANeat*3VjOvMx$G&69->T?OLIqSVO zk1O$xTo*x2adxpjU15YN==8pPfRBZ7hmDO_SU94hf*1F4XlMu=Pa=8jM$NPXbYh6m zf^_aC!2&gKJ2<5}zQ&c0#u}yhI{3fzwZ16Pxu? zf@4BIgQutP-Me=+G&KYF_nmAwKzPxe);jLT5h73&@8ByftOG6Z#%YJJW|JmxF56SJ z!OhLmZ)0N2&CKABa^q~eni^F$O1uLQvr1)KL{qKfC{pXki9hh*zVBQqhy`J1VmOK{ zIx*wpdiX>{vgGnMTwoVIfBxJwn6F%6H_oU1>{%{2o3pdCLw%pz)fOdbQlylS$!}Pe zaww-X1V5T4A|RLt#E_bSp>u7GeS4+>FRWGCcMte;Sl4lLB8Bf8snspV}gu>GV zUwteqBU)Ts)b3$x_JlrjWG%HgH^ulSFe4%l3x?L;fGlV?YX-ah#kldE|Mfu@`@MUz zuC5Ptz8FS)`gBM8>C>E=8ln92%zg~Q`a%J!HVgMV*dE-4-)gS<6hPCiR3|DYhjFUr zHHNRRFZJ!)9h;k+n{F5NKCUC_>X(yv4W>#s;omYgEypELdpKG*;&B#b#?}q+g~yD( zn@+VR_o>0larHNSx}^VYnrJLnS6AHzuZKD%x`F@x*;w(X23Q+J_}wAAG}LnS3*YmC z2saG}tt-xY`m(^byn4k0T5_S8nYY1#w*pXEcWD8pdfs@&tZE1RWdx=Y28#m@L@fV0 z)A%{=!k{HlA_wtj4WhJo#q(f@&pPICB7#Y+5S68>wDSS)C75Xc{y%PJe;ccB&)2f{I67#`AX&90fuq4+W|V597`e?(jtlyyV|D^G(q-Cref`LBF_)Zb`>XU?P+XjH+p6$= zI>NAiqFPA~1r;RBRv6`9>57+^$1-!piiCq7_MNrjZ?O|VCjw}~p^=3Z-q+Xnxv1!I zIBf!6vPJwaDOlTUVL~BIB9iWw#kOGO`WFCA1RbWNEG^l9EfKM|_ZrttE?0-0oSb@* zK3|>ROUL_VpcZ#B^Syg`PPO3v79e%D%FM$PGqS+O z;j$T{RhXV^)@8F^q8$9KeQ~epWo6$5V-nI{fOh!1>K!spN<4zUX=!P}or{6&k%@AO zw)DjN%{t0&PdXHB*Z_j89eGjXV}ZlU&CSK=dC%2~K_nJ{ecJ;9QO#>f3hD@cx$3gO zqamZ$rnE#z85gKu36`w58rHMi^5+jR_wyfbdXjm}O-vxKhD@6#P_}Gh5!8!Sw}@kt zg33V)SF13u?BL)))6np3ON)#ZKMjDuKOz?(AKCBTmC@0m9KOb>Ve{sS)#_-|0$oHC zz7#8A^V|U>fX_Pj9kZ7&vAh~?KX~u}R3u1hhy`aIl|uH0PVPc%P^%*^>*-wz0;Y5j zdC#6vIZV|A9v{1v7&Xw)3fM{6+TMGttjtVf3u1LJ#gWtTVYK(zU&ER)qr#WFEntf zsS$py%$6sk{eQCnm2nvv^nchGR1Fko5nhk1?;4>P+d@h;boKQkU-Y~m@Q$_e1(g9T z3fO1*=N=#w+;?W$L2|NUu|&rc2T856%ROK~GFm+01BdtM(WC5Po7cyidi;Wdi-Fj; z(W`mSWe{3Y!r5pMgdv$M2luUU=);b8#4Qr6ZSp!&ptkeZsBDi55=df1#bEQ^~yuZw$0 zlI>VTaE|v@o1OgdO%tdL{0|>$nwjv^nDW!)jBfCXi${aP24cqC#s(LxBHsZa@H4>P zpFdBE=7Cg+xLN@jI4{7D;Akuu9MrNKD~H91?IUSuY2y+SWTzjrX*HL|7{=!hv&Gv* zRtRuv;svIi*2>{s0$>D=OAF{@I4*V&^!D{>4n5f0>ZIxI^oxmZdse7TzMIUc7X*;# z@5so8@JUFC3W8Bd)Q?z0og@BRPM)m{{3M`i*lm{yc%%+S(Sn z2G!sz%D2zYo4$>XmM$0?s3hxp-DzjsCWAHhSEhPWHO64Xrb@_p1s}NhVPOn@)c2l! z0dEcSB`lN=P=0B%zRqpNPv6QWo~%vc#!W~_xUkS3CFFlC=H=~e%9k?dDCG0axA2&* z^u0R2Z-`9b=BC}!%F5u6Bk<4X!a{Vp<);8^x?x(SZ;b^{OTDGaNphyDrg`Wgc0WZ0ygsC+w$wVW z{+XDF1WlxQbdBt9|?a-E8u^y>8`80uX(x?FdJ{SUu>6##ZDr+IBhq_$)*htL8!Ev?K6^9j`Av z)5X0h#l^+T$L;<;>s<*}_@S$qFB~7He%bywpA;h-Ng)$0XFr_w&CJudvn1=W&jco!l7zhMwJa5uXF9^1!w0t{%^1JaK}V2 z-;ggYFEjG+0D40(_RX3M5F2#aK0G|kuCAtX-<}HWwg{TsIxZ9^w!R_}@D9*LqJQMw zJ3<#%S52mJzbh|&<$xGL>@^oggqava+icq^B1m~N0dKbjFQNMl80wfCt1t(-^wIS! z;#0@@3Ufvs!UDOry1WcLOhSO}R`&Mtva%SFQBkYIL&-BdaWJox$LG5HY|$yi2-x`e zI67hhcM@=zQj8LFb$4Gx^Duh)kan{i3)G+oY$Q^yV`SOM6zmv1J^k9|X4^_%+TNIX zjOlRE(}NXca_=1iJlH;mE1e?~LwrJ1dt-UJ8rv7c+Mw^>1<+L;Fv#;$B`=-4CLUL! z@Q>jj{5bW#B8qiNTT&m|{`vd&4Zx1i+C`og(l#9{2ls0!;r@EqpfcV~ii{5|HK;bT zv%{aBo<;*W$Up#10KJA4v`e<~WwE-0A%!`^LsDgvDWwN99T2>_yc@|vXWF#>RJz?U&V+@tFwm=i$X zAJn5WlNZ#d3R-3UIg@uoy<2sI4QsytX5C$?v0Six0q_)TC78p=O}bDlG@^`cr6B#Q zAaD+L22>!^zSM`h)eiJJWk%5eL_8H1znZkJzWNsr^Iuf`C5jg$g@GLX@;nLvoQs>= z0^rzy(*o|EorJ{stgvt!k_uMq)4p%{3Y_Z3)j0O~59mTd<<1c=wiQ=n zCbv5JC2!t>(SikVq7v9Wa+SJzH$P=^q(Irbd+%O_!?Z|=apP^!Lj92Zy_3>&-dWUF z3C6Lb(vgLT;5PVst#37C93K2SVWJ zG-Zgoms=0r5j$H-u(GjPV0|Ne8a7V&N^7DM$U{n?I|;rwHZpX+qi>BMWBX4Nrj-sc zaO{$y(76BnIlX}0SO8c@_Lnce3&`(pkgYT|dI&i2y|;rHD{vE&l5&9FB9gqkk7fr4 ztNppf#drw`3H-n1xRb-SXiP9Lmz{(Sxv4rjIvm&jpfhj|G_pIu!Zo$E+bGxxFQq40 z?Tz@iRL2(sax2((L%w=Y(v81~= zu-2)Z^EhfTKseTN`(uL6KCp$KKYxCDeX;WzfGIV8sCNQii=m%&y9DXLET|e>2H!R2 zESWo$M0ATV{ImZV5B~CuK&+U|THq^VP|a@q zD<&0>QQj~sJs~#GHgcNyQOI)N9PPXFM_xHY>yH2aa!md7rLA+@YFUht+?G{5oB1%) zl=TiF`*vymbZLonOC~n;(p7|hVsi2|$k4ZM-vZ()ouhI3!M1h03+F@Kv;!dMfa&Ju z=Z8>Qa`5n+Sd+p0tVy!8HwCT77ai61nMa7e8+S!`@wANdvzTx(t&n(#~}Gm zHjL2J!UEw|mtk`i2T2w`T?S{8lLG^L)Xp3u7F1Mpm!Sy}yZ_S9dJ-fNrYEqs8jNDV zs1gtsfW9>YDPA^;7J|Sy`Oj5pd09E|FT72At6!3u)8sb;JLzDy279(vVeJ2$qI|80 z^mICMO3IJr<;0$zo?ZR@3Q9^i06F8bvSI)b0te#>fuNy@L3m#r0ky?F*aw>z@J(tJV;0Av6bLqkWKiP$bbn%F5sqW0>BQ^|+t2 zqfUC@Hew00so;De!{_SiO2}o6SS>>YGeb=QBadnLJmQu?sZK7q33}-O+W_a-_iV}{ z415auL8P6|&d$IVXm4vneADOppMeEDEHq~Z977=5hDK9!7KVSzg`7%A z2X2rf&yFG2*Sn z)8hsso+=R_vOzF^P`Q&DB0a&D+ykB|0Ek$i1GTlav%h{N1v3O_^$e^Nz%;Y8Xn#mX zL9au^4*$r4a?lb=K>ZckQPl!S~>m}SVrf1!0C&Agc1EH7^KDD;o+Q5pRmySgo7hczf?__ zsp;ic0BMUAowsG5JRt+Y6+*_QrZGzCy`~{F0+|sEZ5)Bp-`drM%=EwZ1Ss3ynUT7fwmc~`RLtxCfe0X5@9rFzT^&+sm zz)^vp`dC*-iyHz5Y8Na3O$2>?#cgbDHTCq?pNLs9Mg)-8fVm(r!B_6SV+2_J0vKvU zQ{hjaJ~7>8Y0FeKt6pb~o_bPLa*f(uK*9Ex zlzgs^Mr&R#Vu3`^zWY=zI;=#Ky=2V)XwArKmh z^^Z?i(IhsLO1f(mP?HLv9?(nplY^E9V0L|<;|0B%aokt{aG!kk+Nsbw3y-HhU}9pz z^JLXqbLCJvFG=G`D8!Ol;e^-L!YN?=BYwcDZN4>Z4@{Q~YW=*Mba&yegfubQvRUp) zUhHPeY6F@Q1pnN@jm4v5>{Meg;RM7kSceqw%+Piakkr)CkN(vquk_>jEg157{;a6L zv^%T=MhkEWfP>4+#}^bu#ls{i$?*S~a^Rr?3bt6y)lHsbQz;aReoP{D5$9ZTMQZ>K!AG6`^2jr=QTS5MpfgwL5cP( zfvbY%j196r!Y=cGs%Pe0? zPy`cI^6K_0sYm5F`xASwiOqc#zj#{Cm?gZsh^7_X1&dwW9IpiZ41SM$hJ9HdIx4?@ z1ukL?m9Y;AfOC1k)~2O z-C8GBewx1e(?~h2w_isfzN}xrewDwPmEh;+pX-WaM(Zsw($jL6P34QMgeYhs0jYpC z>UbIsNrCdM$R4Rs89mN*&-(P}qP?A;IGv8gEvLeF(@_Ym$9(p?*#OFZqIIrm_5(`- zgM(rAjN&r)zfien{Ff2GoV&E`; zu8q~x_Bk;^WEId*P(lF?WSr-00R-zH!xUT)?*WKO#Bux z)oF4+8<_sd+|OG881RaS;HLFG8mb-h*=#2-%-VhrlliseBn-kzoLRbTbP;$;fUh>m z5{bV-3jshmz@c-n?VjP08vbzMF!J7d2+XgAx}Be`Ks=dL>H8K5h3W?bN91MYEdY1G z8S+?8dRFebp_3{edzU33*zX@65qznM;r(|t0TnRaa(Vh%w^WB=){dPqY#$J9z_u1Z zh;^dnM0|X_r^4YKS#K7;2M>b3eBnm-0f5Lx7f+KYy2!=;wtiOEH<%KnK1y16qN1W` zLiMe>+8l^ekJI@uG;PQ_z#&ydAf?{}yeS^`0G~xbXB*&>ISmaNI_mf@l43hB508%K zdHm1t4!y)P{m!j{&c}TAU1*oBu`#zw#tDr=OKnU6KH%MbK&qTBCmWkojx+t*VJm!Y zQ$+)`XA>AqLfbkQA1o5)TMw}>0anQX8~E)bX0AW7??RTdvog~dVb8zM`~={EaR7kmGWlDIt&gP^^E{Hrqfa!M=f6zPt8S+z{7=m5*9}>EZg&)qv zIo?ohp693-L`^ru_F~?_yg{SN)yZrpKn^A>f3369Z{Ns)HPK4@sy+~{#pxPT+5Jnj z$w(0}(B~8KKIZ&+eQrx9;)1Mpm<}4Z`^2GdCn1u2X#9XhM~WB$LKkpsbWV(x8KXO= z;spViGjjCZ_o{?GYu^RPnhkCMyjY+vK(d?awJ17fqmV#ufW}bhw1WZwC)S57BaE-< z@jaSHl{mdUv%k)4UekQ$bpLE7J?npSr5V$?_#y_f^lx*-P7yESp*zsxyz|+ru};?< zKb}C&j+Ydw@z`UU@!k=~$H)J}>@$9_6pS-q<>c(Vw-Cj%@T~DXNnJBb&id+F5$$$! z8~@zQ-4mrF4F9N@6=8cBHF;MmND!bInr(p|XQgHTLQdH27NDxp#lcI?^<$|9A{~$o zG|>I{A@W}^fM5m2T?_ox`#GR9=)}FqL6aDK^kzrX$Y^`fYi>e>-S%1IGJe$u5{0#mz{OxwOK)nA zq5_>^rH6h3x{fsf zEC+4z1@JT-;5Ma6B)=z~vXcWPicYgRr`p{P+E>}2uC0_{|9lj`6QugcyUjvOkrQEB zLH1T#PuEVeXW1!T!j}fUsa;))a&gy74?q_eZDk&BPb>F7RDW4P3R;nioiVu$4fKHN z@P}nZudSxh%>L11k}3gZ(HAYqz$Wjuk*S*;tn_`8kmpmBGBUdDceP#QE(5M;E za4=@4IM^!@1?oO%3xE-cJ+R9Wnc26xD_VSf^RUC&LLe4+_=9n;*vC;(^w=pXp}=4C zIUG;`#cgkZP9_3$RZ&QwBRie@#lygd7)B4v-&+JWc3ZTp4;AbUsY?zFjp;^VjGopa z45HeYK>zd4_;>`EAIz?-Bm+ID4&X+TQai9iv`|WGhl(h^p9@Gsrmx^ zF5trgnF?O1si_(o8pQWS1Ipr-HaGloPj@VzAbOC<(`nQRG6^$Q3(+)ta|QEaM=7zw zV}C_G#=S;ULhqf1FfaHLWqWtB4nnF?3ENL zDn*e*MRs;VX(B60LXypUpML+htE;Q4@g3(m=QHl{Iawwl2R?CAD~yjLv7UHTdg<_s zbLuk5RRiudrT6*QZ(c#+-R>HtzpCZ;zXA%;z{qIMX*ciuvue%I$#BZq({uZsyLZ81 zE|1jFd~c$3=GVtc^&kNOad9S~)wh^TQ<}f!P3_pXnlz?}VyNZczJGr^FHdgAS=wTe zi`2yzMWR5?13Ia)DW|x*2L2AktMytRM+TyQNM+xB(9Q$zn@7leotGVOU(c2 z^YyNeM8*jqg|tIkbh`Vmq<&LHy1tOK;VlV}>h0k{phnk>2(9VRX!Q+rv=d zYi67Fl4En=z=1EHQ(4MO*{2M()(btR=-Zn1x6;PwN~!G(UjE`u3*H_s6he_?RJdo9 zr_;|%Ch8Hmz-u_SC=bQQ@l~PQr{hA!Jf>aL6+Hbh4z6w$9u9rSFbq%a+N>$x+bx78nM(Oiy*5LHI(`WS(!wfF{=0 zMCh4_bzgKfEqm?~Btfs<)8<{3rcYs?g-(U$J{$P~#URPns$%@8p1+BquTK48a?)78^@Y%2U71P~q{CnqQAI3dMAec$U> z9P`H#)HdWrJxv)g{VO349VS;kSOiA z0M+ArET486>*CHEEBXDV1z)Ervle_bnCPcd-Zg%dquC#+$*V7Nd!EOT$+E@qWxR36 zH-KaztMsO$gYkB&ZYL{5Q+wC7a{Rv*AoG5Vlh5TU8UUfd(tvCeAP6lE&7k)aflBMU z&!6*XYipAdtkr3GBZr!9`Ij%e2*xN#fg7)#ux=DptYhM7bPZI!J}h#DnhsJ>d(9HH z`e*!J>bs|)ebdalpNxn!<4D%MNFj*t8C%CS1IwBnH31Ei$6D0o&5Gq($%wUMXesB;T4=5KXBCs4<@69qr%DZL#29zOgl_&Md}-`)%l`u4@XJyfp?jo0M6gH!Z$U!8=PxWg&?woHe)->Iix zRt%>5LjVU)?3$P%Pr~(fALLL`T6@ESJFo+x_+f*XJ@GZ##y(XI2RE$ zndJ830I1vuYXh?FBnSKJl4EpqG$=(F85kJI^G0zW@a7{;{}Kv5kRSj;FdkrFoZ2J8 z)yffId`3iuDSo&N={jMLlWB!`uTzWej7mYDokP3>*vD|<@-oN zRb3osGcYu4yQu*udE0?d{qg~K$OB00jHm^Ga^lUi9O-brG{0%l2=!V;ELB+0n1|rjP1r+7v0-)lP zKJ@eo?a7j`G67~Z(ZK@DPn^sY`_X`d{c+3t{fXA2#Y^cwF1U(|0?cvR-+r?MU zfByVA-+#&P&)>go#kAND@-zmBG07?-mpx;7ZAP1uAF1;^MR z?SP4)ZWN_4f&f8sgB?0XH5&so8 z47s_Eil1!;9+u}bKm`y9O;w%xl;{C4qKv>#b&)N6>jIdX?p}YgXl| z@-UZF>32J$IfK4eBm964Abo3-3c;eg(qACTw%?{mwTOobkV>G2? zLlV-Vs^{u%j?59^hvi@=d@;cUQ9tWc->_n-_Nx~H7q+i7`EVOJKNA~YHMX&N)0dHR z(C8xg_GBenS3(&#G?#BSbF>0d$A61}x{E2kzS`)wKF3uY_4IP1-$l@EqrLj4TOFGb z9DQLWGkchWzxUjW^OtluF4_;H`tG|OtrEVA#b&SyWN;MepHLU~dIg}0Y?H5wIIFz- zx53c-yq(fEZ<+e^>zSj_ZH%E7{0bN@q5ksy@lp3@#%5-hPzta6Ty&brRkl~Pey(wZq?+|2hHoUdau$@iz-WeEi1Fx zrEcnkErtY8WL%Dlh`=X5N2%Ft(~bXN07P2C_(8*sv}Jr-e8LIpb39CQpl)eCzjp9T zQT$8Q9MM`o)9o@N`+~1q#t$IQD0=?lMQ@d$=U^Pq`|-=W<6ED)bcSO?x9?T%2=zbb zg5c}p;ep_q;Q#K9hZFB$YdKa>JjNcJ};`5e-MuxEfh(8=)i0)$=2mU}q zrhZgC^cxlc@t-h>MW1^-cH&pm^bni@`^zwv_&x~c^^|z4$j85KHle;vEFajg=tyjV z8X4taDt%WjhK10xTYzP=_8r$ooW|DOCn~V^4Z;+9N3u&v91dnv(Z&3h!H@zStrLR1 zyUy^OSQ>0euZ>n+WR9=bik?~_2B;QjJCVd#d-X6t!qlq0L3Rv2`sr_}r@zh2Kl{tS z<9mU96t$z3Ltjr1#UdIeLWqI#18*w~>H>KjLTnIXmn6d3wPU$FZ43RM9_KwImS7$R z5Eq>(G^@z+kiA2@j^6_x^w_xpzY=_R%NEVtRV3|=m^9KQf34(tC6t6Bg9urwL$Le3 zCy$deJdK|92zrb+!;F@XM0{+VoS3^WOl?S#IUEjrQ~Rv7+I^-xrUxt6IAD}KH5q;uEETapJIen!}4v?5WkX?9l!3&=D z6QW{e$gu5^N@iuMC%&Bm?TCPjij^(Pq+V)Xo0_ebeq@quY+@4h>60bV)|Hg-f`}$! zbWk^hq?=;CyXYflH+b4Frr;MgJy3||VB95x$tjJ8;R5ae$=%U`W^ zZB+ZZ8CDj^dC@CYAY+C4&lUM=LTOgWuBAU4x+XM%^8b;2R}S<}ytq-NUB2;ezI%uJ zO9BAvK#6J#K}|&Enmprx662{g+&^q@cg7S`q>``v7pKN?oXJh;}x zES{S_@YQ-#Gc&uYODph^!Pu-_zrG5v)WSNH&_HDS6YTG}rl>NaQbbpR^K#irrWSJ= zg3%Z~e`O~~WB(J}_81-%`13ZpYK=7S?)~XP;Stwj(Nh(o^Hq?VGSGnP0?grfRZ$Smu?BaeL#Wb>b}V z8cRS&L4=&32|xM?0eek_lGZ`=9& zEcPUL{b~T}t?li>U0v3Ac3=s>-ltdHtz+{Pq}iCb3`QKNlGIRcboox~1#j?*m#y5A zgJ_+OEc`f#zLhXLeeCy^fQTX}K7an)6`2_ILGml+%-a#iGJL0DOH+)TOtwteNSCw+ zK3aNSRX<)dy{H=SHQ!dRXI7wBzjY@1ilmx7eZo219%DVOe9{;=a1XmdA(_Khaqd`8rNp!MfaKC4&;kD7Np#9WuWs% zt%yQNY3^r%*Y}Le5_#29-%mq1dBkr<6j*W(2CaqLy$V)6bW$4z0N_gK5X}pZujzoE z4?rw-;A^@I9pZ+eraK=BK5cGW6j0MPSU@oTpR|dfb-^t!|)U#)<&Wj++dIx zmb&%4BXlE|s{$c7xOwj$bBprS^%iUP6W%xVvMRT`Xq*1(De>!axX zN8DKl*Jr?E zfR_VUns?&8jHQi;nT_q4g|daehY0*`_F!v{LUql`d94#`v{wNuhM}YO|IG$KMWE)p zzFjBB!hiE@`GN)W2TGUrC;56OPoj4h-gdg{3QQ$L)(Dsr+YEHZL=H=e1pO5Z<#B^> zZ#C5I8tVD#6W$fCk085aKtFIqn6nN9LYvOkIBd!*e+S@K| zTB$n$*ORvYMW>AjGrI+W3!CyEc67O}Lv5m@YzH_3 z(U+-Iv#F2(Gc7VV)EVP=vy6N9>h1~{Nvj85r2hwJAp{cKV4ly_bol_t1QFfw{MOoKRsc=Khi#MAhG3(*uz!+q8?jP zzRpVNXw?x|o@nBMQj+6Cgoz&ujzLsHA5o)GASoZq%E#Y?!Ud)VQ>AAM=$GkVg{-tRqh*{NX zH<|aCF|SsNF1sQ4KRZ65k18}1O_E^1#n(?+~7b} zc~tFo$D9_07E$`aQbGC#kB6^<>4~@(;>}y{lr4f*g~k9Ixs{u{0;}P+=RlMFDb>~% zA)U_>Y0Iuo?|pM}8zFcwc+&p(3JZIn%S2j8r1K^w>}>G`P#~c@2|$vqaw*cxH9L0Y zTgz9)=iP2fbgj+{|(k!wQ+%RVQ9Wt{+FoKxizWuL;cd z;i)0Y7bXB40f~C`{OoY=IlXiPu_~~4MC^dKU{aO~HGhtWLlhif675>jl%H%<)2I~* zcUtsd{Nr_ax4}EZ-u2&0!~Ib(z^cHS@^|pu3+IY-LI`9SS6}~KJIi5oT`}g&)056l1(Ub1BE zQ|7b!9KE+PiY7+B-sYwM(5P(uYYV6{CJryGZ&$H?q- zcHATfOb26gHo0^@U-NiVgo7~rc@SzsGJyC|3E(1hX})EtnzWCQgdSh_wVeO6RkQ=& zsx7M7PnjtIm0mLg$>W2nOY1<^X~A&3p~WC?}hh#c1QU%Py#THcfpSAJ-wxyXIbl&I3jqT4@4 z^%dS|7K_@x)VvsTJ^sw&kDZF+pWHYOTDYHVj5{E)@0M=(YM;>?^sSqzzf1$A%O%X% z9rzTdcsJC`g`J=1x-jKev^(OM&=Qe+Zz5J>54_2?Ae1Oa3?FF`kGMq z5t)H_#&Yq1gaC?ziCb_?-{yXjW=m?YBRA>5%#dBzdl}9S2sX_d2{#uYyDXu z7rufaseb#`w5~s|d$bl+{=P$p^ao>dxRXS!iAS&(e?&7Jv8^TVUSr4b0*69C6pJ9I zaNUM~R#4QA#ht%a9IA@5=mNW>=r=$8G~TcJiUMqGgjs_+YBT?oxQsvc1Ox%(V|qc3 z=**;@|4vqFDOYk)mu`pM2uG189NT!9hf5Y`hq2BKEfehYJuJi9I!Y+OQm~2?dr$s& zzA#_3aXkkoC$U?Jh>D&vcTh`vR$NS+zjla@hr2>2t9x}1P$vg=Smm)Ly;a}*TESlS z@V}V#JY!PU%u46>D)ex>9UZ@Mb_I?w{~{JQ0N{e#)UU(v9HNZ*ed_Kxj>Mq%f$_7qj^5Z@-QmA`H`9iP1+kN| z%(Q69iGB}~mjKJjTrb1JQ6pREPh(c=5t*v`hKJ$ltTFm%B$Vqf~|_2~0kD1~&OF(s}O< zCFuFK&z~RrN&^(xpu;fUi^vh7Qyaq=*S2|1Gow9Y%0mOXWW zPbTTj@&_W!w8Y(?ot?d6y)rvP;*mmGq@eq~!a= zjNHzh;c!*L4q&o_<7Y~o@STX(mK{Y(nd}MfC(jJ{rwJqxnVb8Q9m9$l2cGveWuPT z!?=9pT-ETTkgzbFY}vegm-p}{L_mgsjURs;>;BaJrgw&^TzHvl_sa{@Q~pby1hL2Q zb1B+Yemru`B7m)8|0NZKVC}@yc{aPhWKBXO4F0G4?Udx|Wn@F40l#S-klXO9u>aJP znXSAnD;%EszoBdfG&Mdx38;y9V%gX`@+S})ZL&cz=W}Q(_t*l&&49xQN#P-%>~+UG z@0c5mXkSWwJomGN=G<79ZwP;f+jy_`e80G=x}BZSChqt||C3S+g}*k3EsUa?C-Yg| zpq#F*si_$9cx9>>;Pv#|;2=5Q&|#fyq!sUEpdtE%-O+p@hIbDY2?dC}MQH?nhH*ob zxqeqefnp^+^)$lVe6?6?(-)C^f0L;)u5hui6UfRiwLmdFPF7SwMJ0+9L%?qohE>fH zFsv%0y^qXIBo;o)+-*0b%W|eYt=Dnw)4j-sk3EiIpsNR8-yj~SFD{-2i7f8COv#NQ zzka#=J2<7mD;Po#Eq@PvJp7uSfeyVZ1HhgUk>?0&d3YN00FUrMepl%9h>AcPcQu>p zYU7KC6+`PC3((YX5uZhR`sH`WSFBaI1tY+=)Y!PVp#FY)5^J9yIEh{nEcLdgHis|B;^W6(?;3@|{-5Fo=%v9pbAI_F*qI3&cHqFN?7d@_O7S;tL=uG)>Mo?u6AkR{ z{9)X%XNlW*Y>xGRkXr*kDQs^4^M?HXEr04eeL%xk@@d7ygm&yWf51p=IPHdU_aVOm zwnPJvitNW1%0BU+)j-Gi1~AP3H2ZFDs~}956QVCC?sRT$ z%nTR~(-dxj6;u1nnJtAT+mBD)gB9y*ISo)6TCeL$#fLpFkns%A_@3FiL@(#y=-7*> zT!A6eJrgrdBTG@Hi&vTqqA#5_Pzx};TZK>w&rmSgXlQ4KqskmcltIw_4<^N1YTj)> zC2;w@%;ajRpF~Lq7#V1j_4@#UciZrl@%vX@r9O1_SG`t1>>eB(47^VrB{7~)US6J> zLte3O{NQ-=0bNA9HqQAsrdK zTs}U#Ac)6Y`VnE7r@ZwU@Z*9-A$Zru-73j6v?wh~eBBoO0h8c?b~g?te@FxflPj%Wv)!Lg$sVc0<#}$Ax7(26TUg zU^q>*|NK$FV!qJ1dPp413BW|m$XBm)h+rKpuBl{1Y0{w!E$8-dC;h(`0L>Zi4tILU zpV0N6;Nk3d`80A48Mp^$AMo(X7zfl2B;cV-0}e#Qe6_nG9OP#F6G*42G^?PnOhBw2G^`J<-SSwG@Ng+M@fVg?ThVStgNA+OhZ>_KjX zTqg2Or^{X2V1$Ambn!%1YKRsmAz?KY?K9H-!t-=zqtH{wtvoCKj&?_5WX|(0n;ie~ zU8=s*>_j;e=AUo;L;KE*140&fnQH8qA-@rkqOzPn!F~Mhs;C95&kwA@G>L!GZcLjn zd*HxF&+ec~YRR%CK6prU_wLQwypY?)Hr{d&XfnKA_|jI}GxsR10wO}9EKt(#oj+kr z11xp`X4iZK@9}5<5lrpOR7%%2&mxPo5G%*?SCRRZg6HH zyp}$yZ?)7CiJa#hx4sW6UTvRSwtZr>Q036bu7gLPPp)+@2zSN0`a@?pj;Z@M(c7}J zvc3(GKDQw(alS#dSK4&E*c`f~nVIv%k&QitID(o2JS^SI*&h$~Ze66|YuV|KIt%k5 zCaTjIgueqJSp1z__U5Ej2gCY(i@ddh$TT^2Zd3=4Dg`+@|p8RWIYx%{+ z_=`(W_}{`V#JOI-(K@|_3BG3F>FsW1Qk`cjESIqR(7825&x$s54^Ci0%~}xO)hNN} ztWxuH<{h&5RoKDGjzd>UQN#icF#)s=cWAae;|6R_rlMfRjl8`4)2!w7FG1<{pwRpN z{riUmZW|aM0BJJTy8TP+C|;xbXqDnp_v%^a+TRdyp7T`!^Fb8oaGhWV0dZ8spzzx? zCY<>qUp>^nzb?ZEYXeVn7goexJNdYAB>?X&D)lQMyLGi#e)Wx2|bXnph5q zCHrgW0dgpWnP_`^d&f|Q*db#9roRa1-+9Fxj(z2j=NEg9^1V)1H43aS8@)1pjCI*E z0$_sxGSBTeyFL|aHoSN*l%$v64`=ll$3?FSUs&VAgtiIyT>v&9T^xel{BYT#6#5K0 zceQhE7y2^A@+vzw#qf?H%8h;QUgW|zdosVtw84LqSpWGn$MT)LQ7hIw!xbWs{gAMn zvt)DFuh+8-G}H>!Jn76ZDToADsm``Wk2P&+p)Wa|Bk6zMcSQu%EK<}`m z4LYBZOzMstJP%frg#5KUJh$}D@0zc1<0ZBUoR_Vd#Zu}Wy}d#>5|eL3ZZqr{x_#@P ze>K0P-{ho6s&ZrSbNB!JPt+? z!Ap@lZNlPX!Owipd_Jso^#1nKr(AAsZVL1~KQr50e{e!CiNi>21bD@jPC6nrKHO1T zYfkc;jyr~>{w0QFC`B)|4`s@n&tB#a!!K|>dsuUrZ`;tX9cyvB4jf)YSpD{?8J|*X zP)R-S4j$2Varwn*bJ?LrHvS0}@L}r(7-N*CzMR39_wZtGly4iJY+DrV{arjVW=rVI z@Nkh-DfNK$wQg5aQ>(J{li#4_Q5Mdq8)LhxzW)Abuf~jBtZUE7$#vCni~(RT2zQh0 z%fYs%dXK*T?Ogfb1B<<$Cr_Lp?i>cyoQ*u{>`u?R0r=?dp4HnLfAHzwqfGSdH4r>S zqtm_G>}dJ=)h+RW;;KzttmrYxMtzdAYCot6h#{%q3W1Lc<8uoj&A%$HB)ULkbDUG5 z`;C=ZySC3AzP_4k{p%H?9jYl8w{F=YJoC^qv(yz|r0`_$*`@gIe9K(ox50*Ocat$` zIS$ze(G+30$JpFLs{7Uky(4!L(p&bolwq#SV*qMxE0Lp&}FOcdH)_$vdU zSY#3M|MPtnA`@N128-2Z$mygKU%7$IcyV%OlWI6(Hv2P}a`yvh?ln^Q9>pKG+?z}9 zri66h5Im{78s-N+sDMDdzW8;|Ml!}EyBakonR@^|@}70id#vcVAIOUNy#f}5WrT)6 zbhK1pAc*+%Vd$f=`P>mAz)XngQ%cqMnF36S*SZhztFE5jEvLgj%r50jA7!OyziEB= za3m&-$nh@|So3cUrpJ0X&0O>5Of4+Jii?W_UM`qS2B9c}&>%#bJ0R>#^pjiBul&w= zdIBuIVOeIPY6^ZBMETC`HZL@?AUs21_jazrE?3H;09`L}TY|*#cFy`#A;qD0WS*CU zk42n1^wH4cZd|cNbN2qb#7TxyY-dE!ftE{{n1=PvgV}{cGtxl&K(=Yz%6Rs5!yY@w zzgEzpPusKDq!D|v?({do3bFLut=97W6TaKoEm4*MOo%Bd7m}%l;1Mian{my#eaLcK z;AFkJ=7ZL=b&)z`3JpN{%(NBRTjj&;CF6@5KYDk528sp20#5oDm#O7I;v$vQ7CbVi3);a_)3lxF+5BdT?@YVd#f;1W_IX3KR*q^w%vE& zKnT#Y_O_$hX4bLK1d+=?6RV@l*TN?1=T!L+X%~`|ltjeI$&I4tlUEMS9f2ITq@*O~ z=M-GSd`ez<**&}_UEEMyfPVgWhIt}sI4jS^wQ;i!CyxFa_aZ`)d~-RN_E6m*k4VeP zRzY^)SWp4WY~Q$2My6sE>NoU^x6izr^j1bDi$RA~U9E+$@qKbO!1kb2#jqi7VUDqc zvAKCD_5sAdjQIQs+bw8m>`T$hp*YdNIJ_GXk2k~|gopyW6W>&ihZjxrr|h#B7KRKT z_vsH@mUr~gJA4&R0K5x^%*FVMt50NhZa(qLzt`Q+z@Qo;Ed9WJPjX-D zT6ZPVUY#GAn6bgvKwO5liA*Gfni!7_PqQ4mo8hH>(yX^FZJfUP9h&C6h)+17!}h)GVik=Daygxen*wOy8ctZ{M)AOzP}6%^rwuxGWUp zjY5+vBIFxIuaBA!yWHa!b>hm#a|`@1Edi{j*h`C-`bHw=`NBt&@S>HKv?y^~KY!Nm zOoVX@qc;h07JNrHS-*F)`RQ(KI*e>ae7AY)Qq<0J3i$<{0e&4C8iK9{^)L;Y>y?u`7s<+R@eKeI0_xhjy zwxKR9TvLi(3FQ_%ZJ)H=v1%A@fYVf0S4Y$aFreaslQ3LKb@py~jnFEK17KwM7rqmZ zPX!|PnH5E$aZeG*Q5M679ee$HNiKT}{AM^WBB^;V;&4pyu<*03txag7xKOiHNrH@# zqTexF+d#31Jv+{%2ps)y94lDh5HGnC7|Tb@`1A8h@fM$Z&;KMf9)vuS5}_B_V=Jw( zlhL3M>Wj3b_=oV><2}v}a)y*%tL$9)m9tFs*)e)!b}8K4R*9d0FR<@HbQAe74hQNP z$fHUmxsOW+Qk0@wFFi5(!b7tGvn`*!L2^YwO`Osw=&@CU8Rh8>*hlZ^WyL*NrPCkP z>B4sJV(8QP4^7o&Hfif7c2EEa_@XaMau?&$G~AfMrN#q)Rn=uSz-m`H)kM}GyxZOb zF*M=z4>T64neksrB8vkgP1x<3pU+2`d_`2{%a5+#6)MtgZZB=pC0~0v_E2GG3p>4g$;`;*%Z=YQ$BhZv)Y!`t z(ilVlbMf{3Thj;o8ZkGI43#0R0%RaR&+VdD_wuz^ioGtr=5&YAQ`N1fCtk!NdlJ!wBOD&D1^) zIfi*D2=8#)YRbsm7@VfDlG12p6W|H=+A?u3tI{LGL>F5hYS$*i1-vD|Ut;%z3E}&( z43CA~K1OC;kPt?pgC$}N$Omym6g)&-I;9U7MVaq_coe%F{W%Vzjjb&WUWkkuAp#|2 zJh2CiPb;_JxqzP3K^6j7b#;Q4UG9B%cQgPrV3+%3)#Q3UCCVE%jBW_KW7*T+m^-$X z{~^OEj}H$PnhZDcFB6Zva^>cO2N9Q4%pWV&WtM^3?12CPA5^f9HQT(|&Ir8}d19#C z2p)_K(C(b575g~pg?d+P05?D%IADoZ0b5(~?2pxGZJdJ=y&CYu{YBBs_h5bw%Ko4l zJKF}%-Ab`oKl}jBrmJQYV4iiOqcbk~hhUwWfY%TBVc=*u)91$Wds~VD*?Tua@(%mO zG-O%N@BuUXXMh2wfT?H6VpmqxwAeL%d-vBW+f)Nu;~@_+D;0e*5sDEO91ddT&FMl( z*LH0HPz!N^HsHR}Xz3ddYWvjT{2gIozyB-KWQzK^+I7{$o`7AE2t8OrMDeGlv|?)_ zf(Or&2^;`%Y0j_)o!zB3vZ1~kfo8ft-pCb&_s-04b8~5_#lqKtPk8>$EucXsQkqt* zKDH-ePj&a~2|IpI&PpjhDd`d*@(lQ(03I07xMRw(Iy-^4!RcWVavdX&u+p=S-F^L= zqBimH@#z5FLx9+y{C0Fo6OQ#Co)BRhNxW|My}bJHH@lEuFvmk8GC%0QC{D&EBG~?r zi5{TYWoM(wZb_N>yM#WklEKWao=uiam{HMdQYuMFtY=Kz7uWgx9 zt!KUFLAc+G=Afqe$)}qJ(Dudi;xl zN|95k_=GTnUIFPPAts9tEymJA0EmvtNYFcXIeZq8bQdxqZz zX-Gjppg~D9Efw?#tjHLgdWWI}A*;g70i0;+!+6PhDS_blHaq)Id^m+Rn6>ik8p$iL z0%5+w&JiQ4(@iEPtx61|`p`7}o4-M@FJBe@CzhuSYG;Exx0uK~<(vPLd_!G+H!aSl zBCdOM_Y=>QFPdfD(I|6)ABB3;mi$LtNKQ9$%zbyJn9g# z0ZKPEHX2C!=ZBacIy9$czN9PD4s%qJPlvrltQV8c#fqm(#thCAf=` z4EI@fOql)alzxl8$kRWlLcq78sc19}lgWMq*<%f^i-}^QOj<8zOuNV=ZFdrHMcD$x z`n!a=S*UqU^-iG~N;+v>SmV4)_shucgAj|U@!;SbqZ%xC0#%L1&RTZH8RF}4ur+PhDZBHJ z6?#m5#$t8I&>R@AM!f5n#VQ40%CLBszSy=EM-96YKG-DTsY**r%W=yNp65foBse_F zy|s6?9%rK2eGnfe8RsAEIqYd2n$ zZN%WNqGG5TA9kvDn~$LAgFl}4tky`adGIcr08UmijxwSa{=oHtTE||yrdLXvb8cNK z6q$3QWzlG&@h?8r8=EawaJEdKEF?{?%G}Q_cUI8${_X|{f#SqjQzYq9KR&aKhsU2V z&$vxzx2^aR;}qd!t*JV{D{Tf%vD~(8uM+fQ-`0tXie>;Nu~A$ZVUHpgo@y2ZR0c|n zX+}4(EKyGsSj2cy*}@87Z+Di3i4F*_Jdu@|c@xU6?4F(WBld46&ebM1uJ(>$UoPt= zXwkHD1Fgd{e&HDFB?yy|OzrN!{D4sR;N2|@;6sr~mD^uXSQSNdXBZO$F{0qHxy$vl znB7|g*R8>4#)xCuc^;=1{8UlwLBy)P%lfefhy%dGsOp&&*QW?QnCjr6DS0<|CjW`# zu2>uzLM4=xG+k$Zd;DJnSk9;!kXXUfS?a@`?l&t501D%}bLOkmv_3{7fJ#vid?+;A^6>sbBiV|%R>*6Q9F85LZ`g(C|Y zuH(Zy=0K2u#X`s=Ieet4j3|buy8YRb8?_r3?3c;9@iImA}gFG=w6s z{%q-ZO>|Cx=}p7*7WmLd0)2p3u1%}yhReke6E>F%f1~JW0-Bb`3Zggz`t0c46#oVq z4wU1UL07yAV!wPCVkhE!1)ThGl3!c9A9OVuB!KvrKK^|IZX693P0K{nML8rz>=B6c z%Bu}iZV9CbF;N;j828;7X-6-DWRuO2IBOmMO|`zh-s7h%t6B7iLUid@fZJgj)|EXW zocd;*DDYo&61WhfNa6eR*L%-jnL9i}Vt>D^z*sSz zz&Mp>r;WRzQ3?twE8no>lpZ+`*z{L-8C!I1)myD1a}iozNZSda4B%t4crwljym9Ox zj^y+#8+;IrK&6O@iJ>a$V6b*~G5CIJs(Ezql`8j>4)X_``$Q5#muvC0W22V`tmdKu zut!0Ofsa_ji_(f+^fY1}D46?B=BKPy>u3J6u(bjbA)OGiG6O311O=O zK>-|5C+sKc7%L+y+XsLOv$QSW3ndDPii->H+LbEyVb6t5kuQp>szy>Y%*IJ;R1ZB7 zq?iZxHC$P>6BAS-A|kND23OvQZvKqA}GGeOmf?0Nio1VQ5_7dtCU07`Uh2#-Iqtb2?^ z{&s_^g)n62w}V{XZ4K6+El0_vMLn%@WHbP%T&27|UyqW8!6^ zcib=pIkW%t3MnN=Z1UJx_0HiOk5`!K@RlyG{l1-sB-6GN(wDuJ<*68j%L`RO76bm! zmE$ArPtHh4D>ZpZ4bK|^&yXQu^pe8rC9~Iotdms^dF&}w z=M;HYFVFkB2z5aytV69%_Bu0kYX8lb{yH&ZKcy;+3s+#NsIod%jtc}(G>XL;tXal@ zk^ll?T+MLQK}ft-$Ds9cc>1$k6~zuK0cmVM6xx3(_v~;E?cW3|DbOp8ER)RxG_Yo~ zIeBi3|APw31~V>!yV0c4WXIRrYd|J)Do%7@D+n>>AS_Mo74bOHQjU|o7d}t{2?;+9 ziz@MkNbA`LHlF?cuXEKkG`v$8$$)cJiGdM7GjFJf$N$;1pMwz^Rhv6)^b{0Wa7>j? zDDCfo)lSIg)mjE=t71N$@cO_C`2#syglQ-dslz|2|CKn15&MsAfnWqqgq_p7#kNtn ziiJc&HE~HIH$F>>)Qbek<&>J)n^-~Ih4bK@UdY9_Q~S{X z{+l_uKbm4y*s%i_=Lj8Iyh1~Lb>k~XcYk9=c~FyJhP+vhe^ehNkqBk9cf1=i9={o` zbRB}A#%rZa4MiUx5(FMn_2IJC_|n8sYuqp43#rJ88$b~9W^jRRh|8+1h>#2J3qe>4aJJF zPsEe9Yg2E!5`WF1 zjF#r&pBY}S%-4C)Dm|pYYehR(a6QQU z=TA#tYgR?;tia>SySr+wNs?`^FYCAx1>wu(Vw)pB($nW09;htiqsG|HTJoo+WrG&^xzCoQ>+F-?m5<{cOBYKMOpy;zs7mM*N(@KV2_J2bq z#JK7PC)Be{Ds1Ee2G7^CZp~*3VBVTC^+DJ~u+ROO(|d#L#_eU@Y+$rXty{D{gnc<0 zUCT*5whtF%3&^>dwHshoc9^Z!y?icOm_2B$SyMg;x^Ksp8Or0y-gsg2X?zWwj30~1 zKQ?ebN)Zu_=-wl??T;|`FK(6tXZY8-&c|az@26}Wm;)Gu1gVdcE%zKI*jPVd0Il_; z$$?orlWbE3mKX*$Y*J>5daM!sWs7D$qv3s7srFAIGBox*w_JId(gss#sOq!QlP1zB z+r;j+ONP-GHe0?zlE!(gIU`+vTX*BE)48fn>G8**HqkiM`)^*ccPP-vd}?uSsX)JX z`J_|L-Qb?}QGHkYc6}Uak*U8ytrJO--v8JzDTfDzp6N)j!7!bJ`=!`1wNw4Q-JzI~ zQYw`*#^=l|(!Oy%k7XT4L*BGmSp;h>1M|0I4(jV)gr@c>-ch3Hsk&eOPnD@4fj8sr zJ!Xp;&*Md`p0>pcI#`I%1)SY3VK>NrMJ7>`x|y!J=M&npW~zbh2nvgf-D--#26KR!MZE3dn^Kjp(g+`|pk!lf_GJTiXP z&U^JO9K)Gp>(k`7tx%L%Eh`}Pw@&In(&~hk!Fbb@H#!;sZ-Vf^-`h1lQ5jb$WN*Sl7ofALY6tKedmz}syvj`Tib_${LLbTn*j zN$jrxlc#U7n7NiVlB)%R?YEUOeKGXuG0#6~9>KXRh&@p&!zO2YT&Q*l*JIbC2aWH) zJARE>V(STiWj0FxtMy+h@4;uq^K>8SsT})v@y~ih9$4Sk^B)*}@a{)|T!6vJ21X3_ ztu@v^Foex3ec)yc$C0G_zV^#FD6Ly*`?#L2kJrnxbJS6mtaVr9wVk3anuH>ZfNe2$nVsYph-QrE8@hXphdB?K%f{r|1cj)a^a(|Cx8&7ASCT8N44!n0oc&PIv3+-=dqi~fZX}&88C)r} z^@9_EgXc4|vM@MfbAij<_H~8fDI;E#JykB|W@0CjvTe6zZc=vZa|*HA$-Uh%ZqMzJ z$9s|4_4Jk<5R=b$2#IA$K)p<(ydl#NtvTX!Tv@kYnX1hZ2cwTGX(;#K)_=l8I%D+3 z2NpzwPC^W3_TcQ^v=~eGBmh~!pG-HtOIlx7C$fTyiHQk7s=cH31-JxO^pu^*z>B>* zNFT|A@f8;-eWc-uS?n;X$`%`nMMtD}pV70pM*2is8lr(_|B<1j&uv0tW6 zre+CUW7=t5$vs4Fbyh@K$&0Pmur+gBqpIZ=QD z6_#tN-b4%%9b7sVU%&b*P0T3Eu1opLskMw$j)Vz1G9V7fEIYqnWROp7CjAc{z5w%M zo1+(X=ly|78k}`Ubp~!9w+6-f-ZkP_0@bZ^d*93T929fK*iP>Imm61Ggxqg+atgbV zAj@x&iZLY+BVlE>C=E=_p^;&OV2f2z2PG_kT%ldN)`PP)sF%Sz1J@+U&zyrVtx_2%Wu#Y;cPS+^av>lJfyb?ktKJ?;gY#56UoaoYX$JN0HvWr zML{HA_!o6E(CaPjv}@JMa~Lc-3YgT2ja3aCgX+8}lr2ykRka;aL;4LM?LqT4EvNtOT}nh;dkk}~unnB&Lm8p)T9VGnnN=UA^pX-KT>oOC zU4t%8k*GjDtQlE7Vc7>xz}PQbvy(YA^I=5w)S1>UwH;U3d1KuQr7beS`=h99x^V)2 z7L28Usoc3M#Gvho9yW#JVF8Pty}#J)Ox%L8 zE!FwtHHH%~wc3Cd#jsRK_835x;M<|OT1V+7l!f*-VZ0YSrfOWutbUN#xti1K(O0pl|_bcW8F`A@IU5NwB zeE79FyGOMiFjX?=_&|=d1Q&&#UkiqCOjMy)kBO%d)rCGEATI!i^1WCw)63LW*~He$nXLDj%;aD!c9 z;<*RL_k)TdL&!@?uG4Ne^J;=v7d&(rquRwOYzWk{TQ%(M;)`{+yf*VQWaK}==~m`k zXeoBhmE9H>rNN$Vu%0$QrcA8^XYL9Fut*LcSp_yfXn<mrL4e5EQe?J(nVE@kJ%=KJ1|bLIR93FLF`&2--#0WO;?P0JCM>lzA5dyvdDhpCzwR(Z9qIF86PgRc8=$)6OfZW!Q1Jn2ed z;O0%SR)n?y7h-IrESw$bm0T`(&;D_SkfePo_HcTuARvF-F9%gFuwE16^0xChz~uTc z+>4#^&AeZQ$n*bDp=ZzYMU0B4$}W#LysL>6F~VFIgm%goH&TLnT2@DABe)zZ4^9|9 zze;=giji}Tlz^UZ^@I`*KDi?9aI3V%QhDF+GDzzj-SyDfd8J+cuT3g_@p}XF#LI$} zl>G_SKOOh5g>-ZnbE_P(e6C_N8ki)Qa%lu?k`!6z0GAM(LtGrkPGL2!fLJC91_2}G z$<@vC$4;4%@o^AU15I&K3}%>8t#w07r2}D7{LP3xH@NN`b2IsP#n^s+ba~9GwMQC3 zW}}ha^3dZyvIrBnF`H|nqWO+p<1f=;l1BbxrwT+t~_(7>Fv{JN}BRgn$je znTiu*Fdk`M&#&l8fv_cto=sK`?CgGxM-ZSfAB!mVfpvCzCcXMj8=bm@8KNd*l^P4G z6po3#z5N?OHTmw-IdoNR+S}>pD?>cgG&t>|H^O)S;2LMW0$ueH0b)+1O!v z2Ph_@i$Up&e+}zy*j>)p1|(|0-*mxiab^&+nw$;G=Dy=7W^Enva$j@cBEuG z`%6{yV2CO%AR@ijIyvo*bYL8Z>DCM-A94vuBiJ8o8X4=7P|W!@UOcwh z1HIgv*4DuK`aRz&bubJWA6|jR_^+4t>o_(h-2_c)vl`ALZi=15(_yiDZ8Fy3Ou+;t~P-wptv4ms{kip;@V!-W1A8L zUo(;6Owev%b|e5wVeuB1V3hUv+1B1Z-li|x1CM)FeG;l1riY=AM`01b@dNZ8Y8#8N zb^ZEv@h-G=lPl5IB{107Cm4gW9APSNdyZL@#3a$$s?M9+_<2L(W8{ymQL5g0s-HHxP;2H>pW9dRu*y_dT{r%{pONHAe4tyn8|e2<&Gx35RELnl3th@MMS^2jS-K|h|TI)cikc? zuUoL|5ntTM_7=<1rQ;n-*#G$7DXH_jyU5ig*pH7V`89V&vJTh@?|Ui`e6g^UTnkKq z&4*{rWF){~De@BKBqJlEgq+RhSGqaAkN+aM!Aw?0CicOTh}l*HQGrrG5n9hLLy#fE zx2-Qs9w3!p?tG2BZiewq$Woxm_xQ~(^*hYK`b*)7_fa`Z(GJR$doIw~>-$fj&ilWb zz66}gZhil??Sxdu(7-lDs3fUm*ygcJB^rj`@-xTZ|y*sQK=>xw-G{ zNx?ZrOcu!BlOsJtBdN1X@qAHfZ(1eGxPbi{2q77vyn2s+J59#DsddJ6+I9&wsG7+P z7m|`6sv!xLMx`?VJh%w0vsd|f3n?{ z)?BXMr!@0XIC#TfCy*3VXMTBj_gp_s>l!Me+N|NcnR)g+Yy zZZgEU4PH0f1;rcEy`Mj}i0#|}KI3$C>GV9DN<B-x?9~+)w7T&hkYKfS zE0Lpjz}kRAF3@KDFWr27;NxJBIqz!B3c@}0Eqs|whwQAoU7zF2XzDVY{YDs z*eJlG?+k{^&>pbrU*>)j_)r_uxdG`s*hWO6MCd<)G z2`f?as5(q|Z`2d73}6x8Ew8wQdl&t=#J5OYAJ>wIhzMbffk~_2;$cWZ*#L4MrZ0s1 z4m#p_`(J8;bqwAZ`#1lgX?tebGYU|H5-!^N_bfPspbLYj=#ItB!*XD&ACNona7QLa z&@gQxKD9V-_laNG&?q2u`pZI1yQ$f(8)U!+@`z(z(rw+{t}_t%;?%S3@~C|9ii5H) zKe)ox!P+<#+w44gGmHmSMY$MedjKVD&vPW}OY{_P`|Y<*AN1jjcnC=Y{$dDM@(pmv z$eajtBRD;PI1S4Y+8vq9G|h^Q0tWj5Lz2&huZ1QB&U@)RuKBknwr|ms8)$?c3l#7Zk)BjT8Om}C8#tQ${pI+s{APik30;NGM@INZ1A(-_Tj_+Hp z)holyEjX&Q?bxu)qL$jUTnyprzg}79q-)_xqaX0weIqR$kx6+_f~n&P??c`0J5&VaZLZ& z4^;NV-wTY>5Qa)m6w0y?1~H7=es7m9q5Le~Y{e6tTQa_b$wWRDv|BjrNYoJm1K z{R{8^-D@;-B<=`#v!LF^>`=A$pFG-lYuxq#o)pK=#F;LAW9{?WOh50^>0m?cZ@(Nz zk|#UPfBVH$`KKsYjF_gg7n0o4dFCDqy6AfQcPv^abkCsp!yE2Rriu;n&;BQnhj!s8 zj^Q0E?pqtFR(}d_$`F>Hrzp&eLy9ER<25GcGZ?UtxN^*KP+?+5OGJoZmzM2KQoEv4 zs=8=37q@omLg4f4rPe=5p;=R7fEEpTAt-Jypjd(y7wEWF`e@bZ0_`%79MziNr#gqb ztHI55|1f6xE(SC_n7pI$4ykgki|^d<^TZQMMb=G)`RA(94IVSD!c*Vg{g-3&xs z`ac2>@RoQt?E+)^nu~$#mfZ*Owi44$+_7yR+)M@HLy2|lQWO~=Biu?myzYhY!NbP+ z=1!W7aQyr+-(RQ>A#|WoKsj)(kU8LgV^Pq$BnaI2^c(6aP;Sv+KByP08)>|0SuhSJ z3#w2G^$!dXXP;f&X0Uuxfc5e~M$@T@vya(96YX>1kG6dGES-@2@ha7Snf-k&3+FZT z9D@p`0Zt~fXCR2k9@?GhYG2bsp?KjcKY#O`zh)hhtDvB*pI5xhocN-wTel9JOax@Q z78%|Loe^~(F-o7F?$I&!_xEQ?f7v2FqilCyX)n9H^HO}g-kYMR$WW?$BWHQF-ma9c z)+Iav8KYH&r!zi?D!sws>HoLc1LJDv1C0Z3pFtRd-jbMxJeya<*~d*W3O4sm=}kBw zoX=I&^q2oj#?HOF_g$@ZN4FPMc~dz%Vru2Ob(d6~6h7^QE&+3KUY5P^r=@Q&J#9l> z0m~?-q%jhvcrc0)o=rG1yyz*Y5N!+!)cV z{&YpXm$hdStQfG#-dRi|)|nwABa`_j3nd9wUiU^ly)mwEc^c#kkb}U~Arce$JB_Ej zZVer9W+@C^N@FPuK4izH3Mlz@1mm3;U6^wZ09L>;%_$foLIT8k)$W`q$>VHcVp8@z zV2YO8`a9*gudfDhFPi2l8#zEQz+10fTY4F8|K8^_yS{b29J*=qB^ThzftiA|X$WCq zGRNt1w`F%BJow>K3nKbbO3hy8!xCqx6P^5r=HNEWu8@mSm`|s>SP^jVTB`l;oEd@H zuV(X7KQ;ws-<`QMo`ncz#06#i4m6#VJ*cT~Hj6hGc9dImbrs%eu2{hXPX~OY$n%A@~TyZS@G2aV?sk>@=O8ra4!^DC>!(| zBm$T|-Me>TF>ktxc7)!_RjY`RdGGkyy^^33g3N?NPH0S***~Vl^YP0mgS|aCV^J!3 zmPT4!-famS36+7`lzM^%I4R*EQbOT%YmS!b2M5fdK}sG(on&La&1~-(x2TkdF)BkW zdr}IJkYJyaUD1DaiI<_R3ieHET zVuNujE^3o)Z}`;LiaHE#2^R}K8LvF^g7Q6aQou8@sWeCjUpa-^l%9s8Pth-PQIyDb zfr>Y8pxv_z;bB>GQT5J)R$8`5WI<~KT!zO-XVOZ_68=d*4bh_5ND1|&pz4eyhUnNs zQ!oE1xp%3;P={sHbWsWH%Fz^hF`Tr8gIPSx1nm+OBANE{F zlZo&|JLk!>!>KI|8-_HEHr>(0YGp(sbH3QsTAr8 z7y+PsAyCa+H6`A$C*@N_Y7j8>SNE=+IalP7aH8Z)OguW)w~ag~4iBX>RMFi41L5*} zgd|l${fB;!{0{Dlu-0P$>ESDrD4#vNM?<0*8O`iWFkWBEsWfu+ea8*x~Y z9CFxt8>2j+>qgk`PqUxQ0h|a~;raF7%ka+8#8d^p$ATM86#YBy+7-BM+&PCM)0RXl zd-(tB&BDc8^Jf3)gomqjeTacP5xmGY;wXrTh+BzCFh50n7$IZ8o$zwesR%A^iUsNZG!jTFV5q)% z^#eYd>%pKLe`Ifkl6?ueO4_*kqIh%DZ?jN{-X4?WnUqOf0~RE~Gl-w-)yvENtcx93 zUS|f#2rl1|a1=4#I+<57QvZ0KSxV2il7!HP3%0q_Bk)nulF=4E_NnWw{_fwcIVTsl z{2Y0hQg~I>X`t|-^3gCo4#@Ix)>35vWZ)uHX5Vl$?J!2q&;es+_TLwQR;BiROrw>^ zkOay!OlKsZGbD>_N6+=w!4(I3B&u?aRj0c(y{@>_UFEqsj25~KZAV02DSI(YNZj4G z@ZV2G%YigX8*#D2Bn5eD-ZAs@!fBvwaK8>IrpvNU9_hf|vOYR_XbjsL##VAPy$lPBkk zcdbH=PN>Lm9%n{WGnjV&`d7>@)SjS-Ep)EkL5umBKELd6<_s-?Y zz50E`dmJ}B^1k8EOz(0Jv*G$5UP59$h=zN?G7W?IXOET!Wm08k@xoNouby@~)#VWx z58^m_6y%SdMM+rU5)8NW&;0=|7eD)*otT}RVY3{5V4 zOBLS~jv%C=+BIoUafC474NYFWvqN;GUs>qgord&~$Q0U$`Py3@ zyNJh4K%0mD_2)Iz1bI10|Gq8LE*#Id+sX>2CG(MLrpuqbrYP}`gE^N_x6!RVv>5#b z7%qF`l{*IGA$X>2QV?Gnbyllo4wbWF)|rDUEw13qU#_%0afsMkqkAFv0TRwhSQg#^ z^!9xcG8S@NiXEI%sTWf}_nsh@8Su9v_L7jPXmqc>`qs(c0esX85?WE{Wz#_3TW)rZ z8_2-_=(-(2v*ST9AtOf_a0M$4;dy%&Hh*dTzN2b4@m)@3kIL_QdJ29rn{FuwNoSF> z64RwG#}e#I>l0pdNhBsDFo1-BmrcAn5ZRFb-~k9Gk3 zp@17navW+AbQo#_QddVIV z2BJdk=|O$&5SFN;H$0T=KDq%n`-Pnch!7mmV8P4FQ z?bA6`Q6LEueFh2%YYW977cC*q@aQLU!{)q%^JgQvu4=OeF&@5ihe&%d3x#`l{jQbL zXXfZqbt7o}@rt78+REkO?oOswh|-RH7ONGcOJmu~=Jz|+gu*QWb5MPN4k%{9#K6CU z0Rn0QwjBn5MmHd}2bvc4!kPQ=)|@czOog(O@UrXgug5n2^XJcH-Z}j5f&5^kHwf^T zw1-DZhPcpcx>l5OCa@2rq4Q{i2{DOmfkl)4@er3o7wA|1o`#GGMzf5vnUxjCr6#vS zscE+vo9jil*ImUkm?)z%Nz5_|p%sG8x<{N2 zMqj~%oCxNwX`JK*Jv#BNkF^j($$JELH%5n!CvP5bvfT7$@tLvVuuoA1yO#MCHd|J> zZnBnQJ{z^lYyZ*0c;|E+FoT5OuQj+nI??r7ca)iB{y4gUpEvBkgbJ8vRN$&5DaE*U zr323iNF8YchZ)%{Bgl>c+E zC7FD9cJjN}m+PFz*Fmy)yyqRkO<{4efjDY_h)X3lDW&sk&e(UB{rssw!o$caLW>hC zYw9rXuwh+l7&`~{TG;fS-=1vC?~D#*yNQS0??0RSYQgbu&&&OL%R_4xbdWX!c{{{} z0uGov4l6PbdEj`2vj#yBfT6+7fHeT*(7w=pbB(l>>)K-R3TIh)Q$gZ%M6`FW|CezI z9f@0SZMv(2(T1f)pvGXw5pS=XYNs@+g7R0)^DMlp_Qf(oKiTc6OM%fX3HsP`_k=D} zZr3AJD?%_4p+tNr_Z&E2Hgq-jONwXeI7F_~?D5h!m>Lo7KW@Aw=qbfpA4i(BZWt=; zQAGd&K-D&+$CqQUpqCyiSf77j=itfTJE0Rn5Ckw$DX$$b-Qm!6wbr6&19(&plH4wWp&&>3$mfIOed$*4vw80qxnFrm=|Y4$CLYS2s88VBr!Q8ez`tA#d;fx z_Z5bzF-N35@j-*8J0*$#%^p`**#qJ^ABC2>IT$Gj6NM6Vmlmy^DJHE~rx~|KUKSKr zFB1sr<5V(Vd;rJ4kfmlY8JfV!!fP9?)8`Um0*Iv_Bx>+Z3`3dNv8i=(LD8X>Tb_yr z>KAw8rui>X1_n`NC_-$K)RSw69d%~E%EWP_f6ot?Iz=WkcpzqL3bu~BOI`l=!0XQM z3eUwWeWr@PHQT$X!3YOxNOClhA`FbAmCr)WW;B}~5b+ip<$ebMXriD;C5TF(%Do`e zSta|I6IoWIn%&e~PnmDowx8!@(dQYM){{7Bx8zpW1K)+(vmUL!^`*pJNAk-&R)kJF z#Ytr-5Ka$PwhXNWa$$44?Zag0FWc`Iq>>O;f}|7IcFcFH_U#lNW-j9mBmE!o{U9#B z;FDZBT6#&wc8Bvx4ZA$Y^ThfZqG>8RoEtPvpM}f?w&CdAn%bP2zC1edeD4?xsv=P%s%8kSjxbkOoLG#o8fv=D6?=!QE-)siu%&)u*XorGh)(n`u3mI%={D_X_3S(e{^feziPWNCpj_o@%vR^8(Tq&}qbcj$D{+ z@XvUI*9BU`gl|*1_NjU?n?qmi!7u*Rl+PJ7BpSp4J7X82wFOx(y@cHy)E|UcS^Nm` z&U^1Ud@KBNvUB=2U4FtLgQ*8ej3Y)m38BS$OYG^oR=0FP;X4YHoTM3%+#M2ofRQK! zCa>C$^7qOI-@d(qDAzGfKn|d-W}rjj4Z&&N{JYX|+^7LCyg7!HHRPOY+{ugA61iuu zViO+cMMX@^7m#?GHGrFvq|ze3a}Pgyz5iJ(MuDdS9VTp5R;07_9>1LOVjUH zevQ6FcX_^!F5+0Va^Nl&kVL29!UY*7JE3xAmu_+lH>z;AZemLbT5GReY0f~O;VY%BN9_V_m`5)oIPS6K`gUcWFBXDoOg_Xuzs zA(Z(q@tR}@Y(4doYoEF^-Z~gllKB#_fHIJi$lMDb4I?Td?;?>UXl`46Unp)ZPE>d= zxWD(l&)9dK9X)*EuSg9I+?ALf;^6-J%%tF6 z0I?`mU_DBB5=a9;7Tmmy^mGnr#nI!P1%IZx+lFh_J0(^8_dUbmA-a)@SUG^@grp$& zN8p$sqzHUgV->NjKk`2wdZD>nPtE@IEX*beFD3oSlN~V(QEwBt>2XQ!%05WbSn@PM#au3y(u-A>hAA5#YTt9yM?J~dX^QZAELKq4eEGM#W({j1D-u}+i0{#Mda&iX_o{>cAHgRT$$)lrM|L&eiyd-;P}` z5agTcAS)QG5JgOSTG_u_I{@))9oCMR=IQM_(-kG~L6JrQ7ix=?Pa%gVxCM7S-OL!m zzLI-#RzYG1TGr6#pqbB2&jX%zOkT8Kr5yihDxk2OweQpBo*5fmy&Wa!jsH-FLu3JwueK=;F;A!xF@kG-Iq7ewu zGbV6UhJ@At{|IKXxU}?iz1c6E`JwzH|10=@8I`F&rX$UK@L{K)RN;`d)O6-)(zBEU*!aDC9o zdQhHT$*1~tg~#QRdR~NiKv%@=Ye?O{Ho6u5YQ$=t(Dlg6vtfgYG%NPA^Vn2?wj?Q!h;!?W z8~V+5SK?>5n-?jY9-3>xxGYDrcS&Gy1KKChHl~Y%l3+ni04@~ZrbD7rX)fJ${qvE+ zW|Gqa$g7j|){x(GyjRtc_6W$l9_i}4ruoc4@jQWi=F5<>vLj}k6tWmd-W@TAA@(EV z1Fhl8)xz@`T@}na>FvNmh>;H*?X9p&4d!4E7t`vqk6mmR{2(&lxhmLGsS$zgrN%(+ zNW%&G3HG0^uwj9&-j;NTe55g9t&5!JreWnz-OgfzBXTEBb;w> zJKNH565w|s=z)+DhY+#d30VEb^z?K_dsM=6y~^t^l0*uj+#r^^1d06g$<9#hz1pHk z&cZsk*C*dAm{poel?@Z+5V17^D*VGq_mId~C+a(8%7w}o>q+cjEpqy5xk=lCvbWk{cA$i_!wP~I# zIRR$ngJ%V9{-9LsEb`O^BLuDOrTO^BWKYLAx;V9h>QkJ zha#WiO#?r7;@d!AI7!uozJyBSdmu~a?xim&ec7%8{1`*Qp=SdD3tMN|v?5;xEg4bNT)aA;!K5kA{C&3Y9Zvx>WwI+Jdt~rBl z+j;0~BXtBLDGE36r#z<^(!+pK0m7Nx_pDeQDW_Ng4kmrG$u_w{m7onv5%5TITnGym zbOd58^Y~~x4b}9e6`X<`^umbL&4z~30yu?HZjjOF#Glip=ir-F(eL5FR{JENDz0VH zJ>{Y*Z@a{M+9%+6WvYQe`{E5cVlX9hyN?tx(t=<|0E{CjFe#64U7CGb$HSqg2%N%n z!)_#~6?ry?;1Y!24Gg6e9d!yf)6Q?3Bfz1uc1Ry-6bX zHe2+}Nf!F&RIC;&J+INHv7RcjDT7lEd~1wgDy#kPrwnnJc^ojMj%D0Gz7Ft=9# zW4jY*S|&y_1a}{?UuK>-t8-pgEwFo2`7U3oF+ICMR*Bx!R`ZvSp!VdMpho>~u8*l5 z@tmd8IBr(*H!lee8*)$}9?saTu*YmFJ}wW-w}iU*Z~VFHr=r}yM#N<@Y2rto+~AmI zeMglI9GY}?aO1XC7v<$T6LmfQ?xN)rH38tO;bD^26f;ZRB}i_!;2^LxtTL zfR0FR9Li0CZX?X#<>5z44Q3^eW#44)&;4IMrims@u3Sd|j4lj!hf z^MOsy(ms8RNtzuqLX?I!nK(|VANmp+F5%0a8t_+~;8p+Y`V6)mm)TSow8>q^f^LUV z3FgOS8S%>G@#~~onAKf>UFg5T<4#Z7H;N}wHt?hx%V}lVykK(72G5l!!yxfI@)t>% zCtw>TyZQW5U6Q?prN2*Y!qoFquEQDRF7adY+dn#dU2nQ7ocw-ik9D+i?xg_aSX-^qSE!e}L3F>;xaUWo+&qG00_8#~3RZT{uB#&s+J*+OJx z@&867lyRJHR%ruoJ)>dXM974w~>J`QFV_Zn8XEI&^_&`jDgZ?$75;hzb zfPO}|HbZag;SHPWVviiRduSM5;;Y z`!9w^mP8gGt#{{{D=5{tr-q-VxnE`AISQvG>Yin!8HKeXwqOUbuNfG4gY{n?r}tnZ zMJ*FEHPGD(uHE|eS&eZXZ*EjF4`fO%2*2?ip#)@0qD99)L{bLHxrAA~^>=L{zF$`a zhZ@A(!=}`pF0*J}aG81ib(Cy*1{M8YJQ7?+k~)apfj+U~|IF)1+YPvgG#Ps7{4d*& zCWxiEg+;!QR+&EToDzr`9hUAs>NN~&2#pmbkdmgP<6~>T1~H?`c5W(IWAk%vj+AS- z5^=2%?@Ek}0K9yCdU_!Vjmp5^aNtM) z<%JlqAaE3eBSTE-gnLI?<&UJk&eMA~>)N8+`;i7W|Ec;q;>*UlWrPc4*E#h zJp_bD<2)kk^WedWiepU+eWoULysHu}T@#Vg6`*XYLuD%{OZO5nX9GL%IiSn}L)ZmQ zN1&NKKGw+v6Q)QQTaexiLm~1V!}O^8dcuQ2g(KaH^|$qhJZ~)1&X50g4*k=Qk%L$y zP;vgR)5oY7X9ixJt8cneIS;+p$9a$WL(Ap*Ko|8R+LwZJ-`|*PFkLJ~ZyK<7VJ5nf zcLwJ;-~p8Gn+WVR%ML(7j41gu$VvgCNErT@pc+UE;P6H`l9J0AInjb`T*~W%;~0MD zMS0vu7iu@&`+h8LVawW~mx{s2$JlMlWhctj=re50m#t-I)QCa_e*iazDOd9nUoB90g~600#`b72 z$>$_%goVS+lx}6xoX6ZeJk3iFT+O-igX<)>=l*NHzA>eNKVHlWNsHYSv}@5A*5$1a zp}2u+NTTpif8fF4O5W~>uv{fgfA*Kg&*mxG=XZyl(z&uP@4=C|xhI7(zn?FXVqWqa zw&WWO%IPVc^f_pB76S*uqg1Zu<~oB1gqqrhUIyF@ojUnsc%%d;KyyvJKa!L6Rm5<3 z#7Wbe6zPHTnm0RFi`4DWV~yXR^yN}sfwRjb9>)#+zvlQ1I(gujXyXZ4wxGbq=R6M) z_J_X`*ml}UOFf$>HC`r?(Hl4-OXIg>SY1xl;NNpo!8wQbf!^1eC8vvb{f}RY72p26 z`VOYDx_D~jv!He(nA1LBC`gBjdAr(qAb%u#UiV;+sG$Hz#rT7p&+L*`Vs;P0(4p}L zQ3HRHs&D!`3Rhe{Rc$OdaZ2iC*0FQ3*Z<x;k2KrZJM+buInem+u)IHv0C$x3+M^ zGSYOfk)PEfBb-K6uH$?+XN}&n$p8IKm&=j{N2)Q7{6hs=2SSw}T>n0BtA8@0YS;Zl zkIn5teFmYjp6?p<(gY1f9%GLEgzF(#&$0*=P{V37pxEYn6kU%__;Z=47wEn-TCGC zJH2;0IZbbpPCm;#yYkTyMIp=R=)|s5OrFK)MFER1Fr z87YkRcZ$ zU4dNB^|zzue4O>F;bvz1(qAha9XwBtKfXY&KvM74uV~Nn+?#|tw-pFPW}f1-=r)=h z=o@-B<~`KaE9BC0R{8)BeR!BhJm`~=AX`wR#@uM@^3Jq#zK36O4;}dZL^N;0GGq5Y zlcoIon9F0&3*M*n8Dm^8Y7r%FRfWrD(M&V z+==r-`N#a$N-bV}aG7xl}SjFe9`={@Hwsr}KWCHa3@Q4`aUHYwNn5UTFGNfn&)xsn0(>MgyH{ zht!h~?Xoy(TaY4@{p^qHYWw~TUG*2o4H>b^1Pb==tdjjz;g#X$aKViJUdHaCh@@5R zOis*RA;V(|aaYwpi1lXgT)O&~t~KlH$m7Z>>7Q#E0qO>)3`}~OE-A0Ckap3!GUc`O zkJ&Z-wAsLpZ#NX8^fTgO^)n{D*J_>`D=2eKqHIQ4B`e7b@k@2tyX@~H`$F5+-Jtts z@z^-fvWxlP!o>$E+|80Jx_3g8vn5y>lU8{u5vMKsfUY(31be>!c~Vw-#Grgx-A>D{ znI!_UL+=oFd-f0x=e5;&d74cCqf^gmNR6)GzoBC!Enu`*T71pLh0A0={*|*o-exft z&a4c*-u-0$;;ORG)ml3;uSJZyIehBcrKQ&kKBV`m*IJbncv~Hg?YRuaGE@t<9$9KcE+IhHNx?@GC*!gZZ?t<{>Qs>X|hw z+v8gKu+D{Q@rg%g(7{|02(IX+3Y0&;wo)u`bcr#p@fFCiII5hUKBJLl{gTDyM2lM& z>>28Oxoxi@hl_xZv~`+1Z`x_L&b#q$`}(;>(@yk7g&g|nRx76jDJg4jZR<5@TT6a> zN3whc+4c!%ez5nymy2D-&uw(bpl+$Z^2-axqeGQCcEZelG2cJNog3bYHudQ&p}aLy z_S{`vP#Pnt!`@XQ)Mu?9x$3>i@EJ!Dqmir4zdf%^ND03_zI+p7sw(unSbLg5`>glC z8yR02n)D|vlH*%fF{Rn*FF;{Zvf*oEV}ezr*m;_)&59va_GI0@&2t7z_ZrTti7<*i zZIISuQYocGUD0*_x3#2Z%D^?(u*;~NsW}{TI>kG0ByV?#_c8m#?(SL^%*r-9BqrZq z%1+w5Om+`VBI$C*`W2SuV&&4~1|2frhjs2Ach?hs-?rJnJk7(b@^{Z+HQL=be(Oh5 zmO5#QMQp8HBS^VD{T6s}My6olrll9!nT+@4aiwWlCc%pY^eH;`lSu8jM=1)lScQeU zN1jo%`ETjIc413)QoJim(jMK95g1;tu~J}camU5d@9{4d_t-4;&1%?KBOH7_Zf`)q zOs%~XU3($($cdA~+}Fp)@n5N3w3}a}>SY zSLf5@DBnJ#TXQNmNn0j-NnzGiFFv&Q#2=j{8I14i%;V`Nf2JNQW^i|6_@CmI7pYmV zas3_JS>>^@Av;9Y6#S}T;FBqS{dXTZeva_aqQI5cJ306*Ddo7qsM3e*^@_BPZ`BAS>+ErLNGv{XAVPU*T%XaL z8)bfnNihb#J$*C_(xsnEH^=={*NelW5yAGY^%v*z$4rbx#JZLQEoSW-ymcz`)bg%@ zwe_}tKbz1Qi(VXjc5m!zVv~vRek-TFxs$UPzW&|obI#eJD{=%1&HeZ9-aW*O&s_f8 zn|f+x8qZ%n6(h$t>fJF^^3Q&@KVan*1?KZpo{M8o6g9Z)wbqY4Z{1?!cOX#R`diSm z?@z6d=rn6C{renNin6BGLqUH7kU~9(yg;?e;}5d6k3tpOiY9nTQps$#0!!AkEsNtp zIkV;xhbeckfz+(4w(<3q8((MocB(ns?zG;tBmY^=j(=Gmi1;&YPqL%Dm8Hdz&Zk4GJ;;8ce;rnbX zyN){~-RGP4(q0~-v6!ctq>&xo6y{cwyIruf8U<3nRz2KW3R{fw9&<9cCpKu zjngkc2MVMLu8te%@{5VTRW4bGoZ1mD6|t``a=1<7?+ediX2=Cc#?#J+_MGGi5lh&k zmcd+hmd@%OFZ8!{^*fQ*xM<_4yS-*q^m-9z*}2Sq-@V?m{xrtpw?RqUN^-e5;!4+~ zC_jDj!Z_ak~AFAlt*x}SZGcQD?0hPb#xhVq?sp^_5>?+GZ!Cwdh8=IPG zt&gB7Oj0`>OH*3Ix~|*Kt!w3oG!Xg5`R(GmKiiJ*OnFN)KDx1P*GwHWq$YfVRt~gw z4vn(AXdxqz=P`E}|kBPs(!&@gb% zdsgRYc|I#W&tBHQ?rvv}ljZ2*leb=qHRTFE$vk-L;OY}E;(k5r9b%8g>&))BW5{aE ztJu`~-`gl17qN6fc(;kHw0V5!&DLR?+LS9~#98Y62!Wf`?Lu@87fk=Qeb~N;u8S5& zXOp&eVCY?Ld*0c3=JK4$ykt7z_@PkweXa>Jcg0+`mR4(>))~7Lk@?bgkBhp)&19|H zLf!9bUY~x{dtwWRm|WnqB|Z6{6ZK{z45a--_H6wTWN297aG`qp*JN2!{{H^{ZrHKB z8wxL}tU5OvG*ls~57&uY4Mxinrh65ug!R5qn)y2{>j~fc2>VJAf!Kw6);vl1D*4MK zK}2Fj!-EUOJr)U+SoPFreYU&ZoJPixA7j<#wj-)ed2bFsT8L*Ssvm9J{Mli2SE3t$ zqw2S+WhSz;N#5o9YPR;$km7c(ErJ_9F*KqsclA7!e>xfCIP5WSWjF2eY;qX8xF+Z% zopE%q?M0zxZ=nJU&FE)N^O0!^khN@O0bg?M>q?up!;VcQKF8m@eECu$abDRlW$y?- zN=!A2)_?czlv|hnIyvievSeD%>5|gUXGVP+b>5|p`8kzvFMfYKaaY&1>xbg@FIJGh zw>@X!5(lo!zqYa*c5;+XbeyXTf3;_YV5Gvxj3+xalH*a!OfN{V|M;K%afb6b)G!m4U~YYr@*b;)7%b<0d5!i?B+zW)8Rqfi9=ntAA;$I|lZ)wlbXd z_vrTc&(`AO#%+-a=eP|v&oPPb+M$tsf3!JUXnHVQ$Da*vh#=F0LX5RN6JxT}>wgx@ z!o{gB!m*=g0wR!}p1u4!kN2~( z-|L9h#%L^(nN{E@YgL(&&dA|>3nK_KqGinjP1N6W*k@8b`+ch#L>rk_Z$II$(?+L>}59-*jRT^^0f4jgIW6Ae;yK|@hig-vNm zPw9hE%O6p*WxXfhg|du)qGXZWyNk=##4oc^>?&`rSd_H(`!Az|l&lOT7*`rM zdon&~Yr3S}m88^h53YBLW5WWcbouSLSt7O+K$zjm%80VOfh1YeqGr#RHeBiP3km5v zRo0ZGzTVNM9zXMG8fK<=3w3kJZdw*=@w1h0O{`-`(#T=ww7niNyH@aCly{Lmqo?%Q zK=)pgrD6jvu}vZ$Vhq`LuFmEv&u-8esJk^NE;>@|4~C3mJ!4IC45~oUn-}OL*5cmm zWa{i>Pzd93qY~%D&5&O`4?jElt%%DyJU#HUrSi?qpx&SSld?-qnAVMQNzZ(8dSYCU zpR`b)if^`Nxz5_nelnTh)ETc!`uF?KA9igiEDz`pG=oy68dHMwILi8H*;>axo({ct z?_N}Tdb$L4%#cc!Aen+rQHC|XuO_Q<$Lfl>E9Z99hv?U~3AZe^ zr>K8ppT%CK`IVB@hJHz5lqAP{-kE+u1Vn-B61-i=T#1uDwx%PJ(&jC*n^%HJ z%hmFo#mUw`<&&z}KNOoqq1Mt&kvU}_o^vP}P^B8I)J1Jc^GMw2YPlt^Eh`!< z?dZa^_KV!<3MPNKCPy!g+}vjvDDhQMU9?zHD~r-vn$b|MoFy6nDx>^3h7 zGxV^T`SU24+@vYWGp**4S?+c)cC>iQ-khTzinU48{(B7SWyiEa4;yPVlj1^|d-a?hJY}W*AAYWjaNJ#&8mDc{UJC`lKoEFGor^;y z4cR-YXKWTxWgREVJ_vFKUXVJ_z@Uh>WjA7A17nn{Ma&zhHJBW=dR}4{(2uB3_VlqX zDZNSSe4lTECnDuEpzpMWc|!KZJ_Ba|Ls4i_edb&0BvLGt(vVaA97rmd;rO^*=T}p7 zvQXH}#YI#Jx7D}IGNByh>X$k*4LZeJQndp}g<2$GeO)w7j>}$+kzAfy&AEz)TX3Q9 zGe$eJo@*o~&O(@3*E)T$rjwhZ+JANNuX$Pfp z|M!_I|Jp`qjSZZb9q0B#kZEm8yT)xFr(D^?r=m;3OP?pK$oSm<`t9uiExFYjTIgod zENKB@txDGAG`WOZlCAr~m@?9=+M$uqye)mU0)bmAXA!L9F*zCd`eff^;NN98_5?N1 zsrw}w%*}7_?BIA?45s2&&RZ@Q7fzB?fXb0RsSxH1*#-Y+egeM)RQ zziIQ4$+eiPc#!HrYTZ!b<^Y#t2Nd_rKH#RDj}1^1>I7%=ImJf8jhE=nk_$BlnNM2> zTGQp|9n+E{TBlY$mngVD&7~G-ymD`&XZF%o>sTg(pZlMd_t7Oi`=%4u^-a8y_9++S z+A)hF8fc3z@_wrW**nDQ?jIkzCi5SGg(|=MJ9Qhr)t0$~Q~ZVJJj{$PF|v2P(qVV% zJV8+LQ#mod!@4H|%Xn=*Go~-k9*leT@8q&B%U_oGRg-cwSFH}FA=@kZyuYg>-@ci; zizQ~t3M12KD~4g(v}(4cbY>^By5FNaK-^#erxMTW2L|0vb{cYSjia;#XzZtj5;0ki z^71SjI(rPc>a3?(gYKShekMsbx# zavKDELg~F>>4o%l2S4#a5*u{6J?7>M9VO`neYd>|dnS(?QB+x5TN>IKk%}93MhD`5 z6iIx#wj39N@o)G5?B1~S-X+=FG?uMllWFaUP`eN}T~F7L)5VDU@%R~nKMGg0LmZxt zL3Hh)cHPbc$I>sHSlba}APuF)`D<0Dsuovc9(i~0Wv2lbeJq7D{}0L4dJaz10Dk^5 zD)d$R4yvs}mlh&w!q3l550T?>s(Z@<8oFdIWi1@e0e>v-?2;1yzc_r>`d)Zw(C4LT hnQFZJS)jzMaddLogCategory( { - category => 'plugin.mog', - defaultLevel => 'ERROR', - description => 'PLUGIN_MOG_MODULE_NAME', -} ); - -sub initPlugin { - my $class = shift; - - Slim::Player::ProtocolHandlers->registerHandler( - mog => 'Slim::Plugin::MOG::ProtocolHandler' - ); - - Slim::Player::ProtocolHandlers->registerIconHandler( - qr|mysqueezebox\.com.*/api/mog/|, - sub { $class->_pluginDataFor('icon') } - ); - - # add custom commands to control radio's diversity - Slim::Control::Request::addDispatch(['mog', 'radiodiversity', '_diversity'], - [0, 1, 1, \&Slim::Plugin::MOG::ProtocolHandler::setRadioDiversity]); - - # Track Info item - Slim::Menu::TrackInfo->registerInfoProvider( mog => ( - after => 'middle', - func => \&trackInfoMenu, - ) ); - - if ( main::WEBUI ) { - # Add a function to view trackinfo in the web - Slim::Web::Pages->addPageFunction( - 'plugins/mog/trackinfo.html', - sub { - my $client = $_[0]; - my $params = $_[1]; - - my $url; - - my $id = $params->{sess} || $params->{item}; - - if ( $id ) { - # The user clicked on a different URL than is currently playing - if ( my $track = Slim::Schema->find( Track => $id ) ) { - $url = $track->url; - } - - # Pass-through track ID as sess param - $params->{sess} = $id; - } - else { - $url = Slim::Player::Playlist::url($client); - } - - Slim::Web::XMLBrowser->handleWebIndex( { - client => $client, - feed => Slim::Plugin::MOG::ProtocolHandler->trackInfoURL( $client, $url ), - path => 'plugins/mog/trackinfo.html', - title => 'MOG Track Info', - timeout => 35, - args => \@_ - } ); - }, - ); - } - - $class->SUPER::initPlugin( - feed => Slim::Networking::SqueezeNetwork->url('/api/mog/v1/opml'), - tag => 'mog', - menu => 'music_services', - weight => 40, - is_app => 1, - ); - -} - -sub trackInfoMenu { - my ( $client, $url, $track, $remoteMeta ) = @_; - - return unless $client; - - # Only show if in the app list - return unless $client->isAppEnabled('mog'); - - my $artist = $track->remote ? $remoteMeta->{artist} : $track->artistName; - my $album = $track->remote ? $remoteMeta->{album} : ( $track->album ? $track->album->name : undef ); - my $title = $track->remote ? $remoteMeta->{title} : $track->title; - - if ( $artist || $album || $title ) { - - my $snURL = Slim::Networking::SqueezeNetwork->url( - '/api/mog/v1/opml/context?artist=' . uri_escape_utf8($artist) - . '&album=' . uri_escape_utf8($album) - . '&track=' . uri_escape_utf8($title) - ); - - return { - type => 'link', - name => $client->string('PLUGIN_ON_MOG'), - url => $snURL, - favorites => 0, - }; - } -} - -sub getDisplayName () { - return 'PLUGIN_MOG_MODULE_NAME'; -} - -# Don't add this item to any menu -sub playerMenu { } - -1; diff --git a/Slim/Plugin/MOG/ProtocolHandler.pm b/Slim/Plugin/MOG/ProtocolHandler.pm deleted file mode 100644 index 8d70b9b6642..00000000000 --- a/Slim/Plugin/MOG/ProtocolHandler.pm +++ /dev/null @@ -1,705 +0,0 @@ -package Slim::Plugin::MOG::ProtocolHandler; - -# $Id: ProtocolHandler.pm 31715 2011-08-16 13:14:29Z shameed $ - -use strict; -use base qw(Slim::Player::Protocols::HTTP); - -use JSON::XS::VersionOneAndTwo; -use Scalar::Util qw(blessed); -use URI::Escape qw(uri_escape_utf8); - -use Slim::Networking::SqueezeNetwork; -use Slim::Music::Info; -use Slim::Utils::Log; -use Slim::Utils::Misc; -use Slim::Utils::Timers; -use Slim::Utils::Prefs; - -use constant MIN_RADIO_QUEUE => 10; - -my $log = Slim::Utils::Log->addLogCategory( { - category => 'plugin.mog', - defaultLevel => 'ERROR', - description => 'PLUGIN_MOG_MODULE_NAME', -} ); - -my $prefs = preferences('server'); -my $log = logger('plugin.mog'); - -my $value = 0; - -sub getFormatForURL { 'mp3' } - -sub isRepeatingStream { 0 } - -sub isRemote { 1 } - -sub canSeek { return 1; } - -sub canSeekError { return ( 'SEEK_ERROR_TYPE_NOT_SUPPORTED', 'MOG' ); } - -# XXX: Port to new streaming - -# To support remote streaming (synced players), we need to subclass Protocols::HTTP -sub new { - my $class = shift; - my $args = shift; - - my $client = $args->{client}; - - my $song = $args->{song}; - my $streamUrl = $song->streamUrl() || return; - my $track = $song->pluginData('info') || {}; - - main::DEBUGLOG && $log->debug( 'Remote streaming MOG track: ' . $streamUrl ); - - my $sock = $class->SUPER::new( { - url => $streamUrl, - song => $args->{song}, - client => $client, - bitrate => ($track->{bitrate} || 320) * 1000, - } ) || return; - - ${*$sock}{contentType} = 'audio/mpeg'; - - return $sock; -} - -# Avoid scanning -sub scanUrl { - my ( $class, $url, $args ) = @_; - - $args->{cb}->( $args->{song}->currentTrack() ); -} - -sub audioScrobblerSource { - my ( $class, $client, $url ) = @_; - - # P = Chosen by the user - return 'P'; -} - -# parseHeaders is used for proxied streaming -sub parseHeaders { - my ( $self, @headers ) = @_; - - __PACKAGE__->parseDirectHeaders( $self->client, $self->url, @headers ); - - return $self->SUPER::parseHeaders( @headers ); -} - -sub parseDirectHeaders { - my ( $class, $client, $url, @headers ) = @_; - - my $song = $client->streamingSong(); - my $track = $song->pluginData('info'); - - my $duration = $track->{duration}; - - my $length; - my $rangelength; - my $redir; - - foreach my $header (@headers) { - if ( $header =~ /^Content-Length:\s*(.*)/i ) { - $length = $1; - } - elsif ($header =~ /^Location:\s*(.*)/i) { - $redir = $1; - } - elsif ( $header =~ m{^Content-Range: .+/(.*)}i ) { - $rangelength = $1; - last; - } - } - - if ( $rangelength ) { - $length = $rangelength; - } - - my $bitrate = $track->{bitrate} * 1000; - - $song->bitrate($bitrate); - $song->duration($duration); - - # ($title, $bitrate, $metaint, $redir, $contentType, $length, $body) - return (undef, $bitrate, 0, $redir, 'mp3', $length, undef); -} - -# Don't allow looping -sub shouldLoop { 0 } - -sub getNextTrack { - my ( $class, $song, $successCb, $errorCb ) = @_; - - my $client = $song->master(); - my $url = $song->track()->url; - - $song->pluginData( abandonSong => 0 ); - - _getTrack($client, $song, { - song => $song, - url => $url, - successCb => $successCb, - errorCb => $errorCb, - }); -} - -sub _getRadioTracks { - my ( $client, $id ) = @_; - - main::DEBUGLOG && $log->is_debug && $log->debug("Getting tracks for station $id from SN"); - - my $http = Slim::Networking::SqueezeNetwork->new( - \&_gotRadioTracks, - sub { - my $http = shift; - - if ( main::DEBUGLOG && $log->is_debug ) { - $log->debug( 'getRadioTracks failed: ' . $http->error ); - } - }, - { - client => $client, - radioId => $id, - }, - ); - - $http->get( - Slim::Networking::SqueezeNetwork->url( - '/api/mog/v1/playback/getRadioTracks?radioid=' . $id - ) - ); -} - -sub _gotRadioTracks { - my $http = shift; - - my $client = $http->params->{client}; - - my $info = eval { from_json( $http->content ) }; - - if ( $@ ) { - $log->error(Data::Dump::dump($info, $@)); - } - else { - my $icon = __PACKAGE__->getIcon(); - my $cache = Slim::Utils::Cache->new; - - if ( main::DEBUGLOG && $log->is_debug ) { - $log->debug( 'getRadioTracks ok: ' . Data::Dump::dump($info) ); - } - - my $song = $client->playingSong(); - my $currentId = 0; - - if ($song && $song->track) { - ($currentId) = getIds($song->track->url || ''); - } - - my @tracks; - foreach my $track ( @$info ) { - next unless ref $track eq 'HASH'; - - # cache the metadata we need for display - my $trackId = delete $track->{id}; - - # don't queue track which we're already playing - next if $trackId == $currentId; - - my $meta = { - artist => $track->{artist}, - album => $track->{album}, - title => $track->{title}, - cover => $track->{cover} || $icon, - duration => $track->{duration}, - genre => $track->{genre}, - year => $track->{year}, - bitrate => '320k CBR', # XXX bulk API call does not know the actual bitrate, it will be replaced in _gotTrack - type => 'MP3 (MOG)', - info_link => 'plugins/mog/trackinfo.html', - icon => $icon, - }; - - $cache->set( 'mog_meta_' . $trackId, $meta, 86400 ); - - my $url = 'mog://' . $http->params->{radioId} . '-' . $trackId . '.mp3'; - - push @tracks, Slim::Schema->updateOrCreate( { - 'url' => $url, - 'attributes' => { - title => $meta->{title}, - album => $meta->{album}, - cover => $meta->{cover}, - duration => $meta->{duration}, - year => $meta->{year}, - CT => 'mp3', - }, - } ); - } - - $client->execute([ 'playlist', 'addtracks', 'listRef', \@tracks ]); - $client->execute([ 'play' ]); - - # Update the playlist time so the web will refresh, etc - $client->currentPlaylistUpdateTime( Time::HiRes::time() ); - - Slim::Control::Request::notifyFromArray( $client, [ 'newmetadata' ] ); - } -} - -sub _getTrack { - my ( $client, $song, $params ) = @_; - - return if $song->pluginData('abandonSong'); - - # Get track URL for the next track - my ($trackId, $radioId) = getIds($params->{url}); - - my $http = Slim::Networking::SqueezeNetwork->new( - sub { - my $http = shift; - my $info = eval { from_json( $http->content ) }; - if ( $@ || $info->{error} || !$info->{url} ) { - if ( main::DEBUGLOG && $log->is_debug ) { - $log->debug( 'getTrackInfo failed: ' . ( $@ || $info->{error} ) ); - $log->debug( ' data received: ' . Data::Dump::dump($info) ); - } - - _gotTrackError( $@ || $info->{error}, $client, $params ); - } - else { - _gotTrack( $client, $info, $params ); - - if ( main::DEBUGLOG && $log->is_debug ) { - delete $info->{url}; - $log->debug( 'getTrackInfo ok: ' . Data::Dump::dump($info) ); - } - } - }, - sub { - my $http = shift; - - if ( main::DEBUGLOG && $log->is_debug ) { - $log->debug( 'getTrackInfo failed: ' . $http->error ); - } - - _gotTrackError( $http->error, $client, $params ); - }, - { - client => $client, - }, - ); - - main::DEBUGLOG && $log->is_debug && $log->debug('Getting next track playback info from SN'); - - $http->get( - # XXX - this call is to be renamed to getMediaURL, it's only kept here until we update mysb.com for backwards compatibility - Slim::Networking::SqueezeNetwork->url( - '/api/mog/v1/playback/playStream?trackid=' . ($trackId || '') . '&radioid=' . ($radioId || '') - ) - ); -} - -sub _gotTrack { - my ( $client, $info, $params ) = @_; - - my $song = $params->{song}; - - return if $song->pluginData('abandonSong'); - - # Save the media URL for use in strm - $song->streamUrl($info->{url}); - - # Cache the rest of the track's metadata - my $meta = { - artist => $info->{artist}, - album => $info->{album}, - title => $info->{title}, - cover => $info->{cover}, - duration => $info->{duration}, - genre => $info->{genre}, - year => $info->{year}, - bitrate => ($info->{bitrate} || 320). 'k CBR', - type => 'MP3 (MOG)', - info_link => 'plugins/mog/trackinfo.html', - }; - - $song->pluginData( info => $info ); - $song->duration( $info->{duration} ); - - my $cache = Slim::Utils::Cache->new; - - $cache->set( 'mog_meta_' . $info->{id}, $meta, 86400 ); - - # Update the playlist time so the web will refresh, etc - $client->currentPlaylistUpdateTime( Time::HiRes::time() ); - - Slim::Control::Request::notifyFromArray( $client, [ 'newmetadata' ] ); - - # Async resolve the hostname so gethostbyname in Player::Squeezebox::stream doesn't block - # When done, callback will continue on to playback - my $dns = Slim::Networking::Async->new; - $dns->open( { - Host => URI->new( $info->{url} )->host, - Timeout => 3, # Default timeout of 10 is too long, - # by the time it fails player will underrun and stop - onDNS => $params->{successCb}, - onError => $params->{successCb}, # even if it errors, keep going - passthrough => [], - } ); - - # Watch for playlist commands - Slim::Control::Request::subscribe( - \&_playlistCallback, - [['playlist'], ['newsong']], - $song->master(), - ); - -} - -# Metadata for a URL, used by CLI/JSON clients -sub getMetadataFor { - my ( $class, $client, $url ) = @_; - - my $icon = $class->getIcon(); - - return {} unless $url; - - my $cache = Slim::Utils::Cache->new; - - # If metadata is not here, fetch it so the next poll will include the data - my ($trackId) = getIds($url); - my $meta = $cache->get( 'mog_meta_' . $trackId ); - - if ( !$meta && !$client->master->pluginData('fetchingMeta') ) { - # Go fetch metadata for all tracks on the playlist without metadata - my @need; - - for my $track ( @{ Slim::Player::Playlist::playList($client) } ) { - my ($trackURL) = blessed($track) ? $track->url : $track; - if ( my ($trackId) = getIds($trackURL) ) { - if ( !$cache->get("mog_meta_$trackId") ) { - push @need, $trackId; - } - } - } - - if ( main::DEBUGLOG && $log->is_debug ) { - $log->debug( "Need to fetch metadata for: " . join( ', ', @need ) ); - } - - if (scalar @need) { - $client->master->pluginData( fetchingMeta => 1 ); - - my $metaUrl = Slim::Networking::SqueezeNetwork->url( - "/api/mog/v1/playback/getBulkMetadata" - ); - - my $http = Slim::Networking::SqueezeNetwork->new( - \&_gotBulkMetadata, - \&_gotBulkMetadataError, - { - client => $client, - timeout => 60, - }, - ); - - $http->post( - $metaUrl, - 'Content-Type' => 'application/x-www-form-urlencoded', - 'trackids=' . join( ',', @need ), - ); - } - } - - #$log->debug( "Returning metadata for: $url" . ($meta ? '' : ': default') ); - - return $meta || { - bitrate => '320k CBR', - type => 'MP3 (MOG)', - icon => $icon, - cover => $icon, - }; -} - -sub getIds { - my $url = shift; - - my ($radioId, $trackId); - - # radio track - if ( $url =~ m{mog://([at].+v\d+)-(.+)\.mp3}) { - $radioId = $1; - $trackId = $2; - } - else { - ($trackId) = $url =~ m{mog://(.+)\.mp3}; - } - - return ($trackId, $radioId); -} - -sub _gotBulkMetadata { - my $http = shift; - - my $client = $http->params->{client}; - - $client->master->pluginData( fetchingMeta => 0 ); - - my $info = eval { from_json( $http->content ) }; - - if ( $@ || ref $info ne 'ARRAY' || !scalar @$info ) { - $log->error( "Error fetching track metadata: " . ( $@ || 'Invalid JSON response' ) ); - return; - } - - if ( main::DEBUGLOG && $log->is_debug ) { - $log->debug( "Caching metadata for " . scalar( @{$info} ) . " tracks: " . Data::Dump::dump($info) ); - } - - # Cache metadata - my $cache = Slim::Utils::Cache->new; - my $icon = Slim::Plugin::MOG::Plugin->_pluginDataFor('icon'); - - for my $track ( @{$info} ) { - next unless ref $track eq 'HASH'; - - # cache the metadata we need for display - my $trackId = delete $track->{id}; - - my $meta = { - artist => $track->{artist}, - album => $track->{album}, - title => $track->{title}, - cover => $track->{cover} || $icon, - duration => $track->{duration}, - genre => $track->{genre}, - year => $track->{year}, - bitrate => '320k CBR', # XXX bulk API call does not know the actual bitrate, it will be replaced in _gotTrack - type => 'MP3 (MOG)', - info_link => 'plugins/mog/trackinfo.html', - icon => $icon, - }; - - $cache->set( 'mog_meta_' . $trackId, $meta, 86400 ); - } - - # Update the playlist time so the web will refresh, etc - $client->currentPlaylistUpdateTime( Time::HiRes::time() ); - - Slim::Control::Request::notifyFromArray( $client, [ 'newmetadata' ] ); -} - -sub _gotBulkMetadataError { - my $http = shift; - - my $client = $http->params('client'); - my $error = $http->error; - - $client->master->pluginData( fetchingMeta => 0 ); - - $log->warn("Error getting track metadata from SN: $error"); -} - -sub _gotTrackError { - my ( $error, $client, $params ) = @_; - - main::DEBUGLOG && $log->debug("Error during getTrackInfo: $error"); - - return if $params->{song}->pluginData('abandonSong'); - - _handleClientError( $error, $client, $params ); -} - -sub _handleClientError { - my ( $error, $client, $params ) = @_; - - my $song = $params->{song}; - - return if $song->pluginData('abandonSong'); - - # Tell other clients to give up - $song->pluginData( abandonSong => 1 ); - - $params->{errorCb}->($error); -} - -sub canDirectStreamSong { - my ( $class, $client, $song ) = @_; - - # We need to check with the base class (HTTP) to see if we - # are synced or if the user has set mp3StreamingMethod - return $class->SUPER::canDirectStream( $client, $song->streamUrl(), $class->getFormatForURL() ); -} - -sub _playlistCallback { - my $request = shift; - - my $client = $request->client(); - my $event = $request->getRequest(1); - - return unless defined $client; - - # check that user is still using MOG Radio - my $song = $client->playingSong(); - - if ( !$song || $song->currentTrackHandler ne __PACKAGE__ ) { - # User stopped playing MOG - main::DEBUGLOG && $log->debug( "Stopped MOG, unsubscribing from playlistCallback" ); - Slim::Control::Request::unsubscribe( \&_playlistCallback, $client ); - - return; - } - - my $url = $song->track->url; - - my ($trackId, $radioId) = getIds($url); - - if ( $radioId ) { - # disable repeat in radio mode - Slim::Player::Playlist::repeat($client, 0); - - Slim::Utils::Timers::killTimers($client, \&_cleanupRadioTracks); - Slim::Utils::Timers::setTimer($client, time() + 5, \&_cleanupRadioTracks, $radioId); - } -} - -sub _cleanupRadioTracks { - my ($client, $radioId) = @_; - - my $pos = Slim::Player::Source::playingSongIndex($client); - - # remove played/skipped tracks from queue - if ($pos > 0) { - my @tracks = Slim::Player::Playlist::songs($client, 0, $pos-1); - $client->execute([ 'playlist', 'deletetracks', 'listRef', \@tracks ]) if scalar @tracks; - } - - my $length = Slim::Player::Playlist::count($client); - - if ($length < MIN_RADIO_QUEUE) { - main::DEBUGLOG && $log->debug( "Need to queue up new MOG Radio tracks... $radioId" ); - _getRadioTracks($client, $radioId); - } -} - -# set diversity of the currently playing radio station -sub setRadioDiversity { - my $request = shift; - my $client = $request->client(); - - $log->error($client); - return unless defined $client; - - # ignore if user is not using Pandora - my $song = $client->playingSong() || return; - my $url = $song->currentTrack()->url; - - my ($trackId, $radioId) = getIds($url); - my $diversity = $request->getParam('_diversity') || 0; - - $radioId =~ s/([at].+v)(\d+)/$1$diversity/; - - # only continue if we're actually playing a radio station - return unless $trackId && $radioId; - - # remove queued tracks - my $pos = Slim::Player::Source::playingSongIndex($client); - my $length = Slim::Player::Playlist::count($client); - - $pos++; - while ($length-- > $pos) { - $client->execute([ 'playlist', 'delete', $pos ]); - } - - _getRadioTracks($client, $radioId); - - $request->setStatusDone(); -} - -sub trackInfoURL { - my ( $class, $client, $url ) = @_; - - my ($trackId, $radioId) = getIds($url); - - # SN URL to fetch track info menu - my $trackInfoURL = Slim::Networking::SqueezeNetwork->url( - '/api/mog/v1/opml/trackinfo?trackid=' . ($trackId || '') . '&radioid=' . ($radioId || '') - ); - - return $trackInfoURL; -} - -# Track Info menu -sub trackInfo { - my ( $class, $client, $track ) = @_; - - my $url = $track->url; - my $trackInfoURL = $class->trackInfoURL( $client, $url ); - - # let XMLBrowser handle all our display - my %params = ( - header => 'PLUGIN_MOG_GETTING_STREAM_INFO', - modeName => 'MOG Now Playing', - title => Slim::Music::Info::getCurrentTitle( $client, $url ), - url => $trackInfoURL, - ); - - main::DEBUGLOG && $log->debug( "Getting track information for $url" ); - - Slim::Buttons::Common::pushMode( $client, 'xmlbrowser', \%params ); - - $client->modeParam( 'handledTransition', 1 ); -} - -sub getIcon { - my ( $class, $url ) = @_; - - return Slim::Plugin::MOG::Plugin->_pluginDataFor('icon'); -} - -# SN only, re-init upon reconnection -sub reinit { - my ( $class, $client, $song ) = @_; - - my $url = $song->track->url(); - - main::DEBUGLOG && $log->is_debug && $log->debug("Re-init MOG - $url"); - - my $cache = Slim::Utils::Cache->new; - my ($trackId, $radioId) = getIds($url); - my $meta = $cache->get( 'mog_meta_' . $trackId ); - - if ( $meta ) { - # We have previous data about the currently-playing song - - # Back to Now Playing - Slim::Buttons::Common::pushMode( $client, 'playlist' ); - - # Reset song duration/progress bar - if ( $meta->{duration} ) { - $song->duration( $meta->{duration} ); - - # On a timer because $client->currentsongqueue does not exist yet - Slim::Utils::Timers::setTimer( - $client, - Time::HiRes::time(), - sub { - my $client = shift; - - $client->streamingProgressBar( { - url => $url, - duration => $meta->{duration}, - } ); - }, - ); - } - } - - return 1; -} - -1; - diff --git a/Slim/Plugin/MOG/install.xml b/Slim/Plugin/MOG/install.xml deleted file mode 100644 index 569a124da17..00000000000 --- a/Slim/Plugin/MOG/install.xml +++ /dev/null @@ -1,18 +0,0 @@ - - - 4f729f30-5c70-11e1-b86c-0800200c9a66 - PLUGIN_MOG_MODULE_NAME - Slim::Plugin::MOG::Plugin - 1.0 - PLUGIN_MOG_MODULE_NAME - Logitech - enabled - http://www.mysqueezebox.com/appgallery/mog - plugins/MOG/html/images/icon.png - 2 - - Squeezebox Server - 7.5 - * - - diff --git a/Slim/Plugin/MOG/strings.txt b/Slim/Plugin/MOG/strings.txt deleted file mode 100644 index f7ccc6f8096..00000000000 --- a/Slim/Plugin/MOG/strings.txt +++ /dev/null @@ -1,32 +0,0 @@ -PLUGIN_MOG_ADD_ALL_PLAYLIST - EN Add All to MOG Playlist - -PLUGIN_ADD_TRACK_PLAYLIST - EN Add track to MOG Playlist - -PLUGIN_MOG_PLAYLIST - EN MOG Playlist Options - -PLUGIN_MOG_BOOKMARKS - EN MOG Favorites Options - -PLUGIN_MOG - EN MOG - -PLUGIN_MOG_MODULE_NAME - EN MOG - -PLUGIN_ON_MOG - CS Na MOG - DA På MOG - DE Auf MOG - EN On MOG - ES En MOG - FI MOGssa - FR Sur MOG - IT Su MOG - NL Op MOG - NO På MOG - PL W usłudze MOG - RU На MOG - SV På MOG