def _loaded(self, task): """Load covers after search for elements has finished. If no search was necessary, *task* is None. """ if task is not None: if not isinstance(task, search.SearchTask): # subclasses might submit over tasks return elids = task.criterion.result if len(elids): filterClause = " AND el.id IN ({})".format(db.csList(elids)) else: self.display().setCovers([], {}) return else: filterClause = " AND el.domain={}".format(self.domain.id) result = db.query(""" SELECT el.id, st.data FROM {p}elements AS el JOIN {p}stickers AS st ON el.id = st.element_id WHERE st.type = 'COVER' {filter} """, filter=filterClause) coverPaths = {id: path for id, path in result} ids = list(coverPaths.keys()) levels.real.collect(ids) if tags.isInDb("artist") and tags.isInDb("date"): sortValues = {} artistTag = tags.get("artist") dateTag = tags.get("date") for id in ids: el = levels.real[id] sortValues[id] = (el.tags[artistTag][0] if artistTag in el.tags else utils.PointAtInfinity(), el.tags[dateTag][0] if dateTag in el.tags else utils.PointAtInfinity()) ids.sort(key=sortValues.get) self.display().setCovers(ids, coverPaths)
def doAction(self): from maestro.gui.tagwidgets import TagValuePropertiesWidget if len(self.tagIds) > 1: tagNames = [tags.get(tagId).name for tagId, valueId in self.tagIds] answer, ok = QtWidgets.QInputDialog.getItem(self.parent(), self.tr("Choose tag mode"), self.tr('Tag:'), tagNames) if not ok: return else: tagName, valueId = self.tagIds[tagNames.index(answer)] else: tagName, valueId = self.tagIds[0] TagValuePropertiesWidget.showDialog(tags.get(tagName), valueId)
def doAction(self): from maestro.gui.tagwidgets import TagValuePropertiesWidget if len(self.tagIds) > 1: tagNames = [tags.get(tagId).name for tagId, valueId in self.tagIds] answer, ok = QtWidgets.QInputDialog.getItem( self.parent(), self.tr("Choose tag mode"), self.tr('Tag:'), tagNames) if not ok: return else: tagName, valueId = self.tagIds[tagNames.index(answer)] else: tagName, valueId = self.tagIds[0] TagValuePropertiesWidget.showDialog(tags.get(tagName), valueId)
def __init__(self, name, type, state): super().__init__(name, type, state) if state is None: state = {} if 'groupTags' in state: self.groupTags = [tags.get(t) for t in state['groupTags']] else: self.groupTags = [tags.get('album')] self.albumTag = self.groupTags[0] if len(self.groupTags) > 0 else None self.directoryMode = state.get('directoryMode', False) self.metaRegex = state.get('metaRegex', self.defaultMetaRegex) try: self.compilationFlag = flags.get(state['compilationFlag']) except (KeyError, ValueError): self.compilationFlag = None
def runTest(self): tagsToRemove = [tags.get(name) for name in ('artist','title','comment')] for tag in tagsToRemove: del self.file.tags[tag] self.file.saveTags() self.file.readTags() self.assertEqual(self.file.tags,{k:v for k,v in ORIGINAL_TAGS.items() if k not in tagsToRemove})
def tagDefinitionAction(self, s, loc, toks): macro = toks["tag"] ret = toks.copy() ret["exists"] = False ret["value"] = None if "levelDef" in toks: level = toks["levelDef"][0] if level > len(self.currentParents): ret["value"] = "" return ret pos,parent = self.currentParents[-level] if macro == "#": ret["value"] = self.positionFormat.format(pos) else: elemTag = parent.tags else: elemTag = self.currentElem.tags if macro == "#": if len(self.currentParents) > 0: ret["value"] = self.positionFormat.format(self.currentParents[0][0]) if macro != "#": if tags.isInDb(macro.lower()): tag = tags.get(macro.lower()) if tag in elemTag: ret["value"] = ",".join(map(str, elemTag[tag])).translate(self.translation) ret["exists"] = (ret["value"] != None) if ret["value"] is None: ret["value"] = "" return ret
def fromString(self, value): """Read a value from the string *value* and store it in this option. The format of *value* depends on the type of the option, see ''export''.""" if self.type == 'string': return value elif self.type == 'int': try: return int(value) except ValueError: logging.warning( __name__, "Invalid int in delegate configuration in storage file.") return None elif self.type == 'bool': return value == 'True' elif self.type == 'tag': if tags.isInDb(value): return tags.get(value) elif self.type == 'datapiece': if value == 'none': return None else: try: return DataPiece.fromString(value) except ValueError: logging.warning( __name__, "Invalid datapiece in delegate configuration in storage file." ) return None
def value(tagSpec, valueId): """Return the value from the tag *tagSpec* with id *valueId* or raise an EmptyResultException if that id does not exist. Date tags will be returned as FlexiDate. """ tag = tagsModule.get(tagSpec) # Check cache if tag in _idToValue: value = _idToValue[tag].get(valueId) if value is not None: return value # Look up value values = list(getValues(tag, "id={}".format(valueId))) if len(values) > 0: value = values[0] else: raise KeyError("There is no value of tag '{}' for id {}".format( tag, valueId)) # Store value in cache if tag.type != tagsModule.TYPE_TEXT: if tag not in _idToValue: _idToValue[tag] = {} _idToValue[tag][id] = value return value
def fromString(self,value): """Read a value from the string *value* and store it in this option. The format of *value* depends on the type of the option, see ''export''.""" if self.type == 'string': return value elif self.type == 'int': try: return int(value) except ValueError: logging.warning(__name__, "Invalid int in delegate configuration in storage file.") return None elif self.type == 'bool': return value == 'True' elif self.type == 'tag': if tags.isInDb(value): return tags.get(value) elif self.type == 'datapiece': if value == 'none': return None else: try: return DataPiece.fromString(value) except ValueError: logging.warning(__name__, "Invalid datapiece in delegate configuration in storage file.") return None
def __init__(self, newtags): super().__init__() self.columns = [self.tr("Import"), self.tr("MusicBrainz Name"), self.tr("Maestro Tag")] self.setColumnCount(len(self.columns)) self.verticalHeader().hide() self.setHorizontalHeaderLabels(self.columns) self.horizontalHeader().setSectionResizeMode(QtWidgets.QHeaderView.ResizeToContents) self.setRowCount(len(newtags)) self.tagMapping = mbplugin.tagMap.copy() from ...gui.tagwidgets import TagTypeBox for row, tagname in enumerate(newtags): if tagname in self.tagMapping: tag = self.tagMapping[tagname] else: tag = tags.get(tagname) checkbox = QtWidgets.QTableWidgetItem() ttBox = TagTypeBox(tag, editable=True) ttBox.tagChanged.connect(self._handleTagTypeChanged) mbname = QtWidgets.QTableWidgetItem(tagname) self.setCellWidget(row, 2, ttBox) if tag is None: checkbox.setCheckState(Qt.Unchecked) ttBox.setEnabled(False) mbname.setFlags(mbname.flags() ^ Qt.ItemIsEnabled) else: checkbox.setCheckState(Qt.Checked) self.tagMapping[tagname] = tag mbname.setFlags(Qt.ItemIsEnabled) self.setItem(row, 0, checkbox) self.setItem(row, 1, mbname) self.cellChanged.connect(self._handleCellChange)
def tagDefinitionAction(self, s, loc, toks): macro = toks["tag"] ret = toks.copy() ret["exists"] = False ret["value"] = None if "levelDef" in toks: level = toks["levelDef"][0] if level > len(self.currentParents): ret["value"] = "" return ret pos, parent = self.currentParents[-level] if macro == "#": ret["value"] = self.positionFormat.format(pos) else: elemTag = parent.tags else: elemTag = self.currentElem.tags if macro == "#": if len(self.currentParents) > 0: ret["value"] = self.positionFormat.format( self.currentParents[0][0]) if macro != "#": if tags.isInDb(macro.lower()): tag = tags.get(macro.lower()) if tag in elemTag: ret["value"] = ",".join(map(str, elemTag[tag])).translate( self.translation) ret["exists"] = (ret["value"] != None) if ret["value"] is None: ret["value"] = "" return ret
def __init__(self, tagList=None, state=None): if tagList is None: assert state is not None tagList = [tags.get(name) for name in state] if any(tag.type != tags.TYPE_VARCHAR for tag in tagList): logging.warning(__name__, "Only tags of type varchar are permitted in the browser's layers.") tagList = {tag for tag in tagList if tag.type == tags.TYPE_VARCHAR} self.tagList = tagList
def sortFunction(wrapper): """Intelligent sort: sort albums by their date, everything else by name.""" element = wrapper.element date = 0 if element.isContainer() and element.type == elements.ContainerType.Album: dateTag = tags.get("date") if dateTag.type == tags.TYPE_DATE and dateTag in element.tags: date = -element.tags[dateTag][0].toSql() # minus leads to descending sort return (date, element.getTitle(neverShowIds=True))
def updateCharts(self): """Create or update the charts.""" innerWidget = QtWidgets.QWidget() self.innerLayout = QtWidgets.QGridLayout(innerWidget) if tags.get("genre").isInDb() and tags.get( "genre").type == tags.TYPE_VARCHAR: sizes, labels = self._filter(self.getGenres()) self._addPie(self.tr("Genres"), 0, 0, sizes, labels, 4, 3) sizes, labels = self._filter(self.getFormats()) self._addPie(self.tr("Formats"), 0, 1, sizes, labels, 3.2, 3) sizes, labels = self._filter(self.getContainerTypes()) self._addPie(self.tr("Container types"), 1, 1, sizes, labels, 3.2, 3) heights, labels = zip(*self.getDates()) self._addBars(self.tr("Dates"), 1, 0, heights, labels, 4, 4) self.scrollArea.setWidget(innerWidget)
def defaultTagList(): """Return the default list of tags in a TagLayer.""" tagList = [ tags.get(name) for name in ('artist', 'composer', 'performer') ] return [ tag for tag in tagList if tag.isInDb() and tag.type == tags.TYPE_VARCHAR ]
def getIdsAndValues(tagSpec, whereClause='1', *args, **kwargs): tag = tagsModule.get(tagSpec) result = db.query( "SELECT id, value FROM {} WHERE tag_id = ? AND {}".format( tag.type.table, whereClause), tag.id, *args, **kwargs) if tag.type != tagsModule.TYPE_DATE: return (tuple(row) for row in result) else: return ((id, utils.FlexiDate.fromSql(date)) for id, date in result)
def getStorage(elid): """Return a tags.Storage object filled with the tags of the element with the given id.""" result = db.query( "SELECT tag_id, value_id FROM {p}tags WHERE element_id = ?", elid) storage = tagsModule.Storage() for tagId, valueId in result: tag = tagsModule.get(tagId) storage.add(tag, value(tag, valueId)) return storage
def getValues(tagSpec, whereClause='1', *args, **kwargs): tag = tagsModule.get(tagSpec) result = db.query( "SELECT value FROM {} WHERE tag_id = ? AND {}".format( tag.type.table, whereClause), tag.id, *args, **kwargs) if tag.type != tagsModule.TYPE_DATE: return result.getSingleColumn() else: return (utils.FlexiDate.fromSql(date) for date in result.getSingleColumn())
def __init__(self, discId, trackCount, level): super().__init__(mainwindow.mainWindow) self.setModal(True) self.level = level self.discid = discId topLayout = QtWidgets.QHBoxLayout() topLayout.addWidget(QtWidgets.QLabel(self.tr('Album title:'))) self.titleEdit = tagwidgets.TagValueEditor(tags.TITLE) self.titleEdit.setValue('unknown album') topLayout.addWidget(self.titleEdit) midLayout = QtWidgets.QHBoxLayout() midLayout.addWidget(QtWidgets.QLabel(self.tr('Artist:'))) self.artistEdit = tagwidgets.TagValueEditor(tags.get('artist')) self.artistEdit.setValue('unknown artist') midLayout.addWidget(self.artistEdit) midLayout.addStretch() midLayout.addWidget(QtWidgets.QLabel(self.tr('Date:'))) self.dateEdit = tagwidgets.TagValueEditor(tags.get('date')) self.dateEdit.setValue(utils.FlexiDate(1900)) midLayout.addWidget(self.dateEdit) layout = QtWidgets.QVBoxLayout() description = QtWidgets.QLabel(self.tr('The MusicBrainz database does not contain a release ' 'for this disc. Please fill the tags manually.')) description.setWordWrap(True) layout.addWidget(description) layout.addLayout(topLayout) layout.addLayout(midLayout) tableLayout = QtWidgets.QGridLayout() edits = [] for i in range(1, trackCount+1): tableLayout.addWidget(QtWidgets.QLabel(self.tr('Track {:2d}:').format(i)), i-1, 0) edits.append(tagwidgets.TagValueEditor(tags.TITLE)) edits[-1].setValue('unknown title') tableLayout.addWidget(edits[-1], i-1, 1) layout.addLayout(tableLayout) box = QDialogButtonBox(QDialogButtonBox.Ok | QDialogButtonBox.Cancel) box.accepted.connect(self.finish) box.rejected.connect(self.reject) layout.addWidget(box) self.setLayout(layout) self.edits = edits
def __init__(self, tagList=None, state=None): if tagList is None: assert state is not None tagList = [tags.get(name) for name in state] if any(tag.type != tags.TYPE_VARCHAR for tag in tagList): logging.warning( __name__, "Only tags of type varchar are permitted in the browser's layers." ) tagList = {tag for tag in tagList if tag.type == tags.TYPE_VARCHAR} self.tagList = tagList
def fromString(string): """Construct a datapiece from a string. The string must either be one of the special datapiece identifiers (e.g. 'length') or something like 't:artist' to construct a datapiece containing a tag. """ if string.startswith('t:'): return DataPiece(tags.get(string[2:], True)) else: for dataPiece in availableDataPieces(): if dataPiece.tag is None and dataPiece.data == string: return dataPiece raise ValueError("'{}' is not a valid datapiece export.".format(string))
def sortFunction(wrapper): """Intelligent sort: sort albums by their date, everything else by name.""" element = wrapper.element date = 0 if element.isContainer( ) and element.type == elements.ContainerType.Album: dateTag = tags.get("date") if dateTag.type == tags.TYPE_DATE and dateTag in element.tags: date = -element.tags[dateTag][0].toSql( ) # minus leads to descending sort return (date, element.getTitle(neverShowIds=True))
def fromString(string): """Construct a datapiece from a string. The string must either be one of the special datapiece identifiers (e.g. 'length') or something like 't:artist' to construct a datapiece containing a tag. """ if string.startswith('t:'): return DataPiece(tags.get(string[2:], True)) else: for dataPiece in availableDataPieces(): if dataPiece.tag is None and dataPiece.data == string: return dataPiece raise ValueError( "'{}' is not a valid datapiece export.".format(string))
def init(): BrowserDelegate.profileType = profiles.createProfileType( name='browser', title=translate('Delegates', 'Browser'), leftData=['t:composer', 't:artist', 't:performer'], rightData=['t:date', 't:conductor'], overwrite={'fitInTitleRowData': profiles.DataPiece(tags.get('date')) if tags.isInDb('date') else None}, addOptions=[ profiles.DelegateOption("showSortValues", translate("Delegates", "Display sort values instead of real values"),"bool",False) ] )
def sortValue(tagSpec, valueId, valueIfNone=False): """Returns the sort value for the given tag value, or None if it is not set. If *valueIfNone=True*, the value itself is returned if no sort value is set.""" tag = tagsModule.get(tagSpec) value, sortValue = db.query( "SELECT value, sort_value FROM {} WHERE tag_id = ? AND id = ?".format( tag.type.table), tag.id, valueId).getSingleRow() if sortValue is not None: return sortValue elif valueIfNone: return value else: return None
def finish(self): elems = [] for i, edit in enumerate(self.edits, start=1): url = urls.URL("audiocd://{0}.{1}{2}/{0}/{1}.flac".format( self.discid, i, os.path.abspath(config.options.audiocd.rippath))) elem = self.level.collect(url) elTags = tags.Storage() elTags[tags.TITLE] = [edit.getValue()] elTags[tags.ALBUM] = [self.titleEdit.getValue()] elTags[tags.get('artist')] = [self.artistEdit.getValue()] elTags[tags.get('date')] = [self.dateEdit.getValue()] diff = tags.TagStorageDifference(None, elTags) self.level.changeTags({elem: diff}) elems.append(elem) contTags = tags.Storage() contTags[tags.TITLE] = [self.titleEdit.getValue()] contTags[tags.ALBUM] = [self.titleEdit.getValue()] contTags[tags.get('date')] = [self.dateEdit.getValue()] contTags[tags.get('artist')] = [self.artistEdit.getValue()] cont = self.level.createContainer(contents=elems, type=ContainerType.Album, domain=domains.default(), tags=contTags) self.container = cont self.accept()
def asMaestroTags(self, mapping=None): """Convert the MBTagStorage to an maestro.tags.Storage object. *mapping* may be a dict mapping strings to Maestro tag types. """ ret = tags.Storage() for key, values in self.items(): if mapping and key in mapping: if mapping[key] is None: continue else: tag = mapping[key] else: tag = tags.get(key) ret[tag] = [tag.convertValue(str(v)) for v in values] # converts AliasEntities to strings return ret
def readTags(self): """Load the tags from disk using pytaglib. Special tags (tracknumber, compilation, discnumber) are stored in the "specialTags" attribute. """ self.tags = tags.Storage() self.specialTags = collections.OrderedDict() try: self._taglibFile = taglib.File(self.url.path) except OSError: if self.url.extension in config.options.main.audio_extensions: logging.warning( __name__, 'TagLib failed to open "{}". Tags will be stored in database only' .format(self.url.path)) return self.length = self._taglibFile.length autoProcessingDone = False for key, values in self._taglibFile.tags.items(): key = key.lower() if key in self.specialTagNames: self.specialTags[key] = values elif key in config.options.tags.auto_delete: autoProcessingDone = True continue elif key in autoReplaceTags: autoProcessingDone = True key = autoReplaceTags[key] elif tags.isValidTagName(key): tag = tags.get(key) validValues = [] for string in values: try: validValues.append(tag.convertValue(string, crop=True)) except tags.TagValueError: logging.error( __name__, "Invalid value for tag '{}' found: {}".format( tag.name, string)) if len(validValues) > 0: self.tags.add(tag, *validValues) else: logging.error( __name__, "Invalid tag name '{}' found : {}".format(key, self.url)) if autoProcessingDone: self.saveTags()
def check(self,values,redo=True): result = db.query("SELECT tagtype,title,icon,private,sort FROM {}tagids WHERE tagname='testtag'" .format(db.prefix)) if values is not None: dbValues = [v if not db.isNull(v) else None for v in result.getSingleRow()] dbValues[3] = bool(dbValues[3]) # private dbValues = tuple(dbValues) self.assertEqual(dbValues,values) tag = tags.get("testtag") self.assertEqual(tag.type.name,values[0]) self.assertEqual(tag.rawTitle,values[1]) self.assertEqual(tag.iconPath,values[2]) self.assertEqual(tag.private,values[3]) self.assertEqual(tags.tagList.index(tag),values[4]) else: self.assertRaises(db.sql.EmptyResultException,result.getSingleRow)
def asMaestroTags(self, mapping=None): """Convert the MBTagStorage to an maestro.tags.Storage object. *mapping* may be a dict mapping strings to Maestro tag types. """ ret = tags.Storage() for key, values in self.items(): if mapping and key in mapping: if mapping[key] is None: continue else: tag = mapping[key] else: tag = tags.get(key) ret[tag] = [tag.convertValue(str(v)) for v in values] # converts AliasEntities to strings return ret
def getGenres(self): """Return sizes and labels for each wedge in the genre chart. The percentage of a wedge is its size divided by the sum of all sizes times 100.""" tag = tags.get("genre") if not tag.isInDb() or tag.type != tags.TYPE_VARCHAR: return [] result = db.query( """ SELECT COUNT(*) AS count, v.value FROM {p}tags AS t JOIN {p}elements AS el ON t.element_id = el.id JOIN {p}values_varchar AS v ON t.tag_id=v.tag_id AND t.value_id = v.id WHERE el.file = 1 AND t.tag_id=? GROUP BY value_id ORDER BY count DESC """, tag.id) return list(result)
def id(tagSpec, value, insert=False): """Return the id of the given value in the tag-table of tag *tagSpec*. If the value does not exist, raise an EmptyResultException, unless the optional parameter *insert* is set to True. In that case insert the value into the table and return its id. """ tag = tagsModule.get(tagSpec) # Check cache if tag in _valueToId: id = _valueToId[tag].get(value) if id is not None: return id # Look up id if tag.type in (tagsModule.TYPE_VARCHAR, tagsModule.TYPE_TEXT) and type == 'mysql': whereClause = "value COLLATE utf8_bin = ?" else: whereClause = "value = ?" args = [tag.sqlFormat(value)] ids = list(getIdsAndValues(tag, whereClause, *args)) if len(ids) > 0: id = ids[0][0] elif insert: if tag.type == tagsModule.TYPE_VARCHAR: columns = 'tag_id, value, search_value' args = [tag.id, tag.sqlFormat(value), _makeSearchValue(value)] else: columns = 'tag_id, value' args = [tag.id, tag.sqlFormat(value)] result = db.query( "INSERT INTO {} ({}) VALUES ({})".format( tag.type.table, columns, ','.join(['?'] * len(args))), *args) id = result.insertId() else: raise KeyError("No value id for tag '{}' and value '{}'".format( tag, value)) # Store id in cache if tag.type != tagsModule.TYPE_TEXT: if tag not in _valueToId: _valueToId[tag] = {} _valueToId[tag][value] = id return id
def check(self, values, redo=True): result = db.query( "SELECT tagtype,title,icon,private,sort FROM {}tagids WHERE tagname='testtag'" .format(db.prefix)) if values is not None: dbValues = [ v if not db.isNull(v) else None for v in result.getSingleRow() ] dbValues[3] = bool(dbValues[3]) # private dbValues = tuple(dbValues) self.assertEqual(dbValues, values) tag = tags.get("testtag") self.assertEqual(tag.type.name, values[0]) self.assertEqual(tag.rawTitle, values[1]) self.assertEqual(tag.iconPath, values[2]) self.assertEqual(tag.private, values[3]) self.assertEqual(tags.tagList.index(tag), values[4]) else: self.assertRaises(db.sql.EmptyResultException, result.getSingleRow)
def readTags(self): """Load the tags from disk using pytaglib. Special tags (tracknumber, compilation, discnumber) are stored in the "specialTags" attribute. """ self.tags = tags.Storage() self.specialTags = collections.OrderedDict() try: self._taglibFile = taglib.File(self.url.path) except OSError: if self.url.extension in config.options.main.audio_extensions: logging.warning(__name__, 'TagLib failed to open "{}". Tags will be stored in database only' .format(self.url.path)) return self.length = self._taglibFile.length autoProcessingDone = False for key, values in self._taglibFile.tags.items(): key = key.lower() if key in self.specialTagNames: self.specialTags[key] = values elif key in config.options.tags.auto_delete: autoProcessingDone = True continue elif key in autoReplaceTags: autoProcessingDone = True key = autoReplaceTags[key] elif tags.isValidTagName(key): tag = tags.get(key) validValues = [] for string in values: try: validValues.append(tag.convertValue(string, crop=True)) except tags.TagValueError: logging.error(__name__, "Invalid value for tag '{}' found: {}".format(tag.name, string)) if len(validValues) > 0: self.tags.add(tag, *validValues) else: logging.error(__name__, "Invalid tag name '{}' found : {}".format(key, self.url)) if autoProcessingDone: self.saveTags()
def getInfo(self, path): """Query MPD to get tags & length of the file at *path* (relative to this MPD instance). Since MPD connection is delegated to a subthread, this method might be slow. """ with self.getClient() as client: info = client.listallinfo(path)[0] storage = tags.Storage() length = None for key, values in info.items(): if key in ("file", "last-modified", "track"): # mpd delivers these but they aren't keys continue if key == "time": length = int(values) continue tag = tags.get(key) if not isinstance(values, list): values = [ values ] storage[tag] = [ tag.convertValue(value, crop=True) for value in values ] return storage, length
import unittest, shutil, os from maestro import application, utils, filebackends, database as db from maestro.core import tags PATH_BASE = os.path.join(os.getcwd(),os.path.dirname(__file__)) PATH_EMPTY = os.path.join(PATH_BASE,'realfiles/empty') PATH_FULL = os.path.join(PATH_BASE,'realfiles/full') PATH_INVALID = os.path.join(PATH_BASE,'realfiles/invalid') PATH_TEST = os.path.join(PATH_BASE,'realfiles/test') ORIGINAL_TAGS = { tags.get("artist"): ["Martin","Michael"], tags.get("title"): ['The "äöü#~♀" Song'], tags.get("album"): ["Dullness"], tags.get("date"): [utils.FlexiDate(2010),utils.FlexiDate(2000,12,24)], tags.get("genre"): ["Dull","Gangsta"], tags.get("comment"): ["äöü#~♀","..."] } TAGS_TO_WRITE = { tags.get("artist"): ["You","Know","Who"], tags.get("date"): [utils.FlexiDate(1900,12,24)], tags.get("comment"): ["Stupid ümläütß"], tags.get("conductor"): ["Absolutely","Nobody"] } ORIGINAL_POSITION = 1 def getFile(path): file = filebackends.getFile('file://'+path)
def toolTipText(self): from maestro.core import tags return '{} ({})'.format(' or '.join(self.getValues()), '/'.join(tags.get(id).title for (id,_) in self.tagIds))
def runTest(self): self.file = getFile(self.test) tag = tags.get('artist') self.assertEqual(list(self.file.tags.keys()),[tag]) self.assertTrue(len(self.file.tags[tag]) == 1) self.assertTrue(tag.isValid(self.file.tags[tag][0]))
def runTest(self): self.file.tags[tags.get('artist')] = ['Someone','Everyone'] self.file.saveTags() self.file.readTags() self.assertEqual(self.file.tags,{tags.get('artist'): ['Someone','Everyone']})
def readTags(self): self.tags = tags.Storage() artist,title = self.url.parsedUrl.path[1:].split(' - ') # skip leading / self.tags.add(tags.TITLE, title) self.tags.add(tags.get('artist'), artist)
def loadFromDb(self, idList, level=None): """Load elements specified by *idList* from the database into *level* which defaults to the real level.""" if level is None: level = self if len(idList) == 0: # queries will fail otherwise return [] csIdList = db.csList(idList) # bare elements result = db.query(""" SELECT el.domain, el.id, el.file, el.type, f.url, f.length FROM {0}elements AS el LEFT JOIN {0}files AS f ON el.id = f.element_id WHERE el.id IN ({1}) """.format(db.prefix, csIdList)) for domainId, id, file, elementType, url, length in result: _dbIds.add(id) if file: level.elements[id] = elements.File( domains.domainById(domainId), level, id, url=URL(url), length=length, type=elements.ContainerType(elementType)) else: level.elements[id] = elements.Container( domains.domainById(domainId), level, id, type=elements.ContainerType(elementType)) # contents result = db.query(""" SELECT el.id, c.position, c.element_id FROM {0}elements AS el JOIN {0}contents AS c ON el.id = c.container_id WHERE el.id IN ({1}) ORDER BY position """.format(db.prefix, csIdList)) for id, pos, contentId in result: level.elements[id].contents.insert(pos, contentId) # parents result = db.query(""" SELECT el.id, c.container_id FROM {0}elements AS el JOIN {0}contents AS c ON el.id = c.element_id WHERE el.id IN ({1}) """.format(db.prefix, csIdList)) for id, contentId in result: level.elements[id].parents.append(contentId) # tags result = db.query(""" SELECT el.id, t.tag_id, t.value_id FROM {0}elements AS el JOIN {0}tags AS t ON el.id = t.element_id WHERE el.id IN ({1}) """.format(db.prefix, csIdList)) for id, tagId, valueId in result: tag = tags.get(tagId) level.elements[id].tags.add(tag, db.tags.value(tag, valueId)) # flags result = db.query(""" SELECT el.id, f.flag_id FROM {0}elements AS el JOIN {0}flags AS f ON el.id = f.element_id WHERE el.id IN ({1}) """.format(db.prefix, csIdList)) for id, flagId in result: level.elements[id].flags.append(flags.get(flagId)) # stickers result = db.query(""" SELECT element_id, type, data FROM {}stickers WHERE element_id IN ({}) ORDER BY element_id, type, sort """.format(db.prefix, csIdList)) # This is a bit complicated because the stickers should be stored in tuples, not lists # Changing the lists would break undo/redo #TODO: is this really necessary? current = None buffer = [] for (id, type, sticker) in result: if current is None: current = (id, type) elif current != (id, type): level.elements[current[0]].stickers[current[1]] = tuple(buffer) current = (id, type) buffer = [] element = level.elements[id] if element.stickers is None: element.stickers = {} buffer.append(sticker) if current is not None: level.elements[current[0]].stickers[current[1]] = tuple(buffer) try: return [self.elements[id] for id in idList] except KeyError: # probably some ids were not contained in the database raise levels.ElementGetError( self, [id for id in idList if id not in self])
def defaultTagList(): """Return the default list of tags in a TagLayer.""" tagList = [tags.get(name) for name in ('artist', 'composer', 'performer')] return [tag for tag in tagList if tag.isInDb() and tag.type == tags.TYPE_VARCHAR]
def loadFromDb(self, idList, level=None): """Load elements specified by *idList* from the database into *level* which defaults to the real level.""" if level is None: level = self if len(idList) == 0: # queries will fail otherwise return [] csIdList = db.csList(idList) # bare elements result = db.query(""" SELECT el.domain, el.id, el.file, el.type, f.url, f.length FROM {0}elements AS el LEFT JOIN {0}files AS f ON el.id = f.element_id WHERE el.id IN ({1}) """.format(db.prefix, csIdList)) for domainId, id, file, elementType, url, length in result: _dbIds.add(id) if file: level.elements[id] = elements.File(domains.domainById(domainId), level, id, url=URL(url), length=length, type=elements.ContainerType(elementType)) else: level.elements[id] = elements.Container(domains.domainById(domainId), level, id, type=elements.ContainerType(elementType)) # contents result = db.query(""" SELECT el.id, c.position, c.element_id FROM {0}elements AS el JOIN {0}contents AS c ON el.id = c.container_id WHERE el.id IN ({1}) ORDER BY position """.format(db.prefix, csIdList)) for id, pos, contentId in result: level.elements[id].contents.insert(pos, contentId) # parents result = db.query(""" SELECT el.id, c.container_id FROM {0}elements AS el JOIN {0}contents AS c ON el.id = c.element_id WHERE el.id IN ({1}) """.format(db.prefix, csIdList)) for id, contentId in result: level.elements[id].parents.append(contentId) # tags result = db.query(""" SELECT el.id, t.tag_id, t.value_id FROM {0}elements AS el JOIN {0}tags AS t ON el.id = t.element_id WHERE el.id IN ({1}) """.format(db.prefix, csIdList)) for id, tagId, valueId in result: tag = tags.get(tagId) level.elements[id].tags.add(tag, db.tags.value(tag, valueId)) # flags result = db.query(""" SELECT el.id, f.flag_id FROM {0}elements AS el JOIN {0}flags AS f ON el.id = f.element_id WHERE el.id IN ({1}) """.format(db.prefix, csIdList)) for id, flagId in result: level.elements[id].flags.append(flags.get(flagId)) # stickers result = db.query(""" SELECT element_id, type, data FROM {}stickers WHERE element_id IN ({}) ORDER BY element_id, type, sort """.format(db.prefix, csIdList)) # This is a bit complicated because the stickers should be stored in tuples, not lists # Changing the lists would break undo/redo #TODO: is this really necessary? current = None buffer = [] for (id, type, sticker) in result: if current is None: current = (id, type) elif current != (id, type): level.elements[current[0]].stickers[current[1]] = tuple(buffer) current = (id, type) buffer = [] element = level.elements[id] if element.stickers is None: element.stickers = {} buffer.append(sticker) if current is not None: level.elements[current[0]].stickers[current[1]] = tuple(buffer) try: return [self.elements[id] for id in idList] except KeyError: # probably some ids were not contained in the database raise levels.ElementGetError(self, [id for id in idList if id not in self])
def _guessHelper(self, files): files = list(files) domain = files[0].domain byKey = OrderedDict() existingParents = [] pureDirMode = self.directoryMode and len(self.groupTags) == 0 for element in files: self.orders[element] = self.currentOrder self.currentOrder += 1 if len(element.parents) > 0: # there are already parents -> use the first one if element.parents[0] not in existingParents: existingParents.append(element.parents[0]) else: if pureDirMode: key = os.path.dirname(element.url.path) else: key = tuple( (tuple(element.tags[tag]) if tag in element.tags else None) for tag in self.groupTags) if key not in byKey: byKey[key] = [] byKey[key].append(element) existing = self.level.collect(existingParents) for elem in existing: self.orders[elem] = self.currentOrder self.currentOrder += 1 self.albums.extend(existing) self.toplevels.update(existing) for key, elements in byKey.items(): flags = set() if self.compilationFlag is not None: for elem in elements: if hasattr(elem, "specialTags") and "compilation" in elem.specialTags \ and elem.specialTags["compilation"][0] not in ("0", ""): flags.add(self.compilationFlag) if pureDirMode or (self.albumTag in elements[0].tags): def position(elem): if hasattr(elem, "specialTags") and "tracknumber" in elem.specialTags: return utils.parsePosition(elem.specialTags["tracknumber"][0]) return None elementsWithoutPos = { e for e in elements if position(e) is None } elementsWithPos = sorted(set(elements) - elementsWithoutPos, key = lambda e: position(e)) children = {} for element in elementsWithPos: if position(element) in children: from maestro.gui.dialogs import warning warning(self.tr("Error guessing albums"), self.tr("position {} appears twice in {}").format(position(element), key)) self.level.removeElements([element]) else: children[position(element)] = element.id firstFreePosition = position(elementsWithPos[-1])+1 if len(elementsWithPos) > 0 else 1 for i, element in enumerate(elementsWithoutPos, start=firstFreePosition): children[i] = element.id albumTags = tags.findCommonTags(elements) albumTags[tags.TITLE] = [key] if pureDirMode else elements[0].tags[self.albumTag] cType = ContainerType.Work if tags.get('composer') in albumTags else ContainerType.Album container = self.level.createContainer(domain=domain, tags=albumTags, flags=list(flags), type=cType, contents=ContentList.fromPairs(children.items())) self.orders[container] = self.orders[elements[0]] self.albums.append(container) self.toplevels.add(container) else: self.toplevels.update(elements)
def readTags(self): self.tags = tags.Storage() artist, title = self.url.parsedUrl.path[1:].split( ' - ') # skip leading / self.tags.add(tags.TITLE, title) self.tags.add(tags.get('artist'), artist)
def isHidden(tagSpec, valueId): """Returns True iff the given tag value is set hidden.""" tag = tagsModule.get(tagSpec) return db.query( "SELECT hide FROM {} WHERE tag_id = ? AND id = ?".format( tag.type.table), tag.id, valueId).getSingle()