Skip to content

Instantly share code, notes, and snippets.

@hendrikswan
Created October 12, 2011 19:20
Show Gist options
  • Save hendrikswan/1282234 to your computer and use it in GitHub Desktop.
Save hendrikswan/1282234 to your computer and use it in GitHub Desktop.
sync-putio-files
credentials.txt
series_sort.py
*.pyc
#!/usr/bin/env python
# encoding: utf-8
# Created by Put.io.
# Copyright (c) 2010 Put.io.
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
"""
Basic Tutorial:
from putio import *
# connecting your put.io with your api key and api secret
api = Api("123456","abcdef")
# getting your items
items = api.get_items()
for it in items:
print "%5s %s" % (it.id, it.name)
# creating a folder
newitem = api.create_folder(name="blabla")
# getting one item
item = api.get_items(id=newitem.id)[0]
#item = api.get_items()[0]
# getting an item info
item = item.update_info()
print item.name, item.id, item.is_dir, item.__dict__
# renaming an item
print "old name: %s" % item.name
newitem = item.rename_item("renamed by api")
print "new name: %s" % newitem.name
# moving an item to a target folder
# 0 being root of your files
newitem.move_item(target=0)
# deleting an item
newitem.delete_item()
# searching items
sresults = api.search_items("avi from:me type:video")
See the site for more info.
Todos:
* oAuth support
* Creating and getting MP4 Files
* File Uploading
* User Methods
"""
import sys
import socket
import urllib
import urllib2
try:
import json
except ImportError:
import simplejson as json
# setup
RPC_URL = "http://api.put.io/v1"
# constants
UNITS = ['B', 'K', 'M', 'G', 'T', 'P', 'E']
TIMEOUT = 60 #seconds
VERSION = "0.91"
# 0.92 transfers/add change
# 0.91 tidying up for the release; unicode human_size, user_idi user info
# bugs; some user, friend and item methods
# 0.90 updated auth, "v1"
# 0.85 reformated input and output. big changes on the api server.
# 0.84b typos
# 0.84 item_search to search_items, get_items orderby, get_friends
# 0.83 new stream url
def human_size(size):
"""
Converts bytes to human readable strings
Takes : An Integer
Returns: A String
Example:
>>> print human_size(12.66) # 12.7B
>>> print human_size(12345) # 12.1K
>>> print human_size(12345678) # 11.8M
>>> print human_size(12345678910) # 11.5G
>>> print human_size(12345678910111) # 11.2T
"""
if isinstance(size, unicode): size = int(size)
s = float(size * 1.0)
i = 0
while size >= 1024.00 and i < len(UNITS):
i += 1
size /= 1024.00
return "%.1f%s" % (size, UNITS[i])
def _send(obj, path, post=None, **args):
"""
Chats with API Server.
Takes : A dict.
Returns: JSON
To format the output, call _result() method.
Request Format:
{
"user_id" : INTEGER,
"api_key" : STRING,
"api_secret" : STRING,
"params" : DICTIONARY
}
Response Format:
{
"user_id" : INTEGER,
"response" : {
"results" : [{
.... data ....
}]
},
"error" :null,
"error_message" :null
}
"""
post_request = dict()
if not obj:
raise PutioError("You need to login first")
else:
post_request['api_key'] = obj.api_key
post_request['api_secret'] = obj.api_secret
url = RPC_URL + path
if args: url += "?" + urllib.urlencode(args)
# print url
# print "0. REQUEST: %s: %s" % (type(post), post)
post_request['params'] = dict()
for k in post.keys():
#if k not in ("user_id", "api_key", "api_secret"):
if k not in ("api_key", "api_secret"):
post_request['params'][k] = post[k]
else:
post_request[k] = post[k]
# print "POSTREQUEST: %s" % post_request
pre_post = {}
pre_post['request'] = json.dumps(post_request)
error_data = ""
try:
# print "1. POST: %s: %s" % (type(pre_post), pre_post)
request = urllib2.Request(url, urllib.urlencode(pre_post))
# default timeout time is 60 seconds.
socket.setdefaulttimeout(TIMEOUT)
if (sys.version_info[0] == 2 and sys.version_info[1] > 5) \
or sys.version_info[0] > 2:
u = urllib2.urlopen(request, timeout=TIMEOUT)
else:
u = urllib2.urlopen(request)
data = u.read()
# print "2. RECEIVED DATA: %s" % data
return _result(obj, data)
except urllib2.HTTPError, e:
error_data = e.read()
if e.code == 500:
raise PutioError("An error occured. This may be a bug. Please \
report to the application provider.", e)
elif e.code == 404:
raise PutioError("Unknown method, service or parameters.", e)
else:
raise PutioError("Service unavailable. Please try again.", e)
except urllib2.URLError, e:
raise PutioError("Request failed. (%s)" % str(e), e)
except UnboundLocalError, e:
raise PutioError("Service unavailable. This may be a bug on the api \
server side. Please report following info: (%s)" % str(e), e)
def _result(obj, data):
"""
Takes : JSON
Returns: A Dict
Checks if the api server returned an error or not.
Returns the error message or the success message.
JSON Format that API Server returns:
{
"error": false,
"error_message": null,
"user_id": some_integer,
"user_name": "username"
"response": {
"results": [ ...Always an array of things... ]
}
}
"""
try:
result = json.loads(data)
# print "3. RESULT DATA: %s" % result
except ValueError, e:
#logger.error('Error: %s' % e)
#logger.error('Data: %s' % data)
#logger.error('Result: %s' % result)
raise PutioError("Json error.", e)
# It's now a python dictionary. Json.loads() makes necessary
# conversions like "false" to "False", "null" to "None", etc.
# print "3. JSON TO DICT: ", result
if result['error'] is False:
obj.user_name = result['user_name']
obj.user_id = result['id']
return result['response']['results']
else:
#raise PutioError(result['error_message'])
return None
def strip_tags(value):
"""
Return the given HTML with all tags stripped.
You may use this to strip the html tags from
Put.io Dashboard Messages. (Optional)
Usage:
print strip_tags(MessageInstance.title)
"""
import re
return re.sub(r'<[^>]*?>', '', value)
class BaseObj(object):
# Creates an object with given dictionary.
def __init__(self, dictionary=None, **args):
if dictionary:
self.__dict__ = dictionary
if len(args) > 0:
for k,v in args.iteritems():
self.__dict__[k] = v
# def __getattr__(self, k):
# return self.__dict__[k]
def _convert_to_string(self):
if self.__dict__.has_key('file_type'):
self.file_type = Item._int_to_filetype(self.file_type)
if self.__dict__.has_key('dl_handler'):
self.dl_handler = UrlBucket.dl_handler[str(self.dl_handler)]
if self.__dict__.has_key('dltype'):
self.dltype = UrlBucket.dltype[str(self.dltype)]
class PutioError(Exception):
"""
PutioError Exception Class
"""
def __init__(self, message='', original=None):
self.message = message
self.original = original
def __str__(self):
if self.original:
original_name = type(self.original).__name__
return '%s (Original Exception: %s, "%s")' % (self.message,
original_name,
self.original.args)
else:
return self.message
class User(BaseObj):
"""
Sample user:
u.name : 'aft'
u.friends_count : 497
u.bw_avail_last_month : '0'
u.bw_quota : '161061273600'
u.shared_items : 3
u.bw_quota_available : '35157040261'
u.disk_quota : '206115891200'
u.disk_quota_available : '158153510402'
u.shared_space : 0
"""
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
class Friend(BaseObj):
"""
Sample friend
f.dir_id : '1407'
f.id : '2'
f.name : 'hasan'
"""
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
def get_items(self, **args):
"""
Takes : A friend instance
Returns: A List of item objects.
Shortcut for listing a friends shared items.
"""
return self.api.get_items(parent_id = self.dir_id, **args)
class Message(BaseObj):
"""
Dashboard message objects.
Message Methods:
message.delete()
Message Attributes:
message.id (Integer)
message.user_id (Integer)
message.title (String)
message.description (None)
message.importance (Integer)
message.file_name (String)
message.file_type (String)
message.user_file_id (Integer)
message.from_user_id (Integer. If None, message is from Put.io)
message.channel (Integer)
message.hidden (Integer, 1 or 0)
Sample:
user_file_id = 4
user_id = 17
description = None
title = '<a rel="userfile" href="/file/4">abcd.mp4
</a> <span class="dash-gray">(89.86K)
downloaded</span>'
importance = 0
file_name = 'abcd.mp4'
id = 3773
file_type = 'audio'
hidden = 0
from_user_id = None
channel = 2
"""
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, *args)
self.file_type = Item._int_to_filetype(self.file_type)
self.api = api
def delete(self):
"""
Deletes messages. Returns none if unsuccessful.
"""
args = {"id":self.id}
result = _send(self.api, path="/messages", post=args, method="delete")
if not result: return None
class Api(object):
"""
A python interface into the Put.io API
Example usage:
To create an instance of the putio.Api class, with authentication:
>>> from putio import *
>>> api = api(YOUR_API_KEY, YOUR_API_SECRET)
To get the list of your files:
>>> items = api.get_items()
>>> for i in items: print i.id, i.name
To get the item list in a specified folder:
>>> items = api.get_items(id=123)
>>> for i in items: print i.id, i.name
Api Methods:
api.get_items()
api.get_transfers()
api.get_user()
api.is_ready()
api.create_folder()
api.search_items()
api.get_messages()
api.create_subscription()
api.get_subscription()
api.get_folder_list()
api.update_user_token()
api.get_user_info()
api.create_bucket()
"""
def __init__(self, api_key, api_secret):
self.user_id = None
self.user_name = None
self.access_token = None
self.api_key = api_key
self.api_secret = api_secret
# Token is required only for streaming links
self.access_token = self._get_user_token()
self.api = self
def update_user_token(self):
"""
Before streaming a video/audio file, its best to update the token.
This method doesn't return anything. It just updates the Api instance.
"""
self.access_token = self._get_user_token()
def get_user_name(self):
"""
Takes : Nothing
Returns: A String
Returns the name of the authenticated user. You can use this to
welcome your user.
"""
return self.user_name
def is_ready(self):
"""
Takes : Nothing.
Returns: True or False
Checks if the authentication is successful. Returns False if it isn't.
Probably, you won't be using this much.
Example:
>>> api = Api("key", "secret")
>>> if api.is_ready(): print "Vuhuu!"
"""
try:
return self.user_name
except:
return None
def get_items(self, limit=20, offset=0, parent_id=0, **arguments):
"""
Takes : Item attributes [Optional]
Returns: An Array of Item objects
Example:
>>> import putio
>>> api = putio.Api(YOUR_API_KEY, YOUR_API_SECRET)
>>> items = api.get_items() #without an argument
>>> items = api.get_items(type="video") #with an argument
>>> for i in items: print i.name, i.id, i.type
You can use these optional parameters while selecting item(s):
id = STRING or INTEGER
parent_id = STRING or INTEGER
offset = INTEGER (Default:0)
limit = INTEGER (Default: 20)
type = STRING (See Item class for available types)
orderby = STRING (Default: createdat_desc)
Orderby parameters:
id_asc
id_desc
type_asc
type_desc
name_asc
name_desc
extention_asc
extention_desc
createdat_asc
createdat_desc (Default)
See Item Class doc for the available attributes.
"""
items = []
args = {"limit":limit, "offset":offset, "parent_id":parent_id}
for k,v in arguments.iteritems(): args[k] = v
if "type" in arguments:
args['type'] = Item._filetype_to_int(arguments["type"])
result = _send(self.api, path="/files", post=args, method="list")
if result:
self.update_user_token()
for r in result:
items.append(Item(self.api, r))
return items
else:
raise PutioError("You have no items to show.")
def get_transfers(self):
"""
Takes : Nothing
Returns: An Array of Transfer objects
Example:
>>> trans = api.get_transfers()
>>> if newtransfers:
>>> for t in newtransfers:
>>> print t.name, t.status, t.percent_done
>>> else: print "you have no active transfers"
See Transfer Class doc for the available attributes.
"""
transfers = []
args = {}
transferlist = _send(self, path="/transfers", post=args, method="list")
if len(transferlist) > 0:
for k in transferlist:
transfers.append(Transfer(self.api, k))
return transfers
else:
return None
raise PutioError('You have no active transfers at the moment.')
def create_folder(self, name="New Folder", parent_id=0):
"""
Takes : A String [Optional], and
An Integer [Optional]
Returns: A Single Item object if successful.
Example:
>>> newfolder = api.create_folder(name="Created by Api")
>>> if newfolder: print "%s is created." % newfolder.name
"""
args = {"name":name, "parent_id":parent_id}
newfolder = _send(self.api,
path="/files",
post=args,
method="create_dir")
if newfolder and isinstance(newfolder, list):
newfolder = newfolder[0]
newfolder['id'] = int(newfolder['id'])
return Item(self.api, newfolder)
else:
#raise PutioError('Folder could not be created.')
return None
def search_items(self, query):
"""
Takes : A String
Returns: An Array of Item objects
Returns search results. You may add search parameters to the string
such as:
"from:'me'" (from:shares|jack|all|etc.)
"type:'video'" (audio|image|iphone|all|etc.)
"ext:'mp3'" (avi|jpg|mp4|all|etc.)
"time:'today'" (yesterday|thismonth|thisweek|all|etc.)
Example:
>>> searchresults = api.search_items("'jazz' from:'me' type:'audio'")
>>> if searchresults:
>>> for sr in searchresults: print sr.name
"""
search_results = []
args = {"query":query}
result = _send(self, path="/files", post=args, method="search")
if result:
self.update_user_token()
for r in result: search_results.append(Item(self.api, r))
return search_results
else:
return None
def get_messages(self):
"""
Takes : Nothing
Returns: An Array of Message objects
Returns your dashboard messages.
Example:
>>> msgs = api.get_messages()
>>> if msgs:
>>> for m in msgs: print m.title
"""
messages = []
args = {}
result = _send(self, path="/messages", post=args, method="list")
if result:
for r in result: messages.append(Message(self.api, r))
return messages
else:
return None
def create_subscription(self, name="My LegalTorrents Subscription",
url="http://www.legaltorrents.com/rss.xml",
**arguments):
"""
Takes : A String for name, a string for URL, and optional args.
Returns: A Single Subscription object
Creates a new subscription and returns it.
Example:
>>> newsub = api.create_subscription(name="Mininova",
url="http://www.mininova.org/rss.xml")
>>> if newsub: print "%s created." % newsub.name
See Subscription Class for available attributes
"""
args = {"title":name, "url":url}
for k in arguments.keys(): args[k] = arguments[k]
result = _send(self, path="/subscriptions", post=args, method="create")
if result:
return Subscription(self.api, result[0])
else:
return None
def get_subscriptions(self, **arguments):
"""
Takes: Nothing
Returns: An Array of Subscription objects
Returns a list of your subscriptions.
Example:
>>> subs = api.get_subscriptions()
>>> if newsub:
>>> for s in subs: print subs.name
See Subscription Class for available attributes
"""
subscriptions = []
args = {}
result = _send(self, path="/subscriptions", post=args, method="list")
if len(result) > 0:
for r in result:
if len(arguments) > 0:
for k,v in arguments.iteritems():
if r[k] == v:
subscriptions.append(Subscription(self.api, r))
else:
subscriptions.append(Subscription(self.api, r))
return subscriptions
else:
return None
def get_folder_list(self):
"""
Takes : Nothing
Returns: An Array of item objects.
Notice that this method returns a flat list of your folders. Create
your own method if you need a tree like list.
Parent_id is id of the container folder
Example:
>>> folderlist = api.get_folder_list()
>>> if folderlist:
>>> for f in folderlist: print f.name
Here is the returned item before being processed:
{
u'dirs': [...{sub folder 1}, {sub folder 2}...], # or []
u'shared': None,
u'id': u'4220',
u'name': u'renamed (4)',
u'default_shared': None
}
See Folder Class for available attributes
"""
folders = []
args = {}
result = _send(self, path="/files", post=args, method="dirmap")
# flattens the folder list
def recursive(folderarray):
if len(folderarray['dirs']) > 0:
for fa in folderarray['dirs']:
folders.append(Folder(self.api, fa))
recursive(fa)
else: folders.append(Folder(self.api, folderarray))
if result:
for r in result['dirs']:
if len(r['dirs']) > 0:
recursive(r)
else: folders.append(Folder(self.api, r))
return folders
else:
return None
def get_user_info(self):
"""
Takes : Nothing
Returns: A Single User object if successful.
Gives information about the authenticated user. Use this to inform
user about its quotas, sharing size, current available space, etc.
All sizes are in bytes. Use human_size(byte) to convert if necessary.
Returned Attributes:
info.bw_quota
info.disk_quota
info.bw_quota_available
info.disk_quota_available
info.name
info.shared_space
info.friends_count
info.shared_items
"""
args = {}
result = _send(self, path="/user", post=args, method="info")[0]
if result:
return User(self.api, result)
else:
return None
def _get_user_token(self):
"""
Internal method for getting the user token.
"""
args = {}
result = _send(self, path="/user", post=args, method="acctoken")
if result:
return result['token']
def get_friends(self):
"""
Takes : Nothing
Returns: An Array of Friend objects
Returns friends of the authenticated user.
Friend attributes:
friend.id
friend.name
friend.dir_id
Example:
>>> friends = api.get_friends()
>>> if friends:
>>> for f in friends: print f.name
To get a friend's files, use dir_id as a parent_id with get_items()
Option 1:
>>> for f in friends:
>>> items[f] = api.get_items(parent_id=f.dir_id)
>>> for i in items['jack']: print i.name
Option 2:
>>> for f in friends:
>>> f_items = f.get_items(limit=1)
>>> for fi in f_items: print fi.name
Returns None if the friend has no items.
"""
friends = []
args = {}
result = _send(self, path="/user", post=args, method="friends")
if result:
for r in result: friends.append(Friend(self.api, r))
return friends
else:
return None
def create_bucket(self):
"""
Takes : Nothing
Returns: An empty bucket object.
You'll need buckets to analyze and fetch URLs. Bucket is basicly a
container of one or more URLs, which then you can analyze and make
Put.io fetch the successfully analyzed URLs.
To fetch some URLs, you'll need to:
* Create a bucket (or use already existing one)
* Use Add method to add some URLs to the bucket
* Make Put.io analyze the bucket
* Add more or delete some of them
* And make Put.io fetch the URLs in the bucket.
After the analyzation, you can get a report about the bucket content
and the user quotas. For this, use get_report() method of UrlBucket
class.
"""
return UrlBucket(self.api)
class Item(BaseObj):
"""
An item can be a file or a folder.
Avaiable Item methods are:
item.rename_item()
item.move_item()
item.delete_item()
item.update_info()
item.get_download_url()
item.get_zip_url()
item.get_stream_url()
Available Item attributes:
Sizes are in bytes. Use human_size(byte) to convert if necessary.
item.id
item.name
item.type
item.size
item.is_dir
item.parent_id
item.screenshot_url
item.thumb_url
item.file_icon_url
item.download_url
Example Folder Item:
"id":"4394",
"name":"Billie Ray Martin The Crackdown Project - Vol 1",
"type":"folder",
"size":"23472048",
"is_dir":true,
"parent_id":"0",
"screenshot_url":"http://put.io/screenshot/b/dgRraFxlXmNl.jpg",
"thumb_url":"http://put.io/screenshot/dgRraFxlXmNl.jpg",
"file_icon_url":"http://put.io/images/file_types/folder.png",
"folder_icon_url":"",
"download_url":"http://node2.endlessdisk.com/download-file/17/4394",
"zip_url":"/stream-basket/17/4394"}
at the moment, type can be a:
folder
file
audio
movie
image
compressed
pdf
ms_doc
text
swf
unknown
"""
filetypes = {
"folder" : 0,
"file" : 1,
"audio" : 2,
"movie" : 3,
"image" : 4,
"compressed" : 5,
"pdf" : 6,
"ms_doc" : 7,
"text" : 8,
"swf" : 9
}
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
@staticmethod
def _filetype_to_int(filetype):
if Item.filetypes.has_key(str(filetype.lower())):
return Item.filetypes[filetype]
else: raise PutioError("Unknown type. Please use one of these: " \
"folder, file, audio, movie, image," \
"compressed, pdf, ms_doc, text, swf")
@staticmethod
def _int_to_filetype(filetypeint):
"""
This method is not used because Api Server gives file types as string
"""
newfiletypes = dict()
# changes {"key":intvalue} to {"strvalue":"key"} first
for k,v in Item.filetypes.iteritems(): newfiletypes[str(v)] = k
# then finds value in keys
if newfiletypes.has_key(str(filetypeint)):
return newfiletypes[str(filetypeint)]
else:
return "unknown"
def rename_item(self, name):
"""
Takes : A String [Required]
Returns: A Single Item object
Example:
>>> print "Old Name: %s" % item.name
>>> item = item.rename_item("New Item Name")
>>> print "New Name: %s" % item.name
Renames the item.
"""
args = {"name":name, "id":self.id}
result = _send(self.api, path="/files", post=args, method="rename")
if result: return Item(self.api, result[0])
else: return None
def move_item(self, target=0):
"""
Takes : An Integer of target folder id. [Optional]
Returns: A Single Item object
Example:
>>> item = item.move_item(target=1221)
>>> if item:
>>> print "New Location: %s" % api.get_items(id=item.id).name
Moves the item to another folder. Parent_id = 0 is the root folder
of your disk space.
"""
args = {"id":self.id, "parent_id":target}
result = _send(self.api, path="/files", post=args, method="move")
if result: return Item(self.api, result[0])
else: return None
def delete_item(self):
"""
Takes : Nothing
Returns: Nothing if successful.
Example:
>>> item.delete_item()
Destroys the item permanently.
"""
args = {"id":self.id}
result = _send(self.api, path="/files", post=args, method="delete")
# fixme, check and return the result
return result
def update_info(self):
"""
Takes : Nothing
Returns: A Single Item object
Example:
>>> item = item.update_info()
>>> print item.__dict__
Refreshes the items attributes. Useful for folders, because folders
can be updated via subscriptions in the background.
Raises an exception if unsuccessful.
"""
args = {"id":self.id}
result = _send(self.api, path="/files", post=args, method="info")
if result:
return Item(self.api, result[0])
else:
raise PutioError("Update failed. Item may be unavailable.")
def get_download_url(self):
"""
Returns: A String
Example:
>>> item = item.update_info() #if necessary
>>> print item.get_download_url()
"""
if self.is_dir == False:
return self.download_url
else:
return None
def get_stream_url(self):
"""
Takes : Nothing
Returns: A String
Returns the stream url of files. If item is a folder, then download
url will be returned.
"""
result = self.update_info()
if result:
if self.is_dir == False:
sturl = str(self.stream_url) + "/atk/" \
+ self.api.access_token
return sturl
else:
return self.get_download_url()
else: raise PutioError("Error: Item not found.")
def create_mp4(self):
"""TODO"""
pass
def create_folder(self, name="New Folder", **args):
"""
Takes : Nothing
Returns: An Item object
Creates and returns a folder in another folder.
"""
return self.api.create_folder(name = name, parent_id = self.id,**args)
class Folder(Item):
"""
Folders are an Item object. Check Item class documentation for more info.
Sample:
folder.id = 4394
folder.name = "Billie Ray Martin The Crackdown Project"
folder.type = "folder"
folder.size = 23472048
folder.is_dir = True
folder.parent_id = 0
folder.screenshot_url = "/images/file_types/file.png"
folder.thumb_url = "/images/file_types/file.png"
folder.file_icon_url = "/images/file_types/folder.png"
folder.folder_icon_url = ""
folder.download_url = "http://XX.put.io/download-file/17/4394"
folder.zip_url = "http://XX.put.io/stream-basket/17/4394"
"""
def __init__(self, api, dictionary=None, **args):
Item.__init__(self, dictionary, **args)
self.api = api
def create_folder(self, name="New Folder", **args):
"""
Creates a folder in another folder. This method is not being used
at the moment.
"""
return self.api.create_folder(name = name, parent_id = self.id,**args)
class Subscription(BaseObj):
"""
Subscription Methods:
edit()
delete()
toggle_status()
undate_info()
add_do_filters()
add_dont_filters()
Subscription Attributes:
subsitem.id (integer)
subsitem.url (string)
subsitem.name (string)
subsitem.do_filters (strings, seperated by commas)
subsitem.dont_filters (strings, seperated by commas)
subsitem.parent_folder_id (integer)
subsitem.last_update_time (string)
subsitem.next_update_time (string)
subsitem.paused (boolean)
Example:
>>> subsitem = api.get_subscriptions()[0]
>>> if subsitem: print subsitem.name
Sample Values:
subsitem.id = 860
subsitem.url = "http://legaltorrents.com/music/rss.xml"
subsitem.name = "Jazz Radio"
subsitem.do_filters = "jazz, mp3"
subsitem.dont_filters = "smooth, wav"
subsitem.parent_folder_id = 234
subsitem.last_update_time = "2010-01-01 00:00"
subsitem.next_update_time = "2010-01-01 00:00"
subsitem.paused = False
"""
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
def edit(self, **arguments):
"""
Takes : A Dictionary of attributes to change
Returns: A Single Subscription object
Example:
>>> newsub = subsitem.edit(name="newname", url="http://newurl", ...)
>>> if newsubs: print "Subscription updated."
Changes values of any given subscription attribute.
"""
args = {"id":self.id, "title":self.name, "url":self.url}
for k in arguments.keys(): args[k] = arguments[k]
result = _send(self.api,
path="/subscriptions",
post=args,
method="edit")
if result: return Subscription(self.api, result[0])
else: return None
def delete(self):
"""
Deletes the subscription item permanently.
Example:
>>> subsitem.delete()
>>> if newsubs: print "Subscription updated."
"""
args = {"id":self.id}
result = _send(self.api, path="/subscriptions", post=args, method="delete")
# fixme, better solution?
if not result: return None
def toggle_status(self):
"""
Toggles the activity status of a subscription item. You may also
change this value by editing the subscription item. This is just a
shortcut we use.
Example:
>>> print subsitem.paused # print True
>>> subsitem.toggle_status()
>>> print subsitem.paused # print False
"""
args = {"id":self.id}
result = _send(self.api, path="/subscriptions", post=args, method="pause")
if result:
self.paused = result[0]['paused']
return Subscription(self.api, result[0])
else:
return None
def update_info(self):
"""
Refreshes the subscription info. Use this to get the latest info
about the subscriptions.
Example:
>>> subs.update_info()
or
>>> subs = subs.update_info
"""
args = {"id":self.id}
result = _send(self.api, path="/subscriptions", post=args, method="info")
if result:
return Subscription(self.api, result[0])
else:
raise PutioError("Subscription update failed.")
def add_do_filters(self, *arguments):
"""
Takes : An Array of strings
Returns: Updated Subscription object
Adds keyword(s) to "do fetch" filter
"""
args = self._modify_filter("add", *arguments)
result = _send(self.api, path="/subscriptions", post=args, method="edit")
if result:
return Subscription(self.api, result[0])
else:
return False
def add_dont_filters(self, *arguments):
"""
Takes : An Array of strings
Returns: Updated Subscription object
Adds keyword(s) to "don't fetch" filter
"""
args = self._modify_filter("add", *arguments)
result = _send(self.api, path="/subscriptions", post=args, method="edit")
if result:
return Subscription(self.api, result[0])
else:
return False
def del_do_filters(self, *arguments):
"""
Takes : An Array of strings
Returns: Updated Subscription object
Deletes keyword(s) from "do fetch" filter
"""
args = self._modify_filter("remove", *arguments)
result = _send(self.api, path="/subscriptions", post=args, method="edit")
if result:
return Subscription(self.api, result[0])
else:
return False
def del_dont_filters(self, *arguments):
"""
Takes : An Array of strings
Returns: Updated Subscription object
Deletes keyword(s) from "dont fetch" filter
"""
args = self._modify_filter("remove", *arguments)
result = _send(self.api, path="/subscriptions", post=args, method="edit")
if result:
return Subscription(self.api, result[0])
else:
return False
def _modify_filter(self, task, *arguments):
if self.do_filters is None: self.do_filters = ""
if self.dont_filters is None: self.dont_filters = ""
if task == "remove":
self.do_filters = self._remove_filter(self.do_filters, *arguments)
self.dont_filters = self._remove_filter(self.dont_filters, *arguments)
else:
self.do_filters = self._add_filter(self.do_filters, *arguments)
self.dont_filters = self._add_filter(self.dont_filters, *arguments)
args = {
"id":self.id,
"title":self.name,
"url":self.url,
"do_filters":self.do_filters,
"dont_filters":self.dont_filters
}
return args
@staticmethod
def _add_filter(filt, *arguments):
filters = list(filt.split(','))
for a in arguments:filters.append(a)
for n in range(len(filters)): filters[n] = str(filters[n]).strip()
filters_str = ",".join(filters)
return filters_str
@staticmethod
def _remove_filter(filt, *arguments):
filters = list(filt.split(','))
for a in arguments:filters.append(a)
for n in range(len(filters)): filters[n] = str(filters[n]).strip()
filters_str = ",".join(filters)
return filters_str
class Transfer(BaseObj):
"""
Transfer Methods:
instance.destroy_transfer()
Transfer Attributes:
instance.id
instance.name
instance.status
instance.percent_done
human_size : 200.0M
name : The.Messenger.DVDRip.XviD-AMIABLE.part1.rar
url : http://rapidshare.com/files/3232/abc.part1.rar'
needs_pass : 0,
source : rapidshare.com',
paid_bw : 209715200',
error : None,
type : file',
size : 209715200'
Samples:
instance.status = 'Completed'
instance.percent_done = '100'
instance.id = '45'
instance.name = 'A video file.avi'
instance.status : 'Waiting'
instance.percent_done : '0'
instance.id : '47'
instance.name : 'abcde.mp4'
"""
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
def destroy_transfer(self):
"""
Destroys an active transfer irreversibly.
Example:
>>> instance.destroy_transfer()
"""
args = {"id":self.id}
result = _send(self.api, path="/transfers", post=args, method="cancel")
if result: return True
else: return None
def send_password(self):
"""TODO"""
pass
class UrlBucket(object):
"""
Url bucket is a completion of urls ready to fetch.
UrlBucket Methods:
bucket.add()
bucket.analyze_and_add_urls()
bucket.get_report()
bucket.fetch()
Static Methods:
extract_urls()
crawl_webpage()
Abilities:
* Add urls to the bucket
* Get report of the bucket if you need to.
* Fetch the urls
You can also:
* Analyze urls and add
* Crawl a web page and extract URLs
* Extract URLs from a text block
"""
def __init__(self, api, singleurl=[], torrenturl=[], multiparturl=[], **info):
self.api = api
self.report = {}
self.last_analyzed_bw_avail = None
self.last_analyzed_disk_avail = None
self.req_space = None
self.paid_bw = None
self.links = {"multiparturl":[], "torrenturl":[], "singleurl":[], "error":[]}
self._add(singleurl=singleurl, torrenturl=torrenturl, multiparturl=multiparturl)
def _add(self, singleurl=[], torrenturl=[], multiparturl=[], error=[], **info):
"""
Internal method. Use add() instead, or better yet use analyze().
"""
for v in [singleurl, torrenturl, multiparturl, error]:
if not isinstance(v, list):
raise PutioError("Add method takes only arrays.")
if len(multiparturl) > 0:
for m in multiparturl:
self.links['multiparturl'].append(m)
if len(torrenturl) > 0:
for i in torrenturl:
self.links['torrenturl'].append(i)
if len(singleurl) > 0:
for i in singleurl:
self.links['singleurl'].append(i)
if len(error) > 0:
for i in error:
self.links['error'].append(i)
if len(info.keys()) > 0:
self.last_analyzed_bw_avail = info["last_analyzed_bw_avail"]
self.last_analyzed_disk_avail = info["last_analyzed_disk_avail"]
self.req_space = info["req_space"]
self.paid_bw = info["paid_bw"]
return self
def add(self, url):
"""
Takes: String or array
Returns: Dictionary of bucket items
Adds url(s) to the bucket to analyze or fetch. We recommend using
analyze_and_add_urls() method.
"""
if url and isinstance(url, str):
arg = {"url":url}
self.links['singleurl'].append(Url(self.api, arg))
elif url and isinstance(url, list):
for u in url:
arg = {"url":url}
self.links['singleurl'].append(Url(self.api, arg))
else:
raise PutioError("Add method takes a string or a list")
return self.links
def get_report(self):
"""
Returns the report of your bucket's last analyzation. Each UrlBucket
has its own report. After updating a bucket, remember to check its
new report.
Analyzed urls return like this one:
{
'dl_handler': 1,
'name': 'Itemname.ext',
'file_type': 1,
'error': None,
'url': 'http://torrent.a.b/file.torrent',
'paid_bw': 0,
'file_size': 244091464,
'type_name': 'file',
'dltype': 2, #fixme whats this?
'human_size': '232.78M'
}
"""
return {"Current Available Disk Space": self.last_analyzed_disk_avail,
"Current Available Bandwidth": self.last_analyzed_bw_avail,
"Required Space": self.req_space,
"Bandwidth to be deducted from quota": self.paid_bw,
"Urls":self.links}
def fetch(self):
"""
Takes : A bucket instance
Returns: Array of transfers. (All the active transfers, to be exact.)
Initiates fetching all the links of the given URL Bucket instance.
Returns newly added transfers. Erroneous transfers will have the word
"Error" in "status".
Also you may edit below to raise exceptions, if result.error is not
false.
"""
go_fetch = []
for k in self.links.keys():
for i in self.links[k]:
if k != "error": go_fetch.append(i.url)
self.links = {}
args = {"links":go_fetch}
result = _send(self.api, path="/transfers", post=args, method="add")
transfers = []
if result:
for r in result:
transfers.append(Transfer(self.api, r))
return transfers
else:
return None
def analyze(self, links=None):
"""
Takes : An Array of URLs.
Returns: An Array of Link objects.
Example:
Link objects can be fetched one by one by iterating the array.
>>> links = bucket.analyze(array)
>>> for l in links: l.fetch()
or
The whole bucket can be fetched with a single request.
>>> bucket.analyze(array)
>>> mytransfers = bucket.fetch()
"""
multipart_urls = []
single_urls = []
torrent_urls = []
error_urls = []
paid_bw = 0
req_space = 0
#args = {"links":urllib.quote(text)}
if links and isinstance(links, list):
args = {"links":links}
elif links and isinstance(links, str):
raise PutioError("Analyze method takes a list. Use " + \
"extract_urls() to convert string of urls to a list.")
else:
go_analyze = []
for k in self.links.keys():
for i in self.links[k]:
if k != "error": go_analyze.append(i.url)
args = {"links":go_analyze}
result = _send(self.api, path="/urls", post=args, method="analyze")
if result:
if len(result['items']['multiparturl']) > 0:
for r in result['items']['multiparturl']:
# unicode to int
paid_bw += int(r['paid_bw'])
req_space += int(r['size'])
for p in r["parts"]:
multipart_urls.append(Multipart(self.api, p))
if len(result['items']['torrent']) > 0:
for r in result['items']['torrent']:
torrent_urls.append(Torrent(self.api, r))
paid_bw += int(r['paid_bw'])
req_space += int(r['size'])
if len(result['items']['singleurl']) > 0:
for r in result['items']['singleurl']:
single_urls.append(Url(self.api, r))
paid_bw += int(r['paid_bw'])
req_space += int(r['size'])
if len(result['items']['error']) > 0:
for r in result['items']['error']:
error_urls.append(Url(self.api, r))
self.links = {"multiparturl":[], "torrenturl":[],
"singleurl":[], "error":[]}
self._add(multiparturl=multipart_urls,
singleurl=single_urls,
torrenturl=torrent_urls,
error=error_urls,
last_analyzed_disk_avail=result['disk_avail'],
last_analyzed_bw_avail=result['bw_avail'],
paid_bw=paid_bw,
req_space=req_space
)
return self.get_report()
else:
return None
def crawl_webpage(self, url):
"""
Takes : A String of a web page URL
Returns: A String of cleaned url list
Example:
>>> newbucket = api.create_bucket()
>>> cleaned_urls = newbucket.crawl_webpage("http://a.b.c/downloads")
"""
return self.extract_urls(url)
def extract_urls(self, text):
"""
Takes : A String
Returns: A list of cleaned urls
Example:
>>> txt = "Check these out: http://a.b/c.torrent and ftp://a.b/c.rar"
>>> urls = bucket.extract_urls(txt)
>>> print urls
["http://a.b/c.torrent", "ftp://a.b/c.rar"]
Extracts URLs from a block of text.
* Links in the text are recognized by the protocol names such as http,
ftp, etc.
* Torrent file links do have to have .torrent extention.
"""
extracted_urls = []
args = {"txt":text}
result = _send(self.api, path="/urls", post=args, method="extracturls")
if result:
for r in result: extracted_urls.append(r['url'])
#return " \n".join(extracted_urls)
return extracted_urls
else:
return []
class Url(BaseObj):
"""
For us, Url is a downloadable file link. These files can be any type.
Sources can be ftp, http, etc.
Url Attributes:
instance.dl_handler = 'Single Url'
instance.name = 'name_of_the_file.mp4'
instance.file_type = 'file'
instance.error = None
instance.url = 'ftp://a.b.c/name_of_the_file.mp4'
instance.paid_bw = 0
instance.file_size = '92014'
instance.type_name = 'file'
instance.dltype = 3
instance.human_size = '89.86K'
"""
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
class Error(BaseObj):
"""
SingleRapid is a Rapidshare url, which is generally an archive file. If
the downloaded file have a multiple volume, it's a Multipart object.
Url Attributes:
instance.dl_handler = 'Rapid'
instance.name = 'ABCDE.rar'
instance.file_type = 'file'
instance.error = None
instance.url = 'http://rapidshare.com/files/abcde.rar'
instance.paid_bw = '104961263'
instance.file_size = '104961263'
instance.type_name = 'file'
instance.dltype = 1
instance.human_size = '100.10M'
"""
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
class Torrent(BaseObj):
"""
Torrent objects are fetched via our Torrent clients. It can contain
single, multi part archive files, or any other file type. Send the
torrent URL, we figure out the rest.
Torrent Attributes:
instance.dl_handler = 'Torrent'
instance.name = 'ABCDE.avi'
instance.file_type = 'file'
instance.file_size = 244091464
instance.url = 'http://a.b/c.torrent'
instance.paid_bw = 0
instance.error = None
instance.type_name = 'file'
instance.dltype = 2
instance.human_size = '232.78M'
"""
def __init__(self, api, dictionary=None, url=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
class Multipart(BaseObj):
"""
Multipart objects are files, which contains patterns like "part1",
"001", etc. These files are automatically extracted and saved to your
space.
Multipart Attributes:
instance.name = 'ABCDE'
instance.size = 366500388
instance.human_size = 350.0M
instance.paid_bw = 366500388
instance.parts = [
{
"url":"http:\/\/rapidshare.com\/files\/1\/M.part3.rar",
"size":"47711664",
"paid_bw":"47711664",
"source":"rapidshare.com",
"name":"Mdb35.part3.rar",
"human_size":"45.5M",
"type":"file",
"needs_pass":0,
"error":null
},
{
"url":"http:\/\/rapidshare.com\/files\/2\/M.part1.rar",
"size":"55000000",
"paid_bw":"55000000",
"source":"rapidshare.com",
"name":"Mdb35.part1.rar",
"human_size":"52.5M",
"type":"file",
"needs_pass":0,
"error":null
}
]
instance.error = None
"""
def __init__(self, api, dictionary=None, **args):
BaseObj.__init__(self, dictionary, **args)
self.api = api
if __name__ == '__main__':
print "This file should not be executed."
import putio
import urllib2
import base64
import utils
import os
import time
from datetime import timedelta
import datetime
import time
putio_uname = "your.put.io.username"
putio_secret_key = "your.put.io.secret.key"
movies_folder = "c:\media\movies"
series_folder = "c:\media\series"
series_downloads_folder = "c:\media\downloads"
def get_root_folder(api, folder_name):
folders = filter(lambda it: it.name==folder_name, api.get_items())
if len(movies_folder) == 0: return
return folders[0]
def download_file(url, file_name, target_dir):
file_path = target_dir + file_name
try:
base64string = base64.encodestring('%s:%s' % (putio_uname, putio_secret_key))[:-1]
req = urllib2.Request(url)
req.add_header("Authorization", "Basic %s" % base64string)
u = urllib2.urlopen(req)
f = open(file_path, 'wb')
meta = u.info()
file_size = int(meta.getheaders("Content-Length")[0])
print "Downloading: %s Bytes %s" % (file_name, file_size)
start_time = datetime.datetime.now()
print start_time
file_size_dl = 0
block_sz = 48 * 1024
while True:
buffer = u.read(block_sz)
if not buffer:
break
elapsed = datetime.datetime.now() - start_time
hours, remainder = divmod(elapsed.seconds, 3600)
minutes, seconds = divmod(remainder, 60)
elapsed_seconds = elapsed.seconds if elapsed.seconds > 0 else 1
size_per_second = (file_size_dl/1000) / elapsed_seconds
file_size_dl += len(buffer)
f.write(buffer)
status = r"%10d [%3.2f%%] - time since start '%sh%sm%ss' - %3.2fkB/s" % (file_size_dl, file_size_dl * 100. / file_size, hours, minutes, seconds, size_per_second)
status = status + chr(8)*(len(status)+1)
print status,
f.close()
except:
try:
os.remove(file_path)
except:
print "couldn't delete the file %s" % file_path
raise
def download_movie(api, mov):
print "downloading movie %s" % (mov.name)
if len(api.get_items(id=mov.id)) == 0:
print "the movie has been removed"
return False
friendly_movie_name = mov.name.replace("-", " ").strip().replace(".", "_").replace(" ", "_")
movie_target = movies_folder + "/" + friendly_movie_name + "/"
if not os.path.isdir(movie_target):
os.mkdir(movie_target)
print "created movie directory ", movie_target
if mov.type == "movie":
#download_file(mov.download_url, mov.name, movie_target)
download_file(mov.download_url, mov.name, movie_target)
return True
movie_files = filter(lambda it: not it.is_dir, api.get_items(parent_id=mov.id))
#utils.print_items(movie_files)
for it in movie_files:
download_file(it.download_url, it.name, movie_target)
return True
def download_movies(api):
movies_folder = get_root_folder(api, "movies")
new_movies = api.get_items(parent_id=movies_folder.id)
for mov in new_movies:
utils.reflect_item(mov)
if download_movie(api, mov):
mov.delete_item()
def download_series(api):
put_series_folder = get_root_folder(api, "series")
new_series = api.get_items(parent_id=put_series_folder.id)
print "found ", len(new_series), " series files, starting download ....."
for it in new_series:
if len(api.get_items(id=it.id)) == 0:
continue
download_file(it.download_url, it.name, series_downloads_folder + "/" )
it.delete_item()
if __name__=="__main__":
api = putio.Api(putio_uname, putio_secret_key)
while True:
try:
download_movies(api)
download_series(api)
#except Exception as inst:
# print "error occurred.."
# utils.reflect_item(inst)
finally:
time.sleep(10)
# todo: do not download sample.avi files
def print_items(items):
for it in items:
print it.id, it.name
reflect_item(it)
def reflect_item(item):
for property, value in vars(item).iteritems():
print "\t", property, ':', value
@bdmorin
Copy link

bdmorin commented May 19, 2020

i realize this is a blast from the past, but what library it import putio from?

@bachi76
Copy link

bachi76 commented May 30, 2022

I guess the credentials in credentials.txt aren't up to date anymore, but still... :)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment