/usr/share/pyshared/musiclibrarian/musicfile.py is in musiclibrarian 1.6-2.2.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 | # musicfile.py
# Copyright 2004 Daniel Burrows
#
# Core classes for storing information about a music file.
import listenable
class MusicFileError(Exception):
def __init__(self, strerror):
self.strerror=strerror
def __str__(self):
return self.strerror
# The set of known file extensions and associated classes. It is
# assumed that None is not included in this dictionary.
file_types = { }
def register_file_type(ext, cls):
"""Add a new file type. 'cls' is a class or other callable object
which takes a single argument (the name of the file to load) and
returns a file instance."""
file_types[ext]=cls
class File(listenable.Listenable):
"""This is the most generic abstraction of a file.
Actual files must implement additional methods like get_tags,
set_tags, etc."""
def __init__(self, store):
listenable.Listenable.__init__(self)
self.store=store
# A file with a dict interface. This is an abstract class; subclasses
# need to implement the write_to_file() and get_file() methods and
# initialize "comments" in their constructor. It is ASSUMED that the
# keys in "comments" are upper-case.
#
# write_to_file() is responsible for actually writing out the current
# data to the file. get_file() returns a "file-like" object:
# specifically, one with a read() method. This method will return a
# tuple (data,amt) when called.
class DictFile(File):
"""This class represents a file whose attributes 'look' like a
dictionary. Most files will fall into this category. All keys of
the dictionary are assumed to be upper-case.
Subclasses are responsible for implementing write_to_file()."""
def __init__(self, store, comments):
"""Set this instance up with the given initial store pointer and
comment dictionary."""
File.__init__(self, store)
self.comments=comments
self.__rationalize_comments()
self.backup_comments=self.comments.copy()
self.modified=0
def __rationalize_comments(self):
"""Purge empty comments from the comments dictionary."""
for key,val in self.comments.items():
if val == []:
del self.comments[key]
def get_tag(self, key):
"""Returns a list of zero or more strings associated with the
given case-insensitive key."""
return self.comments.get(key.upper(), [])
def set_tags(self, dict):
"""Sets all tags of this object to the values stored in the
given dictionary."""
if self.comments <> dict:
oldcomments=self.comments
self.comments=dict.copy()
self.__rationalize_comments()
self.modified = (self.comments <> self.backup_comments)
self.store.set_modified(self, self.modified)
self.call_listeners(self, oldcomments)
def set_tag(self, key, val):
"""Sets a single tag to the given value."""
if self.get_tag(key) <> val:
upkey=key.upper()
# used to handle updating structures in the GUI:
oldcomments=self.comments.copy()
if val==[]:
del self.comments[upkey]
else:
self.comments[upkey]=val
# careful here. (could just always compare dictionaries,
# but this is a little more efficient in some common
# cases)
if (not self.modified):
# if it wasn't modified, we can just compare the new value
# to the old value.
self.modified=(val <> self.backup_comments.get(upkey, []))
else:
# it was modified; if this key is now the same as its
# original value, compare the whole dictionary. (no
# way around this right now) Note that if it isn't the
# same, you might as well just leave it modified.
if val == self.backup_comments.get(upkey, []):
self.modified = (self.comments <> self.backup_comments)
self.store.set_modified(self, self.modified)
self.call_listeners(self, oldcomments)
def get_cache(self):
"""Returns a dictionary whose members are the comments attached to this file."""
return self.backup_comments.copy()
def tags(self):
"""Returns a list of the tags of this file."""
return self.comments.keys()
def values(self):
"""Returns a list of the values associated with tags of this file."""
return self.comments.values()
def items(self):
"""Returns a list of pairs (tag,value) representing the tags of this file."""
return self.comments.items()
def set_tag_first(self, key, val):
"""Sets only the first entry of the given tag, leaving the
rest of the entries (if there are any) unmodified."""
cur=self.get_tag(key)
if cur==[]:
if val <> None:
self.set_tag(key, [val])
elif val==None:
new=list(cur)
del new[0]
self.set_tag(key, new)
elif cur[0] <> val:
new=list(cur)
new[0]=val
self.set_tag(key, new)
def commit(self):
"""Commit any changes to the backing file."""
if self.modified:
self.write_to_file()
self.modified=0
self.store.set_modified(self, False)
self.call_listeners(self, self.comments)
self.backup_comments=self.comments.copy()
def revert(self):
"""Revert any modified comments to their original values."""
if self.modified:
# no copy, we aren't modifying them.
oldcomments=self.comments
self.comments=self.backup_comments.copy()
self.modified=0
self.store.set_modified(self, False)
self.call_listeners(self, oldcomments)
|