async Task <IReadOnlyCollection <JObject> > Jsonl(StoreFileMd f)
        {
            await using var sr = await Store.Load(f.Path);

            var existingJs = sr.LoadJsonlGz <JObject>();

            return(existingJs);
        }
        async Task ReplaceJsonLFile(StoreFileMd f, StringPath newPath, IEnumerable <JToken> upgradedJs)
        {
            await using var stream = upgradedJs.ToJsonlGzStream();
            await Store.Save(newPath, stream);

            var deleted = await Store.Delete(f.Path);

            if (!deleted)
            {
                throw new InvalidOperationException($"Didn't delete old file {f.Path}");
            }
            Log.Information("Upgraded {OldFile} to {File}", f.Path, newPath);
        }
        async Task <int> UpdateRecs_0to1()
        {
            var toUpgrade = await FilesToUpgrade("recs", 0);

            V0UpdateTime = DateTime.Parse("2019-11-02T13:50:00Z").ToUniversalTime();
            await toUpgrade.BlockAction(async f => {
                var existingJs = await Jsonl(f);
                var upgradedJs = existingJs.GroupBy(j => j["FromVideoId"].Value <string>()).SelectMany(g => {
                    return(g.Select((j, i) => {
                        var newJ = j.DeepClone();
                        newJ["Updated"] = V0UpdateTime;
                        newJ["Rank"] = i + 1;
                        return newJ;
                    }));
                });
                var newPath = StoreFileMd.FilePath(f.Path.Parent, V0UpdateTime.FileSafeTimestamp(), "1");
                await ReplaceJsonLFile(f, newPath, upgradedJs);
            }, Cfg.DefaultParallel);

            return(toUpgrade.Count);
        }
 static StringPath NewFilePath(StoreFileMd f, int version) =>
 StoreFileMd.FilePath(f.Path.Parent, StoreFileMd.GetTs(f.Path), version.ToString());