def __init__(self):
        # create app directory
        try:
            os.makedirs(app_dir)
        except OSError:
            if not os.path.isdir(app_dir):
                raise

        # open config file
        self.config = Config(os.path.join(app_dir, config_file))
        self.config.read()

        # configure cache
        region.configure('dogpile.cache.dbm', expiration_time=timedelta(days=30),
                         arguments={'filename': os.path.join(app_dir, cache_file), 'lock_factory': MutexLock})
class SubliminalExtension(GObject.GObject, Nautilus.MenuProvider):
    def __init__(self):
        # create app directory
        try:
            os.makedirs(app_dir)
        except OSError:
            if not os.path.isdir(app_dir):
                raise

        # open config file
        self.config = Config(os.path.join(app_dir, config_file))
        self.config.read()

        # configure cache
        region.configure('dogpile.cache.dbm', expiration_time=timedelta(days=30),
                         arguments={'filename': os.path.join(app_dir, cache_file), 'lock_factory': MutexLock})

    def get_file_items(self, window, files):
        # lightweight filter on file type and extension
        if not any(f.is_directory() or f.get_name().endswith(VIDEO_EXTENSIONS) for f in files):
            return

        # create subliminal menu
        subliminal_menuitem = Nautilus.MenuItem(name='SubliminalMenu::Subliminal', label='Subliminal')
        sub_menus = Nautilus.Menu()
        subliminal_menuitem.set_submenu(sub_menus)

        # create choose submenu on single file
        if len(files) == 1 and not files[0].is_directory():
            choose_menuitem = Nautilus.MenuItem(name='SubliminalSubMenu::Choose', label=_('Choose subtitles'))
            choose_menuitem.connect('activate', self.choose_callback, files)
            sub_menus.append_item(choose_menuitem)

        # create download submenu
        download_menuitem = Nautilus.MenuItem(name='SubliminalSubMenu::Download', label=_('Download subtitles'))
        download_menuitem.connect('activate', self.download_callback, files)
        sub_menus.append_item(download_menuitem)

        # create configure submenu
        configure_menuitem = Nautilus.MenuItem(name='SubliminalSubMenu::Configure', label=_('Configure...'))
        configure_menuitem.connect('activate', self.config_callback)
        sub_menus.append_item(configure_menuitem)

        return subliminal_menuitem,

    def get_background_items(self, window, current_folder):
        return []

    def choose_callback(self, menuitem, files):
        # scan the video
        video = scan_video(files[0].get_location().get_path(), subtitles=False, embedded_subtitles=False)

        # load the interface
        builder = Gtk.Builder()
        builder.set_translation_domain('subliminal')
        builder.add_from_file(os.path.join(os.path.dirname(__file__), 'subliminal', 'ui', 'choose.glade'))

        # set the video filename
        video_filename = builder.get_object('video_filename_label')
        video_filename.set_text(files[0].get_name())

        # start the spinner
        spinner = builder.get_object('spinner')
        spinner.start()

        def _list_subtitles():
            # list subtitles
            with ProviderPool(providers=self.config.providers, provider_configs=self.config.provider_configs) as pool:
                subtitles = pool.list_subtitles(video, self.config.languages)

            # fill the subtitle liststore
            subtitle_liststore = builder.get_object('subtitle_liststore')
            for s in subtitles:
                matches = s.get_matches(video, hearing_impaired=self.config.hearing_impaired)
                scaled_score = compute_score(matches, video)
                if s.hearing_impaired == self.config.hearing_impaired:
                    scaled_score -= video.scores['hearing_impaired']
                scaled_score *= 100 / video.scores['hash']
                subtitle_liststore.append([s.id, nice_language(s.language), scaled_score, s.provider_name.capitalize(),
                                           s.hearing_impaired, s.page_link, False])
            subtitle_liststore.set_sort_column_id(2, Gtk.SortType.DESCENDING)

            # stop the spinner
            spinner.stop()

            # connect signals
            builder.connect_signals(ChooseHandler(self.config, video, subtitles, spinner))

        threading.Thread(target=_list_subtitles).start()

        # display window
        window = builder.get_object('subtitle_window')
        window.show_all()
        Gtk.main()

    def download_callback(self, menuitem, files):
        # scan videos
        videos = []
        for f in files:
            # ignore non-writable locations
            if not f.can_write():
                continue

            # directories
            if f.is_directory():
                try:
                    scanned_videos = scan_videos(f.get_location().get_path(), subtitles=True,
                                                 embedded_subtitles=self.config.embedded_subtitles)
                except:
                    continue
                for video in scanned_videos:
                    if check_video(video, languages=self.config.languages, age=self.config.age,
                                   undefined=self.config.single):
                        videos.append(video)
                continue

            # other inputs
            try:
                video = scan_video(f.get_location().get_path(), subtitles=True,
                                   embedded_subtitles=self.config.embedded_subtitles)
            except:
                continue
            if check_video(video, languages=self.config.languages, undefined=self.config.single):
                videos.append(video)

        # download best subtitles
        downloaded_subtitles = defaultdict(list)
        with ProviderPool(providers=self.config.providers, provider_configs=self.config.provider_configs) as pool:
            for v in videos:
                subtitles = pool.download_best_subtitles(
                    pool.list_subtitles(v, self.config.languages - v.subtitle_languages),
                    v, self.config.languages, min_score=v.scores['hash'] * self.config.min_score / 100,
                    hearing_impaired=self.config.hearing_impaired, only_one=self.config.single
                )
                downloaded_subtitles[v] = subtitles

        # save subtitles
        for v, subtitles in downloaded_subtitles.items():
            save_subtitles(v, subtitles, single=self.config.single)

    def config_callback(self, *args, **kwargs):
        # load the interface
        builder = Gtk.Builder()
        builder.set_translation_domain('subliminal')
        builder.add_from_file(os.path.join(os.path.dirname(__file__), 'subliminal', 'ui', 'config.glade'))

        # configure the about page
        aboutdialog = builder.get_object('aboutdialog')
        aboutdialog.set_version(__version__)
        aboutdialog.set_copyright(__copyright__)
        aboutdialog.vbox.reparent(builder.get_object('about_box'))

        # fill the language liststore
        available_languages = set()
        for provider in provider_manager:
            available_languages |= provider.plugin.languages
        language_liststore = builder.get_object('language_liststore')
        for language in sorted(available_languages - ignored_languages, key=nice_language):
            language_liststore.append([nice_language(language), str(language)])

        # set language selection
        language_treeselection = builder.get_object('language_treeselection')
        for language in language_liststore:
            if Language.fromietf(language[1]) in self.config.languages:
                language_treeselection.select_path(language.path)

        # fill the provider liststore
        provider_liststore = builder.get_object('provider_liststore')
        for provider in sorted([p.name for p in provider_manager]):
            provider_liststore.append([provider.capitalize(), str(self.config.provider_configs.get(provider, ''))])

        # set provider selection
        provider_treeselection = builder.get_object('provider_treeselection')
        for provider in provider_liststore:
            if provider[0].lower() in self.config.providers:
                provider_treeselection.select_iter(provider.iter)

        # set single state
        single_switch = builder.get_object('single_switch')
        single_switch.set_active(self.config.single)

        # set embedded subtitles state
        embedded_subtitles_switch = builder.get_object('embedded_subtitles_switch')
        embedded_subtitles_switch.set_active(self.config.embedded_subtitles)

        # set age value
        age_spinbutton = builder.get_object('age_spinbutton')
        age_spinbutton.set_value(self.config.age.days)

        # set hearing impaired state
        hearing_impaired_switch = builder.get_object('hearing_impaired_switch')
        hearing_impaired_switch.set_active(self.config.hearing_impaired)

        # set min score value
        min_score_spinbutton = builder.get_object('min_score_spinbutton')
        min_score_spinbutton.set_value(self.config.min_score)

        # connect signals
        builder.connect_signals(ConfigHandler(self.config))

        # display window
        window = builder.get_object('config_window')
        window.show_all()
        Gtk.main()