Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 0 additions & 6 deletions src/lib/Subscription.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,11 +59,6 @@ export default class Subscription {
private static isChannelCache: Record<string, isChannel> = {};
private static isChannelHelper = `const isChannel = (post, channelId) => (typeof post.channel !== 'string' ? post.channel.id : post.channel) === channelId`;

private async fetchTextTracks(attachmentId: string) {
const video = await fApi.content.video(attachmentId);
return video.textTracks?.filter((track) => track.kind === "captions") ?? [];
}

private async *matchChannel(blogPost: BlogPost): AsyncGenerator<Video> {
if (blogPost.videoAttachments === undefined) return;
let dateOffset = 0;
Expand Down Expand Up @@ -121,7 +116,6 @@ export default class Subscription {
channelTitle: channel.title,
videoTitle: post.title,
releaseDate: new Date(new Date(post.releaseDate).getTime() + dateOffset * 1000),
textTracks: video.textTracks,
});
break;
}
Expand Down
38 changes: 21 additions & 17 deletions src/lib/Video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,17 +62,15 @@ export type VideoInfo = {
channelTitle: string;
videoTitle: string;
releaseDate: Date;
textTracks?: VideoContent["textTracks"];
};

const byteToMbits = 131072;

export class Video extends Attachment {
public readonly description: string;
public readonly artworkUrl?: string;
public readonly textTracks?: VideoContent["textTracks"];
private readonly description: string;

public static State = VideoState;
private static State = VideoState;

private static readonly MaxRetries = 1;
private static readonly DownloadThreads = 8;
Expand All @@ -86,7 +84,7 @@ export class Video extends Attachment {
private readonly logger = new Video.ProgressLogger(this.videoTitle);

// Static cache of instances
public static readonly Videos: Record<string, Video> = {};
private static readonly Videos: Record<string, Video> = {};
public static getOrCreate(videoInfo: VideoInfo): Video {
if (this.Videos[videoInfo.attachmentId] !== undefined) return this.Videos[videoInfo.attachmentId];
return (this.Videos[videoInfo.attachmentId] = new this(videoInfo));
Expand All @@ -97,7 +95,6 @@ export class Video extends Attachment {

this.description = videoInfo.description;
this.artworkUrl = videoInfo.artworkUrl;
this.textTracks = videoInfo.textTracks;
// Ensure onError is bound to this instance
this.onError = this.onError.bind(this);
}
Expand All @@ -114,6 +111,9 @@ export class Video extends Attachment {
if (settings.extras.downloadArtwork) {
await this.downloadArtwork().catch(withContext(`Saving artwork`)).catch(this.onError);
}
if (settings.extras.downloadCaptions) {
await this.downloadCaptions().catch(withContext(`Downloading captions`)).catch(this.onError);
}
if ((await this.getState()) === Video.State.Muxed) {
this.logger.done(chalk`{green Exists! Skipping}`);
return;
Expand Down Expand Up @@ -292,18 +292,22 @@ export class Video extends Attachment {
}

private async downloadCaptions() {
if (this.textTracks === undefined) return;
const captions = this.textTracks.filter((track) => track.kind === "captions");
if (captions.length === 0) return;
this.logger.log("Saving captions");

for (const caption of captions) {
const captionPath = `${this.filePath}${caption.language ? `.${caption.language}` : ""}.vtt`;
if (await fileExists(captionPath)) continue;
const captionContent = await fetch(caption.src).then((res) => res.text());
await writeFile(captionPath, captionContent, "utf8");
try {
const video = await fApi.content.video(this.attachmentId);
const textTracks = video.textTracks?.filter((track) => track.kind === "captions") ?? [];
if (textTracks.length === 0) return;

for (const caption of textTracks) {
const captionPath = `${this.filePath}${caption.language ? `.${caption.language}` : ""}.vtt`;
if (await fileExists(captionPath)) continue;
this.logger.log(`Saving ${caption.language} captions`);
const captionContent = await (await fetch(caption.src)).text();
await writeFile(captionPath, captionContent, "utf8");
this.logger.log(`Saved ${caption.language} captions`);
}
} catch (error) {
this.onError(`Failed to download captions: ${error}`);
}
this.logger.log("Saved captions");
}

// The number of available slots for making delivery requests,
Expand Down
1 change: 1 addition & 0 deletions src/lib/defaults.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ export const defaultSettings: Settings = {
downloadArtwork: true,
saveNfo: true,
considerAllNonPartialDownloaded: false,
downloadCaptions: true,
},
artworkSuffix: "",
postProcessingCommand: "",
Expand Down
1 change: 1 addition & 0 deletions src/lib/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export interface Extras extends Record<string, boolean> {
downloadArtwork: boolean;
saveNfo: boolean;
considerAllNonPartialDownloaded: boolean;
downloadCaptions: boolean;
}

export type Resolution = ValueOfA<Resolutions>;
Expand Down
12 changes: 12 additions & 0 deletions wiki/settings.md
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,18 @@ This may result in files without muxed metadata and should only be used for reco

<br>

**extras.downloadCaptions**:
Controls whether video captions/subtitles are downloaded when available.
Caption files are saved as `.vtt` files in the same directory as the video.

```json
"extras": {
"downloadCaptions": true
}
```

<br>

## Plex

Use **quickstartPrompts** to easily set plex settings.
Expand Down
Loading