维护并行的有损和无损音乐收藏

维护并行的有损和无损音乐收藏

我有大量的音乐收藏。其中一些是无损文件,一些是有损文件。

我想维护该集合的副本,其中包含原始集合的有损文件以及原始集合无损文件的有损转码。

一些假设:

  • 我知道如何使用 ffmpeg 将 flac 转换为 opus 文件
  • 我只有需要转换的 flac 文件,没有 wav 或 alac 编解码器
  • 有损文件可以是 opus、vorbis 或 mp3

我想要:

  • 使用最少的存储空间存储新的音乐收藏。即,它会在适当的情况下链接回原始有损文件。
  • 当我向原始文件添加更多有损和无损文件或更新元数据时,请保持集合最新。
  • 不必重新转码未修改的无损文件。

我想我需要使用一些自定义脚本来完成此任务,但如果有人在我投入大量时间之前提供建议或提示,我将永远感激不已。

答案1

我不喜欢 Makefile(我可能同意家伙);但是,make开箱即用,做你想做的事:

例如,您定义一个规则,您希望.opus每个源.flac文件都有一个文件:

Makefile,来自我的脑海

TARGETDIR=/path/to/compressed/library
%.opus: %.flac
    ffmpeg -ffmpegflags -and -stuff -i "$<" -o "$@"
$(TARGETDIR)/%.opus: %.opus
    cp --reflink=always "$<" "$@"

这可以将所有 FLAC 转换为树内的 OPUS。仅当 .opus 文件尚不存在或早于 FLAC 中的最后一次更改时,它才会执行此操作。

我不喜欢它,因为它发生在树内,即您最终不会得到一个干净的“仅原始”目录。至少cp在支持引用链接的文件系统上使用支持引用链接的文件系统,这样您的副本就很浅(并且实际上不需要任何空间)。我想,它也不能优雅地处理子目录,你会发现

然后,老实说,make 的功能实际上只是:

对于每个通配符源文件 (%.flac),检查结果(同一文件 .opus)是否已构建,如果没有(或者构建比源文件旧),则进行构建。

这有点倒退,而且还没有复杂到需要依赖 Make。所以,shell 脚本。我用的是zsh。虽然我不测试我写的内容,但我尝试对其进行评论:

#!/usr/bin/zsh
# Copyright 2022 Marcus Müller
# SPDX-License-Identifier: BSD-3-Clause
# Find the license text under https://spdx.org/licenses/BSD-3-Clause.html

# set options:
setopt null_glob    # Don't fail if there's no file matching a pattern
setopt no_case_glob # Don't care about case in matching

TARGET_DIR=../compressed_library

make_containing_dir() {
  target_dir="${1:h}"
  if [[ ! -d "${target_dir}" ]] ; then
    logger -p user.debug "Creating directory ${target_dir}"
    mkdir -p "${target_dir}" || logger -p user.err "can't mkdir ${target_dir}"
}

for compressed_source in **/*.{mp3,opus,vorbis,mp4} ; do
  if [[ -d "${compressed_source}" ]]; then
    continue # skip directories that happen to have a matching suffix
  fi

  logger -p user.debug "dealing with compressed source ${compressed_source}"
  
  target_file="${TARGET_DIR}/${compressed_source}"
  make_containing_dir "${target_file}"

  # -h : check whether target exists and is symlink
  if [[ ! -h "${target_file}" ]] ; then
   ln -s "$(pwd)/${compressed_source}" "${target_file}" \
     || logger -p user.err "copying ${compressed_source} failed"
  fi

done

for uncompressed_source in **/*.flac ; do
  if [[ -d "${uncompressed_source}" ]]; then
    continue # skip directories that happen to have a matching suffix
  fi

  logger -p user.debug "dealing with uncompressed source ${compressed_source}"

  target_file="${TARGET_DIR}/${uncompressed_source%%.flac}.opus"
  #                                               ^ strip the .flac suffix
  make_containing_dir "${target_file}"

  #         /-- compare source file for "older than"
  #         |   target file; this returns !=0 if the source file
  #         |   is newer, or the target file nonexisting
  #         \--------------------\
  #                              |
  if [[ "${uncompressed_source}" -ot "${target_file}" ]]; then
    ffmpeg -loglevel=fatal \
           -i "${uncompressed_source}" \
           -b:a 96k \
           "${target_file}" \ 
      || logger -p user.err "transcoding ${uncompressed_source} failed"
  fi

done

这是非常未经测试,但至少它会记录到系统日志(journalctl -xef是你的朋友)

相关内容