-
-
Save sjorge/db3661f5d3a79349a380da6cdc85eb4e to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| using System; | |
| using System.IO; | |
| using System.Globalization; | |
| using System.Text; | |
| using System.Security.Cryptography; | |
| using Microsoft.Data.Sqlite; | |
| namespace jf_subtitle_cache_cleaner; | |
| class Program | |
| { | |
| private static Guid GetMD5(string str) | |
| { | |
| return new Guid(MD5.HashData(Encoding.Unicode.GetBytes(str))); | |
| } | |
| private static string GetSubtitleCachePath(string subtitleCachePath, string path, int streamIndex, string outputSubtitleExtension) | |
| { | |
| var ticksParam = string.Empty; | |
| var date = File.GetLastWriteTimeUtc(path); | |
| ReadOnlySpan<char> filename = GetMD5(path + "_" + streamIndex.ToString(CultureInfo.InvariantCulture) + "_" + date.Ticks.ToString(CultureInfo.InvariantCulture) + ticksParam) + outputSubtitleExtension; | |
| var prefix = filename.Slice(0, 1); | |
| return Path.Join(subtitleCachePath, prefix, filename); | |
| } | |
| private static string GetSubtitleExtension(string codec) | |
| { | |
| if (codec.ToLower() == "ass" || codec.ToLower() == "ssa") | |
| { | |
| return "." + codec; | |
| } | |
| else | |
| { | |
| return ".srt"; | |
| } | |
| } | |
| private static bool IsTextFormat(string format) | |
| { | |
| string codec = format ?? string.Empty; | |
| // microdvd and dvdsub/vobsub share the ".sub" file extension, but it's text-based. | |
| return codec.Contains("microdvd", StringComparison.OrdinalIgnoreCase) | |
| || (!codec.Contains("pgs", StringComparison.OrdinalIgnoreCase) | |
| && !codec.Contains("dvdsub", StringComparison.OrdinalIgnoreCase) | |
| && !codec.Contains("dvbsub", StringComparison.OrdinalIgnoreCase) | |
| && !string.Equals(codec, "sup", StringComparison.OrdinalIgnoreCase) | |
| && !string.Equals(codec, "sub", StringComparison.OrdinalIgnoreCase)); | |
| } | |
| static void Main(string[] args) | |
| { | |
| var dataPath = "/var/lib/jellyfin/data"; | |
| var dbPath = Path.Join(dataPath, "library.db"); | |
| var subtitleCachePath = Path.Join(dataPath, "subtitles"); | |
| IDictionary<string, string> pathMappingCache = new Dictionary<string, string>(); | |
| List<string> whitelistSubtitleCacheFiles = new List<string>{}; | |
| var purgeCount = 0; | |
| var keepCount = 0; | |
| Console.WriteLine("Opening " + dbPath + " ..."); | |
| using (var connection = new SqliteConnection("Data Source=" + dbPath + ";Mode=ReadOnly;")) | |
| { | |
| connection.Open(); | |
| var commandMediaStream = connection.CreateCommand(); | |
| commandMediaStream.CommandText = | |
| @" | |
| SELECT * | |
| FROM mediastreams | |
| WHERE StreamType = @streamType AND IsExternal = @isExternal; | |
| "; | |
| commandMediaStream.Parameters.AddWithValue("@streamType", "Subtitle"); | |
| commandMediaStream.Parameters.AddWithValue("@isExternal", 0); | |
| Console.WriteLine("Looking up subtitle mediastreams ..."); | |
| using (var readerMediaStream = commandMediaStream.ExecuteReader()) | |
| { | |
| while (readerMediaStream.Read()) | |
| { | |
| var guid = readerMediaStream.GetGuid(0); | |
| if (!IsTextFormat(readerMediaStream.GetString(readerMediaStream.GetOrdinal("Codec")))) | |
| { | |
| continue; | |
| } | |
| if (!pathMappingCache.ContainsKey(guid.ToString())) | |
| { | |
| var commandBaseItem = connection.CreateCommand(); | |
| commandBaseItem.CommandText = | |
| @" | |
| SELECT * | |
| FROM TypedBaseItems WHERE guid = @guid; | |
| "; | |
| commandBaseItem.Parameters.AddWithValue("@guid", guid.ToByteArray()); | |
| using (var readerBaseItem = commandBaseItem.ExecuteReader()) | |
| { | |
| while (readerBaseItem.Read()) | |
| { | |
| pathMappingCache.Add(guid.ToString(), readerBaseItem.GetString(readerBaseItem.GetOrdinal("Path"))); | |
| } | |
| } | |
| } | |
| var path = pathMappingCache[guid.ToString()]; | |
| var streamIndex = readerMediaStream.GetInt32(readerMediaStream.GetOrdinal("StreamIndex")); | |
| var extension = GetSubtitleExtension(readerMediaStream.GetString(readerMediaStream.GetOrdinal("Codec"))); | |
| var subtitleCacheFile = GetSubtitleCachePath(subtitleCachePath, path, streamIndex, extension); | |
| whitelistSubtitleCacheFiles.Add(subtitleCacheFile); | |
| } | |
| } | |
| Console.WriteLine("Detected " + whitelistSubtitleCacheFiles.Count + " valid subtitle cache paths."); | |
| foreach (string subtitleFile in Directory.GetFiles(subtitleCachePath, "*", SearchOption.AllDirectories)) | |
| { | |
| if (!whitelistSubtitleCacheFiles.Contains(subtitleFile)) | |
| { | |
| purgeCount += 1; | |
| File.Delete(subtitleFile); | |
| } | |
| else | |
| { | |
| keepCount += 1; | |
| } | |
| } | |
| Console.WriteLine("Subtitle cache: purged=" + purgeCount + ", kept=" + keepCount); | |
| } | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment