{ *** Импорт данных по персонам с Kino-Teatr.ru ***
  Автор: irwin, videodb.info/forum_ru/
  Внимание! Серии добавляются как фильмы.
  Настройка скрипта: 
   - Установить флаг на белом фоне для "Фильмографии"
   - При совместном использовании со скриптом kinopoisk_person -
	 1) плюс флаг на белом фоне для kinopoisk_person
	 2) плюс установить флаг "Объединять фильмографию".
	 (Используйте пакетный режим KinoPoisk+Kino-Teatr или соблюдайте
	 последовательность kinopoisk_person, затем с kino_teatr_person)
}
const
 //Script modes
 smSearch = 0; smBio = 1; smPhoto  = 2; smFinished = 3;

 //Parse results
 prError = 0; prFinished = 1; prList = 2; prDownload  = 4;
 
 //Типы работы скрипта
 stMovies = 0;
 stPeople = 1; 
 stPoster = 2;

 //Script data
 SCRIPT_VERSION = '0.0.3.0';
 SCRIPT_NAME = 'Кино-Театр.ру Персоны';
 SCRIPT_DESC = '[RU] Импортировать информацию о персоне с Kino-Teatr.ru';
 SCRIPT_LANG = $19;	//Russian
 SCRIPT_TYPE = stPeople;	//люди
 BASE_URL = 'http://kino-teatr.ru';
 CODE_PAGE = 1251;	//Кодировка сайта. 0 для Autodetect, 65001 для utf-8

 // Гугл
 GResUnitMarker = 'class="r">';
 GMinDelay = 3; GMidDelay = 20; GMaxDelay = 60; 

//Global variables
var
 Mode : Byte;
// PhotoURL : String;
 GSearchStr : String;
 GLastQueryTime : Extended;
 ExtraLinks : array [smBio..smFinished] of String;

//Functions
function GetScriptName : String; begin Result := SCRIPT_NAME end;
function GetScriptDesc : String; begin Result := SCRIPT_DESC end;
function GetScriptLang : Cardinal; begin Result := SCRIPT_LANG end;
function GetScriptVersion : String; begin Result := SCRIPT_VERSION end;
function GetCodePage : Cardinal; begin Result := CODE_PAGE end;
function GetScriptType : Byte; begin Result := SCRIPT_TYPE end;
function GetBaseURL : AnsiString; begin Result := BASE_URL end;
function GetCurrentMode : Byte; begin Result := Mode end;

function GetDownloadURL : AnsiString;
 var
	curDT, days, secs : Extended; 
	delay: Integer;
	s: String;
 begin
  if Mode = smSearch then begin
	GSearchStr := 'http://www.google.com.ua/search?hl=ru&oe=ISO-8859-5&filter=0&q=site%3Akino-teatr.ru+inurl%3Aworks%2Fprint+intitle%3A%22%s%22';
	curDT := CurrentDateTime;
	GLastQueryTime := StrToFloat(FileToString ('kino-teatr_person.cfg')); // создается, если нет
	if GLastQueryTime=0 then GLastQueryTime:=curDT;
	days := curDT-GLastQueryTime;
	secs := days * 86400;
	if secs < GMaxDelay then begin
	 s := FloatToStr(secs);
	 s := Copy(s,Pos('.',s)+7,5);
	 while Length(s) < 5 do  s := s + '0';
	  if s < '20000' then delay:=GMinDelay else if s < '80000' then delay:=GMidDelay else delay:=GMaxDelay;
	  if delay > secs then Wait((delay-round(secs))*1000);
	end;
//LogMessage(IntToStr(round(secs))+' c без запросов; сгенерированная '+IntToStr(delay)+' с');
	StringToFile ('kino-teatr_person.cfg', FloatToStr (CurrentDateTime), false, false);
    Result := GSearchStr;
	exit end
  else
    Result := ExtraLinks[Mode]
 end;

function isDigit(const sym:String):Boolean;
 begin Result:=(sym<='9') and (sym>='0') end;

function GetNumL(const s:String; var mpos:Integer):String;
 var i:Integer; ret:String;
 begin
  for i:=Length(s) downto 1 do
	if isDigit(s[i]) then ret:=s[i]+ret else break;
  mpos:=mpos-Length(ret)-1;
  Result:=ret
 end;
 
function EpisodePostfix(const ep:String):String;
 var
  mpos, mlen, i: Integer; epnum, s: String;
  EpiMark: array[1..8] of string;
 begin
  EpiMark[1]:='СЕРИЯ';  EpiMark[2]:='ФИЛЬМ';  EpiMark[3]:='СЕРИИ';
  EpiMark[4]:='ЧАСТЬ';  EpiMark[5]:='НОВЕЛЛА';  EpiMark[6]:='ИСТОРИЯ';
  EpiMark[7]:='ДЕЛО';	EpiMark[8]:='ЭПИЗОД';
  ep := StringReplace( ep, '  ', ' ', true, false, false );
  ep := StringReplace( ep, '—', '-', true, false, false );
  ep := StringReplace( ep, '-серия', ' серия', false, true, false );
  ep := StringReplace( ep, 'фильм №', 'фильм ', false, true, false );
  ep := StringReplace( ep, 'серия №', 'серия ', false, true, false );
  ep := StringReplace( ep, 'пизод №', 'пизод ', false, true, false );
  ep := StringReplace( ep, '-я сери', ' сери', false, true, false );
  ep := StringReplace( ep, '-я и ', ' и ', true, true, false );
  ep := StringReplace( ep, '-ой фильм', ' фильм', false, true, false );
  ep := StringReplace( ep, '-ий фильм', ' фильм', false, true, false );
  ep := StringReplace( ep, '-ый фильм', ' фильм', false, true, false );
  ep := StringReplace( ep, '-й фильм', ' фильм', false, true, false );
  ep := StringReplace( ep, '-й и ', ' и ', false, true, false );
  ep := StringReplace( ep, '  ', ' ', true, true, false );
  if Pos('стория',ep)>0 then ep := StringReplace( ep, '-я', '', false, true, false );
  if (Pos('фильм ',ep)>0) and (Pos('-й',ep)>0) then Delete(ep,Pos('-й',ep),2);
  if PosFrom('Дело №',ep,1)>1 then ep := StringReplace( ep, 'Дело №', 'Дело ', false, true, false );
  if (Pos('Фильм ',ep)>0) and (Pos('Серия ',ep)>0) then begin
	ep := StringReplace( ep, 'Серия ', 'Серия '+Copy(ep,Pos('Фильм ',ep)+6,1), false, true, false );
	Delete(ep, Pos('Фильм ',ep),6) end;
  ep:=UpperCase(ep);
  for i:=1 to high(EpiMark) do begin
	mlen:=Length(EpiMark[i]);
	mpos := Pos(EpiMark[i], ep);
	if (mpos=1) and (EpiMark[i]='ДЕЛО') then mpos := PosFrom(EpiMark[i], ep, 4);
	if mpos>0 then begin
	  if (mpos+mlen < Length(ep)) and (Pos('(',ep)=0) then
		if ep[mpos+mlen]=' ' then begin
		  if isDigit(ep[mpos+mlen+1]) then epnum:=ep[mpos+mlen+1];
		  if mpos+mlen+1 < Length(ep) then
			if isDigit(ep[mpos+mlen+2]) then epnum:=epnum+ep[mpos+mlen+2] else continue else continue;
		  if mpos+mlen+2 < Length(ep) then
			if isDigit(ep[mpos+mlen+3]) then epnum:=epnum+ep[mpos+mlen+3] else continue else continue;
		end else continue
	  else begin // число до
		if (mpos>2) and (ep[mpos-1]=' ') then epnum:=GetNumL(Copy(ep,1,mpos-2),mpos) else continue;
		if mpos>2 then if ep[mpos-1]='-' then epnum:=GetNumL(Copy(ep,1,mpos-2),mpos)+'-'+epnum else 
		if mpos>4 then if (Pos(' и '+epnum,ep)>0) then epnum:=GetNumL(Copy(ep,1,mpos-4),mpos)+'-'+epnum else continue;
	  end;
	  if Length(epnum)>0 then break;
	end
  end;
  if Pos('ПЕРВЫЙ СЕЗОН',ep)>0 then s:=' 1' else
  if Pos('ВТОРОЙ СЕЗОН',ep)>0 then s:=' 2'else
  if Pos('ТРЕТИЙ СЕЗОН',ep)>0 then s:=' 3'else
  if Pos('ЧЕТВЕРТЫЙ СЕЗОН',ep)>0 then s:=' 4'else
  if Pos('ПЯТЫЙ СЕЗОН',ep)>0 then s:=' 5';
  if Length(epnum)>0 then Result:=s+'.e'+epnum else Result:=''
 end; 

function Format(Title:String): String; // без дефисов; к Кинопоиску
 var
	dPos :Integer; //s:String;
 begin
Title := StringReplace( Title, '&ndash', '-', true, false, false );
	dPos:=Pos(' - ',Title);
	if dPos>0 then if isDigit(Title[dPos+3]) then Delete(Title, LastPos('- ',Title),2);
	dPos:=Pos(' -', Title);
	if dPos>0 then if isDigit(Title[dPos+2]) then Delete(Title, LastPos(' -',Title)+1,1);
	dPos:=Pos('- ', Title);
	if dPos>0 then if isDigit(Title[dPos+2]) then Delete(Title, LastPos('- ',Title),1);
	dPos:=Pos('-', Title);
	if dPos>0 then if isDigit(Title[dPos+1]) then
		begin Delete(Title, dPos,1); Insert(' ',Title,dPos) end;
	Title:=StringReplace( Title, '-й сезон', ' сезон', false, true, false );
	dPos:=Pos(' сезон)',Title);
	if dPos>0 then begin
		Title:=StringReplace( Title, '(', Title[dPos-1], false, true, false );
		Delete(Title, dPos-1, 8);
	end;
	Result:=Title
 end;
 
function ParseRole(var row, sCat: String; curPos: Integer): String;
 var s: string;
 begin
  s := sCat;
  if PosFrom('::', row, curPos) >0 then
	begin
	  if Length(s)>0 then s := s + '; ';
	  s := s + TextBetween( row, '::&nbsp;</b>', '</span', false, curPos);
	  s := StringReplace( s, 'главная роль', 'ГР', false, true, true )
	  if PosFrom('"descr">', row, curPos) >0 then
		s := s + '; ' + TextBetween( row, '"descr">', '<', false, curPos)
	end
  else if PosFrom('"descr">', row, curPos) >0 then
	begin
	  if Length(s)>0 then s := s + '; ';
	  s := s + TextBetween( row, '"descr">', '<', false, curPos)
	end;
	if Length(s)>255 then delete(s,256,Length(s)-255);
  Result := s;
 end;

procedure ParseFilmography (FilmographyURL : AnsiString; var HTML : String);
var
  docPos, rowPos, curPos, Category : Integer;
  txt, row, sCat, Year, URL, Title, OrigTitle, Role, 
	SeriesTitle, OrigSeriesTitle, SeriesRole, SeriesURL, TMarkerB, TMarkerE : String;
begin
  docPos := Pos('"100%"><tr', HTML);
  if (docPos = 0) then begin
	  if Pos('temporarily unavailable', HTML)>0 then 
		ShowMessage('Страница недоступна, попробуйте позже','') else
		ShowMessage('Неверная страница либо изменился код на сайте','');
	  exit;
  end;
  if Pos('works/print/',FilmographyURL)>0 then AddFieldValueXML('url',FilmographyURL);
  while PosFrom( '<tr', HTML, docPos ) >0 do
  begin
	rowPos := PosFrom( '<tr>', HTML, docPos );
	row := Copy(HTML, rowPos, PosFrom( '<tr>', HTML, rowPos+1)-rowPos);
	if Length(row)=0 then row := Copy(HTML, rowPos, PosFrom( '</tr>', HTML, rowPos+1)-rowPos);
	if Length(row) > 0 then begin
{Год} Year := Copy(TextBetween(row, '"god">', '<', false, curPos), 1, 4);
	  if Length(Year) > 0 then
	    begin
		  OrigTitle := '';  Role := '';	  URL := '';
	  	  if PosFrom('href="', row, curPos) >0 then // есть ссылка
			begin
{URL}		  URL := BASE_URL + TextBetween( row, 'href="', '"', false, curPos);
			  TMarkerB := 'title="'; TMarkerE := '"';
			end
		  else 
//			begin TMarkerB := '"red">'; TMarkerE := '<'; end;
			begin TMarkerB := '"blue">'; TMarkerE := '<'; end;
		  if Length(SeriesTitle)=0 then begin
{Title-Ф}	Title := Format(TextBetween(row, TMarkerB, TMarkerE, false, curPos));
			if (PosFrom('<table', row, curPos) = 0) then begin // не строка, за которой будут серии
			  if (PosFrom('| ', row, curPos) > 0) then 
{OrigTtl-Ф} 	begin OrigTitle := TextBetween(row, TMarkerB, TMarkerE, false, curPos) 
				  if (PosFrom('|', row, curPos) > 0) then 
					OrigTitle := OrigTitle+' | '+TextBetween(row, TMarkerB, TMarkerE, false, curPos)
				  end end
			else begin
			  if PosFrom('|', row, curPos) > 0 then // ориг.название сериала, после постфикса
				OrigSeriesTitle := ' | '+TextBetween(row, TMarkerB, TMarkerE, false, curPos);
			  SeriesTitle := Title; // для последующих строк серий
			end;
{Роль-Ф}	Role := ParseRole(row, sCat, curPos);
			if PosFrom('<table', row, curPos) > 0 then 
			  begin SeriesRole := Role; SeriesURL:=URL end
			else
			  begin SeriesRole := ''; SeriesURL:='' end
		  end
		  else begin
{Title-С}	Title := SeriesTitle;
{Роль-С}	if PosFrom('::', row, curPos) >0 then Role := ParseRole(row, sCat, curPos) else
			  Role := SeriesRole;
{OrigTtl-С} OrigTitle := TextBetween(row, TMarkerB, TMarkerE, false, curPos);
			if PosFrom('| ', row, curPos) >0 then 
			  begin
				OrigTitle := OrigTitle + '. ' + TextBetween( row, TMarkerB, TMarkerE, false, curPos);
				if PosFrom('| ', row, curPos) >0 then 
				  begin
					OrigTitle:=StringReplace(OrigTitle, '. ', ' | ', false, true, true);
					OrigTitle := OrigTitle + '. ' + TextBetween( row, TMarkerB, TMarkerE, false, curPos);
				  end;
			  end;
			Title:=Title+EpisodePostfix(OrigTitle)+OrigSeriesTitle;
			if Length(URL)=0 then URL := SeriesURL
		  end;
		  if PosFrom('</table', row, curPos) >0 then // последняя серия
		    begin SeriesTitle := ''; OrigSeriesTitle := ''; SeriesRole := ''; SeriesURL:='' end;
		  if Pos('<table', row) = 0 then begin // заголовок сериала не пишем
{		  LogMessage('Title: '+Title+' ОрНзв: '+OrigTitle+' Роль: '+Role+' Год: '+Year+#13#10+ 
			' URL: '+URL+' Кат: '+IntToStr(Category));}
			AddPersonMovie(Title, Format(OrigTitle), Role, Year, URL, Category) //; end
		  end;
		end
	  else curPos := 0;
	  txt := TextBetween(row, 'ucase">', '</tr', false, curPos);
	  if Length(txt) >0 then begin
		case txt of
			'Режиссер': Category := 1;	'Сценарист': Category := 2;
			'Композитор': Category :=3; 'Продюсер': Category := 4;
	    end;
		if (Category=0) and (txt <> 'роли в кино') then
		  begin if txt='участие в фильмах' then sCat := 'участие' else sCat := txt end else sCat := ''
	  end;
	  curPos := 0;
	end;
	docPos := docPos + Length(row);
  end;
  ExtraLinks[smBio]:=StringReplace(FilmographyURL, 'works/print', 'bio', false, true, true);
end;

procedure ParseSearchResults( var HTML : String );
 var
  curPos : Integer;
  Title, URL, previewURL, s : String;
 begin
  curPos := Pos( GResUnitMarker, HTML );
  if curPos < 1 then begin
	ShowMessage('Не найден признак результата поиска', 'Ошибка'); 
    Mode := smFinished ; Exit
  end;
  repeat
    Begin
{URL} URL := TextBetween( HTML, 'href="', '"', true, curPos );
	  if Pos('/url?q=',URL)>0 then Delete(URL,Pos('/url?q=',URL),7);
	  if LastPos('print/',URL)+5<>Length(URL) then
		Delete(URL,LastPos('print/',URL)+6,Length(URL)-LastPos('print/',URL)+5);
		if (Pos('/teatr',URL)>0) and (Pos('/activist',URL)>0) then
		  URL:=StringReplace( URL, '/teatr', '/kino', false, true, true );
		if Pos( '/teatr', URL ) = 0 then begin // исключить театральные дубли
{Title}	Title := TextBetween(HTML, '">', '</a>', true, curPos);
		Title := StringReplace( Title, ' - фильмография -', ',', false, true, true ); 
		if Pos( ' - ', Title ) > 0 then
		  Title := Copy(Title, 1, Pos( ' - ', Title));  // убрать " - Кинотеатр.РУ"
		s:= StringReplace( URL, 'print/', '', false, true, true );
		s:= StringReplace( s, 'kino/', '', false, true, true );
		s:= StringReplace( s, 'bitpart/', '', false, true, true );
		s:= StringReplace( s, '/w/', '/foto/', false, true, true );
		s:= StringReplace( s, '/m/', '/foto/', false, true, true );
		s:= StringReplace( s, '/c/', '/foto/', false, true, true );
{JPEG}	previewURL := StringReplace( s, '/works/', '.jpg', false, true, true );
		AddSearchResult( Title, '', '', URL, previewURL );
	  end;
   	  curPos := PosFrom ( GResUnitMarker, HTML, curPos + 700 );
    end; 
  until curPos = 0;
 end;

procedure ParseBio(BioURL : AnsiString; var HTML : String);
var
 curPos,iPos: Integer;
 s, sname: String;
 begin
// имена  
  curPos:=Pos('<title>',HTML);
  s:=TextBetween(HTML, '<title>', '</title>', true, curPos);
  iPos:=1;
  while PosFrom('(', s, iPos)>0 do 
   begin
	sname:=TextBetween(s, '(', ')', false, iPos);
	if (Pos('ros', BioURL)>0) or (Pos('sov', BioURL)>0) or (Pos('post', BioURL)>0) then
	  if (sname[1]>='А') and (sname[1]<='Я') then 
		AddFieldValueXML('altnames',sname) else AddFieldValueXML('transname',sname)
	else AddFieldValueXML('altnames',sname)
  end;
// фото
  iPos:=Pos('/acter/foto',HTML);
  if iPos>0 then 
	if iPos < Pos('">обсужд', HTML) then // не jpg в ревью  
	  ExtraLinks[smPhoto]:=BASE_URL+Copy(HTML,iPos,PosFrom('.jpg',HTML,iPos)-iPos+4)
	else iPos:=Pos('биография</strong>',HTML)
  else iPos:=Pos('биография</strong>',HTML); // за "В этот день"
// даты
  curPos:=PosFrom('/day/y',HTML, iPos);
  if curPos>0 then begin
	curPos:=PosFrom('дата рождения:',HTML, iPos);
	if curPos>0 then
	  AddFieldValueXML('birthday', HTMLValues (HTML, 'title=', '</span>', '>', '<', '', curPos))
    else begin
	  curPos:=PosFrom('- <span><a href="/mourn',HTML, iPos)-90;
	  if curPos>-90 then
		begin
		  AddFieldValueXML('birthday', HTMLValues (HTML, 'title=', '</span>', '>', '<', '', curPos));
		  AddFieldValueXML('death', HTMLValues (HTML, 'title=', '</span>', '>', '<', '', curPos));
		end else curPos:=PosFrom('/day/y',HTML, iPos) //curPos:=iPos
	end
  end else curPos:=iPos; // дат нет
// биография
  curPos:=PosFrom('<strong>биография', HTML, curPos);
  if curPos>0 then begin
	iPos:=PosFrom('align="left">', HTML, curPos)+13; // ФИО
	s:=Copy(HTML,iPos,PosFrom('</h2>', HTML, iPos)-iPos);
  end;
  if Copy(HTML, PosFrom('</h2>', HTML, curPos)+5, 3)='<p>' then
	begin
	  curPos:=PosFrom('<p></p>',HTML,curPos)+7;
	  if Copy(HTML, curPos, 3)='<p>' then
		curPos:=PosFrom('<p></p>',HTML,curPos)+7
	end
  else curPos:=PosFrom('</h2',HTML,curPos)+5;
  if curPos < PosFrom('<p class="sm"', HTML, curPos) then begin
	s:=s+'. '+Copy(HTML,curPos,PosFrom('<p class="sm"',HTML,curPos)-curPos);
	Delete(s, Pos('<div class="sep', s), Length(s)-Pos('<div class="sep', s)+1); 	
	s:=StringReplace(s, '<br />', '', true, true, true);
	iPos:=1;
	s:=HTMLToText(s);
	AddFieldValueXML('bio', s);
  end
 end;
 
function ParsePage( HTML : String; URL : AnsiString ) : Cardinal;
 begin
  if (Pos('Google', URL)>0) then // страница от Гугл
	begin
	 if Pos(GResUnitMarker, HTML)>0 then // хоть один результат
		begin
		 ParseSearchResults(HTML);
		 Result := prList; Exit
		end
	 else if Pos( 'sorry', URL )>0 then
		begin
		 ShowMessage ('Сработала защита Google. Возможно, нужно ввести проверочный код', 'Ошибка');
		 Result := prError; Exit
		end
	 else if Pos('е найдено', HTML) > 0 then // Гугл не нашел
		begin
		 ShowMessage ('Ничего не найдено', 'Ошибка');
		 Result := prError; Exit
		end else ShowMessage ('Неопределенная ошибка Гугл', 'Ошибка');
	 end
  else if Pos('temporarily unavailable', HTML)>0 then 
		begin
			ShowMessage('Страница недоступна, попробуйте позже','Ошибка')
			Result:=prError; Exit;
		end
	   else
		case mode of
		smSearch:
			if Pos('"100%"><tr', HTML)>0 then				
				ParseFilmography(URL, HTML)
			else begin 
				ShowMessage('Не распознана фильмография','Ошибка');
				Result:=prError; Exit;
			end;
		smBio	:
			if Pos('- биография -', HTML)>0 then
				ParseBio(URL, HTML)
			else begin 
				ShowMessage('Нет страницы биографии','Предупреждение');
			end;
		end;
  Repeat Inc(Mode) until (mode >= smFinished) Or (GetDownloadURL <> '');
  if Mode >= smFinished then Result := prFinished else Result := prDownload;
 end;

begin end.
{История версий:
v0.0.1.0 от 11.02.12
Первая публичная версия

v0.0.1.1 от 13.02.12
! Обновлен разбор результатов поиска

v0.0.2.0 от 16.02.12
! Скрипт переименован в kino-teatr_person.psf
+ Даты рождения, смерти
+ Другие имена, переведенное имя
+ Биография
+ Фото

v0.0.2.1 от 17.02.12
* Постфикс серий: Удаление двойного пробела
* Постфикс серий: Обработка '-_й фильм'
! Ссылка серии в отсутствие ссылки на сайте
* Описание скрипта: "информацию о персоне"

v0.0.2.2 от 21.02.12
* Обработка отсутствия фотографии в био
* Имя скрипта 'Кино-Театр.ру Персоны'
* Подстройка поискового запроса
* Константа типа скрипта для обновления

v0.0.2.3 от 25.02.12
+ Третье название фильма
+ Второе название серии и сериала
* Постфикс: скобка после маркера серии
* "Сезон" в наименовании серии и сериала

v0.0.3.0 от 31.03.12
! Обновлен парсинг согласно изменениям на сайте
}