3
51
fe329d86-741f-4816-a195-57c0a4e876dc
234DF17B-418C-4FDC-9DFE-CD0C586D2E76
4
YoutubeChannels_v3
527
--maxresults=50 --dopvideo=off --orderby=date --relevancelanguage=ru --safesearch=none --pattern='[{num0}] {title} ({published})' --replacefrom='' --replaceto='' --replacefrom2='' --replaceto2='' --replacefrom3='' --replaceto3='' --dopvideopattern='{num0} {title} ({published})' --delemptybrackets=on --getfilesize=off --gettimelength=on --backupstars=on --log=off
515
1
700
0
701
-1
702
-1
517
578-720,722-1080,482-576,402-480,322-400,202-320,0-200
518
0
512
0
532
1
553
1
522
0
570
0
245
fe329d86-741f-4816-a195-57c0a4e876dc
93
41825,5740458449
530
// [GetList] Скрипт получения списка видео с Youtube
// Made By Vadim_S, http://storvild.ru
const
VER = 'HMS.3.0.28.beta'; //Release версия пишется с точкой в конце HMS.3.1.0. для удобного разделения через explode.
csSiteLink = 'HMS';
INTERNET_FLAG_NO_COOKIES = $00080000; { no automatic cookie handling }
INTERNET_FLAG_NO_AUTO_REDIRECT = $00200000;
INTERNET_FLAG_RELOAD = $80000000;
mpiYoutubePageToken = 141206;
mpiYoutubeType = 141207;
mpiYoutubeCode = 141208;
mpiYoutubeUrl = 141209;
mpiYoutubeCurPage = 141210;
mpiYoutubeSearch = 141220;
mpiYoutubeTTSUrl = 141211;
mpiYoutubeVideoHeight = 141212;
mpiYoutubeVideoID = 141213;
mpiYoutubeViewCount = 141214;
mpiYoutubeDuration = 141215;
mpiYoutubeDescription = 141216;
mpiYoutubeChannelTitle= 141217;
mpiYoutubeChannelId = 141218;
mpiYoutubeRating = 141219;
// = 141221;
var
csStarsBackupFileName: string = HmsDataDirectory+'\hms_stars.backup';
gpSafeSearch: string = 'moderate'; //Показывать ли закрытый контент. none | moderate | strict
gpMaxResults: Integer = 50; //Кол-во результатов на страницу 1-50
gpOrderBy: string = 'date'; // date | rating | relevance | title | viewCount | videoCount
gpPublishedAfter: string = ''; //Найти видео до указанной даты '2014-02-23T00:00:00Z'
gpPublishedBefore: string = ''; //Найти видео после указанной даты '2015-03-01T00:00:00Z'
gpRelevanceLanguage: string = ''; // Язык роликов ('ru', 'en')
gpVideoDimension: string = 'any'; // any | 2d | 3d
gpVideoDefinition: string = 'any'; // Качество (any | high | standard)
gpVideoDuration: string = 'any'; // Длина видео (any | long | medium | short)
gpReplaceFrom: string = '';
gpReplaceTo: string = '';
gpReplaceFrom2: string = '';
gpReplaceTo2: string = '';
gpReplaceFrom3: string = '';
gpReplaceTo3: string = '';
gpPattern: string = '[{num0}] {title} ({published})'; //num, title, published, year, month, day
gpPlaylistPattern: string = '';
gpChannelPattern: string = '';
gpDopVideo: string = 'off';
gpDopVideoPattern: string = '{num0} {title} ({published})';
gpDopVideoMaxResults: Integer = 25; //Максимальное кол-во доп.видео 1-50
gpDopVideoInFolder: string = 'on';
gpDelEmptyBrackets: string = 'on'; //Удаление пустых скобок (круглых и квадратных) образованных после замены по шаблону
gpStarsBackup: string = 'on';
log: string = 'off';
logError: string = 'on';
procedure InitParameters;
var lmaxresults,lstartindex: integer;
ltype: string;
begin
gpMaxResults := StrToIntDef(GetPodcastParam('--maxresults', ''), gpMaxResults); //50
gpSafeSearch := GetPodcastParam('--safesearch', gpSafeSearch); // moderate (none | moderate | strict)
gpOrderBy := GetPodcastParam('--orderby', gpOrderBy); // date (date | rating | relevance | title | viewCount)
gpPublishedAfter := GetPodcastParam('--publishedafter', gpPublishedAfter); //Найти видео до указанной даты '2014-02-23T00:00:00Z'
gpPublishedBefore := GetPodcastParam('--publishedbefore', gpPublishedBefore); //Найти видео после указанной даты '2015-03-01T00:00:00Z'
gpRelevanceLanguage := GetPodcastParam('--relevancelanguage', gpRelevanceLanguage); // Язык роликов ('ru')
gpVideoDimension := GetPodcastParam('--videodimension', gpVideoDimension); // any | 2d | 3d
gpVideoDefinition := GetPodcastParam('--videodefinition', gpVideoDefinition); // Качество (any | high | standard)
gpVideoDuration := GetPodcastParam('--videoduration', gpVideoDuration); // Длина видео (any | long | medium | short)
gpReplaceFrom:= GetPodcastParam('--replacefrom', gpReplaceFrom); // ''
gpReplaceTo := GetPodcastParam('--replaceto', gpReplaceTo); // ''
gpReplaceFrom2:= GetPodcastParam('--replacefrom2', gpReplaceFrom2); // ''
gpReplaceTo2 := GetPodcastParam('--replaceto2', gpReplaceTo2); // ''
gpReplaceFrom3:= GetPodcastParam('--replacefrom3', gpReplaceFrom3); // ''
gpReplaceTo3 := GetPodcastParam('--replaceto3', gpReplaceTo3); // ''
gpPattern := GetPodcastParam('--pattern', gpPattern); // '[{num}] {title} {published}' (pubyear,pubmonth,pubday)
gpPlaylistPattern := GetPodcastParam('--playlistpattern', gpPlaylistPattern); // ''
gpChannelPattern := GetPodcastParam('--channelpattern', gpPlaylistPattern); // ''
gpDopVideo := GetPodcastParam('--dopvideo', gpDopVideo); // off (on | off)
gpDopVideoPattern := GetPodcastParam('--dopvideopattern', gpDopVideoPattern); // '[{parentnum}] {parenttitle,10} {num} {title}'
gpDelEmptyBrackets := GetPodcastParam('--delemptybrackets', gpDelEmptyBrackets); // on (on | off)
gpDopVideoInFolder := GetPodcastParam('--dopvideoinfolder', gpDopVideoInFolder); // on (on | off)
gpDopVideoMaxResults := StrToIntDef(GetPodcastParam('--dopvideomaxresults', ''), gpDopVideoMaxResults); //25
gpStarsBackup := GetPodcastParam('--starsbackup', gpStarsBackup); // on (on | off)
log := GetPodcastParam('--log',log); //off (on | off)
logError := GetPodcastParam('--logerror',logError); //off (on | off)
end;
//============= Общие функции ================================================//
function GetTimeLength(const aValue: string): string;
var
eDuration: Extended;
begin
eDuration := StrToFloatDef(aValue, 0);
if eDuration > 0 then
Result := FormatDateTime('HH:NN:SS.ZZZ', eDuration / (24 * 60 * 60))
else
Result := ''
end;
function GetPublishedDate(const aValue: string): string;
begin
Result:=Copy(aValue,9,2)+'.'+Copy(aValue,6,2)+'.'+Copy(aValue,1,4);
end;
//Заменяет в шаблоне параметр в фигурных скобках {title}. Если параметр {title,10}, то укорачивает до 10 символов
function ReplacePattern(inPattern, inParamName, inParamValue: string): string;
var mparam, mparamcount_str: string;
mparamcount: Integer;
pvshort: string;
res: string;
lParamValue: string;
begin
//Если в шаблоне используется ограничение по размеру текста {title,10}
if HmsRegExMatch2('({'+inParamName+',\s*(\d+)})', inPattern, mparam, mparamcount_str) then
begin
res := inPattern;
lParamValue := inParamValue;
mparamcount := StrToInt(mparamcount_str);
if (mparamcount>=0) and (length(lParamValue)>mparamcount) then
lParamValue := Copy(lParamValue,1,mparamcount)+'...';
res := ReplaceStr(res, mparam, lParamValue);
end
else
begin
res := inPattern;
lParamValue := inParamValue;
//Замена параметра в фигурных скобках
res := ReplaceStr(res, '{'+inParamName+'}', lParamValue);
end;
Result := res;
end;
function GetPodcastParam(const aParamName, aDefaultValue: string): string;
begin
Result := ExtractParam(mpPodcastParameters, aParamName);
if Result = '' then Result := aDefaultValue
end;
//Удаляет двойные пробелы и переводы строк (заменяет пробелами)
function trimex(inText: string): string;
begin
if (pos(' ',inText)>0) or (pos(#13,inText)>0) or (pos(#10,inText)>0) then
begin
Result := ReplaceStr(inText, ' ', ' ');
Result := ReplaceStr(Result, #13, ' ');
Result := ReplaceStr(Result, #10, ' ');
Result := trimex(Result);
end
else
Result := trim(inText);
end;
function Ceil(inVal: Extended): Integer;
begin
Result:=Int(inVal);
if (Int(inVal)<inVal) then
Result := Int(inVal)+1;
end;
function DownloadUrl(const aUrl: string; const aUserAgent: string = ''): string;
var
iPort: Integer;
sHeader, sReferer, sServer, sObject, sProtocol: string;
begin
aUrl := HmsUtf8Encode(aUrl); //Преобразование ссылки в UTF-8
if HmsRegExMatch3('http(.?)://([^/]*)(/.*)?', aUrl, sProtocol, sServer, sObject) then
begin
if sObject = '' then sObject := '/';
if sProtocol = 's' then iPort := 443 else iPort := 80;
sReferer := csSiteLink + #13#10 + 'Accept: */*'#13#10'Accept-Encoding: gzip,deflate';
if aUserAgent <> '' then
sReferer := sReferer + #13#10'User-Agent: ' + aUserAgent;
Result := HmsSendRequestEx(sServer, sObject, 'GET', '', sReferer, '', iPort,
INTERNET_FLAG_NO_COOKIES or INTERNET_FLAG_RELOAD, sHeader, True);
if (Result <> '') and (Ord(Result[1]) = 31) then
Result := HmsDecompressString(Result);
end
else if Pos('http', aUrl) = 1 then
Result := HmsDownloadUrl(aUrl)
else
Result := '';
Result := HmsUtf8Decode(Result); //Преобразование результата из UTF-8
end;
function ReplaceStrI(const cStr1, cSrch, cReplace: String): String;
var sLowerSrch, sTempStr, sResultStr: string;
i: integer;
begin
//HmsRegExReplace(cSrch, cStr1, cReplace, sResultStr, 1, PCRE_CASELESS);
sResultStr := '';
sLowerSrch := Lowercase(cSrch);
sTempStr := cStr1;
while Pos(sLowerSrch, Lowercase(sTempStr))>0 do
begin
i := Pos(sLowerSrch, Lowercase(sTempStr));
sResultStr := sResultStr+Copy(sTempStr, 1, i-1)+cReplace;
Delete(sTempStr,1, i-1+Length(sLowerSrch));
end;
sResultStr := sResultStr+sTempStr;
Result := sResultStr;
end;
procedure addLog(inMsg: string; inTimeON: boolean = true);
var TimeStr: string = '';
begin
if (log='on') then
begin
if inTimeON then
TimeStr := FormatDateTime('HH:NN:SS.ZZZ',Now())+' ';
HmsLogMessage(1, TimeStr+' '+inMsg);
end;
end;
procedure addLogError(inMsg: string; inTimeON: boolean = true);
var TimeStr: string = '';
begin
if (logError='on') then
begin
if inTimeON then
TimeStr := FormatDateTime('HH:NN:SS.ZZZ',Now())+' ';
HmsLogMessage(2, TimeStr+' '+inMsg);
end;
end;
//============= Ф-ции для работы с подкастами ================================//
//Получение списка видео по параметрам
procedure CreateVideoItems_By_Json(inParentFolder: THmsScriptMediaItem; inUrl, inParentTitle, inParentNum, inPattern, inDopVideo: string; inType: string = ''; inCode: string = ''; inSearch: string = '');
var MainObj, EntryObj, ItemObj: TJsonObject;
i,j,k,n: integer;
lurl: string;
lYtCode, lYtLink, lTitle, lPublishedDate, lDuration, lThumbnail, lViewCount,
lNumLikes, lNumDislikes, lEpisodeNum, lChannelTitle, lChannelId, lNumRaters, lDescription,
lFirstReleased, lYear: string;
lRatingF: double;
lRating: string;
lShowNum: string = '';
lShowDate: string = '';
lShowEpisodeNum: string = '';
MediaItem: THmsScriptMediaItem;
lPattern: string;
lComment: string;
lTitleFormat: string;
mtitle,mtitlecount: string;
lParentFolder: THmsScriptMediaItem;
lCountVideo: Integer;
ldownloadStr: string;
lNextFolder: THmsScriptMediaItem;
lNextPageToken: string;
lItemsPerPage,lItemsCount,lPageCount: integer;
DopVideoFolder: THmsScriptMediaItem;
lYtCodes: string = '';
lStarsBackupText: string;
lNum, lPosition: integer;
lPublishedDateFormat: string;
begin
//Временно
//gpMaxResults:=10;
//inChannel:='thisishorosho';
// RaiseException(IntToStr(1));
if gpStarsBackup='on' then
begin
lStarsBackupText := HmsStringFromFile(csStarsBackupFileName);
end;
MainObj := TJsonObject.Create();
lurl := inUrl;
try
//ldownloadStr := HmsUtf8Decode(DownloadUrl(HmsUtf8Encode(lurl)));
ldownloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(ldownloadStr);
EntryObj := MainObj.O['items'];
//Папка "Следующая страница"
//RaiseException(lurl);
lNextPageToken := MainObj.S['nextPageToken'];
lItemsPerPage := MainObj.I['itemsPerPage'];
lItemsCount := MainObj.I['itemsCount'];
lPageCount := Ceil(lItemsCount/lItemsPerPage);
if (lNextPageToken<>'') then
begin
//Youtube показывает кол-во видео не учитывая поисковую строку
// поэтому проверяем если кол-во видео на текущей странице меньше чем максимальное, то скорее всего следующей страницы не будет
if (inSearch='') or (EntryObj.AsArray.Length>=gpMaxResults) then
begin
lNextFolder := inParentFolder.AddFolder(mpFilePath+'-'+IntToStr(lPageCount)); //Если AddFolder('') то папка не создается. Одинаковые имена типа '-' не подходят для создания более одной папки
lNextFolder[mpiFilePath] := inParentFolder[mpiFilePath];
lNextFolder[mpiTitle] := 'Следующая страница 2/'+IntToStr(lPageCount);
lNextFolder[mpiFolderSortOrder] := inParentFolder[mpiFolderSortOrder];
lNextFolder[mpiComment] := inParentFolder[mpiComment];
//lNextFolder[mpiPartNo] := 2;
//lNextFolder[mpiPartTotal] := lPageCount;
lNextFolder[mpiThumbnail] := '';
lNextFolder[mpiYoutubePageToken] := lNextPageToken;
lNextFolder[mpiYoutubeType] := inType;
lNextFolder[mpiYoutubeCode] := inCode;
lNextFolder[mpiYoutubeSearch] := inSearch;
lNextFolder[mpiYoutubeUrl] := inUrl;
lNextFolder[mpiYoutubeCurPage] := 2; //2 страница
if log='on' then
lNextFolder[mpiComment] := lNextFolder[mpiComment]+' --NextPageToken='+lNextPageToken;
end;
end;
if (gpDopVideo='on') and (gpDopVideoInFolder='on') then
begin
DopVideoFolder := FolderItem.AddFolder('Дополнительные видео');
DopVideoFolder[mpiYoutubeCurPage] := 1;
DopVideoFolder[mpiFolderSortOrder] := FolderItem[mpiFolderSortOrder];
end;
for i:=0 to EntryObj.AsArray.Length-1 do
begin
ItemObj := EntryObj.AsArray.O[i];
lYtCode := ItemObj.S['videoId'];
if lYtCode<>'' then
begin
lYtCodes:=lYtCodes+','+lYtCode;
addLog('Добавлено видео: '+lYtCode);
lYtLink := 'http://www.youtube.com/watch?v='+lYtCode;
lPublishedDate := ItemObj.S['publishedAt'];
lPublishedDateFormat := GetPublishedDate(lPublishedDate);
lThumbnail := ItemObj.S['thumbnail'];
lChannelTitle := ItemObj.S['channelTitle'];
lChannelId := ItemObj.S['channelId'];
lDescription := ItemObj.S['description'];
lDuration := ItemObj.S['duration'];
lYear := Copy(ItemObj.S['publishedAt'],1,4);
lViewCount := ItemObj.S['viewCount'];
lComment := lDescription+#13#10+'Канал: '+lChannelTitle;
if lViewCount<>'' then
lComment := lComment+#13#10+'Кол-во просмотров на Youtube: '+lViewCount;
lNum := i+1;
lPosition := i+1;
lShowNum := PadLeft(IntToStr(i+1),length(gpMaxResults),'0'); //lShowNum := FormatFloat('000',i+1)+' ';
lTitle := ItemObj.S['title'];
lTitleFormat := GetTitleByPattern(lTitle, inPattern, lNum, lPosition, lPublishedDate, lChannelTitle);
MediaItem := HmsCreateMediaItem(lYtLink, inParentFolder.ItemID);
MediaItem.Properties[mpiTitle] := lTitleFormat;
MediaItem.Properties[mpiCreateDate] := lPublishedDateFormat;
MediaItem.Properties[mpiComment] := lComment;
MediaItem.Properties[mpiThumbnail] := lThumbnail;
MediaItem.Properties[mpiTimeLength] := GetTimeLength(lDuration);
MediaItem.Properties[mpiPartNo] := i+1;
MediaItem.Properties[mpiPartTotal] := lItemsCount;
MediaItem.Properties[mpiYear] := lYear;
MediaItem.Properties[mpiProducer] := HmsUtf8Decode(HmsHttpDecode(lChannelTitle));
MediaItem.Properties[mpiProgramID] := lViewCount;
MediaItem.Properties[mpiSeriesEpisodeTitle] := lTitle;
MediaItem.Properties[mpiSeriesEpisodeNo] := lNum;
MediaItem.Properties[mpiYoutubeChannelTitle] := HmsUtf8Decode(HmsHttpDecode(lChannelTitle));
MediaItem.Properties[mpiYoutubeChannelId] := lChannelId;
MediaItem.Properties[mpiYoutubeDescription] := lDescription;
MediaItem.Properties[mpiYoutubeDuration] := StrToIntDef(lDuration,0);
MediaItem.Properties[mpiYoutubeViewCount] := StrToIntDef(lViewCount,0);
MediaItem.Properties[mpiYoutubeVideoID] := lYtCode;
if lStarsBackupText<>'' then
MediaItem.Properties[mpiRatingInStars] := GetStarsFromBackup(lYtCode, lStarsBackupText);
//MediaItem.Properties[mpiSeriesEpisodeNo] := i+1;
//MediaItem.Properties[mpiFileSize] := ;
//MediaItem.RetrieveProperties; //Получение свойств видеофайла (размер, длительность) ОЧЕНЬ МЕДЛЕННО!
end; //lYtCode<>''
end; //for
if DopVideoFolder <> nil then
begin
//DopVideoFolder[mpiFilePath]:=lYtCodes;
DopVideoFolder[mpiComment]:=lYtCodes;
end;
finally
MainObj.Free;
end;
end;
function AddDopVideo(inParentFolder: THmsScriptMediaItem; inYtCode, inParentTitle, inParentNum: string):Integer;
var MainObj, EntryObj, ItemObj: TJsonObject;
i,j,k,n: integer;
lUrl: string;
lEpisodeNum: string;
lDescription: string;
lYtCode, lYtCodes: string;
lTitle, lPublishedDate, lDuration, lThumbnail, lViewCOunt: string;
MediaItem: THmsScriptMediaItem;
downloadStr: string;
re: TRegExpr;
begin
MainObj := TJsonObject.Create();
try
lurl := Format('http://storvild.ru/yt.php?videoId=%s&app=%s', [inYtCode, VER]);
downloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(downloadStr);
EntryObj := MainObj.O['items'];
ItemObj := EntryObj.AsArray.O[0];
lDescription := ItemObj.S['description'];
lYtCodes := '';
n := 0;
re := TRegExpr.Create('youtube\.com\/watch\?v\=(\S{11})|youtu\.be\/(\S{11})');
try
if re.Search(lDescription) then
repeat
n := n+1;
lYtCode := re.Match(1);
if (lYtCode='') then
lYtCode := re.Match(2);
lYtCodes := lYtCodes+','+lYtCode; //Собираем все коды доп.видео, чтобы получить сразу их все
if (n>=gpDopVideoMaxResults) then
break;
until not re.SearchAgain
finally
re.Free;
end;
addLog('Получение доп. видео: '+lYtCodes+' ('+inYtCode+')');
if (lYtCodes<>'') then
begin
lUrl := Format('http://storvild.ru/yt.php?videoId=%s&app=%s', [lYtCodes, VER]);
CreateVideoItems_By_Json(inParentFolder, lUrl, inParentTitle, inParentNum, gpDopVideoPattern, 'off');
end;
finally
MainObj.Free;
Result:=n;
end;
end;
function CheckApp(inParentFolder: THmsScriptMediaItem):Integer;
var MainObj, EntryObj, ItemObj: TJsonObject;
i,j,k,n: integer;
lUrl, downloadStr: string;
MediaItem: THmsScriptMediaItem;
lYtCode, lYtLink, lLink, lTitle, lComment, lPublishedDate, lThumbnail: string;
begin
lUrl :=Format('http://storvild.ru/yt.php?appcheck=1&app=%s', [VER]);
MainObj := TJsonObject.Create();
try
downloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(downloadStr);
if (MainObj.I['result']<=0) then
begin
Result:=MainObj.I['result'];
EntryObj := MainObj.O['items'];
ItemObj := EntryObj.AsArray.O[0];
lLink := ItemObj.S['url'];
lYtCode := ItemObj.S['videoId'];
lTitle := ItemObj.S['title'];
lPublishedDate := ItemObj.S['publishedAt'];
lComment := ItemObj.S['description'];
lThumbnail := ItemObj.S['thumbnail'];
MediaItem := HmsCreateMediaItem(lLink, inParentFolder.ItemID);
MediaItem.Properties[mpiTitle] := lTitle;
MediaItem.Properties[mpiCreateDate] := lPublishedDate; //Now;
MediaItem.Properties[mpiComment] := lComment;
MediaItem.Properties[mpiThumbnail] := lThumbnail;
end
else
begin
Result:= MainObj.I['result'];
end;
finally
MainObj.Free;
end;
end;
function GetStarsFromBackup(inYtCode, inText: string): integer;
var lExistStar: string;
begin
Result := NULL;
if HmsRegExMatch(inYtCode+'=(\d+)', inText, lExistStar) then
begin
Result := StrToInt(lExistStar);
end;
end;
procedure BackupStarsToFile();
var lStarsBackupTextOrig: string;
lStarsBackupTextNew: string;
begin
if gpStarsBackup='on' then
begin
if not FileExists(csStarsBackupFileName) then
HmsStringToFile('', csStarsBackupFileName); //Создаем файл если его не было
lStarsBackupTextOrig := HmsStringFromFile(csStarsBackupFileName);
lStarsBackupTextNew := CopyStarsToStringRec(FolderItem, lStarsBackupTextOrig);
if lStarsBackupTextOrig<>lStarsBackupTextNew then
HmsStringToFile(lStarsBackupTextNew, csStarsBackupFileName);
end;
end;
function CopyStarsToStringRec(inFolder: THmsScriptMediaItem; var inExistCodes: string): string;
var i: integer;
lMediaItem: THmsScriptMediaItem;
lYtCode: string;
s: string;
lExistStar: string;
lExistRpl: string;
lSeparate: string = chr(13)+chr(10);
begin
s := inExistCodes;
for i:=0 to inFolder.ChildCount-1 do
begin
lMediaItem := inFolder.ChildItems[i];
if lMediaItem.isFolder then
begin
if lMediaItem.ChildCount>0 then
begin
s := CopyStarsToStringRec(lMediaItem, s);
end;
end
else
begin
// Это не папка
if lMediaItem.Properties[mpiRatingInStars] <> NULL then
begin
//Здесь мы убираем существующие коды из s и добавляем в s новые
lYtCode := VarToStr(lMediaItem.Properties[mpiYoutubeVideoID]);
// Если нашли код, то проверяем совпадает ли оценка
if HmsRegExMatch(lYtCode+'=(\d+)', s, lExistStar) then //, PCRE_DOTALL+PCRE_MULTILINE
begin
if lMediaItem.Properties[mpiRatingInStars]<>lExistStar then
begin
if Pos(lSeparate+lYtCode+'='+lExistStar, s)>0 then
s := ReplaceStr(s, lSeparate+lYtCode+'='+lExistStar, '')
else
s := ReplaceStr(s, lYtCode+'='+lExistStar, '');
s := s+lSeparate+lYtCode+'='+ VarToStr(lMediaItem.Properties[mpiRatingInStars]);
end
end
else
s := s+lSeparate+lYtCode+'='+ VarToStr(lMediaItem.Properties[mpiRatingInStars]);
end;
end;
end;
Result := s;
end;
procedure CreatePlaylists(inFolder: THmsScriptMediaItem; inFilePath: string; inPageToken: string);
var playlistsFolder: THmsScriptMediaItem;
lfilepath, lcode, lcodeex, lsearch: string;
lurl: string;
i: integer;
MainObj, EntryObj, ItemObj: TJsonObject;
ldownloadStr: string;
itemTitle, itemPlaylistId, itemChannelId, itemDescription, itemChannelTitle, itemPublished, itemThumbnail: string;
itemPosition: integer;
lNextPageToken: string;
lItemsPerPage, lItemsCount, lPageCount, lNextPage, lCurPage: integer;
lNextFolder: THmsScriptMediaItem;
lYear: string;
lTitleFormat: string;
lNum, lPosition: integer;
begin
lurl := '';
lfilepath := inFilePath;
lcode := '';
lsearch := GetSearch(inFolder);
if (HmsRegExMatch('channel\/([^& \/]+)', lfilepath, lcode)) or
(HmsRegExMatch('^(UC[^& \/]+)', lfilepath, lcode)) then
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&channelId=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lcode, lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'channel/'+lcode+'/playlists';
end
else if HmsRegExMatch('user\/([^& \/]+)', lfilepath, lcode) then
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&channel=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lcode, lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'user/'+lcode+'/playlists';
end
else if HmsRegExMatch('search_query=([^& \/]+)', lfilepath, lcode) then
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lcode, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := lcode;
end
else if HmsRegExMatch('^([^& \/]+)\/playlists', lfilepath, lcode) then
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&channel=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lcode, lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
end
else
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := lfilepath;
end;
MainObj := TJsonObject.Create();
try
ldownloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(ldownloadStr);
EntryObj := MainObj.O['items'];
//Папка "Следующая страница"
lNextPageToken := MainObj.S['nextPageToken'];
lItemsPerPage := MainObj.I['itemsPerPage'];
lItemsCount := MainObj.I['itemsCount'];
lPageCount := Ceil(lItemsCount/lItemsPerPage);
lNextPage := 2;
lCurPage := 1;
if lNextPageToken<>'' then
begin
//Youtube показывает кол-во видео не учитывая поисковую строку
// поэтому проверяем если кол-во видео на текущей странице меньше чем максимальное, то скорее всего следующей страницы не будет
if (inFolder[mpiComment]='') or (EntryObj.AsArray.Length>=gpMaxResults) then
begin
lNextFolder := inFolder.AddFolder(mpFilePath+'-'+IntToStr(lPageCount)); //Если AddFolder('') то папка не создается. Одинаковые имена типа '-' не подходят для создания более одной папки
lNextFolder[mpiFilePath] := inFolder[mpiFilePath];
lNextFolder[mpiTitle] := 'Следующая страница '+IntToStr(lNextPage)+'/'+IntToStr(lPageCount);
lNextFolder[mpiComment] := inFolder[mpiComment];
lNextFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
lNextFolder[mpiThumbnail] := '';
lNextFolder[mpiYoutubePageToken] := lNextPageToken;
lNextFolder[mpiYoutubeType] := 'playlists';
lNextFolder[mpiYoutubeCode] := lcodeex;
lNextFolder[mpiYoutubeUrl] := inFilePath;
lNextFolder[mpiYoutubeCurPage] := IntToStr(lNextPage);
lNextFolder[mpiYoutubeSearch] := lsearch;
if log='on' then
lNextFolder[mpiComment] := lNextFolder[mpiComment]+' --NextPageToken='+lNextPageToken;
// lNextFolder[mpiComment] := VarToStr(lNextFolder[mpiYoutubeType])+'#'+VarToStr(lNextFolder[mpiYoutubeCode])+'#'+VarToStr(lNextFolder[mpiYoutubePageToken]);
end;
end;
for i:=0 to EntryObj.AsArray.Length-1 do
begin
ItemObj := EntryObj.AsArray.O[i];
itemTitle := ItemObj.S['title'];
itemPlaylistId := ItemObj.S['playlistId'];
itemChannelId := ItemObj.S['channelId'];
itemDescription := ItemObj.S['description'];
itemChannelTitle := ItemObj.S['channelTitle'];
itemThumbnail := ItemObj.S['thumbnail'];
itemPosition := ItemObj.I['position'];
itemPublished := ItemObj.S['publishedAt'];
lYear := Copy(itemPublished,1,4);
lNum := i+1+(lCurPage-1)*lItemsPerPage;
lPosition := itemPosition;
lTitleFormat := GetTitleByPattern(itemTitle, gpPlaylistPattern, lNum, lPosition, itemPublished, itemChannelTitle);
playlistsFolder := inFolder.AddFolder(itemPlaylistId);
playlistsFolder[mpiFilePath] := 'http://www.youtube.com/playlist?list='+itemPlaylistId; //'http://www.youtube.com/playlist?list=PLIw_h2Az1xWYQjB3csaArQGjhTU3wbCbN';
//playlistsFolder := inFolder.AddFolder('http://www.youtube.com/playlist?list='+itemPlaylistId);
playlistsFolder[mpiTitle] := lTitleFormat;
playlistsFolder[mpiComment] := itemDescription;
playlistsFolder[mpiYoutubeCurPage] := 1;
playlistsFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
playlistsFolder[mpiYoutubeType] := 'playlist';
playlistsFolder[mpiYoutubeCode] := itemPlaylistId;
playlistsFolder[mpiYoutubePageToken] := '';
playlistsFolder[mpiThumbnail] := itemThumbnail;
playlistsFolder[mpiYoutubeChannelTitle] := itemChannelTitle;
playlistsFolder[mpiYoutubeChannelId] := itemChannelId;
playlistsFolder[mpiYoutubeDescription] := itemDescription;
playlistsFolder[mpiPartNo] := lNum;
playlistsFolder[mpiPartTotal] := lItemsCount;
playlistsFolder[mpiYear] := lYear;
playlistsFolder[mpiProducer] := HmsUtf8Decode(HmsHttpDecode(itemChannelTitle));
playlistsFolder[mpiCreateDate] := GetPublishedDate(itemPublished);
end; //for
finally
MainObj.Free;
end;
end;
procedure CreateChannels(inFolder: THmsScriptMediaItem; inFilePath: string; inPageToken: string);
var channelsFolder: THmsScriptMediaItem;
lfilepath, lcode, lcodeex, lsearch: string;
lurl: string;
i: integer;
MainObj, EntryObj, ItemObj: TJsonObject;
ldownloadStr: string;
itemTitle, itemChannelId, itemDescription, itemChannelTitle, itemPublished, itemThumbnail: string;
itemPosition: integer;
lNextPageToken: string;
lItemsPerPage, lItemsCount, lPageCount, lNextPage, lCurPage: integer;
lNextFolder: THmsScriptMediaItem;
lYear: string;
lTitleFormat: string;
lNum, lPosition: integer;
begin
lurl := '';
lfilepath := inFilePath;
lcode := '';
lsearch := GetSearch(inFolder);
if HmsRegExMatch('search_query=([^& \/]+)', lfilepath, lsearch) then
begin
lurl := Format('http://storvild.ru/yt.php?type=channel&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'search/'+lcode;
end
else
begin
lurl := Format('http://storvild.ru/yt.php?type=channel&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := lfilepath;
end;
MainObj := TJsonObject.Create();
try
ldownloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(ldownloadStr);
EntryObj := MainObj.O['items'];
//Папка "Следующая страница"
lNextPageToken := MainObj.S['nextPageToken'];
lItemsPerPage := MainObj.I['itemsPerPage'];
lItemsCount := MainObj.I['itemsCount'];
lPageCount := Ceil(lItemsCount/lItemsPerPage);
lNextPage := 2;
lCurPage := 1;
if lNextPageToken<>'' then
begin
lNextFolder := inFolder.AddFolder(mpFilePath+'-'+IntToStr(lPageCount)); //Если AddFolder('') то папка не создается. Одинаковые имена типа '-' не подходят для создания более одной папки
//lNextFolder[mpiFilePath] := mpFilePath;
lNextFolder[mpiFilePath] := inFolder[mpiFilePath];
lNextFolder[mpiComment] := inFolder[mpiComment];
lNextFolder[mpiTitle] := 'Следующая страница '+IntToStr(lNextPage)+'/'+IntToStr(lPageCount);
lNextFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
lNextFolder[mpiThumbnail] := '';
lNextFolder[mpiYoutubePageToken] := lNextPageToken;
lNextFolder[mpiYoutubeType] := 'channels';
lNextFolder[mpiYoutubeCode] := lcodeex;
lNextFolder[mpiYoutubeSearch] := lsearch;
lNextFolder[mpiYoutubeUrl] := inFilePath;
lNextFolder[mpiYoutubeCurPage] := IntToStr(lNextPage);
if log='on' then
lNextFolder[mpiComment] := lNextFolder[mpiComment]+' --NextPageToken='+lNextPageToken;
// lNextFolder[mpiComment] := VarToStr(lNextFolder[mpiYoutubeType])+'#'+VarToStr(lNextFolder[mpiYoutubeCode])+'#'+VarToStr(lNextFolder[mpiYoutubePageToken]);
end;
for i:=0 to EntryObj.AsArray.Length-1 do
begin
ItemObj := EntryObj.AsArray.O[i];
itemTitle := ItemObj.S['title'];
itemChannelId := ItemObj.S['channelId'];
itemDescription := ItemObj.S['description'];
itemChannelTitle := ItemObj.S['channelTitle'];
itemThumbnail := ItemObj.S['thumbnail'];
itemPosition := ItemObj.I['position'];
itemPublished := ItemObj.S['publishedAt'];
lYear := Copy(itemPublished,1,4);
lNum := i+1+(lCurPage-1)*lItemsPerPage;
lPosition := itemPosition;
lTitleFormat := GetTitleByPattern(itemTitle, gpChannelPattern, lNum, lPosition, itemPublished, itemChannelTitle);
channelsFolder := inFolder.AddFolder(itemChannelId);
channelsFolder[mpiFilePath] := 'http://www.youtube.com/channel/'+itemChannelId;
channelsFolder[mpiTitle] := lTitleFormat;
channelsFolder[mpiComment] := itemDescription;
channelsFolder[mpiYoutubeCurPage] := 1;
channelsFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
channelsFolder[mpiYoutubeType] := 'channel';
channelsFolder[mpiYoutubeCode] := itemChannelId;
channelsFolder[mpiYoutubePageToken] := '';
channelsFolder[mpiThumbnail] := itemThumbnail;
channelsFolder[mpiYoutubeChannelTitle] := itemChannelTitle;
channelsFolder[mpiYoutubeChannelId] := itemChannelId;
channelsFolder[mpiYoutubeDescription] := itemDescription;
channelsFolder[mpiPartNo] := lNum;
channelsFolder[mpiPartTotal] := lItemsCount;
channelsFolder[mpiYear] := lYear;
channelsFolder[mpiProducer] := HmsUtf8Decode(HmsHttpDecode(itemChannelTitle));
channelsFolder[mpiCreateDate] := GetPublishedDate(itemPublished);
end; //for
finally
MainObj.Free;
end;
end;
function GetTitleByPattern(inTitle, inPattern: string; inNum, inPosition: integer; inPublishedDate, inChannelTitle: string): string;
var lPattern: string;
lNum0, lPosition0: string;
lTitleFormat: string;
lPublishedDate: string;
begin
lTitleFormat := inTitle;
lTitleFormat := ReplaceStrI(lTitleFormat, gpReplaceFrom, gpReplaceTo);
lTitleFormat := ReplaceStrI(lTitleFormat, gpReplaceFrom2, gpReplaceTo2);
lTitleFormat := ReplaceStrI(lTitleFormat, gpReplaceFrom3, gpReplaceTo3);
lPattern := inPattern;
if trim(lPattern)<>'' then
begin
lNum0 := PadLeft(IntToStr(inNum),length(gpMaxResults),'0'); //lShowNum := FormatFloat('000',inNum+1)+' ';
lPosition0 := PadLeft(IntToStr(inPosition),length(gpMaxResults),'0');
lPattern := ReplacePattern(lPattern, 'title', lTitleFormat);
lPublishedDate := GetPublishedDate(inPublishedDate);
lPattern := ReplaceStr(lPattern, '{published}', Copy(lPublishedDate,1,10));
lPattern := ReplaceStr(lPattern, '{pubyear}', Copy(lPublishedDate,7,4));
lPattern := ReplaceStr(lPattern, '{pubyear2}', Copy(lPublishedDate,9,2));
lPattern := ReplaceStr(lPattern, '{pubmonth}', Copy(lPublishedDate,4,2));
lPattern := ReplaceStr(lPattern, '{pubday}', Copy(lPublishedDate,1,2));
lPattern := ReplaceStr(lPattern, '{num0}', lNum0);
lPattern := ReplaceStr(lPattern, '{num}', IntToStr(inNum));
lPattern := ReplaceStr(lPattern, '{position0}', lPosition0);
lPattern := ReplaceStr(lPattern, '{position}', IntToStr(inPosition));
lPattern := ReplaceStr(lPattern, '{channel}', inChannelTitle);
//Если стоит параметр --delemptybrackets=on то удаляем пустые круглые и квадратные скобки
if (gpDelEmptyBrackets='on') then
begin
lPattern := ReplaceStr(lPattern, '()', '');
lPattern := ReplaceStr(lPattern, '[]', '');
end;
lPattern := trimex(lPattern);
end
else
lPattern := lTitleFormat;
Result := lPattern;
end;
function GetSearch(inMediaItem: THmsScriptMediaItem):string;
var lsearch: string;
begin
lsearch := inMediaItem[mpiComment];
if pos(' --',lsearch)>0 then
lsearch := Copy(lsearch, 1, pos(' --', lsearch)-1);
lsearch := trim(lsearch);
//HmsRegExMatch('(.*) --.*', lsearch, lsearch);
//lsearch := HmsHttpEncode(lsearch); //Не работает
lsearch := ReplaceStr(lsearch, ' ', '+');
lsearch := ReplaceStr(lsearch, '&', '%26');
lsearch := ReplaceStr(lsearch, '/', '%2F');
lsearch := ReplaceStr(lsearch, '%', '%25');
//lsearch := ReplaceStr(lsearch, '+', '%2B');
Result := lsearch;
end;
//=================== Главная программа =======================================//
var s: string;
lurl: string;
lfilepath: string;
reSearch1: TRegExpr;
b: boolean;
desc: string;
lYtCode: string;
ltype, ltypeitems: string;
lcode, lsearch: string;
lStarsBackupText: string;
TempFolder: THmsScriptMediaItem;
begin
//if log = 'on' then
// HmsLogMessage(1,'TEST'); //Лог
//RaiseException('RaiseTEST'); //Исключение
//Exit; //Выход из программы
//b:= HmsRegExMatch2('user\/([^& \/]+)', mpFilePath, lcode, ltype);
//Exit;
InitParameters;
//Перед удалением сохраняем пользовательские рейтинги видео
BackupStarsToFile();
FolderItem.DeleteChildItems; //Удаление всех элементов
if (CheckApp(FolderItem)<=0) then
Exit;
if (mpTitle='') then
begin
addLogError('Ошибка: Заголовок (mpTitle) не должен быть пустым');
Exit;
end;
//Временное исправление старого механизма
if (mpComment='playlists') or (mpComment='channels') then
begin
if pos('/'+mpComment, mpFilePath)=0 then
FolderItem[mpiFilePath] := mpFilePath+'/'+mpComment;
FolderItem[mpiComment] := '';
end
else if (mpComment='playlist') or (mpComment='video') or (mpComment='channel') or (mpComment='user') then
begin
if pos(mpComment+'/', mpFilePath)<>1 then
FolderItem[mpiFilePath] := mpComment+'/'+mpFilePath;
FolderItem[mpiComment] := '';
end
else if (mpComment='channel') then
begin
if pos('channel/', mpFilePath)<>1 then
FolderItem[mpiFilePath] := 'user/'+mpFilePath;
FolderItem[mpiComment] := '';
end;
//===================== Определение типа ===================================//
ltype := '';
lcode := '';
lfilepath :=FolderItem[mpiFilePath];
lsearch := GetSearch(FolderItem);
if HmsRegExMatch('\/playlists$', lfilepath, lcode) then
begin
ltype := 'playlists';
end
else if HmsRegExMatch('\/channels$', lfilepath, lcode) then
begin
ltype := 'channels';
end
else if HmsRegExMatch('^\/video$', lfilepath, lcode) then
begin
ltype := 'search';
end
else if (HmsRegExMatch('channel\/([^& \/]+)', lfilepath, lcode))
or (HmsRegExMatch('^(UC[^& \/]+)', lfilepath, lcode)) then
begin
ltype := 'channel';
end
else if (HmsRegExMatch('user\/([^& \/]+)', lfilepath, lcode)) then
begin
ltype := 'user';
end
else if (HmsRegExMatch('list=([^& \/]+)', lfilepath, lcode))
or (HmsRegExMatch('^(PL[^& \/]+)', lfilepath, lcode))
or (HmsRegExMatch('playlist/(PL[^& \/]+)', lfilepath, lcode)) then
begin
ltype := 'playlist';
end
else if (HmsRegExMatch('\/watch\?v\=(\S{11})', lfilepath, lcode))
or (HmsRegExMatch('youtu\.be\/(\S{11})', lfilepath, lcode))
or (HmsRegExMatch('video\/([^& \/]+)', lfilepath, lcode)) then
begin
ltype := 'video';
end
else if (HmsRegExMatch('search_query=([^& \/]+)', lfilepath, lsearch)) then
begin
//http://www.youtube.com/results?filters=channel&lclk=channel&search_query=квн
if pos('filters=channel', lfilepath)>0 then
ltype := 'channels'
else if pos('filters=playlist', lfilepath)>0 then
ltype := 'playlists'
else
ltype := 'search';
end
else if (lfilepath<>'') then
begin
lcode := lfilepath;
ltype := 'user';
end
else
begin
ltype := 'search';
end;
addLog('Получение списка видео: '+ltype+' с кодом: '+lcode);
//================ Получение видео/плейлистов/каналов по типу ==============//
if (ltype='playlists') then
begin
CreatePlaylists(FolderItem, lfilepath, '');
end
else if (ltype='channels') then
begin
CreateChannels(FolderItem, lfilepath, '');
end
else if (ltype='user') then
begin
lurl := Format('http://storvild.ru/yt.php?type=video&channel=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&videoDimension=%s&gpVideoDefinition=%s&videoDuration=%s&relevanceLanguage=%s&app=%s',
[lcode, lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpVideoDimension, gpVideoDefinition, gpVideoDuration, gpRelevanceLanguage, VER]);
CreateVideoItems_By_Json(FolderItem, lurl, '', '', gpPattern, gpDopVideo, ltype, lcode, lsearch);
end
else if (ltype='channel') then
begin
lurl := Format('http://storvild.ru/yt.php?type=video&channelId=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&videoDimension=%s&gpVideoDefinition=%s&videoDuration=%s&relevanceLanguage=%s&app=%s',
[lcode, lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpVideoDimension, gpVideoDefinition, gpVideoDuration, gpRelevanceLanguage, VER]);
CreateVideoItems_By_Json(FolderItem, lurl, '', '', gpPattern, gpDopVideo, ltype, lcode, lsearch);
end
else if (ltype='playlist') then
begin
lurl := Format('http://storvild.ru/yt.php?type=video&playlistId=%s&q=%s&maxResults=%d&app=%s',
[lcode, lsearch, gpMaxResults, VER]);
CreateVideoItems_By_Json(FolderItem, lurl, '', '', gpPattern, gpDopVideo, ltype, lcode, lsearch);
end
else if (ltype='video') then
begin
lYtCode := lfilepath;
lurl := Format('http://storvild.ru/yt.php?videoId=%s&app=%s', [lcode, VER]);
CreateVideoItems_By_Json(FolderItem, lurl, '', '', gpPattern, gpDopVideo, ltype, lcode, '');
end
else if (ltype='search') then
begin
lurl := Format('http://storvild.ru/yt.php?type=video&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&videoDimension=%s&gpVideoDefinition=%s&videoDuration=%s&relevanceLanguage=%s&app=%s',
[lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpVideoDimension, gpVideoDefinition, gpVideoDuration, gpRelevanceLanguage, VER]);
CreateVideoItems_By_Json(FolderItem, lurl, '', '', gpPattern, gpDopVideo, ltype, lcode, lsearch);
end
else
begin
addLogError('Ошибка: Не определен тип ссылки...');
end;
//Сортировка
if FolderItem[mpiFolderSortOrder]<>null then
FolderItem.Sort(FolderItem[mpiFolderSortOrder]);
end.
531
PascalScript
550
// [GetLink] Скрипт получения ссылки на видео, а также "Следующая страница", "Дополнительные видео"
// Made By Vadim_S, http://storvild.ru
const
VER = 'HMSL.3.0.28.beta'; //Версия скрипта. Передается в запросах.
csSiteLink = 'HMS'; //Приложение. Передается как http_referer
INTERNET_FLAG_NO_COOKIES = $00080000; { no automatic cookie handling }
INTERNET_FLAG_NO_AUTO_REDIRECT = $00200000;
INTERNET_FLAG_RELOAD = $80000000;
mpiYoutubePageToken = 141206;
mpiYoutubeType = 141207;
mpiYoutubeCode = 141208;
mpiYoutubeUrl = 141209;
mpiYoutubeCurPage = 141210;
mpiYoutubeSearch = 141220;
mpiYoutubeTTSUrl = 141211;
mpiYoutubeVideoHeight = 141212;
mpiYoutubeVideoID = 141213;
mpiYoutubeViewCount = 141214;
mpiYoutubeDuration = 141215;
mpiYoutubeDescription = 141216;
mpiYoutubeChannelTitle= 141217;
mpiYoutubeChannelId = 141218;
mpiYoutubeRating = 141219;
// = 141221;
var
// Глобальные переменные
csStarsBackupFileName: string = HmsDataDirectory+'\hms_stars.backup';
gpSafeSearch: string = 'moderate'; //Показывать ли закрытый контент. none | moderate | strict
gpMaxResults: Integer = 50; //Кол-во результатов на страницу 1-50
gpOrderBy: string = 'date'; // date | rating | relevance | title | viewCount
gpPublishedAfter: string = ''; //Найти видео до указанной даты '2014-02-23T00:00:00Z'
gpPublishedBefore: string = ''; //Найти видео после указанной даты '2015-03-01T00:00:00Z'
gpRelevanceLanguage: string = ''; // Язык роликов ('ru', 'en')
gpVideoDimension: string = 'any'; // any | 2d | 3d
gpVideoDefinition: string = 'any'; // Качество (any | high | standard)
gpVideoDuration: string = 'any'; // Длина видео (any | long | medium | short)
gpReplaceFrom: string = '';
gpReplaceTo: string = '';
gpReplaceFrom2: string = '';
gpReplaceTo2: string = '';
gpReplaceFrom3: string = '';
gpReplaceTo3: string = '';
gpPattern: string = '[{num0}] {title} ({published})'; //num, title, published, year, month, day
gpPlaylistPattern: string = '';
gpChannelPattern: string = '';
gpDopVideo: string = 'off';
gpDopVideoPattern: string = '{num0} {title} ({published})';
gpDopVideoMaxResults: Integer = 25; //Максимальное кол-во доп.видео 1-50
gpDopVideoInFolder: string = 'on';
gpDelEmptyBrackets: string = 'on'; //Удаление пустых скобок (круглых и квадратных) образованных после замены по шаблону
gpMaxHeight: string = '1080'; //Без этого значения неправильно получается ссылка субтитров от WendyH
gpSubtitles: string = 'off';
gpSubLanguage: string = 'ru';
gpAdaptive: string = 'off';
gpGetFileSize: string = 'off';
gpGetTimeLength: string = 'on';
gpStarsBackup: string = 'on';
gpMethodGetLink: string = 'remote'; //Метод получения ссылки на видео, remote - с сайта, local - вычисление внутренним методом
log: string = 'off';
logError: string = 'on';
// Инициализация глобальных переменных из mpPodcastParameters
procedure InitParameters;
begin
gpMaxResults := StrToIntDef(GetPodcastParam('--maxresults', ''), gpMaxResults); //50
gpSafeSearch := GetPodcastParam('--safesearch', gpSafeSearch); // moderate (none | moderate | strict)
gpOrderBy := GetPodcastParam('--orderby', gpOrderBy); // date (date | rating | relevance | title | viewCount)
gpPublishedAfter := GetPodcastParam('--publishedafter', gpPublishedAfter); //Найти видео до указанной даты '2014-02-23T00:00:00Z'
gpPublishedBefore := GetPodcastParam('--publishedbefore', gpPublishedBefore); //Найти видео после указанной даты '2015-03-01T00:00:00Z'
gpRelevanceLanguage := GetPodcastParam('--relevancelanguage', gpRelevanceLanguage); // Язык роликов ('ru')
gpVideoDimension := GetPodcastParam('--videodimension', gpVideoDimension); // any | 2d | 3d
gpVideoDefinition := GetPodcastParam('--videodefinition', gpVideoDefinition); // Качество (any | high | standard)
gpVideoDuration := GetPodcastParam('--videoduration', gpVideoDuration); // Длина видео (any | long | medium | short)
gpReplaceFrom:= GetPodcastParam('--replacefrom', gpReplaceFrom); // ''
gpReplaceTo := GetPodcastParam('--replaceto', gpReplaceTo); // ''
gpReplaceFrom2:= GetPodcastParam('--replacefrom2', gpReplaceFrom2); // ''
gpReplaceTo2 := GetPodcastParam('--replaceto2', gpReplaceTo2); // ''
gpReplaceFrom3:= GetPodcastParam('--replacefrom3', gpReplaceFrom3); // ''
gpReplaceTo3 := GetPodcastParam('--replaceto3', gpReplaceTo3); // ''
gpPattern := GetPodcastParam('--pattern', gpPattern); // '[{num}] {title} {published}' (pubyear,pubmonth,pubday)
gpPlaylistPattern := GetPodcastParam('--playlistpattern', gpPlaylistPattern); // ''
gpChannelPattern := GetPodcastParam('--channelpattern', gpPlaylistPattern); // ''
gpDopVideo := GetPodcastParam('--dopvideo', gpDopVideo); // off (on | off)
gpDopVideoPattern := GetPodcastParam('--dopvideopattern', gpDopVideoPattern); // '[{parentnum}] {parenttitle,10} {num} {title}'
gpDelEmptyBrackets := GetPodcastParam('--delemptybrackets', gpDelEmptyBrackets); // on (on | off)
gpDopVideoInFolder := GetPodcastParam('--dopvideoinfolder', gpDopVideoInFolder); // on (on | off)
gpDopVideoMaxResults := StrToIntDef(GetPodcastParam('--dopvideomaxresults', ''), gpDopVideoMaxResults); //25
gpMaxHeight := GetPodcastParam('--maxheight', gpMaxHeight);
gpSubLanguage := GetPodcastParam('--sublanguage', gpSubLanguage);
gpSubtitles := GetPodcastParam('--subtitles', gpSubLanguage);
gpAdaptive := GetPodcastParam('--adaptive', gpAdaptive);
gpGetFileSize := GetPodcastParam('--getfilesize', gpGetFileSize);
gpGetTimeLength := GetPodcastParam('--gettimelength', gpGetTimeLength);
gpStarsBackup := GetPodcastParam('--starsbackup', gpStarsBackup); // on (on | off)
gpMethodGetLink := GetPodcastParam('--methodgetlink', gpMethodGetLink); // remote
log := GetPodcastParam('--log', log); //off (on | off)
logError := GetPodcastParam('--logerror',logError); //on (on | off)
end;
//=========================== Общие функции ==================================
//
// Получение параметра aParamName, а если его нет то подставляется значение по умолчанию aDefaultValue
function GetPodcastParam(const aParamName, aDefaultValue: string): string;
begin
Result := ExtractParam(mpPodcastParameters, aParamName);
if Result = '' then Result := aDefaultValue
end;
//Загрузка контента по указанному URL. Запрос преобразуется в Utf8, Ответ из Utf8
function DownloadUrl(const aUrl: string; const aUserAgent: string = ''): string;
var
iPort: Integer;
sHeader, sReferer, sServer, sObject, sProtocol: string;
begin
aUrl := HmsUtf8Encode(aUrl); //Преобразование ссылки в UTF-8
if HmsRegExMatch3('http(.?)://([^/]*)(/.*)?', aUrl, sProtocol, sServer, sObject) then
begin
if sObject = '' then sObject := '/';
if sProtocol = 's' then iPort := 443 else iPort := 80;
sReferer := csSiteLink + #13#10 + 'Accept: */*'#13#10'Accept-Encoding: gzip,deflate';
if aUserAgent <> '' then
sReferer := sReferer + #13#10'User-Agent: ' + aUserAgent;
Result := HmsSendRequestEx(sServer, sObject, 'GET', '', sReferer, '', iPort,
INTERNET_FLAG_NO_COOKIES or INTERNET_FLAG_RELOAD, sHeader, True);
if (Result <> '') and (Ord(Result[1]) = 31) then
Result := HmsDecompressString(Result);
end
else if Pos('http', aUrl) = 1 then
Result := HmsDownloadUrl(aUrl)
else
Result := '';
Result := HmsUtf8Decode(Result); //Преобразование результата из UTF-8
end;
//Регистронезависимая замена текста в строке
function ReplaceStrI(const cStr1, cSrch, cReplace: String): String;
var sLowerSrch, sTempStr, sResultStr: string;
i: integer;
begin
sResultStr := '';
sLowerSrch := Lowercase(cSrch);
sTempStr := cStr1;
while Pos(sLowerSrch, Lowercase(sTempStr))>0 do
begin
i := Pos(sLowerSrch, Lowercase(sTempStr));
sResultStr := sResultStr+Copy(sTempStr, 1, i-1)+cReplace;
Delete(sTempStr,1, i-1+Length(sLowerSrch));
end;
sResultStr := sResultStr+sTempStr;
Result := sResultStr;
end;
// Получение даты в форомате 31.12.2015 из 2015-12-31T00:00:00Z
function GetPublishedDate(const aValue: string): string;
begin
Result:=Copy(aValue,9,2)+'.'+Copy(aValue,6,2)+'.'+Copy(aValue,1,4);
end;
//Заменяет в шаблоне параметр в фигурных скобках {title}. Если параметр {title,10}, то укорачивает до 10 символов
function ReplacePattern(inPattern, inParamName, inParamValue: string): string;
var mparam, mparamcount_str: string;
mparamcount: Integer;
pvshort: string;
res: string;
lParamValue: string;
begin
//Если в шаблоне используется ограничение по размеру текста {title,10}
if HmsRegExMatch2('({'+inParamName+',\s*(\d+)})', inPattern, mparam, mparamcount_str) then
begin
res := inPattern;
lParamValue := inParamValue;
mparamcount := StrToInt(mparamcount_str);
if (mparamcount>=0) and (length(lParamValue)>mparamcount) then
lParamValue := Copy(lParamValue,1,mparamcount)+'...';
res := ReplaceStr(res, mparam, lParamValue);
end
else
begin
res := inPattern;
lParamValue := inParamValue;
//Замена параметра в фигурных скобках
res := ReplaceStr(res, '{'+inParamName+'}', lParamValue);
end;
Result := res;
end;
//Удаляет двойные пробелы и переводы строк (заменяет пробелами)
function trimex(inText: string): string;
begin
if (pos(' ',inText)>0) or (pos(#13,inText)>0) or (pos(#10,inText)>0) then
begin
Result := ReplaceStr(inText, ' ', ' ');
Result := ReplaceStr(Result, #13, ' ');
Result := ReplaceStr(Result, #10, ' ');
Result := trimex(Result);
end
else
Result := trim(inText);
end;
// Округление в большую сторону
function Ceil(inVal: Extended): Integer;
begin
Result:=Int(inVal);
if (Int(inVal)<inVal) then
Result := Result+1;
end;
//Округление до нужного знака
function RoundTo(inVal: Extended; inPrecision: Integer): Extended;
var lPow: Extended;
begin
// Result := Exp(Y*Ln(X)); // X^Y
lPow := Exp(inPrecision*Ln(10));
Result := Round(inVal*lPow)/lPow;
end;
//Преобразование секунд во время в формате: 00:01:31.000
function GetTimeLength(const aValue: string): string;
var
eDuration: Extended;
begin
eDuration := StrToFloatDef(aValue, 0);
if eDuration > 0 then
Result := FormatDateTime('HH:NN:SS.ZZZ', eDuration / (24 * 60 * 60))
else
Result := '';
end;
procedure addLog(inMsg: string; inTimeON: boolean = true);
var TimeStr: string = '';
begin
if (log='on') then
begin
if inTimeON then
TimeStr := FormatDateTime('HH:NN:SS.ZZZ',Now())+' ';
HmsLogMessage(1, TimeStr+' '+inMsg);
end;
end;
procedure addLogError(inMsg: string; inTimeON: boolean = true);
var TimeStr: string = '';
begin
if (logError='on') then
begin
if inTimeON then
TimeStr := FormatDateTime('HH:NN:SS.ZZZ',Now())+' ';
HmsLogMessage(2, TimeStr+' '+inMsg);
end;
end;
//======================= Фукции обработки папок и видео =====================//
// Получение реальной ссылки на видео и добавление субтитров // By WendyH
function GetLinkYoutube3: string;
var sData, sSubtitlesUrl, sFile, sVal, sMsg: string;
sVideoID: string = '';
sAudio: string = '';
JSON: TJsonObject;
bNotDE: boolean;
sLink: string;
sHeight: string;
begin
Result := '';
sLink := mpFilePath; //PodcastItem[mpiFilePath];
addLog('Начало получения (GetLinkYoutube3): '+PodcastItem[mpiTitle]);
//Получаем VideoId из переданной ссылки sLink (mpFilePath)
if (not HmsRegExMatch('[\\?&]v=([^&]+)' , sLink, sVideoID)) then
HmsRegExMatch('/(?:embed|v)/([^\\?]+)', sLink, sVideoID);
if (sVideoID = '') then
begin
addLogError('Невозможно получить Video ID в ссылке Youtube');
Exit;
end;
//Быстрое получение, без настроек
//Result := HmsDownloadUrl('http://hms.lostcut.net/youtube/g.php?link_only=1&v='+sVideoID);
//Exit;
bNotDE := (Pos('notde=1', sLink)>0);
sLink := 'http://hms.lostcut.net/youtube/g.php?v='+sVideoID;
if (gpMaxHeight<>'' ) then sLink := sLink+'&max_height='+gpMaxHeight;
if (Trim(mpPodcastMediaFormats )<>'') then sLink := sLink+'&media_formats='+mpPodcastMediaFormats;
if (gpAdaptive='on' ) then sLink := sLink+'&adaptive=1';
if (bNotDE ) then sLink := sLink+'¬de=1';
sData := HmsDownloadUrl(sLink);
// При ошибке в json - выдавать
if (HmsRegExMatch('"reason":"(.*?)"' , sData, sMsg)) then
begin
addLog(sMsg);
Exit;
end;
JSON := TJsonObject.Create();
try
JSON.LoadFromString(sData);
Result := JSON.S['url'];
sSubtitlesUrl := JSON.S['ttsUrl'];
sAudio := JSON.S['audio'];
sHeight := JSON.S['height'];
PodcastItem[mpiYoutubeTTSUrl] := sSubtitlesUrl;
PodcastItem[mpiYoutubeVideoHeight] := StrToIntDef(sHeight,0);
if (sAudio<>'') then
Result := Format('-i "%s" -i "%s"', [Result, sAudio]);
finally
JSON.Free();
end;
if (Pos('m3u8', Result)>0) then
Result := ' '+Trim(Result);
addLog('Получено видео '+sHeight+'p'); // url:'+Result);
// Если есть субтитры и в дополнительных параметрах указано их показывать - загружаем
if ((gpSubtitles='on') and (sSubtitlesUrl<>'')) then
begin
sFile := HmsSubtitlesDirectory+'\Youtube\'+PodcastItem.ItemID+'.'+gpSubLanguage+'.srt';
sLink := sSubtitlesUrl+'&format=srt&lang=';
addLog('Субтитры: '+sLink+gpSubLanguage);
if (not HmsDownloadURLToFile(sLink+gpSubLanguage, sFile, 'Accept-Encoding: gzip, deflate')) then
begin
//Если не найдены субтитры с необходимым языком загружать английские
HmsDownloadURLToFile(sLink+'en', sFile, 'Accept-Encoding: gzip, deflate');
end;
addLog('Субтитры сохранены в : '+sFile);
PodcastItem[mpiSubtitleLanguage] := sFile;
end;
end;
function GetStarsFromBackup(inYtCode, inText: string): integer;
var lExistStar: string;
begin
Result := NULL;
if HmsRegExMatch(inYtCode+'=(\d+)', inText, lExistStar) then
begin
Result := StrToInt(lExistStar);
end;
end;
//Создание списка видео в указанной папке по параметрам
// inType, inCode используются для занесения в раздел "Следующая страница"
procedure CreateVideoItems_By_Json(inParentFolder: THmsScriptMediaItem; inUrl, inPattern: string; inType: string = ''; inCode: string = ''; inSearch: string = '');
var MainObj, EntryObj, ItemObj: TJsonObject;
i,j,k,n: integer;
lurl: string;
lYtCode, lYtLink, lTitle, lPublishedDate, lDuration, lThumbnail, lViewCount,
lChannelTitle, lChannelId, lNumRaters, lDescription, lFirstReleased, lYear: string;
lShowNum: string = '';
MediaItem: THmsScriptMediaItem;
lPattern: string;
lComment: string;
lTitleFormat: string;
ldownloadStr: string;
lNextFolder: THmsScriptMediaItem;
lNextPageToken: string;
lItemsPerPage, lItemsCount, lPageCount, lNextPage, lCurPage: integer;
lStarsBackupText: string;
lNum, lPosition: integer;
lPublishedDateFormat: string;
begin
MainObj := TJsonObject.Create();
lurl := inUrl;
if gpStarsBackup='on' then
begin
lStarsBackupText := HmsStringFromFile(csStarsBackupFileName);
end;
try
ldownloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(ldownloadStr);
EntryObj := MainObj.O['items'];
//Папка "Следующая страница"
lNextPageToken := MainObj.S['nextPageToken'];
lItemsPerPage := MainObj.I['itemsPerPage'];
lItemsCount := MainObj.I['itemsCount'];
lPageCount := Ceil(lItemsCount/lItemsPerPage);
lNextPage := inParentFolder[mpiYoutubeCurPage]+1;
lCurPage := inParentFolder[mpiYoutubeCurPage];
if lNextPageToken<>'' then
begin
if (inSearch='') or (EntryObj.AsArray.Length>=gpMaxResults) then
begin
lNextFolder := inParentFolder.AddFolder(mpFilePath+'_');
lNextFolder[mpiFilePath] := inParentFolder[mpiFilePath];
lNextFolder[mpiComment] := inParentFolder[mpiComment];
lNextFolder[mpiTitle] := 'Следующая страница '+IntToStr(lNextPage)+'/'+IntToStr(lPageCount);
lNextFolder[mpiFolderSortOrder] := inParentFolder[mpiFolderSortOrder];
lNextFolder[mpiPartNo] := 1;
lNextFolder[mpiThumbnail] := '';
lNextFolder[mpiYoutubePageToken] := lNextPageToken;
lNextFolder[mpiYoutubeType] := inType;
lNextFolder[mpiYoutubeCode] := inCode;
lNextFolder[mpiYoutubeSearch] := inSearch;
lNextFolder[mpiYoutubeUrl] := inUrl;
lNextFolder[mpiYoutubeCurPage] := IntToStr(lNextPage);
if log='on' then
lNextFolder[mpiComment] := lNextFolder[mpiComment]+' --pageToken='+lNextPageToken;
end;
end;
for i:=0 to EntryObj.AsArray.Length-1 do
begin
ItemObj := EntryObj.AsArray.O[i];
lYtCode := ItemObj.S['videoId'];
if lYtCode<>'' then
begin
lYtLink := 'http://www.youtube.com/watch?v='+lYtCode;
lPublishedDate := ItemObj.S['publishedAt'];
lPublishedDateFormat := GetPublishedDate(lPublishedDate);
lThumbnail := ItemObj.S['thumbnail'];
lChannelTitle := ItemObj.S['channelTitle'];
lChannelId := ItemObj.S['channelId'];
lDescription := ItemObj.S['description'];
lDuration := ItemObj.S['duration'];
lYear := Copy(ItemObj.S['publishedAt'],1,4);
lViewCount := ItemObj.S['viewCount'];
lComment := lDescription+#13#10+'Канал: '+lChannelTitle;
if lViewCount<>'' then
lComment := lComment+#13#10+'Кол-во просмотров на Youtube: '+lViewCount;
lNum := (lCurPage-1)*lItemsPerPage+i+1;
lPosition := i+1;
lShowNum := PadLeft(IntToStr(i+1),length(gpMaxResults),'0'); //lShowNum := FormatFloat('000',i+1)+' ';
lTitle := ItemObj.S['title'];
lTitleFormat := GetTitleByPattern(lTitle, inPattern, lNum, lPosition, lPublishedDate, lChannelTitle);
MediaItem := HmsCreateMediaItem(lYtLink, inParentFolder.ItemID);
MediaItem.Properties[mpiTitle] := lTitleFormat;
MediaItem.Properties[mpiCreateDate] := lPublishedDateFormat; //Now;
MediaItem.Properties[mpiComment] := lComment;
MediaItem.Properties[mpiThumbnail] := lThumbnail;
MediaItem.Properties[mpiTimeLength] := GetTimeLength(lDuration);
MediaItem.Properties[mpiPartNo] := lNum;
MediaItem.Properties[mpiPartTotal] := lItemsCount;
MediaItem.Properties[mpiYear] := lYear;
MediaItem.Properties[mpiProducer] := lChannelTitle;
MediaItem.Properties[mpiProgramID] := lViewCount;
MediaItem.Properties[mpiSeriesEpisodeTitle] := lTitle;
MediaItem.Properties[mpiSeriesEpisodeNo] := lNum;
MediaItem.Properties[mpiYoutubeChannelTitle] := lChannelTitle;
MediaItem.Properties[mpiYoutubeChannelId] := lChannelId;
MediaItem.Properties[mpiYoutubeDescription] := lDescription;
MediaItem.Properties[mpiYoutubeDuration] := StrToIntDef(lDuration,0);
MediaItem.Properties[mpiYoutubeViewCount] := StrToIntDef(lViewCount,0);
MediaItem.Properties[mpiYoutubeVideoID] := lYtCode;
if lStarsBackupText<>'' then
begin
MediaItem.Properties[mpiRatingInStars] := GetStarsFromBackup(lYtCode, lStarsBackupText);
end;
addLog('Добавлено видео: '+lYtCode);
end; //lYtCode<>''
end; //for
finally
MainObj.Free;
end;
end;
// Создание Доп.видео при нажатии на папку с этим дополнительным видео
procedure CreateDopVideo(inFolder: THmsScriptMediaItem);
var MainObj, EntryObj, ItemObj: TJsonObject;
i,j,k,n: integer;
lUrl: string;
lEpisodeNum: string;
lDescription: string;
lCurYtCode, lYtCode, lYtCodes: string;
lTitle, lPublishedDate, lDuration, lThumbnail, lViewCOunt: string;
MediaItem: THmsScriptMediaItem;
downloadStr: string;
re: TRegExpr;
begin
HmsRegExMatch('youtube.*v=([^& ]+)', VarToStr(inFolder[mpiFilePath]), lCurYtCode);
MainObj := TJsonObject.Create();
try
lurl := Format('http://storvild.ru/yt.php?videoId=%s&app=%s', [lCurYtCode, VER]);
downloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(downloadStr);
EntryObj := MainObj.O['items'];
ItemObj := EntryObj.AsArray.O[0];
lDescription := ItemObj.S['description'];
lYtCodes := '';
n := 0;
re := TRegExpr.Create('youtube\.com\/watch\?v\=(\S{11})|youtu\.be\/(\S{11})');
try
if re.Search(lDescription) then
repeat
n := n+1;
lYtCode := re.Match(1);
if (lYtCode='') then
lYtCode := re.Match(2);
lYtCodes := lYtCodes+','+lYtCode; //Собираем все коды доп.видео, чтобы получить сразу их все
if (n>=gpDopVideoMaxResults) then
break;
until not re.SearchAgain
finally
re.Free;
end;
addLog('Получение доп. видео: '+lYtCodes+' ('+lCurYtCode+')');
if (lYtCodes<>'') then
begin
lUrl := Format('http://storvild.ru/yt.php?videoId=%s&app=%s', [lYtCodes, VER]);
CreateVideoItems_By_Json(inFolder, lUrl, gpDopVideoPattern, 'video', lYtCodes);
end;
finally
MainObj.Free;
end;
end;
// Создание папок для Доп.видео (без самих видео)
procedure CreateDopVideoFolders(inFolder: THmsScriptMediaItem);
var lDopFolder: THmsScriptMediaItem;
MainObj, EntryObj, ItemObj: TJsonObject;
i,j,k,n: integer;
lUrl: string;
downloadStr: string;
re: TRegExpr;
lDescription: string;
lCurYtVideos, lYtCode, lYtCodes: string;
lTitle: string;
lParentFolder: THmsScriptMediaItem;
lMediaItem, newFolder: THmsScriptMediaItem;
begin
lParentFolder := inFolder.ItemParent;
for i:=0 to lParentFolder.ChildCount-1 do
begin
if not lParentFolder.ChildItems[i].IsFolder then
begin
lMediaItem := lParentFolder.ChildItems[i];
if (gpDopVideoInFolder='on') then
begin
newFolder := inFolder.AddFolder(lMediaItem[mpiFilePath]);
newFolder[mpiTitle] := lMediaItem[mpiTitle];
//newFolder[mpiComment] := '#dopvideo#';
newFolder[mpiYoutubeCurPage] := '1';
newFolder[mpiYoutubeType] := 'dopvideo';
end;
end;
end;
end;
// Создание списка видео в указанной папке в зависимости от типа и кода
procedure CreateVideos(inFolder: THmsScriptMediaItem; inType, inCode, inSearch, inPageToken: string);
var lurl: string;
lDopFolder: THmsScriptMediaItem;
begin
addLog('Получение списка видео: '+inType+' с кодом: '+inCode+' и поиском "'+inSearch+'" pageToken='+inPageToken);
if inCode=NULL then
inCode := '';
if (inType='channel') then
begin
lurl := Format('http://storvild.ru/yt.php?type=video&channelId=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&videoDimension=%s&gpVideoDefinition=%s&videoDuration=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[inCode, inSearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpVideoDimension, gpVideoDefinition, gpVideoDuration, gpRelevanceLanguage, inPageToken, VER]);
CreateVideoItems_By_Json(inFolder, lurl, gpPattern, inType, inCode, inSearch);
end
else if (inType='user') then
begin
lurl := Format('http://storvild.ru/yt.php?type=video&channel=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&videoDimension=%s&gpVideoDefinition=%s&videoDuration=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[inCode, inSearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpVideoDimension, gpVideoDefinition, gpVideoDuration, gpRelevanceLanguage, inPageToken, VER]);
CreateVideoItems_By_Json(inFolder, lurl, gpPattern, inType, inCode, inSearch);
end
else if (inType='playlist') then
begin
lurl := Format('http://storvild.ru/yt.php?type=video&playlistId=%s&q=%s&maxResults=%d&pageToken=%s&app=%s',
[inCode, inSearch, gpMaxResults, inPageToken, VER]);
CreateVideoItems_By_Json(inFolder, lurl, gpPattern, inType, inCode, inSearch);
end
else if (inType='video') then
begin
lurl := Format('http://storvild.ru/yt.php?videoId=%s&pageToken=%s&app=%s',
[inCode, inPageToken, VER]);
CreateVideoItems_By_Json(inFolder, lurl, gpPattern, inType, inCode, inSearch);
end
else if (inType='search') then
begin
lurl := Format('http://storvild.ru/yt.php?type=video&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&videoDimension=%s&gpVideoDefinition=%s&videoDuration=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[inSearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpVideoDimension, gpVideoDefinition, gpVideoDuration, gpRelevanceLanguage, inPageToken, VER]);
CreateVideoItems_By_Json(inFolder, lurl, gpPattern, inType, inCode, inSearch);
end
else
begin
addLogError('Ошибка: Не определен тип ссылки...');
end;
//Создание папки Домолнительные материалы
if (gpDopVideo='on') then
begin
lDopFolder := inFolder.AddFolder('Дополнительные видео '+IntToStr(inFolder[mpiYoutubeCurPage]));
lDopFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
//lDopFolder[mpiYoutubeCurPage] := inFolder[mpiYoutubeCurPage];
end;
end;
// Получение размера файла //By WendyH
function UpdateVideoFileSize: integer;
var
sServ, sLink, sHeaders: string; iPort: Integer;
begin
iPort := 80;
if LeftCopy(Trim(MediaResourceLink), 5)='https' then
iPort := 443;
if HmsRegExMatch2('//(.*?)(/.*)', MediaResourceLink, sServ, sLink) then
begin
HmsSendRequestEx(sServ, sLink, 'HEAD', '', '', '', iPort, 0, sHeaders, true);
HmsRegExMatch('Content-Length: (\d+)' , sHeaders, PodcastItem[mpiFileSize]);
HmsRegExMatch('Content-Type: (.*?)'#13, sHeaders, PodcastItem[mpiMimeType]);
Result := StrToIntDef(PodcastItem[mpiFileSize],0);
end;
end;
function GetVideoId(inPodcastItem: THmsScriptMediaItem): string;
var sVideoId: string;
begin
sVideoId := VarToStr(inPodcastItem[mpiYoutubeVideoID]);
if (sVideoId = '') and (HmsRegExMatch('/watch\?v=([^&]+)', inPodcastItem[mpiFilePath], sVideoID)) then
inPodcastItem[mpiYoutubeVideoID] := sVideoID;
Result := sVideoId;
end;
// Получение информации о видео в виде строки
function GetVideoInfo(inVideoId: string): string;
var csMobileUserAgent, csGetVideoInfoLink: string;
sVideoInfo: string;
begin
csMobileUserAgent := 'Mozilla/5.0 (iPad; CPU OS 6_1_3 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25';
csGetVideoInfoLink := 'https://www.youtube.com/get_video_info?eurl=%s&video_id=%s&el=embedded&sts=16100&has_verified=1';
sVideoInfo := DownloadUrl(Format(csGetVideoInfoLink, [HmsHttpEncode('http://m.youtube.com/watch?v='+inVideoId), inVideoId]));
Result := sVideoInfo;
end;
// Обновление доп.полей из информации inVideoInfo (viewCount, Rating, Author, ChannelId)
function UpdateFromVideoInfo(inVideoInfo: string): boolean;
var lViewCount, lAvgRating, lChannelTitle, lChannelId: string;
begin
if HmsRegExMatch('view_count=([^&]*)', inVideoInfo, lViewCount) then
PodcastItem[mpiYoutubeViewCount] := StrToIntDef(lViewCount,0);
if HmsRegExMatch('avg_rating=([^&]*)', inVideoInfo, lAvgRating) then
PodcastItem[mpiYoutubeRating] := RoundTo(StrToFloatDef(lAvgRating,0),2);
if HmsRegExMatch('author=([^&]*)', inVideoInfo, lChannelTitle) then
PodcastItem[mpiYoutubeChannelTitle] := HmsUtf8Decode(HmsHttpDecode(lChannelTitle));
if HmsRegExMatch('ucid=([^&]*)', inVideoInfo, lChannelId) then
PodcastItem[mpiYoutubeChannelId] := HmsHttpDecode(lChannelId);
//PodcastItem.Properties[mpiRatingInStars] := Round(PodcastItem[mpiYoutubeRating]); //Рейтинг может проставлять пользователь
PodcastItem.Properties[mpiProducer] := HmsUtf8Decode(HmsHttpDecode(lChannelTitle));
PodcastItem.Properties[mpiProgramID] := lViewCount;
end;
//Обновление времени ролика из VideoInfo
function UpdateVideoTimeLength(inVideoInfo: string): integer;
var sTimeLength: string;
begin
if HmsRegExMatch('length_seconds=([^&]*)', inVideoInfo, sTimeLength) then
sTimeLength := HmsHttpDecode(sTimeLength);
if sTimeLength<>'' then
PodcastItem[mpiTimeLength] := GetTimeLength(sTimeLength);
Result := StrToIntDef(sTimeLength,0);
end;
//Обновление времени с сайта storvild.ru
function UpdateVideoTimeLength2: integer;
var MainObj, EntryObj, ItemObj: TJsonObject;
lurl, downloadStr: string;
lYtCode, lTimeLength: string;
begin
MainObj := TJsonObject.Create();
try
HmsRegExMatch('youtube.*v=([^& ]+)', PodcastItem[mpiFilePath], lYtCode);
lurl := Format('http://storvild.ru/yt.php?videoId=%s&app=%s', [lYtCode, VER]);
downloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(downloadStr);
EntryObj := MainObj.O['items'];
ItemObj := EntryObj.AsArray.O[0];
lTimeLength := ItemObj.S['duration'];
PodcastItem[mpiTimeLength] := GetTimeLength(lTimeLength);
Result := StrToIntDef(lTimeLength, 0);
finally
MainObj.Free;
end;
end;
procedure CreatePlaylists(inFolder: THmsScriptMediaItem; inFilePath: string; inPageToken: string);
var playlistsFolder: THmsScriptMediaItem;
lfilepath, lcode, lsearch, lcodeex: string;
lurl: string;
i: integer;
MainObj, EntryObj, ItemObj: TJsonObject;
ldownloadStr: string;
playlistId: string;
itemTitle, itemPlaylistId, itemChannelId, itemDescription, itemChannelTitle, itemPublished, itemThumbnail: string;
itemPosition: integer;
lNextPageToken: string;
lItemsPerPage, lItemsCount, lPageCount, lNextPage, lCurPage: integer;
lNextFolder: THmsScriptMediaItem;
lYear: string;
lTitleFormat: string;
lNum, lPosition: integer;
begin
lurl := '';
lfilepath := inFolder[mpiFilePath];
lcode := '';
lsearch := GetSearch(inFolder);
if HmsRegExMatch('channel\/([^& \/]+)', lfilepath, lcode) then
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&channelId=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lcode, lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'channel/'+lcode;
end
else if Pos('UC',lfilepath)=1 then
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&channelId=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lfilepath, lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'channel/'+lfilepath;
end
else if HmsRegExMatch('user\/([^& \/]+)', lfilepath, lcode) then
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&channel=%s&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lcode, lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'user/'+lcode;
end
else if HmsRegExMatch('search_query=([^& \/]+)', lfilepath, lsearch) then
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'search/'+lcode;
end
else
begin
lurl := Format('http://storvild.ru/yt.php?type=playlist&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'search/'+lfilepath;
end;
MainObj := TJsonObject.Create();
try
ldownloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(ldownloadStr);
EntryObj := MainObj.O['items'];
//Папка "Следующая страница"
lNextPageToken := MainObj.S['nextPageToken'];
lItemsPerPage := MainObj.I['itemsPerPage'];
lItemsCount := MainObj.I['itemsCount'];
lPageCount := Ceil(lItemsCount/lItemsPerPage);
lNextPage := inFolder[mpiYoutubeCurPage]+1;
lCurPage := inFolder[mpiYoutubeCurPage];
if lNextPageToken<>'' then
begin
if (inFolder[mpiComment]='') or (EntryObj.AsArray.Length>=gpMaxResults) then
begin
lNextFolder := inFolder.AddFolder(mpFilePath+'-'+IntToStr(lPageCount)); //Если AddFolder('') то папка не создается. Одинаковые имена типа '-' не подходят для создания более одной папки
lNextFolder[mpiFilePath] := mpFilePath;
lNextFolder[mpiTitle] := 'Следующая страница '+IntToStr(lNextPage)+'/'+IntToStr(lPageCount);
lNextFolder[mpiComment] := lsearch;
lNextFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
lNextFolder[mpiThumbnail] := '';
lNextFolder[mpiYoutubePageToken] := lNextPageToken;
lNextFolder[mpiYoutubeType] := 'playlists';
lNextFolder[mpiYoutubeCode] := lcode;
lNextFolder[mpiYoutubeSearch] := lsearch;
lNextFolder[mpiYoutubeUrl] := inFilePath;
lNextFolder[mpiYoutubeCurPage] := IntToStr(lNextPage);
if log='on' then
lNextFolder[mpiComment] := lNextFolder[mpiComment]+' --NextPageToken'+lNextPageToken;
//lNextFolder[mpiComment] := VarToStr(lNextFolder[mpiYoutubeType])+'#'+VarToStr(lNextFolder[mpiYoutubeCode])+'#'+VarToStr(lNextFolder[mpiYoutubePageToken]);
end;
end;
for i:=0 to EntryObj.AsArray.Length-1 do
begin
ItemObj := EntryObj.AsArray.O[i];
itemTitle := ItemObj.S['title'];
itemPlaylistId := ItemObj.S['playlistId'];
itemChannelId := ItemObj.S['channelId'];
itemDescription := ItemObj.S['description'];
itemChannelTitle := ItemObj.S['channelTitle'];
itemThumbnail := ItemObj.S['thumbnail'];
itemPosition := ItemObj.I['position'];
itemPublished := ItemObj.S['publishedAt'];
lYear := Copy(itemPublished,1,4);
lNum := i+1+(lCurPage-1)*lItemsPerPage;
lPosition := itemPosition;
lTitleFormat := GetTitleByPattern(itemTitle, gpPlaylistPattern, lNum, lPosition, itemPublished, itemChannelTitle);
playlistsFolder := inFolder.AddFolder(itemPlaylistId);
playlistsFolder[mpiFilePath] := 'http://www.youtube.com/playlist?list='+itemPlaylistId; //'http://www.youtube.com/playlist?list=PLIw_h2Az1xWYQjB3csaArQGjhTU3wbCbN';
//playlistsFolder := inFolder.AddFolder('http://www.youtube.com/playlist?list='+itemPlaylistId);
playlistsFolder[mpiTitle] := lTitleFormat;
playlistsFolder[mpiComment] := itemDescription;
playlistsFolder[mpiYoutubeCurPage] := 1;
playlistsFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
playlistsFolder[mpiYoutubeType] := 'playlist';
playlistsFolder[mpiYoutubeCode] := itemPlaylistId;
playlistsFolder[mpiYoutubePageToken] := '';
playlistsFolder[mpiThumbnail] := itemThumbnail;
playlistsFolder[mpiYoutubeChannelTitle] := itemChannelTitle;
playlistsFolder[mpiYoutubeChannelId] := itemChannelId;
playlistsFolder[mpiYoutubeDescription] := itemDescription;
playlistsFolder[mpiPartNo] := lNum;
playlistsFolder[mpiPartTotal] := lItemsCount;
playlistsFolder[mpiYear] := lYear;
playlistsFolder[mpiProducer] := HmsUtf8Decode(HmsHttpDecode(itemChannelTitle));
playlistsFolder[mpiCreateDate] := GetPublishedDate(itemPublished);
end; //for
finally
MainObj.Free;
end;
end;
procedure CreateChannels(inFolder: THmsScriptMediaItem; inFilePath: string; inPageToken: string);
var channelsFolder: THmsScriptMediaItem;
lfilepath, lcode, lsearch, lcodeex: string;
lurl: string;
i: integer;
MainObj, EntryObj, ItemObj: TJsonObject;
ldownloadStr: string;
itemTitle, itemChannelId, itemDescription, itemChannelTitle, itemPublished, itemThumbnail: string;
itemPosition: integer;
lNextPageToken: string;
lItemsPerPage, lItemsCount, lPageCount, lNextPage, lCurPage: integer;
lNextFolder: THmsScriptMediaItem;
lYear: string;
lTitleFormat: string;
lNum, lPosition: integer;
begin
lurl := '';
lfilepath := inFilePath;
lcode := '';
lsearch := GetSearch(inFolder);
if HmsRegExMatch('search_query=([^& \/]+)', lfilepath, lsearch) then
begin
lurl := Format('http://storvild.ru/yt.php?type=channel&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'search/'+lcode;
end
else
begin
lurl := Format('http://storvild.ru/yt.php?type=channel&q=%s&maxResults=%d&order=%s&safeSearch=%s&publishedBefore=%s&publishedAfter=%s&relevanceLanguage=%s&pageToken=%s&app=%s',
[lsearch, gpMaxResults, gpOrderBy, gpSafeSearch, gpPublishedBefore, gpPublishedAfter, gpRelevanceLanguage, inPageToken, VER]);
//lcodeex := 'search/'+lfilepath;
end;
MainObj := TJsonObject.Create();
try
ldownloadStr := DownloadUrl(lurl);
MainObj.LoadFromString(ldownloadStr);
EntryObj := MainObj.O['items'];
//Папка "Следующая страница"
lNextPageToken := MainObj.S['nextPageToken'];
lItemsPerPage := MainObj.I['itemsPerPage'];
lItemsCount := MainObj.I['itemsCount'];
lPageCount := Ceil(lItemsCount/lItemsPerPage);
lNextPage := 2;
lCurPage := 1;
if lNextPageToken<>'' then
begin
lNextFolder := inFolder.AddFolder(mpFilePath+'-'+IntToStr(lPageCount)); //Если AddFolder('') то папка не создается. Одинаковые имена типа '-' не подходят для создания более одной папки
//lNextFolder[mpiFilePath] := mpFilePath;
lNextFolder[mpiFilePath] := '/channels';
lNextFolder[mpiComment] := lsearch;
lNextFolder[mpiTitle] := 'Следующая страница '+IntToStr(lNextPage)+'/'+IntToStr(lPageCount);
lNextFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
lNextFolder[mpiThumbnail] := '';
lNextFolder[mpiYoutubePageToken] := lNextPageToken;
lNextFolder[mpiYoutubeType] := 'channels';
lNextFolder[mpiYoutubeCode] := '';
lNextFolder[mpiYoutubeSearch] := lsearch;
lNextFolder[mpiYoutubeUrl] := inFilePath;
lNextFolder[mpiYoutubeCurPage] := IntToStr(lNextPage);
if log='on' then
lNextFolder[mpiComment] := lNextFolder[mpiComment]+ ' --NextPageToken='+lNextPageToken;
// lNextFolder[mpiComment] := VarToStr(lNextFolder[mpiYoutubeType])+'#'+VarToStr(lNextFolder[mpiYoutubeCode])+'#'+VarToStr(lNextFolder[mpiYoutubePageToken]);
end;
for i:=0 to EntryObj.AsArray.Length-1 do
begin
ItemObj := EntryObj.AsArray.O[i];
itemTitle := ItemObj.S['title'];
itemChannelId := ItemObj.S['channelId'];
itemDescription := ItemObj.S['description'];
itemChannelTitle := ItemObj.S['channelTitle'];
itemThumbnail := ItemObj.S['thumbnail'];
itemPosition := ItemObj.I['position'];
itemPublished := ItemObj.S['publishedAt'];
lYear := Copy(itemPublished,1,4);
lNum := i+1+(lCurPage-1)*lItemsPerPage;
lPosition := itemPosition;
lTitleFormat := GetTitleByPattern(itemTitle, gpChannelPattern, lNum, lPosition, itemPublished, itemChannelTitle);
channelsFolder := inFolder.AddFolder(itemChannelId);
channelsFolder[mpiFilePath] := 'http://www.youtube.com/channel/'+itemChannelId;
channelsFolder[mpiTitle] := lTitleFormat;
channelsFolder[mpiComment] := itemDescription;
channelsFolder[mpiYoutubeCurPage] := 1;
channelsFolder[mpiFolderSortOrder] := inFolder[mpiFolderSortOrder];
channelsFolder[mpiYoutubeType] := 'channel';
channelsFolder[mpiYoutubeCode] := itemChannelId;
channelsFolder[mpiYoutubePageToken] := '';
channelsFolder[mpiThumbnail] := itemThumbnail;
channelsFolder[mpiYoutubeChannelTitle] := itemChannelTitle;
channelsFolder[mpiYoutubeChannelId] := itemChannelId;
channelsFolder[mpiYoutubeDescription] := itemDescription;
channelsFolder[mpiPartNo] := lNum;
channelsFolder[mpiPartTotal] := lItemsCount;
channelsFolder[mpiYear] := lYear;
channelsFolder[mpiProducer] := HmsUtf8Decode(HmsHttpDecode(itemChannelTitle));
channelsFolder[mpiCreateDate] := GetPublishedDate(itemPublished);
end; //for
finally
MainObj.Free;
end;
end;
function GetTitleByPattern(inTitle, inPattern: string; inNum, inPosition: integer; inPublishedDate, inChannelTitle: string): string;
var lPattern: string;
lNum0, lPosition0: string;
lTitleFormat: string;
lPublishedDate: string;
begin
lTitleFormat := inTitle;
lTitleFormat := ReplaceStrI(lTitleFormat, gpReplaceFrom, gpReplaceTo);
lTitleFormat := ReplaceStrI(lTitleFormat, gpReplaceFrom2, gpReplaceTo2);
lTitleFormat := ReplaceStrI(lTitleFormat, gpReplaceFrom3, gpReplaceTo3);
lPattern := inPattern;
if trim(lPattern)<>'' then
begin
lNum0 := PadLeft(IntToStr(inNum),length(gpMaxResults),'0'); //lShowNum := FormatFloat('000',inNum+1)+' ';
lPosition0 := PadLeft(IntToStr(inPosition),length(gpMaxResults),'0');
lPattern := ReplacePattern(lPattern, 'title', lTitleFormat);
lPublishedDate := GetPublishedDate(inPublishedDate);
lPattern := ReplaceStr(lPattern, '{published}', Copy(lPublishedDate,1,10));
lPattern := ReplaceStr(lPattern, '{pubyear}', Copy(lPublishedDate,7,4));
lPattern := ReplaceStr(lPattern, '{pubyear2}', Copy(lPublishedDate,9,2));
lPattern := ReplaceStr(lPattern, '{pubmonth}', Copy(lPublishedDate,4,2));
lPattern := ReplaceStr(lPattern, '{pubday}', Copy(lPublishedDate,1,2));
lPattern := ReplaceStr(lPattern, '{num0}', lNum0);
lPattern := ReplaceStr(lPattern, '{num}', IntToStr(inNum));
lPattern := ReplaceStr(lPattern, '{position0}', lPosition0);
lPattern := ReplaceStr(lPattern, '{position}', IntToStr(inPosition));
lPattern := ReplaceStr(lPattern, '{channel}', inChannelTitle);
//Если стоит параметр --delemptybrackets=on то удаляем пустые круглые и квадратные скобки
if (gpDelEmptyBrackets='on') then
begin
lPattern := ReplaceStr(lPattern, '()', '');
lPattern := ReplaceStr(lPattern, '[]', '');
end;
lPattern := trimex(lPattern);
end
else
lPattern := lTitleFormat;
Result := lPattern;
end;
//=============== Локальное получение ссылки =================================//
function YoutubeMaxHeight(aFormat: Integer): Integer;
begin
case aFormat of
5, 36, 83: Result := 240;
6: Result := 270;
13, 17: Result := 144;
18, 34, 43, 82, 100: Result := 360;
35, 44, 101: Result := 480;
85: Result := 520;
22, 45, 84, 102, 120: Result := 720;
37, 46: Result := 1080;
38: Result := 3072
else
Result := 0
end
end;
function YoutubeDecryptClone(const aText: string; aStart: Integer): string;
begin
Result := Copy(aText, aStart + 1, Length(aText))
end;
function YoutubeDecryptReverse(const aText: string) : string;
var
i: Integer;
begin
Result := '';
for i := Length(aText) downto 1 do
Result := Result + aText[i]
end;
function YoutubeDecryptSwap(const aText: string; aPosition: Integer) : string;
var
C: Char;
begin
aPosition := (aPosition mod Length(aText)) + 1;
Result := aText; C := aText[1];
Result[1] := aText[aPosition];
Result[aPosition] := C;
end;
function YoutubeDecrypt(const S: string): string;
begin
if S <> '' then begin
Result := YoutubeDecryptClone(S, 2);
Result := YoutubeDecryptSwap(Result, 36);
Result := YoutubeDecryptClone(Result, 1);
Result := YoutubeDecryptReverse(Result);
Result := YoutubeDecryptSwap(Result, 18);
Result := YoutubeDecryptReverse(Result);
Result := YoutubeDecryptSwap(Result, 19);
Result := YoutubeDecryptReverse(Result);
end else
Result := ''
end;
function GetMediaResourceLink(const aVideoInfo: string): string;
var
sVideoSign: string;
begin
if Pos('url=', aVideoInfo) > 0 then begin
Result := ' ' + HmsHttpDecode(ExtractParam(aVideoInfo, 'url', '', '&'));
if Pos('&signature=', Result) = 0 then begin
sVideoSign := HmsHttpDecode(ExtractParam(aVideoInfo, 'sig', '', '&'));
if sVideoSign = '' then
sVideoSign := YoutubeDecrypt(HmsHttpDecode(ExtractParam(aVideoInfo, 's', '', '&')));
if sVideoSign <> '' then
Result := Result + '&signature=' + sVideoSign
end
end else
Result := '';
end;
function CreateHlsVideoLinks(const aHlsUrl: string): string;
var
bFolderMode, bUsePriority: Boolean;
i, iMinPriority, iHeight, iMaxHeight, iPriority: Integer;
sMediaResourceLink, sVideoLinks: string;
begin
addLog('Начало получения (CreateHlsVideoLinks): '+PodcastItem[mpiTitle]);
Result := '';
bFolderMode := PodcastItem.IsFolder;
iMinPriority := 100; iMaxHeight := -1;
sVideoLinks := DownloadUrl(aHlsUrl);
with TRegExpr.Create('BANDWIDTH=(\d+).*?RESOLUTION=(\d+)x(\d+).*?(http[^#]*)', PCRE_SINGLELINE) do try
if Search(sVideoLinks) then
repeat
sMediaResourceLink := ' ' + Match(4);
iHeight := StrToIntDef(Match(3), 0);
if (iHeight > iMaxHeight) and (iHeight <= StrToIntDef(gpMaxHeight,1080)) then
begin
iMaxHeight := iHeight;
Result := sMediaResourceLink;
end
until not SearchAgain
finally
Free
end
end;
function ReplaceSign(const aUrl: string): string;
begin
Result := aUrl;
with TRegExpr.Create('\/s\/(.*?)\/') do try
if Search(aUrl) then begin
Replace('/signature/' + YoutubeDecrypt(Match) + '/');
Result := Subject
end
finally
Free
end
end;
function FindBestDashLink(aAdaptationSet: TXMLItem): string;
var
i, iBandWidth, iMaxBandWidth: Integer;
RepresentationItem: TXMLItem;
begin
Result := ''; iMaxBandWidth := 0;
for i := 0 to aAdaptationSet.Count - 1 do begin
RepresentationItem := aAdaptationSet.Items[i];
if SameText(RepresentationItem.Name, 'Representation') then begin
iBandWidth := StrToIntDef(RepresentationItem.Attributes['bandwidth'], 0);
if iBandWidth > iMaxBandWidth then begin
iMaxBandWidth := iBandWidth;
Result := RepresentationItem.ChildValues['BaseURL']
end
end
end
end;
const
csDashMediaResourceLink = '-i "%s" -i "%s"';
function CreateDashVideoLinks(const aDashMpdLink: string): string;
var
bFolderMode, bUsePriority: Boolean;
AdaptationSetItem, AudioAdaptationSetItem, VideoAdaptationSetItem,
PeriodItem, RepresentationItem: TXMLItem;
i, iHeight, iMinPriority, iMaxHeight, iPriority: Integer;
MpdInfo: TXMLDocument;
sAudioLink, sBaseUrl, sMediaResourceLink, sMimeType, sSign, sVideoLink: string;
begin
addLog('Начало получения (CreateDashVideoLinks): '+PodcastItem[mpiTitle]);
Result := '';
MpdInfo := TXMLDocument.Create;
try
MpdInfo.LoadFromString(DownloadUrl(ReplaceSign(aDashMpdLink), ''));
PeriodItem := MpdInfo.Root.Find('Period');
if PeriodItem <> nil then begin
AudioAdaptationSetItem := nil; VideoAdaptationSetItem := nil;
for i := 0 to PeriodItem.Count - 1 do begin
AdaptationSetItem := PeriodItem.Items[i];
if SameText(AdaptationSetItem.Name, 'AdaptationSet') then begin
sMimeType := LowerCase(AdaptationSetItem.Attributes['mimeType']);
if Pos('video/', sMimeType) = 1 then begin
if VideoAdaptationSetItem = nil then
VideoAdaptationSetItem := AdaptationSetItem
end else if Pos('audio/', sMimeType) = 1 then begin
if AudioAdaptationSetItem = nil then
AudioAdaptationSetItem := AdaptationSetItem
end
end
end;
if (VideoAdaptationSetItem <> nil) and (AudioAdaptationSetItem <> nil) then begin
sAudioLink := FindBestDashLink(AudioAdaptationSetItem);
if sAudioLink <> '' then begin
iMinPriority := 100; iMaxHeight := -1;
for i := 0 to VideoAdaptationSetItem.Count - 1 do
begin
RepresentationItem := VideoAdaptationSetItem.Items[i];
if SameText(RepresentationItem.Name, 'Representation') then
begin
sVideoLink := RepresentationItem.ChildValues['BaseURL'];
if sVideoLink <> '' then
begin
sMediaResourceLink := Format(csDashMediaResourceLink, [sVideoLink, sAudioLink]);
iHeight := StrToIntDef(RepresentationItem.Attributes['height'], 0);
if (iHeight > iMaxHeight) and (iHeight <= StrToIntDef(gpMaxHeight,1080)) then
begin
iMaxHeight := iHeight;
Result := sMediaResourceLink;
end
end //sVideoLink
end
end //for
end
end
end
finally
MpdInfo.Free
end
end;
function CreateMobileVideoLinks(): string;
var
bFolderMode, bUsePriority: Boolean;
i, iMinPriority, iHeight, iMaxHeight, iPriority, iVideoFormat: Integer;
sHlsUrl, sMediaResourceLink, sPlayerConfig, sStreamMap, sStreamInfo, sTtsUrl,
sVideo3D, sVideoID, sVideoInfo, sVideoLink, sVideoPlayer, sVideoSign, sVideoType: string;
FmtStreamMap, StreamInfo, VideoInfo: TJsonObject;
csMobileUserAgent: string;
begin
addLog('Начало получения (CreateMobileVideoLinks): '+PodcastItem[mpiTitle]);
csMobileUserAgent := 'Mozilla/5.0 (iPad; CPU OS 6_1_3 like Mac OS X) AppleWebKit/536.26 (KHTML, like Gecko) Version/6.0 Mobile/10B329 Safari/8536.25';
sVideoID := GetVideoId(PodcastItem);
if sVideoID <> '' then
begin
if HmsRegExMatch('({.*})', DownloadUrl('http://m.youtube.com/watch?ajax=1&ipadtype=3&sts=16100&layout=tablet&v=' + sVideoID + '&has_verified=1', csMobileUserAgent), sVideoInfo) then begin
VideoInfo := TJsonObject.Create;
try
VideoInfo.LoadFromString(sVideoInfo); sTtsUrl := '';
FmtStreamMap := VideoInfo.O['content\video\fmt_stream_map'];
if FmtStreamMap = nil then
FmtStreamMap := VideoInfo.O['content\player_data\fmt_stream_map'];
if (FmtStreamMap <> nil) and (FmtStreamMap.DataType = jtArray) then begin
{if gbSubtitles then
begin
sVideoInfo := VideoInfo.S['content\player_data\desktop_get_video_info'];
if (Pos('has_cc=True', sVideoInfo) > 0) and (HmsRegExMatch('ttsurl=(.*?)&', sVideoInfo, sTtsUrl)) then
LoadSubtitles(GetSubtitlesUrl(HmsHttpDecode(sTtsUrl)))
end;
}
bFolderMode := PodcastItem.IsFolder;
bUsePriority := mpPodcastMediaFormats <> '';
iMinPriority := 100; iMaxHeight := -1;
for i := 0 to FmtStreamMap.AsArray.Length - 1 do begin
StreamInfo := FmtStreamMap.AsArray.O[i];
sVideoType := StreamInfo.AsObject.S['type'];
iVideoFormat := StrToIntDef(StreamInfo.AsObject.S['itag'], 0);
sVideo3D := StreamInfo.AsObject.S['stered3d'];
if sVideo3D = '1' then sVideo3D := '[3D]';
sMediaResourceLink := StreamInfo.AsObject.S['url'];
if sMediaResourceLink <> '' then begin
sMediaResourceLink := ' ' + sMediaResourceLink;
sVideoSign := StreamInfo.AsObject.S['sig'];
if sVideoSign <> '' then
sMediaResourceLink := sMediaResourceLink + '&signature=' + YoutubeDecrypt(sVideoSign);
if (Pos('flv', sVideoType) > 0) or (Pos('mp4', sVideoType) > 0) then begin
iHeight := YoutubeMaxHeight(iVideoFormat);
if bUsePriority then begin
iPriority := HmsMediaFormatPriority(iHeight, mpPodcastMediaFormats);
if (iPriority <> -1) and (iPriority < iMinPriority) then begin
iMinPriority := iPriority;
Result := sMediaResourceLink
end
end else if (iHeight > iMaxHeight) and (iHeight <= StrToIntDef(gpMaxHeight,1080)) then begin
iMaxHeight := iHeight;
Result := sMediaResourceLink;
end
end
end
end
end else
begin
sHlsUrl := VideoInfo.S['content\player_data\stream_url'];
//if sHlsUrl <> '' then CreateHlsVideoLinks(sHlsUrl) //Пока закомментарено
end
finally
VideoInfo.Free
end
end
end
end;
function GetLinkYoutube3Local(inVideoInfo: string): string;
var
bFolderMode, bSuccess, bUsePriority: Boolean;
i, iMinPriority, iHeight, iMaxHeight, iPriority, iVideoFormat: Integer;
sDashMpdLink, sMediaResourceLink, sPlayerConfig, sStreamMap, sStreamInfo,
sHlsUrl, sTtsUrl, sVideo3D, sVideoID, sVideoInfo, sVideoLink, sVideoType: string;
sHlsVideoLink, sDashVideoLink: string;
begin
sVideoID := GetVideoId(PodcastItem);
sStreamMap := '';
bSuccess := False;
sTtsUrl := '';
sDashMpdLink := '';
sHlsUrl := '';
if sVideoID <> '' then
begin
sVideoInfo := inVideoInfo;
if (Pos('has_cc=True', sVideoInfo) > 0) and (HmsRegExMatch('ttsurl=([^&]*)', sVideoInfo, sTtsUrl)) then
sTtsUrl := HmsHttpDecode(sTtsUrl);
if HmsRegExMatch('dashmpd=([^&]*)', sVideoInfo, sDashMpdLink) then
sDashMpdLink := HmsHttpDecode(sDashMpdLink);
if HmsRegExMatch('url_encoded_fmt_stream_map=([^&]*)', sVideoInfo, sStreamMap) then
sStreamMap := HmsHttpDecode(sStreamMap);
if HmsRegExMatch('hlsvp=([^&]*)', sVideoInfo, sHlsUrl) then
sHlsUrl := HmsHttpDecode(sHlsUrl);
end;
if (sStreamMap = '') and (sDashMpdLink = '') and (sHlsUrl = '') then begin
if sVideoID <> '' then
sVideoLink := 'http://www.youtube.com/watch?v=' + sVideoID
else
sVideoLink := mpFilePath;
sVideoLink := sVideoLink + '&has_verified=1&bpctr=' +
IntToStr(DateTimeToTimeStamp1970(Now + EncodeTime(2, 30, 0, 0), False));
if HmsRegExMatch('ytplayer.config.*?({.*?});', DownloadUrl(sVideoLink), sPlayerConfig) then begin
with TJsonObject.Create do try
LoadFromString(sPlayerConfig);
sDashMpdLink := S['args\dashmpd'];
sStreamMap := S['args\url_encoded_fmt_stream_map'];
sHlsUrl := S['args\hlsvp'];
if S['args\cc_asr'] = '1' then
sTtsUrl := S['args\ttsurl']
finally
Free
end
end
end;
if sStreamMap <> '' then
begin
addLog('Начало получения (GetLinkYoutube3Local): '+PodcastItem[mpiTitle]);
bFolderMode := PodcastItem.IsFolder;
bUsePriority := mpPodcastMediaFormats <> '';
i := 1; iMinPriority := 100; iMaxHeight := -1;
while i <= Length(sStreamMap) do begin
sStreamInfo := Trim(ExtractStr(sStreamMap, ',', i));
sVideoType := HmsHttpDecode(ExtractParam(sStreamInfo, 'type', '', '&'));
iVideoFormat := StrToIntDef(ExtractParam(sStreamInfo, 'itag', '', '&'), 0);
sVideo3D := ExtractParam(sStreamInfo, 'stereo3d', '', '&');
if sVideo3D = '1' then sVideo3D := '[3D]';
sMediaResourceLink := GetMediaResourceLink(sStreamInfo);
if sMediaResourceLink <> '' then begin
bSuccess := True;
if (Pos('flv', sVideoType) > 0) or (Pos('mp4', sVideoType) > 0) then
begin
iHeight := YoutubeMaxHeight(iVideoFormat);
if bUsePriority then
begin
iPriority := HmsMediaFormatPriority(iHeight, mpPodcastMediaFormats);
if (iPriority <> -1) and (iPriority < iMinPriority) then
begin
iMinPriority := iPriority;
Result := sMediaResourceLink
end
end
else if (iHeight > iMaxHeight) and (iHeight <= StrToIntDef(gpMaxHeight,1080)) then
begin
iMaxHeight := iHeight;
Result := sMediaResourceLink;
end
end // flv, mp4
end //sMediaResourceLink
end
end
else if sHlsUrl <> '' then
begin
sHlsVideoLink := CreateHlsVideoLinks(sHlsUrl);
bSuccess := sHlsVideoLink<>'';
Result := sHlsVideoLink;
end
else if sDashMpdLink <> '' then
begin
sDashVideoLink := CreateDashVideoLinks(sDashMpdLink);
bSuccess := sDashVideoLink<>'';
Result := sDashVideoLink;
end;
if not bSuccess then
Result:=CreateMobileVideoLinks();
end;
function GetSearch(inMediaItem: THmsScriptMediaItem):string;
var lsearch: string;
begin
lsearch := inMediaItem[mpiComment];
if pos(' --',lsearch)>0 then
lsearch := Copy(lsearch, 1, pos(' --', lsearch)-1);
lsearch := trim(lsearch);
//HmsRegExMatch('(.*) --.*', lsearch, lsearch);
//lsearch := HmsHttpEncode(lsearch); //Не работает
lsearch := ReplaceStr(lsearch, ' ', '+');
lsearch := ReplaceStr(lsearch, '&', '%26');
lsearch := ReplaceStr(lsearch, '/', '%2F');
lsearch := ReplaceStr(lsearch, '%', '%25');
//lsearch := ReplaceStr(lsearch, '+', '%2B');
Result := lsearch;
end;
//=================== Главная программа ======================================//
var
i: integer;
lDopFolder, lNextFolder: THmsScriptMediaItem;
sVideoInfo, sVideoId: string;
ltype, lcode, lsearch, lpagetoken: string;
begin
InitParameters;
MediaResourceLink := '';
if PodcastItem.IsFolder then begin
PodcastItem.DeleteChildItems;
ltype := VarToStr(PodcastItem[mpiYoutubeType]);
lcode := VarToStr(PodcastItem[mpiYoutubeCode]);
lsearch := VarToStr(PodcastItem[mpiYoutubeSearch]);
lpagetoken := VarToStr(PodcastItem[mpiYoutubePageToken]);
if (Pos('Дополнительные видео', mpTitle)=1) then
begin
CreateDopVideoFolders(PodcastItem);
end
else if (Pos('Следующая страница', mpTitle)=1) and (ltype = 'playlists') then
begin
CreatePlaylists(PodcastItem, lcode, lpagetoken);
end
else if (Pos('Следующая страница', mpTitle)=1) and (ltype = 'channels') then
begin
CreateChannels(PodcastItem, lcode, lpagetoken);
end
else if (Pos('Следующая страница', mpTitle)=1) then
begin
CreateVideos(PodcastItem, ltype, lcode, lsearch, lpagetoken);
end
else if (ltype = 'dopvideo') then
begin
CreateDopVideo(PodcastItem);
end
else if (ltype = 'playlist')
or (ltype = 'channel')
or (ltype = 'user')
or (ltype = 'video')
or (ltype = 'search') then
begin
CreateVideos(PodcastItem, ltype, lcode, lsearch, lpagetoken);
end
else
begin
addLogError('Не определен тип папки...');
end;
//Сортировка
if PodcastItem[mpiFolderSortOrder]<>null then
PodcastItem.Sort(PodcastItem[mpiFolderSortOrder]);
end
else
begin
//Если не папка, выдать ссылку
// Получение кода видео
sVideoId := GetVideoId(PodcastItem);
if gpMethodGetLink='remote' then
MediaResourceLink := GetLinkYoutube3;
//Если ссылка не получена или не remote
if MediaResourceLink='' then
begin
if sVideoInfo='' then
sVideoInfo := GetVideoInfo(sVideoId);
MediaResourceLink := GetLinkYoutube3Local(sVideoInfo);
end;
//Если ссылка уже была определена, используем ее
//if (PodcastItem[mpiMediaResourceLink] = NULL) then
//begin
// MediaResourceLink := GetLinkYoutube3;
// PodcastItem[mpiMediaResourceLink] := MediaResourceLink;
//end
//else
// MediaResourceLink := PodcastItem[mpiMediaResourceLink];
if (gpGetTimeLength='on') and (PodcastItem[mpiTimeLength] = NULL) then
begin
addLog('Получение VideoInfo');
if sVideoInfo='' then
sVideoInfo := GetVideoInfo(sVideoId);
addLog('Начало получения времени ролика');
UpdateVideoTimeLength(sVideoInfo); //Обновление времени
UpdateFromVideoInfo(sVideoInfo); //Обновление другой информации
addLog('Время ролика = '+PodcastItem[mpiTimeLength]);
end;
if (gpGetFileSize='on') and ((PodcastItem[mpiFileSize] = NULL) or (PodcastItem[mpiMimeType] = NULL)) then
begin
addLog('Начало получения размера файла');
UpdateVideoFileSize; // By WendyH
addLog('Размер файла = '+IntToStr(PodcastItem[mpiFileSize])+'. MimeType = '+PodcastItem[mpiMimeType]);
end;
end;
end.
551
PascalScript