Skip to content

Commit

Permalink
rename to media_hosts, add README, license and packaging stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
Changaco committed Jan 30, 2013
1 parent cd8f09f commit 0713c98
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 24 deletions.
1 change: 1 addition & 0 deletions MANIFEST.in
@@ -0,0 +1 @@
include version.py
36 changes: 36 additions & 0 deletions README.rst
@@ -0,0 +1,36 @@
python-media-hosts gets media info from sites like Youtube and Soundcloud.

Installation
============

pip install media-hosts

media-hosts is compatible with python 2 and 3.

Dependencies:

- miss

Optional dependencies:

- python-dateutil: to parse dates returned by various APIs
- gdata: for some Youtube data
- jsonpickle: for __main__.py

Usage
=====

From python::

from media_hosts import MediaHosts, MediaHostException
media_hosts = MediaHosts(_=_, settings=settings)
media_hosts.get_info_by_url(url)

From the command line::

$ python -m media_hosts 'https://www.youtube.com/watch?v=Gn3QXjNQNsk'

License
=======

LGPLv3+
28 changes: 18 additions & 10 deletions media_info/__init__.py → media_hosts/__init__.py
@@ -1,3 +1,11 @@
# This file is part of a program licensed under the terms of the GNU Lesser
# General Public License version 3 (or at your option any later version)
# as published by the Free Software Foundation.
#
# If you have not received a copy of the GNU Lesser General Public License
# along with this file, see <http://www.gnu.org/licenses/>.


from importlib import import_module
from os import listdir
from os.path import dirname
Expand All @@ -17,14 +25,14 @@ def list_backends():
if a[0] != '_' and a.endswith('.py')]


class MediaInfo:
class MediaHosts:

def __init__(self, backends=None, _=mirror, settings={}):
self._ = _
self.backends = []
for backend in (backends or list_backends()):
try:
module = import_module('media_info.backends.'+backend)
module = import_module('media_hosts.backends.'+backend)
cls = getattr(module, backend+'_backend')
instance = cls(_, settings)
self.backends.append(instance)
Expand All @@ -33,30 +41,30 @@ def __init__(self, backends=None, _=mirror, settings={}):
self.domains = {domain: backend for backend in self.backends
for domain in backend.DOMAINS}

def get(self, url_str, raw=False):
url, backend = self.get_backend(url_str)
return self.get_by_id(backend, backend.get_id(url), raw)

def get_backend(self, url_str):
url = urlparse(url_str) # this doesn't validate the URL and doesn't throw exceptions
try:
return (url, self.domains[pstrip(url.hostname, 'www.')])
except KeyError:
raise MediaInfoException(self._('Unrecognized domain name: %s') % url.hostname)
raise MediaHostException(self._('Unrecognized domain name: %s') % url.hostname)

def get_by_id(self, backend, media_id, raw=False):
def get_info_by_id(self, backend, media_id, raw=False):
info = backend.get_info(media_id, raw)
info.id = media_id
info.service = backend.NAME
return info

def get_info_by_url(self, url_str, raw=False):
url, backend = self.get_backend(url_str)
return self.get_info_by_id(backend, backend.get_id(url), raw)

def normalize(self, url_str):
url, backend = self.get_backend(url_str)
i = backend.get_id(url)
return (backend, backend.normalize(i), i)


class MediaInfoBackend(object):
class MediaHost(object):

def __init__(self, _, settings):
self._ = _
Expand All @@ -67,4 +75,4 @@ def init(self):
pass


class MediaInfoException(Exception): pass
class MediaHostException(Exception): pass
16 changes: 12 additions & 4 deletions media_info/__main__.py → media_hosts/__main__.py
@@ -1,3 +1,11 @@
# This file is part of a program licensed under the terms of the GNU Lesser
# General Public License version 3 (or at your option any later version)
# as published by the Free Software Foundation.
#
# If you have not received a copy of the GNU Lesser General Public License
# along with this file, see <http://www.gnu.org/licenses/>.


from __future__ import print_function, unicode_literals

import argparse
Expand All @@ -7,7 +15,7 @@
except ImportError:
jsonpickle = None

from . import MediaInfo, MediaInfoException
from . import MediaHosts, MediaHostException
from .utils import urldecode


Expand All @@ -28,10 +36,10 @@ def default(self, obj):


settings = dict(s.split('=') for s in args.settings)
media_info = MediaInfo(backends=args.backends, settings=settings)
media_hosts = MediaHosts(backends=args.backends, settings=settings)
try:
info = media_info.get(args.uri, args.raw)
except MediaInfoException as e:
info = media_hosts.get_info_by_url(args.uri, args.raw)
except MediaHostException as e:
print(e.args[0])
exit(1)

Expand Down
File renamed without changes.
@@ -1,3 +1,11 @@
# This file is part of a program licensed under the terms of the GNU Lesser
# General Public License version 3 (or at your option any later version)
# as published by the Free Software Foundation.
#
# If you have not received a copy of the GNU Lesser General Public License
# along with this file, see <http://www.gnu.org/licenses/>.


from __future__ import division, print_function, unicode_literals

import json
Expand All @@ -7,15 +15,15 @@

track_id_re = re.compile(r'/track/[^/]*/([0-9]+)')

class beatport_backend(MediaInfoBackend):
class beatport_backend(MediaHost):

DOMAINS = ('beatport.com',)
NAME = 'Beatport'

def get_id(self, url):
track_id = track_id_re.search(url.path)
if not track_id:
raise MediaInfoException(self._('Unrecognized url: %s') % url.geturl())
raise MediaHostException(self._('Unrecognized url: %s') % url.geturl())
return track_id.group(1)

def get_info(self, track_id, raw):
Expand All @@ -26,7 +34,7 @@ def get_info(self, track_id, raw):
return info
t = iNS(info.results.get('track', None))
if not t:
raise MediaInfoException(self._('Failed to retrieve information for track %s' % (track_id)))
raise MediaHostException(self._('Failed to retrieve information for track %s' % (track_id)))
make_author = safe(lambda a: {'name':a['name'], 'urlname':a['slug']})
r.authors = list(filter(None, map(make_author, t.artists)))
waveform = t.images.pop('waveform', identity)
Expand Down
@@ -1,3 +1,11 @@
# This file is part of a program licensed under the terms of the GNU Lesser
# General Public License version 3 (or at your option any later version)
# as published by the Free Software Foundation.
#
# If you have not received a copy of the GNU Lesser General Public License
# along with this file, see <http://www.gnu.org/licenses/>.


from __future__ import division, print_function, unicode_literals

import json
Expand All @@ -6,7 +14,7 @@
from ..imports import *


class soundcloud_backend(MediaInfoBackend):
class soundcloud_backend(MediaHost):

DOMAINS = ('soundcloud.com',)
NAME = 'Soundcloud'
Expand All @@ -24,13 +32,13 @@ def get_id(self, url):

def get_info(self, track_id, raw):
if not hasattr(self, 'info_url'):
raise MediaInfoException(self._('Error: you need to provide a client ID to use the %s backend') % self.NAME)
raise MediaHostException(self._('Error: you need to provide a client ID to use the %s backend') % self.NAME)
r = iNS(type='audio')
info = iNS(json.loads(urlopen(self.info_url+track_id).read()))
if raw:
return info
if info.kind != 'track':
raise MediaInfoException(self._('%s is a %s, not a track' % (track_id,info.kind)))
raise MediaHostException(self._('%s is a %s, not a track' % (track_id,info.kind)))
r.alternates = call(lambda url: [{'type':'video','url':url}], info.video_url or identity)
make_author = safe(lambda u: {'name':u['username'], 'urlname':u['permalink']})
r.authors = call(lambda a: [a], make_author(info.user or {}))
Expand Down
14 changes: 11 additions & 3 deletions media_info/backends/youtube.py → media_hosts/backends/youtube.py
@@ -1,3 +1,11 @@
# This file is part of a program licensed under the terms of the GNU Lesser
# General Public License version 3 (or at your option any later version)
# as published by the Free Software Foundation.
#
# If you have not received a copy of the GNU Lesser General Public License
# along with this file, see <http://www.gnu.org/licenses/>.


from __future__ import division, print_function, unicode_literals

from ..imports import *
Expand All @@ -11,15 +19,15 @@

video_id_re = re.compile(r'/(?:watch\?.*v=)?([a-zA-Z0-9_-]{11})')

class youtube_backend(MediaInfoBackend):
class youtube_backend(MediaHost):

DOMAINS = ('youtube.com', 'youtu.be')
NAME = 'Youtube'

def get_id(self, url):
video_id = video_id_re.search(url.path + '?' + url.query)
if not video_id:
raise MediaInfoException(self._('Unrecognized url: %s') % url.geturl())
raise MediaHostException(self._('Unrecognized url: %s') % url.geturl())
return video_id.group(1)

def get_info(self, video_id, raw):
Expand Down Expand Up @@ -101,7 +109,7 @@ def get_info(self, video_id, raw):
error = self._('Youtube said:\n"%s"') % max(errors, key=len)
else:
error = self._('unknown reason')
raise MediaInfoException(self._('Getting video info failed, %s') % error)
raise MediaHostException(self._('Getting video info failed, %s') % error)

return r

Expand Down
10 changes: 9 additions & 1 deletion media_info/imports.py → media_hosts/imports.py
@@ -1,3 +1,11 @@
# This file is part of a program licensed under the terms of the GNU Lesser
# General Public License version 3 (or at your option any later version)
# as published by the Free Software Foundation.
#
# If you have not received a copy of the GNU Lesser General Public License
# along with this file, see <http://www.gnu.org/licenses/>.


from datetime import datetime
import re
try:
Expand All @@ -16,5 +24,5 @@
from miss.namespace import iNS
from miss.six import ustr

from . import MediaInfoBackend, MediaInfoException, mirror
from . import MediaHost, MediaHostException, mirror
from .utils import *
8 changes: 8 additions & 0 deletions media_info/utils.py → media_hosts/utils.py
@@ -1,3 +1,11 @@
# This file is part of a program licensed under the terms of the GNU Lesser
# General Public License version 3 (or at your option any later version)
# as published by the Free Software Foundation.
#
# If you have not received a copy of the GNU Lesser General Public License
# along with this file, see <http://www.gnu.org/licenses/>.


try:
from urllib.parse import parse_qsl
except ImportError:
Expand Down
22 changes: 22 additions & 0 deletions setup.py
@@ -0,0 +1,22 @@
# -*- coding: utf-8 -*-

from os.path import join, dirname

from setuptools import setup, find_packages

from version import get_version

setup(
name='media-hosts',
version=get_version(),
description='Gets media info from sites like Youtube and Soundcloud',
author='Changaco',
author_email='changaco ατ changaco δοτ net',
url='https://github.com/Changaco/python-media-hosts',
license='LGPLv3+',
packages=find_packages(),
long_description=open(join(dirname(__file__), 'README.rst')).read(),
install_requires = [
'miss>=0.2',
],
)
36 changes: 36 additions & 0 deletions version.py
@@ -0,0 +1,36 @@
# This program is placed into the public domain.

__all__ = ('get_version')

from os.path import dirname, isdir, join
import re
from subprocess import CalledProcessError, check_output

version_re = re.compile('^Version: (.+)$', re.M)

def get_version():
d = dirname(__file__)

if isdir(join(d, '.git')):
# Get the version using "git describe".
cmd = 'git describe --tags --match [0-9]*'.split()
try:
version = check_output(cmd).decode().strip()
except CalledProcessError:
print('Unable to get version number from git tags')
exit(1)

# PEP 386 compatibility
if '-' in version:
version = '.post'.join(version.split('-')[:2])

else:
# Extract the version from the PKG-INFO file.
with open(join(d, 'PKG-INFO')) as f:
version = version_re.search(f.read()).group(1)

return version


if __name__ == '__main__':
print(get_version())

0 comments on commit 0713c98

Please sign in to comment.