Movie add

JSONP
This commit is contained in:
Ruud committed 2011-03-03 13:48:31 +01:00
1 parent f9523444f4
commit e39de40d2d
24 files changed
+871 -5029

No files matched your search

+15 -3
View File
@@ -1,5 +1,6 @@
from couchpotato.core.logger import CPLog
from axl.axel import Event
from couchpotato.core.logger import CPLog
import traceback
log = CPLog(__name__)
events = {}
@@ -21,7 +22,17 @@ def fireEvent(name, *args, **kwargs):
try:
e = events[name]
e.asynchronous = False
return e(*args, **kwargs)
result = e(*args, **kwargs)
results = []
for r in result:
if r[0] == True:
results.append(r[1])
else:
etype, value, tb = r[1]
log.debug(''.join(traceback.format_exception(etype, value, tb)))
return results
except Exception, e:
log.debug(e)
@@ -29,7 +40,8 @@ def fireEventAsync(name, *args, **kwargs):
try:
e = events[name]
e.asynchronous = True
return e(*args, **kwargs)
e(*args, **kwargs)
return True
except Exception, e:
log.debug(e)
-37
View File
@@ -1,37 +0,0 @@
def latinToAscii(unicrap):
xlate = {0xc0:'A', 0xc1:'A', 0xc2:'A', 0xc3:'A', 0xc4:'A', 0xc5:'A',
0xc6:'Ae', 0xc7:'C',
0xc8:'E', 0xc9:'E', 0xca:'E', 0xcb:'E', 0x86:'e',
0xcc:'I', 0xcd:'I', 0xce:'I', 0xcf:'I',
0xd0:'Th', 0xd1:'N',
0xd2:'O', 0xd3:'O', 0xd4:'O', 0xd5:'O', 0xd6:'O', 0xd8:'O',
0xd9:'U', 0xda:'U', 0xdb:'U', 0xdc:'U',
0xdd:'Y', 0xde:'th', 0xdf:'ss',
0xe0:'a', 0xe1:'a', 0xe2:'a', 0xe3:'a', 0xe4:'a', 0xe5:'a',
0xe6:'ae', 0xe7:'c',
0xe8:'e', 0xe9:'e', 0xea:'e', 0xeb:'e',
0xec:'i', 0xed:'i', 0xee:'i', 0xef:'i',
0xf0:'th', 0xf1:'n',
0xf2:'o', 0xf3:'o', 0xf4:'o', 0xf5:'o', 0xf6:'o', 0xf8:'o',
0xf9:'u', 0xfa:'u', 0xfb:'u', 0xfc:'u',
0xfd:'y', 0xfe:'th', 0xff:'y',
0xa1:'!', 0xa2:'{cent}', 0xa3:'{pound}', 0xa4:'{currency}',
0xa5:'{yen}', 0xa6:'|', 0xa7:'{section}', 0xa8:'{umlaut}',
0xa9:'{C}', 0xaa:'{^a}', 0xab:'<<', 0xac:'{not}',
0xad:'-', 0xae:'{R}', 0xaf:'_', 0xb0:'{degrees}',
0xb1:'{+/-}', 0xb2:'{^2}', 0xb3:'{^3}', 0xb4:"'",
0xb5:'{micro}', 0xb6:'{paragraph}', 0xb7:'*', 0xb8:'{cedilla}',
0xb9:'{^1}', 0xba:'{^o}', 0xbb:'>>',
0xbc:'{1/4}', 0xbd:'{1/2}', 0xbe:'{3/4}', 0xbf:'?',
0xd7:'*', 0xf7:'/'
}
r = ''
for i in unicrap:
if xlate.has_key(ord(i)):
r += xlate[ord(i)]
elif ord(i) >= 0x80:
pass
else:
r += str(i)
return r
+1
View File
@@ -0,0 +1 @@
+26
View File
@@ -0,0 +1,26 @@
from couchpotato.core.logger import CPLog
from string import ascii_letters, digits
import re
import unicodedata
log = CPLog(__name__)
def toSafeString(original):
valid_chars = "-_.() %s%s" % (ascii_letters, digits)
cleanedFilename = unicodedata.normalize('NFKD', toUnicode(original)).encode('ASCII', 'ignore')
return ''.join(c for c in cleanedFilename if c in valid_chars)
def simplifyString(original):
string = toSafeString(original)
split = re.split('\W+', string.lower())
return toUnicode(' '.join(split))
def toUnicode(original, *args):
try:
if type(original) is unicode:
return original
else:
return unicode(original, *args)
except UnicodeDecodeError:
ascii_text = str(original).encode('string_escape')
return unicode(ascii_text)
+22
View File
@@ -0,0 +1,22 @@
from flask.globals import current_app
from flask.helpers import json, jsonify
import flask
def getParams():
return getattr(flask.request, 'args')
def getParam(attr, default = None):
return getattr(flask.request, 'args').get(attr, default)
def padded_jsonify(callback, *args, **kwargs):
content = str(callback) + '(' + json.dumps(dict(*args, **kwargs)) + ')'
return current_app.response_class(content, mimetype = 'text/javascript')
def jsonified(*args, **kwargs):
from couchpotato.environment import Env
callback = getParam('json_callback', None)
if callback:
return padded_jsonify(callback, *args, **kwargs)
else:
return jsonify(*args, **kwargs)
+33
View File
@@ -0,0 +1,33 @@
from couchpotato.core.logger import CPLog
import xml.etree.ElementTree as XMLTree
log = CPLog(__name__)
class RSS():
def gettextelements(self, xml, path):
''' Find elements and return tree'''
textelements = []
try:
elements = xml.findall(path)
except:
return
for element in elements:
textelements.append(element.text)
return textelements
def gettextelement(self, xml, path):
''' Find element and return text'''
try:
return xml.find(path).text
except:
return
def getItems(self, data, path = 'channel/item'):
try:
return XMLTree.parse(data).findall(path)
except Exception, e:
log.error('Error parsing RSS. %s' % e)
return []
@@ -1,6 +1,5 @@
from couchpotato.api import addApiView
from couchpotato.environment import Env
from flask.helpers import jsonify
from couchpotato.core.helpers.request import getParam, jsonified
import os
import string
@@ -45,12 +44,12 @@ class FileBrowser():
def view(self):
try:
fb = FileBrowser(Env.getParam('path', '/'))
fb = FileBrowser(getParam('path', '/'))
dirs = fb.getDirectories()
except:
dirs = []
return jsonify({
return jsonified({
'empty': len(dirs) == 0,
'dirs': dirs,
})
+17 -11
View File
@@ -1,30 +1,36 @@
from couchpotato.api import addApiView
from couchpotato.core.event import getEvent, fireEvent
from couchpotato.core.event import fireEvent
from couchpotato.core.helpers.request import getParams, jsonified
from couchpotato.core.plugins.base import Plugin
from couchpotato.environment import Env
from flask.helpers import jsonify
class MovieAdd(Plugin):
def __init__(self):
addApiView('movie.add.search', self.search)
addApiView('movie.add.select', self.select)
def search(self):
a = Env.getParams()
a = getParams()
print fireEvent('provider.movie.search', q = a.get('q'))
results = fireEvent('provider.movie.search', q = a.get('q'))
movies = [
{'id': 1, 'name': 'test'}
]
# Combine movie results
movies = []
for r in results:
movies += r
return jsonify({
return jsonified({
'success': True,
'empty': len(movies) == 0,
'movies': movies,
})
def select(self):
pass
a = getParams()
return jsonified({
'success': True,
'added': True,
})
@@ -0,0 +1,6 @@
from couchpotato.core.plugins.movie_list.main import MovieList
def start():
return MovieList()
config = []
@@ -0,0 +1,41 @@
from couchpotato import get_session
from couchpotato.api import addApiView
from couchpotato.core.helpers.request import getParams, jsonified
from couchpotato.core.plugins.base import Plugin
from couchpotato.core.settings.model import Movie, Release
class MovieList(Plugin):
def __init__(self):
addApiView('movie.list', self.list)
def list(self):
a = getParams()
results = get_session().query(Movie).filter(
Movie.releases.any(
Release.status.has(identifier = 'wanted')
)
).all()
movies = []
for movie in results:
temp = {
'id': movie.id,
'name': movie.id,
'releases': [],
}
for release in movie.releases:
temp['releases'].append({
'status': release.status.label,
'quality': release.quality.label
})
movies.append(temp)
return jsonified({
'success': True,
'empty': len(movies) == 0,
'movies': movies,
})
-45
View File
@@ -1,9 +1,4 @@
from couchpotato.core.helpers import latinToAscii
from couchpotato.core.logger import CPLog
from string import ascii_letters, digits
import re
import unicodedata
import xml.etree.ElementTree as XMLTree
log = CPLog(__name__)
@@ -15,43 +10,3 @@ class Provider():
def __init__(self):
pass
def toSaveString(self, string):
string = latinToAscii(string)
string = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
safe_chars = ascii_letters + digits + '_ -.,\':!?'
r = ''.join([char if char in safe_chars else ' ' for char in string])
return re.sub('\s+' , ' ', r)
def toSearchString(self, string):
string = latinToAscii(string)
string = ''.join((c for c in unicodedata.normalize('NFD', unicode(string)) if unicodedata.category(c) != 'Mn'))
safe_chars = ascii_letters + digits + ' \''
r = ''.join([char if char in safe_chars else ' ' for char in string])
return re.sub('\s+' , ' ', r).replace('\'s', 's').replace('\'', ' ')
def gettextelements(self, xml, path):
''' Find elements and return tree'''
textelements = []
try:
elements = xml.findall(path)
except:
return
for element in elements:
textelements.append(element.text)
return textelements
def gettextelement(self, xml, path):
''' Find element and return text'''
try:
return xml.find(path).text
except:
return
def getItems(self, data, path = 'channel/item'):
try:
return XMLTree.parse(data).findall(path)
except Exception, e:
log.error('Error parsing RSS. %s' % e)
return []
+34 -29
View File
@@ -1,10 +1,12 @@
from __future__ import with_statement
from couchpotato.core.event import addEvent
from couchpotato.core.helpers.encoding import simplifyString, toUnicode
from couchpotato.core.logger import CPLog
from couchpotato.core.providers.base import Provider
from couchpotato.environment import Env
from urllib import quote_plus
import urllib2
import simplejson as json
log = CPLog(__name__)
@@ -21,7 +23,7 @@ class TMDB(Provider):
def conf(self, attr):
return Env.setting(attr, 'TheMovieDB')
def search(self, q, limit = 8, alternative = True):
def search(self, q, limit = 12, alternative = True):
''' Find movie by name '''
if self.isDisabled():
@@ -29,58 +31,61 @@ class TMDB(Provider):
log.debug('TheMovieDB - Searching for movie: %s' % q)
url = "%s/%s/%s/xml/%s/%s" % (self.apiUrl, 'Movie.search', 'en', self.conf('api_key'), quote_plus(self.toSearchString(q)))
url = "%s/%s/%s/json/%s/%s" % (self.apiUrl, 'Movie.search', 'en', self.conf('api_key'), quote_plus(simplifyString(q)))
log.info('Searching: %s' % url)
data = urllib2.urlopen(url)
jsn = json.load(data)
return self.parseXML(data, limit, alternative = alternative)
return self.parse(jsn, limit, alternative = alternative)
def parseXML(self, data, limit, alternative = True):
def parse(self, data, limit, alternative = True):
if data:
log.debug('TheMovieDB - Parsing RSS')
try:
xml = self.getItems(data, 'movies/movie')
results = []
nr = 0
for movie in xml:
id = int(self.gettextelement(movie, "id"))
for movie in data:
name = self.gettextelement(movie, "name")
imdb = self.gettextelement(movie, "imdb_id")
year = str(self.gettextelement(movie, "released"))[:4]
year = movie['released'][:4]
# Poster url
poster = ''
for p in movie['posters']:
p = p['image']
if(p['size'] == 'thumb'):
poster = p['url']
break
# 1900 is the same as None
if year == '1900':
year = 'None'
year = None
results.append({
'id': id,
'name': self.toSaveString(name),
'imdb': imdb,
'year': year
})
movie_data = {
'id': int(movie['id']),
'name': toUnicode(movie['name']),
'poster': poster,
'imdb': movie['imdb_id'],
'year': year,
'tagline': 'This is the tagline of the movie',
}
results.append(movie_data)
alternativeName = self.gettextelement(movie, "alternative_name")
alternativeName = movie['alternative_name']
if alternativeName and alternative:
if alternativeName.lower() != name.lower() and alternativeName.lower() != 'none' and alternativeName != None:
results.append({
'id': id,
'name': self.toSaveString(alternativeName),
'imdb': imdb,
'year': year
})
if alternativeName.lower() != movie['name'].lower() and alternativeName.lower() != 'none' and alternativeName != None:
movie_data['name'] = toUnicode(alternativeName)
results.append(movie_data)
nr += 1
if nr == limit:
break
#log.info('TheMovieDB - Found: %s' % results)
log.info('TheMovieDB - Found: %s' % [result['name'] + u' (' + str(result['year']) + ')' for result in results])
return results
except SyntaxError:
log.error('TheMovieDB - Failed to parse XML response from TheMovieDb')
except SyntaxError, e:
log.error('TheMovieDB - Failed to parse XML response from TheMovieDb: %s' % e)
return False
+4 -4
View File
@@ -1,7 +1,7 @@
from __future__ import with_statement
from couchpotato.api import addApiView
from couchpotato.core.event import addEvent
from flask.helpers import jsonify
from couchpotato.core.helpers.request import getParams, jsonified
import ConfigParser
import os.path
import time
@@ -107,14 +107,14 @@ class Settings():
def view(self):
return jsonify({
return jsonified({
'options': self.getOptions(),
'values': self.getValues()
})
def saveView(self):
a = Env.getParams()
a = getParams()
section = a.get('section')
option = a.get('name')
@@ -123,6 +123,6 @@ class Settings():
self.set(option, section, value)
self.save()
return jsonify({
return jsonified({
'success': True,
})
+84 -16
View File
@@ -2,7 +2,7 @@ from elixir.entity import Entity
from elixir.fields import Field
from elixir.options import options_defaults
from elixir.relationships import OneToMany, ManyToOne
from sqlalchemy.types import Integer, String, Unicode
from sqlalchemy.types import Integer, String, Unicode, UnicodeText, Boolean
options_defaults["shortnames"] = True
@@ -13,43 +13,111 @@ options_defaults["shortnames"] = True
# http://elixir.ematia.de/trac/wiki/Recipes/MultipleDatabasesOneMetadata
__session__ = None
class Resource(Entity):
"""Represents a resource of movies.
This resources can be online or offline."""
name = Field(Unicode(255))
path = Field(Unicode(255))
class Movie(Entity):
"""Movie Resource a movie could have multiple releases
The files belonging to the movie object are global for the whole movie
such as trailers, nfo, thumbnails"""
mooli_id = Field(Integer)
profile = ManyToOne('Profile')
releases = OneToMany('Release')
files = OneToMany('File')
class Release(Entity):
"""Logically groups all files that belong to a certain release, such as
parts of a movie, subtitles, nfo, trailers etc."""
files = OneToMany('File')
mooli_id = Field(Integer)
resource = ManyToOne('Resource')
parts of a movie, subtitles."""
movie = ManyToOne('Movie')
status = ManyToOne('Status')
quality = ManyToOne('Quality')
files = OneToMany('File')
history = OneToMany('History')
class Status(Entity):
"""The status of a release, such as Downloaded, Deleted, Wanted etc"""
identifier = Field(String(20), unique = True)
label = Field(String(20))
releases = OneToMany('Release')
class Quality(Entity):
"""Quality name of a release, DVD, 720P, DVD-Rip etc"""
identifier = Field(String(20), unique = True)
label = Field(String(20))
releases = OneToMany('Release')
profile_types = ManyToOne('ProfileType')
class Profile(Entity):
""""""
identifier = Field(String(20), unique = True)
label = Field(Unicode(50))
order = Field(Integer)
wait_for = Field(Integer)
movie = OneToMany('Movie')
profile_type = OneToMany('ProfileType')
class ProfileType(Entity):
""""""
order = Field(Integer)
mark_completed = Field(Boolean)
wait_for = Field(Integer)
type = OneToMany('Quality')
profile = ManyToOne('Profile')
class File(Entity):
"""File that belongs to a release."""
history = OneToMany('RenameHistory')
path = Field(Unicode(255), nullable = False, unique = True)
# Subtitles can have multiple parts, too
part = Field(Integer)
history = OneToMany('RenameHistory')
movie = ManyToOne('Movie')
release = ManyToOne('Release')
# Let's remember the size so we know about offline media.
size = Field(Integer, nullable = False)
type = ManyToOne('FileType')
properties = OneToMany('FileProperty')
class FileType(Entity):
"""Types could be trailer, subtitle, movie, partial movie etc."""
identifier = Field(String(20), unique = True)
name = Field(Unicode(255), nullable = False)
name = Field(Unicode(50), nullable = False)
files = OneToMany('File')
class FileProperty(Entity):
"""Properties that can be bound to a file for off-line usage"""
identifier = Field(String(20))
value = Field(Unicode(255), nullable = False)
file = ManyToOne('File')
class History(Entity):
"""History of actions that are connected to a certain release,
such as, renamed to, downloaded, deleted, download subtitles etc"""
message = Field(UnicodeText())
release = ManyToOne('Release')
class RenameHistory(Entity):
"""Remembers from where to where files have been moved."""
file = ManyToOne('File')
old = Field(String(255))
new = Field(String(255))
file = ManyToOne('File')