Esempio n. 1
0
    def api_fetchalbumart(self, value):
        cherrypy.session.release_lock()
        params = json.loads(value)
        directory = params['directory']

        #try getting a cached album art image
        b64imgpath = albumArtFilePath(directory)
        img_data = self.albumartcache_load(b64imgpath)
        if img_data:
            cherrypy.response.headers["Content-Length"] = len(img_data)
            return img_data

        #try getting album art inside local folder
        fetcher = albumartfetcher.AlbumArtFetcher()
        localpath = os.path.join(cherry.config['media.basedir'], directory)
        header, data, resized = fetcher.fetchLocal(localpath)

        if header:
            if resized:
                #cache resized image for next time
                self.albumartcache_save(b64imgpath, data)
            cherrypy.response.headers.update(header)
            return data
        elif cherry.config['media.fetch_album_art']:
            #fetch album art from online source
            album = os.path.basename(directory)
            artist = os.path.basename(os.path.dirname(directory))
            keywords = artist + ' ' + album
            log.i("Fetching album art for keywords '%s'" % keywords)
            header, data = fetcher.fetch(keywords)
            if header:
                cherrypy.response.headers.update(header)
                self.albumartcache_save(b64imgpath, data)
                return data
        cherrypy.HTTPRedirect("/res/img/folder.png", 302)
Esempio n. 2
0
 def addUser(self, username, password, admin):
     if not (username.strip() or password.strip()):
         log.d(_('empty username or password!'))
         return False
     user = User.create(username, password, admin)
     try:
         exists = self.conn.execute(
             'SELECT username'
             ' FROM users WHERE lower(username) = lower(?)',
             (username, )).fetchone()
         if (not exists):
             self.conn.execute(
                 '''
             INSERT INTO users
             (username, admin, password, salt)
             VALUES (?,?,?,?)''', (user.name, 1 if user.isadmin else 0,
                                   user.password, user.salt))
         else:
             raise sqlite3.IntegrityError
     except sqlite3.IntegrityError:
         log.e('cannot create user "%s", already exists!' % user.name)
         return False
     self.conn.commit()
     log.i('added user: ' + user.name)
     return True
Esempio n. 3
0
 def createOrAlterTable(self, sqlconn):
     updatedTable = False
     #table exists?
     if sqlconn.execute("""SELECT name FROM sqlite_master
         WHERE type='table' AND name=? """, (self.tablename,)).fetchall():
         dbtablelayout = sqlconn.execute("""PRAGMA table_info('%s')""" % self.tablename).fetchall()
         #map dict to column name
         dbtablelayout = dict((col[1], col) for col in dbtablelayout)
         #remove columns from db when not in template
         for columnname in dbtablelayout.keys():
             if columnname not in self.columns:
                 #can't do this in sqlite...
                 #log.i('Dropping column %s from table %s' % (columnname, self.tablename))
                 #sqlconn.execute("""ALTER TABLE %s DROP COLUMN %s"""%(self.tablename, columnname))
                 #updatedTable = True
                 pass
             else:
                 log.d('Column %s in table %s exists and needs no change' % (columnname, self.tablename))
         #add new columns to db when not in db
         for templatecolumnname, templatecolumn in self.columns.items():
             if templatecolumnname not in dbtablelayout.keys():
                 log.i('Adding column %s to table %s' % (templatecolumnname, self.tablename))
                 sqlconn.execute("""ALTER TABLE %s ADD COLUMN %s""" % (self.tablename, templatecolumn.sql()))
                 updatedTable = True
             else:
                 log.d('Column %s in table %s exists and needs no change' % (templatecolumnname, self.tablename))
         #TODO add checks for DEFAULT value and NOT NULL
     else:
         log.i('Creating table %s' % self.tablename)
         sqlconn.execute("""CREATE TABLE %s (%s)""" % (self.tablename, ', '.join(map(lambda x: x.sql(), self.columns.values()))))
         updatedTable = True
     return updatedTable
Esempio n. 4
0
def ensure_current_version(dbname=None, autoconsent=False, consentcallback=None):
    """Make sure all defined databases exist and are up to date.

    Will connect to all these databases and try to update their layout, if
    necessary, possibly asking the user for consent.

    dbname : str
        When given, only make sure of the database with that name.
    autoconsent : bool
        When ``True``, don't ask for consent, ever.
    consentcallback: callable
        Called when an update requires user consent; if the return value
        does not evaluate to ``True``, abort don't run any updates and
        return ``False``. If no callback is given or autoconsent == True,
        the value of autoconsent will be used to decide if the update
        should run.
    Returns : bool
        ``True`` if requirements are met.
    """
    if autoconsent or consentcallback is None:
        consentcallback = lambda _: autoconsent
    with MultiUpdater(dbname) as update:
        if update.needed:
            if update.requires_consent and not consentcallback(update.prompts):
                return False
            log.w("Database schema update; don't turn off the program!")
            update.run()
            log.i("Database schema update complete.")
    return True
Esempio n. 5
0
    def api_fetchalbumart(self, directory):
        cherrypy.session.release_lock()

        #try getting a cached album art image
        b64imgpath = albumArtFilePath(directory)
        img_data = self.albumartcache_load(b64imgpath)
        if img_data:
            cherrypy.response.headers["Content-Length"] = len(img_data)
            return img_data

        #try getting album art inside local folder
        fetcher = albumartfetcher.AlbumArtFetcher()
        localpath = os.path.join(cherry.config['media.basedir'], directory)
        header, data, resized = fetcher.fetchLocal(localpath)

        if header:
            if resized:
                #cache resized image for next time
                self.albumartcache_save(b64imgpath, data)
            cherrypy.response.headers.update(header)
            return data
        elif cherry.config['media.fetch_album_art']:
            #fetch album art from online source
            album = os.path.basename(directory)
            artist = os.path.basename(os.path.dirname(directory))
            keywords = artist+' '+album
            log.i("Fetching album art for keywords '%s'" % keywords)
            header, data = fetcher.fetch(keywords)
            if header:
                cherrypy.response.headers.update(header)
                self.albumartcache_save(b64imgpath, data)
                return data
        cherrypy.HTTPRedirect("/res/img/folder.png", 302)
Esempio n. 6
0
    def remove_recursive(self, fileobj, progress=None):
        '''recursively remove fileobj and all its children from the media db.'''


        if progress is None:
            log.i(
                  _('removing dead reference(s): %s "%s"'),
                  'directory' if fileobj.isdir else 'file',
                  fileobj.relpath,
                  )
            factory = None
            remove = lambda item: self.remove_file(item)
        else:
            def factory(new, pnt):
                if pnt is None:
                    return (new, None, progress)
                return (new, pnt, pnt[2].spawnchild('[-] ' + new.relpath))
            remove = lambda item: (self.remove_file(item[0]), item[2].tick())

        deld = 0
        try:
            with self.conn:
                for item in self.db_recursive_filelister(fileobj, factory):
                    remove(item)
                    deld += 1
        except Exception as e:
            log.e(_('error while removing dead reference(s): %s'), e)
            log.e(_('rolled back to safe state.'))
            return 0
        else:
            return deld
Esempio n. 7
0
 def index(self, *args, **kwargs):
     self.getBaseUrl(redirect_unencrypted=True)
     firstrun = 0 == self.userdb.getUserCount();
     if debug:
         #reload pages everytime in debig mode
         self.mainpage = readRes('res/main.html')
         self.loginpage = readRes('res/login.html')
         self.firstrunpage = readRes('res/firstrun.html')
     if 'login' in kwargs:
         username = kwargs.get('username','')
         password = kwargs.get('password','')
         login_action = kwargs.get('login','')
         if login_action == 'login':
             self.session_auth(username, password)
             if cherrypy.session['username']:
                 log.i('user ' + cherrypy.session['username'] + ' just logged in.')
         elif login_action == 'create admin user':
             if firstrun:
                 if username.strip() and password.strip():
                     self.userdb.addUser(username, password, True)
                     self.session_auth(username, password)
                     return self.mainpage
             else:
                 return "No, you can't."
     if firstrun:
         return self.firstrunpage
     else:
         if self.isAuthorized():
             return self.mainpage
         else:
             return self.loginpage
Esempio n. 8
0
 def index(self, *args, **kwargs):
     self.getBaseUrl(redirect_unencrypted=True)
     firstrun = 0 == self.userdb.getUserCount()
     show_page = self.mainpage  # generated main.html from devel.html
     if "devel" in kwargs:
         # reload pages everytime in devel mode
         show_page = readRes("res/devel.html")
         self.loginpage = readRes("res/login.html")
         self.firstrunpage = readRes("res/firstrun.html")
     if "login" in kwargs:
         username = kwargs.get("username", "")
         password = kwargs.get("password", "")
         login_action = kwargs.get("login", "")
         if login_action == "login":
             self.session_auth(username, password)
             if cherrypy.session["username"]:
                 username = cherrypy.session["username"]
                 log.i(_("user {name} just logged in.").format(name=username))
         elif login_action == "create admin user":
             if firstrun:
                 if username.strip() and password.strip():
                     self.userdb.addUser(username, password, True)
                     self.session_auth(username, password)
                     return show_page
             else:
                 return "No, you can't."
     if firstrun:
         return self.firstrunpage
     else:
         if self.isAuthorized():
             return show_page
         else:
             return self.loginpage
Esempio n. 9
0
 def update_word_occurrences(self):
     log.i(_('updating word occurrences...'))
     with closing(
             self.conn.execute('''UPDATE dictionary SET occurrences = (
             select count(*) from search WHERE search.drowid = dictionary.rowid
         )''')):
         pass
Esempio n. 10
0
 def update_word_occurrences(self):
     log.i("updating word occurrences...")
     self.conn.execute(
         """UPDATE dictionary SET occurrences = (
             select count(*) from search WHERE search.drowid = dictionary.rowid
         )"""
     )
Esempio n. 11
0
    def remove_recursive(self, fileobj, progress=None):
        '''recursively remove fileobj and all its children from the media db.'''


        if progress is None:
            log.i(
                  _('removing dead reference(s): %s "%s"'),
                  'directory' if fileobj.isdir else 'file',
                  fileobj.relpath,
                  )
            factory = None
            remove = lambda item: self.remove_file(item)
        else:
            def factory(new, pnt):
                if pnt is None:
                    return (new, None, progress)
                return (new, pnt, pnt[2].spawnchild('[-] ' + new.relpath))
            remove = lambda item: (self.remove_file(item[0]), item[2].tick())

        deld = 0
        try:
            with self.conn:
                for item in self.db_recursive_filelister(fileobj, factory):
                    remove(item)
                    deld += 1
        except Exception as e:
            log.e(_('error while removing dead reference(s): %s'), e)
            log.e(_('rolled back to safe state.'))
            return 0
        else:
            return deld
Esempio n. 12
0
def ensure_current_version(dbname=None,
                           autoconsent=False,
                           consentcallback=None):
    '''Make sure all defined databases exist and are up to date.

    Will connect to all these databases and try to update their layout, if
    necessary, possibly asking the user for consent.

    dbname : str
        When given, only make sure of the database with that name.
    autoconsent : bool
        When ``True``, don't ask for consent, ever.
    consentcallback: callable
        Called when an update requires user consent; if the return value
        does not evaluate to ``True``, abort don't run any updates and
        return ``False``. If no callback is given or autoconsent == True,
        the value of autoconsent will be used to decide if the update
        should run.
    Returns : bool
        ``True`` if requirements are met.
    '''
    if autoconsent or consentcallback is None:
        consentcallback = lambda _: autoconsent
    with MultiUpdater(dbname) as update:
        if update.needed:
            if update.requires_consent and not consentcallback(update.prompts):
                return False
            log.w("Database schema update; don't turn off the program!")
            update.run()
            log.i('Database schema update complete.')
    return True
Esempio n. 13
0
    def update_db_recursive(self, fullpath, skipfirst=False):
        '''recursively update the media database for a path in basedir'''

        from collections import namedtuple
        Item = namedtuple('Item', 'infs indb parent progress')
        def factory(fs, db, parent):
            fileobj = fs if fs is not None else db
            name = fileobj.relpath or fileobj.fullpath if fileobj else '<path not found in filesystem or database>'
            if parent is None:
                progress = ProgressTree(name=name)
                maxlen = lambda s: util.trim_to_maxlen(50, s)
                progress.reporter = ProgressReporter(lvl=1, namefmt=maxlen)
            else:
                progress = parent.progress.spawnchild(name)
            return Item(fs, db, parent, progress)

        log.d(_('recursive update for %s'), fullpath)
        generator = self.enumerate_fs_with_db(fullpath, itemfactory=factory)
        skipfirst and generator.send(None)
        adds_without_commit = 0
        add = 0
        deld = 0
        try:
            with self.conn:
                for item in generator:
                    infs, indb, progress = (item.infs, item.indb, item.progress)
                    if infs and indb:
                        if infs.isdir != indb.isdir:
                            progress.name = '[±] ' + progress.name
                            deld += self.remove_recursive(indb, progress)
                            self.register_file_with_db(infs)
                            adds_without_commit = 1
                        else:
                            infs.uid = indb.uid
                            progress.name = '[=] ' + progress.name
                    elif indb:
                        progress.name = '[-] ' + progress.name
                        deld += self.remove_recursive(indb, progress)
                        adds_without_commit = 0
                        continue    # progress ticked by remove; don't tick again
                    elif infs:
                        self.register_file_with_db(item.infs)
                        adds_without_commit += 1
                        progress.name = '[+] ' + progress.name
                    else:
                        progress.name = '[?] ' + progress.name
                    if adds_without_commit == AUTOSAVEINTERVAL:
                        self.conn.commit()
                        add += adds_without_commit
                        adds_without_commit = 0
                    progress.tick()
        except Exception as exc:
            log.e(_("error while updating media: %s %s"), exc.__class__.__name__, exc)
            log.e(_("rollback to previous commit."))
            traceback.print_exc()
            raise exc
        finally:
            add += adds_without_commit
            log.i(_('items added %d, removed %d'), add, deld)
            self.load_db_to_memory()
Esempio n. 14
0
    def db_find_file_by_path(self, fullpath, create=False):
        '''Finds an absolute path in the file database. If found, returns
        a File object matching the database record; otherwise, returns None.
        Paths matching a media basedir are a special case: these will yield a
        File object with an invalid record id matching the one listed by its
        children.
        '''
        assert os.path.isabs(fullpath)
        basedir = cherry.config['media.basedir']
        if not fullpath.startswith(basedir):
            return None

        relpath = fullpath[len(basedir):].strip(os.path.sep)
        root = File(basedir, isdir=True, uid= -1)
        if not relpath:
            return root

        file = root
        for part in relpath.split(os.path.sep):
            found = False
            for child in self.fetch_child_files(file):  # gotta be ugly: don't know if name/ext split in db
                if part == child.basename:
                    found = True
                    file = child
                    break
            if not found:
                if create:
                    file = File(part, parent=file)
                    log.i('creating database entry for %r', file.relpath)
                    self.register_file_with_db(file)
                else:
                    return None
        return file
Esempio n. 15
0
 def index(self, *args, **kwargs):
     self.getBaseUrl(redirect_unencrypted=True)
     firstrun = 0 == self.userdb.getUserCount()
     show_page = self.mainpage #generated main.html from devel.html
     if 'devel' in kwargs:
         #reload pages everytime in devel mode
         show_page = readRes('res/devel.html')
         self.loginpage = readRes('res/login.html')
         self.firstrunpage = readRes('res/firstrun.html')
     if 'login' in kwargs:
         username = kwargs.get('username', '')
         password = kwargs.get('password', '')
         login_action = kwargs.get('login', '')
         if login_action == 'login':
             self.session_auth(username, password)
             if cherrypy.session['username']:
                 username = cherrypy.session['username']
                 log.i(_('user {name} just logged in.').format(name=username))
         elif login_action == 'create admin user':
             if firstrun:
                 if username.strip() and password.strip():
                     self.userdb.addUser(username, password, True)
                     self.session_auth(username, password)
                     return show_page
             else:
                 return "No, you can't."
     if firstrun:
         return self.firstrunpage
     else:
         if self.isAuthorized():
             return show_page
         else:
             return self.loginpage
Esempio n. 16
0
 def index(self, *args, **kwargs):
     self.getBaseUrl(redirect_unencrypted=True)
     firstrun = 0 == self.userdb.getUserCount()
     show_page = self.mainpage #generated main.html from devel.html
     if 'devel' in kwargs:
         #reload pages everytime in devel mode
         show_page = readRes('res/devel.html')
         self.loginpage = readRes('res/login.html')
         self.firstrunpage = readRes('res/firstrun.html')
     if 'login' in kwargs:
         username = kwargs.get('username', '')
         password = kwargs.get('password', '')
         login_action = kwargs.get('login', '')
         if login_action == 'login':
             self.session_auth(username, password)
             if cherrypy.session['username']:
                 username = cherrypy.session['username']
                 log.i(_('user {name} just logged in.').format(name=username))
         elif login_action == 'create admin user':
             if firstrun:
                 if username.strip() and password.strip():
                     self.userdb.addUser(username, password, True)
                     self.session_auth(username, password)
                     return show_page
             else:
                 return "No, you can't."
     if firstrun:
         return self.firstrunpage
     else:
         if self.isAuthorized():
             return show_page
         else:
             return self.loginpage
Esempio n. 17
0
    def update_db_recursive(self, fullpath, skipfirst=False):
        '''recursively update the media database for a path in basedir'''

        from collections import namedtuple
        Item = namedtuple('Item', 'infs indb parent progress')
        def factory(fs, db, parent):
            fileobj = fs if fs is not None else db
            name = fileobj.relpath or fileobj.fullpath if fileobj else '<path not found in filesystem or database>'
            if parent is None:
                progress = ProgressTree(name=name)
                maxlen = lambda s: util.trim_to_maxlen(50, s)
                progress.reporter = ProgressReporter(lvl=1, namefmt=maxlen)
            else:
                progress = parent.progress.spawnchild(name)
            return Item(fs, db, parent, progress)

        log.d(_('recursive update for %s'), fullpath)
        generator = self.enumerate_fs_with_db(fullpath, itemfactory=factory)
        skipfirst and generator.send(None)
        adds_without_commit = 0
        add = 0
        deld = 0
        try:
            with self.conn:
                for item in generator:
                    infs, indb, progress = (item.infs, item.indb, item.progress)
                    if infs and indb:
                        if infs.isdir != indb.isdir:
                            progress.name = '[±] ' + progress.name
                            deld += self.remove_recursive(indb, progress)
                            self.register_file_with_db(infs)
                            adds_without_commit = 1
                        else:
                            infs.uid = indb.uid
                            progress.name = '[=] ' + progress.name
                    elif indb:
                        progress.name = '[-] ' + progress.name
                        deld += self.remove_recursive(indb, progress)
                        adds_without_commit = 0
                        continue    # progress ticked by remove; don't tick again
                    elif infs:
                        self.register_file_with_db(item.infs)
                        adds_without_commit += 1
                        progress.name = '[+] ' + progress.name
                    else:
                        progress.name = '[?] ' + progress.name
                    if adds_without_commit == AUTOSAVEINTERVAL:
                        self.conn.commit()
                        add += adds_without_commit
                        adds_without_commit = 0
                    progress.tick()
        except Exception as exc:
            log.e(_("error while updating media: %s %s"), exc.__class__.__name__, exc)
            log.e(_("rollback to previous commit."))
            traceback.print_exc()
            raise exc
        finally:
            add += adds_without_commit
            log.i(_('items added %d, removed %d'), add, deld)
            self.load_db_to_memory()
Esempio n. 18
0
    def db_find_file_by_path(self, fullpath, create=False):
        '''Finds an absolute path in the file database. If found, returns
        a File object matching the database record; otherwise, returns None.
        Paths matching a media basedir are a special case: these will yield a
        File object with an invalid record id matching the one listed by its
        children.
        '''
        assert os.path.isabs(fullpath)
        basedir = cherry.config['media.basedir']
        if not fullpath.startswith(basedir):
            return None

        relpath = fullpath[len(basedir):].strip(os.path.sep)
        root = File(basedir, isdir=True, uid=-1)
        if not relpath:
            return root

        file = root
        for part in relpath.split(os.path.sep):
            found = False
            for child in self.fetch_child_files(
                    file):  # gotta be ugly: don't know if name/ext split in db
                if part == child.basename:
                    found = True
                    file = child
                    break
            if not found:
                if create:
                    file = File(part, parent=file)
                    log.i('creating database entry for %r', file.relpath)
                    self.register_file_with_db(file)
                else:
                    return None
        return file
Esempio n. 19
0
    def setup_config(self, createNewConfig, browsersetup, cfg_override):
        """start the in-browser configuration server, create a config if
        no configuration is found or provide migration help for old CM
        versions

        initialize the configuration if no config setup is needed/requested
        """
        if browsersetup:
            port = cfg_override.pop('server.port', False)
            cherrymusicserver.browsersetup.configureAndStartCherryPy(port)
        if createNewConfig:
            newconfigpath = pathprovider.configurationFile() + '.new'
            cfg.write_to_file(cfg.from_defaults(), newconfigpath)
            log.i(_('New configuration file was written to:{br}{path}').format(
                path=newconfigpath,
                br=os.linesep
            ))
            sys.exit(0)
        if not pathprovider.configurationFileExists():
            if pathprovider.fallbackPathInUse():   # temp. remove @ v0.30 or so
                self.printMigrationNoticeAndExit()
            else:
                cfg.write_to_file(cfg.from_defaults(), pathprovider.configurationFile())
                self.printWelcomeAndExit()
        self._init_config(cfg_override)
Esempio n. 20
0
    def api_fetchalbumart(self, directory):
        _save_and_release_session()
        default_folder_image = "../res/img/folder.png"

        log.i('Fetching album art for: %s' % directory)
        filepath = os.path.join(cherry.config['media.basedir'], directory)

        if os.path.isfile(filepath):
            # if the given path is a file, try to get the image from ID3
            tag = TinyTag.get(filepath, image=True)
            image_data = tag.get_image()
            if image_data:
                log.d('Image found in tag.')
                header = {'Content-Type': 'image/jpg', 'Content-Length': len(image_data)}
                cherrypy.response.headers.update(header)
                return image_data
            else:
                # if the file does not contain an image, display the image of the
                # parent directory
                directory = os.path.dirname(directory)

        #try getting a cached album art image
        b64imgpath = albumArtFilePath(directory)
        img_data = self.albumartcache_load(b64imgpath)
        if img_data:
            cherrypy.response.headers["Content-Length"] = len(img_data)
            return img_data

        #try getting album art inside local folder
        fetcher = albumartfetcher.AlbumArtFetcher()
        localpath = os.path.join(cherry.config['media.basedir'], directory)
        header, data, resized = fetcher.fetchLocal(localpath)

        if header:
            if resized:
                #cache resized image for next time
                self.albumartcache_save(b64imgpath, data)
            cherrypy.response.headers.update(header)
            return data
        elif cherry.config['media.fetch_album_art']:
            #fetch album art from online source
            try:
                foldername = os.path.basename(directory)
                keywords = foldername
                log.i(_("Fetching album art for keywords {keywords!r}").format(keywords=keywords))
                header, data = fetcher.fetch(keywords)
                if header:
                    cherrypy.response.headers.update(header)
                    self.albumartcache_save(b64imgpath, data)
                    return data
                else:
                    # albumart fetcher failed, so we serve a standard image
                    raise cherrypy.HTTPRedirect(default_folder_image, 302)
            except:
                # albumart fetcher threw exception, so we serve a standard image
                raise cherrypy.HTTPRedirect(default_folder_image, 302)
        else:
            # no local album art found, online fetching deactivated, show default
            raise cherrypy.HTTPRedirect(default_folder_image, 302)
Esempio n. 21
0
 def createIndex(self, sqlconn, columns):
     for c in columns:
         if not c in self.columns:
             raise IndexError('column %s does not exist in table %s, cannot create index!' % (c, self.tablename))
     existing_indexes = map(itemgetter(0), sqlconn.execute("""SELECT name FROM sqlite_master WHERE type='index' ORDER BY name""").fetchall())
     indexname = '_'.join(['idx', self.tablename, '_'.join(columns)])
     if not indexname in existing_indexes:
         log.i('Creating index %s' % indexname)
         sqlconn.execute('CREATE INDEX IF NOT EXISTS %s ON %s(%s)' % (indexname, self.tablename, ', '.join(columns)))
Esempio n. 22
0
 def run(self):
     """Update database schema to the highest possible version."""
     self._validate_locked()
     log.i('%r: updating database schema', self.name)
     log.d('from version %r to %r', self._version, self._target)
     if None is self._version:
         self._init_with_version(self._target)
     else:
         for version in self._updates_due:
             self._update_to_version(version)
Esempio n. 23
0
 def session_auth(self, username, password):
     user = self.userdb.auth(username, password)
     allow_remote = cherry.config["server.permit_remote_admin_login"]
     is_loopback = cherrypy.request.remote.ip in ("127.0.0.1", "::1")
     if not is_loopback and user.isadmin and not allow_remote:
         log.i(_("Rejected remote admin login from user: {name}").format(name=user.name))
         user = userdb.User.nobody()
     cherrypy.session["username"] = user.name
     cherrypy.session["userid"] = user.uid
     cherrypy.session["admin"] = user.isadmin
Esempio n. 24
0
 def run(self):
     """Update database schema to the highest possible version."""
     self._validate_locked()
     log.i('%r: updating database schema', self.name)
     log.d('from version %r to %r', self._version, self._target)
     if None is self._version:
         self._init_with_version(self._target)
     else:
         for version in self._updates_due:
             self._update_to_version(version)
Esempio n. 25
0
 def session_auth(self, username, password):
     user = self.userdb.auth(username, password)
     allow_remote = cherry.config['server.permit_remote_admin_login']
     is_loopback = cherrypy.request.remote.ip in ('127.0.0.1', '::1')
     if not is_loopback and user.isadmin and not allow_remote:
         log.i(_('Rejected remote admin login from user: {name}').format(name=user.name))
         user = userdb.User.nobody()
     cherrypy.session['username'] = user.name
     cherrypy.session['userid'] = user.uid
     cherrypy.session['admin'] = user.isadmin
Esempio n. 26
0
 def session_auth(self, username, password):
     user = self.userdb.auth(username, password)
     allow_remote = cherry.config['server.permit_remote_admin_login']
     is_loopback = cherrypy.request.remote.ip in ('127.0.0.1', '::1')
     if not is_loopback and user.isadmin and not allow_remote:
         log.i(_('Rejected remote admin login from user: {name}').format(name=user.name))
         user = userdb.User.nobody()
     cherrypy.session['username'] = user.name
     cherrypy.session['userid'] = user.uid
     cherrypy.session['admin'] = user.isadmin
Esempio n. 27
0
 def __init__(self, db_file, table_to_dump):
     log.i("Loading files database into memory...")
     self.db = sqlite3.connect(":memory:", check_same_thread=False)
     cu = self.db.cursor()
     cu.execute("attach database '" + db_file + "' as attached_db")
     cu.execute("select sql from attached_db.sqlite_master " "where type='table' and name='" + table_to_dump + "'")
     sql_create_table = cu.fetchone()[0]
     cu.execute(sql_create_table)
     cu.execute("insert into " + table_to_dump + " select * from attached_db." + table_to_dump)
     self.db.commit()
     cu.execute("detach database attached_db")
Esempio n. 28
0
    def full_update(self):
        '''verify complete media database against the filesystem and make
        necesary changes.'''

        log.i(_('running full update...'))
        try:
            self.update_db_recursive(cherry.config['media.basedir'], skipfirst=True)
        except:
            log.e(_('error during media update. database update incomplete.'))
        finally:
            self.update_word_occurrences()
            log.i(_('media database update complete.'))
Esempio n. 29
0
    def full_update(self):
        """verify complete media database against the filesystem and make
        necesary changes."""

        log.i("running full update...")
        try:
            self.update_db_recursive(cherry.config["media.basedir"], skipfirst=True)
        except:
            log.e("error during media update. database update incomplete.")
        finally:
            self.update_word_occurrences()
            log.i("media database update complete.")
Esempio n. 30
0
    def full_update(self):
        '''verify complete media database against the filesystem and make
        necesary changes.'''

        log.i(_('running full update...'))
        try:
            self.update_db_recursive(cherry.config['media.basedir'], skipfirst=True)
        except:
            log.e(_('error during media update. database update incomplete.'))
        finally:
            self.update_word_occurrences()
            log.i(_('media database update complete.'))
Esempio n. 31
0
 def __init__(self, db_file, table_to_dump):
     log.i(_("Loading files database into memory..."))
     self.db = sqlite3.connect(':memory:', check_same_thread=False)
     cu = self.db.cursor()
     cu.execute('attach database "%s" as attached_db' % db_file)
     cu.execute("select sql from attached_db.sqlite_master "
                "where type='table' and name='" + table_to_dump + "'")
     sql_create_table = cu.fetchone()[0]
     cu.execute(sql_create_table);
     cu.execute("insert into " + table_to_dump +
                " select * from attached_db." + table_to_dump)
     self.db.commit()
     cu.execute("detach database attached_db")
Esempio n. 32
0
    def full_update(self):
        '''verify complete media database against the filesystem and make
        necesary changes.'''

        log.i('running full update...')

        try:
            self.update_db_recursive(cherry.config.media.basedir.str, skipfirst=True)
        except:
            log.e('error during media update. database update incomplete.')
        finally:
            self.__create_index_if_non_exist()
            self.update_word_occurences()
            log.i('media database update complete.')
Esempio n. 33
0
def migrate_databases():
    """ Makes sure CherryMusic's databases are up to date, migrating them if
        necessary.

        This might prompt the user for consent if a migration requires it and
        terminate the program if no consent is obtained.

        See :mod:`~cherrymusicserver.databases`.
    """
    db_is_ready = database.ensure_current_version(
        consentcallback=_get_user_consent_for_db_schema_update)
    if not db_is_ready:
        log.i(_("database schema update aborted. quitting."))
        sys.exit(1)
Esempio n. 34
0
def migrate_databases():
    """ Makes sure CherryMusic's databases are up to date, migrating them if
        necessary.

        This might prompt the user for consent if a migration requires it and
        terminate the program if no consent is obtained.

        See :mod:`~cherrymusicserver.databases`.
    """
    db_is_ready = database.ensure_current_version(
        consentcallback=_get_user_consent_for_db_schema_update)
    if not db_is_ready:
        log.i(_("database schema update aborted. quitting."))
        sys.exit(1)
Esempio n. 35
0
 def reset(self):
     """Delete all content from the database along with supporting structures."""
     self._validate_locked()
     version = self._version
     log.i('%s: resetting database', self.name)
     log.d('version: %s', version)
     if None is version:
         log.d('nothing to reset.')
         return
     with self.db.connection() as cxn:
         cxn.executescript(self.desc[version]['drop.sql'])
         cxn.executescript(self._metatable['drop.sql'])
         cxn.executescript(self._metatable['create.sql'])
         self._setversion(None, cxn)
     cxn.close()
Esempio n. 36
0
 def reset(self):
     """Delete all content from the database along with supporting structures."""
     self._validate_locked()
     version = self._version
     log.i('%s: resetting database', self.name)
     log.d('version: %s', version)
     if None is version:
         log.d('nothing to reset.')
         return
     with self.db.connection() as cxn:
         cxn.executescript(self.desc[version]['drop.sql'])
         cxn.executescript(self._metatable['drop.sql'])
         cxn.executescript(self._metatable['create.sql'])
         self._setversion(None, cxn)
     cxn.close()
Esempio n. 37
0
def transcode(filepath, newformat):
    log.i("""Transcoding file {}
{} ---[{}]---> {}""".format(filepath,filetype(filepath),Encoders[newformat][0],newformat))
    try:
        fromdecoder = decode(filepath)
        encoder = encode(newformat, fromdecoder)
        while True:
            data = encoder.stdout.read(TRANSCODE_BUFFER)
            if not data:
                break               
            yield data
    except OSError:
        log.w("Transcode of file '{}' to format '{}' failed!".format(filepath,newformat))
    finally:
        encoder.terminate()
        fromdecoder.terminate()
Esempio n. 38
0
 def addUser(self, username, password, admin):
     if not (username.strip() or password.strip()):
         log.d(_('empty username or password!'))
         return False
     user = User.create(username, password, admin)
     try:
         self.conn.execute('''
         INSERT INTO users
         (username, admin, password, salt)
         VALUES (?,?,?,?)''',
         (user.name, 1 if user.isadmin else 0, user.password, user.salt))
     except sqlite3.IntegrityError:
         log.e('cannot create user "%s", already exists!' % user.name)
         return False
     self.conn.commit()
     log.i('added user: ' + user.name)
     return True
Esempio n. 39
0
 def addUser(self, username, password, admin):
     if not (username.strip() or password.strip()):
         log.d(_('empty username or password!'))
         return False
     user = User.create(username, password, admin)
     try:
         self.conn.execute(
             '''
         INSERT INTO users
         (username, admin, password, salt)
         VALUES (?,?,?,?)''', (user.name, 1 if user.isadmin else 0,
                               user.password, user.salt))
     except sqlite3.IntegrityError:
         log.e('cannot create user "%s", already exists!' % user.name)
         return False
     self.conn.commit()
     log.i('added user: ' + user.name)
     return True
Esempio n. 40
0
 def setup_config(self, createNewConfig, browsersetup, cfg_override):
     if browsersetup:
         port = cfg_override.pop('server.port', False)
         cherrymusicserver.browsersetup.configureAndStartCherryPy(port)
     if createNewConfig:
         newconfigpath = pathprovider.configurationFile() + '.new'
         cfg.write_to_file(cfg.from_defaults(), newconfigpath)
         log.i('New configuration file was written to:{br}{path}'.format(
             path=newconfigpath, br=os.linesep))
         sys.exit(0)
     if not pathprovider.configurationFileExists():
         if pathprovider.fallbackPathInUse():  # temp. remove @ v0.30 or so
             self.printMigrationNoticeAndExit()
         else:
             cfg.write_to_file(cfg.from_defaults(),
                               pathprovider.configurationFile())
             self.printWelcomeAndExit()
     self._init_config(cfg_override)
Esempio n. 41
0
    def api_fetchalbumart(self, directory):
        cherrypy.session.release_lock()
        default_folder_image = "/res/img/folder.png"

        #try getting a cached album art image
        b64imgpath = albumArtFilePath(directory)
        img_data = self.albumartcache_load(b64imgpath)
        if img_data:
            cherrypy.response.headers["Content-Length"] = len(img_data)
            return img_data

        #try getting album art inside local folder
        fetcher = albumartfetcher.AlbumArtFetcher()
        localpath = os.path.join(cherry.config['media.basedir'], directory)
        header, data, resized = fetcher.fetchLocal(localpath)

        if header:
            if resized:
                #cache resized image for next time
                self.albumartcache_save(b64imgpath, data)
            cherrypy.response.headers.update(header)
            return data
        elif cherry.config['media.fetch_album_art']:
            #fetch album art from online source
            try:
                foldername = os.path.basename(directory)
                keywords = foldername
                log.i(
                    _("Fetching album art for keywords {keywords!r}").format(
                        keywords=keywords))
                header, data = fetcher.fetch(keywords)
                if header:
                    cherrypy.response.headers.update(header)
                    self.albumartcache_save(b64imgpath, data)
                    return data
                else:
                    # albumart fetcher failed, so we serve a standard image
                    raise cherrypy.HTTPRedirect(default_folder_image, 302)
            except:
                # albumart fetcher threw exception, so we serve a standard image
                raise cherrypy.HTTPRedirect(default_folder_image, 302)
        else:
            # no local album art found, online fetching deactivated, show default
            raise cherrypy.HTTPRedirect(default_folder_image, 302)
Esempio n. 42
0
    def __init__(self, update=None, createNewConfig=False, dropfiledb=False):
        if createNewConfig:
            newconfigpath = util.configurationFile() + '.new'
            configuration.write_to_file(configuration.from_defaults(), newconfigpath)
            log.i('''New configuration file was written to: 
''' + newconfigpath)
            exit(0)
        if not util.configurationFileExists():
            configuration.write_to_file(configuration.from_defaults(), util.configurationFile())
            self.printWelcomeAndExit()
        self._init_config()
        self.db = sqlitecache.SQLiteCache(util.databaseFilePath('cherry.cache.db'))
        
        if not update == None or dropfiledb:
            CherryMusic.UpdateThread(self.db,update,dropfiledb).start()
        else:
            self.cherrymodel = cherrymodel.CherryModel(self.db)
            self.httphandler = httphandler.HTTPHandler(config, self.cherrymodel)
            self.server()
Esempio n. 43
0
 def setup_config(self, createNewConfig, browsersetup, cfg_override):
     if browsersetup:
         port = cfg_override.pop('server.port', False)
         cherrymusicserver.browsersetup.configureAndStartCherryPy(port)
     if createNewConfig:
         newconfigpath = pathprovider.configurationFile() + '.new'
         cfg.write_to_file(cfg.from_defaults(), newconfigpath)
         log.i('New configuration file was written to:{br}{path}'.format(
             path=newconfigpath,
             br=os.linesep
         ))
         sys.exit(0)
     if not pathprovider.configurationFileExists():
         if pathprovider.fallbackPathInUse():   # temp. remove @ v0.30 or so
             self.printMigrationNoticeAndExit()
         else:
             cfg.write_to_file(cfg.from_defaults(), pathprovider.configurationFile())
             self.printWelcomeAndExit()
     self._init_config(cfg_override)
Esempio n. 44
0
 def setup_databases(self, update, dropfiledb, setup):
     if dropfiledb:
         update = ()
         database.resetdb(sqlitecache.DBNAME)
     if setup:
         update = update or ()
     db_is_ready = database.ensure_current_version(
         consentcallback=self._get_user_consent_for_db_schema_update)
     if not db_is_ready:
         log.i("database schema update aborted. quitting.")
         sys.exit(1)
     if update is not None:
         cacheupdate = threading.Thread(name="Updater",
                                        target=self._update_if_necessary,
                                        args=(update,))
         cacheupdate.start()
         # self._update_if_necessary(update)
         if not setup:
             sys.exit(0)
Esempio n. 45
0
    def api_fetchalbumart(self, directory):
        cherrypy.session.release_lock()
        default_folder_image = "/res/img/folder.png"

        #try getting a cached album art image
        b64imgpath = albumArtFilePath(directory)
        img_data = self.albumartcache_load(b64imgpath)
        if img_data:
            cherrypy.response.headers["Content-Length"] = len(img_data)
            return img_data

        #try getting album art inside local folder
        fetcher = albumartfetcher.AlbumArtFetcher()
        localpath = os.path.join(cherry.config['media.basedir'], directory)
        header, data, resized = fetcher.fetchLocal(localpath)

        if header:
            if resized:
                #cache resized image for next time
                self.albumartcache_save(b64imgpath, data)
            cherrypy.response.headers.update(header)
            return data
        elif cherry.config['media.fetch_album_art']:
            #fetch album art from online source
            try:
                foldername = os.path.basename(directory)
                keywords = foldername
                log.i(_("Fetching album art for keywords {keywords!r}").format(keywords=keywords))
                header, data = fetcher.fetch(keywords)
                if header:
                    cherrypy.response.headers.update(header)
                    self.albumartcache_save(b64imgpath, data)
                    return data
                else:
                    # albumart fetcher failed, so we serve a standard image
                    raise cherrypy.HTTPRedirect(default_folder_image, 302)
            except:
                # albumart fetcher threw exception, so we serve a standard image
                raise cherrypy.HTTPRedirect(default_folder_image, 302)
        else:
            # no local album art found, online fetching deactivated, show default
            raise cherrypy.HTTPRedirect(default_folder_image, 302)
Esempio n. 46
0
    def _check_for_config_updates(self, default, known_config):
        """check if there are new or deprecated configuration keys in
        the config file
        """
        new = []
        deprecated = []
        transform = lambda s: '[{0}]: {2}'.format(*(s.partition('.')))

        for property in cfg.to_list(default):
            if property.key not in known_config and not property.hidden:
                new.append(transform(property.key))
        for property in cfg.to_list(known_config):
            if property.key not in default:
                deprecated.append(transform(property.key))

        if new:
            log.i(_('''New configuration options available:
                        %s
                    Using default values for now.'''),
                  '\n\t\t\t'.join(new))
        if deprecated:
            log.i(_('''The following configuration options are not used anymore:
                        %s'''),
                  '\n\t\t\t'.join(deprecated))
        if new or deprecated:
            log.i(_('Start with --newconfig to generate a new default config'
                    ' file next to your current one.'))
Esempio n. 47
0
    def _check_for_config_updates(self, known_config):
        new = []
        deprecated = []
        default = configuration.from_defaults()

        for property in configuration.to_list(default):     #@ReservedAssignment
            if property.name not in known_config and not property.hidden:
                new.append(property.name)
        for property in configuration.to_list(known_config): #@ReservedAssignment
            if property.name not in default:
                deprecated.append(property.name)

        if new:
            log.i('''New configuration options available: 
                        %s
                    Using default values for now.''',
                  '\n\t\t\t'.join(new))
        if deprecated:
            log.i('''The following configuration options are not used anymore: 
                        %s''',
                  '\n\t\t\t'.join(deprecated))
        if new or deprecated:
            log.i('''Start with --newconfig to generate a new default config file next to your current one.
                  ''',
            )
Esempio n. 48
0
 def setup_databases(self, update, dropfiledb, setup):
     """ delete or update the file db if so requested.
     check if the db schema is up to date
     """
     if dropfiledb:
         update = ()
         database.resetdb(sqlitecache.DBNAME)
     if setup:
         update = update or ()
     db_is_ready = database.ensure_current_version(
         consentcallback=self._get_user_consent_for_db_schema_update)
     if not db_is_ready:
         log.i(_("database schema update aborted. quitting."))
         sys.exit(1)
     if update is not None:
         cacheupdate = threading.Thread(name="Updater",
                                        target=self._update_if_necessary,
                                        args=(update,))
         cacheupdate.start()
         # self._update_if_necessary(update)
         if not setup:
             CherryMusic.stopAndCleanUp()
Esempio n. 49
0
    def __init__(self, lvl=-1, dly=1, timefmt=None, namefmt=None, repf=None):
        '''
        Creates a progress reporter with the following customization options:

        lvl : int (default -1)
            The maximum level in the progress hierarchy that will trigger a
            report. When a report is triggered, it will contain all progress
            events up to this level that have occurred since the last report. A
            negative value will use the time trigger (see ``dly``) to report the
            newest progress event with the upmost available level.

        dly : float (default 1)
            The target maximum delay between reports, in seconds. Triggers a
            report conforming with ``lvl`` if ``dly`` seconds have passed
            since the last report. Set to 0 to turn off timed reporting;
            set to a value < 0 for a time trigger without delay.

        timefmt : callable(float) -> str (default ProgressReport.timefmt)
            A function that turns the number for the estimated completion time
            into a string. That number is provided by ``progress.root.eta``.
            Per default, it interpreted as seconds until completion, with
            negative values meaning overtime since estimated completion.

        namefmt : callable(str) -> str (default: no conversion)
            A function that converts the name given by ``progress.name`` into
            a more suitable format.

        repf : callable(dict) (default: log '%(eta) %(nam) (%(tix))' as info)
           Function callback to handle the actual reporting. The dict argument
           contains the following items::
               'eta': completion time as str,
               'nam': progress name,
               'tix': str giving total ticks registered with this reporter,
               'progress': progress to report on, containing the raw data
        '''
        self._eta_adjuster = lambda e: e + 1
        self._eta_formatter = self.prettytime if timefmt is None else timefmt
        self._name_formatter = (lambda s: s) if namefmt is None else namefmt
        self._reportfunc = (lambda d: log.i('%(eta)s %(nam)s (%(tix)s)', d)
                            ) if repf is None else repf
        self._replevel = lvl
        self._repintvl = dly
        self._maxlevel = 0
        self._levelcache = {}
        self._ticks = 0
        self._lastreport = 0
Esempio n. 50
0
 def partial_update(self, path, *paths):
     basedir = cherry.config['media.basedir']
     paths = (path,) + paths
     log.i(_('updating paths: %s') % (paths,))
     for path in paths:
         path = os.path.normcase(path)
         abspath = path if os.path.isabs(path) else os.path.join(basedir, path)
         normpath = os.path.normpath(abspath)
         if not normpath.startswith(basedir):
             log.e(_('path is not in basedir. skipping %r') % abspath)
             continue
         log.i(_('updating %r...') % path)
         try:
             self.update_db_recursive(normpath, skipfirst=False)
         except Exception as exception:
             log.e(_('update incomplete: %r'), exception)
     self.update_word_occurrences()
     log.i(_('done updating paths.'))
Esempio n. 51
0
    def api_fetchalbumart(self, directory):
        _save_and_release_session()
        default_folder_image = "../res/img/folder.png"

        log.i('Fetching album art for: %s' % directory)
        filepath = os.path.join(cherry.config['media.basedir'], directory)

        if os.path.isfile(filepath):
            # if the given path is a file, try to get the image from ID3
            tag = TinyTag.get(filepath, image=True)
            image_data = tag.get_image()
            if image_data:
                log.d('Image found in tag.')
                header = {'Content-Type': 'image/jpg', 'Content-Length': len(image_data)}
                cherrypy.response.headers.update(header)
                return image_data
            else:
                # if the file does not contain an image, display the image of the
                # parent directory
                directory = os.path.dirname(directory)

        #try getting a cached album art image
        b64imgpath = albumArtFilePath(directory)
        img_data = self.albumartcache_load(b64imgpath)
        if img_data:
            cherrypy.response.headers["Content-Length"] = len(img_data)
            return img_data

        #try getting album art inside local folder
        fetcher = albumartfetcher.AlbumArtFetcher()
        localpath = os.path.join(cherry.config['media.basedir'], directory)
        header, data, resized = fetcher.fetchLocal(localpath)

        if header:
            if resized:
                #cache resized image for next time
                self.albumartcache_save(b64imgpath, data)
            cherrypy.response.headers.update(header)
            return data
        elif cherry.config['media.fetch_album_art']:
            # maximum of files to try to fetch metadata for albumart keywords
            METADATA_ALBUMART_MAX_FILES = 10
            #fetch album art from online source
            try:
                foldername = os.path.basename(directory)
                keywords = foldername
                # remove any odd characters from the folder name
                keywords = re.sub('[^A-Za-z\s]', ' ', keywords)
                # try getting metadata from files in the folder for a more
                # accurate match
                files = os.listdir(localpath)
                for i, filename in enumerate(files):
                    if i >= METADATA_ALBUMART_MAX_FILES:
                        break
                    path = os.path.join(localpath, filename)
                    metadata = metainfo.getSongInfo(path)
                    if metadata.artist and metadata.album:
                        keywords = '{} - {}'.format(metadata.artist, metadata.album)
                        break

                log.i(_("Fetching album art for keywords {keywords!r}").format(keywords=keywords))
                header, data = fetcher.fetch(keywords)
                if header:
                    cherrypy.response.headers.update(header)
                    self.albumartcache_save(b64imgpath, data)
                    return data
                else:
                    # albumart fetcher failed, so we serve a standard image
                    raise cherrypy.HTTPRedirect(default_folder_image, 302)
            except:
                # albumart fetcher threw exception, so we serve a standard image
                raise cherrypy.HTTPRedirect(default_folder_image, 302)
        else:
            # no local album art found, online fetching deactivated, show default
            raise cherrypy.HTTPRedirect(default_folder_image, 302)
Esempio n. 52
0
def create_default_config_file(path):
    """ Creates or overwrites a default configuration file at `path` """
    cfg.write_to_file(cfg.from_defaults(), path)
    log.i(_('Default configuration file written to %(path)r'), {'path': path})
Esempio n. 53
0
    def start(self, httphandler):
        socket_host = "127.0.0.1" if config['server.localhost_only'] else "0.0.0.0"

        resourcedir = os.path.abspath(pathprovider.getResourcePath('res'))

        if config['server.ssl_enabled']:
            cherrypy.config.update({
                'server.ssl_certificate': config['server.ssl_certificate'],
                'server.ssl_private_key': config['server.ssl_private_key'],
                'server.socket_port': config['server.ssl_port'],
            })
            # Create second server for http redirect:
            redirecter = cherrypy._cpserver.Server()
            redirecter.socket_port = config['server.port']
            redirecter._socket_host = socket_host
            redirecter.thread_pool = 10
            redirecter.subscribe()
        else:
            cherrypy.config.update({
                'server.socket_port': config['server.port'],
            })

        cherrypy.config.update({
            'log.error_file': os.path.join(
                pathprovider.getUserDataPath(), 'server.log'),
            'environment': 'production',
            'server.socket_host': socket_host,
            'server.thread_pool': 30,
            'tools.sessions.on': True,
            'tools.sessions.timeout': 60 * 24,
        })

        if not config['server.keep_session_in_ram']:
            sessiondir = os.path.join(
                pathprovider.getUserDataPath(), 'sessions')
            if not os.path.exists(sessiondir):
                os.mkdir(sessiondir)
            cherrypy.config.update({
                'tools.sessions.storage_type': "file",
                'tools.sessions.storage_path': sessiondir,
            })

        cherrypy.tree.mount(
            httphandler, '/',
            config={
                '/res': {
                    'tools.staticdir.on': True,
                    'tools.staticdir.dir': resourcedir,
                    'tools.staticdir.index': 'index.html',
                    'tools.caching.on': False,
                },
                '/serve': {
                    'tools.staticdir.on': True,
                    'tools.staticdir.dir': config['media.basedir'],
                    'tools.staticdir.index': 'index.html',
                    'tools.encode.on': True,
                    'tools.encode.encoding': 'utf-8',
                    'tools.caching.on': False,
                },
                '/favicon.ico': {
                    'tools.staticfile.on': True,
                    'tools.staticfile.filename': resourcedir + '/favicon.ico',
                }})
        log.i('Starting server on port %s ...' % config['server.port'])

        cherrypy.lib.caching.expires(0)  # disable expiry caching
        cherrypy.engine.start()
        cherrypy.engine.block()
Esempio n. 54
0
    def api_fetchalbumart(self, directory):
        _save_and_release_session()
        default_folder_image = "../res/img/folder.png"

        log.i('Fetching album art for: %s' % directory)
        filepath = os.path.join(cherry.config['media.basedir'], directory)

        if os.path.isfile(filepath):
            # if the given path is a file, try to get the image from ID3
            tag = TinyTag.get(filepath, image=True)
            image_data = tag.get_image()
            if image_data:
                log.d('Image found in tag.')
                header = {
                    'Content-Type': 'image/jpg',
                    'Content-Length': len(image_data)
                }
                cherrypy.response.headers.update(header)
                return image_data
            else:
                # if the file does not contain an image, display the image of the
                # parent directory
                directory = os.path.dirname(directory)

        #try getting a cached album art image
        b64imgpath = albumArtFilePath(directory)
        img_data = self.albumartcache_load(b64imgpath)
        if img_data:
            cherrypy.response.headers["Content-Length"] = len(img_data)
            return img_data

        #try getting album art inside local folder
        fetcher = albumartfetcher.AlbumArtFetcher()
        localpath = os.path.join(cherry.config['media.basedir'], directory)
        header, data, resized = fetcher.fetchLocal(localpath)

        if header:
            if resized:
                #cache resized image for next time
                self.albumartcache_save(b64imgpath, data)
            cherrypy.response.headers.update(header)
            return data
        elif cherry.config['media.fetch_album_art']:
            #fetch album art from online source
            try:
                foldername = os.path.basename(directory)
                keywords = foldername
                log.i(
                    _("Fetching album art for keywords {keywords!r}").format(
                        keywords=keywords))
                header, data = fetcher.fetch(keywords)
                if header:
                    cherrypy.response.headers.update(header)
                    self.albumartcache_save(b64imgpath, data)
                    return data
                else:
                    # albumart fetcher failed, so we serve a standard image
                    raise cherrypy.HTTPRedirect(default_folder_image, 302)
            except:
                # albumart fetcher threw exception, so we serve a standard image
                raise cherrypy.HTTPRedirect(default_folder_image, 302)
        else:
            # no local album art found, online fetching deactivated, show default
            raise cherrypy.HTTPRedirect(default_folder_image, 302)
Esempio n. 55
0
    def start_server(self, httphandler):
        """use the configuration to setup and start the cherrypy server
        """
        cherrypy.config.update({'log.screen': True})
        ipv6_enabled = config['server.ipv6_enabled']
        if config['server.localhost_only']:
            socket_host = "::1" if ipv6_enabled else "127.0.0.1"
        else:
            socket_host = "::" if ipv6_enabled else "0.0.0.0"

        resourcedir = os.path.abspath(pathprovider.getResourcePath('res'))

        if config['server.ssl_enabled']:
            cert = pathprovider.absOrConfigPath(config['server.ssl_certificate'])
            pkey = pathprovider.absOrConfigPath(config['server.ssl_private_key'])
            cherrypy.config.update({
                'server.ssl_certificate': cert,
                'server.ssl_private_key': pkey,
                'server.socket_port': config['server.ssl_port'],
            })
            # Create second server for redirecting http to https:
            redirecter = cherrypy._cpserver.Server()
            redirecter.socket_port = config['server.port']
            redirecter._socket_host = socket_host
            redirecter.thread_pool = 10
            redirecter.subscribe()
        else:
            cherrypy.config.update({
                'server.socket_port': config['server.port'],
            })

        cherrypy.config.update({
            'log.error_file': os.path.join(
                pathprovider.getUserDataPath(), 'server.log'),
            'environment': 'production',
            'server.socket_host': socket_host,
            'server.thread_pool': 30,
            'tools.sessions.on': True,
            'tools.sessions.timeout': 60 * 24,
        })

        if not config['server.keep_session_in_ram']:
            sessiondir = os.path.join(
                pathprovider.getUserDataPath(), 'sessions')
            if not os.path.exists(sessiondir):
                os.mkdir(sessiondir)
            cherrypy.config.update({
                'tools.sessions.storage_type': "file",
                'tools.sessions.storage_path': sessiondir,
            })
        basedirpath = config['media.basedir']
        if sys.version_info < (3,0):
            basedirpath = codecs.encode(basedirpath, 'utf-8')
            scriptname = codecs.encode(config['server.rootpath'], 'utf-8')
        else:
            # fix cherrypy unicode issue (only for Python3)
            # see patch to cherrypy.lib.static.serve_file way above and
            # https://bitbucket.org/cherrypy/cherrypy/issue/1148/wrong-encoding-for-urls-containing-utf-8
            basedirpath = codecs.decode(codecs.encode(basedirpath, 'utf-8'), 'latin-1')
            scriptname = config['server.rootpath']
        cherrypy.tree.mount(
            httphandler, scriptname,
            config={
                '/res': {
                    'tools.staticdir.on': True,
                    'tools.staticdir.dir': resourcedir,
                    'tools.staticdir.index': 'index.html',
                    'tools.caching.on': False,
                    'tools.gzip.mime_types': ['text/html', 'text/plain', 'text/javascript', 'text/css'],
                    'tools.gzip.on': True,
                },
                '/serve': {
                    'tools.staticdir.on': True,
                    'tools.staticdir.dir': basedirpath,
                    # 'tools.staticdir.index': 'index.html',    if ever needed: in py2 MUST utf-8 encode
                    'tools.staticdir.content_types': MimeTypes,
                    'tools.encode.on': True,
                    'tools.encode.encoding': 'utf-8',
                    'tools.caching.on': False,
                    'tools.cm_auth.on': True,
                    'tools.cm_auth.httphandler': httphandler,
                },
                '/favicon.ico': {
                    'tools.staticfile.on': True,
                    'tools.staticfile.filename': resourcedir + '/img/favicon.ico',
                }})
        api.v1.mount('/api/v1')
        log.i(_('Starting server on port %s ...') % config['server.port'])

        cherrypy.lib.caching.expires(0)  # disable expiry caching
        cherrypy.engine.start()
        cherrypy.engine.block()