My uncle needed a solution to corrupted music files with the backup copies to replace. It had already saved all defective title in a M3U playlist. The problem of the matter is, however, that the paths of the backups were not exactly predictable. They hide in part in different subfolders. However, all paths end according to the scheme / Artist / Album / title.ext .
why I've developed a script that does just that. The following parameters are passed:
- - input-file The path of the M3U playlist must be passed.
- - import-dir : The path of the root folder of the backup copies must be passed.
- - export-dir : The directory into which the files should be copied, must be passed as well.
- - convert-sep : (Optional) Convert the given character in the path of the separator system.
- - encoding : (optional) Input and output encoding of the M3U files. In most cases, M3U files be latin-1 or utf-8 encoded .
Assuming that the path of a file in the import directory is / Home / stefan / backup / music / artist / album / title.ext , then it is transformed to Artist / Album / title.ext . Now the program takes place in an M3U file the path C: \\ Music \\ Artist \\ Album \\ title.ext . If a parameter - convert-sep '\\' was passed, the path also to Artist / Album / title.ext being transformed. The file / home / stefan / backup / music / artist / album / title.ext now after \u0026lt;export-dir> / Artist / Album copied / title.ext .
is used internally Sqlite3 a database that is created in memory.
It is not much to it, but decayed before the script on my hard drive, it can also have someone else.
# / usr / bin / env python3 # -*- coding: utf-8 -*- # # Copyright (C) 2010 Stefan Haller \u0026lt;haliner@googlemail.com> # # This program is free software: you can redistribute it and / or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import optparse
import os
import os.path
import re
import shutil
import sqlite3
class M3UObject(object):
__slots__ = ( 'path' , 'extinf' )
def __init__ (self, path, extinf):
self.path = path
self.extinf = extinf
def __str__ (self):
if self.extinf is not None:
return "%s\n%s" % (self.extinf, self.path)
else :
return self.path
def __repr__ (self):
return "M3Object(%r, %r)" % (self.path, self.extinf)
class M3UProcessor(object):
__slots__ = ( 'filename' , 'encoding' )
def __init__ (self, filename, encoding):
self.filename = filename
self.encoding = encoding
def process (self):
extm3u = False
extinf = None
f = open(self.filename, 'r' , encoding=self.encoding)
first_line = True
for line in f:
line = line.strip()
if first_line:
first_line = False
if line == '#EXTM3U' :
extm3u = True
continue
if extm3u and line.startswith( '#EXTINF' ):
extinf = line
continue
if line.startswith( '#' ):
continue
yield M3UObject(line, extinf)
extinf = None
f.close()
class Main(object):
__slots__ = ( 'options' , 'args' , 'db' , 'regex' )
def __init__ (self):
self.options = None
self.args = None
self.db = None
self.regex = None
def setup_db (self):
self.db = sqlite3.connect( ':memory:' )
self.db.execute(
"CREATE TABLE import ("
"id INTEGER PRIMARY KEY,"
"path TEXT,"
"transformed TEXT"
")" )
self.db.execute(
"CREATE TABLE input ("
"id INTEGER PRIMARY KEY,"
"path TEXT,"
"extinf TEXT,"
"transformed TEXT"
")" )
self.db.execute(
"CREATE TABLE copies ("
"id INTEGER PRIMARY KEY,"
"input INTEGER,"
"import INTEGER"
")" )
self.db.execute(
"CREATE TABLE output ("
"id INTEGER PRIMARY KEY,"
"input INTEGER"
")" )
def close_db (self):
self.db.close()
def transform_path (self, path, convert_sep = False):
if convert_sep and self.options.convert_sep is not None:
path = path.replace(self.options.convert_sep, os.sep)
if self.regex is None:
self.regex = re.compile( r'(' +
os.sep +
r'[^' +
os.sep +
r']*){3}$' )
match = self.regex.search(path)
if match is not None:
return match.group( 0 )[ 1 :]
else :
return None
def parse_arguments (self):
parser = optparse.OptionParser()
parser.add_option( '--input-file' )
parser.add_option( '--import-dir' )
parser.add_option( '--export-dir' )
parser.add_option( '--convert-sep' )
parser.add_option( '--encoding' )
(self.options,
self.args) = parser.parse_args()
def check_arguments (self):
result = True
for i in ( 'input_file' ,
'import_dir' ,
'export_dir' ):
if getattr(self.options, i) is None:
print ( "Please specify --%s!" % i.replace( '_' , '-' ))
result = False
return result
def scan_import_dir (self):
self.db.execute( "DELETE FROM import" )
for root, dirs, files in os.walk(self.options.import_dir):
for f in files:
path = os.path.join(root, f)
transformed = self.transform_path(path)
if transformed is not None:
self.db.execute(
"INSERT INTO import "
"(path, transformed) "
"VALUES (?, ?)" ,
(path, transformed))
def process_input (self):
self.db.execute( "DELETE FROM input" )
processor = M3UProcessor(self.options.input_file,
self.options.encoding)
for m3u in processor.process():
transformed = self.transform_path(m3u.path, True)
if transformed is not None:
self.db.execute(
"INSERT INTO input "
"(path, extinf, transformed) "
"VALUES (?, ?, ?)" ,
(m3u.path, m3u.extinf, transformed))
def find_copies (self):
self.db.execute( "DELETE FROM copies" )
self.db.execute(
"INSERT INTO copies "
"(input, import) "
"SELECT input.id, import.id "
"FROM input "
"INNER JOIN import ON import.transformed = input.transformed"
)
self.db.execute( "DELETE FROM output" )
self.db.execute(
"INSERT INTO output "
"(input) "
"SELECT input.id "
"FROM input "
"WHERE input.id NOT IN ("
"SELECT copies.input "
"FROM copies"
")" )
def copy (self):
for result in self.db.execute(
"SELECT import.path, input.transformed "
"FROM copies "
"INNER JOIN input ON input.id = copies.input "
"INNER JOIN import ON import.id = copies.import"
):
src = result[ 0 ]
dest = os.path.join(self.options.export_dir,
result[ 1 ])
try :
os.makedirs(os.path.dirname(dest))
except OSError:
pass
shutil.copy(src, dest)
def write_output (self):
f = open(self.options.input_file + '.new' , 'w' ,
encoding=self.options.encoding)
f.write( '#EXTM3U\n' )
for result in self.db.execute(
"SELECT input.path, input.extinf " "FROM output" "INNER JOIN ON input input.id output.input =" ): m3u M3UObject = (result [0 ] result [ a ]) f.write (str (m3u)) f.write ( '\\ n' ) f.close () def run (self): self.setup_db ()
self.parse_arguments()
if not self.check_arguments():
return
self.scan_import_dir()
self.process_input()
self.find_copies()
self.copy()
self.write_output()
self.close_db()
if __name__ == '__main__' :
Main().run()
0 comments:
Post a Comment