Logo Search packages:      
Sourcecode: fatrat-opensubtitles version File versions  Download package

SubtitlesDlg.cpp

/*
FatRat download manager
http://fatrat.dolezel.info

Copyright (C) 2006-2008 Lubos Dolezel <lubos a dolezel.info>

This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
version 2 as published by the Free Software Foundation.

This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
GNU General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
*/

#include "SubtitlesDlg.h"
#include <fatrat/XmlRpc.h>
#include <zlib.h>

#include <fatrat/fatrat.h>
#include <fatrat/RuntimeException.h>
#include <fatrat/Settings.h>

#include <unistd.h>

#include <QMessageBox>
#include <QFileDialog>
#include <QHeaderView>
#include <QtEndian>
#include <QBuffer>
#include <QHttp>
#include <QFile>
#include <QtDebug>

static const char* USER_AGENT = "FatRat " VERSION;
static const char* SERVER_NAME = "www.opensubtitles.org";
static const char* RPC_PATH = "/xml-rpc";

extern const char* g_movieSuffixes[];

SubtitlesDlg::SubtitlesDlg(QWidget* parent)
      : QDialog(parent), m_http(0), m_buffer(0)
{
      setupUi(this);
      
      QStringList hdr = QStringList() << tr("Name") << tr("Language") << tr("Release name") << tr("Part") << tr("Downloads") << tr("Rating");
      treeResults->setHeaderLabels(hdr);
      
      QHeaderView* phdr = treeResults->header();
      phdr->resizeSection(0, 200);
      phdr->resizeSection(1, 80);
      phdr->resizeSection(3, 60);
      phdr->resizeSection(4, 80);
      phdr->resizeSection(5, 80);
      
      connect(toolBrowse, SIGNAL(clicked()), this, SLOT(chooseFile()));
      connect(treeResults, SIGNAL(itemDoubleClicked(QTreeWidgetItem*,int)), this, SLOT(itemDoubleClicked(QTreeWidgetItem*)));
      connect(this, SIGNAL(finished(int)), this, SLOT(deleteLater()));
      
      m_http = new QHttp(SERVER_NAME, 80, this);
      connect(m_http, SIGNAL(done(bool)), this, SLOT(requestDone(bool)));
}

QWidget* SubtitlesDlg::create()
{
      return new SubtitlesDlg(getMainWindow());
}

void SubtitlesDlg::chooseFile()
{
      QString filter = "(";
      QString chosenFile;
      
      for(size_t i=0;g_movieSuffixes[i];i++)
      {
            filter += '*';
            filter += g_movieSuffixes[i];
            filter += ' ';
      }
      filter += ')';
      
      chosenFile = QFileDialog::getOpenFileName(this, "FatRat", lineFile->text(), filter);
      
      if(chosenFile.isEmpty())
            return;
      
      search(chosenFile);
}

void SubtitlesDlg::search(QString file)
{
      qint64 filesize;
      QString checksum;
      
      lineFile->setText(file);
      
      checksum = computeMovieHash(file, filesize);
      if(!checksum.isEmpty())
            search(checksum, filesize);
}

void SubtitlesDlg::search(QString checksum, qint64 fileSize)
{
      m_strChecksum = checksum;
      m_fileSize = fileSize;
      
      treeResults->clear();
      treeResults->setEnabled(false);
      toolBrowse->setEnabled(false);
      
      if(m_strSession.isEmpty())
            createSession();
      else
            performSearch();
}

void SubtitlesDlg::createSession()
{
      QByteArray postData;
      m_buffer = new QBuffer(m_http);
      
      postData = XmlRpc::createCall(m_strLastFunction = "LogIn", QVariantList() << "" << "" << "eng" << USER_AGENT);
      
      m_http->post(RPC_PATH, postData, m_buffer);
}

void SubtitlesDlg::performSearch()
{
      QVariantList movies;
      QByteArray postData;
      
      QStringList langs = getSettingsValue("subtitle_search/languages").toString().split(';', QString::SkipEmptyParts);
      
      foreach(QString lang, langs)
      {
            QVariantMap movie;
            movie["sublanguageid"] = lang;
            movie["moviehash"] = m_strChecksum;
            movie["moviebytesize"] = double(m_fileSize);
            movies << movie;
      }
      
      postData = XmlRpc::createCall(m_strLastFunction = "SearchSubtitles", QVariantList() << m_strSession << QVariant(movies));
      m_buffer = new QBuffer(m_http);
      m_http->post(RPC_PATH, postData, m_buffer);
}

void SubtitlesDlg::noOperation()
{
      QByteArray postData;
      m_buffer = new QBuffer(m_http);
      
      postData = XmlRpc::createCall(m_strLastFunction = "NoOperation", QVariantList() << m_strSession);
      
      m_http->post(RPC_PATH, postData, m_buffer);
}

void SubtitlesDlg::requestDone(bool error)
{
      m_buffer->deleteLater();
      
      try
      {
            if(error)
                  throw RuntimeException(tr("The server failed to process our request."));
            
            QVariant result = XmlRpc::parseResponse(m_buffer->data());
            if(result.isNull())
                  throw RuntimeException(tr("The server has returned an empty result"));
            
            if(m_strLastFunction == "LogIn")
            {
                  QByteArray postData;
                  
                  m_strSession = result.toMap()["token"].toString();
                  
                  m_timer.start(10*60*1000);
                  connect(&m_timer, SIGNAL(timeout()), this, SLOT(noOperation()));

                  if(!m_strChecksum.isEmpty())
                        performSearch();
                  else
                  {
                        treeResults->setEnabled(true);
                        toolBrowse->setEnabled(true);
                  }
            }
            else if(m_strLastFunction == "SearchSubtitles")
            {
                  QVariantList list = result.toMap()["data"].toList();
                  
                  foreach(QVariant sub, list)
                  {
                        QVariantMap map = sub.toMap();
                        
                        SubtitleTreeWidgetItem* item = new SubtitleTreeWidgetItem(treeResults);
                        
                        item->setText(0, QString("%1 (%2)").arg(map["MovieName"].toString()).arg(map["MovieYear"].toString()));
                        item->setText(1, map["LanguageName"].toString());
                        item->setText(2, map["MovieReleaseName"].toString());
                        
                        int cds = map["SubSumCD"].toInt();
                        if(cds)
                              item->setText(3, QString("%1/%2").arg(map["SubActualCD"].toInt()).arg(cds));
                        
                        item->setText(4, map["SubDownloadsCnt"].toString());
                        item->setText(5, map["SubRating"].toString());
                        
                        item->m_id = map["IDSubtitleFile"].toInt();
                        item->m_lang = map["SubLanguageID"].toString();
                        item->m_format = map["SubFormat"].toString().toLower();
                        
                        treeResults->addTopLevelItem(item);
                  }
                  
                  if(list.isEmpty())
                        QMessageBox::information(this, "FatRat", tr("No subtitles found!"));
                  
                  treeResults->setEnabled(true);
                  toolBrowse->setEnabled(true);
                  m_strChecksum.clear();
            }
            else if(m_strLastFunction == "DownloadSubtitles")
            {
                  QString proposedName, originalName, filter;
                  originalName = lineFile->text();
                  
                  SubtitleTreeWidgetItem* item = static_cast<SubtitleTreeWidgetItem*>(treeResults->topLevelItem(m_sel));
                  
                  int pos = originalName.lastIndexOf('.');
                  if(pos < 0)
                        pos = originalName.size() - 4;
                  
                  proposedName = originalName.left(pos+1);
                  proposedName += QString("%1.%2").arg(item->m_lang).arg(item->m_format);
                  
                  filter = QString("(*.%1)").arg(item->m_format);
                  
                  proposedName = QFileDialog::getSaveFileName(this, "FatRat", proposedName, filter);
                  if(!proposedName.isEmpty())
                  {
                        int fds[2];
                        
                        if(!::pipe(fds))
                        {
                              // The server sends it as a string, despite the fact that it's a base64
                              // encoded data, for which the XML-RPC specifies a distinct type
                              // Therefore we have to do the base64 decoding now
                              
                              QVariantList list = result.toMap()["data"].toList();
                              QByteArray data;
                              
                              if(list.isEmpty())
                                    throw RuntimeException(tr("The server didn't return the requested data"));
                              
                              data = QByteArray::fromBase64(list[0].toMap()["data"].toByteArray());
                              
                              ::write(fds[1], data.constData(), data.size());
                              ::close(fds[1]);
                              
                              data.clear();
                              
                              decompressFile(fds[0], proposedName);
                        }
                        else
                              throw RuntimeException(tr("Failed to allocate a pipe"));
                  }
                  
                  treeResults->setEnabled(true);
                  toolBrowse->setEnabled(true);
            }
      }
      catch(const RuntimeException& e)
      {
            QMessageBox::critical(this, "FatRat", e.what());
            toolBrowse->setEnabled(true);
      }
}

QString SubtitlesDlg::computeMovieHash(QString filename, qint64& fsize)
{
      const int CHUNK = 65536;
      qint64* buffer;
      QFile file(filename);
      int read;
      quint64 checksum;
      
      if(!file.open(QIODevice::ReadOnly))
      {
            qDebug() << "Failed to open" << filename;
            return QString();
      }
      
      buffer = new qint64[CHUNK/8];
      checksum = fsize = file.size();
      
      read = file.read((char*) buffer, CHUNK);
      for(int i=0;i<read/8;i++)
            checksum += qFromLittleEndian(buffer[i]);
      
      file.seek(qMax<qint64>(0, fsize-CHUNK));
      
      read = file.read((char*) buffer, CHUNK);
      for(int i=0;i<read/8;i++)
            checksum += qFromLittleEndian(buffer[i]);
      
      delete [] buffer;
      
      if(fsize < 65536)
      {
            QMessageBox::warning(getMainWindow(), "FatRat",
                  QObject::tr("You have selected a file smaller than 64 kilobytes. "
                        "The OpenSubtitles.org checksumming algorithm is not designed to deal with such files. "
                        "Therefore the generated checksum is very likely unusable."));
      }
      
      return QString::number(checksum, 16);
}

void SubtitlesDlg::itemDoubleClicked(QTreeWidgetItem* i)
{
      QByteArray postData;
      QVariantList subs;
      SubtitleTreeWidgetItem* item = static_cast<SubtitleTreeWidgetItem*>(i);
      
      m_buffer = new QBuffer(m_http);
      subs << item->m_id;
      postData = XmlRpc::createCall(m_strLastFunction = "DownloadSubtitles", QVariantList() << m_strSession << QVariant(subs));
      
      treeResults->setEnabled(false);
      toolBrowse->setEnabled(false);
      
      m_sel = treeResults->indexOfTopLevelItem(i);
      m_http->post(RPC_PATH, postData, m_buffer);
}

void SubtitlesDlg::decompressFile(int infd, QString unzipped)
{
      char buffer[4096];
      QFile fout(unzipped);
      gzFile file;
      
      if(!fout.open(QIODevice::WriteOnly))
            return;
      
      file = gzdopen(infd, "rb");
      if(!file)
            return;
      
      while(true)
      {
            int bytes = gzread(file, buffer, sizeof buffer);
            if(bytes <= 0)
                  break;
            fout.write(buffer, bytes);
      }
      
      gzclose(file);
}


Generated by  Doxygen 1.6.0   Back to index