#define _GNU_SOURCE #include #include #include #include #include #include #include #include #include #include #include #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]; /* .txt / .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); } 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 */