abspod/Program.cs
2025-03-31 20:47:28 -05:00

147 lines
4.6 KiB
C#

using System.Text.Json;
using AbsPod.Model;
using AbsPod.Util;
var configDir = Path.Combine(
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
"abspod");
var configFile = Path.Combine(configDir, "config.json");
// if no config file, generate one and exit
if (!Directory.Exists(configDir)
|| !File.Exists(configFile)) {
// first run
var exampleOptions = new AbsPodConfig {
HostUrl = "https://myaudiobookshelfserver.com",
Username = "myuser",
Password = "mypassword",
LibraryId = Guid.Empty,
SyncToDirectory = "/path/to/podcasts",
MaxEpisodesPerPodcast = 10,
};
if (!Directory.Exists(configDir))
Directory.CreateDirectory(configDir);
File.WriteAllText(configFile, JsonSerializer.Serialize(
exampleOptions, JsonOpts.Options));
Console.WriteLine(
$"Generated example config at {configFile}. Manually edit before running again.");
Environment.Exit(0);
}
AbsPodConfig? config;
var configStr = File.ReadAllText(configFile);
config = JsonSerializer.Deserialize<AbsPodConfig>(configStr, JsonOpts.Options);
if (config == null) {
Console.WriteLine("Unable to read config.");
Environment.Exit(1);
}
if (!Directory.Exists(config.SyncToDirectory)) {
Console.WriteLine($"Sync directory {config.SyncToDirectory} does not exist.");
Environment.Exit(1);
}
var podMetaPath = Path.Combine(config.SyncToDirectory, ".podmeta.json");
var podMeta = File.Exists(podMetaPath)
? JsonSerializer.Deserialize<PodcastMeta>(File.ReadAllText(podMetaPath), JsonOpts.Options)
: new() { Podcasts = [] };
if (podMeta == null) {
Console.WriteLine("Error reading podcast metadata.");
Environment.Exit(1);
}
var client = new AbsClient(
config.HostUrl,
config.Username,
config.Password);
if (!await client.Authenticate()) {
Console.WriteLine("Failed to authenticate.");
Environment.Exit(1);
}
var podcasts = await client.GetPodcasts(config.LibraryId);
foreach (var (podcastId, podcastName) in podcasts) {
var meta = podMeta.Podcasts.SingleOrDefault(p => p.Id == podcastId);
if (meta == null) {
Console.WriteLine($"Found new podcast: {podcastName}.");
meta = new() {
Id = podcastId,
};
podMeta.Podcasts.Add(meta);
} else {
Console.WriteLine($"Processing podcast: {podcastName}.");
}
var podDir = Path.Combine(config.SyncToDirectory, FileNamer.PodDir(podcastName));
if (!Directory.Exists(podDir)) Directory.CreateDirectory(podDir);
var episodes = await client.GetEpisodes(podcastId);
var lastDownloadedIndex = -1;
if (meta.LastEpisodeId.HasValue) {
foreach (var (episodeId, _, _, _) in episodes) {
lastDownloadedIndex++;
if (episodeId == meta.LastEpisodeId) {
break;
}
}
}
var downloadedCount = 0;
for (var i = 0; i < episodes.Length; i++) {
var (episodeId, episodeName, episodeDate, extension) = episodes[i];
var epFile = Path.Combine(podDir, FileNamer.EpFile(episodeName, episodeDate, extension));
var fileExists = File.Exists(epFile);
var isFinished = await client.GetMediaFinished(podcastId, episodeId);
if (i <= lastDownloadedIndex) {
// handle files we already may have downloaded
if (fileExists) {
if (isFinished) {
// episode was finished somewhere else, delete it
Console.WriteLine($"Deleting finished episode: {episodeName}");
File.Delete(epFile);
} else {
// episode was downloaded and not yet finished, keep it
downloadedCount++;
}
} else {
if (!isFinished) {
// file was previously downloaded and deleted, mark it as finished on the server
Console.WriteLine($"Marking episode finished: {episodeName}");
await client.SetMediaFinished(podcastId, episodeId);
}
}
} else {
if (fileExists) {
// probably means we crashed last time and didn't save metadata
// let's delete it and re-process
File.Delete(epFile);
}
// handle new episodes not yet downloaded
if (downloadedCount >= config.MaxEpisodesPerPodcast) {
// if we're already at the max we don't need to download anymore
break;
}
if (!isFinished) {
// download the episode
await client.DownloadEpisode(podcastId, episodeId, episodeName, epFile);
meta.LastEpisodeId = episodeId;
downloadedCount++;
}
}
}
}
// save metadata
var oldPath = $"{podMetaPath}.old";
if (File.Exists(oldPath)) File.Delete(oldPath);
if (File.Exists(podMetaPath)) File.Move(podMetaPath, oldPath);
File.WriteAllText(podMetaPath, JsonSerializer.Serialize(podMeta, JsonOpts.Options));