Commits

Markus Kuppe committed c168a66

Read tracks from a given cue sheet file and add as CHAPs to mp3 file

  • Participants
  • Parent commits 8c9bcd5

Comments (0)

Files changed (2)

File src/eyed3/cue/cue.py

+# -*- coding: utf-8 -*-
+################################################################################
+#  Copyright (C) 2013  Markus Alexander Kuppe <eyeD3@lemmster.de>
+#
+#  This program is free software; you can redistribute it and/or modify
+#  it under the terms of the GNU General Public License as published by
+#  the Free Software Foundation; either version 2 of the License, or
+#  (at your option) any later version.
+#
+#  This program is distributed in the hope that it will be useful,
+#  but WITHOUT ANY WARRANTY; without even the implied warranty of
+#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#  GNU General Public License for more details.
+#
+#  You should have received a copy of the GNU General Public License
+#  along with this program; if not, write to the Free Software
+#  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+#
+################################################################################
+
+import re
+from eyed3 import LOCAL_ENCODING
+from eyed3.id3.frames import ChapterFrame
+
+class CueTrackData(object):
+   
+    def __init__(self,
+                 index_time   = None,
+                 title        = None):
+
+        # convert 12:43:22 (min:sec:microsec) into milliseconds
+        times = index_time.split(':')
+        self.index_time = int(times[0]) * 60 * 1000
+        self.index_time += int(times[1]) * 1000
+        self.index_time += int(times[2]) * 100
+        
+        self.title        = unicode(title, LOCAL_ENCODING)
+        
+    def get(self):
+        return (self.index_time, ChapterFrame.NO_OFFSET, self.title)
+
+class CueData(object):
+    INDEX_RE_STR = r'''(?P<const_track>TRACK)               # the TRACK statement
+                       (?P<whitespace0>\s*)                 # whitespace  
+                       (?P<track_number>\d{2})              # track number
+                       (?P<whitespace1>\s*)                 # whitespace  
+                       (?P<const_audio>AUDIO)               # the AUDIO statement
+                       (?P<whitespace2>\s*)                 # whitespace
+                       (?P<const_title>TITLE)               # the TITLE statement
+                       (?P<whitespace3>\s*)                 # whitespace
+                       (?P<opening1>\")                     # "
+                       (?P<title>.*)                        # the title
+                       (?P<closing1>\")                     # "
+                       (?P<whitespace6>\s*)                 # whitespace 
+                       (?P<const_index>INDEX)               # the INDEX statement       
+                       (?P<whitespace7>\s*)                 # whitespace  
+                       (?P<index_number>\d{2})              # index number
+                       (?P<whitespace8>\s*)                 # whitespace 
+                       (?P<index_time>\d{2}:\d{2}:\d{2})    # index time
+                       '''
+    INDEX_RE = re.compile(INDEX_RE_STR, re.VERBOSE)
+
+    def __init__(self, cue_str):
+
+        self.cue_str = cue_str
+        self.tracks = []
+
+    def parse(self):
+
+        res = CueData.INDEX_RE.finditer(self.cue_str)
+        for item in res:
+            data = CueTrackData(
+                    title        = item.group('title'),
+                    index_time   = item.group('index_time')
+                    )
+            self.tracks.append(data)
+        
+        return self.tracks
+    

File src/eyed3/plugins/classic.py

 import os, stat, exceptions, re
 from eyed3 import LOCAL_ENCODING
 from eyed3.plugins import LoaderPlugin
-from eyed3 import core, id3, mp3, utils
+from eyed3 import core, id3, mp3, utils, cue
 from eyed3.utils.cli import (printMsg, printError, printWarning, boldText,
                              getColor, RESET, HEADER_COLOR)
 from eyed3.id3.frames import ImageFrame, StartEndTuple
+from eyed3.cue.cue import CueData
 
 import logging
 log = logging.getLogger(__name__)
             # title is optional
             title = vals[2] if len(vals) > 2 else u""
             return (int(start), int(end), title)
+        
+        def CueArg(arg):
+            arg = unicode(arg, LOCAL_ENCODING)
+            try:
+                with open(arg, "rb") as fp:
+                    return fp.read()
+            except:
+                raise ValueError("Unable to read file")
 
         def LyricsArg(arg):
             text, desc, lang = CommentArg(arg)
         gid3.add_argument("--add-chapter", action="append", dest="chapters",
                           metavar="START[:END[:TITLE]]", default=[],
                           type=ChapterArg, help=ARGS_HELP["--add-chapter"])
+        gid3.add_argument("--add-chapter-from-cue", action="store", dest="cue",
+                          metavar="CUEFILE", default=None,
+                          type=CueArg, help=ARGS_HELP["--add-chapter-from-cue"])
 
         gid3.add_argument("--add-lyrics", action="append", type=LyricsArg,
                           dest="lyrics", default=[],
                 accessor.set(text, desc, lang)
                 retval = True
 
+        # --add-chapter-from cue (flattens cuedata into chapters if set)
+        if self.args.cue is not None:
+            cues = CueData(self.args.cue).parse()
+            for track in cues:
+                self.args.chapters.append(track.get())
+        
         # --add-chapter
         chCnt = 1
         for start, end, title in self.args.chapters:
           "Start and end times is milliseconds offset from start. "
           "End as well as TITLE are optional. If TITLE should be "
           "given without END, use 4294967295 for END",
+        "--add-chapter-from-cue":
+          "Adds all chapter found in the given cue sheet file. ",
 
 
         "--add-object": "Add or replace an object. There may be more than one "