Merge branch 'develop'
Conflicts: couchpotato/core/database.py
This commit is contained in:
@@ -29,17 +29,21 @@ OS X:
|
||||
* Then do `python CouchPotatoServer/CouchPotato.py`
|
||||
* Your browser should open up, but if it doesn't go to `http://localhost:5050/`
|
||||
|
||||
Linux (Ubuntu / Debian):
|
||||
Linux:
|
||||
|
||||
* Install [GIT](http://git-scm.com/) with `apt-get install git-core`
|
||||
* (Ubuntu / Debian) Install [GIT](http://git-scm.com/) with `apt-get install git-core`
|
||||
* (Fedora / CentOS) Install [GIT](http://git-scm.com/) with `yum install git`
|
||||
* 'cd' to the folder of your choosing.
|
||||
* Run `git clone https://github.com/RuudBurger/CouchPotatoServer.git`
|
||||
* Then do `python CouchPotatoServer/CouchPotato.py` to start
|
||||
* To run on boot copy the init script `sudo cp CouchPotatoServer/init/ubuntu /etc/init.d/couchpotato`
|
||||
* Copy the default paths file `sudo cp CouchPotatoServer/init/ubuntu.default /etc/default/couchpotato`
|
||||
* Change the paths inside the default file `sudo nano /etc/default/couchpotato`
|
||||
* Make it executable `sudo chmod +x /etc/init.d/couchpotato`
|
||||
* Add it to defaults `sudo update-rc.d couchpotato defaults`
|
||||
* (Ubuntu / Debian) To run on boot copy the init script `sudo cp CouchPotatoServer/init/ubuntu /etc/init.d/couchpotato`
|
||||
* (Ubuntu / Debian) Copy the default paths file `sudo cp CouchPotatoServer/init/ubuntu.default /etc/default/couchpotato`
|
||||
* (Ubuntu / Debian) Change the paths inside the default file `sudo nano /etc/default/couchpotato`
|
||||
* (Ubuntu / Debian) Make it executable `sudo chmod +x /etc/init.d/couchpotato`
|
||||
* (Ubuntu / Debian) Add it to defaults `sudo update-rc.d couchpotato defaults`
|
||||
* (systemd) To run on boot copy the systemd config `sudo cp CouchPotatoServer/init/couchpotato.fedora.service /etc/systemd/system/couchpotato.service`
|
||||
* (systemd) Update the systemd config file with your user and path to CouchPotato.py
|
||||
* (systemd) Enable it at boot with `sudo systemctl enable couchpotato`
|
||||
* Open your browser and go to `http://localhost:5050/`
|
||||
|
||||
|
||||
|
||||
+5
-1
@@ -13,6 +13,8 @@ Lastly, for anything related to CouchPotato, feel free to stop by the [forum](ht
|
||||
## Issues
|
||||
Issues are intended for reporting bugs and weird behaviour or suggesting improvements to CouchPotatoServer.
|
||||
Before you submit an issue, please go through the following checklist:
|
||||
* **FILL IN ALL THE FIELDS ASKED FOR**
|
||||
* **POST MORE THAN A SINGLE LINE LOG**, if you do, you'd better have a easy reproducable bug
|
||||
* Search through existing issues (*including closed issues!*) first: you might be able to get your answer there.
|
||||
* Double check your issue manually, because it could be an external issue.
|
||||
* Post logs with your issue: Without seeing what is going on, the developers can't reproduce the error.
|
||||
@@ -25,12 +27,14 @@ Before you submit an issue, please go through the following checklist:
|
||||
* What hardware / OS are you using and what are its limitations? For example: NAS can be slow and maybe have a different version of python installed than when you use CP on OS X or Windows.
|
||||
* Your issue might be marked with the "can't reproduce" tag. Don't ask why your issue was closed if it says so in the tag.
|
||||
* If you're running on a NAS (QNAP, Austor, Synology etc.) with pre-made packages, make sure these are set up to use our source repository (RuudBurger/CouchPotatoServer) and nothing else!
|
||||
* Do not "bump" issues with "Any updates on this" or whatever. Yes I've seen it, you don't have to remind me of it. There will be an update when the code is done or I need information. If you feel the need to do so, you'd better have more info on the issue.
|
||||
|
||||
The more relevant information you provide, the more likely that your issue will be resolved.
|
||||
If you don't follow any of the checks above, I'll close the issue. If you are wondering why (and ask) I'll block you from posting new issues and the repo.
|
||||
|
||||
## Pull Requests
|
||||
Pull requests are intended for contributing code or documentation to the project. Before you submit a pull request, consider the following:
|
||||
* Make sure your pull request is made for the *develop* branch (or relevant feature branch).
|
||||
* Have you tested your PR? If not, why?
|
||||
* Does your PR have any limitations we should know of?
|
||||
* Does your PR have any limitations I should know of?
|
||||
* Is your PR up-to-date with the branch you're trying to push into?
|
||||
|
||||
@@ -143,7 +143,7 @@ class Updater(Plugin):
|
||||
}
|
||||
|
||||
def doShutdown(self, *args, **kwargs):
|
||||
if not Env.get('dev'):
|
||||
if not Env.get('dev') and not Env.get('desktop'):
|
||||
removePyc(Env.get('app_dir'), show_logs = False)
|
||||
|
||||
return super(Updater, self).doShutdown(*args, **kwargs)
|
||||
|
||||
@@ -238,6 +238,7 @@ class Database(object):
|
||||
old_db = os.path.join(Env.get('data_dir'), 'couchpotato.db.old')
|
||||
|
||||
if not os.path.isdir(failed_location) and os.path.isfile(old_db):
|
||||
log.error('Corrupt database, trying migrate again')
|
||||
db.close()
|
||||
|
||||
# Rename database folder
|
||||
@@ -331,6 +332,8 @@ class Database(object):
|
||||
log.info('Getting data took %s', time.time() - migrate_start)
|
||||
|
||||
db = self.getDB()
|
||||
if not db.opened:
|
||||
return
|
||||
|
||||
# Use properties
|
||||
properties = migrate_data['properties']
|
||||
|
||||
@@ -23,16 +23,14 @@ class Transmission(DownloaderBase):
|
||||
log = CPLog(__name__)
|
||||
trpc = None
|
||||
|
||||
def connect(self, reconnect = False):
|
||||
def connect(self):
|
||||
# Load host from config and split out port.
|
||||
host = cleanHost(self.conf('host'), protocol = False).split(':')
|
||||
if not isInt(host[1]):
|
||||
log.error('Config properties are not filled in correctly, port is missing.')
|
||||
return False
|
||||
|
||||
if not self.trpc or reconnect:
|
||||
self.trpc = TransmissionRPC(host[0], port = host[1], rpc_url = self.conf('rpc_url').strip('/ '), username = self.conf('username'), password = self.conf('password'))
|
||||
|
||||
self.trpc = TransmissionRPC(host[0], port = host[1], rpc_url = self.conf('rpc_url').strip('/ '), username = self.conf('username'), password = self.conf('password'))
|
||||
return self.trpc
|
||||
|
||||
def download(self, data = None, media = None, filedata = None):
|
||||
@@ -88,7 +86,7 @@ class Transmission(DownloaderBase):
|
||||
return self.downloadReturnId(remote_torrent['torrent-added']['hashString'])
|
||||
|
||||
def test(self):
|
||||
if self.connect(True) and self.trpc.get_session():
|
||||
if self.connect() and self.trpc.get_session():
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -4,7 +4,7 @@ import time
|
||||
import traceback
|
||||
from string import ascii_lowercase
|
||||
|
||||
from CodernityDB.database import RecordNotFound
|
||||
from CodernityDB.database import RecordNotFound, RecordDeleted
|
||||
from couchpotato import tryInt, get_db
|
||||
from couchpotato.api import addApiView
|
||||
from couchpotato.core.event import fireEvent, fireEventAsync, addEvent
|
||||
@@ -146,7 +146,7 @@ class MediaPlugin(MediaBase):
|
||||
|
||||
return media
|
||||
|
||||
except RecordNotFound:
|
||||
except (RecordNotFound, RecordDeleted):
|
||||
log.error('Media with id "%s" not found', media_id)
|
||||
except:
|
||||
raise
|
||||
@@ -415,7 +415,7 @@ class MediaPlugin(MediaBase):
|
||||
db.delete(release)
|
||||
total_deleted += 1
|
||||
|
||||
if (total_releases == total_deleted and media['status'] != 'active') or (total_releases == 0 and not new_media_status) or (not new_media_status and delete_from == 'late'):
|
||||
if (total_releases == total_deleted) or (total_releases == 0 and not new_media_status) or (not new_media_status and delete_from == 'late'):
|
||||
db.delete(media)
|
||||
deleted = True
|
||||
elif new_media_status:
|
||||
@@ -488,18 +488,21 @@ class MediaPlugin(MediaBase):
|
||||
db.update(m)
|
||||
|
||||
# Tag media as recent
|
||||
self.tag(media_id, 'recent')
|
||||
self.tag(media_id, 'recent', update_edited = True)
|
||||
|
||||
return m['status']
|
||||
except:
|
||||
log.error('Failed restatus: %s', traceback.format_exc())
|
||||
|
||||
def tag(self, media_id, tag):
|
||||
def tag(self, media_id, tag, update_edited = False):
|
||||
|
||||
try:
|
||||
db = get_db()
|
||||
m = db.get('id', media_id)
|
||||
|
||||
if update_edited:
|
||||
m['last_edit'] = int(time.time())
|
||||
|
||||
tags = m.get('tags') or []
|
||||
if tag not in tags:
|
||||
tags.append(tag)
|
||||
|
||||
@@ -45,7 +45,7 @@ class Base(NZBProvider, RSS):
|
||||
def _searchOnHost(self, host, media, quality, results):
|
||||
|
||||
query = self.buildUrl(media, host)
|
||||
url = '%s&%s' % (self.getUrl(host['host']), query)
|
||||
url = '%s%s' % (self.getUrl(host['host']), query)
|
||||
nzbs = self.getRSSData(url, cache_timeout = 1800, headers = {'User-Agent': Env.getIdentifier()})
|
||||
|
||||
for nzb in nzbs:
|
||||
@@ -83,7 +83,7 @@ class Base(NZBProvider, RSS):
|
||||
try:
|
||||
# Get details for extended description to retrieve passwords
|
||||
query = self.buildDetailsUrl(nzb_id, host['api_key'])
|
||||
url = '%s&%s' % (self.getUrl(host['host']), query)
|
||||
url = '%s%s' % (self.getUrl(host['host']), query)
|
||||
nzb_details = self.getRSSData(url, cache_timeout = 1800, headers = {'User-Agent': Env.getIdentifier()})[0]
|
||||
|
||||
description = self.getTextElement(nzb_details, 'description')
|
||||
@@ -220,7 +220,7 @@ config = [{
|
||||
'description': 'Enable <a href="http://newznab.com/" target="_blank">NewzNab</a> such as <a href="https://nzb.su" target="_blank">NZB.su</a>, \
|
||||
<a href="https://nzbs.org" target="_blank">NZBs.org</a>, <a href="http://dognzb.cr/" target="_blank">DOGnzb.cr</a>, \
|
||||
<a href="https://github.com/spotweb/spotweb" target="_blank">Spotweb</a>, <a href="https://nzbgeek.info/" target="_blank">NZBGeek</a>, \
|
||||
<a href="https://smackdownonyou.com" target="_blank">SmackDown</a>, <a href="https://www.nzbfinder.ws" target="_blank">NZBFinder</a>',
|
||||
<a href="https://www.nzbfinder.ws" target="_blank">NZBFinder</a>',
|
||||
'wizard': True,
|
||||
'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQAgMAAABinRfyAAAACVBMVEVjhwD///86aRovd/sBAAAAMklEQVQI12NgAIPQUCCRmQkjssDEShiRuRIqwZqZGcDAGBrqANUhGgIkWAOABKMDxCAA24UK50b26SAAAAAASUVORK5CYII=',
|
||||
'options': [
|
||||
@@ -231,30 +231,30 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'use',
|
||||
'default': '0,0,0,0,0,0'
|
||||
'default': '0,0,0,0,0'
|
||||
},
|
||||
{
|
||||
'name': 'host',
|
||||
'default': 'api.nzb.su,api.dognzb.cr,nzbs.org,https://index.nzbgeek.info, https://smackdownonyou.com, https://www.nzbfinder.ws',
|
||||
'default': 'api.nzb.su,api.dognzb.cr,nzbs.org,https://api.nzbgeek.info,https://www.nzbfinder.ws',
|
||||
'description': 'The hostname of your newznab provider',
|
||||
},
|
||||
{
|
||||
'name': 'extra_score',
|
||||
'advanced': True,
|
||||
'label': 'Extra Score',
|
||||
'default': '0,0,0,0,0,0',
|
||||
'default': '0,0,0,0,0',
|
||||
'description': 'Starting score for each release found via this provider.',
|
||||
},
|
||||
{
|
||||
'name': 'custom_tag',
|
||||
'advanced': True,
|
||||
'label': 'Custom tag',
|
||||
'default': ',,,,,',
|
||||
'default': ',,,,',
|
||||
'description': 'Add custom tags, for example add rls=1 to get only scene releases from nzbs.org',
|
||||
},
|
||||
{
|
||||
'name': 'api_key',
|
||||
'default': ',,,,,',
|
||||
'default': ',,,,',
|
||||
'label': 'Api Key',
|
||||
'description': 'Can be found on your profile page',
|
||||
'type': 'combined',
|
||||
|
||||
@@ -61,7 +61,7 @@ class Base(TorrentProvider):
|
||||
'name': re.sub('[^A-Za-z0-9\-_ \(\).]+', '', '%s (%s) %s' % (name, year, torrent_desc)),
|
||||
'url': self.urls['download'] % (torrent_id, authkey, self.conf('passkey')),
|
||||
'detail_url': self.urls['detail'] % torrent_id,
|
||||
'size': self.parseSize(entry.find('size').get_text()),
|
||||
'size': tryInt(entry.find('size').get_text()) / 1048576,
|
||||
'seeders': tryInt(entry.find('seeders').get_text()),
|
||||
'leechers': tryInt(entry.find('leechers').get_text()),
|
||||
'score': torrentscore
|
||||
|
||||
@@ -13,12 +13,12 @@ log = CPLog(__name__)
|
||||
class Base(TorrentProvider):
|
||||
|
||||
urls = {
|
||||
'test': 'https://torrentshack.net/',
|
||||
'login': 'https://torrentshack.net/login.php',
|
||||
'login_check': 'https://torrentshack.net/inbox.php',
|
||||
'detail': 'https://torrentshack.net/torrent/%s',
|
||||
'search': 'https://torrentshack.net/torrents.php?action=advanced&searchstr=%s&scene=%s&filter_cat[%d]=1',
|
||||
'download': 'https://torrentshack.net/%s',
|
||||
'test': 'http://torrentshack.eu/',
|
||||
'login': 'http://torrentshack.eu/login.php',
|
||||
'login_check': 'http://torrentshack.eu/inbox.php',
|
||||
'detail': 'http://torrentshack.eu/torrent/%s',
|
||||
'search': 'http://torrentshack.eu/torrents.php?action=advanced&searchstr=%s&scene=%s&filter_cat[%d]=1',
|
||||
'download': 'http://torrentshack.eu/%s',
|
||||
}
|
||||
|
||||
http_time_between_calls = 1 # Seconds
|
||||
@@ -80,7 +80,7 @@ config = [{
|
||||
'tab': 'searcher',
|
||||
'list': 'torrent_providers',
|
||||
'name': 'TorrentShack',
|
||||
'description': '<a href="https://www.torrentshack.net/">TorrentShack</a>',
|
||||
'description': '<a href="http://torrentshack.eu/">TorrentShack</a>',
|
||||
'wizard': True,
|
||||
'icon': 'iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAIAAACQkWg2AAABmElEQVQoFQXBzY2cVRiE0afqvd84CQiAnxWWtyxsS6ThINBYg2Dc7mZBMEjE4mzs6e9WcY5+ePNuVFJJodQAoLo+SaWCy9rcV8cmjah3CI6iYu7oRU30kE5xxELRfamklY3k1NL19sSm7vPzP/ZdNZzKVDaY2sPZJBh9fv5ITrmG2+Vp4e1sPchVqTCQZJnVXi+/L4uuAJGly1+Pw8CprLbi8Om7tbT19/XRqJUk11JP9uHj9ulxhXbvJbI9qJvr5YkGXFG2IBT8tXczt+sfzDZCp3765f3t9tHEHGEDACma77+8o4oATKk+/PfW9YmHruRFjWoVSFsVsGu1YSKq6Oc37+n98unPZSRlY7vsKDqN+92X3yR9+PdXee3iJNKMStqdcZqoTJbUSi5JOkpfRlhSI0mSpEmCFKoU7FqSNOLAk54uGwCStMUCgLrVic62g7oDoFmmdI+P3S0pDe1xvDqb6XrZqbtzShWNoh9fv/XQHaDdM9OqrZi2M7M3UrB2vlkPS1IbdEBk7UiSoD6VlZ6aKWer4aH4f/AvKoHUTjuyAAAAAElFTkSuQmCC',
|
||||
'options': [
|
||||
|
||||
@@ -11,7 +11,7 @@ autoload = 'TorrentLeech'
|
||||
class TorrentLeech(MovieProvider, Base):
|
||||
|
||||
cat_ids = [
|
||||
([13], ['720p', '1080p']),
|
||||
([13], ['720p', '1080p', 'bd50']),
|
||||
([8], ['cam']),
|
||||
([9], ['ts', 'tc']),
|
||||
([10], ['r5', 'scr']),
|
||||
|
||||
@@ -240,7 +240,7 @@ class MovieSearcher(SearcherBase, MovieTypeBase):
|
||||
break
|
||||
|
||||
if total_result_count > 0:
|
||||
fireEvent('media.tag', movie['_id'], 'recent', single = True)
|
||||
fireEvent('media.tag', movie['_id'], 'recent', update_edited = True, single = True)
|
||||
|
||||
if len(too_early_to_search) > 0:
|
||||
log.info2('Too early to search for %s, %s', (too_early_to_search, default_title))
|
||||
|
||||
@@ -84,7 +84,8 @@ config = [{
|
||||
},
|
||||
{
|
||||
'name': 'api_key',
|
||||
'label': 'User API Key'
|
||||
'label': 'Access Token',
|
||||
'description': 'Can be found on <a href="https://www.pushbullet.com/account" target="_blank">Account Settings</a>',
|
||||
},
|
||||
{
|
||||
'name': 'devices',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from httplib import HTTPSConnection
|
||||
|
||||
from couchpotato.core.helpers.encoding import toUnicode, tryUrlencode
|
||||
from couchpotato.core.helpers.variable import getTitle
|
||||
from couchpotato.core.helpers.variable import getTitle, getIdentifier
|
||||
from couchpotato.core.logger import CPLog
|
||||
from couchpotato.core.notifications.base import Notification
|
||||
|
||||
@@ -27,9 +27,9 @@ class Pushover(Notification):
|
||||
'sound': self.conf('sound'),
|
||||
}
|
||||
|
||||
if data and data.get('identifier'):
|
||||
if data and getIdentifier(data):
|
||||
api_data.update({
|
||||
'url': toUnicode('http://www.imdb.com/title/%s/' % data['identifier']),
|
||||
'url': toUnicode('http://www.imdb.com/title/%s/' % getIdentifier(data)),
|
||||
'url_title': toUnicode('%s on IMDb' % getTitle(data)),
|
||||
})
|
||||
|
||||
|
||||
@@ -208,7 +208,7 @@ class XBMC(Notification):
|
||||
log.debug('Returned from request %s: %s', (host, response))
|
||||
|
||||
return response
|
||||
except (MaxRetryError, requests.exceptions.Timeout):
|
||||
except (MaxRetryError, requests.exceptions.Timeout, ConnectionError):
|
||||
log.info2('Couldn\'t send request to XBMC, assuming it\'s turned off')
|
||||
return []
|
||||
except:
|
||||
|
||||
@@ -157,7 +157,7 @@ class Plugin(object):
|
||||
os.rmdir(full_path)
|
||||
except:
|
||||
if show_error:
|
||||
log.error('Couldn\'t remove empty directory %s: %s', (full_path, traceback.format_exc()))
|
||||
log.info2('Couldn\'t remove directory %s: %s', (full_path, traceback.format_exc()))
|
||||
|
||||
try:
|
||||
os.rmdir(folder)
|
||||
|
||||
@@ -241,7 +241,7 @@ Running on: ...\n\
|
||||
'href': 'https://github.com/RuudBurger/CouchPotatoServer/blob/develop/contributing.md'
|
||||
}),
|
||||
new Element('span', {
|
||||
'text': ' before posting, then copy the text below'
|
||||
'text': ' before posting (kittens die if you don\'t), then copy the text below.'
|
||||
})
|
||||
),
|
||||
textarea = new Element('textarea', {
|
||||
|
||||
@@ -29,9 +29,9 @@ class QualityPlugin(Plugin):
|
||||
{'identifier': 'dvdr', 'size': (3000, 10000), 'label': 'DVD-R', 'alternative': ['br2dvd', ('dvd', 'r')], 'allow': [], 'ext':['iso', 'img', 'vob'], 'tags': ['pal', 'ntsc', 'video_ts', 'audio_ts', ('dvd', 'r'), 'dvd9']},
|
||||
{'identifier': 'dvdrip', 'size': (600, 2400), 'label': 'DVD-Rip', 'width': 720, 'alternative': [('dvd', 'rip')], 'allow': [], 'ext':['avi'], 'tags': [('dvd', 'rip'), ('dvd', 'xvid'), ('dvd', 'divx')]},
|
||||
{'identifier': 'scr', 'size': (600, 1600), 'label': 'Screener', 'alternative': ['screener', 'dvdscr', 'ppvrip', 'dvdscreener', 'hdscr'], 'allow': ['dvdr', 'dvdrip', '720p', '1080p'], 'ext':[], 'tags': ['webrip', ('web', 'rip')]},
|
||||
{'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr'], 'ext':[]},
|
||||
{'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': [], 'ext':[]},
|
||||
{'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': [], 'ext':[]},
|
||||
{'identifier': 'r5', 'size': (600, 1000), 'label': 'R5', 'alternative': ['r6'], 'allow': ['dvdr', '720p'], 'ext':[]},
|
||||
{'identifier': 'tc', 'size': (600, 1000), 'label': 'TeleCine', 'alternative': ['telecine'], 'allow': ['720p'], 'ext':[]},
|
||||
{'identifier': 'ts', 'size': (600, 1000), 'label': 'TeleSync', 'alternative': ['telesync', 'hdts'], 'allow': ['720p'], 'ext':[]},
|
||||
{'identifier': 'cam', 'size': (600, 1000), 'label': 'Cam', 'alternative': ['camrip', 'hdcam'], 'allow': ['720p'], 'ext':[]}
|
||||
]
|
||||
pre_releases = ['cam', 'ts', 'tc', 'r5', 'scr']
|
||||
@@ -454,6 +454,9 @@ class QualityPlugin(Plugin):
|
||||
'Moviename 2014 720p HDCAM XviD DualAudio': {'size': 4000, 'quality': 'cam'},
|
||||
'Moviename (2014) - 720p CAM x264': {'size': 2250, 'quality': 'cam'},
|
||||
'Movie Name (2014).mp4': {'size': 750, 'quality': 'brrip'},
|
||||
'Moviename.2014.720p.R6.WEB-DL.x264.AC3-xyz': {'size': 750, 'quality': 'r5'},
|
||||
'Movie name 2014 New Source 720p HDCAM x264 AC3 xyz': {'size': 750, 'quality': 'cam'},
|
||||
'Movie.Name.2014.720p.HD.TS.AC3.x264': {'size': 750, 'quality': 'ts'}
|
||||
}
|
||||
|
||||
correct = 0
|
||||
|
||||
@@ -234,8 +234,9 @@ class Release(Plugin):
|
||||
db = get_db()
|
||||
|
||||
try:
|
||||
rel = db.get('id', id, with_doc = True)
|
||||
self.updateStatus(id, 'available' if rel['status'] in ['ignored', 'failed'] else 'ignored')
|
||||
if id:
|
||||
rel = db.get('id', id, with_doc = True)
|
||||
self.updateStatus(id, 'available' if rel['status'] in ['ignored', 'failed'] else 'ignored')
|
||||
|
||||
return {
|
||||
'success': True
|
||||
@@ -327,7 +328,7 @@ class Release(Plugin):
|
||||
log_movie = '%s (%s) in %s' % (getTitle(media), media['info']['year'], rls['quality'])
|
||||
snatch_message = 'Snatched "%s": %s' % (data.get('name'), log_movie)
|
||||
log.info(snatch_message)
|
||||
fireEvent('%s.snatched' % data['type'], message = snatch_message, data = rls)
|
||||
fireEvent('%s.snatched' % data['type'], message = snatch_message, data = media)
|
||||
|
||||
# Mark release as snatched
|
||||
if renamer_enabled:
|
||||
@@ -364,7 +365,7 @@ class Release(Plugin):
|
||||
let_through = False
|
||||
filtered_results = []
|
||||
|
||||
# If a single release comes through the "wait for", let through all
|
||||
# Filter out ignored and other releases we don't want
|
||||
for rel in results:
|
||||
|
||||
if rel['status'] in ['ignored', 'failed']:
|
||||
@@ -379,6 +380,11 @@ class Release(Plugin):
|
||||
log.info('Ignored, size "%sMB" to low: %s', (rel['size'], rel['name']))
|
||||
continue
|
||||
|
||||
if 'seeders' in rel and rel.get('seeders') <= 0:
|
||||
log.info('Ignored, no seeders: %s', (rel['name']))
|
||||
continue
|
||||
|
||||
# If a single release comes through the "wait for", let through all
|
||||
rel['wait_for'] = False
|
||||
if quality_custom.get('index') != 0 and quality_custom.get('wait_for', 0) > 0 and rel.get('age') <= quality_custom.get('wait_for', 0):
|
||||
rel['wait_for'] = True
|
||||
|
||||
@@ -123,11 +123,6 @@ class Renamer(Plugin):
|
||||
no_process = [to_folder]
|
||||
cat_list = fireEvent('category.all', single = True) or []
|
||||
no_process.extend([item['destination'] for item in cat_list])
|
||||
try:
|
||||
if Env.setting('library', section = 'manage').strip():
|
||||
no_process.extend([sp(manage_folder) for manage_folder in splitString(Env.setting('library', section = 'manage'), '::')])
|
||||
except:
|
||||
pass
|
||||
|
||||
# Check to see if the no_process folders are inside the "from" folder.
|
||||
if not os.path.isdir(base_folder) or not os.path.isdir(to_folder):
|
||||
@@ -202,14 +197,18 @@ class Renamer(Plugin):
|
||||
db = get_db()
|
||||
|
||||
# Extend the download info with info stored in the downloaded release
|
||||
keep_original = self.moveTypeIsLinked()
|
||||
is_torrent = False
|
||||
if release_download:
|
||||
release_download = self.extendReleaseDownload(release_download)
|
||||
is_torrent = self.downloadIsTorrent(release_download)
|
||||
keep_original = True if is_torrent and self.conf('file_action') not in ['move'] else keep_original
|
||||
|
||||
# Unpack any archives
|
||||
extr_files = None
|
||||
if self.conf('unrar'):
|
||||
folder, media_folder, files, extr_files = self.extractFiles(folder = folder, media_folder = media_folder, files = files,
|
||||
cleanup = self.conf('cleanup') and not self.downloadIsTorrent(release_download))
|
||||
cleanup = self.conf('cleanup') and not keep_original)
|
||||
|
||||
groups = fireEvent('scanner.scan', folder = folder if folder else base_folder,
|
||||
files = files, release_download = release_download, return_ignored = False, single = True) or []
|
||||
@@ -326,7 +325,7 @@ class Renamer(Plugin):
|
||||
if file_type is 'nfo' and not self.conf('rename_nfo'):
|
||||
log.debug('Skipping, renaming of %s disabled', file_type)
|
||||
for current_file in group['files'][file_type]:
|
||||
if self.conf('cleanup') and (not self.downloadIsTorrent(release_download) or self.fileIsAdded(current_file, group)):
|
||||
if self.conf('cleanup') and (not keep_original or self.fileIsAdded(current_file, group)):
|
||||
remove_files.append(current_file)
|
||||
continue
|
||||
|
||||
@@ -523,7 +522,7 @@ class Renamer(Plugin):
|
||||
|
||||
# Mark media for dashboard
|
||||
if mark_as_recent:
|
||||
fireEvent('media.tag', group['media'].get('_id'), 'recent', single = True)
|
||||
fireEvent('media.tag', group['media'].get('_id'), 'recent', update_edited = True, single = True)
|
||||
|
||||
# Remove leftover files
|
||||
if not remove_leftovers: # Don't remove anything
|
||||
@@ -532,7 +531,7 @@ class Renamer(Plugin):
|
||||
log.debug('Removing leftover files')
|
||||
for current_file in group['files']['leftover']:
|
||||
if self.conf('cleanup') and not self.conf('move_leftover') and \
|
||||
(not self.downloadIsTorrent(release_download) or self.fileIsAdded(current_file, group)):
|
||||
(not keep_original or self.fileIsAdded(current_file, group)):
|
||||
remove_files.append(current_file)
|
||||
|
||||
# Remove files
|
||||
@@ -579,7 +578,7 @@ class Renamer(Plugin):
|
||||
self.makeDir(os.path.dirname(dst))
|
||||
|
||||
try:
|
||||
self.moveFile(src, dst, forcemove = not self.downloadIsTorrent(release_download) or self.fileIsAdded(src, group))
|
||||
self.moveFile(src, dst, use_default = not is_torrent or self.fileIsAdded(src, group))
|
||||
group['renamed_files'].append(dst)
|
||||
except:
|
||||
log.error('Failed renaming the file "%s" : %s', (os.path.basename(src), traceback.format_exc()))
|
||||
@@ -595,7 +594,7 @@ class Renamer(Plugin):
|
||||
self.untagRelease(group = group, tag = 'failed_rename')
|
||||
|
||||
# Tag folder if it is in the 'from' folder and it will not be removed because it is a torrent
|
||||
if self.movieInFromFolder(media_folder) and self.downloadIsTorrent(release_download):
|
||||
if self.movieInFromFolder(media_folder) and keep_original:
|
||||
self.tagRelease(group = group, tag = 'renamed_already')
|
||||
|
||||
# Remove matching releases
|
||||
@@ -606,7 +605,7 @@ class Renamer(Plugin):
|
||||
except:
|
||||
log.error('Failed removing %s: %s', (release, traceback.format_exc()))
|
||||
|
||||
if group['dirname'] and group['parentdir'] and not self.downloadIsTorrent(release_download):
|
||||
if group['dirname'] and group['parentdir'] and not keep_original:
|
||||
if media_folder:
|
||||
# Delete the movie folder
|
||||
group_folder = media_folder
|
||||
@@ -768,10 +767,15 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
|
||||
return False
|
||||
|
||||
def moveFile(self, old, dest, forcemove = False):
|
||||
def moveFile(self, old, dest, use_default = False):
|
||||
dest = sp(dest)
|
||||
try:
|
||||
if forcemove or self.conf('file_action') not in ['copy', 'link']:
|
||||
|
||||
move_type = self.conf('file_action')
|
||||
if use_default:
|
||||
move_type = self.conf('default_file_action')
|
||||
|
||||
if move_type not in ['copy', 'link']:
|
||||
try:
|
||||
shutil.move(old, dest)
|
||||
except:
|
||||
@@ -780,16 +784,16 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
os.unlink(old)
|
||||
else:
|
||||
raise
|
||||
elif self.conf('file_action') == 'copy':
|
||||
elif move_type == 'copy':
|
||||
shutil.copy(old, dest)
|
||||
elif self.conf('file_action') == 'link':
|
||||
else:
|
||||
# First try to hardlink
|
||||
try:
|
||||
log.debug('Hardlinking file "%s" to "%s"...', (old, dest))
|
||||
link(old, dest)
|
||||
except:
|
||||
# Try to simlink next
|
||||
log.debug('Couldn\'t hardlink file "%s" to "%s". Simlinking instead. Error: %s.', (old, dest, traceback.format_exc()))
|
||||
log.debug('Couldn\'t hardlink file "%s" to "%s". Symlinking instead. Error: %s.', (old, dest, traceback.format_exc()))
|
||||
shutil.copy(old, dest)
|
||||
try:
|
||||
symlink(dest, old + '.link')
|
||||
@@ -803,7 +807,7 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
if os.name == 'nt' and self.conf('ntfs_permission'):
|
||||
os.popen('icacls "' + dest + '"* /reset /T')
|
||||
except:
|
||||
log.error('Failed setting permissions for file: %s, %s', (dest, traceback.format_exc(1)))
|
||||
log.debug('Failed setting permissions for file: %s, %s', (dest, traceback.format_exc(1)))
|
||||
except:
|
||||
log.error('Couldn\'t move file "%s" to "%s": %s', (old, dest, traceback.format_exc()))
|
||||
raise
|
||||
@@ -1089,6 +1093,9 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
return False
|
||||
return src in group['before_rename']
|
||||
|
||||
def moveTypeIsLinked(self):
|
||||
return self.conf('default_file_action') in ['copy', 'link']
|
||||
|
||||
def statusInfoComplete(self, release_download):
|
||||
return release_download.get('id') and release_download.get('downloader') and release_download.get('folder')
|
||||
|
||||
@@ -1140,14 +1147,20 @@ Remove it if you want it to be renamed (again, or at least let it try again)
|
||||
|
||||
log.info('Archive %s found. Extracting...', os.path.basename(archive['file']))
|
||||
try:
|
||||
rar_handle = RarFile(archive['file'])
|
||||
rar_handle = RarFile(archive['file'], custom_path = self.conf('unrar_path'))
|
||||
extr_path = os.path.join(from_folder, os.path.relpath(os.path.dirname(archive['file']), folder))
|
||||
self.makeDir(extr_path)
|
||||
for packedinfo in rar_handle.infolist():
|
||||
if not packedinfo.isdir and not os.path.isfile(sp(os.path.join(extr_path, os.path.basename(packedinfo.filename)))):
|
||||
extr_file_path = sp(os.path.join(extr_path, os.path.basename(packedinfo.filename)))
|
||||
if not packedinfo.isdir and not os.path.isfile(extr_file_path):
|
||||
log.debug('Extracting %s...', packedinfo.filename)
|
||||
rar_handle.extract(condition = [packedinfo.index], path = extr_path, withSubpath = False, overwrite = False)
|
||||
extr_files.append(sp(os.path.join(extr_path, os.path.basename(packedinfo.filename))))
|
||||
if self.conf('unrar_modify_date'):
|
||||
try:
|
||||
os.utime(extr_file_path, (os.path.getatime(archive['file']), os.path.getmtime(archive['file'])))
|
||||
except:
|
||||
log.error('Rar modify date enabled, but failed: %s', traceback.format_exc())
|
||||
extr_files.append(extr_file_path)
|
||||
del rar_handle
|
||||
except Exception as e:
|
||||
log.error('Failed to extract %s: %s %s', (archive['file'], e, traceback.format_exc()))
|
||||
@@ -1282,6 +1295,18 @@ config = [{
|
||||
'description': 'Extract rar files if found.',
|
||||
'default': False,
|
||||
},
|
||||
{
|
||||
'advanced': True,
|
||||
'name': 'unrar_path',
|
||||
'description': 'Custom path to unrar bin',
|
||||
},
|
||||
{
|
||||
'advanced': True,
|
||||
'name': 'unrar_modify_date',
|
||||
'type': 'bool',
|
||||
'description': ('Set modify date of unrar-ed files to the rar-file\'s date.', 'This will allow XBMC to recognize extracted files as recently added even if the movie was released some time ago.'),
|
||||
'default': False,
|
||||
},
|
||||
{
|
||||
'name': 'cleanup',
|
||||
'type': 'bool',
|
||||
@@ -1332,14 +1357,23 @@ config = [{
|
||||
'label': 'Folder-Separator',
|
||||
'description': ('Replace all the spaces with a character.', 'Example: ".", "-" (without quotes). Leave empty to use spaces.'),
|
||||
},
|
||||
{
|
||||
'name': 'default_file_action',
|
||||
'label': 'Default File Action',
|
||||
'default': 'move',
|
||||
'type': 'dropdown',
|
||||
'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')],
|
||||
'description': ('<strong>Link</strong>, <strong>Copy</strong> or <strong>Move</strong> after download completed.',
|
||||
'Link first tries <a href="http://en.wikipedia.org/wiki/Hard_link">hard link</a>, then <a href="http://en.wikipedia.org/wiki/Sym_link">sym link</a> and falls back to Copy.'),
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
'name': 'file_action',
|
||||
'label': 'Torrent File Action',
|
||||
'default': 'link',
|
||||
'type': 'dropdown',
|
||||
'values': [('Link', 'link'), ('Copy', 'copy'), ('Move', 'move')],
|
||||
'description': ('<strong>Link</strong>, <strong>Copy</strong> or <strong>Move</strong> after download completed.',
|
||||
'Link first tries <a href="http://en.wikipedia.org/wiki/Hard_link">hard link</a>, then <a href="http://en.wikipedia.org/wiki/Sym_link">sym link</a> and falls back to Copy. It is perfered to use link when downloading torrents as it will save you space, while still beeing able to seed.'),
|
||||
'description': 'See above. It is prefered to use link when downloading torrents as it will save you space, while still beeing able to seed.',
|
||||
'advanced': True,
|
||||
},
|
||||
{
|
||||
|
||||
@@ -553,7 +553,7 @@ class Scanner(Plugin):
|
||||
scan_result = []
|
||||
for p in paths:
|
||||
if not group['is_dvd']:
|
||||
video = Video.from_path(toUnicode(p))
|
||||
video = Video.from_path(sp(p))
|
||||
video_result = [(video, video.scan())]
|
||||
scan_result.extend(video_result)
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
|
||||
{% for url in fireEvent('clientscript.get_styles', as_html = True, location = 'front', single = True) %}
|
||||
<link rel="stylesheet" href="{{ Env.get('web_base') }}{{ url }}" type="text/css">{% end %}
|
||||
|
||||
@@ -4,22 +4,24 @@
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"/>
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="mobile-web-app-capable" content="yes">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||
|
||||
{% for url in fireEvent('clientscript.get_styles', as_html = True, location = 'front', single = True) %}
|
||||
<link rel="stylesheet" href="{{ Env.get('web_base') }}{{ url }}" type="text/css">{% end %}
|
||||
|
||||
|
||||
{% for url in fireEvent('clientscript.get_scripts', as_html = True, location = 'front', single = True) %}
|
||||
<script type="text/javascript" src="{{ Env.get('web_base') }}{{ url }}"></script>{% end %}
|
||||
|
||||
<link href="{{ Env.get('static_path') }}images/favicon.ico" rel="icon" type="image/x-icon" />
|
||||
<link rel="apple-touch-icon" href="{{ Env.get('static_path') }}images/homescreen.png" />
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
|
||||
window.addEvent('domready', function(){
|
||||
new Form.Check($('remember_me'));
|
||||
});
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<title>CouchPotato</title>
|
||||
@@ -35,4 +37,4 @@
|
||||
</div>
|
||||
</form>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
+14
-14
@@ -21,7 +21,7 @@
|
||||
# SOFTWARE.
|
||||
|
||||
"""
|
||||
pyUnRAR2 is a ctypes based wrapper around the free UnRAR.dll.
|
||||
pyUnRAR2 is a ctypes based wrapper around the free UnRAR.dll.
|
||||
|
||||
It is an modified version of Jimmy Retzlaff's pyUnRAR - more simple,
|
||||
stable and foolproof.
|
||||
@@ -45,8 +45,8 @@ if in_windows:
|
||||
from windows import RarFileImplementation
|
||||
else:
|
||||
from unix import RarFileImplementation
|
||||
|
||||
|
||||
|
||||
|
||||
import fnmatch, time, weakref
|
||||
|
||||
class RarInfo(object):
|
||||
@@ -62,7 +62,7 @@ class RarInfo(object):
|
||||
isdir - True if the file is a directory
|
||||
size - size in bytes of the uncompressed file
|
||||
comment - comment associated with the file
|
||||
|
||||
|
||||
Note - this is not currently intended to be a Python file-like object.
|
||||
"""
|
||||
|
||||
@@ -74,7 +74,7 @@ class RarInfo(object):
|
||||
self.size = data['size']
|
||||
self.datetime = data['datetime']
|
||||
self.comment = data['comment']
|
||||
|
||||
|
||||
|
||||
|
||||
def __str__(self):
|
||||
@@ -86,7 +86,7 @@ class RarInfo(object):
|
||||
|
||||
class RarFile(RarFileImplementation):
|
||||
|
||||
def __init__(self, archiveName, password=None):
|
||||
def __init__(self, archiveName, password=None, custom_path = None):
|
||||
"""Instantiate the archive.
|
||||
|
||||
archiveName is the name of the RAR file.
|
||||
@@ -99,7 +99,7 @@ class RarFile(RarFileImplementation):
|
||||
This is a test.
|
||||
"""
|
||||
self.archiveName = archiveName
|
||||
RarFileImplementation.init(self, password)
|
||||
RarFileImplementation.init(self, password, custom_path)
|
||||
|
||||
def __del__(self):
|
||||
self.destruct()
|
||||
@@ -130,31 +130,31 @@ class RarFile(RarFileImplementation):
|
||||
"""Read specific files from archive into memory.
|
||||
If "condition" is a list of numbers, then return files which have those positions in infolist.
|
||||
If "condition" is a string, then it is treated as a wildcard for names of files to extract.
|
||||
If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object
|
||||
If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object
|
||||
and returns boolean True (extract) or False (skip).
|
||||
If "condition" is omitted, all files are returned.
|
||||
|
||||
|
||||
Returns list of tuples (RarInfo info, str contents)
|
||||
"""
|
||||
checker = condition2checker(condition)
|
||||
return RarFileImplementation.read_files(self, checker)
|
||||
|
||||
|
||||
|
||||
def extract(self, condition='*', path='.', withSubpath=True, overwrite=True):
|
||||
"""Extract specific files from archive to disk.
|
||||
|
||||
|
||||
If "condition" is a list of numbers, then extract files which have those positions in infolist.
|
||||
If "condition" is a string, then it is treated as a wildcard for names of files to extract.
|
||||
If "condition" is a function, it is treated as a callback function, which accepts a RarInfo object
|
||||
and returns either boolean True (extract) or boolean False (skip).
|
||||
DEPRECATED: If "condition" callback returns string (only supported for Windows) -
|
||||
DEPRECATED: If "condition" callback returns string (only supported for Windows) -
|
||||
that string will be used as a new name to save the file under.
|
||||
If "condition" is omitted, all files are extracted.
|
||||
|
||||
|
||||
"path" is a directory to extract to
|
||||
"withSubpath" flag denotes whether files are extracted with their full path in the archive.
|
||||
"overwrite" flag denotes whether extracted files will overwrite old ones. Defaults to true.
|
||||
|
||||
|
||||
Returns list of RarInfos for extracted files."""
|
||||
checker = condition2checker(condition)
|
||||
return RarFileImplementation.extract(self, checker, path, withSubpath, overwrite)
|
||||
|
||||
+21
-9
@@ -21,25 +21,37 @@
|
||||
# SOFTWARE.
|
||||
|
||||
# Unix version uses unrar command line executable
|
||||
|
||||
import platform
|
||||
import stat
|
||||
import subprocess
|
||||
import gc
|
||||
|
||||
import os, os.path
|
||||
import time, re
|
||||
import os
|
||||
import os.path
|
||||
import time
|
||||
import re
|
||||
|
||||
from rar_exceptions import *
|
||||
|
||||
|
||||
class UnpackerNotInstalled(Exception): pass
|
||||
|
||||
rar_executable_cached = None
|
||||
rar_executable_version = None
|
||||
|
||||
def call_unrar(params):
|
||||
osx_unrar = os.path.join(os.path.dirname(__file__), 'unrar')
|
||||
if os.path.isfile(osx_unrar) and 'darwin' in platform.platform().lower():
|
||||
try:
|
||||
st = os.stat(osx_unrar)
|
||||
os.chmod(osx_unrar, st.st_mode | stat.S_IEXEC)
|
||||
except:
|
||||
pass
|
||||
|
||||
def call_unrar(params, custom_path = None):
|
||||
"Calls rar/unrar command line executable, returns stdout pipe"
|
||||
global rar_executable_cached
|
||||
if rar_executable_cached is None:
|
||||
for command in ('unrar', 'rar', os.path.join(os.path.dirname(__file__), 'unrar')):
|
||||
for command in (custom_path, 'unrar', 'rar', osx_unrar):
|
||||
if not command: continue
|
||||
try:
|
||||
subprocess.Popen([command], stdout = subprocess.PIPE)
|
||||
rar_executable_cached = command
|
||||
@@ -59,10 +71,10 @@ def call_unrar(params):
|
||||
|
||||
class RarFileImplementation(object):
|
||||
|
||||
def init(self, password = None):
|
||||
def init(self, password = None, custom_path = None):
|
||||
global rar_executable_version
|
||||
self.password = password
|
||||
|
||||
self.custom_path = custom_path
|
||||
|
||||
stdoutdata, stderrdata = self.call('v', []).communicate()
|
||||
|
||||
@@ -118,7 +130,7 @@ class RarFileImplementation(object):
|
||||
def call(self, cmd, options = [], files = []):
|
||||
options2 = options + ['p' + self.escaped_password()]
|
||||
soptions = ['-' + x for x in options2]
|
||||
return call_unrar([cmd] + soptions + ['--', self.archiveName] + files)
|
||||
return call_unrar([cmd] + soptions + ['--', self.archiveName] + files, self.custom_path)
|
||||
|
||||
def infoiter(self):
|
||||
|
||||
|
||||
+12
-12
@@ -174,7 +174,7 @@ class PassiveReader:
|
||||
def __init__(self, usercallback = None):
|
||||
self.buf = []
|
||||
self.ucb = usercallback
|
||||
|
||||
|
||||
def _callback(self, msg, UserData, P1, P2):
|
||||
if msg == UCM_PROCESSDATA:
|
||||
data = (ctypes.c_char*P2).from_address(P1).raw
|
||||
@@ -183,7 +183,7 @@ class PassiveReader:
|
||||
else:
|
||||
self.buf.append(data)
|
||||
return 1
|
||||
|
||||
|
||||
def get_result(self):
|
||||
return ''.join(self.buf)
|
||||
|
||||
@@ -197,10 +197,10 @@ class RarInfoIterator(object):
|
||||
raise IncorrectRARPassword
|
||||
self.arc.lockStatus = "locked"
|
||||
self.arc.needskip = False
|
||||
|
||||
|
||||
def __iter__(self):
|
||||
return self
|
||||
|
||||
|
||||
def next(self):
|
||||
if self.index>0:
|
||||
if self.arc.needskip:
|
||||
@@ -208,9 +208,9 @@ class RarInfoIterator(object):
|
||||
self.res = RARReadHeaderEx(self.arc._handle, ctypes.byref(self.headerData))
|
||||
|
||||
if self.res:
|
||||
raise StopIteration
|
||||
raise StopIteration
|
||||
self.arc.needskip = True
|
||||
|
||||
|
||||
data = {}
|
||||
data['index'] = self.index
|
||||
data['filename'] = self.headerData.FileName
|
||||
@@ -224,7 +224,7 @@ class RarInfoIterator(object):
|
||||
self.index += 1
|
||||
return data
|
||||
|
||||
|
||||
|
||||
def __del__(self):
|
||||
self.arc.lockStatus = "finished"
|
||||
|
||||
@@ -237,7 +237,7 @@ def generate_password_provider(password):
|
||||
|
||||
class RarFileImplementation(object):
|
||||
|
||||
def init(self, password=None):
|
||||
def init(self, password=None, custom_path = None):
|
||||
self.password = password
|
||||
archiveData = RAROpenArchiveDataEx(ArcNameW=self.archiveName, OpenMode=RAR_OM_EXTRACT)
|
||||
self._handle = RAROpenArchiveEx(ctypes.byref(archiveData))
|
||||
@@ -254,9 +254,9 @@ class RarFileImplementation(object):
|
||||
|
||||
if password:
|
||||
RARSetPassword(self._handle, password)
|
||||
|
||||
|
||||
self.lockStatus = "ready"
|
||||
|
||||
|
||||
|
||||
|
||||
def destruct(self):
|
||||
@@ -287,7 +287,7 @@ class RarFileImplementation(object):
|
||||
self.needskip = False
|
||||
res.append((info, reader.get_result()))
|
||||
return res
|
||||
|
||||
|
||||
|
||||
def extract(self, checker, path, withSubpath, overwrite):
|
||||
res = []
|
||||
@@ -300,7 +300,7 @@ class RarFileImplementation(object):
|
||||
fn = os.path.split(fn)[-1]
|
||||
target = os.path.join(path, fn)
|
||||
else:
|
||||
raise DeprecationWarning, "Condition callbacks returning strings are deprecated and only supported in Windows"
|
||||
raise DeprecationWarning, "Condition callbacks returning strings are deprecated and only supported in Windows"
|
||||
target = checkres
|
||||
if overwrite or (not os.path.exists(target)):
|
||||
tmpres = RARProcessFile(self._handle, RAR_EXTRACT, None, target)
|
||||
|
||||
Reference in New Issue
Block a user