def convert_file(cuefile): if not os.path.isfile(cuefile): print("Error: argument needs to be a file") return cuesheet = CueSheet() cuesheet.setOutputFormat("%title%", "%offset%") with open(cuefile, "r") as f: cuesheet.setData(f.read()) cuesheet.parse() timestamps = [] for track in cuesheet.tracks: timestamps.append(datetime.strptime(str(track), "%M:%S:%f")) converted = convert_time(timestamps) write_timing_file(converted)
def __init__(self, cuepath, filepath=None, prefer_codec='flac', ffmpeg_bin='ffmpeg', encoding=None): self.prefer_codec = prefer_codec self.ffmpeg_bin = ffmpeg_bin with open(cuepath, 'rb') as fp: self.cuesheet = CueSheet() if encoding is None: self.cuesheet.setData(bytes2str(fp.read())) else: self.cuesheet.setData(fp.read().decode(encoding)) self.cuesheet.setOutputFormat( '%performer% - %title%\n%file%\n%tracks%', '%performer% - %title%', ) self.cuesheet.parse() self.n_tracks = len(self.cuesheet.tracks) print(self.cuesheet.output()) if filepath and isfile(filepath): self.filepath = filepath else: if isfile(self.cuesheet.file): self.filepath = self.cuesheet.file else: filepath = join(dirname(cuepath), self.cuesheet.file) if isfile(filepath): self.filepath = filepath else: raise IOError("Can't not find file: %s" % self.filepath) self.amtime = (stat(self.filepath).st_atime, stat(self.filepath).st_mtime)
def import_cue(self, path: Path, scening_list: SceningList, out_of_range_count: int) -> None: ''' Imports tracks as scenes. Uses TITLE for scene label. ''' from cueparser import CueSheet def offset_to_time(offset: str) -> Optional[Time]: pattern = re.compile(r'(\d{1,2}):(\d{1,2}):(\d{1,2})') match = pattern.match(offset) if match is None: return None return Time( minutes = int(match[1]), seconds = int(match[2]), milliseconds = int(match[3]) / 75 * 1000) cue_sheet = CueSheet() cue_sheet.setOutputFormat('') cue_sheet.setData(path.read_text()) cue_sheet.parse() for track in cue_sheet.tracks: if track.offset is None: continue offset = offset_to_time(track.offset) if offset is None: logging.warning( f'Scening import: INDEX timestamp \'{track.offset}\'' ' format isn\'t suported.') continue start = Frame(offset) end = None if track.duration is not None: end = Frame(offset + TimeInterval(track.duration)) label = '' if track.title is not None: label = track.title try: scening_list.add(start, end, label) except ValueError: out_of_range_count += 1
def get_mixes(): mixes_dir = Path.home() / 'Dropbox' / 'mixes' mixes = [] for filename in sorted(os.listdir(str(mixes_dir))): if filename.endswith('mp3'): mp3_filename = str(mixes_dir / filename) mp3_link = _get_dropbox_link(filename) name = filename[:-4] print(name) parts = name.split('-') if len(parts) > 1: anchor = parts[1] else: anchor = parts[0] anchor = anchor.strip().replace(' ', '-').replace('-[unmixed]', '').replace(',', '').lower() duration = _to_minutes(MP3(mp3_filename).info.length) cue_filename = str(mixes_dir / f'{name}.cue') tracks = [] cue_link = None if os.path.exists(cue_filename): cue_link = _get_dropbox_link(name + '.cue') cuesheet = CueSheet() cuesheet.setOutputFormat('%performer% - %title%\n%file%\n%tracks%', '%performer% - %title%') with open(cue_filename, encoding='latin1') as f: cuesheet.setData(f.read()) cuesheet.parse() tracks = cuesheet.tracks mixes.append({ 'name': name, 'anchor': anchor, 'tracks': tracks, 'duration': duration, 'mp3_link': mp3_link, 'cue_link': cue_link, }) return mixes
def main(): args = parser.parse_args() new_foldername = copy_dir(args.foldername) for root, dirs, files in os.walk(new_foldername): # keep a list of all cues and flacs flac_filenames = [] cue_filenames = [] thumbnail_filename = None for name in files: if fnmatch(name, "*.flac") or fnmatch(name, ".m4a") or fnmatch( name, '.wma'): flac_filenames.append(os.path.join(root, name)) if fnmatch(name, "*.cue"): cue_filenames.append(os.path.join(root, name)) if fnmatch(name, "*.jpg"): thumbnail_filename = os.path.join(root, name) # we should now have exactly one flac and one cue file if len(flac_filenames) == 1 and len(cue_filenames) == 1: ps = subprocess.Popen(('cuebreakpoints', cue_filenames[0]), stdout=subprocess.PIPE) subprocess.check_output(('shnsplit', '-o', 'flac', flac_filenames[0], '-a', flac_filenames[0].replace(root,'').replace('.flac', ''), '-d', root), stdin=ps.stdout) ps.wait() # move the old flac file to the trash send2trash(flac_filenames[0]) if len(cue_filenames)>0: cuesheet = CueSheet() cuesheet.setOutputFormat('%performer% - %title%\n%file%\n%tracks%', '%title%') with open(cue_filenames[0], "r") as f: cuesheet.setData(f.read()) cuesheet.parse() else: cuesheet = None print root # convert the other flacs to mp3 convert_files(root, cuesheet=cuesheet, thumbnail_filename=thumbnail_filename)
def process_one_set(opts): if opts.base is None: die("Need to specify basename") if opts.debug_mismatch_sizes: opts.do_write_no_backup = False opts.do_write = True if opts.debug_short_cue: pass #opts.debug = True if opts.do_write_no_backup: opts.do_write = True if (opts.gen_tl == False) and (opts.gen_cue == False): opts.gen_tl = True opts.gen_cue = True if opts.redo_all: opts.gen_cue = True opts.gen_tl = True opts.do_write = True if opts.dry_run: opts.gen_cue = False opts.gen_tl = False opts.do_write = False ### #print(file_base) file_base = os.path.basename(opts.base) file_base = Path(file_base).stem file_base = remove_last_dot(file_base) print(file_base) file_music = "%s.mp3" % file_base opts.output_type = "MP3" if os.path.isfile(file_music): print("MP3 file found") else: file_music = "%s.wav" % file_base opts.output_type = "WAV" if os.path.isfile(file_music): print("WAV file found") else: die("No mp3 or WAV file found. Check for case sensitivity problems." ) #print(file_base) opts.file_cue = "%s.cue" % file_base opts.file_tl = "%s.txt" % file_base opts.file_tl_simple = "%s.tracklist" % file_base opts.file_info = "%s.nfo" % file_base opts.file_lyrics = "%s.lyrics" % file_base if opts.cuefile: input_cue = read_file(opts.cuefile) else: input_cue = read_file(opts.file_cue) input_cue = remove_empty_lines(input_cue) if opts.debug: print("\n\nRead CUE contents:") print(input_cue) if opts.reload_standard_tracklist: opts.tracklist = opts.file_tl if opts.tracklist: #opts.ignore_tl_num = True #opts.file_tl = opts.tracklist input_tl = read_file(opts.tracklist) else: print("Reading tracklist __from CUE file itself__") # TODO: do this by hand from cueparser import CueSheet template_header = "%performer% - %title%\n%file%\n%tracks%" # (also can be %format%, %rem%, %songwriter%) template_header = "%tracks%" template_tracks = "%performer% - %title%" #(also can be %offset%, %index%, %songwriter%) cuesheet = CueSheet() #cuesheet.setOutputFormat(args.header, args.track) cuesheet.setOutputFormat(template_header, template_tracks) #cuesheet.setOutputFormat(args.header, args.track) #with open(cuefile, "r") as f: # cuesheet.setData(f.read()) cuesheet.setData(input_cue) import pdb try: has_error = False cuesheet.parse() except Exception: has_error = True print( "ERROR: cue sheet error - check frames > 59\n Check also PREGAP 2 entries\n" ) #has_error = True if has_error: pdb.set_trace() cuesheet.parse() sys.exit(1) input_tl = cuesheet.output() opts.ignore_tl_num = False opts.has_tl_header = False #print(input_tl) if opts.debug: print("\n\n------") print("TL FROM CUE READ:\n %s\n\n" % input_tl) #print("CUE READ:\n %s\n\n" % input_cue) generate_cue(file_music, input_tl, input_cue)
class CueCut: def __init__(self, cuepath, filepath=None, prefer_codec='flac', ffmpeg_bin='ffmpeg', encoding=None): self.prefer_codec = prefer_codec self.ffmpeg_bin = ffmpeg_bin with open(cuepath, 'rb') as fp: self.cuesheet = CueSheet() if encoding is None: self.cuesheet.setData(bytes2str(fp.read())) else: self.cuesheet.setData(fp.read().decode(encoding)) self.cuesheet.setOutputFormat( '%performer% - %title%\n%file%\n%tracks%', '%performer% - %title%', ) self.cuesheet.parse() self.n_tracks = len(self.cuesheet.tracks) print(self.cuesheet.output()) if filepath and isfile(filepath): self.filepath = filepath else: if isfile(self.cuesheet.file): self.filepath = self.cuesheet.file else: filepath = join(dirname(cuepath), self.cuesheet.file) if isfile(filepath): self.filepath = filepath else: raise IOError("Can't not find file: %s" % self.filepath) self.amtime = (stat(self.filepath).st_atime, stat(self.filepath).st_mtime) @staticmethod def vaildname(name): if name in RESERVED_NAME: print('Unable to resloving name issue: It`s a reserved name %s' % name) vaild = "".join(i if i not in r"\/:*?<>|" else " " for i in name) if vaild != name: print('Invaild name resloved.', ' "%s"' % name, ' --> "%s"' % vaild, sep='\n', end='\n\n') return vaild @staticmethod def offset(indextime): tick = [int(j) for j in indextime.split(':')] return tick[0] * 60 + tick[1] + tick[2] * 1e-2 @staticmethod def time_plus_deltatime(start, delta): if delta is None: return CueCut.offset('99:59:99') return CueCut.offset(start) + delta.seconds + delta.microseconds * 1e-6 def _output(self, trackidx): performer = self.cuesheet.tracks[trackidx].performer title = self.cuesheet.tracks[trackidx].title return join( dirname(self.filepath), '.'.join([ self.vaildname(' - '.join( filter(lambda x: x is not None, [performer, title]))), self.prefer_codec ]), ) def cut(self): for idx in range(self.n_tracks): self._cut(idx) def _cut(self, trackidx): track = self.cuesheet.tracks[trackidx] output = self._output(trackidx) commandline = [ self.ffmpeg_bin, '-hide_banner', # I reckon you would not want to see the BANNER '-y', # CAUTION: Overwrite by default '-i', # def: Input Stream self.filepath, '-map_metadata', # Drop metadata if exists '-1', '-c:a', # def: Output Stream Codec self.prefer_codec, '-ss', # From where '%.2f' % self.offset(track.offset), '-to', # To where '%.2f' % self.time_plus_deltatime(track.offset, track.duration), '-loglevel', # Disable massive log record 'fatal', '-metadata', # def: Metas 'title=%s' % track.title, '-metadata', 'artist=%s' % (track.songwriter or track.performer, ), '-metadata', 'performer=%s' % track.performer, '-metadata', 'album=%s' % self.cuesheet.title, '-metadata', 'track=%s/%s' % (track.number, self.n_tracks), '-metadata', 'album_artist=%s' % self.cuesheet.performer, '-metadata', 'composer=%s' % self.cuesheet.performer, '-write_id3v1', # Enable ID3V1 '1', '-vn', # Disable video stream # For some audio file with album image bulit in, # may cause the issue # `No packets were sent for some of the attached pictures.` # which leads to loss of the duration of output output, # def: Output Stream ] run(commandline, check=True, stderr=PIPE) utime(output, self.amtime) return 0
return ".wav" elif cueFormat == "MP3": return ".mp3" elif cueFormat == "AIFF": return ".aiff" elif cueFormat == "BINARY": return ".bin" else: return ".wav" parser = argparse.ArgumentParser() parser.add_argument("file", help="path to cue file") args = parser.parse_args() cuesheet = CueSheet() cuesheet.setOutputFormat("%performer% - %title%\n%file%\n%tracks%", "%title%") cuefile = args.file with open(cuefile, "rb") as f: cuesheet.setData(f.read().decode(sys.stdout.encoding)) cuesheet.parse() ffprobeCmd = "ffprobe -print_format json -show_format -sexagesimal " + cuefile.file process = Popen(shlex.split(ffprobeCmd), stdout=PIPE, stderr=PIPE) out = process.communicate() exit_code = process.wait() ffprobe_out = json.loads(out[0].decode("utf-8")) end_time = ffprobe_out["format"]["duration"]