Skip to content

Commit c961bb8

Browse files
committed
check url
1 parent 6e861be commit c961bb8

7 files changed

+216
-28
lines changed

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ OBJS = $(PLUGIN).o common.o config.o device.o pidscanner.o \
8383
protocoludp.o sectionfilter.o setup.o sidscanner.o socket.o \
8484
protocolm3u.o protocolradio.o protocolstream.o \
8585
m3u8handler.o process.o process_unix.o ffmpeghandler.o streambasehandler.o \
86-
vlchandler.o source.o statistics.o streamer.o radioimage.o
86+
vlchandler.o source.o statistics.o streamer.o radioimage.o checkurl.o
8787

8888
### The main target:
8989

checkurl.cpp

+140
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,140 @@
1+
#include <vdr/channels.h>
2+
#include <curl/curl.h>
3+
#include "checkurl.h"
4+
#include "source.h"
5+
#include "protocolm3u.h"
6+
7+
#define READLIMIT_BYTES (1024 * 50)
8+
#define READ_TIMEOUT_SEC 10L
9+
10+
struct memory {
11+
char *response;
12+
size_t size;
13+
};
14+
15+
CheckURL::CheckURL() {
16+
isRunning = false;
17+
}
18+
19+
CheckURL::~CheckURL() {
20+
abort();
21+
}
22+
23+
void CheckURL::start() {
24+
if (isRunning) {
25+
return;
26+
}
27+
28+
isRunning = true;
29+
checkThread = std::thread(&CheckURL::executeChecks, this);
30+
}
31+
32+
void CheckURL::abort() {
33+
isRunning = false;
34+
checkThread.join();
35+
}
36+
37+
38+
static size_t check_header_callback(char *buffer, size_t size, size_t nitems, void *userdata)
39+
{
40+
// ignore the header;
41+
return nitems * size;
42+
}
43+
44+
size_t fullsizeRead = 0;
45+
static size_t check_body_callback(char *data, size_t size, size_t nmemb, void *clientp)
46+
{
47+
// ignore body
48+
size_t realsize = size * nmemb;
49+
fullsizeRead += realsize;
50+
51+
if (fullsizeRead > READLIMIT_BYTES) {
52+
return -1;
53+
} else {
54+
return realsize;
55+
}
56+
}
57+
58+
void CheckURL::executeChecks() {
59+
channels.clear();
60+
61+
// collect list of all channels and URLs
62+
{
63+
LOCK_CHANNELS_READ;
64+
for (const cChannel *Channel = Channels->First(); Channel; Channel = Channels->Next(Channel)) {
65+
if (Channel->IsSourceType('I')) {
66+
std::string u;
67+
cIptvTransponderParameters itp(Channel->Parameters());
68+
69+
switch (itp.Protocol()) {
70+
case cIptvTransponderParameters::eProtocolM3U:
71+
u = cIptvProtocolM3U::findUrl(itp.Parameter(), itp.Address());
72+
break;
73+
74+
case cIptvTransponderParameters::eProtocolRadio:
75+
case cIptvTransponderParameters::eProtocolStream:
76+
u = itp.Address();
77+
break;
78+
79+
default:
80+
// currently not supported
81+
break;
82+
}
83+
84+
if (!u.empty()) {
85+
u = ReplaceAll(u, "%3A", ":");
86+
u = ReplaceAll(u, "%7C", "|");
87+
88+
}
89+
channels[*Channel->GetChannelID().ToString()] = u;
90+
}
91+
}
92+
}
93+
94+
// iterate over all channels and checks the URL
95+
CURLcode ret;
96+
CURL *hnd = curl_easy_init();
97+
curl_easy_setopt(hnd, CURLOPT_HEADER, 1);
98+
// curl_easy_setopt(hnd, CURLOPT_NOBODY, 1);
99+
curl_easy_setopt(hnd, CURLOPT_FOLLOWLOCATION, 1L);
100+
curl_easy_setopt(hnd, CURLOPT_HEADERFUNCTION, check_header_callback);
101+
curl_easy_setopt(hnd, CURLOPT_WRITEFUNCTION, check_body_callback);
102+
curl_easy_setopt(hnd, CURLOPT_TIMEOUT, READ_TIMEOUT_SEC);
103+
104+
struct memory chunk = {nullptr};
105+
curl_easy_setopt(hnd, CURLOPT_WRITEDATA, (void *)&chunk);
106+
107+
for (auto a : channels) {
108+
if (!isRunning) {
109+
break;
110+
}
111+
fullsizeRead = 0;
112+
curl_easy_setopt(hnd, CURLOPT_URL, a.second.c_str());
113+
114+
ret = curl_easy_perform(hnd);
115+
free(chunk.response);
116+
chunk.response = nullptr;
117+
chunk.size = 0;
118+
119+
long response_code;
120+
curl_easy_getinfo(hnd, CURLINFO_RESPONSE_CODE, &response_code);
121+
122+
if (!((ret == CURLE_OK || fullsizeRead >= READLIMIT_BYTES) && (response_code < 400))) {
123+
printf("Error: %ld: %s -> %s\n", response_code, a.first.c_str(), a.second.c_str());
124+
brokenChannels.emplace(a.first.c_str());
125+
126+
/*
127+
{
128+
LOCK_CHANNELS_READ;
129+
tChannelID testChannelID = tChannelID::FromString(a.first.c_str());
130+
const cChannel *testChannel = Channels->GetByChannelID(testChannelID);
131+
cDevice::PrimaryDevice()->SwitchChannel(testChannel, true);
132+
}
133+
*/
134+
}
135+
}
136+
137+
curl_easy_cleanup(hnd);
138+
139+
isRunning = false;
140+
}

checkurl.h

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
#pragma once
2+
3+
#include <thread>
4+
#include <map>
5+
6+
class CheckURL {
7+
private:
8+
std::thread checkThread;
9+
bool isRunning;
10+
std::map<std::string, std::string> channels;
11+
std::set<std::string> brokenChannels;
12+
13+
private:
14+
void executeChecks();
15+
16+
public:
17+
CheckURL();
18+
~CheckURL();
19+
20+
void start();
21+
void abort();
22+
};
23+

iptv.cpp

+8
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,9 @@ const char **cPluginIptv::SVDRPHelpPages() {
264264
" adds a STREAM URL to channels. PIDs and frequency will be automatically set\n",
265265
"ADDR <name>#<URL>\n"
266266
" adds a RADIO URL to channels. PIDs and frequency will be automatically set\n",
267+
"CHKU\n"
268+
" checks all configured URL and checks if a connect is possible and a valid result is returned.\n"
269+
" If this is not the case the channel will be marked with ' - 404'\n",
267270
nullptr
268271
};
269272
return HelpPages;
@@ -567,6 +570,11 @@ cString cPluginIptv::SVDRPCommand(const char *commandP, const char *optionP, int
567570

568571
return replyMessage;
569572
}
573+
} else if (strcasecmp(commandP, "CHKS") == 0) {
574+
chk.start();
575+
576+
replyCodeP = 250;
577+
return { "URL check started" };
570578
}
571579

572580
return nullptr;

iptv.h

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
#include "setup.h"
1313
#include "device.h"
1414
#include "iptvservice.h"
15+
#include "checkurl.h"
1516

1617
class cPluginIptv : public cPlugin {
1718
private:
@@ -22,6 +23,8 @@ class cPluginIptv : public cPlugin {
2223
void addChannel(cString& channel, int &replyCode, cString& replyMessage);
2324
int addM3UCfg(const std::string& cfgFile, const std::string& url);
2425

26+
CheckURL chk;
27+
2528
public:
2629
cPluginIptv();
2730
~cPluginIptv() override;

protocolm3u.cpp

+39-27
Original file line numberDiff line numberDiff line change
@@ -93,12 +93,39 @@ cIptvProtocolM3U::SetSource(SourceParameter parameter) {
9393

9494
this->useYtdlp = parameter.useYtDlp;
9595

96+
url = findUrl(parameter.parameterP, parameter.locationP);
97+
98+
if (url.empty()) {
99+
return false;
100+
}
101+
102+
channelId = parameter.channelNumber;
103+
104+
if (handler != nullptr) {
105+
handler->stop();
106+
}
107+
108+
delete handler;
109+
handler = nullptr;
110+
111+
if (parameter.handlerType == 'F') {
112+
handler = new FFmpegHandler(channelId);
113+
} else if (parameter.handlerType == 'V') {
114+
handler = new VlcHandler(channelId);
115+
}
116+
117+
return true;
118+
}
119+
120+
std::string cIptvProtocolM3U::findUrl(int parameterP, const char* locationP) {
121+
std::string m3uUrl;
122+
96123
struct stat stbuf;
97-
cString configFileM = cString::sprintf("%s/%s", IptvConfig.GetM3uCfgPath(), parameter.locationP);
124+
cString configFileM = cString::sprintf("%s/%s", IptvConfig.GetM3uCfgPath(), locationP);
98125
if ((stat(*configFileM, &stbuf)!=0) || (strstr(*configFileM, "..") != nullptr)) {
99126
error("Non-existent or relative configuration file '%s'", *configFileM);
100127

101-
return false;
128+
return "";
102129
}
103130

104131
std::ifstream file(configFileM);
@@ -108,16 +135,16 @@ cIptvProtocolM3U::SetSource(SourceParameter parameter) {
108135
unsigned long idx = line.find(':');
109136
if (idx > 0) {
110137
std::string nr = line.substr(0, idx);
111-
if (nr==std::to_string(parameter.parameterP)) {
112-
url = line.substr(idx + 1);
113-
if (url.empty()) {
114-
error("URL with index %d in file %s not found", parameter.parameterP, *configFileM);
138+
if (nr==std::to_string(parameterP)) {
139+
m3uUrl = line.substr(idx + 1);
140+
if (m3uUrl.empty()) {
141+
error("URL with index %d in file %s not found", parameterP, *configFileM);
115142

116143
file.close();
117-
return false;
144+
return "";
118145
}
119146

120-
debug1("Found URL %s", url.c_str());
147+
debug1("Found URL %s", m3uUrl.c_str());
121148
break;
122149
}
123150
}
@@ -126,27 +153,12 @@ cIptvProtocolM3U::SetSource(SourceParameter parameter) {
126153
file.close();
127154
}
128155

129-
if (url.empty()) {
130-
error("URL with index %d in file %s not found", parameter.parameterP, *configFileM);
131-
return false;
132-
}
133-
134-
channelId = parameter.channelNumber;
135-
136-
if (handler != nullptr) {
137-
handler->stop();
156+
if (m3uUrl.empty()) {
157+
error("URL with index %d in file %s not found", parameterP, *configFileM);
158+
return "";
138159
}
139160

140-
delete handler;
141-
handler = nullptr;
142-
143-
if (parameter.handlerType == 'F') {
144-
handler = new FFmpegHandler(channelId);
145-
} else if (parameter.handlerType == 'V') {
146-
handler = new VlcHandler(channelId);
147-
}
148-
149-
return true;
161+
return m3uUrl;
150162
}
151163

152164
bool cIptvProtocolM3U::SetPid(int pidP, int typeP, bool onP) {

protocolm3u.h

+2
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,6 @@ class cIptvProtocolM3U : public cIptvProtocolIf {
2525
bool Open() override;
2626
bool Close() override;
2727
cString GetInformation() override;
28+
29+
static std::string findUrl(int parameterP, const char* locationP);
2830
};

0 commit comments

Comments
 (0)