Hacktoberfest 2026:メンテナが10月に向けて印を付けた、オープンで初心者向けの issue。 Hacktoberfest の issue を見る

New Plugin c/o ChatGPT

オープン
#262 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

メンテナーはふだん 1 日以内に返信

まだ誰も着手していません。

評価

難易度
5/5
見積もり時間
1週間以上
初心者へのやさしさ
25/100
issue の種類
機能追加
明瞭さ
説明が足りない
活発さ
停滞
技術スタック
csharp
領域
desktop, tooling

調査の方向性

まず、提供された ProblemLinePlugin コードと Nikse.SubtitleEdit.PluginInterface.dll への参照を確認し、次に、その IPlugin 実装を Subtitle Edit の Plugins セクションの基盤となっているプラグイン検出動作と比較します。プラグインを表示するために必要なことと、提案されている強調表示された行の修正ワークフローがサポートされているかどうかを判断します。完了とは、プラグインが Subtitle Edit で読み込まれ、対象の問題行を指定できることです。

索引モデルが issue の本文から書いたものです。

説明

I thought I'd see if I could get ChatGPT to help write a plugin for Subtitle Edit that would allow me to select all highlighted problem lines for targeted correction (a feature I've wanted incorporating into SE for a long time). This would allow, for example, the auto-balancing of only lines deemed to be of excessive length (rather than every single line, as is currently the case).

Unfortunately, I kept hitting a snag seemingly relating to Nikse.SubtitleEdit.PluginInterface.dll (in that the code below would build successfully in Visual Studio but not appear in the SE Plugins section) and ChatGPT could offer no useful advice about how to resolve this issue.

Could anyone who knows anything about Subtitle Edit plugins please take a look at the code below and let me know if it was even anywhere close to working? Possibly this whole endeavour was a waste of time, given that I know nothing about C#, but it seemed worth a try!

using Nikse.SubtitleEdit.PluginInterface;
using System;
using System.Collections.Generic;
using System.Windows.Forms;
using System.Text;
using System.Text.RegularExpressions;

namespace SubtitleEdit.ProblemLineHighlighter
{
    public class ProblemLinePlugin : IPlugin
    {
        private Subtitle _subtitle;
        private SubRip _subRipFormat;

        public string Name => "Problem Line Highlighter";
        public string Text => "Highlight problem lines in subtitles";
        public string Version => "1.0";

        public void Initialize()
        {
            _subRipFormat = new SubRip();
            _subtitle = new Subtitle(_subRipFormat);
        }

        public bool DoesShowDialog()
        {
            return true;
        }

        public void ShowDialog()
        {
            MessageBox.Show("Problem Line Highlighter Plugin loaded!");
            // Here you can later add UI or further plugin logic
        }

        // You can expose your subtitle loading logic here, example:
        public void LoadSubtitleFromLines(string[] lines)
        {
            _subtitle.Paragraphs.Clear();
            var listLines = new List<string>(lines);
            _subRipFormat.LoadSubtitle(_subtitle, listLines, "Example.srt");
            _subtitle.Renumber(1);
        }

        public string GetSubtitleText()
        {
            return _subtitle.ToText();
        }
    }

    // Paste your Subtitle, SubRip, Paragraph, TimeCode classes here (internal or public as needed)
    // For brevity, you can keep them as internal classes below the plugin class,
    // or move to separate files as your project grows.

    internal class Paragraph
    {
        public int Number { get; set; }
        public TimeCode StartTime { get; set; }
        public TimeCode EndTime { get; set; }
        public string Text { get; set; }

        public Paragraph()
        {
            Text = string.Empty;
            StartTime = new TimeCode();
            EndTime = new TimeCode();
        }
    }

    internal class TimeCode
    {
        public int Hours { get; set; }
        public int Minutes { get; set; }
        public int Seconds { get; set; }
        public int Milliseconds { get; set; }

        public TimeCode() { }

        public TimeCode(int hours, int minutes, int seconds, int milliseconds)
        {
            Hours = hours;
            Minutes = minutes;
            Seconds = seconds;
            Milliseconds = milliseconds;
        }

        public override string ToString()
        {
            return $"{Hours:D2}:{Minutes:D2}:{Seconds:D2},{Milliseconds:D3}";
        }

        public void AddTime(TimeSpan timeSpan)
        {
            var totalMs = ToTimeSpan().TotalMilliseconds + timeSpan.TotalMilliseconds;
            if (totalMs < 0) totalMs = 0;

            var ts = TimeSpan.FromMilliseconds(totalMs);
            Hours = ts.Hours;
            Minutes = ts.Minutes;
            Seconds = ts.Seconds;
            Milliseconds = ts.Milliseconds;
        }

        public TimeSpan ToTimeSpan()
        {
            return new TimeSpan(0, Hours, Minutes, Seconds, Milliseconds);
        }
    }

    internal class Subtitle
    {
        private List<Paragraph> _paragraphs;
        private readonly SubRip _format;

        public string Header { get; set; }
        public string Footer { get; set; }
        public string FileName { get; set; }
        public bool IsHearingImpaired { get; private set; }
        public List<Paragraph> Paragraphs => _paragraphs;

        public Subtitle(SubRip subrip)
        {
            _paragraphs = new List<Paragraph>();
            FileName = "Untitled";
            _format = subrip;
        }

        public Paragraph GetParagraphOrDefault(int index)
        {
            if (_paragraphs == null || _paragraphs.Count <= index || index < 0)
                return null;
            return _paragraphs[index];
        }

        public string ToText()
        {
            return _format.ToText(this, System.IO.Path.GetFileNameWithoutExtension(FileName));
        }

        public void AddTimeToAllParagraphs(TimeSpan time)
        {
            foreach (var paragraph in Paragraphs)
            {
                paragraph.StartTime.AddTime(time);
                paragraph.EndTime.AddTime(time);
            }
        }

        public void Renumber(int startNumber)
        {
            int num = ((startNumber >= 0) ? startNumber : 0);
            foreach (var paragraph in _paragraphs)
            {
                paragraph.Number = num;
                num++;
            }
        }
    }

    internal class SubRip
    {
        private enum ExpectingLine
        {
            Number,
            TimeCodes,
            Text
        }

        private static Regex RegexTimeCodes = new Regex(@"^\d{1,2}:\d{2}:\d{2}[,.:]\d{1,3}\s*-->\s*\d{1,2}:\d{2}:\d{2}[,.:]\d{1,3}$", RegexOptions.Compiled);

        private ExpectingLine _expecting;
        private Paragraph _paragraph;
        private int _errorCount;

        public string Extension => ".srt";
        public string Name => "SubRip";
        public int ErrorCount => _errorCount;

        public void LoadSubtitle(Subtitle subtitle, List<string> lines, string fileName)
        {
            bool flag = false;
            _paragraph = new Paragraph();
            _expecting = ExpectingLine.Number;
            _errorCount = 0;
            subtitle.Paragraphs.Clear();

            for (int i = 0; i < lines.Count; i++)
            {
                string text = lines[i].TrimEnd();
                text = text.Trim('\u007f');
                string next = (i + 1 < lines.Count) ? lines[i + 1] : string.Empty;

                if (_expecting == ExpectingLine.Text && i + 1 < lines.Count && _paragraph != null && !string.IsNullOrEmpty(_paragraph.Text) && int.TryParse(text, out _) && RegexTimeCodes.IsMatch(lines[i + 1]))
                {
                    _errorCount++;
                    ReadLine(subtitle, string.Empty, string.Empty);
                }
                if (_expecting == ExpectingLine.Number && RegexTimeCodes.IsMatch(text))
                {
                    _errorCount++;
                    _expecting = ExpectingLine.TimeCodes;
                    flag = true;
                }
                ReadLine(subtitle, text, next);
            }

            if (_paragraph.Text.Trim().Length > 0)
                subtitle.Paragraphs.Add(_paragraph);

            foreach (var paragraph in subtitle.Paragraphs)
                paragraph.Text = paragraph.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);

            if (_errorCount < 100 && flag)
                subtitle.Renumber(1);
        }

        public string ToText(Subtitle subtitle, string title)
        {
            var sb = new StringBuilder();
            foreach (var paragraph in subtitle.Paragraphs)
            {
                var text = paragraph.Text.Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
                sb.Append($"{paragraph.Number}\r\n{paragraph.StartTime} --> {paragraph.EndTime}\r\n{text}\r\n\r\n");
            }
            return sb.ToString().Trim();
        }

        private void ReadLine(Subtitle subtitle, string line, string next)
        {
            switch (_expecting)
            {
                case ExpectingLine.Number:
                    if (int.TryParse(line, out int number))
                    {
                        _paragraph.Number = number;
                        _expecting = ExpectingLine.TimeCodes;
                    }
                    else if (line.Trim().Length > 0)
                    {
                        _errorCount++;
                    }
                    break;
                case ExpectingLine.TimeCodes:
                    if (TryReadTimeCodesLine(line, _paragraph))
                    {
                        _paragraph.Text = string.Empty;
                        _expecting = ExpectingLine.Text;
                    }
                    else if (line.Trim().Length > 0)
                    {
                        _errorCount++;
                        _expecting = ExpectingLine.Number;
                    }
                    break;
                case ExpectingLine.Text:
                    if (!string.IsNullOrWhiteSpace(line))
                    {
                        if (_paragraph.Text.Length > 0)
                            _paragraph.Text += Environment.NewLine;

                        _paragraph.Text += line.TrimEnd().Replace(Environment.NewLine + Environment.NewLine, Environment.NewLine);
                    }
                    else if (!string.IsNullOrWhiteSpace(next) && (int.TryParse(next, out _) || RegexTimeCodes.IsMatch(next)))
                    {
                        subtitle.Paragraphs.Add(_paragraph);
                        _paragraph = new Paragraph();
                        _expecting = ExpectingLine.Number;
                    }
                    break;
            }
        }

        private bool TryReadTimeCodesLine(string line, Paragraph paragraph)
        {
            line = line.Replace('،', ',').Replace('\uf7c8', ',').Replace('¡', ',');
            line = line.Replace(" -> ", " --> ").Replace(" - > ", " --> ").Replace(" ->> ", " --> ");
            line = line.Replace(" -- > ", " --> ").Replace(" - -> ", " --> ").Replace(" -->> ", " --> ").Replace(" ---> ", " --> ");

            line = line.Trim();
            line = line.Replace('.', ':');
            line = Regex.Replace(line, @"\s+", " ");

            if (!RegexTimeCodes.IsMatch(line))
                return false;

            string[] parts = line.Split(new[] { ' ', '-', '>', ':', ',' }, StringSplitOptions.RemoveEmptyEntries);
            try
            {
                int h1 = int.Parse(parts[0]);
                int m1 = int.Parse(parts[1]);
                int s1 = int.Parse(parts[2]);
                int ms1 = int.Parse(parts[3]);

                int h2 = int.Parse(parts[4]);
                int m2 = int.Parse(parts[5]);
                int s2 = int.Parse(parts[6]);
                int ms2 = int.Parse(parts[7]);

                paragraph.StartTime = new TimeCode(h1, m1, s1, ms1);
                paragraph.EndTime = new TimeCode(h2, m2, s2, ms2);

                return true;
            }
            catch
            {
                return false;
            }
        }
    }
}

主要言語
C#
スター
138
フォーク
49
平均マージ
28分
マージ済み PR(30日)
24

環境構築

このプロジェクトには開発コンテナ、Dockerfile、コントリビューションガイドがありません。まず README を読み、一般的な手順ははじめてのコントリビューションガイドを参照してください。

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

SubtitleEdit/plugins のほかの issue

SubtitleEdit/plugins の issue をすべて見る

似ている issue

C# の issue をもっと見る

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。