Пример #1
0
        // Create sitemap.xml file from HTML files in output directory. Obviously, this
        // needs to go after the processor that creates the HTML files.
        // directory: information about the directory to process, e.g., input directory,
        // output directory and the processor (transformation) to use.
        public static void ProcessSitemap(DirectoryToProcess directory)
        {
            directory = directory ?? throw new ArgumentNullException(nameof(directory));
            var myConfig = (from p in directory.Processors
                            where p.Class == $"{typeof(Sitemap)}" &&
                            p.Method == nameof(ProcessSitemap)
                            select p).FirstOrDefault() ?? new Processor();

            if (Directory.Exists(directory.OutputPath))
            {
                var normalizedPath = Path.GetFullPath(directory.OutputPath);
                var outputFileName = Path.Combine(normalizedPath, "sitemap.xml");
                var sitemap        = new StringBuilder(@"<?xml version='1.0' encoding='UTF-8'?>
<urlset xmlns='http://www.sitemaps.org/schemas/sitemap/0.9'>
");
                myConfig.Wildcards.ForEach(wc =>
                {
                    Directory.EnumerateFiles(normalizedPath, wc, SearchOption.AllDirectories)
                    .Except(myConfig.Exclusions.Select(e => Path.Combine(normalizedPath, e)))
                    .OrderBy(p => p)
                    .ToList()
                    .ForEach(htmlFileName =>
                    {
                        // Do the bare minimum escaping, because & the likeliest problematic
                        // file or path name character we're going to find.
                        sitemap.Append($"\t<url><loc>{directory.TargetURL.Replace("&", "&amp;")}/{Path.GetFileName(htmlFileName).Replace("&", "&amp;")}</loc></url>\n");
                    });
                });
                sitemap.Append("</urlset>");
                File.WriteAllText(outputFileName, sitemap.ToString());
            }
        }
Пример #2
0
        // Recursively copy files by wildcard from input to output directory. These are done
        // in parallel and hence should be autonomous from each other.
        // directory: information about the directory to process, e.g., input directory,
        // output directory and the processor (transformation) to use.
        public static void ProcessStaticFiles(DirectoryToProcess directory)
        {
            directory = directory ?? throw new ArgumentNullException(nameof(directory));
            var myConfig = (from p in directory.Processors
                            where p.Class == $"{typeof(BasicProcessors)}" &&
                            p.Method == nameof(ProcessStaticFiles)
                            select p).FirstOrDefault() ?? new Processor();

            if (Directory.Exists(directory.InputPath))
            {
                var normalizedPath = Path.GetFullPath(directory.InputPath);

                Parallel.ForEach(myConfig.Wildcards, wc =>
                {
                    Parallel.ForEach(Directory.EnumerateFiles(normalizedPath, wc, SearchOption.AllDirectories)
                                     .Except(myConfig.Exclusions.Select(e => Path.Combine(normalizedPath, e))), inputFileName =>
                    {
                        var subdirectory    = GetSubdirectory(normalizedPath, Path.GetDirectoryName(inputFileName));
                        var outputDirectory = Path.Combine(directory.OutputPath, subdirectory);
                        Directory.CreateDirectory(outputDirectory);
                        var outputFileName = Path.Combine(outputDirectory, Path.GetFileName(inputFileName));

                        // Only process file if the output file doesn't exist or exists and is older than the input file.
                        if ((File.Exists(outputFileName) &&
                             File.GetLastWriteTimeUtc(outputFileName) < File.GetLastWriteTimeUtc(inputFileName)) ||
                            !File.Exists(outputFileName))
                        {
                            File.Copy(inputFileName, outputFileName, true);
                        }
                    });
                });
            }
        }
Пример #3
0
        // Add watermark to images. These are done in parallel and hence should be
        // autonomous from each other.
        // Note that this processes files in the output directory, and does NOT update
        // the original input files.
        // directory: information about the directory to process, e.g., input directory,
        // output directory and the processor (transformation) to use.
        public static void AddWatermark(DirectoryToProcess directory)
        {
            directory = directory ?? throw new ArgumentNullException(nameof(directory));
            var myConfig = (from p in directory.Processors
                            where p.Class == $"{typeof(ImageProcessor)}" &&
                            p.Method == nameof(AddWatermark)
                            select p).FirstOrDefault() ?? new Processor();
            var procConfig = GetConfiguration <swingor_image.ProcessorConfiguration>(myConfig.ConfigFilePath, "ProcessorConfiguration");
            var font       = SystemFonts.CreateFont(procConfig.Font, procConfig.FontSize);

            if (Directory.Exists(directory.OutputPath))
            {
                var normalizedPath = Path.GetFullPath(directory.OutputPath);

                Parallel.ForEach(myConfig.Wildcards, wc =>
                {
                    Parallel.ForEach(Directory.EnumerateFiles(normalizedPath, wc, SearchOption.AllDirectories)
                                     .Except(myConfig.Exclusions.Select(e => Path.Combine(normalizedPath, e))), imgFileName =>
                    {
                        using (var img = Image.Load(imgFileName))
                        {
                            if (img.MetaData.ExifProfile != null && img.MetaData.ExifProfile.GetValue(ExifTag.Copyright) != null)
                            {
                                using (var img2 = img.Clone(ctx =>
                                                            ctx.ApplyScalingWaterMarkSimple(font, Rgba32.HotPink, 5, procConfig.ScalingFactor,
                                                                                            $"© {img.MetaData.ExifProfile.GetValue(ExifTag.Copyright).Value.ToString()}")))
                                {
                                    img2.Save(imgFileName);
                                };
                            }
                        }
                    });
                });
            }
        }
Пример #4
0
        // Recursively add copyright info to the EXIF metadata. These are done
        // in parallel and hence should be autonomous from each other.
        // Note that this processes files in the output directory, and does NOT update
        // the original input files.
        // directory: information about the directory to process, e.g., input directory,
        // output directory and the processor (transformation) to use.
        public static void ProcessImageExifs(DirectoryToProcess directory)
        {
            directory = directory ?? throw new ArgumentNullException(nameof(directory));
            var myConfig = (from p in directory.Processors
                            where p.Class == $"{typeof(ImageProcessor)}" &&
                            p.Method == nameof(ProcessImageExifs)
                            select p).FirstOrDefault() ?? new Processor();

            if (Directory.Exists(directory.OutputPath))
            {
                var normalizedPath = Path.GetFullPath(directory.OutputPath);

                Parallel.ForEach(myConfig.Wildcards, wc =>
                {
                    Parallel.ForEach(Directory.EnumerateFiles(normalizedPath, wc, SearchOption.AllDirectories)
                                     .Except(myConfig.Exclusions.Select(e => Path.Combine(normalizedPath, e))), imgFileName =>
                    {
                        using (var img = Image.Load(imgFileName))
                        {
                            img.MetaData.ExifProfile = img.MetaData.ExifProfile ?? new ExifProfile();
                            var changed = false;
                            changed     = img.AddExifTagIfMissing(ExifTag.Artist, directory.DefaultAuthor);
                            changed     = img.AddExifTagIfMissing(ExifTag.Copyright, $"{directory.DefaultAuthor} - {DateTime.Now.Year}");

                            if (changed)
                            {
                                img.Save(imgFileName);
                            }
                        }
                    });
                });
            }
        }
Пример #5
0
        // Process Markdown files. These are done in parallel and hence should be autonomous from
        // each other.
        // directory: information about the directory to process, e.g., input directory,
        // output directory and the processor (transformation) to use.
        public static void ProcessMarkdownFiles(DirectoryToProcess directory)
        {
            directory = directory ?? throw new ArgumentNullException(nameof(directory));
            var myConfig = (from p in directory.Processors
                            where p.Class == $"{typeof(BasicProcessors)}" &&
                            p.Method == nameof(ProcessMarkdownFiles)
                            select p).FirstOrDefault() ?? new Processor();

            if (Directory.Exists(directory.InputPath))
            {
                var normalizedPath = Path.GetFullPath(directory.InputPath);
                var pipeline       = new MarkdownPipelineBuilder().UseYamlFrontMatter().UseAdvancedExtensions().Build();

                Parallel.ForEach(myConfig.Wildcards, wc =>
                {
                    Parallel.ForEach(Directory.EnumerateFiles(normalizedPath, wc, SearchOption.AllDirectories)
                                     .Except(myConfig.Exclusions.Select(e => Path.Combine(normalizedPath, e))), inputFileName =>
                    {
                        var subdirectory    = GetSubdirectory(normalizedPath, Path.GetDirectoryName(inputFileName));
                        var outputDirectory = Path.Combine(directory.OutputPath, subdirectory);
                        Directory.CreateDirectory(outputDirectory);
                        var outputFileName = Path.Combine(outputDirectory, $"{Path.GetFileNameWithoutExtension(inputFileName)}.html");

                        // Only process file if the output file doesn't exist or exists and is older than the input file.
                        if ((File.Exists(outputFileName) &&
                             File.GetLastWriteTimeUtc(outputFileName) < File.GetLastWriteTimeUtc(inputFileName)) ||
                            !File.Exists(outputFileName))
                        {
                            var outputText = new StringBuilder();
                            myConfig.Prepends.ForEach(file => outputText.Append(File.ReadAllText(Path.Combine(directory.InputPath, file))));
                            var fileText = File.ReadAllText(inputFileName);
                            var meta     = ParseYAML(fileText, directory.DefaultAuthor, directory.DefaultTitle);
                            outputText.Append(Markdown.ToHtml(fileText, pipeline));
                            myConfig.Postpends.ForEach(file => outputText.Append(File.ReadAllText(Path.Combine(directory.InputPath, file))));
                            ReplaceTemplatesWithMetadata(outputText, meta);
                            File.WriteAllText(outputFileName, outputText.ToString());
                        }
                    });
                });
            }
        }
Пример #6
0
        // Create rss.xml file from HTML files in output directory. This should typically
        // go after the processor that creates the HTML files, although it works off the
        // input files and just makes assumptions about the output.
        // directory: information about the directory to process, e.g., input directory,
        // output directory and the processor (transformation) to use.
        public static void ProcessRSSFeed(DirectoryToProcess directory)
        {
            directory = directory ?? throw new ArgumentNullException(nameof(directory));
            var myConfig = (from p in directory.Processors
                            where p.Class == $"{typeof(RSS)}" &&
                            p.Method == nameof(ProcessRSSFeed)
                            select p).FirstOrDefault() ?? new Processor();
            var stopAfter = myConfig.StopAfter;

            if (Directory.Exists(directory.OutputPath))
            {
                var normalizedPath = Path.GetFullPath(directory.OutputPath);
                var outputFileName = Path.Combine(normalizedPath, "rss.xml");
                var rss            = new StringBuilder($@"<?xml version='1.0' encoding='UTF-8'?>
<rss version='2.0'>
    <channel>
        <title>{directory.SiteTitle.Replace("&", "&amp;")}</title>
        <description>{directory.SiteDescription.Replace("&", "&amp;")}</description>
        <link>{directory.TargetURL.Replace("&", "&amp;")}</link>
        <lastBuildDate>{DateTime.Now.ToString("R")}</lastBuildDate>
        <pubDate>{DateTime.Now.ToString("R")}</pubDate>
        <ttl>1800</ttl>'
");
                var inputDir       = new DirectoryInfo(directory.InputPath);
                myConfig.Wildcards.ForEach(wc =>
                {
                    (from fi in inputDir.EnumerateFileSystemInfos(wc, SearchOption.AllDirectories)
                     where !myConfig.Exclusions.Contains(fi.Name)
                     select fi)
                    .OrderByDescending(fi => fi.LastWriteTimeUtc)
                    .Take(stopAfter.HasValue && stopAfter.Value > 0 ? stopAfter.Value : int.MaxValue)
                    .ToList()
                    .ForEach(fi =>
                    {
                        using (var hasher = MD5.Create())
                        {
                            // A bit of a hack - the RSS spec says the Guid should be static for any given
                            // article, even if it changes. So, using the input file name to compute a hash and
                            // using that for the article identifying Guid. If the file name changes, the output
                            // file name will also change, and IMHO, then the Guid should, too.
                            var guid = new Guid(hasher.ComputeHash(Encoding.UTF8.GetBytes(fi.Name))).ToString();
                            // Could use the ParseYAML method from the main program, but our needs here are
                            // simple (for now), and a bit specialized.
                            var lines = File.ReadAllLines(fi.FullName).ToList();
                            var title = lines.Where(l => l.StartsWith("title:")).FirstOrDefault();

                            // Look for YAML front matter for the title. If don't find it, look for first
                            // level-1 header. Both of these are a bit "fragile."
                            if (!string.IsNullOrEmpty(title))
                            {
                                title = title.Replace("title:", "").Trim();
                            }
                            else
                            {
                                title = lines.Where(l => l.StartsWith("# ")).FirstOrDefault();
                            }

                            if (!string.IsNullOrEmpty(title))
                            {
                                title = title.Replace("# ", "").Trim();
                            }
                            else
                            {
                                // Otherwise use the base file name.
                                title = Path.GetFileNameWithoutExtension(fi.Name);
                            }

                            var date = lines.Where(l => l.StartsWith("date:")).FirstOrDefault();

                            if (!string.IsNullOrEmpty(date))
                            {
                                date = date.Replace("date:", "").Trim();
                            }
                            else
                            {
                                date = fi.LastWriteTimeUtc.ToString("R");
                            }

                            rss.Append("\t\t<item>\n")
                            .Append($"\t\t\t<title>{title.Replace("&", "&amp;")}</title>\n")
                            .Append($"\t\t\t<link>{directory.TargetURL.Replace("&", "&amp;")}/{Path.GetFileNameWithoutExtension(fi.Name).Replace("&", "&amp;")}.html</link>\n")
                            .Append($"\t\t\t<guid isPermaLink='false'>{guid}</guid>\n")
                            .Append($"\t\t\t<pubDate>{date}</pubDate>\n")
                            .Append("\t\t</item>\n");
                        }
                    });
                });

                rss.Append("\t</channel>\n</rss>");
                File.WriteAllText(outputFileName, rss.ToString());
            }
        }