mirror of
https://github.com/fmang/opustags.git
synced 2025-07-07 10:04:30 +02:00
Compare commits
11 Commits
Author | SHA1 | Date | |
---|---|---|---|
7174a1f2f2 | |||
1a8aaff933 | |||
4973a4deab | |||
8e9d98ac62 | |||
fcd647003b | |||
a8e22fb5db | |||
f75e484ebb | |||
6f6cf8024b | |||
f1828d926f | |||
1021b4394d | |||
1e138ad00d |
26
README.md
26
README.md
@ -3,6 +3,32 @@ opustags
|
||||
|
||||
View and edit Opus comments.
|
||||
|
||||
**Please note this project is old and not actively maintained.**
|
||||
Maybe you should use something else.
|
||||
|
||||
It was built because at the time nothing supported Opus, but now things have
|
||||
changed for the better.
|
||||
|
||||
For alternative, check out these projects:
|
||||
|
||||
- [EasyTAG](https://wiki.gnome.org/Apps/EasyTAG)
|
||||
- [Beets](http://beets.io/)
|
||||
- [Picard](https://picard.musicbrainz.org/)
|
||||
- [puddletag](http://docs.puddletag.net/)
|
||||
- [Quod Libet](https://quodlibet.readthedocs.io/en/latest/)
|
||||
- [Goggles Music Manager](https://gogglesmm.github.io/)
|
||||
|
||||
See also these libraries if you need a lower-level access:
|
||||
|
||||
- [TagLib](http://taglib.org/)
|
||||
- [mutagen](https://mutagen.readthedocs.io/en/latest/)
|
||||
|
||||
Requirements
|
||||
------------
|
||||
|
||||
* A POSIX-compliant system,
|
||||
* libogg.
|
||||
|
||||
Installing
|
||||
----------
|
||||
|
||||
|
18
opustags.1
18
opustags.1
@ -1,4 +1,4 @@
|
||||
.TH opustags 1 "January 1st 2013"
|
||||
.TH opustags 1 "January 2013"
|
||||
.SH NAME
|
||||
opustags \- Opus comment editor
|
||||
.SH SYNOPSIS
|
||||
@ -26,6 +26,7 @@ This could be useful to preview some changes before writing them.
|
||||
As for the edition mode, you need to specify an output file (or \fB-\fP for
|
||||
\fBstdout\fP). It must be different from the input file.
|
||||
You may want to use \fB--overwrite\fP if you know what you’re doing.
|
||||
To overwrite the input file, use \fB--in-place\fP.
|
||||
.PP
|
||||
Tag edition can be made with the \fB--add\fP, \fB--delete\fP and \fB--set\fP
|
||||
options. They can be written in any order and don’t conflict with each other.
|
||||
@ -57,6 +58,14 @@ specified output file. If \fIFILE\fP is \fB-\fP then the resulting Opus file
|
||||
will be written to \fBstdout\fP. As the input file is read incrementally, the
|
||||
output file can’t be the same as the input file.
|
||||
.TP
|
||||
.B \-i, \-\-in-place \fR[\fP\fISUFFIX\fP\fR]\fP
|
||||
Use this when you want to modify the input file in-place. This creates a
|
||||
temporary file with the specified suffix (.otmp by default). This implies
|
||||
\fB--overwrite\fP in that if a file with the same temporary name already
|
||||
exists, it will be overwritten without warning. Of course, this overwrites
|
||||
the input file too. You cannot use this option when the input file is actually
|
||||
\fBstdin\fP.
|
||||
.TP
|
||||
.B \-y, \-\-overwrite
|
||||
By default, \fBopustags\fP refuses to overwrite an already existent file. Use
|
||||
this option to allow that. Note that this doesn’t allow in-place edition, the
|
||||
@ -89,10 +98,9 @@ or \fB--set\fP, which, in that case, are equivalent.
|
||||
.B \-S, \-\-set-all
|
||||
Sets the tags from scratch. All the original tags are deleted and new ones are
|
||||
read from \fBstdin\fP. Each line must specify a \fIFIELD=VALUE\fP pair and be
|
||||
LF-terminated. If the last line isn’t terminated when the end of the stream is
|
||||
reached, it is ignored. Invalid lines are skipped and cause a warning to be
|
||||
issued. This could be useful for batch processing tags through an utility like
|
||||
\fBsed\fP.
|
||||
LF-terminated (except for the last line). Invalid lines are skipped and cause
|
||||
a warning to be issued. Blank lines are ignored. This mode could be useful for
|
||||
batch processing tags through an utility like \fBsed\fP.
|
||||
.SH SEE ALSO
|
||||
.BR vorbiscomment (1),
|
||||
.BR sed (1)
|
||||
|
111
opustags.c
111
opustags.c
@ -7,6 +7,12 @@
|
||||
#include <unistd.h>
|
||||
#include <ogg/ogg.h>
|
||||
|
||||
#ifdef __APPLE__
|
||||
#include <libkern/OSByteOrder.h>
|
||||
#define htole32(x) OSSwapHostToLittleInt32(x)
|
||||
#define le32toh(x) OSSwapLittleToHostInt32(x)
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
uint32_t vendor_length;
|
||||
const char *vendor_string;
|
||||
@ -50,8 +56,10 @@ int parse_tags(char *data, long len, opus_tags *tags){
|
||||
if(pos > len)
|
||||
return -1;
|
||||
}
|
||||
if(pos != len)
|
||||
return -1;
|
||||
|
||||
if(pos < len)
|
||||
fprintf(stderr, "warning: %ld unused bytes at the end of the OpusTags packet\n", len - pos);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -156,7 +164,7 @@ int write_page(ogg_page *og, FILE *stream){
|
||||
return 0;
|
||||
}
|
||||
|
||||
const char *version = "opustags version 1.0\n";
|
||||
const char *version = "opustags version 1.1.1\n";
|
||||
|
||||
const char *usage =
|
||||
"Usage: opustags --help\n"
|
||||
@ -167,6 +175,7 @@ const char *help =
|
||||
"Options:\n"
|
||||
" -h, --help print this help\n"
|
||||
" -o, --output write the modified tags to a file\n"
|
||||
" -i, --in-place [SUFFIX] use a temporary file then replace the original file\n"
|
||||
" -y, --overwrite overwrite the output file if it already exists\n"
|
||||
" -d, --delete FIELD delete all the fields of a specified type\n"
|
||||
" -a, --add FIELD=VALUE add a field\n"
|
||||
@ -177,12 +186,14 @@ const char *help =
|
||||
struct option options[] = {
|
||||
{"help", no_argument, 0, 'h'},
|
||||
{"output", required_argument, 0, 'o'},
|
||||
{"in-place", optional_argument, 0, 'i'},
|
||||
{"overwrite", no_argument, 0, 'y'},
|
||||
{"delete", required_argument, 0, 'd'},
|
||||
{"add", required_argument, 0, 'a'},
|
||||
{"set", required_argument, 0, 's'},
|
||||
{"delete-all", no_argument, 0, 'D'},
|
||||
{"set-all", no_argument, 0, 'S'}
|
||||
{"set-all", no_argument, 0, 'S'},
|
||||
{NULL, 0, 0, 0}
|
||||
};
|
||||
|
||||
int main(int argc, char **argv){
|
||||
@ -191,7 +202,7 @@ int main(int argc, char **argv){
|
||||
fputs(usage, stdout);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
const char *path_in, *path_out = NULL;
|
||||
char *path_in, *path_out = NULL, *inplace = NULL;
|
||||
const char* to_add[argc];
|
||||
const char* to_delete[argc];
|
||||
int count_add = 0, count_delete = 0;
|
||||
@ -200,7 +211,7 @@ int main(int argc, char **argv){
|
||||
int overwrite = 0;
|
||||
int print_help = 0;
|
||||
int c;
|
||||
while((c = getopt_long(argc, argv, "ho:yd:a:s:DS", options, NULL)) != -1){
|
||||
while((c = getopt_long(argc, argv, "ho:i::yd:a:s:DS", options, NULL)) != -1){
|
||||
switch(c){
|
||||
case 'h':
|
||||
print_help = 1;
|
||||
@ -208,6 +219,9 @@ int main(int argc, char **argv){
|
||||
case 'o':
|
||||
path_out = optarg;
|
||||
break;
|
||||
case 'i':
|
||||
inplace = optarg == NULL ? ".otmp" : optarg;
|
||||
break;
|
||||
case 'y':
|
||||
overwrite = 1;
|
||||
break;
|
||||
@ -248,6 +262,10 @@ int main(int argc, char **argv){
|
||||
fputs("invalid arguments\n", stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(inplace && path_out){
|
||||
fputs("cannot combine --in-place and --output\n", stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
path_in = argv[optind];
|
||||
if(path_out != NULL && strcmp(path_in, "-") != 0){
|
||||
char canon_in[PATH_MAX+1], canon_out[PATH_MAX+1];
|
||||
@ -258,30 +276,14 @@ int main(int argc, char **argv){
|
||||
}
|
||||
}
|
||||
}
|
||||
FILE *out = NULL;
|
||||
if(path_out != NULL){
|
||||
if(strcmp(path_out, "-") == 0)
|
||||
out = stdout;
|
||||
else{
|
||||
if(!overwrite){
|
||||
if(access(path_out, F_OK) == 0){
|
||||
fprintf(stderr, "'%s' already exists (use -y to overwrite)\n", path_out);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
out = fopen(path_out, "w");
|
||||
if(!out){
|
||||
perror("fopen");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
FILE *in;
|
||||
if(strcmp(path_in, "-") == 0){
|
||||
if(set_all){
|
||||
fputs("can't open stdin for input when -S is specified\n", stderr);
|
||||
if(out)
|
||||
fclose(out);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
if(inplace){
|
||||
fputs("cannot modify stdin 'in-place'\n", stderr);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
in = stdin;
|
||||
@ -290,10 +292,40 @@ int main(int argc, char **argv){
|
||||
in = fopen(path_in, "r");
|
||||
if(!in){
|
||||
perror("fopen");
|
||||
if(out)
|
||||
fclose(out);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
FILE *out = NULL;
|
||||
if(inplace != NULL){
|
||||
path_out = malloc(strlen(path_in) + strlen(inplace) + 1);
|
||||
if(path_out == NULL){
|
||||
fputs("failure to allocate memory\n", stderr);
|
||||
fclose(in);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
strcpy(path_out, path_in);
|
||||
strcat(path_out, inplace);
|
||||
}
|
||||
if(path_out != NULL){
|
||||
if(strcmp(path_out, "-") == 0)
|
||||
out = stdout;
|
||||
else{
|
||||
if(!overwrite && !inplace){
|
||||
if(access(path_out, F_OK) == 0){
|
||||
fprintf(stderr, "'%s' already exists (use -y to overwrite)\n", path_out);
|
||||
fclose(in);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
out = fopen(path_out, "w");
|
||||
if(!out){
|
||||
perror("fopen");
|
||||
fclose(in);
|
||||
if(inplace)
|
||||
free(path_out);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
}
|
||||
ogg_sync_state oy;
|
||||
ogg_stream_state os, enc;
|
||||
ogg_page og;
|
||||
@ -380,16 +412,19 @@ int main(int argc, char **argv){
|
||||
}
|
||||
else{
|
||||
char *raw_comment[256];
|
||||
size_t raw_len = fread(raw_tags, 1, 16384, stdin);
|
||||
if(raw_len == 16384)
|
||||
size_t raw_len = fread(raw_tags, 1, 16383, stdin);
|
||||
if(raw_len == 16383)
|
||||
fputs("warning: truncating comment to 16 KiB\n", stderr);
|
||||
raw_tags[raw_len] = '\0';
|
||||
uint32_t raw_count = 0;
|
||||
size_t field_len = 0;
|
||||
int caught_eq = 0;
|
||||
size_t i = 0;
|
||||
char *cursor = raw_tags;
|
||||
for(i=0; i<raw_len && raw_count < 256; i++){
|
||||
if(raw_tags[i] == '\n'){
|
||||
for(i=0; i <= raw_len && raw_count < 256; i++){
|
||||
if(raw_tags[i] == '\n' || raw_tags[i] == '\0'){
|
||||
if(field_len == 0)
|
||||
continue;
|
||||
if(caught_eq)
|
||||
raw_comment[raw_count++] = cursor;
|
||||
else
|
||||
@ -460,7 +495,19 @@ int main(int argc, char **argv){
|
||||
error = "opustags: invalid file";
|
||||
if(error){
|
||||
fprintf(stderr, "%s\n", error);
|
||||
if(path_out != NULL && out != stdout)
|
||||
remove(path_out);
|
||||
if(inplace)
|
||||
free(path_out);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
else if(inplace){
|
||||
if(rename(path_out, path_in) == -1){
|
||||
perror("rename");
|
||||
free(path_out);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
free(path_out);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
Reference in New Issue
Block a user