Browse Source

add --site option (closes #1)

add sites table to the database. Every subscription now has to be associated
with a video site. Currently youtube is supported and is the default but more
sites will be added later. This concludes this round of refactoring.
Helmut Pozimski 6 years ago
parent
commit
d3d10ba7b2
7 changed files with 176 additions and 35 deletions
  1. 1 1
      lib_stov/configuration.py
  2. 89 17
      lib_stov/database.py
  3. 4 0
      lib_stov/helpers.py
  4. 13 3
      lib_stov/main.py
  5. 50 10
      lib_stov/program.py
  6. 11 0
      lib_stov/stov_exceptions.py
  7. 8 4
      lib_stov/subscription.py

+ 1 - 1
lib_stov/configuration.py

@@ -54,7 +54,7 @@ class Conf(object):
             "youtube-dl": "",
             "notify": "yes",
             "config_version": "9",
-            "db_version": "5",
+            "db_version": "6",
             "videocodec": "mp4",
             "maxresolution": "1080p",
             "maxfails": 50,

+ 89 - 17
lib_stov/database.py

@@ -1,4 +1,4 @@
-#       This file is part of stov, written by Helmut Pozimski 2012-2015.
+#       This file is part of stov, written by Helmut Pozimski 2012-2017.
 #
 #       stov is free software: you can redistribute it and/or modify
 #       it under the terms of the GNU General Public License as published by
@@ -18,11 +18,14 @@
 """This module is responsible for all database related operations."""
 
 import sqlite3
+import logging
 
 from lib_stov import stov_exceptions
 from lib_stov import youtube
 from lib_stov import subscription
 
+LOGGER = logging.getLogger("stov")
+
 
 class Db(object):
     """This class is used to cosntruct the module which will take care of all
@@ -53,11 +56,12 @@ class Db(object):
         """Executes a statement, works as a wrapper around cursor execute."""
 
         try:
-            if argument is None:
+            if not argument:
                 result = self.__cursor.execute(statement)
             else:
                 result = self.__cursor.execute(statement, argument)
-        except sqlite3.OperationalError:
+        except sqlite3.OperationalError as error:
+            LOGGER.debug(error)
             raise stov_exceptions.DBWriteAccessFailedException()
         else:
             self.__connection.commit()
@@ -73,7 +77,8 @@ class Db(object):
             type TEXT,
             searchstring TEXT,
             directory TEXT,
-            disabled INTEGER DEFAULT 0
+            disabled INTEGER DEFAULT 0,
+            site INTEGER NOT NULL
             );""")
         self._execute_statement("""CREATE TABLE videos (
             id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
@@ -83,6 +88,9 @@ class Db(object):
             downloaded INT,
             failcnt INTEGER DEFAULT 0
             );""")
+        self._execute_statement("""CREATE TABLE sites(
+            id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+            title TEXT);""")
 
     def update(self):
         """Updates the database structure to match the current version"""
@@ -99,10 +107,8 @@ class Db(object):
             self._execute_statement("UPDATE subscriptions SET disabled=0;")
             self.__version = 3
         if int(self.__version) == 3:
-            """
-            Pseudo version without changes to the database structure,
-            converts existing channel subscriptions into user ones.
-            """
+            # Pseudo version without changes to the database structure,
+            # converts existing channel subscriptions into user ones.
             self._execute_statement("UPDATE subscriptions SET type='user' "
                                     "WHERE type='channel'")
             self.__version = 4
@@ -120,8 +126,35 @@ class Db(object):
                                     " id, title, ytid, subscription_id, "
                                     "downloaded, failcnt FROM videos;")
             self._execute_statement("DROP TABLE videos;")
-            self._execute_statement("ALTER TABLE videos_backup RENAME TO videos;")
+            self._execute_statement("ALTER TABLE videos_backup RENAME TO "
+                                    "videos;")
             self.__version = 5
+        if int(self.__version) == 5:
+            self._execute_statement("""CREATE TABLE sites(
+                        id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+                        title TEXT);""")
+            self._execute_statement("""ALTER TABLE subscriptions ADD
+            COLUMN site INTEGER;""")
+            self.add_site("youtube")
+            self._execute_statement("""UPDATE subscriptions SET site=1;""")
+            self._execute_statement("""CREATE TABLE subscriptions_backup (
+                        id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
+                        title TEXT,
+                        name TEXT,
+                        type TEXT,
+                        searchstring TEXT,
+                        directory TEXT,
+                        disabled INTEGER DEFAULT 0,
+                        site INTEGER NOT NULL
+            );""")
+            self._execute_statement("INSERT INTO subscriptions_backup SELECT"
+                                    " id, title, name, type, "
+                                    "searchstring, directory, disabled, "
+                                    "site FROM subscriptions;")
+            self._execute_statement("DROP TABLE subscriptions;")
+            self._execute_statement("ALTER TABLE subscriptions_backup "
+                                    "RENAME TO subscriptions;")
+            self.__version = 6
 
     def get_version(self):
         """Simple getter method, returns the DB version"""
@@ -186,9 +219,9 @@ class Db(object):
 
     def insert_subscription(self, data):
         """Inserts a subscription with the given data into the database"""
-        subscription_insert = "INSERT INTO subscriptions (title, type, " \
-                              "searchstring, directory, name, disabled) " \
-                              "VALUES (?, ?, ?, ?, ?, ?)"
+        subscription_insert = """INSERT INTO subscriptions (title, type,
+        searchstring, directory, name, disabled, site)
+        VALUES (?, ?, ?, ?, ?, ?, ?)"""
         self._execute_statement(subscription_insert, data)
         subscription_getid = "SELECT id from subscriptions where title=?"
         query_cursor = self._execute_statement(subscription_getid, (data[0],))
@@ -217,8 +250,10 @@ class Db(object):
 
     def get_subscription(self, sub_id):
         """Retrieves a specific subscription from the database"""
-        sub_query = "SELECT id,title,type,name,searchstring, directory," \
-                    "disabled FROM subscriptions where id=?"
+        sub_query = """SELECT subscriptions.id, subscriptions.title,
+        type, name,searchstring, directory,disabled, sites.title FROM
+        subscriptions INNER JOIN sites ON subscriptions.site=sites.id WHERE
+        subscriptions.id=?"""
         result_cursor = self._execute_statement(sub_query, (sub_id,))
         result = result_cursor.fetchall()
         return result
@@ -242,15 +277,16 @@ class Db(object):
     def get_subscriptions(self, conf):
         """Retrieves all subscriptions from the database"""
         subscriptions_list = []
-        subscriptions_query = "SELECT id,title,type,name,searchstring," \
-                              "directory,disabled FROM subscriptions"
+        subscriptions_query = """SELECT subscriptions.id, subscriptions.title,
+        type, name,searchstring, directory,disabled, sites.title FROM
+        subscriptions INNER JOIN sites ON subscriptions.site=sites.id;"""
         result_cursor = self._execute_statement(subscriptions_query)
         result = result_cursor.fetchall()
         for sub in result:
             subscriptions_list.append(subscription.Sub(
                 subscription_id=sub[0], title=sub[1], subscription_type=sub[2],
                 name=sub[3], search=sub[4], directory=sub[5], disabled=sub[6],
-                conf=conf))
+                site=sub[7], conf=conf))
         return subscriptions_list
 
     def vacuum(self):
@@ -264,3 +300,39 @@ class Db(object):
         """
         update_statement = "UPDATE subscriptions SET disabled=? WHERE id=?"
         self._execute_statement(update_statement, (state, sub_id))
+
+    def add_site(self, name):
+        """
+        Adds a site with the specified name to the database.
+
+        :param name: name of the new site
+        :type site: str
+        """
+        insert_statement = "INSERT INTO sites (title) VALUES (?)"
+        self._execute_statement(insert_statement, (name,))
+
+    def get_site_id(self, name):
+        """
+        Get the ID of a specific site
+
+        :param name: name of the new site
+        :type site: str
+        :return: the site ID
+        :rtype: int
+        """
+        query = "SELECT id FROM sites WHERE title=?"
+        cursor = self._execute_statement(query, (name,))
+        result = cursor.fetchone()[0]
+        return result
+
+    def get_sites(self):
+        """
+        Retrieves all sites from the database.
+
+        :return: list of sites with their respective IDs
+        :rtype: tuple
+        """
+        query = "SELECT id,title FROM sites"
+        cursor = self._execute_statement(query)
+        result = cursor.fetchall()
+        return result

+ 4 - 0
lib_stov/helpers.py

@@ -139,6 +139,10 @@ def parse_arguments():
     parser.add_argument("--disable", type=int, dest="disableid",
                         help=_("disables the subscription with the "
                                "provided ID"))
+    parser.add_argument("-s", "--site", dest="site",
+                        help=_("specify the site to work with (for --add)"))
+    parser.add_argument("--lssites", dest="lssites", action="store_true",
+                        help=_("list the supported sites"))
     return parser
 
 

+ 13 - 3
lib_stov/main.py

@@ -36,10 +36,20 @@ def main():
     logger = logging.getLogger("stov")
     database = helpers.setup_database(conf)
     helpers.find_youtubedl(conf)
+    program.initialize_sites(database)
     if arguments.add:
-        program.add_subscription(conf, database, arguments.channel,
-                                 arguments.searchparameter,
-                                 arguments.playlist)
+        if arguments.site:
+            program.add_subscription(conf, database,
+                                     arguments.channel,
+                                     arguments.searchparameter,
+                                     arguments.playlist,
+                                     arguments.site)
+        else:
+            program.add_subscription(conf, database, arguments.channel,
+                                     arguments.searchparameter,
+                                     arguments.playlist)
+    elif arguments.lssites:
+        program.list_sites(database)
     elif arguments.list:
         program.list_subscriptions(conf, database)
     elif arguments.deleteid:

+ 50 - 10
lib_stov/program.py

@@ -33,7 +33,8 @@ from lib_stov import stov_exceptions
 LOGGER = logging.getLogger("stov")
 
 
-def add_subscription(conf, database, channel="", search="", playlist=""):
+def add_subscription(conf, database, channel="",
+                     search="", playlist="", site="youtube"):
     """
     Takes care of adding a new subscription to the database.
 
@@ -41,6 +42,8 @@ def add_subscription(conf, database, channel="", search="", playlist=""):
     :type conf: lib_stov.configuration.Conf
     :param database: database object
     :type database: lib_stov.database.Db
+    :param site: site the subscription is about to be created for
+    :type site: str
     :param channel: optional channel name
     :type channel: str
     :param search: optional search string
@@ -50,31 +53,36 @@ def add_subscription(conf, database, channel="", search="", playlist=""):
     """
     if channel and not search:
         new_subscription = subscription.Sub(subscription_type="user",
-                                            name=channel, conf=conf)
+                                            name=channel, conf=conf, site=site)
     elif channel and search:
         new_subscription = subscription.Sub(subscription_type="user",
                                             name=channel,
                                             search=search,
-                                            conf=conf)
+                                            conf=conf, site=site)
     elif not channel and search:
         new_subscription = subscription.Sub(subscription_type="search",
                                             name=_("Search_"),
                                             search=search,
-                                            conf=conf)
+                                            conf=conf, site=site)
     elif playlist:
         if search:
             LOGGER.error(_("Playlists do not support searching, the search "
                            "option will be ignored!"))
         new_subscription = subscription.Sub(subscription_type="playlist",
                                             name=playlist,
-                                            conf=conf)
+                                            conf=conf, site=site)
     else:
         LOGGER.error(_("None or invalid subscription type given, please check "
                        "the type option and try again."))
         sys.exit(1)
     try:
-        subscription_id = database.insert_subscription(
-            new_subscription.add_sub())
+        subscription_data = new_subscription.add_sub()
+        site_id = database.get_site_id(subscription_data[6])
+        new_sub_data = (subscription_data[0], subscription_data[1],
+                        subscription_data[2], subscription_data[3],
+                        subscription_data[4], subscription_data[5],
+                        site_id)
+        subscription_id = database.insert_subscription(new_sub_data)
         new_subscription.set_id(subscription_id)
     except stov_exceptions.DBWriteAccessFailedException as error:
         LOGGER.error(error)
@@ -117,14 +125,14 @@ def list_subscriptions(conf, database):
     subscriptions_list = database.get_subscriptions(conf)
     sub_state = None
     if subscriptions_list:
-        LOGGER.info(_("ID Title"))
+        LOGGER.info(_("ID Title Site"))
         for sub in subscriptions_list:
             if not sub.disabled:
                 sub_state = _("enabled")
             elif sub.disabled:
                 sub_state = _("disabled")
             LOGGER.info(str(sub.get_id()) + " " + sub.get_title() +
-                        " (%s)" % sub_state)
+                        " " + sub.site + " " + "(%s)" % sub_state)
     else:
         LOGGER.info(_("No subscriptions added yet, add one!"))
 
@@ -331,7 +339,8 @@ def list_videos(database, conf, sub_id):
                                    name=data[0][3],
                                    search=data[0][4],
                                    directory=data[0][5],
-                                   disabled=data[0][6], conf=conf)
+                                   disabled=data[0][6],
+                                   site=data[0][7], conf=conf)
             videos = database.get_videos(sub.get_id(), conf)
             sub.gather_videos(videos)
             videos_list = sub.print_videos()
@@ -484,3 +493,34 @@ def download_notify(database, conf):
             LOGGER.error(_("Could not determine how you want to be informed "
                            "about new videos, please check the notify "
                            "parameter in your configuration."))
+
+
+def initialize_sites(database):
+    """
+    Adds sites to the database if they are not in there yet.
+
+    :param database: database object
+    :type database: lib_stov.database.Db
+    """
+    supported_sites = ["youtube"]
+    sites = database.get_sites()
+    for site in supported_sites:
+        site_found = False
+        for result in sites:
+            if site in result:
+                site_found = True
+        if not site_found:
+            database.add_site(site)
+
+
+def list_sites(database):
+    """
+    Lists the currently supported sites.
+
+    :param database: database object
+    :type database: lib_stov.database.Db
+    """
+    sites = database.get_sites()
+    LOGGER.info(_("Sites currently supported by stov:"))
+    for entry in sites:
+        LOGGER.info(entry[1])

+ 11 - 0
lib_stov/stov_exceptions.py

@@ -212,3 +212,14 @@ class ChannelNotFound(Exception):
 
     def __str__(self):
         return self._message
+
+
+class SiteUnsupported(Exception):
+    """ Will be raised when stov is called for an unsupported site
+    """
+    def __init__(self):
+        super(SiteUnsupported, self).__init__()
+        self._message = _("Error: This site is not (yet) supported by stov!")
+
+    def __str__(self):
+        return self._message

+ 8 - 4
lib_stov/subscription.py

@@ -26,7 +26,7 @@ class Sub(object):
     """This class constructs a object that stores all the attributes that define
     a subscription and performs the necessary operations on it.
     """
-    def __init__(self, subscription_type, name, conf, search="",
+    def __init__(self, subscription_type, name, conf, site, search="",
                  subscription_id=0, title="", directory="", disabled=0):
         self._id = subscription_id
         self._title = title
@@ -35,6 +35,7 @@ class Sub(object):
         self._search = search
         self._directory = directory
         self._conf = conf
+        self.site = site
         self.downloaded_videos = []
         self.failed_videos_count = 0
         self.failed_videos = []
@@ -48,8 +49,11 @@ class Sub(object):
         elif int(disabled) == 1:
             self.disabled = True
 
-        self._connector = yt_noapi.Connector(self._type, self._name,
-                                             self._conf, self._search)
+        if site == "youtube":
+            self._connector = yt_noapi.Connector(self._type, self._name,
+                                                 self._conf, self._search)
+        else:
+            raise stov_exceptions.SiteUnsupported()
 
     def get_title(self):
         """Returns the title attribute."""
@@ -121,7 +125,7 @@ class Sub(object):
         self._title = parsed_response.title
         self._directory = self._name + "_" + self._search.replace(" ", "_")
         data = (self._title, self._type, self._search, self._directory,
-                self._name, 0)
+                self._name, 0, self.site)
         return data
 
     def check_and_delete(self, videos):