From a5b136d992f3cb4b0905be14863e57913cf1edca Mon Sep 17 00:00:00 2001 From: TheCaptain989 Date: Tue, 30 Aug 2022 14:05:44 -0500 Subject: [PATCH 1/4] Switch to hybrid S6 - Comments updated --- .../s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run | 4 ++-- root/usr/local/bin/flac2mp3.sh | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run b/root/etc/s6-overlay/s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run index 75e655e..edb63c7 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run +++ b/root/etc/s6-overlay/s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run @@ -4,8 +4,8 @@ cat <>> Flac2MP3 Mod by TheCaptain989 <<< Repos: -Dev/test: https://github.com/TheCaptain989/lidarr-flac2mp3 -Prod: https://github.com/linuxserver/docker-mods/tree/lidarr-flac2mp3 + Dev/test: https://github.com/TheCaptain989/lidarr-flac2mp3 + Prod: https://github.com/linuxserver/docker-mods/tree/lidarr-flac2mp3 Version: {{VERSION}} ---------------- diff --git a/root/usr/local/bin/flac2mp3.sh b/root/usr/local/bin/flac2mp3.sh index f95fef8..6fc9516 100755 --- a/root/usr/local/bin/flac2mp3.sh +++ b/root/usr/local/bin/flac2mp3.sh @@ -258,7 +258,7 @@ elif [[ "${flac2mp3_type,,}" = "lidarr" ]]; then [ -z "$flac2mp3_tracks" ] && flac2mp3_tracks="$lidarr_trackfile_path" else # Called in an unexpected way - echo -e "Error|Unknown or missing 'lidarr_eventtype' environment variable: ${flac2mp3_type}\nNot called within Lidarr?\nTry using Batch Mode option: -f " + echo -e "Error|Unknown or missing 'lidarr_eventtype' environment variable: ${flac2mp3_type}\nNot called within Lidarr.\nTry using Batch Mode option: -f " exit 7 fi @@ -330,6 +330,7 @@ function check_rescan { done return $flac2mp3_return } +### End Functions # Check for required binaries if [ ! -f "/usr/bin/ffmpeg" ]; then @@ -439,6 +440,7 @@ fi #find "$lidarr_artist_path" -name "*.flac" -exec bash -c 'ffmpeg -loglevel warning -i "{}" -y -acodec libmp3lame -b:a 320k "${0/.flac}.mp3" && rm "{}"' {} \; #### BEGIN MAIN +# Build dynamic log message flac2mp3_message="Info|Lidarr event: ${lidarr_eventtype}" if [ "$flac2mp3_type" != "batch" ]; then flac2mp3_message+=", Artist: ${lidarr_artist_name} (${lidarr_artist_id}), Album: ${lidarr_album_title} (${lidarr_album_id})" @@ -457,6 +459,7 @@ fi flac2mp3_message+=", Track(s): ${flac2mp3_tracks}" echo "${flac2mp3_message}" | log +# Process tracks echo -n "$flac2mp3_tracks" | awk -v Debug=$flac2mp3_debug \ -v Recycle="$flac2mp3_recyclebin" \ -v Bitrate="$flac2mp3_bitrate" \ @@ -526,7 +529,6 @@ BEGIN { } } ' | log - #### END MAIN # Check for awk script completion From 64b36034eecba9460af7674317d6345580aaba10 Mon Sep 17 00:00:00 2001 From: TheCaptain989 Date: Sun, 27 Nov 2022 09:56:09 -0600 Subject: [PATCH 2/4] Release 2.2 - Switch to hybrid S6 - **Added --regex option** TheCaptain989/lidarr-flac2mp3#33 - **Added optional use of environment variable** TheCaptain989/lidarr-flac2mp3#33 - **Added new --tags option to resolve TheCaptain989/lidarr-flac2mp3#15** - Added checks for identical track name - Recycled files are now moved to subdirectory paths that more closely resemble the original path - Better error handling in awk script when calling system commands - Added logging for skipped tracks - Corrected some logging anomalies - Modified exit codes - Updated command line help - Fixed missing executable attribute on some script files - Updated README --- .assets/lidarr-synology-2.png | Bin 0 -> 17591 bytes README.md | 88 ++++-- SECURITY.md | 4 +- root/etc/cont-init.d/98-flac2mp3 | 1 - .../init-mod-lidarr-flac2mp3-add-package/run | 31 +- root/usr/local/bin/flac2mp3-tags.sh | 3 + root/usr/local/bin/flac2mp3.sh | 286 ++++++++++++++---- 7 files changed, 313 insertions(+), 100 deletions(-) create mode 100644 .assets/lidarr-synology-2.png create mode 100644 root/usr/local/bin/flac2mp3-tags.sh diff --git a/.assets/lidarr-synology-2.png b/.assets/lidarr-synology-2.png new file mode 100644 index 0000000000000000000000000000000000000000..eb9f2215a56e06ce9a7af3d41ba517812fe4e738 GIT binary patch literal 17591 zcmb`ucT`i|wmuxGN>x#i&Z`2_k=_w0N>{3M5JD&PPN<3kQly8jKmh425UPmO&_fHM z_a1uaeDOVJjC+5-@7#0lH^%n|?CiZt)>=Dju4g{;nGtU^UXhc~kpKVya^=?wS^xlE z765RI^EMIgNmIugC*0+hyVk3hfU*IG4crZZjhwn108klAdTBw3yT9Y|+Q1zEp!ogg z?^Y5Q1w8+j7W;Q;m*3Z^qEtm;om{nW4zo{$hQK4vlx9F4b zsQoxshGTf+RYl^95(uoq>!9rpX}-mem$3D3#}($XJ}SHr9&$YLtGKZk<83WFL-ahj z;+_?;x;>Mj(MwZ*T34Mv>lD2Xxtt9Wcl@tM+0AEA|C9hqTz?ZZ zU@6?!#*yM_;_5Gl{-nZP;4FIt0Kg00+kY2DiCWv(n3*!ur(ZaR7rw5~COjo+H>>ZE4YI}UJtuqZF}OA}^gPhThwG9?_{1YFey>|F<~ zg`ceE@Zi1~5HB=4t^K1pWpH)7qlRMYPn(?KEWE+GAZ&KM_r*k%9b?T3ZfGxZrSqrI zXWl_)LG|7tHz)o#wf@9^Mn0USy@PA?qV6BV{C6{GA;`L}=J(}g!`0U@!y6n4Iwn3r zWuA{+E2`Pzfx-13tkfE`>4Yu&+L_KsoCO$C)oi&m z)M&nf(&GV|?5}uJglC_QQQ+$RyEpvLmrpFVcNu+&W|mEoJ&&5JU!dM>O_n`J+8j7< zfyBN=Z2-h1uFv63`vrq5U+D<|O@1{#cw#we`gGY>9fH0~gV*Eaw*VAT_zXUQMlCn8 znTgp~NasciKzWKY0vm{-KUizMl=NNgBnW&GbcUGg&Azi)77d?VuR>fE=N!2dRqmSuE)%5d!;V9XQu82u%CnNWj6IG`!3rDjcUUu9~g`A zXu1BB^IW97{&u;bf1`Xof>F}Gkvv1`&n05O`ovQyq_^}tFKg+AYNS+Zpjy0u7jwr# zds6$FH@|xTf;jtT&zX*oA@;iSa#iw1jKt9?s<-_5Nh$r$kRzwF>vhp)4}Z@7S(x!U z*$N(DId=^#C7eUXci%AeW&s1{qF}qy8Q)~$r7-;wI2!v-FksEwsAQ_VB_?HQ?lSWd zPES4$INz0x6y!*=PI2y$jSB7G?fsaHn@0}DkIAQ#=8k%GkoNW3-fE5x=hn)pXxKjE zNEBi{=rnoMNkv`zcsGnK@OW#k!8`hTuPcDmpAEnu%9rG9+P>Md})zLauI#m7ae=0$9Aor!NwJB zGo6#y;%{sk6ZGV|%r{y^;sudQ&ZJuJ9DzFt5Ai4KLZZHZ%NV9)pGcXUq97ZG=Q% z@MVe_cxM*Z7!W-hSbT=REZ%mlt>CM;$waqy@atL=x|~sbvx`db;yZ^ z3G;krcfmDiZ`7)CA(elL%{1FG2e;HN_f7cepYKOf#MiU*)xgbL=Z++I4^LMZJvIjK zONvfbl79@AKD*kdMPO+B7O?I5)i3jtthXjBK>e^VBbu8eLkj=*Y%NG1H@xy_OG4Vj z&?fgja(vpF={`<(sujERD%i?#f7f%kO_wIM)nwCe$JUX;?l42Qbjm~}1*Y+aRuPQ$ z`MY(gIPUI7`Bl$fz$hkb^ujD$um@5PDy_=~*SeoY4f8NK+@FJYbIOlw;Lp2+mw z{(HVIe7DKGv&Vq}Y?qE2&^%84`@%r?iZs`3AL91a<{zI?3wHYl=^Iiax4xwhfSlxs za($=2!jT)kH52xXiYfwYP%zfozzQBO<>Tbiz_Q2_)&hzJb(8DQZ+QKX;zc-N4jTnO zh9^+@T&0p;ZA|0`^L3vdO9}*)RR+k%H^Wk2e5=}3@`+!uJ8#fR;bW@fs7Cu`#`Mf0 z$OK}OmP;I8^GtLmr%4@@H?F>$glLMK5_F+LOF-$CufvedLdJ}@h`Gl=UekWcvyDQJ zm(UwwP_}Gt2Xk@ur9kriNneP!+|EyN?Pm{mjN~0&LVtpdHMb|LY5LptD$yvuT|}h# z8#cA*B5`dWGa;lmQhDG>p>O`$uwk*M+Hiu+YpX7T(U69cK;vq;%2qO!6t4Q8qxmNl zyzRNHw6|qQ9u&X<3%1HO%!YaZRe@Ro9>1vveEGd`AHF2&C*Jp@p>VH!cWfwflFX!P z&9kUiWgbv9#+|gVe)>Ks$-?h(whwLQauxg9QjoQgTXLV@tJY)Q!r7BM1c%W(Wj}IMA$Z0pl~8eS#JIw zZ%{#(m0&+spZ%9Py5$mle|J!r4}Z<6b^hHRtUx5(r&UvJqN%~KEZmo`ekbFB8RknL zi(n&bO-TCOVtid+11RvM(qbk(gSbJq{8Lwig;CAc!I_|_Bh+`r4#i-D6p+cg^+WG{ zR=4}bH>Owq;zSe$VGn&kpzh7d&*Jyg%|-MynS{d{+Kfz9caK*U>9T4l%-f<$ zrN35NoGe)zgip{kw491?_7Eo!8e9prOxlW93hTx2k)U)Rgtss`+C2?S59U zNSf-clyIn~2>_Kc7Wip+1aJ5ohii8lnl3EHs~jrUc@msTl?ln&Chud>=!C~nbrPg3 zqA8c2W880jDtCT0bo=jOGy584yTaWU6UowdjuoxX?D3+VzRIN&X$=2dTKG&ZcEW6) z7xPzRYl9etr$I95Th-G3X;w}GPlEu7`)Wq9aE-d`JIf#+(3bnR82)|Bk!$26JB3hEgbd%VtcS4G#*^a+?$sxJA;P}7F}gSuSy z9YemTRm`}X@LA(GZKtr`pT#RVw^$*F*ZVYc6MBP@)BGQQ5<6l2-Ylutu@Hg9)zp>M z`t#dC1V45p(#~{$?RXtWs0lsL3e3*2iWYT5Ju@9iYZUh_@66sQO}ZskPc#4_eec6t zV`sM+ZdwtZOI@1dMw?YV{zjZv)bZ{JtA0e*XMww`cpkhNEwIr1k);khzV>eq^|V`d zJ*7V>H`)| zCiLP#7vJ%SU%rcL)X$Kh&}wM&(Y`b7l`FY=Gu!m*aI*C?I=D~LrZKSz^|LtGTRTGJ za>MFaewx1dTU&E&ylBHDNTI);)Wct{I0lyb5s|HS#1PB?vSQ|J`Yt*5d77iu(FH4Yu#12BCNG-!AMo=>;$tzShm@4D#IS~ z+aKA^u8$r~Pe%3qlw1c**yUSjR>9JXZ*O?&UzUW3&YH&hSI&MK4NQ0YLCJ}1le30Z>=@+cd(qDUC- zz75&Qj`fohp+O^&pa9F2;^rKN+Xeoj_R*1i@B9XOv+%-SG~hi_FF>l)+#?hMNkm`W z#d5wT?TX*9MC%qoXgpD_&S+Au?$!LF3G10)wW0gY#ndbAXkYB&}^4fAY z;FatsIcT%YD^!$>Eqo?4d9g3qs}AFFQ4?(!lY@WXr-AeRVW?n7YcH(mbCS4??j{ubV)oW&M( zxAcolJT!0LsS-5pecqAmZ_)caGiJKPuhbbTWw_}in*F;e$>E-6N(5M3#KUx|Vj(}& zX@~>alQU#fPAP|Q{!Zv9d-O;Jdn`QB3Pox-J=s}hHJ$62mss^|-$Pmcr4hvLTqt~j zwd9+0s3ij@(^^4|UZyi}`6r1{drk$$In^0W&*?iis0>VSr=ZHpmyl$p%JY!z8#`0k zmJ~2jZ8N7*xCrvH3bQyyF21F48|}v0fTXZxpDSX8Z-4|5O{c zGPpH>e4J!=F{fBGOXxe_iBV2mY-E@=UaUP}(^}LoKB;t#pcdkr$x!3?go4ahlV)cf z7(OCBh)Fiv^=|XP4)G6p%o;Sviu{tStfiOVlf*PEgeIAvA+L6YefWbeb(&fP8qEkV znirs@(!+X_cC<(3`G+IEDRDX~P5Tn7Gf(2CY^H+7O%t{QXcUy1!LEymCao@zDNHew z1oSE%IWaQ-Zr!e-8C6+0;RZhXe!&fIS+f5R5whmzf7G#k^TSeY^>@nK?j~%=HFErM$n^OD;L8BB(3A+l3P@!2V zey|j!bgN#*zWvlU94WD#?ImZs-T=jU1_p}DvDANfha*Owzp0M@410(7ml9hpHyyAz zR~cY?bYkR;b5De3m&O0IUzf~U`a_aT{~MCz|1Jt{U+MruAkBP@pbF+c_;?Z8Vh~44 zMf%u&_af3j*~bD9FHid=uo8?IFoMHvNLx=R&6rEW0-5 zah}O5uNfPmadLRA(B(!7FLlmZ10fD``z5@K=hnLJjehzm`0R)`HIu2C&P(Mq^E7zY zr1)u2g3kuFiZU;Y=>>qIW@ak9+g6!q+66PVJd6W>Tn%md3ySJY-JcU<;0yYWhsd-q z*&Oh1vs*VZPM9Qa`8n+2EwNkviuXicj3K!VkKu)$Xz$HHeX;4=;Ew%;iBMF~xp+u%6XJhQPwm94CPG|GJ6oDlm- zk)p^;`?jf&M^?cY~Ib zJ*CKA818CQts9s2&Z|i^cNDw6$-7vJY>l+#z~;8C|yJRlGA|MLVuI>yB)e z=HS6XT1`uF4!gotv5PM-vHOu9;uZh~Ps13GuRr`x6kuxK zobm5@Za1l&6|GgeJ-GbfHSvQ-%hS-}q_>hkVTp6V@0N-hvd?*1qwuM=sY-bvP0g#NM|&2plcqT|QHiwbxMFJTW)R+VIX zxhHHugb}MCe~PBpB8|;Y^l2tJ7_hrwvyKV#rTTD+mdnSwJ9wT27+J9!61#7k9KcrI z7|6UD}&X!20pIm;dg`d$8cq z&E>im+{{dT+Bi`IcqX?2gjYG*6Z6>X19EsGT^%fKF6pVAUX1=|O|pIuL)Yzgl+uPf zDMkWeKk~Kr1bKL4)NyaAwxjITmWM@^Dl*X1>$&EZhk@>^3!z~NW1PR+Sce;x7@fyC z$BkYz6(di3RKs?Co%FCLi*%fiM zY4@ZT)Jj=4Z5Bqa5Eb?QI_AYGqCbYLtF0z5b++} zFp0zX#|p8`*E=#21~J-mxR)L?!8{EM76u1I!c0F;ba5F8BpHA0KqB}1$v1RX159S6 zzb)08y>I#=+80o@LD=w=DzH`3ay~DciF|cKmW^ZgkjcYN1|5vXnp)1=ZWQboHl%%vu3?3fu))6dh)iFUR5O*UJ|F%g(KkJ{6IpR1Gf zYDjASGbOg}Kz(N`_iRG+H`J4uGzP@Zz^A}cjOoCPX-Rnk=4?0sPUXFio|kgGvD+pR4GOaD(3o<<$ zMr6x+hk=K%;HeP(DzdBDLE?pXCk*}WbvJ8WZ=ug^T5w$ay2{YY(XyNIz{&9_&_&L1 zs&!byjwVVpO=4R~s{*dRO*vliyU-4(MAM`I6{*8Syk2mc9C06AEp*}RVb zcO>I~;Nvnz{yQJHrT#ZbFV*#@J-EyL?-&UE{|A!K#df<)*UA-1p6A5d{V@X3FFUA{ z&`}nIB=$nlJbm}Y9Ky>zwmozW+3255F%eDNTWsk|_3bYi+sOgVk@mOvFK;_ZI`isU z9e<>MjWQNMMx9s&Jw17Ae?#ceY{(-YwREdnju~&Tp=4mQXRTR_-OI)8yu5_UKr?HV zT@3JB&G)%kN>RGE)sJ;0MnhJJAtZVK*ghT56|}=T`5bs$`h#p>`sqCF zl=SY*V)v+)}FnwLzKRMe9>c^w{!S*<3So=JWAIaVc?jZ~fiWi3~Mq zky>Ax0QRaSjr zc7H;VzF5OSdKKJ3pa?ZKZb|_~lfv42dny7v%hL<2SZmc2$*gus*Z1EJfTj$0O+?-`6ss%2GS zQs+oAyx$%9ELY4|hKOl!Ec)rG}K{yU)h5Cfb zrFe*e8*zFkYgQ^*-G_UYRH;IV9UA>c&ofPBFCTYE7q~-TmRt!CEH?40v^t z*S$74-SsyG^%TPz>f=pT0^B(?_ss=XqIt8jGh0(Qj$#E%1Mj-UuCPq^ohXdQ0M9GDZ zC;|>iLuy~75x6iUUC-zEy>Sw~McfaXt(DL|cyb3E$rc}V`HK(uqpg3#4#L@a%XVnWAlc&LV|)O=L2OI;%I==B z?8maC^9tQsUNb4)Zj_c%|m7kw5`7X?DQiD3w-%wQDAaPRwTfxiQ2>)5^i|sbEJI*pYDJSH(e%5ju`XI%`@$ z3VV`C^eyRF%Y$t5#MY&RB@72JqRNV+H%`X8@Z+)>@CMTY-rL(+^f&l$a6en5Gj4cz z3-G>8v1TN%mYwTaoF%I`JrPwpy$HE1>+Y|NdryCJjIglWwkyu@nd5134sBY;Gm9@$ zb>%9Cin5Y;;k$B`5Q#3<@)NbY7CR*`SFO8IPHg}QrUruVaR0KK(yIWb7}i<_(6$A1 z6GXf{H^KM8ir9`FhU!x3XncYE?1g%G`dy!==B#R^*Hljkzf6%#zAP>4s(@V61X_Nq z(~Q}2#?fHQP~a!0@ctpN^9fSno$`Kc)N6Z5dO97pP^+uVJ9M>Uu=k;}HJv%UtIS6E z!!LDijV!VHl=GZC)=seSn=XG)&%Iy;R}X%~ped6Mx)E!E!DN_%FOPWkrmKkSiIU}< zkeH^jwt`adREoo$^@D+zfb!UizT1Yap(G9m8nZR^ippW3`+{rzie`>x2YPZZlYe{H z0(+Wk9V4f8bJi}5!Z+N!6`iH_5%2Vw>g*>S_-IR}SOx{;>YIC5xv_#;Q!{(W6aC_< z6(x-N8@oHn#gC)dUhHPUD2Tdp=3GX0B-rq_3w37+cHZNyxm)-(Rci51cI?v)do<3G zOr$hkEJ8OVeR`-vBs0J z$jPtE&O)pV5qRn5MnZXAlKFB4cSLe)z@tg#vEPM)E~n-aC_`nkycl<`Z{zj;b{$dp zeqe57)_-D*j$=gM-yK;}oqCz=_|_^S8+oE?= z(p*EdMuSEOK67nZc;i*ardgFba+xW0TxKba;|ks;9CDf={uvL%ne zM%;0zIS^8ir4YY8C;s*;ac&@|Og)-!{nMfr-O(*VQ_I!G_4A33vD#JMYf4vKVNvew zOO}o~iQ6_sbB7niFx?CaP=G5i8T??{lSb?TLI8+2%OCB(^zBL!GucvfU1k4M{4r%; zRo|$J>(-*DcAyfbx#cUH&z%O=53z&8?D&0(SDaTld4g%6tmJ*qiRPcRDnzB9tZ{7b z;WX^FVWSOhMb?1|!U#tIGN7 z;`Ih#Z0(oc*n3{&!=4i$n?Q%+(#g|@wB(8Xup^^o;sJGZdi<}^(>3U?B3`s{_IVUv;L!nlHaz4*tKV4y=4O3mFuSo$zWn$+-~=ffXmH(v{qYB* zfycYmxDF5h24_ob=tIxpE7wDmW_rL)HFLp9n1+xu>>9v^;@ahtSu&jx9DqQew?7;Tpb(p zu13~raK_Lw*^-+2E$Maiv^qj$$jsUH=$|2091E1ynT}H`{7>?h|AV?Oepcg1 zpA?{V>25fFqr*dZT#LEm-*|4nxMvN5Jm@MYSTfz1yCpX5)q!`dLr=LAhvU- zOc!WV#ci{5H+Z5mDou)iMXo1_)`A+SYs4vxSTN*j|MD4}U;QlY2c>L!VfQAf=&a5M zuB}x}10pMKfH=6tM>wRw^aYHRmWPIrBbwT3%T7Qs@Y2|T$f>vh=JUWRG+AxFHUVNi zSJt~`W1PIC)k74gj|&Q&v|GRZ&Qiq|rC%%{w$vi4&EL*@%B_KzPCb(pVGQuP3NRZv z1MD?y8O_6Mw`(O*Cz4h?f)JriOSN8XQe_2(^eP7-Fuwub?y%vi2Q!m-X*x}38&NV@ zKi}T-sI)6Ap-5W6FMlz4Sz{gA^|kQJvlg@Dg&p!DI~O?ouVN^-FW-3ux3;{e@0;8V zKyVZ8EWrhZNewBcw@$1YlB#&kKG>DjAU}qWhlh-O?)EAk*EsjVdzDNSE$^DvoV1WI z>l=hSop(!xJyc>E>tF}|Q_X~c<;AhQDO_yU(ONYOIGNB#4)@`?Mq8k%#Yh=0$XWek3<2Wxf5* zPwj+PTL$1?^bZFU6~>F-!mE7CIgKn-?OYW)`)j_Z(})&Bn<5JQjk%i0X956#=A%eF z&0Mh^32YZwfC~r8{Rd0aj`j&+o~l>JCQ)hrwCzpgs$*y6)OUp3pLwPeUz85~T5$sh zXP}LRWpUWs9D&HvnL$6v<9 zu-#tmPEFK&Nq^E8JqDpa$Yhgoz3-p zT(P7A+kZrPE_)28I{y1=%2pQ+bXB=FZX^2S`FXv?LXauTNDd^Wd!8T6fh7XFb@T@FL$s z;U-C3b-yqK?G}nOfC}&OXL}{etd7bq<@Nb5tsD%D#6tJE*!w%$jm_y8B%O_m!g91*%q{xURmhrySS_NIcn zibvqsg8iX-t6O@D#XZ2@4~BIe|Iyxs+Hkf9xH)nQH2@r8b? zn%2+-!M7Lzrp@@XUC3HDZvCcod_WWES74@g3N(z}g!^Nx72S;xC@{>dmhzH2ywrXh)P+t+T0IqE}qfT;7k4uVN{;^+<7;oUW zalOL-`tLf-f3so!S@d0#GOoO5@i)#3|H}c#@Rw)fUfeS++|LnbjELpi6Wnp~l)Gde z=I$F+I^#3JkxL|&OrQFCz_fxDeg}C^kg<5)K2En@p#7o zHbSjLO;rfhPYCBXb9lG*TVlg4@R^%@3MyK~p|yneqV4kVq)ixKJiIujFLDRtiXqsiaha5=f|(h}*0)#@Sla3O;a-*cWNZ%qnSo5@Jn zw**yubjd_s@MT#01)N|7V}yZq$|g|GCkN(b9hHJ6$(bKd4_&4*d5yNNR*C#LeYw4i zOaTB>X?kb-d6bc+*>%%F7hlj`Q~^kA52YD&bg(u!^CAJ`!FK8dcf+g*iYiH8bi|ro zFAIXP`g}nLSIo_qA!i!=45!-#)`3aPzO9IN;cHma>-h^4?v$(RSTm(+*H*rpqw?v` z=I{~355z%2_I15Q)5zfE+^g-HE2qgjGt@mhciqv$+4}A0qM#>@0knur9)H=3u9xE{ z5&0>y&3)?Q7{MU4HBVT&hJ8zqWg*JTp{mb1E0amI4RieH82Sxu+9lklpE8DvR$51Q zDY}$&btxpQfV{S?zF<61hM2lp)hwD)EG1yxd?d9RTXaGr>|0?HSDkO=ps=qsO;_^| zV}KohUulrku@m2?So$R-#N9n2Ik}n!QFXWvMvaBwAkK=b0h`j!tA0jFzC&EN0zq1J z{wvyH1xFj_ebt7NbdX`n!f~O1QOIAC+bj7~r(yl{r^#ZNA45tSbg;{=+#9@gvJLxjw3*A011}cNA8tYFl)2+qe z{n`>z;Z(26OJFN7lpUYU4D^zkUO; zKRl?{Y3X#jCtqLaU?L8OJKXDF7Q7j2EAR<=WT8Un8SnuTG>T*DZBjRDqkW!U)cI~T3& zEr((Bv;718<-;iNc%G<^Dz&p)5TX;pb^#C${1QJ)7A3yX#!tZPut&AUEbjFsJb#qe&nb_1<~j2ig~ulnMEQ29ht$}+SS47(4Bs)Ko1n~Dh%hPW0Gsqhlo zYkx0rEZ%(a=!=a1VCUhHm8_ zRn^*XF{h~vMn0-l4L;*0`@U0|JS=VLZYSpz-Y1fZX*w8>NJc=Xw(Dl?XGtglVp0db z8Szd@_feHJ7deX0eK4v-lKnzZJtGdBERdHkT>YF?fGhMz+v3(OSFYN6=2Mr5AMz#3lMF`vv))En&)1r)vC_0qcr=o|q4_6?7U z@P_X9MF^N`hEYEASr0MWvpMa}I#wZ+ zG+$RljY#J5k6}gaN)(Wm2ciG~la2O%MJ$jl(@Txe9md+R^~Av4bBGeCo$LY#+?D#Qw4zbD}SOflKH}l9@Kn+t|^WD=(&Z z8xQKVaGg5dB9m$TE~@A;U3I_a{U-UtYpEmOG=KQ5UzpZH9+j}wmvcKaCnrk8() zeR4lcHU(rBdof6~Xd^py#XyM4WRypNrNU&IBCa#3Xa|wgeCUIWVZY+i4Uw6$Ef|Yr zgo;+ePpPP4D?8F3Wr(cMF@_7D&4jy)mbw#-3G8=qQ%RGNe!+x0;y>Fp`BvZ!7CvM% zn!PE_+iyjlHe6B4G>Bjl>c7aXoAX09JN*7faD(*!2i3&KJ7Avf)zz1K6X0=< z%+B|9vhle1geea9Hqa9V=N4co<0eA=Oszmb!$^?Mn;bo>{iB-cW}Wz%+DKyqjfZ78 zVV9l39Ah^8iT3_)^hneT;0$>o4&Su|ni5T$w@Yau?t`-)nI&{Y{6P>Jov}c^g4fEc zpse_$VqKJ*gVG6ZKp#e*gGKNsCLn!Tqf~KKBK2FVj`$WZ)!GV&G5_ z4STiY>SMZ~xh7CdcbJj1<8UR-KtratSS%c=3y%kN&l8V5Koiq>ygf56P#9LnGWLLg zUz-_5qCp}MI71Mih2!hf>~J9$G%Lj6w!MnL*v7}49wD5D(ie4Sy=qLIWkpjne=oPwnWJ4a7f3xsK9sWK)c`j;_by%m|T70vSZyf8(N|^u&e)^d`eH#q$N}>Znynb zl@a>x{vgD|;ky6cSDW*YJF8`SKz>J@Ly>ABd-}it{OsVXQ)Mhyli^>@(snUt{O-d; z$FIUV2@S@0MHigu1@Lg7iYbMoO=wFiBGgEmt%3KIQ-RGmCQ@&z{Wx8jj}pKjbJYmP zQ4nX{59*6!5eIKS-Rk?X1Ge*A|Lj?M-h17D3++f==~@Qy|xVIf}QjeV@1&B)#|OBeycpr zMiKnHww;o#AV#7ZC+I{)Ai#y-h;J{z)eL0IEGG+|4c{?a*~gc45}#BpZGG;ehWbOA zpleEUYJtTzKVoef>2ofr#}@PFMPVKJq<%}9Vy)YA6Dv{HJT(QR8+*A*Hc|#E}L!D(~wkK-UU7=)gjy9g+_QK5+oC5%t?jw%962vBt zx@Uh+6Lmd;lrgU$VPow(_3Re?#pd9s7hT6a+KJJ3?KDCr$&O#Yd@RkP2l?>D&ybw2 zzEd868v5aZjBHA?PoD3bgN$=pYmWJf9aNB!!0#{ZIL%^b&|rtRU#odi4NSaoo-mZ- z#sdcJIQjy6Xaf+lI=}A|ewFGNm8}j>e^I)UIW;v=^d1usJ8~c*trvB!w4_CA=fG^@6=i^Q`{LtguEcIKv8KW;yY6{SLWov!LfB_AP^ zA@;Q9@#Lhwc;P!g)F*Xs`P||+N}_|Pbh7Gm32zRQuhk*edwO=vqorSDR^Iyh*cq;N zs`)<1I&^}jl!>87B>@a>&6HA3pH4&jd>IehWi%pKxQ3ZWq?ZK0Rtd`?kLQP91-AC= zW);z>J@E;6BSpSr^N=X%?b^-yVF^%~6mL9ceWvzAJPW5;+QeRrHXim@MFi=W`3LCm zX-}MsJ@R)~l!~3-*YbBB%Zp`q12StS4o?irH1zA(w_EzR@M5xtDEr`^+JZb~H`UFz ziV|#=U`tx73m(qWU)#R%3w}2suv;&uo#tRp`E(L-|GZwrh}v3{@BR27(T{V8ue89{ z*bhw!00U8TP_Pu>C~npVyRPPaBlMX0{Ml2i=F{Ig{LT>pqg~enBA>yA1DCJp0WObh zn|yun2MbxB8=bfAF^I^iDpy~dWL|qenXf?3gZOR;3uQ;vfT!8r9s!t}-1xGKm(%&L zoK2UNWB{Kg7byOO|6Octn^3J?9Jr4FrvK>Ze_!h20H2Lb>@wo253TRi&3a(y_8I=@ z9G64?=BHUi|5f_te^Jr@N7CoKxxcHKH5+Hu`Ag!>DhdBT+QXCFiK*34<99#cA%UpD z6OC&x_YJqGl~SDV|0n+^?8+*Zy_$PfxcY>)iz=>Kh9FDMBA31PrkD3k=r6WwQy*95 zVLXvTf7HkCJoA6&2ED!!@gZ#OF&rn{Xxb(X%Dz^wH!a%V0NzP?o>*J5fq7Ow|HTyC z3b-ZqT12+A=M)l8|I$Vc0xsqh_*)dveF?V&U0zQ*#OO0&1>@8Qxl+s-*T1#Y`cO1d zp<6&WV=My}uy?_Mqid`+NtKVV?_(T$z5FY{1PW@vOm0MXhxwyIlCu&YLXsON1`)yZ z>{{{gy}DiDpt5-xRo<=Xcj}x5ZKkB{@}36!<4!n#P*Q&Xh=-h0d+YfhZk;Z4e7Q;_ zu*?AN_cqif zZqpv{v2@tMrE&W>w*6;0?nIsd23Tu!UPTsZ@Z|!KEMr8O7tf|6r$Q^P6tWr@H2zS9 z^KN5=fen3xkot8RM`-euYd=9fGUE7|slctJH$3-hpmr8cVG9+C?(Xt9QG8m{{CbCv zA;qxp?a5rL1c)Vr;PO1sbjOnLsx%LeXydmh0z`2qx!+P?Ohkp48ral`_)7FHC z6}9hVxz2oDO7eQ={`l2Ts}4t#wm>NAlqO516x1AM_B=oK+p=4-Rj;#-xL%wbIW!w? zF@LwaJhniZcIzMq%5G_5Pq~HT3J(lf@o@&9mIA-EkEQsI`+c)NJR{a1cl;H(VG^~9Qzkf)v_88_XzKlU z4voXDG!974W3F{uD=yEpw=QcV&^yyY_?AZ6>J={SIxJMB?{=leey&AgAsS2JX1+pe zKLQ>Fhy?AnJ_m*ssX4$Gx5q`S97-5;UTQ<)&`<{D4D@w_FVILY4XmpaiYZMcDLb1< zGJ4GH+n%jJ-$9@mIR4u@?bo~#zCTq)w0fhR-OA*3H%Z;wJxH0qchb?j3NnjcG;i*O z!p?YBH;P}<3U;-3{!$>Pf0`GUXG7;4)+sPe-~^Ic@|w{mr|V6eyAMnV-J!;ySzMwg zf;0xwd_{WgbxRQg%5~~9=fe@M-9>(RX$vd0Lc?NgE()^`adhZzxo}Q zcsJGTnN}dVd7$WX|8M1>leT8DfS@zUVpO2tj169Gh4qGM;8c)Epp`Fq*YUwK*?1lDE}uljV;ntSv0$m*EJ7v06kI2nx~6kIX8Sc{=YIvz_H z_Ev|}EouR|eHEoNX$l2(miM6@_3xRV{DQ_(zHZY^7Y@2 z@6hmsC#$o)NmJo-rmiDH0rQg!tXn^z`Da24t6)(EgWD^+FI}gM0;{7tn~t5{t%iQd zcF4#kTBECHuM3;lGdJRBzDgRihsdzOMDtdOp%zgUrq~f$x%ptk^F1;W9<%Q(%6p+T z6eF`nv6?-vVEIqDJikk9Efv{a|0c}iGJFj=rd6-?Qfi8NH?{FHAsFZS%f)idw|iF3 zP_3`wZ_6n|)likd&{FZ%Uv`aGpLg7ubavxQ3j=|=eAhgvpG9o39(W=Zs)W&jtOxeO zzGXJI4b>X^d-I-h9&IRvSSKyB9h zL2)^T;Zs5%)}6Vgh~2Xxbp$kpYzh;>c3*wzm|W_YX?)m5`guDt?k%HU3QNJGo(S-k z{RFj*Z|9OK+StLk4G~%KI#VU2X$^>`jlYKbf36v7PI-7Zr(92rkMDa#)4m|KuM#s$ zan-g{o6`|<8Bpc8xGITe(cNmkFDeKnT*-u@YXcH=^fP`(r=E=cX!WY<@EE-R@Z2N! zMU+Yv(dL4iJr=)wE^s1EgwuA&F0>z)M&th!dBrF=K@imFHFBSxFlJZO-EueVO;mW? zdL2*fM`@ow@io}nAx?DDJz_DRgF8~fJmwdNzc;ApxmnK#A5Cd$JF`Vy6F4Wl&6{Utn!eoB-j zH578Gx+G-ttykM}W~68(X}D(rQ*QBoRy%qkj5M(~S)Dup&8XdcdwTAN76Qw|hrSZwr3T#8ewl+pu8Iv+Mln9N&SvwvcPs zmewd-g{h0r>N<-vcVcHaWIoGW) z6KYPyVg7wuQqAPz=sdpIX68R}EqDfbs(VDA$fGF4D)juJC)XH-wtw{7LOwa=uoGaH@jsC-e8$z7=2E zL9CqdNCsOYBBjJoKDky}Ll13KacZA}%IaRi!%XWnH@zr3tQyL}h|2`!fS9Ss&OuR> z9XQE09xD*#j(3JOQ8}p|R;`?yUeFMkFoZTD`nR;7%b9d@hm{t7njiv8WnL5xMq)0m z)@YVP=$T(Ti8c>+69t^xavidp@M7S%e(yn7nEhez_a@V08)06f<g%6YH!NlObf7{25m<2Kyk=_Xz@?}M@#NM w#Gn5;d?FF}zY%|;7dK|{aqtcN_}~U#!<58x^n1cK?jE45sG(5y(meG40lR0sTL1t6 literal 0 HcmV?d00001 diff --git a/README.md b/README.md index c61f41d..d8d94d6 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # About -A [Docker Mod](https://github.com/linuxserver/docker-mods) for the LinuxServer.io Lidarr Docker container that uses ffmpeg and a script to automatically convert downloaded FLAC files to MP3s. Default output quality is 320Kbps constant bit rate. +A [Docker Mod](https://github.com/linuxserver/docker-mods) for the LinuxServer.io Lidarr Docker container that uses ffmpeg and a script to automatically convert downloaded FLAC (or other format) files to MP3s. Default output quality is 320Kbps constant bit rate. Advanced options act as a light wrapper to ffmpeg, allowing conversion to any supported audio format, including AAC, AC3, Opus, and many others. A [Batch Mode](./README.md#batch-mode) is also supported that allows usage outside of Lidarr. @@ -17,7 +17,7 @@ Production Container info: ![Docker Image Size](https://img.shields.io/docker/im 2. Configure the Docker container with all the port, volume, and environment settings from the *original container documentation* here: **[linuxserver/lidarr](https://hub.docker.com/r/linuxserver/lidarr "Docker container")** 1. Add a **DOCKER_MODS** environment variable to the `docker run` command, as follows: - - Dev/test release: `-e DOCKER_MODS=thecaptain989/lidarr-flac2mp3:latest` + - Dev/test release: `-e DOCKER_MODS=thecaptain989/lidarr-flac2mp3:latest` - Stable release: `-e DOCKER_MODS=linuxserver/mods:lidarr-flac2mp3` *Example Docker CLI Configuration* @@ -49,25 +49,24 @@ Production Container info: ![Docker Image Size](https://img.shields.io/docker/im This will use the defaults to create a 320Kbps MP3 file. - *For any other setting, you **must** either use one of the [included wrapper scripts](./README.md#included-wrapper-scripts) or create a custom script with the command line options you desire. See the [Syntax](./README.md#syntax) section below.* + *For any other setting, you **must** use one of the supported methods to pass arguments to the script. See the [Syntax](./README.md#syntax) section below.* ## Usage -New file(s) with will be placed in the same directory as the original FLAC file(s) (unless redirected with the `--output` option below) and have the same owner and permissions. Existing files with the same track name will be overwritten. +New file(s) will be placed in the same directory as the original FLAC file(s) (unless redirected with the `--output` option below) and have the same owner and permissions. Existing files with the same track name will be overwritten. By default, if you've configured Lidarr's **Recycle Bin** path correctly, the original audio file will be moved there. -![danger] **NOTE:** If you have *not* configured the Recycle Bin, the original FLAC audio file(s) will be deleted and permanently lost. This behavior may be modifed with the `--keep-file` option. +![danger] **NOTE:** If you have *not* configured the Recycle Bin, the original FLAC audio file(s) will be deleted and permanently lost. This behavior may be modified with the `--keep-file` option. ### Syntax ->**Note:** The **Arguments** field for Custom Scripts was removed in Lidarr release [v0.7.0.1347](https://github.com/lidarr/Lidarr/commit/b9d240924f8965ebb2c5e307e36b810ae076101e "Lidarr commit notes") due to security concerns. -To support options with this version and later, a wrapper script can be manually created that will call *flac2mp3.sh* with the required arguments. +>**Note:** The _Arguments_ field for Custom Scripts was removed in Lidarr release [v0.7.0.1347](https://github.com/lidarr/Lidarr/commit/b9d240924f8965ebb2c5e307e36b810ae076101e "Lidarr commit notes") due to security concerns. + +To supply arguments to the script, you **must** either use one of the **[included wrapper scripts](./README.md#included-wrapper-scripts)**, create a **[custom wrapper script](./README.md#example-wrapper-script)**, or set the `FLAC2MP3_ARGS` **[environment variable](./README.md#environment-variable)**. #### Command Line Options and Arguments The script may be called with optional command line arguments. The syntax for the command line is: -`flac2mp3 [OPTIONS] [{-b|--bitrate} | {-v|--quality} | {-a|--advanced} "" {-e|--extension} ]` -OR -`flac2mp3 [OPTIONS] {-f|--file} ` +`flac2mp3 [{-d|--debug} []] [{-b|--bitrate} | {-v|--quality} | {-a|--advanced} "" {-e|--extension} ] [{-f|--file} ] [{-k|--keepfile}] [{-o|--output} ] [{-r|--regex} ''] [{-t|--tags} ]` Where: @@ -76,11 +75,13 @@ Option|Argument|Description -d, --debug|\[\\]|Enables debug logging. Level is optional.
Default of 1 (low).
2 includes API and FFmpeg output. -b, --bitrate|\|Sets the output quality in constant bits per second (CBR).
Examples: 160k, 240k, 300000
**Note:** May not be specified with `-v`, `-a`, or `-e`. -v, --quality|\|Sets the output variable bit rate (VBR).
Specify a value between 0 and 9, with 0 being the highest quality.
See the [FFmpeg MP3 Encoding Guide](https://trac.ffmpeg.org/wiki/Encode/MP3) for more details.
**Note:** May not be specified with `-b`, `-a`, or `-e`. --a, --advanced|\"\\"|Advanced ffmpeg options.
The specified `options` replace all script defaults and are sent directly to ffmpeg.
The `options` value must be enclosed in quotes.
See [FFmpeg Options](https://ffmpeg.org/ffmpeg.html#Options) for details on valid options, and [Guidelines for high quality audio encoding](https://trac.ffmpeg.org/wiki/Encode/HighQualityAudio) for suggested usage.
**Note:** Requires the `-e` option to also be specified. May not be specified with `-v` or `-b`.
![danger] **WARNING:** You must specify an audio codec (by including a `-c:a ` ffmpeg option) or the resulting file will contain no audio!
![danger] **WARNING:** Invalid `options` could result in script failure! --e, --extension|\|Sets the output file extension
The extension may be prefixed by a dot (".") or not.
**Note:** Requires the `-a` option to also be specified. May not be specified with `-v` or `-b`. --f, --file||If included, the script enters **[Batch Mode](./README.md#batch-mode)** and converts the specified audio file.
![danger] **WARNING:** Do not use this argument when called from Lidarr! +-a, --advanced|\"\\"|Advanced ffmpeg options.
The specified `options` replace all script defaults and are sent directly to ffmpeg.
The `options` value must be enclosed in quotes.
See [FFmpeg Options](https://ffmpeg.org/ffmpeg.html#Options) for details on valid options, and [Guidelines for high quality audio encoding](https://trac.ffmpeg.org/wiki/Encode/HighQualityAudio) for suggested usage.
**Note:** Requires the `-e` option to also be specified. May not be specified with `-v` or `-b`.
![warning] **WARNING:** You must specify an audio codec (by including a `-c:a ` ffmpeg option) or the resulting file will contain no audio!
![warning] **WARNING:** Invalid `options` could result in script failure! +-e, --extension|\|Sets the output file extension.
The extension may be prefixed by a dot (".") or not.
Example: .ogg
**Note:** Requires the `-a` option to also be specified. May not be specified with `-v` or `-b`. +-f, --file||If included, the script enters **[Batch Mode](./README.md#batch-mode)** and converts the specified audio file.
![warning] **WARNING:** Do not use this argument when called from Lidarr! -o, --output|\|Converted audio file(s) are saved to `directory` instead of being located in the same directory as the source audio file.
The path will be created if it does not exist. -k, --keep-file| |Do not delete the source file or move it to the Lidarr Recycle bin.
**Note:** This also disables triggering a Lidarr rescan after conversion. +-r, --regex|'\'|Sets the regular expression used to select input files.
The `regex` value should be enclosed in single quotes and escaped properly.
Defaults to `"\.flac$"`. +-t, --tags|\'|Comma separated list of metadata tags to apply automated corrections to.
See [Metadata Corrections](./README.md#metadata-corrections) section. --help| |Display help and exit. --version| |Display version and exit. @@ -91,28 +92,42 @@ The `-a` option effectively makes the script a generic wrapper for ffmpeg. FFmp The exact format of the executed ffmpeg command is: ``` -ffmpeg -loglevel error -i "input.flac" ${options} "output.${extension}" +ffmpeg -loglevel error -nostdin -i "input.flac" ${options} "output.${extension}" ``` +#### Technical notes on regex +By default, the script only matches and interacts with FLAC files (specifically, files ending in ".flac"). The `-r` option allows the script to match on a user specified regular expression (i.e. "regex") pattern. + +Files are passed to the script with the full Linux path intact. (Ex: `/path/to/audio/a-ha/Hunting High and Low/01 Take on Me.mp3`). Craft your regex with this in mind. + +![warning] **NOTE:** Escaping special regex characters (like a dot `.`) requires a double backslash, _even when single quoted!_ This is because **awk** (the program that processes audio files in the script) in most cases [strips a single backslash](https://www.gnu.org/software/gawk/manual/html_node/Escape-Sequences.html "GNU awk reference") from strings. Double quoted or unquoted strings require _four_ backslashes to preserve a regex escape because the bash shell will process the escapes first. + +For example, to convert all audio files to AAC audio files, use the following options: +``` +-a "-y -map 0 -c:a aac -b:a 240k -c:v copy" -e m4a --regex '\\.[^.]*$' +``` + +Regular expression syntax is beyond the scope of this document. See this [tutorial](https://www.regular-expressions.info/tutorial.html "Regular Expressions Tutorial") for more information. Regex patterns may be tested [here](http://regexstorm.net/tester "regex tester"). + ### Examples ``` -b 320k # Output 320 kbit/s MP3 (non-VBR; same as default behavior) -v 0 # Output variable bitrate MP3, VBR 220-260 kbit/s -d -b 160k # Enable debugging level 1, and output a 160 kbit/s MP3 +-r '\\.[^.]*$' # Convert any file to MP3 (not just FLAC) -a "-c:v libtheora -map 0 -q:v 10 -c:a libopus -b:a 192k" -e .opus - # Convert to Opus format, VBR 192 kbit/s, cover art, no overwright + # Convert to Opus format, 192 kbit/s, cover art +-a "-vn -c:a libopus -b:a 192K" -e .opus -r '\.mp3$' + # Convert .mp3 files to Opus format, 192 kbit/s, no cover art -a "-y -map 0 -c:a aac -b:a 240k -c:v copy" -e mp4 # Convert to MP4 format, using AAC 240 kbit/s audio, cover art, overwrite file --file "/path/to/audio/a-ha/Hunting High and Low/01 Take on Me.flac" # Batch Mode # Output 320kbit/s MP3 -o "/path/to/audio" -k - # Place the converted file(s) in the specified directory and do not delete the original audio file(s). + # Place the converted file(s) in the specified directory and do not delete the original audio file(s) ``` -### Wrapper Scripts -To supply arguments to the script, one of the included wrapper scripts may be used or a custom wrapper script must be created. - #### Included Wrapper Scripts For your convenience, several wrapper scripts are included in the `/usr/local/bin/` directory. You may use any of these scripts in place of the `flac2mp3.sh` mentioned in the [Installation](./README.md#installation) section above. @@ -141,6 +156,26 @@ Then put `/config/flac2mp3-custom.sh` in the **Path** field in place of `/usr/lo >**Note:** If you followed the Linuxserver.io recommendations when configuring your container, the `/config` directory will be mapped to an external storage location. It is therefore recommended to place custom scripts in the `/config` directory so they will survive container updates, but they may be placed anywhere that is accessible by Lidarr. +### Environment Variable +The `flac2mp3.sh` script also allows the use of arguments provided by the `FLAC2MP3_ARGS` environment variable. This allows advanced use cases without having to provide a custom script. + +For example, the following value would convert any .mp3 to Opus: +``` +-e FLAC2MP3_ARGS='-a "-vn -c:a libopus -b:a 192k" -e .opus -r "\\.mp3$"' +``` + +Make sure to correctly use quotes and/or escape special characters when using this method. (See [regex notes](./README.md#technical-notes-on-regex) above.) +In Docker Compose, the previous command would need an extra `$` to match the end-of-line: +```yaml +environment: + - FLAC2MP3_ARGS=-a "-vn -c:a libopus -b:a 192k" -e .opus -r '\\.mp3$$' +``` + +*Example Synology Configuration* +![flac2mp3](.assets/lidarr-synology-2.png "Synology container settings") + +>**NOTE:** The environment variable settings are _only_ used when **no** command line arguments are present. **Any** command line argument will disable the use of the environment variable. + ### Triggers The only events/notification triggers that are supported are **On Release Import** and **On Upgrade** @@ -171,6 +206,21 @@ This log can be downloaded from Lidarr under *System* > *Log Files* Log rotation is performed, with 5 log files of 1MB each kept, matching Lidarr's log retention. >![danger] **NOTE:** If debug logging is enabled with a level above 1, the log file can grow very large very quickly and is much more likely to be rotated. *Do not leave high-level debug logging enabled permanently.* +#### Metadata Corrections +This feature is not meant for general purpose use. It is only documented for completeness. + +List of supported tags and metadata corrections that are applied: + +|Tag|Original|Correction +|---|---|--- +|disc|1|1/1 +|genre|/Pop/|"Pop" +| |/Indie/|"Alternative & Indie" +| |/Industrial/|"Industrial Rock" +| |/Electronic/|"Electronica & Dance" +| |/Punk\|Alternative/|"Alternative & Punk" +| |/Rock/|"Rock" + ## Credits This would not be possible without the following: diff --git a/SECURITY.md b/SECURITY.md index b1e653c..cbb6d71 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -6,8 +6,8 @@ Only the latest major and minor version are supported. | Version | Supported | | ------- | ------------------ | -| 2.0.x | :heavy_check_mark: | -| < 2.0 | :x: | +| 2.2.x | :heavy_check_mark: | +| < 2.2 | :x: | ## Reporting a Vulnerability diff --git a/root/etc/cont-init.d/98-flac2mp3 b/root/etc/cont-init.d/98-flac2mp3 index 9b5de82..9de3545 100644 --- a/root/etc/cont-init.d/98-flac2mp3 +++ b/root/etc/cont-init.d/98-flac2mp3 @@ -44,4 +44,3 @@ if [ ! -x /usr/local/bin/flac2mp3.sh ]; then echo "Making scripts executable." chmod +x /usr/local/bin/flac2*.sh fi - diff --git a/root/etc/s6-overlay/s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run b/root/etc/s6-overlay/s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run index edb63c7..ab2abb4 100755 --- a/root/etc/s6-overlay/s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run +++ b/root/etc/s6-overlay/s6-rc.d/init-mod-lidarr-flac2mp3-add-package/run @@ -13,19 +13,24 @@ EOF # Determine if setup is needed if [ ! -f /usr/bin/ffmpeg ]; then - echo "**** Adding ffmpeg to package install list ****" - echo "ffmpeg" >> /mod-repo-packages-to-install.list + echo "**** Adding ffmpeg to package install list ****" + echo "ffmpeg" >> /mod-repo-packages-to-install.list +else + echo "**** flac2mp3 deps already installed, skipping ****" fi -# Change ownership -if [ $(stat -c '%G' /usr/local/bin/flac2mp3.sh) != "abc" ]; then - echo "Changing ownership on scripts." - chown abc:abc /usr/local/bin/flac2*.sh -fi - -# Make executable -if [ ! -x /usr/local/bin/flac2mp3.sh ]; then - echo "Making scripts executable." - chmod +x /usr/local/bin/flac2*.sh -fi +# Check ownership and attributes on each script file +for file in /usr/local/bin/flac2mp3*.sh +do + # Change ownership + if [ $(stat -c '%G' $file) != "abc" ]; then + echo "Changing ownership on $file script." + chown abc:abc $file + fi + # Make executable + if [ ! -x $file ]; then + echo "Making $file script executable." + chmod +x $file + fi +done diff --git a/root/usr/local/bin/flac2mp3-tags.sh b/root/usr/local/bin/flac2mp3-tags.sh new file mode 100644 index 0000000..6bf4d4f --- /dev/null +++ b/root/usr/local/bin/flac2mp3-tags.sh @@ -0,0 +1,3 @@ +#!/bin/bash + +. /usr/local/bin/flac2mp3.sh --tags disc,genre diff --git a/root/usr/local/bin/flac2mp3.sh b/root/usr/local/bin/flac2mp3.sh index 6fc9516..95a66fd 100755 --- a/root/usr/local/bin/flac2mp3.sh +++ b/root/usr/local/bin/flac2mp3.sh @@ -7,6 +7,7 @@ # Dependencies: # ffmpeg +# ffprobe # awk # curl # jq @@ -18,7 +19,7 @@ # Exit codes: # 0 - success; or test -# 1 - no audio file specified on command line +# 1 - no audio tracks detected # 2 - ffmpeg not found # 3 - invalid command line arguments # 5 - specified audio file not found @@ -48,20 +49,21 @@ Audio conversion script designed for use with Lidarr Source: https://github.com/TheCaptain989/lidarr-flac2mp3 Usage: - $0 [OPTIONS] [-b | -v | -a \"\" -e ] - $0 [OPTIONS] {-f|--file} + $0 [{-d|--debug} []] [{-b|--bitrate} | {-v|--quality} | {-a|--advanced} \"\" {-e|--extension} ] [{-f|--file} ] [{-k|--keepfile}] [{-o|--output} ] [{-r|--regex} ''] [{-t|--tags} ] Options: - -d, --debug [] enable debug logging - Level is optional, default of 1 (low) - -b, --bitrate set output quality in constant bits per second + -d, --debug [] Enable debug logging + level is optional, between 1-3 + 1 is lowest, 3 is highest + [default: 1] + -b, --bitrate Set output quality in constant bits per second [default: 320k] Ex: 160k, 240k, 300000 - -v, --quality set variable bitrate; quality between 0-9 + -v, --quality Set variable bitrate; quality between 0-9 0 is highest quality, 9 is lowest For more details, see: https://trac.ffmpeg.org/wiki/Encode/MP3 - -a, --advanced \"\" advanced ffmpeg options enclosed in quotes + -a, --advanced \"\" Advanced ffmpeg options enclosed in quotes Specified options replace all script defaults and are sent as entered to ffmpeg for processing. @@ -73,32 +75,40 @@ Options: Requires -e option to also be specified For more details, see: https://github.com/TheCaptain989/lidarr-flac2mp3 - -e, --extension file extension for output file, with or without + -e, --extension File extension for output file, with or without dot Required when -a is specified! - -f, --file the script enters batch mode, using the + -f, --file The script enters batch mode, using the specified audio file as input WARNING: Do not use this argument when called from Lidarr! - -o, --output specify a directory for the converted audio - file(s) - This will be created if it does not exist. - -k, --keep-file do not delete the source file or move it to the + -o, --output Specify a destination directory for the + converted audio file(s) + It will be created if it does not exist. + -k, --keep-file Do not delete the source file or move it to the Lidarr Recycle bin This also disables the Lidarr rescan. - --help display this help and exit - --version display script version and exit + -r, --regex '' Regular expression used to select input files + [default: \.flac$] + -t, --tags Comma separated list of metadata tags to apply + automated corrections to. + Supports: disc, genre + --help Display this help and exit + --version Display script version and exit Examples: $flac2mp3_script -b 320k # Output 320 kbit/s MP3 (non-VBR; same as default behavior) $flac2mp3_script -v 0 # Output variable bitrate MP3, VBR 220-260 kbit/s - $flac2mp3_script -d -b 160k # Enable debugging level 1 and set output a + $flac2mp3_script -d -b 160k # Enable debugging level 1 and output a 160 kbit/s MP3 - $flac2mp3_script -a \"-vn -c:a libopus -b:a 192K\" -e .opus - # Convert to Opus format, VBR 192 kbit/s, no - cover art + $flac2mp3_script -r '\\\\.[^.]*$' # Convert any file to MP3 (not just FLAC) + $flac2mp3_script -a \"-c:v libtheora -map 0 -q:v 10 -c:a libopus -b:a 192k\" -e .opus + # Convert to Opus format, 192 kbit/s, cover art + $flac2mp3_script -a \"-vn -c:a libopus -b:a 192K\" -e .opus -r '\.mp3$' + # Convert .mp3 files to Opus format, 192 kbit/s + no cover art $flac2mp3_script -a \"-y -map 0 -c:a aac -b:a 240K -c:v copy\" -e mp4 # Convert to MP4 format, using AAC 240 kbit/s audio, cover art, overwrite file @@ -107,12 +117,23 @@ Examples: Output 320 kbit/s MP3 $flac2mp3_script -o \"/path/to/audio\" -k # Place the converted file(s) in specified - directory and do not delete the original audio - file(s). + directory and do not delete the original + audio file(s) " echo "$usage" >&2 } +# Check for environment variable arguments +if [ -n "$FLAC2MP3_ARGS" ]; then + if [ $# -ne 0 ]; then + flac2mp3_prelogmessage="Warning|FLAC2MP3_ARGS environment variable set but will be ignored because command line arguments were also specified." + else + # Move the environment variable arguments to the command line for processing + flac2mp3_prelogmessage="Info|Using settings from environment variable." + eval set -- "$FLAC2MP3_ARGS" + fi +fi + # Process arguments while (( "$#" )); do case "$1" in @@ -142,7 +163,7 @@ while (( "$#" )); do else echo "Error|Invalid option: $1 requires an argument." >&2 usage - exit 1 + exit 3 fi ;; -b|--bitrate ) # Set constant bit rate @@ -220,13 +241,33 @@ while (( "$#" )); do usage exit 3 fi - # Test for trailing slash + # Test for trailing backslash [ "${flac2mp3_output: -1:1}" != "/" ] && flac2mp3_output="${flac2mp3_output}/" ;; -k|--keep-file ) # Do not delete source file(s) export flac2mp3_keep=1 shift ;; + -r|--regex ) # Sets the regex used to match input files + if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then + export flac2mp3_regex="$2" + shift 2 + else + echo "Error|Invalid option: $1 requires an argument." >&2 + usage + exit 3 + fi + ;; + -t|--tags ) # Metadata tags to correct + if [ -n "$2" ] && [ ${2:0:1} != "-" ]; then + export flac2mp3_tags="$2" + shift 2 + else + echo "Error|Invalid option: $1 requires an argument." >&2 + usage + exit 3 + fi + ;; -*|--*=) # Unknown option echo "Error|Unknown option: $1" >&2 usage @@ -258,7 +299,7 @@ elif [[ "${flac2mp3_type,,}" = "lidarr" ]]; then [ -z "$flac2mp3_tracks" ] && flac2mp3_tracks="$lidarr_trackfile_path" else # Called in an unexpected way - echo -e "Error|Unknown or missing 'lidarr_eventtype' environment variable: ${flac2mp3_type}\nNot called within Lidarr.\nTry using Batch Mode option: -f " + echo -e "Error|Unknown or missing 'lidarr_eventtype' environment variable: ${flac2mp3_type}\nNot called from Lidarr.\nTry using Batch Mode option: -f " exit 7 fi @@ -323,7 +364,7 @@ function check_rescan { else # It may have timed out, so let's wait a second local flac2mp3_return=1 - [ $flac2mp3_debug -ge 1 ] && echo "Debug|Job not done. Waiting 1 second." | log + [ $flac2mp3_debug -ge 1 ] && echo "Debug|Job not done. Waiting 1 second." | log sleep 1 fi fi @@ -339,12 +380,25 @@ if [ ! -f "/usr/bin/ffmpeg" ]; then echo "$flac2mp3_message" >&2 exit 2 fi +if [ ! -f "/usr/bin/ffprobe" ]; then + flac2mp3_message="Error|/usr/bin/ffprobe is required by this script" + echo "$flac2mp3_message" | log + echo "$flac2mp3_message" >&2 + exit 2 +fi # Log Debug state if [ $flac2mp3_debug -ge 1 ]; then flac2mp3_message="Debug|Enabling debug logging level ${flac2mp3_debug}. Starting ${lidarr_eventtype^} run." echo "$flac2mp3_message" | log - echo "$flac2mp3_message" >&2 + echo "$flac2mp3_message" +fi + +# Log FLAC2MP3_ARGS usage +if [ -n "$flac2mp3_prelogmessage" ]; then + # flac2mp3_prelogmessage is set above, before argument processing + echo "$flac2mp3_prelogmessage" | log + [ $flac2mp3_debug -ge 1 ] && echo "Debug|FLAC2MP3_ARGS: ${FLAC2MP3_ARGS}" | log fi # Log environment @@ -393,15 +447,32 @@ elif [ -f "$flac2mp3_config" ]; then -H "Content-Type: application/json" \ -X GET "$flac2mp3_api_url/config/mediamanagement") flac2mp3_return=$?; [ "$flac2mp3_return" != 0 ] && { - flac2mp3_message="Error|[$flac2mp3_return] curl error when parsing: \"$flac2mp3_api_url/v3/config/mediamanagement\"" + flac2mp3_message="Error|[$flac2mp3_return] curl error when parsing: \"$flac2mp3_api_url/config/mediamanagement\"" echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 } [ $flac2mp3_debug -ge 2 ] && echo "API returned: $flac2mp3_result" | awk '{print "Debug|"$0}' | log flac2mp3_recyclebin="$(echo $flac2mp3_result | jq -crM .recycleBin)" + # Test for trailing backslash + [ "${flac2mp3_recyclebin: -1:1}" != "/" ] && flac2mp3_recyclebin="${flac2mp3_recyclebin}/" [ $flac2mp3_debug -ge 1 ] && echo "Debug|Detected Lidarr RecycleBin '$flac2mp3_recyclebin'" | log + + # Get root folder path from Artist info + if [ "$lidarr_artist_id" != "" ]; then + flac2mp3_result=$(curl -s -H "X-Api-Key: $flac2mp3_apikey" \ + -H "Content-Type: application/json" \ + -X GET $flac2mp3_api_url/artist/$lidarr_artist_id) + flac2mp3_return=$?; [ "$flac2mp3_return" != 0 ] && { + flac2mp3_message="Error|[$flac2mp3_return] curl error when parsing: \"$flac2mp3_api_url/artist/$lidarr_artist_id\"" + echo "$flac2mp3_message" | log + echo "$flac2mp3_message" >&2 + } + [ $flac2mp3_debug -ge 2 ] && echo "API returned: $flac2mp3_result" | awk '{print "Debug|"$0}' | log + flac2mp3_root="$(echo $flac2mp3_result | jq -crM .rootFolderPath)" + [ $flac2mp3_debug -ge 1 ] && echo "Debug|Detected Lidarr Root Folder '$flac2mp3_root'" | log + fi else - # No config file means we can't call the API. Best effort at this point. + # No config file means we can't call the API. Best effort at this point. flac2mp3_message="Warn|Unable to locate Lidarr config file: '$flac2mp3_config'" echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 @@ -412,7 +483,7 @@ if [[ "$lidarr_eventtype" = "Test" ]]; then echo "Info|Lidarr event: $lidarr_eventtype" | log flac2mp3_message="Info|Script was test executed successfully." echo "$flac2mp3_message" | log - echo "$flac2mp3_message" >&2 + echo "$flac2mp3_message" exit 0 fi @@ -424,12 +495,20 @@ if [ "$flac2mp3_type" = "batch" -a ! -f "$flac2mp3_tracks" ]; then exit 5 fi +# Check for empty track variable +if [ -z "$flac2mp3_tracks" ]; then + flac2mp3_message="Error|No audio tracks were detected or specified!" + echo "$flac2mp3_message" | log + echo "$flac2mp3_message" >&2 + exit 1 +fi + # If specified, check if destination folder exists and create if necessary if [ "$flac2mp3_output" -a ! -d "$flac2mp3_output" ]; then - [ $flac2mp3_debug -ge 1 ] && echo "Debug|Destination directory does not exist. Creating: $flac2mp3_output" | log + [ $flac2mp3_debug -ge 1 ] && echo "Debug|Destination directory does not exist. Creating: $flac2mp3_output" | log mkdir -p "$flac2mp3_output" flac2mp3_return=$?; [ "$flac2mp3_return" != 0 ] && { - flac2mp3_message="Error|[$flac2mp3_return] mkdir returned an error. Unable to create output directory." + flac2mp3_message="Error|[$flac2mp3_return] mkdir returned an error. Unable to create output directory." echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 exit 6 @@ -456,6 +535,9 @@ fi if [ $flac2mp3_keep = 1 ]; then flac2mp3_message+=", Keep source" fi +if [ -n "$flac2mp3_regex" ]; then + flac2mp3_message+=", Matching regex: '${flac2mp3_regex}'" +fi flac2mp3_message+=", Track(s): ${flac2mp3_tracks}" echo "${flac2mp3_message}" | log @@ -465,67 +547,141 @@ echo -n "$flac2mp3_tracks" | awk -v Debug=$flac2mp3_debug \ -v Bitrate="$flac2mp3_bitrate" \ -v VBR="$flac2mp3_vbrquality" \ -v FFmpegADV="$flac2mp3_ffmpegadv" \ --v EXT="$flac2mp3_extension" \ +-v Ext="$flac2mp3_extension" \ -v Output="$flac2mp3_output" \ --v Keep="$flac2mp3_keep" ' +-v Keep=$flac2mp3_keep \ +-v Pat="$flac2mp3_regex" \ +-v Root="$flac2mp3_root" \ +-v Taglist="$flac2mp3_tags" ' BEGIN { - FFmpeg="/usr/bin/ffmpeg" - FS="|" - RS="|" - IGNORECASE=1 - if (EXT == "") EXT=".mp3" - if (Debug == 0) FFmpegLOG="error" - else if (Debug == 1) FFmpegLOG="warning" - else if (Debug >= 2) FFmpegLOG="info" + FFmpeg = "/usr/bin/ffmpeg" + FS = "|" + RS = "|" + IGNORECASE = 1 + # Set default extension, pattern, and logging values + if (Ext == "") Ext = ".mp3" + if (Pat == "") Pat = "\\.flac$" + if (Debug == 0) FFmpegLOG = "error" + else if (Debug == 1) FFmpegLOG = "warning" + else if (Debug >= 2) FFmpegLOG = "info" if (Bitrate) { if (Debug >= 1) print "Debug|Using constant bitrate of "Bitrate - BrCommand="-b:a "Bitrate + BrCommand = "-b:a "Bitrate } else if (VBR >= 0) { if (Debug >= 1) print "Debug|Using variable quality of "VBR - BrCommand="-q:a "VBR + BrCommand = "-q:a "VBR } else if (FFmpegADV) { - if (Debug >= 1) print "Debug|Using advanced ffmpeg options: \""FFmpegADV"\"" - if (Debug >= 1) print "Debug|Exporting with file extension "EXT + if (Debug >= 1) print "Debug|Using advanced ffmpeg options \""FFmpegADV"\"" + if (Debug >= 1) print "Debug|Exporting with file extension \""Ext"\"" + FFmpegOPTS = FFmpegADV } + if (Debug >= 1) print "Debug|Matching tracks against regex \""Pat"\"" + # Set default ffmpeg options + if (FFmpegOPTS == "") FFmpegOPTS = "-c:v copy -map 0 -y -acodec libmp3lame "BrCommand" -write_id3v1 1 -id3v2_version 3" } -/\.flac$/ { - # Get each FLAC file name and create a new MP3 (or other) name - Track=$1 - NewTrack=substr(Track, 1, length(Track)-5) EXT +$0 !~ Pat { + if (Debug >= 1) print "Debug|Skipping track that did not match regex: "$0 +} +$0 ~ Pat { + # Get each audio file name and create a new track name with the given extension + Track = $0 + Last = split(Track, Parts, ".") + NewTrack = substr(Track, 1, length(Track) - length(Parts[Last]) - 1) Ext # Redirect output if asked - if (Output) sub(/^.*\//,Output ,NewTrack) + if (Output) sub(/^.*\//, Output, NewTrack) + # Check for same track name + if (Track == NewTrack) { + print "Error|The original track name and new name are the same! Skipping track: "Track + next + } + # Set metadata options to fix tags if asked + if (Taglist) { + Metadata = ""; JSON = "" + NumTags = split(Taglist, Tag, ",") + if (Debug >= 1) print "Debug|Detecting and fixing common problems with the following metadata tags: "Taglist + Command = "/usr/bin/ffprobe -hide_banner -loglevel fatal -print_format json=compact=1 -show_format -show_entries \"format=tags : format_tags=disc,genre\" -i \""Track"\" 2>&1" + if (Debug >= 2) print "Debug|Executing: "Command + # Concatenate FFprobe output into one line + while (Command | getline Line) JSON = JSON Line + for (i = 1; i <= NumTags; i++) { + if (Tag[i] == "disc") { + # Fix one disc by itself (\47 is single quote in octal) + Command = "echo \47"JSON"\47 | jq -crM \47.format.tags.disc\47 2>&1" + if (Debug >= 2) print "Debug|Executing: "Command + Command | getline Disc + sub(/\n/, "", Disc) # I do not yet know why this is needed + if (Debug >= 1) print "Debug|Discovered disc: "Disc + if (Disc ~ /^1$/) Metadata = "-metadata disc=\"1/1\" "Metadata + } + if (Tag[i] == "genre") { + # Fix multiple genres + Command = "echo \47"JSON"\47 | jq -crM \47.format.tags | to_entries[] | select(.key | match(\"genre\";\"i\")).value\47 2>&1" + if (Debug >= 2) print "Debug|Executing: "Command + Command | getline Genre + sub(/\n/, "", Genre) # I do not yet know why this is needed + if (Debug >= 1) print "Debug|Discovered genre: "Genre + if (Genre ~ /;/) { + if (Genre ~ /Pop/) Metadata = "-metadata genre=\"Pop\" "Metadata + else if (Genre ~ /Indie/) Metadata = "-metadata genre=\"Alternative & Indie\" "Metadata + else if (Genre ~ /Industrial/) Metadata = "-metadata genre=\"Industrial Rock\" "Metadata + else if (Genre ~ /Electronic/) Metadata = "-metadata genre=\"Electronica & Dance\" "Metadata + else if (Genre ~ /Punk|Alternative/) Metadata = "-metadata genre=\"Alternative & Punk\" "Metadata + else if (Genre ~ /Rock/) Metadata = "-metadata genre=\"Rock\" "Metadata + } + } + if (Debug >= 2) print "Debug|Metadata on pass "i"/"NumTags": "Metadata + } + } print "Info|Writing: "NewTrack - # Check for advanced options - if (FFmpegADV) FFmpegOPTS=FFmpegADV - else FFmpegOPTS="-c:v copy -map 0 -y -acodec libmp3lame "BrCommand" -write_id3v1 1 -id3v2_version 3" # Convert the track - if (Debug >= 1) print "Debug|Executing: nice "FFmpeg" -loglevel "FFmpegLOG" -nostdin -i \""Track"\" "FFmpegOPTS" \""NewTrack"\"" - Result=system("nice "FFmpeg" -loglevel "FFmpegLOG" -nostdin -i \""Track"\" "FFmpegOPTS" \""NewTrack"\" 2>&1") + Command = "nice "FFmpeg" -loglevel "FFmpegLOG" -nostdin -i \""Track"\" "FFmpegOPTS" "Metadata"\""NewTrack"\" 2>&1" + if (Debug >= 1) print "Debug|Executing: "Command + Result = system(Command) if (Debug >= 2) print "Debug|ffmpeg exited" if (Result) { print "Error|Exit code "Result" converting \""Track"\"" } else { + # Build system command to set owner and permissions, etc. if (Keep == 1) { # Do not delete the source file if (Debug >= 1) print "Debug|Keeping original: \""Track"\" and setting permissions on \""NewTrack"\"" - Command="if [ -s \""NewTrack"\" ]; then if [ -f \""Track"\" ]; then chown --reference=\""Track"\" \""NewTrack"\"; chmod --reference=\""Track"\" \""NewTrack"\"; fi; fi" + Command = "if [ -s \""NewTrack"\" ]; then if [ -f \""Track"\" ]; then chown --reference=\""Track"\" \""NewTrack"\"; chmod --reference=\""Track"\" \""NewTrack"\"; fi; fi" } else { if (Recycle == "") { # No Recycle Bin, so check for non-zero size new file and delete the old one if (Debug >= 1) print "Debug|Deleting: \""Track"\" and setting permissions on \""NewTrack"\"" - Command="if [ -s \""NewTrack"\" ]; then if [ -f \""Track"\" ]; then chown --reference=\""Track"\" \""NewTrack"\"; chmod --reference=\""Track"\" \""NewTrack"\"; rm \""Track"\"; fi; fi" + Command = "if [ -s \""NewTrack"\" ]; then if [ -f \""Track"\" ]; then chown --reference=\""Track"\" \""NewTrack"\"; chmod --reference=\""Track"\" \""NewTrack"\"; rm \""Track"\"; fi; fi" } else { # Recycle Bin is configured, so check if it exists, append a relative path to it from the track, check for non-zero size new file, and move the old one to the Recycle Bin - match(Track,/^\/?[^\/]+\//) - RecPath=substr(Track,RSTART+RLENGTH) - sub(/[^\/]+$/,"",RecPath) - RecPath=Recycle RecPath + if (Root == "") { + # Legacy way in case the Root music folder cannot be determined + print "Warning|Root music folder is blank. Falling back to legacy split method." + match(Track, /^\/?[^\/]+\//) + } else { + # The following logic tests that the Root folder is a substring of the Track folder, though based on the observed Lidarr behavior this should be a safe assumption. A warning is displayed just in case. + RSTART = index(Track, Root) + if (RSTART) { + RLENGTH = length(Root) + } else { + print "Warning|The root music folder \""Root"\" is not part of \""Track"\". Recycled tracks may not appear as expected." + RLENGTH = 0 + } + } + if (Debug >= 2) print "Debug|Splitting track name on RSTART: "RSTART", RLENGTH: "RLENGTH" to prepend recycle path." + RecPath = substr(Track, RSTART + RLENGTH) + sub(/^\/+/, "", RecPath) # remove trailing track name + sub(/[^\/]+$/, "", RecPath) # remove leading backslash + RecPath = Recycle RecPath if (Debug >= 1) print "Debug|Recycling: \""Track"\" to \""RecPath"\" and setting permissions on \""NewTrack"\"" - Command="if [ ! -e \""RecPath"\" ]; then mkdir -p \""RecPath"\"; fi; if [ -s \""NewTrack"\" ]; then if [ -f \""Track"\" ]; then chown --reference=\""Track"\" \""NewTrack"\"; chmod --reference=\""Track"\" \""NewTrack"\"; mv -t \""RecPath"\" \""Track"\"; fi; fi" + Command = "if [ ! -e \""RecPath"\" ]; then mkdir -p \""RecPath"\"; fi; if [ -s \""NewTrack"\" ]; then if [ -f \""Track"\" ]; then chown --reference=\""Track"\" \""NewTrack"\"; chmod --reference=\""Track"\" \""NewTrack"\"; mv -t \""RecPath"\" \""Track"\"; fi; fi" } } + # Set owner, permissions, etc. if (Debug >= 2) print "Debug|Executing: "Command - system(Command) + Result = system(Command) + if (Result) { + print "Error|Exit code "Result" setting permissions and/or recycling on \""NewTrack"\"" + } } } ' | log @@ -535,7 +691,7 @@ BEGIN { flac2mp3_return="${PIPESTATUS[1]}" # captures awk exit status [ $flac2mp3_debug -ge 2 ] && echo "Debug|awk exited with code: $flac2mp3_return" | log if [ "$flac2mp3_return" != 0 ]; then - flac2mp3_message="Error|[$flac2mp3_return] Script exited abnormally. File permissions issue?" + flac2mp3_message="Error|[$flac2mp3_return] Script exited abnormally. File permissions issue?" echo "$flac2mp3_message" | log echo "$flac2mp3_message" >&2 exit 10 From e115c2e10f7d94b5d0dbe44f89a1b9343399a40e Mon Sep 17 00:00:00 2001 From: TheCaptain989 Date: Sun, 27 Nov 2022 10:00:00 -0600 Subject: [PATCH 3/4] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index d8d94d6..d5bb8f3 100644 --- a/README.md +++ b/README.md @@ -81,7 +81,7 @@ Option|Argument|Description -o, --output|\|Converted audio file(s) are saved to `directory` instead of being located in the same directory as the source audio file.
The path will be created if it does not exist. -k, --keep-file| |Do not delete the source file or move it to the Lidarr Recycle bin.
**Note:** This also disables triggering a Lidarr rescan after conversion. -r, --regex|'\'|Sets the regular expression used to select input files.
The `regex` value should be enclosed in single quotes and escaped properly.
Defaults to `"\.flac$"`. --t, --tags|\'|Comma separated list of metadata tags to apply automated corrections to.
See [Metadata Corrections](./README.md#metadata-corrections) section. +-t, --tags|\|Comma separated list of metadata tags to apply automated corrections to.
See [Metadata Corrections](./README.md#metadata-corrections) section. --help| |Display help and exit. --version| |Display version and exit. From ec8932718e8dcdc6cc0a6341dd80c56519ec8c12 Mon Sep 17 00:00:00 2001 From: TheCaptain989 Date: Sun, 27 Nov 2022 10:07:04 -0600 Subject: [PATCH 4/4] Update lidarr-synology.png --- .assets/lidarr-synology.png | Bin 13074 -> 12253 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/.assets/lidarr-synology.png b/.assets/lidarr-synology.png index d29fe585e811b3a7cb69127c44abe819c6cc0d9f..e0f58bd86f0a88585e4a5c67591d5fcf8b3bc24f 100644 GIT binary patch literal 12253 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 z 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 H-Io6kvePe~