├── tests
├── __init__.py
├── test_playback.py
├── test_extension.py
├── test_search.py
├── test_images.py
├── test_lookup.py
├── conftest.py
├── test_browse.py
├── test_configmap.py
└── test_translator.py
├── docs
├── .gitignore
├── changelog.rst
├── install.rst
├── license.rst
├── conf.py
├── index.rst
├── config.rst
└── Makefile
├── setup.py
├── .gitignore
├── .readthedocs.yml
├── mopidy_internetarchive
├── playback.py
├── ext.conf
├── backend.py
├── __init__.py
├── client.py
├── translator.py
└── library.py
├── MANIFEST.in
├── pyproject.toml
├── tox.ini
├── .github
└── workflows
│ └── ci.yml
├── setup.cfg
├── README.rst
├── CHANGELOG.rst
└── LICENSE
/tests/__init__.py:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/docs/.gitignore:
--------------------------------------------------------------------------------
1 | _build
2 |
--------------------------------------------------------------------------------
/setup.py:
--------------------------------------------------------------------------------
1 | from setuptools import setup
2 |
3 | setup()
4 |
--------------------------------------------------------------------------------
/docs/changelog.rst:
--------------------------------------------------------------------------------
1 | **********
2 | Change Log
3 | **********
4 |
5 | .. include:: ../CHANGELOG.rst
6 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | *.pyc
2 | /*.egg-info
3 | /.coverage
4 | /.mypy_cache/
5 | /.pytest_cache/
6 | /.tox/
7 | /MANIFEST
8 | /build/
9 | /dist/
10 | /docs/_build/
11 |
--------------------------------------------------------------------------------
/.readthedocs.yml:
--------------------------------------------------------------------------------
1 | version: 2
2 |
3 | python:
4 | version: 3.7
5 | install:
6 | - method: pip
7 | path: .
8 | extra_requirements:
9 | - docs
10 |
11 | sphinx:
12 | builder: htmldir
13 | configuration: docs/conf.py
14 |
15 | formats:
16 | - pdf
17 | - epub
18 |
--------------------------------------------------------------------------------
/mopidy_internetarchive/playback.py:
--------------------------------------------------------------------------------
1 | from mopidy import backend
2 |
3 | from . import translator
4 |
5 |
6 | class InternetArchivePlaybackProvider(backend.PlaybackProvider):
7 | def translate_uri(self, uri):
8 | identifier, filename, _ = translator.parse_uri(uri)
9 | return self.backend.client.geturl(identifier, filename)
10 |
--------------------------------------------------------------------------------
/tests/test_playback.py:
--------------------------------------------------------------------------------
1 | def test_translate_url(playback, client_mock):
2 | url = "http://archive.org/download/item/file.mp3"
3 | client_mock.geturl.return_value = url
4 | result = playback.translate_uri("internetarchive:item#file.mp3")
5 | assert client_mock.geturl.called_once()
6 | assert client_mock.geturl.call_args == (("item", "file.mp3"),)
7 | assert result == url
8 |
--------------------------------------------------------------------------------
/docs/install.rst:
--------------------------------------------------------------------------------
1 | ************
2 | Installation
3 | ************
4 |
5 | Debian/Ubuntu/Raspbian: Install the ``mopidy-internetarchive`` package
6 | from `apt.mopidy.com `_::
7 |
8 | apt-get install mopidy-internetarchive
9 |
10 | Otherwise, install the package from `PyPI
11 | `_::
12 |
13 | pip install Mopidy-Internetarchive
14 |
--------------------------------------------------------------------------------
/MANIFEST.in:
--------------------------------------------------------------------------------
1 | include *.py
2 | include *.rst
3 | include .mailmap
4 | include .readthedocs.yml
5 | include LICENSE
6 | include MANIFEST.in
7 | include pyproject.toml
8 | include tox.ini
9 |
10 | recursive-include .circleci *
11 | recursive-include .github *
12 |
13 | include mopidy_*/ext.conf
14 |
15 | recursive-include tests *.py
16 | recursive-include tests/data *
17 |
18 | recursive-include docs *
19 | prune docs/_build
20 |
--------------------------------------------------------------------------------
/pyproject.toml:
--------------------------------------------------------------------------------
1 | [build-system]
2 | requires = ["setuptools >= 30.3.0", "wheel"]
3 |
4 |
5 | [tool.black]
6 | target-version = ["py37", "py38"]
7 | line-length = 80
8 |
9 |
10 | [tool.isort]
11 | multi_line_output = 3
12 | include_trailing_comma = true
13 | force_grid_wrap = 0
14 | use_parentheses = true
15 | line_length = 88
16 | known_tests = "tests"
17 | sections = "FUTURE,STDLIB,THIRDPARTY,FIRSTPARTY,TESTS,LOCALFOLDER"
18 |
--------------------------------------------------------------------------------
/tox.ini:
--------------------------------------------------------------------------------
1 | [tox]
2 | envlist = py37, py38, py39, py310, check-manifest, docs, flake8
3 |
4 | [testenv]
5 | sitepackages = true
6 | deps = .[test]
7 | commands =
8 | python -m pytest \
9 | --basetemp={envtmpdir} \
10 | --cov=mopidy_internetarchive --cov-report=term-missing \
11 | {posargs}
12 |
13 | [testenv:check-manifest]
14 | deps = .[lint]
15 | commands = python -m check_manifest
16 |
17 | [testenv:docs]
18 | deps = .[docs]
19 | changedir = docs
20 | commands = python -m sphinx -b html -d {envtmpdir}/doctrees . {envtmpdir}/html
21 |
22 | [testenv:flake8]
23 | deps = .[lint]
24 | commands = python -m flake8 --show-source --statistics
25 |
--------------------------------------------------------------------------------
/docs/license.rst:
--------------------------------------------------------------------------------
1 | *******
2 | License
3 | *******
4 |
5 | Mopidy-InternetArchive is Copyright (c) 2014-2019 Thomas Kemmer.
6 |
7 | Licensed under the Apache License, Version 2.0 (the "License"); you
8 | may not use this software except in compliance with the License. You
9 | may obtain a copy of the License at
10 |
11 | http://www.apache.org/licenses/LICENSE-2.0
12 |
13 | Unless required by applicable law or agreed to in writing, software
14 | distributed under the License is distributed on an "AS IS" BASIS,
15 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
16 | implied. See the License for the specific language governing
17 | permissions and limitations under the License.
18 |
--------------------------------------------------------------------------------
/tests/test_extension.py:
--------------------------------------------------------------------------------
1 | from unittest import mock
2 |
3 | from mopidy_internetarchive import Extension, backend
4 |
5 |
6 | def test_get_default_config():
7 | ext = Extension()
8 |
9 | config = ext.get_default_config()
10 |
11 | assert "[internetarchive]" in config
12 | assert "enabled = true" in config
13 |
14 |
15 | def test_get_config_schema():
16 | ext = Extension()
17 |
18 | schema = ext.get_config_schema()
19 |
20 | assert "audio_formats" in schema
21 | assert "base_url" in schema
22 | assert "browse_limit" in schema
23 | assert "browse_order" in schema
24 | assert "cache_size" in schema
25 | assert "cache_ttl" in schema
26 | assert "collections" in schema
27 | assert "exclude_collections" in schema
28 | assert "exclude_mediatypes" in schema
29 | assert "image_formats" in schema
30 | assert "retries" in schema
31 | assert "search_limit" in schema
32 | assert "search_order" in schema
33 | assert "timeout" in schema
34 |
35 |
36 | def test_setup():
37 | registry = mock.Mock()
38 |
39 | ext = Extension()
40 | ext.setup(registry)
41 |
42 | registry.add.assert_called_with("backend", backend.InternetArchiveBackend)
43 |
--------------------------------------------------------------------------------
/docs/conf.py:
--------------------------------------------------------------------------------
1 | import configparser
2 | import pathlib
3 |
4 |
5 | def setup(app):
6 | app.add_object_type(
7 | "confval",
8 | "confval",
9 | objname="configuration value",
10 | indextemplate="pair: %s; configuration value",
11 | )
12 |
13 |
14 | def get_version():
15 | # Get current library version without requiring the library to be
16 | # installed, like ``pkg_resources.get_distribution(...).version`` requires.
17 | cp = configparser.ConfigParser()
18 | cp.read(pathlib.Path(__file__).parent.parent / "setup.cfg")
19 | return cp["metadata"]["version"]
20 |
21 |
22 | project = "Mopidy-InternetArchive"
23 | copyright = "2014-2022 Thomas Kemmer"
24 | version = get_version()
25 | release = version
26 |
27 | exclude_patterns = ["_build"]
28 | master_doc = "index"
29 | html_theme = "default"
30 |
31 | latex_documents = [
32 | (
33 | "index",
34 | "Mopidy-InternetArchive.tex",
35 | "Mopidy-InternetArchive Documentation",
36 | "Thomas Kemmer",
37 | "manual",
38 | )
39 | ]
40 |
41 | man_pages = [
42 | (
43 | "index",
44 | "mopidy-internetarchive",
45 | "Mopidy-InternetArchive Documentation",
46 | ["Thomas Kemmer"],
47 | 1,
48 | )
49 | ]
50 |
--------------------------------------------------------------------------------
/mopidy_internetarchive/ext.conf:
--------------------------------------------------------------------------------
1 | [internetarchive]
2 | enabled = true
3 |
4 | # archive.org base URL
5 | base_url = http://archive.org
6 |
7 | # top-level collections for browsing
8 | collections =
9 | audio
10 | etree
11 | librivoxaudio
12 | audio_bookspoetry
13 | audio_tech
14 | audio_music
15 | audio_news
16 | audio_foreign
17 | audio_podcast
18 | audio_religion
19 |
20 | # audio file formats in order of preference
21 | audio_formats = VBR MP3, 64Kbps MP3
22 |
23 | # image file formats in order of preference
24 | image_formats = JPEG, JPEG Thumb
25 |
26 | # maximum number of browse results
27 | browse_limit = 100
28 |
29 | # list of collection browse views: (asc|desc) |
30 | browse_views =
31 | downloads desc | Views
32 | titleSorter asc | Title
33 | publicdate desc | Date Archived
34 | date desc | Date Published
35 | creatorSorter asc | Creator
36 |
37 | # maximum number of search results
38 | search_limit = 20
39 |
40 | # sort order for searching: (asc|desc); default is score
41 | search_order =
42 |
43 | # number of items to cache
44 | cache_size = 128
45 |
46 | # cache time-to-live in seconds
47 | cache_ttl = 86400
48 |
49 | # maximum number of HTTP connection retries
50 | retries = 3
51 |
52 | # HTTP request timeout in seconds
53 | timeout = 10
54 |
--------------------------------------------------------------------------------
/tests/test_search.py:
--------------------------------------------------------------------------------
1 | from mopidy import models
2 |
3 |
4 | def test_search_any(library, client_mock):
5 | client_mock.search.return_value = client_mock.SearchResult(
6 | {
7 | "responseHeader": {"params": {"query": "album"}},
8 | "response": {
9 | "docs": [
10 | {
11 | "identifier": "album1",
12 | "title": "Album #1",
13 | "mediatype": "audio",
14 | },
15 | {
16 | "identifier": "album2",
17 | "title": "Album #2",
18 | "mediatype": "etree",
19 | },
20 | ],
21 | "numFound": 2,
22 | },
23 | }
24 | )
25 | result = library.search(dict(any=["album"]))
26 | assert client_mock.search.called_once()
27 | assert result == models.SearchResult(
28 | uri="internetarchive:?q=album",
29 | albums=[
30 | models.Album(name="Album #1", uri="internetarchive:album1"),
31 | models.Album(name="Album #2", uri="internetarchive:album2"),
32 | ],
33 | )
34 |
35 |
36 | def test_search_unknown(library, client_mock):
37 | result = library.search(dict(foo=["bar"]))
38 | client_mock.search.assert_not_called()
39 | assert result is None
40 |
--------------------------------------------------------------------------------
/mopidy_internetarchive/backend.py:
--------------------------------------------------------------------------------
1 | import pykka
2 | from mopidy import backend, httpclient
3 |
4 | import cachetools
5 |
6 | from . import Extension
7 | from .client import InternetArchiveClient
8 | from .library import InternetArchiveLibraryProvider
9 | from .playback import InternetArchivePlaybackProvider
10 |
11 |
12 | def _cache(cache_size=None, cache_ttl=None, **kwargs):
13 | if cache_size is None:
14 | return None
15 | elif cache_ttl is None:
16 | return cachetools.LRUCache(cache_size)
17 | else:
18 | return cachetools.TTLCache(cache_size, cache_ttl)
19 |
20 |
21 | class InternetArchiveBackend(pykka.ThreadingActor, backend.Backend):
22 |
23 | uri_schemes = [Extension.ext_name]
24 |
25 | def __init__(self, config, audio):
26 | super().__init__()
27 | ext_config = config[Extension.ext_name]
28 |
29 | self.client = client = InternetArchiveClient(
30 | ext_config["base_url"],
31 | retries=ext_config["retries"],
32 | timeout=ext_config["timeout"],
33 | )
34 | product = f"{Extension.dist_name}/{Extension.version}"
35 | client.useragent = httpclient.format_user_agent(product)
36 | proxy = httpclient.format_proxy(config["proxy"])
37 | client.proxies.update({"http": proxy, "https": proxy})
38 | client.cache = _cache(**ext_config)
39 |
40 | self.library = InternetArchiveLibraryProvider(ext_config, self)
41 | self.playback = InternetArchivePlaybackProvider(audio, self)
42 |
--------------------------------------------------------------------------------
/tests/test_images.py:
--------------------------------------------------------------------------------
1 | from mopidy import models
2 |
3 | URL = "http://archive.org/download/album/cover.jpg"
4 |
5 | ITEM = {
6 | "files": [
7 | {
8 | "name": "track02.mp3",
9 | "title": "Track #2",
10 | "format": "VBR MP3",
11 | "track": "02",
12 | },
13 | {
14 | "name": "track01.mp3",
15 | "title": "Track #1",
16 | "format": "VBR MP3",
17 | "track": "01",
18 | },
19 | {"name": "cover.jpg", "format": "JPEG"},
20 | ],
21 | "metadata": {"identifier": "album", "title": "Album", "mediatype": "audio"},
22 | }
23 |
24 | IMAGES = [models.Image(uri=URL)]
25 |
26 |
27 | def test_root_images(library, client_mock):
28 | results = library.get_images(["internetarchive:"])
29 | client_mock.getitem.assert_not_called()
30 | assert results == {}
31 |
32 |
33 | def test_album_images(library, client_mock):
34 | client_mock.getitem.return_value = ITEM
35 | client_mock.geturl.return_value = URL
36 | results = library.get_images(["internetarchive:album"])
37 | client_mock.getitem.assert_called_once_with("album")
38 | assert results == {"internetarchive:album": IMAGES}
39 |
40 |
41 | def test_track_images(library, client_mock):
42 | client_mock.getitem.return_value = ITEM
43 | client_mock.geturl.return_value = URL
44 | results = library.get_images(
45 | [
46 | "internetarchive:album#track01.jpg",
47 | "internetarchive:album#track02.jpg",
48 | ]
49 | )
50 | client_mock.getitem.assert_called_once_with("album")
51 | assert results == {
52 | "internetarchive:album#track01.jpg": IMAGES,
53 | "internetarchive:album#track02.jpg": IMAGES,
54 | }
55 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | main:
7 | strategy:
8 | fail-fast: false
9 | matrix:
10 | include:
11 | - name: "Test: Python 3.7"
12 | python: "3.7"
13 | tox: py37
14 | - name: "Test: Python 3.8"
15 | python: "3.8"
16 | tox: py38
17 | - name: "Test: Python 3.9"
18 | python: "3.9"
19 | tox: py39
20 | - name: "Test: Python 3.10"
21 | python: "3.10"
22 | tox: py310
23 | coverage: true
24 | - name: "Lint: check-manifest"
25 | python: "3.10"
26 | tox: check-manifest
27 | - name: "Lint: flake8"
28 | python: "3.10"
29 | tox: flake8
30 | - name: "Docs"
31 | python: "3.10"
32 | tox: docs
33 |
34 | name: ${{ matrix.name }}
35 | runs-on: ubuntu-20.04
36 | container: ghcr.io/mopidy/ci:latest
37 |
38 | steps:
39 | - uses: actions/checkout@v2
40 | - uses: actions/setup-python@v2
41 | with:
42 | python-version: ${{ matrix.python }}
43 | - name: Fix home dir permissions to enable pip caching
44 | run: chown -R root /github/home
45 | - name: Cache pip
46 | uses: actions/cache@v2
47 | with:
48 | path: ~/.cache/pip
49 | key: ${{ runner.os }}-${{ matrix.python }}-${{ matrix.tox }}-pip-${{ hashFiles('setup.cfg') }}-${{ hashFiles('tox.ini') }}
50 | restore-keys: |
51 | ${{ runner.os }}-${{ matrix.python }}-${{ matrix.tox }}-pip-
52 | - run: python -m pip install pygobject tox
53 | - run: python -m tox -e ${{ matrix.tox }}
54 | if: ${{ ! matrix.coverage }}
55 | - run: python -m tox -e ${{ matrix.tox }} -- --cov-report=xml
56 | if: ${{ matrix.coverage }}
57 | - uses: codecov/codecov-action@v1
58 | if: ${{ matrix.coverage }}
59 |
--------------------------------------------------------------------------------
/setup.cfg:
--------------------------------------------------------------------------------
1 | [metadata]
2 | name = Mopidy-InternetArchive
3 | version = 3.0.1
4 | url = https://github.com/tkem/mopidy-internetarchive
5 | author = Thomas Kemmer
6 | author_email = tkemmer@computer.org
7 | license = Apache License, Version 2.0
8 | license_file = LICENSE
9 | description = Mopidy extension for playing music from the Internet Archive
10 | long_description = file: README.rst
11 | classifiers =
12 | Environment :: No Input/Output (Daemon)
13 | Intended Audience :: End Users/Desktop
14 | License :: OSI Approved :: Apache Software License
15 | Operating System :: OS Independent
16 | Programming Language :: Python :: 3
17 | Programming Language :: Python :: 3.7
18 | Programming Language :: Python :: 3.8
19 | Programming Language :: Python :: 3.9
20 | Programming Language :: Python :: 3.10
21 | Topic :: Multimedia :: Sound/Audio :: Players
22 |
23 |
24 | [options]
25 | zip_safe = False
26 | include_package_data = True
27 | packages = find:
28 | python_requires = >= 3.7
29 | install_requires =
30 | Mopidy >= 3.0.0
31 | Pykka >= 2.0.1
32 | cachetools >= 1.0
33 | requests >= 2.0
34 | setuptools
35 | uritools >= 1.0
36 |
37 |
38 | [options.extras_require]
39 | docs =
40 | sphinx
41 | lint =
42 | black
43 | check-manifest
44 | flake8
45 | flake8-black
46 | flake8-bugbear
47 | flake8-import-order
48 | isort[pyproject]
49 | release =
50 | twine
51 | wheel
52 | test =
53 | pytest
54 | pytest-cov
55 | dev =
56 | %(docs)s
57 | %(lint)s
58 | %(release)s
59 | %(test)s
60 |
61 |
62 | [options.packages.find]
63 | exclude =
64 | tests
65 | tests.*
66 |
67 |
68 | [options.entry_points]
69 | mopidy.ext =
70 | internetarchive = mopidy_internetarchive:Extension
71 |
72 |
73 | [flake8]
74 | application-import-names = mopidy_internetarchive, tests
75 | max-line-length = 80
76 | exclude = .git, .tox, build
77 | select =
78 | # Regular flake8 rules
79 | C, E, F, W
80 | # flake8-bugbear rules
81 | B
82 | # B950: line too long (soft speed limit)
83 | B950
84 | # pep8-naming rules
85 | N
86 | ignore =
87 | # E203: whitespace before ':' (not PEP8 compliant)
88 | E203
89 | # E501: line too long (replaced by B950)
90 | E501
91 | # W503: line break before binary operator (not PEP8 compliant)
92 | W503
93 |
94 |
95 | [build_sphinx]
96 | source-dir = docs/
97 | build-dir = docs/_build
98 | all_files = 1
99 |
--------------------------------------------------------------------------------
/tests/test_lookup.py:
--------------------------------------------------------------------------------
1 | from mopidy import models
2 |
3 | import pytest
4 |
5 | ITEM = {
6 | "files": [
7 | {
8 | "name": "track02.mp3",
9 | "title": "Track #2",
10 | "format": "VBR MP3",
11 | "track": "02",
12 | },
13 | {
14 | "name": "track01.mp3",
15 | "title": "Track #1",
16 | "format": "VBR MP3",
17 | "track": "01",
18 | },
19 | ],
20 | "metadata": {"identifier": "album", "title": "Album", "mediatype": "audio"},
21 | }
22 |
23 | ALBUM = models.Album(name="Album", uri="internetarchive:album")
24 |
25 | TRACK1 = models.Track(
26 | album=ALBUM,
27 | name="Track #1",
28 | track_no=1,
29 | uri="internetarchive:album#track01.mp3",
30 | )
31 |
32 | TRACK2 = models.Track(
33 | album=ALBUM,
34 | name="Track #2",
35 | track_no=2,
36 | uri="internetarchive:album#track02.mp3",
37 | )
38 |
39 |
40 | def test_lookup_root(library, client_mock):
41 | assert library.lookup("internetarchive:") == []
42 |
43 |
44 | def test_lookup_album(library, client_mock):
45 | client_mock.getitem.return_value = ITEM
46 | results = library.lookup("internetarchive:album")
47 | client_mock.getitem.assert_called_once_with("album")
48 | assert results == [TRACK1, TRACK2]
49 |
50 |
51 | def test_lookup_track(library, client_mock):
52 | client_mock.getitem.return_value = ITEM
53 | results = library.lookup("internetarchive:album#track01.mp3")
54 | client_mock.getitem.assert_called_once_with("album")
55 | assert results == [TRACK1]
56 | # assert lookup cache is used
57 | client_mock.reset_mock()
58 | results = library.lookup("internetarchive:album#track02.mp3")
59 | client_mock.getitem.assert_not_called()
60 | assert results == [TRACK2]
61 |
62 |
63 | def test_lookup_refresh(library, client_mock):
64 | client_mock.getitem.return_value = ITEM
65 | results = library.lookup("internetarchive:album#track01.mp3")
66 | client_mock.getitem.assert_called_once_with("album")
67 | assert results == [TRACK1]
68 | # clear lookup cache
69 | library.refresh()
70 | assert client_mock.cache.clear.called
71 | # assert lookup cache is cleared
72 | client_mock.reset_mock()
73 | results = library.lookup("internetarchive:album#track02.mp3")
74 | client_mock.getitem.assert_called_once_with("album")
75 | assert results == [TRACK2]
76 |
77 |
78 | def test_lookup_unknown(library, client_mock):
79 | client_mock.getitem.side_effect = LookupError("null")
80 | with pytest.raises(LookupError):
81 | library.lookup("internetarchive:null")
82 | client_mock.getitem.assert_called_once_with("null")
83 |
--------------------------------------------------------------------------------
/tests/conftest.py:
--------------------------------------------------------------------------------
1 | import collections
2 |
3 | from unittest import mock
4 |
5 | import mopidy_internetarchive as ext
6 | import pytest
7 |
8 |
9 | @pytest.fixture
10 | def config():
11 | return {
12 | "internetarchive": {
13 | "base_url": "http://archive.org",
14 | "collections": ("audio", "etree", "foo"),
15 | "audio_formats": ("Flac", "VBR MP3"),
16 | "image_formats": ("JPEG", "PNG"),
17 | "browse_limit": None,
18 | "browse_views": collections.OrderedDict(
19 | [("title asc", "Title"), ("creator asc", "Creator")]
20 | ),
21 | "search_limit": None,
22 | "search_order": None,
23 | "cache_size": None,
24 | "cache_ttl": None,
25 | "retries": 0,
26 | "timeout": None,
27 | },
28 | "proxy": {},
29 | }
30 |
31 |
32 | @pytest.fixture
33 | def root_collections():
34 | from mopidy.models import Ref
35 |
36 | return [
37 | Ref.directory(name="Audio Archive", uri="internetarchive:audio"),
38 | Ref.directory(name="Live Music Archive", uri="internetarchive:etree"),
39 | ]
40 |
41 |
42 | @pytest.fixture
43 | def audio_mock():
44 | audio_mock = mock.Mock()
45 | return audio_mock
46 |
47 |
48 | @pytest.fixture
49 | def client_mock():
50 | client_mock = mock.Mock(spec=ext.client.InternetArchiveClient)
51 | client_mock.SearchResult = ext.client.InternetArchiveClient.SearchResult
52 | client_mock.cache = mock.Mock(spec=dict)
53 | client_mock.search.return_value = client_mock.SearchResult(
54 | {
55 | "responseHeader": {"params": {"q": "album"}},
56 | "response": {
57 | "numFound": 2,
58 | "docs": [
59 | {
60 | "identifier": "etree",
61 | "title": "Live Music Archive",
62 | "mediatype": "collection",
63 | },
64 | {
65 | "identifier": "audio",
66 | "title": "Audio Archive",
67 | "mediatype": "collection",
68 | },
69 | ],
70 | },
71 | }
72 | )
73 | return client_mock
74 |
75 |
76 | @pytest.fixture
77 | def backend_mock(client_mock, config):
78 | backend_mock = mock.Mock(spec=ext.backend.InternetArchiveBackend)
79 | backend_mock.client = client_mock
80 | return backend_mock
81 |
82 |
83 | @pytest.fixture
84 | def library(backend_mock, config):
85 | return ext.library.InternetArchiveLibraryProvider(
86 | config["internetarchive"], backend_mock
87 | )
88 |
89 |
90 | @pytest.fixture
91 | def playback(audio_mock, backend_mock):
92 | return ext.playback.InternetArchivePlaybackProvider(
93 | audio_mock, backend_mock
94 | )
95 |
--------------------------------------------------------------------------------
/README.rst:
--------------------------------------------------------------------------------
1 | **********************
2 | Mopidy-InternetArchive
3 | **********************
4 |
5 | .. image:: https://img.shields.io/pypi/v/Mopidy-InternetArchive
6 | :target: https://pypi.org/project/Mopidy-InternetArchive/
7 | :alt: Latest PyPI version
8 |
9 | .. image:: https://img.shields.io/github/workflow/status/tkem/mopidy-internetarchive/CI
10 | :target: https://github.com/tkem/mopidy-internetarchive/actions
11 | :alt: CI build status
12 |
13 | .. image:: https://img.shields.io/readthedocs/mopidy-internetarchive
14 | :target: https://mopidy-internetarchive.readthedocs.io/
15 | :alt: Read the Docs build status
16 |
17 | .. image:: https://img.shields.io/codecov/c/gh/tkem/mopidy-internetarchive
18 | :target: https://codecov.io/gh/tkem/mopidy-internetarchive
19 | :alt: Test coverage
20 |
21 | .. image:: https://img.shields.io/github/license/tkem/mopidy-internetarchive
22 | :target: https://raw.github.com/tkem/mopidy-internetarchive/master/LICENSE
23 | :alt: License
24 |
25 | .. image:: https://img.shields.io/badge/code%20style-black-000000.svg
26 | :target: https://github.com/psf/black
27 | :alt: Code style: black
28 |
29 | Mopidy-InternetArchive is a Mopidy_ extension for playing music from
30 | the `Internet Archive`_.
31 |
32 | This extension lets you search for and stream recordings ranging from
33 | `alternative news programming`_, to `Grateful Dead concerts`_, to `Old
34 | Time Radio shows`_, to `book and poetry readings`_, to `original
35 | music`_ uploaded by Internet Archive users. It also gives you access
36 | to a vast number of high-quality live recordings from the `Live Music
37 | Archive`_, and thousands of free audiobooks from the LibriVox_
38 | collection.
39 |
40 | For more information and installation instructions, please see
41 | Mopidy-InternetArchive's online documentation_.
42 |
43 | .. _Mopidy: http://www.mopidy.com/
44 | .. _Internet Archive: http://archive.org
45 | .. _alternative news programming: https://archive.org/details/audio_news
46 | .. _Grateful Dead concerts: https://archive.org/details/GratefulDead
47 | .. _Old Time Radio shows: https://archive.org/details/radioprograms
48 | .. _book and poetry readings: https://archive.org/details/audio_bookspoetry
49 | .. _original music: https://archive.org/details/opensource_audio
50 | .. _Live Music Archive: https://archive.org/details/etree
51 | .. _LibriVox: https://archive.org/details/librivoxaudio
52 | .. _Documentation: http://mopidy-internetarchive.readthedocs.org/en/latest/
53 |
54 |
55 | Project resources
56 | =================
57 |
58 | - `Source code `_
59 | - `Issue tracker `_
60 | - `Changelog `_
61 |
62 |
63 | Credits
64 | =======
65 |
66 | - Original author: `Thomas Kemmer `__
67 | - Current maintainer: `Thomas Kemmer `__
68 | - `Contributors `_
69 |
--------------------------------------------------------------------------------
/mopidy_internetarchive/__init__.py:
--------------------------------------------------------------------------------
1 | import collections
2 | import pathlib
3 |
4 | import pkg_resources
5 |
6 | from mopidy import config, ext
7 |
8 | __version__ = pkg_resources.get_distribution("Mopidy-InternetArchive").version
9 |
10 | SORT_FIELDS = [
11 | f"{f} {o}"
12 | for o in ("asc", "desc")
13 | for f in (
14 | "addeddate",
15 | "avg_rating",
16 | "call_number",
17 | "createdate",
18 | "creatorSorter",
19 | "date",
20 | "downloads",
21 | "foldoutcount",
22 | "headerImage",
23 | "identifier",
24 | "imagecount",
25 | "indexdate",
26 | "languageSorter",
27 | "licenseurl",
28 | "month",
29 | "nav_order",
30 | "num_reviews",
31 | "publicdate",
32 | "reviewdate",
33 | "stars",
34 | "titleSorter",
35 | "week",
36 | "year",
37 | )
38 | ]
39 |
40 |
41 | class ConfigMap(config.ConfigValue):
42 |
43 | default_keys = config.String()
44 |
45 | default_values = config.String()
46 |
47 | def __init__(
48 | self,
49 | keys=default_keys,
50 | values=default_values,
51 | delim="|",
52 | optional=False,
53 | ):
54 | self.__keys = keys
55 | self.__values = values
56 | self.__delim = delim
57 | self.__optional = optional
58 |
59 | def deserialize(self, value):
60 | dict = collections.OrderedDict()
61 | for s in config.List(optional=self.__optional).deserialize(value):
62 | parts = s.partition(self.__delim)
63 | key = self.__keys.deserialize(parts[0])
64 | val = self.__values.deserialize(parts[2])
65 | dict[key] = val
66 | return dict
67 |
68 | def serialize(self, value, display=False):
69 | if not value:
70 | return ""
71 | d = config.String().serialize(self.__delim)
72 | return config.List().serialize(
73 | [
74 | self.__keys.serialize(k) + d + self.__values.serialize(v)
75 | for k, v in value.items()
76 | ]
77 | )
78 |
79 |
80 | class Extension(ext.Extension):
81 |
82 | dist_name = "Mopidy-InternetArchive"
83 | ext_name = "internetarchive"
84 | version = __version__
85 |
86 | def get_default_config(self):
87 | return config.read(pathlib.Path(__file__).parent / "ext.conf")
88 |
89 | def get_config_schema(self):
90 | schema = super().get_config_schema()
91 | schema.update(
92 | base_url=config.String(),
93 | collections=config.List(),
94 | audio_formats=config.List(),
95 | image_formats=config.List(),
96 | browse_limit=config.Integer(minimum=1, optional=True),
97 | browse_views=ConfigMap(keys=config.String(choices=SORT_FIELDS)),
98 | search_limit=config.Integer(minimum=1, optional=True),
99 | search_order=config.String(choices=SORT_FIELDS, optional=True),
100 | cache_size=config.Integer(minimum=1, optional=True),
101 | cache_ttl=config.Integer(minimum=0, optional=True),
102 | retries=config.Integer(minimum=0),
103 | timeout=config.Integer(minimum=0, optional=True),
104 | # no longer used
105 | browse_order=config.Deprecated(),
106 | exclude_collections=config.Deprecated(),
107 | exclude_mediatypes=config.Deprecated(),
108 | username=config.Deprecated(),
109 | )
110 | return schema
111 |
112 | def setup(self, registry):
113 | from .backend import InternetArchiveBackend
114 |
115 | registry.add("backend", InternetArchiveBackend)
116 |
--------------------------------------------------------------------------------
/tests/test_browse.py:
--------------------------------------------------------------------------------
1 | from mopidy import models
2 |
3 | COLLECTION = {
4 | "metadata": {
5 | "identifier": "directory",
6 | "title": "Directory",
7 | "mediatype": "collection",
8 | }
9 | }
10 |
11 | ITEM = {
12 | "files": [
13 | {
14 | "name": "track02.mp3",
15 | "title": "Track #2",
16 | "format": "VBR MP3",
17 | "track": "02",
18 | },
19 | {
20 | "name": "track01.mp3",
21 | "title": "Track #1",
22 | "format": "VBR MP3",
23 | "track": "01",
24 | },
25 | ],
26 | "metadata": {"identifier": "album", "title": "Album", "mediatype": "audio"},
27 | }
28 |
29 | TRACKS = [
30 | models.Ref.track(name="Track #1", uri="internetarchive:album#track01.mp3"),
31 | models.Ref.track(name="Track #2", uri="internetarchive:album#track02.mp3"),
32 | ]
33 |
34 | VIEWS = [
35 | models.Ref.directory(
36 | name="Title", uri="internetarchive:directory?sort=title%20asc"
37 | ),
38 | models.Ref.directory(
39 | name="Creator", uri="internetarchive:directory?sort=creator%20asc"
40 | ),
41 | ]
42 |
43 |
44 | def test_has_root_directory(library):
45 | assert library.root_directory == models.Ref.directory(
46 | name="Internet Archive", uri="internetarchive:"
47 | )
48 |
49 |
50 | def test_browse_root_directory(library, client_mock, root_collections):
51 | results = library.browse(library.root_directory.uri)
52 | assert client_mock.search.called_once()
53 | assert results == root_collections
54 |
55 |
56 | def test_browse_collection(library, client_mock):
57 | client_mock.getitem.return_value = COLLECTION
58 | results = library.browse("internetarchive:directory")
59 | client_mock.getitem.assert_called_once_with("directory")
60 | assert results == VIEWS
61 |
62 |
63 | def test_browse_audio(library, client_mock):
64 | client_mock.getitem.return_value = ITEM
65 | results = library.browse("internetarchive:album")
66 | client_mock.getitem.assert_called_once_with("album")
67 | assert results == TRACKS
68 |
69 |
70 | def test_browse_video(library, client_mock):
71 | client_mock.getitem.return_value = {
72 | "files": [],
73 | "metadata": {"identifier": "item", "mediatype": "video"},
74 | }
75 | results = library.browse("internetarchive:album")
76 | client_mock.getitem.assert_called_once_with("album")
77 | assert results == []
78 |
79 |
80 | def test_browse_view(library, client_mock):
81 | client_mock.search.return_value = client_mock.SearchResult(
82 | {
83 | "responseHeader": {"params": {"q": "album"}},
84 | "response": {
85 | "docs": [
86 | {
87 | "identifier": "album",
88 | "title": "Album",
89 | "mediatype": "audio",
90 | },
91 | {
92 | "identifier": "directory",
93 | "title": "Directory",
94 | "mediatype": "collection",
95 | },
96 | ],
97 | "numFound": 2,
98 | },
99 | }
100 | )
101 | results = library.browse("internetarchive:audio?sort=title%20asc")
102 | assert library.backend.client.search.called_once()
103 | assert results == [
104 | models.Ref.album(name="Album", uri="internetarchive:album"),
105 | models.Ref.directory(name="Directory", uri="internetarchive:directory"),
106 | ]
107 |
108 |
109 | def test_browse_file(library, client_mock):
110 | results = library.browse("internetarchive:album#file.mp3")
111 | client_mock.getitem.assert_not_called()
112 | client_mock.search.assert_not_called()
113 | assert results == []
114 |
--------------------------------------------------------------------------------
/CHANGELOG.rst:
--------------------------------------------------------------------------------
1 | v3.0.1 (2022-04-03)
2 | ===================
3 |
4 | - Officially support Python 3.9 and 3.10.
5 |
6 |
7 | v3.0.0 (2019-12-26)
8 | ===================
9 |
10 | - Depend on final release of Mopidy 3.0.0.
11 |
12 |
13 | v3.0.0a1 (2019-12-06)
14 | =====================
15 |
16 | - Require Python >= 3.7.
17 |
18 | - Require Mopidy >= 3.0.0a5.
19 |
20 | - Require Pykka >= 2.0.1.
21 |
22 | - Update project setup.
23 |
24 |
25 | v2.0.3 (2017-06-16)
26 | ===================
27 |
28 | - Handle archive.org JSON API changes.
29 |
30 |
31 | v2.0.2 (2017-01-09)
32 | ===================
33 |
34 | - Fix file name handling.
35 |
36 |
37 | v2.0.1 (2017-01-09)
38 | ===================
39 |
40 | - Handle multiple item titles.
41 |
42 |
43 | v2.0.0 (2015-12-08)
44 | ===================
45 |
46 | - Support configurable sort criteria when browsing collections via
47 | "browse views".
48 |
49 | - Include collections in browse results.
50 |
51 | - Add support for ``LibraryProvider.get_images()``.
52 |
53 | - Drop support for deprecated ``Album.images``.
54 |
55 | - Drop special handling of bookmarks.
56 |
57 | - Cache root collections.
58 |
59 | - Update documentation.
60 |
61 |
62 | v1.3.0 (2015-09-11)
63 | ===================
64 |
65 | - Require Mopidy >= 1.1.
66 |
67 | - Use Mopidy proxy settings and HTTP User-Agent.
68 |
69 | - Fix track bitrates represented in Kbit/s.
70 |
71 | - Drop exact search support.
72 |
73 | - Only cache items.
74 |
75 |
76 | v1.2.1 (2015-03-25)
77 | ===================
78 |
79 | - Remove search query normalization.
80 |
81 | - Prepare for Mopidy v1.0 exact search API.
82 |
83 |
84 | v1.2.0 (2015-03-19)
85 | ===================
86 |
87 | - Remove playlists provider.
88 |
89 | - Add bookmarks to root directory for browsing.
90 |
91 |
92 | v1.1.0 (2014-11-19)
93 | ===================
94 |
95 | - Load bookmarks as individual playlists.
96 |
97 | - Clear library cache when refreshing playlists.
98 |
99 | - Encode filenames in URIs.
100 |
101 | - Add HTTP connection retries.
102 |
103 |
104 | v1.0.3 (2014-11-14)
105 | ===================
106 |
107 | - Fix handling of re-derived VBR MP3 files.
108 |
109 | - Remove Ogg Vorbis from default audio formats.
110 |
111 |
112 | v1.0.2 (2014-11-07)
113 | ===================
114 |
115 | - Update dependencies.
116 |
117 | - Browse Internet Archive items as albums.
118 |
119 | - Make caching optional.
120 |
121 | - Disable PNG image format in default configuration.
122 |
123 | - Temporarily disable VBR MP3 and track comments.
124 |
125 |
126 | v1.0.1 (2014-09-29)
127 | ===================
128 |
129 | - Add item descriptions as track comments.
130 |
131 | - Filter search results for exact queries.
132 |
133 |
134 | v1.0.0 (2014-09-26)
135 | ===================
136 |
137 | - Major rewrite for version 1.0.0.
138 |
139 |
140 | v0.5.0 (2014-02-28)
141 | ===================
142 |
143 | - Update `README` with link to documentation.
144 |
145 | - New config values: ``search_order``, ``browse_order``.
146 |
147 | - Allow empty queries for searching.
148 |
149 |
150 | v0.4.0 (2014-02-25)
151 | ===================
152 |
153 | - Various performance and stability improvements.
154 |
155 | - Option to exclude specific collections from searching/browsing.
156 |
157 | - Add image URLs to albums.
158 |
159 |
160 | v0.3.1 (2014-02-21)
161 | ===================
162 |
163 | - Fix default configuration.
164 |
165 |
166 | v0.3.0 (2014-02-21)
167 | ===================
168 |
169 | - Add bookmark browsing support.
170 |
171 | - Better filtering of search results.
172 |
173 | - Stability and performance improvements.
174 |
175 |
176 | v0.2.0 (2014-01-31)
177 | ===================
178 |
179 | - Add library browsing support.
180 |
181 | - Cache search results and metadata.
182 |
183 | - Properly quote/encode query terms.
184 |
185 |
186 | v0.1.0 (2014-01-24)
187 | ===================
188 |
189 | - Initial release.
190 |
--------------------------------------------------------------------------------
/tests/test_configmap.py:
--------------------------------------------------------------------------------
1 | import collections
2 | import re
3 |
4 | from mopidy.config import types
5 |
6 | import pytest
7 | from mopidy_internetarchive import ConfigMap
8 |
9 |
10 | def test_deserialize():
11 | expected = collections.OrderedDict([("a", "1"), ("b", "2"), ("c", "3")])
12 | type = ConfigMap()
13 | assert type.deserialize("a|1, b|2 , c|3") == expected
14 | assert type.deserialize("a|1\n b|2 \nc|3") == expected
15 |
16 | with pytest.raises(ValueError):
17 | type.deserialize("")
18 | with pytest.raises(ValueError):
19 | type.deserialize("a")
20 | with pytest.raises(ValueError):
21 | type.deserialize("a|")
22 | with pytest.raises(ValueError):
23 | type.deserialize("|1")
24 |
25 |
26 | def test_deserialize_choice_keys():
27 | expected = collections.OrderedDict([("a", "1"), ("b", "2"), ("c", "3")])
28 | type = ConfigMap(keys=types.String(choices=["a", "b", "c"]))
29 | assert type.deserialize("a|1, b|2 , c|3") == expected
30 | assert type.deserialize("a|1\n b|2 \nc|3") == expected
31 |
32 | with pytest.raises(ValueError):
33 | type.deserialize("x|0")
34 |
35 |
36 | def test_deserialize_integer_keys():
37 | expected = collections.OrderedDict([(1, "a"), (2, "b"), (3, "c")])
38 | type = ConfigMap(keys=types.Integer())
39 | assert type.deserialize("1|a, 2|b , 3|c") == expected
40 | assert type.deserialize("1|a\n 2|b \n3|c") == expected
41 |
42 |
43 | def test_deserialize_list_keys():
44 | expected = collections.OrderedDict([(("a",), "1"), (("b", "c"), "2")])
45 | type = ConfigMap(keys=types.List())
46 | assert type.deserialize("a|1\n b, c|2") == expected
47 |
48 |
49 | def test_deserialize_choice_values():
50 | expected = collections.OrderedDict([("a", "1"), ("b", "2"), ("c", "3")])
51 | type = ConfigMap(values=types.String(choices=["1", "2", "3"]))
52 | assert type.deserialize("a|1, b|2 , c|3") == expected
53 | assert type.deserialize("a|1\n b|2 \nc|3") == expected
54 |
55 | with pytest.raises(ValueError):
56 | type.deserialize("x|0")
57 |
58 |
59 | def test_deserialize_integer_values():
60 | expected = collections.OrderedDict([("a", 1), ("b", 2), ("c", 3)])
61 | type = ConfigMap(values=types.Integer())
62 | assert type.deserialize("a|1, b|2 , c|3") == expected
63 | assert type.deserialize("a|1\n b|2 \nc|3") == expected
64 |
65 | with pytest.raises(ValueError):
66 | type.deserialize("x|y")
67 |
68 |
69 | def test_deserialize_list_values():
70 | expected = collections.OrderedDict([("a", ("1",)), ("b", ("2", "3"))])
71 | type = ConfigMap(values=types.List())
72 | assert type.deserialize("a|1\n b|2,3") == expected
73 |
74 |
75 | def test_deserialize_optional_values():
76 | expected = collections.OrderedDict([("a", "1"), ("b", None), ("c", None)])
77 | type = ConfigMap(values=types.String(optional=True))
78 | assert type.deserialize("a|1, b| , c") == expected
79 | assert type.deserialize("a|1\n b| \nc") == expected
80 |
81 |
82 | def test_delim():
83 | expected = collections.OrderedDict([("a", "1"), ("b", "2"), ("c", "3")])
84 | type = ConfigMap(delim=":")
85 | assert type.deserialize("a:1, b: 2 , c :3") == expected
86 | assert type.deserialize("a:1\n b: 2 \nc :3") == expected
87 |
88 |
89 | def test_optional():
90 | expected = collections.OrderedDict()
91 | assert ConfigMap(optional=True).deserialize("") == expected
92 |
93 | with pytest.raises(ValueError):
94 | ConfigMap(optional=False).deserialize("")
95 | with pytest.raises(ValueError):
96 | ConfigMap().deserialize("")
97 |
98 |
99 | def test_serialize():
100 | type = ConfigMap(keys=types.String(), values=types.Integer())
101 | value = collections.OrderedDict([("a", 1), ("b", 2), ("c", 3)])
102 | result = type.serialize(value)
103 | assert re.match(r"\s*a|1\n\s*b|2\n\s*c|3", result)
104 |
105 |
106 | def test_serialize_none():
107 | type = ConfigMap(keys=types.String(), values=types.Integer())
108 | result = type.serialize(None)
109 | assert result == ""
110 |
--------------------------------------------------------------------------------
/docs/index.rst:
--------------------------------------------------------------------------------
1 | **********************
2 | Mopidy-InternetArchive
3 | **********************
4 |
5 | Mopidy-InternetArchive is a Mopidy_ extension for playing music from
6 | the `Internet Archive`_.
7 |
8 | This extension lets you search for and stream recordings ranging from
9 | `alternative news programming`_, to `Grateful Dead concerts`_, to `Old
10 | Time Radio shows`_, to `book and poetry readings`_, to `original
11 | music`_ uploaded by Internet Archive users. It also gives you access
12 | to a vast number of high-quality live recordings from the `Live Music
13 | Archive`_, and thousands of free audiobooks from the LibriVox_
14 | collection.
15 |
16 |
17 | Browsing the Internet Archive
18 | =============================
19 |
20 | If your Mopidy client supports browsing, there should be a top-level
21 | directory named *Internet Archive*. Beneath that, you will find the
22 | Internet Archive collections listed in
23 | :confval:`internetarchive/collections`, and you should be able to
24 | browse individual audio items (albums) and files (tracks) within
25 | these.
26 |
27 | For practical and performance reasons, the number of items that will
28 | be shown within a collection is limited, e.g. you will not see all
29 | 167,967 audio items of the Live Music Archive [#footnote1]_. The
30 | :ref:`default configuration ` sets this limit to 100, but
31 | this can be changed using :confval:`internetarchive/browse_limit`.
32 |
33 | To allow browsing collections using different sort criteria, every
34 | collection provides a number of *views*, virtual subdirectories which
35 | let you browse the collection's items by popularity, title, publish
36 | date, and so on. The default views are set up to resemble the
37 | archive.org_ Web interface, but can be changed at your own discretion
38 | with :confval:`internetarchive/browse_views`.
39 |
40 |
41 | Searching the Internet Archive
42 | ==============================
43 |
44 | The Internet Archive only supports searching for *items*, but not for
45 | individual files or tracks. Therefore, only *albums* will show up
46 | when searching in Mopidy. This also means that only album-related
47 | search fields are supported, so searching for track names or numbers
48 | will yield no results from the Internet Archive.
49 |
50 | The number and ordering of search results returned from the Internet
51 | Archive can be changed with :confval:`internetarchive/search_limit`
52 | and :confval:`internetarchive/search_order`. Unless you explicitly
53 | specify an Internet Archive collection to search within, search scope
54 | will also be limited to the collections listed in
55 | :confval:`internetarchive/collections`.
56 |
57 |
58 | Archive Favorites
59 | =================
60 |
61 | If you have an Internet Archive account - also termed a `Virtual
62 | Library Card`_ - you can access your `Archive Favorites`_ from Mopidy.
63 | To do so, you just need to add the identifier of your favorites
64 | collection to :confval:`internetarchive/collections`. Typically, the
65 | identifier is *fav-{username}*, but you should be able to figure it
66 | out from the archive.org_ Web site. When added to
67 | :confval:`internetarchive/collections`, you will be able to browse and
68 | search your Archive Favorites just like the other collections listed
69 | there.
70 |
71 |
72 | .. toctree::
73 | :hidden:
74 |
75 | install
76 | config
77 | changelog
78 | license
79 |
80 |
81 | .. rubric:: Footnotes
82 |
83 | .. [#footnote1] As of Jan. 9, 2017.
84 |
85 |
86 | .. _Mopidy: http://www.mopidy.com/
87 | .. _Internet Archive: http://archive.org
88 | .. _alternative news programming: https://archive.org/details/audio_news
89 | .. _Grateful Dead concerts: https://archive.org/details/GratefulDead
90 | .. _Old Time Radio shows: https://archive.org/details/radioprograms
91 | .. _book and poetry readings: https://archive.org/details/audio_bookspoetry
92 | .. _original music: https://archive.org/details/opensource_audio
93 | .. _Live Music Archive: https://archive.org/details/etree
94 | .. _LibriVox: https://archive.org/details/librivoxaudio
95 |
96 | .. _archive.org: https://archive.org/
97 | .. _Virtual Library Card: https://archive.org/account/login.createaccount.php
98 | .. _Archive Favorites: https://archive.org/bookmarks.php
99 |
--------------------------------------------------------------------------------
/docs/config.rst:
--------------------------------------------------------------------------------
1 | *************
2 | Configuration
3 | *************
4 |
5 | This extension has a number of configuration values that can be
6 | tweaked. However, the :ref:`default configuration ` contains
7 | everything to get you up and running, and will usually require only a
8 | few modifications to match personal preferences.
9 |
10 |
11 | .. _confvals:
12 |
13 | Configuration Values
14 | ====================
15 |
16 | .. confval:: internetarchive/enabled
17 |
18 | Whether this extension should be enabled or not.
19 |
20 | .. confval:: internetarchive/base_url
21 |
22 | Base URL to access the Internet Archive.
23 |
24 | .. confval:: internetarchive/collections
25 |
26 | A list of collection identifiers to show as top-level directories
27 | when browsing. These are also used to limit the search scope when
28 | no search base is given by Mopidy clients explicitly.
29 |
30 | .. confval:: internetarchive/audio_formats
31 |
32 | A list of audio file formats, in order of preference.
33 |
34 | This entry contains a list of `Internet Archive file formats`_. By
35 | default, only audio formats suitable for streaming are requested.
36 | Note that the Internet Archive also contains a large number of
37 | high-quality media files in FLAC_ and other lossless formats, but
38 | for sake of bandwidth (both your's and the Archive's), it is
39 | recommended that you stick to lossy audio formats for streaming
40 | through Mopidy.
41 |
42 | .. confval:: internetarchive/image_formats
43 |
44 | A list of image file formats, in order of preference.
45 |
46 | This entry contains a list of `Internet Archive file formats`_ to
47 | be considered when providing images for Internet Archive items.
48 | Note that some Mopidy clients, especially MPD clients, will ignore
49 | album art provided by Mopidy-InternetArchive or other Mopidy
50 | extensions.
51 |
52 | .. confval:: internetarchive/browse_limit
53 |
54 | The maximum number of browse results.
55 |
56 | This is used to limit the number of items returned when browsing
57 | the Internet Archive.
58 |
59 | .. confval:: internetarchive/browse_views
60 |
61 | When browsing Internet Archive collections (or *directories* in
62 | Mopidy), this provides a list of virtual subdirectories so results
63 | can be retrieved using a particular :ref:`sort order`.
64 |
65 | The format for each entry is `` (asc|desc) |