Exemplo n.º 1
0
class _TagsView(_Widget):
  ''' A view of some `Tag`s.
  '''

  def __init__(self, parent, *, get_tag_widget=None, **kw):
    super().__init__(parent, **kw)
    # the working TagSet, distinct from those supplied
    self.tags = TagSet()
    # a function to get Tag suggestions from a Tag name
    self.get_suggested_tag_values = lambda tag_name: ()
    # a reference TagSet of background Tags
    self.bg_tags = TagSet()
    if get_tag_widget is None:
      get_tag_widget = TagWidget
    self.get_tag_widget = get_tag_widget

  def set_tags(self, tags, get_suggested_tag_values=None, bg_tags=None):
    ''' Update `self.tags` to match `tags`.
        Optionally set `self.get_suggested_tag_values`
        if `get_suggested_tag_values` is not `None`.
        Optionally set `self.bg_tags` if `bg_tags` is not `None`.
    '''
    self.tags.clear()
    self.tags.update(tags)
    if get_suggested_tag_values is not None:
      self.get_suggested_tag_values = get_suggested_tag_values
    if bg_tags is not None:
      self.bg_tags = bg_tags
Exemplo n.º 2
0
 def __init__(self, parent, *, get_tag_widget=None, **kw):
   super().__init__(parent, **kw)
   # the working TagSet, distinct from those supplied
   self.tags = TagSet()
   # a function to get Tag suggestions from a Tag name
   self.get_suggested_tag_values = lambda tag_name: ()
   # a reference TagSet of background Tags
   self.bg_tags = TagSet()
   if get_tag_widget is None:
     get_tag_widget = TagWidget
   self.get_tag_widget = get_tag_widget
Exemplo n.º 3
0
 def cmd_ls(self, argv):
     ''' Usage: {cmd} [-l]
       List the contents of the Calibre library.
 '''
     long = False
     if argv and argv[0] == '-l':
         long = True
         argv.pop(0)
     if argv:
         raise GetoptError("extra arguments: %r" % (argv, ))
     options = self.options
     calibre = options.calibre
     for book in calibre:
         with Pfx("%d:%s", book.id, book.title):
             print(f"{book.title} ({book.dbid})")
             if long:
                 print(" ", book.path)
                 identifiers = book.identifiers_as_dict()
                 if identifiers:
                     print("   ", TagSet(identifiers))
                 for fmt, subpath in book.formats_as_dict().items():
                     with Pfx(fmt):
                         fspath = calibre.pathto(subpath)
                         size = pfx_call(os.stat, fspath).st_size
                         print("   ", fmt, transcribe_bytes_geek(size),
                               subpath)
Exemplo n.º 4
0
 def __init__(self, *, get_tag_widget=None, **kw):
   if get_tag_widget is None:
     get_tag_widget = lambda tag: TagWidget(tag)
   self._get_tag_widget = get_tag_widget
   super().__init__(
       size=(800, 400), layout=self.layout_tags(TagSet(a=1, b=2)), **kw
   )
Exemplo n.º 5
0
 def apply_defaults(self):
     ''' Set default options:
     * empty `.categories`, a `set`
     * empty `.tags`, a `TagSet`
     * `.when` = `time.time()`
 '''
     self.options.categories = set()
     self.options.dbpath = expanduser(DEFAULT_DBPATH)
     self.options.logpath = expanduser(DEFAULT_LOGPATH)
     self.options.tags = TagSet()
     self.options.when = time.time()
Exemplo n.º 6
0
class _TagsView(_Widget):

  @typechecked
  def __init__(self, *a, **kw):
    self.tags = TagSet()
    super().__init__(*a, **kw)

  def set_tags(self, tags):
    ''' Set the tags.
    '''
    self.tags.clear()
    self.tags.update(tags)

  def _row_of_tags(self):
    ''' Make a row of tag widgets.
    '''
    X("TagsView_Frame: TAGS=%s", self.tags)
    ##row = [self._get_tag_widget(tag) for tag in self.tags]
    row = [sg.Text(str(tag)) for tag in self.tags]
    X("ROW OF TAGS: %r", row)
    return row
Exemplo n.º 7
0
 def auto_name(self, srcpath, dstdirpath, tags):
     ''' Generate a filename computed from `srcpath`, `dstdirpath` and `tags`.
 '''
     tagged = self.fstags[dstdirpath]
     formats = self.conf_tag(tagged.merged_tags(), 'auto_name', ())
     if isinstance(formats, str):
         formats = [formats]
     if formats:
         if not isinstance(tags, TagSet):
             tags = TagSet()
             for tag in tags:
                 tags.add(tag)
         for fmt in formats:
             with Pfx(repr(fmt)):
                 try:
                     formatted = pfx_call(tags.format_as, fmt, strict=True)
                     if formatted.endswith('/'):
                         formatted += basename(srcpath)
                     return formatted
                 except FormatAsError:
                     ##warning("%s", e)
                     ##print("auto_name(%r): %r: %s", srcpath, fmt, e)
                     continue
     return basename(srcpath)
Exemplo n.º 8
0
 def __init__(self, tags: TagSet, tag_name: str, alt_values=None):
   if alt_values is None:
     alt_values = set()
   self.tags = tags
   self.tag_name = tag_name
   self.alt_values = alt_values
   layout = [
       [
           sg.Text(tag_name + ("" if tag_name in tags else " (missing)")),
           sg.Combo(
               list(self.alt_values),
               default_value=tags.get(tag_name),
           )
       ]
   ]
   super().__init__(layout=layout)
Exemplo n.º 9
0
 def fspath(self, new_fspath):
   ''' Switch the preview to look at a new filesystem path.
   '''
   print("SET fspath =", repr(new_fspath))
   self._fspath = new_fspath
   self._tag_widgets = {}
   self.config(text=shortpath(new_fspath) or "NONE")
   self.preview.fspath = new_fspath
   tagged = self.tagged
   all_tags = TagSet(tagged.merged_tags())
   suggested_tags = self.suggested_tags
   for sg_name in suggested_tags.keys():
     if sg_name not in all_tags:
       all_tags[sg_name] = None
   self.tagsview.set_tags(
       tagged, lambda tag: suggested_tags.get(tag.name), bg_tags=all_tags
   )
   print("tag suggestions =", repr(self.suggested_tags))
Exemplo n.º 10
0
 def tagset(self):
     ''' Return a `TagSet` with the ID3 tag information.
 '''
     return TagSet(
         {
             k: v
             for k, v in dict(
                 title=self.title,
                 artist=self.artist,
                 album=self.album,
                 year=self.year,
                 comment=self.comment,
                 track=self.track,
                 genre_id=self.genre_id,
             ).items()
             if v is not None and not (isinstance(v, int) and v == 0)
         },
         _ontology=self.ONTOLOGY,
     )
Exemplo n.º 11
0
 def tagset(self):
     ''' Return a `TagSet` with the ID3 tag information.
 '''
     tags = TagSet(
         _ontology=self.ONTOLOGY,
         v1=self.v1,
         v2=self.v2,
         version=f'{self.v1}.{self.v2}',
     )
     for tag_frame in self.tag_frames:
         tag_id = tag_frame.tag_id.decode('ascii').lower()
         tags.set(tag_id, tag_frame.datafrome_body.value)
         if tag_frame.flags != 0:
             tags.set(f"{tag_id}.flags", tag_frame.flags)
     return tags
Exemplo n.º 12
0
 def set_tags(self, tags, get_suggested_tag_values=None, bg_tags=None):
   old_tags = list(self.tags)
   super().set_tags(
       tags,
       get_suggested_tag_values=get_suggested_tag_values,
       bg_tags=bg_tags
   )
   display_tags = TagSet(self.tags)
   if bg_tags:
     # fill in background tags if not present
     for tag_name, tag_value in bg_tags.items():
       if tag_name not in tags:
         display_tags[tag_name] = tag_value
   # remove tags no longer named
   for tag in old_tags:
     if tag.name not in display_tags:
       self._del_tag(tag.name)
   # redo the displayed tags
   # TODO: update the widgets directly instead
   for tag in display_tags:
     alt_values = self.get_suggested_tag_values(tag)
     w = self.tag_widget(tag, alt_values=alt_values)
     self._add_tag(tag.name, w)
     w.grid(sticky=tk.W)
Exemplo n.º 13
0
def tags_of(bfr):
    ''' Scan `bfr` containing MP3 data.
      Return a `TagSet` containing the tags found in an mp3 buffer.

      The returned `Tag`s have the prefix `'id3v1.'` for ID3v1 tags
      and `'id3v2'` for ID3v2 tags.
  '''
    tags = TagSet()
    for frame in MP3Frame.scan(bfr):
        frame_type = type(frame)
        if issubclass(frame_type, (ID3V1Frame, ID3V2Frame)):
            tags.update(frame.tagset(),
                        prefix={
                            ID3V1Frame: 'id3v1',
                            ID3V2Frame: 'id3v2'
                        }[frame_type])
        elif issubclass(frame_type, MP3AudioFrame):
            tags.update(dict(bitrate_kbps=frame.bitrate_kbps), prefix='audio')
        else:
            warning("unhandled tag type %s", frame_type.__name__)
    return tags
Exemplo n.º 14
0
def rip(
    device,
    mbdb,
    *,
    output_dirpath,
    disc_id=None,
    fstags=None,
    no_action=False
):
  ''' Pull audio from `device` and save in `output_dirpath`.
  '''
  if disc_id is None:
    dev_info = discid.read(device=device)
    disc_id = dev_info.id
  if fstags is None:
    fstags = FSTags()
  with Pfx("MB: discid %s", disc_id, print=True):
    disc = mbdb.discs[disc_id]
  level1 = ", ".join(disc.artist_names).replace(os.sep, '_') or "NO_ARTISTS"
  level2 = disc.title or "UNTITLED"
  if disc.medium_count > 1:
    level2 += f" ({disc.medium_position} of {disc.medium_count})"
  subdir = joinpath(output_dirpath, level1, level2)
  if not isdirpath(subdir):
    with Pfx("makedirs(%r)", subdir, print=True):
      os.makedirs(subdir)
  fstags[subdir].update(
      TagSet(discid=disc.id, title=disc.title, artists=disc.artist_names)
  )
  for tracknum, recording_id in enumerate(disc.recordings, 1):
    recording = disc.ontology.metadata('recording', recording_id)
    track_fstags = TagSet(
        discid=disc.mbkey,
        artists=recording.artist_names,
        title=recording.title,
        track=tracknum
    )
    track_artists = ", ".join(recording.artist_names)
    track_base = f"{tracknum:02} - {recording.title} -- {track_artists}".replace(
        os.sep, '-'
    )
    wav_filename = joinpath(subdir, track_base + '.wav')
    mp3_filename = joinpath(subdir, track_base + '.mp3')
    if existspath(mp3_filename):
      warning("MP3 file already exists, skipping track: %r", mp3_filename)
    else:
      with NamedTemporaryFile(dir=subdir,
                              prefix=f"cdparanoia--track{tracknum}--",
                              suffix='.wav') as T:
        if existspath(wav_filename):
          info("using existing WAV file: %r", wav_filename)
        else:
          argv = ['cdparanoia', '-d', '1', '-w', str(tracknum), T.name]
          if no_action:
            print(*argv)
          else:
            with Pfx("+ %r", argv, print=True):
              subprocess.run(argv, stdin=subprocess.DEVNULL, check=True)
            with Pfx("%r => %r", T.name, wav_filename, print=True):
              os.link(T.name, wav_filename)
      if no_action:
        print("fstags[%r].update(%s)" % (wav_filename, track_fstags))
      else:
        fstags[wav_filename].update(track_fstags)
        fstags[wav_filename].rip_command = argv
      argv = [
          'lame',
          '-q',
          '7',
          '-V',
          '0',
          '--tt',
          recording.title or "UNTITLED",
          '--ta',
          track_artists or "NO ARTISTS",
          '--tl',
          level2,
          ## '--ty',recording year
          '--tn',
          str(tracknum),
          ## '--tg', recording genre
          ## '--ti', album cover filename
          wav_filename,
          mp3_filename
      ]
      if no_action:
        print(*argv)
      else:
        with Pfx("+ %r", argv, print=True):
          subprocess.run(argv, stdin=subprocess.DEVNULL, check=True)
      fstags[mp3_filename].conversion_command = argv
    if no_action:
      print("fstags[%r].update(%s)" % (mp3_filename, track_fstags))
    else:
      fstags[mp3_filename].update(track_fstags)
  if not no_action:
    subprocess.run(['ls', '-la', subdir])
    os.system("eject")
Exemplo n.º 15
0
 def __init__(self, *a, **kw):
   self.tags = TagSet()
   super().__init__(*a, **kw)
Exemplo n.º 16
0
 def conf_tags(cls, tags: TagSet):
     ''' The `Tagger` related subtags from `tags`, a `TagSet`.
 '''
     return tags.subtags(cls.TAG_PREFIX)
Exemplo n.º 17
0
def dlog(
    headline: str,
    *,
    logpath: Optional[str] = None,
    sqltags: Optional[SQLTags] = None,
    tags=None,
    categories: Optional[Iterable] = None,
    when: Union[None, int, float, datetime] = None,
):
    ''' Log `headline` to the dlog.

      Parameters:
      * `headline`: the log line message
      * `logpath`: optional text log pathname,
        default `{DEFAULT_LOGPATH}` from DEFAULT_LOGPATH
      * `sqltags`: optional `SQLTags` instance,
        default uses `{DEFAULT_DBPATH}` from DEFAULT_DBPATH
      * `tags`: optional iterable of `Tag`s to associate with the log entry
      * `categories`: optional iterable of category strings
      * `when`: optional UNIX time or `datetime`, default now
  '''
    if sqltags is None:
        # pylint: disable=redefined-argument-from-local
        with SQLTags(expanduser(DEFAULT_DBPATH)) as sqltags:
            dlog(headline,
                 logpath=logpath,
                 sqltags=sqltags,
                 tags=tags,
                 categories=categories,
                 when=when)
    if logpath is None:
        logpath = expanduser(DEFAULT_LOGPATH)
    logtags = TagSet()
    if tags:
        for tag in tags:
            logtags.add(tag)
    categories = sorted((
    ) if categories is None else set(map(str.lower, categories)))
    if when is None:
        when = time.time()
    elif isinstance(when, (int, float)):
        pass
    elif isinstance(when, datetime):
        dt = when
        if dt.tzinfo is None:
            # create a nonnaive datetime in the local zone
            dt = dt.astimezone()
        when = datetime2unixtime(dt)
    else:
        raise TypeError("when=%s:%r: unhandled type" %
                        (type(when).__name__, when))
    tt = time.localtime(when)
    print_args = [
        '{:4d}-{:02d}-{:02d} {:02d}:{:02d}:{:02d}'.format(
            tt.tm_year,
            tt.tm_mon,
            tt.tm_mday,
            tt.tm_hour,
            tt.tm_min,
            tt.tm_sec,
        )
    ]
    if categories:
        print_args.append(','.join(categories).upper() + ':')
    print_args.append(headline)
    if logtags:
        print_args.append('[' + ' '.join(map(str, logtags)) + ']')
    with pfx_call(open, logpath, 'a') as logf:
        print(*print_args, file=logf)
    # add the headline and categories to the tags
    logtags.add('headline', headline)
    if categories:
        logtags.add('categories', sorted(categories))
    sqltags.default_factory(None, unixtime=when, tags=logtags)