diff --git a/couchpotato/core/media/_base/media/main.py b/couchpotato/core/media/_base/media/main.py
index 01dc0f1..f561941 100755
--- a/couchpotato/core/media/_base/media/main.py
+++ b/couchpotato/core/media/_base/media/main.py
@@ -1,6 +1,7 @@
-from datetime import timedelta
+import os
import time
import traceback
+from datetime import timedelta
from string import ascii_lowercase
from CodernityDB.database import RecordNotFound, RecordDeleted
@@ -68,6 +69,7 @@ class MediaPlugin(MediaBase):
'params': {
'id': {'desc': 'Media ID(s) you want to delete.', 'type': 'int (comma separated)'},
'delete_from': {'desc': 'Delete media from this page', 'type': 'string: all (default), wanted, manage'},
+ 'with_files': {'desc': 'Delete the files as well', 'type': 'bool (true or false)'},
}
})
@@ -416,12 +418,62 @@ class MediaPlugin(MediaBase):
tempChar = lambda *args, **kwargs : self.charView(type = media_type, **kwargs)
addApiView('%s.available_chars' % media_type, tempChar)
- def delete(self, media_id, delete_from = None):
+ def deleteFiles(self, instance):
+ directories = dict()
+
+ # Walk through all files in the Couch database
+ for name, paths in instance['files'].iteritems():
+ log.info('Removing %s', name)
+ for path in paths:
+ # Add the directories and filename prefixes to a list so we can
+ # remove the directories and related files as well
+ directory = os.path.dirname(path)
+ if directory not in directories:
+ directories[directory] = set()
+
+ directories[directory].add(os.path.splitext(path)[0])
+
+ if os.path.isfile(path):
+ try:
+ os.remove(path)
+ log.info('Removed %s', path)
+ except:
+ log.error('Unable to remove %s', path)
+
+ # Walk through the directories and file prefixes for removal if
+ # possible
+ for directory, prefixes in directories.iteritems():
+ if os.path.isdir(directory):
+ # If the files in the directory have the same name as the
+ # expected files (except for extensions and stuff), remove them
+ files = os.listdir(directory)
+ removed = 0
+ for file_ in files:
+ for prefix in prefixes:
+ if file_.startswith(prefix):
+ try:
+ os.remove(file_)
+ removed += 1
+ log.info('Removed %s', file_)
+ except:
+ log.error('Unable to remove %s', file_)
+
+ try:
+ if len(files) == removed:
+ os.rmdir(directory)
+ log.info('Removed %s', directory)
+ else:
+ log.info('Not removing %s, %d files in directory',
+ (directory, len(files) - removed))
+ except Exception:
+ log.error('Unable to remove %s', directory)
+ def delete(self, media_id, delete_from = None, with_files = False):
try:
db = get_db()
media = db.get('id', media_id)
+
if media:
deleted = False
@@ -430,8 +482,14 @@ class MediaPlugin(MediaBase):
if delete_from == 'all':
# Delete connected releases
for release in media_releases:
+ if with_files:
+ self.deleteFiles(release)
+
db.delete(release)
+ if with_files:
+ self.deleteFiles(media)
+
db.delete(media)
deleted = True
else:
@@ -450,8 +508,13 @@ class MediaPlugin(MediaBase):
if release.get('status') == 'done' or media.get('status') == 'done':
db.delete(release)
total_deleted += 1
+ if with_files:
+ self.deleteFiles(release)
if (total_releases == total_deleted) or (total_releases == 0 and not new_media_status) or (not new_media_status and delete_from == 'late'):
+ if with_files:
+ self.deleteFiles(media)
+
db.delete(media)
deleted = True
elif new_media_status:
@@ -460,7 +523,7 @@ class MediaPlugin(MediaBase):
# Remove profile (no use for in manage)
if new_media_status == 'done':
media['profile_id'] = None
-
+
db.update(media)
fireEvent('media.untag', media['_id'], 'recent', single = True)
@@ -478,7 +541,7 @@ class MediaPlugin(MediaBase):
ids = splitString(id)
for media_id in ids:
- self.delete(media_id, delete_from = kwargs.get('delete_from', 'all'))
+ self.delete(media_id, delete_from = kwargs.get('delete_from', 'all'), with_files = kwargs.get('with_files'))
return {
'success': True,
diff --git a/couchpotato/core/media/movie/_base/static/movie.actions.js b/couchpotato/core/media/movie/_base/static/movie.actions.js
index a00829e..e84bf9c 100644
--- a/couchpotato/core/media/movie/_base/static/movie.actions.js
+++ b/couchpotato/core/media/movie/_base/static/movie.actions.js
@@ -841,9 +841,10 @@ MA.Delete = new Class({
var self = this;
return new Element('a.delete', {
'text': 'Delete',
- 'title': 'Remove the movie from this CP list',
+ 'title': 'Remove the movie from this CP list, and optionally delete files from disk',
'events': {
- 'click': self.showConfirm.bind(self)
+ 'click': self.movie.list.options.identifier === 'manage' ?
+ self.showConfirmWithFiles.bind(self) : self.showConfirm.bind(self)
}
});
},
@@ -859,7 +860,7 @@ MA.Delete = new Class({
'click': function(e){
e.target.set('text', 'Deleting...');
- self.del();
+ self.del(false);
}
}
}, {
@@ -869,7 +870,57 @@ MA.Delete = new Class({
},
- del: function(){
+ showConfirmWithFiles: function(e){
+ var self = this;
+ (e).stopPropagation();
+
+ self.question = new Question('Are you sure you want to delete ' + self.getTitle() + '?', '', [{
+ 'text': 'Yes, delete',
+ 'class': 'delete',
+ 'events': {
+ 'click': function(e){
+ e.target.set('text', 'Deleting...');
+
+ self.del(false);
+ }
+ }
+ }, {
+ 'text': 'Yes, and also delete files from disk',
+ 'class': 'delete',
+ 'events': {
+ 'click': self.showConfirmWithFilesReally.bind(self)
+ }
+ }, {
+ 'text': 'Cancel',
+ 'cancel': true
+ }]);
+
+ },
+
+ showConfirmWithFilesReally: function(e){
+ var self = this;
+ (e).stopPropagation();
+
+ if(self.question)
+ self.question.close();
+
+ self.question = new Question('Are you sure you want to delete all media files for ' + self.getTitle() + '?', '', [{
+ 'text': 'Yes, really delete all files for '+self.getTitle(),
+ 'class': 'delete',
+ 'events': {
+ 'click': function(e){
+ e.target.set('text', 'Deleting from cp and disk...');
+
+ self.del(true);
+ }
+ }
+ }, {
+ 'text': 'Cancel',
+ 'cancel': true
+ }]);
+ },
+
+ del: function(withFiles){
var self = this;
var movie = $(self.movie);
@@ -877,7 +928,8 @@ MA.Delete = new Class({
Api.request('media.delete', {
'data': {
'id': self.movie.get('_id'),
- 'delete_from': self.movie.list.options.identifier
+ 'delete_from': self.movie.list.options.identifier,
+ 'with_files': !!withFiles
},
'onComplete': function(){
if(self.question)
diff --git a/couchpotato/core/plugins/profile/static/profile.js b/couchpotato/core/plugins/profile/static/profile.js
index 457aaa9..e821c0a 100644
--- a/couchpotato/core/plugins/profile/static/profile.js
+++ b/couchpotato/core/plugins/profile/static/profile.js
@@ -140,7 +140,7 @@ var Profile = new Class({
};
Array.each(self.type_container.getElements('.type'), function(type){
- if(!type.hasClass('deleted') && type.getElement('select').get('value') != -1 && type.getElement('select').get('value') != "")
+ if(!type.hasClass('deleted') && type.getElement('select').get('value') != -1 && type.getElement('select').get('value') !== "")
data.types.include({
'quality': type.getElement('select').get('value'),
'finish': +type.getElement('input.finish[type=checkbox]').checked,
@@ -258,7 +258,7 @@ Profile.Type = new Class({
self.create();
self.addEvent('change', function(){
- var has_quality = !(self.qualities.get('value') == '-1' || self.qualities.get('value') == '');
+ var has_quality = !(self.qualities.get('value') == '-1' || self.qualities.get('value') === '');
self.el[!has_quality ? 'addClass' : 'removeClass']('is_empty');
self.el[has_quality && Quality.getQuality(self.qualities.get('value')).allow_3d ? 'addClass': 'removeClass']('allow_3d');
self.deleted = !has_quality;
diff --git a/couchpotato/static/style/main.scss b/couchpotato/static/style/main.scss
index 82e93b5..1cc0e85 100644
--- a/couchpotato/static/style/main.scss
+++ b/couchpotato/static/style/main.scss
@@ -745,7 +745,7 @@ input[type=text], textarea {
.inner {
width: 100%;
- max-width: 500px;
+ max-width: 600px;
}
h3 {