1
0
mirror of https://github.com/Radarr/Radarr.git synced 2026-04-18 21:35:51 -04:00

Compare commits

...

15 Commits

Author SHA1 Message Date
Leonardo Galli 5a8d944397 Fixed sorting in movie list view. Also added new downloaded quality column.
Fixes #128
2017-01-12 23:07:09 +01:00
Leonardo Galli 0b70a5c315 Should fix ordering of releases. Fixes #147 (hopefully) 2017-01-12 22:12:32 +01:00
Leonardo Galli 91cded3b71 Merge pull request #202 from Radarr/onedr0p-patch-1
Update UserAgentBuilder.cs
2017-01-12 21:48:16 +01:00
Leonardo Galli 84112dc85b Should fix queueService failed while processing
(#178)
2017-01-12 21:48:02 +01:00
Devin Buhl b1b947ae7f Update UserAgentBuilder.cs
Make user-agent Radarr
2017-01-12 15:41:56 -05:00
Leonardo Galli 1b035f8657 Update Parser to support large array of Extended, Director, Collectors, ... Cut, Edition, etc.
Fixes #192
2017-01-12 19:21:09 +01:00
Devin Buhl 9d50f4d651 Merge pull request #199 from Radarr/patch/add-uhd-todefault-indexers
Add UHD to default movie categories for newsnab providers
2017-01-12 12:58:23 -05:00
Devin Buhl c06a6dc988 Add UHD to default movie categories for newsnab providers 2017-01-12 12:53:09 -05:00
Devin Buhl f198ca2b77 Merge pull request #177 from Radarr/patch/sort-title
Movies in list don't sort correctly #174
2017-01-12 01:08:32 -05:00
Devin Buhl 9cfc766889 Merge pull request #180 from wcomartin/Issue115
Changed Sonarr Branding to Radarr
2017-01-11 23:27:31 -05:00
William Comartin 5ef1e40403 Change Sonarr to Radarr in CLA.md and CONTRIBUTING.md 2017-01-11 22:02:54 -05:00
William Comartin c9e6835d7b Change Sonarr to Radarr in Help Text, and in Notification Text
Change sonarr log files to radarr log files
2017-01-11 21:59:13 -05:00
William Comartin 604cea00f6 Replace Sonarr With Radarr in UI Directory 2017-01-11 21:42:47 -05:00
Tim Turner b4782da1d1 Update sortValue when selecting movie for manual import 2017-01-11 19:29:22 -05:00
Devin Buhl d5504043c5 Movies in list don't sort correctly #174 2017-01-11 19:06:08 -05:00
58 changed files with 262 additions and 99 deletions
+2 -2
View File
@@ -1,6 +1,6 @@
# Sonarr Individual Contributor License Agreement #
# Radarr Individual Contributor License Agreement #
Thank you for your interest in contributing to Sonarr ("We" or "Us").
Thank you for your interest in contributing to Radarr ("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 ##
+4 -4
View File
@@ -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 Radarr 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 Radarr
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/Radarr/Radarr/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 Radarr'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
@@ -27,6 +27,7 @@ namespace NzbDrone.Api.Calendar
Get["/NzbDrone.ics"] = options => GetCalendarFeed();
Get["/Sonarr.ics"] = options => GetCalendarFeed();
Get["/Radarr.ics"] = options => GetCalendarFeed();
}
private Response GetCalendarFeed()
+1 -1
View File
@@ -23,7 +23,7 @@ namespace NzbDrone.Common
Console.WriteLine(" Commands:");
Console.WriteLine(" /{0} Install the application as a Windows Service ({1}).", StartupContext.INSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Uninstall already installed Windows Service ({1}).", StartupContext.UNINSTALL_SERVICE, ServiceProvider.NZBDRONE_SERVICE_NAME);
Console.WriteLine(" /{0} Don't open Sonarr in a browser", StartupContext.NO_BROWSER);
Console.WriteLine(" /{0} Don't open Radarr in a browser", StartupContext.NO_BROWSER);
Console.WriteLine(" <No Arguments> Run application in console mode.");
}
+3 -3
View File
@@ -9,12 +9,12 @@ namespace NzbDrone.Common.Http
static UserAgentBuilder()
{
UserAgent = string.Format("Sonarr/{0} ({1} {2})",
UserAgent = string.Format("Radarr/{0} ({1} {2})",
BuildInfo.Version,
OsInfo.Os, OsInfo.Version.ToString(2));
UserAgentSimplified = string.Format("Sonarr/{0}",
UserAgentSimplified = string.Format("Radarr/{0}",
BuildInfo.Version.ToString(2));
}
}
}
}
@@ -103,9 +103,9 @@ namespace NzbDrone.Common.Instrumentation
private static void RegisterAppFile(IAppFolderInfo appFolderInfo)
{
RegisterAppFile(appFolderInfo, "appFileInfo", "sonarr.txt", 5, LogLevel.Info);
RegisterAppFile(appFolderInfo, "appFileDebug", "sonarr.debug.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileTrace", "sonarr.trace.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileInfo", "radarr.txt", 5, LogLevel.Info);
RegisterAppFile(appFolderInfo, "appFileDebug", "radarr.debug.txt", 50, LogLevel.Off);
RegisterAppFile(appFolderInfo, "appFileTrace", "radarr.trace.txt", 50, LogLevel.Off);
}
private static LoggingRule RegisterAppFile(IAppFolderInfo appFolderInfo, string name, string fileName, int maxArchiveFiles, LogLevel minLogLevel)
@@ -0,0 +1,45 @@
using System.Data;
using FluentMigrator;
using NzbDrone.Core.Datastore.Migration.Framework;
namespace NzbDrone.Core.Datastore.Migration
{
[Migration(115)]
public class update_movie_sorttitle : NzbDroneMigrationBase
{
protected override void MainDbUpgrade()
{
// Create.Column("SortTitle").OnTable("Series").AsString().Nullable();
Execute.WithConnection(SetSortTitles);
}
private void SetSortTitles(IDbConnection conn, IDbTransaction tran)
{
using (IDbCommand getSeriesCmd = conn.CreateCommand())
{
getSeriesCmd.Transaction = tran;
getSeriesCmd.CommandText = @"SELECT Id, Title FROM Movies";
using (IDataReader seriesReader = getSeriesCmd.ExecuteReader())
{
while (seriesReader.Read())
{
var id = seriesReader.GetInt32(0);
var title = seriesReader.GetString(1);
var sortTitle = Parser.Parser.NormalizeTitle(title).ToLower();
using (IDbCommand updateCmd = conn.CreateCommand())
{
updateCmd.Transaction = tran;
updateCmd.CommandText = "UPDATE Movies SET SortTitle = ? WHERE Id = ?";
updateCmd.AddParameter(sortTitle);
updateCmd.AddParameter(id);
updateCmd.ExecuteNonQuery();
}
}
}
}
}
}
}
@@ -24,8 +24,6 @@ namespace NzbDrone.Core.DecisionEngine
{
CompareQuality,
CompareProtocol,
CompareEpisodeCount,
CompareEpisodeNumber,
ComparePeersIfTorrent,
CompareAgeIfUsenet,
CompareSize
@@ -56,6 +54,12 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareQuality(DownloadDecision x, DownloadDecision y)
{
if (x.IsForMovie && y.IsForMovie)
{
return CompareAll(CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.Movie.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedMovieInfo.Quality.Quality)),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.ParsedMovieInfo.Quality.Revision.Real),
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.ParsedMovieInfo.Quality.Revision.Version));
}
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Series.Profile.Value.Items.FindIndex(v => v.Quality == remoteEpisode.ParsedEpisodeInfo.Quality.Quality)),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Real),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.Quality.Revision.Version));
@@ -63,6 +67,7 @@ namespace NzbDrone.Core.DecisionEngine
private int CompareProtocol(DownloadDecision x, DownloadDecision y)
{
var result = CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
{
var delayProfile = _delayProfileService.BestForTags(remoteEpisode.Series.Tags);
@@ -70,13 +75,22 @@ namespace NzbDrone.Core.DecisionEngine
return downloadProtocol == delayProfile.PreferredProtocol;
});
if (x.IsForMovie)
{
result = CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var delayProfile = _delayProfileService.BestForTags(remoteEpisode.Movie.Tags);
var downloadProtocol = remoteEpisode.Release.DownloadProtocol;
return downloadProtocol == delayProfile.PreferredProtocol;
});
}
return result;
}
private int CompareEpisodeCount(DownloadDecision x, DownloadDecision y)
{
return CompareAll(CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.ParsedEpisodeInfo.FullSeason),
CompareByReverse(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Episodes.Count));
return 0;
}
private int CompareEpisodeNumber(DownloadDecision x, DownloadDecision y)
@@ -88,20 +102,20 @@ namespace NzbDrone.Core.DecisionEngine
{
// Different protocols should get caught when checking the preferred protocol,
// since we're dealing with the same series in our comparisions
if (x.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent ||
y.RemoteEpisode.Release.DownloadProtocol != DownloadProtocol.Torrent)
if (x.RemoteMovie.Release.DownloadProtocol != DownloadProtocol.Torrent ||
y.RemoteMovie.Release.DownloadProtocol != DownloadProtocol.Torrent)
{
return 0;
}
return CompareAll(
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var seeders = TorrentInfo.GetSeeders(remoteEpisode.Release);
return seeders.HasValue && seeders.Value > 0 ? Math.Round(Math.Log10(seeders.Value)) : 0;
}),
CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var peers = TorrentInfo.GetPeers(remoteEpisode.Release);
@@ -117,7 +131,7 @@ namespace NzbDrone.Core.DecisionEngine
return 0;
}
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode =>
return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode =>
{
var ageHours = remoteEpisode.Release.AgeHours;
var age = remoteEpisode.Release.Age;
@@ -145,7 +159,7 @@ namespace NzbDrone.Core.DecisionEngine
{
// TODO: Is smaller better? Smaller for usenet could mean no par2 files.
return CompareBy(x.RemoteEpisode, y.RemoteEpisode, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes()));
return CompareBy(x.RemoteMovie, y.RemoteMovie, remoteEpisode => remoteEpisode.Release.Size.Round(200.Megabytes()));
}
}
}
@@ -26,10 +26,10 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
private static readonly TorrentBlackholeSettingsValidator Validator = new TorrentBlackholeSettingsValidator();
[FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Sonarr will store the .torrent file")]
[FieldDefinition(0, Label = "Torrent Folder", Type = FieldType.Path, HelpText = "Folder in which Radarr will store the .torrent file")]
public string TorrentFolder { get; set; }
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Sonarr should import completed downloads")]
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Radarr should import completed downloads")]
public string WatchFolder { get; set; }
[DefaultValue(false)]
@@ -39,7 +39,7 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
[DefaultValue(false)]
[JsonProperty(DefaultValueHandling = DefaultValueHandling.Populate)]
[FieldDefinition(3, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Sonarr to Copy or Hardlink (depending on settings/system configuration)")]
[FieldDefinition(3, Label = "Read Only", Type = FieldType.Checkbox, HelpText = "Instead of moving files this will instruct Radarr to Copy or Hardlink (depending on settings/system configuration)")]
public bool ReadOnly { get; set; }
public NzbDroneValidationResult Validate()
@@ -19,10 +19,10 @@ namespace NzbDrone.Core.Download.Clients.Blackhole
{
private static readonly UsenetBlackholeSettingsValidator Validator = new UsenetBlackholeSettingsValidator();
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Sonarr will store the .nzb file")]
[FieldDefinition(0, Label = "Nzb Folder", Type = FieldType.Path, HelpText = "Folder in which Radarr will store the .nzb file")]
public string NzbFolder { get; set; }
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Sonarr should import completed downloads")]
[FieldDefinition(1, Label = "Watch Folder", Type = FieldType.Path, HelpText = "Folder from which Radarr should import completed downloads")]
public string WatchFolder { get; set; }
public NzbDroneValidationResult Validate()
@@ -306,7 +306,7 @@ namespace NzbDrone.Core.Download.Clients.Deluge
{
return new NzbDroneValidationFailure("TvCategory", "Configuration of label failed")
{
DetailedDescription = "Sonarr as unable to add the label to Deluge."
DetailedDescription = "Radarr as unable to add the label to Deluge."
};
}
}
@@ -43,7 +43,7 @@ namespace NzbDrone.Core.Download.Clients.NzbVortex
[FieldDefinition(2, Label = "API Key", Type = FieldType.Textbox)]
public string ApiKey { get; set; }
[FieldDefinition(3, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(3, Label = "Group", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; }
[FieldDefinition(4, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbVortexPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
@@ -337,7 +337,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
return new NzbDroneValidationFailure(string.Empty, "NzbGet setting KeepHistory should be greater than 0")
{
InfoLink = string.Format("http://{0}:{1}/", Settings.Host, Settings.Port),
DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Sonarr from seeing completed downloads."
DetailedDescription = "NzbGet setting KeepHistory is set to 0. Which prevents Radarr from seeing completed downloads."
};
}
@@ -45,7 +45,7 @@ namespace NzbDrone.Core.Download.Clients.Nzbget
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(NzbgetPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
@@ -372,7 +372,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("Version", "Sabnzbd develop version, assuming version 1.1.0 or higher.")
{
IsWarning = true,
DetailedDescription = "Sonarr may not be able to support new features added to SABnzbd when running develop versions."
DetailedDescription = "Radarr may not be able to support new features added to SABnzbd when running develop versions."
};
}
@@ -431,7 +431,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("", "Disable 'Check before download' option in Sabnbzd")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/switches/", Settings.Host, Settings.Port),
DetailedDescription = "Using Check before download affects Sonarr ability to track new downloads. Also Sabnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective."
DetailedDescription = "Using Check before download affects Radarr ability to track new downloads. Also Sabnzbd recommends 'Abort jobs that cannot be completed' instead since it's more effective."
};
}
@@ -450,7 +450,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Enable Job folders")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/categories/", Settings.Host, Settings.Port),
DetailedDescription = "Sonarr prefers each download to have a separate folder. With * appended to the Folder/Path Sabnzbd will not create these job folders. Go to Sabnzbd to fix it."
DetailedDescription = "Radarr prefers each download to have a separate folder. With * appended to the Folder/Path Sabnzbd will not create these job folders. Go to Sabnzbd to fix it."
};
}
}
@@ -475,7 +475,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable TV Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd TV Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it."
DetailedDescription = "You must disable Sabnzbd TV Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
};
}
}
@@ -489,7 +489,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable Movie Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd Movie Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it."
DetailedDescription = "You must disable Sabnzbd Movie Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
};
}
}
@@ -503,7 +503,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
return new NzbDroneValidationFailure("TvCategory", "Disable Date Sorting")
{
InfoLink = string.Format("http://{0}:{1}/sabnzbd/config/sorting/", Settings.Host, Settings.Port),
DetailedDescription = "You must disable Sabnzbd Date Sorting for the category Sonarr uses to prevent import issues. Go to Sabnzbd to fix it."
DetailedDescription = "You must disable Sabnzbd Date Sorting for the category Radarr uses to prevent import issues. Go to Sabnzbd to fix it."
};
}
}
@@ -58,7 +58,7 @@ namespace NzbDrone.Core.Download.Clients.Sabnzbd
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; }
[FieldDefinition(6, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(SabnzbdPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
@@ -232,7 +232,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
_logger.Error(ex, ex.Message);
return new NzbDroneValidationFailure("Username", "Authentication failure")
{
DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Sonarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name)
DetailedDescription = string.Format("Please verify your username and password. Also verify if the host running Radarr isn't blocked from accessing {0} by WhiteList limitations in the {0} configuration.", Name)
};
}
catch (WebException ex)
@@ -50,7 +50,7 @@ namespace NzbDrone.Core.Download.Clients.Transmission
[FieldDefinition(4, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")]
[FieldDefinition(5, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional. Creates a [category] subdirectory in the output directory.")]
public string TvCategory { get; set; }
[FieldDefinition(6, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default Transmission location")]
@@ -49,7 +49,7 @@ namespace NzbDrone.Core.Download.Clients.RTorrent
[FieldDefinition(5, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional.")]
[FieldDefinition(6, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional.")]
public string TvCategory { get; set; }
[FieldDefinition(7, Label = "Directory", Type = FieldType.Textbox, Advanced = true, HelpText = "Optional location to put downloads in, leave blank to use the default rTorrent location")]
@@ -38,7 +38,7 @@ namespace NzbDrone.Core.Download.Clients.UTorrent
[FieldDefinition(3, Label = "Password", Type = FieldType.Password)]
public string Password { get; set; }
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Sonarr avoids conflicts with unrelated downloads, but it's optional")]
[FieldDefinition(4, Label = "Category", Type = FieldType.Textbox, HelpText = "Adding a category specific to Radarr avoids conflicts with unrelated downloads, but it's optional")]
public string TvCategory { get; set; }
[FieldDefinition(5, Label = "Recent Priority", Type = FieldType.Select, SelectOptions = typeof(UTorrentPriority), HelpText = "Priority to use when grabbing episodes that aired within the last 14 days")]
@@ -109,7 +109,11 @@ namespace NzbDrone.Core.Download
if (movie == null)
{
movie = _movieService.GetMovie(historyItem.MovieId);
if (historyItem != null)
{
movie = _movieService.GetMovie(historyItem.MovieId);
}
if (movie == null)
{
@@ -72,7 +72,7 @@ namespace NzbDrone.Core.Download
if (grabbedItems.Empty())
{
trackedDownload.Warn("Download wasn't grabbed by sonarr, skipping");
trackedDownload.Warn("Download wasn't grabbed by Radarr, skipping");
return;
}
@@ -60,7 +60,7 @@ namespace NzbDrone.Core.Indexers.Newznab
public NewznabSettings()
{
Categories = new[] { 2030, 2035, 2040, 2050 };
Categories = new[] { 2030, 2035, 2040, 2045, 2050 };
AnimeCategories = Enumerable.Empty<int>();
}
@@ -327,7 +327,7 @@ namespace NzbDrone.Core.MetadataSource.SkyHook
imdbMovie.TmdbId = result.id;
try
{
imdbMovie.SortTitle = result.title;
imdbMovie.SortTitle = Parser.Parser.NormalizeTitle(result.title);
imdbMovie.Title = result.title;
string titleSlug = result.title;
imdbMovie.TitleSlug = titleSlug.ToLower().Replace(" ", "-");
@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Notifications.Email
public override void OnGrab(GrabMessage grabMessage)
{
const string subject = "Sonarr [TV] - Grabbed";
const string subject = "Radarr [TV] - Grabbed";
var body = string.Format("{0} sent to queue.", grabMessage.Message);
_emailService.SendEmail(Settings, subject, body);
@@ -26,7 +26,7 @@ namespace NzbDrone.Core.Notifications.Email
public override void OnDownload(DownloadMessage message)
{
const string subject = "Sonarr [TV] - Downloaded";
const string subject = "Radarr [TV] - Downloaded";
var body = string.Format("{0} Downloaded and sorted.", message.Message);
_emailService.SendEmail(Settings, subject, body);
@@ -64,7 +64,7 @@ namespace NzbDrone.Core.Notifications.Email
try
{
SendEmail(settings, "Sonarr - Test Notification", body);
SendEmail(settings, "Radarr - Test Notification", body);
}
catch (Exception ex)
{
+2 -2
View File
@@ -18,14 +18,14 @@ namespace NzbDrone.Core.Notifications.Join
public override void OnGrab(GrabMessage grabMessage)
{
const string title = "Sonarr - Episode Grabbed";
const string title = "Radarr - Episode Grabbed";
_proxy.SendNotification(title, grabMessage.Message, Settings);
}
public override void OnDownload(DownloadMessage message)
{
const string title = "Sonarr - Episode Downloaded";
const string title = "Radarr - Episode Downloaded";
_proxy.SendNotification(title, message.Message, Settings);
}
@@ -18,7 +18,7 @@ namespace NzbDrone.Core.Notifications.MediaBrowser
public override void OnGrab(GrabMessage grabMessage)
{
const string title = "Sonarr - Grabbed";
const string title = "Radarr - Grabbed";
if (Settings.Notify)
{
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.MediaBrowser
public override void OnDownload(DownloadMessage message)
{
const string title = "Sonarr - Downloaded";
const string title = "Radarr - Downloaded";
if (Settings.Notify)
{
@@ -18,13 +18,13 @@ namespace NzbDrone.Core.Notifications.Plex
public override void OnGrab(GrabMessage grabMessage)
{
const string header = "Sonarr [TV] - Grabbed";
const string header = "Radarr [TV] - Grabbed";
_plexClientService.Notify(Settings, header, grabMessage.Message);
}
public override void OnDownload(DownloadMessage message)
{
const string header = "Sonarr [TV] - Downloaded";
const string header = "Radarr [TV] - Downloaded";
_plexClientService.Notify(Settings, header, message.Message);
}
@@ -23,14 +23,14 @@ namespace NzbDrone.Core.Notifications.Plex
public override void OnGrab(GrabMessage grabMessage)
{
const string header = "Sonarr - Grabbed";
const string header = "Radarr - Grabbed";
Notify(Settings, header, grabMessage.Message);
}
public override void OnDownload(DownloadMessage message)
{
const string header = "Sonarr - Downloaded";
const string header = "Radarr - Downloaded";
Notify(Settings, header, message.Message);
}
@@ -98,7 +98,7 @@ namespace NzbDrone.Core.Notifications.Plex
{
if (version >= new Version(1, 3, 0) && version < new Version(1, 3, 1))
{
throw new PlexVersionException("Found version {0}, upgrade to PMS 1.3.1 to fix library updating and then restart Sonarr", version);
throw new PlexVersionException("Found version {0}, upgrade to PMS 1.3.1 to fix library updating and then restart Radarr", version);
}
}
@@ -18,14 +18,14 @@ namespace NzbDrone.Core.Notifications.PushBullet
public override void OnGrab(GrabMessage grabMessage)
{
const string title = "Sonarr - Episode Grabbed";
const string title = "Radarr - Episode Grabbed";
_proxy.SendNotification(title, grabMessage.Message, Settings);
}
public override void OnDownload(DownloadMessage message)
{
const string title = "Sonarr - Episode Downloaded";
const string title = "Radarr - Episode Downloaded";
_proxy.SendNotification(title, message.Message, Settings);
}
@@ -92,7 +92,7 @@ namespace NzbDrone.Core.Notifications.PushBullet
{
try
{
const string title = "Sonarr - Test Notification";
const string title = "Radarr - Test Notification";
const string body = "This is a test message from Radarr";
SendNotification(title, body, settings);
@@ -28,7 +28,7 @@ namespace NzbDrone.Core.Notifications.Pushalot
[FieldDefinition(1, Label = "Priority", Type = FieldType.Select, SelectOptions = typeof(PushalotPriority))]
public int Priority { get; set; }
[FieldDefinition(2, Label = "Image", Type = FieldType.Checkbox, HelpText = "Include Sonarr logo with notifications")]
[FieldDefinition(2, Label = "Image", Type = FieldType.Checkbox, HelpText = "Include Radarr logo with notifications")]
public bool Image { get; set; }
public bool IsValid => !string.IsNullOrWhiteSpace(AuthToken);
@@ -101,7 +101,7 @@ namespace NzbDrone.Core.Notifications.Slack
{
try
{
var message = $"Test message from Sonarr posted at {DateTime.Now}";
var message = $"Test message from Radarr posted at {DateTime.Now}";
var payload = new SlackPayload
{
IconEmoji = Settings.Icon,
@@ -125,7 +125,7 @@ namespace NzbDrone.Core.Notifications.Twitter
{
try
{
var body = "Sonarr: Test Message @ " + DateTime.Now;
var body = "Radarr: Test Message @ " + DateTime.Now;
SendNotification(body, settings);
}
+2 -2
View File
@@ -23,14 +23,14 @@ namespace NzbDrone.Core.Notifications.Xbmc
public override void OnGrab(GrabMessage grabMessage)
{
const string header = "Sonarr - Grabbed";
const string header = "Radarr - Grabbed";
Notify(Settings, header, grabMessage.Message);
}
public override void OnDownload(DownloadMessage message)
{
const string header = "Sonarr - Downloaded";
const string header = "Radarr - Downloaded";
Notify(Settings, header, message.Message);
UpdateAndClean(message.Series, message.OldFiles.Any());
+1
View File
@@ -183,6 +183,7 @@
<Compile Include="Datastore\Migration\002_remove_tvrage_imdb_unique_constraint.cs" />
<Compile Include="Datastore\Migration\003_remove_clean_title_from_scene_mapping.cs" />
<Compile Include="Datastore\Migration\004_updated_history.cs" />
<Compile Include="Datastore\Migration\115_update_movie_sorttitle.cs" />
<Compile Include="Datastore\Migration\111_remove_bitmetv.cs" />
<Compile Include="Datastore\Migration\112_remove_torrentleech.cs" />
<Compile Include="Datastore\Migration\114_remove_fanzub.cs" />
+2 -8
View File
@@ -18,16 +18,10 @@ namespace NzbDrone.Core.Parser
private static readonly Regex[] ReportMovieTitleRegex = new[]
{
//Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.Special.Edition.2011
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\w+\.?edition))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)",
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\.?((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX)))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Special, Despecialized, etc. Edition Movies, e.g: Mission.Impossible.3.2011.Special.Edition //TODO: Seems to slow down parsing heavily!
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((\w+\.?){1,3}edition))",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Cut Movies, e.g: Mission.Impossible.3.Directors.Cut.2011
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<edition>(\w+\.?cut))\.(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Cut Movies, e.g: Mission.Impossible.3.2011.Directors.Cut
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((\w+\.?){1,3}cut))",
new Regex(@"^(?<title>.+?)?(?:(?:[-_\W](?<![)\[!]))*(?<year>(19|20)\d{2}(?!p|i|\d+|\]|\W\d+)))+(\W+|_|$)(?!\\)(?<edition>((Extended.|Ultimate.)?(Director.?s|Collector.?s|Theatrical|Ultimate|Final|Extended|Rogue|Special|Despecialized).(Cut|Edition|Version)|Extended|Uncensored|Remastered|Unrated|Uncut|IMAX))",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Normal movie format, e.g: Mission.Impossible.3.2011
+1 -1
View File
@@ -24,7 +24,7 @@ namespace Radarr.Host
SecurityProtocolPolicy.Register();
X509CertificateValidationPolicy.Register();
Logger.Info("Starting Sonarr - {0} - Version {1}", Assembly.GetCallingAssembly().Location, Assembly.GetExecutingAssembly().GetName().Version);
Logger.Info("Starting Radarr - {0} - Version {1}", Assembly.GetCallingAssembly().Location, Assembly.GetExecutingAssembly().GetName().Version);
if (!PlatformValidation.IsValidate(userAlert))
{
@@ -91,7 +91,7 @@
{{/if_eq}}
{{#if_eq reason compare="MissingFromDisk"}}
Sonarr was unable to find the file on disk so it was removed
Radarr was unable to find the file on disk so it was removed
{{/if_eq}}
{{#if_eq reason compare="Upgrade"}}
+1 -1
View File
@@ -1,7 +1,7 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Sonarr Calendar feed</h3>
<h3>Radarr Calendar feed</h3>
</div>
<div class="modal-body edit-series-modal">
<div class="form-horizontal">
+34
View File
@@ -0,0 +1,34 @@
var Backgrid = require('backgrid');
var ProfileCollection = require('../Profile/ProfileCollection');
var _ = require('underscore');
module.exports = Backgrid.Cell.extend({
className : 'profile-cell',
_originalInit : Backgrid.Cell.prototype.initialize,
initialize : function () {
this._originalInit.apply(this, arguments);
this.listenTo(ProfileCollection, 'sync', this.render);
},
render : function() {
this.$el.empty();
if (this.model.get("movieFile")) {
var profileId = this.model.get("movieFile").quality.quality.id;
var profile = _.findWhere(ProfileCollection.models, { id : profileId });
if (profile) {
this.$el.html(profile.get('name'));
} else {
this.$el.html(this.model.get("movieFile").quality.quality.name);
}
}
return this;
}
});
+4
View File
@@ -3,4 +3,8 @@ var TemplatedCell = require('./TemplatedCell');
module.exports = TemplatedCell.extend({
className : 'movie-title-cell',
template : 'Cells/MovieDownloadStatusTemplate',
sortKey : function(model) {
debugger;
return 0;
}
});
@@ -21,7 +21,7 @@ module.exports = Marionette.Layout.extend({
name : 'title',
label : 'Title',
cell : 'String',
sortValue : 'sortTitle'
sortValue : 'title'
}
],
+14 -1
View File
@@ -13,6 +13,7 @@ var MovieLinksCell = require('../../Cells/MovieLinksCell');
var MovieActionCell = require('../../Cells/MovieActionCell');
var MovieStatusCell = require('../../Cells/MovieStatusCell');
var MovieDownloadStatusCell = require('../../Cells/MovieDownloadStatusCell');
var DownloadedQualityCell = require('../../Cells/DownloadedQualityCell');
var FooterView = require('./FooterView');
var FooterModel = require('./FooterModel');
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
@@ -40,6 +41,11 @@ module.exports = Marionette.Layout.extend({
cell : MovieTitleCell,
cellValue : 'this',
},
{
name : "downloadedQuality",
label : "Downloaded",
cell : DownloadedQualityCell,
},
{
name : 'profileId',
label : 'Profile',
@@ -54,12 +60,19 @@ module.exports = Marionette.Layout.extend({
name : 'this',
label : 'Links',
cell : MovieLinksCell,
className : "movie-links-cell"
className : "movie-links-cell",
sortable : false,
},
{
name : "this",
label : "Status",
cell : MovieDownloadStatusCell,
sortValue : function(m, k) {
if (m.get("downloaded")) {
return -1;
}
return 0;
}
},
{
name : 'this',
+26
View File
@@ -9,5 +9,31 @@ module.exports = Backbone.Model.extend({
episodeCount : 0,
isExisting : false,
status : 0
},
getStatus : function() {
var monitored = this.get("monitored");
var status = this.get("status");
var inCinemas = this.get("inCinemas");
var date = new Date(inCinemas);
var timeSince = new Date().getTime() - date.getTime();
var numOfMonths = timeSince / 1000 / 60 / 60 / 24 / 30;
if (status === "announced") {
return "announced"
}
if (numOfMonths < 3 && numOfMonths > 0) {
return "inCinemas";
}
if (status === 'released') {
return "released";
}
if (numOfMonths > 3) {
return "released";//TODO: Update for PreDB.me
}
}
});
+31 -4
View File
@@ -15,10 +15,10 @@ var Collection = PageableCollection.extend({
tableName : 'movie',
state : {
sortKey : 'title',
sortKey : 'sortTitle',
order : 1,
pageSize : 100000,
secondarySortKey : 'title',
secondarySortKey : 'sortTitle',
secondarySortOrder : -1
},
@@ -73,9 +73,28 @@ var Collection = PageableCollection.extend({
sortMappings : {
title : {
sortKey : 'title'
sortKey : 'sortTitle'
},
statusWeight : {
sortValue : function(model, attr) {
if (model.getStatus() == "released") {
return 1;
}
if (model.getStatus() == "inCinemas") {
return 0;
}
return -1;
}
},
downloadedQuality : {
sortValue : function(model, attr) {
if (model.get("movieFile")) {
return 1000-model.get("movieFile").quality.quality.id;
}
return -1;
}
},
nextAiring : {
sortValue : function(model, attr, order) {
var nextAiring = model.get(attr);
@@ -91,7 +110,15 @@ var Collection = PageableCollection.extend({
return Number.MAX_VALUE;
}
},
status: {
sortValue : function(model, attr) {
debugger;
if (model.get("downloaded")) {
return -1;
}
return 0;
}
},
percentOfEpisodes : {
sortValue : function(model, attr) {
var percentOfEpisodes = model.get(attr);
+2 -2
View File
@@ -7,12 +7,12 @@
<span class="icon-sonarr-navbar-collapsed fa-lg"></span>
</button>
<a class="navbar-brand" href="{{UrlBase}}/">
<!--<img src="{{UrlBase}}/Content/Images/logo.png?v=2" alt="Sonarr">-->
<!--<img src="{{UrlBase}}/Content/Images/logo.png?v=2" alt="Radarr">-->
<img src="{{UrlBase}}/Content/Images/logos/128.png" class="visible-lg"/>
<img src="{{UrlBase}}/Content/Images/logos/64.png" class="visible-md visible-sm"/>
<span class="visible-xs">
<img src="{{UrlBase}}/Content/Images/logos/32.png"/>
<span class="logo-text">sonarr</span>
<span class="logo-text">Radarr</span>
</span>
</a>
@@ -27,7 +27,7 @@
</label>
<span class="help-inline-checkbox">
<i class="icon-sonarr-form-info" title="Should Sonarr download episodes for this series?"/>
<i class="icon-sonarr-form-info" title="Should Radarr download episodes for this series?"/>
</span>
</div>
</div>
@@ -10,7 +10,7 @@
<div class="modal-body remotepath-mapping-modal">
<div class="form-horizontal">
<div>
<p>Use this feature if you have a remotely running Download Client. Sonarr will use the information provided to translate the paths provided by the Download Client API to something Sonarr can access and import.</p>
<p>Use this feature if you have a remotely running Download Client. Radarr will use the information provided to translate the paths provided by the Download Client API to something Radarr can access and import.</p>
</div>
<div class="form-group">
<label class="col-sm-3 control-label">Host</label>
@@ -40,7 +40,7 @@
<label class="col-sm-3 control-label">Local Path</label>
<div class="col-sm-1 col-sm-push-5 help-inline">
<i class="icon-sonarr-form-info" title="Path that Sonarr should use to access the same directory remotely." />
<i class="icon-sonarr-form-info" title="Path that Radarr should use to access the same directory remotely." />
</div>
<div class="col-sm-5 col-sm-pull-1">
@@ -100,7 +100,7 @@
</label>
<span class="help-inline-checkbox">
<i class="icon-sonarr-form-info" title="Open a web browser and navigate to Sonarr homepage on app start. Has no effect if installed as a windows service"/>
<i class="icon-sonarr-form-info" title="Open a web browser and navigate to Radarr homepage on app start. Has no effect if installed as a windows service"/>
</span>
</div>
</div>
@@ -114,7 +114,7 @@
<div class="col-sm-1 col-sm-push-4 help-inline">
<i class="icon-sonarr-form-warning" title="Requires restart to take effect"/>
<i class="icon-sonarr-form-info" title="Require Username and Password to access Sonarr"/>
<i class="icon-sonarr-form-info" title="Require Username and Password to access Radarr"/>
</div>
<div class="col-sm-4 col-sm-pull-1">
@@ -308,7 +308,7 @@
</label>
<span class="help-inline-checkbox">
<i class="icon-sonarr-form-info" title="Send anonymous information about your browser and which parts of the web interface you use to Sonarr servers. We use this information to prioritize features and browser support. We will NEVER include any personal information or any information that could identify you."/>
<i class="icon-sonarr-form-info" title="Send anonymous information about your browser and which parts of the web interface you use to Radarr servers. We use this information to prioritize features and browser support. We will NEVER include any personal information or any information that could identify you."/>
<i class="icon-sonarr-form-warning" title="Requires restart to take effect"/>
</span>
</div>
@@ -5,7 +5,7 @@
</div>
<div class="modal-body">
<div class="alert alert-info">
Sonarr supports any indexer that uses the Newznab standard, as well as other indexers listed below.<br/>
Radarr supports any indexer that uses the Newznab standard, as well as other indexers listed below.<br/>
For more information on the individual indexers, click on the info buttons.
</div>
<div class="add-indexer add-thingies">
@@ -61,7 +61,7 @@
</label>
<span class="help-inline-checkbox">
<i class="icon-sonarr-form-info" title="Extract video information such as resolution, runtime and codec information from files. This requires Sonarr to read parts of the file which may cause high disk or network activity during scans."/>
<i class="icon-sonarr-form-info" title="Extract video information such as resolution, runtime and codec information from files. This requires Radarr to read parts of the file which may cause high disk or network activity during scans."/>
</span>
</div>
</div>
@@ -28,7 +28,7 @@
<label class="col-sm-3 control-label">File chmod mask</label>
<div class="col-sm-1 col-sm-push-4 help-inline">
<i class="icon-sonarr-form-info" title="Octal, applied to media files when imported/renamed by Sonarr"/>
<i class="icon-sonarr-form-info" title="Octal, applied to media files when imported/renamed by Radarr"/>
</div>
<div class="col-sm-4 col-sm-pull-1">
@@ -40,7 +40,7 @@
<label class="col-sm-3 control-label">Folder chmod mask</label>
<div class="col-sm-1 col-sm-push-4 help-inline">
<i class="icon-sonarr-form-info" title="Octal, applied to series/season folders created by Sonarr"/>
<i class="icon-sonarr-form-info" title="Octal, applied to series/season folders created by Radarr"/>
</div>
<div class="col-sm-4 col-sm-pull-1">
@@ -40,6 +40,6 @@
</div>
<div class="col-sm-1 help-inline">
<i class="icon-sonarr-form-info" title="Once this quality is reached Sonarr will no longer download episodes"/>
<i class="icon-sonarr-form-info" title="Once this quality is reached Radarr will no longer download episodes"/>
</div>
</div>
+1 -1
View File
@@ -40,7 +40,7 @@
<link rel="mask-icon" href="/Content/Images/safari/logo.svg" color="#35c5f4">
<link rel="icon" type="image/ico" href="/Content/Images/favicon.ico"/>
<link rel="alternate" type="text/calendar" title="iCalendar feed for Sonarr" href="/feed/calendar/NzbDrone.ics"/>
<link rel="alternate" type="text/calendar" title="iCalendar feed for Radarr" href="/feed/calendar/NzbDrone.ics"/>
</head>
<body>
<div class="container">
+1 -1
View File
@@ -27,7 +27,7 @@
<div class="container-fluid main-region" id="main-region">
<div class="col-md-2 col-md-offset-5">
<form name="login" id="login" class="login" method="POST">
<h2><img src="/Content/Images/logos/32.png" alt=""/> Sonarr</h2>
<h2><img src="/Content/Images/logos/32.png" alt=""/> Radarr</h2>
<div class="form-group">
<label for="username" class="sr-only">Email address</label>
<input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>