mirror of
https://github.com/lidarr/lidarr.git
synced 2025-08-19 21:13:28 -07:00
Merge branch 'develop' of https://github.com/mattman86/Lidarr into develop
This commit is contained in:
commit
f50ce4abf9
55 changed files with 856 additions and 327 deletions
2
.github/ISSUE_TEMPLATE.md
vendored
2
.github/ISSUE_TEMPLATE.md
vendored
|
@ -2,4 +2,4 @@
|
|||
|
||||
|
||||
Provide a description of the feature request or bug, the more details the better.
|
||||
Please use https://forums.sonarr.tv/ for support or other questions. (When in doubt, use the forums)
|
||||
Please use http://lidarr.audio for support or other questions. (When in doubt, use the forums)
|
||||
|
|
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -101,7 +101,7 @@ App_Data/*.ldf
|
|||
_NCrunch_*
|
||||
_TeamCity*
|
||||
|
||||
# Sonarr
|
||||
# Lidarr
|
||||
config.xml
|
||||
nzbdrone.log*txt
|
||||
UpdateLogs/
|
||||
|
|
2
.idea/.name
generated
2
.idea/.name
generated
|
@ -1 +1 @@
|
|||
Sonarr
|
||||
Lidarr
|
4
CLA.md
4
CLA.md
|
@ -1,6 +1,6 @@
|
|||
# Sonarr Individual Contributor License Agreement #
|
||||
# Lidarr Individual Contributor License Agreement #
|
||||
|
||||
Thank you for your interest in contributing to Sonarr ("We" or "Us").
|
||||
Thank you for your interest in contributing to Lidarr ("We" or "Us").
|
||||
This contributor agreement ("Agreement") documents the rights granted by contributors to Us. To make this document effective, please complete the form below. This is a legally binding document, so please read it carefully before agreeing to it. The Agreement may cover more than one software project managed by Us.
|
||||
|
||||
## 1. Definitions ##
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
# How to Contribute #
|
||||
|
||||
We're always looking for people to help make Sonarr even better, there are a number of ways to contribute.
|
||||
We're always looking for people to help make Lidarr even better, there are a number of ways to contribute.
|
||||
|
||||
## Documentation ##
|
||||
Setup guides, FAQ, the more information we have on the wiki the better.
|
||||
|
@ -15,7 +15,7 @@ Setup guides, FAQ, the more information we have on the wiki the better.
|
|||
|
||||
### Getting started ###
|
||||
|
||||
1. Fork Sonarr
|
||||
1. Fork Lidarr
|
||||
2. Clone (develop branch) *you may need pull in submodules separately if you client doesn't clone them automatically (CurlSharp)*
|
||||
3. Run `npm install`
|
||||
4. Run `npm start` - Used to compile the UI components and copy them.
|
||||
|
@ -24,8 +24,8 @@ Setup guides, FAQ, the more information we have on the wiki the better.
|
|||
5. Compile in Visual Studio
|
||||
|
||||
### Contributing Code ###
|
||||
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/Sonarr/Sonarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
|
||||
- Rebase from Sonarr's develop branch, don't merge
|
||||
- If you're adding a new, already requested feature, please comment on [Github Issues](https://github.com/lidarr/Lidarr/issues "Github Issues") so work is not duplicated (If you want to add something not already on there, please talk to us first)
|
||||
- Rebase from Lidarr's develop branch, don't merge
|
||||
- Make meaningful commits, or squash them
|
||||
- Feel free to make a pull request before work is complete, this will let us see where its at and make comments/suggest improvements
|
||||
- Reach out to us on the forums or on IRC if you have any questions
|
||||
|
|
|
@ -18,13 +18,13 @@ Lidarr is a music collection manager for Usenet and BitTorrent users. It can mon
|
|||
|
||||
## Feature Requests
|
||||
|
||||
[](http://feathub.com/mattman86/Lidarr)
|
||||
[](http://feathub.com/lidarr/Lidarr)
|
||||
|
||||
## Configuring Development Environment:
|
||||
|
||||
### Requirements
|
||||
|
||||
* Visual Studio 2015 (https://www.visualstudio.com/vs/)
|
||||
* Visual Studio 2015 or higher (https://www.visualstudio.com/vs/). The community version is free and works (https://www.visualstudio.com/downloads/).
|
||||
* [Git](https://git-scm.com/downloads)
|
||||
* [NodeJS](https://nodejs.org/en/download/)
|
||||
|
||||
|
@ -35,6 +35,8 @@ Lidarr is a music collection manager for Usenet and BitTorrent users. It can mon
|
|||
* Grab the submodules `git submodule init && git submodule update`
|
||||
* Install the required Node Packages `npm install`
|
||||
* Start gulp to monitor your dev environment for any changes that need post processing using `npm start` command.
|
||||
* Run the project in Visual Studio
|
||||
* Open http://localhost:8686
|
||||
|
||||
*Please note gulp must be running at all times while you are working with Lidarr client source files.*
|
||||
|
||||
|
|
8
debian/control
vendored
8
debian/control
vendored
|
@ -2,11 +2,11 @@ Section: web
|
|||
Priority: optional
|
||||
Maintainer: Sonarr <contact@nzbdrone.com>
|
||||
Source: nzbdrone
|
||||
Homepage: https://sonarr.tv
|
||||
Vcs-Git: git@github.com:Sonarr/Sonarr.git
|
||||
Vcs-Browser: https://github.com/Sonarr/Sonarr
|
||||
Homepage: https://lidarr.audio
|
||||
Vcs-Git: git@github.com:lidarr/Lidarr.git
|
||||
Vcs-Browser: https://github.com/lidarr/Lidarr
|
||||
|
||||
Package: nzbdrone
|
||||
Architecture: all
|
||||
Depends: libmono-cil-dev (>= 3.2), sqlite3 (>= 3.7), mediainfo (>= 0.7.52)
|
||||
Description: Sonarr is an internet PVR
|
||||
Description: Lidarr is a music collection manager
|
||||
|
|
4
debian/copyright
vendored
4
debian/copyright
vendored
|
@ -1,9 +1,9 @@
|
|||
Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
||||
Upstream-Name: nzbdrone
|
||||
Source: https://github.com/Sonarr/Sonarr
|
||||
Source: https://github.com/lidarr/Lidarr
|
||||
|
||||
Files: *
|
||||
Copyright: 2010-2016 Sonarr <hello@sonarr.tv>
|
||||
Copyright: 2010-2016 Lidarr <hello@lidarr.audio>
|
||||
|
||||
License: GPL-3.0+
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// will download and run sonarr (server) in a non-windows enviroment
|
||||
// will download and run Lidarr (server) in a non-windows enviroment
|
||||
// you can use this if you don't care about the server code and just want to work
|
||||
// with the web code.
|
||||
|
||||
|
@ -31,7 +31,7 @@ function getLatest(cb) {
|
|||
}
|
||||
});
|
||||
|
||||
var url = 'http://services.sonarr.tv/v1/update/' + branch + '?os=osx';
|
||||
var url = 'http://services.lidarr.audio/v1/update/' + branch + '?os=osx';
|
||||
|
||||
console.log('Checking for latest version:', url);
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
{
|
||||
"name": "Sonarr",
|
||||
"version": "2.0.0",
|
||||
"description": "Sonarr",
|
||||
"name": "Lidarr",
|
||||
"version": "1.0.0",
|
||||
"description": "Lidarr",
|
||||
"main": "main.js",
|
||||
"scripts": {
|
||||
"build": "gulp build",
|
||||
|
@ -9,7 +9,7 @@
|
|||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git://github.com/Sonarr/Sonarr.git"
|
||||
"url": "git://github.com/lidarr/Lidarr.git"
|
||||
},
|
||||
"author": "",
|
||||
"license": "GPL-3.0",
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
; Script generated by the Inno Setup Script Wizard.
|
||||
; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES!
|
||||
|
||||
#define AppName "Sonarr"
|
||||
#define AppPublisher "Team Sonarr"
|
||||
#define AppURL "https://sonarr.tv/"
|
||||
#define ForumsURL "https://forums.sonarr.tv/"
|
||||
#define AppName "Lidarr"
|
||||
#define AppPublisher "Team Lidarr"
|
||||
#define AppURL "https://lidarr.audio/"
|
||||
#define ForumsURL "https://forums.lidarr.audio/"
|
||||
#define AppExeName "NzbDrone.exe"
|
||||
#define BuildNumber "2.0"
|
||||
#define BuildNumber GetEnv('BUILD_NUMBER')
|
||||
|
|
|
@ -8,7 +8,7 @@ namespace NzbDrone.Api.Music
|
|||
{
|
||||
public class AlbumResource
|
||||
{
|
||||
public int AlbumId { get; set; }
|
||||
public string AlbumId { get; set; }
|
||||
public string AlbumName { get; set; }
|
||||
public bool Monitored { 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.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();
|
||||
}
|
||||
|
@ -98,7 +98,6 @@ namespace NzbDrone.Api.Music
|
|||
{
|
||||
//var seriesStats = _seriesStatisticsService.SeriesStatistics();
|
||||
var artistResources = _artistService.GetAllArtists().ToResource();
|
||||
|
||||
MapCoversToLocal(artistResources.ToArray());
|
||||
//LinkSeriesStatistics(seriesResources, seriesStats);
|
||||
//PopulateAlternateTitles(seriesResources);
|
||||
|
@ -106,9 +105,9 @@ namespace NzbDrone.Api.Music
|
|||
return artistResources;
|
||||
}
|
||||
|
||||
private int AddArtist(ArtistResource seriesResource)
|
||||
private int AddArtist(ArtistResource artistResource)
|
||||
{
|
||||
var model = seriesResource.ToModel();
|
||||
var model = artistResource.ToModel();
|
||||
|
||||
return _addSeriesService.AddArtist(model).Id;
|
||||
}
|
||||
|
@ -175,16 +174,6 @@ namespace NzbDrone.Api.Music
|
|||
BroadcastResourceChange(ModelAction.Updated, message.Artist.Id);
|
||||
}
|
||||
|
||||
//public void Handle(ArtistDeletedEvent message)
|
||||
//{
|
||||
// BroadcastResourceChange(ModelAction.Deleted, message.Artist.ToResource());
|
||||
//}
|
||||
|
||||
//public void Handle(ArtistRenamedEvent message)
|
||||
//{
|
||||
// BroadcastResourceChange(ModelAction.Updated, message.Artist.Id);
|
||||
//}
|
||||
|
||||
//public void Handle(MediaCoversUpdatedEvent message)
|
||||
//{
|
||||
// BroadcastResourceChange(ModelAction.Updated, message.Artist.Id);
|
||||
|
|
|
@ -19,9 +19,8 @@ namespace NzbDrone.Api.Music
|
|||
|
||||
//View Only
|
||||
public string ArtistName { get; set; }
|
||||
public int ItunesId { get; set; }
|
||||
//public List<AlternateTitleResource> AlternateTitles { get; set; }
|
||||
//public string SortTitle { get; set; }
|
||||
public string SpotifyId { get; set; }
|
||||
public string Overview { get; set; }
|
||||
|
||||
public int AlbumCount
|
||||
{
|
||||
|
@ -29,7 +28,7 @@ namespace NzbDrone.Api.Music
|
|||
{
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -82,7 +81,7 @@ namespace NzbDrone.Api.Music
|
|||
//EpisodeFileCount
|
||||
//SizeOnDisk
|
||||
//Status = resource.Status,
|
||||
//Overview = resource.Overview,
|
||||
Overview = model.Overview,
|
||||
//NextAiring
|
||||
//PreviousAiring
|
||||
//Network = resource.Network,
|
||||
|
@ -106,7 +105,7 @@ namespace NzbDrone.Api.Music
|
|||
//FirstAired = resource.FirstAired,
|
||||
//LastInfoSync = resource.LastInfoSync,
|
||||
//SeriesType = resource.SeriesType,
|
||||
ItunesId = model.ItunesId,
|
||||
SpotifyId = model.SpotifyId,
|
||||
ArtistSlug = model.ArtistSlug,
|
||||
|
||||
RootFolderPath = model.RootFolderPath,
|
||||
|
@ -135,7 +134,7 @@ namespace NzbDrone.Api.Music
|
|||
//EpisodeFileCount
|
||||
//SizeOnDisk
|
||||
//Status = resource.Status,
|
||||
//Overview = resource.Overview,
|
||||
Overview = resource.Overview,
|
||||
//NextAiring
|
||||
//PreviousAiring
|
||||
//Network = resource.Network,
|
||||
|
@ -150,16 +149,8 @@ namespace NzbDrone.Api.Music
|
|||
|
||||
ArtistFolder = resource.ArtistFolder,
|
||||
Monitored = resource.Monitored,
|
||||
|
||||
//UseSceneNumbering = resource.UseSceneNumbering,
|
||||
//Runtime = resource.Runtime,
|
||||
//TvdbId = resource.TvdbId,
|
||||
//TvRageId = resource.TvRageId,
|
||||
//TvMazeId = resource.TvMazeId,
|
||||
//FirstAired = resource.FirstAired,
|
||||
//LastInfoSync = resource.LastInfoSync,
|
||||
//SeriesType = resource.SeriesType,
|
||||
ItunesId = resource.ItunesId,
|
||||
SpotifyId = resource.SpotifyId,
|
||||
ArtistSlug = resource.ArtistSlug,
|
||||
|
||||
RootFolderPath = resource.RootFolderPath,
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace NzbDrone.Common.Cloud
|
|||
{
|
||||
IHttpRequestBuilderFactory Services { get; }
|
||||
IHttpRequestBuilderFactory Search { get; }
|
||||
IHttpRequestBuilderFactory InternalSearch { get; }
|
||||
IHttpRequestBuilderFactory SkyHookTvdb { get; }
|
||||
}
|
||||
|
||||
|
@ -16,7 +17,11 @@ namespace NzbDrone.Common.Cloud
|
|||
Services = new HttpRequestBuilder("http://services.lidarr.tv/v1/")
|
||||
.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();
|
||||
|
||||
InternalSearch = new HttpRequestBuilder("https://itunes.apple.com/WebObjects/MZStore.woa/wa/{route}") //viewArtist or search
|
||||
.CreateFactory();
|
||||
|
||||
SkyHookTvdb = new HttpRequestBuilder("http://skyhook.lidarr.tv/v1/tvdb/{route}/{language}/")
|
||||
|
@ -28,6 +33,8 @@ namespace NzbDrone.Common.Cloud
|
|||
|
||||
public IHttpRequestBuilderFactory Search { get; }
|
||||
|
||||
public IHttpRequestBuilderFactory InternalSearch { get; }
|
||||
|
||||
public IHttpRequestBuilderFactory SkyHookTvdb { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,11 +13,12 @@ namespace NzbDrone.Core.Datastore.Migration
|
|||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Create.TableForModel("Artist")
|
||||
.WithColumn("ItunesId").AsInt32().Unique()
|
||||
.WithColumn("SpotifyId").AsString().Nullable().Unique()
|
||||
.WithColumn("ArtistName").AsString().Unique()
|
||||
.WithColumn("ArtistSlug").AsString().Nullable() //.Unique()
|
||||
.WithColumn("CleanTitle").AsString().Nullable() // Do we need this?
|
||||
.WithColumn("Monitored").AsBoolean()
|
||||
.WithColumn("Overview").AsString().Nullable()
|
||||
.WithColumn("AlbumFolder").AsBoolean().Nullable()
|
||||
.WithColumn("ArtistFolder").AsBoolean().Nullable()
|
||||
.WithColumn("LastInfoSync").AsDateTime().Nullable()
|
||||
|
@ -36,8 +37,8 @@ namespace NzbDrone.Core.Datastore.Migration
|
|||
;
|
||||
|
||||
Create.TableForModel("Albums")
|
||||
.WithColumn("AlbumId").AsInt32()
|
||||
.WithColumn("ArtistId").AsInt32()
|
||||
.WithColumn("AlbumId").AsString().Unique()
|
||||
.WithColumn("ArtistId").AsInt32() // Should this be artistId (string)
|
||||
.WithColumn("Title").AsString()
|
||||
.WithColumn("Year").AsInt32()
|
||||
.WithColumn("Image").AsInt32()
|
||||
|
@ -48,12 +49,13 @@ namespace NzbDrone.Core.Datastore.Migration
|
|||
|
||||
Create.TableForModel("Tracks")
|
||||
.WithColumn("ItunesTrackId").AsInt32().Unique()
|
||||
.WithColumn("AlbumId").AsInt32()
|
||||
.WithColumn("AlbumId").AsString()
|
||||
.WithColumn("ArtistsId").AsString().Nullable()
|
||||
.WithColumn("TrackNumber").AsInt32()
|
||||
.WithColumn("Title").AsString().Nullable()
|
||||
.WithColumn("Ignored").AsBoolean().Nullable()
|
||||
.WithColumn("Explict").AsBoolean()
|
||||
.WithColumn("Monitored").AsBoolean()
|
||||
.WithColumn("TrackExplicitName").AsString().Nullable()
|
||||
.WithColumn("TrackCensoredName").AsString().Nullable()
|
||||
.WithColumn("TrackFileId").AsInt32().Nullable()
|
||||
|
|
|
@ -8,8 +8,8 @@ namespace NzbDrone.Core.Datastore.Migration
|
|||
{
|
||||
protected override void MainDbUpgrade()
|
||||
{
|
||||
Alter.Table("NamingConfig").AddColumn("ArtistFolderFormat").AsAnsiString();
|
||||
Alter.Table("NamingConfig").AddColumn("AlbumFolderFormat").AsAnsiString();
|
||||
Alter.Table("NamingConfig").AddColumn("ArtistFolderFormat").AsAnsiString().Nullable();
|
||||
Alter.Table("NamingConfig").AddColumn("AlbumFolderFormat").AsAnsiString().Nullable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace NzbDrone.Core.Datastore
|
|||
.Relationships.AutoMapICollectionOrComplexProperties()
|
||||
.For("Tracks")
|
||||
.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);
|
||||
|
||||
Mapper.Entity<Track>().RegisterModel("Tracks")
|
||||
|
|
|
@ -8,24 +8,24 @@ namespace NzbDrone.Core.Exceptions
|
|||
{
|
||||
public class ArtistNotFoundException : NzbDroneException
|
||||
{
|
||||
public int ItunesId { get; set; }
|
||||
public string SpotifyId { get; set; }
|
||||
|
||||
public ArtistNotFoundException(int itunesId)
|
||||
: base(string.Format("Series with iTunesId {0} was not found, it may have been removed from iTunes.", itunesId))
|
||||
public ArtistNotFoundException(string spotifyId)
|
||||
: 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)
|
||||
{
|
||||
ItunesId = itunesId;
|
||||
SpotifyId = spotifyId;
|
||||
}
|
||||
|
||||
public ArtistNotFoundException(int itunesId, string message)
|
||||
public ArtistNotFoundException(string spotifyId, string message)
|
||||
: base(message)
|
||||
{
|
||||
ItunesId = itunesId;
|
||||
SpotifyId = spotifyId;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,7 @@ using NzbDrone.Core.Messaging.Commands;
|
|||
using NzbDrone.Core.Messaging.Events;
|
||||
using NzbDrone.Core.Tv.Commands;
|
||||
using NzbDrone.Core.Update.Commands;
|
||||
using NzbDrone.Core.Music.Commands;
|
||||
|
||||
namespace NzbDrone.Core.Jobs
|
||||
{
|
||||
|
@ -64,9 +65,10 @@ namespace NzbDrone.Core.Jobs
|
|||
new ScheduledTask{ Interval = 1, TypeName = typeof(CheckForFinishedDownloadCommand).FullName},
|
||||
new ScheduledTask{ Interval = 5, TypeName = typeof(MessagingCleanupCommand).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 = 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 = 7*24*60, TypeName = typeof(BackupCommand).FullName},
|
||||
|
||||
|
|
|
@ -6,6 +6,6 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
|||
{
|
||||
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,27 +5,36 @@ using System.Text;
|
|||
|
||||
namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
||||
{
|
||||
public class AlbumResource
|
||||
{
|
||||
public AlbumResource()
|
||||
|
||||
public class AristResultResource
|
||||
{
|
||||
public AristResultResource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public string ArtistName { get; set; }
|
||||
public int ArtistId { get; set; }
|
||||
public string CollectionName { get; set; }
|
||||
public int CollectionId { get; set; }
|
||||
public string PrimaryGenreName { get; set; }
|
||||
public string ArtworkUrl100 { get; set; }
|
||||
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<ArtistInfoResource> Items { get; set; }
|
||||
}
|
||||
|
||||
public class AlbumResultResource
|
||||
{
|
||||
public AlbumResultResource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public List<AlbumInfoResource> Items { get; set; }
|
||||
}
|
||||
|
||||
public class TrackResultResource
|
||||
{
|
||||
public TrackResultResource()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
public List<TrackInfoResource> Items { get; set; }
|
||||
}
|
||||
public class ArtistResource
|
||||
{
|
||||
public ArtistResource()
|
||||
|
@ -33,9 +42,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
|||
|
||||
}
|
||||
|
||||
public int ResultCount { get; set; }
|
||||
public List<AlbumResource> Results { get; set; }
|
||||
//public string ArtistName { get; set; }
|
||||
//public List<AlbumResource> Albums { get; set; }
|
||||
public AristResultResource Artists { get; set; }
|
||||
public AristResultResource Albums { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,10 @@ namespace NzbDrone.Core.MetadataSource.SkyHook.Resource
|
|||
public class ImageResource
|
||||
{
|
||||
public string CoverType { get; set; }
|
||||
|
||||
// Spotify Mapping
|
||||
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; }
|
||||
|
||||
}
|
||||
}
|
|
@ -30,6 +30,8 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
|||
_logger = logger;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public Tuple<Series, List<Episode>> GetSeriesInfo(int tvdbSeriesId)
|
||||
{
|
||||
Console.WriteLine("[GetSeriesInfo] id:" + tvdbSeriesId);
|
||||
|
@ -63,86 +65,44 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
|||
|
||||
public List<Series> SearchForNewSeries(string title)
|
||||
{
|
||||
try
|
||||
{
|
||||
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 tempSeries = new Series();
|
||||
tempSeries.Title = "AFI";
|
||||
tempList.Add(tempSeries);
|
||||
return tempList;
|
||||
|
||||
return httpResponse.Resource.SelectList(MapSeries);
|
||||
}
|
||||
catch (HttpException)
|
||||
{
|
||||
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);
|
||||
}
|
||||
// TODO: Remove this API
|
||||
var tempList = new List<Series>();
|
||||
var tempSeries = new Series();
|
||||
tempSeries.Title = "AFI";
|
||||
tempList.Add(tempSeries);
|
||||
return tempList;
|
||||
}
|
||||
|
||||
public Tuple<Artist, List<Track>> GetArtistInfo(int itunesId)
|
||||
|
||||
public Tuple<Artist, List<Track>> GetArtistInfo(string spotifyId)
|
||||
{
|
||||
Console.WriteLine("[GetArtistInfo] id:" + itunesId);
|
||||
//https://itunes.apple.com/lookup?id=909253
|
||||
|
||||
_logger.Debug("Getting Artist with SpotifyId of {0}", spotifyId);
|
||||
|
||||
///v1/albums/{id}
|
||||
//
|
||||
|
||||
// We need to perform a direct lookup of the artist
|
||||
var httpRequest = _requestBuilder.Create()
|
||||
.SetSegment("route", "lookup")
|
||||
.AddQueryParam("id", itunesId.ToString())
|
||||
.SetSegment("route", "artists/" + spotifyId)
|
||||
//.SetSegment("route", "search")
|
||||
//.AddQueryParam("type", "artist,album")
|
||||
//.AddQueryParam("q", spotifyId.ToString())
|
||||
.Build();
|
||||
|
||||
|
||||
|
||||
httpRequest.AllowAutoRedirect = true;
|
||||
httpRequest.SuppressHttpError = true;
|
||||
|
||||
var httpResponse = _httpClient.Get<ArtistResource>(httpRequest);
|
||||
var httpResponse = _httpClient.Get<ArtistInfoResource>(httpRequest);
|
||||
|
||||
|
||||
if (httpResponse.HasHttpError)
|
||||
{
|
||||
if (httpResponse.StatusCode == HttpStatusCode.NotFound)
|
||||
{
|
||||
throw new ArtistNotFoundException(itunesId);
|
||||
throw new ArtistNotFoundException(spotifyId);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -150,13 +110,91 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
|||
}
|
||||
}
|
||||
|
||||
Console.WriteLine("GetArtistInfo, GetArtistInfo");
|
||||
//var tracks = httpResponse.Resource.Episodes.Select(MapEpisode);
|
||||
//var artist = MapArtist(httpResponse.Resource);
|
||||
// I don't know how we are getting tracks from iTunes yet.
|
||||
return new Tuple<Artist, List<Track>>(MapArtists(httpResponse.Resource)[0], new List<Track>());
|
||||
//return new Tuple<Artist, List<Track>>(artist, tracks.ToList());
|
||||
Artist artist = new Artist();
|
||||
artist.ArtistName = httpResponse.Resource.Name;
|
||||
artist.SpotifyId = httpResponse.Resource.Id;
|
||||
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)
|
||||
{
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
List<Album> albums = new List<Album>();
|
||||
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)
|
||||
{
|
||||
try
|
||||
|
@ -168,16 +206,14 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
|||
{
|
||||
var slug = lowerTitle.Split(':')[1].Trim();
|
||||
|
||||
int itunesId;
|
||||
|
||||
if (slug.IsNullOrWhiteSpace() || slug.Any(char.IsWhiteSpace) || !int.TryParse(slug, out itunesId) || itunesId <= 0)
|
||||
if (slug.IsNullOrWhiteSpace() || slug.Any(char.IsWhiteSpace))
|
||||
{
|
||||
return new List<Artist>();
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return new List<Artist> { GetArtistInfo(itunesId).Item1 };
|
||||
return new List<Artist> { GetArtistInfo(slug).Item1 };
|
||||
}
|
||||
catch (ArtistNotFoundException)
|
||||
{
|
||||
|
@ -187,15 +223,18 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
|||
|
||||
var httpRequest = _requestBuilder.Create()
|
||||
.SetSegment("route", "search")
|
||||
.AddQueryParam("entity", "album")
|
||||
.AddQueryParam("term", title.ToLower().Trim())
|
||||
.AddQueryParam("type", "artist,album")
|
||||
.AddQueryParam("q", title.ToLower().Trim())
|
||||
.Build();
|
||||
|
||||
|
||||
|
||||
var httpResponse = _httpClient.Get<ArtistResource>(httpRequest);
|
||||
|
||||
return MapArtists(httpResponse.Resource);
|
||||
|
||||
List<Artist> artists = MapArtists(httpResponse.Resource);
|
||||
|
||||
return artists;
|
||||
}
|
||||
catch (HttpException)
|
||||
{
|
||||
|
@ -208,45 +247,52 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
|
|||
}
|
||||
}
|
||||
|
||||
private Artist MapArtistInfo(ArtistInfoResource resource)
|
||||
{
|
||||
// This expects ArtistInfoResource, thus just need to populate one artist
|
||||
Artist artist = new Artist();
|
||||
//artist.Overview = resource.artistBio;
|
||||
//artist.ArtistName = resource.name;
|
||||
//foreach(var genre in resource.genreNames)
|
||||
//{
|
||||
// artist.Genres.Add(genre);
|
||||
//}
|
||||
|
||||
return artist;
|
||||
}
|
||||
|
||||
private List<Artist> MapArtists(ArtistResource resource)
|
||||
{
|
||||
Album tempAlbum;
|
||||
|
||||
|
||||
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);
|
||||
tempAlbum = MapAlbum(album);
|
||||
|
||||
if (index >= 0)
|
||||
{
|
||||
artists[index].Albums.Add(tempAlbum);
|
||||
}
|
||||
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);
|
||||
}
|
||||
|
||||
Artist artist = new Artist();
|
||||
artist.ArtistName = artistResource.Name;
|
||||
artist.SpotifyId = artistResource.Id;
|
||||
artist.Genres = artistResource.Genres;
|
||||
//artist.ArtistSlug = a//TODO implement artistSlug mapping;
|
||||
artists.Add(artist);
|
||||
}
|
||||
|
||||
// Maybe? Get all the albums for said artist
|
||||
|
||||
|
||||
return artists;
|
||||
}
|
||||
|
||||
private Album MapAlbum(AlbumResource albumQuery)
|
||||
{
|
||||
Album album = new Album();
|
||||
//private Album MapAlbum(AlbumResource albumQuery)
|
||||
//{
|
||||
// Album album = new Album();
|
||||
|
||||
album.AlbumId = albumQuery.CollectionId;
|
||||
album.Title = albumQuery.CollectionName;
|
||||
album.Year = albumQuery.ReleaseDate.Year;
|
||||
album.ArtworkUrl = albumQuery.ArtworkUrl100;
|
||||
album.Explicitness = albumQuery.CollectionExplicitness;
|
||||
return album;
|
||||
}
|
||||
// album.AlbumId = albumQuery.CollectionId;
|
||||
// album.Title = albumQuery.CollectionName;
|
||||
// album.Year = albumQuery.ReleaseDate.Year;
|
||||
// album.ArtworkUrl = albumQuery.ArtworkUrl100;
|
||||
// album.Explicitness = albumQuery.CollectionExplicitness;
|
||||
// return album;
|
||||
//}
|
||||
|
||||
private static Series MapSeries(ShowResource show)
|
||||
{
|
||||
|
|
|
@ -48,7 +48,7 @@ namespace NzbDrone.Core.Music
|
|||
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -63,7 +63,7 @@ namespace NzbDrone.Core.Music
|
|||
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);
|
||||
|
||||
return newArtist;
|
||||
|
@ -75,22 +75,21 @@ namespace NzbDrone.Core.Music
|
|||
|
||||
try
|
||||
{
|
||||
tuple = _artistInfo.GetArtistInfo(newArtist.ItunesId);
|
||||
tuple = _artistInfo.GetArtistInfo(newArtist.SpotifyId);
|
||||
}
|
||||
catch (SeriesNotFoundException)
|
||||
{
|
||||
_logger.Error("tvdbid {1} was not found, it may have been removed from TheTVDB.", newArtist.ItunesId);
|
||||
_logger.Error("SpotifyId {1} was not found, it may have been removed from Spotify.", newArtist.SpotifyId);
|
||||
|
||||
throw new ValidationException(new List<ValidationFailure>
|
||||
{
|
||||
new ValidationFailure("TvdbId", "A series with this ID was not found", newArtist.ItunesId)
|
||||
new ValidationFailure("SpotifyId", "An artist with this ID was not found", newArtist.SpotifyId)
|
||||
});
|
||||
}
|
||||
|
||||
var artist = tuple.Item1;
|
||||
|
||||
// If seasons were passed in on the new series use them, otherwise use the seasons from Skyhook
|
||||
// TODO: Refactor for albums
|
||||
// If albums were passed in on the new artist use them, otherwise use the albums from Skyhook
|
||||
newArtist.Albums = newArtist.Albums != null && newArtist.Albums.Any() ? newArtist.Albums : artist.Albums;
|
||||
|
||||
artist.ApplyChanges(newArtist);
|
||||
|
|
|
@ -19,7 +19,7 @@ namespace NzbDrone.Core.Music
|
|||
SeriesPathValidator seriesPathValidator,
|
||||
DroneFactoryValidator droneFactoryValidator,
|
||||
SeriesAncestorValidator seriesAncestorValidator,
|
||||
ArtistSlugValidator seriesTitleSlugValidator)
|
||||
ArtistSlugValidator artistTitleSlugValidator)
|
||||
{
|
||||
RuleFor(c => c.Path).Cascade(CascadeMode.StopOnFirstFailure)
|
||||
.IsValidPath()
|
||||
|
@ -28,7 +28,7 @@ namespace NzbDrone.Core.Music
|
|||
.SetValidator(droneFactoryValidator)
|
||||
.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>();
|
||||
}
|
||||
|
||||
public int AlbumId { get; set; }
|
||||
public string AlbumId { get; set; }
|
||||
public string Title { get; set; } // NOTE: This should be CollectionName in API
|
||||
public int Year { get; set; }
|
||||
public int TrackCount { get; set; }
|
||||
public List<Track> Tracks { get; set; }
|
||||
public int DiscCount { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public List<MediaCover.MediaCover> Images { get; set; }
|
||||
|
|
|
@ -22,53 +22,38 @@ namespace NzbDrone.Core.Music
|
|||
|
||||
}
|
||||
|
||||
public int ItunesId { get; set; }
|
||||
public string SpotifyId { get; set; }
|
||||
public string ArtistName { get; set; }
|
||||
public string ArtistSlug { get; set; }
|
||||
public string CleanTitle { get; set; }
|
||||
public string Overview { get; set; }
|
||||
public bool Monitored { get; set; }
|
||||
public bool AlbumFolder { get; set; }
|
||||
public bool ArtistFolder { get; set; }
|
||||
public DateTime? LastInfoSync { get; set; }
|
||||
public DateTime? LastDiskSync { get; set; }
|
||||
|
||||
public int Status { get; set; } // TODO: Figure out what this is, do we need it?
|
||||
public string Path { get; set; }
|
||||
public List<MediaCover.MediaCover> Images { get; set; }
|
||||
public List<string> Genres { get; set; }
|
||||
public int QualityProfileId { get; set; }
|
||||
|
||||
public string RootFolderPath { get; set; }
|
||||
public DateTime Added { get; set; }
|
||||
public LazyLoaded<Profile> Profile { get; set; }
|
||||
public int ProfileId { get; set; }
|
||||
public List<Album> Albums { get; set; }
|
||||
public HashSet<int> Tags { 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()
|
||||
{
|
||||
return string.Format("[{0}][{1}]", ItunesId, ArtistName.NullSafe());
|
||||
return string.Format("[{0}][{1}]", SpotifyId, ArtistName.NullSafe());
|
||||
}
|
||||
|
||||
public void ApplyChanges(Artist otherArtist)
|
||||
{
|
||||
|
||||
ItunesId = otherArtist.ItunesId;
|
||||
SpotifyId = otherArtist.SpotifyId;
|
||||
ArtistName = otherArtist.ArtistName;
|
||||
ArtistSlug = otherArtist.ArtistSlug;
|
||||
CleanTitle = otherArtist.CleanTitle;
|
||||
|
@ -87,18 +72,11 @@ namespace NzbDrone.Core.Music
|
|||
ArtistFolder = otherArtist.ArtistFolder;
|
||||
AddOptions = otherArtist.AddOptions;
|
||||
|
||||
|
||||
//TODO: Implement
|
||||
ItunesId = otherArtist.ItunesId;
|
||||
|
||||
Albums = otherArtist.Albums;
|
||||
Path = otherArtist.Path;
|
||||
ProfileId = otherArtist.ProfileId;
|
||||
|
||||
AlbumFolder = otherArtist.AlbumFolder;
|
||||
Monitored = otherArtist.Monitored;
|
||||
|
||||
//SeriesType = otherArtist.SeriesType;
|
||||
RootFolderPath = otherArtist.RootFolderPath;
|
||||
Tags = otherArtist.Tags;
|
||||
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);
|
||||
Artist FindByName(string cleanTitle);
|
||||
Artist FindByItunesId(int iTunesId);
|
||||
Artist FindById(string spotifyId);
|
||||
}
|
||||
|
||||
public class ArtistRepository : BasicRepository<Artist>, IArtistRepository
|
||||
|
@ -24,9 +24,9 @@ namespace NzbDrone.Core.Music
|
|||
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)
|
||||
|
|
|
@ -17,7 +17,7 @@ namespace NzbDrone.Core.Music
|
|||
Artist GetArtist(int artistId);
|
||||
List<Artist> GetArtists(IEnumerable<int> artistIds);
|
||||
Artist AddArtist(Artist newArtist);
|
||||
Artist FindByItunesId(int itunesId);
|
||||
Artist FindById(string spotifyId);
|
||||
Artist FindByName(string title);
|
||||
Artist FindByTitleInexact(string title);
|
||||
void DeleteArtist(int artistId, bool deleteFiles);
|
||||
|
@ -69,9 +69,9 @@ namespace NzbDrone.Core.Music
|
|||
_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)
|
||||
|
@ -114,7 +114,7 @@ namespace NzbDrone.Core.Music
|
|||
|
||||
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 int ItunesTrackId { get; set; }
|
||||
public int AlbumId { get; set; }
|
||||
public LazyLoaded<Artist> ArtistsId { get; set; }
|
||||
public int SpotifyTrackId { get; set; }
|
||||
public string AlbumId { get; set; }
|
||||
public LazyLoaded<Artist> Artist { get; set; }
|
||||
public string ArtistId { get; set; }
|
||||
public int CompilationId { get; set; }
|
||||
public bool Compilation { get; set; }
|
||||
public int TrackNumber { get; set; }
|
||||
|
@ -28,11 +29,10 @@ namespace NzbDrone.Core.Music
|
|||
public bool Explict { get; set; }
|
||||
public string TrackExplicitName { get; set; }
|
||||
public string TrackCensoredName { get; set; }
|
||||
public string Monitored { get; set; }
|
||||
public int TrackFileId { get; set; } // JVM: Is this needed with TrackFile reference?
|
||||
public bool Monitored { get; set; }
|
||||
public int TrackFileId { 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 List<MediaCover.MediaCover> Images { get; set; }*/
|
||||
|
||||
|
@ -46,7 +46,7 @@ namespace NzbDrone.Core.Music
|
|||
|
||||
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);
|
||||
List<Track> GetTracks(IEnumerable<int> ids);
|
||||
Track FindTrack(int artistId, int albumId, int trackNumber);
|
||||
Track FindTrackByTitle(int artistId, int albumId, string releaseTitle);
|
||||
List<Track> GetTrackByArtist(int artistId);
|
||||
List<Track> GetTracksByAblum(int artistId, int albumId);
|
||||
List<Track> GetTracksByAblumTitle(int artistId, string albumTitle);
|
||||
List<Track> TracksWithFiles(int artistId);
|
||||
Track FindTrack(string artistId, string albumId, int trackNumber);
|
||||
Track FindTrackByTitle(string artistId, string albumId, string releaseTitle);
|
||||
List<Track> GetTrackByArtist(string artistId);
|
||||
List<Track> GetTracksByAlbum(string artistId, string albumId);
|
||||
List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle);
|
||||
List<Track> TracksWithFiles(string artistId);
|
||||
PagingSpec<Track> TracksWithoutFiles(PagingSpec<Track> pagingSpec);
|
||||
List<Track> GeTracksByFileId(int trackFileId);
|
||||
void UpdateTrack(Track track);
|
||||
|
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Music
|
|||
void InsertMany(List<Track> tracks);
|
||||
void UpdateMany(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
|
||||
|
@ -34,12 +34,12 @@ namespace NzbDrone.Core.Music
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Track FindTrack(int artistId, int albumId, int trackNumber)
|
||||
public Track FindTrack(string artistId, string albumId, int trackNumber)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Track FindTrackByTitle(int artistId, int albumId, string releaseTitle)
|
||||
public Track FindTrackByTitle(string artistId, string albumId, string releaseTitle)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -54,7 +54,7 @@ namespace NzbDrone.Core.Music
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public List<Track> GetTrackByArtist(int artistId)
|
||||
public List<Track> GetTrackByArtist(string artistId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -64,12 +64,12 @@ namespace NzbDrone.Core.Music
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public List<Track> GetTracksByAblum(int artistId, int albumId)
|
||||
public List<Track> GetTracksByAlbum(string artistId, string albumId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public List<Track> GetTracksByAblumTitle(int artistId, string albumTitle)
|
||||
public List<Track> GetTracksByAlbumTitle(string artistId, string albumTitle)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
@ -84,12 +84,12 @@ namespace NzbDrone.Core.Music
|
|||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public void SetTrackMonitoredByAlbum(int artistId, int albumId, bool monitored)
|
||||
public void SetTrackMonitoredByAlbum(string artistId, string albumId, bool monitored)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public List<Track> TracksWithFiles(int artistId)
|
||||
public List<Track> TracksWithFiles(string artistId)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
|
|
@ -815,6 +815,8 @@
|
|||
<Compile Include="Messaging\IProcessMessage.cs" />
|
||||
<Compile Include="MetadataSource\IProvideArtistInfo.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\EpisodeResource.cs" />
|
||||
<Compile Include="MetadataSource\SkyHook\Resource\ImageResource.cs" />
|
||||
|
@ -822,6 +824,7 @@
|
|||
<Compile Include="MetadataSource\SkyHook\Resource\SeasonResource.cs" />
|
||||
<Compile Include="MetadataSource\SkyHook\Resource\ShowResource.cs" />
|
||||
<Compile Include="MetadataSource\SkyHook\Resource\TimeOfDayResource.cs" />
|
||||
<Compile Include="MetadataSource\SkyHook\Resource\TrackInfoResource.cs" />
|
||||
<Compile Include="MetadataSource\SkyHook\SkyHookProxy.cs" />
|
||||
<Compile Include="MetadataSource\SearchSeriesComparer.cs" />
|
||||
<Compile Include="MetadataSource\SkyHook\SkyHookException.cs" />
|
||||
|
@ -851,14 +854,20 @@
|
|||
<Compile Include="Music\AddArtistValidator.cs" />
|
||||
<Compile Include="Music\Album.cs" />
|
||||
<Compile Include="Music\Artist.cs" />
|
||||
<Compile Include="Music\ArtistAddedHandler.cs" />
|
||||
<Compile Include="Music\ArtistNameNormalizer.cs" />
|
||||
<Compile Include="Music\ArtistSlugValidator.cs" />
|
||||
<Compile Include="Music\ArtistRepository.cs" />
|
||||
<Compile Include="Music\ArtistService.cs" />
|
||||
<Compile Include="Music\Commands\RefreshArtistCommand.cs" />
|
||||
<Compile Include="Music\Events\ArtistAddedEvent.cs" />
|
||||
<Compile Include="Music\Events\ArtistDeletedEvent.cs" />
|
||||
<Compile Include="Music\Events\ArtistEditedEvent.cs" />
|
||||
<Compile Include="Music\Events\ArtistRefreshStartingEvent.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\TrackService.cs" />
|
||||
<Compile Include="Notifications\Join\JoinAuthException.cs" />
|
||||
|
|
|
@ -24,7 +24,7 @@ namespace NzbDrone.Core.Parser.Model
|
|||
public MediaInfoModel MediaInfo { get; set; }
|
||||
public bool ExistingFile { get; set; }
|
||||
|
||||
public int Album
|
||||
public string Album
|
||||
{
|
||||
get
|
||||
{
|
||||
|
@ -32,7 +32,7 @@ namespace NzbDrone.Core.Parser.Model
|
|||
}
|
||||
}
|
||||
|
||||
public bool IsSpecial => Album == 0;
|
||||
public bool IsSpecial => Album != "";
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace NzbDrone.Core.Validation.Paths
|
|||
private readonly IArtistService _artistService;
|
||||
|
||||
public ArtistExistsValidator(IArtistService artistService)
|
||||
: base("This artist has already been added")
|
||||
: base("This artist has already been added.")
|
||||
{
|
||||
_artistService = artistService;
|
||||
}
|
||||
|
@ -21,9 +21,7 @@ namespace NzbDrone.Core.Validation.Paths
|
|||
{
|
||||
if (context.PropertyValue == null) return true;
|
||||
|
||||
var itunesId = Convert.ToInt32(context.PropertyValue.ToString());
|
||||
|
||||
return (!_artistService.GetAllArtists().Exists(s => s.ItunesId == itunesId));
|
||||
return (!_artistService.GetAllArtists().Exists(s => s.SpotifyId == context.PropertyValue.ToString()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace NzbDrone.Update.UpdateEngine
|
|||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
_logger.Warn(e, "Couldn't start Lidarr Service (Most likely due to permission issues). falling back to console.");
|
||||
_logger.Warn(e, "Couldn't start Lidarr Service (Most likely due to permission issues). Falling back to console.");
|
||||
StartConsole(installationFolder);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ var ExistingSeriesCollectionView = require('./Existing/AddExistingSeriesCollecti
|
|||
var AddSeriesView = require('./AddSeriesView');
|
||||
var ProfileCollection = require('../Profile/ProfileCollection');
|
||||
var RootFolderCollection = require('./RootFolders/RootFolderCollection');
|
||||
require('../Series/SeriesCollection');
|
||||
require('../Artist/ArtistCollection');
|
||||
|
||||
module.exports = Marionette.Layout.extend({
|
||||
template : 'AddSeries/AddSeriesLayoutTemplate',
|
||||
|
|
|
@ -223,12 +223,12 @@ var view = Marionette.ItemView.extend({
|
|||
self.close();
|
||||
|
||||
Messenger.show({
|
||||
message : 'Added: ' + self.model.get('title'),
|
||||
message : 'Added: ' + self.model.get('artistName'),
|
||||
actions : {
|
||||
goToSeries : {
|
||||
label : 'Go to Artist',
|
||||
action : function() {
|
||||
Backbone.history.navigate('/artist/' + self.model.get('titleSlug'), { trigger : true });
|
||||
Backbone.history.navigate('/artist/' + self.model.get('artistSlug'), { trigger : true });
|
||||
}
|
||||
}
|
||||
},
|
||||
|
|
|
@ -1,34 +1,33 @@
|
|||
var NzbDroneController = require('../Shared/NzbDroneController');
|
||||
var AppLayout = require('../AppLayout');
|
||||
var ArtistCollection = require('./ArtistCollection');
|
||||
var SeriesIndexLayout = require('./Index/SeriesIndexLayout');
|
||||
var SeriesIndexLayout = require('../Series/Index/SeriesIndexLayout');
|
||||
var SeriesDetailsLayout = require('../Series/Details/SeriesDetailsLayout');
|
||||
|
||||
module.exports = NzbDroneController.extend({
|
||||
_originalInit : NzbDroneController.prototype.initialize,
|
||||
|
||||
initialize : function() {
|
||||
this.route('', this.series);
|
||||
this.route('artist', this.series);
|
||||
this.route('artist/:query', this.seriesDetails);
|
||||
this.route('', this.artist);
|
||||
this.route('artist', this.artist);
|
||||
this.route('artist/:query', this.artistDetails);
|
||||
|
||||
this._originalInit.apply(this, arguments);
|
||||
},
|
||||
|
||||
artist : function() {
|
||||
this.setTitle('Lidarr');
|
||||
this.setArtistName('Lidarr');
|
||||
this.showMainRegion(new SeriesIndexLayout());
|
||||
},
|
||||
|
||||
seriesDetails : function(query) {
|
||||
artistDetails : function(query) {
|
||||
var artists = ArtistCollection.where({ artistNameSlug : query });
|
||||
console.log('seriesDetails, artists: ', artists);
|
||||
console.log('artistDetails, artists: ', artists);
|
||||
if (artists.length !== 0) {
|
||||
var targetSeries = artists[0];
|
||||
console.log("[ArtistController] targetSeries: ", targetSeries);
|
||||
this.setTitle(targetSeries.get('title'));
|
||||
this.setArtistName(targetSeries.get('artistName'));
|
||||
this.setTitle(targetSeries.get('artistName')); // TODO: Update NzbDroneController
|
||||
//this.setArtistName(targetSeries.get('artistName'));
|
||||
this.showMainRegion(new SeriesDetailsLayout({ model : targetSeries }));
|
||||
} else {
|
||||
this.showNotFound();
|
||||
|
|
|
@ -71,6 +71,28 @@ Handlebars.registerHelper('seasonCountHelper', function() {
|
|||
return new Handlebars.SafeString('<span class="label label-info">{0} Seasons</span>'.format(seasonCount));
|
||||
});
|
||||
|
||||
Handlebars.registerHelper ('truncate', function (str, len) {
|
||||
if (str && str.length > len && str.length > 0) {
|
||||
var new_str = str + " ";
|
||||
new_str = str.substr (0, len);
|
||||
new_str = str.substr (0, new_str.lastIndexOf(" "));
|
||||
new_str = (new_str.length > 0) ? new_str : str.substr (0, len);
|
||||
|
||||
return new Handlebars.SafeString ( new_str +'...' );
|
||||
}
|
||||
return str;
|
||||
});
|
||||
|
||||
Handlebars.registerHelper('albumCountHelper', function() {
|
||||
var albumCount = this.albumCount;
|
||||
|
||||
if (albumCount === 1) {
|
||||
return new Handlebars.SafeString('<span class="label label-info">{0} Albums</span>'.format(albumCount));
|
||||
}
|
||||
|
||||
return new Handlebars.SafeString('<span class="label label-info">{0} Albums</span>'.format(albumCount));
|
||||
});
|
||||
|
||||
/*Handlebars.registerHelper('titleWithYear', function() {
|
||||
if (this.title.endsWith(' ({0})'.format(this.year))) {
|
||||
return this.title;
|
||||
|
|
|
@ -8,24 +8,22 @@
|
|||
<div class="col-md-10 col-xs-9">
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-xs-10">
|
||||
<a href="{{route}}" target="_blank">
|
||||
<h2>{{title}}</h2>
|
||||
<a href="artist/{{artistNameSlug}}" target="_blank">
|
||||
<h2>{{artistName}}</h2>
|
||||
</a>
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-2">
|
||||
<div class="pull-right series-overview-list-actions">
|
||||
<i class="icon-lidarr-refresh x-refresh" title="Update series info and scan disk"/>
|
||||
<i class="icon-lidarr-edit x-edit" title="Edit Series"/>
|
||||
<i class="icon-lidarr-refresh x-refresh" title="Update artist info and scan disk"/>
|
||||
<i class="icon-lidarr-edit x-edit" title="Edit Artist"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12 col-xs-12">
|
||||
<a href="{{route}}">
|
||||
<div>
|
||||
{{overview}}
|
||||
</div>
|
||||
</a>
|
||||
<div class="col-md-10 col-xs-12">
|
||||
<div>
|
||||
{{truncate overview 600}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
|
@ -35,21 +33,26 @@
|
|||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-10 col-xs-8">
|
||||
{{#if_eq status compare="ended"}}
|
||||
<!--{{#if_eq status compare="ended"}}
|
||||
<span class="label label-danger">Ended</span>
|
||||
{{/if_eq}}
|
||||
{{/if_eq}}-->
|
||||
|
||||
<!--
|
||||
NOTE: We can show next drop date of album in future
|
||||
{{#if nextAiring}}
|
||||
<span class="label label-default">{{RelativeDate nextAiring}}</span>
|
||||
{{/if}}
|
||||
{{/if}}-->
|
||||
|
||||
{{seasonCountHelper}}
|
||||
{{albumCountHelper}}
|
||||
|
||||
{{profile profileId}}
|
||||
</div>
|
||||
<div class="col-md-2 col-xs-4">
|
||||
{{> EpisodeProgressPartial }}
|
||||
</div>
|
||||
<div class="col-md-8 col-xs-10">
|
||||
Path {{path}}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -4,7 +4,7 @@ var Backgrid = require('backgrid');
|
|||
var PosterCollectionView = require('./Posters/SeriesPostersCollectionView');
|
||||
var ListCollectionView = require('./Overview/SeriesOverviewCollectionView');
|
||||
var EmptyView = require('./EmptyView');
|
||||
var SeriesCollection = require('../SeriesCollection');
|
||||
var ArtistCollection = require('../../Artist/ArtistCollection');
|
||||
var RelativeDateCell = require('../../Cells/RelativeDateCell');
|
||||
var SeriesTitleCell = require('../../Cells/SeriesTitleCell');
|
||||
var TemplatedCell = require('../../Cells/TemplatedCell');
|
||||
|
@ -20,6 +20,7 @@ require('../../Mixins/backbone.signalr.mixin');
|
|||
module.exports = Marionette.Layout.extend({
|
||||
template : 'Series/Index/SeriesIndexLayoutTemplate',
|
||||
|
||||
|
||||
regions : {
|
||||
seriesRegion : '#x-series',
|
||||
toolbar : '#x-toolbar',
|
||||
|
@ -111,28 +112,28 @@ module.exports = Marionette.Layout.extend({
|
|||
},
|
||||
|
||||
initialize : function() {
|
||||
this.seriesCollection = SeriesCollection.clone();
|
||||
this.seriesCollection.shadowCollection.bindSignalR();
|
||||
this.artistCollection = ArtistCollection.clone();
|
||||
this.artistCollection.shadowCollection.bindSignalR();
|
||||
|
||||
this.listenTo(this.seriesCollection.shadowCollection, 'sync', function(model, collection, options) {
|
||||
this.seriesCollection.fullCollection.resetFiltered();
|
||||
this.listenTo(this.artistCollection, 'sync', function(model, collection, options) {
|
||||
this.artistCollection.fullCollection.resetFiltered();
|
||||
this._renderView();
|
||||
});
|
||||
|
||||
this.listenTo(this.seriesCollection.shadowCollection, 'add', function(model, collection, options) {
|
||||
this.seriesCollection.fullCollection.resetFiltered();
|
||||
this.listenTo(this.artistCollection, 'add', function(model, collection, options) {
|
||||
this.artistCollection.fullCollection.resetFiltered();
|
||||
this._renderView();
|
||||
});
|
||||
|
||||
this.listenTo(this.seriesCollection.shadowCollection, 'remove', function(model, collection, options) {
|
||||
this.seriesCollection.fullCollection.resetFiltered();
|
||||
this.listenTo(this.artistCollection, 'remove', function(model, collection, options) {
|
||||
this.artistCollection.fullCollection.resetFiltered();
|
||||
this._renderView();
|
||||
});
|
||||
|
||||
this.sortingOptions = {
|
||||
type : 'sorting',
|
||||
storeState : false,
|
||||
viewCollection : this.seriesCollection,
|
||||
viewCollection : this.artistCollection,
|
||||
items : [
|
||||
{
|
||||
title : 'Title',
|
||||
|
@ -243,7 +244,7 @@ module.exports = Marionette.Layout.extend({
|
|||
|
||||
_showTable : function() {
|
||||
this.currentView = new Backgrid.Grid({
|
||||
collection : this.seriesCollection,
|
||||
collection : this.artistCollection,
|
||||
columns : this.columns,
|
||||
className : 'table table-hover'
|
||||
});
|
||||
|
@ -253,7 +254,7 @@ module.exports = Marionette.Layout.extend({
|
|||
|
||||
_showList : function() {
|
||||
this.currentView = new ListCollectionView({
|
||||
collection : this.seriesCollection
|
||||
collection : this.artistCollection
|
||||
});
|
||||
|
||||
this._renderView();
|
||||
|
@ -261,14 +262,15 @@ module.exports = Marionette.Layout.extend({
|
|||
|
||||
_showPosters : function() {
|
||||
this.currentView = new PosterCollectionView({
|
||||
collection : this.seriesCollection
|
||||
collection : this.artistCollection
|
||||
});
|
||||
|
||||
this._renderView();
|
||||
},
|
||||
|
||||
_renderView : function() {
|
||||
if (SeriesCollection.length === 0) {
|
||||
// Problem is this is calling before artistCollection has updated. Where are the promises with backbone?
|
||||
if (this.artistCollection.length === 0) {
|
||||
this.seriesRegion.show(new EmptyView());
|
||||
|
||||
this.toolbar.close();
|
||||
|
@ -282,13 +284,14 @@ module.exports = Marionette.Layout.extend({
|
|||
},
|
||||
|
||||
_fetchCollection : function() {
|
||||
this.seriesCollection.fetch();
|
||||
this.artistCollection.fetch();
|
||||
console.log('index page, collection: ', this.artistCollection);
|
||||
},
|
||||
|
||||
_setFilter : function(buttonContext) {
|
||||
var mode = buttonContext.model.get('key');
|
||||
|
||||
this.seriesCollection.setFilterMode(mode);
|
||||
this.artistCollection.setFilterMode(mode);
|
||||
},
|
||||
|
||||
_showToolbar : function() {
|
||||
|
@ -317,22 +320,22 @@ module.exports = Marionette.Layout.extend({
|
|||
|
||||
_showFooter : function() {
|
||||
var footerModel = new FooterModel();
|
||||
var series = SeriesCollection.models.length;
|
||||
var series = this.artistCollection.models.length;
|
||||
var episodes = 0;
|
||||
var episodeFiles = 0;
|
||||
var ended = 0;
|
||||
var continuing = 0;
|
||||
var monitored = 0;
|
||||
|
||||
_.each(SeriesCollection.models, function(model) {
|
||||
episodes += model.get('episodeCount');
|
||||
_.each(this.artistCollection.models, function(model) {
|
||||
episodes += model.get('episodeCount'); // TODO: Refactor to Seasons and Tracks
|
||||
episodeFiles += model.get('episodeFileCount');
|
||||
|
||||
if (model.get('status').toLowerCase() === 'ended') {
|
||||
/*if (model.get('status').toLowerCase() === 'ended') {
|
||||
ended++;
|
||||
} else {
|
||||
continuing++;
|
||||
}
|
||||
}*/
|
||||
|
||||
if (model.get('monitored')) {
|
||||
monitored++;
|
||||
|
|
|
@ -8,6 +8,12 @@
|
|||
max-width: 100%;
|
||||
}
|
||||
|
||||
.truncate {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
.edit-series-modal, .delete-series-modal {
|
||||
overflow : visible;
|
||||
|
||||
|
|
|
@ -25,8 +25,8 @@
|
|||
<dd>Consultation - <a href="https://github.com/galli-leo">Galli-leo</a></dd>
|
||||
|
||||
<dt>Feature Requests</dt>
|
||||
<dd><a href="http://feathub.com/mattman86/Lidarr/">feathub.com/mattman86/Lidarr</a></dd>
|
||||
<dd><a href="https://github.com/mattman86/Lidarr/issues">github.com/mattman86/Lidarr/issues</a> <b>(Please post issues on the forum first and not on github)</b></dd>
|
||||
<dd><a href="http://feathub.com/lidarr/Lidarr/">feathub.com/lidarr/Lidarr</a></dd>
|
||||
<dd><a href="https://github.com/lidarr/Lidarr/issues">github.com/lidarr/Lidarr/issues</a> <b>(Please post issues on the forum first and not on github)</b></dd>
|
||||
</dl>
|
||||
</fieldset>
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ var RouteBinder = require('./jQuery/RouteBinder');
|
|||
var SignalRBroadcaster = require('./Shared/SignalRBroadcaster');
|
||||
var NavbarLayout = require('./Navbar/NavbarLayout');
|
||||
var AppLayout = require('./AppLayout');
|
||||
var SeriesController = require('./Series/SeriesController');
|
||||
var ArtistController = require('./Artist/ArtistController');
|
||||
var Router = require('./Router');
|
||||
var ModalController = require('./Shared/Modal/ModalController');
|
||||
var ControlPanelController = require('./Shared/ControlPanel/ControlPanelController');
|
||||
|
@ -20,7 +20,7 @@ require('./Hotkeys/Hotkeys');
|
|||
require('./Shared/piwikCheck');
|
||||
require('./Shared/VersionChangeMonitor');
|
||||
|
||||
new SeriesController();
|
||||
new ArtistController();
|
||||
new ModalController();
|
||||
new ControlPanelController();
|
||||
new Router();
|
||||
|
|
|
@ -5,6 +5,8 @@ var vent = new Wreqr.EventAggregator();
|
|||
vent.Events = {
|
||||
SeriesAdded : 'series:added',
|
||||
SeriesDeleted : 'series:deleted',
|
||||
ArtistAdded : 'artist:added',
|
||||
ArtistDeleted : 'artist:deleted',
|
||||
CommandComplete : 'command:complete',
|
||||
ServerUpdated : 'server:updated',
|
||||
EpisodeFileDeleted : 'episodefile:deleted'
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue