cmus2tsv

convert cmus' cache format to TSV
Log | Files | Refs

commit d02493de7c441623171e3d0996faa391741760c6
Author: hhvn <dev@hhvn.uk>
Date:   Sun,  3 Jul 2022 20:32:45 +0100

Init

Diffstat:
A.gitignore | 2++
AMakefile | 13+++++++++++++
Acmus2tsv.c | 159+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 174 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,2 @@ +cmus2tsv +cmus2tsv.o diff --git a/Makefile b/Makefile @@ -0,0 +1,13 @@ +SRC = cmus2tsv.c +OBJ = $(SRC:.c=.o) +BIN = cmus2tsv +CFLAGS = -g3 -O0 + +all: $(BIN) +$(BIN): $(OBJ) + $(CC) $(CFLAGS) $(LDFLAGS) -o $(BIN) $(OBJ) + +clean: + rm $(BIN) $(OBJ) + +.PHONY: all clean diff --git a/cmus2tsv.c b/cmus2tsv.c @@ -0,0 +1,159 @@ +#include <stdio.h> +#include <fcntl.h> +#include <errno.h> +#include <unistd.h> +#include <stdlib.h> +#include <libgen.h> +#include <stdint.h> +#include <string.h> +#include <sys/stat.h> +#include <sys/mman.h> + +#define ALIGN(size) (((size) + sizeof(long) - 1) & ~(sizeof(long) - 1)) + +#define CACHE_VERSION 0x0d + +#define CACHE_64_BIT 0x01 +#define CACHE_BE 0x02 + +#define ENTRY_USED_SIZE 28 +#define ENTRY_RESERVED_SIZE 52 +#define ENTRY_TOTAL_SIZE (ENTRY_RESERVED_SIZE + ENTRY_USED_SIZE) + +char cache_header[8] = "CTC\0\0\0\0\0"; +struct entry { + uint32_t size; + + int32_t play_count; + int64_t mtime; + int32_t duration; + int32_t bitrate; + int32_t bpm; + + uint8_t _reserved[ENTRY_RESERVED_SIZE]; + + char strings[]; +}; + +void +display_header(void) { + printf("Filename\tTitle\tArtist\tAlbum\tTrack\tDuration\tDuration(s)\tPlay count\n"); +} + +void +display(struct entry *e) { + int size, pos, i, count; + char *metakey, *metaval; + char *title, *artist, *album, *track; + + for (size = e->size - sizeof(*e), count = i = 0; i < size; i++) { + if (!e->strings[i]) + count++; + } + count = (count - 3) / 2; + + printf("%s\t", e->strings); /* filename */ + pos = strlen(e->strings) + 1; + pos += strlen(e->strings + pos) + 1; + pos += strlen(e->strings + pos) + 1; + + for (i = 0; i < count; i++) { + size = strlen(e->strings + pos) + 1; + metakey = e->strings + pos; + pos += size; + + size = strlen(e->strings + pos) + 1; + metaval = e->strings + pos; + pos += size; + + if (strcmp(metakey, "title") == 0) + title = metaval; + else if (strcmp(metakey, "artist") == 0) + artist = metaval; + else if (strcmp(metakey, "album") == 0) + album = metaval; + else if (strcmp(metakey, "tracknumber") == 0) + track = metaval; + } + + printf("%s\t%s\t%s\t%s\t", title, artist, album, track); + + if (e->duration < 60) + printf("00:%02d\t", e->duration); + else + printf("%02d:%02d\t", ((int)e->duration / 60), e->duration - ((int)e->duration / 60) * 60); + + printf("%ld\t%d\n", e->duration, e->play_count); +} + +void +read_cache(char *file) { + struct entry *e; + unsigned int flags = 0; + unsigned int size, offset = 0; + struct stat st; + char *buf; + int fd; + +#ifdef BIG_ENDIAN + flags |= CACHE_BE; +#endif + + if (sizeof(long) == 8) + flags |= CACHE_64_BIT; + + cache_header[7] = flags & 0xff; flags >>= 8; + cache_header[6] = flags & 0xff; flags >>= 8; + cache_header[5] = flags & 0xff; flags >>= 8; + cache_header[4] = flags & 0xff; + + cache_header[3] = CACHE_VERSION; + + if ((fd = open(file, O_RDONLY)) < 0) { + fprintf(stderr, "%s: %s\n", strerror(errno), file); + exit(EXIT_FAILURE); + } + fstat(fd, &st); + if (st.st_size < sizeof(cache_header)) + goto close; + size = st.st_size; + + buf = mmap(NULL, size, PROT_READ, MAP_PRIVATE, fd, 0); + if (buf == MAP_FAILED) + goto close; + + if (memcmp(buf, cache_header, sizeof(cache_header))) + goto corrupt; + + offset = sizeof(cache_header); + while (offset < size) { + e = (void *)buf + offset; + display(e); + offset += ALIGN(e->size); + } + + munmap(buf, size); + close(fd); + return; + +corrupt: + fprintf(stderr, "Corrupt: %s\n", file); + munmap(buf, size); +close: + close(fd); +} + +int +main(int argc, char *argv[]) { + char *base; + + base = basename(argv[0]); + + if (argc != 2) { + fprintf(stderr, "usage: %s <cachefile>\n", base); + return 2; + } + + display_header(); + read_cache(argv[1]); +}