Advertisement
Guest User

Untitled

a guest
May 31st, 2016
190
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Perl 6 15.38 KB | None | 0 0
  1. package Plugins::GoogleMusic::AllAccess;
  2.  
  3. use strict;
  4. use warnings;
  5. use Readonly;
  6.  
  7. use Slim::Utils::Prefs;
  8. use Slim::Utils::Log;
  9. use Slim::Utils::Cache;
  10.  
  11. use Plugins::GoogleMusic::GoogleAPI;
  12.  
  13. # Cache most of the results for one hour
  14. Readonly my $CACHE_TIME => 3600;
  15. # Cache track information for one day because get_album_info() would
  16. # be used too often
  17. Readonly my $CACHE_TIME_LONG => 24 * 3600;
  18. # Cache user modifiable data shorlty
  19. Readonly my $CACHE_TIME_SHORT => 30;
  20.  
  21. my $log = logger('plugin.googlemusic');
  22. my $prefs = preferences('plugin.googlemusic');
  23. my $googleapi = Plugins::GoogleMusic::GoogleAPI::get();
  24. my $cache;
  25.  
  26. sub init {
  27.     $cache = shift;
  28.  
  29.     return;
  30. }
  31.  
  32. # Convert an All Access Google Music Song dictionary to a consistent
  33. # robust track representation
  34. sub to_slim_track {
  35.     my $song = shift;
  36.  
  37.     my $uri = 'googlemusic:track:' . $song->{storeId};
  38.  
  39.     if (my $track = $cache->get($uri)) {
  40.         return $track;
  41.     }
  42.  
  43.     my $cover = '/html/images/cover.png';
  44.     if (exists $song->{albumArtRef}) {
  45.         $cover = $song->{albumArtRef}[0]{url};
  46.         $cover = Plugins::GoogleMusic::Image->uri($cover);
  47.     }
  48.  
  49.     # Get/create the album for this song
  50.     my $album = to_slim_album($song);
  51.  
  52.     # Build track info
  53.     my $track = {
  54.         uri => $uri,
  55.         id => $song->{storeId},
  56.         title => $song->{title},
  57.         album => $album,
  58.         artist => to_slim_artist($song),
  59.         year => $song->{year} || 0,
  60.         cover => $cover,
  61.         secs => $song->{durationMillis} / 1000,
  62.         bitrate => 320,
  63.         genre => $song->{genre},
  64.         filesize => $song->{estimatedSize},
  65.         trackNumber => $song->{trackNumber} || 1,
  66.         discNumber => $song->{discNumber} || 1,
  67.         rating => $song->{rating} || 0,
  68.     };
  69.  
  70.     # Add to the cache
  71.     $cache->set($uri, $track, $CACHE_TIME_LONG);
  72.  
  73.     return $track;
  74. }
  75.  
  76. # Convert an All Access Google Music Song dictionary to a consistent
  77. # robust album representation
  78. sub to_slim_album {
  79.     my $song = shift;
  80.  
  81.     my $artist = to_slim_album_artist($song);
  82.     my $name = $song->{album};
  83.     my $year = $song->{year} || 0;
  84.  
  85.     my $uri = 'googlemusic:album:' . $song->{albumId};
  86.  
  87.     my $cover = '/html/images/cover.png';
  88.     if (exists $song->{albumArtRef}) {
  89.         $cover = $song->{albumArtRef}[0]{url};
  90.         $cover = Plugins::GoogleMusic::Image->uri($cover);
  91.     }
  92.  
  93.     my $album = {
  94.         uri => $uri,
  95.         id => $song->{albumId},
  96.         name => $name,
  97.         artist => $artist,
  98.         year => $year,
  99.         cover => $cover,
  100.         tracks => [],
  101.     };
  102.  
  103.     return $album;
  104. }
  105.  
  106. # Convert an All Access Google Music Song dictionary to a consistent
  107. # robust artist representation
  108. sub to_slim_artist {
  109.     my $song = shift;
  110.  
  111.     my $id = scalar $song->{artistId} ? $song->{artistId}[0] : 'unknown';
  112.     my $uri = 'googlemusic:artist:' . $id;
  113.     my $name = $song->{artist};
  114.  
  115.     my $image = '/html/images/artists.png';
  116.     if (exists $song->{artistArtRef}) {
  117.         $image = $song->{artistArtRef}[0]{url};
  118.         $image = Plugins::GoogleMusic::Image->uri($image);
  119.     }
  120.  
  121.     my $artist = {
  122.         uri => $uri,
  123.         id => $id,
  124.         name => $name,
  125.         image => $image,
  126.     };
  127.  
  128.     return $artist;
  129. }
  130.  
  131. # Convert an All Access Google Music Song dictionary to a consistent
  132. # robust album artist representation
  133. sub to_slim_album_artist {
  134.     my $song = shift;
  135.  
  136.     # In one test case (the band 'Poliça') GoogleMusic messed up the
  137.     # 'artist' is sometime lowercase, where the 'albumArtist' is
  138.     # uppercase the albumArtist is the most consistent so take that or
  139.     # else we will see multiple entries in the Artists listing (lower
  140.     # + upper case)
  141.     my $name = $song->{albumArtist} || $song->{artist};
  142.    
  143.     # TODO: No album artist ID in tracks. Should we fetch the album info?
  144.     my $uri = 'googlemusic:artist:unknown';
  145.  
  146.     my $image = '/html/images/artists.png';
  147.     # Check to see if this album is a compilation from various
  148.     # artists. The Google Music webinterface also shows a 'Various
  149.     # artists' in my library instead of all seperate artists.. which
  150.     # should justify this functionality
  151.     my $various = (index(lc($song->{artist}), lc($song->{albumArtist} || '')) == -1) ? 1 : 0;
  152.     if (exists $song->{artistArtRef} and not $various) {
  153.         $image = $song->{artistArtRef}[0]{url};
  154.         $image = Plugins::GoogleMusic::Image->uri($image);
  155.     }
  156.    
  157.     my $artist = {
  158.         uri => $uri,
  159.         id => 'unknown',
  160.         name => $name,
  161.         various => $various,
  162.         image => $image,
  163.     };
  164.  
  165.     return $artist;
  166. }
  167.  
  168. sub get_track {
  169.     my $uri = shift;
  170.  
  171.     return unless $prefs->get('all_access_enabled');
  172.  
  173.     if (my $track = $cache->get($uri)) {
  174.         return $track;
  175.     }
  176.  
  177.     my ($id) = $uri =~ m{^googlemusic:track:(.*)$}x;
  178.     my $song;
  179.  
  180.     eval {
  181.         # For some reasons Google only knows this does not return the
  182.         # rating of the track. Surprisingly, fetching the whole album
  183.         # returns the rating. Thus, we get the track info and after
  184.         # that the album info for that track.
  185.         $song = $googleapi->get_track_info($id);
  186.     };
  187.     if ($@) {
  188.         $log->error("Not able to get the track info for track ID $id: $@");
  189.         return;
  190.     }
  191.  
  192.     # Get the album and process all tracks to get them into the cache
  193.     my $album = get_album_info('googlemusic:album:' . $song->{albumId});
  194.  
  195.     # Get the track from the cache including the rating field :-)
  196.     if (my $track = $cache->get($uri)) {
  197.         return $track;
  198.     }
  199.  
  200.     $log->error("Not able to get the track info for track ID $id from the album information");
  201.  
  202.     return;
  203. }
  204.  
  205. sub get_track_by_id {
  206.     my $id = shift;
  207.  
  208.     return get_track('googlemusic:track:' . $id);
  209. }
  210.  
  211. # Search All Access
  212. sub search {
  213.     my $query = shift;
  214.  
  215.     return unless $prefs->get('all_access_enabled');
  216.  
  217.     my $uri = 'googlemusic:search:' . $query;
  218.  
  219.     if (my $result = $cache->get($uri)) {
  220.         return $result;
  221.     }
  222.  
  223.     my $googleResult;
  224.     my $result = {
  225.         tracks => [],
  226.         albums => [],
  227.         artists => [],
  228.     };       
  229.  
  230.     eval {
  231.         $googleResult = $googleapi->search($query, $prefs->get('max_search_items'));
  232.     };
  233.     if ($@) {
  234.         $log->error("Not able to search All Access for \"$query\": $@");
  235.         return;
  236.     }
  237.     for my $hit (@{$googleResult->{song_hits}}) {
  238.         push @{$result->{tracks}}, to_slim_track($hit->{track});
  239.     }
  240.     for my $hit (@{$googleResult->{album_hits}}) {
  241.         push @{$result->{albums}}, album_to_slim_album($hit->{album});
  242.     }
  243.     for my $hit (@{$googleResult->{artist_hits}}) {
  244.         push @{$result->{artists}}, artist_to_slim_artist($hit->{artist});
  245.     }
  246.  
  247.     # Add to the cache
  248.     $cache->set($uri, $result, $CACHE_TIME);
  249.  
  250.     return $result;
  251. }
  252.  
  253. # Search All Access tracks only. Do not cache these searches for now.
  254. sub searchTracks {
  255.     my ($query, $maxResults) = @_;
  256.  
  257.     my $googleResult;
  258.     my $tracks = [];
  259.  
  260.     return $tracks unless $prefs->get('all_access_enabled');
  261.  
  262.     eval {
  263.         $googleResult = $googleapi->search($query, $maxResults);
  264.     };
  265.  
  266.     if ($@) {
  267.         $log->error("Not able to search All Access for \"$query\": $@");
  268.     } else {
  269.         for my $hit (@{$googleResult->{song_hits}}) {
  270.             push @$tracks, to_slim_track($hit->{track});
  271.         }
  272.     }
  273.  
  274.     return $tracks;
  275. }
  276.  
  277. # Get information for an artist
  278. sub get_artist_info {
  279.     my $uri = shift;
  280.  
  281.     return unless $prefs->get('all_access_enabled');
  282.  
  283.     if (my $artist = $cache->get($uri)) {
  284.         return $artist;
  285.     }
  286.  
  287.     my ($id) = $uri =~ m{^googlemusic:artist:(.*)$}x;
  288.  
  289.     my $googleArtist;
  290.     my $artist;
  291.  
  292.     eval {
  293.         $googleArtist = $googleapi->get_artist_info($id, $Inline::Python::Boolean::true, $prefs->get('max_artist_tracks'), $prefs->get('max_related_artists'));
  294.     };
  295.     if ($@) {
  296.         $log->error("Not able to get the artist info for artist ID $id: $@");
  297.         return;
  298.     }
  299.    
  300.     $artist = artist_to_slim_artist($googleArtist);
  301.  
  302.     # Add to the cache
  303.     $cache->set($uri, $artist, $CACHE_TIME);
  304.  
  305.     return $artist;
  306. }
  307.  
  308. sub get_artist_image {
  309.     my $uri = shift;
  310.  
  311.     return unless $prefs->get('all_access_enabled');
  312.  
  313.     # First try to get the image from the artist cache
  314.     if (my $artist = $cache->get($uri)) {
  315.         return $artist->{image};
  316.     }
  317.  
  318.     my ($id) = $uri =~ m{^googlemusic:artist:(.*)$}x;
  319.  
  320.     my $imageuri = 'googlemusic:artistimage:' . $id;
  321.  
  322.     if (my $artistImage = $cache->get($imageuri)) {
  323.         return $artistImage;
  324.     }
  325.  
  326.     my $googleArtist;
  327.  
  328.     eval {
  329.         $googleArtist = $googleapi->get_artist_info($id, $Inline::Python::Boolean::false, 0, 0);
  330.     };
  331.     if ($@) {
  332.         $log->error("Not able to get the artist image for artist ID $id: $@");
  333.     }
  334.  
  335.     my $image = '/html/images/artists.png';
  336.     if ($googleArtist && exists $googleArtist->{artistArtRef}) {
  337.         $image = $googleArtist->{artistArtRef};
  338.         $image = Plugins::GoogleMusic::Image->uri($image);
  339.     }
  340.  
  341.     # Add to the cache
  342.     $cache->set($imageuri, $image, $CACHE_TIME);
  343.  
  344.     return $image;
  345. }
  346.  
  347. # Get information for an album
  348. sub get_album_info {
  349.     my $uri = shift;
  350.  
  351.     return unless $prefs->get('all_access_enabled');
  352.  
  353.     if (my $album = $cache->get($uri)) {
  354.         return $album;
  355.     }
  356.  
  357.     my ($id) = $uri =~ m{^googlemusic:album:(.*)$}x;
  358.  
  359.     my $googleAlbum;
  360.     my $album;
  361.  
  362.     eval {
  363.         $googleAlbum = $googleapi->get_album_info($id);
  364.     };
  365.     if ($@) {
  366.         $log->error("Not able to get the album info for album ID $id: $@");
  367.         return;
  368.     }
  369.  
  370.     $album = album_to_slim_album($googleAlbum);
  371.  
  372.     # Add to the cache
  373.     $cache->set($uri, $album, $CACHE_TIME);
  374.  
  375.     return $album;
  376. }
  377.  
  378. # Convert an All Access Google album dictionary to a consistent
  379. # robust album representation
  380. sub album_to_slim_album {
  381.     my $googleAlbum = shift;
  382.  
  383.     my $artistId = scalar $googleAlbum->{artistId} ? $googleAlbum->{artistId}[0] : 'unknown';
  384.  
  385.     # TODO: Sometimes the array has multiple entries
  386.     my $artist = {
  387.         uri => 'googlemusic:artist:' . $artistId,
  388.         id => $artistId,
  389.         name => $googleAlbum->{albumArtist} || $googleAlbum->{artist},
  390.         various => 0,
  391.     };
  392.  
  393.     my $name = $googleAlbum->{name};
  394.     my $year = $googleAlbum->{year} || 0;
  395.  
  396.     my $uri = 'googlemusic:album:' . $googleAlbum->{albumId};
  397.  
  398.     my $cover = '/html/images/cover.png';
  399.     if (exists $googleAlbum->{albumArtRef}) {
  400.         $cover = $googleAlbum->{albumArtRef};
  401.         $cover = Plugins::GoogleMusic::Image->uri($cover);
  402.     }
  403.  
  404.     my $album = {
  405.         uri => $uri,
  406.         id => $googleAlbum->{albumId},
  407.         name => $name,
  408.         artist => $artist,
  409.         year => $year,
  410.         cover => $cover,
  411.         tracks => [],
  412.     };
  413.  
  414.     if (exists $googleAlbum->{tracks}) {
  415.         foreach (@{$googleAlbum->{tracks}}) {
  416.             my $track = to_slim_track($_);
  417.             if ($track->{album}->{artist}->{various}) {
  418.                 $artist->{various} = 1;
  419.             }
  420.             push @{$album->{tracks}}, $track;
  421.         }
  422.     }
  423.  
  424.     if (exists $googleAlbum->{description}) {
  425.         $album->{description} = $googleAlbum->{description};
  426.     }
  427.  
  428.     return $album;
  429. }
  430.  
  431. # Convert an All Access Google Music artist dictionary to a consistent
  432. # robust artist representation
  433. sub artist_to_slim_artist {
  434.     my $googleArtist = shift;
  435.  
  436.     my $name = $googleArtist->{name};
  437.  
  438.     my $uri = 'googlemusic:artist:' . $googleArtist->{artistId};
  439.  
  440.     my $image = '/html/images/artists.png';
  441.     if (exists $googleArtist->{artistArtRef}) {
  442.         $image = $googleArtist->{artistArtRef};
  443.         $image = Plugins::GoogleMusic::Image->uri($image);
  444.     }
  445.  
  446.     my $artist = {
  447.         uri => $uri,
  448.         id => $googleArtist->{artistId},
  449.         name => $name,
  450.         image => $image,
  451.         tracks => [],
  452.         albums => [],
  453.         related => [],
  454.     };
  455.  
  456.     if (exists $googleArtist->{topTracks}) {
  457.         for my $track (@{$googleArtist->{topTracks}}) {
  458.             push @{$artist->{tracks}}, to_slim_track($track);
  459.         }
  460.     }
  461.     if (exists $googleArtist->{albums}) {
  462.         for my $album (@{$googleArtist->{albums}}) {
  463.             push @{$artist->{albums}}, album_to_slim_album($album);
  464.         }
  465.     }
  466.     if (exists $googleArtist->{related_artists}) {
  467.         for my $related (@{$googleArtist->{related_artists}}) {
  468.             push @{$artist->{related}}, artist_to_slim_artist($related);
  469.         }
  470.     }
  471.  
  472.     if (exists $googleArtist->{artistBio}) {
  473.         $artist->{artistBio} = $googleArtist->{artistBio};
  474.     }
  475.  
  476.     return $artist;
  477. }
  478.  
  479. # Get Google Music genres, either parents or child genres
  480. sub getGenres {
  481.     my $uri = shift;
  482.  
  483.     return unless $prefs->get('all_access_enabled');
  484.    
  485.     my ($parent) = $uri =~ m{^googlemusic:genres:(.*)$}x;
  486.  
  487.     my $genres;
  488.  
  489.     if ($genres = $cache->get($uri)) {
  490.         return $genres;
  491.     }
  492.  
  493.     my $googleGenres;
  494.     $genres = [];
  495.  
  496.     eval {
  497.         if (Plugins::GoogleMusic::GoogleAPI::get_version() lt '4.1.0') {
  498.             $googleGenres = $googleapi->get_genres($parent)->{genres};
  499.         } else {
  500.             $googleGenres = $googleapi->get_genres($parent);
  501.         }
  502.     };
  503.     if ($@) {
  504.         $log->error("Not able to get genres: $@");
  505.         return $genres;
  506.     }
  507.  
  508.     for my $genre (@{$googleGenres}) {
  509.         push @{$genres}, genreToSlimGenre($genre);
  510.     }
  511.    
  512.     $cache->set($uri, $genres, $CACHE_TIME);
  513.  
  514.     return $genres;
  515. }
  516.  
  517. # Convert an All Access Google Music genre dictionary to a consistent
  518. # robust genre representation
  519. sub genreToSlimGenre {
  520.     my $googleGenre = shift;
  521.  
  522.     my $uri = 'googlemusic:genre:' . $googleGenre->{id};
  523.  
  524.     my $image = '/html/images/genres.png';
  525.     if (exists $googleGenre->{images}) {
  526.         $image = $googleGenre->{images}[0]{url};
  527.         $image = Plugins::GoogleMusic::Image->uri($image);
  528.     }
  529.  
  530.     my $genre = {
  531.         uri => $uri,
  532.         id => $googleGenre->{id},
  533.         name => $googleGenre->{name},
  534.         image => $image,
  535.     };
  536.  
  537.     if (exists $googleGenre->{children}) {
  538.         $genre->{children} = $googleGenre->{children};
  539.     }
  540.     if (exists $googleGenre->{parentId}) {
  541.         $genre->{parent} = $googleGenre->{parentId};
  542.     }
  543.  
  544.     $cache->set($uri, $genre, $CACHE_TIME);
  545.  
  546.     return $genre;
  547. }
  548.  
  549. # Get all user-created radio stations
  550. sub getStations {
  551.    
  552.     return [] unless $prefs->get('all_access_enabled');
  553.    
  554.     my $stations;
  555.     my $uri = 'googlemusic:stations';
  556.  
  557.     if ($stations = $cache->get($uri)) {
  558.         return $stations;
  559.     }
  560.  
  561.     eval {
  562.         $stations = $googleapi->get_all_stations();
  563.     };
  564.     if ($@) {
  565.         $log->error("Not able to get user created radio stations: $@");
  566.         return [];
  567.     }
  568.  
  569.     $cache->set($uri, $stations, $CACHE_TIME_SHORT);
  570.  
  571.     return $stations;
  572. }
  573.  
  574. sub deleteStation {
  575.     my $id = shift;
  576.    
  577.     eval {
  578.         $googleapi->delete_stations($id);
  579.     };
  580.     if ($@) {
  581.         $log->error("Not able to delete radio station $id: $@");
  582.         return;
  583.     }
  584.  
  585.     # Remove from cache to force reload
  586.     $cache->remove('googlemusic:stations');
  587.  
  588.     # Return the ID on success
  589.     return $id;
  590. }
  591.  
  592. # Get a specific genre (from the cache)
  593. sub getGenre {
  594.     my $uri = shift;
  595.  
  596.     my $genre;
  597.  
  598.     if ($genre = $cache->get($uri)) {
  599.         return $genre;
  600.     }
  601.  
  602.     # Not found in the cache. Refresh parent genres.
  603.     my $genres = getGenres('googlemusic:genres');
  604.     # Try again
  605.     if ($genre = $cache->get($uri)) {
  606.         return $genre;
  607.     }
  608.  
  609.     # Still not found. Must be a child genre.
  610.     my ($id) = $uri =~ m{^googlemusic:genre:(.*)$}x;
  611.     # Search a matching parent and get its childs
  612.     for my $parent (@{$genres}) {
  613.         if ( grep { $_ eq $id } @{$parent->{children}} ) {
  614.             $genres = getGenres($parent->{uri});
  615.             last;
  616.         }
  617.     }
  618.     # Try again
  619.     if ($genre = $cache->get($uri)) {
  620.         return $genre;
  621.     }
  622.  
  623.     $log->error("Not able to get genre: $uri");
  624.  
  625.     return;
  626. }
  627.  
  628. # Change the rating of a track
  629. sub changeRating {
  630.     my ($uri, $rating) = @_;
  631.  
  632.     return unless $prefs->get('all_access_enabled');
  633.  
  634.     my ($id) = $uri =~ m{^googlemusic:track:(.*)$}x;
  635.     my $song;
  636.  
  637.     # Get the Google track info first
  638.     eval {
  639.         $song = $googleapi->get_track_info($id);
  640.     };
  641.     if ($@) {
  642.         $log->error("Not able to get the track info for track ID $id: $@");
  643.         return;
  644.     }
  645.  
  646.     # Now change the rating value
  647.     $song->{rating} = $rating;
  648.  
  649.     # And apply it
  650.     eval {
  651.         $song = $googleapi->change_song_metadata($song);
  652.     };
  653.     if ($@) {
  654.         $log->error("Not able to change the song metadata for track ID $id: $@");
  655.         return;
  656.     }
  657.  
  658.     # TBD: This only updates the track in the cach NOT albums, search
  659.     # results etc.
  660.     # Also need to update our cache. Get it from the cache first.
  661.     my $track = get_track($uri);
  662.     # Change the rating
  663.     $track->{rating} = $rating;
  664.     # And update the cache
  665.     $cache->set($uri, $track, $CACHE_TIME_LONG);
  666.  
  667.     return;
  668. }
  669.  
  670. 1;
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement