experimental yt cache (currently no gui integration)

Conflicts:
	src/gui/moviebrowser.cpp
	src/gui/movieinfo.cpp
	src/system/Makefile.am
This commit is contained in:
martii
2013-06-12 17:16:22 +02:00
committed by [CST] Focus
parent 824b6facb1
commit 70e5e66327
9 changed files with 412 additions and 84 deletions

View File

@@ -44,5 +44,6 @@ libneutrino_system_a_SOURCES = \
ping.cpp \
settings.cpp \
sysload.cpp \
ytcache.cpp \
ytparser.cpp \
setting_helpers.cpp

View File

@@ -509,7 +509,7 @@ bool CFileHelpers::copyFile(const char *Src, const char *Dst, mode_t mode)
unlink(Dst);
if ((fd1 = open(Src, O_RDONLY)) < 0)
return false;
if ((fd2 = open(Dst, O_WRONLY | O_CREAT, 0666)) < 0) {
if ((fd2 = open(Dst, O_WRONLY | O_CREAT, mode)) < 0) {
close(fd1);
return false;
}
@@ -574,7 +574,6 @@ bool CFileHelpers::copyFile(const char *Src, const char *Dst, mode_t mode)
return false;
}
chmod(Dst, mode);
return true;
}

254
src/system/ytcache.cpp Normal file
View File

@@ -0,0 +1,254 @@
/*
ytcache.cpp -- cache youtube movies
Copyright (C) 2013 martii
License: GPL
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "ytcache.h"
#include <OpenThreads/ScopedLock>
#include <stdio.h>
#include <unistd.h>
#include <curl/curl.h>
#include <curl/easy.h>
#if LIBCURL_VERSION_NUM < 0x071507
#include <curl/types.h>
#endif
#define URL_TIMEOUT 60
#include "helpers.h"
#include "settings.h"
#include <global.h>
static cYTCache *instance = NULL;
cYTCache::cYTCache(void)
{
cancelled = false;
thread = 0;
}
cYTCache::~cYTCache(void)
{
instance = NULL;
}
cYTCache *cYTCache::getInstance(void)
{
if (!instance)
instance = new cYTCache();
return instance;
}
std::string cYTCache::getName(MI_MOVIE_INFO *mi, std::string ext)
{
char ytitag[10];
snprintf(ytitag, sizeof(ytitag), "%d", mi->ytitag);
return g_settings.downloadcache_dir + "/" + mi->ytid + "-" + std::string(ytitag) + "." + ext;
}
bool cYTCache::useCachedCopy(MI_MOVIE_INFO *mi)
{
std::string cache = getName(mi);
if (!access(cache.c_str(), R_OK)) {
mi->file.Url = cache;
return true;
}
return false;
}
int cYTCache::curlProgress(void *clientp, double /*dltotal*/, double /*dlnow*/, double /*ultotal*/, double /*ulnow*/)
{
cYTCache *caller = (cYTCache *) clientp;
if (caller->cancelled)
return 1;
return 0;
}
bool cYTCache::download(MI_MOVIE_INFO *mi)
{
std::string file = getName(mi);
std::string tmpfile = file + ".tmp";
if (!access(file.c_str(), R_OK) || !access(tmpfile.c_str(), R_OK))
return true;
FILE * fp = fopen(file.c_str(), "wb");
if (!fp) {
perror(file.c_str());
return false;
}
CURL *curl = curl_easy_init();
if (!curl) {
fclose(fp);
return false;
}
curl_easy_setopt(curl, CURLOPT_URL, mi->file.Url.c_str());
curl_easy_setopt(curl, CURLOPT_FILE, fp);
curl_easy_setopt(curl, CURLOPT_FAILONERROR, 1);
curl_easy_setopt(curl, CURLOPT_TIMEOUT, URL_TIMEOUT);
curl_easy_setopt(curl, CURLOPT_NOSIGNAL, (long)1);
curl_easy_setopt(curl, CURLOPT_PROGRESSFUNCTION, cYTCache::curlProgress);
curl_easy_setopt(curl, CURLOPT_PROGRESSDATA, this);
curl_easy_setopt(curl, CURLOPT_NOPROGRESS, (long)0);
char cerror[CURL_ERROR_SIZE];
curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, cerror);
CURLcode res = curl_easy_perform(curl);
curl_easy_cleanup(curl);
fclose(fp);
if (res) {
fprintf(stderr, "curl error: %s\n", cerror);
unlink(tmpfile.c_str());
return false;
}
rename (tmpfile.c_str(), file.c_str());
CMovieInfo cMovieInfo;
CFile File;
File.Name = getName(mi, "xml");
cMovieInfo.convertTs2XmlName(&File.Name);
cMovieInfo.saveMovieInfo(*mi, &File);
std::string thumbnail_dst = getName(mi, "jpg");
std::string thumbnail_src = "/tmp/ytparser/" + mi->ytid + ".jpg";
CFileHelpers::getInstance()->copyFile(thumbnail_src.c_str(), thumbnail_dst.c_str(), 0644);
return true;
}
void *cYTCache::downloadThread(void *arg) {
cYTCache *caller = (cYTCache *)arg;
while (caller->thread) {
MI_MOVIE_INFO mi;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(caller->mutex);
if (caller->pending.empty()) {
caller->cancelled = false;
caller->thread = 0;
continue;
}
mi = caller->pending.front();
}
fprintf(stderr, "download start: %s\n", mi.file.Url.c_str());
bool res = caller->download(&mi);
fprintf(stderr, "download end: %s %s\n", mi.file.Url.c_str(), res ? "succeeded" : "failed");
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(caller->mutex);
if (res)
caller->done.push_front(mi);
else
caller->failed.push_front(mi);
caller->cancelled = false;
if (caller->pending.empty())
caller->thread = 0;
else
caller->pending.pop_front();
}
}
pthread_exit(NULL);
}
bool cYTCache::addToCache(MI_MOVIE_INFO *mi)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
pending.push_back(*mi);
if (!thread) {
if (pthread_create(&thread, NULL, downloadThread, this)) {
perror("pthread_create");
return false;
}
pthread_detach(thread);
}
return true;
}
void cYTCache::cancel(MI_MOVIE_INFO *mi)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
if (pending.empty())
return;
if (compareMovieInfo(mi, &pending.front())) {
cancelled = true;
return;
}
for (std::list<MI_MOVIE_INFO>::iterator it = pending.begin(); it != pending.end(); ++it)
if (compareMovieInfo(&(*it), mi)) {
pending.erase(it);
break;
}
}
void cYTCache::cancelAll(void)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
if (pending.empty())
return;
cancelled = true;
while (thread)
usleep(100000);
cancelled = false;
pending.clear();
}
std::list<MI_MOVIE_INFO> cYTCache::getFailed(bool clear)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
std::list<MI_MOVIE_INFO> res = failed;
if (clear)
failed.clear();
return res;
}
std::list<MI_MOVIE_INFO> cYTCache::getDone(bool clear)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
std::list<MI_MOVIE_INFO> res = done;
if (clear)
done.clear();
return res;
}
void cYTCache::clearFailed(void)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
failed.clear();
}
void cYTCache::clearDone(void)
{
OpenThreads::ScopedLock<OpenThreads::Mutex> m_lock(mutex);
done.clear();
}
bool cYTCache::compareMovieInfo(MI_MOVIE_INFO *a, MI_MOVIE_INFO *b)
{
return a->ytid == b->ytid && a->ytitag == b->ytitag;
return true;
}

62
src/system/ytcache.h Normal file
View File

@@ -0,0 +1,62 @@
/*
ytcache.h -- cache youtube movies
Copyright (C) 2013 martii
License: GPL
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __YTCACHE_H__
#define __YTCACHE_H__
#include <config.h>
#include <string>
#include <list>
#include <OpenThreads/Thread>
#include <OpenThreads/Condition>
#include <gui/movieinfo.h>
class cYTCache
{
private:
pthread_t thread;
bool cancelled;
std::list<MI_MOVIE_INFO> pending;
std::list<MI_MOVIE_INFO> done;
std::list<MI_MOVIE_INFO> failed;
OpenThreads::Mutex mutex;
bool download(MI_MOVIE_INFO *mi);
std::string getName(MI_MOVIE_INFO *mi, std::string ext = "mp4");
static void *downloadThread(void *arg);
static int curlProgress(void *clientp, double dltotal, double dlnow, double ultotal, double ulnow);
bool compareMovieInfo(MI_MOVIE_INFO *a, MI_MOVIE_INFO *b);
public:
static cYTCache *getInstance();
cYTCache();
~cYTCache();
bool useCachedCopy(MI_MOVIE_INFO *mi);
bool addToCache(MI_MOVIE_INFO *mi);
void cancel(MI_MOVIE_INFO *mi);
void cancelAll(void);
std::list<MI_MOVIE_INFO> getDone(bool clear = false);
std::list<MI_MOVIE_INFO> getFailed(bool clear = false);
void clearDone(void);
void clearFailed(void);
};
#endif

View File

@@ -63,27 +63,29 @@ void cYTVideoInfo::Dump()
printf("===================================================================\n");
}
std::string cYTVideoInfo::GetUrl(int fmt, bool mandatory)
std::string cYTVideoInfo::GetUrl(int *fmt, bool mandatory)
{
int default_fmt = 0;
if (!*fmt)
fmt = &default_fmt;
yt_urlmap_iterator_t it;
if (fmt) {
if ((it = formats.find(fmt)) != formats.end())
if (*fmt) {
if ((it = formats.find(*fmt)) != formats.end()) {
return it->second.GetUrl();
if (mandatory)
}
if (mandatory) {
*fmt = 0;
return "";
}
}
if ((it = formats.find(37)) != formats.end()) // 1080p MP4
return it->second.GetUrl();
if ((it = formats.find(22)) != formats.end()) // 720p MP4
return it->second.GetUrl();
#if 0
if ((it = formats.find(35)) != formats.end()) // 480p FLV
return it->second.GetUrl();
if ((it = formats.find(34)) != formats.end()) // 360p FLV
return it->second.GetUrl();
#endif
if ((it = formats.find(18)) != formats.end()) // 270p/360p MP4
return it->second.GetUrl();
int itags[] = { 37 /* 1080p MP4*/, 22 /* 720p MP4 */, 18 /* 270p/360p MP4 */, 0 };
for (int *fmtp = itags; *fmtp; fmtp++)
if ((it = formats.find(*fmtp)) != formats.end()) {
*fmt = *fmtp;
return it->second.GetUrl();
}
return "";
}

View File

@@ -62,7 +62,7 @@ class cYTVideoInfo
bool ret;
void Dump();
std::string GetUrl(int fmt = 0, bool mandatory = true);
std::string GetUrl(int *fmt = NULL, bool mandatory = true);
};