From 138ffbbba32aeb3eb0799e626c6c8edfe02328bf Mon Sep 17 00:00:00 2001 From: Jaidyn Ann Date: Sat, 21 Nov 2020 19:59:57 -0600 Subject: [PATCH] Init --- README.txt | 29 ++++++++ album_attr/Makefile | 131 +++++++++++++++++++++++++++++++++ album_attr/main.cpp | 41 +++++++++++ songattr | 171 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 372 insertions(+) create mode 100644 README.txt create mode 100644 album_attr/Makefile create mode 100644 album_attr/main.cpp create mode 100755 songattr diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..58042c0 --- /dev/null +++ b/README.txt @@ -0,0 +1,29 @@ +================================================================================ +SONGATTR +================================================================================ +songattr.sh uses FFMPEG to fill the Audio:* & Media:* attributes for audio +files. It will populate Audio:Artist, Audio:Album, Audio:Track, Media:Title, +and Media:Year, as possible. Additionally, if lyrics are embedded in the file, +it will set the non-standard Audio:Lyrics attribute. + +In addition, it can set the Album:Artist and Media:Year attributes for the +parent directory (assumed to be the Album folder), using a custom audio/x-album +filetype. You can add the Album type to your system by building and running +album_attr/ once. + +======================================== +USAGE +======================================== +usage: songattr [-ha] file +-h print more detailed help info +-a treat the parent directory as an Album file + +Since songattr can only take one file at a time, you'll probably end up using +it like this: + $ for file in ./*.mp3; do songattr "$file"; done + +======================================== +BORING STUFF +======================================== +MIT License +Jaidyn Ann, jadedctrl@teknik.io diff --git a/album_attr/Makefile b/album_attr/Makefile new file mode 100644 index 0000000..8918c73 --- /dev/null +++ b/album_attr/Makefile @@ -0,0 +1,131 @@ +## Haiku Generic Makefile v2.6 ## + +## Fill in this file to specify the project being created, and the referenced +## Makefile-Engine will do all of the hard work for you. This handles any +## architecture of Haiku. +## +## For more information, see: +## file:///system/develop/documentation/makefile-engine.html + +# The name of the binary. +NAME = album_attr + +# The type of binary, must be one of: +# APP: Application +# SHARED: Shared library or add-on +# STATIC: Static library archive +# DRIVER: Kernel driver +TYPE = APP + +# If you plan to use localization, specify the application's MIME signature. +APP_MIME_SIG = + +# The following lines tell Pe and Eddie where the SRCS, RDEFS, and RSRCS are +# so that Pe and Eddie can fill them in for you. +#%{ +# @src->@ + +# Specify the source files to use. Full paths or paths relative to the +# Makefile can be included. All files, regardless of directory, will have +# their object files created in the common object directory. Note that this +# means this Makefile will not work correctly if two source files with the +# same name (source.c or source.cpp) are included from different directories. +# Also note that spaces in folder names do not work well with this Makefile. +SRCS = main.cpp + +# Specify the resource definition files to use. Full or relative paths can be +# used. +RDEFS = + +# Specify the resource files to use. Full or relative paths can be used. +# Both RDEFS and RSRCS can be utilized in the same Makefile. +RSRCS = + +# End Pe/Eddie support. +# @<-src@ +#%} + +# Specify libraries to link against. +# There are two acceptable forms of library specifications: +# - if your library follows the naming pattern of libXXX.so or libXXX.a, +# you can simply specify XXX for the library. (e.g. the entry for +# "libtracker.so" would be "tracker") +# +# - for GCC-independent linking of standard C++ libraries, you can use +# $(STDCPPLIBS) instead of the raw "stdc++[.r4] [supc++]" library names. +# +# - if your library does not follow the standard library naming scheme, +# you need to specify the path to the library and it's name. +# (e.g. for mylib.a, specify "mylib.a" or "path/mylib.a") +LIBS = be $(STDCPPLIBS) + +# Specify additional paths to directories following the standard libXXX.so +# or libXXX.a naming scheme. You can specify full paths or paths relative +# to the Makefile. The paths included are not parsed recursively, so +# include all of the paths where libraries must be found. Directories where +# source files were specified are automatically included. +LIBPATHS = + +# Additional paths to look for system headers. These use the form +# "#include
". Directories that contain the files in SRCS are +# NOT auto-included here. +SYSTEM_INCLUDE_PATHS = + +# Additional paths paths to look for local headers. These use the form +# #include "header". Directories that contain the files in SRCS are +# automatically included. +LOCAL_INCLUDE_PATHS = + +# Specify the level of optimization that you want. Specify either NONE (O0), +# SOME (O1), FULL (O3), or leave blank (for the default optimization level). +OPTIMIZE := + +# Specify the codes for languages you are going to support in this +# application. The default "en" one must be provided too. "make catkeys" +# will recreate only the "locales/en.catkeys" file. Use it as a template +# for creating catkeys for other languages. All localization files must be +# placed in the "locales" subdirectory. +LOCALES = + +# Specify all the preprocessor symbols to be defined. The symbols will not +# have their values set automatically; you must supply the value (if any) to +# use. For example, setting DEFINES to "DEBUG=1" will cause the compiler +# option "-DDEBUG=1" to be used. Setting DEFINES to "DEBUG" would pass +# "-DDEBUG" on the compiler's command line. +DEFINES = + +# Specify the warning level. Either NONE (suppress all warnings), +# ALL (enable all warnings), or leave blank (enable default warnings). +WARNINGS = + +# With image symbols, stack crawls in the debugger are meaningful. +# If set to "TRUE", symbols will be created. +SYMBOLS := + +# Includes debug information, which allows the binary to be debugged easily. +# If set to "TRUE", debug info will be created. +DEBUGGER := + +# Specify any additional compiler flags to be used. +COMPILER_FLAGS = + +# Specify any additional linker flags to be used. +LINKER_FLAGS = + +# Specify the version of this binary. Example: +# -app 3 4 0 d 0 -short 340 -long "340 "`echo -n -e '\302\251'`"1999 GNU GPL" +# This may also be specified in a resource. +APP_VERSION := + +# (Only used when "TYPE" is "DRIVER"). Specify the desired driver install +# location in the /dev hierarchy. Example: +# DRIVER_PATH = video/usb +# will instruct the "driverinstall" rule to place a symlink to your driver's +# binary in ~/add-ons/kernel/drivers/dev/video/usb, so that your driver will +# appear at /dev/video/usb when loaded. The default is "misc". +DRIVER_PATH = + +## Include the Makefile-Engine +DEVEL_DIRECTORY := \ + $(shell findpaths -r "makefile_engine" B_FIND_PATH_DEVELOP_DIRECTORY) +include $(DEVEL_DIRECTORY)/etc/makefile-engine diff --git a/album_attr/main.cpp b/album_attr/main.cpp new file mode 100644 index 0000000..9d56251 --- /dev/null +++ b/album_attr/main.cpp @@ -0,0 +1,41 @@ +#include +#include +#include +#include + +// add the given attribute to a BMessage for use as attr info +// borrowed from mailserver, thanks! +static void addAttribute +( BMessage& msg, const char* name, const char* publicName, int32 type, int32 width ) +{ + msg.AddString( "attr:name", name ); + msg.AddString( "attr:public_name", publicName ); + msg.AddInt32( "attr:type", type ); + msg.AddInt32( "attr:width", width ); + msg.AddInt32( "attr:alignment", B_ALIGN_LEFT ); + msg.AddBool( "attr:extra", false ); + msg.AddBool( "attr:viewable", true ); + msg.AddBool( "attr:editable", true ); +} + +// install the x-album filetype for folders containing music +int main ( int argc, char** argv ) +{ + BMessage info; + BMimeType mime( "audio/x-album" ); + if ( mime.IsInstalled() ) return 2; + + std::cout << "DAD"; + mime.GetAttrInfo( &info ); + + mime.SetShortDescription( "Album" ); + mime.SetLongDescription( "Generic audio container" ); + + addAttribute( info, "Audio:Artist", "", B_STRING_TYPE, 200 ); + addAttribute( info, "Album:Title", "", B_STRING_TYPE, 200 ); + addAttribute( info, "Media:Year", "Year", B_INT32_TYPE, 200 ); + + mime.SetAttrInfo( &info ); + + return 0; +} diff --git a/songattr b/songattr new file mode 100755 index 0000000..e30eebf --- /dev/null +++ b/songattr @@ -0,0 +1,171 @@ +#!/bin/sh +# -------------------------------------- +# name: music2attr +# main: jadedctrl<@teknik.io> +# lisc: MIT +# date: 2020-11 +# -------------------------------------- + +# ====================================== +# FFMPEG PARSING +# ====================================== +# prints metadata output from FFMPEG of the given file +function song_metadata { + local file="$1" + + ffmpeg -i "$file" 2>&1 \ + | sed '1,/Metadata/d' \ + | head -n -1 \ + | sed 's% : %:%' +} + +# get value of song's metadata key, from it's `song_metadata` output +function get_value { + local trait="$1" + + sed -n "/^ *${trait}.*:/I,\$p" \ + | sed "s/$trait//i" \ + | print_until_nonempty +} + +# ====================================== +# ATTRIBUTES +# ====================================== + + +# set a song's metadata attribute, if file contains it +# pipe in output from `song_metadata` +function set_attr { + local file="$1" + local key="$2" + local attr="$3" + local type="$4" + + local value="$(get_value "$key")" + + if test "$type" = "time"; then set_time_attr "$file" "$attr" "$value"; return; fi + if echo "$type"|grep -q "^int";then set_int_attr "$file" "$attr" "$value" "$type";return;fi + + if test -n "$value"; then + addattr -t "$type" "$attr" "$value" "$file" + fi +} + +# we have some special sanitization to do for time attributes (helper of 'set_attr') +function set_time_attr { + local file="$1"; local attr="$2"; local value="$3" + + if test 5 -eq "$(echo "$value" | wc -c)"; then + value="${value}-01-01" + fi + if test -n "$value"; then + addattr -t "time" "$attr" "$value" "$file" + fi +} + +# we have some special sanitization to do for ints, too (helper of 'set_attr') +function set_int_attr { + local file="$1"; local attr="$2"; local value="$3"; local type="$4" + + value="$(echo "$value" | sed 's%[A-z/, -].*%%')" + if test -n "$value"; then + addattr -t "$type" "$attr" "$value" "$file" + fi +} + +# set a file's entire all song-related attributes +function set_song_attrs { + local file="$1" + meta="$(song_metadata "$file")" + + echo "$meta" | set_title_attr "$file" "Media:title" + echo "$meta" | set_year_attr "$file" "Media:Year" + echo "$meta" | set_lyrics_attr "$file" "Audio:Lyrics" + echo "$meta" | set_artist_attr "$file" "Audio:Artist" + echo "$meta" | set_album_attr "$file" "Audio:Album" + echo "$meta" | set_track_attr "$file" "Audio:Track" +} + +# set all of an album's necessary attributes +function set_album_attrs { + song="$1" + album="$(dirname "$song")" + meta="$(song_metadata "$song")" + + addattr -t mime "BEOS:TYPE" "audio/x-album" "$album" + echo "$meta" | set_artist_attr "$album" "Album:Artist" + echo "$meta" | set_year_attr "$album" "Media:Year" +} + +function set_title_attr { set_attr "$1" "TITLE" "$2" "string"; } +function set_year_attr { set_attr "$1" "DATE" "$2" "int32"; } +function set_lyrics_attr { set_attr "$1" "LYRICS" "$2" "string"; } +function set_artist_attr { set_attr "$1" "ARTIST" "$2" "string"; } +function set_album_attr { set_attr "$1" "ALBUM" "$2" "string"; } +function set_track_attr { set_attr "$1" "TRACK" "$2" "int32"; } + +# ====================================== +# UTIL +# ====================================== +# selects all fields except the first, until a a line's first field is non-empty +# delimiter used is a colon. +function print_until_nonempty { + awk -F ':' \ + '{ lines[NR] = $0 } + END { i = 1 + split( lines[i], L ) + while ( i <= NR && match( L[1], /^( )+$/ ) ) { + for ( j = 2; j <= length(L); j = j + 1 ) { + if ( j > 2 ) + printf ":" + printf L[j] + } + printf "\n" + i = i + 1 + split( lines[i], L ) + } + }' +} + +# ====================================== +# INVOCATION +# ====================================== +function help { + echo "usage: $(basename $0) [-hi] [-A|a] file" + echo + echo \ +"Use a song file's metadata to populate related attributes. The attributes set +are 'Media:Title', 'Audio:Artist', 'Audio:Album', 'Audio:Track', and 'Media:year'. + +In addition, the parent directory is assumed to be the 'album' folder, its +type is set to 'audio/x-album', and has its attributes populated with relevant +metadata. The attributes set to 'album' folders are 'Album:Artist' and +'Media:Year', based on the song files' metadata. + +$(basename $0) requires 'ffmpeg' to be installed. + +-a don't edit the parent directory's attributes +-A only edit the parent directory's attributes +-h prints this message." + exit 1 +} + +ALBUM=1 +SONG=1 + +while getopts ":haAi" arg; do + case $arg in + a) ALBUM=0;; + A) ALBUM=1; SONG=0;; + + h) help;; + esac +done + +shift $(($OPTIND - 1)) + +if test -z "$@"; then help; fi +if test ! -e /bin/ffmpeg; then help; fi + +if test "$SONG" -eq 1; then set_song_attrs "$@"; fi +if test "$ALBUM" -eq 1; then set_album_attrs "$@"; fi