The branch, eden has been updated
       via  bea2237630b203c94c271bd76ad94c1f27292b18 (commit)
      from  87fc7b027d8ad999a513bd8e07fbb0ab424524d7 (commit)

- Log -----------------------------------------------------------------
http://xbmc.git.sourceforge.net/git/gitweb.cgi?p=xbmc/plugins;a=commit;h=bea2237630b203c94c271bd76ad94c1f27292b18

commit bea2237630b203c94c271bd76ad94c1f27292b18
Author: beenje <[email protected]>
Date:   Tue Jan 15 22:59:45 2013 +0100

    [plugin.video.academicearth] updated to version 1.3.3

diff --git a/plugin.video.academicearth/README.md 
b/plugin.video.academicearth/README.md
index ca0fef4..da1699c 100644
--- a/plugin.video.academicearth/README.md
+++ b/plugin.video.academicearth/README.md
@@ -1,6 +1,7 @@
 Academic Earth for XBMC
 =======================
-version 1.2.0
+
+[![Build 
Status](https://secure.travis-ci.org/jbeluch/xbmc-academic-earth.png)](http://travis-ci.org/jbeluch/xbmc-academic-earth)
 
 ### Summary
 
diff --git a/plugin.video.academicearth/addon.py 
b/plugin.video.academicearth/addon.py
index cf4104d..e08f191 100755
--- a/plugin.video.academicearth/addon.py
+++ b/plugin.video.academicearth/addon.py
@@ -15,8 +15,7 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 from operator import itemgetter
 from xbmcswift2 import Plugin
-from resources.lib.academicearth.api import (AcademicEarth, Subject, Course,
-                                             Lecture)
+from resources.lib.academicearth import api
 
 PLUGIN_NAME = 'Academic Earth'
 PLUGIN_ID = 'plugin.video.academicearth'
@@ -28,14 +27,35 @@ def show_index():
     items = [
         {'label': plugin.get_string(30200),
          'path': plugin.url_for('show_subjects')},
+
+        {'label': plugin.get_string(30201),
+         'path': plugin.url_for('show_universities')},
+
+        {'label': plugin.get_string(30202),
+         'path': plugin.url_for('show_speakers')},
     ]
     return items
 
 
[email protected]('/universities/')
+def show_universities():
+    ae = api.AcademicEarth()
+    unis = ae.get_universities()
+
+    items = [{
+        'label': uni.name,
+        'path': plugin.url_for('show_university_info', url=uni.url),
+        'icon': uni.icon,
+    } for uni in unis]
+
+    sorted_items = sorted(items, key=lambda item: item['label'])
+    return sorted_items
+
+
 @plugin.route('/subjects/')
 def show_subjects():
-    api = AcademicEarth()
-    subjects = api.get_subjects()
+    ae = api.AcademicEarth()
+    subjects = ae.get_subjects()
 
     items = [{
         'label': subject.name,
@@ -46,20 +66,37 @@ def show_subjects():
     return sorted_items
 
 
[email protected]('/subjects/<url>/')
-def show_subject_info(url):
-    subject = Subject.from_url(url)
[email protected]('/speakers/')
+def show_speakers():
+    ae = api.AcademicEarth()
+    speakers = ae.get_speakers()
+
+    items = [{
+        'label': speaker.name,
+        'path': plugin.url_for('show_speaker_info', url=speaker.url),
+    } for speaker in speakers]
+
+    sorted_items = sorted(items, key=lambda item: item['label'])
+    return sorted_items
+
+
[email protected]('/subjects/<url>/', 'show_subject_info', {'cls': api.Subject})
[email protected]('/universities/<url>/', 'show_university_info',
+              {'cls': api.University})
[email protected]('/speakers/<url>/', 'show_speaker_info', {'cls': api.Speaker})
+def show_info(url, cls):
+    uni = cls.from_url(url)
 
     courses = [{
         'label': course.name,
         'path': plugin.url_for('show_course_info', url=course.url),
-    } for course in subject.courses]
+    } for course in uni.courses]
 
     lectures = [{
         'label': 'Lecture: %s' % lecture.name,
         'path': plugin.url_for('play_lecture', url=lecture.url),
         'is_playable': True,
-    } for lecture in subject.lectures]
+    } for lecture in uni.lectures]
 
     by_label = itemgetter('label')
     items = sorted(courses, key=by_label) + sorted(lectures, key=by_label)
@@ -68,7 +105,7 @@ def show_subject_info(url):
 
 @plugin.route('/courses/<url>/')
 def show_course_info(url):
-    course = Course.from_url(url)
+    course = api.Course.from_url(url)
     lectures = [{
         'label': 'Lecture: %s' % lecture.name,
         'path': plugin.url_for('play_lecture', url=lecture.url),
@@ -80,8 +117,9 @@ def show_course_info(url):
 
 @plugin.route('/lectures/<url>/')
 def play_lecture(url):
-    lecture = Lecture.from_url(url)
-    url = 'plugin://plugin.video.youtube/?action=play_video&videoid=%s' % 
lecture.youtube_id
+    lecture = api.Lecture.from_url(url)
+    ptn = 'plugin://plugin.video.youtube/?action=play_video&videoid=%s'
+    url = ptn % lecture.youtube_id
     plugin.log.info('Playing url: %s' % url)
     plugin.set_resolved_url(url)
 
diff --git a/plugin.video.academicearth/addon.xml 
b/plugin.video.academicearth/addon.xml
index d3c2ee7..572651a 100644
--- a/plugin.video.academicearth/addon.xml
+++ b/plugin.video.academicearth/addon.xml
@@ -1,15 +1,16 @@
-<addon id="plugin.video.academicearth" name="Academic Earth" 
provider-name="Jonathan Beluch (jbel)" version="1.3.1">
+<addon id="plugin.video.academicearth" name="Academic Earth" 
provider-name="Jonathan Beluch (jbel)" version="1.3.3">
   <requires>
     <import addon="xbmc.python" version="2.0" />
-    <import addon="script.module.beautifulsoup" version="3.0.8" />
-    <import addon="script.module.xbmcswift2" version="1.1.1" />
-    <import addon="plugin.video.youtube" version="3.1.0" />
+    <import addon="script.module.beautifulsoup" version="3.2.0" />
+    <import addon="script.module.xbmcswift2" version="1.3.1" />
+    <import addon="plugin.video.youtube" version="3.4.1" />
   </requires>
   <extension library="addon.py" point="xbmc.python.pluginsource">
     <provides>video</provides>
   </extension>
   <extension point="xbmc.addon.metadata">
     <platform>all</platform>
+    <language />
     <summary>Watch lectures from Academic Earth 
(http://academicearth.org)</summary>
     <description>Browse online courses and lectures from the world's top 
scholars.</description>
   </extension>
diff --git a/plugin.video.academicearth/changelog.txt 
b/plugin.video.academicearth/changelog.txt
index 3a689e6..8f86539 100644
--- a/plugin.video.academicearth/changelog.txt
+++ b/plugin.video.academicearth/changelog.txt
@@ -1,3 +1,9 @@
+Version 1.3.3
+* Add missing language tag to addon.xml.
+
+Version 1.3.2
+* Update addon to reflect website changes.
+
 Version 1.3.1
 * New version number to force updates
 
diff --git a/plugin.video.academicearth/resources/lib/academicearth/api.py 
b/plugin.video.academicearth/resources/lib/academicearth/api.py
index dc17d6c..df8b346 100644
--- a/plugin.video.academicearth/resources/lib/academicearth/api.py
+++ b/plugin.video.academicearth/resources/lib/academicearth/api.py
@@ -1,14 +1,11 @@
 '''
-
     academicearth.api
     ~~~~~~~~~~~~~~~~~
 
     This module contains the API classes and method to parse information from
     the Academic Earth website.
-
 '''
-from scraper import (get_subjects, get_courses, get_subject_metadata,
-                     get_course_metadata, get_lecture_metadata)
+import scraper
 
 
 class AcademicEarth(object):
@@ -21,121 +18,88 @@ class AcademicEarth(object):
 
     def get_subjects(self):
         '''Returns a list of subjects available on the website.'''
-        return [Subject(**info) for info in get_subjects()]
+        return [Subject(info, partial=True) for info
+                in scraper.Subject.get_subjects_partial()]
 
+    def get_universities(self):
+        '''Returns a list of universities available on the website.'''
+        return [University(info, partial=True) for info
+                in scraper.University.get_universities_partial()]
 
-class Subject(object):
-    '''Object representing an Academic Earth subject.'''
+    def get_speakers(self):
+        '''Returns a list of speakers available on the website.'''
+        return [Speaker(info, partial=True) for info
+                in scraper.Speaker.get_speakers_partial()]
 
-    def __init__(self, url, name=None):
-        self.url = url
-        self._name = name
-        self._courses = None
-        self._lectures = None
-        self._loaded = False
 
-    @classmethod
-    def from_url(cls, url):
-        return cls(url=url)
+class _BaseAPIObject(object):
+
+    scraper_cls = scraper.University
+
+    def __init__(self, info, partial=False):
+        self._setattrs(info)
+        self.partial = partial
+
+    def __getattr__(self, name):
+        if self.partial:
+            self.load()
+            return getattr(self, name)
+        raise AttributeError, '%s instance has no attribute %s' % 
(self.__class__.__name__, name)
 
     def __repr__(self):
-        return u"<Subject '%s'>" % self.name
-
-    def _load_metadata(self):
-        resp = get_subject_metadata(self.url)
-        if not self._name:
-            self._name = resp['name']
-        self._courses = [Course(**info) for info in resp['courses']]
-        self._lectures = [Lecture(**info) for info in resp['lectures']]
-        self._description = resp['description']
-        self._loaded = True
-
-    @property
-    def name(self):
-        '''Subject name'''
-        if not self._name:
-            self._load_metadata()
-        return self._name
-        
-    @property
-    def courses(self):
-        '''List of courses available for this subject'''
-        if not self._loaded:
-            self._load_metadata()
-        return self._courses
-
-    @property
-    def lectures(self):
-        '''List of lectures available for this subject'''
-        if not self._loaded:
-            self._load_metadata()
-        return self._lectures
-
-
-class Course(object):
-
-    def __init__(self, url, name=None, **kwargs):
-        self.url = url
-        self._name = name
-        self._loaded = False
-        self._lectures = None
+        return u"<%s '%s'>" % (self.__class__.__name__, self.name)
+
+    def _setattrs(self, info):
+        for attr_name, attr_value in info.items():
+            setattr(self, attr_name, attr_value)
+
+    def load(self):
+        full_info = self.scraper_cls.from_url(url)
+        self._setattrs(info)
+        self.partial = False
 
     @classmethod
     def from_url(cls, url):
-        return cls(url=url)
+        info = cls.scraper_cls.from_url(url)
+        return cls(info)
 
-    def __repr__(self):
-        return u"<Course '%s'>" % self.name
+class _ObjectWithCoursesLectures(_BaseAPIObject):
 
-    def _load_metadata(self):
-        resp = get_course_metadata(self.url)
-        if not self._name:
-            self._name = resp['name']
-        self._lectures = [Lecture(**info) for info in resp['lectures']]
-        self._loaded = True
+    def _setattrs(self, info):
+        for attr_name, attr_value in info.items():
+            if attr_name == 'courses':
+                setattr(self, attr_name, [Course(info) for info in attr_value])
+            elif attr_name == 'lectures':
+                setattr(self, attr_name, [Lecture(info) for info in 
attr_value])
+            else:
+                setattr(self, attr_name, attr_value)
 
-    @property
-    def name(self):
-        if not self._name:
-            self._load_metadata()
-        return self._name
+class Subject(_ObjectWithCoursesLectures):
 
-    @property
-    def lectures(self):
-        if not self._loaded:
-            self._load_metadata()
-        return self._lectures
+    scraper_cls = scraper.Subject
 
 
-class Lecture(object):
+class University(_ObjectWithCoursesLectures):
 
-    def __init__(self, url, name=None, **kwargs):
-        self.url = url
-        self._name = name
-        self._loaded = False
+    scraper_cls = scraper.University
 
-    @classmethod
-    def from_url(cls, url):
-        return cls(url=url)
 
-    def __repr__(self):
-        return u"<Lecture '%s'>" % self.name
-
-    def _load_metadata(self):
-        resp = get_lecture_metadata(self.url)
-        if not self._name:
-            self._name = resp['name']
-        self._youtube_id = resp['youtube_id']
-        self._loaded = True
-
-    @property
-    def name(self):
-        if not self._name:
-            self._load_metadata()
-        return self._name
-
-    @property
-    def youtube_id(self):
-        if not self._loaded:
-            self._load_metadata()
-        return self._youtube_id
+class Speaker(_ObjectWithCoursesLectures):
+
+    scraper_cls = scraper.Speaker
+
+
+class Course(_BaseAPIObject):
+
+    scraper_cls = scraper.Course
+
+    def _setattrs(self, info):
+        for attr_name, attr_value in info.items():
+            if attr_name == 'lectures':
+                setattr(self, attr_name, [Lecture(info) for info in 
attr_value])
+            else:
+                setattr(self, attr_name, attr_value)
+
+
+class Lecture(_BaseAPIObject):
+    scraper_cls = scraper.Lecture
diff --git a/plugin.video.academicearth/resources/lib/academicearth/scraper.py 
b/plugin.video.academicearth/resources/lib/academicearth/scraper.py
index 1e14ec7..334423c 100644
--- a/plugin.video.academicearth/resources/lib/academicearth/scraper.py
+++ b/plugin.video.academicearth/resources/lib/academicearth/scraper.py
@@ -4,22 +4,27 @@
 
     This module contains some functions which do the website scraping for the
     API module. You shouldn't have to use this module directly.
+
+    This module is meant to emulate responses from a "virtual" API server. All
+    website scraping is handled in this module, and clean dict responses are
+    returned. The `api` module, acts as a python client library for this
+    module's API.
 '''
 import re
-from urllib2 import urlopen
-from urlparse import urljoin
+import urllib
+import urlparse
 from BeautifulSoup import BeautifulSoup as BS
 
 
 BASE_URL = 'http://www.academicearth.org'
 def _url(path):
-    '''Returns a full url for the given path'''            
-    return urljoin(BASE_URL, path)
+    '''Returns a full url for the given path'''
+    return urlparse.urljoin(BASE_URL, path)
 
 
 def get(url):
     '''Performs a GET request for the given url and returns the response'''
-    conn = urlopen(url)
+    conn = urllib.urlopen(url)
     resp = conn.read()
     conn.close()
     return resp
@@ -36,68 +41,208 @@ def make_showall_url(url):
     '''
     if not url.endswith('/'):
         url += '/'
-    return url + 'page:1/show:500'
-
-
-def get_subjects():
-    '''Returns a list of subjects for the website. Each subject is a dict with
-    keys of 'name' and 'url'.
-    '''
-    url = _url('subjects')
-    html = _html(url)
-    subjs = html.findAll('a',
-        {'href': lambda attr_value: attr_value.startswith('/subjects/')
-                                    and len(attr_value) > len('/subjects/')})
-
-    # subjs will contain some duplicates so we will key on url
-    items = []
-    urls = set()
-    for subj in subjs:
-        url = _url(subj['href'])
-        if url not in urls:
-            urls.add(url)
-            items.append({
-                'name': subj.string,
-                'url': url,
-            })
-
-    # filter out any items that didn't parse correctly
-    return [item for item in items if item['name'] and item['url']]
-
-
-def get_subject_metadata(subject_url):
-    '''Returns metadata for a subject parsed from the given url'''
-    html = _html(make_showall_url(subject_url))
-    name = get_subject_name(html)
-    courses = get_courses(html)
-    lectures = get_lectures(html)
-    desc = get_subject_description(html)
-
-    return {
-        'name': name,
-        'courses': courses,
-        'lectures': lectures,
-        'description': desc,
-    }
-
-
-def get_subject_name(html):
-    return html.find('article').h1.text
-
-
-def get_course_name(html):
-    return html.find('section', {'class': 'pagenav'}).span.text
+    return url + 'page:1/show:1000'
+
+
+class University(object):
+
+    listing_url = make_showall_url(_url('universities'))
+
+    @classmethod
+    def get_universities_partial(cls):
+        '''Returns a list of universities available on the website.'''
+        html = _html(cls.listing_url)
+
+        parent = html.find('div', {'class': 'lectureVideosIndex'}).div
+        unis = parent.findAll('div')
+
+        return [{
+            'name': uni.findAll('a')[-1].string.strip(),
+            'url': _url(uni.a['href']),
+            'icon': uni.img['src'],
+        } for uni in unis]
+
+    @classmethod
+    def from_url(cls, url):
+        '''Returns metadata for a university parsed from the given url'''
+        html = _html(make_showall_url(url))
+        name = cls.get_name(html)
+        desc = cls.get_description(html)
+        courses = cls.get_courses(html)
+        lectures = cls.get_lectures(html)
+
+        return {
+            'name': name,
+            'courses': courses,
+            'lectures': lectures,
+            'description': desc,
+        }
+
+    @staticmethod
+    def get_name(html):
+        return html.find('div', {'class': 'title'}).h1.text
+
+    @staticmethod
+    def get_description(html):
+        desc_nodes = html.find('div', {'class': 
'courseDetails'}).findAll('span')
+        return '\n'.join(node.text.strip() for node in desc_nodes)
+
+    @staticmethod
+    def get_courses(html):
+        return _get_courses_or_lectures('course', html)
+
+    @staticmethod
+    def get_lectures(html):
+        return _get_courses_or_lectures('lecture', html)
+
+
+class Subject(object):
+
+    listing_url = _url('subjects')
+
+    @classmethod
+    def get_subjects_partial(cls):
+        '''Returns a list of subjects for the website. Each subject is a dict 
with
+        keys of 'name' and 'url'.
+        '''
+        html = _html(cls.listing_url)
+        subjs = html.findAll('a',
+            {'href': lambda attr_value: attr_value.startswith('/subjects/')
+                                        and len(attr_value) > 
len('/subjects/')})
+
+        # subjs will contain some duplicates so we will key on url
+        items = []
+        urls = set()
+        for subj in subjs:
+            url = _url(subj['href'])
+            if url not in urls:
+                urls.add(url)
+                items.append({
+                    'name': subj.string,
+                    'url': url,
+                })
+
+        # filter out any items that didn't parse correctly
+        return [item for item in items if item['name'] and item['url']]
+
+    @classmethod
+    def from_url(cls, url):
+        '''Returns metadata for a subject parsed from the given url'''
+        html = _html(make_showall_url(url))
+        name = cls.get_name(html)
+        desc = cls.get_description(html)
+        courses = cls.get_courses(html)
+        lectures = cls.get_lectures(html)
+
+        return {
+            'name': name,
+            'courses': courses,
+            'lectures': lectures,
+            'description': desc,
+        }
+
+    @staticmethod
+    def get_name(html):
+        return html.find('article').h1.text
+
+    @staticmethod
+    def get_description(html):
+        return html.find('article').p.text
+
+    @staticmethod
+    def get_courses(html):
+        return _get_courses_or_lectures('course', html)
+
+    @staticmethod
+    def get_lectures(html):
+        return _get_courses_or_lectures('lecture', html)
+
+
+class Speaker(object):
+
+    listing_url = make_showall_url(_url('speakers'))
+
+    @classmethod
+    def get_speakers_partial(cls):
+        '''Returns a list of speakers available on the website.'''
+        html = _html(cls.listing_url)
+        speakers = html.findAll('div', {'class': 'blue-hover'})
+        return [{
+            'name': spkr.div.string.strip(),
+            'url': _url(spkr.a['href']),
+        } for spkr in speakers]
+
+    @classmethod
+    def from_url(cls, url):
+        '''Returns metadata for a speaker parsed from the given url'''
+        html = _html(make_showall_url(url))
+        name = cls.get_name(html)
+        courses = cls.get_courses(html)
+        lectures = cls.get_lectures(html)
+
+        return {
+            'name': name,
+            'courses': courses,
+            'lectures': lectures,
+        }
+
+    @staticmethod
+    def get_name(html):
+        return html.find('div', {'class': 'title'}).h1.text
+
+    @staticmethod
+    def get_courses(html):
+        return _get_courses_or_lectures('course', html)
+
+    @staticmethod
+    def get_lectures(html):
+        return _get_courses_or_lectures('lecture', html)
+
+
+class Course(object):
+
+    @classmethod
+    def from_url(cls, url):
+        html = _html(make_showall_url(url))
+        lectures = cls.get_lectures(html)
+        name = cls.get_name(html)
+        return {
+            'lectures': lectures,
+            'name': name,
+        }
+
+    @staticmethod
+    def get_name(html):
+        return html.find('section', {'class': 'pagenav'}).span.text
+
+    @staticmethod
+    def get_lectures(html):
+        return _get_courses_or_lectures('lecture', html)
+
+
+class Lecture(object):
+
+    @classmethod
+    def from_url(cls, url):
+        html = _html(url)
+        name = cls.get_name(html)
+        youtube_id = cls.parse_youtube_id(html)
+        return {
+            'name': name,
+            'youtube_id': youtube_id
+        }
+
+    @staticmethod
+    def get_name(html):
+        return html.find('section', {'class': 'pagenav'}).span.text
+
+    @staticmethod
+    def parse_youtube_id(html):
+        url = html.find('a',
+                        href=lambda h: h.startswith('http://www.youtube.com'))
+        return url['href'].split('=')[1]
 
 
-def get_lecture_name(html):
-    return html.find('section', {'class': 'pagenav'}).span.text
-
-
-def get_subject_description(html):
-    desc_nodes = html.find('article').findAll('span')
-    return '\n'.join(node.text.strip() for node in desc_nodes)
-    
-
 def _get_courses_or_lectures(class_type, html):
     '''class_type can be 'course' or 'lecture'.'''
     nodes = html.findAll('div', {'class': class_type})
@@ -111,41 +256,3 @@ def _get_courses_or_lectures(class_type, html):
     } for node in nodes]
 
     return items
-
-
-def get_lectures(html):
-    return _get_courses_or_lectures('lecture', html)
-
-
-def get_courses(html):
-    return _get_courses_or_lectures('course', html)
-
-
-def get_course_metadata(course_url):
-    html = _html(make_showall_url(course_url))
-    lectures = get_lectures(html)
-    name = get_course_name(html)
-    return {
-        'lectures': lectures,
-        'name': name,
-    }
-
-
-def get_lecture_metadata(lecture_url):
-    html = _html(lecture_url)
-    name = get_lecture_name(html)
-    youtube_id = parse_youtube_id(html)
-    return {
-        'name': name,
-        'youtube_id': youtube_id        
-    }
-    
-
-
-def parse_youtube_id(html):
-    embed = html.find('embed')
-    yt_ptn = re.compile(r'http://www.youtube.com/v/(.+?)\?')
-    match = yt_ptn.search(embed['src'])
-    if match:
-        return match.group(1)
-    return None

-----------------------------------------------------------------------

Summary of changes:
 .../.travis.yml                                    |    0
 plugin.video.academicearth/README.md               |    3 +-
 plugin.video.academicearth/addon.py                |   62 +++-
 plugin.video.academicearth/addon.xml               |    9 +-
 plugin.video.academicearth/changelog.txt           |    6 +
 .../requirements.txt                               |    3 +-
 .../resources/lib/academicearth/api.py             |  172 +++++-------
 .../resources/lib/academicearth/scraper.py         |  313 +++++++++++++-------
 .../resources/lib/academicearth/test_scraper.py    |   55 ++++
 .../resources/tests}/__init__.py                   |    0
 .../resources/tests/test_addon.py                  |   91 ++++++
 .../resources/tests/test_api.py                    |   18 ++
 12 files changed, 506 insertions(+), 226 deletions(-)
 copy {plugin.video.vimcasts => plugin.video.academicearth}/.travis.yml (100%)
 copy {plugin.video.classiccinema => 
plugin.video.academicearth}/requirements.txt (73%)
 create mode 100644 
plugin.video.academicearth/resources/lib/academicearth/test_scraper.py
 copy {plugin.audio.radio_de/resources => 
plugin.video.academicearth/resources/tests}/__init__.py (100%)
 create mode 100644 plugin.video.academicearth/resources/tests/test_addon.py
 create mode 100644 plugin.video.academicearth/resources/tests/test_api.py


hooks/post-receive
-- 
Plugins

------------------------------------------------------------------------------
Master SQL Server Development, Administration, T-SQL, SSAS, SSIS, SSRS
and more. Get SQL Server skills now (including 2012) with LearnDevNow -
200+ hours of step-by-step video tutorials by Microsoft MVPs and experts.
SALE $99.99 this month only - learn more at:
http://p.sf.net/sfu/learnmore_122512
_______________________________________________
Xbmc-addons mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/xbmc-addons

Reply via email to