Browse Source

Update put.io code

pull/3898/merge
Ruud 11 years ago
parent
commit
d0f1e7c6a3
  1. 131
      couchpotato/core/downloaders/putio.py
  2. 69
      couchpotato/core/downloaders/putio/__init__.py
  3. 271
      couchpotato/core/downloaders/putio/api.py
  4. 87
      couchpotato/core/downloaders/putio/main.py
  5. 68
      couchpotato/core/downloaders/putio/static/putio.js

131
couchpotato/core/downloaders/putio.py

@ -1,131 +0,0 @@
from __future__ import with_statement
import os
import traceback
import putio
import shutil
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent
from couchpotato.core._base.downloader.main import DownloaderBase
from couchpotato.core.helpers.encoding import sp
from couchpotato.core.helpers.variable import getDownloadDir
from couchpotato.core.logger import CPLog
from couchpotato.environment import Env
log = CPLog(__name__)
autoload = 'Putiodownload'
class Putiodownload(DownloaderBase):
protocol = ['torrent', 'torrent_magnet']
status_support = False
def __init__(self):
addApiView('putiodownload.getfrom', self.getFromPutio, docs = {
'desc': 'Allows you to download file from prom Put.io',
})
return super(Putiodownload,self).__init__()
def download(self, data = None, media = None, filedata = None):
if not media: media = {}
if not data: data = {}
log.info ('Sending "%s" to put.io', data.get('name'))
url = data.get('url')
OAUTH_TOKEN = self.conf('oauth_token')
client = putio.Client(OAUTH_TOKEN)
# Need to constuct a the API url a better way.
callbackurl = None
if self.conf('download'):
callbackurl = 'http://'+self.conf('callback_host')+'/'+self.conf('url_base', section='core')+'/api/'+self.conf('api_key', section='core')+'/putiodownload.getfrom/'
client.Transfer.add_url(url,callback_url=callbackurl)
return True
def test(self):
OAUTH_TOKEN = self.conf('oauth_token')
try:
client = putio.Client(OAUTH_TOKEN)
if client.File.list():
return True
except:
log.info('Failed to get file listing, check OAUTH_TOKEN')
return False
def getFromPutio(self, **kwargs):
log.info('Put.io Download has been called')
OAUTH_TOKEN = self.conf('oauth_token')
client = putio.Client(OAUTH_TOKEN)
files = client.File.list()
delete = self.conf('detele_file')
downloaddir = self.conf('download_dir')
tempdownloaddir = self.conf('tempdownload_dir')
for f in files:
if str(f.id) == str(kwargs.get('file_id')):
# Need to read this in from somewhere
client.File.download(f, dest=tempdownloaddir, delete_after_download=delete)
shutil.move(tempdownloaddir+"/"+str(f.name),downloaddir)
return True
config = [{
'name': 'putiodownload',
'groups': [
{
'tab': 'downloaders',
'list': 'download_providers',
'name': 'putiodownload',
'label': 'put.io Download',
'description': 'This will start a torrent download on Put.io. <BR>Note: you must have a putio account and API',
'wizard': True,
'options': [
{
'name': 'enabled',
'default': 0,
'type': 'enabler',
'radio_group': 'torrent',
},
{
'name': 'oauth_token',
'label': 'oauth_token',
'description': 'This is the OAUTH_TOKEN from your putio API',
},
{
'name': 'callback_host',
'description': 'This is used to generate the callback url',
},
{
'name': 'download',
'description': 'Set this to have CouchPotato download the file from Put.io',
'type': 'bool',
'default': 0,
},
{
'name': 'detele_file',
'description': 'Set this to remove the file from putio after sucessful download Note: does nothing if you don\'t select download',
'type': 'bool',
'default': 0,
},
{
'name': 'download_dir',
'label': 'Download Directory',
'description': 'The Directory to download files to, does nothing if you don\'t select download',
'default': '/',
},
{
'name': 'tempdownload_dir',
'label': 'Temporary Download Directory',
'description': 'The Temporary Directory to download files to, does nothing if you don\'t select download',
'default': '/',
},
{
'name': 'manual',
'default': 0,
'type': 'bool',
'advanced': True,
'description': 'Disable this downloader for automated searches, but use it when I manually send a release.',
},
],
}
],
}]

69
couchpotato/core/downloaders/putio/__init__.py

@ -0,0 +1,69 @@
from .main import PutIO
def autoload():
return PutIO()
config = [{
'name': 'putio',
'groups': [
{
'tab': 'downloaders',
'list': 'download_providers',
'name': 'putio',
'label': 'put.io',
'description': 'This will start a torrent download on <a href="http://put.io">Put.io</a>.',
'wizard': True,
'options': [
{
'name': 'enabled',
'default': 0,
'type': 'enabler',
'radio_group': 'torrent',
},
{
'name': 'oauth_token',
'label': 'oauth_token',
'description': 'This is the OAUTH_TOKEN from your putio API',
'advanced': True,
},
{
'name': 'callback_host',
'description': 'This is used to generate the callback url',
},
{
'name': 'download',
'description': 'Set this to have CouchPotato download the file from Put.io',
'type': 'bool',
'default': 0,
},
{
'name': 'delete_file',
'description': 'Set this to remove the file from putio after sucessful download Note: does nothing if you don\'t select download',
'type': 'bool',
'default': 0,
},
{
'name': 'download_dir',
'type': 'directory',
'label': 'Download Directory',
'description': 'The Directory to download files to, does nothing if you don\'t select download',
},
{
'name': 'tempdownload_dir',
'type': 'directory',
'label': 'Temporary Download Directory',
'description': 'The Temporary Directory to download files to, does nothing if you don\'t select download',
},
{
'name': 'manual',
'default': 0,
'type': 'bool',
'advanced': True,
'description': 'Disable this downloader for automated searches, but use it when I manually send a release.',
},
],
}
],
}]

271
couchpotato/core/downloaders/putio/api.py

@ -0,0 +1,271 @@
# -*- coding: utf-8 -*-
# Changed
# Removed iso8601 library requirement
# Added CP logging
import os
import re
import json
import webbrowser
from urllib import urlencode
from couchpotato import CPLog
from dateutil.parser import parse
import requests
BASE_URL = 'https://api.put.io/v2'
ACCESS_TOKEN_URL = 'https://api.put.io/v2/oauth2/access_token'
AUTHENTICATION_URL = 'https://api.put.io/v2/oauth2/authenticate'
log = CPLog(__name__)
class AuthHelper(object):
def __init__(self, client_id, client_secret, redirect_uri, type='code'):
self.client_id = client_id
self.client_secret = client_secret
self.callback_url = redirect_uri
self.type = type
@property
def authentication_url(self):
"""Redirect your users to here to authenticate them."""
params = {
'client_id': self.client_id,
'response_type': self.type,
'redirect_uri': self.callback_url
}
return AUTHENTICATION_URL + "?" + urlencode(params)
def open_authentication_url(self):
webbrowser.open(self.authentication_url)
def get_access_token(self, code):
params = {
'client_id': self.client_id,
'client_secret': self.client_secret,
'grant_type': 'authorization_code',
'redirect_uri': self.callback_url,
'code': code
}
response = requests.get(ACCESS_TOKEN_URL, params=params)
log.debug(response)
assert response.status_code == 200
return response.json()['access_token']
class Client(object):
def __init__(self, access_token):
self.access_token = access_token
self.session = requests.session()
# Keep resource classes as attributes of client.
# Pass client to resource classes so resource object
# can use the client.
attributes = {'client': self}
self.File = type('File', (_File,), attributes)
self.Transfer = type('Transfer', (_Transfer,), attributes)
self.Account = type('Account', (_Account,), attributes)
def request(self, path, method='GET', params=None, data=None, files=None,
headers=None, raw=False, stream=False):
"""
Wrapper around requests.request()
Prepends BASE_URL to path.
Inserts oauth_token to query params.
Parses response as JSON and returns it.
"""
if not params:
params = {}
if not headers:
headers = {}
# All requests must include oauth_token
params['oauth_token'] = self.access_token
headers['Accept'] = 'application/json'
url = BASE_URL + path
log.debug('url: %s', url)
response = self.session.request(
method, url, params=params, data=data, files=files,
headers=headers, allow_redirects=True, stream=stream)
log.debug('response: %s', response)
if raw:
return response
log.debug('content: %s', response.content)
try:
response = json.loads(response.content)
except ValueError:
raise Exception('Server didn\'t send valid JSON:\n%s\n%s' % (
response, response.content))
if response['status'] == 'ERROR':
raise Exception(response['error_type'])
return response
class _BaseResource(object):
client = None
def __init__(self, resource_dict):
"""Constructs the object from a dict."""
# All resources must have id and name attributes
self.id = None
self.name = None
self.__dict__.update(resource_dict)
try:
self.created_at = parse(self.created_at)
except AttributeError:
self.created_at = None
def __str__(self):
return self.name.encode('utf-8')
def __repr__(self):
# shorten name for display
name = self.name[:17] + '...' if len(self.name) > 20 else self.name
return '<%s id=%r, name="%r">' % (
self.__class__.__name__, self.id, name)
class _File(_BaseResource):
@classmethod
def get(cls, id):
d = cls.client.request('/files/%i' % id, method='GET')
t = d['file']
return cls(t)
@classmethod
def list(cls, parent_id=0):
d = cls.client.request('/files/list', params={'parent_id': parent_id})
files = d['files']
return [cls(f) for f in files]
@classmethod
def upload(cls, path, name=None):
with open(path) as f:
if name:
files = {'file': (name, f)}
else:
files = {'file': f}
d = cls.client.request('/files/upload', method='POST', files=files)
f = d['file']
return cls(f)
def dir(self):
"""List the files under directory."""
return self.list(parent_id=self.id)
def download(self, dest='.', delete_after_download=False):
if self.content_type == 'application/x-directory':
self._download_directory(dest, delete_after_download)
else:
self._download_file(dest, delete_after_download)
def _download_directory(self, dest='.', delete_after_download=False):
name = self.name
if isinstance(name, unicode):
name = name.encode('utf-8', 'replace')
dest = os.path.join(dest, name)
if not os.path.exists(dest):
os.mkdir(dest)
for sub_file in self.dir():
sub_file.download(dest, delete_after_download)
if delete_after_download:
self.delete()
def _download_file(self, dest='.', delete_after_download=False):
response = self.client.request(
'/files/%s/download' % self.id, raw=True, stream=True)
filename = re.match(
'attachment; filename=(.*)',
response.headers['content-disposition']).groups()[0]
# If file name has spaces, it must have quotes around.
filename = filename.strip('"')
with open(os.path.join(dest, filename), 'wb') as f:
for chunk in response.iter_content(chunk_size=1024):
if chunk: # filter out keep-alive new chunks
f.write(chunk)
f.flush()
if delete_after_download:
self.delete()
def delete(self):
return self.client.request('/files/delete', method='POST',
data={'file_ids': str(self.id)})
def move(self, parent_id):
return self.client.request('/files/move', method='POST',
data={'file_ids': str(self.id), 'parent_id': str(parent_id)})
def rename(self, name):
return self.client.request('/files/rename', method='POST',
data={'file_id': str(self.id), 'name': str(name)})
class _Transfer(_BaseResource):
@classmethod
def list(cls):
d = cls.client.request('/transfers/list')
transfers = d['transfers']
return [cls(t) for t in transfers]
@classmethod
def get(cls, id):
d = cls.client.request('/transfers/%i' % id, method='GET')
t = d['transfer']
return cls(t)
@classmethod
def add_url(cls, url, parent_id=0, extract=False, callback_url=None):
d = cls.client.request('/transfers/add', method='POST', data=dict(
url=url, parent_id=parent_id, extract=extract,
callback_url=callback_url))
t = d['transfer']
return cls(t)
@classmethod
def add_torrent(cls, path, parent_id=0, extract=False, callback_url=None):
with open(path) as f:
files = {'file': f}
d = cls.client.request('/files/upload', method='POST', files=files,
data=dict(parent_id=parent_id,
extract=extract,
callback_url=callback_url))
t = d['transfer']
return cls(t)
@classmethod
def clean(cls):
return cls.client.request('/transfers/clean', method='POST')
class _Account(_BaseResource):
@classmethod
def info(cls):
return cls.client.request('/account/info', method='GET')
@classmethod
def settings(cls):
return cls.client.request('/account/settings', method='GET')

87
couchpotato/core/downloaders/putio/main.py

@ -0,0 +1,87 @@
import shutil
from couchpotato.api import addApiView
from couchpotato.core._base.downloader.main import DownloaderBase
from couchpotato.core.logger import CPLog
import api as pio
log = CPLog(__name__)
autoload = 'Putiodownload'
class PutIO(DownloaderBase):
protocol = ['torrent', 'torrent_magnet']
status_support = False
def __init__(self):
addApiView('downloader.putio.getfrom', self.getFromPutio, docs = {
'desc': 'Allows you to download file from prom Put.io',
})
addApiView('downloader.putio.auth_url', self.getAuthorizationUrl)
return super(PutIO, self).__init__()
def download(self, data = None, media = None, filedata = None):
if not media: media = {}
if not data: data = {}
log.info('Sending "%s" to put.io', data.get('name'))
url = data.get('url')
client = pio.Client(self.conf('oauth_token'))
# Need to constuct a the API url a better way.
callbackurl = None
if self.conf('download'):
callbackurl = 'http://' + self.conf('callback_host') + '/' + self.conf('url_base',
section = 'core') + '/api/' + self.conf(
'api_key', section = 'core') + '/downloader.putiodownload.getfrom/'
client.Transfer.add_url(url, callback_url = callbackurl)
return True
def test(self):
try:
client = pio.Client(self.conf('oauth_token'))
if client.File.list():
return True
except:
log.info('Failed to get file listing, check OAUTH_TOKEN')
return False
def getAuthorizationUrl(self):
# See notification/twitter
pass
def getCredentials(self):
# Save oauth_token here to settings
pass
def getAllDownloadStatus(self, ids):
# See other downloaders for examples
# Check putio for status
# Check "getFromPutio" progress
pass
def getFromPutio(self, **kwargs):
log.info('Put.io Download has been called')
client = pio.Client(self.conf('oauth_token'))
files = client.File.list()
tempdownloaddir = self.conf('tempdownload_dir')
downloaddir = self.conf('download_dir')
for f in files:
if str(f.id) == str(kwargs.get('file_id')):
# Need to read this in from somewhere
client.File.download(f, dest = tempdownloaddir, delete_after_download = self.conf('delete_file'))
shutil.move(tempdownloaddir + "/" + str(f.name), downloaddir)
# Mark status of file_id as "done" here for getAllDownloadStatus
return True

68
couchpotato/core/downloaders/putio/static/putio.js

@ -0,0 +1,68 @@
var PutIODownloader = new Class({
initialize: function(){
var self = this;
App.addEvent('loadSettings', self.addRegisterButton.bind(self));
},
addRegisterButton: function(){
var self = this;
var setting_page = App.getPage('Settings');
setting_page.addEvent('create', function(){
var fieldset = setting_page.tabs.downloaders.groups.putio,
l = window.location;
var putio_set = 0;
fieldset.getElements('input[type=text]').each(function(el){
putio_set += +(el.get('value') != '');
});
new Element('.ctrlHolder').adopt(
// Unregister button
(putio_set > 0) ?
[
self.unregister = new Element('a.button.red', {
'text': 'Unregister "'+fieldset.getElement('input[name*=screen_name]').get('value')+'"',
'events': {
'click': function(){
fieldset.getElements('input[type=text]').set('value', '').fireEvent('change');
self.unregister.destroy();
self.unregister_or.destroy();
}
}
}),
self.unregister_or = new Element('span[text=or]')
]
: null,
// Register button
new Element('a.button', {
'text': putio_set > 0 ? 'Register a different account' : 'Register your put.io account',
'events': {
'click': function(){
Api.request('downloader.putio.auth_url', {
'data': {
'host': l.protocol + '//' + l.hostname + (l.port ? ':' + l.port : '')
},
'onComplete': function(json){
window.location = json.url;
}
});
}
}
})
).inject(fieldset.getElement('.test_button'), 'before');
})
}
});
window.addEvent('domready', function(){
new PutIODownloader();
});
Loading…
Cancel
Save