Skip to content

Commit

Permalink
Merge pull request #2 from ndm13/chapter-support
Browse files Browse the repository at this point in the history
Chapter support
  • Loading branch information
ndm13 authored Dec 24, 2022
2 parents f4b44a4 + 5620f5e commit 6b3965b
Show file tree
Hide file tree
Showing 7 changed files with 82 additions and 13 deletions.
8 changes: 6 additions & 2 deletions automkv.example.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
batch: # Every file has a root batch element
- watch: # Watch as many folders as you want
folder: Season 1 # Folder name is static
files: S01E\d{2}\.mkv # but file name is a RegExp
files: S01E\d{2}\.mkv$ # but file name is a RegExp
edits: # Edits are per-track
- edit: track:a2 # Specify using mkvpropedit syntax
set: # Set values to update
flag-commentary: 1 # Booleans are 1/0 as per spec
name: Director's Commentary # Strings don't need any special formatting
name: Director's Commentary # Strings don't need any special formatting
chapters: # Chapters will only be mapped if there is
- Opening Credits # an entry for each chapter.
- Episode # automkv files may include chapters, edits,
- Ending Credits # both, or neither (but why neither?)
2 changes: 1 addition & 1 deletion automkv.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import Watcher from "./watcher.ts";

const mkvpropedit = await getExe("mkvpropedit");
const mkvextract = await getExe("mkvextract");
const runner = new Runner(mkvpropedit);
const runner = new Runner(mkvpropedit, mkvextract);
const watcher = new Watcher(runner);

console.log("Running automkv with versions:");
Expand Down
7 changes: 6 additions & 1 deletion readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,12 @@ batch: # Every file has a root batch element
set: # Set values to update
flag-commentary: 1 # Booleans are 1/0 as per spec
name: Director's Commentary # Strings don't need any special formatting
chapters: # Chapters will only be mapped if there is
- Opening Credits # an entry for each chapter.
- Episode # automkv files may include chapters, edits,
- Ending Credits # both, or neither (but why neither?)
```
The commands under `edits` use the same formatting as `mkvpropedit` and are
passed more or less unchanged: see [the guide](https://mkvtoolnix.download/doc/mkvpropedit.html)
for more details.
for more details. For the `chapters` section, updated chapter titles will only
be applied if the actual number of chapters is the same as the number provided.
57 changes: 53 additions & 4 deletions runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,14 @@ import {Edit} from "./types.ts";
import {readYaml} from "./utils.ts";

export default class Runner {
private readonly decoder: TextDecoder = new TextDecoder();
private readonly encoder: TextEncoder = new TextEncoder();
private readonly mkvpropedit: string;
private readonly mkvextract: string;

constructor(mkvpropedit: string) {
constructor(mkvpropedit: string, mkvextract: string) {
this.mkvpropedit = mkvpropedit;
this.mkvextract = mkvextract;
}

async batch(automkv: string): Promise<number> {
Expand All @@ -16,10 +20,16 @@ export default class Runner {

const promises = [];
for (const batch of yml) {
if (!batch.edits && !batch.chapters) continue;
const folder = path.join(path.dirname(automkv), batch.watch.folder || '.');
for await (const file of Deno.readDir(folder))
if (file.name.match(batch.watch.files))
promises.push(this.edits(path.join(folder, file.name), batch.edits));
for await (const entry of Deno.readDir(folder))
if (entry.name.match(batch.watch.files)) {
const file = path.join(folder, entry.name);
if (batch.edits)
promises.push(this.edits(file, batch.edits));
if (batch.chapters)
promises.push(this.chapters(file, batch.chapters));
}
}

return Promise.all(promises).then((mods: number[]) => mods.reduce((a, b) => a + b));
Expand All @@ -41,4 +51,43 @@ export default class Runner {
.status()
.then(s => s.success ? 1 : 0);
}

async chapters(file: string, chapters: string[]): Promise<number> {
if (!chapters)
return 0;

log.info(`Updating ${chapters.length} chapters for ${file}`);
const markers = await this.getChapterMarkers(file);
log.debug(`Found chapter markers at ${markers.join(', ')}`);
if (chapters.length !== markers.length) {
log.warning(`Expected ${chapters.length} chapters, found ${markers.length} for ${file}`);
return 0;
}
let chapterData = "";
for (const i in markers) {
const id = ((i + 1) as string).padStart(2, "0");
chapterData += "CHAPTER" + id + "=" + markers[i] + "\n";
chapterData += "CHAPTER" + id + "NAME=" + chapters[i] + "\n";
}
const chapterFile = file + "-chapters";
Deno.writeFileSync(chapterFile, this.encoder.encode(chapterData));
const cmd = [this.mkvpropedit, file, "-c", chapterFile];
log.debug(cmd.join(' '));
return await Deno.run({cmd: cmd, stdout: "inherit", stderr: "inherit"})
.status()
.then(s => s.success ? 1 : 0);
}

private async getChapterMarkers(file: string) {
const cmd = [this.mkvextract, "chapters", file, "-s"];
log.debug(cmd.join(' '));
const output = await Deno.run({cmd: cmd, stdout: "piped", stderr: "inherit"}).output();
return this.decoder
.decode(output)
.split("\n")
.filter((_e, i) => i % 2 == 0)
.map(s => s.trim())
.filter(s => s.length > 0)
.map(s => s.split('=')[1]);
}
}
3 changes: 2 additions & 1 deletion types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@ export type Batch = {
folder: string;
files: RegExp;
};
edits: Edit[];
edits?: Edit[];
chapters?: string[];
};

export type Edit = {
Expand Down
5 changes: 3 additions & 2 deletions utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,9 @@ export function readYaml(ymlPath: string): Batch[] {
// While functionally a map, we need to make it official
for (const batch of yml) {
batch.watch.files = new RegExp(batch.watch.files);
for (const edit of batch.edits)
edit.set = new Map(Object.entries(edit.set));
if (batch.edits)
for (const edit of batch.edits)
edit.set = new Map(Object.entries(edit.set));
}

return yml;
Expand Down
13 changes: 11 additions & 2 deletions watcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,16 @@ export default class Watcher {
return readYaml(configPath).map(batch => this.watch(
path.join(path.dirname(configPath), batch.watch.folder),
batch.watch.files,
file => this.runner.edits(file, batch.edits))
file => {
const promises = [];
if (batch.edits)
promises.push(this.runner.edits(file, batch.edits));
if (batch.chapters)
promises.push(this.runner.chapters(file, batch.chapters));
return Promise
.all(promises)
.then((edits: number[]) => edits.reduce((a, b) => a + b));
})
).reduce((last, current) => {
return {
cancel: reduceVoid(last.cancel, current.cancel),
Expand Down Expand Up @@ -73,7 +82,7 @@ export default class Watcher {
Deno.mkdirSync(folder, {recursive: true});
const watcher = Deno.watchFs(folder);
const promise = async () => {
const latch = new Map<string,number>();
const latch = new Map<string, number>();
for await (const event of watcher) {
if (event.paths.length < 1) continue;
const file = event.paths[0];
Expand Down

0 comments on commit 6b3965b

Please sign in to comment.