public void RemoveAlbum(IDBClient db, Album toRemove)
        {
            var db4oClient = db as Db4oClient;

            // remove the Album from the database
            if (db4oClient != null) db4oClient.Client.Delete(toRemove);
        }
        public void AddAlbum(IDBClient db, Album toAdd)
        {
            var db4oClient = db as Db4oClient;

            // add the Album to the database
            if (db4oClient != null) db4oClient.Client.Store(toAdd);
        }
Example #3
0
 /// <summary>
 /// Create a new Track record
 /// </summary>
 /// <param name="trackNumber"></param>
 /// <param name="trackLength"></param>
 /// <param name="album"></param>
 /// <param name="artists"></param>
 /// <param name="title"></param>
 /// <param name="fileName"></param>
 public Track(int trackNumber, IList<Artist> artists, Album album, Duration trackLength, string title,
              string fileName)
 {
     TrackNumber = trackNumber;
     Artists = artists;
     Album = album;
     TrackLength = trackLength;
     Title = title;
     FileName = fileName;
 }
        public string RetrieveImage(Album theAlbum, string path)
        {
            // initialise the URL string
            string urlString;

            // generate the hash using the artist and title and the compression
            string coverArtFile =
                GenerateMD5Hash(theAlbum.Artist.Name + "-" + theAlbum.Title + "-" + theAlbum.Compression) + ".jpg";

            // init the full path
            string fullPath = path + "/" + coverArtFile;

            // we'll check first to see if we have the file already
            if (File.Exists(fullPath))
            {
                // return right away without querying Amazon
                return fullPath;
            }

            // retrieve the URL used for the RESTful query
            if (theAlbum.Artist.Name == Artist.VARIOUS_ARTISTS.Name)
            {
                // initialise the search string
                string searchString;

                // corner case discovered from the CARS soundtrack
                if (theAlbum.Genre == "Soundtrack")
                {
                    searchString = theAlbum.Title + " " + theAlbum.Genre;
                }
                else
                {
                    searchString = theAlbum.Title;
                }

                // prepare the string for the search
                searchString = PrepareString(searchString);
                urlString = RetrieveRestURLNoArtist(searchString);
            }
            else
            {
                // prepare the search fields
                string artistName = PrepareString(theAlbum.Artist.Name);
                string title = PrepareString(theAlbum.Title);
                urlString = RetrieveRestURLWithArtistAndTitle(artistName, title);
            }

            XmlDocument amazonResponse = RetrieveDocumentForUrl(urlString);
            if (null != amazonResponse)
            {
                var nsmgr = new XmlNamespaceManager(amazonResponse.NameTable);
                nsmgr.AddNamespace("amazon", "http://webservices.amazon.com/AWSECommerceService/2005-10-05");

                XmlNodeList items = amazonResponse.SelectNodes("/amazon:ItemSearchResponse/amazon:Items/amazon:Item",
                                                               nsmgr);

                // if we didn't get anything, try again via the artist name
                // it's usually that we're being too specific
                if (items != null)
                {
                    if (items.Count == 0 && theAlbum.Artist.Name != Artist.VARIOUS_ARTISTS.Name)
                    {
                        // try a less specific search
                        string searchString = PrepareString(theAlbum.Artist.Name);
                        urlString = RetrieveRestURLWithArtist(searchString);

                        amazonResponse = RetrieveDocumentForUrl(urlString);

                        if (null != amazonResponse)
                            items = amazonResponse.SelectNodes("/amazon:ItemSearchResponse/amazon:Items/amazon:Item",
                                                               nsmgr);
                    }

                    // if we returned some results, lets do some processing
                    if (items != null)
                        if (items.Count > 0)
                        {
                            // check the first album, it's nearly always it
                            XmlNode image = CheckTracks(items[0], theAlbum, nsmgr);

                            // if it wasn't the first, hit up the second
                            if (null == image && items.Count >= 2)
                            {
                                image = CheckTracks(items[1], theAlbum, nsmgr);
                            }

                            // still no love?  hit up the third before we try text matching
                            if (null == image && items.Count >= 3)
                            {
                                image = CheckTracks(items[2], theAlbum, nsmgr);
                            }

                            // check the titles for the best textual match
                            if (null == image)
                            {
                                // take the best textual match
                                XmlNode lowestItem = CheckTitles(items, theAlbum, nsmgr);

                                // if the item met the threshold, this won't be null
                                if (null != lowestItem)
                                {
                                    // retrieve the image
                                    image = lowestItem.SelectSingleNode("amazon:LargeImage/amazon:URL", nsmgr);

                                    // if this is still null, it's because Amazon doesn't have an image for it, even though it has a record
                                    if (null == image)
                                    {
                                        return Album.NO_COVERART;
                                    }
                                }

                                //// then check whether the tracks match
                                //image = CheckTracks(lowestItem, theAlbum, nsmgr);
                            }

                            // check whether we found a match
                            if (null == image)
                            {
                                _log.Info("No matching album for: " + theAlbum.Title + ". Using default");
                                return Album.NO_COVERART;
                            }

                            // get the URL of the image
                            string imageUrl = image.InnerText;

                            // retrieve the image itself as a stream
                            Stream coverArtStream = GetImageFromURL(imageUrl);

                            if (null != coverArtStream)
                            {
                                // create the output stream
                                var outputFile = new FileStream(fullPath, FileMode.Create);

                                int Length = 256;
                                var buffer = new Byte[Length];
                                int bytesRead = coverArtStream.Read(buffer, 0, Length);
                                // write the required bytes
                                while (bytesRead > 0)
                                {
                                    outputFile.Write(buffer, 0, bytesRead);
                                    bytesRead = coverArtStream.Read(buffer, 0, Length);
                                }
                                coverArtStream.Close();
                                outputFile.Close();

                                return fullPath;
                            }

                            // use null to signify to try again next time
                            return null;
                        }
                        else
                        {
                            return null;
                        }
                }
            }
            // failed, we'll try again next time
            return null;
        }
        /// <summary>
        /// Compare the tracks of this item from Amazon with the tracks on Amazon
        /// </summary>
        /// <param name="amazonItem"></param>
        /// <param name="theAlbum"></param>
        /// <param name="nsmgr"></param>
        /// <returns></returns>
        private XmlNode CheckTracks(XmlNode amazonItem, Album theAlbum, XmlNamespaceManager nsmgr)
        {
            XmlNode image = null;

            // extract the tracks out of the XML
            XmlNodeList amazonTracks = amazonItem.SelectNodes("amazon:Tracks/amazon:Disc/amazon:Track", nsmgr);

            // init the error marker
            int incorrectTracks = 0;

            foreach (Track track in theAlbum.Tracks)
            {
                // assume it's not here
                bool found = false;

                string ourTrackTitle = track.Title.ToLower();

                // check against each of the tracks
                if (amazonTracks != null)
                    foreach (XmlNode amazonTrack in amazonTracks)
                    {
                        string trackTitle = amazonTrack.InnerText.ToLower();

                        if (trackTitle.Contains(ourTrackTitle))
                        {
                            // perhaps match track number
                            found = true;
                            break;
                        }
                    }

                // if the track isn't located in the album, increment the incorrect list
                if (!found)
                {
                    incorrectTracks++;
                }
            }

            // if the incorrect tracks is an acceptable level
            if (incorrectTracks <= (theAlbum.Tracks.Count / 4 + 1))
            {
                // retrieve the image
                image = amazonItem.SelectSingleNode("amazon:LargeImage/amazon:URL", nsmgr);
            }

            return image;
        }
        /// <summary>
        /// Compare the titles of the albums returned by Amazon with the current Album
        /// from the DB.  Return the best match if it meets the threshold.
        /// </summary>
        /// <param name="items"></param>
        /// <param name="theAlbum"></param>
        /// <param name="nsmgr"></param>
        /// <returns>Return </returns>
        private XmlNode CheckTitles(XmlNodeList items, Album theAlbum, XmlNamespaceManager nsmgr)
        {
            // start off with a really high number, the only way is down
            int lowestScore = 1000;
            XmlNode lowestItem = null;

            // work through each node
            foreach (XmlNode item in items)
            {
                // extract the title of this item
                XmlNode amazonTitle = item.SelectSingleNode("amazon:ItemAttributes/amazon:Title", nsmgr);

                string amazonTitleString = PrepareString(amazonTitle.InnerText);
                string albumTitle = PrepareString(theAlbum.Title);

                // calculate the distance between this and the actual album
                int distance = LevenshteinDistance.CalculateDistance(amazonTitleString, albumTitle);

                // store the reference if this is the best match
                if (distance < lowestScore)
                {
                    lowestItem = item;
                    lowestScore = distance;
                }
            }

            // send back the best match if it meets the threshold
            if (lowestScore < AmazonConstants.LEVENSHTEIN_THRESHOLD)
            {
                return lowestItem;
            }

            return null;
        }
        public Album GetTracks(CDDBEntry cddbResult)
        {
            // initialise the album to output
            Album newAlbum = null;

            // build up the command string
            var commandBuilder = new StringBuilder();
            commandBuilder.Append("?");
            commandBuilder.Append(FreeDBConstants.COMMAND);
            commandBuilder.Append(FreeDBConstants.COMMAND_READ);
            commandBuilder.Append("+");
            commandBuilder.Append(cddbResult.Genre);
            commandBuilder.Append("+");
            commandBuilder.Append(cddbResult.DiscID);
            commandBuilder.Append("&");
            commandBuilder.Append(GenerateHello());
            commandBuilder.Append("&");
            commandBuilder.Append(GenerateProtocol());

            // make the URL to use
            var urlBuilder = new StringBuilder();
            urlBuilder.Append(FreeDBConstants.URL_FREEDB_MAIN);
            urlBuilder.Append(FreeDBConstants.URL_HTTP_ACCESS);

            // hit up FreeDB for the result
            IList<string> freeDBResponse = Query(urlBuilder.ToString(), commandBuilder.ToString());

            // parse the query
            if (freeDBResponse.Count > 0)
            {
                // extract the response code
                ResponseCode responseCode = ExtractResponseCode(freeDBResponse[0]);

                switch (responseCode)
                {
                    case ResponseCode.RESPONSE_CODE_DEFAULT:
                        _log.Error("Problem querying album using command: " + commandBuilder);
                        break;
                    case ResponseCode.RESPONSE_CODE_200:
                    case ResponseCode.RESPONSE_CODE_210:
                        _log.Debug("Found track/s");

                        // get a hook to the DB
                        IDBClient db = Database.RetrieveClient();

                        // we'll look for an artist to attach this all to
                        Artist theArtist = Database.RetrieveArtistByName(db, cddbResult.ArtistName);

                        // TODO Check for artists, as opposed to just one

                        // if we don't we'll create one
                        if (null == theArtist)
                        {
                            theArtist = new Artist(cddbResult.ArtistName);
                        }

                        db.Close();

                        // we'll create an album for this with invalid data, we'll fill that out after
                        newAlbum = new Album(cddbResult.Title, theArtist, null, 1900, Compression.Undecided);

                        // a list to keep track of the track lengths in seconds
                        IList<int> trackLengths = new List<int>();

                        // keep track of the track offsets for track lengths
                        int lastTrackOffset = 0;

                        // break up each of the results
                        foreach (string responseLine in freeDBResponse)
                        {
                            // extract the track lengths
                            if (responseLine.Contains("#\t"))
                            {
                                // parse the next offset
                                int newOffset = Int32.Parse(responseLine.Substring(2));

                                // stop the first one being processed
                                if (lastTrackOffset > 0)
                                {
                                    int seconds = (newOffset - lastTrackOffset)/75;
                                    trackLengths.Add(seconds);
                                }

                                // store the new offset
                                lastTrackOffset = newOffset;
                            }

                            // extract the total number of seconds so we can calculate the last tracks length
                            if (responseLine.Contains("# Disc length"))
                            {
                                // parse the total length of the album in seconds
                                int totalLength = Int32.Parse(responseLine.Substring(15, 4));

                                int secondsToDate = lastTrackOffset/75;

                                // extract the length of the last track from this to find out the length of the final track
                                int lastTrackLength = totalLength - secondsToDate;

                                // add this to the lengths
                                trackLengths.Add(lastTrackLength);
                            }

                            // extract the year
                            if (responseLine.Contains("DYEAR"))
                            {
                                string year = responseLine.Substring(6);
                                newAlbum.ReleaseYear = Int32.Parse(year);
                            }

                            // extract a track
                            if (responseLine.Contains("TTITLE"))
                            {
                                // marker for where to parse string to
                                int indexOfSpace = responseLine.IndexOf('=', 6);
                                string numberString = responseLine.Substring(6, (indexOfSpace - 6));

                                int trackNumber = Int32.Parse(numberString) + 1;

                                string trackTitle = responseLine.Substring(indexOfSpace + 1);
                                // create this with no path as it's not currently set
                                var artists = new List<Artist>();
                                artists.Add(theArtist);
                                var newTrack = new Track(trackNumber, artists, newAlbum,
                                                         new Duration(new TimeSpan(0, 0, trackLengths[trackNumber - 1])),
                                                         trackTitle, null);
                                newAlbum.AddTrack(newTrack);
                            }
                        }
                        break;
                    default:
                        _log.Error("Response came back we weren't expecting, handle it");
                        break;
                }
            }

            // pass back the resulting album
            return newAlbum;
        }
        private void ProcessTrack(IDBClient db, string file)
        {
            // open the file using taglib
            try
            {
                // work out the compression type from the extension
                var compression = Compression.Lossy;
                if (AudioConstants.SUPPORTED_LOSSLESS.Contains(Path.GetExtension(file)))
                    compression = Compression.Lossless;

                File musicFile = File.Create(file);

                // check whether or not the tag information is valid
                if (null == musicFile.Tag.Album || null == musicFile.Tag.Title)
                {
                    _log.Error("Invalid tag information for: " + file);
                }
                else
                {
                    // retrieve the album artist first
                    if (null == _albumArtist)
                    {
                        // find the artist that should be for this album
                        _albumArtist = Database.RetrieveArtistByName(db, musicFile.Tag.AlbumArtists[0]);

                        // check if we have an existing album artist
                        if (null == _albumArtist)
                        {
                            // if not, create one
                            _albumArtist = new Artist(musicFile.Tag.AlbumArtists[0]);
                        }
                    }

                    // have an album to work with
                    if (null == _theAlbum)
                    {
                        // we'll attempt to find an album to add it to
                        _theAlbum = Database.RetrieveAlbumByName(db, musicFile.Tag.Album, compression);

                        // if there isn't an existing album
                        if (null == _theAlbum)
                        {
                            // create a new album
                            _theAlbum = new Album(musicFile.Tag.Album, _albumArtist, musicFile.Tag.FirstGenre,
                                                  (int) musicFile.Tag.Year, compression);
                        }
                    }
                    else
                    {
                        // make sure we have the right album
                        if (_theAlbum.Title != musicFile.Tag.Album)
                        {
                            // we'll attempt to find an album to add it to or create a new one
                            _theAlbum = Database.RetrieveAlbumByName(db, musicFile.Tag.Album, compression) ??
                                        new Album(musicFile.Tag.Album, _albumArtist, musicFile.Tag.FirstGenre,
                                            (int) musicFile.Tag.Year, compression);

                            // if there isn't an existing album
                        }
                    }

                    // initialise the output track
                    Track theTrack;
                    var trackArtists = new List<Artist>();

                    // special case for tracks that have more than one artist
                    if (musicFile.Tag.Performers.Count() > 1)
                    {
                        foreach (var artist in musicFile.Tag.Performers)
                        {
                            // we'll try with the album artist first
                            var performer = _albumArtist;
                            if (artist != _albumArtist.Name)
                                performer = Database.RetrieveArtistByName(db, artist) ?? new Artist(artist);

                            trackArtists.Add(performer);
                        }
                    }
                    else
                    {
                        // we'll try with the album artist first
                        if (musicFile.Tag.FirstPerformer == _albumArtist.Name)
                        {
                            trackArtists.Add(_albumArtist);
                        }
                        else
                        {
                            var performer = Database.RetrieveArtistByName(db, musicFile.Tag.FirstPerformer) ??
                                            new Artist(musicFile.Tag.FirstPerformer);
                            trackArtists.Add(performer);
                        }

                        // check for a track in the local object instead of hitting the DB
                        try
                        {
                            // TODO not sure if this will work with the multiple artists now
                            _theAlbum.Tracks.First(
                                x => (x.TrackNumber == (int) musicFile.Tag.Track && x.Title == musicFile.Tag.Title));
                        }
                        catch (InvalidOperationException) {}
                    }

                    // update the running tally
                    foreach (var artist in trackArtists)
                    {
                        int result = 0;
                        if (_tally.ContainsKey(artist))
                            result = _tally[artist];

                        if (0 == result)
                            _tally.Add(artist, 1);
                        else
                            _tally[artist] = ++result;

                    }

                    // create a new track
                    theTrack = new Track((int) musicFile.Tag.Track, trackArtists, _theAlbum,
                                         musicFile.Properties.Duration, musicFile.Tag.Title, file);

                    // add the new track to the album
                    _theAlbum.AddTrack(theTrack);

                    // update the reference in the DB
                    Database.UpdateAddTrack(db, theTrack);
                }
            }
            catch (ArgumentOutOfRangeException e)
            {
                _log.Error("Taglib had problem reading: " + file, e);
                return;
            }
            catch (Db4oException e)
            {
                _log.Error("DB problem when processing track: " + file, e);
            }
            catch (IOException e)
            {
                _log.Error("File IO problem when processing track: " + file, e);
            }
        }