コード例 #1
0
ファイル: show.py プロジェクト: 8cylinder/tv-overlord
    def _add_new_db(self, season=0, episode=0):
        if self.db.show_exists(self.id):
            sql = '''UPDATE shows SET
                        status="active",
                        episode=:episode,
                        season=:season
                     WHERE thetvdb_series_id=:thetvdb_id;'''
            values = {
                'thetvdb_id': self.id,
                'episode': episode,
                'season': season
            }
            msg = '%s is already in the db. Its status is now set to "active"' % self.seriesname
        else:
            sql = '''
                INSERT INTO shows (
                  network_status, status, thetvdb_series_id,
                  name, season, episode)
                VALUES (:network_status, :status, :thetvdb_id,
                        :name, :season, :episode)'''
            values = {'network_status': self.status,
                      'status': 'active',
                      'thetvdb_id': self.id,
                      'name': self.seriesname,
                      'season': season,
                      'episode': episode}
            episode += 1
            episode = str(episode)
            if season == 0:
                season += 1
            season = str(season)
            msg = '%s %s added.' % (self.seriesname, sxxexx(season, episode))

        DB.run_sql(sql, values)
        return msg
コード例 #2
0
ファイル: tvol.py プロジェクト: 8cylinder/tv-overlord
def tvol(no_cache, config_name):
    """Download and manage tv shows.

    Use `tvol COMMAND -h` to get help for each command.

    \b
       \/    TVOverlord source code is available at:
      [. ]   https://github.com/8cylinder/tv-overlord
       ^
      /^\\    Any feature requests or bug reports should go there.
     //^\\\\
    -^-._.--.-^^-.____._^-.^._
    """
    Config.get_config_data(config_name)
    DB.configure()

    if Config.version_notification:
        if v.new_version():
            msg = tvu.style(v.message, fg='green')
            click.secho(msg, err=True)

    te.ask()

    if no_cache:
        Config.use_cache = False
    else:
        Config.use_cache = True
コード例 #3
0
ファイル: show.py プロジェクト: 8cylinder/tv-overlord
 def _update_db(self, season, episode):
     sql = """UPDATE shows
              SET season=:season, episode=:episode
              WHERE thetvdb_series_id=:tvdb_id"""
     values = {'season': season, 'episode': episode,
               'tvdb_id': self.db_thetvdb_series_id}
     DB.run_sql(sql, values)
コード例 #4
0
ファイル: tracking.py プロジェクト: 8cylinder/tv-overlord
    def save(self, show_title, season, episode, data,
             chosen_url, nondbshow=False):
        magnet_hash = self._extract_hash(chosen_url)
        data = json.dumps(data)
        now = datetime.datetime.today()
        date = now.isoformat()
        # oneoff is a show that was downloaded via 'nondbshow'
        oneoff = 1 if nondbshow else 0

        sql = '''
            INSERT INTO tracking(
                download_date, show_title, season,
                episode, chosen, chosen_hash, one_off)
            VALUES(
                :date, :show_title, :season, :episode,
                :chosen, :hash, :one_off);'''

        values = {
            'date': date,
            'show_title': show_title,
            'season': season,
            'episode': episode,
            'chosen': chosen_url,
            'hash': magnet_hash,
            'one_off': oneoff,
        }
        DB.run_sql(sql, values)
コード例 #5
0
ファイル: remote.py プロジェクト: 8cylinder/tv-overlord
    def new_version(self):
        """
        compare current version with version in db
        """

        self.remote_version = DB.get_config('version_remote')
        self.message = DB.get_config('version_msg')

        if not self.remote_version:
            return False
        elif LV(self.local_version) < LV(self.remote_version):
            return True
        else:
            return False
コード例 #6
0
def qbittorrent(info_hash, torrent_name, torrent_dir, debug):
    """Manage torrents downloaded by qBittorrent.

    \b
    In tools > options > downloads > Run external program
    Add: /absolute/path/to/qbittorrent_done %I %N %D
    """
    Config.get_config_data()
    DB.configure()
    telemetry('qbittorrent_done')

    if debug:
        click.echo('info_hash: %s' % info_hash)
        click.echo('torrent_dir: %s' % torrent_dir)
        click.echo('torrent_name: %s' % torrent_name)

    DownloadManager(info_hash, torrent_dir,
                    torrent_name, debug=debug)
コード例 #7
0
def deluge(torrent_hash, torrent_name, torrent_dir, debug):
    """Manage torrents downloaded by deluge.

    Deluge will call this script when the torrent has been downloaded.
    It will pass TORRENT_HASH, TORRENT_NAME and TORRENT_DIR as arguements.

    \b
    The execute plugin is needed for this to work.
    http://dev.deluge-torrent.org/wiki/Plugins/Execute
    """
    Config.get_config_data()
    DB.configure()
    telemetry('deluge_done')

    if debug:
        click.echo('torrent_hash: %s' % torrent_hash)
        click.echo('torrent_dir: %s' % torrent_dir)
        click.echo('torrent_name: %s' % torrent_name)

    DownloadManager(torrent_hash, torrent_dir,
                    torrent_name, debug=debug)
コード例 #8
0
ファイル: remote.py プロジェクト: 8cylinder/tv-overlord
    def ask(self):
        if not DB.get_config('telemetry_asked'):
            question = format_paragraphs("""
                May TV Overlord report anonymous usage statistics?

                This is the single most useful thing a user can do.  This
                data will help decide the features that get the most
                attention and the future direction of TV Overlord.

                More information at:

                https://github.com/8cylinder/tv-overlord/wiki/telemetry-data

            """)
            question = click.style(question, fg='green')
            click.echo()
            telemetry_ok = click.confirm(question, default=True)
            click.echo()

            DB.set_config('telemetry_ok', telemetry_ok)
            DB.set_config('telemetry_asked', True)
            if not DB.get_config('uuid4'):
                DB.set_config('uuid4', str(uuid.uuid4()))
                DB.set_config('uuid1', str(uuid.uuid1()))
コード例 #9
0
def transmission(debug):
    """This script passes the enviroment variables from transmission to
    tvoverlord.

    Transmission exports these enviroment variables:

    \b
    X TR_TORRENT_DIR
    X TR_TORRENT_HASH
    X TR_TORRENT_NAME
      TR_APP_VERSION
      TR_TIME_LOCALTIME
      TR_TORRENT_ID

    This script uses the ones marked with an X.  This information is
    used to manage the torrent after its been downloaded.

    """
    Config.get_config_data()
    DB.configure()
    telemetry('transmission_done')

    try:
        torrent_dir = os.environ['TR_TORRENT_DIR']
        torrent_hash = os.environ['TR_TORRENT_HASH']
        torrent_name = os.environ['TR_TORRENT_NAME']
    except KeyError:
        sys.exit('Enviroment variables not set')

    if debug:
        click.echo('torrent_hash: %s' % torrent_hash)
        click.echo('torrent_dir: %s' % torrent_dir)
        click.echo('torrent_name: %s' % torrent_name)

    DownloadManager(torrent_hash, torrent_dir,
                    torrent_name, debug=debug)
コード例 #10
0
ファイル: shows.py プロジェクト: 8cylinder/tv-overlord
    def _query_db(self, sqlfilter, statusfilter):
        # print('>>>',sqlfilter, '|', statusfilter)
        # if sqlfilter:
            # sqlfilter = 'AND %s' % sqlfilter

        # print('sqlfilter: %s, statusfilter: %s' % (sqlfilter, statusfilter))
        if statusfilter and sqlfilter:
            where = '%s AND %s' % (statusfilter, sqlfilter)
        elif sqlfilter:
            where = sqlfilter
        elif statusfilter:
            where = statusfilter

        sql = """
            SELECT
                name,
                season,
                episode,
                search_by_date,
                date_format,
                thetvdb_series_id,
                ragetv_series_id,
                search_engine_name,
                status
            FROM
                shows
            WHERE
                %s
            ORDER BY
                %s;""" % (
            where,
            self.sort_field
        )
        ddata = DB.run_sql(sql, named_fields=True)
        data = []
        for i in ddata:
            data.append(i)
        self.show_count = len(data)
        return data
コード例 #11
0
ファイル: show.py プロジェクト: 8cylinder/tv-overlord
 def delete(self):
     sql = '''DELETE from shows
              WHERE thetvdb_series_id=:tvdb_id'''
     values = {'tvdb_id': self.db_thetvdb_series_id}
     DB.run_sql(sql, values)
コード例 #12
0
ファイル: show.py プロジェクト: 8cylinder/tv-overlord
 def set_inactive(self):
     sql = '''UPDATE shows
              SET status="inactive"
              WHERE thetvdb_series_id=:tvdb_id'''
     values = {'tvdb_id': self.db_thetvdb_series_id}
     DB.run_sql(sql, values)
コード例 #13
0
ファイル: show.py プロジェクト: 8cylinder/tv-overlord
 def set_next_episode(self, next_date):
     sql = 'UPDATE shows SET next_episode=:next_date WHERE name=:show_name'
     values = {'next_date': next_date.isoformat(),
               'show_name': self.db_name}
     DB.run_sql(sql, values)
コード例 #14
0
    def __init__(self, torrent_hash, path, filename, debug=False):
        if debug:
            console = Config.logging.StreamHandler()
            formater = Config.logging.Formatter(">>> %(message)s")
            console.setFormatter(formater)
            Config.logging.getLogger("").addHandler(console)

        Config.logging.info("-" * 50)
        Config.logging.info("hash: %s", torrent_hash)
        Config.logging.info("path: %s", path)
        Config.logging.info("filename: %s", filename)

        filename = os.path.join(path, filename)
        DB.save_info(torrent_hash, filename)

        debug_command = """export TR_TORRENT_NAME='%s'; export TR_TORRENT_DIR='%s'; export TR_TORRENT_HASH='%s'; transmission_done"""
        Config.logging.info(debug_command, filename, path, torrent_hash)

        if DB.is_oneoff(torrent_hash):
            Config.logging.info("Download is a one off, doing nothing.")
            return

        if not os.path.exists(Config.tv_dir):
            msg = 'Destination: "{}" does not exist'.format(Config.tv_dir)
            Config.logging.error(msg)
            raise OSError(msg)
            # sys.exit(msg)

        if not os.path.exists(filename):
            msg = "Source does not exist".format(filename)
            Config.logging.error(msg)
            raise OSError(msg)
            # sys.exit(msg)

        source = filename
        if Config.single_file:
            # extract largest file from dir
            source = self.get_show_file(filename)

        if Config.template:
            template = Config.template
        else:
            template = "{show}/{original}"
        dest_filename = self.pretty_names(source, torrent_hash, template)
        Config.logging.info("Destination filename: %s" % dest_filename)

        dest = os.path.join(Config.tv_dir, dest_filename)
        dest_path = os.path.dirname(dest)

        if not os.path.exists(dest_path):
            os.makedirs(dest_path, exist_ok=True)
            Config.logging.info("creating dir: %s" % dest)

        DB.save_dest(torrent_hash, dest)

        Config.logging.info("copying %s to %s" % (source, dest))
        if self.copy(source, dest):
            Tell("%s done" % os.path.basename(dest))
            DB.set_torrent_complete(torrent_hash)
        else:
            Config.logging.info("Destination full")
            Tell("Destination full")
            sys.exit("Destination full")
コード例 #15
0
    def pretty_names(self, filename, torrent_hash, template=None):
        if not template:
            template = "{show}/{original}"

        fields = {}
        (fields["show"], fields["searchname"], fields["season"], fields["episode"]) = DB.get_show_info(torrent_hash)
        fields["original"] = os.path.basename(filename)
        fields["s00e00"] = sxxexx(fields["season"], fields["episode"])
        fields["0x00"] = sxee(fields["season"], fields["episode"])
        fields["season"] = fields["season"].rjust(2, "0")
        fields["episode"] = fields["episode"].rjust(2, "0")

        if fields["searchname"] is None:
            fields["searchname"] = ""

        # search original filename for info
        fields["resolution"] = ""
        for res in Config.categories.resolution:
            if res.lower() in filename.lower():
                fields["resolution"] = res
        fields["source"] = ""
        for srs in Config.categories.sources:
            if srs.lower() in filename.lower():
                fields["source"] = srs
        fields["codec"] = ""
        for cod in Config.categories.codecs:
            if cod.lower() in filename.lower():
                fields["codec"] = cod
        fields["audio"] = ""
        for aud in Config.categories.audio:
            if aud.lower() in filename.lower():
                fields["audio"] = aud

        # short cut tags
        all = [
            fields["show"],
            fields["s00e00"],
            fields["resolution"],
            fields["source"],
            fields["codec"],
            fields["audio"],
        ]
        all = [i for i in all if i]  # remove empty
        fields["all"] = " ".join(all)

        ext = ""
        if os.path.isfile(filename):
            ext = os.path.splitext(filename)[-1]

        broke = re.split("({.*?})", template)
        if not broke[-1]:
            broke = broke[:-1]

        new = []
        is_section = False

        for section in broke:
            if section.startswith("{"):
                chunks = section.strip("{}").split("|")
                field = chunks[0].lower()
                filters = [i.lower() for i in chunks[1:]]
                try:
                    if not fields[field]:
                        continue
                except KeyError:
                    continue
                complete = self.format(fields[field], filters)
                new.append(complete)
                is_section = True
            else:
                new.append(section)
                is_section = False
        # remove adjacent duplicates
        new = [i[0] for i in itertools.groupby(new)]
        if not is_section:
            new = new[:-1]
        new.append(ext)
        full = "".join(new)
        full = os.path.normpath(full)
        return full
コード例 #16
0
ファイル: tvol.py プロジェクト: 8cylinder/tv-overlord
def config(edit, test_se, show, create_config_name):
    """tvol's config information.

    Show information of where various files are, (config.ini,
    database) and a list of the search engines and the url's they use.
    """
    send(te, v)

    if create_config_name:
        Config.create_config(create_config_name, create=True)
        return

    if edit:
        click.edit(filename=Config.user_config)
        return

    if test_se:
        search = Search()
        search.test_each(test_se, show)
        return

    import shutil

    title = 'green'
    bold = True
    ul = True

    # file locations
    click.echo()
    click.secho('File locations:', fg=title, bold=bold, underline=ul)
    click.echo()

    click.echo('config file:     %s' % Config.user_config)
    click.echo('Database file:   %s' % Config.db_file)
    click.echo('NZB staging dir: %s' % Config.staging)
    click.echo('TV dir:          %s' % Config.tv_dir)
    click.echo('Alt client:      %s' % Config.client)
    click.echo('Magnet dir:      %s' % Config.magnet_dir)
    click.echo('Template:        %s' % Config.template)

    click.echo()
    for script in ['tvol', 'transmission_done', 'deluge_done']:
        loc = shutil.which(script)
        script = script + ':'
        click.echo('%s %s' % (script.ljust(18), loc))

    # config sets
    files = [f for f in os.listdir(Config.user_dir)
             if re.match('^.*\..*\.(sqlite3|ini)', f)]
    files.sort(key=lambda f: f.split('.')[1])
    if files:
        click.echo()
        click.secho('Config sets', fg=title, bold=bold, underline=ul)
        click.echo()
        count = 1
        for gr, items in groupby(files, key=lambda f: f.split('.')[1]):
            config_set = ', '.join(list(items))
            click.echo('%s. %s' % (count, config_set))
            count += 1

    # search engines
    click.echo()
    click.secho('Search engines:', fg=title, bold=bold, underline=ul)
    search = Search()
    engines_types = [search.torrent_engines, search.newsgroup_engines]
    for engines in engines_types:
        for engine in engines:
            click.echo()
            click.secho(engine.Provider.name, bold=True, nl=False)
            click.echo(' (%s)' % engine.Provider.shortname)
            for url in engine.Provider.provider_urls:
                click.echo('  %s' % url)

    # blacklisted search engines
    if Config.blacklist:
        click.echo()
        click.secho('Search engine blacklist:',
                    fg=title, bold=bold, underline=ul)
        click.echo()
        for bl in Config.blacklist:
            click.echo(bl)

    # ip addresses
    click.echo()
    click.secho('Ip address information:',
                fg=title, bold=bold, underline=ul)
    click.echo()

    l = Location()
    click.echo('Your public ip address:')
    click.secho('  %s' % l.ip, bold=True)
    if Config.warnvpn:
        click.echo()
        click.echo('Your whitelisted ip addresses:')
        whitelist = DB.get_config('ip_whitelist')
        short = '.'.join(l.ip.split('.')[:Config.parts_to_match])
        for ip in whitelist:
            color = None
            if ip.startswith(short):
                color = 'green'
            click.secho('  %s' % ip, fg=color)
コード例 #17
0
ファイル: tracking.py プロジェクト: 8cylinder/tv-overlord
 def display(self):
     sql = '''
         SELECT * FROM tracking;
     '''
     rows = DB.run_sql(sql, named_fields=True)
     return rows
コード例 #18
0
 def set_inactive(self):
     sql = '''UPDATE shows
              SET status="inactive"
              WHERE thetvdb_series_id=:tvdb_id'''
     values = {'tvdb_id': self.db_thetvdb_series_id}
     DB.run_sql(sql, values)