mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-20 21:43:33 -07:00
Merge branch 'develop' of https://github.com/mattman86/Lidarr into runraid
This commit is contained in:
commit
67714561ea
39 changed files with 1270 additions and 354 deletions
|
@ -18,7 +18,7 @@ Lidarr is a music collection manager for Usenet and BitTorrent users. It can mon
|
||||||
|
|
||||||
## Feature Requests
|
## Feature Requests
|
||||||
|
|
||||||
[](http://feathub.com/lidarr/Lidarr)
|
[](http://feathub.com/mattman86/Lidarr)
|
||||||
|
|
||||||
## Configuring Development Environment:
|
## Configuring Development Environment:
|
||||||
|
|
||||||
|
|
53
appveyor.yml
Normal file
53
appveyor.yml
Normal file
|
@ -0,0 +1,53 @@
|
||||||
|
version: '0.2.0.{build}'
|
||||||
|
|
||||||
|
assembly_info:
|
||||||
|
patch: true
|
||||||
|
file: 'src\NzbDrone.Common\Properties\SharedAssemblyInfo.cs'
|
||||||
|
assembly_version: '{version}'
|
||||||
|
assembly_file_version: '{version}'
|
||||||
|
assembly_informational_version: '{version}-rc1'
|
||||||
|
|
||||||
|
environment:
|
||||||
|
DOTNET_CLI_TELEMETRY_OPTOUT: 1
|
||||||
|
|
||||||
|
install:
|
||||||
|
- git submodule update --init --recursive
|
||||||
|
|
||||||
|
build_script:
|
||||||
|
- ps: ./build-appveyor.ps1
|
||||||
|
|
||||||
|
# test: off
|
||||||
|
test:
|
||||||
|
assemblies:
|
||||||
|
- '_tests\*Test.dll'
|
||||||
|
categories:
|
||||||
|
except:
|
||||||
|
- IntegrationTest
|
||||||
|
- AutomationTest
|
||||||
|
|
||||||
|
artifacts:
|
||||||
|
- path: '_artifacts\*.zip'
|
||||||
|
- path: '_artifacts\*.exe'
|
||||||
|
- path: '_artifacts\*.tar.gz'
|
||||||
|
|
||||||
|
cache:
|
||||||
|
- '%USERPROFILE%\.nuget\packages'
|
||||||
|
- node_modules
|
||||||
|
|
||||||
|
pull_requests:
|
||||||
|
do_not_increment_build_number: true
|
||||||
|
|
||||||
|
on_failure:
|
||||||
|
- ps: Get-ChildItem .\_artifacts\*.zip | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||||
|
- ps: Get-ChildItem .\_artifacts\*.exe | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||||
|
- ps: Get-ChildItem .\_artifacts\*.tar.gz | % { Push-AppveyorArtifact $_.FullName -FileName $_.Name }
|
||||||
|
|
||||||
|
only_commits:
|
||||||
|
files:
|
||||||
|
- src/
|
||||||
|
- osx/
|
||||||
|
- gulp/
|
||||||
|
- logo/
|
||||||
|
- setup/
|
||||||
|
- appveyor.yml
|
||||||
|
- build-appveyor.cake
|
313
build-appveyor.cake
Normal file
313
build-appveyor.cake
Normal file
|
@ -0,0 +1,313 @@
|
||||||
|
#addin "Cake.Npm"
|
||||||
|
#addin "SharpZipLib"
|
||||||
|
#addin "Cake.Compression"
|
||||||
|
|
||||||
|
// Build variables
|
||||||
|
var outputFolder = "./_output";
|
||||||
|
var outputFolderMono = outputFolder + "_mono";
|
||||||
|
var outputFolderOsx = outputFolder + "_osx";
|
||||||
|
var outputFolderOsxApp = outputFolderOsx + "_app";
|
||||||
|
var testPackageFolder = "./_tests";
|
||||||
|
var testSearchPattern = "*.Test/bin/x86/Release";
|
||||||
|
var sourceFolder = "./src";
|
||||||
|
var solutionFile = sourceFolder + "/NzbDrone.sln";
|
||||||
|
var updateFolder = outputFolder + "/NzbDrone.Update";
|
||||||
|
var updateFolderMono = outputFolderMono + "/NzbDrone.Update";
|
||||||
|
|
||||||
|
// Artifact variables
|
||||||
|
var artifactsFolder = "./_artifacts";
|
||||||
|
var artifactsFolderWindows = artifactsFolder + "/windows";
|
||||||
|
var artifactsFolderLinux = artifactsFolder + "/linux";
|
||||||
|
var artifactsFolderOsx = artifactsFolder + "/osx";
|
||||||
|
var artifactsFolderOsxApp = artifactsFolder + "/osx-app";
|
||||||
|
|
||||||
|
// Utility methods
|
||||||
|
public void RemoveEmptyFolders(string startLocation) {
|
||||||
|
foreach (var directory in System.IO.Directory.GetDirectories(startLocation))
|
||||||
|
{
|
||||||
|
RemoveEmptyFolders(directory);
|
||||||
|
|
||||||
|
if (System.IO.Directory.GetFiles(directory).Length == 0 &&
|
||||||
|
System.IO.Directory.GetDirectories(directory).Length == 0)
|
||||||
|
{
|
||||||
|
DeleteDirectory(directory, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CleanFolder(string path, bool keepConfigFiles) {
|
||||||
|
DeleteFiles(path + "/**/*.transform");
|
||||||
|
|
||||||
|
if (!keepConfigFiles) {
|
||||||
|
DeleteFiles(path + "/**/*.dll.config");
|
||||||
|
}
|
||||||
|
|
||||||
|
DeleteFiles(path + "/**/FluentValidation.resources.dll");
|
||||||
|
DeleteFiles(path + "/**/App.config");
|
||||||
|
|
||||||
|
DeleteFiles(path + "/**/*.less");
|
||||||
|
|
||||||
|
DeleteFiles(path + "/**/*.vshost.exe");
|
||||||
|
|
||||||
|
DeleteFiles(path + "/**/*.dylib");
|
||||||
|
|
||||||
|
RemoveEmptyFolders(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void CreateMdbs(string path) {
|
||||||
|
foreach (var file in System.IO.Directory.EnumerateFiles(path, "*.pdb", System.IO.SearchOption.AllDirectories)) {
|
||||||
|
var actualFile = file.Substring(0, file.Length - 4);
|
||||||
|
|
||||||
|
if (FileExists(actualFile + ".exe")) {
|
||||||
|
StartProcess("./tools/pdb2mdb/pdb2mdb.exe", new ProcessSettings()
|
||||||
|
.WithArguments(args => args.Append(actualFile + ".exe")));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (FileExists(actualFile + ".dll")) {
|
||||||
|
StartProcess("./tools/pdb2mdb/pdb2mdb.exe", new ProcessSettings()
|
||||||
|
.WithArguments(args => args.Append(actualFile + ".dll")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Build Tasks
|
||||||
|
Task("Compile").Does(() => {
|
||||||
|
// Build
|
||||||
|
if (DirectoryExists(outputFolder)) {
|
||||||
|
DeleteDirectory(outputFolder, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
MSBuild(solutionFile, config =>
|
||||||
|
config.UseToolVersion(MSBuildToolVersion.VS2015)
|
||||||
|
.WithTarget("Clean")
|
||||||
|
.SetVerbosity(Verbosity.Minimal));
|
||||||
|
|
||||||
|
NuGetRestore(solutionFile);
|
||||||
|
|
||||||
|
MSBuild(solutionFile, config =>
|
||||||
|
config.UseToolVersion(MSBuildToolVersion.VS2015)
|
||||||
|
.SetPlatformTarget(PlatformTarget.x86)
|
||||||
|
.SetConfiguration("Release")
|
||||||
|
.WithProperty("AllowedReferenceRelatedFileExtensions", new string[] { ".pdb" })
|
||||||
|
.WithTarget("Build")
|
||||||
|
.SetVerbosity(Verbosity.Minimal));
|
||||||
|
|
||||||
|
CleanFolder(outputFolder, false);
|
||||||
|
|
||||||
|
// Add JsonNet
|
||||||
|
DeleteFiles(outputFolder + "/Newtonsoft.Json.*");
|
||||||
|
CopyFiles(sourceFolder + "/packages/Newtonsoft.Json.*/lib/net35/*.dll", outputFolder);
|
||||||
|
CopyFiles(sourceFolder + "/packages/Newtonsoft.Json.*/lib/net35/*.dll", updateFolder);
|
||||||
|
|
||||||
|
// Remove Mono stuff
|
||||||
|
DeleteFile(outputFolder + "/Mono.Posix.dll");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("Gulp").Does(() => {
|
||||||
|
NpmInstall(new NpmInstallSettings {
|
||||||
|
LogLevel = NpmLogLevel.Silent,
|
||||||
|
WorkingDirectory = "./",
|
||||||
|
Production = true
|
||||||
|
});
|
||||||
|
|
||||||
|
NpmRunScript("build");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("PackageMono").Does(() => {
|
||||||
|
// Start mono package
|
||||||
|
if (DirectoryExists(outputFolderMono)) {
|
||||||
|
DeleteDirectory(outputFolderMono, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyDirectory(outputFolder, outputFolderMono);
|
||||||
|
|
||||||
|
// Create MDBs
|
||||||
|
CreateMdbs(outputFolderMono);
|
||||||
|
|
||||||
|
// Remove PDBs
|
||||||
|
DeleteFiles(outputFolderMono + "/**/*.pdb");
|
||||||
|
|
||||||
|
// Remove service helpers
|
||||||
|
DeleteFiles(outputFolderMono + "/ServiceUninstall.*");
|
||||||
|
DeleteFiles(outputFolderMono + "/ServiceInstall.*");
|
||||||
|
|
||||||
|
// Remove native windows binaries
|
||||||
|
DeleteFiles(outputFolderMono + "/sqlite3.*");
|
||||||
|
DeleteFiles(outputFolderMono + "/MediaInfo.*");
|
||||||
|
|
||||||
|
// Adding NzbDrone.Core.dll.config (for dllmap)
|
||||||
|
CopyFile(sourceFolder + "/NzbDrone.Core/NzbDrone.Core.dll.config", outputFolderMono + "/NzbDrone.Core.dll.config");
|
||||||
|
|
||||||
|
// Adding CurlSharp.dll.config (for dllmap)
|
||||||
|
CopyFile(sourceFolder + "/NzbDrone.Common/CurlSharp.dll.config", outputFolderMono + "/CurlSharp.dll.config");
|
||||||
|
|
||||||
|
// Renaming Lidarr.Console.exe to Lidarr.exe
|
||||||
|
DeleteFiles(outputFolderMono + "/Lidarr.exe*");
|
||||||
|
MoveFile(outputFolderMono + "/Lidarr.Console.exe", outputFolderMono + "/Lidarr.exe");
|
||||||
|
MoveFile(outputFolderMono + "/Lidarr.Console.exe.config", outputFolderMono + "/Lidarr.exe.config");
|
||||||
|
MoveFile(outputFolderMono + "/Lidarr.Console.exe.mdb", outputFolderMono + "/Lidarr.exe.mdb");
|
||||||
|
|
||||||
|
// Remove NzbDrone.Windows.*
|
||||||
|
DeleteFiles(outputFolderMono + "/NzbDrone.Windows.*");
|
||||||
|
|
||||||
|
// Adding NzbDrone.Mono to updatePackage
|
||||||
|
CopyFiles(outputFolderMono + "/NzbDrone.Mono.*", updateFolderMono);
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("PackageOsx").Does(() => {
|
||||||
|
// Start osx package
|
||||||
|
if (DirectoryExists(outputFolderOsx)) {
|
||||||
|
DeleteDirectory(outputFolderOsx, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CopyDirectory(outputFolderMono, outputFolderOsx);
|
||||||
|
|
||||||
|
// Adding sqlite dylibs
|
||||||
|
CopyFiles(sourceFolder + "/Libraries/Sqlite/*.dylib", outputFolderOsx);
|
||||||
|
|
||||||
|
// Adding MediaInfo dylib
|
||||||
|
CopyFiles(sourceFolder + "/Libraries/MediaInfo/*.dylib", outputFolderOsx);
|
||||||
|
|
||||||
|
// Adding Startup script
|
||||||
|
CopyFile("./osx/Lidarr", outputFolderOsx + "/Lidarr");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("PackageOsxApp").Does(() => {
|
||||||
|
// Start osx app package
|
||||||
|
if (DirectoryExists(outputFolderOsxApp)) {
|
||||||
|
DeleteDirectory(outputFolderOsxApp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateDirectory(outputFolderOsxApp);
|
||||||
|
|
||||||
|
// Copy osx package files
|
||||||
|
CopyDirectory("./osx/Lidarr.app", outputFolderOsxApp + "/Lidarr.app");
|
||||||
|
CopyDirectory(outputFolderOsx, outputFolderOsxApp + "/Lidarr.app/Contents/MacOS");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("PackageTests").Does(() => {
|
||||||
|
// Start tests package
|
||||||
|
if (DirectoryExists(testPackageFolder)) {
|
||||||
|
DeleteDirectory(testPackageFolder, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateDirectory(testPackageFolder);
|
||||||
|
|
||||||
|
// Copy tests
|
||||||
|
CopyFiles(sourceFolder + "/" + testSearchPattern + "/*", testPackageFolder);
|
||||||
|
foreach (var directory in System.IO.Directory.GetDirectories(sourceFolder, "*.Test")) {
|
||||||
|
var releaseDirectory = directory + "/bin/x86/Release";
|
||||||
|
if (DirectoryExists(releaseDirectory)) {
|
||||||
|
foreach (var releaseSubDirectory in System.IO.Directory.GetDirectories(releaseDirectory)) {
|
||||||
|
Information(System.IO.Path.GetDirectoryName(releaseSubDirectory));
|
||||||
|
CopyDirectory(releaseSubDirectory, testPackageFolder + "/" + System.IO.Path.GetFileName(releaseSubDirectory));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Install NUnit.ConsoleRunner
|
||||||
|
NuGetInstall("NUnit.ConsoleRunner", new NuGetInstallSettings {
|
||||||
|
Version = "3.2.0",
|
||||||
|
OutputDirectory = testPackageFolder
|
||||||
|
});
|
||||||
|
|
||||||
|
// Copy dlls
|
||||||
|
CopyFiles(outputFolder + "/*.dll", testPackageFolder);
|
||||||
|
|
||||||
|
// Copy scripts
|
||||||
|
CopyFiles("./*.sh", testPackageFolder);
|
||||||
|
|
||||||
|
// Create MDBs for tests
|
||||||
|
CreateMdbs(testPackageFolder);
|
||||||
|
|
||||||
|
// Remove config
|
||||||
|
DeleteFiles(testPackageFolder + "/*.log.config");
|
||||||
|
|
||||||
|
// Clean
|
||||||
|
CleanFolder(testPackageFolder, true);
|
||||||
|
|
||||||
|
// Adding NzbDrone.Core.dll.config (for dllmap)
|
||||||
|
CopyFile(sourceFolder + "/NzbDrone.Core/NzbDrone.Core.dll.config", testPackageFolder + "/NzbDrone.Core.dll.config");
|
||||||
|
|
||||||
|
// Adding CurlSharp.dll.config (for dllmap)
|
||||||
|
CopyFile(sourceFolder + "/NzbDrone.Common/CurlSharp.dll.config", testPackageFolder + "/CurlSharp.dll.config");
|
||||||
|
|
||||||
|
// Adding CurlSharp libraries
|
||||||
|
CopyFiles(sourceFolder + "/ExternalModules/CurlSharp/libs/i386/*", testPackageFolder);
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("CleanupWindowsPackage").Does(() => {
|
||||||
|
// Remove mono
|
||||||
|
DeleteFiles(outputFolder + "/NzbDrone.Mono.*");
|
||||||
|
|
||||||
|
// Adding NzbDrone.Windows to updatePackage
|
||||||
|
CopyFiles(outputFolder + "/NzbDrone.Windows.*", updateFolder);
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("Build")
|
||||||
|
.IsDependentOn("Compile")
|
||||||
|
.IsDependentOn("Gulp")
|
||||||
|
.IsDependentOn("PackageMono")
|
||||||
|
.IsDependentOn("PackageOsx")
|
||||||
|
.IsDependentOn("PackageOsxApp")
|
||||||
|
.IsDependentOn("PackageTests")
|
||||||
|
.IsDependentOn("CleanupWindowsPackage");
|
||||||
|
|
||||||
|
// Build Artifacts
|
||||||
|
Task("CleanArtifacts").Does(() => {
|
||||||
|
if (DirectoryExists(artifactsFolder)) {
|
||||||
|
DeleteDirectory(artifactsFolder, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
CreateDirectory(artifactsFolder);
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("ArtifactsWindows").Does(() => {
|
||||||
|
CopyDirectory(outputFolder, artifactsFolderWindows + "/Lidarr");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("ArtifactsWindowsInstaller").Does(() => {
|
||||||
|
InnoSetup("./setup/nzbdrone.iss", new InnoSetupSettings {
|
||||||
|
OutputDirectory = artifactsFolder,
|
||||||
|
ToolPath = "./setup/inno/ISCC.exe"
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("ArtifactsLinux").Does(() => {
|
||||||
|
CopyDirectory(outputFolderMono, artifactsFolderLinux + "/Lidarr");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("ArtifactsOsx").Does(() => {
|
||||||
|
CopyDirectory(outputFolderOsx, artifactsFolderOsx + "/Lidarr");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("ArtifactsOsxApp").Does(() => {
|
||||||
|
CopyDirectory(outputFolderOsxApp, artifactsFolderOsxApp);
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("CompressArtifacts").Does(() => {
|
||||||
|
var prefix = "";
|
||||||
|
|
||||||
|
if (AppVeyor.IsRunningOnAppVeyor) {
|
||||||
|
prefix += AppVeyor.Environment.Repository.Branch.Replace("/", "-") + ".";
|
||||||
|
prefix += AppVeyor.Environment.Build.Version + ".";
|
||||||
|
}
|
||||||
|
|
||||||
|
Zip(artifactsFolderWindows, artifactsFolder + "/Lidarr." + prefix + "windows.zip");
|
||||||
|
GZipCompress(artifactsFolderLinux, artifactsFolder + "/Lidarr." + prefix + "linux.tar.gz");
|
||||||
|
GZipCompress(artifactsFolderOsx, artifactsFolder + "/Lidarr." + prefix + "osx.tar.gz");
|
||||||
|
Zip(artifactsFolderOsxApp, artifactsFolder + "/Lidarr." + prefix + "osx-app.zip");
|
||||||
|
});
|
||||||
|
|
||||||
|
Task("Artifacts")
|
||||||
|
.IsDependentOn("CleanArtifacts")
|
||||||
|
.IsDependentOn("ArtifactsWindows")
|
||||||
|
.IsDependentOn("ArtifactsWindowsInstaller")
|
||||||
|
.IsDependentOn("ArtifactsLinux")
|
||||||
|
.IsDependentOn("ArtifactsOsx")
|
||||||
|
.IsDependentOn("ArtifactsOsxApp")
|
||||||
|
.IsDependentOn("CompressArtifacts");
|
||||||
|
|
||||||
|
// Run
|
||||||
|
RunTarget("Build");
|
||||||
|
RunTarget("Artifacts");
|
184
build-appveyor.ps1
Normal file
184
build-appveyor.ps1
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
##########################################################################
|
||||||
|
# This is the Cake bootstrapper script for PowerShell.
|
||||||
|
# This file was downloaded from https://github.com/cake-build/resources
|
||||||
|
# Feel free to change this file to fit your needs.
|
||||||
|
##########################################################################
|
||||||
|
|
||||||
|
<#
|
||||||
|
.SYNOPSIS
|
||||||
|
This is a Powershell script to bootstrap a Cake build.
|
||||||
|
.DESCRIPTION
|
||||||
|
This Powershell script will download NuGet if missing, restore NuGet tools (including Cake)
|
||||||
|
and execute your Cake build script with the parameters you provide.
|
||||||
|
.PARAMETER Script
|
||||||
|
The build script to execute.
|
||||||
|
.PARAMETER Target
|
||||||
|
The build script target to run.
|
||||||
|
.PARAMETER Configuration
|
||||||
|
The build configuration to use.
|
||||||
|
.PARAMETER Verbosity
|
||||||
|
Specifies the amount of information to be displayed.
|
||||||
|
.PARAMETER Experimental
|
||||||
|
Tells Cake to use the latest Roslyn release.
|
||||||
|
.PARAMETER WhatIf
|
||||||
|
Performs a dry run of the build script.
|
||||||
|
No tasks will be executed.
|
||||||
|
.PARAMETER Mono
|
||||||
|
Tells Cake to use the Mono scripting engine.
|
||||||
|
.PARAMETER SkipToolPackageRestore
|
||||||
|
Skips restoring of packages.
|
||||||
|
.PARAMETER ScriptArgs
|
||||||
|
Remaining arguments are added here.
|
||||||
|
.LINK
|
||||||
|
http://cakebuild.net
|
||||||
|
#>
|
||||||
|
|
||||||
|
[CmdletBinding()]
|
||||||
|
Param(
|
||||||
|
[string]$Script = "build-appveyor.cake",
|
||||||
|
[string]$Target = "Default",
|
||||||
|
[ValidateSet("Release", "Debug")]
|
||||||
|
[string]$Configuration = "Release",
|
||||||
|
[ValidateSet("Quiet", "Minimal", "Normal", "Verbose", "Diagnostic")]
|
||||||
|
[string]$Verbosity = "Verbose",
|
||||||
|
[switch]$Experimental,
|
||||||
|
[Alias("DryRun","Noop")]
|
||||||
|
[switch]$WhatIf,
|
||||||
|
[switch]$Mono,
|
||||||
|
[switch]$SkipToolPackageRestore,
|
||||||
|
[Parameter(Position=0,Mandatory=$false,ValueFromRemainingArguments=$true)]
|
||||||
|
[string[]]$ScriptArgs
|
||||||
|
)
|
||||||
|
|
||||||
|
[Reflection.Assembly]::LoadWithPartialName("System.Security") | Out-Null
|
||||||
|
function MD5HashFile([string] $filePath)
|
||||||
|
{
|
||||||
|
if ([string]::IsNullOrEmpty($filePath) -or !(Test-Path $filePath -PathType Leaf))
|
||||||
|
{
|
||||||
|
return $null
|
||||||
|
}
|
||||||
|
|
||||||
|
[System.IO.Stream] $file = $null;
|
||||||
|
[System.Security.Cryptography.MD5] $md5 = $null;
|
||||||
|
try
|
||||||
|
{
|
||||||
|
$md5 = [System.Security.Cryptography.MD5]::Create()
|
||||||
|
$file = [System.IO.File]::OpenRead($filePath)
|
||||||
|
return [System.BitConverter]::ToString($md5.ComputeHash($file))
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
if ($file -ne $null)
|
||||||
|
{
|
||||||
|
$file.Dispose()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Host "Preparing to run build script..."
|
||||||
|
|
||||||
|
if(!$PSScriptRoot){
|
||||||
|
$PSScriptRoot = Split-Path $MyInvocation.MyCommand.Path -Parent
|
||||||
|
}
|
||||||
|
|
||||||
|
$TOOLS_DIR = Join-Path $PSScriptRoot "tools-cake"
|
||||||
|
$NUGET_EXE = Join-Path $TOOLS_DIR "nuget.exe"
|
||||||
|
$CAKE_EXE = Join-Path $TOOLS_DIR "Cake/Cake.exe"
|
||||||
|
$NUGET_URL = "https://dist.nuget.org/win-x86-commandline/latest/nuget.exe"
|
||||||
|
$PACKAGES_CONFIG = Join-Path $TOOLS_DIR "packages.config"
|
||||||
|
$PACKAGES_CONFIG_MD5 = Join-Path $TOOLS_DIR "packages.config.md5sum"
|
||||||
|
|
||||||
|
# Should we use mono?
|
||||||
|
$UseMono = "";
|
||||||
|
if($Mono.IsPresent) {
|
||||||
|
Write-Verbose -Message "Using the Mono based scripting engine."
|
||||||
|
$UseMono = "-mono"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Should we use the new Roslyn?
|
||||||
|
$UseExperimental = "";
|
||||||
|
if($Experimental.IsPresent -and !($Mono.IsPresent)) {
|
||||||
|
Write-Verbose -Message "Using experimental version of Roslyn."
|
||||||
|
$UseExperimental = "-experimental"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Is this a dry run?
|
||||||
|
$UseDryRun = "";
|
||||||
|
if($WhatIf.IsPresent) {
|
||||||
|
$UseDryRun = "-dryrun"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make sure tools folder exists
|
||||||
|
if ((Test-Path $PSScriptRoot) -and !(Test-Path $TOOLS_DIR)) {
|
||||||
|
Write-Verbose -Message "Creating tools directory..."
|
||||||
|
New-Item -Path $TOOLS_DIR -Type directory | out-null
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make sure that packages.config exist.
|
||||||
|
if (!(Test-Path $PACKAGES_CONFIG)) {
|
||||||
|
Write-Verbose -Message "Downloading packages.config..."
|
||||||
|
try { (New-Object System.Net.WebClient).DownloadFile("http://cakebuild.net/download/bootstrapper/packages", $PACKAGES_CONFIG) } catch {
|
||||||
|
Throw "Could not download packages.config."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Try find NuGet.exe in path if not exists
|
||||||
|
if (!(Test-Path $NUGET_EXE)) {
|
||||||
|
Write-Verbose -Message "Trying to find nuget.exe in PATH..."
|
||||||
|
$existingPaths = $Env:Path -Split ';' | Where-Object { (![string]::IsNullOrEmpty($_)) -and (Test-Path $_) }
|
||||||
|
$NUGET_EXE_IN_PATH = Get-ChildItem -Path $existingPaths -Filter "nuget.exe" | Select -First 1
|
||||||
|
if ($NUGET_EXE_IN_PATH -ne $null -and (Test-Path $NUGET_EXE_IN_PATH.FullName)) {
|
||||||
|
Write-Verbose -Message "Found in PATH at $($NUGET_EXE_IN_PATH.FullName)."
|
||||||
|
$NUGET_EXE = $NUGET_EXE_IN_PATH.FullName
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Try download NuGet.exe if not exists
|
||||||
|
if (!(Test-Path $NUGET_EXE)) {
|
||||||
|
Write-Verbose -Message "Downloading NuGet.exe..."
|
||||||
|
try {
|
||||||
|
(New-Object System.Net.WebClient).DownloadFile($NUGET_URL, $NUGET_EXE)
|
||||||
|
} catch {
|
||||||
|
Throw "Could not download NuGet.exe."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Save nuget.exe path to environment to be available to child processed
|
||||||
|
$ENV:NUGET_EXE = $NUGET_EXE
|
||||||
|
|
||||||
|
# Restore tools from NuGet?
|
||||||
|
if(-Not $SkipToolPackageRestore.IsPresent) {
|
||||||
|
Push-Location
|
||||||
|
Set-Location $TOOLS_DIR
|
||||||
|
|
||||||
|
# Check for changes in packages.config and remove installed tools if true.
|
||||||
|
[string] $md5Hash = MD5HashFile($PACKAGES_CONFIG)
|
||||||
|
if((!(Test-Path $PACKAGES_CONFIG_MD5)) -Or
|
||||||
|
($md5Hash -ne (Get-Content $PACKAGES_CONFIG_MD5 ))) {
|
||||||
|
Write-Verbose -Message "Missing or changed package.config hash..."
|
||||||
|
Remove-Item * -Recurse -Exclude packages.config,nuget.exe
|
||||||
|
}
|
||||||
|
|
||||||
|
Write-Verbose -Message "Restoring tools from NuGet..."
|
||||||
|
$NuGetOutput = Invoke-Expression "&`"$NUGET_EXE`" install -ExcludeVersion -OutputDirectory `"$TOOLS_DIR`""
|
||||||
|
|
||||||
|
if ($LASTEXITCODE -ne 0) {
|
||||||
|
Throw "An error occured while restoring NuGet tools."
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
$md5Hash | Out-File $PACKAGES_CONFIG_MD5 -Encoding "ASCII"
|
||||||
|
}
|
||||||
|
Write-Verbose -Message ($NuGetOutput | out-string)
|
||||||
|
Pop-Location
|
||||||
|
}
|
||||||
|
|
||||||
|
# Make sure that Cake has been installed.
|
||||||
|
if (!(Test-Path $CAKE_EXE)) {
|
||||||
|
Throw "Could not find Cake.exe at $CAKE_EXE"
|
||||||
|
}
|
||||||
|
|
||||||
|
# Start Cake
|
||||||
|
Write-Host "Running build script..."
|
||||||
|
Invoke-Expression "& `"$CAKE_EXE`" `"$Script`" -target=`"$Target`" -configuration=`"$Configuration`" -verbosity=`"$Verbosity`" $UseMono $UseDryRun $UseExperimental $ScriptArgs"
|
||||||
|
exit $LASTEXITCODE
|
6
build.sh
6
build.sh
|
@ -181,7 +181,7 @@ PackageOsx()
|
||||||
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderOsx
|
cp $sourceFolder/Libraries/MediaInfo/*.dylib $outputFolderOsx
|
||||||
|
|
||||||
echo "Adding Startup script"
|
echo "Adding Startup script"
|
||||||
cp ./osx/Sonarr $outputFolderOsx
|
cp ./osx/Lidarr $outputFolderOsx
|
||||||
|
|
||||||
echo "##teamcity[progressFinish 'Creating OS X Package']"
|
echo "##teamcity[progressFinish 'Creating OS X Package']"
|
||||||
}
|
}
|
||||||
|
@ -192,8 +192,8 @@ PackageOsxApp()
|
||||||
rm -rf $outputFolderOsxApp
|
rm -rf $outputFolderOsxApp
|
||||||
mkdir $outputFolderOsxApp
|
mkdir $outputFolderOsxApp
|
||||||
|
|
||||||
cp -r ./osx/Sonarr.app $outputFolderOsxApp
|
cp -r ./osx/Lidarr.app $outputFolderOsxApp
|
||||||
cp -r $outputFolderOsx $outputFolderOsxApp/Sonarr.app/Contents/MacOS
|
cp -r $outputFolderOsx $outputFolderOsxApp/Lidarr.app/Contents/MacOS
|
||||||
|
|
||||||
echo "##teamcity[progressFinish 'Creating OS X App Package']"
|
echo "##teamcity[progressFinish 'Creating OS X App Package']"
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,7 +8,7 @@ namespace NzbDrone.Api.Music
|
||||||
{
|
{
|
||||||
public class AlbumResource
|
public class AlbumResource
|
||||||
{
|
{
|
||||||
public int AlbumId { get; set; }
|
public string AlbumId { get; set; }
|
||||||
public string AlbumName { get; set; }
|
public string AlbumName { get; set; }
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
public int Year { get; set; }
|
public int Year { get; set; }
|
||||||
|
|
|
@ -71,7 +71,7 @@ namespace NzbDrone.Api.Music
|
||||||
|
|
||||||
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
|
PostValidator.RuleFor(s => s.Path).IsValidPath().When(s => s.RootFolderPath.IsNullOrWhiteSpace());
|
||||||
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
|
PostValidator.RuleFor(s => s.RootFolderPath).IsValidPath().When(s => s.Path.IsNullOrWhiteSpace());
|
||||||
PostValidator.RuleFor(s => s.ItunesId).GreaterThan(0).SetValidator(artistExistsValidator);
|
PostValidator.RuleFor(s => s.SpotifyId).NotEqual("").SetValidator(artistExistsValidator);
|
||||||
|
|
||||||
PutValidator.RuleFor(s => s.Path).IsValidPath();
|
PutValidator.RuleFor(s => s.Path).IsValidPath();
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,9 +19,7 @@ namespace NzbDrone.Api.Music
|
||||||
|
|
||||||
//View Only
|
//View Only
|
||||||
public string ArtistName { get; set; }
|
public string ArtistName { get; set; }
|
||||||
public int ItunesId { get; set; }
|
public string SpotifyId { get; set; }
|
||||||
//public List<AlternateTitleResource> AlternateTitles { get; set; }
|
|
||||||
//public string SortTitle { get; set; }
|
|
||||||
public string Overview { get; set; }
|
public string Overview { get; set; }
|
||||||
|
|
||||||
public int AlbumCount
|
public int AlbumCount
|
||||||
|
@ -30,7 +28,7 @@ namespace NzbDrone.Api.Music
|
||||||
{
|
{
|
||||||
if (Albums == null) return 0;
|
if (Albums == null) return 0;
|
||||||
|
|
||||||
return Albums.Where(s => s.AlbumId > 0).Count(); // TODO: CHeck this condition
|
return Albums.Where(s => s.AlbumId != "").Count(); // TODO: CHeck this condition
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +105,7 @@ namespace NzbDrone.Api.Music
|
||||||
//FirstAired = resource.FirstAired,
|
//FirstAired = resource.FirstAired,
|
||||||
//LastInfoSync = resource.LastInfoSync,
|
//LastInfoSync = resource.LastInfoSync,
|
||||||
//SeriesType = resource.SeriesType,
|
//SeriesType = resource.SeriesType,
|
||||||
ItunesId = model.ItunesId,
|
SpotifyId = model.SpotifyId,
|
||||||
ArtistSlug = model.ArtistSlug,
|
ArtistSlug = model.ArtistSlug,
|
||||||
|
|
||||||
RootFolderPath = model.RootFolderPath,
|
RootFolderPath = model.RootFolderPath,
|
||||||
|
@ -151,16 +149,8 @@ namespace NzbDrone.Api.Music
|
||||||
|
|
||||||
ArtistFolder = resource.ArtistFolder,
|
ArtistFolder = resource.ArtistFolder,
|
||||||
Monitored = resource.Monitored,
|
Monitored = resource.Monitored,
|
||||||
|
|
||||||
//UseSceneNumbering = resource.UseSceneNumbering,
|
|
||||||
//Runtime = resource.Runtime,
|
|
||||||
//TvdbId = resource.TvdbId,
|
|
||||||
//TvRageId = resource.TvRageId,
|
|
||||||
//TvMazeId = resource.TvMazeId,
|
|
||||||
//FirstAired = resource.FirstAired,
|
|
||||||
//LastInfoSync = resource.LastInfoSync,
|
//LastInfoSync = resource.LastInfoSync,
|
||||||
//SeriesType = resource.SeriesType,
|
SpotifyId = resource.SpotifyId,
|
||||||
ItunesId = resource.ItunesId,
|
|
||||||
ArtistSlug = resource.ArtistSlug,
|
ArtistSlug = resource.ArtistSlug,
|
||||||
|
|
||||||
RootFolderPath = resource.RootFolderPath,
|
RootFolderPath = resource.RootFolderPath,
|
||||||
|
|
|
@ -17,7 +17,8 @@ namespace NzbDrone.Common.Cloud
|
||||||
Services = new HttpRequestBuilder("http://services.lidarr.tv/v1/")
|
Services = new HttpRequestBuilder("http://services.lidarr.tv/v1/")
|
||||||
.CreateFactory();
|
.CreateFactory();
|
||||||
|
|
||||||
Search = new HttpRequestBuilder("https://itunes.apple.com/{route}/")
|
Search = new HttpRequestBuilder("https://api.spotify.com/{version}/{route}/") // TODO: maybe use {version}
|
||||||
|
.SetSegment("version", "v1")
|
||||||
.CreateFactory();
|
.CreateFactory();
|
||||||
|
|
||||||
InternalSearch = new HttpRequestBuilder("https://itunes.apple.com/WebObjects/MZStore.woa/wa/{route}") //viewArtist or search
|
InternalSearch = new HttpRequestBuilder("https://itunes.apple.com/WebObjects/MZStore.woa/wa/{route}") //viewArtist or search
|
||||||
|
|
|
@ -13,7 +13,7 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||||
protected override void MainDbUpgrade()
|
protected override void MainDbUpgrade()
|
||||||
{
|
{
|
||||||
Create.TableForModel("Artist")
|
Create.TableForModel("Artist")
|
||||||
.WithColumn("ItunesId").AsInt32().Unique()
|
.WithColumn("SpotifyId").AsString().Nullable().Unique()
|
||||||
.WithColumn("ArtistName").AsString().Unique()
|
.WithColumn("ArtistName").AsString().Unique()
|
||||||
.WithColumn("ArtistSlug").AsString().Nullable() //.Unique()
|
.WithColumn("ArtistSlug").AsString().Nullable() //.Unique()
|
||||||
.WithColumn("CleanTitle").AsString().Nullable() // Do we need this?
|
.WithColumn("CleanTitle").AsString().Nullable() // Do we need this?
|
||||||
|
@ -37,8 +37,8 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||||
;
|
;
|
||||||
|
|
||||||
Create.TableForModel("Albums")
|
Create.TableForModel("Albums")
|
||||||
.WithColumn("AlbumId").AsInt32()
|
.WithColumn("AlbumId").AsString().Unique()
|
||||||
.WithColumn("ArtistId").AsInt32()
|
.WithColumn("ArtistId").AsInt32() // Should this be artistId (string)
|
||||||
.WithColumn("Title").AsString()
|
.WithColumn("Title").AsString()
|
||||||
.WithColumn("Year").AsInt32()
|
.WithColumn("Year").AsInt32()
|
||||||
.WithColumn("Image").AsInt32()
|
.WithColumn("Image").AsInt32()
|
||||||
|
@ -49,12 +49,13 @@ namespace NzbDrone.Core.Datastore.Migration
|
||||||
|
|
||||||
Create.TableForModel("Tracks")
|
Create.TableForModel("Tracks")
|
||||||
.WithColumn("ItunesTrackId").AsInt32().Unique()
|
.WithColumn("ItunesTrackId").AsInt32().Unique()
|
||||||
.WithColumn("AlbumId").AsInt32()
|
.WithColumn("AlbumId").AsString()
|
||||||
.WithColumn("ArtistsId").AsString().Nullable()
|
.WithColumn("ArtistsId").AsString().Nullable()
|
||||||
.WithColumn("TrackNumber").AsInt32()
|
.WithColumn("TrackNumber").AsInt32()
|
||||||
.WithColumn("Title").AsString().Nullable()
|
.WithColumn("Title").AsString().Nullable()
|
||||||
.WithColumn("Ignored").AsBoolean().Nullable()
|
.WithColumn("Ignored").AsBoolean().Nullable()
|
||||||
.WithColumn("Explict").AsBoolean()
|
.WithColumn("Explict").AsBoolean()
|
||||||
|
.WithColumn("Monitored").AsBoolean()
|
||||||
.WithColumn("TrackExplicitName").AsString().Nullable()
|
.WithColumn("TrackExplicitName").AsString().Nullable()
|
||||||
.WithColumn("TrackCensoredName").AsString().Nullable()
|
.WithColumn("TrackCensoredName").AsString().Nullable()
|
||||||
.WithColumn("TrackFileId").AsInt32().Nullable()
|
.WithColumn("TrackFileId").AsInt32().Nullable()
|
||||||
|
|
|
@ -102,7 +102,7 @@ namespace NzbDrone.Core.Datastore
|
||||||
.Relationships.AutoMapICollectionOrComplexProperties()
|
.Relationships.AutoMapICollectionOrComplexProperties()
|
||||||
.For("Tracks")
|
.For("Tracks")
|
||||||
.LazyLoad(condition: parent => parent.Id > 0,
|
.LazyLoad(condition: parent => parent.Id > 0,
|
||||||
query: (db, parent) => db.Query<Track>().Where(c => c.ItunesTrackId == parent.Id).ToList())
|
query: (db, parent) => db.Query<Track>().Where(c => c.SpotifyTrackId == parent.Id).ToList())
|
||||||
.HasOne(file => file.Artist, file => file.AlbumId);
|
.HasOne(file => file.Artist, file => file.AlbumId);
|
||||||
|
|
||||||
Mapper.Entity<Track>().RegisterModel("Tracks")
|
Mapper.Entity<Track>().RegisterModel("Tracks")
|
||||||
|
|
|
@ -8,24 +8,24 @@ namespace NzbDrone.Core.Exceptions
|
||||||
{
|
{
|
||||||
public class ArtistNotFoundException : NzbDroneException
|
public class ArtistNotFoundException : NzbDroneException
|
||||||
{
|
{
|
||||||
public int ItunesId { get; set; }
|
public string SpotifyId { get; set; }
|
||||||
|
|
||||||
public ArtistNotFoundException(int itunesId)
|
public ArtistNotFoundException(string spotifyId)
|
||||||
: base(string.Format("Series with iTunesId {0} was not found, it may have been removed from iTunes.", itunesId))
|
: base(string.Format("Artist with SpotifyId {0} was not found, it may have been removed from Spotify.", spotifyId))
|
||||||
{
|
{
|
||||||
ItunesId = itunesId;
|
SpotifyId = spotifyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtistNotFoundException(int itunesId, string message, params object[] args)
|
public ArtistNotFoundException(string spotifyId, string message, params object[] args)
|
||||||
: base(message, args)
|
: base(message, args)
|
||||||
{
|
{
|
||||||
ItunesId = itunesId;
|
SpotifyId = spotifyId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ArtistNotFoundException(int itunesId, string message)
|
public ArtistNotFoundException(string spotifyId, string message)
|
||||||
: base(message)
|
: base(message)
|
||||||
{
|
{
|
||||||
ItunesId = itunesId;
|
SpotifyId = spotifyId;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,7 @@ using NzbDrone.Core.Messaging.Commands;
|
||||||
using NzbDrone.Core.Messaging.Events;
|
using NzbDrone.Core.Messaging.Events;
|
||||||
using NzbDrone.Core.Tv.Commands;
|
using NzbDrone.Core.Tv.Commands;
|
||||||
using NzbDrone.Core.Update.Commands;
|
using NzbDrone.Core.Update.Commands;
|
||||||
|
using NzbDrone.Core.Music.Commands;
|
||||||
|
|
||||||
namespace NzbDrone.Core.Jobs
|
namespace NzbDrone.Core.Jobs
|
||||||
{
|
{
|
||||||
|
@ -64,9 +65,10 @@ namespace NzbDrone.Core.Jobs
|
||||||
new ScheduledTask{ Interval = 1, TypeName = typeof(CheckForFinishedDownloadCommand).FullName},
|
new ScheduledTask{ Interval = 1, TypeName = typeof(CheckForFinishedDownloadCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 5, TypeName = typeof(MessagingCleanupCommand).FullName},
|
new ScheduledTask{ Interval = 5, TypeName = typeof(MessagingCleanupCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 6*60, TypeName = typeof(ApplicationUpdateCommand).FullName},
|
new ScheduledTask{ Interval = 6*60, TypeName = typeof(ApplicationUpdateCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName},
|
//new ScheduledTask{ Interval = 3*60, TypeName = typeof(UpdateSceneMappingCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 6*60, TypeName = typeof(CheckHealthCommand).FullName},
|
new ScheduledTask{ Interval = 6*60, TypeName = typeof(CheckHealthCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshSeriesCommand).FullName},
|
new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshArtistCommand).FullName},
|
||||||
|
new ScheduledTask{ Interval = 12*60, TypeName = typeof(RefreshSeriesCommand).FullName}, // TODO: Remove
|
||||||
new ScheduledTask{ Interval = 24*60, TypeName = typeof(HousekeepingCommand).FullName},
|
new ScheduledTask{ Interval = 24*60, TypeName = typeof(HousekeepingCommand).FullName},
|
||||||
new ScheduledTask{ Interval = 7*24*60, TypeName = typeof(BackupCommand).FullName},
|
new ScheduledTask{ Interval = 7*24*60, TypeName = typeof(BackupCommand).FullName},
|
||||||
|
|
||||||
|
|
|
@ -6,6 +6,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
{
|
{
|
||||||
public interface IProvideArtistInfo
|
public interface IProvideArtistInfo
|
||||||
{
|
{
|
||||||
Tuple<Artist, List<Track>> GetArtistInfo(int itunesId);
|
Tuple<Artist, List<Track>> GetArtistInfo(string spotifyId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
|
{
|
||||||
|
public class AlbumInfoResource
|
||||||
|
{
|
||||||
|
public AlbumInfoResource()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
public string AlbumType { get; set; } // Might need to make this a separate class
|
||||||
|
public List<ArtistInfoResource> Artists { get; set; } // Will always be length of 1 unless a compilation
|
||||||
|
public string Url { get; set; } // Link to the endpoint api to give full info for this object
|
||||||
|
public string Id { get; set; } // This is a unique Album ID. Needed for all future API calls
|
||||||
|
public List<ImageResource> Images { get; set; }
|
||||||
|
public string Name { get; set; } // In case of a takedown, this may be empty
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
|
{
|
||||||
|
public class ArtistInfoResource
|
||||||
|
{
|
||||||
|
public ArtistInfoResource() { }
|
||||||
|
|
||||||
|
public List<string> Genres { get; set; }
|
||||||
|
public string AristUrl { get; set; }
|
||||||
|
public string Id { get; set; }
|
||||||
|
public List<ImageResource> Images { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
// We may need external_urls.spotify to external linking...
|
||||||
|
}
|
||||||
|
}
|
|
@ -5,63 +5,36 @@ using System.Text;
|
||||||
|
|
||||||
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
{
|
{
|
||||||
public class StorePlatformDataResource
|
|
||||||
|
public class AristResultResource
|
||||||
{
|
{
|
||||||
public StorePlatformDataResource() { }
|
public AristResultResource()
|
||||||
public ArtistInfoResource Artist { get; set; }
|
|
||||||
//public Lockup lockup { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ArtistInfoResource
|
|
||||||
{
|
|
||||||
public ArtistInfoResource() { }
|
|
||||||
public Dictionary<int, ArtistInfoResource> Results { get; set; }
|
|
||||||
|
|
||||||
public bool HasArtistBio { get; set; }
|
|
||||||
|
|
||||||
public string url { get; set; }
|
|
||||||
public string shortUrl { get; set; }
|
|
||||||
|
|
||||||
public List<string> artistContemporaries { get; set; }
|
|
||||||
public List<string> genreNames { get; set; }
|
|
||||||
public bool hasSocialPosts { get; set; }
|
|
||||||
public string artistBio { get; set; }
|
|
||||||
public bool isGroup { get; set; }
|
|
||||||
public string id { get; set; }
|
|
||||||
public string bornOrFormed { get; set; }
|
|
||||||
public string name { get; set; }
|
|
||||||
public string latestAlbumContentId { get; set; }
|
|
||||||
public string nameRaw { get; set; }
|
|
||||||
|
|
||||||
//public string kind { get; set; }
|
|
||||||
//public List<Gallery> gallery { get; set; }
|
|
||||||
//public List<Genre> genres { get; set; }
|
|
||||||
public List<object> artistInfluencers { get; set; }
|
|
||||||
public List<object> artistFollowers { get; set; }
|
|
||||||
//public string umcArtistImageUrl { get; set; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class AlbumResource
|
|
||||||
{
|
|
||||||
public AlbumResource()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public string ArtistName { get; set; }
|
public List<ArtistInfoResource> Items { get; set; }
|
||||||
public int ArtistId { get; set; }
|
}
|
||||||
public string CollectionName { get; set; }
|
|
||||||
public int CollectionId { get; set; }
|
public class AlbumResultResource
|
||||||
public string PrimaryGenreName { get; set; }
|
{
|
||||||
public string ArtworkUrl100 { get; set; }
|
public AlbumResultResource()
|
||||||
public string Country { get; set; }
|
{
|
||||||
public string CollectionExplicitness { get; set; }
|
|
||||||
public int TrackCount { get; set; }
|
|
||||||
public string Copyright { get; set; }
|
|
||||||
public DateTime ReleaseDate { get; set; }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<AlbumInfoResource> Items { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TrackResultResource
|
||||||
|
{
|
||||||
|
public TrackResultResource()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<TrackInfoResource> Items { get; set; }
|
||||||
|
}
|
||||||
public class ArtistResource
|
public class ArtistResource
|
||||||
{
|
{
|
||||||
public ArtistResource()
|
public ArtistResource()
|
||||||
|
@ -69,10 +42,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ResultCount { get; set; }
|
public AristResultResource Artists { get; set; }
|
||||||
public List<AlbumResource> Results { get; set; }
|
public AristResultResource Albums { get; set; }
|
||||||
//public string ArtistName { get; set; }
|
|
||||||
//public List<AlbumResource> Albums { get; set; }
|
|
||||||
public StorePlatformDataResource StorePlatformData { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,10 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
public class ImageResource
|
public class ImageResource
|
||||||
{
|
{
|
||||||
public string CoverType { get; set; }
|
public string CoverType { get; set; }
|
||||||
|
|
||||||
|
// Spotify Mapping
|
||||||
public string Url { get; set; }
|
public string Url { get; set; }
|
||||||
|
public int Height { get; set; }
|
||||||
|
public int Width { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||||
|
{
|
||||||
|
public class TrackInfoResource
|
||||||
|
{
|
||||||
|
public TrackInfoResource()
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public int DiscNumber { get; set; }
|
||||||
|
public int DurationMs { get; set; }
|
||||||
|
public string Href { get; set; }
|
||||||
|
public string Id { get; set; }
|
||||||
|
public string Name { get; set; }
|
||||||
|
public int TrackNumber { get; set; }
|
||||||
|
public bool Explicit { get; set; }
|
||||||
|
public List<ArtistInfoResource> Artists { get; set; }
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,16 +22,16 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
private readonly Logger _logger;
|
private readonly Logger _logger;
|
||||||
|
|
||||||
private readonly IHttpRequestBuilderFactory _requestBuilder;
|
private readonly IHttpRequestBuilderFactory _requestBuilder;
|
||||||
private readonly IHttpRequestBuilderFactory _internalRequestBuilder;
|
|
||||||
|
|
||||||
public SkyHookProxy(IHttpClient httpClient, ILidarrCloudRequestBuilder requestBuilder, Logger logger)
|
public SkyHookProxy(IHttpClient httpClient, ILidarrCloudRequestBuilder requestBuilder, Logger logger)
|
||||||
{
|
{
|
||||||
_httpClient = httpClient;
|
_httpClient = httpClient;
|
||||||
_requestBuilder = requestBuilder.Search;
|
_requestBuilder = requestBuilder.Search;
|
||||||
_internalRequestBuilder = requestBuilder.InternalSearch;
|
|
||||||
_logger = logger;
|
_logger = logger;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public Tuple<Series, List<Episode>> GetSeriesInfo(int tvdbSeriesId)
|
public Tuple<Series, List<Episode>> GetSeriesInfo(int tvdbSeriesId)
|
||||||
{
|
{
|
||||||
Console.WriteLine("[GetSeriesInfo] id:" + tvdbSeriesId);
|
Console.WriteLine("[GetSeriesInfo] id:" + tvdbSeriesId);
|
||||||
|
@ -65,150 +65,136 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
|
|
||||||
public List<Series> SearchForNewSeries(string title)
|
public List<Series> SearchForNewSeries(string title)
|
||||||
{
|
{
|
||||||
try
|
// TODO: Remove this API
|
||||||
{
|
|
||||||
var lowerTitle = title.ToLowerInvariant();
|
|
||||||
Console.WriteLine("Searching for " + lowerTitle);
|
|
||||||
|
|
||||||
//if (lowerTitle.StartsWith("tvdb:") || lowerTitle.StartsWith("tvdbid:"))
|
|
||||||
//{
|
|
||||||
// var slug = lowerTitle.Split(':')[1].Trim();
|
|
||||||
|
|
||||||
// int tvdbId;
|
|
||||||
|
|
||||||
// if (slug.IsNullOrWhiteSpace() || slug.Any(char.IsWhiteSpace) || !int.TryParse(slug, out tvdbId) || tvdbId <= 0)
|
|
||||||
// {
|
|
||||||
// return new List<Series>();
|
|
||||||
// }
|
|
||||||
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// return new List<Series> { GetSeriesInfo(tvdbId).Item1 };
|
|
||||||
// }
|
|
||||||
// catch (SeriesNotFoundException)
|
|
||||||
// {
|
|
||||||
// return new List<Series>();
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
// Majora: Temporarily, use iTunes to test.
|
|
||||||
var httpRequest = _requestBuilder.Create()
|
|
||||||
.AddQueryParam("entity", "album")
|
|
||||||
.AddQueryParam("term", title.ToLower().Trim())
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Console.WriteLine("httpRequest: ", httpRequest);
|
|
||||||
|
|
||||||
var httpResponse = _httpClient.Get<List<ShowResource>>(httpRequest);
|
|
||||||
|
|
||||||
//Console.WriteLine("Response: ", httpResponse.GetType());
|
|
||||||
//_logger.Info("Response: ", httpResponse.Resource.ResultCount);
|
|
||||||
|
|
||||||
//_logger.Info("HTTP Response: ", httpResponse.Resource.ResultCount);
|
|
||||||
var tempList = new List<Series>();
|
var tempList = new List<Series>();
|
||||||
var tempSeries = new Series();
|
var tempSeries = new Series();
|
||||||
tempSeries.Title = "AFI";
|
tempSeries.Title = "AFI";
|
||||||
tempList.Add(tempSeries);
|
tempList.Add(tempSeries);
|
||||||
return tempList;
|
return tempList;
|
||||||
|
|
||||||
return httpResponse.Resource.SelectList(MapSeries);
|
|
||||||
}
|
}
|
||||||
catch (HttpException)
|
|
||||||
|
|
||||||
|
public Tuple<Artist, List<Track>> GetArtistInfo(string spotifyId)
|
||||||
{
|
{
|
||||||
throw new SkyHookException("Search for '{0}' failed. Unable to communicate with SkyHook.", title);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
_logger.Warn(ex, ex.Message);
|
|
||||||
throw new SkyHookException("Search for '{0}' failed. Invalid response received from SkyHook.", title);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//public Artist GetArtistInfo(int itunesId)
|
_logger.Debug("Getting Artist with SpotifyId of {0}", spotifyId);
|
||||||
//{
|
|
||||||
// Console.WriteLine("[GetArtistInfo] id:" + itunesId);
|
|
||||||
// //https://itunes.apple.com/lookup?id=909253
|
|
||||||
// //var httpRequest = _requestBuilder.Create()
|
|
||||||
// // .SetSegment("route", "lookup")
|
|
||||||
// // .AddQueryParam("id", itunesId.ToString())
|
|
||||||
// // .Build();
|
|
||||||
|
|
||||||
// // TODO: Add special header, add Overview to Artist model
|
///v1/albums/{id}
|
||||||
// var httpRequest = _requestBuilder.Create()
|
//
|
||||||
// .SetSegment("route", "viewArtist")
|
|
||||||
// .AddQueryParam("id", itunesId.ToString())
|
|
||||||
// .Build();
|
|
||||||
// httpRequest.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3");
|
|
||||||
|
|
||||||
// httpRequest.AllowAutoRedirect = true;
|
// We need to perform a direct lookup of the artist
|
||||||
// httpRequest.SuppressHttpError = true;
|
var httpRequest = _requestBuilder.Create()
|
||||||
|
.SetSegment("route", "artists/" + spotifyId)
|
||||||
// var httpResponse = _httpClient.Get<ArtistResource>(httpRequest);
|
//.SetSegment("route", "search")
|
||||||
|
//.AddQueryParam("type", "artist,album")
|
||||||
// if (httpResponse.HasHttpError)
|
//.AddQueryParam("q", spotifyId.ToString())
|
||||||
// {
|
|
||||||
// if (httpResponse.StatusCode == HttpStatusCode.NotFound)
|
|
||||||
// {
|
|
||||||
// throw new ArtistNotFoundException(itunesId);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// throw new HttpException(httpRequest, httpResponse);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Console.WriteLine("GetArtistInfo, GetArtistInfo");
|
|
||||||
// return MapArtists(httpResponse.Resource)[0];
|
|
||||||
//}
|
|
||||||
|
|
||||||
public Tuple<Artist, List<Track>> GetArtistInfo(int itunesId)
|
|
||||||
{
|
|
||||||
_logger.Debug("Getting Artist with iTunesID of {0}", itunesId);
|
|
||||||
var httpRequest1 = _requestBuilder.Create()
|
|
||||||
.SetSegment("route", "lookup")
|
|
||||||
.AddQueryParam("id", itunesId.ToString())
|
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
var httpRequest2 = _internalRequestBuilder.Create()
|
|
||||||
.SetSegment("route", "viewArtist")
|
|
||||||
.AddQueryParam("id", itunesId.ToString())
|
|
||||||
.Build();
|
|
||||||
httpRequest2.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3");
|
|
||||||
httpRequest2.Headers.ContentType = "application/json";
|
|
||||||
|
|
||||||
httpRequest1.AllowAutoRedirect = true;
|
|
||||||
httpRequest1.SuppressHttpError = true;
|
|
||||||
|
|
||||||
var httpResponse = _httpClient.Get<ArtistResource>(httpRequest1);
|
httpRequest.AllowAutoRedirect = true;
|
||||||
|
httpRequest.SuppressHttpError = true;
|
||||||
|
|
||||||
|
var httpResponse = _httpClient.Get<ArtistInfoResource>(httpRequest);
|
||||||
|
|
||||||
|
|
||||||
if (httpResponse.HasHttpError)
|
if (httpResponse.HasHttpError)
|
||||||
{
|
{
|
||||||
if (httpResponse.StatusCode == HttpStatusCode.NotFound)
|
if (httpResponse.StatusCode == HttpStatusCode.NotFound)
|
||||||
{
|
{
|
||||||
throw new ArtistNotFoundException(itunesId);
|
throw new ArtistNotFoundException(spotifyId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
throw new HttpException(httpRequest1, httpResponse);
|
throw new HttpException(httpRequest, httpResponse);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
List<Artist> artists = MapArtists(httpResponse.Resource);
|
Artist artist = new Artist();
|
||||||
List<Artist> newArtists = new List<Artist>(artists.Count);
|
artist.ArtistName = httpResponse.Resource.Name;
|
||||||
int count = 0;
|
artist.SpotifyId = httpResponse.Resource.Id;
|
||||||
foreach (var artist in artists)
|
artist.Genres = httpResponse.Resource.Genres;
|
||||||
|
|
||||||
|
|
||||||
|
artist = MapAlbums(artist);
|
||||||
|
|
||||||
|
|
||||||
|
// TODO: implement tracks api call
|
||||||
|
return new Tuple<Artist, List<Track>>(artist, new List<Track>());
|
||||||
|
}
|
||||||
|
|
||||||
|
private Artist MapAlbums(Artist artist)
|
||||||
{
|
{
|
||||||
newArtists.Add(AddOverview(artist));
|
|
||||||
count++;
|
// Find all albums for the artist and all tracks for said album
|
||||||
|
///v1/artists/{id}/albums
|
||||||
|
var httpRequest = _requestBuilder.Create()
|
||||||
|
.SetSegment("route", "artists/" + artist.SpotifyId + "/albums")
|
||||||
|
.Build();
|
||||||
|
httpRequest.AllowAutoRedirect = true;
|
||||||
|
httpRequest.SuppressHttpError = true;
|
||||||
|
|
||||||
|
var httpResponse = _httpClient.Get<AlbumResultResource>(httpRequest);
|
||||||
|
|
||||||
|
if (httpResponse.HasHttpError)
|
||||||
|
{
|
||||||
|
throw new HttpException(httpRequest, httpResponse);
|
||||||
}
|
}
|
||||||
|
|
||||||
// I don't know how we are getting tracks from iTunes yet.
|
List<Album> albums = new List<Album>();
|
||||||
return new Tuple<Artist, List<Track>>(newArtists[0], new List<Track>());
|
foreach(var albumResource in httpResponse.Resource.Items)
|
||||||
|
{
|
||||||
|
Album album = new Album();
|
||||||
|
album.AlbumId = albumResource.Id;
|
||||||
|
album.Title = albumResource.Name;
|
||||||
|
album.ArtworkUrl = albumResource.Images[0].Url;
|
||||||
|
album.Tracks = MapTracksToAlbum(album);
|
||||||
|
albums.Add(album);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: We now need to get all tracks for each album
|
||||||
|
|
||||||
|
artist.Albums = albums;
|
||||||
|
return artist;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Track> MapTracksToAlbum(Album album)
|
||||||
|
{
|
||||||
|
var httpRequest = _requestBuilder.Create()
|
||||||
|
.SetSegment("route", "albums/" + album.AlbumId + "/tracks")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
httpRequest.AllowAutoRedirect = true;
|
||||||
|
httpRequest.SuppressHttpError = true;
|
||||||
|
|
||||||
|
var httpResponse = _httpClient.Get<TrackResultResource>(httpRequest);
|
||||||
|
|
||||||
|
if (httpResponse.HasHttpError)
|
||||||
|
{
|
||||||
|
throw new HttpException(httpRequest, httpResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Track> tracks = new List<Track>();
|
||||||
|
foreach(var trackResource in httpResponse.Resource.Items)
|
||||||
|
{
|
||||||
|
Track track = new Track();
|
||||||
|
track.AlbumId = album.AlbumId;
|
||||||
|
//track.Album = album; // This will cause infinite loop when trying to serialize.
|
||||||
|
// TODO: Implement more track mapping
|
||||||
|
//track.Artist = trackResource.Artists
|
||||||
|
//track.ArtistId = album.
|
||||||
|
track.Explict = trackResource.Explicit;
|
||||||
|
track.Compilation = trackResource.Artists.Count > 1;
|
||||||
|
track.TrackNumber = trackResource.TrackNumber;
|
||||||
|
track.TrackExplicitName = trackResource.Name;
|
||||||
|
track.TrackCensoredName = trackResource.Name;
|
||||||
|
tracks.Add(track);
|
||||||
|
}
|
||||||
|
|
||||||
|
return tracks;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public List<Artist> SearchForNewArtist(string title)
|
public List<Artist> SearchForNewArtist(string title)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
|
@ -220,16 +206,14 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
{
|
{
|
||||||
var slug = lowerTitle.Split(':')[1].Trim();
|
var slug = lowerTitle.Split(':')[1].Trim();
|
||||||
|
|
||||||
int itunesId;
|
if (slug.IsNullOrWhiteSpace() || slug.Any(char.IsWhiteSpace))
|
||||||
|
|
||||||
if (slug.IsNullOrWhiteSpace() || slug.Any(char.IsWhiteSpace) || !int.TryParse(slug, out itunesId) || itunesId <= 0)
|
|
||||||
{
|
{
|
||||||
return new List<Artist>();
|
return new List<Artist>();
|
||||||
}
|
}
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
return new List<Artist> { GetArtistInfo(itunesId).Item1 };
|
return new List<Artist> { GetArtistInfo(slug).Item1 };
|
||||||
}
|
}
|
||||||
catch (ArtistNotFoundException)
|
catch (ArtistNotFoundException)
|
||||||
{
|
{
|
||||||
|
@ -239,8 +223,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
|
|
||||||
var httpRequest = _requestBuilder.Create()
|
var httpRequest = _requestBuilder.Create()
|
||||||
.SetSegment("route", "search")
|
.SetSegment("route", "search")
|
||||||
.AddQueryParam("entity", "album")
|
.AddQueryParam("type", "artist,album")
|
||||||
.AddQueryParam("term", title.ToLower().Trim())
|
.AddQueryParam("q", title.ToLower().Trim())
|
||||||
.Build();
|
.Build();
|
||||||
|
|
||||||
|
|
||||||
|
@ -249,16 +233,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
|
|
||||||
|
|
||||||
List<Artist> artists = MapArtists(httpResponse.Resource);
|
List<Artist> artists = MapArtists(httpResponse.Resource);
|
||||||
List<Artist> newArtists = new List<Artist>(artists.Count);
|
|
||||||
int count = 0;
|
|
||||||
foreach (var artist in artists)
|
|
||||||
{
|
|
||||||
newArtists.Add(AddOverview(artist));
|
|
||||||
count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
return artists;
|
||||||
return newArtists;
|
|
||||||
}
|
}
|
||||||
catch (HttpException)
|
catch (HttpException)
|
||||||
{
|
{
|
||||||
|
@ -271,77 +247,52 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Artist AddOverview(Artist artist)
|
|
||||||
{
|
|
||||||
var httpRequest = _internalRequestBuilder.Create()
|
|
||||||
.SetSegment("route", "viewArtist")
|
|
||||||
.AddQueryParam("id", artist.ItunesId.ToString())
|
|
||||||
.Build();
|
|
||||||
httpRequest.Headers.Add("X-Apple-Store-Front", "143459-2,32 t:music3");
|
|
||||||
httpRequest.Headers.ContentType = "application/json";
|
|
||||||
var httpResponse = _httpClient.Get<ArtistResource>(httpRequest);
|
|
||||||
|
|
||||||
if (!httpResponse.HasHttpError)
|
|
||||||
{
|
|
||||||
artist.Overview = httpResponse.Resource.StorePlatformData.Artist.Results[artist.ItunesId].artistBio;
|
|
||||||
}
|
|
||||||
|
|
||||||
return artist;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Artist MapArtistInfo(ArtistInfoResource resource)
|
private Artist MapArtistInfo(ArtistInfoResource resource)
|
||||||
{
|
{
|
||||||
// This expects ArtistInfoResource, thus just need to populate one artist
|
// This expects ArtistInfoResource, thus just need to populate one artist
|
||||||
Artist artist = new Artist();
|
Artist artist = new Artist();
|
||||||
artist.Overview = resource.artistBio;
|
//artist.Overview = resource.artistBio;
|
||||||
artist.ArtistName = resource.name;
|
//artist.ArtistName = resource.name;
|
||||||
foreach(var genre in resource.genreNames)
|
//foreach(var genre in resource.genreNames)
|
||||||
{
|
//{
|
||||||
artist.Genres.Add(genre);
|
// artist.Genres.Add(genre);
|
||||||
}
|
//}
|
||||||
|
|
||||||
return artist;
|
return artist;
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<Artist> MapArtists(ArtistResource resource)
|
private List<Artist> MapArtists(ArtistResource resource)
|
||||||
{
|
{
|
||||||
Album tempAlbum;
|
|
||||||
|
|
||||||
List<Artist> artists = new List<Artist>();
|
List<Artist> artists = new List<Artist>();
|
||||||
foreach (var album in resource.Results)
|
foreach(var artistResource in resource.Artists.Items)
|
||||||
{
|
{
|
||||||
int index = artists.FindIndex(a => a.ItunesId == album.ArtistId);
|
Artist artist = new Artist();
|
||||||
tempAlbum = MapAlbum(album);
|
artist.ArtistName = artistResource.Name;
|
||||||
|
artist.SpotifyId = artistResource.Id;
|
||||||
if (index >= 0)
|
artist.Genres = artistResource.Genres;
|
||||||
{
|
//artist.ArtistSlug = a//TODO implement artistSlug mapping;
|
||||||
artists[index].Albums.Add(tempAlbum);
|
artists.Add(artist);
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Artist tempArtist = new Artist();
|
|
||||||
tempArtist.ItunesId = album.ArtistId;
|
|
||||||
tempArtist.ArtistName = album.ArtistName;
|
|
||||||
tempArtist.Genres.Add(album.PrimaryGenreName);
|
|
||||||
tempArtist.Albums.Add(tempAlbum);
|
|
||||||
artists.Add(tempArtist);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
// Maybe? Get all the albums for said artist
|
||||||
|
|
||||||
|
|
||||||
return artists;
|
return artists;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Album MapAlbum(AlbumResource albumQuery)
|
//private Album MapAlbum(AlbumResource albumQuery)
|
||||||
{
|
//{
|
||||||
Album album = new Album();
|
// Album album = new Album();
|
||||||
|
|
||||||
album.AlbumId = albumQuery.CollectionId;
|
// album.AlbumId = albumQuery.CollectionId;
|
||||||
album.Title = albumQuery.CollectionName;
|
// album.Title = albumQuery.CollectionName;
|
||||||
album.Year = albumQuery.ReleaseDate.Year;
|
// album.Year = albumQuery.ReleaseDate.Year;
|
||||||
album.ArtworkUrl = albumQuery.ArtworkUrl100;
|
// album.ArtworkUrl = albumQuery.ArtworkUrl100;
|
||||||
album.Explicitness = albumQuery.CollectionExplicitness;
|
// album.Explicitness = albumQuery.CollectionExplicitness;
|
||||||
return album;
|
// return album;
|
||||||
}
|
//}
|
||||||
|
|
||||||
private static Series MapSeries(ShowResource show)
|
private static Series MapSeries(ShowResource show)
|
||||||
{
|
{
|
||||||
|
|
|
@ -48,7 +48,7 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(newArtist.Path))
|
if (string.IsNullOrWhiteSpace(newArtist.Path))
|
||||||
{
|
{
|
||||||
var folderName = newArtist.ArtistName;// _fileNameBuilder.GetArtistFolder(newArtist);
|
var folderName = newArtist.ArtistName;// TODO: _fileNameBuilder.GetArtistFolder(newArtist);
|
||||||
newArtist.Path = Path.Combine(newArtist.RootFolderPath, folderName);
|
newArtist.Path = Path.Combine(newArtist.RootFolderPath, folderName);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -63,7 +63,7 @@ namespace NzbDrone.Core.Music
|
||||||
throw new ValidationException(validationResult.Errors);
|
throw new ValidationException(validationResult.Errors);
|
||||||
}
|
}
|
||||||
|
|
||||||
_logger.Info("Adding Series {0} Path: [{1}]", newArtist, newArtist.Path);
|
_logger.Info("Adding Artist {0} Path: [{1}]", newArtist, newArtist.Path);
|
||||||
_artistService.AddArtist(newArtist);
|
_artistService.AddArtist(newArtist);
|
||||||
|
|
||||||
return newArtist;
|
return newArtist;
|
||||||
|
@ -75,15 +75,15 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
tuple = _artistInfo.GetArtistInfo(newArtist.ItunesId);
|
tuple = _artistInfo.GetArtistInfo(newArtist.SpotifyId);
|
||||||
}
|
}
|
||||||
catch (SeriesNotFoundException)
|
catch (SeriesNotFoundException)
|
||||||
{
|
{
|
||||||
_logger.Error("iTunesId {1} was not found, it may have been removed from iTunes.", newArtist.ItunesId);
|
_logger.Error("SpotifyId {1} was not found, it may have been removed from Spotify.", newArtist.SpotifyId);
|
||||||
|
|
||||||
throw new ValidationException(new List<ValidationFailure>
|
throw new ValidationException(new List<ValidationFailure>
|
||||||
{
|
{
|
||||||
new ValidationFailure("iTunesId", "An artist with this ID was not found", newArtist.ItunesId)
|
new ValidationFailure("SpotifyId", "An artist with this ID was not found", newArtist.SpotifyId)
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -19,7 +19,7 @@ namespace NzbDrone.Core.Music
|
||||||
SeriesPathValidator seriesPathValidator,
|
SeriesPathValidator seriesPathValidator,
|
||||||
DroneFactoryValidator droneFactoryValidator,
|
DroneFactoryValidator droneFactoryValidator,
|
||||||
SeriesAncestorValidator seriesAncestorValidator,
|
SeriesAncestorValidator seriesAncestorValidator,
|
||||||
ArtistSlugValidator seriesTitleSlugValidator)
|
ArtistSlugValidator artistTitleSlugValidator)
|
||||||
{
|
{
|
||||||
RuleFor(c => c.Path).Cascade(CascadeMode.StopOnFirstFailure)
|
RuleFor(c => c.Path).Cascade(CascadeMode.StopOnFirstFailure)
|
||||||
.IsValidPath()
|
.IsValidPath()
|
||||||
|
@ -28,7 +28,7 @@ namespace NzbDrone.Core.Music
|
||||||
.SetValidator(droneFactoryValidator)
|
.SetValidator(droneFactoryValidator)
|
||||||
.SetValidator(seriesAncestorValidator);
|
.SetValidator(seriesAncestorValidator);
|
||||||
|
|
||||||
RuleFor(c => c.ArtistSlug).SetValidator(seriesTitleSlugValidator);// TODO: Check if we are going to use a slug or artistName
|
RuleFor(c => c.ArtistSlug).SetValidator(artistTitleSlugValidator);// TODO: Check if we are going to use a slug or artistName
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,10 +14,11 @@ namespace NzbDrone.Core.Music
|
||||||
Images = new List<MediaCover.MediaCover>();
|
Images = new List<MediaCover.MediaCover>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int AlbumId { get; set; }
|
public string AlbumId { get; set; }
|
||||||
public string Title { get; set; } // NOTE: This should be CollectionName in API
|
public string Title { get; set; } // NOTE: This should be CollectionName in API
|
||||||
public int Year { get; set; }
|
public int Year { get; set; }
|
||||||
public int TrackCount { get; set; }
|
public int TrackCount { get; set; }
|
||||||
|
public List<Track> Tracks { get; set; }
|
||||||
public int DiscCount { get; set; }
|
public int DiscCount { get; set; }
|
||||||
public bool Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
public List<MediaCover.MediaCover> Images { get; set; }
|
public List<MediaCover.MediaCover> Images { get; set; }
|
||||||
|
|
|
@ -22,7 +22,7 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int ItunesId { get; set; }
|
public string SpotifyId { get; set; }
|
||||||
public string ArtistName { get; set; }
|
public string ArtistName { get; set; }
|
||||||
public string ArtistSlug { get; set; }
|
public string ArtistSlug { get; set; }
|
||||||
public string CleanTitle { get; set; }
|
public string CleanTitle { get; set; }
|
||||||
|
@ -32,44 +32,28 @@ namespace NzbDrone.Core.Music
|
||||||
public bool ArtistFolder { get; set; }
|
public bool ArtistFolder { get; set; }
|
||||||
public DateTime? LastInfoSync { get; set; }
|
public DateTime? LastInfoSync { get; set; }
|
||||||
public DateTime? LastDiskSync { get; set; }
|
public DateTime? LastDiskSync { get; set; }
|
||||||
|
|
||||||
public int Status { get; set; } // TODO: Figure out what this is, do we need it?
|
public int Status { get; set; } // TODO: Figure out what this is, do we need it?
|
||||||
public string Path { get; set; }
|
public string Path { get; set; }
|
||||||
public List<MediaCover.MediaCover> Images { get; set; }
|
public List<MediaCover.MediaCover> Images { get; set; }
|
||||||
public List<string> Genres { get; set; }
|
public List<string> Genres { get; set; }
|
||||||
public int QualityProfileId { get; set; }
|
public int QualityProfileId { get; set; }
|
||||||
|
|
||||||
public string RootFolderPath { get; set; }
|
public string RootFolderPath { get; set; }
|
||||||
public DateTime Added { get; set; }
|
public DateTime Added { get; set; }
|
||||||
public LazyLoaded<Profile> Profile { get; set; }
|
public LazyLoaded<Profile> Profile { get; set; }
|
||||||
public int ProfileId { get; set; }
|
public int ProfileId { get; set; }
|
||||||
public List<Album> Albums { get; set; }
|
public List<Album> Albums { get; set; }
|
||||||
public HashSet<int> Tags { get; set; }
|
public HashSet<int> Tags { get; set; }
|
||||||
|
|
||||||
public AddSeriesOptions AddOptions { get; set; }
|
public AddSeriesOptions AddOptions { get; set; }
|
||||||
|
|
||||||
//public string SortTitle { get; set; }
|
|
||||||
//public SeriesStatusType Status { get; set; }
|
|
||||||
//public int Runtime { get; set; }
|
|
||||||
//public SeriesTypes SeriesType { get; set; }
|
|
||||||
//public string Network { get; set; }
|
|
||||||
//public bool UseSceneNumbering { get; set; }
|
|
||||||
//public string TitleSlug { get; set; }
|
|
||||||
//public int Year { get; set; }
|
|
||||||
//public Ratings Ratings { get; set; }
|
|
||||||
//public List<Actor> Actors { get; set; } // MOve to album?
|
|
||||||
//public string Certification { get; set; }
|
|
||||||
//public DateTime? FirstAired { get; set; }
|
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return string.Format("[{0}][{1}]", ItunesId, ArtistName.NullSafe());
|
return string.Format("[{0}][{1}]", SpotifyId, ArtistName.NullSafe());
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyChanges(Artist otherArtist)
|
public void ApplyChanges(Artist otherArtist)
|
||||||
{
|
{
|
||||||
|
|
||||||
ItunesId = otherArtist.ItunesId;
|
SpotifyId = otherArtist.SpotifyId;
|
||||||
ArtistName = otherArtist.ArtistName;
|
ArtistName = otherArtist.ArtistName;
|
||||||
ArtistSlug = otherArtist.ArtistSlug;
|
ArtistSlug = otherArtist.ArtistSlug;
|
||||||
CleanTitle = otherArtist.CleanTitle;
|
CleanTitle = otherArtist.CleanTitle;
|
||||||
|
@ -88,18 +72,11 @@ namespace NzbDrone.Core.Music
|
||||||
ArtistFolder = otherArtist.ArtistFolder;
|
ArtistFolder = otherArtist.ArtistFolder;
|
||||||
AddOptions = otherArtist.AddOptions;
|
AddOptions = otherArtist.AddOptions;
|
||||||
|
|
||||||
|
|
||||||
//TODO: Implement
|
|
||||||
ItunesId = otherArtist.ItunesId;
|
|
||||||
|
|
||||||
Albums = otherArtist.Albums;
|
Albums = otherArtist.Albums;
|
||||||
Path = otherArtist.Path;
|
Path = otherArtist.Path;
|
||||||
ProfileId = otherArtist.ProfileId;
|
ProfileId = otherArtist.ProfileId;
|
||||||
|
|
||||||
AlbumFolder = otherArtist.AlbumFolder;
|
AlbumFolder = otherArtist.AlbumFolder;
|
||||||
Monitored = otherArtist.Monitored;
|
Monitored = otherArtist.Monitored;
|
||||||
|
|
||||||
//SeriesType = otherArtist.SeriesType;
|
|
||||||
RootFolderPath = otherArtist.RootFolderPath;
|
RootFolderPath = otherArtist.RootFolderPath;
|
||||||
Tags = otherArtist.Tags;
|
Tags = otherArtist.Tags;
|
||||||
AddOptions = otherArtist.AddOptions;
|
AddOptions = otherArtist.AddOptions;
|
||||||
|
|
26
src/NzbDrone.Core/Music/ArtistAddedHandler.cs
Normal file
26
src/NzbDrone.Core/Music/ArtistAddedHandler.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.Music.Commands;
|
||||||
|
using NzbDrone.Core.Music.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music
|
||||||
|
{
|
||||||
|
public class ArtistAddedHandler : IHandle<ArtistAddedEvent>
|
||||||
|
{
|
||||||
|
private readonly IManageCommandQueue _commandQueueManager;
|
||||||
|
|
||||||
|
public ArtistAddedHandler(IManageCommandQueue commandQueueManager)
|
||||||
|
{
|
||||||
|
_commandQueueManager = commandQueueManager;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Handle(ArtistAddedEvent message)
|
||||||
|
{
|
||||||
|
_commandQueueManager.Push(new RefreshArtistCommand(message.Artist.Id));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ namespace NzbDrone.Core.Music
|
||||||
{
|
{
|
||||||
bool ArtistPathExists(string path);
|
bool ArtistPathExists(string path);
|
||||||
Artist FindByName(string cleanTitle);
|
Artist FindByName(string cleanTitle);
|
||||||
Artist FindByItunesId(int iTunesId);
|
Artist FindById(string spotifyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class ArtistRepository : BasicRepository<Artist>, IArtistRepository
|
public class ArtistRepository : BasicRepository<Artist>, IArtistRepository
|
||||||
|
@ -24,9 +24,9 @@ namespace NzbDrone.Core.Music
|
||||||
return Query.Where(c => c.Path == path).Any();
|
return Query.Where(c => c.Path == path).Any();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Artist FindByItunesId(int iTunesId)
|
public Artist FindById(string spotifyId)
|
||||||
{
|
{
|
||||||
return Query.Where(s => s.ItunesId == iTunesId).SingleOrDefault();
|
return Query.Where(s => s.SpotifyId == spotifyId).SingleOrDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Artist FindByName(string cleanName)
|
public Artist FindByName(string cleanName)
|
||||||
|
|
|
@ -17,7 +17,7 @@ namespace NzbDrone.Core.Music
|
||||||
Artist GetArtist(int artistId);
|
Artist GetArtist(int artistId);
|
||||||
List<Artist> GetArtists(IEnumerable<int> artistIds);
|
List<Artist> GetArtists(IEnumerable<int> artistIds);
|
||||||
Artist AddArtist(Artist newArtist);
|
Artist AddArtist(Artist newArtist);
|
||||||
Artist FindByItunesId(int itunesId);
|
Artist FindById(string spotifyId);
|
||||||
Artist FindByName(string title);
|
Artist FindByName(string title);
|
||||||
Artist FindByTitleInexact(string title);
|
Artist FindByTitleInexact(string title);
|
||||||
void DeleteArtist(int artistId, bool deleteFiles);
|
void DeleteArtist(int artistId, bool deleteFiles);
|
||||||
|
@ -69,9 +69,9 @@ namespace NzbDrone.Core.Music
|
||||||
_eventAggregator.PublishEvent(new ArtistDeletedEvent(artist, deleteFiles));
|
_eventAggregator.PublishEvent(new ArtistDeletedEvent(artist, deleteFiles));
|
||||||
}
|
}
|
||||||
|
|
||||||
public Artist FindByItunesId(int itunesId)
|
public Artist FindById(string spotifyId)
|
||||||
{
|
{
|
||||||
return _artistRepository.FindByItunesId(itunesId);
|
return _artistRepository.FindById(spotifyId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Artist FindByName(string title)
|
public Artist FindByName(string title)
|
||||||
|
@ -114,7 +114,7 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
if (storedAlbum != null && album.Monitored != storedAlbum.Monitored)
|
if (storedAlbum != null && album.Monitored != storedAlbum.Monitored)
|
||||||
{
|
{
|
||||||
_trackService.SetTrackMonitoredByAlbum(artist.Id, album.AlbumId, album.Monitored);
|
_trackService.SetTrackMonitoredByAlbum(artist.SpotifyId, album.AlbumId, album.Monitored);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
26
src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs
Normal file
26
src/NzbDrone.Core/Music/Commands/RefreshArtistCommand.cs
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music.Commands
|
||||||
|
{
|
||||||
|
public class RefreshArtistCommand : Command
|
||||||
|
{
|
||||||
|
public int? ArtistId { get; set; }
|
||||||
|
|
||||||
|
public RefreshArtistCommand()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public RefreshArtistCommand(int? artistId)
|
||||||
|
{
|
||||||
|
ArtistId = artistId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool SendUpdatesToClient => true;
|
||||||
|
|
||||||
|
public override bool UpdateScheduledTask => !ArtistId.HasValue;
|
||||||
|
}
|
||||||
|
}
|
18
src/NzbDrone.Core/Music/Events/ArtistRefreshStartingEvent.cs
Normal file
18
src/NzbDrone.Core/Music/Events/ArtistRefreshStartingEvent.cs
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
using NzbDrone.Common.Messaging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music.Events
|
||||||
|
{
|
||||||
|
public class ArtistRefreshStartingEvent : IEvent
|
||||||
|
{
|
||||||
|
public bool ManualTrigger { get; set; }
|
||||||
|
|
||||||
|
public ArtistRefreshStartingEvent(bool manualTrigger)
|
||||||
|
{
|
||||||
|
ManualTrigger = manualTrigger;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
23
src/NzbDrone.Core/Music/Events/TrackInfoRefreshedEvent.cs
Normal file
23
src/NzbDrone.Core/Music/Events/TrackInfoRefreshedEvent.cs
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
using NzbDrone.Common.Messaging;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.ObjectModel;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music.Events
|
||||||
|
{
|
||||||
|
public class TrackInfoRefreshedEvent : IEvent
|
||||||
|
{
|
||||||
|
public Artist Artist { get; set; }
|
||||||
|
public ReadOnlyCollection<Track> Added { get; private set; }
|
||||||
|
public ReadOnlyCollection<Track> Updated { get; private set; }
|
||||||
|
|
||||||
|
public TrackInfoRefreshedEvent(Artist artist, IList<Track> added, IList<Track> updated)
|
||||||
|
{
|
||||||
|
Artist = artist;
|
||||||
|
Added = new ReadOnlyCollection<Track>(added);
|
||||||
|
Updated = new ReadOnlyCollection<Track>(updated);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
173
src/NzbDrone.Core/Music/RefreshArtistService.cs
Normal file
173
src/NzbDrone.Core/Music/RefreshArtistService.cs
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Common.Instrumentation.Extensions;
|
||||||
|
using NzbDrone.Core.Exceptions;
|
||||||
|
using NzbDrone.Core.MediaFiles;
|
||||||
|
using NzbDrone.Core.Messaging.Commands;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.MetadataSource.SkyHook;
|
||||||
|
using NzbDrone.Core.Music.Commands;
|
||||||
|
using NzbDrone.Core.Music.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.IO;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music
|
||||||
|
{
|
||||||
|
public class RefreshArtistService : IExecute<RefreshArtistCommand>
|
||||||
|
{
|
||||||
|
private readonly IProvideArtistInfo _artistInfo;
|
||||||
|
private readonly IArtistService _artistService;
|
||||||
|
private readonly IRefreshTrackService _refreshTrackService;
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
//private readonly IDailySeriesService _dailySeriesService;
|
||||||
|
private readonly IDiskScanService _diskScanService;
|
||||||
|
//private readonly ICheckIfArtistShouldBeRefreshed _checkIfArtistShouldBeRefreshed;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public RefreshArtistService(IProvideArtistInfo artistInfo,
|
||||||
|
IArtistService artistService,
|
||||||
|
IRefreshTrackService refreshTrackService,
|
||||||
|
IEventAggregator eventAggregator,
|
||||||
|
IDiskScanService diskScanService,
|
||||||
|
//ICheckIfArtistShouldBeRefreshed checkIfArtistShouldBeRefreshed,
|
||||||
|
Logger logger)
|
||||||
|
{
|
||||||
|
_artistInfo = artistInfo;
|
||||||
|
_artistService = artistService;
|
||||||
|
_refreshTrackService = refreshTrackService;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
_diskScanService = diskScanService;
|
||||||
|
//_checkIfArtistShouldBeRefreshed = checkIfArtistShouldBeRefreshed;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RefreshArtistInfo(Artist artist)
|
||||||
|
{
|
||||||
|
_logger.ProgressInfo("Updating Info for {0}", artist.ArtistName);
|
||||||
|
|
||||||
|
Tuple<Artist, List<Track>> tuple;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
tuple = _artistInfo.GetArtistInfo(artist.SpotifyId);
|
||||||
|
}
|
||||||
|
catch (ArtistNotFoundException)
|
||||||
|
{
|
||||||
|
_logger.Error("Artist '{0}' (SpotifyId {1}) was not found, it may have been removed from Spotify.", artist.ArtistName, artist.SpotifyId);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var artistInfo = tuple.Item1;
|
||||||
|
|
||||||
|
if (artist.SpotifyId != artistInfo.SpotifyId)
|
||||||
|
{
|
||||||
|
_logger.Warn("Artist '{0}' (SpotifyId {1}) was replaced with '{2}' (SpotifyId {3}), because the original was a duplicate.", artist.ArtistName, artist.SpotifyId, artistInfo.ArtistName, artistInfo.SpotifyId);
|
||||||
|
artist.SpotifyId = artistInfo.SpotifyId;
|
||||||
|
}
|
||||||
|
|
||||||
|
artist.ArtistName = artistInfo.ArtistName;
|
||||||
|
artist.ArtistSlug = artistInfo.ArtistSlug;
|
||||||
|
artist.Overview = artistInfo.Overview;
|
||||||
|
artist.Status = artistInfo.Status;
|
||||||
|
artist.CleanTitle = artistInfo.CleanTitle;
|
||||||
|
artist.LastInfoSync = DateTime.UtcNow;
|
||||||
|
artist.Images = artistInfo.Images;
|
||||||
|
//artist.Actors = artistInfo.Actors;
|
||||||
|
artist.Genres = artistInfo.Genres;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
artist.Path = new DirectoryInfo(artist.Path).FullName;
|
||||||
|
artist.Path = artist.Path.GetActualCasing();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Warn(e, "Couldn't update artist path for " + artist.Path);
|
||||||
|
}
|
||||||
|
|
||||||
|
artist.Albums = UpdateAlbums(artist, artistInfo);
|
||||||
|
|
||||||
|
_artistService.UpdateArtist(artist);
|
||||||
|
_refreshTrackService.RefreshTrackInfo(artist, tuple.Item2);
|
||||||
|
|
||||||
|
_logger.Debug("Finished artist refresh for {0}", artist.ArtistName);
|
||||||
|
_eventAggregator.PublishEvent(new ArtistUpdatedEvent(artist));
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<Album> UpdateAlbums(Artist artist, Artist artistInfo)
|
||||||
|
{
|
||||||
|
var albums = artistInfo.Albums.DistinctBy(s => s.AlbumId).ToList();
|
||||||
|
|
||||||
|
foreach (var album in albums)
|
||||||
|
{
|
||||||
|
var existingAlbum = artist.Albums.FirstOrDefault(s => s.AlbumId == album.AlbumId);
|
||||||
|
|
||||||
|
//Todo: Should this should use the previous season's monitored state?
|
||||||
|
if (existingAlbum == null)
|
||||||
|
{
|
||||||
|
//if (album.SeasonNumber == 0)
|
||||||
|
//{
|
||||||
|
// album.Monitored = false;
|
||||||
|
// continue;
|
||||||
|
//}
|
||||||
|
|
||||||
|
_logger.Debug("New album ({0}) for artist: [{1}] {2}, setting monitored to true", album.Title, artist.SpotifyId, artist.ArtistName);
|
||||||
|
album.Monitored = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
album.Monitored = existingAlbum.Monitored;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return albums;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Execute(RefreshArtistCommand message)
|
||||||
|
{
|
||||||
|
_eventAggregator.PublishEvent(new ArtistRefreshStartingEvent(message.Trigger == CommandTrigger.Manual));
|
||||||
|
|
||||||
|
if (message.ArtistId.HasValue)
|
||||||
|
{
|
||||||
|
var artist = _artistService.GetArtist(message.ArtistId.Value);
|
||||||
|
RefreshArtistInfo(artist);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var allArtists = _artistService.GetAllArtists().OrderBy(c => c.ArtistName).ToList();
|
||||||
|
|
||||||
|
foreach (var artist in allArtists)
|
||||||
|
{
|
||||||
|
if (message.Trigger == CommandTrigger.Manual /*|| _checkIfArtistShouldBeRefreshed.ShouldRefresh(artist)*/)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
RefreshArtistInfo(artist);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Error(e, "Couldn't refresh info for {0}", artist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
_logger.Info("Skipping refresh of artist: {0}", artist.ArtistName);
|
||||||
|
//TODO: _diskScanService.Scan(artist);
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Error(e, "Couldn't rescan artist {0}", artist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
125
src/NzbDrone.Core/Music/RefreshTrackService.cs
Normal file
125
src/NzbDrone.Core/Music/RefreshTrackService.cs
Normal file
|
@ -0,0 +1,125 @@
|
||||||
|
using NLog;
|
||||||
|
using NzbDrone.Common.Extensions;
|
||||||
|
using NzbDrone.Core.Messaging.Events;
|
||||||
|
using NzbDrone.Core.Music.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace NzbDrone.Core.Music
|
||||||
|
{
|
||||||
|
public interface IRefreshTrackService
|
||||||
|
{
|
||||||
|
void RefreshTrackInfo(Artist artist, IEnumerable<Track> remoteTracks);
|
||||||
|
}
|
||||||
|
|
||||||
|
public class RefreshTrackService : IRefreshTrackService
|
||||||
|
{
|
||||||
|
private readonly ITrackService _trackService;
|
||||||
|
private readonly IEventAggregator _eventAggregator;
|
||||||
|
private readonly Logger _logger;
|
||||||
|
|
||||||
|
public RefreshTrackService(ITrackService trackService, IEventAggregator eventAggregator, Logger logger)
|
||||||
|
{
|
||||||
|
_trackService = trackService;
|
||||||
|
_eventAggregator = eventAggregator;
|
||||||
|
_logger = logger;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RefreshTrackInfo(Artist artist, IEnumerable<Track> remoteTracks)
|
||||||
|
{
|
||||||
|
_logger.Info("Starting track info refresh for: {0}", artist);
|
||||||
|
var successCount = 0;
|
||||||
|
var failCount = 0;
|
||||||
|
|
||||||
|
var existingTracks = _trackService.GetTrackByArtist(artist.SpotifyId);
|
||||||
|
var albums = artist.Albums;
|
||||||
|
|
||||||
|
var updateList = new List<Track>();
|
||||||
|
var newList = new List<Track>();
|
||||||
|
var dupeFreeRemoteTracks = remoteTracks.DistinctBy(m => new { m.AlbumId, m.TrackNumber }).ToList();
|
||||||
|
|
||||||
|
foreach (var track in OrderTracks(artist, dupeFreeRemoteTracks))
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var trackToUpdate = GetTrackToUpdate(artist, track, existingTracks);
|
||||||
|
|
||||||
|
if (trackToUpdate != null)
|
||||||
|
{
|
||||||
|
existingTracks.Remove(trackToUpdate);
|
||||||
|
updateList.Add(trackToUpdate);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
trackToUpdate = new Track();
|
||||||
|
trackToUpdate.Monitored = GetMonitoredStatus(track, albums);
|
||||||
|
newList.Add(trackToUpdate);
|
||||||
|
}
|
||||||
|
trackToUpdate.ArtistId = artist.SpotifyId; // TODO: Ensure LazyLoaded<Artist> field gets updated.
|
||||||
|
trackToUpdate.TrackNumber = track.TrackNumber;
|
||||||
|
trackToUpdate.Title = track.Title ?? "Unknown";
|
||||||
|
|
||||||
|
// TODO: Implement rest of [RefreshTrackService] fields
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
successCount++;
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
_logger.Fatal(e, "An error has occurred while updating track info for artist {0}. {1}", artist, track);
|
||||||
|
failCount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var allTracks = new List<Track>();
|
||||||
|
allTracks.AddRange(newList);
|
||||||
|
allTracks.AddRange(updateList);
|
||||||
|
|
||||||
|
// TODO: See if anything needs to be done here
|
||||||
|
//AdjustMultiEpisodeAirTime(artist, allTracks);
|
||||||
|
//AdjustDirectToDvdAirDate(artist, allTracks);
|
||||||
|
|
||||||
|
_trackService.DeleteMany(existingTracks);
|
||||||
|
_trackService.UpdateMany(updateList);
|
||||||
|
_trackService.InsertMany(newList);
|
||||||
|
|
||||||
|
_eventAggregator.PublishEvent(new TrackInfoRefreshedEvent(artist, newList, updateList));
|
||||||
|
|
||||||
|
if (failCount != 0)
|
||||||
|
{
|
||||||
|
_logger.Info("Finished track refresh for artist: {0}. Successful: {1} - Failed: {2} ",
|
||||||
|
artist.ArtistName, successCount, failCount);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_logger.Info("Finished track refresh for artist: {0}.", artist);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool GetMonitoredStatus(Track track, IEnumerable<Album> albums)
|
||||||
|
{
|
||||||
|
if (track.TrackNumber == 0 /*&& track.AlbumId != 1*/)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var album = albums.SingleOrDefault(c => c.AlbumId == track.AlbumId);
|
||||||
|
return album == null || album.Monitored;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private Track GetTrackToUpdate(Artist artist, Track track, List<Track> existingTracks)
|
||||||
|
{
|
||||||
|
return existingTracks.FirstOrDefault(e => e.AlbumId == track.AlbumId && e.TrackNumber == track.TrackNumber);
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<Track> OrderTracks(Artist artist, List<Track> tracks)
|
||||||
|
{
|
||||||
|
return tracks.OrderBy(e => e.AlbumId).ThenBy(e => e.TrackNumber);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -17,9 +17,10 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
public const string RELEASE_DATE_FORMAT = "yyyy-MM-dd";
|
public const string RELEASE_DATE_FORMAT = "yyyy-MM-dd";
|
||||||
|
|
||||||
public int ItunesTrackId { get; set; }
|
public int SpotifyTrackId { get; set; }
|
||||||
public int AlbumId { get; set; }
|
public string AlbumId { get; set; }
|
||||||
public LazyLoaded<Artist> ArtistsId { get; set; }
|
public LazyLoaded<Artist> Artist { get; set; }
|
||||||
|
public string ArtistId { get; set; }
|
||||||
public int CompilationId { get; set; }
|
public int CompilationId { get; set; }
|
||||||
public bool Compilation { get; set; }
|
public bool Compilation { get; set; }
|
||||||
public int TrackNumber { get; set; }
|
public int TrackNumber { get; set; }
|
||||||
|
@ -28,11 +29,10 @@ namespace NzbDrone.Core.Music
|
||||||
public bool Explict { get; set; }
|
public bool Explict { get; set; }
|
||||||
public string TrackExplicitName { get; set; }
|
public string TrackExplicitName { get; set; }
|
||||||
public string TrackCensoredName { get; set; }
|
public string TrackCensoredName { get; set; }
|
||||||
public string Monitored { get; set; }
|
public bool Monitored { get; set; }
|
||||||
public int TrackFileId { get; set; } // JVM: Is this needed with TrackFile reference?
|
public int TrackFileId { get; set; }
|
||||||
public DateTime? ReleaseDate { get; set; }
|
public DateTime? ReleaseDate { get; set; }
|
||||||
/*public int? SceneEpisodeNumber { get; set; }
|
/*
|
||||||
public bool UnverifiedSceneNumbering { get; set; }
|
|
||||||
public Ratings Ratings { get; set; } // This might be aplicable as can be pulled from IDv3 tags
|
public Ratings Ratings { get; set; } // This might be aplicable as can be pulled from IDv3 tags
|
||||||
public List<MediaCover.MediaCover> Images { get; set; }*/
|
public List<MediaCover.MediaCover> Images { get; set; }*/
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ namespace NzbDrone.Core.Music
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
return string.Format("[{0}]{1}", ItunesTrackId, Title.NullSafe());
|
return string.Format("[{0}]{1}", SpotifyTrackId, Title.NullSafe());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,12 +10,12 @@ namespace NzbDrone.Core.Music
|
||||||
{
|
{
|
||||||
Track GetTrack(int id);
|
Track GetTrack(int id);
|
||||||
List<Track> GetTracks(IEnumerable<int> ids);
|
List<Track> GetTracks(IEnumerable<int> ids);
|
||||||
Track FindTrack(int artistId, int albumId, int trackNumber);
|
Track FindTrack(string artistId, string albumId, int trackNumber);
|
||||||
Track FindTrackByTitle(int artistId, int albumId, string releaseTitle);
|
Track FindTrackByTitle(string artistId, string albumId, string releaseTitle);
|
||||||
List<Track> GetTrackByArtist(int artistId);
|
List<Track> GetTrackByArtist(string artistId);
|
||||||
List<Track> GetTracksByAblum(int artistId, int albumId);
|
List<Track> GetTracksByAlbum(string artistId, string albumId);
|
||||||
List<Track> GetTracksByAblumTitle(int artistId, string albumTitle);
|
List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle);
|
||||||
List<Track> TracksWithFiles(int artistId);
|
List<Track> TracksWithFiles(string artistId);
|
||||||
PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
||||||
List<Track> GeTracksByFileId(int trackFileId);
|
List<Track> GeTracksByFileId(int trackFileId);
|
||||||
void UpdateTrack(Track track);
|
void UpdateTrack(Track track);
|
||||||
|
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Music
|
||||||
void InsertMany(List<Track> tracks);
|
void InsertMany(List<Track> tracks);
|
||||||
void UpdateMany(List<Track> tracks);
|
void UpdateMany(List<Track> tracks);
|
||||||
void DeleteMany(List<Track> tracks);
|
void DeleteMany(List<Track> tracks);
|
||||||
void SetTrackMonitoredByAlbum(int artistId, int albumId, bool monitored);
|
void SetTrackMonitoredByAlbum(string artistId, string albumId, bool monitored);
|
||||||
}
|
}
|
||||||
|
|
||||||
public class TrackService : ITrackService
|
public class TrackService : ITrackService
|
||||||
|
@ -34,12 +34,12 @@ namespace NzbDrone.Core.Music
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Track FindTrack(int artistId, int albumId, int trackNumber)
|
public Track FindTrack(string artistId, string albumId, int trackNumber)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Track FindTrackByTitle(int artistId, int albumId, string releaseTitle)
|
public Track FindTrackByTitle(string artistId, string albumId, string releaseTitle)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -54,7 +54,7 @@ namespace NzbDrone.Core.Music
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> GetTrackByArtist(int artistId)
|
public List<Track> GetTrackByArtist(string artistId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -64,12 +64,12 @@ namespace NzbDrone.Core.Music
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> GetTracksByAblum(int artistId, int albumId)
|
public List<Track> GetTracksByAlbum(string artistId, string albumId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> GetTracksByAblumTitle(int artistId, string albumTitle)
|
public List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
@ -84,12 +84,12 @@ namespace NzbDrone.Core.Music
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetTrackMonitoredByAlbum(int artistId, int albumId, bool monitored)
|
public void SetTrackMonitoredByAlbum(string artistId, string albumId, bool monitored)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<Track> TracksWithFiles(int artistId)
|
public List<Track> TracksWithFiles(string artistId)
|
||||||
{
|
{
|
||||||
throw new NotImplementedException();
|
throw new NotImplementedException();
|
||||||
}
|
}
|
||||||
|
|
|
@ -815,6 +815,8 @@
|
||||||
<Compile Include="Messaging\IProcessMessage.cs" />
|
<Compile Include="Messaging\IProcessMessage.cs" />
|
||||||
<Compile Include="MetadataSource\IProvideArtistInfo.cs" />
|
<Compile Include="MetadataSource\IProvideArtistInfo.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ActorResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ActorResource.cs" />
|
||||||
|
<Compile Include="MetadataSource\SkyHook\Resource\AlbumInfoResource.cs" />
|
||||||
|
<Compile Include="MetadataSource\SkyHook\Resource\ArtistInfoResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ArtistResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ArtistResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\EpisodeResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\EpisodeResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ImageResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ImageResource.cs" />
|
||||||
|
@ -822,6 +824,7 @@
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\SeasonResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\SeasonResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\ShowResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\ShowResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" />
|
<Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" />
|
||||||
|
<Compile Include="MetadataSource\SkyHook\Resource\TrackInfoResource.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
|
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
|
||||||
<Compile Include="MetadataSource\SearchSeriesComparer.cs" />
|
<Compile Include="MetadataSource\SearchSeriesComparer.cs" />
|
||||||
<Compile Include="MetadataSource\SkyHook\SkyHookException.cs" />
|
<Compile Include="MetadataSource\SkyHook\SkyHookException.cs" />
|
||||||
|
@ -851,14 +854,20 @@
|
||||||
<Compile Include="Music\AddArtistValidator.cs" />
|
<Compile Include="Music\AddArtistValidator.cs" />
|
||||||
<Compile Include="Music\Album.cs" />
|
<Compile Include="Music\Album.cs" />
|
||||||
<Compile Include="Music\Artist.cs" />
|
<Compile Include="Music\Artist.cs" />
|
||||||
|
<Compile Include="Music\ArtistAddedHandler.cs" />
|
||||||
<Compile Include="Music\ArtistNameNormalizer.cs" />
|
<Compile Include="Music\ArtistNameNormalizer.cs" />
|
||||||
<Compile Include="Music\ArtistSlugValidator.cs" />
|
<Compile Include="Music\ArtistSlugValidator.cs" />
|
||||||
<Compile Include="Music\ArtistRepository.cs" />
|
<Compile Include="Music\ArtistRepository.cs" />
|
||||||
<Compile Include="Music\ArtistService.cs" />
|
<Compile Include="Music\ArtistService.cs" />
|
||||||
|
<Compile Include="Music\Commands\RefreshArtistCommand.cs" />
|
||||||
<Compile Include="Music\Events\ArtistAddedEvent.cs" />
|
<Compile Include="Music\Events\ArtistAddedEvent.cs" />
|
||||||
<Compile Include="Music\Events\ArtistDeletedEvent.cs" />
|
<Compile Include="Music\Events\ArtistDeletedEvent.cs" />
|
||||||
<Compile Include="Music\Events\ArtistEditedEvent.cs" />
|
<Compile Include="Music\Events\ArtistEditedEvent.cs" />
|
||||||
|
<Compile Include="Music\Events\ArtistRefreshStartingEvent.cs" />
|
||||||
<Compile Include="Music\Events\ArtistUpdatedEvent.cs" />
|
<Compile Include="Music\Events\ArtistUpdatedEvent.cs" />
|
||||||
|
<Compile Include="Music\Events\TrackInfoRefreshedEvent.cs" />
|
||||||
|
<Compile Include="Music\RefreshArtistService.cs" />
|
||||||
|
<Compile Include="Music\RefreshTrackService.cs" />
|
||||||
<Compile Include="Music\Track.cs" />
|
<Compile Include="Music\Track.cs" />
|
||||||
<Compile Include="Music\TrackService.cs" />
|
<Compile Include="Music\TrackService.cs" />
|
||||||
<Compile Include="Notifications\Join\JoinAuthException.cs" />
|
<Compile Include="Notifications\Join\JoinAuthException.cs" />
|
||||||
|
|
|
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Parser.Model
|
||||||
public MediaInfoModel MediaInfo { get; set; }
|
public MediaInfoModel MediaInfo { get; set; }
|
||||||
public bool ExistingFile { get; set; }
|
public bool ExistingFile { get; set; }
|
||||||
|
|
||||||
public int Album
|
public string Album
|
||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
|
@ -32,7 +32,7 @@ namespace NzbDrone.Core.Parser.Model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsSpecial => Album == 0;
|
public bool IsSpecial => Album != "";
|
||||||
|
|
||||||
public override string ToString()
|
public override string ToString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -12,7 +12,7 @@ namespace NzbDrone.Core.Validation.Paths
|
||||||
private readonly IArtistService _artistService;
|
private readonly IArtistService _artistService;
|
||||||
|
|
||||||
public ArtistExistsValidator(IArtistService artistService)
|
public ArtistExistsValidator(IArtistService artistService)
|
||||||
: base("This artist has already been added")
|
: base("This artist has already been added.")
|
||||||
{
|
{
|
||||||
_artistService = artistService;
|
_artistService = artistService;
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,7 @@ namespace NzbDrone.Core.Validation.Paths
|
||||||
{
|
{
|
||||||
if (context.PropertyValue == null) return true;
|
if (context.PropertyValue == null) return true;
|
||||||
|
|
||||||
var itunesId = Convert.ToInt32(context.PropertyValue.ToString());
|
return (!_artistService.GetAllArtists().Exists(s => s.SpotifyId == context.PropertyValue.ToString()));
|
||||||
|
|
||||||
return (!_artistService.GetAllArtists().Exists(s => s.ItunesId == itunesId));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -223,12 +223,12 @@ var view = Marionette.ItemView.extend({
|
||||||
self.close();
|
self.close();
|
||||||
|
|
||||||
Messenger.show({
|
Messenger.show({
|
||||||
message : 'Added: ' + self.model.get('title'),
|
message : 'Added: ' + self.model.get('artistName'),
|
||||||
actions : {
|
actions : {
|
||||||
goToSeries : {
|
goToSeries : {
|
||||||
label : 'Go to Artist',
|
label : 'Go to Artist',
|
||||||
action : function() {
|
action : function() {
|
||||||
Backbone.history.navigate('/artist/' + self.model.get('titleSlug'), { trigger : true });
|
Backbone.history.navigate('/artist/' + self.model.get('artistSlug'), { trigger : true });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
<div class="col-md-10 col-xs-9">
|
<div class="col-md-10 col-xs-9">
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-md-10 col-xs-10">
|
<div class="col-md-10 col-xs-10">
|
||||||
<a href="artist/{{artistName}}" target="_blank">
|
<a href="artist/{{artistNameSlug}}" target="_blank">
|
||||||
<h2>{{artistName}}</h2>
|
<h2>{{artistName}}</h2>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
@ -50,6 +50,9 @@
|
||||||
<div class="col-md-2 col-xs-4">
|
<div class="col-md-2 col-xs-4">
|
||||||
{{> EpisodeProgressPartial }}
|
{{> EpisodeProgressPartial }}
|
||||||
</div>
|
</div>
|
||||||
|
<div class="col-md-8 col-xs-10">
|
||||||
|
Path {{path}}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue