diff --git a/bin/mtag/README.md b/bin/mtag/README.md index 1dcd2cda..fb418120 100644 --- a/bin/mtag/README.md +++ b/bin/mtag/README.md @@ -4,6 +4,7 @@ some of these rely on libraries which are not MIT-compatible * [audio-bpm.py](./audio-bpm.py) detects the BPM of music using the BeatRoot Vamp Plugin; imports GPL2 * [audio-key.py](./audio-key.py) detects the melodic key of music using the Mixxx fork of keyfinder; imports GPL3 +* [media-hash.py](./media-hash.py) generates checksums for audio and video streams; uses FFmpeg (LGPL or GPL) # dependencies @@ -18,7 +19,10 @@ run [`install-deps.sh`](install-deps.sh) to build/install most dependencies requ # usage from copyparty -`copyparty -e2dsa -e2ts -mtp key=f,audio-key.py -mtp .bpm=f,audio-bpm.py` +`copyparty -e2dsa -e2ts` followed by any combination of these: +* `-mtp key=f,audio-key.py` +* `-mtp .bpm=f,audio-bpm.py` +* `-mtp ahash,vhash=f,media-hash.py` * `f,` makes the detected value replace any existing values * the `.` in `.bpm` indicates numeric value @@ -29,6 +33,9 @@ run [`install-deps.sh`](install-deps.sh) to build/install most dependencies requ ## usage with volume-flags instead of affecting all volumes, you can set the options for just one volume like so: -``` -copyparty -v /mnt/nas/music:/music:r:cmtp=key=f,audio-key.py:cmtp=.bpm=f,audio-bpm.py:ce2dsa:ce2ts -``` + +`copyparty -v /mnt/nas/music:/music:r:c,e2dsa:c,e2ts` immediately followed by any combination of these: + +* `:c,mtp=key=f,audio-key.py` +* `:c,mtp=.bpm=f,audio-bpm.py` +* `:c,mtp=ahash,vhash=f,media-hash.py` diff --git a/bin/mtag/media-hash.py b/bin/mtag/media-hash.py new file mode 100644 index 00000000..3820027c --- /dev/null +++ b/bin/mtag/media-hash.py @@ -0,0 +1,73 @@ +#!/usr/bin/env python + +import re +import sys +import json +import time +import base64 +import hashlib +import subprocess as sp + +try: + from copyparty.util import fsenc +except: + + def fsenc(p): + return p + + +""" +dep: ffmpeg +""" + + +def det(): + # fmt: off + cmd = [ + "ffmpeg", + "-nostdin", + "-hide_banner", + "-v", "fatal", + "-i", fsenc(sys.argv[1]), + "-f", "framemd5", + "-" + ] + # fmt: on + + p = sp.Popen(cmd, stdout=sp.PIPE) + # ps = io.TextIOWrapper(p.stdout, encoding="utf-8") + ps = p.stdout + + chans = {} + for ln in ps: + if ln.startswith(b"#stream#"): + break + + m = re.match(r"^#media_type ([0-9]): ([a-zA-Z])", ln.decode("utf-8")) + if m: + chans[m.group(1)] = m.group(2) + + hashers = [hashlib.sha512(), hashlib.sha512()] + for ln in ps: + n = int(ln[:1]) + v = ln.rsplit(b",", 1)[-1].strip() + hashers[n].update(v) + + r = {} + for k, v in chans.items(): + dg = hashers[int(k)].digest()[:12] + dg = base64.urlsafe_b64encode(dg).decode("ascii") + r[v[0].lower() + "hash"] = dg + + print(json.dumps(r, indent=4)) + + +def main(): + try: + det() + except: + pass # mute + + +if __name__ == "__main__": + main() diff --git a/copyparty/__main__.py b/copyparty/__main__.py index 6337f6f4..6c48e099 100644 --- a/copyparty/__main__.py +++ b/copyparty/__main__.py @@ -342,7 +342,7 @@ def run_argparse(argv, formatter): ap2.add_argument("--no-mtag-ff", action="store_true", help="never use FFprobe as tag reader") ap2.add_argument("-mtm", metavar="M=t,t,t", type=u, action="append", help="add/replace metadata mapping") ap2.add_argument("-mte", metavar="M,M,M", type=u, help="tags to index/display (comma-sep.)", - default="circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,res,.fps") + default="circle,album,.tn,artist,title,.bpm,key,.dur,.q,.vq,.aq,vc,ac,res,.fps,ahash,vhash") ap2.add_argument("-mth", metavar="M,M,M", type=u, help="tags to hide by default (comma-sep.)", default=".vq,.aq,vc,ac,res,.fps") ap2.add_argument("-mtp", metavar="M=[f,]bin", type=u, action="append", help="read tag M using bin") diff --git a/copyparty/web/browser.js b/copyparty/web/browser.js index 80c9a327..521f953c 100644 --- a/copyparty/web/browser.js +++ b/copyparty/web/browser.js @@ -3319,6 +3319,8 @@ var filecols = (function () { "q": "quality / bitrate", "Ac": "audio codec", "Vc": "video codec", + "Ahash": "audio checksum", + "Vhash": "video checksum", "Res": "resolution", "T": "filetype", "aq": "audio quality / bitrate",