#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <tcp.c>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <errno.h>
#include <time.h>
#define NIZ_DODATEK(a) #a
#define NIZ(a) NIZ_DODATEK(a)
#define RTV_CLIENT_ID "82013fb3a531d5414f478747c1aca622" /* enak za vse */
#define RTV_CLIENT_ID_SIZEOF 32+1
#define RTV_VIDEO_TIP 1
#define RTV_AUDIO_TIP 2
#define RTV_NEPOZNAN_TIP 0
#define RTV_NACIN argv[1]
#define RTV_URL argv[2]
#define RTV_P_SIZEOF 128 /* privzeti sizeof */
#define RTV_TEST_NASLOV "Vreme ob 22h"
#define RTV_TEST_ZANRI "\"INFORMATIVNE VSEBINE\"," \
"\"DNEVNO INFORMATIVNE ODDAJE\",\"Vremenska poročila\""
#define RTV_TEST_OPIS "Vreme je na sporedu vsak dan po Poročilih, pred in po" \
" Dnevniku ter po Odmevih. Napoved vremena pa vas čaka tudi vsak delavnik" \
" zjutraj v rubriki oddaje Dobro jutro. \\r\\n"
#define RTV_TEST_ID 89614963
#define RTV_TEST_DOLZINA 155
#define RTV_TEST_TIP_ODDAJE_IME "Vreme"
#define RTV_TEST_TIP_ODDAJE_ID 608
#define RTV_TEST_PROGRAM "SLO1"
#define RTV_TEST_OBJAVLJENO "2010-12-03 23:20:10"
#define RTV_TEST_PREDVAJANO "2010-12-03 23:20:10"
#define RTV_TEST_TIP_POSNETKA RTV_VIDEO_TIP
#define RTV_SLD "rtvslo.si"
#define RTV_TEST_SLICICA "http://img." RTV_SLD "/_up/ava/ava_misc/show_logos" \
"/VREME_VECERNO-980.jpg"
#define RTV_TEST_KONTROLNA_VREDNOST 1186735842
#define RTV_TEST_VELIKOST 9396765
#define RTV_NAPISI_NAPAKA 1
#define RTV_NAPISI_INFO 1
#define RTV_NAPISI_OPOZORILO 1
#define RTV_NAPISI_HROSC 1
#define RTV_NAPISI(kaj, frmt, ...) /* pazi na format string RCE! */ \
do { if ( RTV_NAPISI_ ##kaj ) fprintf(stderr, \
"[" #kaj "] %s@%s:" NIZ(__LINE__) " " frmt "\n", \
__func__, __FILE__, ##__VA_ARGS__ ); } while(0);
#define RTV_VER "0.0.6"
#define RTV_USER_AGENT "Mozilla/5.0 equivalent (rtv4d-dl " RTV_VER "; C " \
NIZ(__STDC_VERSION__) " GCC " __VERSION__ "; " __DATE__ " " __TIME__ "; " \
__FILE__ ":" NIZ(__LINE__) ")"
#define RTV_HTTP_GET \
"GET /%s HTTP/1.0\r\nHost: %s\r\nUser-Agent: " RTV_USER_AGENT \
"\r\nAccept: */*\r\nX-Requested-With: tcp.c (" __FILE__ ":" NIZ(__LINE__) \
")\r\nConnection: close\r\n\r\n"
#define RTV_HTTP_TIMEOUT 9999999 /* sekund */
#define RTV_NE_BO_POSLAL NULL /* bere več znakov */
#define RTV_API_META_URL "http://api." RTV_SLD "/ava/getRecordingDrm/" \
"%u?client_id=" RTV_CLIENT_ID
#define RTV_API_META_URL_SIZEOF 128 /* vključno z nul znakom */
#define RTV_API_MEDIA_URL "http://api." RTV_SLD "/ava/getMedia/%u/?client_id=" \
RTV_CLIENT_ID "&jwt=%s"
#define RTV_API_MEDIA_URL_SIZEOF 64 + RTV_CLIENT_ID_SIZEOF + RTV_JWT_SIZEOF
#define RTV_JWT_SIZEOF 43+1
#define RTV_PREDVAJALNIK_URL "http://4d." RTV_SLD "/arhiv/v/%u"
#define RTV_PREDVAJALNIK_URL_SIZEOF (32+1 + 12)
#define RTV_ZIVO_PROGRAM_SIZEOF 12
#define RTV_API_ZIVO_URL \
"http://api." RTV_SLD "/ava/getLiveStream/tv.%." \
NIZ(RTV_ZIVO_PROGRAM_SIZEOF) "s?client_id=" RTV_CLIENT_ID
#define RTV_API_ZIVO_URL_SIZEOF (64 + 32 + 1 + RTV_ZIVO_PROGRAM_SIZEOF)
#define RTV_HTTPS_V_HTTP(url) \
if (url[4] == 's') { memmove((url)+4, (url)+5, strlen((url)+5)+1); }
#define RTV_ZCARON 4294967237
#define strtollu strtoull
#define strtolu strtoul
#define strtolld strtoll
#define strtold strtol
#define strtod strtol
#define strtou strtoul
#define RTV_JSON_INIT() \
char * RTV_JSON_cp; size_t RTV_JSON_i, RTV_JSON_globina; char RTV_JSON_c;
#define RTV_JSON_VALUE(str, i, key) (str+i+strlen(key)+2)
#define RTV_JSON_TIP_u unsigned int
#define RTV_JSON_TIP_s char
#define RTV_JSON_OBLIKA_s(imem, format, loc) \
RTV_JSON_c = RTV_JSON_cp[0]; \
RTV_JSON_cp[0] = '\0'; /* omejimo json string, ne potrebujemo strncpy */ \
imem##_sizeof = sizeof(char)*(strlen(loc)+1+RTV_P_SIZEOF); \
imem = realloc(imem, imem##_sizeof); \
strcpy(imem, loc); \
RTV_JSON_cp[0] = RTV_JSON_c; /* ponastavimo originalno vrednost */
#define RTV_JSON_OBLIKA_u(imem, format, loc) /* -1: št niso v "" v JSON */ \
imem = strto##format(loc, NULL, 10);
#define RTV_JSON_IZPOLNI(imem, format, splitter, loc) \
do { RTV_JSON_cp = strchr(loc, splitter); \
if ( RTV_JSON_cp == NULL) { \
RTV_NAPISI(OPOZORILO, "RTV_JSON: napaka pri: " #imem); \
} else { \
RTV_JSON_OBLIKA_##format(imem, format, loc) \
RTV_NAPISI(HROSC, "RTV_JSON: " #imem " => %" #format, \
imem); \
} } while (0);
#define RTV_JSON(str, strl, key, out, fmt, spl, otr) /* strl je strlen */ \
do { \
for (RTV_JSON_i = 0; RTV_JSON_i < strl; RTV_JSON_i++) { /* keyss=sizeof */ \
if (otr != NULL) { \
if (str[RTV_JSON_i] == '}' && RTV_JSON_globina > 0) RTV_JSON_globina--;\
if (str[RTV_JSON_i] == '{') \
if (strncmp(otr, str+RTV_JSON_i-(2+strlen(otr)), strlen(otr)) == 0) \
RTV_JSON_globina++; \
} \
if ( (RTV_JSON_globina > 0 || otr == NULL) \
&& strncmp(str+RTV_JSON_i, key, strlen(key)) == 0) { \
RTV_JSON_IZPOLNI(out, fmt, spl, RTV_JSON_VALUE(str, RTV_JSON_i, key)); \
break; \
} \
} \
} while (0);
#define RTV_FREE(param) do { free(param); param = NULL; } while (0)
#define RTV_HTTP_SUCCESS(koda) ((koda / 100) == 2) /* če je koda 2xx */
#define RTV_ZIVO_P_DOLZINA 10
#define RTV_INT_V_SLO(niz) /* niz naj bo http url */ \
if (strstr(niz, "-int")) { /* je strstr-jati dvakrat potratno? */ \
strncpy(strstr(niz, "-int"), "-slo", strlen("-slo")); /* nast. pov. \/ */ \
niz[8]--; /* pri medn. je štev. ponav. za 1 večja */ /* kot mednarodno */ \
RTV_NAPISI(OPOZORILO, "izven Slovenije prenašate vsebino RTVSLO. Če ne " \
"plačujete RTV prispevka ali če za to niste pooblaščeni, KRŠITE " \
"AVTORSKE PRAVICE in STE V PREKRŠKU - v tem primeru nemudoma " \
"prenehajte uporabljati program s Ctrl-C! naslovi so bili spremenjeni, " \
"tako da bo prenos še vedno deloval, če pa ne, pa poglejte navodila."); \
}
#define RTV_ZIVO_KAZ_POSK 12
#define RTV_ZIVO_KAZ_POSK_SPANJE 1
struct meta_oddaja {
size_t naslov_sizeof;
char * naslov; /* Vreme ob 22h */
unsigned int tip_oddaje_id; /* 608 */
size_t zanri_sizeof;
char * zanri; /* INFORMATIVNE VSEBINE, DNEVNO INFORMATIVNE ODDAJE, Vrem... */
size_t opis_sizeof;
char * opis; /* Vreme je na sporedu vsak dan po Poročilih, pred in po Dn... */
unsigned int id; /* 89614963 */
unsigned int dolzina; /* 155 */
size_t jwt_sizeof; /* 44 */
char * jwt; /* 1lKYcP7j8iNwqVQeQC8htsrBN47LpiHq3sm5bFlxRys */
size_t tip_oddaje_ime_sizeof;
char * tip_oddaje_ime; /* Vreme */
size_t program_sizeof;
char * program; /* SLO1 */
size_t objavljeno_sizeof;
char * objavljeno; /* 2010-12-03 23:20:10 */
unsigned short int tip_posnetka; /* RTV_VIDEO_TIP */
size_t slicica_sizeof;
char * slicica; /* http://img.rtvslo.si/_up/ava/ava_misc/show_logos/VREM... */
size_t predvajano_sizeof;
char * predvajano; /* 2010-12-03 23:20:10 */
/* unsigned long long int kontrolna_vrednost; */
/* unsigned long long int velikost; */ /* število bajtov posnetka */
char get_meta_url[RTV_API_META_URL_SIZEOF]; /* http://api.rtvslo.si/ava/... */
char get_media_url[RTV_API_MEDIA_URL_SIZEOF]; /* http://api.rtvslo.si/av... */
char predvajalnik_url[RTV_PREDVAJALNIK_URL_SIZEOF]; /* http://4d.rtvslo.... */
size_t posnetek_url_sizeof;
char * posnetek_url; /* http://progressive.rtvslo.si/encrypted00/2010/12... */
char * podnapisi_url; /* http://img.rtvslo.si/_up/ava/ava_misc/subs/2020... */
size_t podnapisi_url_sizeof; /* upam, da nikoli ni več kot ena datoteka. */
};
struct meta_oddaja * meta_oddaja_alloc () {
struct meta_oddaja * m = malloc(sizeof(struct meta_oddaja));
#define RTV_META_ALLOC(param) \
param = malloc(sizeof(char)*RTV_P_SIZEOF); param##_sizeof = RTV_P_SIZEOF; \
param[0] = '\0'; /* da ga nastavimo na prazno vrednost, ne na fuckery*/
RTV_META_ALLOC(m->naslov);
RTV_META_ALLOC(m->zanri);
RTV_META_ALLOC(m->opis);
RTV_META_ALLOC(m->tip_oddaje_ime);
RTV_META_ALLOC(m->program);
RTV_META_ALLOC(m->slicica);
RTV_META_ALLOC(m->jwt);
RTV_META_ALLOC(m->objavljeno);
RTV_META_ALLOC(m->predvajano);
RTV_META_ALLOC(m->posnetek_url);
RTV_META_ALLOC(m->podnapisi_url);
return m;
}
int meta_oddaja_free (struct meta_oddaja * m) {
RTV_FREE(m->naslov);
RTV_FREE(m->zanri);
RTV_FREE(m->opis);
RTV_FREE(m->tip_oddaje_ime);
RTV_FREE(m->program);
RTV_FREE(m->slicica);
RTV_FREE(m->jwt);
RTV_FREE(m->objavljeno);
RTV_FREE(m->predvajano);
RTV_FREE(m->posnetek_url);
RTV_FREE(m->podnapisi_url);
RTV_FREE(m);
return 0;
}
struct rtv_zivo_meta {
char program[RTV_ZIVO_PROGRAM_SIZEOF]; /* slo1, slo2 */
unsigned int sedanjost; /* recimo 5580, številka zadnjega kosa */
unsigned int prvi; /* recimo 1234, številka prvega kosa, ki ga ima strežnik */
unsigned int dolzina; /* koliko dolg je en kos */
unsigned int diskrepanca; /* 1, če niso vsi kosi enako dolgi, 0 drugače */
unsigned int prenesenih_kosov_preteklost;
unsigned int prenesenih_kosov_prihodnost;
unsigned int preteklost; /* število sekund, ki naj jih prenesem za nazaj */
unsigned int prihodnost; /* število sekund, ki naj jih prenesem za naprej */
char * seznam_predvajanja_url; /* http://30-rtvslo-tv-slo1.../playlist.m3u8 */
size_t seznam_predvajanja_url_sizeof;
char * kazalo_url; /* http://30-rtvslo-tv-slo-tv.../chunklist_b4128000.m3u8 */
size_t kazalo_url_sizeof;
char * kos_format; /* http://30-rtvslo-tv-slo-tv-sl.../media_b4128000_%u.ts */
size_t kos_format_sizeof;
char * api_url; /* http://api.rtvslo.si/ava/getLiveStream/tv.slo1?client... */
size_t api_url_sizeof;
};
struct rtv_zivo_meta * rtv_zivo_meta_alloc () {
struct rtv_zivo_meta * m = malloc(sizeof(struct rtv_zivo_meta));
RTV_META_ALLOC(m->seznam_predvajanja_url);
RTV_META_ALLOC(m->kazalo_url);
RTV_META_ALLOC(m->kos_format);
RTV_META_ALLOC(m->api_url);
return m;
}
int rtv_zivo_meta_free (struct rtv_zivo_meta * m) {
RTV_FREE(m->seznam_predvajanja_url);
RTV_FREE(m->kazalo_url);
RTV_FREE(m->kos_format);
RTV_FREE(m->api_url);
RTV_FREE(m);
return 0;
}
int niz_realloc (char * s, size_t * v) {
*v = (*v)*2;
s = realloc(s, sizeof(char)*(*v));
return 0;
}
int http_get (/* const */ char * u, FILE * r) { /* url ostane enak */
unsigned int p = 80; /* HTTP port ^~~~~~~~ glej man open_memstream, fmemopen*/
char * k; /* označuje konec imena gostitelja */
char t; /* začasna shramba za končni znak, ko je le-ta null */
int c = -42069; /* connection descriptor */
int ret; /* izhodni status write funkcij */
char * path; /* kaže na path requesta */
int returnstatus = 0;
char * header; /* za headerje */
FILE * headerstream;
size_t header_sizeof = 0;
long long int contentlength = -69420;
char * request_buf; /* za request buffer */
int response_code = -69420;
#define RTV_URL_HOST (u+7)
if (u == NULL) {
RTV_NAPISI(NAPAKA, "URL je null!");
return 1;
}
if (strncmp(u, "http://", 7) != 0) {
RTV_NAPISI(NAPAKA, "URL ni podprt: %s", u);
return 2;
}
k = strchr(RTV_URL_HOST, '/');
if (k - strchrnul(RTV_URL_HOST, ':') > 0) { /* pred pathom je PORT */
k = strchrnul(RTV_URL_HOST, ':');
p = atoi(k+1);
}
if (p > 65535) { /* port je nepodpisan, če gre v minus bo itak ZELO > 65535 */
RTV_NAPISI(NAPAKA, "port je neveljaven");
return 3;
}
if (k == NULL)
k = strchrnul(RTV_URL_HOST, '/');
path = strchr(RTV_URL_HOST, '/');
if (path == NULL) {
u[6] = '\0'; /* na koncu popravimo nazaj, izkoristimo ta memory xD */
path = u+5;
} else {
path++;
}
t = *k;
*k = '\0';
RTV_NAPISI(HROSC, "http://%s:%d/%s",
RTV_URL_HOST, p, path);
c = spawn_conn(RTV_URL_HOST, p);
if (c < 0) {
RTV_NAPISI(NAPAKA, "TCP povezava ni uspela!");
returnstatus = 4;
goto http_get_returncleanly;
} else {
RTV_NAPISI(HROSC, "Povezan na strežnik.");
}
request_buf = malloc (sizeof(char) * ( sizeof(RTV_HTTP_GET)
+ strlen(RTV_URL_HOST)+ strlen(path) + 1 ));
sprintf(request_buf, RTV_HTTP_GET, path, RTV_URL_HOST); /* =dovolj prostora */
RTV_NAPISI(HROSC, "Pošiljam %lu bajtov ...", strlen(request_buf));
ret = sync_write(c, request_buf, strlen(request_buf), RTV_HTTP_TIMEOUT);
if (ret != 0) {
returnstatus = 5;
RTV_NAPISI(NAPAKA, "TCP časovna omejitev pri pošiljanju je potekla");
goto http_get_returncleanly;
} else {
RTV_NAPISI(HROSC, "Uspešno poslano.");
}
headerstream = open_memstream(&header, &header_sizeof);
while (1) {
rewind(headerstream);
ret = read_until(c, headerstream, RTV_HTTP_TIMEOUT, "\n", -1); /* unsig!*/
fflush(headerstream);
if (response_code == -69420 && strchr(header, ' ') != NULL) {
response_code = atoi(strchr(header, ' ')+1);
}
if (ret != 0) {
returnstatus = 6;
RTV_NAPISI(NAPAKA, "Branje headerja ni uspelo!");
goto http_get_returncleanly;
}
if (strncasecmp("Content-Length: ", header, strlen("Content-Length: "))
== 0) {
contentlength = strtoll(header+16, NULL, 10);
RTV_NAPISI(HROSC, "Pridobil %lld bajtni odgovor.", contentlength);
}
if (strncmp("\r\n", header, 2) == 0 || header[0] == '\n')
break;
}
if (contentlength == -69420) {
RTV_NAPISI(HROSC, "Manjka Content-Length, berem do konca."); /* legalno */
} else {
RTV_NAPISI(HROSC, "Začetek telesa odgovora. Berem in pišem.");
}
fclose(headerstream);
ret = read_until(c, r, RTV_HTTP_TIMEOUT, RTV_NE_BO_POSLAL, contentlength);
if (ret != 0) {
returnstatus = 7;
RTV_NAPISI(NAPAKA, "Branje in pisanje telesa odgovora ni uspelo!");
goto http_get_returncleanly;
}
RTV_NAPISI(HROSC, "Prejel in uspešno shranil/prebral HTTP odgovor.");
http_get_returncleanly:
free(request_buf);
request_buf = NULL;
free(header);
header = NULL;
if(c != -69420 && kill_conn(c) < 0) /* kill_conn se ne izvede, če prvi ni 1.*/
RTV_NAPISI(OPOZORILO, "TCP povezave ni uspelo prekiniti");
*k = t;
u[6] = '/';
if (returnstatus == 0) {
returnstatus = response_code;
}
return returnstatus;
}
int rtv_meta_izpolni(struct meta_oddaja * m) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
int returnstatus = 0;
FILE * odgstream;
char * odg;
size_t sizeloc;
RTV_JSON_INIT();
snprintf(m->get_meta_url, RTV_API_META_URL_SIZEOF, RTV_API_META_URL, m->id);
snprintf(m->predvajalnik_url, RTV_PREDVAJALNIK_URL_SIZEOF, RTV_PREDVAJALNIK_URL, m->id);
odgstream = open_memstream(&odg, &sizeloc);
returnstatus = http_get(m->get_meta_url, odgstream); /* shranimo metapodat. */
if (!RTV_HTTP_SUCCESS(returnstatus)) {
RTV_NAPISI(NAPAKA, "API zahteva je spodletela s kodo %d", returnstatus);
goto rtv_meta_izpolni_returncleanly;
} else returnstatus = 0;
fflush(odgstream);
RTV_JSON(odg, sizeloc, "\"title\"", m->naslov, s, '"', NULL);
/* pri številkah je vseeno, kaj je spl */
RTV_JSON(odg, sizeloc, "\"showId\"", m->tip_oddaje_id, u, '"', NULL);
RTV_JSON(odg, sizeloc, "\"genre\"", m->zanri, s, ']', NULL);
RTV_JSON(odg, sizeloc, "\"description\"", m->opis, s, '"', NULL);
if (m->opis[0] == '\0') /* če je prejšnji spodletel */
RTV_JSON(odg, sizeloc, "\"showDescription\"", m->opis, s, '"', NULL);
RTV_JSON(odg, sizeloc, "\"duration\"", m->dolzina, u, '"', NULL);
RTV_JSON(odg, sizeloc, "\"jwt\"", m->jwt, s, '"', NULL);
if (RTV_JWT_SIZEOF != m->jwt_sizeof)
RTV_NAPISI(OPOZORILO, "Shranil nepričakovano dolg JWT! Je vdor?");
RTV_JSON(odg, sizeloc, "\"showName\"", m->tip_oddaje_ime, s, '"', NULL);
RTV_JSON(odg, sizeloc, "\"source\"", m->program, s, '"', NULL);
RTV_JSON(odg, sizeloc, "\"publishDate\"", m->objavljeno, s, '"', NULL);
/* uporabimo kar m->slicica za začasno shrambo mediaType (: */
RTV_JSON(odg, sizeloc, "\"mediaType\"", m->slicica, s, '"', NULL);
switch (m->slicica[0]) {
case 'v':
m->tip_posnetka = RTV_VIDEO_TIP;
break;
case 'a':
m->tip_posnetka = RTV_AUDIO_TIP;
break;
default:
m->tip_posnetka = RTV_NEPOZNAN_TIP;
break;
}
/* sedaj pa m->slicica napolnimo s pravilnim tekstom */
RTV_JSON(odg, sizeloc, "\"orig\"", m->slicica, s, '"', NULL);
RTV_JSON(odg, sizeloc, "\"broadcastDate\"", m->predvajano, s, '"', NULL);
m->podnapisi_url[0] = '\0';
RTV_JSON(odg, sizeloc, "\"file\"", m->podnapisi_url, s, '"', NULL);
if (m->podnapisi_url[0] != '\0') {
RTV_HTTPS_V_HTTP(m->podnapisi_url);
}
snprintf(m->get_media_url, RTV_API_MEDIA_URL_SIZEOF, RTV_API_MEDIA_URL, m->id, m->jwt);
rewind(odgstream);
fflush(odgstream);
returnstatus = http_get(m->get_media_url, odgstream); /* shranimo video url */
if (!RTV_HTTP_SUCCESS(returnstatus)) {
RTV_NAPISI(NAPAKA, "Zahteva za ključ videa iz API strežnika je spodletela");
goto rtv_meta_izpolni_returncleanly;
} else returnstatus = 0;
fflush(odgstream);
/*
* sedaj pridobimo direktni URL do mp4 datoteke, ki ima keylockhash v GET
* parametru. OPOMBA: treba je najti NAJKVALITETNEJŠO datoteko, ker RTVSLO
* strežnik pošilja zmešan vrstni red JSON parametrov, bo treba najti drug
* način. ugotovil sem, da, če je stream v addaptiveMedia JSON podobjektu,
* bo ta vedno največji, če pa obstaja samo en stream, pa addaptiveMedia
* podobjekta sploh ne bo. torej, če se je string addaptiveMedia pojavil
* tik pred tem URLjem, bo ta najboljši in lahko nehamo. */
m->posnetek_url[0] = '\0';
RTV_JSON(odg, sizeloc, "\"http\"", m->posnetek_url, s, '"',
"addaptiveMedia");
if (m->posnetek_url[0] != '\0')
goto rtv_meta_izpolni_naselnajboljsistream;
RTV_JSON(odg, sizeloc, "\"https\"", m->posnetek_url, s, '"',
"addaptiveMedia");
if (m->posnetek_url[0] != '\0') {
RTV_HTTPS_V_HTTP(m->posnetek_url);
goto rtv_meta_izpolni_naselnajboljsistream;
}
RTV_JSON(odg, sizeloc, "\"http\"", m->posnetek_url, s, '"', NULL);
if (m->posnetek_url[0] != '\0')
goto rtv_meta_izpolni_naselnajboljsistream;
RTV_JSON(odg, sizeloc, "\"https\"", m->posnetek_url, s, '"', NULL);
if (m->posnetek_url[0] != '\0') {
RTV_HTTPS_V_HTTP(m->posnetek_url);
goto rtv_meta_izpolni_naselnajboljsistream;
}
rtv_meta_izpolni_naselnajboljsistream:
rtv_meta_izpolni_returncleanly:
fclose(odgstream);
free(odg);
odg = NULL;
return returnstatus;
#pragma GCC diagnostic pop
}
int rtv_zivo_izpolni(struct rtv_zivo_meta * m) {
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wnonnull"
#pragma GCC diagnostic ignored "-Wstringop-truncation" /* za RTV_INT_V_SLO(u) */
int returnstatus = 0;
FILE * odgstream;
char * odg;
size_t sizeloc;
size_t i = 0;
char * temp = malloc(sizeof(char)*6);
size_t temp_sizeof = 6;
char * e; /* char pointer for the memes */
int j; /* int for the memes */
RTV_JSON_INIT();
m->kazalo_url[0] = '\0';
snprintf(m->api_url, RTV_API_ZIVO_URL_SIZEOF,
RTV_API_ZIVO_URL, m->program);
odgstream = open_memstream(&odg, &sizeloc);
returnstatus = http_get(m->api_url, odgstream);
if (!RTV_HTTP_SUCCESS(returnstatus)) {
RTV_NAPISI(NAPAKA, "Napaka %d pri zahtevi na API strežnik.", returnstatus);
returnstatus = 1;
goto rtv_zivo_izpolni_returncleanly;
} else returnstatus = 0;
fflush(odgstream);
RTV_JSON(odg, sizeloc, "\"streamer\"", m->seznam_predvajanja_url, s, '"',
NULL);
RTV_HTTPS_V_HTTP(m->seznam_predvajanja_url);
RTV_JSON(odg, sizeloc, "\"streamer\"", m->kazalo_url, s, '"', NULL);
RTV_JSON(odg, sizeloc, "\"streamer\"", m->kos_format, s, '"', NULL);
i = strlen(m->kazalo_url);
m->kazalo_url[i++] = '/'; /* ja, allocata se dodaten ram */
m->kazalo_url[i] = '\0';
i = strlen(m->kos_format);
m->kos_format[i++] = '/'; /* ja, allocata se dodaten ram */
m->kos_format[i] = '\0';
RTV_HTTPS_V_HTTP(m->kazalo_url);
RTV_JSON(odg, sizeloc, "\"file\"", temp, s, '"', NULL);
m->seznam_predvajanja_url_sizeof =
strlen(m->seznam_predvajanja_url)+strlen(temp)+1;
m->seznam_predvajanja_url = realloc(m->seznam_predvajanja_url,
sizeof(char) * m->seznam_predvajanja_url_sizeof);
strcat(m->seznam_predvajanja_url, temp); /* ja, je varno */
/* če obstaja ?DVR na koncu, bo vsebina gzipana, to nas samo moti */
strchrnul(m->seznam_predvajanja_url, '?')[0] = '\0'; /* odstrani parametre */
RTV_HTTPS_V_HTTP(m->seznam_predvajanja_url);
#ifndef RTV_NASTAVITEV_BREZ_LOKACIJSKIH_SPREMEMB
RTV_INT_V_SLO(m->seznam_predvajanja_url);
#endif
rewind(odgstream);
http_get(m->seznam_predvajanja_url, odgstream);
for (i = 0; i < ftell(odgstream); i++) {
if (odg[i] == '\n' && odg[i+1] != '#') {
e = strchr(odg+i+1, '\r');
if (strchr(odg+i+1, '\n') > e) /* 1080p je najvišja resolucija => */
e = strchr(odg+i+1, '\n'); /* prvo kazalo je najvišja resolucija */
if (e != NULL) {
j = e[0];
e[0] = '\0';
}
m->kazalo_url_sizeof = strlen(m->kazalo_url)+strlen(odg+i+1)+1;
m->kazalo_url = realloc(m->kazalo_url, sizeof(char)*m->kazalo_url_sizeof);
strcat(m->kazalo_url, odg+i+1); /* ja, je varno /\ */
if (e != NULL)
e[0] = j; /* popravimo nazaj */
break; /* spet, potrebujemo samo prvi t. i. "chunklist". */
}
}
#ifndef RTV_NASTAVITEV_BREZ_LOKACIJSKIH_SPREMEMB
RTV_INT_V_SLO(m->kazalo_url);
#endif
e = strstr(m->kazalo_url, "_DVR");
if (e != NULL) {
memmove(e, e+strlen("_DVR"), strlen(e+strlen("_DVR"))+1);
RTV_NAPISI(HROSC, "RTV je poslal DVR, čeprav ga nočemo. popravil sem.");
}
RTV_NAPISI(HROSC, "kazalo_url => %s", m->kazalo_url);
rewind(odgstream);
returnstatus = http_get(m->kazalo_url, odgstream);
if (!RTV_HTTP_SUCCESS(returnstatus)) {
RTV_NAPISI(NAPAKA, "Napaka %d pri zahtevi na API strežnik.", returnstatus);
returnstatus = 1;
goto rtv_zivo_izpolni_returncleanly;
} else returnstatus = 0;
e = strstr(odg, "#EXT-X-TARGETDURATION:");
if (e != NULL) {
m->dolzina = atoi(e+strlen("#EXT-X-TARGETDURATION:"));
RTV_NAPISI(HROSC, "(v prvo) dolžina kosa je %u sekund", m->dolzina);
} else {
e = strstr(odg, "#EXTINF:");
if (e != NULL) {
m->dolzina = atoi(e+strlen("#EXTINF:"));
RTV_NAPISI(HROSC, "(v drugo) dolžina kosa je %u sekund", m->dolzina)
} else {
m->dolzina = RTV_ZIVO_P_DOLZINA;
RTV_NAPISI(OPOZORILO, "nisem našel dolžine kosa-uporabim %u", m->dolzina);
}
}
e = strstr(odg, "#EXT-X-MEDIA-SEQUENCE:");
if (e != NULL) {
m->prvi = atoi(e+strlen("#EXT-X-MEDIA-SEQUENCE:")); /* prvi v kazalu */
RTV_NAPISI(HROSC, "našel sekvenčno številko %u", m->prvi);
} else {
RTV_NAPISI(NAPAKA, "pri iskanju MEDIA-SEQUENCE: ne morem narediti formata");
returnstatus = 2;
goto rtv_zivo_izpolni_returncleanly;
}
m->sedanjost = -69420;
m->prenesenih_kosov_preteklost = 0;
m->prenesenih_kosov_prihodnost = 0;
for (i = 0; i < ftell(odgstream); i++) {
if (odg[i+1] != '#' && odg[i] == '\n') {
e = strchr(odg+i+1, '\r');
if (strchr(odg+i+1, '\n') > e)
e = strchr(odg+i+1, '\n');
j = -69420;
if (e != NULL) {
j = e[0];
e[0] = '\0';
}
snprintf(temp, temp_sizeof, "%u", m->prvi);
e = strstr(odg+i+1, temp);
if (e != NULL) {
m->kos_format_sizeof = ((strchr(odg+i+1, '\n')
?(odg+i+1)-strchr(odg+i+1, '\n'):strlen(odg+i+1))+1+RTV_P_SIZEOF
)*sizeof(char);
m->kos_format = realloc(m->kos_format, m->kos_format_sizeof);
strncat(m->kos_format, odg+i+1, m->kos_format_sizeof/sizeof(char));
if (strchr(m->kos_format, '\r')) strchr(m->kos_format, '\r')[0] = '\0';
if (strchr(m->kos_format, '\n')) strchr(m->kos_format, '\n')[0] = '\0';
e = strstr(m->kos_format, temp);
memmove(e+2, e+strlen(temp), strlen(temp)+1); /* naredimo prostor 2 */
e[0] = '%'; e[1] = 'u'; /* napišemo format v prostorček */
RTV_HTTPS_V_HTTP(m->kos_format);
#ifndef RTV_NASTAVITEV_BREZ_LOKACIJSKIH_SPREMEMB
RTV_INT_V_SLO(m->kos_format);
#endif
RTV_NAPISI(HROSC, "m->kos_format => %s", m->kos_format);
}
e = strrchr(m->kos_format+strlen("http://"), '/')+1;
if (strchr(m->kos_format, '%') != NULL && e != NULL) {
sscanf(odg+i+1, e, &(m->sedanjost));
}
if (j != -69420)
*(odg+i+strlen(odg+i)) = j; /* popravimo */
}
}
if (m->sedanjost == -69420) {
RTV_NAPISI(NAPAKA, "ni uspelo pridobiti sedanjosti.");
returnstatus = 3;
goto rtv_zivo_izpolni_returncleanly;
} else {
RTV_NAPISI(HROSC, "sedanjost je %u", m->sedanjost);
}
rtv_zivo_izpolni_returncleanly:
RTV_FREE(temp);
fclose(odgstream);
free(odg);
odg = NULL;
return returnstatus;
#pragma GCC diagnostic pop
}
int main (int argc, char ** argv) {
if (argc < 1+1) {
fprintf(stderr, "preberi README.md pred uporabo programa, saj vsebuje "
"navodila in ostalo.\n");
return 1;
}
struct meta_oddaja * m = meta_oddaja_alloc();
struct rtv_zivo_meta * z = rtv_zivo_meta_alloc();
char fn[420]; /* <id>.txt / <id>.mp4 - NE USER INPUT */
char * e; /* char pointer for the memes */
FILE * fd;
FILE * fd2;
unsigned int i = 0;
time_t * casi = NULL;
time_t cas;
DIR * dir;
unsigned short int returnstatus = 0;
switch (RTV_NACIN[0]) {
case 'O': /* za vse, kar potrebuje ID oddaje, nastavimo ID oddaje */
case 'o':
case 'M':
case 'm':
case 'T':
case 't':
case 'p':
case 'P':
case 's':
case 'S':
RTV_NAPISI(HROSC, "Ta način potrebuje ID oddaje, sedaj ga nastavljam.");
if (RTV_URL != NULL) {
m->id = atoi(RTV_URL);
if (strrchr(RTV_URL, '/') != NULL)
m->id = atoi(strrchr(RTV_URL, '/')+1);
if (m->id <= 0) {
fprintf(stderr, "IDja oddaje ni uspelo dobiti. preverite vnos.\n");
returnstatus = 3;
goto returncleanly;
}
} else {
m->id = RTV_TEST_ID;
}
break;
}
switch (RTV_NACIN[0]) {
case 'T':
case 't': /* test - program se preizkusi brez prenosa datotek */
RTV_NAPISI(NAPAKA, "samotestiranje še ni izdelano!");
break;
case 'O':
case 'o': /* oddaja - prenese oddajo v datoteko */
if (rtv_meta_izpolni(m) != 0) {
RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov oddaje.");
returnstatus = 4;
goto returncleanly;
}
if (argc < 4) {
e = strrchr(m->posnetek_url, '.');
snprintf(fn, 68, "%u%.4s", m->id, e);
fd = fopen(fn, "w");
} else {
fd = fopen(argv[3], "w");
}
if (fd == NULL) {
RTV_NAPISI(NAPAKA, "Ni uspelo odpreti datoteke za pisanje vanjo.");
returnstatus = 5;
goto returncleanly;
}
if (RTV_HTTP_SUCCESS(http_get(m->posnetek_url, fd))) {
fclose(fd);
RTV_NAPISI(INFO, "uspešno shranjeno.");
} else {
fclose(fd);
RTV_NAPISI(NAPAKA, "Nekaj je spodletelo pri http_get()");
returnstatus = 6;
}
break;
case 'M':
case 'm': /* meta-oddaja - prenese metapodatke o oddaji */
if (rtv_meta_izpolni(m) != 0) {
RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov oddaje.");
returnstatus = 4;
goto returncleanly;
}
if (argc < 4) {
/* e = strrchr(m->posnetek_url, '.'); */
snprintf(fn, 68, "%u%s", m->id, ".txt");
fd = fopen(fn, "w");
} else {
fd = fopen(argv[3], "w");
}
if (fd == NULL) {
RTV_NAPISI(NAPAKA, "Ni uspelo odpreti datoteke za pisanje vanjo.");
returnstatus = 5;
goto returncleanly;
}
fprintf(fd, "id: %u\nnaslov: %s\ntip_oddaje_id: %u\nzanri: %s\nopis: %s\n"
"dolzina: %u\njwt: %s\ntip_oddaje_ime: %s\nprogram: %s\n"
"objavljeno: %s\ntip_posnetka: %s\nslicica: %s\npredvajano: %s\n"
/* "velikost: %llu\n" */ "get_meta_url: %s\nget_media_url: %s\n"
"predvajalnik_url: %s\nposnetek_url: %s\npodnapisi_url: %s\n",
m->id, m->naslov, m->tip_oddaje_id, m->zanri, m->opis, m->dolzina,
m->jwt, m->tip_oddaje_ime, m->program, m->objavljeno,
m->tip_posnetka == RTV_VIDEO_TIP ? "RTV_VIDEO_TIP" : \
m->tip_posnetka == RTV_AUDIO_TIP ? "RTV_AUDIO_TIP":"RTV_NEPOZNAN_TIP",
m->slicica, m->predvajano, /* m->velikost, */ m->get_meta_url,
m->get_media_url, m->predvajalnik_url, m->posnetek_url,
m->podnapisi_url);
fclose(fd);
break;
case 's': /* sličica - prenese sličico oddaje */
case 'S':
if (rtv_meta_izpolni(m) != 0) {
RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov oddaje.");
returnstatus = 4;
goto returncleanly;
}
if (argc < 4) {
e = strrchr(m->slicica, '.');
snprintf(fn, 68, "%u%.4s", m->id, e);
fd = fopen(fn, "w");
} else {
fd = fopen(argv[3], "w");
}
if (fd == NULL) {
RTV_NAPISI(NAPAKA, "Ni uspelo odpreti datoteke za pisanje vanjo.");
returnstatus = 5;
goto returncleanly;
}
if (RTV_HTTP_SUCCESS(http_get(m->slicica, fd))) {
fclose(fd);
RTV_NAPISI(INFO, "uspešno shranjeno.");
} else {
fclose(fd);
RTV_NAPISI(NAPAKA, "Nekaj je spodletelo pri http_get()");
returnstatus = 6;
}
break;
case 'p': /* podnapisi */ /* F za Gašperja Tiča, voditelja otroške oddaje */
case 'P': /* Iz popotne torbe, ki je uporabljena kot primer v README.md */
if (rtv_meta_izpolni(m) != 0) {
RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov oddaje.");
returnstatus = 4;
goto returncleanly;
}
if (argc < 4) {
e = strrchr(m->podnapisi_url, '.');
snprintf(fn, sizeof(fn), "%u%s", m->id, e);
fd = fopen(fn, "w");
} else {
fd = fopen(argv[3], "w");
}
if (fd == NULL) {
RTV_NAPISI(NAPAKA, "Ni uspelo odpreti datoteke za pisanje vanjo.");
returnstatus = 5;
goto returncleanly;
}
if (RTV_HTTP_SUCCESS(http_get(m->podnapisi_url, fd))) {
fclose(fd);
RTV_NAPISI(INFO, "uspešno shranjeno.");
} else {
fclose(fd);
RTV_NAPISI(NAPAKA, "Nekaj je spodletelo pri http_get()");
returnstatus = 6;
}
break;
case 'z': /* zivo */
case 'Z':
case RTV_ZCARON: /* živo / Živo, ž = 0xc5be, Ž = 0xc5bd, tudi šivo dela! */
if (argc <= 2) {
strcpy(z->program, "slo1");
} else {
strncpy(z->program, RTV_URL, RTV_ZIVO_PROGRAM_SIZEOF);
}
z->preteklost = argc > 4 ? atoi(argv[4]) : 9999999;
z->prihodnost = argc > 5 ? atoi(argv[5]) : 9999999;
e = argc > 3 ? argv[3] : z->program; /* dirEktorij */
dir = opendir(e);
if (dir) {
closedir(dir); /* direktorij obstaja, hvala */
} else if (errno == ENOENT) {
if (mkdir(e, 0755) != 0) {
RTV_NAPISI(NAPAKA, "med izdelavo direktorija: %s", strerror(errno));
}
} else {
RTV_NAPISI(NAPAKA, "med iskanjem direktorija: %s",
strerror(errno));
returnstatus = 5;
goto returncleanly;
}
if (rtv_zivo_izpolni(z) != 0) {
RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov pretoka.");
returnstatus = 4;
}
casi = realloc(casi, sizeof(time_t)*(z->sedanjost+1));
for (i = z->sedanjost; i >= 0; i--) {
cas = time(NULL);
casi[i] = cas;
if ((z->prenesenih_kosov_preteklost)*(z->dolzina) >= z->preteklost) {
i++;
RTV_NAPISI(INFO, "končal preteklost: kosov: %u, sekund: %u.",
z->prenesenih_kosov_preteklost,
(z->prenesenih_kosov_preteklost)*(z->dolzina));
break;
}
snprintf(fn, sizeof(fn), "%s/%u-%lu%s",
e, i, cas, strrchr(z->kos_format, '.')); /* printf je NULL safe */
fd = fopen(fn, "w");
snprintf(fn, sizeof(fn), z->kos_format, i);
if (!RTV_HTTP_SUCCESS(http_get(fn, fd))) { /* napaka je verjetno 404 */
i++;
RTV_NAPISI(INFO, "ni več kosov v preteklosti");
fclose(fd);
snprintf(fn, sizeof(fn), "%s/%u-%lu%s",
e, i-1, cas, strrchr(z->kos_format, '.'));
unlink(fn);
break;
}
RTV_NAPISI(INFO, "prenesel kos %u iz preteklosti", i);
fclose(fd);
z->prenesenih_kosov_preteklost++;
}
snprintf(fn, sizeof(fn), "%s/seznam_predvajanja.m3u8", e);
fd2 = fopen(fn, "w");
fprintf(fd2, "# generirano z rtv4d-dl " RTV_VER "\n");
for (i = i; i <= z->sedanjost; i++) {
fprintf(fd2, "%u-%lu%s\n", i, casi[i], strrchr(z->kos_format, '.'));
}
free(casi);
casi = NULL;
for (i = (z->sedanjost)+1; 1 == 1; i++) {
cas = time(NULL);
if ((z->prenesenih_kosov_prihodnost)*(z->dolzina) >= z->prihodnost) {
i++;
RTV_NAPISI(INFO, "končal prihodnost: kosov: %u, sekund: %u.",
z->prenesenih_kosov_prihodnost,
(z->prenesenih_kosov_prihodnost)*(z->dolzina));
break;
}
snprintf(fn, sizeof(fn), "%s/%u-%lu%s",
e, i, cas, strrchr(z->kos_format, '.'));
fd = fopen(fn, "w");
snprintf(fn, sizeof(fn), z->kos_format, i);
returnstatus = http_get(fn, fd);
if (!RTV_HTTP_SUCCESS(returnstatus)) {
if (returnstatus != 404) {
RTV_NAPISI(NAPAKA, "strežnik odvrnil %u namesto 200/404.",
returnstatus);
returnstatus = 1;
fclose(fd2);
fclose(fd);
snprintf(fn, sizeof(fn), "%s/%u-%lu%s",
e, i, cas, strrchr(z->kos_format, '.'));
unlink(fn);
goto returncleanly;
break;
}
RTV_NAPISI(INFO, "kos ne obstaja, čakam %u sekund.", (z->dolzina)-1);
sleep(z->dolzina-1);
snprintf(fn, sizeof(fn), "%s/%u-%lu%s",
e, i, cas, strrchr(z->kos_format, '.'));
fclose(fd);
unlink(fn);
i--; /* ponovno poskusimo ta kos */
continue;
}
fprintf(fd2, "%u-%lu%s\n", i, cas, strrchr(z->kos_format, '.'));
RTV_NAPISI(INFO, "prenesel kos %u iz prihodnosti", i);
z->prenesenih_kosov_prihodnost++;
fclose(fd);
returnstatus = 0;
}
fprintf(stdout, "program: %s\nsedanjost: %u\nprvi: %u\ndolzina: %u\n"
"diskrepanca: %u\nprenesenih_kosov_preteklost: %u\n"
"prenesenih_kosov_prihodnost: %u\npreteklost: %u\nprihodnost: %u\n"
"seznam_predvajanja_url: %s\nkazalo_url: %s\nkos_format: %s\n"
"api_url: %s\n", z->program, z->sedanjost, z->prvi, z->dolzina,
z->diskrepanca, z->prenesenih_kosov_preteklost,
z->prenesenih_kosov_prihodnost, z->preteklost, z->prihodnost,
z->seznam_predvajanja_url, z->kazalo_url, z->kos_format, z->api_url);
fclose(fd2);
break;
case 'r': /* skrivna funkcija za razhroščevanje/razvijanje */
if (argc <= 2) {
strcpy(z->program, "slo1");
} else {
strncpy(z->program, RTV_URL, RTV_ZIVO_PROGRAM_SIZEOF);
}
while (1) {
if (rtv_zivo_izpolni(z) != 0) {
RTV_NAPISI(NAPAKA, "Ni uspelo pridobiti metapodatkov pretoka.");
returnstatus = 4;
goto returncleanly;
}
if (i != z->prvi) {
i = z->prvi;
fprintf(stdout, "številka prvega kosa (z->prvi) je po novem %u\n",
i);
fflush(stdout);
}
sleep(9);
}
default:
RTV_NAPISI(NAPAKA, "opcija (%c/%u) ne obstaja!",
RTV_NACIN[0], RTV_NACIN[0]);
returnstatus = 2;
goto returncleanly;
break;
}
returncleanly:
meta_oddaja_free(m); m = NULL;
rtv_zivo_meta_free(z); z = NULL;
return returnstatus;
}
/* VODILO ZA 80 znakovno omejitev - konec sledečega komentarja je 80. znak: */
/* 4567890 (2)01234567890 (4)01234567890 (6)01234567890 */