aboutsummaryrefslogtreecommitdiffstats
path: root/bin/syncmpdpl
blob: 85cf7230536f935c8d3f2e1371a679033fb7ce8b (plain)
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()