]>
code.delx.au - monosys/blob - mythtv/mythmusic_importm3u.py
160f5f45007a13619b77ae8a82b42c4421dfb7dd
3 # This script reads in m3u playlists and inserts them into the mythtv database
5 # Copyright 2008 James Bunton <jamesbunton@fastmail.fm>
6 # Licensed for distribution under the GPL version 2
8 # Copyright 2005 Patrick Riley (http://pathacks.blogspot.com/). You are
9 # free to use and modify this code as you see fit, but it comes with
10 # no warranty whatsoever.
13 import logging
, optparse
, os
.path
, re
, sys
17 class PlaylistBuilder(object):
18 findPlaylistQuery
= """
21 WHERE playlist_name = %s
25 FROM music_directories dir JOIN music_songs song ON dir.directory_id = song.directory_id
26 WHERE concat(dir.path, '/', song.filename) = %s
28 insertPlaylistQuery
= """
30 music_playlists(playlist_name, playlist_songs)
33 updatePlaylistQuery
= """
34 UPDATE music_playlists
35 SET playlist_songs = %s
36 WHERE playlist_id = %s
39 def __init__(self
, dbHost
, dbUser
, dbPassword
, dbName
="mythconverg"):
40 self
.dbconnection
= MySQLdb
.connect(db
=dbName
, host
=dbHost
, user
=dbUser
, passwd
=dbPassword
)
41 self
.cursor
= self
.dbconnection
.cursor()
43 def startPlaylist(self
, name
):
44 self
.playlistName
= name
46 self
.cursor
.execute(self
.findPlaylistQuery
, [self
.playlistName
])
47 if self
.cursor
.rowcount
< 0:
48 raise ValueError("rowcount < 0?")
49 elif self
.cursor
.rowcount
> 0:
50 assert self
.cursor
.rowcount
== 1, "More than one playlist with the same name?"
51 self
.playlistId
= self
.cursor
.fetchone()[0]
53 self
.addFile
= self
.addFileFirst
55 def addFileNormal(self
, filename
):
56 filename
= filename
.split(os
.path
.sep
, self
.strip
)[-1]
57 self
.cursor
.execute(self
.findItemQuery
, [filename
])
58 if self
.cursor
.rowcount
!= 1:
59 logging
.warning("Did not find an entry for '%s' (return=%d)" % (filename
, self
.cursor
.rowcount
))
61 self
.ids
.append("%d" % self
.cursor
.fetchone()[0])
63 def addFileFirst(self
, filename
):
64 self
.addFile
= self
.addFileNormal
66 # Pull off path components until we find it
69 self
.cursor
.execute(self
.findItemQuery
, [filename
])
70 if self
.cursor
.rowcount
== 1:
72 filename
= filename
.split(os
.path
.sep
, 1)[1]
75 logging
.warning("Did not find entry for first file in playlist: '%s' No autodetection of strip amount." % filename
)
79 logging
.info("Found file: '%s', auto detected strip amount: %d" % (filename
, self
.strip
))
80 self
.ids
.append("%d" % self
.cursor
.fetchone()[0])
82 def finishPlaylist(self
):
83 idsString
= ",".join(self
.ids
)
84 logging
.info("Playlist '%s' is: %s" % (self
.playlistName
, idsString
))
85 if self
.playlistId
< 0:
86 self
.cursor
.execute(self
.insertPlaylistQuery
, [self
.playlistName
, idsString
])
88 self
.cursor
.execute(self
.updatePlaylistQuery
, [idsString
, self
.playlistId
])
89 # We can't check the rowcount here because update will say 0 if no values actually changed
90 # assert cursor.rowcount == 1, "Insert/update failed? %d" % (playlistId)
93 def stripComments(it
):
94 re_comment
= re
.compile(r
"^\s*#")
95 re_all_whitespace
= re
.compile(r
"^\s*$")
98 if re_comment
.match(line
):
100 if re_all_whitespace
.match(line
):
106 def readMysqlTxt(filename
, vars):
107 for line
in stripComments(open(filename
)):
109 key
, value
= line
.split("=")
111 logging
.warning("Couldn't parse mysql.txt line -- %s" % line
)
112 vars[key
.lower().strip()] = value
.strip()
115 parser
= optparse
.OptionParser(usage
="""%prog [options] Playlist.m3u [Another.m3u] ...
117 Converts m3u style playlists into playlists for MythTV
119 A m3u file is a list of filenames where # begins comments. This script
120 makes one playlist for each m3u file you give it (stripping the extention
121 from the filename to get the playlist name)
123 This script works by connecting to your MythTV MySQL database and querying
124 for the filenames found in the .m3u file.
126 The database connection settings will be read from the .mythtv/mysql.txt
127 file if possible, otherwise specify them on the command line.
129 parser
.add_option("--dbhost", help="MythTV database host to connect to",
130 action
="store", dest
="host", default
=vars["dbhostname"])
131 parser
.add_option("--dbuser", help="MythTV database username",
132 action
="store", dest
="user", default
=vars["dbusername"])
133 parser
.add_option("--dbpassword", help="MythTV database password",
134 action
="store", dest
="password", default
=vars["dbpassword"])
135 parser
.add_option("--dbname", help="MythTV database name",
136 action
="store", dest
="database", default
=vars["dbname"])
137 parser
.add_option("-v", "--verbose", help="Be verbose",
138 action
="store_true", dest
="verbose")
140 options
, filenames
= parser
.parse_args()
141 vars["dbhostname"] = options
.host
142 vars["dbusername"] = options
.user
143 vars["dbpassword"] = options
.password
144 vars["dbname"] = options
.database
146 logging
.basicConfig(level
=logging
.INFO
)
148 logging
.basicConfig(level
=logging
.WARNING
)
155 "dbhostname": "localhost",
156 "dbusername": "mythtv",
157 "dbpassword": "mythtv",
158 "dbname": "mythconverg",
162 for path
in ["~/.mythtv/mysql.txt", "~mythtv/.mythtv/mysql.txt", "/etc/mythtv/mysql.txt"]:
164 readMysqlTxt(os
.path
.expanduser(path
), vars)
169 filenames
= readArgs(vars)
171 logging
.warning("Could not read mysql.txt, try running as the mythtv user.")
173 builder
= PlaylistBuilder(vars["dbhostname"], vars["dbusername"], vars["dbpassword"], vars["dbname"])
174 for filename
in filenames
:
175 playlistName
= os
.path
.basename(filename
)
176 playlistName
= playlistName
.rsplit(".", 1)[0]
177 logging
.info("Processing '%s' as playlist '%s'" % (filename
, playlistName
))
179 baseDir
= os
.path
.dirname(filename
)
180 builder
.startPlaylist(playlistName
)
181 for line
in stripComments(open(filename
)):
182 line
= re
.sub("^(\.\./)*", "", line
)
183 if not line
.startswith("/"):
184 line
= os
.path
.abspath(os
.path
.join(baseDir
, line
))
185 builder
.addFile(line
)
186 builder
.finishPlaylist()
189 if __name__
== '__main__':