Initial commit — AllMusicGuide scraper and music metadata tagger
This commit is contained in:
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class AlbumClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string albumId)
|
||||
{
|
||||
var settings = AlbumScraper.Default;
|
||||
return ReadFromCriteria(settings.AlbumUrl, albumId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class AlbumResultPageClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string criteria)
|
||||
{
|
||||
var settings = AlbumResultPageScraper.Default;
|
||||
return ReadFromCriteria(settings.AlbumResultUrl, criteria);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class ArtistClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string artistId)
|
||||
{
|
||||
var settings = ArtistScaper.Default;
|
||||
return ReadFromCriteria(settings.ArtistUrl, artistId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class ArtistDiscographyAlbumPageClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string artistId)
|
||||
{
|
||||
var settings = ArtistDiscographyPageScraper.Default;
|
||||
return ReadFromCriteria(settings.DiscographyAlbumUrl, artistId);
|
||||
}
|
||||
}
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class ArtistDiscographyCompilationPageClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string artistId)
|
||||
{
|
||||
var settings = ArtistDiscographyPageScraper.Default;
|
||||
return ReadFromCriteria(settings.DiscographyCompilationUrl, artistId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class ArtistDiscographyEpSinglesPageClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string artistId)
|
||||
{
|
||||
var settings = ArtistDiscographyPageScraper.Default;
|
||||
return ReadFromCriteria(settings.DiscographyEpSinglesUrl, artistId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class ArtistResultPageClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string criteria)
|
||||
{
|
||||
var settings = ArtistResultPageScraper.Default;
|
||||
return ReadFromCriteria(settings.ArtistResultUrl, criteria);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using System.Xml;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public interface IXmlClientBase
|
||||
{
|
||||
XmlDocument Read(string criteria);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class SongClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string criteria)
|
||||
{
|
||||
var settings = SongScraper.Default;
|
||||
return ReadFromCriteria(settings.SongUrl, criteria);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
using System.Xml;
|
||||
using MusicMetaTagger.Client.AllMusicGuide.Properties;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public class SongResultPageClient : XmlClientBase
|
||||
{
|
||||
public override XmlDocument Read(string criteria)
|
||||
{
|
||||
var settings = SongResultPageScraper.Default;
|
||||
return ReadFromCriteria(settings.SongResultUrl, criteria);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Xml;
|
||||
using HtmlAgilityPack;
|
||||
using MusicMetaTagger.Core.Model;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public static class WebXmlClient
|
||||
{
|
||||
/// <summary>
|
||||
/// Returns the HTML grabbed from the passed in URL.
|
||||
/// </summary>
|
||||
/// <param name="url"> The URL. </param>
|
||||
/// <returns> A string containing html </returns>
|
||||
public static string GetHtml(Uri url)
|
||||
{
|
||||
var tries = 0;
|
||||
while (true)
|
||||
{
|
||||
try
|
||||
{
|
||||
var client = new WebClient();
|
||||
using (var data = client.OpenRead(url))
|
||||
using (var reader = new StreamReader(data))
|
||||
{
|
||||
return reader.ReadToEnd();
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (tries++ > 3)
|
||||
throw new WebException(ex.Message + " : " + url, ex);
|
||||
Thread.Sleep(100);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void SaveFile(Uri url, string filename)
|
||||
{
|
||||
var client = new WebClient();
|
||||
client.DownloadFile(url, filename);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the HTML parsed into a standard XmlDocument. Uses the HtmlAgilityPack library for "out of the web" (poorly formatted) html file support.
|
||||
/// </summary>
|
||||
/// <param name="html"> The HTML </param>
|
||||
/// <returns> An XmlDocument from the HTML </returns>
|
||||
public static XmlDocument GetHtmlXml(string html)
|
||||
{
|
||||
// Sometimes the biography will have "<..." in it. Annoying.
|
||||
html = html.Replace("<...", "");
|
||||
html = html.Replace("<class=\"subtitle\">", "");
|
||||
html = html.Replace("< ", "");
|
||||
|
||||
var htmlDoc = new HtmlDocument {OptionOutputAsXml = true};
|
||||
htmlDoc.LoadHtml(html);
|
||||
// this element will have unencoded entities in an attribute, messing up the xml
|
||||
var selectSingleNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@class='rovi-share']");
|
||||
if (selectSingleNode != null)
|
||||
selectSingleNode.Remove();
|
||||
selectSingleNode = htmlDoc.DocumentNode.SelectSingleNode("//*[@id='similar-albums']");
|
||||
if (selectSingleNode != null)
|
||||
selectSingleNode.Remove();
|
||||
// fix for ajax results having more than one root node
|
||||
if (htmlDoc.DocumentNode.ChildNodes.All(n => n.Name != "html"))
|
||||
{
|
||||
var el = htmlDoc.CreateElement("html");
|
||||
var nodes = htmlDoc.DocumentNode.ChildNodes;
|
||||
el.AppendChildren(nodes);
|
||||
htmlDoc.DocumentNode.RemoveAllChildren();
|
||||
htmlDoc.DocumentNode.AppendChild(el);
|
||||
}
|
||||
|
||||
var xmlDocument = new XmlDocument();
|
||||
using (Stream stream = new MemoryStream())
|
||||
using (var xmlTextWriter = new XmlTextWriter(stream, Encoding.UTF8))
|
||||
{
|
||||
htmlDoc.Save(xmlTextWriter);
|
||||
|
||||
stream.Seek(0, SeekOrigin.Begin);
|
||||
|
||||
using (var streamReader = new StreamReader(stream))
|
||||
xmlDocument.Load(streamReader);
|
||||
}
|
||||
return xmlDocument;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the HTML grabbed from the passed in URL parsed into a standard XmlDocument. Uses the HtmlAgilityPack library for "out of the web" html file support.
|
||||
/// </summary>
|
||||
/// <param name="url"> The URL. </param>
|
||||
/// <returns> An XmlDocument from the HTML </returns>
|
||||
public static XmlDocument GetHtmlXml(Uri url)
|
||||
{
|
||||
return GetHtmlXml(GetHtml(url));
|
||||
}
|
||||
|
||||
// TODO: Refactor!
|
||||
/// <summary>
|
||||
/// Downloads the cover art to a local file. If the album doesn't have cover art, this will return null.
|
||||
/// </summary>
|
||||
/// <exception cref="HttpException">Thrown on HTTP error.</exception>
|
||||
/// <returns> </returns>
|
||||
public static FileInfo DownloadCoverArt(Album album)
|
||||
{
|
||||
if (String.IsNullOrEmpty(album.CoverUrl))
|
||||
return null;
|
||||
if (album.CoverUrl.Contains("no_cover"))
|
||||
return null;
|
||||
|
||||
var directoryInfo = new DirectoryInfo(".\\ImageCache");
|
||||
if (!Directory.Exists(directoryInfo.FullName))
|
||||
directoryInfo.Create();
|
||||
|
||||
var fileInfo = new FileInfo(directoryInfo.FullName + "\\" + album.AlbumId + ".jpg");
|
||||
|
||||
if (!fileInfo.Exists)
|
||||
SaveFile(new Uri(album.CoverUrl), fileInfo.FullName);
|
||||
|
||||
return fileInfo;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
using System;
|
||||
using System.Xml;
|
||||
|
||||
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client
|
||||
{
|
||||
public abstract class XmlClientBase : IXmlClientBase
|
||||
{
|
||||
public abstract XmlDocument Read(string criteria);
|
||||
|
||||
protected static XmlDocument ReadFromCriteria(string urlFormat, string criteria)
|
||||
{
|
||||
|
||||
if (String.IsNullOrEmpty(criteria))
|
||||
return new XmlDocument();
|
||||
//throw new ArgumentNullException("criteria");
|
||||
|
||||
criteria = Uri.EscapeUriString(criteria);
|
||||
var url = String.Format(urlFormat, criteria);
|
||||
return WebXmlClient.GetHtmlXml(new Uri(url));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user