コード例 #1
0
def remove_song_from_playlist(playlistid, songid):
    """Removes a given song to a specified playlist.

    Arguments:
        playlistid (str): Integer string identifying a unique playlist.
        songid (str): Integer string identifying a unique song.
    """
    with access_db() as db_conn:
        try:
            song = db_conn.query(Song).get(songid)
            playlist = db_conn.query(Playlist).get(playlistid)
        except:
            logger.warn(
                f"Exception encountered while trying to access db to remove song {songid} from playlist {playlistid}"
            )
            return
        else:
            if not song:
                logger.warn(f"Song {songid} does not exist.")
                return
            if not playlist:
                logger.warn(f"Playlist {playlistid} does not exist.")
                return

            try:
                playlist.songs.remove(song)
            except ValueError:
                pass
            else:
                db_conn.commit()
コード例 #2
0
def add_song_to_playlist(playlistid, songid):
    """Adds a given song to a specified playlist.

    Arguments:
        playlistid (str): Integer string identifying a unique playlist.
        songid (str): Integer string identifying a unique song.
    """
    with access_db() as db_conn:
        try:
            song = db_conn.query(Song).get(songid)
            playlist = db_conn.query(Playlist).get(playlistid)
        except:
            logger.warn(
                f"Exception encountered while trying to access db to add song {songid} to playlst {playlistid}"
            )
            return
        else:
            if not song:
                logger.warn(f"Song {songid} does not exist.")
                return
            if not playlist:
                logger.warn(f"Playlist {playlistid} does not exist.")
                return

            playlist.songs.append(song)
            db_conn.commit()
コード例 #3
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def update_username(input_uuid, username):
    """Update the username of a given user.

    Because we will support username changes, this is how that will be accomplished.

    Arguments:
        input_uuid (uuid): UUID associated with the account that should have its username changed.
        username (str): Value to change the account's username to.

    Returns:
        success (bool): True if the username was changed successfully, False otherwise.
    """
    success = False
    if username_taken(username):
        logger.critical(
            f'Attempted to update username of user with UUID {input_uuid} to {username}, even though it was taken already.'
        )
        return success
    with access_db() as db_conn:
        result = db_conn.query(User).\
                         get(input_uuid)
        if not user:
            logger.critical(
                f'Attempted to update username for user that doesn\'t exist with UUID {input_uuid}.'
            )
            return success
        result.username = username
        db_conn.commit()
        success = True
        return success
    return success
コード例 #4
0
def get_playlists_for_user(user_guid):
    """Fetch public playlists, and playlists owned by a specific user.

    Arguments:
        user_guid (uuid): UUID identifying the specific user to fetch playlists for.
    """
    result = {'playlists': []}
    with access_db() as db_conn:
        accessible = db_conn.query(Playlist)\
                       .filter(
                            or_(Playlist.owner_guid==user_guid,
                                Playlist.public==True)
                        )
        for playlist in accessible:
            owner_name = db_conn.query(User.username)\
                .filter(User.guid==user_guid)\
                .scalar()
            data = {
                'id': playlist.id,
                'name': playlist.name,
                'owner_name': owner_name,
                'public': playlist.public,
            }
            result['playlists'].append(data)
    return result
コード例 #5
0
def owns_playlist(playlistid, owner_guid):
    """Check if a given user owns a specific playlist.

    Arguments:
        playlistid (str): Integer string identifying the playlist.
        owner_guid (uuid): UUID identifying the user to check against.
    """
    # Ensure both playlist and owner_guid are not None
    if (not playlistid) or (not owner_guid):
        logger.warn(
            f"Trying to check ownership with invalid playlistid of owner_guid."
        )
        return False

    with access_db() as db_conn:
        try:
            playlist = db_conn.query(Playlist).get(playlistid)
        except:
            logger.warn(
                f"Exception encountered while trying to access playlist id: {playlistid}."
            )
            return False
        else:
            if not playlist:
                logger.warn(f"No playlist found with id {playlistid}.")
                return False
            if playlist.owner_guid == owner_guid:
                return True

    logger.warn(
        f"User with guid {owner_guid} attempted to modify playlist they do not own with id {playlistid}."
    )
    return False
コード例 #6
0
def fetch_track_info(songid):
    """Fetch detailed information about a given track.

    Arguments:
        songid (str): Integer string identifying the track to fetch information on.
    """
    with access_db() as db_conn:
        try:
            track = db_conn.query(Song).get(songid)
        except:
            logger.warn(
                f"Exception encountered while trying to access song id: {songid}"
            )
            return None
        else:
            if not track:
                logger.warn(f"No song found with id {songid}.")
                return None
            return {
                'title': track.track_name,
                'artist': track.artist_name,
                'album': track.album_name,
                'track_length': track.track_length,
                'id': track.id,
            }
コード例 #7
0
def refresh_database_thread():
    """Walk through all files in the music folder, adding them to the database as necessary."""
    logger.info('Started refreshing.')

    try:
        # This handles directory walking, it's kind of nasty to use this iterator
        for dirpath, dirname, filename in os.walk(MUSIC_FOLDER):
            for f in filename:
                # Do nothing with non-mp3 tracks
                if not f.endswith('.mp3'):
                    continue
                full_file_path = os.path.join(dirpath, f)
                track_info = load_track_data(full_file_path)
                if track_info:
                    add_track_to_database(track_info)
    except Exception as e:
        logger.warn('Exception encountered while refreshing database.')
        logger.warn(e)
        raise e
    finally:
        # Return to a non-refreshing state once finished.
        with access_db() as db_conn:
            try:
                state = db_conn.query(RefreshState).one()
                state.is_refreshing = False
                db_conn.commit()
            except MultipleResultsFound as e:
                state = db_conn.query(RefreshState).first().delete()
                db_conn.commit()
                logger.info('Refreshing finished!')
                return
        logger.info('Refreshing finished!')
コード例 #8
0
def clean_database():
    """Remove all missing tracks from the database."""
    with access_db() as db_conn:
        missing_tracks = db_conn.query(Song)\
                                .filter(Song.file_missing == True)
        for track in missing_tracks:
            db_conn.delete(track)
        db_conn.commit()
コード例 #9
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def get_all_admins():
    """Fetch all admin emails from database."""
    result = []
    with access_db() as db_conn:
        all_admins = db_conn.query(User).filter(User.admin == True).all()
        for admin in all_admins:
            result.append((admin.email, admin.username))

    return result
コード例 #10
0
def remove_track_from_database(track_id):
    """Remove a song from the database."""
    with access_db() as db_conn:
        track = db_conn.query(Song).get(track_id)
        if track:
            db_conn.delete(track)
            db_conn.commit()
            return True
    return False
コード例 #11
0
def create_new_playlist(playlist_name, owner_guid, owner_name):
    """Create a new playlist for a user.

    Arguments:
        playlist_name (str): Name for the new playlist.
        owner_guid (uuid): UUID of the user that is creating the playlist.
    """
    with access_db() as db_conn:
        new_playlist = Playlist(name=playlist_name, owner_guid=owner_guid)
        db_conn.add(new_playlist)
        db_conn.commit()
コード例 #12
0
def label_track_missing(track_path, missing):
    """Update the track_missing flag in the database.

    Arguments:
        track_path (str): The path to the track that is missing.
    """
    with access_db() as db_conn:
        track = db_conn.query(Song)\
                       .filter(Song.track_path==track_path)\
                       .first()
        if track:
            track.file_missing = missing
            db_conn.commit()
コード例 #13
0
def get_playlist_data_from_id(playlistid):
    """Fetch information about a given playlist, as well as its tracks, from a unique id.

    Arguments:
        playlistid (str): Integer string identifying a unique playlist.

    Returns:
        playlist_data (dict): Dictionary containing information about the playlist, as well as its contents.
    """
    if not playlistid:
        logger.warn(f"Trying to access playlist without id.")
        return None
    with access_db() as db_conn:
        try:
            playlist = db_conn.query(Playlist)\
                              .get(playlistid)
        except:
            logger.warn(
                f"Exception encountered while trying to access playlist with id {playlistid}"
            )
            return None
        else:
            if not playlist:
                return None
            owner_name = db_conn.query(User.username)\
                .filter(User.guid==playlist.owner_guid)\
                .scalar()
            playlist_data = {
                'tracks': [],
                'owner_name': owner_name,
                'name': playlist.name,
                'public': playlist.public,
            }
            if playlist.songs:
                # Sorts by artist name, then album name, then track name.
                sorted_songs = sorted(
                    playlist.songs,
                    key=lambda x:
                    ((x.artist_name and x.artist_name.lower() or ''),
                     (x.album_name and x.album_name.lower() or ''),
                     (x.track_name and x.track_name.lower() or '')))
                for track in sorted_songs:
                    track_info = {
                        'title': track.track_name,
                        'artist': track.artist_name,
                        'album': track.album_name,
                        'id': track.id,
                        'length': track.track_length
                    }
                    playlist_data['tracks'].append(track_info)
            return playlist_data
コード例 #14
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def fetch_user_by_uuid(input_uuid):
    """Fetch the user entry in the database associated with the provided UUID.
    
    Arguments:
        input_uuid (UUID): The UUID to look up.

    Returns:
        result (User or None): The User object associated with the provided UUID if it exists, else None.
    """
    with access_db() as db_conn:
        result = db_conn.query(User).\
                         filter(User.guid==input_uuid).\
                         first()
        return result
コード例 #15
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def fetch_user_by_username(username):
    """Fetch the user entry in the database associated with the provided username.
    
    Arguments:
        username (str): The username to look up.

    Returns:
        result (User or None): The User object associated with the provided username if it exists, else None.
    """
    with access_db() as db_conn:
        result = db_conn.query(User).\
                         filter(User.username==username).\
                         first()
        return result
コード例 #16
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def fetch_user_by_email(email):
    """Fetch the user entry in the database associated with the provided email.

    Arguments:
        email (str): The email account to lookup.

    Returns:
        result (User or None): The User object associated with the provided email if it exists, else None.
    """
    with access_db() as db_conn:
        result = db_conn.query(User).\
                         filter(User.email==email).\
                         first()
        return result
コード例 #17
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def log_login_attempt(email, success, ip):
    """Create a database entry when a user attempts to login.

    Arguments:
        email (str): The email the login is attempted for.
        success (str): Whether the login was successful or not.
        ip (ipaddress): The ip address that the login attempt occurred from.
    """
    with access_db() as db_conn:
        curr_time = datetime.datetime.now()
        login_attempt = LoginAttempt(email=email,
                                     success=success,
                                     source_ip=ip,
                                     attempt_time=curr_time)
        db_conn.add(login_attempt)
        db_conn.commit()
コード例 #18
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def username_taken(username):
    """Check if a given username already exists in the database.

    Arguments:
        username (str): The username to look up.

    Returns:
        result (bool): Whether or not the user exists in the database.
    """
    with access_db() as db_conn:
        result = db_conn.query(User).\
                         filter(User.username==username).\
                         first()
        if result:
            return True
        return False
コード例 #19
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def create_full_user(email, username, password):
    """Creates a user in the database given an email, username, and password.

    Arguments:
        email (str): The email to register the account with.
        username (str): The username to register the account with.
        password (str): The password to register the account with.
    """
    with access_db() as db_conn:
        generated_uuid = uuid.uuid1()
        storable_password_hash = create_storable_password(password)
        user = User(email=email,
                    guid=generated_uuid,
                    username=username,
                    password_hash=storable_password_hash)
        db_conn.add(user)
        db_conn.commit()
        return generated_uuid
コード例 #20
0
def fetch_track_hash(songid):
    """Fetch the file hash for a given track id.
    
    Arguments:
        songid (str): Integer string identifying the song to fetch the file hash for.
    """
    with access_db() as db_conn:
        try:
            track = db_conn.query(Song).get(songid)
        except:
            logger.warn(
                f"Exception encountered while trying to access song id: {songid}"
            )
            return None
        else:
            if not track:
                logger.warn(f"No song found with id {songid}.")
                return None
            return track.track_hash
コード例 #21
0
def get_playlists_owned_by_user(user_guid):
    """Fetch playlists owned by a specific user.

    Arguments:
        user_guid (uuid): UUID identifying the specific user to fetch playlists for.
    """
    result = {'playlists': []}
    with access_db() as db_conn:
        accessible = db_conn.query(Playlist)\
                       .filter(Playlist.owner_guid==user_guid)
        for playlist in accessible:
            data = {
                'id': playlist.id,
                'name': playlist.name,
                'owner_name': playlist.owner_name,
                'public': playlist.public,
            }
            result['playlists'].append(data)
    return result
コード例 #22
0
def check_file_missing(songid):
    """Check whether a specific track's file is missing in the database.

    Arguments:
        songid (int): ID for the track to be checked.
    """
    with access_db() as db_conn:
        try:
            track = db_conn.query(Song).get(songid)
        except:
            logger.warn(
                f"Exception encountered while trying to access song id: {songid}"
            )
            return None
        else:
            if not track:
                logger.warn(f"No song found with id {songid}.")
                return None
            return track.file_missing
コード例 #23
0
def set_playlist_publicity(playlistid, publicity):
    """Set a playlist to public or private."""
    with access_db() as db_conn:
        try:
            playlist = db_conn.query(Playlist).get(playlistid)
        except:
            logger.warn(
                f'Exception encountered while trying to fetch playlist {playlistid}'
            )
            return False
        else:
            if not playlist:
                logger.warn(f'Playlist {playlistid} does not exist.')
                return False

            playlist.public = publicity
            db_conn.commit()

    return True
コード例 #24
0
def get_playlist_from_id(playlistid):
    """Fetch the ORM object for a given playlist id.

    Arguments:
        playlistid (str): Integer string identifying a unique playlist.
    """
    if not playlistid:
        logger.warn(f"Trying to access playlist without id.")
        return None
    with access_db() as db_conn:
        try:
            playlist = db_conn.query(Playlist).get(playlistid)
        except:
            logger.warn(
                f"Exception encountered while trying to access playlist with id {playlistid}"
            )
            return None
        else:
            return playlist
コード例 #25
0
def get_public_playlists():
    """Fetch publicly available playlists."""
    result = {'playlists': []}
    with access_db() as db_conn:
        # Filter all playlists by publicity.
        public = db_conn.query(Playlist)\
                       .filter(Playlist.public==True)
        for playlist in public:
            # Fetch the playlist owner in order to get their username
            owner_name = db_conn.query(User.username)\
                .filter(User.guid==playlist.owner_guid)\
                .scalar()
            data = {
                'id': playlist.id,
                'name': playlist.name,
                'owner_name': owner_name,
                'public': playlist.public,
            }
            result['playlists'].append(data)
    return result
コード例 #26
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def make_user_admin(email):
    """Make a user with a given email address an admin.

    Arguments:
        email (string): The email address of the user to enable admin privileges on.

    Returns:
        success (bool): True if the user exists, and was made an admin; false otherwise.
    """
    success = False
    with access_db() as db_conn:
        result = db_conn.query(User).\
                         filter(User.email==email).\
                         first()
        try:
            result.admin = True
            db_conn.commit()
            success = True
        except AttributeError:
            logger.warning('Attempted to make a non-existant user an admin.')
    return success
コード例 #27
0
ファイル: util.py プロジェクト: tfdahlin/lindele
def update_password(input_uuid, old_password, new_password):
    """Update the password of a given user.

    Arguments:
        input_uuid (uuid): UUID associated with the account that should have its password changed.
        old_password (str): Old password of the user, used for verification of request.
        new_password (str): New password of the user.

    Returns:
        success (bool): True if password change was successful, False otherwise.
    """
    # Fetch the user, fail if it doesn't exist.
    success = False
    user = fetch_user_by_uuid(input_uuid)
    if not user:
        return success

    # Make sure that the old password matches the stored password, fail if not.
    passwords_match = check_password(old_password, user.password_hash)
    if not passwords_match:
        return success

    # Generate a new hash, and store it for the user.
    storable_password_hash = create_storable_password(password)

    with access_db() as db_conn:
        user = db_conn.query(User).\
                         filter(User.guid==input_uuid).\
                         first()
        if not user:
            # This should never happen, unless there is a database race condition for some reason.
            # For safety sake, we make the check anyway.
            return success
        user.password_hash = storable_password_hash
        db_conn.commit()
        success = True
        return success

    return success
コード例 #28
0
def get_all_tracks():
    """Fetch track info for all songs in the database."""
    with access_db() as db_conn:
        result = []
        all_tracks = db_conn.query(Song).filter(
            Song.file_missing == False).all()

        # Sort by artist name, then album name, then track name.
        all_tracks.sort(key=lambda x: (x.artist_name and x.artist_name.lower(
        ) or '', x.album_name and x.album_name.lower() or '', x.track_name and
                                       x.track_name.lower() or ''))
        for track in all_tracks:
            track_info = {
                'title': track.track_name,
                'artist': track.artist_name,
                'album': track.album_name,
                'id': track.id,
                'track_length': track.track_length
            }
            result.append(track_info)
        return result
    return None
コード例 #29
0
def add_track_to_database(track_info):
    """Add a track to the database.

    Arguments:
        track_info (dict): Specific track information to be entered into the database.
    """
    track_path = track_info['track_path']
    track_hash = track_info['track_hash']
    with access_db() as db_conn:
        # Make sure the track doesn't already exist
        # First by checking the track path
        exists = db_conn.query(Song)\
                        .filter(Song.track_path==track_path)\
                        .first()
        if exists:
            return False
        else:
            # Then by checking the track hash
            exists = db_conn.query(Song)\
                            .filter(Song.track_hash==track_hash)\
                            .first()
            if exists and exists.file_missing:
                # If the track hash exists, and the file is missing, update its path
                exists.track_path = track_path
                exists.file_missing = False
                db_conn.commit()
                return False

        # Create and add ORM object to the database
        song = Song(track_name=track_info['title'],
                    artist_name=track_info['artist'],
                    album_name=track_info['album'],
                    track_path=track_info['track_path'],
                    track_length=track_info['track_length'],
                    track_hash=track_info['track_hash'])
        db_conn.add(song)
        db_conn.commit()
        return True
    return False
コード例 #30
0
def refresh_database():
    """Update the song database.

    Uses a single database entry to decide whether or not it's allowed to update the database.
    This is only allowed once every 5 minutes, at max.
    """
    with access_db() as db_conn:
        try:
            entry = db_conn.query(RefreshState).one()
        except MultipleResultsFound as e:
            logger.info('Multiple rows found. Deleting and refreshing.')
            for db_entry in db_conn.query(RefreshState).all():
                db_conn.delete(db_entry)
            db_conn.commit()
        except NoResultFound as e:
            logger.info('No rows found. Creating.')
            state = RefreshState(last_refresh=datetime.datetime.now())
            db_conn.add(state)
            db_conn.commit()
            async_refresh()
        else:
            if entry.last_refresh:
                # If the database has been refreshed at least once, check that it's been 5 minutes.
                delta = datetime.datetime.now() - entry.last_refresh
                if delta > datetime.timedelta(minutes=5):
                    # If 5 minutes have passed, allow an update.
                    async_refresh()
                    entry.last_refresh = datetime.datetime.now()
                    db_conn.commit()
                else:
                    logger.info(
                        f'Not enough time has passed since last refresh. Time delta: {delta}'
                    )
                    logger.info(f'Last refresh: {entry.last_refresh}')
            else:
                # If the database has never been refreshed, then go for it
                async_refresh()
                entry.last_refresh = datetime.datetime.now()
                db_conn.commit()