summaryrefslogblamecommitdiffstats
path: root/rtv4d-dl.c
blob: 1428a6977984fdcd1409da0cb5a17039fd4fba86 (plain) (tree)
1
2
3
4
5
6
7
8
9
10
11





                   




                      
                 






















                                                                                     


                                                                               










                                                                                                                                                          
                                                                           






                                                                                     

                                                                        

                                                                
                                                                                


                                                                           

                                                               
                       























































                                                                                                             























                                                                                       
                                                                             




                                                                                      

                                                                                      


                                                                    













                                                                                   


                                               














































                                                                                        



















                                                                                      
                                   





                                                   
                                                           





















                                                                                       

                                               





                                                              
                                                           



                                                                                      
                                                                           





                                                                                       
                                                       





                                                                                          


                                                                             







                                                                                       
                                                                                          




                                                                         
                                                                                          
                
                                                                                







                                                                                   
                                                                              








                                                                                       


                                             


                                              

                                          



                             
                        



                                                                                                

                                                                                        
                                                    
                                
                          


























                                                                                     
                 






                                                                                 




                                                                                               
                                              

                                                                                              
                                
                          







                                                                                     


















                                                                           
                                              





                                       
                          
 















































































































































                                                                                                        

                                   

                                                                                      


                                                     

                                                               

                                                  
                   
                       

                             
                  
                                            
























                                                                                                           

                               
                         


                                                                                
                         

















                                                                                                   

                                                                              

                                                                                
                                           



                                                                                         
                         




















                                                                                                         
                                                                                                        


                                                                                                          


                                                                                                              

                                                                                               


                                                                  
                         
















                                                                                                   

                                                                         

                                                                                




























                                                                                                   



                                                                                         

                                    


























                                                                                                            











                                                                                                                  


                                                                                                       
                                            

                                                                                        


                                                                                                



                                                                                      
                                                                 

                                                                                  

                                                                              
                                                             












































                                                                                                              
                         
                                    
                              
                        

                                                                        




                                           

                                        

                            

                                                                                
#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_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 69 /* 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_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 */
	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);
	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 (!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"
	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");
		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;
	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;
			}
			for (i = z->sedanjost; i >= 0; i--) {
				casi = realloc(casi, sizeof(time_t)*i+1); /* NE GOTO PRED SPROŠČENJEM */
				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, 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;
			}
			fclose(fd2);
			break;
		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        */