1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
#! /usr/bin/env python2
# -*- coding: utf-8 -*-
#
# script to sync a mpd playlist to a mass storage audio player
#
# needs
# - python-sqlite2
# - eyed3
# - python-argparser
# - python-mpd
import argparse
import os
import sys
import hashlib
import subprocess
#from time import time
import time
from mpd import (MPDClient, CommandError)
import shutil
# set encoding to utf-8
reload(sys)
sys.setdefaultencoding( "utf-8" )
config_dir = '/home/konni/.config/pmsync'
music = '/net/media/music'
playlist = 'sync'
mpdhost = 'innocence'
destination = '/run/media/konni/ville/music'
verbose = False
def _recode(string):
string = string.replace('"','')
# byte2utf8
# string = unicode( string, "utf-8", errors="ignore" )
# utf2byte
string = string.encode('utf-8')
return string
def main():
global verbose, playlist, mpdhost, destination
parser = argparse.ArgumentParser(prog='pmsync')
parser.add_argument('--version', action='version', version='%(prog)s 0.1')
parser.add_argument('-v', '--verbose', action='store_true', dest='verbose', help='verbose mode', default=False)
parser.add_argument('action', metavar='sync | list | import')
parser.add_argument('-m', dest='host', metavar='mpdhost', action='store', help='mpd host', default=mpdhost)
parser.add_argument('-p', dest='pl', metavar='playlist', action='store', help='playlist to use', default=playlist)
parser.add_argument('-d', dest='dest', metavar='destination', action='store', help='destination', default=destination)
parser.add_argument('-e', dest='exact', action='store_true', help='exact checking (slow)', default=False)
args = parser.parse_args()
if args.verbose:
verbose = args.verbose
print args
## action sync
if args.action == 'sync':
print '>> syncing \"{0}/{1}\"to \"{2}\"'.format(args.host, args.pl, args.dest)
_sync(args.host, args.pl, args.dest, args.exact)
## action list
elif args.action == 'list':
print '>> listing from \"{0}/{1}\"'.format(args.host, args.pl)
_list(args.host, args.pl)
## action import
elif args.action == 'import':
print '>> importing from \"{0}\" to playlist \"{1}/{2}\"'.format(args.dest, args.host, args.pl)
port='6600'
CON_ID = {'host':args.host, 'port':port}
client = MPDClient()
mpdConnect(client, CON_ID)
playlistinfo = client.listplaylistinfo(playlist)
for dirname, dirnames, filenames in os.walk(args.dest):
for filename in filenames:
destfile = os.path.join(dirname, filename)
path=destfile.replace(args.dest+"/","")
found = False
for item in playlistinfo:
if item['file'] == path:
found = True
if not found:
print '[x] {0}'.format(path)
client.add(path)
client.rm(args.pl)
client.save(args.pl)
## show help if no action called
else:
parser.print_help()
def _sync(host, pl, dest, exact):
_checkdir(dest)
port='6600'
CON_ID = {'host':host, 'port':port}
client = MPDClient()
mpdConnect(client, CON_ID)
playlistinfo = client.listplaylistinfo(pl)
## copy files
print '> copy files'
for item in playlistinfo:
path = item['file']
srcfile=os.path.join(music, path)
destfile=os.path.join(dest, path)
destdir=os.path.dirname(destfile)
if not os.path.isdir(destdir):
print '[^] {0}'.format(destdir)
os.makedirs(destdir)
if not os.path.isfile(destfile):
shutil.copyfile(srcfile,destfile)
print '[+] {0}'.format(path)
else:
## hash based on md5sum or just file size
if exact:
srchash = hashlib.md5(open(srcfile, 'rb').read()).hexdigest()
desthash = hashlib.md5(open(destfile, 'rb').read()).hexdigest()
else:
srchash = os.path.getsize(srcfile)
desthash = os.path.getsize(destfile)
if srchash != desthash:
shutil.copyfile(srcfile,destfile)
print '[o] {0}'.format(path)
else:
if verbose:
print '[ ] {0}'.format(path)
print 'done'
## clean files
print '> clean files and directories'
for dirname, dirnames, filenames in os.walk(dest):
for filename in filenames:
destfile = os.path.join(dirname, filename)
path=destfile.replace(dest+"/","")
found = False
for item in playlistinfo:
if item['file'] == path:
found = True;
if not found:
try:
os.remove(destfile)
print '[-] {0}'.format(path)
except OSError:
print 'error removing {0}'.format(destfile)
## clean directories
for dirname, dirnames, filenames in os.walk(dest,topdown=False):
if os.listdir(dirname) == [] and dirname != dest:
try:
os.rmdir(dirname)
print '[v] {0}'.format(destdir)
except OSError:
print 'error removing {0} '.format(dirname)
print 'done'
print '> writing filesystem changes'
subprocess.call("/bin/sync")
def _list(host, pl):
port='6600'
CON_ID = {'host':host, 'port':port}
client = MPDClient()
mpdConnect(client, CON_ID)
playlistinfo = client.listplaylistinfo(pl)
## output
print '> copy files'
prev_album = ''
for item in playlistinfo:
artist = item['artist']
album = item['album']
if album != prev_album:
print '{0}/{1}'.format(artist, album)
prev_album = album
def _output(artist, album, track, title, symbol='', extra=''):
output_format = '{symbol:4}{artist:20.20} :: {album:20.20} :: {track:2d} - {title:25.25} {extra}'
print output_format.format(symbol=symbol, artist=artist, album=album, track=track, title=title, extra=extra)
def mpdConnect(client, con_id):
"""
Simple wrapper to connect MPD.
"""
try:
client.connect(**con_id)
except SocketError:
return False
return True
def _checkdir(dir):
if not os.path.isdir(dir):
print "destination not mounted"
sys.exit(1)
if __name__ == '__main__':
main()
|