* add more ducking command * fix current music command * provide more controls, like pause, resume, clear. * add more controls in the web interface * refactored and improved: 1. move get_music_tag_info to util, and 2. refined logic related to it. 3. now playlist will check for tag info thus update_music_tag_info is useless and was removed 4. add "add folder" feature to !file asked in #65, 5. fix bugs related to !file * truncate file list if too long * fixed several tiny bugs * fixed several tiny bugs continue * fixed several tiny bugs continue continue * fixed several tiny bugs continue**3 * fixed several tiny bugs continue**4 * added !filematch command to add files that match a regex pattern. * truncate long message * fix web interface delete file issue * refresh after delete file * add regex to listfile command * refactored command part, added partial match support for commands * organized * added random command * typo * typo * Fixed many bugs. * Added workaround for azlux/pymumble#44 to fix the memory leak. * changed logging style. * fixed bugs related to random and resume * fix now playing * fixed issue related to download * fixed issue related to download 2 * fixed thumbnail issue * fixed add url problem in web interface * REFACTORED, turned db.ini into sqlite3 database. * fixed remove song problem * fixed radio get title problem. auto download if tmp file is deleted * fixed current index not loaded from database * fixed: order of songs loaded from the database * fixed: some obscure bugs. beautified error of commands * added a workaround for TerryGeng/botamusique#1. * beautified * fixed: channel not loaded in the config * fixed: auto checked for updates * fixed: mysterious bug: sometimes "now playing" string cannot be properly displayed. The real reason is: do use <br />, do not use <br>. I tried hours to find out this. * chore: unified debug messages that refer to music items * feav: fetch ffmpeg stderr mentioned in #72, reformatted logs. * fix: async download not working * fix: async download not working, still * fix: async download not working, finished * feat: queue command: ▶current playing item◀ * feat: support more than one command prefix * chore: added some WARNINGs into default config file to avoid people to touch it. * refactor: packed all string contants into constants.py, just to avoid people messing it around. * refactor: required by azlux. Added a configuration.example.ini to keep people away from configuration.default.ini
94 lines
3.0 KiB
Python
94 lines
3.0 KiB
Python
import sqlite3
|
|
|
|
class DatabaseError(Exception):
|
|
pass
|
|
|
|
class Database:
|
|
def __init__(self, db_path):
|
|
self.db_path = db_path
|
|
|
|
# connect
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
|
|
# check if table exists, or create one
|
|
tables = cursor.execute("SELECT name FROM sqlite_master WHERE type='table' AND name='botamusique';").fetchall()
|
|
if len(tables) == 0:
|
|
cursor.execute("CREATE TABLE botamusique (section text, option text, value text, UNIQUE(section, option))")
|
|
conn.commit()
|
|
|
|
conn.close()
|
|
|
|
def get(self, section, option, **kwargs):
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
result = cursor.execute("SELECT value FROM botamusique WHERE section=? AND option=?", (section, option)).fetchall()
|
|
conn.close()
|
|
|
|
if len(result) > 0:
|
|
return result[0][0]
|
|
else:
|
|
if 'fallback' in kwargs:
|
|
return kwargs['fallback']
|
|
else:
|
|
raise DatabaseError("Item not found")
|
|
|
|
def getboolean(self, section, option, **kwargs):
|
|
return bool(int(self.get(section, option, **kwargs)))
|
|
|
|
def getfloat(self, section, option, **kwargs):
|
|
return float(self.get(section, option, **kwargs))
|
|
|
|
def getint(self, section, option, **kwargs):
|
|
return int(self.get(section, option, **kwargs))
|
|
|
|
def set(self, section, option, value):
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
cursor.execute('''
|
|
INSERT OR REPLACE INTO botamusique (section, option, value)
|
|
VALUES (?, ?, ?)
|
|
''', (section, option, value))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def has_option(self, section, option):
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
result = cursor.execute("SELECT value FROM botamusique WHERE section=? AND option=?", (section, option)).fetchall()
|
|
conn.close()
|
|
if len(result) > 0:
|
|
return True
|
|
else:
|
|
return False
|
|
|
|
def remove_option(self, section, option):
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
cursor.execute("DELETE FROM botamusique WHERE section=? AND option=?", (section, option))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def remove_section(self, section):
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
cursor.execute("DELETE FROM botamusique WHERE section=?", (section, ))
|
|
conn.commit()
|
|
conn.close()
|
|
|
|
def items(self, section):
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
results = cursor.execute("SELECT option, value FROM botamusique WHERE section=?", (section, )).fetchall()
|
|
conn.close()
|
|
|
|
return map(lambda v: (v[0], v[1]), results)
|
|
|
|
def drop_table(self):
|
|
conn = sqlite3.connect(self.db_path)
|
|
cursor = conn.cursor()
|
|
cursor.execute("DROP TABLE botamusique")
|
|
conn.close()
|
|
|
|
|