mirror of
https://github.com/beetbox/beets.git
synced 2026-03-28 00:06:51 +01:00
Add filename extension for music files with no extension during import
This commit is contained in:
parent
1943b14565
commit
4b5db35945
5 changed files with 135 additions and 0 deletions
|
|
@ -18,10 +18,12 @@ import logging
|
|||
import os
|
||||
import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import time
|
||||
from collections import defaultdict
|
||||
from collections.abc import Callable
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from tempfile import mkdtemp
|
||||
from typing import TYPE_CHECKING, Any
|
||||
|
||||
|
|
@ -1077,6 +1079,12 @@ class ImportTaskFactory:
|
|||
If an item cannot be read, return `None` instead and log an
|
||||
error.
|
||||
"""
|
||||
|
||||
# Check if the file has an extention,
|
||||
# Add an extention if there isn't one.
|
||||
if os.path.isfile(path):
|
||||
path = self.check_extension(path)
|
||||
|
||||
try:
|
||||
return library.Item.from_path(path)
|
||||
except library.ReadError as exc:
|
||||
|
|
@ -1090,6 +1098,102 @@ class ImportTaskFactory:
|
|||
"error reading {}: {}", util.displayable_path(path), exc
|
||||
)
|
||||
|
||||
def check_extension(self, path: util.PathBytes):
|
||||
path = Path(path.decode("utf-8"))
|
||||
# if there is an extension, ignore
|
||||
if path.suffix != "":
|
||||
return path
|
||||
|
||||
# no extension detexted
|
||||
# use ffprobe to find the format
|
||||
formats = []
|
||||
output = subprocess.run(
|
||||
[
|
||||
"ffprobe",
|
||||
"-hide_banner",
|
||||
"-loglevel",
|
||||
"fatal",
|
||||
"-show_format",
|
||||
path,
|
||||
],
|
||||
capture_output=True,
|
||||
)
|
||||
out = output.stdout.decode("utf-8")
|
||||
err = output.stderr.decode("utf-8")
|
||||
if err != "":
|
||||
log.error("ffprobe error\n", err)
|
||||
for line in out.split("\n"):
|
||||
if line.startswith("format_name="):
|
||||
formats = line.split("=")[1].split(",")
|
||||
# a list of audio formats I got from wikipedia https://en.wikipedia.org/wiki/Audio_file_format
|
||||
wiki_formats = [
|
||||
"3gp",
|
||||
"aa",
|
||||
"aac",
|
||||
"aax",
|
||||
"act",
|
||||
"aiff",
|
||||
"alac",
|
||||
"amr",
|
||||
"ape",
|
||||
"au",
|
||||
"awb",
|
||||
"dss",
|
||||
"dvf",
|
||||
"flac",
|
||||
"gsm",
|
||||
"iklax",
|
||||
"ivs",
|
||||
"m4a",
|
||||
"m4b",
|
||||
"m4p",
|
||||
"mmf",
|
||||
"movpkg",
|
||||
"mp1",
|
||||
"mp2",
|
||||
"mp3",
|
||||
"mpc",
|
||||
"msv",
|
||||
"nmf",
|
||||
"ogg",
|
||||
"oga",
|
||||
"mogg",
|
||||
"opus",
|
||||
"ra",
|
||||
"rm",
|
||||
"raw",
|
||||
"rf64",
|
||||
"sln",
|
||||
"tta",
|
||||
"voc",
|
||||
"vox",
|
||||
"wav",
|
||||
"wma",
|
||||
"wv",
|
||||
"webm",
|
||||
"8svx",
|
||||
"cda",
|
||||
]
|
||||
format = ""
|
||||
# The first format from ffprobe that is on this list is taken
|
||||
for f in formats:
|
||||
if f in wiki_formats:
|
||||
format = f
|
||||
break
|
||||
|
||||
# if ffprobe can't find a format, the file is prob not music
|
||||
if format == "":
|
||||
return path
|
||||
|
||||
# cp and add ext. If already exist, use that file
|
||||
# assume, for example, the only diff between 'asdf.mp3' and 'asdf' is format
|
||||
new_path = path.with_suffix("." + format)
|
||||
if not new_path.exists():
|
||||
util.copy(path, new_path)
|
||||
else:
|
||||
log.info("Import file with matching format to original target")
|
||||
return new_path
|
||||
|
||||
|
||||
MULTIDISC_MARKERS = (rb"dis[ck]", rb"cd")
|
||||
MULTIDISC_PAT_FMT = rb"^(.*%s[\W_]*)\d"
|
||||
|
|
|
|||
|
|
@ -26,6 +26,9 @@ New features
|
|||
:bug:`2661`
|
||||
- :doc:`plugins/play`: Added ``-R``/``--randomize`` flag to shuffle the playlist
|
||||
order before passing it to the player.
|
||||
- Use ffprobe to recognize format of any import music file that has no
|
||||
extension. If the file cannot be recognized as a music file, leave it alone.
|
||||
:bug:`4881`
|
||||
|
||||
Bug fixes
|
||||
~~~~~~~~~
|
||||
|
|
|
|||
BIN
test/rsrc/no_ext
Normal file
BIN
test/rsrc/no_ext
Normal file
Binary file not shown.
0
test/rsrc/no_ext_not_music
Normal file
0
test/rsrc/no_ext_not_music
Normal file
|
|
@ -351,6 +351,34 @@ class ImportTest(PathsMixin, AutotagImportTestCase):
|
|||
self.prepare_album_for_import(1)
|
||||
self.setup_importer()
|
||||
|
||||
def test_recognize_format(self):
|
||||
resource_path = os.path.join(_common.RSRC, b"no_ext")
|
||||
self.setup_importer()
|
||||
self.importer.paths = [resource_path]
|
||||
self.importer.run()
|
||||
assert self.lib.items().get().path.endswith(b".mp3")
|
||||
util.remove(os.path.join(_common.RSRC, b"no_ext.mp3"))
|
||||
|
||||
def test_recognize_format_already_exist(self):
|
||||
resource_path = os.path.join(_common.RSRC, b"no_ext")
|
||||
new_path = os.path.join(_common.RSRC, b"no_ext.mp3")
|
||||
util.copy(resource_path, new_path)
|
||||
self.setup_importer()
|
||||
self.importer.paths = [resource_path]
|
||||
self.importer.run()
|
||||
assert self.lib.items().get().path.endswith(b".mp3")
|
||||
with capture_log() as logs:
|
||||
self.importer.run()
|
||||
assert "Import file with matching format to original target" in logs
|
||||
util.remove(new_path)
|
||||
|
||||
def test_recognize_format_not_music(self):
|
||||
resource_path = os.path.join(_common.RSRC, b"no_ext_not_music")
|
||||
self.setup_importer()
|
||||
self.importer.paths = [resource_path]
|
||||
self.importer.run()
|
||||
assert len(self.lib.items()) == 0
|
||||
|
||||
def test_asis_moves_album_and_track(self):
|
||||
self.importer.add_choice(importer.Action.ASIS)
|
||||
self.importer.run()
|
||||
|
|
|
|||
Loading…
Reference in a new issue