コード例 #1
0
ファイル: filesystem.py プロジェクト: daveisadork/Blofeld
 def update(self):
     """Initiates the process of updating the database by removing any
     songs that have gone missing, adding new songs and updating changed
     songs.
     """
     if self.updating.is_set():
         logger.warn("Library update requested, but already updating.")
         return self.current_job
     self.updating.set()
     self.update_thread = Thread(target=self._update)
     ticket = hashlib.sha1(str(time())).hexdigest()
     self.jobs[ticket] = {
         'status': 'Initializing',
         'current_item': self.music_path,
         'removed_items': 0,
         'new_items': 0,
         'changed_items': 0,
         'unchanged_items': 0,
         'queued_items': 0,
         'processed_items': 0,
         'total_time': 0
         }
     self.current_job = ticket
     logger.info("Starting library update.")
     self.update_thread.start()
     return ticket
コード例 #2
0
ファイル: web.py プロジェクト: daveisadork/Blofeld
 def flush_db(self):
     logger.debug("%s (%s)\tshutdown()\tHeaders: %s" % (utils.find_originating_host(cherrypy.request.headers), cherrypy.request.login, cherrypy.request.headers))
     if cfg['REQUIRE_LOGIN'] and cherrypy.request.login not in cfg['GROUPS']['admin']:
         logger.warn("%(user)s (%(ip)s) requested that the database be flushed, but was denied because %(user)s is not a member of the admin group." % {'user': cherrypy.request.login, 'ip': utils.find_originating_host(cherrypy.request.headers)})
         raise cherrypy.HTTPError(401,'Not Authorized')
     try:
         cherrypy.response.headers['Content-Type'] = 'application/json'
         logger.info("Received flush database request, complying.")
         return json.dumps({'flush_db': True})
     except:
         pass
     finally:
         self.library.db.flush()
コード例 #3
0
ファイル: web.py プロジェクト: daveisadork/Blofeld
 def shutdown(self):
     logger.debug("%s (%s)\tshutdown()\tHeaders: %s" % (utils.find_originating_host(cherrypy.request.headers), cherrypy.request.login, cherrypy.request.headers))
     if cfg['REQUIRE_LOGIN'] and cherrypy.request.login not in cfg['GROUPS']['admin']:
         logger.warn("%(user)s (%(ip)s) requested that the server shut down, but was denied because %(user)s is not a member of the admin group." % {'user': cherrypy.request.login, 'ip': utils.find_originating_host(cherrypy.request.headers)})
         raise cherrypy.HTTPError(401,'Not Authorized')
     try:
         cherrypy.response.headers['Content-Type'] = 'application/json'
         logger.info("Received shutdown request, complying.")
         self.transcoder.stop()
         self.library.scanner.stop()
         return json.dumps({'shutdown': True})
     except:
         pass
     finally:
         shutting_down.set()
         logger.debug("Stopping CherryPy.")
         cherrypy.engine.exit()
コード例 #4
0
ファイル: filesystem.py プロジェクト: daveisadork/Blofeld
 def _update(self):
     self.start_time = time()
     # Clean the database of files that no longer exist and get a list
     # of the remaining songs in the database.
     self._clean()
     # Create a queue of files from which we need to read metadata.
     self._scan()
     if self.read_queue.qsize() > 0:
         # Spawn a new thread to handle the queue of files that need read.
         self.read_thread = Thread(target=self._process_read_queue)
         self.read_thread.start()
         # Give the read_thread a chance to get going and then spawn a
         # new thread to handle inserting metadata into the database.
         sleep(5)
         self.db_thread = Thread(target=self._add_items_to_db)
         self.db_thread.start()
         # Block while we wait for everything to finish
         self.db_working.wait(None)
         # Join our threads back
         self.read_thread.join()
         self.db_thread.join()
     self.jobs[self.current_job]['current_item'] = 'None'
     # Compact the database so it doesn't get unreasonably large. We do
     # this in a separate thread so we can go ahead and return since the
     # we've added everything we need to the database already and we
     # don't want to wait for this to finish.
     self.compact_thread = Thread(target=self._compact)
     self.compact_thread.start()
     self._compact()
     self.updating.clear()
     self.finished.set()
     finish_time = time() - self.start_time
     self.jobs[self.current_job]['status'] = 'Finished'
     self.jobs[self.current_job]['total_time'] = finish_time
     logger.debug("Added all new songs in %0.2f seconds." % finish_time)
     logger.info("Updated library in %0.2f seconds." % finish_time)
コード例 #5
0
ファイル: web.py プロジェクト: daveisadork/Blofeld
    def get_song(self, songid=None, format=False, bitrate=False):
        logger.debug("%s (%s)\tget_song(songid=%s, format=%s, bitrate=%s)\tHeaders: %s" % (utils.find_originating_host(cherrypy.request.headers), cherrypy.request.login, songid, format, bitrate, cherrypy.request.headers))
        log_message = "%s (%s) is listening to " % (cherrypy.request.login, utils.find_originating_host(cherrypy.request.headers))
        last = self.multi_requests.get(songid, None)
        show_log = False
        if not last or (last and time.time() > (last + 30)):
            show_log = True
        self.multi_requests[songid] = time.time()
        try:
            range_request = cherrypy.request.headers['Range']
        except:
            range_request = "bytes=0-"
        try:
            song = self.library.db[songid]
            path = song['location']
        except:
            log_message += "a song ID which could not be found: %s" % str(songid)
            logger.error(log_message)
            raise cherrypy.HTTPError(404)
        log_message += '"%s" by %s from %s ' % (song['title'].encode(cfg['ENCODING']), song['artist'].encode(cfg['ENCODING']), song['album'].encode(cfg['ENCODING']))
        try:
            client_os, client_browser = httpagentparser.simple_detect(cherrypy.request.headers['User-Agent'])
        #    b = self.bc(cherrypy.request.headers['User-Agent'])
        #    if b:
        #        browser = "%s %s.%s on %s" % (b.name(), b.version()[0], b.version()[1], b.get("platform"))
        #    else:
        #        browser = cherrypy.request.headers['User-Agent']
        #    log_message += "using %s." % browser
        except:
            client_os = 'an OS'
            client_browser = 'a browser'
        try:
            if bitrate:
                bitrate = str(bitrate)
            force_transcode = False
            if bitrate and \
               (int(bitrate) in [8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112,
                                          128, 160, 192, 224, 256, 320]) and \
               (song['bitrate'] / 1024 > int(bitrate)):
                force_transcode = True
        except:
            pass
        try:
            song_format = [song['mimetype'].split('/')[1],
                            os.path.splitext(path)[1].lower()[1:]]
        except:
            song_format = [os.path.splitext(path)[1].lower()[1:]]
        if True in [True for x in song_format if x in ['mp3']]:
            song_mime = 'audio/mpeg'
            song_format = ['mp3']
        elif True in [True for x in song_format if x in ['ogg', 'vorbis', 'oga']]:
            song_mime = 'audio/ogg'
            song_format = ['ogg', 'vorbis', 'oga']
        elif True in [True for x in song_format if x in ['m4a', 'aac', 'mp4']]:
            song_mime = 'audio/x-m4a'
            song_format = ['m4a', 'aac', 'mp4']
        else:
            song_mime = 'application/octet-stream'
        if not (format or bitrate):
            #log_message += " The client did not request any specific format or bitrate so the file is being sent as-is (%s kbps %s)." % (str(song['bitrate'] / 1000), str(song_format))
            log_message += "(%skbps %s)" % (str(song['bitrate']), song_format[0])
            if client_os and client_browser:
                log_message += " using %s on %s." % (client_browser, client_os)
            else:
                log_message += "."
            logger.info(log_message)
            if not os.name == 'nt':
                path = path.encode(cfg['ENCODING'])
            return serve_file(path, song_mime,
                                "inline", os.path.split(path)[1])
        if format:
            format = str(format).split(',')
        else:
            format = song_format
        logger.debug("The client wants %s and the file is %s" % (format, song_format))
        if True in [True for x in format if x in song_format] and not force_transcode:
            #if bitrate:
            #    log_message += " The client requested %s kbps %s, but the file is already %s kbps %s, so the file is being sent as-is." % (bitrate, format, str(song['bitrate'] / 1000), str(song_format))
            #else:
            #    log_message += " The client requested %s, but the file is already %s, so the file is being sent as-is." % (format, str(song_format))
            log_message += "(%skbps %s)" % (str(song['bitrate'] / 1000), song_format[0])
            if client_os and client_browser:
                log_message += " using %s on %s." % (client_browser, client_os)
            else:
                log_message += "."
            if show_log:
                logger.info(log_message)
            if not os.name == 'nt':
                path = path.encode(cfg['ENCODING'])
            return serve_file(path, song_mime,
                                "inline", os.path.split(path)[1])
        else:
            #if bitrate:
            #    log_message = " The client requested %s kbps %s, but the file is %s kbps %s, so we're transcoding the file for them." % (bitrate, format, str(song['bitrate'] / 1000), str(song_format))
            #else:
            #    log_message += " The client requested %s, but the file %s, so we're transcoding the file for them." % (format, str(song_format))
            log_message += "(transcoded from %skbps %s to %skbps %s)" % (str(song['bitrate'] / 1000), song_format[0], str(bitrate), format[0])
            if client_os and client_browser:
                log_message += " using %s on %s." % (client_browser, client_os)
            else:
                log_message += "."
            if show_log:
                logger.info(log_message)
        # If we're transcoding audio and the client is trying to make range
        # requests, we have to throw an error 416. This sucks because it breaks
        # <audio> in all the WebKit browsers I've tried, but at least it stops
        # them from spawning a zillion transcoder threads (I'm looking at you,
        # Chromium).
        if True in [True for x in format if x in ['mp3']]:
#            cherrypy.response.headers['Content-Length'] = '-1'
            if range_request != 'bytes=0-':
                logger.debug("Got a range request for a file that needs transcoded: %s" % range_request)
                raise cherrypy.HTTPError(416)
            else:
                cherrypy.response.headers['Content-Type'] = 'audio/mpeg'
                try:
                    if cherrypy.request.headers['Referer'].lower().endswith('jplayer.swf'):
                        cherrypy.response.headers['Content-Type'] = 'audio/mp3'
                except:
                    pass
                #cherrypy.response.headers['Content-Type'] = 'application/octet-stream'
                return self.transcoder.transcode(path, 'mp3', bitrate)
        elif True in [True for x in format if x in ['ogg', 'vorbis', 'oga']]:
#            cherrypy.response.headers['Content-Length'] = '-1'
            if range_request != 'bytes=0-':
                logger.debug("Got a range request for a file that needs transcoded: %s" % range_request)
                raise cherrypy.HTTPError(416)
            else:
                cherrypy.response.headers['Content-Type'] = 'audio/ogg'
                #cherrypy.response.headers['Content-Type'] = 'application/octet-stream'
                return self.transcoder.transcode(path, 'ogg', bitrate)
        elif True in [True for x in format if x in ['m4a', 'aac', 'mp4']]:
#            cherrypy.response.headers['Content-Length'] = '-1'
            if range_request != 'bytes=0-':
                logger.debug("Got a range request for a file that needs transcoded: %s" % range_request)
                raise cherrypy.HTTPError(416)
            else:
                cherrypy.response.headers['Content-Type'] = 'audio/x-m4a'
                #cherrypy.response.headers['Content-Type'] = 'application/octet-stream'
                return self.transcoder.transcode(path, 'm4a', bitrate)
        else:
            raise cherrypy.HTTPError(501)
コード例 #6
0
ファイル: __init__.py プロジェクト: daveisadork/Blofeld
 def stop(self):
     logger.info("Preventing new library calls.")
     self.shutting_down.set()
     self.scanner.stop()
コード例 #7
0
ファイル: transcode.py プロジェクト: daveisadork/Blofeld
def transcode_process(conn, path, stop, format='mp3', bitrate=False):
    import gi
    gi.require_version('Gst', '1.0')
    from gi.repository import GObject, Gst

    GObject.threads_init()
    Gst.init(None)

    # If we were passed a bitrate argument, make sure it's actually a number
    try:
        bitrate = int(bitrate)
    except:
        bitrate = False
    log_message = "Transcoding %s to %s" % (path.encode(cfg['ENCODING']), format)
    # Create our transcoding pipeline using one of the strings at the end of
    # this module.
    transcoder = Gst.parse_launch(pipeline[format])
    # Set the bitrate we were asked for
    if bitrate in [8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192,
                                                                224, 256, 320]:
        log_message += " at %d kbps" % bitrate
        encoder = transcoder.get_by_name('encoder')
        muxer = transcoder.get_by_name('muxer')
        if format is 'mp3':
            encoder.set_property("target", "bitrate")
            encoder.set_property("bitrate", bitrate)
        elif format is 'ogg':
            encoder.set_property("max-bitrate", bitrate * 1024)
        elif format is 'm4a':
            encoder.set_property("bitrate", bitrate * 1000)
            #encoder.set_property("outputformat", 1)
            #encoder.set_property("profile", 1)
            #muxer.set_property("faststart", True)
    # Load our file into the transcoder
    log_message += "."
    logger.info(log_message)
    source = transcoder.get_by_name('source')
    try:
        source.set_property("location", path.encode('utf-8'))
        # Set the output to be asynchronous so the transcoding happens as quickly
        # as possible rather than real time.
        output = transcoder.get_by_name('output')
        output.set_property("sync", False)
        # Start the pipeline running so we can start grabbing data out of it
        transcoder.set_state(Gst.State.PLAYING)
        #transcoder.get_state()
        try:
            # Grab a bit of encoded data and yield it to the client
            while True:
                sample = output.emit('pull-sample')
                if sample and not stop.is_set():
                    buf = sample.get_buffer()
                    data = buf.extract_dup(0, buf.get_size())
                    conn.send(data)
                else:
                    break
        except Exception as e:
            logger.warn(str(e))
            logger.warn("Some type of error occured during transcoding.")
    except Exception as e:
        logger.error(str(e))
        logger.error("Could not open the file for transcoding. This is probably happening because there are non-ASCII characters in the filename.")
    finally:
        try:
            # I think this is supposed to free the memory used by the transcoder
            transcoder.set_state(Gst.State.NULL)
        except:
            pass
        if not stop.is_set():
            conn.send(False)
            conn.close()