Browse Source

reintroduce more debug messages, create directories according to the title for youtube channels and playlists (closes #2)

Helmut Pozimski 6 years ago
parent
commit
3f287a0e98

+ 2 - 2
README.md

@@ -1,6 +1,6 @@
 # ABOUT
 
-stov is a program which allows you to subscribe to channels, playlists and search results on youtube. (More platforms may be implemented later) You don't need a youtube account for the program to work since it uses read-only access to the youtube API or retrieves the needed data using youtube-dl. You can for example run it as a cron job to automatically download your favourite videos.
+stov is a program which allows you to subscribe to channels, playlists and search results on youtube. (More platforms will be implemented later) You don't need a youtube account for the program to work since it retrieves the needed data using youtube-dl. You can for example run it as a cron job to automatically download your favourite videos.
 
 ## USAGE
 
@@ -57,7 +57,7 @@ Currently none, contact me if you find any.
 ## INSTALLATION
 
 You can just run the main file "stov" directly from this directory by calling
-it from the shell or with the python interpreter ("./stov" or "python stov").
+it from the shell or with the python interpreter ("./stov" or "python3 stov").
 Furthermore, stov uses python distutils as default installation method. Just
 run "python setup.py install" to install it into your system. For further
 information an options run "python setup.py --help". 

+ 19 - 12
lib_stov/configuration.py

@@ -21,12 +21,13 @@ stov, the configuration file is expected to be in json format and reside in
 """
 
 import os
-import subprocess
 import json
 import logging
 
 from lib_stov import stov_exceptions
 
+LOGGER = logging.getLogger("stov")
+
 
 class Conf(object):
     """This class is used to create the object which is responsible for all
@@ -96,6 +97,7 @@ class Conf(object):
         the user by default.
 
         """
+        LOGGER.debug(_("Writing configuration file."))
         try:
             configfile = open(str(os.environ['HOME']) +
                               "/.stov/stov.json", "w")
@@ -111,15 +113,12 @@ class Conf(object):
         configuration file.
 
         """
+        LOGGER.debug(_("Creating stov configuration directory"))
         try:
             os.mkdir(str(os.environ['HOME']) + "/.stov", 0o750)
         except os.error:
             raise stov_exceptions.DirectoryCreationFailedException()
         else:
-            process = subprocess.Popen(["which", "youtube-dl"],
-                                       stdout=subprocess.PIPE)
-            self.values["youtube-dl"] = \
-                process.communicate()[0].strip().decode("utf-8")
             self.write_config()
 
     def read_old_config(self):
@@ -128,6 +127,8 @@ class Conf(object):
         are overwritten.
 
         """
+        LOGGER.debug(_("Opening old configuration file to convert it to "
+                       "json"))
         try:
             configfile = open(str(os.environ['HOME']) +
                               "/.stov/stov.config", "r")
@@ -145,6 +146,7 @@ class Conf(object):
         """Reads the existing json configuration files and loads the values in
         the dictionary.
         """
+        LOGGER.debug(_("Opening configuration file"))
         try:
             configfile = open(str(os.environ['HOME']) + "/.stov/stov.json",
                               "r")
@@ -160,24 +162,24 @@ class Conf(object):
 
         """
         try:
-            currentversion = int(self.values["config_version"])
+            current_version = int(self.values["config_version"])
         except ValueError:
             raise stov_exceptions.InvalidConfigurationVersionException()
         self.values["config_version"] = "0"
         self.read_config()
         if self.values["config_version"] == "0" \
-                or int(self.values["config_version"]) < currentversion:
-            self.values["config_version"] = str(currentversion)
+                or int(self.values["config_version"]) < current_version:
+            self.values["config_version"] = str(current_version)
             return False
         else:
-            self.values["config_version"] = currentversion
+            self.values["config_version"] = current_version
             return True
 
     def update_config(self):
         """Update the configuration to the latest version"""
-        versionbuffer = self.values["config_version"]
+        version_buffer = self.values["config_version"]
         self.read_config()
-        self.values["config_version"] = versionbuffer
+        self.values["config_version"] = version_buffer
         self.write_config()
 
     def check_db(self):
@@ -227,7 +229,12 @@ class Conf(object):
                 itag_value = 37
             elif self.values["maxresolution"] == "3072p":
                 itag_value = 38
-        return itag_value
+        if itag_value:
+            return itag_value
+        else:
+            LOGGER.debug(_("Could not determine an itag value "
+                           "from the configuration"))
+            return 38
 
     def assist(self):
         """ Ask the user to set all required configuration parameters """

+ 3 - 0
lib_stov/database.py

@@ -57,8 +57,11 @@ class Db(object):
 
         try:
             if not argument:
+                LOGGER.debug(_("Executing STATEMENT: %s"), statement)
                 result = self.__cursor.execute(statement)
             else:
+                LOGGER.debug(_("Executing STATEMENT: %s with arguments %s"),
+                             statement, argument)
                 result = self.__cursor.execute(statement, argument)
         except sqlite3.OperationalError as error:
             LOGGER.debug(error)

+ 7 - 5
lib_stov/helpers.py

@@ -177,9 +177,9 @@ def create_lock():
         LOCK_FILE.write(str(os.getpid()))
         LOCK_FILE.close()
     except IOError:
-        print(_("The lock file could not be created, please check that /tmp"
-                " is writable and properly configured, quitting now."),
-              file=sys.stderr)
+        LOGGER.error(_("The lock file could not be created, please check "
+                       "that /tmp is writable and properly configured, "
+                       "quitting now."), file=sys.stderr)
         sys.exit(1)
 
 
@@ -187,6 +187,7 @@ def remove_lock():
     """
     Removes the lock file when exiting the application.
     """
+    LOGGER.debug(_("Removing lock file"))
     try:
         os.remove("/tmp/stov.lock")
         sys.exit(0)
@@ -271,6 +272,7 @@ def setup_database(conf):
     :return: database object
     :rtype: lib_stov.database.Db
     """
+    LOGGER.debug(_("Connecting to stov database"))
     if os.access(conf.dbpath, os.F_OK):
         try:
             db = database.Db(path=conf.dbpath,
@@ -324,8 +326,8 @@ def find_youtubedl(conf):
         youtubedl_path = find_executable("youtube-dl")
         conf.values["youtube-dl"] = youtubedl_path
         if os.access(conf.values["youtube-dl"], os.F_OK & os.R_OK & os.X_OK):
-            LOGGER.info(_("Found youtube-dl, writing it's path to the "
-                          "configuration file."))
+            LOGGER.info(_("Found youtube-dl at %s, writing it's path to the "
+                          "configuration file."), conf.values["youtube-dl"])
             LOGGER.debug("Opening configuration file.")
             try:
                 conf.write_config()

+ 5 - 1
lib_stov/program.py

@@ -51,6 +51,9 @@ def add_subscription(conf, database, channel="",
     :param playlist: optional playlist ID
     :type playlist: str
     """
+    LOGGER.debug(_("Creating new subscription with the following "
+                   "parameters:\nChannel: %s\nSearch: %s\nPlaylist: %s"),
+                 channel, search, playlist)
     if channel and not search:
         new_subscription = subscription.Sub(subscription_type="user",
                                             name=channel, conf=conf, site=site)
@@ -169,6 +172,7 @@ def update_subscriptions(database, conf):
     """
     subscriptions_list = database.get_subscriptions(conf)
     for element in subscriptions_list:
+        LOGGER.debug(_("Updating subscription %s"), element.get_title())
         videos = database.get_videos(element.get_id(), conf)
         element.gather_videos(videos)
         try:
@@ -366,7 +370,7 @@ def catchup(database, sub_id):
         LOGGER.error(error)
         sys.exit(1)
     else:
-        if sub_data != []:
+        if sub_data:
             try:
                 database.mark_video_downloaded(sub_id)
             except stov_exceptions.DBWriteAccessFailedException as error:

+ 7 - 2
lib_stov/subscription.py

@@ -82,7 +82,6 @@ class Sub(object):
     def gather_videos(self, video_list):
         """Gathers all videos in the subscription and saves
         them in an the internal list so they can be accessed by the object
-
         """
         self._video_list = video_list
 
@@ -123,7 +122,12 @@ class Sub(object):
         """Adds a new subscription to the database"""
         parsed_response = self._connector.parse_api_data([])
         self._title = parsed_response.title
-        self._directory = self._name + "_" + self._search.replace(" ", "_")
+        self._type = parsed_response.type
+        if self._type == "channel" or self._type == "playlist":
+            self._directory = self._title.replace(" ", "_")
+        else:
+            self._directory = self._name + "_" + \
+                              self._search.replace(" ", "_")
         data = (self._title, self._type, self._search, self._directory,
                 self._name, 0, self.site)
         return data
@@ -134,6 +138,7 @@ class Sub(object):
 
         """
         parsed_response = self._connector.parse_api_data([])
+        self._type = parsed_response.type
         self.gather_videos(videos)
         for entry in parsed_response.videos:
             self._id_list.append(entry.ytid)

+ 10 - 27
lib_stov/youtube.py

@@ -19,10 +19,12 @@
 """This module takes care of managing and downloading single videos."""
 
 import os
-import sys
-import subprocess
+import logging
 
 from lib_stov import stov_exceptions
+from lib_stov import youtubedl_wrapper
+
+LOGGER = logging.getLogger("stov")
 
 
 class Video(object):
@@ -41,8 +43,9 @@ class Video(object):
     def download_video(self, directory, itag_value, video_codec):
         """Downloads the video by calling youtube-dl as an external process"""
         targetdir = self.__conf.values["downloaddir"] + "/" + directory
-        if os.access(targetdir, os.F_OK) is False:
+        if not os.access(targetdir, os.F_OK):
             try:
+                LOGGER.debug(_("Creating directory %s"), targetdir)
                 os.makedirs(targetdir, 0o750)
             except os.error:
                 raise stov_exceptions.DirectoryCreationFailedException()
@@ -50,30 +53,10 @@ class Video(object):
         os.chdir(targetdir)
         if self.downloaded == 0:
             try:
-                if self.__conf.outputlevel == "default":
-                    subprocess.check_call([self.__conf.values["youtube-dl"], "-f %s/%s"
-                                           % (itag_value, video_codec), "-o",
-                                           "%(title)s-%(id)s.%(ext)s",
-                                           "http://www.youtube.com/watch?v=%s"
-                                           % self.ytid],
-                                          stderr=sys.stderr,
-                                          stdout=open("/dev/null", "w"))
-                elif self.__conf.outputlevel == "verbose":
-                    subprocess.check_call([self.__conf.values["youtube-dl"], "-f %s/%s"
-                                           % (itag_value, video_codec), "-o",
-                                           "%(title)s-%(id)s.%(ext)s",
-                                           "http://www.youtube.com/watch?v=%s"
-                                           % self.ytid],
-                                          stderr=sys.stderr, stdout=sys.stdout)
-                elif self.__conf.outputlevel == "quiet":
-                    subprocess.check_call([self.__conf.values["youtube-dl"], "-f %s/%s"
-                                           % (itag_value, video_codec), "-o",
-                                           "%(title)s-%(id)s.%(ext)s",
-                                           "http://www.youtube.com/watch?v=%s"
-                                           % self.ytid],
-                                          stderr=open("/dev/null", "w"),
-                                          stdout=open("/dev/null", "w"))
-            except subprocess.CalledProcessError:
+                url = "http://www.youtube.com/watch?v=%s" % self.ytid
+                youtubedl_wrapper.download_video(self.__conf, url,
+                                                 itag_value, video_codec)
+            except stov_exceptions.YoutubeDlCallFailed:
                 self.failcnt = int(self.failcnt) + 1
                 return False
             else:

+ 39 - 3
lib_stov/youtubedl_wrapper.py

@@ -22,6 +22,8 @@ import subprocess
 import sys
 import logging
 
+from lib_stov import stov_exceptions
+
 LOGGER = logging.getLogger("stov")
 
 
@@ -43,7 +45,7 @@ def get_ids(conf, url, title=""):
     else:
         stderr = open("/dev/null", "w")
     if title:
-        LOGGER.debug("Executing command: %s %s %s %s %s %s %s",
+        LOGGER.debug(_("Executing command: %s %s %s %s %s %s %s"),
                      conf.values["youtube-dl"], "--max-downloads",
                      conf.values["maxvideos"], "--match-title",
                      title, "--get-id", url)
@@ -55,7 +57,7 @@ def get_ids(conf, url, title=""):
         except subprocess.CalledProcessError as error_message:
             video_ids = error_message.output
     else:
-        LOGGER.debug("Executing command: %s %s %s %s %s",
+        LOGGER.debug(_("Executing command: %s %s %s %s %s"),
                      conf.values["youtube-dl"], "--max-downloads",
                      conf.values["maxvideos"], "--get-id", url)
         try:
@@ -84,9 +86,43 @@ def get_title(conf, url):
         stderr = sys.stderr
     else:
         stderr = open("/dev/null", "w")
-    LOGGER.debug("Executing command: %s %s %s",
+    LOGGER.debug(_("Executing command: %s %s %s"),
                  conf.values["youtube-dl"], "--get-title", url)
     video_title = subprocess.check_output([
         conf.values["youtube-dl"], "--get-title", url], stderr=stderr)
     video_title = video_title.decode(sys.stdout.encoding)
     return video_title
+
+
+def download_video(conf, url, itag_value, video_codec):
+    """
+    Downloads a video from a specified url using youtube-dl.
+
+    :param conf: configuration object
+    :type conf: lib_stov.configuration.Conf
+    :param url: URL to pass to youtube-dl
+    :type url: str
+    """
+    try:
+        LOGGER.debug(_("Executing command: %s -f %s/%s %s"),
+                     conf.values["youtube-dl"], itag_value, video_codec, url)
+        if conf.outputlevel == "default":
+            subprocess.check_call([conf.values["youtube-dl"], "-f %s/%s"
+                                   % (itag_value, video_codec),
+                                   "-o", "%(title)s-%(id)s.%(ext)s", url],
+                                  stderr=sys.stderr,
+                                  stdout=open("/dev/null", "w"))
+        elif conf.outputlevel == "verbose":
+            subprocess.check_call([conf.values["youtube-dl"], "-f %s/%s"
+                                   % (itag_value, video_codec),
+                                   "-o", "%(title)s-%(id)s.%(ext)s", url],
+                                  stderr=sys.stderr, stdout=sys.stdout)
+        elif conf.outputlevel == "quiet":
+            subprocess.check_call([conf.values["youtube-dl"], "-f %s/%s"
+                                   % (itag_value, video_codec),
+                                   "-o", "%(title)s-%(id)s.%(ext)s", url],
+                                  stderr=open("/dev/null", "w"),
+                                  stdout=open("/dev/null", "w"))
+    except subprocess.CalledProcessError as error:
+        LOGGER.debug(_("Error while calling youtube-dl: %s"), error.output)
+        raise stov_exceptions.YoutubeDlCallFailed()

+ 4 - 0
lib_stov/yt_noapi.py

@@ -35,6 +35,7 @@ LOGGER = logging.getLogger("stov")
 class YtChannel(object):
     """Stores the relevant attributes of a youtube channel."""
     def __init__(self):
+        self.type = ""
         self.title = ""
         self.videos = []
 
@@ -76,11 +77,13 @@ class Connector(object):
         elif self._type == "playlist":
             self._url = "https://www.youtube.com/playlist?list=%s" \
                         % urllib.parse.quote(self._name)
+        LOGGER.debug(_("Constructed URL for subscription: %s"), self._url)
 
     def _fetch_title(self):
         """Retrieves the title of the HTML page to use as a title for the
         subscription."""
         try:
+            LOGGER.debug(_("Opening URL: %s to fetch title"), self._url)
             data = urllib.request.urlopen(self._url)
         except urllib.error.HTTPError as err:
             if err.code == 404 and self._type == "user":
@@ -153,4 +156,5 @@ class Connector(object):
         channel = YtChannel()
         channel.title = self._title
         channel.videos = videos
+        channel.type = self._type
         return channel