23 Commits
v0.1 ... v0.2.1

Author SHA1 Message Date
1b23e84331 Update README.md 2023-04-16 21:34:27 +02:00
ca41f7a5f3 Reupload dockerfile with credits to author
Co-authored-by: Pypeaday <32268273+pypeaday@users.noreply.github.com>
2023-04-16 21:05:39 +02:00
915b4807f2 Delete dockerfile 2023-04-16 20:41:10 +02:00
49d3f6e2be Update musort-docker.py 2023-04-16 20:10:17 +02:00
ff2b0ce943 Merge pull request #4 from pypeaday/dockerize
Docker installation added
2023-04-16 20:06:21 +02:00
860bc8bd68 Docker installation from source updated 2023-04-16 19:42:07 +02:00
63e9b3a9d7 Separate python file for docker installation 2023-04-16 19:35:50 +02:00
d47e1ce80d Update dockerfile
changed the working directory to / (root) and entrypoint to /musort.py
2023-04-16 19:34:02 +02:00
8f7666005f Fix alias tip in docker note in readme 2023-04-12 07:09:04 -05:00
5bd6788f13 update comment 2023-04-12 07:07:48 -05:00
d9dee8e00a add docker note to readme 2023-04-12 07:07:40 -05:00
bf3b769739 add dockerfile 2023-04-12 07:04:53 -05:00
ce68184ca3 Update README.md 2023-01-28 19:06:56 +01:00
f4e4aa51bc Updated version number 2023-01-28 18:31:55 +01:00
ee8753d561 Error prevention: invalid separator 2023-01-28 18:30:06 +01:00
f968b002a5 Merge pull request #2 from notssh/main
Avoid forbidden path symbols
2023-01-28 17:21:37 +00:00
e87cc49b28 Update musort.py 2023-01-28 16:44:53 +00:00
3c79631a84 Update - avoid forbidden path symbols 2023-01-28 14:48:39 +00:00
b13711f3d6 Removed unnessesary code 2023-01-27 21:04:28 +01:00
0aeb8a15ce Merge pull request #1 from dudozermaks/readability
Removed repeating code and improved readability
2023-01-27 21:02:58 +01:00
239a71bec8 removed repeting code and improved readability 2023-01-27 16:43:07 +03:00
6388a53867 Merge branch 'main' of github.com:tdeerenberg/Musort 2023-01-25 23:42:31 +01:00
dfde71a213 Update README.md 2023-01-25 13:44:54 +01:00
4 changed files with 246 additions and 56 deletions

View File

@ -1,7 +1,7 @@
https://user-images.githubusercontent.com/113618658/214463785-49c419e9-c959-4849-91d2-f3407ecaa73d.mp4 https://user-images.githubusercontent.com/113618658/214463785-49c419e9-c959-4849-91d2-f3407ecaa73d.mp4
# Musort # Musort
A Python3 program that renames all selected music/audio files in a folder with a specified naming convention. Names are generated from the metadata (ID3) from the audio files. Before using this program, use a metadata editor like MusicBrainz Picard, Beets or EasyTAG to add the correct metadata to the audio files. Ogranize your music library. A Python3 program that renames all selected music/audio files in a folder with a specified naming convention. Names are generated from the metadata (ID3) from the audio files. Before using this program, use a metadata editor like MusicBrainz Picard, Beets or EasyTAG to add the correct metadata to the audio files.
## Features ## Features
@ -43,6 +43,17 @@ cd Musort
pip install requirements.txt pip install requirements.txt
``` ```
After that, run the program with `python3 musort.py`. After that, run the program with `python3 musort.py`.
### Method 3: Docker installation
``` Bash
git clone https://github.com/tdeerenberg/Musort.git
cd Musort
docker build -t musort .
```
After the docker installation is complete, musort can be run with: `docker run --name musort --rm -v "/:/HostMountedFS" -it musort`
> Tip: You could alias something like `alias musortd="docker run --name musort --rm -v "/:/HostMountedFS" -it musort"` then use `musortd` juse like `musort` usage is explained above
## Manual (options and arguments) `musort --help` ## Manual (options and arguments) `musort --help`
``` ```
USAGE: USAGE:
@ -87,6 +98,7 @@ year year or date as string
* Rename single file * Rename single file
* Other installation methods (Like AUR, Docker, etc.) * Other installation methods (Like AUR, Docker, etc.)
* Open for suggestions! * Open for suggestions!
* Feel free to open a pull request or issue!
## Authors ## Authors

17
dockerfile Normal file
View File

@ -0,0 +1,17 @@
FROM python:3.10-slim
# Probably should do update/upgrade for any CVEs
WORKDIR /
# Set up requirements
COPY requirements.txt requirements.txt
# hadolint ignore=DL3013
RUN python3 -m pip install pip --upgrade --no-cache-dir && \
python3 -m pip install -r requirements.txt --no-cache-dir
# Copy in code
COPY ./src/musort-docker.py /musort.py
# docker run --name musort --rm -it musort --help
ENTRYPOINT ["python3", "/musort.py"]

190
src/musort-docker.py Normal file
View File

@ -0,0 +1,190 @@
#!/usr/bin/env python
# Licensed under GPLv3
# Copyright (C) 2023 tdeerenberg
from tinytag import TinyTag
import os
import sys, getopt
import logging
supported_formats = ["flac", "mp3", "mp2", "mp1", "opus", "ogg", "wma"]
version = "Musort v0.2 (c) tdeerenberg"
help=\
"""Musort (c) 2023 tdeerenberg (github.com/tdeerenberg)
DESCRIPTION:
A Python3 program that renames all selected music/audio files in a folder with a specified naming convention
USAGE:
musort [DIRECTORY] [NAMING_CONVENTION] [OPTIONAL_OPTIONS]...
USAGE EXAMPLES:
musort ~/music track.title.year -s _ -r
musort /local/music disc.artist.title.album -r
musort ~/my_music track.title
OPTIONAL OPTIONS:
-h, --help Show the help menu
-s, --separator Set the separator for the filename (ex. '-s .' -> 01.track.flac and '-s -' -> 01-track.mp3)
Default separator ( . ) will be used if none is given
-r, --recursive Rename files in subdirectories as well
-v, --version Prints the version number
NAMING CONVENTION:
FORMAT_OPTION.FORMAT_OPTION... The amount of format options does not matter.
It can be one, two, three, even all of them.
(See FORMAT OPTIONS below for all options)
FORMAT OPTIONS:
album album as string
albumartist album artist as string
artist artist name as string
audio_offset number of bytes before audio data begins
bitdepth bit depth for lossless audio
bitrate bitrate in kBits/s
comment file comment as string
composer composer as string
disc disc number
disc_total the total number of discs
duration duration of the song in seconds
filesize file size in bytes
genre genre as string
samplerate samples per second
title title of the song
track track number as string
track_total total number of tracks as string
year year or date as string
SUPPORTED AUDIO FORMATS:
MP3/MP2/MP1 (ID3 v1, v1.1, v2.2, v2.3+)
Wave/RIFF
OGG
OPUS
FLAC
WMA
MP4/M4A/M4B/M4R/M4V/ALAC/AAX/AAXC"""
class Music:
def get_files(self, directory):
"""Scans the set directory for compatible audio files"""
directory = "/HostMountedFS"+os.path.abspath(directory)
self.files = list(map(lambda x: os.path.join(directory, x),os.listdir(directory)))
def get_files_recursive(self, directory):
"""Scans the set directory with subdirectory for compatible audio files"""
files = []
directory = "/HostMountedFS"+os.path.abspath(directory)
for a, b, c in os.walk(directory):
for d in c:
files.append(os.path.join(a,d))
self.files = files
def get_compatible(self):
music = []
for file in self.files:
file_extension = file.split(".")[-1]
if file_extension in supported_formats:
music.append(file)
self.compatible = music
def set_separator(self, sep):
"""Sets the separator for naming the audio files
(ex. 01-songname.mp3 or 01.songname.flac)"""
if sep in ['\\', '/', '|', '*', '<', '>', '"', '?']:
sep = "_"
logging.warning("Given separator contains invalid filename symbols, defaulting to .")
self.separator = sep
def set_format(self, val):
"""Sets the naming convention of the audio files
(ex. title-artist or artist-track-title)"""
self.format = val.split(".")
# Rename files
def rename_music(self):
"""Rename all compatible music files"""
"""Get the file extension (ex. .flac, .mp3, etc)"""
for file in self.compatible:
ext = file.split(".")
ext = "." + ext[-1]
"""Let TinyTag module read the audio file"""
track = TinyTag.get(file)
"""Print the progress (Current track)"""
logging.info(f"Current track: '{track.artist}' - '{track.title}'")
rename = []
"""Uses the given format to set new filename"""
for f in self.format:
if f == "track":
rename.append(f"{int(track.track):02}")
else:
"""getattr gets attribute in track with name f"""
rename.append(getattr(track, f))
rename.append(self.separator)
rename.pop()
rename = ''.join(rename)+ext
"""Replacing forbidden path characters in UNIX and Windows with underscores"""
for forbidden_character in ['\\', '/', '|', '*', '<', '>', '"', '?']:
if forbidden_character in rename:
logging.warning(f"Track contains forbidden path character ({forbidden_character}) in the new file name, replaced symbol with _")
rename = rename.replace(forbidden_character, "_")
"""Get the absolute path and rename the audio file"""
dst = os.path.join(os.path.abspath(os.path.dirname(file)), rename)
os.rename(file, dst)
logging.info("Actions finished")
def main():
level = logging.DEBUG
logging.basicConfig(level=level, format='[%(levelname)s] %(asctime)s - %(message)s')
"""Runs the whole program"""
argv = sys.argv[3:]
try:
opts, args = getopt.getopt(argv, "s:r", ["sep=", "recursive="])
except getopt.GetoptError as err:
logging.error(err)
exit()
music = Music()
for opt, arg in opts:
if opt in ['-s', '--separator']:
logging.info(f"Using {arg} as separator")
music.set_separator(arg)
if opt in ['-r', '--recursive']:
logging.info("Running recursively")
music.get_files_recursive(sys.argv[1])
music.get_compatible()
if sys.argv[1] == "-h" or sys.argv[1] == '--help':
print(help)
exit()
if sys.argv[1] == '-v' or sys.argv[1] == '--version':
print(version)
exit()
try:
music.compatible
except:
logging.info("Running not recursively")
music.get_files(sys.argv[1])
music.get_compatible()
try:
music.separator
except:
logging.info("Using default separator")
music.set_separator(".")
music.set_format(sys.argv[2])
music.rename_music()
if __name__ == "__main__":
main()

View File

@ -8,7 +8,9 @@ import os
import sys, getopt import sys, getopt
import logging import logging
version = "Musort v0.1 (c) tdeerenberg" supported_formats = ["flac", "mp3", "mp2", "mp1", "opus", "ogg", "wma"]
version = "Musort v0.2 (c) tdeerenberg"
help=\ help=\
"""Musort (c) 2023 tdeerenberg (github.com/tdeerenberg) """Musort (c) 2023 tdeerenberg (github.com/tdeerenberg)
@ -80,28 +82,21 @@ class Music:
def get_compatible(self): def get_compatible(self):
music = [] music = []
for file in self.files: for file in self.files:
match file.split("."): file_extension = file.split(".")[-1]
case [*_, "flac"]:
music.append(file) if file_extension in supported_formats:
case [*_, "mp3"]: music.append(file)
music.append(file)
case [*_, "mp1"]:
music.append(file)
case [*_, "mp2"]:
music.append(file)
case [*_, "opus"]:
music.append(file)
case [*_, "ogg"]:
music.append(file)
case [*_, "wma"]:
music.append(file)
self.compatible = music self.compatible = music
def set_separator(self, sep): def set_separator(self, sep):
"""Sets the separator for naming the audio files """Sets the separator for naming the audio files
(ex. 01-songname.mp3 or 01.songname.flac)""" (ex. 01-songname.mp3 or 01.songname.flac)"""
if sep in ['\\', '/', '|', '*', '<', '>', '"', '?']:
sep = "_"
logging.warning("Given separator contains invalid filename symbols, defaulting to .")
self.separator = sep self.separator = sep
self.separator_status = True
def set_format(self, val): def set_format(self, val):
"""Sets the naming convention of the audio files """Sets the naming convention of the audio files
(ex. title-artist or artist-track-title)""" (ex. title-artist or artist-track-title)"""
@ -125,46 +120,21 @@ class Music:
"""Uses the given format to set new filename""" """Uses the given format to set new filename"""
for f in self.format: for f in self.format:
match f:
case "track": if f == "track":
rename.append(f"{int(track.track):02}") rename.append(f"{int(track.track):02}")
case "album": else:
rename.append(track.album) """getattr gets attribute in track with name f"""
case "albumartist": rename.append(getattr(track, f))
rename.append(track.albumartist)
case "artist":
rename.append(track.artist)
case "audio_offset":
rename.append(track.audio_offset)
case "bitdepth":
rename.append(track.bitdepth)
case "bitrate":
rename.append(track.bitrate)
case "comment":
rename.append(track.commment)
case "composer":
rename.append(track.composer)
case "disc":
rename.append(track.disc)
case "disc_total":
rename.append(track.disc_total)
case "duration":
rename.append(track.duration)
case "filesize":
rename.append(track.filesize)
case "genre":
rename.append(track.genre)
case "samplerate":
rename.append(track.samplerate)
case "title":
rename.append(track.title)
case "track_total":
rename.append(track.track_total)
case "year":
rename.append(track.year)
rename.append(self.separator) rename.append(self.separator)
rename.pop() rename.pop()
rename = ''.join(rename)+ext rename = ''.join(rename)+ext
"""Replacing forbidden path characters in UNIX and Windows with underscores"""
for forbidden_character in ['\\', '/', '|', '*', '<', '>', '"', '?']:
if forbidden_character in rename:
logging.warning(f"Track contains forbidden path character ({forbidden_character}) in the new file name, replaced symbol with _")
rename = rename.replace(forbidden_character, "_")
"""Get the absolute path and rename the audio file""" """Get the absolute path and rename the audio file"""
dst = os.path.join(os.path.abspath(os.path.dirname(file)), rename) dst = os.path.join(os.path.abspath(os.path.dirname(file)), rename)
@ -212,5 +182,6 @@ def main():
music.set_format(sys.argv[2]) music.set_format(sys.argv[2])
music.rename_music() music.rename_music()
if __name__ == "__main__": if __name__ == "__main__":
main() main()