1
0
mirror of https://github.com/Radarr/Radarr.git synced 2024-11-09 20:42:37 +01:00

Merge branch 'markus' into kay.one

Conflicts:
	NzbDrone.Core.Test/ProviderTests/LogProviderTest.cs
This commit is contained in:
kay.one 2011-10-20 22:08:55 -07:00
commit 651a63edea
24 changed files with 330 additions and 96 deletions

View File

@ -65,6 +65,7 @@ public void ParseTitle_single(string postTitle, string title, int seasonNumber,
[TestCase(@"D:\shares\TV Shows\Battlestar Galactica (2003)\Season 2\S02E21.avi", 2, 21)]
[TestCase("C:/Test/TV/Chuck.4x05.HDTV.XviD-LOL", 4, 5)]
[TestCase(@"P:\TV Shows\House\Season 6\S06E13 - 5 to 9 - 720p BluRay.mkv", 6, 13)]
[TestCase(@"S:\TV Drop\House - 10x11 - Title [SDTV]\1011 - Title.avi", 10, 11)]
public void PathParse_tests(string path, int season, int episode)
{
var result = Parser.ParsePath(path);

View File

@ -266,4 +266,4 @@ public void pagedLogs()
logs.TotalItems.Should().Be(100);
}
}
}
}

View File

@ -628,5 +628,51 @@ public void Get_Series_NextAiring_skip_ignored()
series.Should().HaveCount(1);
series[0].NextAiring.Should().Be(DateTime.Today.AddMonths(1));
}
[Test]
public void SearchForSeries_should_return_results_that_start_with_query()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
var db = MockLib.GetEmptyDatabase();
mocker.SetConstant(db);
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
var fakeSeries = Builder<Series>.CreateListOfSize(10)
.WhereAll()
.Have(e => e.QualityProfileId = fakeQuality.QualityProfileId)
.Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
var series = mocker.Resolve<SeriesProvider>().SearchForSeries("Titl");
//Assert
series.Should().HaveCount(10);
}
[Test]
public void SearchForSeries_should_not_return_results_that_do_not_start_with_query()
{
var mocker = new AutoMoqer(MockBehavior.Strict);
var db = MockLib.GetEmptyDatabase();
mocker.SetConstant(db);
var fakeQuality = Builder<QualityProfile>.CreateNew().Build();
var fakeSeries = Builder<Series>.CreateListOfSize(10)
.WhereAll()
.Have(e => e.QualityProfileId = fakeQuality.QualityProfileId)
.Build();
db.InsertMany(fakeSeries);
db.Insert(fakeQuality);
//Act
var series = mocker.Resolve<SeriesProvider>().SearchForSeries("NotATitle");
//Assert
series.Should().HaveCount(0);
}
}
}

View File

@ -39,8 +39,12 @@ public static class Parser
new Regex(@"^(?<title>.*?)(?:\W?S?(?<season>\d{1,2}(?!\d+))(?:(?:\-|[ex]|\s)+(?<episode>\d+))+)+\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Supports 1103/1113 naming
new Regex(@"^(?<title>.+?)?(?:\W?(?<season>\d{2})(?<episode>\d{2}))+\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Supports 103/113 naming
new Regex(@"^(?<title>.+?)?(?:\W?(?<season>\d+)(?<episode>\d{2}))+\W?(?!\\)",
new Regex(@"^(?<title>.+?)?(?:\W?(?<season>\d{1})(?<episode>\d{2}))+\W?(?!\\)",
RegexOptions.IgnoreCase | RegexOptions.Compiled),
//Supports Season only releases

View File

@ -114,8 +114,7 @@ public virtual void ProcessDownload(DirectoryInfo subfolderInfo)
//Rename the Directory so it's not processed again.
_diskProvider.MoveDirectory(subfolderInfo.FullName,
Path.Combine(subfolderInfo.Parent.FullName,
"_NzbDrone_InvalidSeries_" + subfolderInfo.Name));
GetNewFolderNameWithPostDownloadStatus(subfolderInfo, PostDownloadStatusType.InvalidSeries));
return;
}
@ -132,9 +131,8 @@ public virtual void ProcessDownload(DirectoryInfo subfolderInfo)
if (importedFiles.Count == 0)
{
Logger.Warn("Unable to Import new download [{0}], unable to parse episode file(s).", subfolderInfo.FullName);
_diskProvider.MoveDirectory(subfolderInfo.FullName,
Path.Combine(subfolderInfo.Parent.FullName,
"_NzbDrone_ParseError_" + subfolderInfo.Name));
_diskProvider.MoveDirectory(subfolderInfo.FullName,
GetNewFolderNameWithPostDownloadStatus(subfolderInfo, PostDownloadStatusType.ParseError));
}
//Unknown Error Importing (Possibly a lesser quality than episode currently on disk)
@ -142,9 +140,7 @@ public virtual void ProcessDownload(DirectoryInfo subfolderInfo)
{
Logger.Warn("Unable to Import new download [{0}].", subfolderInfo.FullName);
_diskProvider.MoveDirectory(subfolderInfo.FullName,
Path.Combine(subfolderInfo.Parent.FullName,
"_NzbDrone_" + subfolderInfo.Name));
_diskProvider.MoveDirectory(subfolderInfo.FullName, GetNewFolderNameWithPostDownloadStatus(subfolderInfo, PostDownloadStatusType.Unknown));
}
}
}

View File

@ -186,6 +186,21 @@ public virtual bool SeriesPathExists(string cleanPath)
return false;
}
public virtual List<Series> SearchForSeries(string title)
{
var query = String.Format(@"SELECT * FROM Series
INNER JOIN QualityProfiles ON Series.QualityProfileId = QualityProfiles.QualityProfileId
WHERE Title LIKE '{0}%'", title);
// var series = _database.Fetch<Series, QualityProfile>(@"SELECT * FROM Series
// INNER JOIN QualityProfiles ON Series.QualityProfileId = QualityProfiles.QualityProfileId
// WHERE Title LIKE '@0%'", title);
var series = _database.Fetch<Series, QualityProfile>(query);
return series;
}
/// <summary>
/// Cleans up the AirsTime Component from TheTVDB since it can be garbage that comes in.
/// </summary>

View File

@ -235,4 +235,11 @@ select, button, input[type="button"], input[type="submit"], input[type="reset"]
{
min-width: 60px;
width: auto;
}
#quickAdd
{
position: fixed;
top: 30px;
right: 15px;
}

View File

@ -0,0 +1,67 @@
.top-slider {
position: absolute;
opacity: 0.85;
width: 300px;
}
.sliderButton {
border: 0;
}
.sliderContent {
background-color:#333333;
text-align:center;
width: 100%;
color:#FFFFFF;
font-weight:bold;
margin: 0px;
display: none;
@*Rounded Edges*@
border:1px solid #444444;
-moz-border-radius-bottomright: 8px;
-webkit-border-bottom-right-radius: 8px;
-moz-border-radius-bottomleft: 8px;
-webkit-border-bottom-left-radius: 8px;
}
.openCloseWrapper {
width: 100%;
text-align: center;
font-size:12px;
font-weight:bold;
color:#FFFFFF;
}
.sliderButtonContainer {
width: 70px;
margin-left:auto;
margin-right:auto;
background-color:#333333;
cursor:pointer;
@*Rounded Edges*@
border: 1px solid #444444;
border-top: 0px;
-moz-border-radius-bottomright: 10px;
-webkit-border-bottom-right-radius: 10px;
-moz-border-radius-bottomleft: 10px;
-webkit-border-bottom-left-radius: 10px;
}
.sliderImage {
width: 16px;
height: 16px;
display: inline-block;
margin-bottom: -3px;
margin-right: -5px;
margin-left: 2px;
}
.sliderClosed {
background:url('../../Content/Images/ui-icons_2e83ff_256x240.png') -64px -16px no-repeat;
}
.sliderOpened {
background:url('../../Content/Images/ui-icons_2e83ff_256x240.png') 0px -16px no-repeat;
}

View File

@ -53,9 +53,7 @@ public ActionResult AddNew()
var defaultQuality = _configProvider.DefaultQualityProfile;
var qualityProfiles = _qualityProvider.All();
ViewData["qualityList"] = qualityProfiles;
ViewData["quality"] = new SelectList(
ViewData["qualityProfiles"] = new SelectList(
qualityProfiles,
"QualityProfileId",
"Name",
@ -132,6 +130,19 @@ public JsonResult AddExistingSeries(string path, string seriesName, int seriesId
}
}
[HttpPost]
public JsonResult QuickAddNewSeries(string seriesName, int seriesId, int qualityProfileId)
{
var path = _rootFolderProvider.GetMostFreeRootDir();
path = Path.Combine(path, MediaFileProvider.CleanFilename(seriesName));
//Create the folder for the new series
//Use the created folder name when adding the series
path = _diskProvider.CreateDirectory(path);
return AddExistingSeries(path, seriesName, seriesId, qualityProfileId);
}
public JsonResult AddSeries(string path, int seriesId, int qualityProfileId)
{
//Get TVDB Series Name
@ -145,6 +156,21 @@ public JsonResult AddSeries(string path, int seriesId, int qualityProfileId)
return new JsonResult { Data = "ok" };
}
[ChildActionOnly]
public ActionResult QuickAdd()
{
var defaultQuality = _configProvider.DefaultQualityProfile;
var qualityProfiles = _qualityProvider.All();
ViewData["qualityProfiles"] = new SelectList(
qualityProfiles,
"QualityProfileId",
"Name",
defaultQuality);
return PartialView();
}
//Root Directory
[HttpPost]

View File

@ -98,25 +98,17 @@ public ActionResult _AjaxSeasonGrid(int seriesId, int seasonNumber)
}
}
public ActionResult SearchForSeries(string seriesName)
public JsonResult LocalSearch(string term)
{
var model = new List<SeriesSearchResultModel>();
//Get Results from the local DB and return
//Get Results from TvDb and convert them to something we can use.
foreach (var tvdbSearchResult in _tvDbProvider.SearchSeries(seriesName))
{
model.Add(new SeriesSearchResultModel
{
TvDbId = tvdbSearchResult.Id,
TvDbName = tvdbSearchResult.SeriesName,
FirstAired = tvdbSearchResult.FirstAired
});
}
var results = _seriesProvider.SearchForSeries(term).Select(s => new SeriesSearchResultModel
{
Id = s.SeriesId,
Title = s.Title
}).ToList();
//model.Add(new SeriesSearchResultModel{ TvDbId = 12345, TvDbName = "30 Rock", FirstAired = DateTime.Today });
//model.Add(new SeriesSearchResultModel { TvDbId = 65432, TvDbName = "The Office (US)", FirstAired = DateTime.Today.AddDays(-100) });
return PartialView("SeriesSearchResults", model);
return Json(results, JsonRequestBehavior.AllowGet);
}
[HttpPost]

View File

@ -25,5 +25,11 @@ public ActionResult Footer()
ViewData["RssTimer"] = _jobProvider.NextScheduledRun(typeof(RssSyncJob)).ToString("yyyyMMddHHmmss");
return PartialView();
}
[ChildActionOnly]
public ActionResult LocalSearch()
{
return PartialView();
}
}
}

View File

@ -4,8 +4,8 @@ namespace NzbDrone.Web.Models
{
public class SeriesSearchResultModel
{
public int TvDbId { get; set; }
public string TvDbName { get; set; }
public int Id { get; set; }
public string Title { get; set; }
public DateTime FirstAired { get; set; }
}
}

View File

@ -328,6 +328,7 @@
<Content Include="Content\2011.2.712\Windows7\slider-v.gif" />
<Content Include="Content\2011.2.712\Windows7\sprite-vertical.png" />
<Content Include="Content\2011.2.712\Windows7\sprite.png" />
<Content Include="Content\Slider.css" />
<Content Include="Content\Grid.css" />
<Content Include="Content\Images\close.png" />
<Content Include="Content\Images\Downloading.png" />
@ -676,6 +677,7 @@
<Content Include="Scripts\2011.2.712\telerik.window.min.js" />
<Content Include="Scripts\AutoComplete.js" />
<Content Include="Scripts\addSeries.js" />
<Content Include="Scripts\slider.js" />
<Content Include="Scripts\Plugins\jquery-1.6.3-vsdoc.js" />
<Content Include="Scripts\Plugins\jquery-1.6.3.js" />
<Content Include="Scripts\Plugins\jquery-1.6.3.min.js" />
@ -722,7 +724,6 @@
<Content Include="Views\Upcoming\Index.cshtml" />
<Content Include="Views\Series\Details.cshtml" />
<Content Include="Views\Series\Index.cshtml" />
<Content Include="Views\Series\SeriesSearchResults.cshtml" />
<Content Include="Views\Shared\Error.cshtml" />
<Content Include="Views\Settings\QualityProfileItem.cshtml" />
<Content Include="Views\System\Indexers.cshtml" />
@ -928,6 +929,12 @@
<ItemGroup>
<Content Include="Views\Settings\System.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Shared\QuickAdd.cshtml" />
</ItemGroup>
<ItemGroup>
<Content Include="Views\Shared\LocalSearch.cshtml" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.

View File

@ -5,6 +5,7 @@
bindFolderAutoComplete(".folderLookup");
bindSeriesAutoComplete(".seriesLookup");
bindLocalSeriesAutoComplete(".localSeriesLookup");
});
function bindFolderAutoComplete(selector) {
@ -51,4 +52,24 @@ function bindSeriesAutoComplete(selector) {
.appendTo(ul);
};
});
}
function bindLocalSeriesAutoComplete(selector) {
$(selector).each(function (index, element) {
$(element).autocomplete({
source: "/Series/LocalSearch",
minLength: 3,
delay: 500,
select: function (event, ui) {
window.location = "../Series/Details?seriesId=" + ui.item.Id;
}
})
.data("autocomplete")._renderItem = function (ul, item) {
return $("<li></li>")
.data("item.autocomplete", item)
.append("<a><strong>" + item.Title + "</strong><br>" + "</a>")
.appendTo(ul);
};
});
}

View File

@ -1,6 +1,7 @@
//URLs
var addSeriesUrl = '../AddSeries/AddExistingSeries';
var addNewSeriesUrl = '../AddSeries/AddNewSeries';
var quickAddNewSeriesUrl = '../AddSeries/QuickAddNewSeries';
var existingSeriesUrl = '../AddSeries/ExistingSeries';
var addNewUrl = '../AddSeries/AddNew';
@ -105,6 +106,26 @@ function reloadAddNew() {
}
//QuickAddNew
$('#quickAddNew').live('click', function () {
var seriesTitle = $("#newSeriesLookup").val();
var seriesId = $("#newSeriesId").val();
var qualityId = $("#qualityList").val();
$.ajax({
type: "POST",
url: quickAddNewSeriesUrl,
data: jQuery.param({ seriesName: seriesTitle, seriesId: seriesId, qualityProfileId: qualityId }),
error: function (req, status, error) {
alert("Sorry! We could not add " + path + " at this time. " + error);
},
success: function () {
$("#newSeriesLookup").val("");
//$('#newSeriesPath').val("");
}
});
});
//On load
jQuery(document).ready(function () {
//RootDir

View File

@ -0,0 +1,13 @@
$(document).ready(function () {
$(".sliderButtonContainer").live('click', function () {
sliderToggle();
});
});
function sliderToggle() {
$('.sliderContent').slideToggle('slow');
$(".sliderButtonContainer").children('.sliderImage').toggleClass('sliderOpened sliderClosed');
//Prevent the Address Bar from changing
return false;
}

View File

@ -7,7 +7,7 @@
@Html.Hidden("newSeriesId", 0, new { @class = "seriesId" })
</div>
@Html.DropDownList("newSeriesPath", new SelectList((IList)ViewData["RootDirs"]), new { style = "width: 406px; margin-left: 0px;" })
@Html.DropDownList("qualityList", new SelectList((IList)ViewData["QualityList"], "QualityProfileId", "Name"), new { @class = "qualitySelector" })
@Html.DropDownList("qualityList", (SelectList)ViewData["QualityProfiles"], new { @class = "qualitySelector" })
<button id="saveNewSeries">
Add</button>
</div>

View File

@ -3,10 +3,6 @@
Add Series
}
@section HeaderContent{
<script src="../../Scripts/addSeries.js" type="text/javascript"></script>
}
@section MainContent{
<h2>Add New Series</h2>
<div id="addNewSeries">

View File

@ -1,30 +0,0 @@
@model List<NzbDrone.Web.Models.SeriesSearchResultModel>
<div id="searchResults">
<fieldset>
<legend>Search Results</legend>
@if (Model.Count == 0)
{
<b>No results found for the series name</b>
}
@{var open = "(";}
@{var close = ")";}
@{int r = 0;}
@foreach (var result in Model)
{
@Html.RadioButton("selectedSeries", result.TvDbId, r == 0,
new {@class = "searchRadio examplePart", id = "searchRadio_" + r})
<b>@result.TvDbName</b> @open @result.FirstAired.ToShortDateString() @close
<br/>
@Html.TextBox(result.TvDbName + "_text", result.TvDbName, new { id = result.TvDbId + "_text", style = "display:none" })
r++;
}
</fieldset>
</div>

View File

@ -1,33 +1,30 @@
<style>
.ui-autocomplete-loading { background: white url('images/ui-anim_basic_16x16.gif') right center no-repeat; }
@{ Layout = null; }
<style type="text/css">
@*Specific to each slider*@
#seriesSearchSlider {
right: 150px;
}
.sliderContent .localSeriesLookup {
width: 94%;
}
.sliderContent {
height: 53px;
}
</style>
<script>
$(function () {
$("#birds").autocomplete({
source: "/Settings/TestResults",
minLength: 3,
delay: 500,
select: function (event, ui) {
$(this).val(ui.item.Title);
return false;
}
})
.data("autocomplete")._renderItem = function (ul, item) {
return $("<li></li>")
.data("item.autocomplete", item)
.append("<a><strong>" + item.Title + "</strong><br>" + item.FirstAired + "</a>")
.appendTo(ul);
};
});
</script>
<div class="demo">
<div class="ui-widget">
<label for="birds">Birds: </label>
<input id="birds" />
<div id="seriesSearchSlider" class="top-slider">
<div class="sliderContent">
Local Series Search
<input class="localSeriesLookup" type="text" />
</div>
<div class="openCloseWrapper">
<div class="sliderButtonContainer">
Search<div class="sliderImage sliderClosed"></div>
</div>
</div>
</div>
</div><!-- End demo -->

View File

@ -0,0 +1,29 @@
@{
Layout = null;
}
<style>
#localSeriesSlider {
right: 150px;
}
.sliderContent .localSeriesLookup {
width: 94%;
}
.sliderContent {
height: 53px;
}
</style>
<div id="localSeriesSlider" class="top-slider">
<div class="sliderContent">
Local Series Search
<input class="localSeriesLookup" type="text" />
</div>
<div class="openCloseWrapper">
<div class="sliderButtonContainer">
Search<div class="sliderImage sliderClosed"></div>
</div>
</div>
</div>

View File

@ -0,0 +1,15 @@
@using System.Collections
@using NzbDrone.Core
<div id="quickAdd">
<input id="quickSeriesLookup" class="seriesLookup" type="text" style="width: 400px" />
@Html.Hidden("newSeriesId", 0, new { @class = "seriesId" })
@Html.DropDownList("qualityList", (SelectList)ViewData["QualityProfiles"], new { @class = "qualitySelector" })
<button id="quickAddNew">Add</button>
</div>
<script type="text/javascript">
jQuery(document).ready(function () {
//AddNew
$('#quickSeriesLookup').watermark('Title of the series you want to add...');
});
</script>

View File

@ -15,6 +15,7 @@
<link type="text/css" rel="stylesheet" href="/Content/ActionButton.css" />
<link type="text/css" rel="stylesheet" href="/Content/overrides.css" />
<link type="text/css" rel="stylesheet" href="/Content/Menu.css" />
<link type="text/css" rel="stylesheet" href="/Content/Slider.css" />
<script type="text/javascript" src="/Scripts/Plugins/jquery-1.6.3.min.js"></script>
<script type="text/javascript" src="/Scripts/Plugins/jquery-ui-1.8.16.min.js"></script>
@ -27,12 +28,15 @@
<script type="text/javascript" src="/Scripts/Plugins/doTimeout.js"></script>
<script type="text/javascript" src="/Scripts/episodeSearch.js"></script>
<script type="text/javascript" src="/Scripts/autocomplete.js"></script>
<script type="text/javascript" src="/Scripts/addSeries.js"></script>
<script type="text/javascript" src="/Scripts/slider.js"></script>
@MvcMiniProfiler.MiniProfiler.RenderIncludes()
@RenderSection("HeaderContent", required: false)
</head>
<body>
<div class="container">
@{Html.RenderAction("LocalSearch", "Shared");}
<div id="menu" class="span-24 last prepend-top append-bottom">
<ul>
@MvcHtmlString.Create(Html.CurrentActionLink("Series", "Index", "Series"))

1
SharedLiveTemplates.xml Normal file
View File

@ -0,0 +1 @@
<TemplatesExport />