1 /*****************************************************************************
2 ** QNapi
3 ** Copyright (C) 2008-2017 Piotr Krzemiński <pio.krzeminski@gmail.com>
4 **
5 ** This program is free software; you can redistribute it and/or modify
6 ** it under the terms of the GNU General Public License as published by
7 ** the Free Software Foundation; either version 2 of the License, or
8 ** (at your option) any later version.
9 **
10 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
11 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
12 **
13 *****************************************************************************/
14 
15 #include "subtitlepostprocessor.h"
16 
17 #include <QFile>
18 #include <QFileInfo>
19 #include <QTextCodec>
20 #include <QTextStream>
21 
SubtitlePostProcessor(const PostProcessingConfig & ppConfig,const QSharedPointer<const SubtitleConverter> & subtitleConverter)22 SubtitlePostProcessor::SubtitlePostProcessor(
23     const PostProcessingConfig& ppConfig,
24     const QSharedPointer<const SubtitleConverter>& subtitleConverter)
25     : ppConfig(ppConfig), subtitleConverter(subtitleConverter) {}
26 
perform(const QString & movieFilePath,const QString & subtitleFilePath) const27 void SubtitlePostProcessor::perform(const QString& movieFilePath,
28                                     const QString& subtitleFilePath) const {
29   if (ppConfig.removeWordsEnabled()) {
30     ppRemoveLinesContainingWords(subtitleFilePath, ppConfig.removeWords());
31   }
32 
33   switch (ppConfig.encodingChangeMethod()) {
34     case ECM_REPLACE_DIACRITICS:
35       ppReplaceDiacriticsWithASCII(subtitleFilePath);
36       break;
37     case ECM_CHANGE:
38       if (!ppConfig.encodingAutoDetectFrom() ||
39           !ppChangeSubtitlesEncoding(subtitleFilePath, ppConfig.encodingTo())) {
40         ppChangeSubtitlesEncoding(subtitleFilePath, ppConfig.encodingFrom(),
41                                   ppConfig.encodingTo());
42       }
43       break;
44     case ECM_ORIGINAL:
45       // Nie ruszaj pobranych napisów!
46       break;
47   }
48 
49   if (!ppConfig.subFormat().isEmpty()) {
50     QString targetFormat = ppConfig.subFormat();
51     subtitleConverter->convertSubtitles(subtitleFilePath, targetFormat,
52                                         subtitleFilePath, movieFilePath);
53   }
54 }
55 
ppReplaceDiacriticsWithASCII(const QString & subtitleFilePath) const56 bool SubtitlePostProcessor::ppReplaceDiacriticsWithASCII(
57     const QString& subtitleFilePath) const {
58   if (!QFileInfo(subtitleFilePath).exists()) return false;
59 
60   QString from = encodingUtils.detectFileEncoding(subtitleFilePath);
61 
62   if (from.isEmpty()) return false;
63 
64   QFile f(subtitleFilePath);
65   if (!f.open(QIODevice::ReadOnly)) return false;
66 
67   QByteArray fileContent = f.readAll();
68 
69   QString contentStr =
70       QTextCodec::codecForName(qPrintable(from))->toUnicode(fileContent);
71   f.close();
72 
73   fileContent.clear();
74   fileContent.append(encodingUtils.replaceDiacriticsWithASCII(contentStr));
75 
76   if (!f.open(QIODevice::WriteOnly)) return false;
77 
78   f.write(fileContent);
79   f.close();
80 
81   return true;
82 }
83 
ppChangeSubtitlesEncoding(const QString & subtitleFilePath,const QString & from,const QString & to) const84 bool SubtitlePostProcessor::ppChangeSubtitlesEncoding(
85     const QString& subtitleFilePath, const QString& from,
86     const QString& to) const {
87   QFile f(subtitleFilePath);
88   if (!f.open(QIODevice::ReadOnly)) return false;
89 
90   QByteArray fileContent = f.readAll();
91 
92   QString contentStr =
93       QTextCodec::codecForName(qPrintable(from))->toUnicode(fileContent);
94   f.close();
95 
96   if (to.compare("UTF-8", Qt::CaseInsensitive) != 0) {
97     fileContent = QTextCodec::codecForName(qPrintable(to))
98                       ->fromUnicode(contentStr.constData(), contentStr.size());
99   } else {
100     fileContent.clear();
101     fileContent.append(contentStr);
102   }
103 
104   if (!f.open(QIODevice::WriteOnly)) return false;
105 
106   f.write(fileContent);
107   f.close();
108 
109   return true;
110 }
111 
ppChangeSubtitlesEncoding(const QString & subtitleFilePath,const QString & to) const112 bool SubtitlePostProcessor::ppChangeSubtitlesEncoding(
113     const QString& subtitleFilePath, const QString& to) const {
114   if (!QFileInfo(subtitleFilePath).exists()) return false;
115 
116   QString from = encodingUtils.detectFileEncoding(subtitleFilePath);
117 
118   if (from.isEmpty()) return false;
119 
120   return ppChangeSubtitlesEncoding(from, to);
121 }
122 
ppRemoveLinesContainingWords(const QString & subtitleFilePath,QStringList wordList) const123 bool SubtitlePostProcessor::ppRemoveLinesContainingWords(
124     const QString& subtitleFilePath, QStringList wordList) const {
125   if (!QFileInfo(subtitleFilePath).exists()) return false;
126 
127   wordList = wordList.filter(QRegExp("^(.+)$"));
128 
129   QString fromCodec = encodingUtils.detectFileEncoding(subtitleFilePath);
130 
131   if (fromCodec.isEmpty()) fromCodec = ppConfig.encodingFrom();
132 
133   QFile f(subtitleFilePath);
134   if (!f.open(QIODevice::ReadOnly | QIODevice::Text)) return false;
135 
136   QList<QByteArray> lines = f.readAll().split('\n');
137   QList<QByteArray> out;
138 
139   foreach (QByteArray line, lines) {
140     int i;
141     while ((i = line.indexOf('\r')) >= 0) line.remove(i, 1);
142 
143     QTextStream ts(line);
144     ts.setCodec(qPrintable(fromCodec));
145     QString encLine = ts.readAll();
146 
147     if (encLine.isEmpty()) {
148       out << line;
149       continue;
150     }
151 
152     bool found = false;
153     foreach (QString word, wordList) {
154       if (encLine.contains(word, Qt::CaseInsensitive)) {
155         found = true;
156         break;
157       }
158     }
159     if (found) continue;
160 
161     out << line;
162   }
163 
164   f.close();
165 
166   if (!f.open(QIODevice::WriteOnly | QIODevice::Text | QIODevice::Truncate))
167     return false;
168 
169   foreach (QByteArray line, out) {
170     f.write(line);
171     f.write("\n", 1);
172   }
173   f.close();
174 
175   return true;
176 }
177