mirror of
https://github.com/rwinkhart/flac2pod.git
synced 2026-09-01 22:57:18 -04:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6d8d6f876a | ||
|
|
65dc1157e1 | ||
|
|
771f540af2 | ||
|
|
4055963026 | ||
|
|
af8c030201 | ||
|
|
c7133060a0 | ||
|
|
a2e0e3fe74 | ||
|
|
d0d34a04ee | ||
|
|
e78a0bc2c2 | ||
|
|
2fd4d685e0 | ||
|
|
6732731c11 | ||
|
|
5c83969ce2 | ||
|
|
19a7f3b7b2 | ||
|
|
be7166e2dd | ||
|
|
8ba7815241 | ||
|
|
d03d73a2f8 | ||
|
|
aa9ec97695 | ||
|
|
fcee6ba3f4 | ||
|
|
1daa02f7ac | ||
|
|
6b8553cbee |
@@ -18,7 +18,11 @@ flac2pod --help
|
|||||||
```
|
```
|
||||||
flac2pod-flacgain <source_dir>
|
flac2pod-flacgain <source_dir>
|
||||||
```
|
```
|
||||||
...to tag your FLAC library with ReplayGain data.
|
...to tag your FLAC library with ReplayGain data, or run...
|
||||||
|
```
|
||||||
|
flac2pod-artconvert <source_dir>
|
||||||
|
```
|
||||||
|
...to convert embedded PNGs (in MP4/M4A files) to iPod-ready JPEGs.
|
||||||
|
|
||||||
In order for flac2pod to function, your source_dir must be structured as follows:
|
In order for flac2pod to function, your source_dir must be structured as follows:
|
||||||
|
|
||||||
@@ -26,8 +30,6 @@ source_dir -> artist dir -> album dir -> FLAC files
|
|||||||
|
|
||||||
# Installation
|
# Installation
|
||||||
|
|
||||||
flac2pod is available in the AUR as "flac2pod". If installing on a non-Arch-based distribution, follow the manual installation instructions:
|
flac2pod is available in the AUR as "flac2pod".
|
||||||
|
|
||||||
- copy the contents of https://github.com/rwinkhart/flac2pod/blob/master/bin/ to your /usr/bin/
|
If installing on a non-Arch-based distribution, a generic package is available on the releases page.
|
||||||
- install flac, python-mutagen, ffmpeg, and screen
|
|
||||||
- start converting
|
|
||||||
|
|||||||
-146
@@ -1,146 +0,0 @@
|
|||||||
#!/bin/python3
|
|
||||||
|
|
||||||
# external modules
|
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
|
||||||
from mutagen.flac import FLAC
|
|
||||||
from os import cpu_count, path, walk
|
|
||||||
from pathlib import Path
|
|
||||||
from subprocess import check_output, Popen
|
|
||||||
from sys import exit as s_exit
|
|
||||||
from time import sleep
|
|
||||||
|
|
||||||
|
|
||||||
# utility functions
|
|
||||||
|
|
||||||
def scan_source(_source):
|
|
||||||
_artist_dirs, _album_dirs, _full_paths = [], [], []
|
|
||||||
for _root, _directories, _files in walk(path.expanduser(_source)):
|
|
||||||
for _dir in sorted(_directories):
|
|
||||||
if not _root.endswith(path.expanduser(_source)):
|
|
||||||
_artist_dirs.append(_root)
|
|
||||||
_artist_dirs = list(set(_artist_dirs))
|
|
||||||
for _artist in sorted(_artist_dirs):
|
|
||||||
for _root, _directories, _files in walk(path.expanduser(_artist)):
|
|
||||||
if not _root.endswith(path.expanduser(_artist)):
|
|
||||||
_album_dirs.append(_root)
|
|
||||||
_album_dirs = list(set(_album_dirs))
|
|
||||||
for _album in sorted(_album_dirs):
|
|
||||||
for _root, _directories, _files in walk(path.expanduser(_album)):
|
|
||||||
for _filename in _files:
|
|
||||||
if _filename.endswith('.flac'):
|
|
||||||
_full_paths.append(f"{_album}/{_filename[:-5]}")
|
|
||||||
return _album_dirs, _full_paths
|
|
||||||
|
|
||||||
|
|
||||||
def create_destination():
|
|
||||||
for _dir in source_directories[0]:
|
|
||||||
Path(path.expanduser(_dir.replace(path.expanduser(source), path.expanduser(destination))))\
|
|
||||||
.mkdir(0o700, parents=True, exist_ok=True)
|
|
||||||
|
|
||||||
|
|
||||||
def scan_destination_convert():
|
|
||||||
_i, _i_total, _total_files = 0, 0, len(source_directories[1])
|
|
||||||
for _file in source_directories[1]:
|
|
||||||
_i_total += 1
|
|
||||||
_progress = round((_i_total / _total_files) * 100, 2)
|
|
||||||
if not Path(f"{_file.replace(path.expanduser(source), path.expanduser(destination))}.mp3").is_file() and not \
|
|
||||||
Path(f"{_file.replace(path.expanduser(source), path.expanduser(destination))}.m4a").is_file():
|
|
||||||
_file_s = _file.replace(' ', '\\ ').replace("'", "\\'").replace(')', '\\)').replace('(', '\\(')\
|
|
||||||
.replace('&', '\\&').replace('`', '\\`').replace('$', '\\$') + '.flac'
|
|
||||||
_file_d = _file.replace(path.expanduser(source), path.expanduser(destination)).replace(' ', '\\ ')\
|
|
||||||
.replace("'", "\\'").replace(')', '\\)').replace('(', '\\(').replace('&', '\\&')\
|
|
||||||
.replace('`', '\\`').replace('$', '\\$') + '.m4a'
|
|
||||||
_active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1
|
|
||||||
if _active_processes >= cpu_count():
|
|
||||||
while _active_processes >= cpu_count():
|
|
||||||
print(f"\nThere are already {cpu_count()} processes running...\n")
|
|
||||||
sleep(1)
|
|
||||||
_active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1
|
|
||||||
_i += 1
|
|
||||||
print(f"\n{_progress}% | \u001b[38;5;0;48;5;15mConverting {_file_s} to {_file_d}...\u001b[0m\n")
|
|
||||||
# get relevant ReplayGain info
|
|
||||||
_audio = FLAC(f"{_file}.flac")
|
|
||||||
if args.albumgain:
|
|
||||||
if _audio.get('replaygain_album_gain'):
|
|
||||||
_rggain = float(_audio.get('replaygain_album_gain')[0][:-3])
|
|
||||||
elif _audio.get('REPLAYGAIN_ALBUM_GAIN'):
|
|
||||||
_rggain = float(_audio.get('REPLAYGAIN_ALBUM_GAIN')[0][:-3])
|
|
||||||
else:
|
|
||||||
_rggain = None
|
|
||||||
if _audio.get('replaygain_album_peak'):
|
|
||||||
_rgpeak = float(_audio.get('replaygain_album_peak')[0])
|
|
||||||
elif _audio.get('REPLAYGAIN_ALBUM_PEAK'):
|
|
||||||
_rgpeak = float(_audio.get('REPLAYGAIN_ALBUM_PEAK')[0])
|
|
||||||
else:
|
|
||||||
_rgpeak = None
|
|
||||||
else:
|
|
||||||
if _audio.get('replaygain_track_gain'):
|
|
||||||
_rggain = float(_audio.get('replaygain_track_gain')[0][:-3])
|
|
||||||
elif _audio.get('REPLAYGAIN_TRACK_GAIN'):
|
|
||||||
_rggain = float(_audio.get('REPLAYGAIN_TRACK_GAIN')[0][:-3])
|
|
||||||
else:
|
|
||||||
_rggain = None
|
|
||||||
if _audio.get('replaygain_track_peak'):
|
|
||||||
_rgpeak = float(_audio.get('replaygain_track_peak')[0])
|
|
||||||
elif _audio.get('REPLAYGAIN_TRACK_PEAK'):
|
|
||||||
_rgpeak = float(_audio.get('REPLAYGAIN_TRACK_PEAK')[0])
|
|
||||||
else:
|
|
||||||
_rgpeak = None
|
|
||||||
# determine ffmpeg arguments
|
|
||||||
if args.bake and _rggain is not None:
|
|
||||||
_bake_args, _flac2pod_rg_tags = '-filter:a "volume=' + str(_rggain) + 'dB"', ''
|
|
||||||
elif _rggain is not None and _rgpeak is not None:
|
|
||||||
_bake_args, _flac2pod_rg_tags = '', f"-metadata comment='FLAC2PODRG#{_rggain}#{_rgpeak}#'"
|
|
||||||
else:
|
|
||||||
_bake_args, _flac2pod_rg_tags = '', ''
|
|
||||||
# generate the appropriate ffmpeg command
|
|
||||||
_cmd = f"screen -DmS flac2pod{_i} ffmpeg -i {_file_s} -c:a aac -ab 256k {_bake_args} -map_metadata 0 " \
|
|
||||||
f"{_flac2pod_rg_tags} -aac_pns 0 -movflags +faststart -vn {_file_d} </dev/null"
|
|
||||||
if _rggain is not None:
|
|
||||||
print(f"\u001b[38;5;0;48;5;15mGain adjustment: {_rggain}dB\u001b[0m")
|
|
||||||
else:
|
|
||||||
print(f"\u001b[38;5;0;48;5;88mNo ReplayGain data found!\u001b[0m")
|
|
||||||
if args.stopifnogain:
|
|
||||||
s_exit(1)
|
|
||||||
Popen(_cmd, shell=True)
|
|
||||||
_active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1
|
|
||||||
while _active_processes != 0:
|
|
||||||
print('\nPlease wait for the remaining conversions to complete...\n')
|
|
||||||
sleep(1)
|
|
||||||
_active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1
|
|
||||||
sleep(1)
|
|
||||||
print('\niPod conversion complete!\n')
|
|
||||||
s_exit(0)
|
|
||||||
|
|
||||||
|
|
||||||
# argument parsing
|
|
||||||
if __name__ == "__main__":
|
|
||||||
parser = ArgumentParser(description='Convert your existing FLAC library to be played on an iPod Classic.')
|
|
||||||
parser.add_argument('source_dir', nargs='+',
|
|
||||||
help='directory tree containing music files - folder must contain library sorted into artists '
|
|
||||||
'and albums')
|
|
||||||
parser.add_argument('destination_dir', nargs='+',
|
|
||||||
help='destination parent directory for output - will be created if it does not already exist')
|
|
||||||
parser.add_argument('-b', '--bake', action='store_true',
|
|
||||||
help='bake (encode) ReplayGain values into the file')
|
|
||||||
parser.add_argument('-a', '--albumgain', action='store_true',
|
|
||||||
help='read ReplayGain album gain instead of ReplayGain track gain')
|
|
||||||
parser.add_argument('-x', '--stopifnogain', action='store_true',
|
|
||||||
help='stop flac2pod if ReplayGain data cannot be found in a file')
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
if args.source_dir[0].endswith('/'):
|
|
||||||
source = args.source_dir[0][:-1]
|
|
||||||
else:
|
|
||||||
source = args.source_dir[0]
|
|
||||||
if args.destination_dir[0].endswith('/'):
|
|
||||||
destination = args.destination_dir[0][:-1]
|
|
||||||
else:
|
|
||||||
destination = args.destination_dir[0]
|
|
||||||
|
|
||||||
source_directories = scan_source(source)
|
|
||||||
create_destination()
|
|
||||||
scan_destination_convert()
|
|
||||||
|
|
||||||
s_exit(0)
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
/usr/bin/flac2pod
|
|
||||||
Binary file not shown.
Executable
+42
@@ -0,0 +1,42 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from flac2pod import scan_source
|
||||||
|
from mutagen import mp4
|
||||||
|
from mutagen.mp4 import MP4Cover
|
||||||
|
from os import listdir
|
||||||
|
from PIL import Image
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = ArgumentParser(description='Convert embedded album art in MP4/M4A files from PNGs to iPod-compatible '
|
||||||
|
'JPEGs.')
|
||||||
|
parser.add_argument('source_dir', nargs='+',
|
||||||
|
help='directory tree containing music files - folder must contain library sorted into artists '
|
||||||
|
'and albums')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.source_dir[0].endswith('/'):
|
||||||
|
source = args.source_dir[0][:-1]
|
||||||
|
else:
|
||||||
|
source = args.source_dir[0]
|
||||||
|
|
||||||
|
source_directories = scan_source(source)
|
||||||
|
|
||||||
|
for album in sorted(source_directories[0]):
|
||||||
|
for song in listdir(album):
|
||||||
|
audio = mp4.MP4(f"{album}/{song}")
|
||||||
|
if audio.tags is None:
|
||||||
|
continue
|
||||||
|
if "covr" not in audio.tags:
|
||||||
|
continue
|
||||||
|
cover = audio.tags["covr"][0]
|
||||||
|
print(f"[{album}/{song}] Converting...")
|
||||||
|
open("/tmp/cover.png", "wb").write(cover)
|
||||||
|
image = Image.open("/tmp/cover.png")
|
||||||
|
image.thumbnail((300, 300))
|
||||||
|
image.save("/tmp/cover.jpeg", "JPEG", quality=50)
|
||||||
|
with open("/tmp/cover.jpeg", "rb") as f:
|
||||||
|
audio["covr"] = [
|
||||||
|
MP4Cover(f.read(), imageformat=MP4Cover.FORMAT_JPEG)
|
||||||
|
]
|
||||||
|
audio.save()
|
||||||
@@ -1,13 +1,12 @@
|
|||||||
#!/bin/python3
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
from argparse import ArgumentParser
|
from argparse import ArgumentParser
|
||||||
from flac2pod import scan_source
|
from flac2pod import scan_source
|
||||||
from mutagen.flac import FLAC
|
from mutagen.flac import FLAC, FLACNoHeaderError
|
||||||
from os import listdir, system
|
from os import listdir, system
|
||||||
from subprocess import PIPE, Popen, STDOUT
|
from subprocess import PIPE, run
|
||||||
from sys import exit as s_exit
|
from sys import exit as s_exit
|
||||||
|
|
||||||
# argument parsing
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = ArgumentParser(description='Quickly tag your FLAC library with ReplayGain data using metaflac.')
|
parser = ArgumentParser(description='Quickly tag your FLAC library with ReplayGain data using metaflac.')
|
||||||
parser.add_argument('source_dir', nargs='+',
|
parser.add_argument('source_dir', nargs='+',
|
||||||
@@ -26,41 +25,41 @@ if __name__ == "__main__":
|
|||||||
|
|
||||||
gain = 1
|
gain = 1
|
||||||
for album in sorted(source_directories[0]):
|
for album in sorted(source_directories[0]):
|
||||||
std_album = album.replace(' ', '\\ ').replace("'", "\\'").replace(')', '\\)') \
|
std_album = album.replace(' ', '\\ ').replace("'", "\\'").replace(')', '\\)').replace('(', '\\(')\
|
||||||
.replace('(', '\\(').replace('&', '\\&').replace('`', '\\`').replace('$', '\\$')
|
.replace(']', '\\]').replace('[', '\\[').replace('&', '\\&').replace('`', '\\`').replace('$', '\\$')
|
||||||
for song in listdir(album):
|
for song in listdir(album):
|
||||||
audio = FLAC(f"{album}/{song}")
|
try:
|
||||||
if audio.get('replaygain_track_gain') or audio.get('REPLAYGAIN_TRACK_GAIN'):
|
audio = FLAC(f"{album}/{song}")
|
||||||
gain = 1
|
if audio.get('replaygain_track_gain') or audio.get('REPLAYGAIN_TRACK_GAIN'):
|
||||||
else:
|
gain = 1
|
||||||
gain = 0
|
else:
|
||||||
break
|
gain = 0
|
||||||
if args.force or gain == 0:
|
break
|
||||||
|
except FLACNoHeaderError:
|
||||||
|
print(f"[{album}/*] Contains non-FLAC files, skipping album...")
|
||||||
|
gain = 2
|
||||||
|
if gain != 2 and (args.force or gain == 0):
|
||||||
print(f"[{album}/*] Adding ReplayGain data...")
|
print(f"[{album}/*] Adding ReplayGain data...")
|
||||||
system(f"metaflac --remove-replay-gain {std_album}/*")
|
system(f"metaflac --remove-replay-gain {std_album}/*")
|
||||||
command = f"metaflac --add-replay-gain {std_album}/*"
|
output = run(f"metaflac --add-replay-gain {std_album}/*", shell=True, stderr=PIPE, text=True)
|
||||||
output = Popen(command, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
|
|
||||||
block = output.communicate()[0].strip() # blocks the program from proceeding until stdout is given
|
|
||||||
if output.returncode == 1:
|
if output.returncode == 1:
|
||||||
if block.decode('utf-8').__contains__('sample') or block.decode('utf-8').__contains__('resolution of'):
|
if output.stderr.__contains__('sample') or output.stderr.__contains__('resolution of') or output.\
|
||||||
print('Resolution/Sample Rate mismatch, scanning tracks as individuals...')
|
stderr.__contains__('does not match'):
|
||||||
|
print('Resolution/Sample Rate/Channel mismatch, scanning tracks as individuals...')
|
||||||
for song in listdir(album):
|
for song in listdir(album):
|
||||||
print(f"[{album}/{song}] Adding ReplayGain data...")
|
print(f"[{album}/{song}] Adding ReplayGain data...")
|
||||||
std_song = song.replace(' ', '\\ ').replace("'", "\\'").replace(')', '\\)')\
|
std_song = song.replace(' ', '\\ ').replace("'", "\\'").replace(')', '\\)')\
|
||||||
.replace('(', '\\(').replace('&', '\\&').replace('`', '\\`').replace('$', '\\$')
|
.replace('(', '\\(').replace('&', '\\&').replace('`', '\\`').replace('$', '\\$')
|
||||||
command = f"metaflac --add-replay-gain {std_album}/{std_song}"
|
output = run(f"metaflac --add-replay-gain {std_album}/{std_song}", shell=True, stderr=PIPE,
|
||||||
output = Popen(command, shell=True, stdin=PIPE, stdout=PIPE, stderr=STDOUT, close_fds=True)
|
text=True)
|
||||||
block = output.communicate()[0].strip()
|
|
||||||
if output.returncode == 1:
|
if output.returncode == 1:
|
||||||
print('There was an error processing your files.')
|
print('There was an error processing your files.')
|
||||||
print(f"Return Code: [{str(output.returncode)}] {block.decode('utf-8')}")
|
print(f"Return Code: [{str(output.returncode)}] {output.stderr}")
|
||||||
s_exit(1)
|
s_exit(1)
|
||||||
else:
|
else:
|
||||||
print('There was an error processing your files.')
|
print('There was an error processing your files.')
|
||||||
print(f"Return Code: [{str(output.returncode)}] {block.decode('utf-8')}")
|
print(f"Return Code: [{str(output.returncode)}] {output.stderr}")
|
||||||
s_exit(1)
|
s_exit(1)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
print(f"[{album}/*] ReplayGain data is already present.")
|
print(f"[{album}/*] ReplayGain data is already present.")
|
||||||
|
|
||||||
s_exit(0)
|
|
||||||
Executable
+158
@@ -0,0 +1,158 @@
|
|||||||
|
#!/usr/bin/env python3
|
||||||
|
|
||||||
|
# external modules
|
||||||
|
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from mutagen.flac import FLAC
|
||||||
|
from os import cpu_count, path, walk
|
||||||
|
from pathlib import Path
|
||||||
|
from shutil import copy
|
||||||
|
from subprocess import check_output, Popen
|
||||||
|
from sys import exit as s_exit
|
||||||
|
from time import sleep
|
||||||
|
|
||||||
|
|
||||||
|
# utility functions
|
||||||
|
|
||||||
|
def scan_source(_source):
|
||||||
|
_artist_dirs, _album_dirs, _full_paths = [], [], []
|
||||||
|
for _root, _directories, _files in walk(path.expanduser(_source)):
|
||||||
|
for _dir in sorted(_directories):
|
||||||
|
if not _root.endswith(path.expanduser(_source)):
|
||||||
|
_artist_dirs.append(_root)
|
||||||
|
_artist_dirs = list(set(_artist_dirs))
|
||||||
|
for _artist in sorted(_artist_dirs):
|
||||||
|
for _root, _directories, _files in walk(path.expanduser(_artist)):
|
||||||
|
if not _root.endswith(path.expanduser(_artist)):
|
||||||
|
_album_dirs.append(_root)
|
||||||
|
_album_dirs = list(set(_album_dirs))
|
||||||
|
for _album in sorted(_album_dirs):
|
||||||
|
for _root, _directories, _files in walk(path.expanduser(_album)):
|
||||||
|
for _filename in _files:
|
||||||
|
if _filename.endswith('.flac') or _filename.endswith('.mp3') or _filename.endswith('.mp4') \
|
||||||
|
or _filename.endswith('.m4a'):
|
||||||
|
_full_paths.append(f"{_album}/{_filename}")
|
||||||
|
return _album_dirs, _full_paths
|
||||||
|
|
||||||
|
|
||||||
|
def create_destination():
|
||||||
|
for _dir in source_directories[0]:
|
||||||
|
Path(path.expanduser(_dir.replace(path.expanduser(source), path.expanduser(destination))))\
|
||||||
|
.mkdir(0o700, parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
|
def scan_destination_convert():
|
||||||
|
_i, _i_total, _total_files = 0, 0, len(source_directories[1])
|
||||||
|
for _file in source_directories[1]:
|
||||||
|
_i_total += 1
|
||||||
|
_progress = round((_i_total / _total_files) * 100, 2)
|
||||||
|
if not Path(f"{(_file.replace(path.expanduser(source), path.expanduser(destination)))[:-5]}.m4a").is_file()\
|
||||||
|
and not Path(f"{(_file.replace(path.expanduser(source), path.expanduser(destination)))}").is_file():
|
||||||
|
if not _file.endswith('.flac'):
|
||||||
|
print(f"[{_file}] Not a FLAC file, copying...")
|
||||||
|
copy(f"{_file}", f"{_file.replace(path.expanduser(source), path.expanduser(destination))}")
|
||||||
|
else:
|
||||||
|
_file_s = _file.replace(' ', '\\ ').replace("'", "\\'").replace(')', '\\)').replace('(', '\\(') \
|
||||||
|
.replace(']', '\\]').replace('[', '\\[').replace('&', '\\&').replace('`', '\\`') \
|
||||||
|
.replace('$', '\\$')
|
||||||
|
_file_d = _file.replace(path.expanduser(source), path.expanduser(destination)).replace(' ', '\\ ') \
|
||||||
|
.replace("'", "\\'").replace(')', '\\)').replace('(', '\\(').replace(']', '\\]') \
|
||||||
|
.replace('[', '\\[').replace('&', '\\&').replace('`', '\\`').replace('$', '\\$')[
|
||||||
|
:-5] + '.m4a'
|
||||||
|
_active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1
|
||||||
|
if _active_processes >= cpu_count():
|
||||||
|
while _active_processes >= cpu_count():
|
||||||
|
print(f"\nThere are already {cpu_count()} processes running...\n")
|
||||||
|
sleep(1)
|
||||||
|
_active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1
|
||||||
|
_i += 1
|
||||||
|
print(f"\n{_progress}% | \u001b[38;5;0;48;5;15mConverting {_file_s} to {_file_d}...\u001b[0m\n")
|
||||||
|
# get relevant ReplayGain info
|
||||||
|
_audio = FLAC(f"{_file}")
|
||||||
|
if args.albumgain:
|
||||||
|
if _audio.get('REPLAYGAIN_ALBUM_GAIN'):
|
||||||
|
_rggain = float(_audio.get('REPLAYGAIN_ALBUM_GAIN')[0][:-3])
|
||||||
|
elif _audio.get('replaygain_album_gain'):
|
||||||
|
_rggain = float(_audio.get('replaygain_album_gain')[0][:-3])
|
||||||
|
else:
|
||||||
|
_rggain = None
|
||||||
|
if _audio.get('REPLAYGAIN_ALBUM_PEAK'):
|
||||||
|
_rgpeak = float(_audio.get('REPLAYGAIN_ALBUM_PEAK')[0])
|
||||||
|
elif _audio.get('replaygain_album_peak'):
|
||||||
|
_rgpeak = float(_audio.get('replaygain_album_peak')[0])
|
||||||
|
else:
|
||||||
|
_rgpeak = None
|
||||||
|
else:
|
||||||
|
if _audio.get('REPLAYGAIN_TRACK_GAIN'):
|
||||||
|
_rggain = float(_audio.get('REPLAYGAIN_TRACK_GAIN')[0][:-3])
|
||||||
|
elif _audio.get('replaygain_track_gain'):
|
||||||
|
_rggain = float(_audio.get('replaygain_track_gain')[0][:-3])
|
||||||
|
else:
|
||||||
|
_rggain = None
|
||||||
|
if _audio.get('REPLAYGAIN_TRACK_PEAK'):
|
||||||
|
_rgpeak = float(_audio.get('REPLAYGAIN_TRACK_PEAK')[0])
|
||||||
|
elif _audio.get('replaygain_track_peak'):
|
||||||
|
_rgpeak = float(_audio.get('replaygain_track_peak')[0])
|
||||||
|
else:
|
||||||
|
_rgpeak = None
|
||||||
|
# determine ffmpeg arguments
|
||||||
|
if args.bake and _rggain is not None:
|
||||||
|
_bake_args, _flac2pod_rg_tags = '-filter:a "volume=' + str(_rggain) + 'dB"', ''
|
||||||
|
elif _rggain is not None and _rgpeak is not None:
|
||||||
|
_bake_args, _flac2pod_rg_tags = '', f"-metadata comment='FLAC2PODRG#{_rggain}#{_rgpeak}#'"
|
||||||
|
else:
|
||||||
|
_bake_args, _flac2pod_rg_tags = '', ''
|
||||||
|
if args.preserve:
|
||||||
|
_art_args = '-c:v copy -map_metadata 0:g'
|
||||||
|
else:
|
||||||
|
_art_args = '-vn'
|
||||||
|
# generate the appropriate ffmpeg command
|
||||||
|
_cmd = f"screen -DmS flac2pod{_i} ffmpeg -i {_file_s} {_art_args} -c:a aac -b:a 256k {_bake_args} " \
|
||||||
|
f"{_flac2pod_rg_tags} -aac_pns 0 -movflags +faststart {_file_d} </dev/null"
|
||||||
|
if _rggain is not None:
|
||||||
|
print(f"\u001b[38;5;0;48;5;15mGain adjustment: {_rggain}dB\u001b[0m")
|
||||||
|
else:
|
||||||
|
print(f"\u001b[38;5;0;48;5;88mNo ReplayGain data found!\u001b[0m")
|
||||||
|
if args.stopifnogain:
|
||||||
|
s_exit(1)
|
||||||
|
Popen(_cmd, shell=True)
|
||||||
|
_active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1
|
||||||
|
while _active_processes != 0:
|
||||||
|
print('\nPlease wait for the remaining conversions to complete...\n')
|
||||||
|
sleep(1)
|
||||||
|
_active_processes = int(check_output('ps -C ffmpeg | wc -l', shell=True)) - 1
|
||||||
|
sleep(1)
|
||||||
|
print('\niPod conversion complete!\n')
|
||||||
|
s_exit(0)
|
||||||
|
|
||||||
|
|
||||||
|
# argument parsing
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = ArgumentParser(description='Convert your existing FLAC library to be played on an iPod Classic.')
|
||||||
|
parser.add_argument('source_dir', nargs='+',
|
||||||
|
help='directory tree containing music files - folder must contain library sorted into artists '
|
||||||
|
'and albums')
|
||||||
|
parser.add_argument('destination_dir', nargs='+',
|
||||||
|
help='destination parent directory for output - will be created if it does not already exist')
|
||||||
|
parser.add_argument('-b', '--bake', action='store_true',
|
||||||
|
help='bake (encode) ReplayGain values into output files')
|
||||||
|
parser.add_argument('-a', '--albumgain', action='store_true',
|
||||||
|
help='read ReplayGain album gain instead of ReplayGain track gain')
|
||||||
|
parser.add_argument('-x', '--stopifnogain', action='store_true',
|
||||||
|
help='stop flac2pod if ReplayGain data cannot be found in a file')
|
||||||
|
parser.add_argument('-p', '--preserve', action='store_true',
|
||||||
|
help='copy album art from input files')
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
if args.source_dir[0].endswith('/'):
|
||||||
|
source = args.source_dir[0][:-1]
|
||||||
|
else:
|
||||||
|
source = args.source_dir[0]
|
||||||
|
if args.destination_dir[0].endswith('/'):
|
||||||
|
destination = args.destination_dir[0][:-1]
|
||||||
|
else:
|
||||||
|
destination = args.destination_dir[0]
|
||||||
|
|
||||||
|
source_directories = scan_source(source)
|
||||||
|
create_destination()
|
||||||
|
scan_destination_convert()
|
||||||
+23
-27
@@ -1,14 +1,12 @@
|
|||||||
#!/bin/bash
|
#!/usr/bin/env bash
|
||||||
|
|
||||||
# This script packages flac2pod (from source) for Arch Linux.
|
echo -e '\n\nthe value entered in this field will only affect the version reported to the package manager - the latest source is used regardless\n'
|
||||||
|
read -r -p "version number: " version
|
||||||
|
read -r -p "revision number: " revision
|
||||||
|
|
||||||
echo -e '\n\nThe value entered in this field will only affect the version reported to the package manager. The latest source is used regardless.\n'
|
echo -e '\noptions (please enter the number only):'
|
||||||
read -r -p "Version number: " version
|
echo -e '\n1. gitHub release tag\n2. local\n'
|
||||||
read -r -p "Revision number: " revision
|
read -r -p "source (for build scripts): " source
|
||||||
|
|
||||||
echo -e '\nOptions (please enter the number only):'
|
|
||||||
echo -e '\n1. GitHub Release Tag\n2. Local\n'
|
|
||||||
read -r -p "Source (for build scripts): " source
|
|
||||||
|
|
||||||
if [ "$source" == "1" ]; then
|
if [ "$source" == "1" ]; then
|
||||||
source='https://github.com/rwinkhart/flac2pod/releases/download/v$pkgver/flac2pod-$pkgver.tar.xz'
|
source='https://github.com/rwinkhart/flac2pod/releases/download/v$pkgver/flac2pod-$pkgver.tar.xz'
|
||||||
@@ -16,22 +14,23 @@ else
|
|||||||
source=local://flac2pod-"$version".tar.xz
|
source=local://flac2pod-"$version".tar.xz
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
# generic packaging
|
||||||
# Archive creation
|
echo -e '\npackaging as generic...\n'
|
||||||
echo -e '\nPackaging as generic...\n'
|
mkdir -p output/generictemp/usr/{bin,lib/flac2pod}
|
||||||
mkdir -p packages/archtemp/usr
|
cp -r lib/. output/generictemp/usr/lib/flac2pod/
|
||||||
cp -r bin packages/archtemp/usr/
|
ln -s /usr/lib/flac2pod/flac2pod.py output/generictemp/usr/bin/flac2pod
|
||||||
cp -r share packages/archtemp/usr/
|
ln -s /usr/lib/flac2pod/flac2pod-artconvert.py output/generictemp/usr/bin/flac2pod-artconvert
|
||||||
tar -C packages/archtemp -cvf packages/flac2pod-"$version".tar.xz usr/
|
ln -s /usr/lib/flac2pod/flac2pod-flacgain.py output/generictemp/usr/bin/flac2pod-flacgain
|
||||||
rm -rf packages/archtemp
|
cp -r share output/generictemp/usr/
|
||||||
sha512="$(sha512sum packages/flac2pod-"$version".tar.xz | awk '{print $1;}')"
|
tar -C output/generictemp -cvJf output/flac2pod-"$version".tar.xz usr/
|
||||||
|
rm -rf output/generictemp
|
||||||
|
sha512="$(sha512sum output/flac2pod-"$version".tar.xz | awk '{print $1;}')"
|
||||||
echo -e "\nsha512 sum:\n$sha512"
|
echo -e "\nsha512 sum:\n$sha512"
|
||||||
echo -e "\nGeneric packaging complete.\n"
|
echo -e "\ngeneric packaging complete\n"
|
||||||
|
|
||||||
# PKGBUILD creation
|
# PKGBUILD creation
|
||||||
echo -e '\nGenerating PKGBUILD...'
|
echo -e '\ngenerating PKGBUILD...'
|
||||||
echo "# Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
echo "# Maintainer: Randall Winkhart <idgr at tutanota dot com>
|
||||||
|
|
||||||
pkgname=flac2pod
|
pkgname=flac2pod
|
||||||
pkgver="$version"
|
pkgver="$version"
|
||||||
pkgrel="$revision"
|
pkgrel="$revision"
|
||||||
@@ -39,15 +38,12 @@ pkgdesc='Converts your FLAC library to be iPod-ready'
|
|||||||
url='https://github.com/rwinkhart/flac2pod'
|
url='https://github.com/rwinkhart/flac2pod'
|
||||||
arch=('any')
|
arch=('any')
|
||||||
license=('GPL2')
|
license=('GPL2')
|
||||||
depends=(ffmpeg flac python python-mutagen screen)
|
depends=(ffmpeg flac python python-mutagen python-pillow screen)
|
||||||
|
|
||||||
source=(\""$source"\")
|
source=(\""$source"\")
|
||||||
sha512sums=('"$sha512"')
|
sha512sums=('"$sha512"')
|
||||||
|
|
||||||
package() {
|
package() {
|
||||||
|
|
||||||
tar xf flac2pod-"\"\$pkgver\"".tar.xz -C "\"\${pkgdir}\""
|
tar xf flac2pod-"\"\$pkgver\"".tar.xz -C "\"\${pkgdir}\""
|
||||||
|
|
||||||
}
|
}
|
||||||
" > packages/PKGBUILD
|
" > output/PKGBUILD
|
||||||
echo -e "\nPKGBUILD generated.\n"
|
echo -e "\nPKGBUILD generated\n"
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
flac2pod is a FOSS audio transcoding script for preparing music for an Apple iPod.
|
flac2pod is a FOSS audio transcoding script for preparing music for an Apple iPod.
|
||||||
Copyright (C) 2022 Randall Winkhart idgr@tutanota.com
|
Copyright (C) 2022-2023 Randall Winkhart idgr@tutanota.com
|
||||||
|
|
||||||
This program is free software; you can redistribute it and/or modify
|
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
|
it under the terms of the GNU General Public License as published by
|
||||||
|
|||||||
Reference in New Issue
Block a user