Initial commit — AllMusicGuide scraper and music metadata tagger

This commit is contained in:
2026-05-10 02:49:16 +00:00
commit 4a541ca04b
194 changed files with 46364 additions and 0 deletions
@@ -0,0 +1,235 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>8.0.30703</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{A86C05AB-9720-4D6E-AC5E-9CDE6364945A}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>MusicMetaTagger.Client.AllMusicGuide</RootNamespace>
<AssemblyName>MusicMetaTagger.Client.AllMusicGuide</AssemblyName>
<TargetFrameworkVersion>v4.0</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="HtmlAgilityPack">
<HintPath>..\packages\HtmlAgilityPack.1.4.6\lib\Net40\HtmlAgilityPack.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.ComponentModel.DataAnnotations" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Services\MusicGuideCache.cs" />
<Compile Include="Services\Searchers\AlbumArtistSearcher.cs" />
<Compile Include="Services\Searchers\AlbumSearcher.cs" />
<Compile Include="Services\Searchers\SongArtistSearcher.cs" />
<Compile Include="Services\TrackOriginalRelease.cs" />
<Compile Include="Services\TrackRater.cs" />
<Compile Include="Services\TrackSearcher.cs" />
<Compile Include="RemoteDataAccess\Client\AlbumClient.cs" />
<Compile Include="RemoteDataAccess\Client\AlbumResultPageClient.cs" />
<Compile Include="RemoteDataAccess\Client\ArtistClient.cs" />
<Compile Include="RemoteDataAccess\Client\ArtistDiscographyAlbumPageClient.cs" />
<Compile Include="RemoteDataAccess\Client\ArtistDiscographyCompilationPageClient.cs" />
<Compile Include="RemoteDataAccess\Client\ArtistDiscographyEpSinglesPageClient.cs" />
<Compile Include="RemoteDataAccess\Client\ArtistResultPageClient.cs" />
<Compile Include="RemoteDataAccess\Client\IXmlClientBase.cs" />
<Compile Include="RemoteDataAccess\Client\SongClient.cs" />
<Compile Include="RemoteDataAccess\Client\SongResultPageClient.cs" />
<Compile Include="RemoteDataAccess\Client\WebXmlClient.cs" />
<Compile Include="RemoteDataAccess\Client\XmlClientBase.cs" />
<Compile Include="RemoteDataAccess\MusicGuideScraper.cs" />
<Compile Include="RemoteDataAccess\Parser\AlbumParser.cs" />
<Compile Include="RemoteDataAccess\Parser\AlbumResultPageParser.cs" />
<Compile Include="RemoteDataAccess\Parser\AlbumResultParser.cs" />
<Compile Include="RemoteDataAccess\Parser\ArtistDiscographyPageParser.cs" />
<Compile Include="RemoteDataAccess\Parser\ArtistDiscographyParser.cs" />
<Compile Include="RemoteDataAccess\Parser\ArtistParser.cs" />
<Compile Include="RemoteDataAccess\Parser\ArtistResultPageParser.cs" />
<Compile Include="RemoteDataAccess\Parser\ArtistResultParser.cs" />
<Compile Include="RemoteDataAccess\Parser\IParser.cs" />
<Compile Include="RemoteDataAccess\Parser\ParserBase.cs" />
<Compile Include="RemoteDataAccess\Parser\SongParser.cs" />
<Compile Include="RemoteDataAccess\Parser\SongResultPageParser.cs" />
<Compile Include="RemoteDataAccess\Parser\SongResultParser.cs" />
<Compile Include="RemoteDataAccess\Parser\TrackParser.cs" />
<Compile Include="RemoteDataAccess\Scraper\AlbumResultPageScraper.cs" />
<Compile Include="RemoteDataAccess\Scraper\AlbumScraper.cs" />
<Compile Include="RemoteDataAccess\Scraper\ArtistDiscographyScraper.cs" />
<Compile Include="RemoteDataAccess\Scraper\ArtistResultPageScraper.cs" />
<Compile Include="RemoteDataAccess\Scraper\ArtistScraper.cs" />
<Compile Include="RemoteDataAccess\Scraper\IScraper.cs" />
<Compile Include="RemoteDataAccess\Scraper\ScraperBase.cs" />
<Compile Include="RemoteDataAccess\Scraper\SongResultPageScraper.cs" />
<Compile Include="RemoteDataAccess\Scraper\SongScraper.cs" />
<Compile Include="Properties\AlbumResultPageScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>AlbumResultPageScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\AlbumResultScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>AlbumResultScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\AlbumScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>AlbumScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\AllMusicGuideSettings.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>AllMusicGuideSettings.settings</DependentUpon>
</Compile>
<Compile Include="Properties\ArtistDiscographyPageScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>ArtistDiscographyPageScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\ArtistDiscographyScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>ArtistDiscographyScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\ArtistResultPageScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>ArtistResultPageScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\ArtistResultScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>ArtistResultScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\ArtistScaper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>ArtistScaper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="Properties\SongAppearsOnAlbumScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>SongAppearsOnAlbumScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\SongResultPageScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>SongResultPageScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\SongResultScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>SongResultScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\SongScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>SongScraper.settings</DependentUpon>
</Compile>
<Compile Include="Properties\TrackScraper.Designer.cs">
<AutoGen>True</AutoGen>
<DesignTimeSharedInput>True</DesignTimeSharedInput>
<DependentUpon>TrackScraper.settings</DependentUpon>
</Compile>
</ItemGroup>
<ItemGroup>
<None Include="app.config">
<SubType>Designer</SubType>
</None>
<None Include="packages.config" />
<None Include="Properties\AlbumResultPageScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>AlbumResultPageScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\AlbumResultScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>AlbumResultScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\AlbumScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>AlbumScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\AllMusicGuideSettings.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>AllMusicGuideSettings.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\ArtistDiscographyPageScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>ArtistDiscographyPageScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\ArtistDiscographyScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>ArtistDiscographyScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\ArtistResultPageScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>ArtistResultPageScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\ArtistResultScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>ArtistResultScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\ArtistScaper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>ArtistScaper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\SongAppearsOnAlbumScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>SongAppearsOnAlbumScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\SongResultPageScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>SongResultPageScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\SongResultScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>SongResultScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\SongScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>SongScraper.Designer.cs</LastGenOutput>
</None>
<None Include="Properties\TrackScraper.settings">
<Generator>SettingsSingleFileGenerator</Generator>
<LastGenOutput>TrackScraper.Designer.cs</LastGenOutput>
</None>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Core\Core.csproj">
<Project>{EDA98A07-0BF5-4FEE-B341-D8A6A7E581F5}</Project>
<Name>Core</Name>
</ProjectReference>
</ItemGroup>
<ItemGroup />
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>
@@ -0,0 +1,44 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class AlbumResultPageScraper : global::System.Configuration.ApplicationSettingsBase {
private static AlbumResultPageScraper defaultInstance = ((AlbumResultPageScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new AlbumResultPageScraper())));
public static AlbumResultPageScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'right-of-image\')]")]
public string AlbumXpath {
get {
return ((string)(this["AlbumXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/search/ajax_results/albums/{0}")]
public string AlbumResultUrl {
get {
return ((string)(this["AlbumResultUrl"]));
}
}
}
}
@@ -0,0 +1,12 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MusicMetaTagger.Client.AllMusicGuide.Properties" GeneratedClassName="AlbumResultPageScraper">
<Profiles />
<Settings>
<Setting Name="AlbumXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'right-of-image')]</Value>
</Setting>
<Setting Name="AlbumResultUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/search/ajax_results/albums/{0}</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,116 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class AlbumResultScraper : global::System.Configuration.ApplicationSettingsBase {
private static AlbumResultScraper defaultInstance = ((AlbumResultScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new AlbumResultScraper())));
public static AlbumResultScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'title\')]/a/@href")]
public string AlbumUrlXpath {
get {
return ((string)(this["AlbumUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string AlbumUrlIdRegex {
get {
return ((string)(this["AlbumUrlIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'artist\')]/a")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'title\')]/a")]
public string TitleXpath {
get {
return ((string)(this["TitleXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("(19|20)\\d{2}")]
public string YearRegex {
get {
return ((string)(this["YearRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'artist\')]/a/@href")]
public string ArtistUrlXpath {
get {
return ((string)(this["ArtistUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string ArtistUrIdRegex {
get {
return ((string)(this["ArtistUrIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'artist\')]")]
public string ArtistNoUrlXpath {
get {
return ((string)(this["ArtistNoUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'info\']")]
public string InfoXpath {
get {
return ((string)(this["InfoXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("(?<=\\n).*")]
public string GenreRegex {
get {
return ((string)(this["GenreRegex"]));
}
}
}
}
@@ -0,0 +1,36 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AllMusicGuide.Client.Properties" GeneratedClassName="AlbumResultScraper">
<Profiles />
<Settings>
<Setting Name="AlbumUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'title')]/a/@href</Value>
</Setting>
<Setting Name="AlbumUrlIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'artist')]/a</Value>
</Setting>
<Setting Name="TitleXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'title')]/a</Value>
</Setting>
<Setting Name="YearRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">(19|20)\d{2}</Value>
</Setting>
<Setting Name="ArtistUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'artist')]/a/@href</Value>
</Setting>
<Setting Name="ArtistUrIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="ArtistNoUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'artist')]</Value>
</Setting>
<Setting Name="InfoXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='info']</Value>
</Setting>
<Setting Name="GenreRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">(?&lt;=\n).*</Value>
</Setting>
</Settings>
</SettingsFile>
+207
View File
@@ -0,0 +1,207 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class AlbumScraper : global::System.Configuration.ApplicationSettingsBase {
private static AlbumScraper defaultInstance = ((AlbumScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new AlbumScraper())));
public static AlbumScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'album-title\']/text()")]
public string TitleXpath {
get {
return ((string)(this["TitleXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/album/{0}")]
public string AlbumUrl {
get {
return ((string)(this["AlbumUrl"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@id=\'tracks\']//tbody/tr[td[@class=\'tracknum\']]")]
public string TrackXpath {
get {
return ((string)(this["TrackXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'image-container\']/img/@src")]
public string CoverUrlXpath {
get {
return ((string)(this["CoverUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'editorial-rating\']//span[@itemprop=\'rating\']")]
public string RatingXpath {
get {
return ((string)(this["RatingXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'release-date\']")]
public string ReleaseDateXpath {
get {
return ((string)(this["ReleaseDateXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'album-artist\']/a")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'album-artist\']/a/@href")]
public string ArtistUrlXpath {
get {
return ((string)(this["ArtistUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'themes\')]//a")]
public string ThemesXpath {
get {
return ((string)(this["ThemesXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'moods\')]//a")]
public string MoodsXpath {
get {
return ((string)(this["MoodsXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'review-body\']/div")]
public string ReviewXpath {
get {
return ((string)(this["ReviewXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@id=\'review\']//*[@class=\'author\']")]
public string ReviewerXpath {
get {
return ((string)(this["ReviewerXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("(?<=by\\s*).*")]
public string ReviewerRegex {
get {
return ((string)(this["ReviewerRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'id-details\']//*[contains(.,\'Rovi Music ID\')]/following-sibling::*[1]/" +
"span")]
public string AlbumIdXpath {
get {
return ((string)(this["AlbumIdXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'styles\')]//a[contains(.,\'Soundtrack\')]")]
public string SoundtrackXpath {
get {
return ((string)(this["SoundtrackXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("\\d{4}")]
public string ReleaseYearRegex {
get {
return ((string)(this["ReleaseYearRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'styles\')]//a")]
public string StylesXpath {
get {
return ((string)(this["StylesXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string ArtistUrlIdRegex {
get {
return ((string)(this["ArtistUrlIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute(".*(?=\\?.*)")]
public string CoverUrlRegex {
get {
return ((string)(this["CoverUrlRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'album-pick\')]")]
public string PickXpath {
get {
return ((string)(this["PickXpath"]));
}
}
}
}
@@ -0,0 +1,66 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AllMusicGuide.Client.Properties" GeneratedClassName="AlbumScraper">
<Profiles />
<Settings>
<Setting Name="TitleXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='album-title']/text()</Value>
</Setting>
<Setting Name="AlbumUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/album/{0}</Value>
</Setting>
<Setting Name="TrackXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@id='tracks']//tbody/tr[td[@class='tracknum']]</Value>
</Setting>
<Setting Name="CoverUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='image-container']/img/@src</Value>
</Setting>
<Setting Name="RatingXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='editorial-rating']//span[@itemprop='rating']</Value>
</Setting>
<Setting Name="ReleaseDateXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='release-date']</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='album-artist']/a</Value>
</Setting>
<Setting Name="ArtistUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='album-artist']/a/@href</Value>
</Setting>
<Setting Name="ThemesXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'themes')]//a</Value>
</Setting>
<Setting Name="MoodsXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'moods')]//a</Value>
</Setting>
<Setting Name="ReviewXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='review-body']/div</Value>
</Setting>
<Setting Name="ReviewerXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@id='review']//*[@class='author']</Value>
</Setting>
<Setting Name="ReviewerRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">(?&lt;=by\s*).*</Value>
</Setting>
<Setting Name="AlbumIdXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='id-details']//*[contains(.,'Rovi Music ID')]/following-sibling::*[1]/span</Value>
</Setting>
<Setting Name="SoundtrackXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'styles')]//a[contains(.,'Soundtrack')]</Value>
</Setting>
<Setting Name="ReleaseYearRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">\d{4}</Value>
</Setting>
<Setting Name="StylesXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'styles')]//a</Value>
</Setting>
<Setting Name="ArtistUrlIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="CoverUrlRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">.*(?=\?.*)</Value>
</Setting>
<Setting Name="PickXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'album-pick')]</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,50 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class AllMusicGuideSettings : global::System.Configuration.ApplicationSettingsBase {
private static AllMusicGuideSettings defaultInstance = ((AllMusicGuideSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new AllMusicGuideSettings())));
public static AllMusicGuideSettings Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0.6")]
public double AlbumResultsRelevanceThreshold {
get {
return ((double)(this["AlbumResultsRelevanceThreshold"]));
}
set {
this["AlbumResultsRelevanceThreshold"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("0.6")]
public double ArtistResultsRelevanceThreshold {
get {
return ((double)(this["ArtistResultsRelevanceThreshold"]));
}
set {
this["ArtistResultsRelevanceThreshold"] = value;
}
}
}
}
@@ -0,0 +1,12 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="ItunesAMG.AllMusicGuide" GeneratedClassName="AllMusicGuideSettings">
<Profiles />
<Settings>
<Setting Name="AlbumResultsRelevanceThreshold" Type="System.Double" Scope="User">
<Value Profile="(Default)">0.6</Value>
</Setting>
<Setting Name="ArtistResultsRelevanceThreshold" Type="System.Double" Scope="User">
<Value Profile="(Default)">0.6</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,99 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class ArtistDiscographyPageScraper : global::System.Configuration.ApplicationSettingsBase {
private static ArtistDiscographyPageScraper defaultInstance = ((ArtistDiscographyPageScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new ArtistDiscographyPageScraper())));
public static ArtistDiscographyPageScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//table[contains(@class,\'album-table\')]/tbody/tr")]
public string DiscographyXpath {
get {
return ((string)(this["DiscographyXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/artist/{0}/overview/main")]
public string DiscographyAlbumUrl {
get {
return ((string)(this["DiscographyAlbumUrl"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/artist/{0}/overview/singles")]
public string DiscographyEpSinglesUrl {
get {
return ((string)(this["DiscographyEpSinglesUrl"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/artist/{0}/overview/compilations")]
public string DiscographyCompilationUrl {
get {
return ((string)(this["DiscographyCompilationUrl"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@id=\"discography\"]//ul[@class=\'sub-tabs\']/li[contains(@class,\'active\')]/@clas" +
"s")]
public string ReleaseTypeXpath {
get {
return ((string)(this["ReleaseTypeXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute(".*(?=\\sactive)")]
public string ReleaseTypeRegex {
get {
return ((string)(this["ReleaseTypeRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'artist-name\']/text()")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'id-details\']//*[contains(.,\'Rovi Music ID\')]/following-sibling::*[1]")]
public string ArtistIdXpath {
get {
return ((string)(this["ArtistIdXpath"]));
}
}
}
}
@@ -0,0 +1,30 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MusicMetaTagger.Client.AllMusicGuide.Properties" GeneratedClassName="ArtistDiscographyPageScraper">
<Profiles />
<Settings>
<Setting Name="DiscographyXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//table[contains(@class,'album-table')]/tbody/tr</Value>
</Setting>
<Setting Name="DiscographyAlbumUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/artist/{0}/overview/main</Value>
</Setting>
<Setting Name="DiscographyEpSinglesUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/artist/{0}/overview/singles</Value>
</Setting>
<Setting Name="DiscographyCompilationUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/artist/{0}/overview/compilations</Value>
</Setting>
<Setting Name="ReleaseTypeXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@id="discography"]//ul[@class='sub-tabs']/li[contains(@class,'active')]/@class</Value>
</Setting>
<Setting Name="ReleaseTypeRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">.*(?=\sactive)</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='artist-name']/text()</Value>
</Setting>
<Setting Name="ArtistIdXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='id-details']//*[contains(.,'Rovi Music ID')]/following-sibling::*[1]</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,107 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class ArtistDiscographyScraper : global::System.Configuration.ApplicationSettingsBase {
private static ArtistDiscographyScraper defaultInstance = ((ArtistDiscographyScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new ArtistDiscographyScraper())));
public static ArtistDiscographyScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//td[contains(@class,\'title\')]/a[contains(@class,\'full-title\')]/@href")]
public string AlbumUrlXpath {
get {
return ((string)(this["AlbumUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string AlbumUrlIdRegex {
get {
return ((string)(this["AlbumUrlIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//td[@class=\'ed-rating\']/@data-sort-value")]
public string RatingXpath {
get {
return ((string)(this["RatingXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//td[contains(@class,\'year\')][contains(@class,\'pick\')]")]
public string PickXpath {
get {
return ((string)(this["PickXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//td[contains(@class,\'year\')]")]
public string YearXpath {
get {
return ((string)(this["YearXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//td[contains(@class,\'title\')]/a[contains(@class,\'full-title\')]")]
public string TitleXpath {
get {
return ((string)(this["TitleXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//td[@class=\'label\']/*[@class=\'full-title\']")]
public string LabelXpath {
get {
return ((string)(this["LabelXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("(?<=\\s*\\[).*(?=\\]$)")]
public string ReleaseTypeRegex {
get {
return ((string)(this["ReleaseTypeRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("\\d{1,2}")]
public string RatingRegex {
get {
return ((string)(this["RatingRegex"]));
}
}
}
}
@@ -0,0 +1,33 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AllMusicGuide.Client.Properties" GeneratedClassName="ArtistDiscographyScraper">
<Profiles />
<Settings>
<Setting Name="AlbumUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//td[contains(@class,'title')]/a[contains(@class,'full-title')]/@href</Value>
</Setting>
<Setting Name="AlbumUrlIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="RatingXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//td[@class='ed-rating']/@data-sort-value</Value>
</Setting>
<Setting Name="PickXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//td[contains(@class,'year')][contains(@class,'pick')]</Value>
</Setting>
<Setting Name="YearXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//td[contains(@class,'year')]</Value>
</Setting>
<Setting Name="TitleXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//td[contains(@class,'title')]/a[contains(@class,'full-title')]</Value>
</Setting>
<Setting Name="LabelXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//td[@class='label']/*[@class='full-title']</Value>
</Setting>
<Setting Name="ReleaseTypeRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">(?&lt;=\s*\[).*(?=\]$)</Value>
</Setting>
<Setting Name="RatingRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">\d{1,2}</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,44 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class ArtistResultPageScraper : global::System.Configuration.ApplicationSettingsBase {
private static ArtistResultPageScraper defaultInstance = ((ArtistResultPageScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new ArtistResultPageScraper())));
public static ArtistResultPageScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/search/ajax_results/artists/{0}")]
public string ArtistResultUrl {
get {
return ((string)(this["ArtistResultUrl"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'right-of-image\')]")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
}
}
@@ -0,0 +1,12 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MusicMetaTagger.Client.AllMusicGuide.Properties" GeneratedClassName="ArtistResultPageScraper">
<Profiles />
<Settings>
<Setting Name="ArtistResultUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/search/ajax_results/artists/{0}</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'right-of-image')]</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class ArtistResultScraper : global::System.Configuration.ApplicationSettingsBase {
private static ArtistResultScraper defaultInstance = ((ArtistResultScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new ArtistResultScraper())));
public static ArtistResultScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'name\')]/a/@href")]
public string ArtistUrlXpath {
get {
return ((string)(this["ArtistUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string ArtistUrIdRegex {
get {
return ((string)(this["ArtistUrIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'name\')]/a")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'info\']")]
public string InfoXpath {
get {
return ((string)(this["InfoXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute(".*(?=\\n)*")]
public string GenreRegex {
get {
return ((string)(this["GenreRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("(?<=\\n).*(19|20)\\d{2}.*")]
public string YearsActiveRegex {
get {
return ((string)(this["YearsActiveRegex"]));
}
}
}
}
@@ -0,0 +1,24 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AllMusicGuide.Client.Properties" GeneratedClassName="ArtistResultScraper">
<Profiles />
<Settings>
<Setting Name="ArtistUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'name')]/a/@href</Value>
</Setting>
<Setting Name="ArtistUrIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'name')]/a</Value>
</Setting>
<Setting Name="InfoXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='info']</Value>
</Setting>
<Setting Name="GenreRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">.*(?=\n)*</Value>
</Setting>
<Setting Name="YearsActiveRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">(?&lt;=\n).*(19|20)\d{2}.*</Value>
</Setting>
</Settings>
</SettingsFile>
+80
View File
@@ -0,0 +1,80 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class ArtistScaper : global::System.Configuration.ApplicationSettingsBase {
private static ArtistScaper defaultInstance = ((ArtistScaper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new ArtistScaper())));
public static ArtistScaper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/artist/{0}")]
public string ArtistUrl {
get {
return ((string)(this["ArtistUrl"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/artist/{0}/overview/main")]
public string DiscographyAlbumUrl {
get {
return ((string)(this["DiscographyAlbumUrl"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'id-details\']//*[contains(.,\'Rovi Music ID\')]/following-sibling::*[1]")]
public string ArtistIdXpath {
get {
return ((string)(this["ArtistIdXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'genres\']//a")]
public string GenreXpath {
get {
return ((string)(this["GenreXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'styles\']//a")]
public string StylesXpath {
get {
return ((string)(this["StylesXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'artist-name\']/text()")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
}
}
@@ -0,0 +1,24 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MusicMetaTagger.Client.AllMusicGuide.Properties" GeneratedClassName="ArtistScaper">
<Profiles />
<Settings>
<Setting Name="ArtistUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/artist/{0}</Value>
</Setting>
<Setting Name="DiscographyAlbumUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/artist/{0}/overview/main</Value>
</Setting>
<Setting Name="ArtistIdXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='id-details']//*[contains(.,'Rovi Music ID')]/following-sibling::*[1]</Value>
</Setting>
<Setting Name="GenreXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='genres']//a</Value>
</Setting>
<Setting Name="StylesXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='styles']//a</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='artist-name']/text()</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("MusicMetaTagger.Client.AllMusicGuide")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("MusicMetaTagger.Client.AllMusicGuide")]
[assembly: AssemblyCopyright("Copyright © 2012")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("26de737f-4e88-484b-95ab-f4edec53c9ee")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]
@@ -0,0 +1,107 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class SongAppearsOnAlbumScraper : global::System.Configuration.ApplicationSettingsBase {
private static SongAppearsOnAlbumScraper defaultInstance = ((SongAppearsOnAlbumScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new SongAppearsOnAlbumScraper())));
public static SongAppearsOnAlbumScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'title\')]/a/@href")]
public string AlbumUrlXpath {
get {
return ((string)(this["AlbumUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string AlbumUrlIdRegex {
get {
return ((string)(this["AlbumUrlIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'artist\')]/a")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'title\')]/a")]
public string TitleXpath {
get {
return ((string)(this["TitleXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("(19|20)\\d{2}")]
public string YearRegex {
get {
return ((string)(this["YearRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'artist\')]/a/@href")]
public string ArtistUrlXpath {
get {
return ((string)(this["ArtistUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string ArtistUrIdRegex {
get {
return ((string)(this["ArtistUrIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'artist\')]")]
public string ArtistNoUrlXpath {
get {
return ((string)(this["ArtistNoUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'year\']")]
public string YearXpath {
get {
return ((string)(this["YearXpath"]));
}
}
}
}
@@ -0,0 +1,33 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AllMusicGuide.Client.Properties" GeneratedClassName="SongAppearsOnAlbumScraper">
<Profiles />
<Settings>
<Setting Name="AlbumUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'title')]/a/@href</Value>
</Setting>
<Setting Name="AlbumUrlIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'artist')]/a</Value>
</Setting>
<Setting Name="TitleXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'title')]/a</Value>
</Setting>
<Setting Name="YearRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">(19|20)\d{2}</Value>
</Setting>
<Setting Name="ArtistUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'artist')]/a/@href</Value>
</Setting>
<Setting Name="ArtistUrIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="ArtistNoUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'artist')]</Value>
</Setting>
<Setting Name="YearXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='year']</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,47 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class SongResultPageScraper : global::System.Configuration.ApplicationSettingsBase {
private static SongResultPageScraper defaultInstance = ((SongResultPageScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new SongResultPageScraper())));
public static SongResultPageScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/search/ajax_results/songs/{0}")]
public string SongResultUrl {
get {
return ((string)(this["SongResultUrl"]));
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'right-of-image\')]")]
public string SongXpath {
get {
return ((string)(this["SongXpath"]));
}
set {
this["SongXpath"] = value;
}
}
}
}
@@ -0,0 +1,12 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MusicMetaTagger.Client.AllMusicGuide.Properties" GeneratedClassName="SongResultPageScraper">
<Profiles />
<Settings>
<Setting Name="SongResultUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/search/ajax_results/songs/{0}</Value>
</Setting>
<Setting Name="SongXpath" Type="System.String" Scope="User">
<Value Profile="(Default)">//*[contains(@class,'right-of-image')]</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -0,0 +1,89 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class SongResultScraper : global::System.Configuration.ApplicationSettingsBase {
private static SongResultScraper defaultInstance = ((SongResultScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new SongResultScraper())));
public static SongResultScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'title\')]/a")]
public string SongTitleXpath {
get {
return ((string)(this["SongTitleXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string SongUrlIdRegex {
get {
return ((string)(this["SongUrlIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'performer\')]/a/@href")]
public string ArtistUrlXpath {
get {
return ((string)(this["ArtistUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string ArtistUrIdRegex {
get {
return ((string)(this["ArtistUrIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'title\')]/a/@href")]
public string SongUrlIdXpath {
get {
return ((string)(this["SongUrlIdXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'performer\')]/a")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("(?<=^\").*(?=\"$)")]
public string SongTitleRegex {
get {
return ((string)(this["SongTitleRegex"]));
}
}
}
}
@@ -0,0 +1,27 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AllMusicGuide.Client.Properties" GeneratedClassName="SongResultScraper">
<Profiles />
<Settings>
<Setting Name="SongTitleXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'title')]/a</Value>
</Setting>
<Setting Name="SongUrlIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="ArtistUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'performer')]/a/@href</Value>
</Setting>
<Setting Name="ArtistUrIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="SongUrlIdXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'title')]/a/@href</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'performer')]/a</Value>
</Setting>
<Setting Name="SongTitleRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">(?&lt;=^").*(?="$)</Value>
</Setting>
</Settings>
</SettingsFile>
+98
View File
@@ -0,0 +1,98 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.18033
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")]
internal sealed partial class SongScraper : global::System.Configuration.ApplicationSettingsBase {
private static SongScraper defaultInstance = ((SongScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new SongScraper())));
public static SongScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'song-title\']")]
public string SongTitleXpath {
get {
return ((string)(this["SongTitleXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string SongUrlIdRegex {
get {
return ((string)(this["SongUrlIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'song-artist\']/a/@href")]
public string ArtistUrlXpath {
get {
return ((string)(this["ArtistUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string ArtistUrIdRegex {
get {
return ((string)(this["ArtistUrIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//link[@rel=\'canonical\']/@href")]
public string SongUrlIdXpath {
get {
return ((string)(this["SongUrlIdXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'song-artist\']/a")]
public string ArtistXpath {
get {
return ((string)(this["ArtistXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("http://www.allmusic.com/song/{0}")]
public string SongUrl {
get {
return ((string)(this["SongUrl"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@id=\'appearances\']//tbody/tr")]
public string AlbumXpath {
get {
return ((string)(this["AlbumXpath"]));
}
}
}
}
@@ -0,0 +1,30 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="MusicMetaTagger.Client.AllMusicGuide.Properties" GeneratedClassName="SongScraper">
<Profiles />
<Settings>
<Setting Name="SongTitleXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='song-title']</Value>
</Setting>
<Setting Name="SongUrlIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="ArtistUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='song-artist']/a/@href</Value>
</Setting>
<Setting Name="ArtistUrIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="SongUrlIdXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//link[@rel='canonical']/@href</Value>
</Setting>
<Setting Name="ArtistXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='song-artist']/a</Value>
</Setting>
<Setting Name="SongUrl" Type="System.String" Scope="Application">
<Value Profile="(Default)">http://www.allmusic.com/song/{0}</Value>
</Setting>
<Setting Name="AlbumXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@id='appearances']//tbody/tr</Value>
</Setting>
</Settings>
</SettingsFile>
+122
View File
@@ -0,0 +1,122 @@
//------------------------------------------------------------------------------
// <auto-generated>
// This code was generated by a tool.
// Runtime Version:4.0.30319.269
//
// Changes to this file may cause incorrect behavior and will be lost if
// the code is regenerated.
// </auto-generated>
//------------------------------------------------------------------------------
namespace MusicMetaTagger.Client.AllMusicGuide.Properties {
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")]
internal sealed partial class TrackScraper : global::System.Configuration.ApplicationSettingsBase {
private static TrackScraper defaultInstance = ((TrackScraper)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new TrackScraper())));
public static TrackScraper Default {
get {
return defaultInstance;
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'pick\')]")]
public string PickXpath {
get {
return ((string)(this["PickXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'tracknum\']")]
public string TrackNumberXpath {
get {
return ((string)(this["TrackNumberXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'title\']//a/@href")]
public string TrackUrlXpath {
get {
return ((string)(this["TrackUrlXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string TrackUrlIdRegex {
get {
return ((string)(this["TrackUrlIdRegex"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'title\']//a")]
public string TitleXpath {
get {
return ((string)(this["TitleXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'artist\')]//a")]
public string ComposerXpath {
get {
return ((string)(this["ComposerXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[@class=\'time\']")]
public string TimeXpath {
get {
return ((string)(this["TimeXpath"]));
}
}
[global::System.Configuration.ApplicationScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'performer\')]/*[@class=\'primary\']/a/@href")]
public string PerformerIdXpath {
get {
return ((string)(this["PerformerIdXpath"]));
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("[a-zA-Z]{2}[0-9]+(?=$)")]
public string PerformerIdRegex {
get {
return ((string)(this["PerformerIdRegex"]));
}
set {
this["PerformerIdRegex"] = value;
}
}
[global::System.Configuration.UserScopedSettingAttribute()]
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
[global::System.Configuration.DefaultSettingValueAttribute("//*[contains(@class,\'performer\')]//a/@href")]
public string PerformerIdAlternateXpath {
get {
return ((string)(this["PerformerIdAlternateXpath"]));
}
set {
this["PerformerIdAlternateXpath"] = value;
}
}
}
}
@@ -0,0 +1,36 @@
<?xml version='1.0' encoding='utf-8'?>
<SettingsFile xmlns="http://schemas.microsoft.com/VisualStudio/2004/01/settings" CurrentProfile="(Default)" GeneratedClassNamespace="AllMusicGuide.Client.Properties" GeneratedClassName="TrackScraper">
<Profiles />
<Settings>
<Setting Name="PickXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'pick')]</Value>
</Setting>
<Setting Name="TrackNumberXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='tracknum']</Value>
</Setting>
<Setting Name="TrackUrlXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='title']//a/@href</Value>
</Setting>
<Setting Name="TrackUrlIdRegex" Type="System.String" Scope="Application">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="TitleXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='title']//a</Value>
</Setting>
<Setting Name="ComposerXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'artist')]//a</Value>
</Setting>
<Setting Name="TimeXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[@class='time']</Value>
</Setting>
<Setting Name="PerformerIdXpath" Type="System.String" Scope="Application">
<Value Profile="(Default)">//*[contains(@class,'performer')]/*[@class='primary']/a/@href</Value>
</Setting>
<Setting Name="PerformerIdRegex" Type="System.String" Scope="User">
<Value Profile="(Default)">[a-zA-Z]{2}[0-9]+(?=$)</Value>
</Setting>
<Setting Name="PerformerIdAlternateXpath" Type="System.String" Scope="User">
<Value Profile="(Default)">//*[contains(@class,'performer')]//a/@href</Value>
</Setting>
</Settings>
</SettingsFile>
@@ -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);
}
}
}
@@ -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));
}
}
}
@@ -0,0 +1,65 @@
using System.Collections.Generic;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Services;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess
{
/// <summary>
/// Inspired by this Perl module: http://backpan.perl.org/authors/id/Y/YO/YOHAMED/WWW-AllMusicGuide.html
/// </summary>
public class MusicGuideScraper : IMusicGuide
{
private readonly IScraper<Album> _albumScraper;
private readonly IScraper<List<AlbumResult>> _albumResultScraper;
private readonly IScraper<Artist> _artistScraper;
private readonly IScraper<List<ArtistResult>> _artistResultScraper;
private readonly IScraper<List<ArtistDiscography>> _discographyScraper;
private readonly IScraper<List<SongResult>> _songResultScraper;
private readonly IScraper<Song> _songScraper;
public MusicGuideScraper(IScraper<Album> albumScraper, IScraper<List<AlbumResult>> albumResultScraper, IScraper<Artist> artistScraper, IScraper<List<ArtistResult>> artistResultScraper, IScraper<List<ArtistDiscography>> discographyScraper, IScraper<List<SongResult>> songResultScraper, IScraper<Song> songScraper)
{
_albumScraper = albumScraper;
_albumResultScraper = albumResultScraper;
_artistScraper = artistScraper;
_artistResultScraper = artistResultScraper;
_discographyScraper = discographyScraper;
_songResultScraper = songResultScraper;
_songScraper = songScraper;
}
public Artist GetArtist(string artistId)
{
var artist = _artistScraper.Scrape(artistId);
artist.Discography = _discographyScraper.Scrape(artistId);
return artist;
}
public Album GetAlbum(string albumId)
{
return _albumScraper.Scrape(albumId);
}
public Song GetSong(string songId)
{
return _songScraper.Scrape(songId);
}
public List<ArtistResult> SearchArtist(string artistName)
{
return _artistResultScraper.Scrape(artistName);
}
public List<AlbumResult> SearchAlbum(string albumName)
{
return _albumResultScraper.Scrape(albumName);
}
public List<SongResult> SearchSong(string songName)
{
return _songResultScraper.Scrape(songName);
}
}
}
@@ -0,0 +1,94 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class AlbumParser : ParserBase<Album>
{
public override Album Parse(XmlDocument xml)
{
var album = new Album();
var settings = AlbumScraper.Default;
if (XpathMatch(xml, settings.ArtistUrlXpath))
{
var artistUrl = XpathMatchString(xml, settings.ArtistUrlXpath);
album.ArtistId = RegexCapture(artistUrl, settings.ArtistUrlIdRegex);
album.ArtistName = XpathMatchString(xml, settings.ArtistXpath);
}
else
{
album.VariousArtists = true;
album.ArtistName = "Various Artists";
}
if (XpathMatch(xml, settings.SoundtrackXpath))
{
album.Soundtrack = true;
}
var albumId = XpathMatchString(xml, settings.AlbumIdXpath);
album.AlbumId = StringUtility.CleanId(albumId);
album.Title = XpathMatchString(xml, settings.TitleXpath);
if (XpathMatch(xml, settings.ReleaseDateXpath))
album.ReleaseDate = XpathMatchString(xml, settings.ReleaseDateXpath);
//rating
if (XpathMatch(xml, settings.RatingXpath))
{
float rating;
if (float.TryParse(XpathMatchString(xml, settings.RatingXpath), out rating))
album.Rating = Convert.ToInt32(rating*2);
}
if (XpathMatch(xml, settings.PickXpath))
album.Pick = true;
if (XpathMatch(xml, settings.CoverUrlXpath))
{
var coverUrl = XpathMatchString(xml, settings.CoverUrlXpath);
album.CoverUrl = RegexCapture(coverUrl, settings.CoverUrlRegex);
}
if (XpathMatch(xml, settings.ReviewXpath))
album.Review = XpathMatchString(xml, settings.ReviewXpath);
if (XpathMatch(xml, settings.ReviewerXpath))
{
var reviewer = XpathMatchString(xml, settings.ReviewerXpath);
album.Reviewer = RegexCapture(reviewer, settings.ReviewerRegex).Trim();
}
if (XpathMatch(xml, settings.StylesXpath))
album.Styles = XpathMatchStringList(xml, settings.StylesXpath);
if (XpathMatch(xml, settings.MoodsXpath))
album.Moods = XpathMatchStringList(xml, settings.MoodsXpath);
if (XpathMatch(xml, settings.ThemesXpath))
album.Themes = XpathMatchStringList(xml, settings.ThemesXpath);
album.Tracks = new List<Track>();
if (XpathMatch(xml, settings.TrackXpath))
{
var trackParser = new TrackParser();
var tracks =
from tXml in XpathMatchXmlList(xml, settings.TrackXpath)
let t = trackParser.Parse(tXml)
where t.PerformerIds.Any() && t.TrackLength > TimeSpan.MinValue
select t;
foreach (var track in tracks)
{
track.AlbumId = album.AlbumId;
album.Tracks.Add(track);
}
}
return album;
}
}
}
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class AlbumResultPageParser : ParserBase<List<AlbumResult>>
{
public override List<AlbumResult> Parse(XmlDocument xml)
{
var albumResults = new List<AlbumResult>();
var settings = AlbumResultPageScraper.Default;
var albumResultScraper = new AlbumResultParser();
if (XpathMatch(xml, settings.AlbumXpath))
{
var albumResultInfoXmlList = XpathMatchXmlList(xml, settings.AlbumXpath);
albumResults.AddRange(albumResultInfoXmlList.Select(albumResultScraper.Parse));
var i = 1;
albumResults.ForEach(ar => ar.ResultOrder = i++);
}
return albumResults;
}
}
}
@@ -0,0 +1,51 @@
using System;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class AlbumResultParser : ParserBase<AlbumResult>
{
public override AlbumResult Parse(XmlDocument xml)
{
var albumResult = new AlbumResult();
var settings = AlbumResultScraper.Default;
var albumUrl = XpathMatchString(xml, settings.AlbumUrlXpath);
albumResult.AlbumId = RegexCapture(albumUrl, settings.AlbumUrlIdRegex);
if (XpathMatch(xml, settings.InfoXpath))
{
var infoRaw = XpathMatchString(xml, settings.InfoXpath);
if (RegexMatch(infoRaw, settings.YearRegex))
{
var year = RegexCapture(infoRaw, settings.YearRegex);
int yearInt;
if (Int32.TryParse(year, out yearInt))
albumResult.Year = yearInt;
}
if (RegexMatch(infoRaw, settings.GenreRegex))
{
var genre = RegexCapture(infoRaw, settings.GenreRegex);
albumResult.Genre = genre.Trim();
}
}
if (XpathMatch(xml, settings.ArtistUrlXpath))
{
var artistId = XpathMatchString(xml, settings.ArtistUrlXpath);
albumResult.ArtistId = RegexCapture(artistId, settings.ArtistUrIdRegex);
albumResult.ArtistName = XpathMatchString(xml, settings.ArtistXpath);
}
else if (XpathMatch(xml, settings.ArtistNoUrlXpath))
{
albumResult.ArtistName = XpathMatchString(xml, settings.ArtistNoUrlXpath);
}
albumResult.AlbumTitle = XpathMatchString(xml, settings.TitleXpath);
return albumResult;
}
}
}
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class ArtistDiscographyPageParser : ParserBase<List<ArtistDiscography>>
{
public override List<ArtistDiscography> Parse(XmlDocument xml)
{
var artistDiscographies = new List<ArtistDiscography>();
var settings = ArtistDiscographyPageScraper.Default;
if (XpathMatch(xml, settings.DiscographyXpath))
{
var releaseTypeStr = XpathMatchString(xml, settings.ReleaseTypeXpath);
releaseTypeStr = RegexCapture(releaseTypeStr, settings.ReleaseTypeRegex);
ReleaseType releaseType;
switch (releaseTypeStr)
{
case "main":
releaseType = ReleaseType.Album;
break;
case "singles":
releaseType = ReleaseType.Single;
break;
case "compilations":
releaseType = ReleaseType.Compilation;
break;
default:
throw new ApplicationException("Unknown release type");
}
var discographyParser = new ArtistDiscographyParser(releaseType);
artistDiscographies.AddRange(
from albumXml in XpathMatchXmlList(xml, settings.DiscographyXpath)
let album = discographyParser.Parse(albumXml)
where album.Year > 0
select album);
var artistId = StringUtility.CleanId(XpathMatchString(xml, settings.ArtistIdXpath));
var artistName = XpathMatchString(xml, settings.ArtistXpath);
foreach (var artistDiscography in artistDiscographies)
{
artistDiscography.ArtistId = artistId;
artistDiscography.ArtistName = artistName;
}
}
return artistDiscographies;
}
}
}
@@ -0,0 +1,66 @@
using System;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class ArtistDiscographyParser : ParserBase<ArtistDiscography>
{
private readonly ReleaseType _releaseType;
public ArtistDiscographyParser(ReleaseType releaseType)
{
_releaseType = releaseType;
}
public override ArtistDiscography Parse(XmlDocument xml)
{
var artistDiscography = new ArtistDiscography();
var settings = ArtistDiscographyScraper.Default;
var albumUrl = XpathMatchString(xml, settings.AlbumUrlXpath);
artistDiscography.AlbumId = RegexCapture(albumUrl, settings.AlbumUrlIdRegex);
var year = XpathMatchString(xml, settings.YearXpath);
if (year.Length > 0 && StringUtility.IsNumeric(year))
artistDiscography.Year = Convert.ToInt32(XpathMatchString(xml, settings.YearXpath));
//rating
if (XpathMatch(xml, settings.RatingXpath))
{
var ratingString = XpathMatchString(xml, settings.RatingXpath);
if (!string.IsNullOrEmpty(ratingString))
{
ratingString = RegexCapture(ratingString, settings.RatingRegex);
int rating;
if (int.TryParse(ratingString, out rating))
artistDiscography.Rating = Convert.ToInt32(rating);
}
}
artistDiscography.Pick = XpathMatch(xml, settings.PickXpath);
if (XpathMatch(xml, settings.LabelXpath))
artistDiscography.Label = XpathMatchString(xml, settings.LabelXpath);
artistDiscography.AlbumTitle = XpathMatchString(xml, settings.TitleXpath);
artistDiscography.ReleaseType = _releaseType;
// EPs will have "[EP]" after the title
if (RegexMatch(artistDiscography.AlbumTitle, settings.ReleaseTypeRegex))
{
var releaseType = RegexCapture(artistDiscography.AlbumTitle, settings.ReleaseTypeRegex);
if (releaseType == "EP")
artistDiscography.ReleaseType = ReleaseType.EP;
}
else if (artistDiscography.AlbumTitle.Contains("Box Set"))
artistDiscography.ReleaseType = ReleaseType.BoxSet;
// TODO: add "has review"
return artistDiscography;
}
}
}
@@ -0,0 +1,30 @@
using System.Collections.Generic;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class ArtistParser : ParserBase<Artist>
{
public override Artist Parse(XmlDocument artistXml)
{
var scraper = ArtistScaper.Default;
var artist = new Artist();
artist.ArtistId = StringUtility.CleanId(XpathMatchString(artistXml, scraper.ArtistIdXpath));
artist.ArtistName = XpathMatchString(artistXml, scraper.ArtistXpath);
if (XpathMatch(artistXml, scraper.GenreXpath))
artist.Genre = XpathMatchString(artistXml, scraper.GenreXpath);
artist.Styles =
XpathMatch(artistXml, scraper.StylesXpath)
? XpathMatchStringList(artistXml, scraper.StylesXpath)
: new List<string>();
return artist;
}
}
}
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class ArtistResultPageParser : ParserBase<List<ArtistResult>>
{
public override List<ArtistResult> Parse(XmlDocument xml)
{
var artistResults = new List<ArtistResult>();
var settings = ArtistResultPageScraper.Default;
var artistResultParser = new ArtistResultParser();
if (XpathMatch(xml, settings.ArtistXpath))
{
var artistResultsXmlList = XpathMatchXmlList(xml, settings.ArtistXpath);
artistResults = artistResultsXmlList.Select(artistResultParser.Parse).ToList();
var i = 1;
artistResults.ForEach(ar => ar.ResultOrder = i++);
}
return artistResults;
}
}
}
@@ -0,0 +1,35 @@
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class ArtistResultParser : ParserBase<ArtistResult>
{
public override ArtistResult Parse(XmlDocument xml)
{
var artistResult = new ArtistResult();
var settings = ArtistResultScraper.Default;
var artistId = XpathMatchString(xml, settings.ArtistUrlXpath);
artistResult.ArtistId = RegexCapture(artistId, settings.ArtistUrIdRegex);
artistResult.ArtistName = XpathMatchString(xml, settings.ArtistXpath);
if (XpathMatch(xml, settings.InfoXpath))
{
var infoRaw = XpathMatchString(xml, settings.InfoXpath);
if (RegexMatch(infoRaw, settings.YearsActiveRegex))
{
var yearsActive = RegexCapture(infoRaw, settings.YearsActiveRegex);
artistResult.YearsActive = yearsActive.Trim();
}
if (RegexMatch(infoRaw, settings.GenreRegex))
{
var genre = RegexCapture(infoRaw, settings.GenreRegex);
artistResult.Genre = genre.Trim();
}
}
return artistResult;
}
}
}
@@ -0,0 +1,9 @@
using System.Xml;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public interface IParser<out TEntity>
{
TEntity Parse(XmlDocument xml);
}
}
@@ -0,0 +1,89 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Text.RegularExpressions;
using System.Xml;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public abstract class ParserBase<TEntity> : IParser<TEntity>
{
public abstract TEntity Parse(XmlDocument xml);
protected static string RegexCapture(string str, string regexStr)
{
var regex = new Regex(regexStr);
var match = regex.Match(str);
if (match.Captures.Count == 0)
throw new ApplicationException("Couldn't make regex match for "
+ regexStr + " in " + str);
return match.Captures[0].Value;
}
protected static bool RegexMatch(string str, string regexStr)
{
var regex = new Regex(regexStr);
var match = regex.Match(str);
return match.Captures.Count > 0;
}
protected static bool XpathMatch(XmlDocument xml, string xPath)
{
var nodes = xml.SelectNodes(xPath);
return nodes != null && nodes.Count > 0;
}
protected static string XpathMatchString(XmlDocument xml, string xPath)
{
var titleNodes = xml.SelectNodes(xPath);
if (titleNodes == null || titleNodes.Count == 0)
{
//xml.Save(@"problem_" + DateTime.Now.Ticks + ".xml");
throw new ApplicationException(string.Format("Couldn't find matching nodes for : \"{0}\" in {1}", xPath,
xml.InnerXml));
}
var decodedValue = WebUtility.HtmlDecode(titleNodes[0].InnerText);
return decodedValue.Trim();
}
protected static List<string> XpathMatchStringList(XmlDocument xml, string xPath)
{
var titleNodes = xml.SelectNodes(xPath);
if (titleNodes == null || titleNodes.Count == 0)
throw new ApplicationException("Couldn't find matching nodes for : " + xPath);
return (
from XmlNode node in titleNodes
select WebUtility.HtmlDecode(node.InnerText)
into decodedValue
select decodedValue.Trim()
).ToList();
}
protected static IEnumerable<XmlDocument> XpathMatchXmlList(XmlDocument xml, string xPath)
{
var titleNodes = xml.SelectNodes(xPath);
if (titleNodes == null || titleNodes.Count == 0)
{
xml.Save("problem.xml");
throw new ApplicationException("Couldn't find matching nodes for : " + xPath);
}
var xmlList = new List<XmlDocument>();
foreach (XmlNode node in titleNodes)
{
var xmlDocument = new XmlDocument();
xmlDocument.LoadXml(xml.FirstChild.InnerXml + "<root></root>");
var importNode = xmlDocument.ImportNode(node, true);
if (xmlDocument.DocumentElement == null)
throw new ApplicationException("xmlDocument.DocumentElement Null");
xmlDocument.DocumentElement.AppendChild(importNode);
xmlList.Add(xmlDocument);
}
return xmlList;
}
}
}
@@ -0,0 +1,74 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class SongAppearsOnAlbumParser : ParserBase<SongAppearsOnAlbum>
{
public override SongAppearsOnAlbum Parse(XmlDocument xml)
{
var albumResult = new SongAppearsOnAlbum();
var settings = SongAppearsOnAlbumScraper.Default;
var albumUrl = XpathMatchString(xml, settings.AlbumUrlXpath);
albumResult.AlbumId = RegexCapture(albumUrl, settings.AlbumUrlIdRegex);
if (XpathMatch(xml, settings.YearXpath))
{
var yearRaw = XpathMatchString(xml, settings.YearXpath);
if (RegexMatch(yearRaw, settings.YearRegex))
{
var year = RegexCapture(yearRaw, settings.YearRegex);
int yearInt;
if (Int32.TryParse(year, out yearInt))
albumResult.Year = yearInt;
}
}
if (XpathMatch(xml, settings.ArtistUrlXpath))
{
var artistId = XpathMatchString(xml, settings.ArtistUrlXpath);
albumResult.ArtistId = RegexCapture(artistId, settings.ArtistUrIdRegex);
albumResult.ArtistName = XpathMatchString(xml, settings.ArtistXpath);
}
else if (XpathMatch(xml, settings.ArtistNoUrlXpath))
{
albumResult.ArtistName = XpathMatchString(xml, settings.ArtistNoUrlXpath);
}
albumResult.AlbumTitle = XpathMatchString(xml, settings.TitleXpath);
return albumResult;
}
}
public class SongParser : ParserBase<Song>
{
public override Song Parse(XmlDocument xml)
{
var song = new Song();
var settings = SongScraper.Default;
song.SongTitle = XpathMatchString(xml, settings.SongTitleXpath);
var songUrlId = XpathMatchString(xml, settings.SongUrlIdXpath);
song.SongId = RegexCapture(songUrlId, settings.SongUrlIdRegex);
song.AppearsOnAlbum = new List<SongAppearsOnAlbum>();
var songAppearsOnAlbumParser = new SongAppearsOnAlbumParser();
if (XpathMatch(xml, settings.AlbumXpath))
{
var albumResultInfoXmlList = XpathMatchXmlList(xml, settings.AlbumXpath);
song.AppearsOnAlbum.AddRange(albumResultInfoXmlList.Select(songAppearsOnAlbumParser.Parse));
var i = 1;
song.AppearsOnAlbum.ForEach(ar => ar.ResultOrder = i++);
}
return song;
}
}
}
@@ -0,0 +1,27 @@
using System.Collections.Generic;
using System.Linq;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class SongResultPageParser : ParserBase<List<SongResult>>
{
public override List<SongResult> Parse(XmlDocument xml)
{
var songResults = new List<SongResult>();
var settings = SongResultPageScraper.Default;
var songResultScraper = new SongResultParser();
if (XpathMatch(xml, settings.SongXpath))
{
var songResultInfoXmlList = XpathMatchXmlList(xml, settings.SongXpath);
songResults.AddRange(songResultInfoXmlList.Select(songResultScraper.Parse));
var i = 1;
songResults.ForEach(ar => ar.ResultOrder = i++);
}
return songResults;
}
}
}
@@ -0,0 +1,31 @@
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class SongResultParser : ParserBase<SongResult>
{
public override SongResult Parse(XmlDocument xml)
{
var songResult = new SongResult();
var settings = SongResultScraper.Default;
songResult.SongTitle = XpathMatchString(xml, settings.SongTitleXpath);
if (RegexMatch(songResult.SongTitle, settings.SongTitleRegex))
songResult.SongTitle = RegexCapture(songResult.SongTitle, settings.SongTitleRegex);
var songUrl = XpathMatchString(xml, settings.SongUrlIdXpath);
songResult.SongId = RegexCapture(songUrl, settings.SongUrlIdRegex);
if (XpathMatch(xml, settings.ArtistUrlXpath))
{
var artistId = XpathMatchString(xml, settings.ArtistUrlXpath);
songResult.ArtistId = RegexCapture(artistId, settings.ArtistUrIdRegex);
songResult.ArtistName = XpathMatchString(xml, settings.ArtistXpath);
}
return songResult;
}
}
}
@@ -0,0 +1,58 @@
using System;
using System.Collections.Generic;
using System.Xml;
using MusicMetaTagger.Client.AllMusicGuide.Properties;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser
{
public class TrackParser : ParserBase<Track>
{
public override Track Parse(XmlDocument xml)
{
var track = new Track();
var settings = TrackScraper.Default;
if (XpathMatch(xml, settings.TrackUrlXpath))
{
var trackUrl = XpathMatchString(xml, settings.TrackUrlXpath);
track.TrackId = RegexCapture(trackUrl, settings.TrackUrlIdRegex);
}
track.TrackNumber = Convert.ToInt32(XpathMatchString(xml, settings.TrackNumberXpath));
track.Pick = XpathMatch(xml, settings.PickXpath);
track.TrackTitle =
XpathMatch(xml, settings.TitleXpath)
? XpathMatchString(xml, settings.TitleXpath)
: string.Empty;
if (XpathMatch(xml, settings.ComposerXpath))
track.Composer =
String.Join(", ",
XpathMatchStringList(xml, settings.ComposerXpath).ToArray());
track.PerformerIds = new List<string>();
var performerIdXpath = settings.PerformerIdXpath;
if (!XpathMatch(xml, settings.PerformerIdXpath))
performerIdXpath = settings.PerformerIdAlternateXpath;
if (XpathMatch(xml, performerIdXpath))
{
var performerIdsRaw = XpathMatchStringList(xml, performerIdXpath);
foreach (var performerIdRaw in performerIdsRaw)
{
if (RegexMatch(performerIdRaw, settings.PerformerIdRegex))
track.PerformerIds.Add(RegexCapture(performerIdRaw, settings.PerformerIdRegex));
}
}
var timeString = XpathMatchString(xml, settings.TimeXpath);
track.TrackLength = StringUtility.ConvertTimeSpan(timeString);
return track;
}
}
}
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public class AlbumResultPageScraper : ScraperBase<List<AlbumResult>, AlbumResultPageClient, AlbumResultPageParser>
{
}
}
@@ -0,0 +1,10 @@
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public class AlbumScraper : ScraperBase<Album, AlbumClient, AlbumParser>
{
}
}
@@ -0,0 +1,24 @@
using System.Collections.Generic;
using System.Linq;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public class ArtistDiscographyScraper : IScraper<List<ArtistDiscography>>
{
public List<ArtistDiscography> Scrape(string artistId)
{
var parser = new ArtistDiscographyPageParser();
var xmlClients =
new IXmlClientBase[]
{
new ArtistDiscographyAlbumPageClient(),
new ArtistDiscographyCompilationPageClient(),
new ArtistDiscographyEpSinglesPageClient()
};
return xmlClients.Select(c => c.Read(artistId)).SelectMany(parser.Parse).ToList();
}
}
}
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public class ArtistResultPageScraper : ScraperBase<List<ArtistResult>, ArtistResultPageClient, ArtistResultPageParser>
{
}
}
@@ -0,0 +1,19 @@
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public class ArtistScraper : ScraperBase<Artist, ArtistClient, ArtistParser>
{
public new Artist Scrape(string artistId)
{
var artist = base.Scrape(artistId);
var artistDiscographyScraper = new ArtistDiscographyScraper();
artist.Discography = artistDiscographyScraper.Scrape(artistId);
return artist;
}
}
}
@@ -0,0 +1,7 @@
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public interface IScraper<out TEntity>
{
TEntity Scrape(string criteria);
}
}
@@ -0,0 +1,16 @@
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public abstract class ScraperBase<TEntity, TClient, TParser> : IScraper<TEntity>
where TClient : IXmlClientBase, new()
where TParser : IParser<TEntity>, new()
{
public TEntity Scrape(string criteria)
{
var xmlDocument = new TClient().Read(criteria);
return new TParser().Parse(xmlDocument);
}
}
}
@@ -0,0 +1,11 @@
using System.Collections.Generic;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public class SongResultPageScraper : ScraperBase<List<SongResult>, SongResultPageClient, SongResultPageParser>
{
}
}
@@ -0,0 +1,10 @@
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Client;
using MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Parser;
using MusicMetaTagger.Core.Model;
namespace MusicMetaTagger.Client.AllMusicGuide.RemoteDataAccess.Scraper
{
public class SongScraper : ScraperBase<Song, SongClient, SongParser>
{
}
}
@@ -0,0 +1,69 @@
using System;
using System.Collections.Generic;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Services;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.Services
{
public class MusicGuideCache : IMusicGuide
{
private readonly Func<string, Album> _getAlbum;
private readonly Func<string, Artist> _getArtist;
private readonly Func<string, Song> _getSong;
private readonly Func<string, List<ArtistResult>> _searchArtist;
private readonly Func<string, List<AlbumResult>> _searchAlbum;
private readonly Func<string, List<SongResult>> _searchSong;
public MusicGuideCache(IMusicGuide @base)
{
Func<string, Album> getAlbum = @base.GetAlbum;
_getAlbum = getAlbum.MemoizeFunction();
Func<string, Artist> getArtist = @base.GetArtist;
_getArtist = getArtist.MemoizeFunction();
Func<string, Song> getSong = @base.GetSong;
_getSong = getSong.MemoizeFunction();
Func<string, List<ArtistResult>> searchArtist = @base.SearchArtist;
_searchArtist = searchArtist.MemoizeFunction();
Func<string, List<AlbumResult>> searchAlbum = @base.SearchAlbum;
_searchAlbum = searchAlbum.MemoizeFunction();
Func<string, List<SongResult>> searchSong = @base.SearchSong;
_searchSong = searchSong.MemoizeFunction();
}
public Artist GetArtist(string artistId)
{
return _getArtist(artistId);
}
public Album GetAlbum(string albumId)
{
return _getAlbum(albumId);
}
public Song GetSong(string songId)
{
return _getSong(songId);
}
public List<ArtistResult> SearchArtist(string artistName)
{
return _searchArtist(artistName);
}
public List<AlbumResult> SearchAlbum(string albumName)
{
return _searchAlbum(albumName);
}
public List<SongResult> SearchSong(string songName)
{
return _searchSong(songName);
}
}
}
@@ -0,0 +1,74 @@
using System.Linq;
using System.Threading;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Queries;
using MusicMetaTagger.Core.Services;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.Services.Searchers
{
public class AlbumArtistSearcher : ITrackSearcher
{
private readonly IMusicGuide _musicGuide;
public AlbumArtistSearcher(IMusicGuide musicGuide)
{
_musicGuide = musicGuide;
}
public event StatusHandler SearchStatusUpdate;
private void OnStatusUpdate(string status)
{
if (SearchStatusUpdate != null)
SearchStatusUpdate.Invoke(new StatusEventArgs(status));
}
public Track SearchTrack(TrackQuery trackQuery, CancellationToken canellationToken)
{
if (string.IsNullOrEmpty(trackQuery.AlbumTitle)
|| string.IsNullOrEmpty(trackQuery.ArtistName))
return null;
var albumResults = ScoreUtility.ScoreResults(_musicGuide.SearchAlbum, trackQuery.AlbumTitle).ToList();
var artistName = GetArtistName(trackQuery);
var artistResults = ScoreUtility.ScoreResults(_musicGuide.SearchArtist, artistName);
if (canellationToken.IsCancellationRequested)
return null;
var matchingAlbums = ScoreUtility.Match(
albumResults, artistResults,
(album, artist) =>
album.ArtistId == artist.ArtistId).ToList();
if (trackQuery.AlbumPartOfCompilation)
matchingAlbums = matchingAlbums.Union(
from album in albumResults
where
album.Obj.ArtistId == null
|| album.Obj.AlbumTitle.Contains("Soundtrack")
select album
).ToList();
if (canellationToken.IsCancellationRequested)
return null;
OnStatusUpdate("Found " + matchingAlbums.Count() + " potential matching albums");
return (from album in matchingAlbums
let tracks = _musicGuide.GetAlbum(album.Obj.AlbumId).Tracks
let match = TrackSimilarity.MatchTrack(trackQuery, tracks)
where match != null
select match).FirstOrDefault();
}
private static string GetArtistName(TrackQuery trackQuery)
{
var artistName = trackQuery.ArtistName;
if (!string.IsNullOrEmpty(trackQuery.AlbumArtistName) && !trackQuery.AlbumArtistName.Contains("Various"))
artistName = trackQuery.AlbumArtistName;
return artistName;
}
}
}
@@ -0,0 +1,40 @@
using System.Linq;
using System.Threading;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Queries;
using MusicMetaTagger.Core.Services;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.Services.Searchers
{
public class AlbumSearcher : ITrackSearcher
{
private readonly IMusicGuide _musicGuide;
public AlbumSearcher(IMusicGuide musicGuide)
{
_musicGuide = musicGuide;
}
public event StatusHandler SearchStatusUpdate;
public Track SearchTrack(TrackQuery trackQuery, CancellationToken canellationToken)
{
if (string.IsNullOrEmpty(trackQuery.AlbumTitle))
return null;
var albumResults = ScoreUtility.ScoreResults(_musicGuide.SearchAlbum, trackQuery.AlbumTitle);
foreach (ScoreContainer<AlbumResult> album in albumResults.Take(10))
{
if (canellationToken.IsCancellationRequested)
return null;
var tracks = _musicGuide.GetAlbum(album.Obj.AlbumId).Tracks;
var match = TrackSimilarity.MatchTrack(trackQuery, tracks);
if (match != null) return match;
}
return null;
}
}
}
@@ -0,0 +1,59 @@
using System.Linq;
using System.Threading;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Queries;
using MusicMetaTagger.Core.Services;
using MusicMetaTagger.Core.Utility;
namespace MusicMetaTagger.Client.AllMusicGuide.Services.Searchers
{
public class SongArtistSearcher : ITrackSearcher
{
private readonly IMusicGuide _musicGuide;
public SongArtistSearcher(IMusicGuide musicGuide)
{
_musicGuide = musicGuide;
}
public event StatusHandler SearchStatusUpdate;
public Track SearchTrack(TrackQuery trackQuery, CancellationToken canellationToken)
{
if (string.IsNullOrEmpty(trackQuery.TrackTitle)
|| string.IsNullOrEmpty(trackQuery.ArtistName))
return null;
var songResults = ScoreUtility.ScoreResults(_musicGuide.SearchSong, trackQuery.TrackTitle);
var artistName = GetArtistName(trackQuery);
var artistResults = ScoreUtility.ScoreResults(_musicGuide.SearchArtist, artistName);
if (canellationToken.IsCancellationRequested)
return null;
var matchingSongs = ScoreUtility.Match(songResults, artistResults, (s, a) => s.ArtistId == a.ArtistId).ToList();
foreach (var song in matchingSongs)
{
foreach (var album in _musicGuide.GetSong(song.Obj.SongId).AppearsOnAlbum)
{
if (canellationToken.IsCancellationRequested)
return null;
var tracks = _musicGuide.GetAlbum(album.AlbumId).Tracks;
var match = TrackSimilarity.MatchTrack(trackQuery, tracks);
if (match != null) return match;
}
}
return null;
}
private static string GetArtistName(TrackQuery trackQuery)
{
var artistName = trackQuery.ArtistName;
if (!string.IsNullOrEmpty(trackQuery.AlbumArtistName) && !trackQuery.AlbumArtistName.Contains("Various"))
artistName = trackQuery.AlbumArtistName;
return artistName;
}
}
}
@@ -0,0 +1,25 @@
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Services;
namespace MusicMetaTagger.Client.AllMusicGuide.Services
{
public class TrackOriginalRelease : ITrackOriginalRelease
{
private readonly IMusicGuide _musicGuide;
public TrackOriginalRelease(IMusicGuide musicGuide)
{
_musicGuide = musicGuide;
}
public Track GetTrackOriginalRelease(Track track)
{
var song = _musicGuide.GetSong(track.TrackId);
// TODO: iterate through albums until track is found
var firstAlbum = song.AppearsOnAlbum[0];
var album = _musicGuide.GetAlbum(firstAlbum.AlbumId);
return TrackSimilarity.MatchTrack(track, album.Tracks);
}
}
}
@@ -0,0 +1,57 @@
using System;
using System.Linq;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Services;
namespace MusicMetaTagger.Client.AllMusicGuide.Services
{
public class TrackRater : ITrackRater
{
private readonly IMusicGuide _musicGuide;
public TrackRater(IMusicGuide musicGuide)
{
_musicGuide = musicGuide;
}
public int? RateTrack(Track track)
{
if (track == null)
throw new ArgumentNullException("track");
if (track.PerformerIds == null || track.PerformerIds.Count == 0)
return null;
// get the track pick statistics for this album
var albumInfo = _musicGuide.GetAlbum(track.AlbumId);
if (!albumInfo.Rating.HasValue)
return null;
// get the album rating statistics for this artist
double artistAlbumRatingMean = 0;
var artistInfo = _musicGuide.GetArtist(albumInfo.ArtistId);
if (artistInfo != null)
{
var currentDi =
artistInfo.Discography.Find(di => di.AlbumId == albumInfo.AlbumId);
artistAlbumRatingMean =
(from di in artistInfo.Discography
where currentDi == null || di.ReleaseType == currentDi.ReleaseType
where di.Rating.HasValue
select di.Rating.Value).Average();
}
// calculate the track rating
var trackRating = albumInfo.Rating.Value;
if (albumInfo.Rating.Value >= 9 && albumInfo.Tracks.Any(t => t.Pick)) trackRating -= 1;
if (artistAlbumRatingMean >= 8 && albumInfo.Tracks.Any(t => t.Pick)) trackRating -= 1;
if (albumInfo.Pick) trackRating += 1;
if (track.Pick) trackRating += 2;
return Math.Min(Math.Max(0, trackRating), 10);
}
}
}
@@ -0,0 +1,87 @@
using System;
using System.Threading;
using MusicMetaTagger.Client.AllMusicGuide.Services.Searchers;
using MusicMetaTagger.Core.Model;
using MusicMetaTagger.Core.Queries;
using MusicMetaTagger.Core.Services;
namespace MusicMetaTagger.Client.AllMusicGuide.Services
{
public class TrackSearcher : ITrackSearcher
{
private readonly IMusicGuide _musicGuide;
private readonly ITrackSearcher[] _searchers;
public TrackSearcher(IMusicGuide musicGuide)
{
_musicGuide = musicGuide;
_searchers = new ITrackSearcher[]
{
new AlbumArtistSearcher(_musicGuide),
new SongArtistSearcher(_musicGuide),
new AlbumSearcher(_musicGuide),
};
foreach (var searcher in _searchers)
searcher.SearchStatusUpdate += NestedSearcher_SearchStatusUpdate;
}
public event StatusHandler SearchStatusUpdate;
private void OnStatusUpdate(string status)
{
if (SearchStatusUpdate != null)
SearchStatusUpdate.Invoke(new StatusEventArgs(status));
}
void NestedSearcher_SearchStatusUpdate(StatusEventArgs data)
{
if (SearchStatusUpdate != null)
SearchStatusUpdate.Invoke(data);
}
public Track SearchTrack(TrackQuery trackQuery, CancellationToken canellationToken)
{
var statusSearch =
String.Format("Searching for Album - Artist - Track: '{0} - {1} - {2}'",
trackQuery.AlbumTitle ?? "unknown", trackQuery.ArtistName ?? "unknown",
trackQuery.TrackTitle);
OnStatusUpdate(statusSearch);
Track matchingTrack = null;
foreach (var searcher in _searchers)
{
var track = searcher.SearchTrack(trackQuery, canellationToken);
if (track != null)
{
matchingTrack = track;
break;
}
if (canellationToken.IsCancellationRequested)
return null;
}
if (matchingTrack != null)
{
TrackMatchFound(matchingTrack);
return matchingTrack;
}
OnStatusUpdate("No Matches Found");
return null;
}
/// <summary>
/// Updates the callback with the matching track
/// </summary>
private void TrackMatchFound(Track matchTrack)
{
var artistName = !string.IsNullOrEmpty(matchTrack.PerformerIds[0])
? _musicGuide.GetArtist(matchTrack.PerformerIds[0]).ArtistName
: "Unknown";
var matchAlbumInfo = _musicGuide.GetAlbum(matchTrack.AlbumId);
var status = String.Format("Found Match: {0} - {1} - {2} - {3}", artistName, matchAlbumInfo.Title, matchTrack.TrackTitle, matchTrack.TrackLength);
OnStatusUpdate(status);
}
}
}
+118
View File
@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
<configSections>
<sectionGroup name="applicationSettings" type="System.Configuration.ApplicationSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MusicMetaTagger.Client.AllMusicGuide.Properties.SongScraper" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistScaper" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistDiscographyPageScraper" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="MusicMetaTagger.Client.AllMusicGuide.Properties.SongResultPageScraper" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistResultPageScraper" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
<section name="MusicMetaTagger.Client.AllMusicGuide.Properties.AlbumResultPageScraper" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" requirePermission="false" />
</sectionGroup>
<sectionGroup name="userSettings" type="System.Configuration.UserSettingsGroup, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" >
<section name="MusicMetaTagger.Client.AllMusicGuide.Properties.SongResultPageScraper" type="System.Configuration.ClientSettingsSection, System, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" allowExeDefinition="MachineToLocalUser" requirePermission="false" />
</sectionGroup>
</configSections>
<applicationSettings>
<MusicMetaTagger.Client.AllMusicGuide.Properties.SongScraper>
<setting name="SongTitleXpath" serializeAs="String">
<value>//*[@class='song-title']</value>
</setting>
<setting name="SongUrlIdRegex" serializeAs="String">
<value>[a-zA-Z]{2}[0-9]+(?=$)</value>
</setting>
<setting name="ArtistUrlXpath" serializeAs="String">
<value>//*[@class='song-artist']/a/@href</value>
</setting>
<setting name="ArtistUrIdRegex" serializeAs="String">
<value>[a-zA-Z]{2}[0-9]+(?=$)</value>
</setting>
<setting name="SongUrlIdXpath" serializeAs="String">
<value>//link[@rel='canonical']/@href</value>
</setting>
<setting name="ArtistXpath" serializeAs="String">
<value>//*[@class='song-artist']/a</value>
</setting>
<setting name="SongUrl" serializeAs="String">
<value>http://www.allmusic.com/song/{0}</value>
</setting>
<setting name="AlbumXpath" serializeAs="String">
<value>//*[@id='appearances']//tbody/tr</value>
</setting>
</MusicMetaTagger.Client.AllMusicGuide.Properties.SongScraper>
<MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistScaper>
<setting name="ArtistUrl" serializeAs="String">
<value>http://www.allmusic.com/artist/{0}</value>
</setting>
<setting name="DiscographyAlbumUrl" serializeAs="String">
<value>http://www.allmusic.com/artist/{0}/overview/main</value>
</setting>
<setting name="ArtistIdXpath" serializeAs="String">
<value>//*[@class='id-details']//*[contains(.,'Rovi Music ID')]/following-sibling::*[1]</value>
</setting>
<setting name="GenreXpath" serializeAs="String">
<value>//*[@class='genres']//a</value>
</setting>
<setting name="StylesXpath" serializeAs="String">
<value>//*[@class='styles']//a</value>
</setting>
<setting name="ArtistXpath" serializeAs="String">
<value>//*[@class='artist-name']/text()</value>
</setting>
</MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistScaper>
<MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistDiscographyPageScraper>
<setting name="DiscographyXpath" serializeAs="String">
<value>//table[contains(@class,'album-table')]/tbody/tr</value>
</setting>
<setting name="DiscographyAlbumUrl" serializeAs="String">
<value>http://www.allmusic.com/artist/{0}/overview/main</value>
</setting>
<setting name="DiscographyEpSinglesUrl" serializeAs="String">
<value>http://www.allmusic.com/artist/{0}/overview/singles</value>
</setting>
<setting name="DiscographyCompilationUrl" serializeAs="String">
<value>http://www.allmusic.com/artist/{0}/overview/compilations</value>
</setting>
<setting name="ReleaseTypeXpath" serializeAs="String">
<value>//*[@id="discography"]//ul[@class='sub-tabs']/li[contains(@class,'active')]/@class</value>
</setting>
<setting name="ReleaseTypeRegex" serializeAs="String">
<value>.*(?=\sactive)</value>
</setting>
<setting name="ArtistXpath" serializeAs="String">
<value>//*[@class='artist-name']/text()</value>
</setting>
<setting name="ArtistIdXpath" serializeAs="String">
<value>//*[@class='id-details']//*[contains(.,'Rovi Music ID')]/following-sibling::*[1]</value>
</setting>
</MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistDiscographyPageScraper>
<MusicMetaTagger.Client.AllMusicGuide.Properties.SongResultPageScraper>
<setting name="SongResultUrl" serializeAs="String">
<value>http://www.allmusic.com/search/ajax_results/songs/{0}</value>
</setting>
</MusicMetaTagger.Client.AllMusicGuide.Properties.SongResultPageScraper>
<MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistResultPageScraper>
<setting name="ArtistResultUrl" serializeAs="String">
<value>http://www.allmusic.com/search/ajax_results/artists/{0}</value>
</setting>
<setting name="ArtistXpath" serializeAs="String">
<value>//*[contains(@class,'right-of-image')]</value>
</setting>
</MusicMetaTagger.Client.AllMusicGuide.Properties.ArtistResultPageScraper>
<MusicMetaTagger.Client.AllMusicGuide.Properties.AlbumResultPageScraper>
<setting name="AlbumXpath" serializeAs="String">
<value>//*[contains(@class,'right-of-image')]</value>
</setting>
<setting name="AlbumResultUrl" serializeAs="String">
<value>http://www.allmusic.com/search/ajax_results/albums/{0}</value>
</setting>
</MusicMetaTagger.Client.AllMusicGuide.Properties.AlbumResultPageScraper>
</applicationSettings>
<userSettings>
<MusicMetaTagger.Client.AllMusicGuide.Properties.SongResultPageScraper>
<setting name="SongXpath" serializeAs="String">
<value>//*[contains(@class,'right-of-image')]</value>
</setting>
</MusicMetaTagger.Client.AllMusicGuide.Properties.SongResultPageScraper>
</userSettings>
</configuration>
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="HtmlAgilityPack" version="1.4.6" targetFramework="net40" />
</packages>