Setup tools for music organisation

This commit is contained in:
Wojciech Kozlowski 2023-02-19 23:46:17 +01:00
parent dc59b9a37e
commit 7a15e2b981
8 changed files with 205 additions and 2 deletions

View File

@ -144,3 +144,25 @@ not being accessed/modified during this process. It is easy to access `yggdrasil
```sh
python scripts/scaleway/baldur.py delete
```
## Music organisation
The `playbooks/music.yml` playbook sets up tools and configuration for organising music. The process
is manual though. The steps for adding a new CD.
All steps below are to be executed as the `music` user.
### Ripping a CD
1. Use a CD ripper and rip the CD to `/var/lib/yggdrasil/home/music/rip` using flac encoding.
2. Samba has been set up to give Windows access to the above directory. Therefore, CD rippers
available only for Windows can also be used, e.g. dBpoweramp.
### Import new music
1. Run `beet import /var/lib/yggdrasil/home/music/rip`. This will move the music files to
`/var/lib/yggdrasil/data/music/flac`.
2. Run `beet convert <match>`, where `<match>` is used to narrow down to new music only. This will
convert the flac files into mp3 files for sharing via Nextcloud.
3. Run `nextcloud-upload /var/tmp/music/mp3/<artist>` for every artist to upload to Nextcloud.
4. Remove the `/var/tmp/music/mp3/<artist>` directory.

View File

@ -98,11 +98,21 @@ music_user_data_directory: "{{ system_var_data_directory }}/{{ music_user_name }
music_user_home_dataset: "rpool{{ music_user_home_directory }}"
music_user_data_dataset: "rpool{{ music_user_data_directory }}"
# --------------------------------------------------------------------------------------------------
# music:backups
# --------------------------------------------------------------------------------------------------
music_user_backups_snapshots_data_dataset: "\
{{ system_backups_snapshots_data_dataset }}/{{ music_user_name }}"
music_user_backups_snapshots_recursive: true
music_user_backups_snapshots_skip_parent: true
# --------------------------------------------------------------------------------------------------
# music:org
# --------------------------------------------------------------------------------------------------
music_user_nextcloud_url: "https://cloud.wojciechkozlowski.eu/public.php/webdav"
music_user_nextcloud_user: "{{ vault_music_user_nextcloud_user }}"
music_user_nextcloud_pswd: "{{ vault_music_user_nextcloud_pswd }}"
# --------------------------------------------------------------------------------------------------
# services
# --------------------------------------------------------------------------------------------------

View File

@ -35,5 +35,5 @@
- "music:backups"
- "music:backups:restic"
- "music:backups:restic:user"
# - role: "music/org"
# tags: "music:org"
- role: "music/org"
tags: "music:org"

View File

@ -0,0 +1,93 @@
#!/usr/bin/env python3
import argparse
import os
import requests
import requests.auth
import urllib
import urllib.parse
import yaml
class WebDavManager:
def __init__(self, url, user, pswd):
self.__url = url
self.__auth = requests.auth.HTTPBasicAuth(user, pswd)
@staticmethod
def __remote_path(dstdir, dst):
path = []
if dstdir is not None:
path.append(dstdir.strip('/'))
path.append(dst.strip('/'))
return '/'.join(path)
def __url_path(self, dstdir, dst):
return '/'.join([
self.__url.strip('/'),
urllib.parse.quote(self.__remote_path(dstdir, dst)),
])
def mkdir(self, dstdir, dir):
print(f"mkdir: {self.__remote_path(dstdir, dir)}", flush=True)
rc = requests.request("MKCOL", self.__url_path(dstdir, dir), auth=self.__auth)
print(f"response: {rc}", flush=True)
if ((rc.status_code // 100) != 2) and (rc.status_code != 405):
raise RuntimeError(f"unexpected response ({rc}) to MKCOL ({rc.url}):\n{rc.text}")
def upload_file(self, srcdir, dstdir, file):
print(f"upload: {self.__remote_path(dstdir, file)}", flush=True)
with open(os.path.join(srcdir, file), "rb") as fobj:
rc = requests.put(self.__url_path(dstdir, file), auth=self.__auth, data=fobj)
print(f"response: {rc}", flush=True)
if (rc.status_code // 100) != 2:
raise RuntimeError(f"unexpected response ({rc}) to PUT ({rc.url}):\n{rc.text}")
@staticmethod
def __walkraise(error):
raise error
def upload_dir(self, target):
prefix = os.path.dirname(os.path.realpath(target))
self.mkdir(None, os.path.relpath(target, prefix))
for root, dirs, files in os.walk(target, topdown=True, onerror=self.__walkraise):
for dir in dirs:
self.mkdir(os.path.relpath(root, prefix), dir)
for file in files:
self.upload_file(root, os.path.relpath(root, prefix), file)
def upload(self, target):
if os.path.isfile(target):
self.upload_file(
os.path.dirname(os.path.realpath(args.target)),
None,
os.path.basename(args.target),
)
else:
assert os.path.isdir(target)
self.upload_dir(target)
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="upload files to nextcloud")
parser.add_argument("--config", type=str, default="~/.config/nextcloud-upload/config.yml",
help="path to configuration")
parser.add_argument("target", type=str, help="file or folder to upload")
args = parser.parse_args()
with open(args.config, encoding="utf-8") as config_file:
config = yaml.safe_load(config_file)
for key in ["url", "user", "pswd"]:
if key not in config:
raise KeyError(f"{key} must be present in {args.config}")
webdav = WebDavManager(config["url"], config["user"], config["pswd"])
webdav.upload(args.target)

View File

@ -0,0 +1,13 @@
---
argument_specs:
main:
options:
music_user_name:
type: "str"
required: true
music_user_home_directory:
type: "str"
required: true
music_user_data_directory:
type: "str"
required: true

View File

@ -0,0 +1,52 @@
---
- name: "install beets"
ansible.builtin.apt:
name:
- "beets"
- "ffmpeg"
- name: "install nextcloud-upload"
ansible.builtin.copy:
src: "./nextcloud-upload"
dest: "/usr/local/bin/nextcloud-upload"
mode: 0755
- block:
- name: "create beets config directory"
ansible.builtin.file:
path: "{{ music_user_home_directory }}/.config/beets"
state: "directory"
owner: "{{ music_user_name }}"
group: "{{ music_user_name }}"
mode: 0755
- name: "create beets convert directory"
ansible.builtin.file:
path: "/var/tmp/{{ music_user_name }}/mp3"
state: "directory"
owner: "{{ music_user_name }}"
group: "{{ music_user_name }}"
mode: 0755
- name: "configure beets"
ansible.builtin.template:
src: "./beets.yml"
dest: "{{ music_user_home_directory }}/.config/beets/config.yaml"
mode: 0644
- name: "create nextcloud-upload config directory"
ansible.builtin.file:
path: "{{ music_user_home_directory }}/.config/nextcloud-upload"
state: "directory"
owner: "{{ music_user_name }}"
group: "{{ music_user_name }}"
mode: 0755
- name: "configure nextcloud-upload"
ansible.builtin.template:
src: "./nextcloud-upload.yml"
dest: "{{ music_user_home_directory }}/.config/nextcloud-upload/config.yml"
mode: 0644
become_user: "{{ music_user_name }}"

View File

@ -0,0 +1,10 @@
plugins: convert
library: {{ music_user_data_directory }}/flac/library.db
directory: {{ music_user_data_directory }}/flac
import:
move: yes
write: no
convert:
dest: /var/tmp/{{ music_user_name }}/mp3

View File

@ -0,0 +1,3 @@
url: "{{ music_user_nextcloud_url }}"
user: "{{ music_user_nextcloud_user }}"
pswd: "{{ music_user_nextcloud_pswd }}"