Привет всем меломанам и остальным welinux'оидам, которые сейчас читают этот пост.
Наверняка не только я один столкнулся с проблемой вызванной необходимостью обновлять музыку у себя на телефоне/плеере.
Под катом я раскажу как я решил эту проблему для себя.
С одной стороны, какая тут проблема? Подключил устройство, удалил немного старых файлов, записал немного новых и все дела.
С другой стороны, если вы слушаете музыку так же как я (всю и в разброс), то зачастую возникает проблема выбора, что удалять что записать и т.д.
В таких ситуациях когда практически одинаково хочешь и того и другого и нельзя выбрать оба сразу (в виду ограниченного размера диска) проще положиться на random.
Хоть я и люблю слушать музыку, но обновлением ее на плеере вручную не всегда охота заниматься, поэтому я написал небольшой скриптик делающий это за меня.
Опции которые принимает скрипт это каталог содержащий всю коллекцию вашей музыки и каталог с музыкой на вашем медиа устройстве.
Так же можно передать параметры:
-f FILLING_PERCENT - процент заполнения диска. Т.е. скрипт никогда не наполнит диск музыкой больше определенного процента. Это полезно если вы ходите иметь некоторый процент доступного пространства для других файлов.
Значение по-умолчанию 80.
-r REFRESHING_PERCENT - процент обновления для старых файлов на устройстве, т.е. сколько музыки (в процентах) будет удалено из устройства перед записью. Значение по-умолчанию 30.
-s, --follow-simlinks - позволяет переходить по ссылкам при поиске музыки. (Отдельное спасибо
mrpot за идею)
Т.к. местами скрипт использует *nix специфичные команды, запускаться он будет только в linux среде, но любой желающий может поменять это поведение.
Пример использования:
|
python update_music.py -r 30 -f 50 /home/user/Music /media/My_MP3player/Media/Music
|
А вот и сам скрипт.
Версия для python 2.6.x-2.7.x update_mysic2.py
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 210
|
#!/usr/bin/env python # -*- coding: utf-8 -*- # # Created with hope to be useful. import subprocess import shutil import os if os.sys.platform != "linux2": print "This code for linux platform only!" exit() class Song: import os def __init__(self, path, collection=None): self.file_name = os.path.basename(path) self.collection = collection self.size = 0 self.full_path = path self.folder = None self.type = None self.size = os.path.getsize(self.full_path) if self.collection: path = self.full_path.split(self.collection)[1].lstrip(os.path.sep) self.folder = os.path.dirname(path) else: self.folder = os.path.dirname(self.full_path) self.type = os.path.splitext(self.file_name)[-1].lstrip('.') class MusicCollection: import os def __init__(self, path, followlinks=False): self.path = path self.Songs = [] self.CollectionSize=0 self.allowed_types = ['mp3', 'wma', 'ogg'] self.follow = followlinks self.scan() def scan(self): # we need reinit collection info if we run this outside __init__ self.CollectionSize = 0 self.Songs = [] for root, dirs, files in os.walk(self.path, followlinks=self.follow): for file in files: songPath = os.path.join(root, file) s = Song(songPath, self.path) if s.type in self.allowed_types: self.Songs.append(s) self.CollectionSize += s.size def add_song(self, song): self.Songs.append(song) self.CollectionSize += song.size def del_song(self, song): self.Songs.remove(song) self.CollectionSize -= song.size def getMount(path): path = os.path.abspath(path) while path != os.path.sep: if os.path.ismount(path): return path path = os.path.abspath(os.path.join(path, os.pardir)) return path def delEmptyDir(path): for root, dirs, files in os.walk(path): if not files and not dirs: # we actually don't need to remove portable music directory if root != path: #print 'Deleting empty:', root os.removedirs(root) elif not files: for dir in dirs: delEmptyDir(os.path.join(root, dir)) def deleteFile(path): d = os.remove(path) def copyFile(path, dest): c = shutil.copy2(path, dest) def main(*args): mountPoint = getMount(portableCollection) if mountPoint == getMount(fullCollection) or mountPoint == '/': print 'Your portable device not mounted! ' \ 'So please mount it and try again.' exit() # checking folders to exist for folder in [fullCollection, portableCollection]: if not os.path.isdir(folder): print "Can't find folder %s" % folder exit() # disk free check df = subprocess.Popen(['df'], stdout=subprocess.PIPE).communicate()[0].strip() for line in df.decode("utf-8").splitlines(): if mountPoint in line: totalSpace=int(line.split()[1])*1024 freeSpace=int(line.split()[3])*1024 spaceToUse=int(freeSpace - (totalSpace -(totalSpace * fillingPercent/100))) if refreshingPercent != 0: # collect files in portableCollection print "Scanning your portable collection... This may take a while" pc = MusicCollection(portableCollection, followSimlinks) filesToDelete=int(len(pc.Songs) * refreshingPercent/100) # deleting some files to refresh music on portable device for i in list(range(filesToDelete)): song = random.choice(pc.Songs) pc.del_song(song) space = spaceToUse/1024./1024. print "Avaliable Space:[%3.1f Mb] Deleting: %s" % (space, song.file_name) deleteFile(song.full_path) spaceToUse += song.size # to clean empty dirs in collection print "Deleting empty directories" delEmptyDir(portableCollection) if spaceToUse < 0: return # collect files in fullCollection print "Scanning your music collection... This may take a while" fc = MusicCollection(fullCollection, followSimlinks) # coping random files to disk while fc.Songs: song = random.choice(fc.Songs) fc.Songs.remove(song) spaceToUse -= song.size if spaceToUse < 0: spaceToUse += song.size break try: os.makedirs(os.path.join(portableCollection, song.folder)) except OSError: pass #print "Copying:", os.path.join(song.folder, song.file_name) space = spaceToUse/1024./1024. print "Avaliable Space:[%3.1f Mb] Copying: %s" % (space, song.file_name) copyFile(song.full_path, os.path.join(portableCollection, song.folder)) if __name__ == "__main__": try: import random import sys import optparse usage = '''run this script with two options: /directory/with/your/music /directory/with/music/on/your/portable/device or use -h to get more help''' parser = optparse.OptionParser(usage=usage) parser.add_option("-f", "--filling-percent", help="percent of disk filling", action="store", default='80') parser.add_option("-r", "--refreshing-percent", help="percent of refreshing music", action="store", default='30') parser.add_option("-s", "--follow-simlinks", help="follow simlinks while scaning", action="store_true", default=False) (opts, args) = parser.parse_args(sys.argv[1:]) if len(args) != 2: raise ValueError('Not all directories was specified') notIntegerMsg = '%s must be integer' bigMsg = '%s can\'t be more than 100' if opts.filling_percent: if not opts.filling_percent.isdigit(): raise Exception(notIntegerMsg % 'Filling percent') elif int(opts.filling_percent) > 100: raise Exception(bigMsg % 'Filling percent') fillingPercent = int(opts.filling_percent) if opts.refreshing_percent: if not opts.refreshing_percent.isdigit(): raise Exception(notIntegerMsg % 'Refreshing percent') elif int(opts.refreshing_percent) > 100: raise Exception(bigMsg % 'Refreshing percent') refreshingPercent = int(opts.refreshing_percent) fullCollection = os.path.abspath(args[0]) portableCollection = os.path.abspath(args[1]) followSimlinks = opts.follow_simlinks main(fullCollection, portableCollection, fillingPercent, refreshingPercent, followSimlinks) print 'Done!' except KeyboardInterrupt: print '\nCtl+C detected... Terminated.' exit() except (ValueError, Exception) as er: parser.error(er) exit()
|
Версия для python 3.x update_music.py
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 210
|
#!/usr/bin/env python # -*- coding: utf-8 -*- # # Created with hope to be useful. import subprocess import shutil import os if os.sys.platform != "linux2": print("This code for linux platform only!") exit() class Song: import os def __init__(self, path, collection=None): self.file_name = os.path.basename(path) self.collection = collection self.size = 0 self.full_path = path self.folder = None self.type = None self.size = os.path.getsize(self.full_path) if self.collection: path = self.full_path.split(self.collection)[1].lstrip(os.path.sep) self.folder = os.path.dirname(path) else: self.folder = os.path.dirname(self.full_path) self.type = os.path.splitext(self.file_name)[-1].lstrip('.') class MusicCollection: import os def __init__(self, path, followlinks=False): self.path = path self.Songs = [] self.CollectionSize=0 self.allowed_types = ['mp3', 'wma', 'ogg'] self.follow = followlinks self.scan() def scan(self): # we need reinit collection info if we run this outside __init__ self.CollectionSize = 0 self.Songs = [] for root, dirs, files in os.walk(self.path, followlinks=self.follow): for file in files: songPath = os.path.join(root, file) s = Song(songPath, self.path) if s.type in self.allowed_types: self.Songs.append(s) self.CollectionSize += s.size def add_song(self, song): self.Songs.append(song) self.CollectionSize += song.size def del_song(self, song): self.Songs.remove(song) self.CollectionSize -= song.size def getMount(path): path = os.path.abspath(path) while path != os.path.sep: if os.path.ismount(path): return path path = os.path.abspath(os.path.join(path, os.pardir)) return path def delEmptyDir(path): for root, dirs, files in os.walk(path): if not files and not dirs: # we actually don't need to remove portable music directory if root != path: #print('Deleting empty:', root) os.removedirs(root) elif not files: for dir in dirs: delEmptyDir(os.path.join(root, dir)) def deleteFile(path): d = os.remove(path) def copyFile(path, dest): c = shutil.copy2(path, dest) def main(*args): mountPoint = getMount(portableCollection) if mountPoint == getMount(fullCollection) or mountPoint == '/': print('Your portable device not mounted! ' 'So please mount it and try again.') exit() # checking folders to exist for folder in [fullCollection, portableCollection]: if not os.path.isdir(folder): print("Can't find folder %s" % folder) exit() # disk free check df = subprocess.Popen(['df'], stdout=subprocess.PIPE).communicate()[0].strip() for line in df.decode("utf-8").splitlines(): if mountPoint in line: totalSpace=int(line.split()[1])*1024 freeSpace=int(line.split()[3])*1024 spaceToUse=int(freeSpace - (totalSpace -(totalSpace * fillingPercent/100))) if refreshingPercent != 0: # collect files in portableCollection print("Scanning your portable collection... This may take a while") pc = MusicCollection(portableCollection, followSimlinks) filesToDelete=int(len(pc.Songs) * refreshingPercent/100) # deleting some files to refresh music on portable device for i in list(range(filesToDelete)): song = random.choice(pc.Songs) pc.del_song(song) space = spaceToUse/1024./1024. print("Avaliable Space:[%3.1f Mb] Deleting: %s" % (space, song.file_name)) deleteFile(song.full_path) spaceToUse += song.size # to clean empty dirs in collection print("Deleting empty directories") delEmptyDir(portableCollection) if spaceToUse < 0: return # collect files in fullCollection print("Scanning your music collection... This may take a while") fc = MusicCollection(fullCollection, followSimlinks) # coping random files to disk while fc.Songs: song = random.choice(fc.Songs) fc.Songs.remove(song) spaceToUse -= song.size if spaceToUse < 0: spaceToUse += song.size break try: os.makedirs(os.path.join(portableCollection, song.folder)) except OSError: pass #print("Copying:", os.path.join(song.folder, song.file_name)) space = spaceToUse/1024./1024. print("Avaliable Space:[%3.1f Mb] Copying: %s" % (space, song.file_name)) copyFile(song.full_path, os.path.join(portableCollection, song.folder)) if __name__ == "__main__": try: import random import sys import optparse usage = '''run this script with two options: /directory/with/your/music /directory/with/music/on/your/portable/device or use -h to get more help''' parser = optparse.OptionParser(usage=usage) parser.add_option("-f", "--filling-percent", help="percent of disk filling", action="store", default='80') parser.add_option("-r", "--refreshing-percent", help="percent of refreshing music", action="store", default='30') parser.add_option("-s", "--follow-simlinks", help="follow simlinks while scaning", action="store_true", default=False) (opts, args) = parser.parse_args(sys.argv[1:]) if len(args) != 2: raise ValueError('Not all directories was specified') notIntegerMsg = '%s must be integer' bigMsg = '%s can\'t be more than 100' if opts.filling_percent: if not opts.filling_percent.isdigit(): raise Exception(notIntegerMsg % 'Filling percent') elif int(opts.filling_percent) > 100: raise Exception(bigMsg % 'Filling percent') fillingPercent = int(opts.filling_percent) if opts.refreshing_percent: if not opts.refreshing_percent.isdigit(): raise Exception(notIntegerMsg % 'Refreshing percent') elif int(opts.refreshing_percent) > 100: raise Exception(bigMsg % 'Refreshing percent') refreshingPercent = int(opts.refreshing_percent) fullCollection = os.path.abspath(args[0]) portableCollection = os.path.abspath(args[1]) followSimlinks = opts.follow_simlinks main(fullCollection, portableCollection, fillingPercent, refreshingPercent, followSimlinks) print('Done!') except KeyboardInterrupt: print('\nCtl+C detected... Terminated.') exit() except (ValueError, Exception) as er: parser.error(er) exit()
|
P.S. Надеюсь кому-нибудь будет полезен так же как и мне. :)
P.P.S. Скрипт немного обновлен, добавлены некоторые проверки, переписаны функции копирования и удаления (спасибо
K-9)