📄 searching.cpp
字号:
/*
This file is part of KCeasy (http://www.kceasy.com)
Copyright (C) 2002-2004 Markus Kern <mkern@kceasy.com>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
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.
*/
//---------------------------------------------------------------------------
#include <winsock.h> // inet_addr
#pragma hdrstop
#include <stdio.h> // sscanf
#include "Searching.h"
#include "Engine.h"
//---------------------------------------------------------------------------
#pragma package(smart_init)
namespace KCeasyEngine {
// number of results immedetialy send to the UI before caching is turned on
static const int RESULTS_SENT_UNCACHED = 20;
// maximum cached results before they are written to the UI
static const int RESULTS_CACHE_SIZE = 40;
// class TSearchResult
// private
TSearchResult::TSearchResult()
: FileSize(0),
FileType(FTUnknown),
Availability(0),
Hashes(NULL)
{
Hashes = new THashSet();
User = new TUser();
}
TSearchResult::~TSearchResult()
{
delete User;
delete Hashes;
}
string TSearchResult::GetMagnetStr() const
{
TMagnet* Magnet = new TMagnet();
// add urns
Magnet->SetExactTopicHashes(GetHashes());
// add display name
if(MetaData.Exists("artist") && MetaData.Exists("title"))
Magnet->SetDisplayName(MetaData.Get("artist") + " - " + MetaData.Get("title"));
else
Magnet->SetDisplayName(FileName);
string MagnetStr = Magnet->Assemble();
delete Magnet;
return MagnetStr;
}
bool TSearchResult::IsBannedByWord(const list<string>& Words)
{
list<string>::const_iterator itr = Words.begin();
for(;itr != Words.end(); ++itr) {
if(string_tolower(FileName).find(*itr) != -1)
return true;
for(TMetaData::Iterator mitr = MetaData.Begin(); mitr != MetaData.End(); ++mitr) {
if(string_tolower((*mitr).Value).find(*itr) != -1)
return true;
}
}
return false;
}
bool TSearchResult::IsBannedByExtension(const list<string>& Extensions)
{
list<string>::const_iterator itr = Extensions.begin();
int pos;
if((pos = FileName.rfind('.')) == -1)
return false;
string Ext = string_tolower(FileName.substr(pos+1));
for(;itr != Extensions.end(); ++itr)
if(Ext == *itr)
return true;
return false;
}
// class TSearch
// private
TSearch::TSearch(TEngine* ParentEngine, const string& NQuery,
TSearchRealm NRealm, const string& NNetwork,
const TMetaData& NMetaData,
bool NBanWords, bool NBanExtensions, bool Intern)
: Engine(ParentEngine),
Internal(Intern),
UData(NULL),
State(New),
GiftId(0),
Realm(NRealm),
Network(NNetwork),
Query(NQuery),
MetaData(NMetaData),
ReceivedResults(0),
BannedResults(0),
BanWords(NBanWords),
BanExtensions(NBanExtensions)
{
}
TSearch::~TSearch()
{
LockResults();
while(!Results.empty()) {
delete Results.front();
Results.pop_front();
}
ReleaseResults();
}
bool TSearch::ProcessItem(TGiftCommand* Cmd)
{
if(Cmd->Name != "ITEM" || string_to_int(Cmd->Value) != GiftId || State != TSearch::Searching)
return false;
Engine->LockSearches();
if(Cmd->Nodes.empty()) {
// end of search
State = Finished;
GiftId = 0; // giFT reuses old IDs
// notify UI
if(IsInternal()) {
if(GetUData())
((TDownload*)GetUData())->SourcesSearch = NULL;
Engine->ReleaseSearches();
Engine->RemoveSearch(this); // this deletes us!
} else {
Engine->ReleaseSearches();
// send remaining results to ui
CommitCachedResults();
Engine->SendCallback(CbcSearchEnd,(void*)this,NULL);
}
return true;
}
TSearchResult* Result = new TSearchResult();
Result->SourceId = Cmd->GetNode("url").Value;
Result->Hashes->AddGiftHash(Cmd->GetNode("hash").Value);
Result->User->SetId(Cmd->GetNode("user").Value);
Result->FileName = FileFromPath(Cmd->GetNode("file").Value);
Result->FileDir = DirFromPath(Cmd->GetNode("file").Value);
Result->FileSize = string_to_int(Cmd->GetNode("size").Value);
Result->MimeType = Cmd->GetNode("mime").Value;
Result->FileType = FileTypeFromMime(Result->MimeType);
Result->Availability = string_to_int(Cmd->GetNode("availability").Value);
Result->Network = ProtocolFromUrl(Result->SourceId);
// get meta data
if(Cmd->NodeExists("META")) {
TGiftCommand& MetaCmd = Cmd->GetNode("META");
for(TGiftCommand::Iterator itr=MetaCmd.Nodes.begin();itr!=MetaCmd.Nodes.end();++itr)
Result->MetaData.Set((*itr).Name,(*itr).Value);
}
// convert legacy resolution tag to width/height
if(!Result->MetaData.Exists("width") && !Result->MetaData.Exists("height")) {
int width = 0, height = 0;
sscanf(Result->MetaData.Get("resolution").c_str(), "%dx%d",&width,&height);
if(width > 0 && height > 0) {
Result->MetaData.SetInt("width",width);
Result->MetaData.SetInt("height",height);
// remove resolution tag
Result->MetaData.Remove("resolution");
}
}
// HACK: giFT doesn't support downloading files > 2 GB, filter them here.
if(Result->FileSize >= 2*1024*1024*1024) {
delete Result;
Engine->ReleaseSearches();
return true;
}
// drop banned results if necessary
if((BanWords && Result->IsBannedByWord(Engine->BannedWordsList)) ||
(BanExtensions && Result->IsBannedByExtension(Engine->BannedExtensionsList)))
{
delete Result;
BannedResults++;
Engine->ReleaseSearches();
return true;
}
LockResults();
#if 1
// is this a redundant result from another search node?
TSearch::TResultIterator ritr = Results.begin();
for(;ritr!=Results.end();++ritr) {
if((*ritr)->FileName == Result->FileName &&
(*ritr)->GetUser()->GetId() == Result->GetUser()->GetId() &&
(*ritr)->GetHashes()->Equals(Result->GetHashes()))
{
break;
}
}
if(ritr != Results.end()) {
delete Result;
ReleaseResults();
Engine->ReleaseSearches();
return true;
}
#endif
if(IsInternal()) {
// add source to download
((TDownload*)GetUData())->AddSource(Result);
delete Result;
} else {
Results.push_back(Result);
// add source to download if we have one with matching hash
if(Result->GetHashes()->Size() != 0) {
Engine->LockDownloads();
TEngine::TDownloadIterator itr = Engine->GetDownloadsBegin();
while(itr != Engine->GetDownloadsEnd() && *(*itr)->GetHashes() != *Result->GetHashes())
++itr;
if(itr != Engine->GetDownloadsEnd() &&
((*itr)->GetState() != TDownload::Completed &&
(*itr)->GetState() != TDownload::Cancelled))
{
(*itr)->AddSource(Result);
}
Engine->ReleaseDownloads();
}
}
ReleaseResults();
Engine->ReleaseSearches();
// notify UI
if(!IsInternal()) {
ReceivedResults++;
ResultCache.push_back(Result);
// send first results immediately, after that cache
if(ReceivedResults <= RESULTS_SENT_UNCACHED ||
ResultCache.size() >= RESULTS_CACHE_SIZE)
{
CommitCachedResults();
}
} else {
/*
if(Results.size() >= GetMaxResults() && GetMaxResults() != 0)
Stop();
*/
}
return true;
}
void TSearch::CommitCachedResults()
{
if(ResultCache.empty())
return;
Engine->SendCallback(CbcSearchResult,(void*)this,&ResultCache);
ResultCache.clear();
}
// public
bool TSearch::Start()
{
TGiftCommand* Cmd;
if(State == Searching)
return false;
GiftId = Engine->GetNextGiftId();
State = Searching;
ReceivedResults = 0;
if(Realm == SRSource) {
Cmd = new TGiftCommand("LOCATE",int_to_string(GiftId));
Cmd->AddNode(TGiftCommand("query",Query.c_str()));
} else if(Realm == SRUser) {
Cmd = new TGiftCommand("BROWSE",int_to_string(GiftId));
Cmd->AddNode(TGiftCommand("query",Query.c_str()));
} else {
Cmd = new TGiftCommand("SEARCH",int_to_string(GiftId));
Cmd->AddNode(TGiftCommand("query",Query.c_str()));
switch(Realm) {
case SRAudio: Cmd->AddNode(TGiftCommand("realm","audio")); break;
case SRVideo: Cmd->AddNode(TGiftCommand("realm","video")); break;
case SRImage: Cmd->AddNode(TGiftCommand("realm","image")); break;
case SRSoftware: Cmd->AddNode(TGiftCommand("realm","application")); break;
case SRDocument: Cmd->AddNode(TGiftCommand("realm","text")); break;
case SRTorrent: Cmd->AddNode(TGiftCommand("realm","application/x-bittorrent")); break;
};
if(Network != "")
Cmd->AddNode(TGiftCommand("protocol",Network.c_str()));
}
Engine->QueueCommand(Cmd);
if(!IsInternal())
Engine->SendCallback(CbcSearchBegin,(void*)this,NULL);
return true;
}
bool TSearch::Cancel()
{
if(State == New)
return false;
if(State == Searching) {
TGiftCommand* Cmd = new TGiftCommand("SEARCH",int_to_string(GiftId));
Cmd->AddNode(TGiftCommand("action","cancel"));
Engine->QueueCommand(Cmd);
State = Finished;
GiftId = 0; // we don't care for further messages from this point on
if(!IsInternal()) {
CommitCachedResults();
Engine->SendCallback(CbcSearchEnd,(void*)this,NULL);
}
}
return true;
}
// Non-localized strings of realms (for config, etc)
static const struct
{
TSearchRealm Realm;
const char* String;
} Realms[] =
{
{ SRAny, "Everything" },
{ SRAudio, "Audio" },
{ SRVideo, "Video" },
{ SRImage, "Images" },
{ SRDocument, "Documents" },
{ SRSoftware, "Software" },
{ SRSource, "Sources" },
{ SRUser, "Users" },
{ SRTorrent, "Torrents" },
{ SRAny, NULL }
};
const char* TSearch::RealmToString(TSearchRealm Realm)
{
for(int i = 0; Realms[i].String; i++)
if(Realms[i].Realm == Realm)
return Realms[i].String;
return "";
}
const TSearchRealm TSearch::StringToRealm(const char* Str)
{
for(int i = 0; Realms[i].String; i++)
if(strcmpi(Realms[i].String, Str) == 0)
return Realms[i].Realm;
return SRAny;
}
} // namespace KCeasyEngine
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -