subscription.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318
  1. #
  2. # This file is part of stov, written by Helmut Pozimski 2012-2014.
  3. #
  4. # stov is free software: you can redistribute it and/or modify
  5. # it under the terms of the GNU General Public License as published by
  6. # the Free Software Foundation, version 2 of the License.
  7. #
  8. # stov is distributed in the hope that it will be useful,
  9. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. # GNU General Public License for more details.
  12. #
  13. # You should have received a copy of the GNU General Public License
  14. # along with stov. If not, see <http://www.gnu.org/licenses/>.
  15. # -*- coding: utf8 -*-
  16. from __future__ import unicode_literals
  17. import sys
  18. import gettext
  19. import sqlite3
  20. import ssl
  21. if sys.version_info >= (3,):
  22. import urllib.request as urllib2
  23. else:
  24. import urllib2
  25. from lib_stov import youtubeAPI
  26. from lib_stov import youtube
  27. from lib_stov.outputhelper import printf
  28. class sub(object):
  29. def __init__(self, type, name, conf, search="", id=0, title="", directory="",
  30. disabled=0):
  31. self.__ID = id
  32. self.__title = title
  33. self.__type = type
  34. self.__name = name
  35. self.__search = search
  36. self.__directory = directory
  37. self.__APIURL = ""
  38. self.__conf = conf
  39. self.DownloadedVideos = []
  40. self.FailedVideos = 0
  41. if int(disabled) == 0:
  42. self.__disabled = False
  43. elif int(disabled) == 1:
  44. self.__disabled = True
  45. if self.__name != "delete":
  46. self.__ConstructAPIURL()
  47. def GetTitle(self):
  48. return self.__title
  49. def Delete(self):
  50. """Deletes all videos from the subscription from the database and deletes
  51. the subscription itself once it's done.
  52. """
  53. try:
  54. self.__connection = sqlite3.connect(self.__conf.dbpath)
  55. except sqlite3.OperationalError:
  56. printf(_("Could not write to database, subscription "
  57. "was NOT deleted. Please check permissions and try again."),
  58. outputlevel="default", level=self.__conf.outputlevel, descriptor="stderr")
  59. else:
  60. self.__cursor = self.__connection.cursor()
  61. self.__checkquery = "SELECT * FROM subscriptions WHERE id=?"
  62. self.__checkresult = self.__cursor.execute(self.__checkquery, (self.__ID,))
  63. if self.__checkresult.fetchall() == []:
  64. printf(_("The subscription could not be found and was therefore "
  65. "not deleted"), outputlevel="default", level=self.__conf.outputlevel,
  66. descriptor="stderr")
  67. self.__connection.close()
  68. return False
  69. else:
  70. self.__deletevideos = "DELETE FROM videos WHERE subscription_id=?"
  71. self.__cursor.execute(self.__deletevideos, (self.__ID,))
  72. self.__deletesubscription = "DELETE FROM subscriptions WHERE id=?"
  73. self.__cursor.execute(self.__deletesubscription, (self.__ID,))
  74. self.__connection.commit()
  75. self.__connection.close()
  76. printf(_("Subscription deleted successfully!"), outputlevel="default",
  77. level=self.__conf.outputlevel, descriptor="stdout")
  78. return True
  79. def UpdateVideos(self):
  80. """Retrieves the current videos from the youtube API and adds them
  81. to the database if they where not already added earlier
  82. """
  83. if self.__disabled is False:
  84. if self.__ParseAPIData() is True:
  85. self.__processed = []
  86. try:
  87. self.__connection = sqlite3.connect(self.__conf.dbpath)
  88. self.__cursor = self.__connection.cursor()
  89. except sqlite3.OperationalError:
  90. printf(_("Could not write to database, "
  91. "new video was NOT added! Please check permissions and try again."),
  92. outputlevel="default", level=self.__conf.outputlevel, descriptor="stderr")
  93. for i in self.__ParsedResponse.videos:
  94. printf(_('Checking if video "%s" exists in the database') % i.title,
  95. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  96. self.__ytid = i.ytid
  97. if self.__search != "" and self.__conf.values["check_title"] == "yes":
  98. if self.__search not in i.title:
  99. printf(_("You have requested title checking and the title of the video "
  100. "does not match with the search string, ignoring it"),
  101. outputlevel="verbose", level=self.__conf.outputlevel,
  102. descriptor="stderr")
  103. continue
  104. self.__videoquery = "SELECT id FROM videos WHERE ytid=?"
  105. self.__cursor.execute(self.__videoquery, (self.__ytid,))
  106. self.__tmpid = self.__cursor.fetchall()
  107. if self.__tmpid == []:
  108. printf(_("Video %s not found in database, inserting...") % i.title,
  109. outputlevel="verbose", level=self.__conf.outputlevel,
  110. descriptor="stderr")
  111. self.__query = "INSERT INTO videos (title, description, \
  112. ytid, subscription_id, downloaded) VALUES (?, ?, ?, ?, ?)"
  113. self.__data = (i.title, i.description, self.__ytid, self.__ID, 0)
  114. self.__cursor.execute(self.__query, self.__data)
  115. self.__connection.commit()
  116. self.__connection.close()
  117. else:
  118. printf(_("Subscription %s is disabled, it will not be updated")
  119. % self.__title, outputlevel="verbose", level=self.__conf.outputlevel,
  120. descriptor="stderr")
  121. def GetVideos(self):
  122. """Retrieves all videos in the subscription from the database and saves
  123. them in an the internal list so they can be accessed by the object
  124. """
  125. self.__videos = []
  126. try:
  127. self.__connection = sqlite3.connect(self.__conf.dbpath)
  128. self.__cursor = self.__connection.cursor()
  129. except sqlite3.OperationalError:
  130. printf(_("Could not access database. "
  131. "Please check permissions and try again."),
  132. outputlevel="default", level=self.__conf.outputlevel, descriptor="stderr")
  133. printf(_("Getting all videos for subscription %s from database")
  134. % self.__title, outputlevel="verbose", level=self.__conf.outputlevel,
  135. descriptor="stderr")
  136. self.__videoquerybysubscription = "SELECT id, title, description, \
  137. ytid, downloaded, failcnt FROM videos WHERE subscription_id=?"
  138. self.__cursor.execute(self.__videoquerybysubscription, (self.__ID,))
  139. self.__videodata = self.__cursor.fetchall()
  140. for i in self.__videodata:
  141. printf(_("Got video %s") % i[1],
  142. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  143. self.__videos.append(youtube.video(id=i[0],
  144. title=i[1], description=i[2], ytid=i[3],
  145. downloaded=i[4], failcount=i[5], conf=self.__conf))
  146. self.__connection.close()
  147. def DownloadVideos(self, itag_value):
  148. """Uses the DownloadVideo method of the video object to download all
  149. videos contained in the subscription and adds them to the list of
  150. downloaded videos if the download succeeds.
  151. """
  152. if self.__disabled is False:
  153. for video in self.__videos:
  154. if video.downloaded == 0:
  155. if video.DownloadVideo(self.__directory, itag_value) is True:
  156. self.DownloadedVideos.append(video.title)
  157. else:
  158. self.FailedVideos = self.FailedVideos + 1
  159. else:
  160. printf(_("Subscription %s is disabled, videos will not be downloaded")
  161. % self.__title, outputlevel="verbose", level=self.__conf.outputlevel,
  162. descriptor="stderr")
  163. """Prints a list of all videos contained in the subscription"""
  164. def PrintVideos(self):
  165. printf(_("Videos in subscription ") + self.__title + ":\n",
  166. outputlevel="default", level=self.__conf.outputlevel, descriptor="stdout")
  167. for i in self.__videos:
  168. if i.downloaded == 0:
  169. printf(i.title + _(" (pending)"), outputlevel="default",
  170. level=self.__conf.outputlevel, descriptor="stdout")
  171. elif i.downloaded == 1:
  172. printf(i.title + _(" (downloaded)"), outputlevel="default",
  173. level=self.__conf.outputlevel, descriptor="stdout")
  174. elif i.downloaded == -1:
  175. printf(i.title + _(" (failed)"), outputlevel="default",
  176. level=self.__conf.outputlevel, descriptor="stdout")
  177. def AddSub(self):
  178. """Adds a new subscription to the database"""
  179. if self.__ParseAPIData() is True:
  180. try:
  181. self.__connection = sqlite3.connect(self.__conf.dbpath)
  182. except sqlite3.OperationalError:
  183. printf(_("Could not write to database, new "
  184. "subscription was NOT added! Please check permissions and try again."),
  185. outputlevel="default", level=self.__conf.outputlevel, descriptor="stderr")
  186. else:
  187. self.__cursor = self.__connection.cursor()
  188. self.__title = self.__ParsedResponse.title
  189. printf(_("Found subscription title %s") % self.__title,
  190. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  191. self.__directory = self.__name + "_" + self.__search.replace(" ", "_")
  192. printf(_("Directory: %s") % self.__directory,
  193. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  194. self.__query = "INSERT INTO subscriptions (title, type, searchstring, \
  195. directory, name, disabled) VALUES (?, ?, ?, ?, ?, ?)"
  196. self.__data = (self.__title, self.__type, self.__search,
  197. self.__directory, self.__name, 0)
  198. printf(_("Writing subscription info into database..."),
  199. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  200. self.__cursor.execute(self.__query, self.__data)
  201. self.__connection.commit()
  202. self.__query = "SELECT id from subscriptions where title=?"
  203. self.__data = (self.__title, )
  204. self.__cursor.execute(self.__query, self.__data)
  205. self.__ID = self.__cursor.fetchone()
  206. self.__ID = self.__ID[0]
  207. printf(_("Subscription got internal ID %s") % self.__ID,
  208. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  209. self.__connection.close()
  210. return True
  211. else:
  212. return False
  213. def __ParseAPIData(self):
  214. """Retrieves the youtube API data and parses them so they can be used
  215. by the object
  216. """
  217. try:
  218. printf(_("Updating subscription %s") % self.__name,
  219. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  220. printf(_("Connectiong to youtube API."),
  221. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  222. self.__ConnectAPI = urllib2.urlopen(self.__APIURL, timeout=30)
  223. printf(_("Connection established, reading data."),
  224. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  225. self.__APIResponse = self.__ConnectAPI.read()
  226. self.__ConnectAPI.close()
  227. except urllib2.HTTPError:
  228. printf(_("Could not get API data, maybe the API "
  229. "is down or you have given wrong parameters"
  230. ", please check and try again!"),
  231. outputlevel="default", level=self.__conf.outputlevel, descriptor="stderr")
  232. return False
  233. except ssl.SSLError:
  234. printf(_("The API Request timed out for subscription %s, "
  235. "please try again later") % self.__title,
  236. outputlevel="default", level=self.__conf.outputlevel, descriptor="stderr")
  237. return False
  238. else:
  239. printf(_("Parsing youtube API response."),
  240. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  241. parser = youtubeAPI.Parser(self.__APIResponse)
  242. self.__ParsedResponse = parser.parse()
  243. return True
  244. def __ConstructAPIURL(self):
  245. """Constructs the API URL which is used to retrieve API data"""
  246. if self.__type == "channel":
  247. self.__APIURL = "https://gdata.youtube.com/feeds/api/users/"\
  248. + urllib2.quote(self.__name) + "/uploads/" + "?v=2"\
  249. + "&max-results=%s" % self.__conf.values["maxvideos"]
  250. if self.__search != "":
  251. self.__APIURL = self.__APIURL + "&q=" + "%22"\
  252. + urllib2.quote(self.__search) + "%22"
  253. elif self.__type == "search":
  254. self.__APIURL = "http://gdata.youtube.com/feeds/api/videos?q="\
  255. + urllib2.quote(self.__search) + "&v=2"\
  256. + "&max-results=%s" % self.__conf.values["maxvideos"]
  257. elif self.__type == "playlist":
  258. self.__APIURL = "https://gdata.youtube.com/feeds/api/playlists/"\
  259. + "%20" + urllib2.quote(self.__name) + "%20" + "?v=2"\
  260. + "&max-results=%s" % self.__conf.values["maxvideos"]
  261. else:
  262. printf(_("None or invalid subscription type given, "
  263. "please check the type option and try again"),
  264. outputlevel="default", level=self.__conf.outputlevel, descriptor="stderr")
  265. printf(_("Constructed the following API URL: %s") % self.__APIURL,
  266. outputlevel="verbose", level=self.__conf.outputlevel, descriptor="stderr")
  267. def CheckAndDelete(self):
  268. """Checks if a video still exists in the current API response and deletes
  269. it if it doesn't
  270. """
  271. self.__ParseAPIData()
  272. self.GetVideos()
  273. self.__id_list = []
  274. for entry in self.__ParsedResponse.videos:
  275. self.__id_list.append(entry.ytid)
  276. for item in self.__videos:
  277. if item.ytid not in self.__id_list:
  278. if item.delete() is True:
  279. printf(_("Video %s deleted from the database!") % item.title,
  280. outputlevel="verbose", level=self.__conf.outputlevel,
  281. descriptor="stdout")
  282. else:
  283. printf(_("Videos %s could not be deleted from the database"),
  284. outputlevel="verbose", level=self.__conf.outputlevel,
  285. descriptor="stderr")