Skip to content

Commit

Permalink
Added user folders
Browse files Browse the repository at this point in the history
  • Loading branch information
redshodan committed Dec 27, 2018
1 parent f474dd1 commit fd59259
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 26 deletions.
13 changes: 0 additions & 13 deletions TODO.rst
Original file line number Diff line number Diff line change
Expand Up @@ -87,10 +87,6 @@ Partial
<shortcut id="11" name="Audio books"/>
<shortcut id="10" name="Podcasts"/>

- getArtists:
resp:
- missing covertArt

- getArtist:
resp:
- handle playCount for album
Expand All @@ -106,14 +102,5 @@ Partial
- getCoverArt:
params: missing size to convert image to

- getUser:
resp: Needs folders

- getUsers:
resp: Needs folders

- createUser:
resp: Needs folders

- getGenres:
resp: Needs album counts
8 changes: 6 additions & 2 deletions test/bin/tester
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import argparse


host = "http://0.0.0.0:6543/"
auth = "u=test&t=c06db68e819be6ec3d26c6038d8e8d1f&s=12345"
auth_test = "u=test&t=c06db68e819be6ec3d26c6038d8e8d1f&s=12345"
auth_admin = "u=admin&t=7488e331b8b64e5794da3fa4eb10ad5d&s=12345"


parser = argparse.ArgumentParser(description='Make API calls to Unsonic.')
Expand All @@ -16,6 +17,8 @@ parser.add_argument('params', type=str, nargs='*',
help='Parameters for the endpoint')
parser.add_argument('-s', '--shares', action='store_true', default=False,
help='Use the /shares/ endpoints.')
parser.add_argument('-a', '--admin', action='store_true', default=False,
help='Use admin user')

args = parser.parse_args()

Expand All @@ -28,7 +31,8 @@ else:
outfile = tempfile.NamedTemporaryFile()

cmd = "wget --quiet -O %s '%s%s/%s?%s" % (outfile.name, host, args.root,
args.endpoint, auth)
args.endpoint,
auth_admin if args.admin else auth_test)

for param in args.params:
cmd += "&" + param
Expand Down
2 changes: 1 addition & 1 deletion test/xsd/unsonic-subsonic-api.xsd
Original file line number Diff line number Diff line change
Expand Up @@ -641,7 +641,7 @@

<xs:complexType name="User">
<xs:sequence>
<xs:element name="folder" type="xs:int" minOccurs="0" maxOccurs="unbounded"/> <!-- Added in 1.12.0 -->
<xs:element name="folder" type="sub:FolderID" minOccurs="0" maxOccurs="unbounded"/> <!-- Added in 1.12.0 -->
</xs:sequence>
<xs:attribute name="username" type="xs:string" use="required"/>
<xs:attribute name="email" type="xs:string" use="optional"/> <!-- Added in 1.6.0 -->
Expand Down
40 changes: 40 additions & 0 deletions unsonic/alembic/versions/2fd8e4ce261c_added_user_folders.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
"""Added user folders
Revision ID: 2fd8e4ce261c
Revises: 33bb0c204f1e
Create Date: 2018-12-26 07:14:46.955048
"""
from alembic import op
import sqlalchemy as sa


# revision identifiers, used by Alembic.
revision = '2fd8e4ce261c'
down_revision = '33bb0c204f1e'
branch_labels = None
depends_on = None


def upgrade():
# UserFolder
op.create_table(
'un_userfolders',
sa.Column('id', sa.Integer(), nullable=False),
sa.Column('user_id', sa.Integer(), nullable=False),
sa.Column('lib_id', sa.Integer(), nullable=False),
sa.ForeignKeyConstraint(['user_id'], ['un_users.id'],
name=op.f('fk_un_userfolders_user_id_un_users'),
ondelete='CASCADE'),
sa.ForeignKeyConstraint(['lib_id'], ['libraries.id'],
name=op.f('fk_un_userfolders_lib_id_libraries'),
ondelete='CASCADE'),
sa.PrimaryKeyConstraint('id', name=op.f('pk_un_userfolders'))
)
op.create_index('userfolders_user_index', 'un_userfolders', ['user_id'],
unique=False)


def downgrade():
op.drop_index('userfolders_user_index', table_name='un_userfolders')
op.drop_table('un_userfolders')
1 change: 1 addition & 0 deletions unsonic/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ def __init__(self, db_user, db_user_config):
self.db_user_config = {u.key: u.value for u in db_user_config}
self._lastfm = None
self.listening = None
self.folders = [f for f in db_user.folders]

def __getattr__(self, name):
from .config import CONFIG
Expand Down
25 changes: 22 additions & 3 deletions unsonic/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,8 @@ class User(Base, OrmObject):
passive_deletes=True)
scrobbles = relation("Scrobble", cascade="all, delete-orphan",
passive_deletes=True)
folders = relation("UserFolder", cascade="all, delete-orphan",
passive_deletes=True)

@staticmethod
def loadTable(session):
Expand Down Expand Up @@ -312,6 +314,22 @@ def __table_args__(cls):
return (Index("scrobble_user_index", "user_id"), )


class UserFolder(Base, OrmObject):
__tablename__ = "un_userfolders"

id = Column(Integer, Sequence("un_userfolders_id_seq"), primary_key=True)
user_id = Column(Integer, ForeignKey("un_users.id", ondelete='CASCADE'),
nullable=False)
lib_id = Column(Integer, ForeignKey("libraries.id", ondelete='CASCADE'),
nullable=False)
user = relation("User")
lib = relation("Library")

@declared_attr
def __table_args__(cls):
return (Index("userfolders_user_index", "user_id"), )


class Share(Base, OrmObject):
__tablename__ = "un_shares"

Expand Down Expand Up @@ -763,6 +781,7 @@ def updatePseudoRatings(session, user_id=None, album_id=ALL, artist_id=ALL):
from . import auth, web # noqa: E402


UN_TYPES = [DBInfo, Config, UserConfig, User, Role, PlayQueue, PlayList,
PlayListUser, PlayListTrack, ArtistRating, AlbumRating, TrackRating,
PlayCount, Scrobble, Share, ShareEntry, Bookmark, InternetRadio]
UN_TYPES = [DBInfo, Config, UserConfig, User, UserFolder, Role, PlayQueue,
PlayList, PlayListUser, PlayListTrack, ArtistRating, AlbumRating,
TrackRating, PlayCount, Scrobble, Share, ShareEntry, Bookmark,
InternetRadio]
8 changes: 7 additions & 1 deletion unsonic/views/rest/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
from ...version import PROTOCOL_VERSION, UNSONIC_PROTOCOL_VERSION
from ...models import (Session, ArtistRating, AlbumRating, TrackRating,
Artist, Album, Track, PlayList, Share, Bookmark,
InternetRadio, Image)
InternetRadio, Image, Library)
from ...auth import Roles
from ... import lastfm

Expand Down Expand Up @@ -497,6 +497,8 @@ def fillID(row):
return f"ir-{row.id}"
elif isinstance(row, Image):
return f"im-{row.id}"
elif isinstance(row, Library):
return f"fl-{row.id}"
else:
raise MissingParam(f"Unknown ID type: {type(row)}")

Expand Down Expand Up @@ -698,6 +700,10 @@ def fillUser(session, row):
for role in Roles.subsonic_roles:
user.set("%sRole" % role,
"true" if role in row.roles else "false")
for folder in row.folders:
f = ET.Element("folder")
f.text = fillID(folder.lib)
user.append(f)
return user


Expand Down
8 changes: 6 additions & 2 deletions unsonic/views/rest/createuser.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from . import Command, registerCmd, InternalError, NoPerm, bool_t, bitrate_t
from ...models import User, Role
from . import folder_t
from ...models import User, Role, UserFolder
from ...auth import Roles


Expand All @@ -24,7 +25,7 @@ class CreateUser(Command):
"podcastRole": {"type": bool_t},
"shareRole": {"type": bool_t},
"videoConversionRole": {"type": bool_t},
# "musicFolderId": {}, # TODO
"musicFolderId": {"type": folder_t, "multi": True},
}
dbsess = True
role_names = {"adminRole": Roles.ADMIN, "settingsRole": Roles.SETTINGS,
Expand Down Expand Up @@ -78,4 +79,7 @@ def handleReq(self, session):
session.add(Role(user_id=user.id, name=Roles.USERS))
session.add(Role(user_id=user.id, name=Roles.REST))

for folder in self.params["musicFolderId"]:
session.add(UserFolder(user_id=user.id, lib_id=folder))

return self.makeResp()
11 changes: 7 additions & 4 deletions unsonic/views/rest/getuser.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,24 @@
from . import Command, registerCmd, NoPerm, fillUser
from . import Command, registerCmd, NoPerm, fillUser, str_t
from ...models import getUserByName


@registerCmd
class GetUser(Command):
name = "getUser.view"
param_defs = {
"username": {"required": True},
"username": {"type": str_t},
}
dbsess = True


def handleReq(self, session):
if self.req.authed_user.name == self.params["username"]:
uname = self.params["username"]
if not uname:
user = self.req.authed_user
elif self.req.authed_user.name == uname:
user = self.req.authed_user
elif self.req.authed_user.isAdmin():
user = getUserByName(session, self.params["username"])
user = getUserByName(session, uname)
else:
raise NoPerm("Can not view a user other than yourself unless you "
"are an admin")
Expand Down

0 comments on commit fd59259

Please sign in to comment.