import json
import re
import glob
import os
import unicodedata
import hashlib
import mmap
import argparse
def make_metapackage(repodir, pkgname, pkgver, depends):
dep = " ".join(depends)
result = f"""
# Generated by musicrepo
pkgname={pkgname}
pkgver={pkgver}
pkgrel=0
pkgdesc="musicrepo"
options="!check"
url="https://music"
arch="noarch"
license="propriatary"
depends="{dep}"
"""
result += '\npackage() {\n\tmkdir -p "$pkgdir"\n}\n'
apkfile = os.path.join(repodir, pkgname, 'APKBUILD')
apkdir = os.path.dirname(apkfile)
os.makedirs(apkdir, exist_ok=True)
with open(apkfile, 'w') as handle:
handle.write(result)
def make_apkbuild(repodir, artist, album, path):
artist_slug = slug(artist)
album_slug = slug(album)
pkgname = f"musicdir-{artist_slug}-{album_slug}"
pkgver = 1
if os.path.isfile(os.path.join(path, 'pkgver')):
with open(os.path.join(path, 'pkgver')) as handle:
raw = handle.read()
pkgver = int(raw.strip())
apkfile = os.path.join(repodir, pkgname, 'APKBUILD')
apkdir = os.path.dirname(apkfile)
os.makedirs(apkdir, exist_ok=True)
os.makedirs(os.path.join(apkdir, "tracks"), exist_ok=True)
sources = ""
sha512sums = []
for file in glob.glob(os.path.join(path, '*')):
sname = "tracks/" + os.path.basename(file).replace(' ', '-')
sources += f"{sname}\n"
if not os.path.islink(os.path.join(apkdir, sname)):
os.symlink(file, os.path.join(apkdir, sname))
h = get_sha512sum(file)
hname = os.path.basename(sname)
sha512sums.append(f"{h} {hname}")
sha512sums = "\n".join(sha512sums)
result = f"""
# Generated by musicrepo
pkgname={pkgname}
_artist="{artist}"
_album="{album}"
pkgver={pkgver}
pkgrel=0
pkgdesc="{album} from {artist}"
url="https://music"
arch="noarch"
license="propriatary"
options="!check"
source="
{sources}
"
"""
result += """
package()
{
cd tracks
for _i in ./*; do
install -Dm644 "$_i" "$pkgdir"/usr/share/music/"$_artist"/"$_album"/"$_i"
done
}
"""
result += f'\nsha512sums="{sha512sums}"\n'
with open(apkfile, 'w') as handle:
handle.write(result)
return pkgname, pkgver
def slug(value):
value = unicodedata.normalize('NFKD', value).encode('ascii', 'ignore').decode('ascii')
value = re.sub('[^\w\s-]', '', value).strip().lower()
return re.sub('[-\s]+', '-', value)
def get_sha512sum(filename):
h = hashlib.sha512()
with open(filename, 'rb') as f:
with mmap.mmap(f.fileno(), 0, prot=mmap.PROT_READ) as mm:
h.update(mm)
return h.hexdigest()
def make_repository(musicdir, repodir):
music = {}
statefile = os.path.join(repodir, 'musicrepo.state.json')
state = {}
bump = set()
if os.path.isfile(statefile):
with open(statefile) as handle:
state = json.loads(handle.read())
for path in glob.glob(os.path.join(musicdir, '*/*'), recursive=True):
part = path.split('/')
album = part[-1]
artist = part[-2]
match = re.match(r'(.+)\(\d\d\d\d\)', album)
if match:
album = match.groups(0)[0].strip()
if artist not in music:
music[artist] = {}
music[artist][album] = path
artist_pkgs = []
for artist in music:
albums_pkgs = []
for album in music[artist]:
path = music[artist][album]
pkgname, pkgver = make_apkbuild(repodir, artist, album, path)
albums_pkgs.append(pkgname)
artist_slug = slug(artist)
if pkgname not in state:
print(f"New album: {album} from {artist}")
bump.add(f'musicdir-{artist_slug}')
state[pkgname] = pkgver
if len(albums_pkgs):
pkgname = f'musicdir-{artist_slug}'
pkgver = 0
if pkgname in state:
pkgver = state[pkgname]
else:
print(f"Artist {artist} is new, creating metapackage")
state[pkgname] = 0
bump.add('musicdir-all')
if pkgname in bump:
state[pkgname] += 1
pkgver += 1
print(f"Artist {artist} has new album packages, bumping version to {pkgver}")
make_metapackage(repodir, pkgname, pkgver, albums_pkgs)
artist_pkgs.append(f'musicdir-{artist_slug}')
if 'musicdir-all' not in state:
state['musicdir-all'] = 0
if 'musicdir-all' in bump:
state['musicdir-all'] += 1
print(f"Artist list changed, bumping musicdir-all to {state['musicdir-all']}")
make_metapackage(repodir, 'musicdir-all', state['musicdir-all'], artist_pkgs)
with open(statefile, 'w') as handle:
handle.write(json.dumps(state))
def main():
parser = argparse.ArgumentParser(description="music repo generator")
parser.add_argument('musicdir')
parser.add_argument('repodir')
args = parser.parse_args()
make_repository(os.path.expanduser(args.musicdir), os.path.expanduser(args.repodir))
if __name__ == '__main__':
main()