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); }
/// <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); } }