Removed old UI

pull/2789/head
Keivan Beigi 7 years ago committed by Taloth Saldono
parent 5894b4fd95
commit e343f8e35e

2
.gitignore vendored

@ -120,7 +120,7 @@ _tests/
setup/Output/
*.~is
UI.Phantom/
UI/
#VS outout folders
bin

@ -95,12 +95,8 @@ RunGulp()
ProgressEnd 'yarn install'
echo "##teamcity[progressStart 'Running gulp']"
CheckExitCode npm run build
CheckExitCode yarn run build --production
echo "##teamcity[progressFinish 'Running gulp']"
echo "##teamcity[progressStart 'Running gulp (phantom)']"
CheckExitCode yarn run build -- --production
echo "##teamcity[progressFinish 'Running gulp (phantom)']"
}
CreateMdbs()

@ -1,18 +0,0 @@
var gulp = require('gulp');
var runSequence = require('run-sequence');
require('./clean');
require('./less');
require('./handlebars');
require('./copy');
gulp.task('build', function() {
return runSequence('clean', [
'webpack',
'less',
'handlebars',
'copyHtml',
'copyContent',
'copyJs'
]);
});

@ -1,8 +0,0 @@
var gulp = require('gulp');
var del = require('del');
var paths = require('./paths');
gulp.task('clean', function() {
return del([paths.dest.root]);
});

@ -1,31 +0,0 @@
var gulp = require('gulp');
var print = require('gulp-print');
var cache = require('gulp-cached');
var livereload = require('gulp-livereload');
var paths = require('./paths.js');
gulp.task('copyJs', function () {
return gulp.src(
[
paths.src.root + 'polyfills.js',
paths.src.root + 'JsLibraries/handlebars.runtime.js'
])
.pipe(cache('copyJs'))
.pipe(print())
.pipe(gulp.dest(paths.dest.root))
.pipe(livereload());
});
gulp.task('copyHtml', function () {
return gulp.src(paths.src.html)
.pipe(cache('copyHtml'))
.pipe(gulp.dest(paths.dest.root))
.pipe(livereload());
});
gulp.task('copyContent', function () {
return gulp.src([paths.src.content + '**/*.*', '!**/*.less', '!**/*.css'])
.pipe(gulp.dest(paths.dest.content))
.pipe(livereload());
});

@ -1,7 +0,0 @@
module.exports = {
onError : function(error) {
//If you want details of the error in the console
console.log(error.toString());
this.emit('end');
}
};

@ -1,10 +0,0 @@
require('./watch.js');
require('./build.js');
require('./clean.js');
require('./handlebars.js');
require('./copy.js');
require('./less.js');
require('./stripBom.js');
require('./imageMin.js');
require('./webpack.js');
require('./start.js');

@ -1,55 +0,0 @@
var gulp = require('gulp');
var handlebars = require('gulp-handlebars');
var declare = require('gulp-declare');
var concat = require('gulp-concat');
var wrap = require("gulp-wrap");
var livereload = require('gulp-livereload');
var path = require('path');
var streamqueue = require('streamqueue');
var stripbom = require('gulp-stripbom');
var paths = require('./paths.js');
gulp.task('handlebars', function() {
var coreStream = gulp.src([
paths.src.templates,
'!*/**/*Partial.*'
])
.pipe(stripbom({ showLog : false }))
.pipe(handlebars())
.pipe(declare({
namespace : 'T',
noRedeclare : true,
processName : function(filePath) {
filePath = path.relative(paths.src.root, filePath);
return filePath.replace(/\\/g, '/')
.toLocaleLowerCase()
.replace('template', '')
.replace('.js', '');
}
}));
var partialStream = gulp.src([paths.src.partials])
.pipe(stripbom({ showLog : false }))
.pipe(handlebars())
.pipe(wrap('Handlebars.template(<%= contents %>)'))
.pipe(wrap('Handlebars.registerPartial(<%= processPartialName(file.relative) %>, <%= contents %>)', {}, {
imports : {
processPartialName : function(fileName) {
return JSON.stringify(
path.basename(fileName, '.js')
);
}
}
}));
return streamqueue({ objectMode : true },
partialStream,
coreStream
).pipe(concat('templates.js'))
.pipe(gulp.dest(paths.dest.root))
.pipe(livereload());
});

@ -1,15 +0,0 @@
var gulp = require('gulp');
var print = require('gulp-print');
var paths = require('./paths.js');
gulp.task('imageMin', function() {
var imagemin = require('gulp-imagemin');
return gulp.src(paths.src.images)
.pipe(imagemin({
progressive : false,
optimizationLevel : 4,
svgoPlugins : [{ removeViewBox : false }]
}))
.pipe(print())
.pipe(gulp.dest(paths.src.content + 'Images/'));
});

@ -1,51 +0,0 @@
var gulp = require('gulp');
var less = require('gulp-less');
var postcss = require('gulp-postcss');
var sourcemaps = require('gulp-sourcemaps');
var autoprefixer = require('autoprefixer');
var livereload = require('gulp-livereload');
var cleancss = require('gulp-clean-css');
var print = require('gulp-print');
var paths = require('./paths');
var errorHandler = require('./errorHandler');
gulp.task('less', function() {
var src = [
paths.src.content + 'bootstrap.less',
paths.src.content + 'theme.less',
paths.src.content + 'overrides.less',
paths.src.content + 'bootstrap.toggle-switch.css',
paths.src.content + 'fullcalendar.css',
paths.src.content + 'Messenger/messenger.css',
paths.src.content + 'Messenger/messenger.flat.css',
paths.src.root + 'Series/series.less',
paths.src.root + 'Activity/activity.less',
paths.src.root + 'AddSeries/addSeries.less',
paths.src.root + 'Calendar/calendar.less',
paths.src.root + 'Cells/cells.less',
paths.src.root + 'ManualImport/manualimport.less',
paths.src.root + 'Settings/settings.less',
paths.src.root + 'System/Logs/logs.less',
paths.src.root + 'System/Update/update.less',
paths.src.root + 'System/Info/info.less'
];
return gulp.src(src)
.pipe(print())
.pipe(sourcemaps.init())
.pipe(less({
dumpLineNumbers : 'false',
compress : false,
yuicompress : false,
ieCompat : true,
strictImports : true
}))
.pipe(postcss([ autoprefixer({ browsers: ['last 2 versions'] }) ]))
.pipe(cleancss())
.on('error', errorHandler.onError)
.pipe(sourcemaps.write(paths.dest.content))
.pipe(gulp.dest(paths.dest.content))
.pipe(livereload());
});

@ -1,21 +0,0 @@
var paths = {
src : {
root : './src/UI/',
templates : './src/UI/**/*.hbs',
html : './src/UI/*.html',
partials : './src/UI/**/*Partial.hbs',
scripts : './src/UI/**/*.js',
less : ['./src/UI/**/*.less'],
content : './src/UI/Content/',
images : './src/UI/Content/Images/**/*',
exclude : {
libs : '!./src/UI/JsLibraries/**'
}
},
dest : {
root : './_output/UI/',
content : './_output/UI/Content/'
}
};
module.exports = paths;

@ -1,112 +0,0 @@
// will download and run sonarr (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.
var http = require('http');
var gulp = require('gulp');
var fs = require('fs');
var targz = require('tar.gz');
var del = require('del');
var print = require('gulp-print');
var spawn = require('child_process').spawn;
function download(url, dest, cb) {
console.log('Downloading ' + url + ' to ' + dest);
var file = fs.createWriteStream(dest);
var request = http.get(url, function (response) {
response.pipe(file);
file.on('finish', function () {
console.log('Download completed');
file.close(cb);
});
});
}
function getLatest(cb) {
var branch = 'develop';
process.argv.forEach(function (val) {
var branchMatch = /branch=([\S]*)/.exec(val);
if (branchMatch && branchMatch.length > 1) {
branch = branchMatch[1];
}
});
var url = 'http://services.sonarr.tv/v1/update/' + branch + '?os=osx';
console.log('Checking for latest version:', url);
http.get(url, function (res) {
var data = '';
res.on('data', function (chunk) {
data += chunk;
});
res.on('end', function () {
var updatePackage = JSON.parse(data).updatePackage;
console.log('Latest version available: ' + updatePackage.version + ' Release Date: ' + updatePackage.releaseDate);
cb(updatePackage);
});
}).on('error', function (e) {
console.log('problem with request: ' + e.message);
});
}
function extract(source, dest, cb) {
console.log('extracting download page to ' + dest);
new targz().extract(source, dest, function (err) {
if (err) {
console.log(err);
}
console.log('Update package extracted.');
cb();
});
}
gulp.task('getSonarr', function () {
//gulp.src('/Users/kayone/git/Sonarr/_start/2.0.0.3288/NzbDrone/*.*')
// .pipe(print())
// .pipe(gulp.dest('./_output
//return;
try {
fs.mkdirSync('./_start/');
} catch (e) {
if (e.code != 'EEXIST') {
throw e;
}
}
getLatest(function (package) {
var packagePath = "./_start/" + package.filename;
var dirName = "./_start/" + package.version;
download(package.url, packagePath, function () {
extract(packagePath, dirName, function () {
// clean old binaries
console.log('Cleaning old binaries');
del.sync(['./_output/*', '!./_output/UI/']);
console.log('copying binaries to target');
gulp.src(dirName + '/NzbDrone/*.*')
.pipe(gulp.dest('./_output/'));
});
});
});
});
gulp.task('startSonarr', function () {
var ls = spawn('mono', ['--debug', './_output/NzbDrone.exe']);
ls.stdout.on('data', function (data) {
process.stdout.write('' + data);
});
ls.stderr.on('data', function (data) {
process.stdout.write('' + data);
});
ls.on('close', function (code) {
console.log('child process exited with code ' + code);
});
});

@ -1,27 +0,0 @@
var gulp = require('gulp');
var paths = require('./paths.js');
var stripbom = require('gulp-stripbom');
var stripBom = function(dest) {
gulp.src([paths.src.scripts, paths.src.exclude.libs])
.pipe(stripbom({
showLog: false
}))
.pipe(gulp.dest(dest));
gulp.src(paths.src.less)
.pipe(stripbom({
showLog: false
}))
.pipe(gulp.dest(dest));
gulp.src(paths.src.templates)
.pipe(stripbom({
showLog: false
}))
.pipe(gulp.dest(dest));
};
gulp.task('stripBom', function() {
stripBom(paths.src.root);
});

@ -1,19 +0,0 @@
var gulp = require('gulp');
var livereload = require('gulp-livereload');
var paths = require('./paths.js');
require('./handlebars.js');
require('./less.js');
require('./copy.js');
require('./webpack.js');
gulp.task('watch', ['handlebars', 'less', 'copyHtml', 'copyContent', 'copyJs'], function () {
livereload.listen();
gulp.start('webpackWatch');
gulp.watch([paths.src.scripts, paths.src.exclude.libs], ['copyJs']);
gulp.watch(paths.src.templates, ['handlebars']);
gulp.watch([paths.src.less, paths.src.exclude.libs], ['less']);
gulp.watch([paths.src.html], ['copyHtml']);
gulp.watch([paths.src.content + '**/*.*', '!**/*.less'], ['copyContent']);
});

@ -1,13 +0,0 @@
var gulp = require('gulp');
var webpackStream = require('webpack-stream');
var livereload = require('gulp-livereload');
var webpackConfig = require('../webpack.config');
gulp.task('webpack', function() {
return gulp.src('main.js').pipe(webpackStream(webpackConfig)).pipe(gulp.dest(''));
});
gulp.task('webpackWatch', function() {
webpackConfig.watch = true;
return gulp.src('main.js').pipe(webpackStream(webpackConfig)).pipe(gulp.dest('')).pipe(livereload());
});

@ -1,7 +1 @@
var phantom = require('./frontend/gulp/helpers/phantom');
if (phantom) {
require('./frontend/gulp/gulpFile.js');
} else {
require('./gulp/gulpFile.js');
}
require('./frontend/gulp/gulpFile.js');

@ -1 +0,0 @@
NzbDrone.UI

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="jQuery-1.9.1" level="application" />
<orderEntry type="library" name="backbone.backgrid.filter.js" level="project" />
</component>
</module>

@ -1,59 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectCodeStyleSettingsManager">
<option name="PER_PROJECT_SETTINGS">
<value>
<option name="LINE_SEPARATOR" value="&#13;&#10;" />
<option name="RIGHT_MARGIN" value="190" />
<option name="HTML_ATTRIBUTE_WRAP" value="0" />
<option name="HTML_KEEP_LINE_BREAKS" value="false" />
<option name="HTML_KEEP_BLANK_LINES" value="1" />
<option name="HTML_ALIGN_ATTRIBUTES" value="false" />
<option name="HTML_INLINE_ELEMENTS" value="" />
<option name="HTML_DONT_ADD_BREAKS_IF_INLINE_CONTENT" value="" />
<CssCodeStyleSettings>
<option name="HEX_COLOR_LOWER_CASE" value="true" />
<option name="HEX_COLOR_LONG_FORMAT" value="true" />
<option name="VALUE_ALIGNMENT" value="1" />
</CssCodeStyleSettings>
<JSCodeStyleSettings>
<option name="SPACE_BEFORE_PROPERTY_COLON" value="true" />
<option name="ALIGN_OBJECT_PROPERTIES" value="2" />
<option name="SPACE_BEFORE_FUNCTION_LEFT_PARENTH" value="false" />
<option name="OBJECT_LITERAL_WRAP" value="2" />
<option name="SPACES_WITHIN_OBJECT_LITERAL_BRACES" value="true" />
</JSCodeStyleSettings>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="CSS">
<indentOptions>
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JavaScript">
<option name="LINE_COMMENT_AT_FIRST_COLUMN" value="true" />
<option name="KEEP_LINE_BREAKS" value="false" />
<option name="KEEP_FIRST_COLUMN_COMMENT" value="false" />
<option name="KEEP_BLANK_LINES_IN_CODE" value="1" />
<option name="CATCH_ON_NEW_LINE" value="true" />
<option name="FINALLY_ON_NEW_LINE" value="true" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />
<option name="ALIGN_MULTILINE_BINARY_OPERATION" value="true" />
<option name="SPACE_BEFORE_METHOD_PARENTHESES" value="true" />
<option name="CALL_PARAMETERS_WRAP" value="1" />
<option name="BINARY_OPERATION_WRAP" value="1" />
<option name="TERNARY_OPERATION_SIGNS_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_WRAP" value="2" />
<option name="ARRAY_INITIALIZER_LBRACE_ON_NEXT_LINE" value="true" />
<option name="ARRAY_INITIALIZER_RBRACE_ON_NEXT_LINE" value="true" />
<option name="IF_BRACE_FORCE" value="3" />
<option name="DOWHILE_BRACE_FORCE" value="3" />
<option name="WHILE_BRACE_FORCE" value="3" />
<option name="FOR_BRACE_FORCE" value="3" />
</codeStyleSettings>
</value>
</option>
<option name="USE_PER_PROJECT_SETTINGS" value="true" />
</component>
</project>

@ -1,20 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="Keivan">
<words>
<w>deps</w>
<w>mixins</w>
<w>nzbdrone</w>
<w>rootdir</w>
<w>rootfolder</w>
<w>rootfolders</w>
<w>signalr</w>
<w>sonarr</w>
<w>templated</w>
<w>thetvdb</w>
<w>trakt</w>
<w>tvdb</w>
<w>xlarge</w>
<w>yyyy</w>
</words>
</dictionary>
</component>

@ -1,13 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="Keivan.Beigi">
<words>
<w>backgrid</w>
<w>bnzbd</w>
<w>clickable</w>
<w>couldn</w>
<w>mouseenter</w>
<w>mouseleave</w>
<w>navbar</w>
</words>
</dictionary>
</component>

@ -1,3 +0,0 @@
<component name="ProjectDictionaryState">
<dictionary name="Mark" />
</component>

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false">
<file url="file://$PROJECT_DIR$/System/Logs/Files/LogFileModel.js" charset="UTF-8" />
<file url="PROJECT" charset="UTF-8" />
</component>
</project>

@ -1,117 +0,0 @@
<component name="InspectionProjectProfileManager">
<profile version="1.0" is_locked="false">
<option name="myName" value="Project Default" />
<option name="myLocal" value="false" />
<inspection_tool class="AssignmentResultUsedJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="AssignmentToForLoopParameterJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="AssignmentToFunctionParameterJS" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="BadExpressionStatementJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="BreakStatementJS" enabled="true" level="SERVER PROBLEM" enabled_by_default="true" />
<inspection_tool class="BreakStatementWithLabelJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ChainedEqualityJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CheckEmptyScriptTag" enabled="false" level="WARNING" enabled_by_default="false" />
<inspection_tool class="ConditionalExpressionWithIdenticalBranchesJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ConstantIfStatementJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ConstantOnLHSOfComparisonJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ContinueStatementWithLabelJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="CssMissingSemicolonInspection" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="CyclomaticComplexityJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="10" />
</inspection_tool>
<inspection_tool class="DefaultNotLastCaseInSwitchJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="DuplicateCaseLabelJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="DuplicateConditionJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="DynamicallyGeneratedCodeJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="EmptyTryBlockJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ExceptionCaughtLocallyJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ForLoopReplaceableByWhileJS" enabled="true" level="INFO" enabled_by_default="true">
<option name="m_ignoreLoopsWithoutConditions" value="false" />
</inspection_tool>
<inspection_tool class="FunctionNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z][A-Za-z]*" />
<option name="m_minLength" value="4" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="FunctionWithInconsistentReturnsJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="HtmlFormInputWithoutLabel" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlPresentationalElement" enabled="false" level="WEAK WARNING" enabled_by_default="false" />
<inspection_tool class="HtmlUnknownAttribute" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="2">
<item index="0" class="java.lang.String" itemvalue="name" />
<item index="1" class="java.lang.String" itemvalue="validation-name" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="HtmlUnknownTag" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myValues">
<value>
<list size="8">
<item index="0" class="java.lang.String" itemvalue="nobr" />
<item index="1" class="java.lang.String" itemvalue="noembed" />
<item index="2" class="java.lang.String" itemvalue="comment" />
<item index="3" class="java.lang.String" itemvalue="noscript" />
<item index="4" class="java.lang.String" itemvalue="embed" />
<item index="5" class="java.lang.String" itemvalue="script" />
<item index="6" class="java.lang.String" itemvalue="icon" />
<item index="7" class="java.lang.String" itemvalue="p" />
</list>
</value>
</option>
<option name="myCustomValuesEnabled" value="true" />
</inspection_tool>
<inspection_tool class="IfStatementWithIdenticalBranchesJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="IfStatementWithTooManyBranchesJS" enabled="true" level="ERROR" enabled_by_default="true">
<option name="m_limit" value="3" />
</inspection_tool>
<inspection_tool class="JSDuplicatedDeclaration" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="JSHint" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="JSLastCommaInArrayLiteral" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="JSPotentiallyInvalidUsageOfThis" enabled="true" level="SERVER PROBLEM" enabled_by_default="true" />
<inspection_tool class="JSUndeclaredVariable" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="JSUnnecessarySemicolon" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="JSUnresolvedFunction" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JSUnresolvedVariable" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="JSUnusedGlobalSymbols" enabled="true" level="WARNING" enabled_by_default="true">
<option name="myReportUnusedDefinitions" value="true" />
<option name="myReportUnusedProperties" value="true" />
</inspection_tool>
<inspection_tool class="LabeledStatementJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="LocalVariableNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z][A-Za-z]*" />
<option name="m_minLength" value="1" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="NegatedIfStatementJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestedAssignmentJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestedFunctionCallJS" enabled="false" level="ERROR" enabled_by_default="false" />
<inspection_tool class="NestedSwitchStatementJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="NestingDepthJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="5" />
</inspection_tool>
<inspection_tool class="NonBlockStatementBodyJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ParameterNamingConventionJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_regex" value="[a-z][A-Za-z]*" />
<option name="m_minLength" value="1" />
<option name="m_maxLength" value="32" />
</inspection_tool>
<inspection_tool class="ParametersPerFunctionJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="4" />
</inspection_tool>
<inspection_tool class="ReservedWordUsedAsNameJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ReuseOfLocalVariableJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="StatementsPerFunctionJS" enabled="true" level="WARNING" enabled_by_default="true">
<option name="m_limit" value="30" />
</inspection_tool>
<inspection_tool class="SwitchStatementWithNoDefaultBranchJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="TailRecursionJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="ThisExpressionReferencesGlobalObjectJS" enabled="true" level="ERROR" enabled_by_default="true" />
<inspection_tool class="ThreeNegationsPerFunctionJS" enabled="true" level="WARNING" enabled_by_default="true" />
<inspection_tool class="UnterminatedStatementJS" enabled="true" level="ERROR" enabled_by_default="true">
<option name="ignoreSemicolonAtEndOfBlock" value="true" />
</inspection_tool>
</profile>
</component>

@ -1,7 +0,0 @@
<component name="InspectionProjectProfileManager">
<settings>
<option name="PROJECT_PROFILE" value="Project Default" />
<option name="USE_PROJECT_PROFILE" value="true" />
<version value="1.0" />
</settings>
</component>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JavaScriptLibraryMappings">
<excludedPredefinedLibrary name="HTML" />
<excludedPredefinedLibrary name="HTML5 / EcmaScript 5" />
</component>
</project>

@ -1,72 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JSHintConfiguration" version="2.6.0" use-config-file="false">
<option asi="false" />
<option bitwise="true" />
<option boss="false" />
<option browser="true" />
<option camelcase="true" />
<option couch="false" />
<option curly="true" />
<option debug="false" />
<option devel="true" />
<option dojo="false" />
<option eqeqeq="true" />
<option eqnull="false" />
<option es3="false" />
<option esnext="false" />
<option evil="false" />
<option expr="false" />
<option forin="true" />
<option freeze="false" />
<option funcscope="false" />
<option gcl="false" />
<option globalstrict="true" />
<option immed="true" />
<option iterator="false" />
<option jquery="false" />
<option lastsemic="false" />
<option latedef="true" />
<option laxbreak="false" />
<option laxcomma="false" />
<option loopfunc="false" />
<option maxdepth="3" />
<option maxerr="50" />
<option mootools="false" />
<option moz="false" />
<option multistr="false" />
<option newcap="true" />
<option noarg="true" />
<option node="true" />
<option noempty="false" />
<option nomen="false" />
<option nonbsp="false" />
<option nonew="true" />
<option nonstandard="false" />
<option notypeof="false" />
<option noyield="false" />
<option onevar="false" />
<option passfail="false" />
<option phantom="false" />
<option plusplus="false" />
<option predef="window, define, require, module" />
<option proto="false" />
<option prototypejs="false" />
<option quotmark="single" />
<option rhino="false" />
<option scripturl="false" />
<option shadow="false" />
<option smarttabs="false" />
<option strict="false" />
<option sub="false" />
<option supernew="false" />
<option trailing="false" />
<option undef="true" />
<option unused="true" />
<option validthis="false" />
<option white="false" />
<option worker="false" />
<option wsh="false" />
<option yui="false" />
</component>
</project>

@ -1,13 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="JSLintConfiguration" html="true" json="true">
<option browser="true" />
<option devel="true" />
<option indent="4" />
<option maxerr="50" />
<option plusplus="true" />
<option todo="true" />
<option white="true" />
</component>
</project>

@ -1,6 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectKey">
<option name="state" value="git@github.com:NzbDrone/NzbDrone.git" />
</component>
</project>

@ -1,8 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/NzbDrone.UI.iml" filepath="$PROJECT_DIR$/.idea/NzbDrone.UI.iml" />
</modules>
</component>
</project>

@ -1,23 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Debug - Chrome" type="JavascriptDebugType" factoryName="JavaScript Debug" singleton="true" uri="http://localhost:8989">
<mapping url="http://localhost:8989/Calendar" local-file="$PROJECT_DIR$/Calendar" />
<mapping url="http://localhost:8989/MainMenuView.js" local-file="$PROJECT_DIR$/MainMenuView.js" />
<mapping url="http://localhost:8989/Settings" local-file="$PROJECT_DIR$/Settings" />
<mapping url="http://localhost:8989/Upcoming" local-file="$PROJECT_DIR$/Upcoming" />
<mapping url="http://localhost:8989/app.js" local-file="$PROJECT_DIR$/app.js" />
<mapping url="http://localhost:8989/Mixins" local-file="$PROJECT_DIR$/Mixins" />
<mapping url="http://localhost:8989/Wanted" local-file="$PROJECT_DIR$/Wanted" />
<mapping url="http://localhost:8989/Quality" local-file="$PROJECT_DIR$/Quality" />
<mapping url="http://localhost:8989/Config.js" local-file="$PROJECT_DIR$/Config.js" />
<mapping url="http://localhost:8989/Shared" local-file="$PROJECT_DIR$/Shared" />
<mapping url="http://localhost:8989/AddSeries" local-file="$PROJECT_DIR$/AddSeries" />
<mapping url="http://localhost:8989/HeaderView.js" local-file="$PROJECT_DIR$/HeaderView.js" />
<mapping url="http://localhost:8989" local-file="$PROJECT_DIR$" />
<mapping url="http://localhost:8989/Routing.js" local-file="$PROJECT_DIR$/Routing.js" />
<mapping url="http://localhost:8989/Controller.js" local-file="$PROJECT_DIR$/Controller.js" />
<mapping url="http://localhost:8989/Series" local-file="$PROJECT_DIR$/Series" />
<RunnerSettings RunnerId="JavascriptDebugRunner" />
<ConfigurationWrapper RunnerId="JavascriptDebugRunner" />
<method />
</configuration>
</component>

@ -1,23 +0,0 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Debug - Firefox" type="JavascriptDebugType" factoryName="JavaScript Debug" singleton="true" engineId="firefox" uri="http://localhost:8989">
<mapping url="http://localhost:8989/Calendar" local-file="$PROJECT_DIR$/Calendar" />
<mapping url="http://localhost:8989/MainMenuView.js" local-file="$PROJECT_DIR$/MainMenuView.js" />
<mapping url="http://localhost:8989/Settings" local-file="$PROJECT_DIR$/Settings" />
<mapping url="http://localhost:8989/Upcoming" local-file="$PROJECT_DIR$/Upcoming" />
<mapping url="http://localhost:8989/app.js" local-file="$PROJECT_DIR$/app.js" />
<mapping url="http://localhost:8989/Mixins" local-file="$PROJECT_DIR$/Mixins" />
<mapping url="http://localhost:8989/Wanted" local-file="$PROJECT_DIR$/Wanted" />
<mapping url="http://localhost:8989/Config.js" local-file="$PROJECT_DIR$/Config.js" />
<mapping url="http://localhost:8989/Quality" local-file="$PROJECT_DIR$/Quality" />
<mapping url="http://localhost:8989/AddSeries" local-file="$PROJECT_DIR$/AddSeries" />
<mapping url="http://localhost:8989/Shared" local-file="$PROJECT_DIR$/Shared" />
<mapping url="http://localhost:8989/HeaderView.js" local-file="$PROJECT_DIR$/HeaderView.js" />
<mapping url="http://localhost:8989" local-file="$PROJECT_DIR$" />
<mapping url="http://localhost:8989/Routing.js" local-file="$PROJECT_DIR$/Routing.js" />
<mapping url="http://localhost:8989/Controller.js" local-file="$PROJECT_DIR$/Controller.js" />
<mapping url="http://localhost:8989/Series" local-file="$PROJECT_DIR$/Series" />
<RunnerSettings RunnerId="JavascriptDebugRunner" />
<ConfigurationWrapper RunnerId="JavascriptDebugRunner" />
<method />
</configuration>
</component>

@ -1,3 +0,0 @@
<component name="DependencyValidationManager">
<scope name="NzbDrone" pattern="!file:JsLibraries//*" />
</component>

@ -1,5 +0,0 @@
<component name="DependencyValidationManager">
<state>
<option name="SKIP_IMPORT_STATEMENTS" value="false" />
</state>
</component>

@ -1,7 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="VcsDirectoryMappings">
<mapping directory="$PROJECT_DIR$/../.." vcs="Git" />
</component>
</project>

@ -1,19 +0,0 @@
{
"-W030": false,
"-W064": false,
"-W097": false,
"-W100": false,
"undef": true,
"curly": true,
"immed": true,
"eqeqeq": true,
"latedef": true,
"globals": {
"module": true,
"require": true,
"define": true,
"window": true,
"document": true,
"console": true
}
}

@ -1,84 +0,0 @@
var Marionette = require('marionette');
var Backbone = require('backbone');
var Backgrid = require('backgrid');
var HistoryLayout = require('./History/HistoryLayout');
var BlacklistLayout = require('./Blacklist/BlacklistLayout');
var QueueLayout = require('./Queue/QueueLayout');
module.exports = Marionette.Layout.extend({
template : 'Activity/ActivityLayoutTemplate',
regions : {
queueRegion : '#queue',
history : '#history',
blacklist : '#blacklist'
},
ui : {
queueTab : '.x-queue-tab',
historyTab : '.x-history-tab',
blacklistTab : '.x-blacklist-tab'
},
events : {
'click .x-queue-tab' : '_showQueue',
'click .x-history-tab' : '_showHistory',
'click .x-blacklist-tab' : '_showBlacklist'
},
initialize : function(options) {
if (options.action) {
this.action = options.action.toLowerCase();
}
},
onShow : function() {
switch (this.action) {
case 'history':
this._showHistory();
break;
case 'blacklist':
this._showBlacklist();
break;
default:
this._showQueue();
}
},
_navigate : function(route) {
Backbone.history.navigate(route, {
trigger : false,
replace : true
});
},
_showHistory : function(e) {
if (e) {
e.preventDefault();
}
this.history.show(new HistoryLayout());
this.ui.historyTab.tab('show');
this._navigate('/activity/history');
},
_showBlacklist : function(e) {
if (e) {
e.preventDefault();
}
this.blacklist.show(new BlacklistLayout());
this.ui.blacklistTab.tab('show');
this._navigate('/activity/blacklist');
},
_showQueue : function(e) {
if (e) {
e.preventDefault();
}
this.queueRegion.show(new QueueLayout());
this.ui.queueTab.tab('show');
this._navigate('/activity/queue');
}
});

@ -1,11 +0,0 @@
<ul class="nav nav-tabs">
<li><a href="#queue" class="x-queue-tab no-router">Queue</a></li>
<li><a href="#history" class="x-history-tab no-router">History</a></li>
<li><a href="#blacklist" class="x-blacklist-tab no-router">Blacklist</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane" id="queue"></div>
<div class="tab-pane" id="history"></div>
<div class="tab-pane" id="blacklist"></div>
</div>

@ -1,28 +0,0 @@
var vent = require('vent');
var NzbDroneCell = require('../../Cells/NzbDroneCell');
var BlacklistDetailsLayout = require('./Details/BlacklistDetailsLayout');
module.exports = NzbDroneCell.extend({
className : 'blacklist-actions-cell',
events : {
'click .x-details' : '_details',
'click .x-delete' : '_delete'
},
render : function() {
this.$el.empty();
this.$el.html('<i class="icon-sonarr-info x-details"></i>' +
'<i class="icon-sonarr-delete x-delete"></i>');
return this;
},
_details : function() {
vent.trigger(vent.Commands.OpenModalCommand, new BlacklistDetailsLayout({ model : this.model }));
},
_delete : function() {
this.model.destroy();
}
});

@ -1,47 +0,0 @@
var BlacklistModel = require('./BlacklistModel');
var PageableCollection = require('backbone.pageable');
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection');
var Collection = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/blacklist',
model : BlacklistModel,
state : {
pageSize : 15,
sortKey : 'date',
order : 1
},
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
'-1' : 'asc',
'1' : 'desc'
}
},
sortMappings : {
'series' : { sortKey : 'series.sortTitle' }
},
parseState : function(resp) {
return { totalRecords : resp.totalRecords };
},
parseRecords : function(resp) {
if (resp) {
return resp.records;
}
return resp;
}
});
Collection = AsSortedCollection.call(Collection);
Collection = AsPersistedStateCollection.call(Collection);
module.exports = Collection;

@ -1,114 +0,0 @@
var vent = require('vent');
var Marionette = require('marionette');
var Backgrid = require('backgrid');
var BlacklistCollection = require('./BlacklistCollection');
var SeriesTitleCell = require('../../Cells/SeriesTitleCell');
var QualityCell = require('../../Cells/QualityCell');
var RelativeDateCell = require('../../Cells/RelativeDateCell');
var BlacklistActionsCell = require('./BlacklistActionsCell');
var GridPager = require('../../Shared/Grid/Pager');
var LoadingView = require('../../Shared/LoadingView');
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
module.exports = Marionette.Layout.extend({
template : 'Activity/Blacklist/BlacklistLayoutTemplate',
regions : {
blacklist : '#x-blacklist',
toolbar : '#x-toolbar',
pager : '#x-pager'
},
columns : [
{
name : 'series',
label : 'Series',
cell : SeriesTitleCell
},
{
name : 'sourceTitle',
label : 'Source Title',
cell : 'string'
},
{
name : 'quality',
label : 'Quality',
cell : QualityCell,
sortable : false
},
{
name : 'date',
label : 'Date',
cell : RelativeDateCell
},
{
name : 'this',
label : '',
cell : BlacklistActionsCell,
sortable : false
}
],
initialize : function() {
this.collection = new BlacklistCollection({ tableName : 'blacklist' });
this.listenTo(this.collection, 'sync', this._showTable);
this.listenTo(vent, vent.Events.CommandComplete, this._commandComplete);
},
onShow : function() {
this.blacklist.show(new LoadingView());
this._showToolbar();
this.collection.fetch();
},
_showTable : function(collection) {
this.blacklist.show(new Backgrid.Grid({
columns : this.columns,
collection : collection,
className : 'table table-hover'
}));
this.pager.show(new GridPager({
columns : this.columns,
collection : collection
}));
},
_showToolbar : function() {
var leftSideButtons = {
type : 'default',
storeState : false,
items : [
{
title : 'Clear Blacklist',
icon : 'icon-sonarr-clear',
command : 'clearBlacklist'
}
]
};
this.toolbar.show(new ToolbarLayout({
left : [
leftSideButtons
],
context : this
}));
},
_refreshTable : function(buttonContext) {
this.collection.state.currentPage = 1;
var promise = this.collection.fetch({ reset : true });
if (buttonContext) {
buttonContext.ui.icon.spinForPromise(promise);
}
},
_commandComplete : function(options) {
if (options.command.get('name') === 'clearblacklist') {
this._refreshTable();
}
}
});

@ -1,11 +0,0 @@
<div id="x-toolbar"/>
<div class="row">
<div class="col-md-12">
<div id="x-blacklist" class="table-responsive"/>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="x-pager"/>
</div>
</div>

@ -1,17 +0,0 @@
var Backbone = require('backbone');
var SeriesCollection = require('../../Series/SeriesCollection');
module.exports = Backbone.Model.extend({
//Hack to deal with Backbone 1.0's bug
initialize : function() {
this.url = function() {
return this.collection.url + '/' + this.get('id');
};
},
parse : function(model) {
model.series = SeriesCollection.get(model.seriesId);
return model;
}
});

@ -1,14 +0,0 @@
var Marionette = require('marionette');
var BlacklistDetailsView = require('./BlacklistDetailsView');
module.exports = Marionette.Layout.extend({
template : 'Activity/Blacklist/Details/BlacklistDetailsLayoutTemplate',
regions : {
bodyRegion : '.modal-body'
},
onShow : function() {
this.bodyRegion.show(new BlacklistDetailsView({ model : this.model }));
}
});

@ -1,18 +0,0 @@
<div class="modal-content">
<div class="history-detail-modal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>
Blacklisted
</h3>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal">Close</button>
</div>
</div>
</div>

@ -1,5 +0,0 @@
var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({
template : 'Activity/Blacklist/Details/BlacklistDetailsViewTemplate'
});

@ -1,23 +0,0 @@
<dl class="dl-horizontal info">
<dt>Name:</dt>
<dd>{{sourceTitle}}</dd>
{{#if protocol}}
{{#unless_eq protocol compare="unknown"}}
<dt>Protocol:</dt>
<dd>{{protocol}}</dd>
{{/unless_eq}}
{{/if}}
{{#if indexer}}
<dt>Indexer:</dt>
<dd>{{indexer}}</dd>
{{/if}}
{{#if message}}
<dt>Message:</dt>
<dd>{{message}}</dd>
{{/if}}
</dl>

@ -1,22 +0,0 @@
var Handlebars = require('handlebars');
var FormatHelpers = require('../../../Shared/FormatHelpers');
Handlebars.registerHelper('historyAge', function() {
var age = this.age;
var unit = FormatHelpers.plural(Math.round(age), 'day');
var ageHours = parseFloat(this.ageHours);
var ageMinutes = this.ageMinutes ? parseFloat(this.ageMinutes) : null;
if (age < 2) {
age = ageHours.toFixed(1);
unit = FormatHelpers.plural(Math.round(ageHours), 'hour');
}
if (age < 2 && ageMinutes) {
age = parseFloat(ageMinutes).toFixed(1);
unit = FormatHelpers.plural(Math.round(ageMinutes), 'minute');
}
return new Handlebars.SafeString('<dt>Age (when grabbed):</dt><dd>{0} {1}</dd>'.format(age, unit));
});

@ -1,35 +0,0 @@
var $ = require('jquery');
var vent = require('vent');
var Marionette = require('marionette');
var HistoryDetailsView = require('./HistoryDetailsView');
module.exports = Marionette.Layout.extend({
template : 'Activity/History/Details/HistoryDetailsLayoutTemplate',
regions : {
bodyRegion : '.modal-body'
},
events : {
'click .x-mark-as-failed' : '_markAsFailed'
},
onShow : function() {
this.bodyRegion.show(new HistoryDetailsView({ model : this.model }));
},
_markAsFailed : function() {
var url = window.NzbDrone.ApiRoot + '/history/failed';
var data = {
id : this.model.get('id')
};
$.ajax({
url : url,
type : 'POST',
data : data
});
vent.trigger(vent.Commands.CloseModalCommand);
}
});

@ -1,23 +0,0 @@
<div class="modal-content">
<div class="history-detail-modal">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>
{{#if_eq eventType compare="grabbed"}}Grabbed{{/if_eq}}
{{#if_eq eventType compare="downloadFailed"}}Download Failed{{/if_eq}}
{{#if_eq eventType compare="downloadFolderImported"}}Episode Imported{{/if_eq}}
{{#if_eq eventType compare="episodeFileDeleted"}}Episode File Deleted{{/if_eq}}
{{#if_eq eventType compare="episodeFileRenamed"}}Episode File Renamed{{/if_eq}}
</h3>
</div>
<div class="modal-body">
</div>
<div class="modal-footer">
{{#if_eq eventType compare="grabbed"}}<button class="btn btn-danger x-mark-as-failed">Mark As Failed</button>{{/if_eq}}
<button class="btn" data-dismiss="modal">Close</button>
</div>
</div>
</div>

@ -1,6 +0,0 @@
var Marionette = require('marionette');
require('./HistoryDetailsAge');
module.exports = Marionette.ItemView.extend({
template : 'Activity/History/Details/HistoryDetailsViewTemplate'
});

@ -1,122 +0,0 @@
{{#if_eq eventType compare="grabbed"}}
<dl class="dl-horizontal info">
<dt>Name:</dt>
<dd>{{sourceTitle}}</dd>
{{#with data}}
{{#if indexer}}
<dt>Indexer:</dt>
<dd>{{indexer}}</dd>
{{/if}}
{{#if releaseGroup}}
<dt>Release Group:</dt>
<dd>{{releaseGroup}}</dd>
{{/if}}
{{#if nzbInfoUrl}}
<dt>Info:</dt>
<dd><a href="{{nzbInfoUrl}}">{{nzbInfoUrl}}</a></dd>
{{/if}}
{{#if downloadClient}}
<dt>Download Client:</dt>
<dd>{{downloadClient}}</dd>
{{/if}}
{{#if downloadId}}
<dt>Grab ID:</dt>
<dd>{{downloadId}}</dd>
{{/if}}
{{#if age}}
{{historyAge}}
{{/if}}
{{#if publishedDate}}
<dt>Published Date:</dt>
<dd>{{ShortDate publishedDate}} {{LTS publishedDate}}</dd>
{{/if}}
{{/with}}
</dl>
{{/if_eq}}
{{#if_eq eventType compare="downloadFailed"}}
<dl class="dl-horizontal">
<dt>Name:</dt>
<dd>{{sourceTitle}}</dd>
{{#with data}}
<dt>Message:</dt>
<dd>{{message}}</dd>
{{/with}}
</dl>
{{/if_eq}}
{{#if_eq eventType compare="downloadFolderImported"}}
<dl class="dl-horizontal">
{{#if sourceTitle}}
<dt>Name:</dt>
<dd>{{sourceTitle}}</dd>
{{/if}}
{{#with data}}
{{#if droppedPath}}
<dt>Source:</dt>
<dd>{{droppedPath}}</dd>
{{/if}}
{{#if importedPath}}
<dt>Imported To:</dt>
<dd>{{importedPath}}</dd>
{{/if}}
{{/with}}
</dl>
{{/if_eq}}
{{#if_eq eventType compare="episodeFileDeleted"}}
<dl class="dl-horizontal">
<dt>Path:</dt>
<dd>{{sourceTitle}}</dd>
{{#with data}}
<dt>Reason:</dt>
<dd>
{{#if_eq reason compare="Manual"}}
File was deleted by via UI
{{/if_eq}}
{{#if_eq reason compare="MissingFromDisk"}}
Sonarr was unable to find the file on disk so it was removed
{{/if_eq}}
{{#if_eq reason compare="Upgrade"}}
File was deleted to import an upgrade
{{/if_eq}}
</dd>
{{/with}}
</dl>
{{/if_eq}}
{{#if_eq eventType compare="episodeFileRenamed"}}
<dl class="dl-horizontal">
<dt>Source Path:</dt>
<dd>{{sourceTitle}}</dd>
{{#with data}}
<dt>Source Relative Path:</dt>
<dd>{{sourceRelativePath}}</dd>
<dt>Path:</dt>
<dd>{{path}}</dd>
<dt>Relative Path:</dt>
<dd>{{relativePath}}</dd>
{{/with}}
</dl>
{{/if_eq}}

@ -1,87 +0,0 @@
var HistoryModel = require('./HistoryModel');
var PageableCollection = require('backbone.pageable');
var AsFilteredCollection = require('../../Mixins/AsFilteredCollection');
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
var AsPersistedStateCollection = require('../../Mixins/AsPersistedStateCollection');
var Collection = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/history',
model : HistoryModel,
state : {
pageSize : 15,
sortKey : 'date',
order : 1
},
queryParams : {
totalPages : null,
totalRecords : null,
pageSize : 'pageSize',
sortKey : 'sortKey',
order : 'sortDir',
directions : {
'-1' : 'asc',
'1' : 'desc'
}
},
filterModes : {
'all' : [
null,
null
],
'grabbed' : [
'eventType',
'1'
],
'imported' : [
'eventType',
'3'
],
'failed' : [
'eventType',
'4'
],
'deleted' : [
'eventType',
'5'
],
'renamed' : [
'eventType',
'6'
]
},
sortMappings : {
'series' : { sortKey : 'series.sortTitle' }
},
initialize : function(options) {
delete this.queryParams.episodeId;
if (options) {
if (options.episodeId) {
this.queryParams.episodeId = options.episodeId;
}
}
},
parseState : function(resp) {
return { totalRecords : resp.totalRecords };
},
parseRecords : function(resp) {
if (resp) {
return resp.records;
}
return resp;
}
});
Collection = AsFilteredCollection.call(Collection);
Collection = AsSortedCollection.call(Collection);
Collection = AsPersistedStateCollection.call(Collection);
module.exports = Collection;

@ -1,21 +0,0 @@
var vent = require('vent');
var NzbDroneCell = require('../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({
className : 'history-details-cell',
events : {
'click' : '_showDetails'
},
render : function() {
this.$el.empty();
this.$el.html('<i class="icon-sonarr-info"></i>');
return this;
},
_showDetails : function() {
vent.trigger(vent.Commands.ShowHistoryDetails, { model : this.model });
}
});

@ -1,161 +0,0 @@
var Marionette = require('marionette');
var Backgrid = require('backgrid');
var HistoryCollection = require('./HistoryCollection');
var EventTypeCell = require('../../Cells/EventTypeCell');
var SeriesTitleCell = require('../../Cells/SeriesTitleCell');
var EpisodeNumberCell = require('../../Cells/EpisodeNumberCell');
var EpisodeTitleCell = require('../../Cells/EpisodeTitleCell');
var HistoryQualityCell = require('./HistoryQualityCell');
var RelativeDateCell = require('../../Cells/RelativeDateCell');
var HistoryDetailsCell = require('./HistoryDetailsCell');
var GridPager = require('../../Shared/Grid/Pager');
var ToolbarLayout = require('../../Shared/Toolbar/ToolbarLayout');
var LoadingView = require('../../Shared/LoadingView');
module.exports = Marionette.Layout.extend({
template : 'Activity/History/HistoryLayoutTemplate',
regions : {
history : '#x-history',
toolbar : '#x-history-toolbar',
pager : '#x-history-pager'
},
columns : [
{
name : 'eventType',
label : '',
cell : EventTypeCell,
cellValue : 'this'
},
{
name : 'series',
label : 'Series',
cell : SeriesTitleCell
},
{
name : 'episode',
label : 'Episode',
cell : EpisodeNumberCell,
sortable : false
},
{
name : 'episode',
label : 'Episode Title',
cell : EpisodeTitleCell,
sortable : false
},
{
name : 'this',
label : 'Quality',
cell : HistoryQualityCell,
sortable : false
},
{
name : 'date',
label : 'Date',
cell : RelativeDateCell
},
{
name : 'this',
label : '',
cell : HistoryDetailsCell,
sortable : false
}
],
initialize : function() {
this.collection = new HistoryCollection({ tableName : 'history' });
this.listenTo(this.collection, 'sync', this._showTable);
},
onShow : function() {
this.history.show(new LoadingView());
this._showToolbar();
},
_showTable : function(collection) {
this.history.show(new Backgrid.Grid({
columns : this.columns,
collection : collection,
className : 'table table-hover'
}));
this.pager.show(new GridPager({
columns : this.columns,
collection : collection
}));
},
_showToolbar : function() {
var filterOptions = {
type : 'radio',
storeState : true,
menuKey : 'history.filterMode',
defaultAction : 'all',
items : [
{
key : 'all',
title : '',
tooltip : 'All',
icon : 'icon-sonarr-all',
callback : this._setFilter
},
{
key : 'grabbed',
title : '',
tooltip : 'Grabbed',
icon : 'icon-sonarr-downloading',
callback : this._setFilter
},
{
key : 'imported',
title : '',
tooltip : 'Imported',
icon : 'icon-sonarr-imported',
callback : this._setFilter
},
{
key : 'failed',
title : '',
tooltip : 'Failed',
icon : 'icon-sonarr-download-failed',
callback : this._setFilter
},
{
key : 'deleted',
title : '',
tooltip : 'Deleted',
icon : 'icon-sonarr-deleted',
callback : this._setFilter
},
{
key : 'renamed',
title : '',
tooltip : 'Renamed',
icon : 'icon-sonarr-rename',
callback : this._setFilter
}
]
};
this.toolbar.show(new ToolbarLayout({
right : [
filterOptions
],
context : this
}));
},
_setFilter : function(buttonContext) {
var mode = buttonContext.model.get('key');
this.collection.state.currentPage = 1;
var promise = this.collection.setFilterMode(mode);
if (buttonContext) {
buttonContext.ui.icon.spinForPromise(promise);
}
}
});

@ -1,11 +0,0 @@
<div id="x-history-toolbar"/>
<div class="row">
<div class="col-md-12">
<div id="x-history" class="table-responsive"/>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="x-history-pager"/>
</div>
</div>

@ -1,12 +0,0 @@
var Backbone = require('backbone');
var SeriesModel = require('../../Series/SeriesModel');
var EpisodeModel = require('../../Series/EpisodeModel');
module.exports = Backbone.Model.extend({
parse : function(model) {
model.series = new SeriesModel(model.series);
model.episode = new EpisodeModel(model.episode);
model.episode.set('series', model.series);
return model;
}
});

@ -1,30 +0,0 @@
var NzbDroneCell = require('../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({
className : 'history-quality-cell',
render : function() {
var title = '';
var quality = this.model.get('quality');
var revision = quality.revision;
if (revision.real && revision.real > 0) {
title += ' REAL';
}
if (revision.version && revision.version > 1) {
title += ' PROPER';
}
title = title.trim();
if (this.model.get('qualityCutoffNotMet')) {
this.$el.html('<span class="badge badge-inverse" title="{0}">{1}</span>'.format(title, quality.quality.name));
} else {
this.$el.html('<span class="badge" title="{0}">{1}</span>'.format(title, quality.quality.name));
}
return this;
}
});

@ -1,23 +0,0 @@
var NzbDroneCell = require('../../Cells/NzbDroneCell');
module.exports = NzbDroneCell.extend({
className : 'progress-cell',
render : function() {
this.$el.empty();
if (this.cellValue) {
var status = this.model.get('status').toLowerCase();
if (status === 'downloading') {
var progress = 100 - (this.model.get('sizeleft') / this.model.get('size') * 100);
this.$el.html('<div class="progress" title="{0}%">'.format(progress.toFixed(1)) +
'<div class="progress-bar progress-bar-purple" style="width: {0}%;"></div></div>'.format(progress));
}
}
return this;
}
});

@ -1,60 +0,0 @@
'use strict';
var $ = require('jquery');
var _ = require('underscore');
var vent = require('../../vent');
var TemplatedCell = require('../../Cells/TemplatedCell');
var RemoveFromQueueView = require('./RemoveFromQueueView');
module.exports = TemplatedCell.extend({
template : 'Activity/Queue/QueueActionsCellTemplate',
className : 'queue-actions-cell',
events : {
'click .x-remove' : '_remove',
'click .x-manual-import' : '_manualImport',
'click .x-grab' : '_grab'
},
ui : {
import : '.x-import',
grab : '.x-grab'
},
_remove : function() {
var status = this.model.get('status');
var showBlacklist = status !== 'Delay' && status !== 'DownloadClientUnavailable';
vent.trigger(vent.Commands.OpenModalCommand, new RemoveFromQueueView({
model : this.model,
showBlacklist : showBlacklist
}));
},
_manualImport : function () {
vent.trigger(vent.Commands.ShowManualImport,
{
downloadId: this.model.get('downloadId'),
title: this.model.get('title')
});
},
_grab : function() {
var self = this;
var data = _.omit(this.model.toJSON(), 'series', 'episode');
var promise = $.ajax({
url : window.NzbDrone.ApiRoot + '/queue/grab',
type : 'POST',
data : JSON.stringify(data)
});
this.$(this.ui.grab).spinForPromise(promise);
promise.success(function() {
//find models that have the same series id and episode ids and remove them
self.model.trigger('destroy', self.model);
});
}
});

@ -1,19 +0,0 @@
{{#if_eq status compare="Completed"}}
{{#if_eq trackedDownloadStatus compare="Warning"}}
<i class="icon-sonarr-import-manual x-manual-import" title="Manual import"></i>
{{/if_eq}}
{{/if_eq}}
{{#if_eq status compare="Delay"}}
<i class="icon-sonarr-download x-grab" title="Add to download queue (Override Delay Profile)"></i>
<i class="icon-sonarr-delete x-remove" title="Remove pending release"></i>
{{else}}
{{#unless_eq status compare="DownloadClientUnavailable"}}
<i class="icon-sonarr-delete x-remove" title="Remove from download client"></i>
{{/unless_eq}}
{{/if_eq}}
{{#if_eq status compare="DownloadClientUnavailable"}}
<i class="icon-sonarr-download x-grab" title="Add to download queue (Retry failed download)"></i>
<i class="icon-sonarr-delete x-remove" title="Remove pending release"></i>
{{/if_eq}}

@ -1,87 +0,0 @@
var _ = require('underscore');
var PageableCollection = require('backbone.pageable');
//var PageableCollection = require('../../Shared/Grid/SonarrPageableCollection');
var QueueModel = require('./QueueModel');
var FormatHelpers = require('../../Shared/FormatHelpers');
var AsSortedCollection = require('../../Mixins/AsSortedCollection');
var AsPageableCollection = require('../../Mixins/AsPageableCollection');
var moment = require('moment');
require('../../Mixins/backbone.signalr.mixin');
var QueueCollection = PageableCollection.extend({
url : window.NzbDrone.ApiRoot + '/queue',
model : QueueModel,
state : {
pageSize : 15,
sortKey: 'timeleft'
},
mode : 'client',
findEpisode : function(episodeId) {
return _.find(this.fullCollection.models, function(queueModel) {
return queueModel.get('episode').id === episodeId;
});
},
sortMappings : {
series : {
sortValue : function(model, attr) {
var series = model.get(attr);
return series.get('sortTitle');
}
},
episode : {
sortValue : function(model, attr) {
var episode = model.get('episode');
return FormatHelpers.pad(episode.get('seasonNumber'), 4) + FormatHelpers.pad(episode.get('episodeNumber'), 4);
}
},
episodeTitle : {
sortValue : function(model, attr) {
var episode = model.get('episode');
return episode.get('title');
}
},
timeleft : {
sortValue : function(model, attr) {
var eta = model.get('estimatedCompletionTime');
if (eta) {
return moment(eta).unix();
}
return Number.MAX_VALUE;
}
},
sizeleft : {
sortValue : function(model, attr) {
var size = model.get('size');
var sizeleft = model.get('sizeleft');
if (size && sizeleft) {
return sizeleft / size;
}
return 0;
}
}
}
});
QueueCollection = AsSortedCollection.call(QueueCollection);
QueueCollection = AsPageableCollection.call(QueueCollection);
var collection = new QueueCollection().bindSignalR();
collection.fetch();
module.exports = collection;

@ -1,97 +0,0 @@
var Marionette = require('marionette');
var Backgrid = require('backgrid');
var QueueCollection = require('./QueueCollection');
var SeriesTitleCell = require('../../Cells/SeriesTitleCell');
var EpisodeNumberCell = require('../../Cells/EpisodeNumberCell');
var EpisodeTitleCell = require('../../Cells/EpisodeTitleCell');
var QualityCell = require('../../Cells/QualityCell');
var QueueStatusCell = require('./QueueStatusCell');
var QueueActionsCell = require('./QueueActionsCell');
var TimeleftCell = require('./TimeleftCell');
var ProgressCell = require('./ProgressCell');
var ProtocolCell = require('../../Release/ProtocolCell');
var GridPager = require('../../Shared/Grid/Pager');
module.exports = Marionette.Layout.extend({
template : 'Activity/Queue/QueueLayoutTemplate',
regions : {
table : '#x-queue',
pager : '#x-queue-pager'
},
columns : [
{
name : 'status',
label : '',
cell : QueueStatusCell,
cellValue : 'this'
},
{
name : 'series',
label : 'Series',
cell : SeriesTitleCell
},
{
name : 'episode',
label : 'Episode',
cell : EpisodeNumberCell
},
{
name : 'episodeTitle',
label : 'Episode Title',
cell : EpisodeTitleCell,
cellValue : 'episode'
},
{
name : 'quality',
label : 'Quality',
cell : QualityCell,
sortable : false
},
{
name : 'protocol',
label : 'Protocol',
cell : ProtocolCell
},
{
name : 'timeleft',
label : 'Time Left',
cell : TimeleftCell,
cellValue : 'this'
},
{
name : 'sizeleft',
label : 'Progress',
cell : ProgressCell,
cellValue : 'this'
},
{
name : 'status',
label : '',
cell : QueueActionsCell,
cellValue : 'this'
}
],
initialize : function() {
this.listenTo(QueueCollection, 'sync', this._showTable);
},
onShow : function() {
this._showTable();
},
_showTable : function() {
this.table.show(new Backgrid.Grid({
columns : this.columns,
collection : QueueCollection,
className : 'table table-hover'
}));
this.pager.show(new GridPager({
columns : this.columns,
collection : QueueCollection
}));
}
});

@ -1,11 +0,0 @@
<div class="row">
<div class="col-md-12">
<div id="x-queue" class="queue table-responsive"/>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="x-queue-pager"/>
</div>
</div>

@ -1,12 +0,0 @@
var Backbone = require('backbone');
var SeriesModel = require('../../Series/SeriesModel');
var EpisodeModel = require('../../Series/EpisodeModel');
module.exports = Backbone.Model.extend({
parse : function(model) {
model.series = new SeriesModel(model.series);
model.episode = new EpisodeModel(model.episode);
model.episode.set('series', model.series);
return model;
}
});

@ -1,91 +0,0 @@
var Marionette = require('marionette');
var NzbDroneCell = require('../../Cells/NzbDroneCell');
var moment = require('moment');
var UiSettingsModel = require('../../Shared/UiSettingsModel');
var FormatHelpers = require('../../Shared/FormatHelpers');
module.exports = NzbDroneCell.extend({
className : 'queue-status-cell',
template : 'Activity/Queue/QueueStatusCellTemplate',
render : function() {
this.$el.empty();
if (this.cellValue) {
var status = this.cellValue.get('status').toLowerCase();
var trackedDownloadStatus = this.cellValue.has('trackedDownloadStatus') ? this.cellValue.get('trackedDownloadStatus').toLowerCase() : 'ok';
var icon = 'icon-sonarr-downloading';
var title = 'Downloading';
var itemTitle = this.cellValue.get('title');
var content = itemTitle;
if (status === 'paused') {
icon = 'icon-sonarr-paused';
title = 'Paused';
}
if (status === 'queued') {
icon = 'icon-sonarr-queued';
title = 'Queued';
}
if (status === 'completed') {
icon = 'icon-sonarr-downloaded';
title = 'Downloaded';
}
if (status === 'delay') {
icon = 'icon-sonarr-pending';
var ect = this.cellValue.get('estimatedCompletionTime');
var time = '{0} at {1}'.format(FormatHelpers.relativeDate(ect), moment(ect).format(UiSettingsModel.time(true, false)));
title = 'Download delayed till {0}'.format(time);
}
if (status === 'downloadclientunavailable') {
icon = 'icon-sonarr-client-unavailable';
title = 'Download pending, download client is unavailable';
}
if (status === 'failed') {
icon = 'icon-sonarr-download-failed';
title = 'Download failed';
}
if (status === 'warning') {
icon = 'icon-sonarr-download-warning';
title = 'Download warning: check download client for more details';
}
if (trackedDownloadStatus === 'warning') {
icon += ' icon-sonarr-warning';
this.templateFunction = Marionette.TemplateCache.get(this.template);
content = this.templateFunction(this.cellValue.toJSON());
}
if (trackedDownloadStatus === 'error') {
if (status === 'completed') {
icon = 'icon-sonarr-import-failed';
title = 'Import failed: ' + itemTitle;
} else {
icon = 'icon-sonarr-download-failed';
title = 'Download failed';
}
this.templateFunction = Marionette.TemplateCache.get(this.template);
content = this.templateFunction(this.cellValue.toJSON());
}
this.$el.html('<i class="{0}"></i>'.format(icon));
this.$el.popover({
content : content,
html : true,
trigger : 'hover',
title : title,
placement : 'right',
container : this.$el
});
}
return this;
}
});

@ -1,8 +0,0 @@
{{#each statusMessages}}
{{title}}
<ul>
{{#each messages}}
<li>{{this}}</li>
{{/each}}
</ul>
{{/each}}

@ -1,40 +0,0 @@
var _ = require('underscore');
var Marionette = require('marionette');
var QueueCollection = require('./QueueCollection');
module.exports = Marionette.ItemView.extend({
tagName : 'span',
initialize : function() {
this.listenTo(QueueCollection, 'sync', this.render);
QueueCollection.fetch();
},
render : function() {
this.$el.empty();
if (QueueCollection.length === 0) {
return this;
}
var count = QueueCollection.fullCollection.length;
var label = 'label-info';
var errors = QueueCollection.fullCollection.some(function(model) {
return model.has('trackedDownloadStatus') && model.get('trackedDownloadStatus').toLowerCase() === 'error';
});
var warnings = QueueCollection.fullCollection.some(function(model) {
return model.has('trackedDownloadStatus') && model.get('trackedDownloadStatus').toLowerCase() === 'warning';
});
if (errors) {
label = 'label-danger';
} else if (warnings) {
label = 'label-warning';
}
this.$el.html('<span class="label {0}">{1}</span>'.format(label, count));
return this;
}
});

@ -1,34 +0,0 @@
var vent = require('../../vent');
var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({
template : 'Activity/Queue/RemoveFromQueueViewTemplate',
events : {
'click .x-confirm-remove' : 'removeItem'
},
ui : {
blacklist : '.x-blacklist',
indicator : '.x-indicator'
},
initialize : function(options) {
this.templateHelpers = {
showBlacklist : options.showBlacklist
};
},
removeItem : function() {
var blacklist = this.ui.blacklist.prop('checked') || false;
this.ui.indicator.show();
this.model.destroy({
data : { 'blacklist' : blacklist },
wait : true
}).done(function() {
vent.trigger(vent.Commands.CloseModalCommand);
});
}
});

@ -1,49 +0,0 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>{{title}}</h3>
</div>
<div class="modal-body remove-from-queue-modal">
<div class="row">
<div class="col-sm-12">
Are you sure you want to remove '{{title}}'?
</div>
</div>
{{#if showBlacklist}}
<div class="row">
<div class="col-sm-12">
<div class="form-horizontal">
<div class="form-group">
<label class="col-sm-4 control-label">Blacklist release</label>
<div class="col-sm-8">
<div class="input-group">
<label class="checkbox toggle well">
<input type="checkbox" class="x-blacklist"/>
<p>
<span>Yes</span>
<span>No</span>
</p>
<div class="btn slide-button btn-danger"/>
</label>
<span class="help-inline-checkbox">
<i class="icon-sonarr-form-info" title="Do you want to blacklist this release?"/>
</span>
</div>
</div>
</div>
</div>
</div>
</div>
{{/if}}
</div>
<div class="modal-footer">
<span class="indicator x-indicator"><i class="icon-sonarr-spinner fa-spin"></i></span>
<button class="btn" data-dismiss="modal">Cancel</button>
<button class="btn btn-danger x-confirm-remove">Remove</button>
</div>
</div>

@ -1,48 +0,0 @@
var NzbDroneCell = require('../../Cells/NzbDroneCell');
var moment = require('moment');
var UiSettingsModel = require('../../Shared/UiSettingsModel');
var FormatHelpers = require('../../Shared/FormatHelpers');
module.exports = NzbDroneCell.extend({
className : 'timeleft-cell',
render : function() {
this.$el.empty();
if (this.cellValue) {
var status = this.cellValue.get('status').toLowerCase();
var ect = this.cellValue.get('estimatedCompletionTime');
var time = '{0} at {1}'.format(FormatHelpers.relativeDate(ect), moment(ect).format(UiSettingsModel.time(true, false)));
if (status === 'delay') {
this.$el.html('<div title="Delaying download till {0}">-</div>'.format(time));
} else if (status === 'downloadclientunavailable') {
this.$el.html('<div title="Retrying download at {0}">-</div>'.format(time));
} else {
var timeleft = this.cellValue.get('timeleft');
var totalSize = FormatHelpers.bytes(this.cellValue.get('size'), 2);
var remainingSize = FormatHelpers.bytes(this.cellValue.get('sizeleft'), 2);
if (timeleft === undefined) {
this.$el.html('-');
} else {
var duration = moment.duration(timeleft);
var days = duration.get('days');
var hours = FormatHelpers.pad(duration.get('hours'), 2);
var minutes = FormatHelpers.pad(duration.get('minutes'), 2);
var seconds = FormatHelpers.pad(duration.get('seconds'), 2);
var formattedTime = '{0}:{1}:{2}'.format(hours, minutes, seconds);
if (days > 0) {
formattedTime = days + 'd ' + formattedTime;
}
this.$el.html('<span title="{1} / {2}">{0}</span>'.format(formattedTime, remainingSize, totalSize));
}
}
}
return this;
}
});

@ -1,27 +0,0 @@
.queue-status-cell .popover {
max-width : 800px;
}
.queue {
.protocol-cell {
text-align : center;
width : 80px;
}
.episode-number-cell {
min-width : 90px;
}
}
.remove-from-queue-modal {
.form-horizontal {
margin-top : 20px;
}
}
.history-detail-modal {
.info {
word-wrap: break-word;
}
}

@ -1,22 +0,0 @@
var Backbone = require('backbone');
var SeriesModel = require('../Series/SeriesModel');
var _ = require('underscore');
module.exports = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/series/lookup',
model : SeriesModel,
parse : function(response) {
var self = this;
_.each(response, function(model) {
model.id = undefined;
if (self.unmappedFolderModel) {
model.path = self.unmappedFolderModel.get('folder').path;
}
});
return response;
}
});

@ -1,53 +0,0 @@
var vent = require('vent');
var AppLayout = require('../AppLayout');
var Marionette = require('marionette');
var RootFolderLayout = require('./RootFolders/RootFolderLayout');
var ExistingSeriesCollectionView = require('./Existing/AddExistingSeriesCollectionView');
var AddSeriesView = require('./AddSeriesView');
var ProfileCollection = require('../Profile/ProfileCollection');
var RootFolderCollection = require('./RootFolders/RootFolderCollection');
require('../Series/SeriesCollection');
module.exports = Marionette.Layout.extend({
template : 'AddSeries/AddSeriesLayoutTemplate',
regions : {
workspace : '#add-series-workspace'
},
events : {
'click .x-import' : '_importSeries',
'click .x-add-new' : '_addSeries'
},
attributes : {
id : 'add-series-screen'
},
initialize : function() {
ProfileCollection.fetch();
RootFolderCollection.fetch().done(function() {
RootFolderCollection.synced = true;
});
},
onShow : function() {
this.workspace.show(new AddSeriesView());
},
_folderSelected : function(options) {
vent.trigger(vent.Commands.CloseModalCommand);
this.workspace.show(new ExistingSeriesCollectionView({ model : options.model }));
},
_importSeries : function() {
this.rootFolderLayout = new RootFolderLayout();
this.listenTo(this.rootFolderLayout, 'folderSelected', this._folderSelected);
AppLayout.modalRegion.show(this.rootFolderLayout);
},
_addSeries : function() {
this.workspace.show(new AddSeriesView());
}
});

@ -1,17 +0,0 @@
<div class="row">
<div class="col-md-12">
<div class="btn-group add-series-btn-group btn-group-lg btn-block">
<button type="button" class="btn btn-default col-md-10 col-xs-8 add-series-import-btn x-import">
<i class="icon-sonarr-hdd"/>
Import existing series on disk
</button>
<button class="btn btn-default col-md-2 col-xs-4 x-add-new"><i class="icon-sonarr-active hidden-xs"></i> Add New Series</button>
</div>
</div>
</div>
<div class="row">
<div class="col-md-12">
<div id="add-series-workspace"></div>
</div>
</div>

@ -1,182 +0,0 @@
var _ = require('underscore');
var vent = require('vent');
var Marionette = require('marionette');
var AddSeriesCollection = require('./AddSeriesCollection');
var SearchResultCollectionView = require('./SearchResultCollectionView');
var EmptyView = require('./EmptyView');
var NotFoundView = require('./NotFoundView');
var ErrorView = require('./ErrorView');
var LoadingView = require('../Shared/LoadingView');
module.exports = Marionette.Layout.extend({
template : 'AddSeries/AddSeriesViewTemplate',
regions : {
searchResult : '#search-result'
},
ui : {
seriesSearch : '.x-series-search',
searchBar : '.x-search-bar',
loadMore : '.x-load-more'
},
events : {
'click .x-load-more' : '_onLoadMore'
},
initialize : function(options) {
this.isExisting = options.isExisting;
this.collection = new AddSeriesCollection();
if (this.isExisting) {
this.collection.unmappedFolderModel = this.model;
}
if (this.isExisting) {
this.className = 'existing-series';
} else {
this.className = 'new-series';
}
this.listenTo(vent, vent.Events.SeriesAdded, this._onSeriesAdded);
this.listenTo(this.collection, 'sync', this._showResults);
this.resultCollectionView = new SearchResultCollectionView({
collection : this.collection,
isExisting : this.isExisting
});
this.throttledSearch = _.debounce(this.search, 1000, { trailing : true }).bind(this);
},
onRender : function() {
var self = this;
this.$el.addClass(this.className);
this.ui.seriesSearch.on('input', function(e) {
if (_.contains([
9,
16,
17,
18,
19,
20,
33,
34,
35,
36,
37,
38,
39,
40,
91,
92,
93
], e.keyCode)) {
return;
}
self._abortExistingSearch();
self.throttledSearch({
term : self.ui.seriesSearch.val()
});
});
this._clearResults();
if (this.isExisting) {
this.ui.searchBar.hide();
}
},
onShow : function() {
this.ui.seriesSearch.focus();
},
search : function(options) {
var self = this;
this.collection.reset();
if (!options.term || options.term === this.collection.term) {
return Marionette.$.Deferred().resolve();
}
this.searchResult.show(new LoadingView());
this.collection.term = options.term;
this.currentSearchPromise = this.collection.fetch({
data : { term : options.term }
});
this.currentSearchPromise.fail(function() {
self._showError();
});
return this.currentSearchPromise;
},
_onSeriesAdded : function(options) {
if (this.isExisting && options.series.get('path') === this.model.get('folder').path) {
this.close();
}
else if (!this.isExisting) {
this.collection.term = '';
this.collection.reset();
this._clearResults();
this.ui.seriesSearch.val('');
this.ui.seriesSearch.focus();
}
},
_onLoadMore : function() {
var showingAll = this.resultCollectionView.showMore();
this.ui.searchBar.show();
if (showingAll) {
this.ui.loadMore.hide();
}
},
_clearResults : function() {
if (!this.isExisting) {
this.searchResult.show(new EmptyView());
} else {
this.searchResult.close();
}
},
_showResults : function() {
if (!this.isClosed) {
if (this.collection.length === 0) {
this.ui.searchBar.show();
this.searchResult.show(new NotFoundView({ term : this.collection.term }));
} else {
this.searchResult.show(this.resultCollectionView);
if (!this.showingAll && this.isExisting) {
this.ui.loadMore.show();
}
}
}
},
_abortExistingSearch : function() {
if (this.currentSearchPromise && this.currentSearchPromise.readyState > 0 && this.currentSearchPromise.readyState < 4) {
console.log('aborting previous pending search request.');
this.currentSearchPromise.abort();
} else {
this._clearResults();
}
},
_showError : function() {
if (!this.isClosed) {
this.ui.searchBar.show();
this.searchResult.show(new ErrorView({ term : this.collection.term }));
this.collection.term = '';
}
}
});

@ -1,24 +0,0 @@
{{#if folder.path}}
<div class="unmapped-folder-path">
<div class="col-md-12">
{{folder.path}}
</div>
</div>{{/if}}
<div class="x-search-bar">
<div class="input-group input-group-lg add-series-search">
<span class="input-group-addon"><i class="icon-sonarr-search"/></span>
{{#if folder}}
<input type="text" class="form-control x-series-search" value="{{folder.name}}">
{{else}}
<input type="text" class="form-control x-series-search" placeholder="Start typing the name of series you want to add ...">
{{/if}}
</div>
</div>
<div class="row">
<div id="search-result" class="result-list col-md-12"/>
</div>
<div class="btn btn-block text-center new-series-loadmore x-load-more" style="display: none;">
<i class="icon-sonarr-load-more"/>
more
</div>

@ -1,5 +0,0 @@
var Marionette = require('marionette');
module.exports = Marionette.CompositeView.extend({
template : 'AddSeries/EmptyViewTemplate'
});

@ -1,3 +0,0 @@
<div class="text-center hint col-md-12">
<span>You can also search by tvdbid using the tvdb: prefixes.</span>
</div>

@ -1,13 +0,0 @@
var Marionette = require('marionette');
module.exports = Marionette.CompositeView.extend({
template : 'AddSeries/ErrorViewTemplate',
initialize : function(options) {
this.options = options;
},
templateHelpers : function() {
return this.options;
}
});

@ -1,7 +0,0 @@
<div class="text-center col-md-12">
<h3>
There was an error searching for '{{term}}'.
</h3>
If the series title contains non-alphanumeric characters try removing them, otherwise try your search again later.
</div>

@ -1,51 +0,0 @@
var Marionette = require('marionette');
var AddSeriesView = require('../AddSeriesView');
var UnmappedFolderCollection = require('./UnmappedFolderCollection');
module.exports = Marionette.CompositeView.extend({
itemView : AddSeriesView,
itemViewContainer : '.x-loading-folders',
template : 'AddSeries/Existing/AddExistingSeriesCollectionViewTemplate',
ui : {
loadingFolders : '.x-loading-folders'
},
initialize : function() {
this.collection = new UnmappedFolderCollection();
this.collection.importItems(this.model);
},
showCollection : function() {
this._showAndSearch(0);
},
appendHtml : function(collectionView, itemView, index) {
collectionView.ui.loadingFolders.before(itemView.el);
},
_showAndSearch : function(index) {
var self = this;
var model = this.collection.at(index);
if (model) {
var currentIndex = index;
var folderName = model.get('folder').name;
this.addItemView(model, this.getItemView(), index);
this.children.findByModel(model).search({ term : folderName }).always(function() {
if (!self.isClosed) {
self._showAndSearch(currentIndex + 1);
}
});
}
else {
this.ui.loadingFolders.hide();
}
},
itemViewOptions : {
isExisting : true
}
});

@ -1,5 +0,0 @@
<div class="x-existing-folders">
<div class="loading-folders x-loading-folders">
Loading search results from TheTVDB for your series, this may take a few minutes.
</div>
</div>

@ -1,20 +0,0 @@
var Backbone = require('backbone');
var UnmappedFolderModel = require('./UnmappedFolderModel');
var _ = require('underscore');
module.exports = Backbone.Collection.extend({
model : UnmappedFolderModel,
importItems : function(rootFolderModel) {
this.reset();
var rootFolder = rootFolderModel;
_.each(rootFolderModel.get('unmappedFolders'), function(folder) {
this.push(new UnmappedFolderModel({
rootFolder : rootFolder,
folder : folder
}));
}, this);
}
});

@ -1,3 +0,0 @@
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({});

@ -1,18 +0,0 @@
<dl class="monitor-tooltip-contents">
<dt>All</dt>
<dd>Monitor all episodes except specials</dd>
<dt>Future</dt>
<dd>Monitor episodes that have not aired yet</dd>
<dt>Missing</dt>
<dd>Monitor episodes that do not have files or have not aired yet</dd>
<dt>Existing</dt>
<dd>Monitor episodes that have files or have not aired yet</dd>
<dt>First Season</dt>
<dd>Monitor all episodes of the first season. All other seasons will be ignored</dd>
<dt>Latest Season</dt>
<dd>Monitor all episodes of the latest season and future seasons</dd>
<dt>None</dt>
<dd>No episodes will be monitored.</dd>
<!--<dt>Latest Season</dt>-->
<!--<dd>Monitor all episodes the latest season only, previous seasons will be ignored</dd>-->
</dl>

@ -1,13 +0,0 @@
var Marionette = require('marionette');
module.exports = Marionette.CompositeView.extend({
template : 'AddSeries/NotFoundViewTemplate',
initialize : function(options) {
this.options = options;
},
templateHelpers : function() {
return this.options;
}
});

@ -1,7 +0,0 @@
<div class="text-center col-md-12">
<h3>
Sorry. We couldn't find any series matching '{{term}}'
</h3>
<a href="https://github.com/NzbDrone/NzbDrone/wiki/FAQ#wiki-why-cant-i-add-a-new-show-to-nzbdrone-its-on-thetvdb">Why can't I find my show?</a>
</div>

@ -1,10 +0,0 @@
var Backbone = require('backbone');
var RootFolderModel = require('./RootFolderModel');
require('../../Mixins/backbone.signalr.mixin');
var RootFolderCollection = Backbone.Collection.extend({
url : window.NzbDrone.ApiRoot + '/rootfolder',
model : RootFolderModel
});
module.exports = new RootFolderCollection();

@ -1,8 +0,0 @@
var Marionette = require('marionette');
var RootFolderItemView = require('./RootFolderItemView');
module.exports = Marionette.CompositeView.extend({
template : 'AddSeries/RootFolders/RootFolderCollectionViewTemplate',
itemViewContainer : '.x-root-folders',
itemView : RootFolderItemView
});

@ -1,13 +0,0 @@
<table class="table table-hover">
<thead>
<tr>
<th class="col-md-10 ">
Path
</th>
<th class="col-md-3">
Free Space
</th>
</tr>
</thead>
<tbody class="x-root-folders"></tbody>
</table>

@ -1,28 +0,0 @@
var Marionette = require('marionette');
module.exports = Marionette.ItemView.extend({
template : 'AddSeries/RootFolders/RootFolderItemViewTemplate',
className : 'recent-folder',
tagName : 'tr',
initialize : function() {
this.listenTo(this.model, 'change', this.render);
},
events : {
'click .x-delete' : 'removeFolder',
'click .x-folder' : 'folderSelected'
},
removeFolder : function() {
var self = this;
this.model.destroy().success(function() {
self.close();
});
},
folderSelected : function() {
this.trigger('folderSelected', this.model);
}
});

@ -1,9 +0,0 @@
<td class="col-md-10 x-folder folder-path">
{{path}}
</td>
<td class="col-md-3 x-folder folder-free-space">
<span>{{Bytes freeSpace}}</span>
</td>
<td class="col-md-1">
<i class="icon-sonarr-delete x-delete"></i>
</td>

@ -1,80 +0,0 @@
var Marionette = require('marionette');
var RootFolderCollectionView = require('./RootFolderCollectionView');
var RootFolderCollection = require('./RootFolderCollection');
var RootFolderModel = require('./RootFolderModel');
var LoadingView = require('../../Shared/LoadingView');
var AsValidatedView = require('../../Mixins/AsValidatedView');
require('../../Mixins/FileBrowser');
var Layout = Marionette.Layout.extend({
template : 'AddSeries/RootFolders/RootFolderLayoutTemplate',
ui : {
pathInput : '.x-path'
},
regions : {
currentDirs : '#current-dirs'
},
events : {
'click .x-add' : '_addFolder',
'keydown .x-path input' : '_keydown'
},
initialize : function() {
this.collection = RootFolderCollection;
this.rootfolderListView = null;
},
onShow : function() {
this.listenTo(RootFolderCollection, 'sync', this._showCurrentDirs);
this.currentDirs.show(new LoadingView());
if (RootFolderCollection.synced) {
this._showCurrentDirs();
}
this.ui.pathInput.fileBrowser();
},
_onFolderSelected : function(options) {
this.trigger('folderSelected', options);
},
_addFolder : function() {
var self = this;
var newDir = new RootFolderModel({
Path : this.ui.pathInput.val()
});
this.bindToModelValidation(newDir);
newDir.save().done(function() {
RootFolderCollection.add(newDir);
self.trigger('folderSelected', { model : newDir });
});
},
_showCurrentDirs : function() {
if (!this.rootfolderListView) {
this.rootfolderListView = new RootFolderCollectionView({ collection : RootFolderCollection });
this.currentDirs.show(this.rootfolderListView);
this.listenTo(this.rootfolderListView, 'itemview:folderSelected', this._onFolderSelected);
}
},
_keydown : function(e) {
if (e.keyCode !== 13) {
return;
}
this._addFolder();
}
});
var Layout = AsValidatedView.apply(Layout);
module.exports = Layout;

@ -1,36 +0,0 @@
<div class="modal-content">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-hidden="true">&times;</button>
<h3>Select Folder</h3>
</div>
<div class="modal-body root-folders-modal">
<div class="validation-errors"></div>
<div class="alert alert-info">Enter the path that contains some or all of your TV series, you will be able to choose which series you want to import<button type="button" class="close" data-dismiss="alert">×</button></div>
<div class="row">
<div class="form-group">
<div class="col-md-12">
<div class="input-group">
<span class="input-group-addon">&nbsp;<i class="icon-sonarr-folder-open"></i></span>
<input class="form-control x-path" type="text" validation-name="path" placeholder="Enter path to folder that contains your shows">
<span class="input-group-btn"><button class="btn btn-success x-add"><i class="icon-sonarr-ok"/></button></span>
</div>
</div>
</div>
</div>
<div class="row root-folders">
<div class="col-md-12">
{{#if items}}
<h4>Recent Folders</h4>
{{/if}}
<div id="current-dirs" class="root-folders-list"></div>
</div>
</div>
</div>
<div class="modal-footer">
<button class="btn" data-dismiss="modal">Close</button>
</div>
</div>

@ -1,8 +0,0 @@
var Backbone = require('backbone');
module.exports = Backbone.Model.extend({
urlRoot : window.NzbDrone.ApiRoot + '/rootfolder',
defaults : {
freeSpace : 0
}
});

@ -1,11 +0,0 @@
<select class="col-md-4 form-control x-root-folder" validation-name="RootFolderPath">
{{#if this}}
{{#each this}}
<option value="{{id}}">{{path}}</option>
{{/each}}
{{else}}
<option value="">Select Path</option>
{{/if}}
<option value="addNew">Add a different path</option>
</select>

@ -1,29 +0,0 @@
var Marionette = require('marionette');
var SearchResultView = require('./SearchResultView');
module.exports = Marionette.CollectionView.extend({
itemView : SearchResultView,
initialize : function(options) {
this.isExisting = options.isExisting;
this.showing = 1;
},
showAll : function() {
this.showingAll = true;
this.render();
},
showMore : function() {
this.showing += 5;
this.render();
return this.showing >= this.collection.length;
},
appendHtml : function(collectionView, itemView, index) {
if (!this.isExisting || index < this.showing || index === 0) {
collectionView.$el.append(itemView.el);
}
}
});

@ -1,288 +0,0 @@
var _ = require('underscore');
var vent = require('vent');
var AppLayout = require('../AppLayout');
var Backbone = require('backbone');
var Marionette = require('marionette');
var Profiles = require('../Profile/ProfileCollection');
var RootFolders = require('./RootFolders/RootFolderCollection');
var RootFolderLayout = require('./RootFolders/RootFolderLayout');
var SeriesCollection = require('../Series/SeriesCollection');
var Config = require('../Config');
var Messenger = require('../Shared/Messenger');
var AsValidatedView = require('../Mixins/AsValidatedView');
require('jquery.dotdotdot');
var view = Marionette.ItemView.extend({
template : 'AddSeries/SearchResultViewTemplate',
ui : {
profile : '.x-profile',
rootFolder : '.x-root-folder',
seasonFolder : '.x-season-folder',
seriesType : '.x-series-type',
monitor : '.x-monitor',
monitorTooltip : '.x-monitor-tooltip',
addButton : '.x-add',
addSearchButton : '.x-add-search',
overview : '.x-overview'
},
events : {
'click .x-add' : '_addWithoutSearch',
'click .x-add-search' : '_addAndSearch',
'change .x-profile' : '_profileChanged',
'change .x-root-folder' : '_rootFolderChanged',
'change .x-season-folder' : '_seasonFolderChanged',
'change .x-series-type' : '_seriesTypeChanged',
'change .x-monitor' : '_monitorChanged'
},
initialize : function() {
if (!this.model) {
throw 'model is required';
}
this.templateHelpers = {};
this._configureTemplateHelpers();
this.listenTo(vent, Config.Events.ConfigUpdatedEvent, this._onConfigUpdated);
this.listenTo(this.model, 'change', this.render);
this.listenTo(RootFolders, 'all', this._rootFoldersUpdated);
},
onRender : function() {
var defaultProfile = Config.getValue(Config.Keys.DefaultProfileId);
var defaultRoot = Config.getValue(Config.Keys.DefaultRootFolderId);
var useSeasonFolder = Config.getValueBoolean(Config.Keys.UseSeasonFolder, true);
var defaultSeriesType = Config.getValue(Config.Keys.DefaultSeriesType, 'standard');
var defaultMonitorEpisodes = Config.getValue(Config.Keys.MonitorEpisodes, 'missing');
if (Profiles.get(defaultProfile)) {
this.ui.profile.val(defaultProfile);
}
if (RootFolders.get(defaultRoot)) {
this.ui.rootFolder.val(defaultRoot);
}
this.ui.seasonFolder.prop('checked', useSeasonFolder);
this.ui.seriesType.val(defaultSeriesType);
this.ui.monitor.val(defaultMonitorEpisodes);
//TODO: make this work via onRender, FM?
//works with onShow, but stops working after the first render
this.ui.overview.dotdotdot({
height : 120
});
this.templateFunction = Marionette.TemplateCache.get('AddSeries/MonitoringTooltipTemplate');
var content = this.templateFunction();
this.ui.monitorTooltip.popover({
content : content,
html : true,
trigger : 'hover',
title : 'Episode Monitoring Options',
placement : 'right',
container : this.$el
});
},
_configureTemplateHelpers : function() {
var existingSeries = SeriesCollection.where({ tvdbId : this.model.get('tvdbId') });
if (existingSeries.length > 0) {
this.templateHelpers.existing = existingSeries[0].toJSON();
}
this.templateHelpers.profiles = Profiles.toJSON();
if (!this.model.get('isExisting')) {
this.templateHelpers.rootFolders = RootFolders.toJSON();
}
},
_onConfigUpdated : function(options) {
if (options.key === Config.Keys.DefaultProfileId) {
this.ui.profile.val(options.value);
}
else if (options.key === Config.Keys.DefaultRootFolderId) {
this.ui.rootFolder.val(options.value);
}
else if (options.key === Config.Keys.UseSeasonFolder) {
this.ui.seasonFolder.prop('checked', options.value);
}
else if (options.key === Config.Keys.DefaultSeriesType) {
this.ui.seriesType.val(options.value);
}
else if (options.key === Config.Keys.MonitorEpisodes) {
this.ui.monitor.val(options.value);
}
},
_profileChanged : function() {
Config.setValue(Config.Keys.DefaultProfileId, this.ui.profile.val());
},
_seasonFolderChanged : function() {
Config.setValue(Config.Keys.UseSeasonFolder, this.ui.seasonFolder.prop('checked'));
},
_rootFolderChanged : function() {
var rootFolderValue = this.ui.rootFolder.val();
if (rootFolderValue === 'addNew') {
var rootFolderLayout = new RootFolderLayout();
this.listenToOnce(rootFolderLayout, 'folderSelected', this._setRootFolder);
AppLayout.modalRegion.show(rootFolderLayout);
} else {
Config.setValue(Config.Keys.DefaultRootFolderId, rootFolderValue);
}
},
_seriesTypeChanged : function() {
Config.setValue(Config.Keys.DefaultSeriesType, this.ui.seriesType.val());
},
_monitorChanged : function() {
Config.setValue(Config.Keys.MonitorEpisodes, this.ui.monitor.val());
},
_setRootFolder : function(options) {
vent.trigger(vent.Commands.CloseModalCommand);
this.ui.rootFolder.val(options.model.id);
this._rootFolderChanged();
},
_addWithoutSearch : function() {
this._addSeries(false);
},
_addAndSearch : function() {
this._addSeries(true);
},
_addSeries : function(searchForMissingEpisodes) {
var addButton = this.ui.addButton;
var addSearchButton = this.ui.addSearchButton;
addButton.addClass('disabled');
addSearchButton.addClass('disabled');
var profile = this.ui.profile.val();
var rootFolderPath = this.ui.rootFolder.children(':selected').text();
var seriesType = this.ui.seriesType.val();
var seasonFolder = this.ui.seasonFolder.prop('checked');
var options = this._getAddSeriesOptions();
options.searchForMissingEpisodes = searchForMissingEpisodes;
this.model.set({
profileId : profile,
rootFolderPath : rootFolderPath,
seasonFolder : seasonFolder,
seriesType : seriesType,
addOptions : options,
monitored : true
}, { silent : true });
var self = this;
var promise = this.model.save();
if (searchForMissingEpisodes) {
this.ui.addSearchButton.spinForPromise(promise);
}
else {
this.ui.addButton.spinForPromise(promise);
}
promise.always(function() {
addButton.removeClass('disabled');
addSearchButton.removeClass('disabled');
});
promise.done(function() {
SeriesCollection.add(self.model);
self.close();
Messenger.show({
message : 'Added: ' + self.model.get('title'),
actions : {
goToSeries : {
label : 'Go to Series',
action : function() {
Backbone.history.navigate('/series/' + self.model.get('titleSlug'), { trigger : true });
}
}
},
hideAfter : 8,
hideOnNavigate : true
});
vent.trigger(vent.Events.SeriesAdded, { series : self.model });
});
},
_rootFoldersUpdated : function() {
this._configureTemplateHelpers();
this.render();
},
_getAddSeriesOptions : function() {
var monitor = this.ui.monitor.val();
var lastSeason = _.max(this.model.get('seasons'), 'seasonNumber');
var firstSeason = _.min(_.reject(this.model.get('seasons'), { seasonNumber : 0 }), 'seasonNumber');
this.model.setSeasonPass(firstSeason.seasonNumber);
var options = {
ignoreEpisodesWithFiles : false,
ignoreEpisodesWithoutFiles : false
};
if (monitor === 'all') {
return options;
}
else if (monitor === 'future') {
options.ignoreEpisodesWithFiles = true;
options.ignoreEpisodesWithoutFiles = true;
}
else if (monitor === 'latest') {
this.model.setSeasonPass(lastSeason.seasonNumber);
}
else if (monitor === 'first') {
this.model.setSeasonPass(lastSeason.seasonNumber + 1);
this.model.setSeasonMonitored(firstSeason.seasonNumber);
}
else if (monitor === 'missing') {
options.ignoreEpisodesWithFiles = true;
}
else if (monitor === 'existing') {
options.ignoreEpisodesWithoutFiles = true;
}
else if (monitor === 'none') {
this.model.setSeasonPass(lastSeason.seasonNumber + 1);
}
return options;
}
});
AsValidatedView.apply(view);
module.exports = view;

@ -1,109 +0,0 @@
<div class="search-item {{#unless isExisting}}search-item-new{{/unless}}">
<div class="row">
<div class="col-md-2">
<a href="{{tvdbUrl}}" target="_blank">
{{poster}}
</a>
</div>
<div class="col-md-10">
<div class="row">
<div class="col-md-12">
<h2 class="series-title">
{{titleWithYear}}
<span class="labels">
<span class="label label-default">{{network}}</span>
{{#unless_eq status compare="continuing"}}
<span class="label label-danger">Ended</span>
{{/unless_eq}}
</span>
</h2>
</div>
</div>
<div class="row new-series-overview x-overview">
<div class="col-md-12 overview-internal">
{{overview}}
</div>
</div>
<div class="row">
{{#unless existing}}
{{#unless path}}
<div class="form-group col-md-4">
<label>Path</label>
{{> RootFolderSelectionPartial rootFolders}}
</div>
{{/unless}}
<div class="form-group col-md-2">
<label>Monitor <i class="icon-sonarr-form-info monitor-tooltip x-monitor-tooltip"></i></label>
<select class="form-control col-md-2 x-monitor">
<option value="all">All</option>
<option value="future">Future</option>
<option value="missing">Missing</option>
<option value="existing">Existing</option>
<option value="first">First Season</option>
<option value="latest">Latest Season</option>
<option value="none">None</option>
</select>
</div>
<div class="form-group col-md-2">
<label>Profile</label>
{{> ProfileSelectionPartial profiles}}
</div>
<div class="form-group col-md-2">
<label>Series Type</label>
{{> SeriesTypeSelectionPartial}}
</div>
<div class="form-group col-md-2">
<label>Season Folders</label>
<div class="input-group">
<label class="checkbox toggle well">
<input type="checkbox" class="x-season-folder"/>
<p>
<span>Yes</span>
<span>No</span>
</p>
<div class="btn btn-primary slide-button"/>
</label>
</div>
</div>
{{/unless}}
</div>
<div class="row">
{{#unless existing}}
{{#if title}}
<div class="form-group col-md-2 col-md-offset-10">
<!--Uncomment if we need to add even more controls to add series-->
<!--<label style="visibility: hidden">Add</label>-->
<div class="btn-group">
<button class="btn btn-success add x-add" title="Add">
<i class="icon-sonarr-add"></i>
</button>
<button class="btn btn-success add x-add-search" title="Add and Search for missing episodes">
<i class="icon-sonarr-search"></i>
</button>
</div>
</div>
{{else}}
<div class="col-md-2 col-md-offset-10" title="Series requires an English title">
<button class="btn add-series disabled">
Add
</button>
</div>
{{/if}}
{{else}}
<div class="col-md-2 col-md-offset-10">
<a class="btn btn-default" href="{{route}}">
Already Exists
</a>
</div>
{{/unless}}
</div>
</div>
</div>
</div>

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save