Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 2 additions & 6 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ classifiers = [
]
dynamic = ["version"]
dependencies = [
"mopidy >= 4.0.0a1",
"mopidy >= 4.0.0a3",
"pykka >= 4",
"setuptools >= 66",
"uritools >= 4",
Expand Down Expand Up @@ -82,20 +82,15 @@ ignore = [
"ANN401", # any-type
"BLE001", # blind-except # TODO
"D", # pydocstyle
"EM101", # raw-string-in-exception # TODO
"FIX001", # line-contains-fixme
"FIX002", # line-contains-todo
"G002", # logging-percent-format
"G004", # logging-f-string
"ISC001", # single-line-implicit-string-concatenation
"PTH123", # builtin-open # TODO
"RUF012", # mutable-class-default # TODO
"S101", # assert # TODO
"TD002", # missing-todo-author
"TD003", # missing-todo-link
"TRY003", # raise-vanilla-args
"TRY400", # error-instead-of-exception
"UP031", # printf-string-formatting # TODO
#
# Conflicting with `ruff format`
"COM812", # missing-trailing-comma
Expand All @@ -112,6 +107,7 @@ ignore = [
"PLR2004", # magic-value-comparison
"PT011", # pytest-raises-too-broad # TODO
"PT027", # pytest-unittest-raises-assertion
"RUF012", # mutable-class-default # TODO
"S101", # assert
"SLF001", # private-member-access
"TRY002", # raise-vanilla-class
Expand Down
3 changes: 2 additions & 1 deletion src/mopidy_local/actor.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import logging
from typing import ClassVar

import pykka
from mopidy import backend
Expand All @@ -12,7 +13,7 @@


class LocalBackend(pykka.ThreadingActor, backend.Backend):
uri_schemes = [UriScheme("local")]
uri_schemes: ClassVar[list[UriScheme]] = [UriScheme("local")]

def __init__(self, config, audio):
super().__init__()
Expand Down
36 changes: 19 additions & 17 deletions src/mopidy_local/library.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

import uritools
from mopidy import backend, models
from mopidy.models import Ref, SearchResult
from mopidy.models import Ref, RefType, SearchResult
from mopidy.types import Uri

from . import Extension, schema
Expand Down Expand Up @@ -52,12 +52,13 @@
def lookup(self, uri):
try:
if uri.startswith("local:album"):
return list(schema.lookup(self._connect(), Ref.ALBUM, uri))
return list(schema.lookup(self._connect(), RefType.ALBUM, uri))

Check warning on line 55 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L55

Added line #L55 was not covered by tests
if uri.startswith("local:artist"):
return list(schema.lookup(self._connect(), Ref.ARTIST, uri))
return list(schema.lookup(self._connect(), RefType.ARTIST, uri))

Check warning on line 57 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L57

Added line #L57 was not covered by tests
if uri.startswith("local:track"):
return list(schema.lookup(self._connect(), Ref.TRACK, uri))
raise ValueError("Invalid lookup URI") # noqa: TRY301
return list(schema.lookup(self._connect(), RefType.TRACK, uri))
msg = "Invalid lookup URI"
raise ValueError(msg) # noqa: TRY301

Check warning on line 61 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L60-L61

Added lines #L60 - L61 were not covered by tests
except Exception as e:
logger.error("Lookup error for %s: %s", uri, e)
return []
Expand All @@ -72,7 +73,8 @@
return self._browse_artist(uri)
if uri.startswith("local:album"):
return self._browse_album(uri)
raise ValueError("Invalid browse URI") # noqa: TRY301
msg = "Invalid browse URI"
raise ValueError(msg) # noqa: TRY301

Check warning on line 77 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L76-L77

Added lines #L76 - L77 were not covered by tests
except Exception as e:
logger.error("Error browsing %s: %s", uri, e)
return []
Expand Down Expand Up @@ -125,27 +127,27 @@
return self._connection

def _browse_album(self, uri, order=("disc_no", "track_no", "name")):
return schema.browse(self._connect(), Ref.TRACK, order, album=uri)
return schema.browse(self._connect(), RefType.TRACK, order, album=uri)

Check warning on line 130 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L130

Added line #L130 was not covered by tests

def _browse_artist(self, uri, order=("type", "name COLLATE NOCASE")):
with self._connect() as c:
albums = schema.browse(c, Ref.ALBUM, order, albumartist=uri)
albums = schema.browse(c, RefType.ALBUM, order, albumartist=uri)

Check warning on line 134 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L134

Added line #L134 was not covered by tests
refs = schema.browse(c, order=order, artist=uri)
album_uris, tracks = {ref.uri for ref in albums}, []
for ref in refs:
if ref.type == Ref.ALBUM and ref.uri not in album_uris:
if ref.type == RefType.ALBUM and ref.uri not in album_uris:

Check warning on line 138 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L138

Added line #L138 was not covered by tests
albums.append(
Ref.directory(
uri=uritools.uricompose(
"local",
None,
"directory",
dict(type=Ref.TRACK, album=ref.uri, artist=uri), # noqa: C408
dict(type=RefType.TRACK, album=ref.uri, artist=uri), # noqa: C408
),
name=ref.name,
),
)
elif ref.type == Ref.TRACK:
elif ref.type == RefType.TRACK:

Check warning on line 150 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L150

Added line #L150 was not covered by tests
tracks.append(ref)
else:
logger.debug("Skipped SQLite browse result %s", ref.uri)
Expand All @@ -166,29 +168,29 @@

# Fix #38: keep sort order of album tracks; this also applies
# to composers and performers
if type_ == Ref.TRACK and "album" in query:
if type_ == RefType.TRACK and "album" in query:

Check warning on line 171 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L171

Added line #L171 was not covered by tests
order = ("disc_no", "track_no", "name")
if type_ == Ref.ARTIST and self._config["use_artist_sortname"]:
if type_ == RefType.ARTIST and self._config["use_artist_sortname"]:

Check warning on line 173 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L173

Added line #L173 was not covered by tests
order = ("coalesce(sortname, name) COLLATE NOCASE",)
roles = role or ("artist", "albumartist") # TODO: re-think 'roles'...

refs = []
for ref in schema.browse(self._connect(), type_, order, role=roles, **query):
if ref.type == Ref.TRACK or (not query and not role):
if ref.type == RefType.TRACK or (not query and not role):

Check warning on line 179 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L179

Added line #L179 was not covered by tests
refs.append(ref)
elif ref.type == Ref.ALBUM:
elif ref.type == RefType.ALBUM:

Check warning on line 181 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L181

Added line #L181 was not covered by tests
refs.append(
Ref.directory(
uri=uritools.uricompose(
"local",
None,
"directory",
dict(query, type=Ref.TRACK, album=ref.uri),
dict(query, type=RefType.TRACK, album=ref.uri),
),
name=ref.name,
),
)
elif ref.type == Ref.ARTIST:
elif ref.type == RefType.ARTIST:

Check warning on line 193 in src/mopidy_local/library.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/library.py#L193

Added line #L193 was not covered by tests
refs.append(
Ref.directory(
uri=uritools.uricompose(
Expand Down
100 changes: 50 additions & 50 deletions src/mopidy_local/schema.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
import re
import sqlite3

from mopidy.models import Album, Artist, Image, Ref, Track
from mopidy.models import Album, Artist, Image, Ref, RefType, Track

_IMAGE_SIZE_RE = re.compile(r".*-(\d+)x(\d+)\.(?:png|gif|jpeg)$")

Expand All @@ -20,37 +20,34 @@
"""

_BROWSE_QUERIES = {
None: """
SELECT CASE WHEN album.uri IS NULL THEN '%s' ELSE '%s' END AS type,
None: f"""
SELECT CASE WHEN album.uri IS NULL THEN
'{RefType.TRACK}' ELSE '{RefType.ALBUM}' END AS type,
coalesce(album.uri, track.uri) AS uri,
coalesce(album.name, track.name) AS name
FROM track LEFT OUTER JOIN album ON track.album = album.uri
WHERE %%s
WHERE %s
GROUP BY coalesce(album.uri, track.uri)
ORDER BY %%s
""" # noqa: S608
% (Ref.TRACK, Ref.ALBUM),
Ref.ALBUM: """
SELECT '%s' AS type, uri AS uri, name AS name
ORDER BY %s
""", # noqa: S608
RefType.ALBUM: f"""
SELECT '{RefType.ALBUM}' AS type, uri AS uri, name AS name
FROM album
WHERE %%s
ORDER BY %%s
""" # noqa: S608
% Ref.ALBUM,
Ref.ARTIST: """
SELECT '%s' AS type, uri AS uri, name AS name
WHERE %s
ORDER BY %s
""", # noqa: S608
RefType.ARTIST: f"""
SELECT '{RefType.ARTIST}' AS type, uri AS uri, name AS name
FROM artist
WHERE %%s
ORDER BY %%s
""" # noqa: S608
% Ref.ARTIST,
Ref.TRACK: """
SELECT '%s' AS type, uri AS uri, name AS name
WHERE %s
ORDER BY %s
""", # noqa: S608
RefType.TRACK: f"""
SELECT '{RefType.TRACK}' AS type, uri AS uri, name AS name
FROM track
WHERE %%s
ORDER BY %%s
""" # noqa: S608
% Ref.TRACK,
WHERE %s
ORDER BY %s
""", # noqa: S608
}

_BROWSE_FILTERS = {
Expand All @@ -64,7 +61,7 @@
"performer": "track.performers = ?",
"max-age": "track.last_modified >= (strftime('%s', 'now') - ?) * 1000",
},
Ref.ARTIST: {
RefType.ARTIST: {
"role": {
"albumartist": """EXISTS (
SELECT * FROM album WHERE album.artists = artist.uri
Expand All @@ -80,7 +77,7 @@
)""",
},
},
Ref.ALBUM: {
RefType.ALBUM: {
"albumartist": "artists = ?",
"artist": """? IN (
SELECT artists FROM track WHERE album = album.uri
Expand All @@ -104,7 +101,7 @@
AND last_modified >= (strftime('%s', 'now') - ?) * 1000
)""",
},
Ref.TRACK: {
RefType.TRACK: {
"album": "album = ?",
"albumartist": """? IN (
SELECT artists FROM album WHERE uri = track.album
Expand All @@ -119,13 +116,13 @@
}

_LOOKUP_QUERIES = {
Ref.ALBUM: """
RefType.ALBUM: """
SELECT * FROM tracks WHERE album_uri = ?
""",
Ref.ARTIST: """
RefType.ARTIST: """
SELECT * FROM tracks WHERE ? IN (artist_uri, albumartist_uri)
""",
Ref.TRACK: """
RefType.TRACK: """
SELECT * FROM tracks WHERE uri = ?
""",
}
Expand Down Expand Up @@ -187,14 +184,16 @@
while user_version != schema_version:
if user_version:
logger.info("Upgrading SQLite database schema v%s", user_version)
filename = "upgrade-v%s.sql" % user_version
filename = f"upgrade-v{user_version}.sql"

Check warning on line 187 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L187

Added line #L187 was not covered by tests
else:
logger.info("Creating SQLite database schema v%s", schema_version)
filename = "schema.sql"
with open(sql_dir / filename) as fh:
with (sql_dir / filename).open() as fh:
c.executescript(fh.read())
new_version = c.execute("PRAGMA user_version").fetchone()[0]
assert new_version != user_version
if new_version == user_version:
msg = "Database schema upgrade failed"
raise AssertionError(msg)

Check warning on line 196 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L195-L196

Added lines #L195 - L196 were not covered by tests
user_version = new_version
return user_version

Expand All @@ -205,24 +204,23 @@

def list_distinct(c, field, query=()):
if field not in _SEARCH_FIELDS:
raise LookupError("Invalid search field: %s" % field)
sql = (
"""
SELECT DISTINCT %s AS field
msg = f"Invalid search field: {field}"
raise LookupError(msg)

Check warning on line 208 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L207-L208

Added lines #L207 - L208 were not covered by tests
sql = f"""
SELECT DISTINCT {field} AS field
FROM search
WHERE field IS NOT NULL
""" # noqa: S608
% field
)
terms = []
params = []
for key, value in query:
if key == "any":
terms.append("? IN (%s)" % ",".join(_SEARCH_FIELDS))
terms.append("? IN ({})".format(",".join(_SEARCH_FIELDS)))

Check warning on line 218 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L218

Added line #L218 was not covered by tests
elif key in _SEARCH_FIELDS:
terms.append("%s = ?" % key)
terms.append(f"{key} = ?")

Check warning on line 220 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L220

Added line #L220 was not covered by tests
else:
raise LookupError("Invalid query field: %s" % key)
msg = f"Invalid query field: {key}"
raise LookupError(msg)

Check warning on line 223 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L222-L223

Added lines #L222 - L223 were not covered by tests
params.append(value)
if terms:
sql += " AND " + " AND ".join(terms)
Expand Down Expand Up @@ -277,12 +275,12 @@
for kwargs in filters:
f, p = _filters(_SEARCH_FILTERS, **kwargs)
if f:
clauses.append("(%s)" % " AND ".join(f))
clauses.append("({})".format(" AND ".join(f)))
params.extend(p)
else:
logger.debug("Skipped SQLite search filter %r", kwargs)
if clauses:
sql += " AND (%s)" % " OR ".join(clauses)
sql += " AND ({})".format(" OR ".join(clauses))
sql += " LIMIT ? OFFSET ?"
params += [limit, offset]
logger.debug("SQLite search query %r: %s", params, sql)
Expand Down Expand Up @@ -454,11 +452,12 @@
params = []
for field, value in query:
if field == "any":
terms.append("? IN (%s)" % ",".join(_SEARCH_FIELDS))
terms.append("? IN ({})".format(",".join(_SEARCH_FIELDS)))

Check warning on line 455 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L455

Added line #L455 was not covered by tests
elif field in _SEARCH_FIELDS:
terms.append("%s = ?" % field)
terms.append(f"{field} = ?")

Check warning on line 457 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L457

Added line #L457 was not covered by tests
else:
raise LookupError("Invalid search field: %s" % field)
msg = f"Invalid search field: {field}"
raise LookupError(msg)

Check warning on line 460 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L459-L460

Added lines #L459 - L460 were not covered by tests
params.append(value)
return (_SEARCH_SQL % ("search", " AND ".join(terms)), params)

Expand All @@ -470,9 +469,10 @@
if field == "any":
terms.append(_SEARCH_SQL % ("fts", "fts MATCH ?"))
elif field in _SEARCH_FIELDS:
terms.append(_SEARCH_SQL % ("fts", "%s MATCH ?" % field))
terms.append(_SEARCH_SQL % ("fts", f"{field} MATCH ?"))
else:
raise LookupError("Invalid search field: %s" % field)
msg = f"Invalid search field: {field}"
raise LookupError(msg)

Check warning on line 475 in src/mopidy_local/schema.py

View check run for this annotation

Codecov / codecov/patch

src/mopidy_local/schema.py#L474-L475

Added lines #L474 - L475 were not covered by tests
params.append(value)
return (" INTERSECT ".join(terms), params)

Expand Down
Loading
Loading