stov.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. #! /usr/bin/env python
  2. # -*- coding: utf8 -*-
  3. #stov - a program to subscribe to channels and users from youtube
  4. # and download the videos automatically
  5. #
  6. # written by Helmut Pozimski in 2012
  7. #
  8. # This program is free software; you can redistribute it and/or
  9. # modify it under the terms of the GNU General Public License
  10. # as published by the Free Software Foundation; version 2
  11. # of the License.
  12. #
  13. # This program is distributed in the hope that it will be useful,
  14. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. # GNU General Public License for more details.
  17. #
  18. # You should have received a copy of the GNU General Public License
  19. # along with this program; if not, write to the Free Software
  20. # Foundation, Inc., 51 Franklin Street, Fifth Floor,
  21. # Boston, MA 02110-1301, USA.
  22. from __future__ import unicode_literals
  23. import sys
  24. import gettext
  25. import os
  26. import sqlite3
  27. import email
  28. import smtplib
  29. import subprocess
  30. from email.mime.multipart import MIMEMultipart
  31. from email.mime.text import MIMEText
  32. from optparse import OptionParser
  33. import subscription
  34. import youtube
  35. import configuration
  36. from outputhelper import printf
  37. """Determine the path where the stov files are for localization"""
  38. locale_path = sys.path[0] + "/locale"
  39. """Initialize gettext to support translation of the program"""
  40. try:
  41. trans = gettext.translation("stov", locale_path, ["de"])
  42. except IOError:
  43. gettext.install("stov")
  44. printf(_("Translation files could not be found, localization "
  45. "won't be available"), outputlevel="default",
  46. descriptor="stderr")
  47. else:
  48. if sys.version_info >= (3, 0):
  49. trans.install()
  50. else:
  51. trans.install(unicode=True)
  52. """Overwrite the default OptionParser class so error messages
  53. can be localized
  54. """
  55. class MyOptionParser(OptionParser):
  56. def error(self, msg):
  57. if "invalid integer" in msg:
  58. printf(_("option %s requires an integer value")
  59. % msg.split()[1],
  60. outputlevel="default", descriptor="stderr")
  61. self.exit()
  62. elif "an argument" in msg:
  63. printf(_("option %s requires an argument") % msg.split()[0],
  64. outputlevel="default", descriptor="stderr")
  65. self.exit()
  66. elif "no such" in msg:
  67. printf(_("invalid option %s") % msg.split()[3],
  68. outputlevel="default", descriptor="stderr")
  69. self.exit()
  70. else:
  71. printf(msg, outputlevel="default", descriptor="stderr")
  72. self.exit()
  73. """Process the given options and parameters,
  74. add: Add a new subscription (which can be a search, channel or playlist)
  75. channel: with add, specify the name of the channel or user
  76. lssubs: List the currently available subscriptions
  77. remove: remove a subscription
  78. update: update the information about the available videos
  79. download: download all available videos which haven't already been downloaded
  80. search: optionally add a search string to a new subscription or create a
  81. search subscription with add
  82. playlist: with add, subscribe to a youtube playlist
  83. catchup: Mark all videos in a subscription as downloaded
  84. version: Print version number
  85. quiet: Suppress all output
  86. verbose: Print normal output + diagnostical messages
  87. clean-database: Clean the database of old entries, meaning videos that
  88. are no longer present in the current API response of youtube
  89. """
  90. parser = MyOptionParser(usage=_("Usage: %prog [options]"), prog="stov",
  91. add_help_option=True, conflict_handler="resolve")
  92. parser.add_option("-a", "--add", dest="add", action="store_true",
  93. help=_("Add a new subscription (requires either --search, \
  94. --channel or --playlist)"))
  95. parser.add_option("-p", "--playlist", dest="playlist",
  96. help=_("Add a new Playlist subscription (requires add)"))
  97. parser.add_option("-l", "--lssubs", action="store_true", dest="list",
  98. help=_("List the currently available subscriptions"))
  99. parser.add_option("-r", "--remove", type="int", dest="deleteid",
  100. help=_("remove a subscription"))
  101. parser.add_option("-u", "--update", action="store_true", dest="update",
  102. help=_("update the information about the available videos"))
  103. parser.add_option("-d", "--download", action="store_true", dest="download",
  104. help=_("download all available videos which haven't already been downloaded"))
  105. parser.add_option("-s", "--search", dest="searchparameter",
  106. help=_("optionally add a search string to a new channel subscription or \
  107. create a new search subscription (requires --add)"))
  108. parser.add_option("-l", "--lsvids", type="int", dest="subscriptionid",
  109. help=_("Print all videos from a subscription"))
  110. parser.add_option("-c", "--catchup", dest="catchup",
  111. help=_("Mark all videos from one channel as read \
  112. (requires subscription-id as argument)"))
  113. parser.add_option("-c", "--channel", dest="channel",
  114. help=_("specify a channel for a new subscription (requires --add)"))
  115. parser.add_option("-l", "--license", dest="license", action="store_true",
  116. help=_("show the license of the program"))
  117. parser.add_option("-v", "--version", dest="version", action="store_true",
  118. help=_("show the current running version number"))
  119. parser.add_option("-q", "--quiet", dest="quiet", action="store_true",
  120. help=_("Suppress all output"))
  121. parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
  122. help=_("Be verbose and print also diagnostical messages"))
  123. parser.add_option("-c", "--clean-database", dest="cleanup",
  124. action="store_true", help=_("Clean the database of entries no longer listed "
  125. "in the current API response"))
  126. (options, arguments) = parser.parse_args()
  127. """Check if stov is run directly from command line since it shouldn't be
  128. loaded as a module
  129. """
  130. if __name__ != "__main__":
  131. print >> sys.stderr, """This file should not be imported as a module
  132. please run it directly from command line"""
  133. sys.exit(1)
  134. """Check which outputlevel is defined and save it to a temporary variable
  135. accordingly. Output generated before this will be printed to stdout regardless
  136. of the user defined setting
  137. """
  138. if options.verbose is True and options.quiet is True:
  139. printf(_("--quiet and --verbose can't be defined at the same time, "
  140. "exiting."), outputlevel="default")
  141. sys.exit(1)
  142. elif options.verbose is True:
  143. outputlevel = "verbose"
  144. elif options.quiet is True:
  145. outputlevel = "quiet"
  146. else:
  147. outputlevel = "default"
  148. """Create the lock file which is used to determine if another instance is
  149. already running by chance, the program shouldn't be run in this case since
  150. we want to prevent concurent access to the database.
  151. """
  152. if os.access("/tmp/stov.lock", os.F_OK):
  153. try:
  154. lockfile = open("/tmp/stov.lock", "r")
  155. except IOError:
  156. printf(_("Lock file could not be opened, please check that "
  157. "it exists and is readable, quitting now"),
  158. outputlevel="default", level=outputlevel, descriptor="stderr")
  159. sys.exit(1)
  160. oldpid = lockfile.read().strip()
  161. if os.access("/proc/" + oldpid, os.F_OK):
  162. printf(_("The lock file already exists, probably another"
  163. "instance of this program is already running\n"
  164. "if you are sure this is not the case, delete it"
  165. " manually and try again!"),
  166. outputlevel="default", level=outputlevel, descriptor="stderr")
  167. sys.exit(1)
  168. lockfile.close()
  169. if os.access("/proc/" + oldpid, os.F_OK) is not True:
  170. try:
  171. os.remove("/tmp/stov.lock")
  172. except os.error:
  173. printf(_("Old lock file could not be deleted!"),
  174. outputlevel="default", level=outputlevel, descriptor="stderr")
  175. try:
  176. lockfile = open("/tmp/stov.lock", "w")
  177. lockfile.write(str(os.getpid()))
  178. lockfile.close()
  179. except IOError:
  180. printf(_("Lock file could not be created, please check that /tmp is "
  181. "writable and properly configured, quitting now"),
  182. outputlevel="default", level=outputlevel, descriptor="stderr")
  183. sys.exit(1)
  184. """Check if the configuration directory exists and is writeable. If it \
  185. doesnt, create it using the configuration class.
  186. """
  187. if os.access(os.environ['HOME'] + "/.stov", os.F_OK & os.W_OK) is not True:
  188. printf(_("This seems to be the first time you run the programm, do you"
  189. " want to run the interactive assistant? (yes/no)"),
  190. outputlevel="default", level=outputlevel, descriptor="stdout")
  191. conf = configuration.conf()
  192. temp_input = raw_input()
  193. if temp_input == "yes":
  194. conf.assist()
  195. else:
  196. printf(_("Writing initial configuration according to default values"),
  197. outputlevel="default", level=outputlevel, descriptor="stdout")
  198. conf.Initialize()
  199. else:
  200. conf = configuration.conf()
  201. if conf.CheckConfig() is not True:
  202. printf(_("Your configuration needs to be updated, performing"
  203. " update now."), outputlevel="default", level=outputlevel,
  204. descriptor="stdout")
  205. conf.UpdateConfig()
  206. if conf.CheckDB() is not True:
  207. printf(_("Your database needs to be updated, performing"
  208. " update now."), outputlevel="default", level=outputlevel,
  209. descriptor="stdout")
  210. conf.UpdateDB()
  211. conf.ReadConfig()
  212. """Check which outputlevel is defined and update the configuration object
  213. accordingly.
  214. """
  215. conf.outputlevel = outputlevel
  216. """youtube-dl is really a dependency but the program will run with limited\
  217. functionality without it so we need to check that here
  218. """
  219. if conf.values["youtube-dl"] == "":
  220. conf.values["youtube-dl"] = subprocess.Popen(["which", "youtube-dl"],
  221. stdout=subprocess.PIPE).communicate()[0].strip()
  222. if os.access(conf.values["youtube-dl"], os.F_OK & os.R_OK & os.X_OK):
  223. printf(_("Found youtube-dl, writing to configuration file."),
  224. outputlevel="default", level=conf.outputlevel, descriptor="stdout")
  225. conf.WriteConfig()
  226. else:
  227. printf(_("Could not find youtube-dl, it either does not exist, "
  228. "is not readable or not executable. Please note that "
  229. "youtube-dl is not needed for the program to run but is"
  230. " needed to use the download option which won't work otherwise."
  231. " If youtube-dl isn't found automatically, you may also enter "
  232. "the path to it in the configuration file."),
  233. outputlevel="default", level=conf.outputlevel, descriptor="stderr")
  234. """Variable to save the text that is later sent as e-mail"""
  235. mailcontent = []
  236. """Check which options are given on the command line and
  237. run the corresponding code
  238. """
  239. if options.add is True:
  240. AddSub = True
  241. if options.channel is not None and options.searchparameter is None:
  242. NewSubscription = subscription.sub(type="channel",
  243. name=options.channel, conf=conf)
  244. elif options.channel is not None and options.searchparameter is not None:
  245. NewSubscription = subscription.sub(type="channel",
  246. name=options.channel, search=options.searchparameter, conf=conf)
  247. elif options.channel is None and options.searchparameter is not None:
  248. NewSubscription = subscription.sub(type="search",
  249. name=_("Search_"), search=options.searchparameter, conf=conf)
  250. elif options.playlist is not None:
  251. if options.searchparameter is not None:
  252. printf(_("Playlists do not support searching, search option will "
  253. "be ignored!"), outputlevel="default", level=conf.outputlevel,
  254. descriptor="stderr")
  255. NewSubscription = subscription.sub(type="playlist",
  256. name=options.playlist, conf=conf)
  257. else:
  258. printf(_("No valid subscription options given, "
  259. "subscription could not be added"),
  260. outputlevel="default", level=conf.outputlevel, descriptor="stderr")
  261. AddSub = False
  262. if AddSub is True:
  263. NewSubscription.AddSub()
  264. NewSubscription.UpdateVideos()
  265. printf(_("New subscription ") + NewSubscription.GetTitle()
  266. + _(" successfully added"), outputlevel="default", level=conf.outputlevel,
  267. descriptor="stdout")
  268. elif options.list is True:
  269. try:
  270. database = sqlite3.connect(conf.dbpath)
  271. cursor = database.cursor()
  272. except sqlite3.OperationalError:
  273. printf(_("Could not access the database, please check path "
  274. "and permissions and try again!"), outputlevel="default",
  275. level=conf.outputlevel, descriptor="stderr")
  276. else:
  277. cursor.execute("SELECT id, title FROM subscriptions")
  278. Listofsubscriptions = cursor.fetchall()
  279. if len(Listofsubscriptions) != 0:
  280. printf(_("ID Title"), outputlevel="default", level=conf.outputlevel,
  281. descriptor="stdout")
  282. for subscription in Listofsubscriptions:
  283. if subscription[0] is not None:
  284. printf(str(subscription[0]) + " " + subscription[1],
  285. outputlevel="default", level=conf.outputlevel, descriptor="stdout")
  286. else:
  287. printf(_("No subscriptions added yet, add one!"), outputlevel="default",
  288. level=conf.outputlevel, descriptor="stdout")
  289. database.close()
  290. elif options.deleteid is not None:
  291. try:
  292. DeleteId = int(options.deleteid)
  293. Subscription = subscription.sub(type="", name="delete",
  294. id=DeleteId, conf=conf)
  295. except ValueError:
  296. printf(_("Invalid Option, please use the ID of the subscription"
  297. "you want to delete as parameter for the remove option"),
  298. outputlevel="default", level=conf.outputlevel, descriptor="stderr")
  299. Subscription.Delete()
  300. elif options.update is True:
  301. listofsubscriptions = []
  302. try:
  303. database = sqlite3.connect(conf.dbpath)
  304. cursor = database.cursor()
  305. except sqlite3.OperationalError:
  306. printf(_("Could not access the database, please check path and "
  307. "permissions and try again!"), outputlevel="default",
  308. level=conf.outputlevel, descriptor="stderr")
  309. else:
  310. cursor.execute("SELECT id,title,type,name,searchstring,directory \
  311. FROM subscriptions")
  312. subscriptions = cursor.fetchall()
  313. database.close()
  314. for element in subscriptions:
  315. listofsubscriptions.append(subscription.sub(id=element[0],
  316. title=element[1], type=element[2], name=element[3],
  317. search=element[4], directory=element[5], conf=conf))
  318. for element in listofsubscriptions:
  319. element.UpdateVideos()
  320. elif options.download is True:
  321. listofsubscriptions = []
  322. try:
  323. database = sqlite3.connect(conf.dbpath)
  324. cursor = database.cursor()
  325. except sqlite3.OperationalError:
  326. printf(_("Could not access the database, please check path"
  327. "and permissions and try again!"), outputlevel="default",
  328. level=conf.outputlevel, descriptor="stderr")
  329. else:
  330. cursor.execute("SELECT id,title,type,name,searchstring,directory \
  331. FROM subscriptions")
  332. subscriptions = cursor.fetchall()
  333. itag_value = conf.GetYoutubeParameter()
  334. if itag_value == 0:
  335. printf(_("Codec and resolution could not be determined, using maximum "
  336. "possible value"), outputlevel="verbose",
  337. level=conf.outputlevel, descriptor="stderr")
  338. itag_value = 38
  339. for element in subscriptions:
  340. listofsubscriptions.append(subscription.sub(id=element[0],
  341. title=element[1], type=element[2], name=element[3],
  342. search=element[4], directory=element[5], conf=conf))
  343. videosdownloaded = 0
  344. videosfailed = 0
  345. for element in listofsubscriptions:
  346. element.GetVideos()
  347. element.DownloadVideos(itag_value)
  348. for entry in element.DownloadedVideos:
  349. mailcontent.append(entry)
  350. videosdownloaded = len(mailcontent)
  351. videosfailed = videosfailed + element.FailedVideos
  352. if videosdownloaded > 0 and conf.values["notify"] == "yes":
  353. MailText = ""
  354. msg = MIMEMultipart()
  355. if videosdownloaded == 1:
  356. msg["Subject"] = _("Downloaded %i new video") % videosdownloaded
  357. MailText = _("The following episode has been downloaded by stov: \n\n")
  358. else:
  359. msg["Subject"] = _("Downloaded %i new videos") % videosdownloaded
  360. MailText = _("The following episodes have been downloaded by stov: \n\n")
  361. msg["From"] = "stov <%s>" % conf.values["mailfrom"]
  362. msg["To"] = "<%s>" % conf.values["mailto"]
  363. for line in mailcontent:
  364. MailText += line + "\n"
  365. msgtext = MIMEText(MailText.encode("utf8"), _charset="utf8")
  366. msg.attach(msgtext)
  367. serverconnection = smtplib.SMTP()
  368. try:
  369. if sys.version_info >= (3, 0):
  370. serverconnection.connect(conf.values["mailhost"], conf.values["smtpport"])
  371. else:
  372. serverconnection.connect(str(conf.values["mailhost"]),
  373. str(conf.values["smtpport"]))
  374. except smtplib.SMTPConnectError:
  375. printf(sys.stderr, _("Could not connect to the smtp server, please"
  376. " check your settings!"), outputlevel="default",
  377. level=conf.outputlevel, descriptor="stderr")
  378. try:
  379. serverconnection.starttls()
  380. except smtplib.SMTPException:
  381. printf(_("TLS not available, proceeding unencrypted"),
  382. outputlevel="default", level=conf.outputlevel, descriptor="stdout")
  383. if conf.values["auth_needed"] == "yes":
  384. try:
  385. serverconnection.login(conf.values["user_name"], conf.values["password"])
  386. except smtplib.SMTPAuthenticationError:
  387. printf(_("Authentication failed, please check user name"
  388. "and password!"), outputlevel="default", level=conf.outputlevel,
  389. descriptor="stderr")
  390. except smtplib.SMTPException:
  391. printf(_("Could not authenticate, server probably does not"
  392. " support authentication!"), outputlevel="default",
  393. level=conf.outputlevel, descriptor="stderr")
  394. try:
  395. serverconnection.sendmail(conf.values["mailfrom"], conf.values["mailto"],
  396. msg.as_string())
  397. except smtplib.SMTPRecipientsRefused:
  398. printf(_("The server refused the recipient address, "
  399. "please check your settings"),
  400. outputlevel="default", level=conf.outputlevel, descriptor="stderr")
  401. except smtplib.SMTPSenderRefused:
  402. printf(_("The server refused the sender address, "
  403. "please check your settings"), outputlevel="default",
  404. level=conf.outputlevel, descriptor="stderr")
  405. serverconnection.quit()
  406. elif videosdownloaded == 0 and videosfailed == 0:
  407. if conf.values["notify"] == "no":
  408. printf(_("No videos to be downloaded."), outputlevel="default",
  409. level=conf.outputlevel, descriptor="stdout")
  410. elif conf.values["notify"] == "no":
  411. if videosfailed == 0:
  412. printf(_("The following videos have been downloaded:\n"),
  413. outputlevel="default", level=conf.outputlevel, descriptor="stdout")
  414. for i in mailcontent:
  415. printf(i, outputlevel="default", level=conf.outputlevel,
  416. descriptor="stdout")
  417. else:
  418. printf(_("Could not determine how you want to be informed "
  419. "about new videos, please check the notify parameter "
  420. "in your configuration"), outputlevel="default",
  421. level=conf.outputlevel, descriptor="stderr")
  422. elif options.subscriptionid is not None:
  423. try:
  424. database = sqlite3.connect(conf.dbpath)
  425. cursor = database.cursor()
  426. except sqlite3.OperationalError:
  427. printf(_("Could not access the database, please check"
  428. "path and permissions and try again!"),
  429. outputlevel="default", level=conf.outputlevel, descriptor="stderr")
  430. else:
  431. subscription_video_select = "SELECT id,title,type,name,searchstring,\
  432. directory FROM subscriptions where id=?"
  433. cursor.execute(subscription_video_select, (options.subscriptionid,))
  434. Data = cursor.fetchall()
  435. if Data != []:
  436. Subscription = subscription.sub(id=Data[0][0], title=Data[0][1],
  437. type=Data[0][2], name=Data[0][3], search=Data[0][4],
  438. directory=Data[0][5], conf=conf)
  439. Subscription.GetVideos()
  440. Subscription.PrintVideos()
  441. else:
  442. printf(_("Invalid subscription, please check the list and try again"),
  443. outputlevel="default", level=conf.outputlevel, descriptor="stderr")
  444. elif options.catchup is not None:
  445. try:
  446. database = sqlite3.connect(conf.dbpath)
  447. cursor = database.cursor()
  448. except sqlite3.OperationalError:
  449. printf(_("Could not access the database, please check "
  450. "path and permissions and try again!"),
  451. outputlevel="default", level=conf.outputlevel, descriptor="stderr")
  452. else:
  453. subscriptionselect = "SELECT title from subscriptions where id=?"
  454. cursor.execute(subscriptionselect, (options.catchup,))
  455. sub_data = cursor.fetchall()
  456. if sub_data != []:
  457. catchup_sql = "UPDATE videos SET downloaded = 1 WHERE subscription_id =?"
  458. cursor.execute(catchup_sql, (options.catchup,))
  459. database.commit()
  460. database.close()
  461. else:
  462. printf(_("Subscription could not be updated, "
  463. "please check if the ID given is correct"),
  464. outputlevel="default", level=conf.outputlevel, descriptor="stderr")
  465. elif options.cleanup is True:
  466. subscriptions_list = []
  467. try:
  468. database = sqlite3.connect(conf.dbpath)
  469. cursor = database.cursor()
  470. except sqlite3.OperationalError:
  471. printf(_("Could not access the database, please check path and "
  472. "permissions and try again!"), outputlevel="default",
  473. level=conf.outputlevel, descriptor="stderr")
  474. else:
  475. cursor.execute("SELECT id,title,type,name,searchstring,directory \
  476. FROM subscriptions")
  477. subscriptions = cursor.fetchall()
  478. database.close()
  479. for element in subscriptions:
  480. subscriptions_list.append(subscription.sub(id=element[0],
  481. title=element[1], type=element[2], name=element[3],
  482. search=element[4], directory=element[5], conf=conf))
  483. for element in subscriptions_list:
  484. element.CheckAndDelete()
  485. try:
  486. database = sqlite3.connect(conf.dbpath)
  487. cursor = database.cursor()
  488. except sqlite3.OperationalError:
  489. printf(_("Could not access the database, please check path and "
  490. "permissions and try again!"), outputlevel="default",
  491. level=conf.outputlevel, descriptor="stderr")
  492. else:
  493. cursor.execute("VACUUM")
  494. database.close()
  495. elif options.license is True:
  496. printf("""
  497. stov is free software: you can redistribute it and/or modify
  498. it under the terms of the GNU General Public License as published by
  499. the Free Software Foundation, version 2 of the License.
  500. stov is distributed in the hope that it will be useful,
  501. but WITHOUT ANY WARRANTY; without even the implied warranty of
  502. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  503. GNU General Public License for more details.
  504. You should have received a copy of the GNU General Public License
  505. along with stov. If not, see <http://www.gnu.org/licenses/>.
  506. """, outputlevel="default", level=conf.outputlevel, descriptor="stdout")
  507. elif options.version is True:
  508. printf("0.4wip", outputlevel="default", level=conf.outputlevel,
  509. descriptor="stdout")
  510. else:
  511. parser.print_help()
  512. """Remove the lock file and end the program so it can be run again"""
  513. try:
  514. os.remove("/tmp/stov.lock")
  515. sys.exit(0)
  516. except os.error:
  517. printf(_("Could not delete the lock file. Please check what "
  518. "went wrong and clean up manually!"),
  519. outputlevel="default", level=conf.outputlevel, descriptor="stderr")