#if !defined(WIN32) && !defined(_WIN32) && !defined(__WIN32) #error Systems other than Windows are not supported. #endif /* libraries */ #include #include #include #pragma comment(lib, "Shell32") #include #pragma comment(lib, "Shlwapi.lib") #include /* convenience macros */ #define err(code, string) do { fprintf(stderr, "%s: %s: %s\n", string, strerror(errno)); exit(code); } while (0) #define die(...) do { fprintf(stderr, __VA_ARGS__); system("pause"); exit(1); } while (0) #define bool int #define true 1 #define false 0 #define USAGE "usage: %s [/c argument-string] URL\n", argv[0] /* constants */ #define MAX_PATH 260 #define SR_ERR_NOASSOC (HINSTANCE)31 /* global variables */ char out[MAX_PATH]; /* temporary playlist file path */ /* handle break signal (Ctrl-Break) */ void sigbreak(int sig) { /* open playlist at break signal */ if (ShellExecute(NULL, "open", out, NULL, NULL, 5) <= (HINSTANCE)32) die("failed to open %s\n", out); exit(0); } /* main interface */ int main(int argc, char *argv[]) { bool new, title; char *cmd, *extra, temp[MAX_PATH], *url; const char *fmt; FILE *f, *p; HINSTANCE r; int c, i, linecount; ULONGLONG last; signal(SIGBREAK, sigbreak); /* construct temporary playlist file path */ if (GetTempPath(1 + MAX_PATH, temp) > 0 && strlen(temp) + 1 + 16 <= MAX_PATH) { strcpy(out, temp); if (temp[strlen(temp)-1] != '\\') strcat(out, "\\"); } strcat(out, "watch.tmp.mpcpl"); /* parse command-line flags and set default parameters */ if (argc == 1) goto open; /* open previous link */ if (argv[1][0] == '/') { if (argv[1][1] != 'c' || argc != 4) die(USAGE); extra = argv[2]; url = argv[3]; } else { if (argc != 2) die(USAGE); extra = "-f \"bestvideo[height<=1200],bestaudio/best\""; url = argv[1]; } /* support watch: urls */ if (strncmp(url, "watch:", 6) == 0) url += 6; /* support cloudtube urls */ if (strncmp(url, "https://cadence.moe/cloudtube/video/", 36) == 0) { url += 36 - 28; memcpy(url, "https://youtube.com/watch?v=", 28); } else if (strncmp(url, "https://cadence.moe/cloudtube/", 30) == 0) { url += 30 - 20; memcpy(url, "https://youtube.com/", 20); } /* validate url format */ for (i = 0; i < strlen(url); i++) if (url[i] == '"') die("URL cannot contain quotes (\")\n"); /* start youtube-dl */ fmt = "youtube-dl -eg --youtube-skip-dash-manifest %s \"%s\""; cmd = malloc(0 + (strlen(fmt) + strlen(url) + strlen(extra)) * sizeof(char)); if (cmd == NULL) err(1, "malloc"); if (sprintf(cmd, fmt, extra, url) < 0) err(1, "sprintf"); p = _popen(cmd, "r"); if (p == NULL) err(1, "popen"); /* open temporary playlist file */ f = fopen(out, "w"); if (f == NULL) err(1, "fopen"); /* read/write stream urls (from youtube-dl, to temporary playlist file) */ linecount = 0; new = true; title = false; last = GetTickCount(); c = fgetc(p); if (c == EOF) goto end; fprintf(f, "MPCPLAYLIST\n"); goto loop; /* In the output from youtube-dl, each line contains a video or audio stream * belonging to a specific video. The only way to tell which streams belong * to the same video is to count the time between the output of each line. If * there is a pause between two lines, then the second one belongs to a new * video. */ while ((c = fgetc(p)) != EOF) { loop: if (new) { new = false; if (GetTickCount() - last > 20) { /* new video */ title = true; linecount++; fprintf(stderr, "Retrieving video #%d\n", linecount); fprintf(f, "%d,type,0\n", linecount); fprintf(f, "%d,label,", linecount); } else fprintf(f, "%d,filename,", linecount); } fputc(c, f); if (c == '\n') { last = GetTickCount(); new = true; } } /* check status of youtube-dl command */ end: if (i = _pclose(p)) /* if unsuccessful */ if (i != -1073741510) /* and it wasn't due to Ctrl-Break (handled above) */ die("youtube-dl exited with %d\n", i); /* open playlist */ open: if (ShellExecute(NULL, "open", out, NULL, NULL, 5) <= (HINSTANCE)32) die("failed to open %s\n", out); return 0; }