diff options
Diffstat (limited to '')
-rw-r--r-- | rtv4d-dl.c | 692 |
1 files changed, 476 insertions, 216 deletions
@@ -4,6 +4,11 @@ #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> #define NIZ_DODATEK(a) #a #define NIZ(a) NIZ_DODATEK(a) #define RTV_CLIENT_ID "82013fb3a531d5414f478747c1aca622" /* enak za vse */ @@ -27,8 +32,9 @@ #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 4D_VIDEO_TIP -#define RTV_TEST_SLICICA "http://img.rtvslo.si/_up/ava/ava_misc/show_logos" \ +#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 @@ -48,18 +54,73 @@ "\r\nAccept: */*\r\nX-Requested-With: tcp.c (" __FILE__ ":" NIZ(__LINE__) \ ")\r\nConnection: close\r\n\r\n" #define RTV_HTTP_TIMEOUT 69 /* sekund */ -#define RTV_NE_BO_POSLAL NULL /* "ToJeMogočeBackdoor!" */ /* bere več znakov */ -#define RTV_API_META_URL "http://api.rtvslo.si/ava/getRecordingDrm/" \ +#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.rtvslo.si/ava/getMedia/%u/?client_id=" \ +#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.rtvslo.si/arhiv/v/%u" -#define RTV_PREDVAJALNIK_URL_SIZEOF 32+1 + 12 +#define RTV_PREDVAJALNIK_URL "http://4d." RTV_SLD "/arhiv/v/%u" +#define RTV_PREDVAJALNIK_URL_SIZEOF (32+1 + 12) #define RTV_VER "0.0.3" - +#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 struct meta_oddaja { size_t naslov_sizeof; char * naslov; /* Vreme ob 22h */ @@ -90,37 +151,75 @@ struct meta_oddaja { 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)); - m->naslov = malloc(sizeof(char)*RTV_P_SIZEOF);m->naslov_sizeof = RTV_P_SIZEOF; - m->zanri = malloc(sizeof(char)*RTV_P_SIZEOF); m->zanri_sizeof = RTV_P_SIZEOF; - m->opis = malloc(sizeof(char)*RTV_P_SIZEOF); m->opis_sizeof = RTV_P_SIZEOF; - m->tip_oddaje_ime=malloc(sizeof(char)*RTV_P_SIZEOF); - m->tip_oddaje_ime_sizeof = RTV_P_SIZEOF; - m->program = malloc(sizeof(char)*RTV_P_SIZEOF);m->program_sizeof=RTV_P_SIZEOF; - m->slicica = malloc(sizeof(char)*RTV_P_SIZEOF);m->slicica_sizeof=RTV_P_SIZEOF; - m->jwt = malloc(sizeof(char)*RTV_P_SIZEOF); m->jwt_sizeof=RTV_P_SIZEOF; - m->objavljeno = malloc(sizeof(char)*RTV_P_SIZEOF); - m->objavljeno_sizeof = RTV_P_SIZEOF; - m->predvajano = malloc(sizeof(char)*RTV_P_SIZEOF); - m->predvajano_sizeof = RTV_P_SIZEOF; - m->posnetek_url = malloc(sizeof(char)*RTV_P_SIZEOF); - m->posnetek_url_sizeof = RTV_P_SIZEOF; +#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) { - free(m->naslov); m->naslov = NULL; - free(m->zanri); m->zanri = NULL; - free(m->opis); m->opis = NULL; - free(m->tip_oddaje_ime); m->tip_oddaje_ime = NULL; - free(m->program); m->program = NULL; - free(m->slicica); m->slicica = NULL; - free(m->jwt); m->jwt = NULL; - free(m->objavljeno); m->objavljeno = NULL; - free(m->predvajano); m->predvajano = NULL; - free(m->posnetek_url); m->posnetek_url = NULL; - free(m); m = NULL; + 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) { @@ -141,6 +240,7 @@ int http_get (/* const */ char * u, FILE * r) { /* url ostane enak */ 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!"); @@ -170,33 +270,36 @@ int http_get (/* const */ char * u, FILE * r) { /* url ostane enak */ } t = *k; *k = '\0'; - RTV_NAPISI(INFO, "Vzpostavljam TCP povezavo na http://%s:%d ...", - RTV_URL_HOST, p); + 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(INFO, "Povezan na strežnik."); + 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(INFO, "Pošiljam %lu bajtov ...", strlen(request_buf)); + 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(INFO, "Uspešno poslano."); + 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!"); @@ -205,15 +308,15 @@ int http_get (/* const */ char * u, FILE * r) { /* url ostane enak */ if (strncasecmp("Content-Length: ", header, strlen("Content-Length: ")) == 0) { contentlength = strtoll(header+16, NULL, 10); - RTV_NAPISI(INFO, "Pridobil %lld bajtni odgovor.", contentlength); + RTV_NAPISI(HROSC, "Pridobil %lld bajtni odgovor.", contentlength); } if (strncmp("\r\n", header, 2) == 0 || header[0] == '\n') break; } if (contentlength == -69420) { - RTV_NAPISI(OPOZORILO, "Manjka Content-Length, berem do konca."); + RTV_NAPISI(HROSC, "Manjka Content-Length, berem do konca."); /* legalno */ } else { - RTV_NAPISI(INFO, "Začetek telesa odgovora. Berem in pišem."); + 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); @@ -222,7 +325,7 @@ int http_get (/* const */ char * u, FILE * r) { /* url ostane enak */ RTV_NAPISI(NAPAKA, "Branje in pisanje telesa odgovora ni uspelo!"); goto http_get_returncleanly; } - RTV_NAPISI(INFO, "Prejel in uspešno shranil/prebral HTTP odgovor."); + RTV_NAPISI(HROSC, "Prejel in uspešno shranil/prebral HTTP odgovor."); http_get_returncleanly: free(request_buf); request_buf = NULL; @@ -232,138 +335,72 @@ int http_get (/* const */ char * u, FILE * r) { /* url ostane enak */ 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; - size_t i, j; - unsigned short int nasel_addaptivemedia = 0; /* glej iskanje stream URLja */ - char * metakeys[] = {"\"title\"", "\"showId\"", "\"genre\"", \ - "\"description\"", "\"showDescription\"", "\"duration\"", "\"jwt\"", \ - "\"showName\"", "\"source\"", "\"publishDate\"", "\"mediaType\"", \ - "\"orig\"", "\"broadcastDate\"", /* getMedia query: */ "\"http\"", \ - "\"https\"", "\"addaptiveMedia\"" }; - char * cp; -#define RTV_META_IZPOLNI_METAKEYS_SIZEOF 13 /* hkrati offset za getMedia */ -#define RTV_META_IZPOLNI_METAKEYS_GETMEDIA_FINAL \ - (RTV_META_IZPOLNI_METAKEYS_SIZEOF+3) /* zadnji+1 za getMedia */ -#define RTV_META_IZPOLNI_VALUE (odg+i+strlen(metakeys[j])+2) -#define RTV_META_IZPOLNI_VALUE_INTERNAL RTV_META_IZPOLNI_VALUE + 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 (returnstatus != 0) { - RTV_NAPISI(NAPAKA, "Zahteva za metapodatke iz API strežnika je spodletela"); + 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); - for (i = 0; i < sizeloc; i++) { - for (j = 0; j < RTV_META_IZPOLNI_METAKEYS_SIZEOF; j++) { - if (strncmp(odg+i, metakeys[j], strlen(metakeys[j])) == 0) { - switch (j) { - case 0: /* title */ -#define strtollu strtoull -#define strtolu strtoul -#define strtolld strtoll -#define strtold strtol -#define strtod strtol -#define strtou strtoul -#define RTV_META_IZPOLNI_V(imem, format, tip) /* za variabilne nize */ \ - cp[0] = '\0'; /* omejimo json string, ne potrebujemo strncpy */ \ - m->imem = realloc(m->imem, sizeof(tip) * \ - strlen(RTV_META_IZPOLNI_VALUE_INTERNAL)+1); \ - m->imem##_sizeof = strlen(RTV_META_IZPOLNI_VALUE_INTERNAL)+1; \ - strcpy(m->imem, RTV_META_IZPOLNI_VALUE_INTERNAL); -#define RTV_META_IZPOLNI_I(imem, format, tip) /* za številke */ \ - m->imem = strto##format(RTV_META_IZPOLNI_VALUE_INTERNAL, NULL, 10); -#define RTV_META_IZPOLNI(imem, oblika, format, splitter, tip) \ - do { cp = strchr(RTV_META_IZPOLNI_VALUE_INTERNAL, splitter); \ - if (cp == NULL) { \ - RTV_NAPISI(OPOZORILO, "Napaka pri iskanju podatka " #imem); \ - /* strcpy(m->imem, "Napaka pri pridobivanju."); */ /* ni vse s*/ \ - } else { \ - oblika(imem, format, tip) \ - RTV_NAPISI(HROSC, "Shranil metapodatek " #imem ": %" #format, \ - m->imem); \ - } } while (0) -/* #pragma GCC diagnostic ignored "-Wint-conversion" */ /* surpressaš opozor. */ - RTV_META_IZPOLNI(naslov, RTV_META_IZPOLNI_V, s, '"', char); - break; - case 1: /* showId */ - RTV_META_IZPOLNI(tip_oddaje_id, RTV_META_IZPOLNI_I, u, '"', unsigned int); - break; - case 2: /* genre je "asdasd","asdasd","sdfsfd" */ - RTV_META_IZPOLNI(zanri, RTV_META_IZPOLNI_V, s, ']', char); - memmove(m->zanri, (m->zanri)+1, strlen(m->zanri)+1); - break; - case 3: /* description */ - RTV_META_IZPOLNI(opis, RTV_META_IZPOLNI_V, s, '"', char); - break; - case 4: /* showDescription */ - if (m->opis[0] == '\0') { /* če je prejšnji spodletel */ - RTV_META_IZPOLNI(opis, RTV_META_IZPOLNI_V, s, '"', char); - } - break; - case 5: /* duration */ -#undef RTV_META_IZPOLNI_VALUE_INTERNAL -#define RTV_META_IZPOLNI_VALUE_INTERNAL RTV_META_IZPOLNI_VALUE-1 - RTV_META_IZPOLNI(dolzina, RTV_META_IZPOLNI_I, u, '"', unsigned int); -#undef RTV_META_IZPOLNI_VALUE_INTERNAL -#define RTV_META_IZPOLNI_VALUE_INTERNAL RTV_META_IZPOLNI_VALUE - break; - case 6: /* jwt */ - RTV_META_IZPOLNI(jwt, RTV_META_IZPOLNI_V, s, '"', char); - if (RTV_JWT_SIZEOF != m->jwt_sizeof) { - RTV_NAPISI(OPOZORILO, "Shranil nepričakovano dolg JWT! Je vdor?"); - } - break; - case 7: /* showName */ - RTV_META_IZPOLNI(tip_oddaje_ime, RTV_META_IZPOLNI_V, s, '"', char); - break; - case 8: /* source */ - RTV_META_IZPOLNI(program, RTV_META_IZPOLNI_V, s, '"', char); - break; - case 9: /* publishDate */ - RTV_META_IZPOLNI(objavljeno, RTV_META_IZPOLNI_V, s, '"', char); - break; - case 10: /* mediaType */ - switch (RTV_META_IZPOLNI_VALUE[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; - } - break; - case 11: /* orig */ /* sličica */ - RTV_META_IZPOLNI(slicica, RTV_META_IZPOLNI_V, s, '"', char); - break; - case 12: /* broadcastDate */ - RTV_META_IZPOLNI(predvajano, RTV_META_IZPOLNI_V, s, '"', char); - break; -/* #pragma GCC diagnostic warning "-Wint-conversion" */ /* undo */ - default: - RTV_NAPISI(OPOZORILO, "Doseg nedefinirane kode!"); - } - } + 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); + memmove(m->zanri, (m->zanri)+1, strlen(m->zanri)+1); + 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 (returnstatus != 0) { + 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 @@ -373,45 +410,25 @@ int rtv_meta_izpolni(struct meta_oddaja * m) { * 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. */ - for (i = 0; i < ftell(odgstream); i++) { - for (j = RTV_META_IZPOLNI_METAKEYS_SIZEOF; - j < RTV_META_IZPOLNI_METAKEYS_GETMEDIA_FINAL; j++) { - if (strncmp(odg+i, metakeys[j], strlen(metakeys[j])) == 0) { - switch (j-RTV_META_IZPOLNI_METAKEYS_SIZEOF) { - case 0: /* http */ /* videofile */ - RTV_META_IZPOLNI(posnetek_url, RTV_META_IZPOLNI_V, s, '"', char); - if (nasel_addaptivemedia == 1) { - RTV_NAPISI(HROSC, "Izmed več streamov izbral najboljšega."); - goto rtv_meta_izpolni_naselnajboljsistream; - } - nasel_addaptivemedia = 0; - break; - case 1: /* https */ - if (m->posnetek_url[0] == '\0') { - RTV_META_IZPOLNI(posnetek_url, RTV_META_IZPOLNI_V, s, '"', - char); - fprintf(stderr, "test: %s\n", m->posnetek_url); - memmove((m->posnetek_url)+4, (m->posnetek_url)+5, - strlen((m->posnetek_url)+5)+1); - RTV_NAPISI(HROSC, "Popravil HTTPS URL na HTTP"); - if (nasel_addaptivemedia == 1) { - RTV_NAPISI(HROSC, "Izmed več streamov izbral najboljšega."); - goto rtv_meta_izpolni_naselnajboljsistream; - } - } - nasel_addaptivemedia = 0; - break; - case 2: /* addaptiveMedia */ - nasel_addaptivemedia = 1; - RTV_NAPISI(HROSC, "Naslednji najden pretok bo najboljši."); - break; - default: - RTV_NAPISI(OPOZORILO, "Doseg nedefinirane kode; case=%lu", - j-RTV_META_IZPOLNI_METAKEYS_SIZEOF); - } - } - } - } /* endfor: for (i = 0; i < ftell(odgstream); i++) */ + 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: @@ -419,28 +436,191 @@ int rtv_meta_izpolni(struct meta_oddaja * m) { 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" + 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(); + 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); + m->kazalo_url[strlen(m->kazalo_url)] = '/'; /* ja, allocata se dodaten ram */ + m->kos_format[strlen(m->kos_format)] = '/'; /* ja, allocata se dodaten ram */ + 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); + 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". */ + } + } + 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" + ". poskusite znova nekajkrat. je RTV strežnik spet GZIPpal? :shrug:"); + 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); + 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"); + fprintf(stderr, "preberi README.md pred uporabo programa, saj vsebuje" + "navodila in ostalo.\n"); return 1; } struct meta_oddaja * m = meta_oddaja_alloc(); - char fn[69]; /* <id>.txt / <id>.mp4 - NE USER INPUT */ + 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; + unsigned int i; + DIR * dir; unsigned short int returnstatus = 0; - 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; + 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': @@ -466,9 +646,11 @@ int main (int argc, char ** argv) { returnstatus = 5; goto returncleanly; } - if (http_get(m->posnetek_url, fd) == 0) { + 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; } @@ -496,13 +678,14 @@ int main (int argc, char ** argv) { "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\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, + "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->get_media_url, m->predvajalnik_url, m->posnetek_url, + m->podnapisi_url); fclose(fd); break; case 's': /* sličica - prenese sličico oddaje */ @@ -524,26 +707,103 @@ int main (int argc, char ** argv) { returnstatus = 5; goto returncleanly; } - if (http_get(m->slicica, fd) == 0) { + 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 0xC5: /* živo / Živo, ž = 0xc5be, Ž = 0xc5bd, tudi šivo dela! */ - RTV_NAPISI(NAPAKA, "pretakanje v živo še ni izdelano!"); + 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; + } + for (i = z->sedanjost; i >= 0; i--) { + snprintf(fn, sizeof(fn), "%s/%u%s", + e, i, 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); + break; + } + RTV_NAPISI(INFO, "prenesel kos %u iz preteklosti", i); + fclose(fd); + } + snprintf(fn, sizeof(fn), "%s/seznam_predvajanja.m3u8", e); + fd = fopen(fn, "w"); + fprintf(fd, "# generirano z rtv4d-dl " RTV_VER "\n"); + for (i = i; i <= z->sedanjost; i++) { + fprintf(fd, "%u%s\n", i, strrchr(z->kos_format, '.')); + } break; default: - fprintf(stderr, "opcija ne obstaja! poskusi test.\n"); + 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; + 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 */ |