Example #1
0
    def explore(self, page=1, **kwargs):
        """Display the most recent 15 media.

        :rtype: Dict
        :returns:
            latest
                Latest media
            popular
                Latest media

        """
        media = Media.query.published()\
            .options(orm.undefer('comment_count_published'))

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())
        featured = None

        featured_cat = helpers.get_featured_category()
        if featured_cat:
            featured = latest.in_category(featured_cat).first()
        if not featured:
            featured = popular.first()

        latest = latest.exclude(featured)[:8]
        popular = popular.exclude(featured, latest)[:5]

        return dict(
            featured = featured,
            latest = latest,
            popular = popular,
        )
Example #2
0
    def tags(self, **kwargs):
        """Display a listing of all tags."""
        tags = Tag.query\
            .options(orm.undefer('media_count_published'))\
            .filter(Tag.media_count_published > 0)
        media = Media.query.published()
 	featured_cat = helpers.get_featured_category()
       
        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())
        recommended = latest.in_category(featured_cat)[:8]
        featured = None

        if featured_cat:
            featured = latest.in_category(featured_cat).first()
        if not featured:
            featured = popular.first()

        latest = latest.exclude(featured)[:8]
        popular = popular.exclude(featured, latest)[:8]    
        return dict(
            tags = tags,
            featured = featured,
	    latest = latest,
	    popular = popular,
	    recommended = recommended,
            categories = Category.query.populated_tree(),
        )
Example #3
0
    def index(self, slug=None, **kwargs):
        media = Media.query.published()\
            .options(orm.undefer('comment_count_published'))

        if c.category:
            media = media.in_category(c.category)

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())
        featured = None

        featured_cat = get_featured_category()
        if featured_cat:
            featured = latest.in_category(featured_cat).first()
        if not featured:
            featured = popular.first()

        latest = latest.exclude(featured)[:5]
        popular = popular.exclude(latest, featured)[:5]

        return dict(
            featured=featured,
            latest=latest,
            popular=popular,
        )
Example #4
0
    def credentials(self, **kwargs):
        """Display a listing of all tags.""" 
        media = Media.query.published()

        featured_cat = helpers.get_featured_category()
        
        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())
        recommended = latest.in_category(featured_cat)[:8]
        featured = None

        if featured_cat:
            featured = latest.in_category(featured_cat).first()
        if not featured:
            featured = popular.first()

        latest = latest.exclude(featured)[:8]
        popular = popular.exclude(featured, latest)[:8]    
        return dict( 
            featured = featured,
	    latest = latest,
	    popular = popular,
	    recommended = recommended,
            categories = Category.query.populated_tree(),
        )         
Example #5
0
    def explore(self, **kwargs):
        """Display the most recent 15 media.

        :rtype: Dict
        :returns:
            latest
                Latest media
            popular
                Latest media

        """
        media = Media.query.published()

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())

        featured = None
        featured_cat = helpers.get_featured_category()
        if featured_cat:
            featured = viewable_media(latest.in_category(featured_cat)).first()
        if not featured:
            featured = viewable_media(popular).first()

        latest = viewable_media(latest.exclude(featured))[:8]
        popular = viewable_media(popular.exclude(featured, latest))[:5]

        return dict(featured=featured, latest=latest, popular=popular, categories=Category.query.populated_tree())
Example #6
0
    def explore(self, page=1, **kwargs):
        """Display the most recent 15 media.

        :rtype: Dict
        :returns:
            latest
                Latest media
            popular
                Latest media

        """
        media = Media.query.published()

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())
        featured = None

        featured_cat = helpers.get_featured_category()
        if featured_cat:
            featured = latest.in_category(featured_cat).first()
        if not featured:
            featured = popular.first()

        latest = latest.exclude(featured)[:8]
        popular = popular.exclude(featured, latest)[:5]

        return dict(
            featured=featured,
            latest=latest,
            popular=popular,
            categories=Category.query.populated_tree(),
        )
Example #7
0
    def featured(self, limit=None, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of the sites featured media."""
        if request.settings["rss_display"] != "True":
            abort(404)

        response.content_type = content_type_for_response(["application/rss+xml", "application/xml", "text/xml"])

        media = Media.query.in_category(get_featured_category()).published().order_by(Media.publish_on.desc())
        if limit is not None:
            media = media.limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(media=media, title="Featured Media")
Example #8
0
    def featured(self, limit=30, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of the sites featured media."""
        if app_globals.settings["rss_display"] != "True":
            abort(404)

        response.content_type = mimeparse.best_match(
            ["application/rss+xml", "application/xml", "text/xml"], request.environ.get("HTTP_ACCEPT", "*/*")
        )

        media = Media.query.in_category(get_featured_category()).order_by(Media.publish_on.desc()).limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(media=media, title="Featured Media")
Example #9
0
    def explore(self, **kwargs):
        """Display the most recent 15 media.

        :rtype: Dict
        :returns:
            latest
                Latest media
            popular
                Latest media

        """
        media = Media.query.published()

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())

        featured = None
        featured_cat = helpers.get_featured_category()
        if featured_cat:
            featured = viewable_media(latest.in_category(featured_cat)).first()
        if not featured:
            featured = viewable_media(popular).first()

        latest = viewable_media(latest.exclude(featured))[:8]
        popular = viewable_media(popular.exclude(featured, latest))[:5]
        if request.settings['sitemaps_display'] == 'True':
            response.feed_links.extend([
                (url_for(controller='/sitemaps',
                         action='google'), u'Sitemap XML'),
                (url_for(controller='/sitemaps',
                         action='mrss'), u'Sitemap RSS'),
            ])
        if request.settings['rss_display'] == 'True':
            response.feed_links.extend([
                (url_for(controller='/sitemaps',
                         action='latest'), u'Latest RSS'),
            ])

        return dict(
            featured=featured,
            latest=latest,
            popular=popular,
            categories=Category.query.populated_tree(),
        )
Example #10
0
    def featured(self, limit=None, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of the sites featured media."""
        if request.settings['rss_display'] != 'True':
            abort(404)

        response.content_type = content_type_for_response(
            ['application/rss+xml', 'application/xml', 'text/xml'])

        media_query = Media.query.in_category(get_featured_category())\
            .published()\
            .order_by(Media.publish_on.desc())
        media = viewable_media(media_query).limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(
            media=media,
            title='Featured Media',
        )
Example #11
0
    def featured(self, limit=None, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of the sites featured media."""
        if request.settings['rss_display'] != 'True':
            abort(404)

        response.content_type = content_type_for_response(
            ['application/rss+xml', 'application/xml', 'text/xml'])

        media_query = Media.query.in_category(get_featured_category())\
            .published()\
            .order_by(Media.publish_on.desc())
        media = viewable_media(media_query).limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(
            media = media,
            title = 'Featured Media',
        )
Example #12
0
    def featured(self, limit=30, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of the sites featured media."""
        if request.settings['rss_display'] != 'True':
            abort(404)

        response.content_type = mimeparse.best_match(
            ['application/rss+xml', 'application/xml', 'text/xml'],
            request.environ.get('HTTP_ACCEPT', '*/*'))

        media = Media.query.in_category(get_featured_category())\
            .order_by(Media.publish_on.desc())\
            .limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(
            media=media,
            title='Featured Media',
        )
Example #13
0
    def explore(self, **kwargs):
        """Display the most recent 15 media.

        :rtype: Dict
        :returns:
            latest
                Latest media
            popular
                Latest media

        """
        media = Media.query.published()

        latest = media.order_by(Media.publish_on.desc())
        popular = media.order_by(Media.popularity_points.desc())

        featured = None
        featured_cat = helpers.get_featured_category()
        if featured_cat:
            featured = viewable_media(latest.in_category(featured_cat)).first()
        if not featured:
            featured = viewable_media(popular).first()

        latest = viewable_media(latest.exclude(featured))[:8]
        popular = viewable_media(popular.exclude(featured, latest))[:5]
        if request.settings['sitemaps_display'] == 'True':
            response.feed_links.extend([
                (url_for(controller='/sitemaps', action='google'), u'Sitemap XML'),
                (url_for(controller='/sitemaps', action='mrss'), u'Sitemap RSS'),
            ])
        if request.settings['rss_display'] == 'True':
            response.feed_links.extend([
                (url_for(controller='/sitemaps', action='latest'), u'Latest RSS'),
            ])

        return dict(
            featured = featured,
            latest = latest,
            popular = popular,
            categories = Category.query.populated_tree(),
        )
Example #14
0
    def featured(self, limit=30, skip=0, **kwargs):
        """Generate a media rss (mRSS) feed of the sites featured media."""
        if request.settings['rss_display'] != 'True':
            abort(404)

        response.content_type = mimeparse.best_match(
            ['application/rss+xml', 'application/xml', 'text/xml'],
            request.environ.get('HTTP_ACCEPT', '*/*')
        )

        media = Media.query.in_category(get_featured_category())\
            .order_by(Media.publish_on.desc())\
            .limit(limit)

        if skip > 0:
            media = media.offset(skip)

        return dict(
            media = media,
            title = 'Featured Media',
        )
Example #15
0
    def explore(self, page=1, **kwargs):
        """Display the most recent 15 media.

        :rtype: Dict
        :returns:
            latest
                Latest media
            popular
                Latest media

        """
        media = Media.query.published()
        featured_cat = helpers.get_featured_category()
        latest = media.order_by(Media.publish_on.desc())
        
	recommended = latest.in_category(featured_cat)[:8]
        popular = media.order_by(Media.popularity_points.desc())
        featured = None

       
        if featured_cat:
            featured = latest.in_category(featured_cat).first()
        if not featured:
            featured = popular.first()

        latest = latest.exclude(featured)[:8]
        popular = popular.exclude(featured, latest)[:8]
        userid = request.environ['repoze.who.identity']['repoze.who.userid']
        if userid == 'guest':
            redirect(controller='/login', action='logout_handler')
        return dict(
            featured = featured,
            latest = latest,
            popular = popular,
            recommended = recommended,
            categories = Category.query.populated_tree(),
            userid = userid,
        )
Example #16
0
    def index(self,
              type=None,
              podcast=None,
              tag=None,
              category=None,
              search=None,
              max_age=None,
              min_age=None,
              order=None,
              offset=0,
              limit=10,
              published_after=None,
              published_before=None,
              featured=False,
              id=None,
              slug=None,
              include_embed=False,
              api_key=None,
              format="json",
              **kwargs):
        """Query for a list of media.

        :param type:
            Filter by '%s' or '%s'. Defaults to any type.

        :param podcast:
            A podcast slug (or slugs) to filter by. Use 0 to include
            only non-podcast media or 1 to include any podcast media.
            For multiple podcasts, separate the slugs with commas.

        :param tag:
            A tag slug to filter by.

        :param category:
            A category slug to filter by.

        :param search:
            A boolean search query. See
            http://dev.mysql.com/doc/refman/5.0/en/fulltext-boolean.html

        :param published_after:
            If given, only media published *on or after* this date is
            returned. The expected format is 'YYYY-MM-DD HH:MM:SS'
            (ISO 8601) and must include the year at a bare minimum.

        :param published_before:
            If given, only media published *on or before* this date is
            returned. The expected format is 'YYYY-MM-DD HH:MM:SS'
            (ISO 8601) and must include the year at a bare minimum.

        :param max_age:
            If given, only media published within this many days is
            returned. This is a convenience shortcut for publish_after
            and will override its value if both are given.
        :type max_age: int

        :param min_age:
            If given, only media published prior to this number of days
            ago will be returned. This is a convenience shortcut for
            publish_before and will override its value if both are given.
        :type min_age: int

        :param order:
            A column name and 'asc' or 'desc', seperated by a space.
            The column name can be any one of the returned columns.
            Defaults to newest media first (publish_on desc).

        :param offset:
            Where in the complete resultset to start returning results.
            Defaults to 0, the very beginning. This is useful if you've
            already fetched the first 50 results and want to fetch the
            next 50 and so on.
        :type offset: int

        :param limit:
            Number of results to return in each query. Defaults to 10.
            The maximum allowed value defaults to 50 and is set via
            :attr:`request.settings['api_media_max_results']`.
        :type limit: int

        :param featured:
            If nonzero, the results will only include media from the
            configured featured category, if there is one.
        :type featured: bool

        :param include_embed:
            If nonzero, the HTML for the embeddable player is included
            for all results.
        :type include_embed: bool

        :param id:
            Filters the results to include the one item with the given ID.
            Note that we still return a list.
        :type id: int or None

        :param slug:
            Filters the results to include the one item with the given slug.
            Note that we still return a list.
        :type slug: unicode or None

        :param api_key:
            The api access key if required in settings
        :type api_key: unicode or None

        :raises APIException:
            If there is an user error in the query params.

        :rtype: JSON-ready dict
        :returns: The returned dict has the following fields:

            count (int)
                The total number of results that match this query.
            media (list of dicts)
                A list of **media_info** dicts, as generated by the
                :meth:`_info <mediacore.controllers.api.media.MediaController._info>`
                method. The number of dicts in this list will be the lesser
                of the number of matched items and the requested limit.
                **Note**: unless the 'include_embed' option is specified,
                The returned **media_info** dicts will not include the
                'embed' entry.

        """

        if asbool(request.settings['api_secret_key_required']) \
            and api_key != request.settings['api_secret_key']:
            return dict(error=AUTHERROR)

        if format not in ("json", "mrss"):
            return dict(error=INVALIDFORMATERROR % format)

        query = Media.query\
            .published()\
            .options(orm.undefer('comment_count_published'))

        # Basic filters
        if id:
            query = query.filter_by(id=id)
        if slug:
            query = query.filter_by(slug=slug)

        if type:
            query = query.filter_by(type=type)

        if podcast:
            podcast_query = DBSession.query(Podcast.id)\
                .filter(Podcast.slug.in_(podcast.split(',')))
            query = query.filter(Media.podcast_id.in_(podcast_query))

        if tag:
            tag = fetch_row(Tag, slug=tag)
            query = query.filter(Media.tags.contains(tag))

        if category:
            category = fetch_row(Category, slug=category)
            query = query.filter(Media.categories.contains(category))

        if max_age:
            published_after = datetime.now() - timedelta(days=int(max_age))
        if min_age:
            published_before = datetime.now() - timedelta(days=int(min_age))

        # FIXME: Parse the date and catch formatting problems before it
        #        it hits the database. Right now support for partial
        #        dates like '2010-02' is thanks to leniancy in MySQL.
        #        Hopefully this leniancy is common to Postgres etc.
        if published_after:
            query = query.filter(Media.publish_on >= published_after)
        if published_before:
            query = query.filter(Media.publish_on <= published_before)

        query = query.order_by(get_order_by(order, order_columns))

        # Search will supercede the ordering above
        if search:
            query = query.search(search)

        if featured:
            featured_cat = get_featured_category()
            if featured_cat:
                query = query.in_category(featured_cat)

        # Preload podcast slugs so we don't do n+1 queries
        podcast_slugs = dict(DBSession.query(Podcast.id, Podcast.slug))

        # Rudimentary pagination support
        start = int(offset)
        end = start + min(int(limit),
                          int(request.settings['api_media_max_results']))

        if format == "mrss":
            request.override_template = "sitemaps/mrss.xml"
            return dict(
                media=query[start:end],
                title="Media Feed",
            )

        media = [
            self._info(m, podcast_slugs, include_embed)
            for m in query[start:end]
        ]

        return dict(
            media=media,
            count=query.count(),
        )
Example #17
0
    def index(
        self,
        type=None,
        podcast=None,
        tag=None,
        category=None,
        search=None,
        max_age=None,
        min_age=None,
        order=None,
        offset=0,
        limit=10,
        published_after=None,
        published_before=None,
        featured=False,
        id=None,
        slug=None,
        include_embed=False,
        api_key=None,
        format="json",
        **kwargs
    ):
        """Query for a list of media.

        :param type:
            Filter by '%s' or '%s'. Defaults to any type.

        :param podcast:
            A podcast slug (or slugs) to filter by. Use 0 to include
            only non-podcast media or 1 to include any podcast media.
            For multiple podcasts, separate the slugs with commas.

        :param tag:
            A tag slug to filter by.

        :param category:
            A category slug to filter by.

        :param search:
            A boolean search query. See
            http://dev.mysql.com/doc/refman/5.0/en/fulltext-boolean.html

        :param published_after:
            If given, only media published *on or after* this date is
            returned. The expected format is 'YYYY-MM-DD HH:MM:SS'
            (ISO 8601) and must include the year at a bare minimum.

        :param published_before:
            If given, only media published *on or before* this date is
            returned. The expected format is 'YYYY-MM-DD HH:MM:SS'
            (ISO 8601) and must include the year at a bare minimum.

        :param max_age:
            If given, only media published within this many days is
            returned. This is a convenience shortcut for publish_after
            and will override its value if both are given.
        :type max_age: int

        :param min_age:
            If given, only media published prior to this number of days
            ago will be returned. This is a convenience shortcut for
            publish_before and will override its value if both are given.
        :type min_age: int

        :param order:
            A column name and 'asc' or 'desc', seperated by a space.
            The column name can be any one of the returned columns.
            Defaults to newest media first (publish_on desc).

        :param offset:
            Where in the complete resultset to start returning results.
            Defaults to 0, the very beginning. This is useful if you've
            already fetched the first 50 results and want to fetch the
            next 50 and so on.
        :type offset: int

        :param limit:
            Number of results to return in each query. Defaults to 10.
            The maximum allowed value defaults to 50 and is set via
            :attr:`request.settings['api_media_max_results']`.
        :type limit: int

        :param featured:
            If nonzero, the results will only include media from the
            configured featured category, if there is one.
        :type featured: bool

        :param include_embed:
            If nonzero, the HTML for the embeddable player is included
            for all results.
        :type include_embed: bool

        :param id:
            Filters the results to include the one item with the given ID.
            Note that we still return a list.
        :type id: int or None

        :param slug:
            Filters the results to include the one item with the given slug.
            Note that we still return a list.
        :type slug: unicode or None

        :param api_key:
            The api access key if required in settings
        :type api_key: unicode or None

        :raises APIException:
            If there is an user error in the query params.

        :rtype: JSON-ready dict
        :returns: The returned dict has the following fields:

            count (int)
                The total number of results that match this query.
            media (list of dicts)
                A list of **media_info** dicts, as generated by the
                :meth:`_info <mediacore.controllers.api.media.MediaController._info>`
                method. The number of dicts in this list will be the lesser
                of the number of matched items and the requested limit.
                **Note**: unless the 'include_embed' option is specified,
                The returned **media_info** dicts will not include the
                'embed' entry.

        """

        if asbool(request.settings["api_secret_key_required"]) and api_key != request.settings["api_secret_key"]:
            return dict(error=AUTHERROR)

        if format not in ("json", "mrss"):
            return dict(error=INVALIDFORMATERROR % format)

        query = Media.query.published().options(orm.undefer("comment_count_published"))

        # Basic filters
        if id:
            query = query.filter_by(id=id)
        if slug:
            query = query.filter_by(slug=slug)

        if type:
            query = query.filter_by(type=type)

        if podcast:
            podcast_query = DBSession.query(Podcast.id).filter(Podcast.slug.in_(podcast.split(",")))
            query = query.filter(Media.podcast_id.in_(podcast_query))

        if tag:
            tag = fetch_row(Tag, slug=tag)
            query = query.filter(Media.tags.contains(tag))

        if category:
            category = fetch_row(Category, slug=category)
            query = query.filter(Media.categories.contains(category))

        if max_age:
            published_after = datetime.now() - timedelta(days=int(max_age))
        if min_age:
            published_before = datetime.now() - timedelta(days=int(min_age))

        # FIXME: Parse the date and catch formatting problems before it
        #        it hits the database. Right now support for partial
        #        dates like '2010-02' is thanks to leniancy in MySQL.
        #        Hopefully this leniancy is common to Postgres etc.
        if published_after:
            query = query.filter(Media.publish_on >= published_after)
        if published_before:
            query = query.filter(Media.publish_on <= published_before)

        query = query.order_by(get_order_by(order, order_columns))

        # Search will supercede the ordering above
        if search:
            query = query.search(search)

        if featured:
            featured_cat = get_featured_category()
            if featured_cat:
                query = query.in_category(featured_cat)

        # Preload podcast slugs so we don't do n+1 queries
        podcast_slugs = dict(DBSession.query(Podcast.id, Podcast.slug))

        # Rudimentary pagination support
        start = int(offset)
        end = start + min(int(limit), int(request.settings["api_media_max_results"]))

        if format == "mrss":
            request.override_template = "sitemaps/mrss.xml"
            return dict(media=query[start:end], title="Media Feed")

        media = [self._info(m, podcast_slugs, include_embed) for m in query[start:end]]

        return dict(media=media, count=query.count())
Example #18
0
    def index(self, type=None, podcast=None, tag=None, category=None, search=None,
              max_age=None, min_age=None, order=None, offset=0, limit=10,
              published_after=None, published_before=None, featured=False,
              id=None, slug=None, include_embed=False, **kwargs):
        """Query for a list of media.

        :param type:
            Filter by 'audio' or 'video'. Defaults to any type.

        :param podcast:
            A podcast slug to filter by. Use 0 to include
            only non-podcast media or 1 to include any podcast media.

        :param tag:
            A tag slug to filter by.

        :param category:
            A category slug to filter by.

        :param search:
            A boolean search query. See
            http://dev.mysql.com/doc/refman/5.0/en/fulltext-boolean.html

        :param published_after:
            If given, only media published *on or after* this date is
            returned. The expected format is 'YYYY-MM-DD HH:MM:SS' and
            must include the year at a bare minimum.

        :param published_before:
            If given, only media published *on or before* this date is
            returned. The expected format is 'YYYY-MM-DD HH:MM:SS' and
            must include the year at a bare minimum.

        :param max_age:
            If given, only media published within this many days is
            returned. This is a convenience shortcut for publish_after
            and will override its value if both are given.
        :type max_age: int

        :param min_age:
            If given, only media published prior to this number of days
            ago will be returned. This is a convenience shortcut for
            publish_before and will override its value if both are given.
        :type min_age: int

        :param order:
            A column name and 'asc' or 'desc', seperated by a space.
            The column name can be any one of the returned columns.
            Defaults to newest media first (publish_on desc).

        :param offset:
            Where in the complete resultset to start returning results.
            Defaults to 0, the very beginning. This is useful if you've
            already fetched the first 50 results and want to fetch the
            next 50 and so on.
        :type offset: int

        :param limit:
            Number of results to return in each query. Defaults to 10.
            The maximum allowed value defaults to 50 and is set via
            :attr:`mediacore.config['app_config'].api_media_max_results`.
        :type limit: int

        :param featured:
            If nonzero, the results will only include media from the
            configured featured category, if there is one.
        :type featured: bool

        :param include_embed:
            If nonzero, the HTML for the embeddable player is included
            for all results.
        :type include_embed: bool

        :param id:
            Filters the results to include the one item with the given ID.
            Note that we still return a list.
        :type id: int or None

        :param slug:
            Filters the results to include the one item with the given slug.
            Note that we still return a list.
        :type slug: unicode or None

        :raises APIException:
            If there is an user error in the query params.

        :rtype: JSON dict
        :returns:
            count
                The total number of results that match this query.
            media
                A list of media info objects.

        """
        query = Media.query\
            .published()\
            .options(orm.undefer('comment_count_published'))

        # Basic filters
        if id:
            query = query.filter_by(id=id)
        if slug:
            query = query.filter_by(slug=slug)

        if type:
            query = query.filter_by(type=type)

        if podcast:
            podcast_query = DBSession.query(Podcast.id).filter_by(slug=podcast)
            query = query.filter_by(podcast_id=podcast_query.scalar())

        if tag:
            tag = fetch_row(Tag, slug=tag)
            query = query.filter(Media.tags.contains(tag))

        if category:
            category = fetch_row(Category, slug=category)
            query = query.filter(Media.categories.contains(category))

        if max_age:
            published_after = datetime.now() - timedelta(days=int(max_age))
        if min_age:
            published_before = datetime.now() - timedelta(days=int(min_age))

        # FIXME: Parse the date and catch formatting problems before it
        #        it hits the database. Right now support for partial
        #        dates like '2010-02' is thanks to leniancy in MySQL.
        #        Hopefully this leniancy is common to Postgres etc.
        if published_after:
            query = query.filter(Media.publish_on >= published_after)
        if published_before:
            query = query.filter(Media.publish_on <= published_before)

        # Split the order into two parts, column and direction
        if not order:
            order_col, order_dir = 'publish_on', 'desc'
        else:
            try:
                order_col, order_dir = unicode(order).strip().lower().split(' ')
                assert order_dir in ('asc', 'desc')
            except:
                raise APIException, 'Invalid order format, must be "column asc/desc", given "%s"' % order

        # Get the order clause for the given column name
        try:
            order_attr = order_columns[order_col]
        except KeyError:
            raise APIException, 'Not allowed to order by "%s", please pick one of %s' % (order_col, ', '.join(order_columns.keys()))

        # Normalize to something that can be used in a query
        if isinstance(order_attr, basestring):
            order = sql.text(order_attr % (order_dir == 'asc' and 'asc' or 'desc'))
        else:
            # Assume this is an sqlalchemy InstrumentedAttribute
            order = getattr(order_attr, order_dir)()
        query = query.order_by(order)

        # Search will supercede the ordering above
        if search:
            query = query.search(search)

        if featured:
            featured_cat = get_featured_category()
            if featured_cat:
                query = query.in_category(featured_cat)

        # Preload podcast slugs so we don't do n+1 queries
        podcast_slugs = dict(DBSession.query(Podcast.id, Podcast.slug))

        # Rudimentary pagination support
        start = int(offset)
        end = start + min(int(limit), int(config['api_media_max_results']))

        media = [self._info(m, podcast_slugs, include_embed) for m in query[start:end]]

        return dict(
            media = media,
            count = query.count()
        )