aboutsummaryrefslogtreecommitdiff
path: root/watch.c
blob: 9f097d21337ec3f5212f58b5824ea677ee81812e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#if !defined(WIN32) && !defined(_WIN32) && !defined(__WIN32)
#error Systems other than Windows are not supported.
#endif

/* libraries */

#include <windows.h>
#include <signal.h>

#include <shellapi.h>
#pragma comment(lib, "Shell32")
#include <shlwapi.h>
#pragma comment(lib, "Shlwapi.lib")

#include <stdio.h>

/* 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;
}