Ejemplo n.º 1
0
    def set_tags(self, showid, newtags):
        """
        Updates the tags of the specified **showid** to **newtags**
        and queues the list update for the next sync.
        """
        # Check if operation is supported by the API
        if 'can_tag' not in self.mediainfo or not self.mediainfo.get(
                'can_tag'):
            raise utils.EngineError('Operation not supported by API.')

        # Get the show and update it
        show = self.get_show_info(showid)
        # More checks
        if show['my_tags'] == newtags:
            raise utils.EngineError("Tags already %s" % newtags)

        # Change score
        self.msg.info(
            self.name,
            "Updating show %s to tags '%s'..." % (show['title'], newtags))
        self.data_handler.queue_update(show, 'my_tags', newtags)

        # Emit signal
        self._emit_signal('tags_changed', show)

        return show
Ejemplo n.º 2
0
    def get_show_info(self, showid=None, title=None, filename=None):
        """
        Returns the show dictionary for the specified **showid**.
        """
        showdict = self.data_handler.get()

        if showid:
            # Get show by ID
            try:
                return showdict[showid]
            except KeyError:
                raise utils.EngineError("Show not found.")
        elif title:
            showdict = self.data_handler.get()
            # Get show by title, slower
            for k, show in showdict.items():
                if show['title'] == title:
                    return show
            raise utils.EngineError("Show not found.")
        elif filename:
            # Guess show by filename
            self.msg.debug(self.name, "Guessing by filename.")

            aie = AnimeInfoExtractor(filename)
            (show_title, ep) = aie.getName(), aie.getEpisode()
            self.msg.debug(self.name, "Guessed {}".format(show_title))

            if show_title:
                show = utils.guess_show(show_title, self._get_tracker_list())
                if show:
                    return (show, ep)
                else:
                    raise utils.EngineError("Show not found.")
            else:
                raise utils.EngineError("File name not recognized.")
Ejemplo n.º 3
0
    def scan_library(self, my_status=None, rescan=False):
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_play'):
            raise utils.EngineError(
                'Operation not supported by current site or mediatype.')
        if not self.config['searchdir']:
            raise utils.EngineError('Media directory is not set.')
        if not utils.dir_exists(self.config['searchdir']):
            raise utils.EngineError('The set media directory doesn\'t exist.')

        t = time.time()
        library = {}
        library_cache = self.data_handler.library_cache_get()

        if not my_status:
            my_status = self.mediainfo['status_start']

        self.msg.info(self.name, "Scanning local library...")
        self.msg.debug(self.name, "Directory: %s" % self.config['searchdir'])
        tracker_list = self._get_tracker_list(my_status)

        # Do a full listing of the media directory
        for fullpath, filename in utils.regex_find_videos(
                'mkv|mp4|avi', self.config['searchdir']):
            (library, library_cache) = self._add_show_to_library(
                library, library_cache, rescan, fullpath, filename,
                tracker_list)

        self.msg.debug(self.name, "Time: %s" % (time.time() - t))
        self.data_handler.library_save(library)
        self.data_handler.library_cache_save(library_cache)
        return library
Ejemplo n.º 4
0
    def set_status(self, showid, newstatus):
        """
        Updates the score of the specified **showid** to **newstatus** (number)
        and queues the list update for the next sync.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_status'):
            raise utils.EngineError('Operation not supported by API.')

        try:
            newstatus = int(newstatus)
        except ValueError:
            pass # It's not necessary for it to be an int

        # Check if the status is valid
        _statuses = self.mediainfo['statuses_dict']
        if newstatus not in _statuses:
            raise utils.EngineError('Invalid status.')

        # Get the show and update it
        show = self.get_show_info(showid)
        # More checks
        if show['my_status'] == newstatus:
            raise utils.EngineError("Show already in %s." % _statuses[newstatus])

        # Change status
        old_status = show['my_status']
        self.msg.info(self.name, "Updating show %s status to %s..." % (show['title'], _statuses[newstatus]))
        self.data_handler.queue_update(show, 'my_status', newstatus)

        # Emit signal
        self._emit_signal('status_changed', show, old_status)

        return show
Ejemplo n.º 5
0
    def play_episode(self, show, playep=0):
        """
        Does a local search in the hard disk (in the folder specified by the config file)
        for the specified episode (**playep**) for the specified **show**.

        If no **playep** is specified, the next episode of the show will be played.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_play'):
            raise utils.EngineError(
                'Operation not supported by current site or mediatype.')

        try:
            playep = int(playep)
        except ValueError:
            raise utils.EngineError('Episode must be numeric.')

        if show:
            playing_next = False
            if not playep:
                playep = show['my_progress'] + 1
                playing_next = True

            if show['total'] and playep > show['total']:
                raise utils.EngineError('Episode beyond limits.')

            if self.config.get('debug_oldsearch'):
                # Deprecated
                self.msg.info(
                    self.name,
                    "Searching for %s %s..." % (show['title'], playep))
                titles = self.data_handler.get_show_titles(show)
                filename, endep = self._search_video(titles, playep)
            else:
                self.msg.info(
                    self.name,
                    "Getting %s %s from library..." % (show['title'], playep))
                filename = self.get_episode_path(show, playep)
                endep = playep

            if filename:
                self.msg.info(self.name, 'Found. Starting player...')
                arg_list = shlex.split(self.config['player'])
                arg_list.append(filename)
                try:
                    with open(os.devnull, 'wb') as DEVNULL:
                        subprocess.Popen(arg_list,
                                         stdout=DEVNULL,
                                         stderr=DEVNULL)
                except OSError:
                    raise utils.EngineError(
                        'Player not found, check your config.json')
                return endep
            else:
                raise utils.EngineError('Episode file not found.')
Ejemplo n.º 6
0
    def set_score(self, showid, newscore):
        """
        Updates the score of the specified **showid** to **newscore**
        and queues the list update for the next sync.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_score'):
            raise utils.EngineError('Operation not supported by API.')

        # Check for the correctness of the score
        if (Decimal(str(newscore)) % Decimal(str(self.mediainfo['score_step']))) != 0:
            raise utils.EngineError('Invalid score.')

        # Convert to proper type
        if isinstance(self.mediainfo['score_step'], int):
            newscore = int(newscore)
        else:
            newscore = float(newscore)

        # Get the show and update it
        show = self.get_show_info(showid)
        # More checks
        if newscore > self.mediainfo['score_max']:
            raise utils.EngineError('Score out of limits.')
        if show['my_score'] == newscore:
            raise utils.EngineError("Score already at %s" % newscore)

        # Change score
        self.msg.info(self.name, "Updating show %s to score %s..." %
                      (show['title'], newscore))
        self.data_handler.queue_update(show, 'my_score', newscore)

        # Emit signal
        self._emit_signal('score_changed', show)

        # Change status if required
        if (
                show['total'] and
                show['my_progress'] == show['total'] and
                show['my_score'] and
                self.mediainfo.get('can_status') and
                self.config['auto_status_change'] and
                self.config['auto_status_change_if_scored'] and
                self.mediainfo.get('statuses_finish')
        ):
            try:
                self.set_status(show['id'], self._guess_new_finish(show))
            except utils.EngineError as e:
                # Only warn about engine errors since status change here is not crtical
                self.msg.warn(
                    self.name, 'Updated episode but status wasn\'t changed: %s' % e)

        return show
Ejemplo n.º 7
0
    def get_episode_path(self, show, episode):
        """
        This function returns the full path of the requested episode from the requested show.
        """

        library = self.library()
        showid = show['id']

        if showid not in library:
            raise utils.EngineError('Show not in library.')
        if episode not in library[showid]:
            raise utils.EngineError('Episode not in library.')

        return library[showid][episode]
Ejemplo n.º 8
0
    def set_status(self, showid, newstatus):
        """
        Updates the score of the specified **showid** to **newstatus** (number)
        and queues the list update for the next sync.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_status'):
            raise utils.EngineError('Operation not supported by API.')

        try:
            newstatus = int(newstatus)
        except ValueError:
            pass  # It's not necessary for it to be an int

        # Check if the status is valid
        _statuses = self.mediainfo['statuses_dict']
        if newstatus not in _statuses:
            raise utils.EngineError('Invalid status.')

        # Get the show and update it
        show = self.get_show_info(showid)
        # More checks
        if show['my_status'] == newstatus:
            raise utils.EngineError("Show already in %s." %
                                    _statuses[newstatus])

        # Change status
        old_status = show['my_status']
        self.msg.info(
            self.name, "Updating show %s status to %s..." %
            (show['title'], _statuses[newstatus]))
        self.data_handler.queue_update(show, 'my_status', newstatus)

        # Change repeat if required
        if self.mediainfo.get('can_repeat'):
            if (old_status == self.mediainfo['statuses_start'][-1]
                    or newstatus == self.mediainfo['statuses_start'][-1]):
                newrepeat = show['my_repeat'] + 1
                try:
                    self.set_repeat(show['id'], newrepeat)
                except utils.EngineError as e:
                    # Only warn about engine errors since repeat change here is not critical
                    self.msg.warn(
                        self.name,
                        'Updated status but repeat wasn\'t changed: %s' % e)

        # Emit signal
        self._emit_signal('status_changed', show, old_status)

        return show
Ejemplo n.º 9
0
 def get_show_info_title(self, pattern):
     showdict = self.data_handler.get()
     # Do title lookup, slower
     for k, show in showdict.items():
         if show['title'] == pattern:
             return show
     raise utils.EngineError("Show not found.")
Ejemplo n.º 10
0
    def _open_folder(self, show_id):
        show = self._engine.get_show_info(show_id)
        try:
            filename = self._engine.get_episode_path(show, 1)
            with open(os.devnull, 'wb') as DEVNULL:
                if sys.platform == 'darwin':
                    subprocess.Popen(
                        ["open", os.path.dirname(filename)],
                        stdout=DEVNULL,
                        stderr=DEVNULL)
                elif sys.platform == 'win32':
                    subprocess.Popen(
                        ["explorer", os.path.dirname(filename)],
                        stdout=DEVNULL,
                        stderr=DEVNULL)
                else:
                    subprocess.Popen(
                        ["/usr/bin/xdg-open",
                         os.path.dirname(filename)],
                        stdout=DEVNULL,
                        stderr=DEVNULL)
        except OSError:
            # xdg-open failed.
            raise utils.EngineError("Could not open folder.")

        except utils.EngineError:
            # Show not in library.
            self._error_dialog_idle("No folder found.")
Ejemplo n.º 11
0
    def play_episode(self, show, playep=0):
        """
        Does a local search in the hard disk (in the folder specified by the config file)
        for the specified episode (**playep**) for the specified **show**.

        If no **playep** is specified, the next episode of the show will be returned.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_play'):
            raise utils.EngineError(
                'Operation not supported by current site or mediatype.')

        try:
            playep = int(playep)
        except ValueError:
            raise utils.EngineError('Episode must be numeric.')

        if show:
            playing_next = False
            if not playep:
                playep = show['my_progress'] + 1
                playing_next = True

            if show['total'] and playep > show['total']:
                raise utils.EngineError('Episode beyond limits.')

            self.msg.info(
                self.name,
                "Getting %s %s from library..." % (show['title'], playep))
            filename = self.get_episode_path(show, playep)
            endep = playep

            if filename:
                self.msg.info(self.name, 'Found. Starting player...')
                args = shlex.split(self.config['player'])

                if len(args) > 0 and shutil.which(args[0]) == None:
                    raise utils.EngineError(
                        'Player not found, check your config.json')

                args.append(filename)
                return args
            else:
                raise utils.EngineError('Episode file not found.')
Ejemplo n.º 12
0
    def scan_library(self, my_status=None, rescan=False):
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_play'):
            raise utils.EngineError(
                'Operation not supported by current site or mediatype.')
        if not self.config['searchdir']:
            raise utils.EngineError('Media directories not set.')

        t = time.time()
        library = {}
        library_cache = self.data_handler.library_cache_get()

        if not my_status:
            if self.config['scan_whole_list']:
                my_status = self.mediainfo['statuses']
            else:
                my_status = self.mediainfo.get(
                    'statuses_library', self.mediainfo['statuses_start'])

        if rescan:
            self.msg.info(self.name,
                          "Scanning local library (overriding cache)...")
        else:
            self.msg.info(self.name, "Scanning local library...")

        tracker_list = self._get_tracker_list(my_status)

        for searchdir in self.searchdirs:
            self.msg.debug(self.name, "Directory: %s" % searchdir)

            # Do a full listing of the media directory
            for fullpath, filename in utils.regex_find_videos(searchdir):
                if self.config['library_full_path']:
                    filename = self._get_show_name_from_full_path(
                        searchdir, fullpath).strip()
                (library, library_cache) = self._add_show_to_library(
                    library, library_cache, rescan, fullpath, filename,
                    tracker_list)

            self.msg.debug(self.name, "Time: %s" % (time.time() - t))
            self.data_handler.library_save(library)
            self.data_handler.library_cache_save(library_cache)
        return library
Ejemplo n.º 13
0
    def get_show_info(self, showid):
        """
        Returns the show dictionary for the specified **showid**.
        """
        showdict = self.data_handler.get()

        try:
            return showdict[showid]
        except KeyError:
            raise utils.EngineError("Show not found.")
Ejemplo n.º 14
0
    def set_dates(self, showid, start_date=None, finish_date=None):
        """
        Updates the start date and finish date of a show.
        If any of the two are None, it won't be changed.
        """
        if not self.mediainfo.get('can_date'):
            raise utils.EngineError('Operation not supported by API.')

        show = self.get_show_info(showid)

        # Change the start date if required
        if start_date:
            if not isinstance(start_date, datetime.date):
                raise utils.EngineError('start_date must be a Date object.')
            self.data_handler.queue_update(show, 'my_start_date', start_date)

        if finish_date:
            if not isinstance(finish_date, datetime.date):
                raise utils.EngineError('finish_date must be a Date object.')
            self.data_handler.queue_update(show, 'my_finish_date', finish_date)
Ejemplo n.º 15
0
    def search(self, criteria, method=utils.SEARCH_METHOD_KW):
        """
        Request a remote list of shows matching the criteria
        and returns it as a list of show dictionaries.
        This is useful to add a show.
        """
        if method not in self.mediainfo.get('search_methods', [utils.SEARCH_METHOD_KW]):
            raise utils.EngineError(
                'Search method not supported by API or mediatype.')

        return self.data_handler.search(criteria, method)
Ejemplo n.º 16
0
    def set_repeat(self, showid, newrepeat):
        """
        Updates the rewatch counter of the specified **showid** to **newrepeat**
        and queues the list update for the next sync.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_repeat'):
            raise utils.EngineError('Operation not supported by API.')

        # Get the show and update it
        show = self.get_show_info(showid)
        # More checks
        if show['my_repeat'] == newrepeat:
            raise utils.EngineError("Show already at %s repeats" % newrepeat)

        # Change repeat
        self.msg.info(
            self.name, "Updating show %s to rewatched $s times" %
            (show['title'], newrepeat))
        self.data_handler.queue_update(show, 'my_repeat', newrepeat)

        # Emit signal
        self._emit_signal('repeat_changed', newrepeat)
Ejemplo n.º 17
0
    def delete_show(self, show):
        """
        Deletes **show** completely from the list and queues the list update for the next sync.
        """
        if not self.mediainfo.get('can_delete'):
            raise utils.EngineError('Operation not supported by API.')

        # Add in data handler
        self.data_handler.queue_delete(show)

        # Update the tracker with the new information
        self._update_tracker()

        # Emit signal
        self._emit_signal('show_deleted', show)
Ejemplo n.º 18
0
    def add_show(self, show, status=None):
        """
        Adds **show** to the list and queues the list update
        for the next sync.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_add'):
            raise utils.EngineError('Operation not supported by API.')

        # Set to the requested status
        if status:
            if status not in self.mediainfo['statuses']:
                raise utils.EngineError('Invalid status.')

            show['my_status'] = status

        # Add in data handler
        self.data_handler.queue_add(show)

        # Update the tracker with the new information
        self._update_tracker()

        # Emit signal
        self._emit_signal('show_added', show)
Ejemplo n.º 19
0
    def do_openfolder(self):
        item = self._get_selected_item()

        try:
            show = self.engine.get_show_info(item.showid)
            filename = self.engine.get_episode_path(show, 1)
            with open(os.devnull, 'wb') as DEVNULL:
                subprocess.Popen(["/usr/bin/xdg-open",
                os.path.dirname(filename)], stdout=DEVNULL, stderr=DEVNULL)
        except OSError:
            # xdg-open failed.
            raise utils.EngineError("Could not open folder.")

        except utils.EngineError:
            # Show not in library.
             self.error("No folder found.")
Ejemplo n.º 20
0
    def play_random(self):
        """
        This function will pick a random show that has a new episode to watch
        and return the arguments to play it.
        """
        library = self.library()
        newep = []

        self.msg.info(self.name, 'Looking for random episode.')

        for showid, eps in library.items():
            show = self.get_show_info(showid)
            if show['my_progress'] + 1 in eps:
                newep.append(show)

        if not newep:
            raise utils.EngineError('No new episodes found to pick from.')

        show = random.choice(newep)
        return self.play_episode(show)
Ejemplo n.º 21
0
    def play_episode(self, show, playep=0):
        """
        Does a local search in the hard disk (in the folder specified by the config file)
        for the specified episode (**playep**) for the specified **show**.

        If no **playep** is specified, the next episode of the show will be played.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_play'):
            raise utils.EngineError(
                'Operation not supported by current site or mediatype.')

        try:
            playep = int(playep)
        except ValueError:
            raise utils.EngineError('Episode must be numeric.')

        if show:
            playing_next = False
            if not playep:
                playep = show['my_progress'] + 1
                playing_next = True

            if show['total'] and playep > show['total']:
                raise utils.EngineError('Episode beyond limits.')

            self.msg.info(self.name, "Getting %s %s from library..." %
                          (show['title'], playep))
            filename = self.get_episode_path(show, playep)
            endep = playep

            if filename:
                self.msg.info(self.name, 'Found. Starting player...')
                arg_list = shlex.split(self.config['player'])

                if len(arg_list) > 0 and shutil.which(arg_list[0]) == None:
                    raise utils.EngineError(
                        'Player not found, check your config.json')

                arg_list.append(filename)

                # Do a double fork on *nix to prevent zombie processes
                if not sys.platform.startswith('win32'):
                    try:
                        pid = os.fork()
                        if pid > 0:
                            os.waitpid(pid, 0)
                            return endep
                    except OSError:
                        sys.exit(1)

                    os.setsid()
                    fd = os.open("/dev/null", os.O_RDWR)
                    os.dup2(fd, 0)
                    os.dup2(fd, 1)
                    os.dup2(fd, 2)
                    try:
                        pid = os.fork()
                        if pid > 0:
                            sys.exit(0)
                    except OSError:
                        sys.exit(1)
                    os.execv(arg_list[0], arg_list)
                else:
                    subprocess.Popen(
                        arg_list, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
                    return endep
            else:
                raise utils.EngineError('Episode file not found.')
Ejemplo n.º 22
0
    def play_episode(self, show, playep=0, playto=0):
        """
        Does a local search in the hard disk (in the folder specified by the config file)
        for the specified episode (**playep**) for the specified **show**.

        If no **playep** is specified, the next episode of the show will be played.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_play'):
            raise utils.EngineError(
                'Operation not supported by current site or mediatype.')

        eps = re.split('-', str(playep))
        if len(eps) > 1:
            if eps[0] != '':
                playep = eps[0]
            else:
                playep = 0
            if eps[1] != '':
                playto = eps[1]
            else:
                playto = show['total']
        try:
            playep = int(playep)
            playto = int(playto)
        except ValueError:
            raise utils.EngineError(
                'Episode[s] must be numeric or with optional range (eg: 1-3, -3, or - to play all unseen episodes)'
            )

        if show:
            playing_next = False
            if not playep:
                playep = show['my_progress'] + 1
                playing_next = True

            if not playto or playto < playep:
                playto = playep

            if show['total']:
                if playep > show['total']:
                    raise utils.EngineError('Episode beyond limits.')
                if playto > show['total']:
                    self.msg.info(
                        self.name,
                        "Play to %i is beyond limits of show %s. Defaulting to total episodes of %s"
                        % (playto, show['title'], show['total']))
                    playto = show['total']

            self.msg.info(
                self.name,
                "Getting %s %s from library..." % (show['title'], playep))
            endep = playep

            if self.get_episode_path(show, playep):
                self.msg.info(self.name, 'Found. Starting player...')
                arg_list = shlex.split(self.config['player'])
                for episode in range(playep, playto + 1):
                    ep = self.get_episode_path(show,
                                               episode,
                                               error_on_fail=False)
                    if ep:
                        arg_list.append(ep)
                try:
                    with open(os.devnull, 'wb') as DEVNULL:
                        subprocess.Popen(arg_list,
                                         stdout=DEVNULL,
                                         stderr=DEVNULL)
                except OSError:
                    raise utils.EngineError(
                        'Player not found, check your config.json')
                return endep
            else:
                raise utils.EngineError('Episode file not found.')
Ejemplo n.º 23
0
    def set_episode(self, showid, newep):
        """
        Updates the progress of the specified **showid** to **newep**
        and queues the list update for the next sync.
        """
        # Check if operation is supported by the API
        if not self.mediainfo.get('can_update'):
            raise utils.EngineError('Operation not supported by API.')

        # Check for the episode number
        try:
            newep = int(newep)
        except ValueError:
            raise utils.EngineError('Episode must be numeric.')

        # Get the show info
        show = self.get_show_info(showid)
        # More checks
        if (show['total'] and newep > show['total']) or newep < 0:
            raise utils.EngineError('Episode out of limits.')
        if show['my_progress'] == newep:
            raise utils.EngineError("Show already at episode %d" % newep)

        # Change episode
        self.msg.info(self.name, "Updating show %s to episode %d..." %
                      (show['title'], newep))
        self.data_handler.queue_update(show, 'my_progress', newep)

        # Emit signal
        self._emit_signal('episode_changed', show)

        # Change status if required
        if self.config['auto_status_change'] and self.mediainfo.get('can_status'):
            try:
                if newep == show['total'] and self.mediainfo.get('statuses_finish'):
                    if (
                            not self.config['auto_status_change_if_scored'] or
                            not self.mediainfo.get('can_score') or
                            show['my_score']
                    ):
                        # Change to finished status
                        self.set_status(
                            show['id'], self._guess_new_finish(show))
                    else:
                        self.msg.warn(
                            self.name, "Updated episode but status won't be changed until a score is set.")
                elif newep == 1 and self.mediainfo.get('statuses_start'):
                    # Change to start status
                    self.set_status(show['id'], self._guess_new_start(show))
            except utils.EngineError as e:
                # Only warn about engine errors since status change here is not crtical
                self.msg.warn(
                    self.name, 'Updated episode but status wasn\'t changed: %s' % e)

        # Change dates if required
        if self.config['auto_date_change'] and self.mediainfo.get('can_date'):
            start_date = finish_date = None

            try:
                if newep == 1:
                    start_date = datetime.date.today()
                if newep == show['total']:
                    finish_date = datetime.date.today()

                self.set_dates(show['id'], start_date, finish_date)
            except utils.EngineError as e:
                # Only warn about engine errors since date change here is not crtical
                self.msg.warn(
                    self.name, 'Updated episode but dates weren\'t changed: %s' % e)

        # Update the tracker with the new information
        self._update_tracker()

        return show