def main():
    # parse commandline options
    parser = OptionParser()
    parser.add_option('-b', '--base_directory', dest='base_directory',
            help='base directory', default=BASE_DIRECTORY)
    parser.add_option('-c', '--config', dest='config',
            help='configuration_file')
    parser.add_option('-d', '--database-file', dest='database',
            help='database file')
    parser.add_option('-s', '--schedule-file', dest='schedule',
            help='schedule file')
    parser.add_option('-f', '--force-rescan', action='store_true',
            dest='force_rescan', help='force reconstruction of recordings',
            default=False)
    parser.add_option('-v', '--verbose', action='store_true', dest='verbose',
            help='add commentary to the process', default=False)
    (options, args) = parser.parse_args()
    if not options.config: options.config = os.path.join(options.base_directory, DEFAULT_CONFIG)
    if not options.database: options.database = os.path.join(options.base_directory, DEFAULT_DATABASE)
    if not options.schedule: options.schedule = os.path.join(options.base_directory, DEFAULT_SCHEDULE)
    if not os.path.exists(options.config):
    	print >> sys.stderr, "fatal error: config file '%s' does not exist" %  options.config
    	sys.exit(1)
    config = ConfigParser(options.config)
    schedule = ScheduleParser(options.config, options.schedule)
    storage = BowerbirdStorage(options.database, config, schedule)

    # if requested, clear all recordings so they are re-constructed
    if options.force_rescan:
        storage.clearRecordingFiles()
        storage.clearRecordings()

    # call the update method
    storage.updateRecordingsFromFiles(options.force_rescan, options.verbose)
Example #2
0
def main():
    # parse commandline options
    parser = OptionParser()
    parser.add_option('-b',
                      '--base_directory',
                      dest='base_directory',
                      help='base directory',
                      default=BASE_DIRECTORY)
    parser.add_option('-c',
                      '--config',
                      dest='config',
                      help='configuration_file')
    parser.add_option('-d',
                      '--database-file',
                      dest='database',
                      help='database file')
    parser.add_option('-s',
                      '--schedule-file',
                      dest='schedule',
                      help='schedule file')
    parser.add_option('-f',
                      '--force-rescan',
                      action='store_true',
                      dest='force_rescan',
                      help='force reconstruction of recordings',
                      default=False)
    parser.add_option('-v',
                      '--verbose',
                      action='store_true',
                      dest='verbose',
                      help='add commentary to the process',
                      default=False)
    (options, args) = parser.parse_args()
    if not options.config:
        options.config = os.path.join(options.base_directory, DEFAULT_CONFIG)
    if not options.database:
        options.database = os.path.join(options.base_directory,
                                        DEFAULT_DATABASE)
    if not options.schedule:
        options.schedule = os.path.join(options.base_directory,
                                        DEFAULT_SCHEDULE)
    if not os.path.exists(options.config):
        print >> sys.stderr, "fatal error: config file '%s' does not exist" % options.config
        sys.exit(1)
    config = ConfigParser(options.config)
    schedule = ScheduleParser(options.config, options.schedule)
    storage = BowerbirdStorage(options.database, config, schedule)

    # if requested, clear all recordings so they are re-constructed
    if options.force_rescan:
        storage.clearRecordingFiles()
        storage.clearRecordings()

    # call the update method
    storage.updateRecordingsFromFiles(options.force_rescan, options.verbose)
Example #3
0
class Root(object):
    def __init__(self, path, database_file):
        self.path = path

        # parse config file(s)
        config_filename = cherrypy.config[CONFIG_KEY]
        if not os.path.exists(config_filename):
            raise IOError(errno.ENOENT, "Config file '%s' not found"
                    % config_filename)
        if cherrypy.config.has_key(CONFIG_DEFAULTS_KEY):
            defaults_filename = cherrypy.config[CONFIG_DEFAULTS_KEY]
            # if defaults file doesn't exist, then don't use it
            if not os.path.exists(defaults_filename):
                sys.stderr.write("Warning: configured defaults file "
                        " '%s' doesn't exist\n" % defaults_filename)
                defaults_filename = None
        else:
            defaults_filename = None
        self._config = ConfigParser(config_filename, defaults_filename)

        # initialise the schedule manager
        schedule_filename = cherrypy.config[SCHEDULE_KEY]
        self._schedule = ScheduleParser(config_filename, schedule_filename)

        # initialise storage
        self._storage = BowerbirdStorage(database_file, self._config,
                self._schedule)


    @cherrypy.expose
    def index(self, **ignored):
        # just redirect to status page
        raise cherrypy.HTTPRedirect('/status')


    @cherrypy.expose
    def name(self, **ignored):
        return self.getStationName()


    @cherrypy.expose
    @template.output('status.html')
    def status(self, **ignored):
        return template.render(station = self.getStationName(),
                uptime = self.getUptime(),
                disk_space = self.getDiskSpace(),
                local_time = self.getLocalTime(),
                last_recording = self.getLastRecording(),
                next_recording = self.getNextRecording())


    @cherrypy.expose
    @template.output('config.html')
    def config(self, config_timestamp=0, load_defaults=False, cancel=False,
            apply=False, export_config=False, new_config=None,
            import_config=False, **data):
        error = None
        values = None

        if cancel:
            raise cherrypy.HTTPRedirect('/')
        elif export_config:
            # use cherrypy utility to push the file for download. This also
            # means that we don't have to move the config file into the
            # web-accessible filesystem hierarchy
            return cherrypy.lib.static.serve_file(
                    os.path.realpath(self._config.filename),
                    "application/x-download", "attachment",
                    os.path.basename(self._config.filename))
        elif apply:
            # if someone else has modified the config, then warn the user
            # before saving their changes (overwriting the other's changes)
            if int(config_timestamp) == self._config.getTimestamp():
                self.updateConfigFromPostData(self._config, data)

                # update file
                try:
                    self._config.saveToFile()
                    self._config.exportForShell(self._config.filename + ".sh")
                    # bounce back to homepage
                    raise cherrypy.HTTPRedirect('/')
                except IOError as e:
                    # if save failed, put up error message and stay on the page
                    error = 'Error saving: %s' % e
            else:
                error = genshi.HTML('''WARNING:
                        Configuration has been changed externally.<br />
                        If you wish to keep your changes and lose the external
                        changes, then click on 'Apply' again. <br />
                        To lose your changes and preserve the external changes,
                        click 'Cancel'.''')
                # load the post data into a temporary configparser to preserve
                # the user's changes when the page is loaded again. This means
                # we don't have to duplicate the horrible POST-to-config code
                temp_conf = ConfigParser()
                self.updateConfigFromPostData(temp_conf, data)
                values = temp_conf.getValues()
                # the page loading below will send the new config timestamp so
                # we don't have to anything else here.

        if load_defaults:
            values = self._config.getDefaultValues()
        elif import_config:
            if new_config.filename:
                try:
                    values = self._config.parseFile(new_config.file)
                except Exception as e:
                    values = None
                    error = 'Unable to parse config file: "%s"' % e
            else:
                error = 'No filename provided for config import'

        if not values:
            values = self._config.getValues()
        return template.render(station=self.getStationName(),
                config_timestamp=self._config.getTimestamp(),
                error=error, using_defaults=load_defaults, values=values,
                file=self._config.filename,
                defaults_file=self._config.defaults_filename)


    @cherrypy.expose
    @template.output('schedule.html')
    def schedule(self, cancel=None, apply=None, add=None, **data):
        error = None
        if cancel:
            raise cherrypy.HTTPRedirect('/')
        elif apply:

            # use dictionary so we can untangle the POST data
            schedules = {}
            # we must check for uniqueness of titles
            titles = set()
            # just get the titles
            for key in data:
                # each schedule comes in three parts: ?.title, ?.start, ?.finish
                if key.endswith('title'):
                    id = key.split('.')[0]
                    title = data[key]
                    if title in titles:
                        error = ('Schedule titles must be unique. Found '
                                'duplicate of "%s"' % title)
                        return template.render(station=self.getStationName(),
                                recording_specs=self._schedule.getScheduleSpecs(),
                                error=error, file=self._schedule.filename, add=None)
                    schedules[id] = title
                    titles.add(title)

            # clear all schedules
            # and add them back in the order they were on the webpage
            self._schedule.clearScheduleSpecs()

            # sort the labels by their id, then add them in that order
            schedule_ids = schedules.keys()
            schedule_ids.sort()
            for id in schedule_ids:
                start_key = "%s.start" % id
                finish_key = "%s.finish" % id
                if data.has_key(start_key) and data.has_key(finish_key):
                    self._schedule.setScheduleSpec(
                            ScheduleParser.parseRecordingSpec(schedules[id],
                            data[start_key], data[finish_key]))

            # update file
            try:
                self._schedule.saveToFile()
                # bounce back to homepage
                raise cherrypy.HTTPRedirect('/')
            except IOError as e:
                # if save failed, put up error message and stay on the page
                error = "Error saving: %s" % e

        elif not add:
            # this should only happen when a remove button has been clicked
            # find the remove key
            for key in data:
                if key.endswith('remove'):
                    # get the schedule key from the title
                    id = key.split('.')[0]
                    title_key = "%s.title" % id
                    if data.has_key(title_key):
                        schedule_key = data[title_key]
                        self._schedule.deleteScheduleSpec(schedule_key)

        return template.render(station=self.getStationName(), error=error,
                recording_specs=self._schedule.getScheduleSpecs(), add=add,
                file=self._schedule.filename)


    @cherrypy.expose
    @template.output('recordings.html')
    def recordings(self, view='month', year=None, month=None, day=None,
            recording_id=None, set_recording_id=False, clear_recording_id=False,
            go_to_start=False, go_to_finish=False, go_to_today=False,
            filter_title=None, filter_start=None, filter_finish=None,
            update_filter=None, reset_filter=None, clear_selected_date=False,
            set_filter_title=None, set_filter_start=None,
            set_filter_finish=None, export_recording=False,
            export_start_time=None, export_finish_time=None,
            export_selection=False, delete_cache=False,
            **ignored):
        # HACK: this helps development slightly
        if ignored:
            print "IGNORED",ignored

        # initialise error list
        errors = []

        if delete_cache:
            # Clear all recordings. The cron job will do the scanning/refilling.
            self._storage.clearRecordings()
            # Clear all recording file entries
            self._storage.clearRecordingFiles()
            # ensure no other commands are enabled
            recording_id = None
            export_selection = None


        if reset_filter:
            filter_title = None
            clearSession(SESSION_FILTER_TITLE_KEY)
            filter_start = None
            clearSession(SESSION_FILTER_START_KEY)
            filter_finish = None
            clearSession(SESSION_FILTER_FINISH_KEY)

        if clear_selected_date:
            clearSession(SESSION_DATE_KEY)

        if clear_recording_id:
            clearSession(SESSION_RECORD_ID_KEY)

        # update filters
        if update_filter:
            # filtering on title
            if filter_title == NO_FILTER_TITLE:
                filter_title = None
                clearSession(SESSION_FILTER_TITLE_KEY)
            else:
                setSession(SESSION_FILTER_TITLE_KEY, filter_title)

            # filtering on start date & time
            if filter_start is not None:
                if filter_start:
                    try:
                        filter_start = parseDateUI(filter_start)
                        setSession(SESSION_FILTER_START_KEY, filter_start)
                    except ValueError, inst:
                        errors.append('Errror parsing filter start time: %s'
                                % inst)
                        filter_start = getSession(SESSION_FILTER_START_KEY)
                else:
                    clearSession(SESSION_FILTER_START_KEY)
                    filter_start = None
            if filter_finish is not None:
                if filter_finish:
                    try:
                        filter_finish = parseDateUI(filter_finish)
                        setSession(SESSION_FILTER_FINISH_KEY, filter_finish)
                    except ValueError, inst:
                        errors.append('Errror parsing filter finish time: %s'
                                % inst)
                        filter_finish = getSession(SESSION_FILTER_FINISH_KEY)
                else:
                    clearSession(SESSION_FILTER_FINISH_KEY)
                    filter_finish = None
Example #4
0
class Root(object):
    def __init__(self, path, database_file):
        self.path = path

        # parse config file(s)
        config_filename = cherrypy.config[CONFIG_KEY]
        if not os.path.exists(config_filename):
            raise IOError(errno.ENOENT,
                          "Config file '%s' not found" % config_filename)
        if cherrypy.config.has_key(CONFIG_DEFAULTS_KEY):
            defaults_filename = cherrypy.config[CONFIG_DEFAULTS_KEY]
            # if defaults file doesn't exist, then don't use it
            if not os.path.exists(defaults_filename):
                sys.stderr.write("Warning: configured defaults file "
                                 " '%s' doesn't exist\n" % defaults_filename)
                defaults_filename = None
        else:
            defaults_filename = None
        self._config = ConfigParser(config_filename, defaults_filename)

        # initialise the schedule manager
        schedule_filename = cherrypy.config[SCHEDULE_KEY]
        self._schedule = ScheduleParser(config_filename, schedule_filename)

        # initialise storage
        self._storage = BowerbirdStorage(database_file, self._config,
                                         self._schedule)

    @cherrypy.expose
    def index(self, **ignored):
        # just redirect to status page
        raise cherrypy.HTTPRedirect('/status')

    @cherrypy.expose
    def name(self, **ignored):
        return self.getStationName()

    @cherrypy.expose
    @template.output('status.html')
    def status(self, **ignored):
        return template.render(station=self.getStationName(),
                               uptime=self.getUptime(),
                               disk_space=self.getDiskSpace(),
                               local_time=self.getLocalTime(),
                               last_recording=self.getLastRecording(),
                               next_recording=self.getNextRecording())

    @cherrypy.expose
    @template.output('config.html')
    def config(self,
               config_timestamp=0,
               load_defaults=False,
               cancel=False,
               apply=False,
               export_config=False,
               new_config=None,
               import_config=False,
               **data):
        error = None
        values = None

        if cancel:
            raise cherrypy.HTTPRedirect('/')
        elif export_config:
            # use cherrypy utility to push the file for download. This also
            # means that we don't have to move the config file into the
            # web-accessible filesystem hierarchy
            return cherrypy.lib.static.serve_file(
                os.path.realpath(self._config.filename),
                "application/x-download", "attachment",
                os.path.basename(self._config.filename))
        elif apply:
            # if someone else has modified the config, then warn the user
            # before saving their changes (overwriting the other's changes)
            if int(config_timestamp) == self._config.getTimestamp():
                self.updateConfigFromPostData(self._config, data)

                # update file
                try:
                    self._config.saveToFile()
                    self._config.exportForShell(self._config.filename + ".sh")
                    # bounce back to homepage
                    raise cherrypy.HTTPRedirect('/')
                except IOError as e:
                    # if save failed, put up error message and stay on the page
                    error = 'Error saving: %s' % e
            else:
                error = genshi.HTML('''WARNING:
                        Configuration has been changed externally.<br />
                        If you wish to keep your changes and lose the external
                        changes, then click on 'Apply' again. <br />
                        To lose your changes and preserve the external changes,
                        click 'Cancel'.''')
                # load the post data into a temporary configparser to preserve
                # the user's changes when the page is loaded again. This means
                # we don't have to duplicate the horrible POST-to-config code
                temp_conf = ConfigParser()
                self.updateConfigFromPostData(temp_conf, data)
                values = temp_conf.getValues()
                # the page loading below will send the new config timestamp so
                # we don't have to anything else here.

        if load_defaults:
            values = self._config.getDefaultValues()
        elif import_config:
            if new_config.filename:
                try:
                    values = self._config.parseFile(new_config.file)
                except Exception as e:
                    values = None
                    error = 'Unable to parse config file: "%s"' % e
            else:
                error = 'No filename provided for config import'

        if not values:
            values = self._config.getValues()
        return template.render(station=self.getStationName(),
                               config_timestamp=self._config.getTimestamp(),
                               error=error,
                               using_defaults=load_defaults,
                               values=values,
                               file=self._config.filename,
                               defaults_file=self._config.defaults_filename)

    @cherrypy.expose
    @template.output('schedule.html')
    def schedule(self, cancel=None, apply=None, add=None, **data):
        error = None
        if cancel:
            raise cherrypy.HTTPRedirect('/')
        elif apply:

            # use dictionary so we can untangle the POST data
            schedules = {}
            # we must check for uniqueness of titles
            titles = set()
            # just get the titles
            for key in data:
                # each schedule comes in three parts: ?.title, ?.start, ?.finish
                if key.endswith('title'):
                    id = key.split('.')[0]
                    title = data[key]
                    if title in titles:
                        error = ('Schedule titles must be unique. Found '
                                 'duplicate of "%s"' % title)
                        return template.render(
                            station=self.getStationName(),
                            recording_specs=self._schedule.getScheduleSpecs(),
                            error=error,
                            file=self._schedule.filename,
                            add=None)
                    schedules[id] = title
                    titles.add(title)

            # clear all schedules
            # and add them back in the order they were on the webpage
            self._schedule.clearScheduleSpecs()

            # sort the labels by their id, then add them in that order
            schedule_ids = schedules.keys()
            schedule_ids.sort()
            for id in schedule_ids:
                start_key = "%s.start" % id
                finish_key = "%s.finish" % id
                if data.has_key(start_key) and data.has_key(finish_key):
                    self._schedule.setScheduleSpec(
                        ScheduleParser.parseRecordingSpec(
                            schedules[id], data[start_key], data[finish_key]))

            # update file
            try:
                self._schedule.saveToFile()
                # bounce back to homepage
                raise cherrypy.HTTPRedirect('/')
            except IOError as e:
                # if save failed, put up error message and stay on the page
                error = "Error saving: %s" % e

        elif not add:
            # this should only happen when a remove button has been clicked
            # find the remove key
            for key in data:
                if key.endswith('remove'):
                    # get the schedule key from the title
                    id = key.split('.')[0]
                    title_key = "%s.title" % id
                    if data.has_key(title_key):
                        schedule_key = data[title_key]
                        self._schedule.deleteScheduleSpec(schedule_key)

        return template.render(
            station=self.getStationName(),
            error=error,
            recording_specs=self._schedule.getScheduleSpecs(),
            add=add,
            file=self._schedule.filename)

    @cherrypy.expose
    @template.output('recordings.html')
    def recordings(self,
                   view='month',
                   year=None,
                   month=None,
                   day=None,
                   recording_id=None,
                   set_recording_id=False,
                   clear_recording_id=False,
                   go_to_start=False,
                   go_to_finish=False,
                   go_to_today=False,
                   filter_title=None,
                   filter_start=None,
                   filter_finish=None,
                   update_filter=None,
                   reset_filter=None,
                   clear_selected_date=False,
                   set_filter_title=None,
                   set_filter_start=None,
                   set_filter_finish=None,
                   export_recording=False,
                   export_start_time=None,
                   export_finish_time=None,
                   export_selection=False,
                   delete_cache=False,
                   **ignored):
        # HACK: this helps development slightly
        if ignored:
            print "IGNORED", ignored

        # initialise error list
        errors = []

        if delete_cache:
            # Clear all recordings. The cron job will do the scanning/refilling.
            self._storage.clearRecordings()
            # Clear all recording file entries
            self._storage.clearRecordingFiles()
            # ensure no other commands are enabled
            recording_id = None
            export_selection = None

        if reset_filter:
            filter_title = None
            clearSession(SESSION_FILTER_TITLE_KEY)
            filter_start = None
            clearSession(SESSION_FILTER_START_KEY)
            filter_finish = None
            clearSession(SESSION_FILTER_FINISH_KEY)

        if clear_selected_date:
            clearSession(SESSION_DATE_KEY)

        if clear_recording_id:
            clearSession(SESSION_RECORD_ID_KEY)

        # update filters
        if update_filter:
            # filtering on title
            if filter_title == NO_FILTER_TITLE:
                filter_title = None
                clearSession(SESSION_FILTER_TITLE_KEY)
            else:
                setSession(SESSION_FILTER_TITLE_KEY, filter_title)

            # filtering on start date & time
            if filter_start is not None:
                if filter_start:
                    try:
                        filter_start = parseDateUI(filter_start)
                        setSession(SESSION_FILTER_START_KEY, filter_start)
                    except ValueError, inst:
                        errors.append('Errror parsing filter start time: %s' %
                                      inst)
                        filter_start = getSession(SESSION_FILTER_START_KEY)
                else:
                    clearSession(SESSION_FILTER_START_KEY)
                    filter_start = None
            if filter_finish is not None:
                if filter_finish:
                    try:
                        filter_finish = parseDateUI(filter_finish)
                        setSession(SESSION_FILTER_FINISH_KEY, filter_finish)
                    except ValueError, inst:
                        errors.append('Errror parsing filter finish time: %s' %
                                      inst)
                        filter_finish = getSession(SESSION_FILTER_FINISH_KEY)
                else:
                    clearSession(SESSION_FILTER_FINISH_KEY)
                    filter_finish = None