<commit_before>var FS = require('fs');
var GM = require('gm');
var Fabric = require('fabric').fabric;
var Cloner = require('cloneextend');
var argv = require('optimist').argv;

//Values obtained from contemporary image markup dialog, 2013-02-18
var colorValues = {
  'red': 'rgba(193,40,11,0.93)',
  'orange': 'rgba(355,144,36,0.93)',
  'yellow': 'rgba(243,224,14,0.93)',
  'green': 'rgba(22,220,129,0.93)',
  'blue': 'rgba(35,67,232,0.93)',
  'violet': 'rgba(220,84,183,0.93)',
  'black': 'rgba(0,0,0,0.93)'
};

function usage(err) {
  console.log('Example Usage:');
  console.log('node imagemarkup.js [--help|-h] - Show this information');
  console.log('node imagemarkup.js --input infile.jpg --output outfile.jpg --markup \'[markup_string]\'');
  console.log('node imagemarkup.js --json \'[json_string]\'');
  //TODO: "See README file for specification"
  if (err)
    process.exit(err);
  else
    process.exit(0);
}

function processArgs() {
  if (argv.help || argv.h) {
    usage();
  }

  var json;
  if (argv.json && argv.markup) {
    console.error('Invalid usage.');
    usage(-1);
  } else if (argv.json) {
    json = JSON.parse(argv.json);
    cleanJSON(json);
    processImage(json);
  } else if (argv.markup) {
    if (!argv.input || !argv.output) {
      console.error('Invalid usage. Input or output path missing.');
      usage(-1);
    }
    convertMarkupToJSON(argv.markup, argv.input, argv.output);
  } else {
    console.error('Invalid uage.');
    usage(-1);
  }
}

function validateJSON(json, level) {
  String.prototype.repeat = function (num) {
    return new Array(num+1).join(this);
  }
  if (!level) {
    console.log("{");
    validateJSON(json, 1);
    console.log("}");
  } else {
    for (property in json) {
      console.log("\t".repeat(level) + property + ": " + json[property] + " [" + typeof(json[property]) + "]");
      if (typeof(json[property]) == 'object') {
        console.log("\t".repeat(level) + "{");
        validateJSON(json[property], level+1);
        console.log("\t".repeat(level) + "}");
      }
    }
  }
}

/**
 * Cycles through an object and changes all numeric fields to ints
 * where necessary.
 */
function cleanJSON(json) {
  for (property in json) {

    if (typeof(json[property]) == 'object') {
      cleanJSON(json[property]);
    }
    else if (property == 'x' || property == 'y' ||
             property == 'width' || property == 'height' ||
             property == 'radius') {
      if (typeof(json[property]) == 'string') {
        json[property] = parseInt(json[property]);
      }
    }
  }
}

function convertMarkupToJSON(markup, infile, outfile) {
  var json = {};

  GM(infile).size(function (err, size) {
    if (err) throw err;

    json['dimensions'] = size;
    json['finalDimensions'] = size;

    json['instructions'] = {};

    var instructions = markup.split(";");
    for (var i = 0; i < instructions.length; ++i) {
      if (instructions[i] == '') continue;

      var args = instructions[i].split(",");
      var command = args[0];
      switch (command) {
        case 'crop':
          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var dimensions = args[2].split("x");
          dimensions[0] = parseInt(dimensions[0]);
          dimensions[1] = parseInt(dimensions[1]);
          var size = {
            'width': dimensions[0],
            'height': dimensions[1]
          };

          var crop = {};
          crop['from'] = from;
          crop['size'] = size;

          json['instructions']['crop'] = crop;
          json['finalDimensions'] = crop['size'];
          break;
        case 'circle':
          if (!json['instructions']['draw']) json['instructions']['draw'] = new Array();

          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var radius = parseInt(args[2]);
          var color = args[3];

          var circle = {};
          circle['from'] = from;
          circle['radius'] = radius;
          circle['color'] = color;

          json['instructions']['draw'].push({'circle': circle});
          break;
        case 'rectangle':
          if (!json['instructions']['draw']) json['instructions']['draw'] = new Array();

          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var dimensions = args[2].split("x");
          dimensions[0] = parseInt(dimensions[0]);
          dimensions[1] = parseInt(dimensions[1]);
          var size = {
            'width': dimensions[0],
            'height': dimensions[1]
          };

          var color = args[3];

          var rectangle = {
            'from': from,
            'size': size,
            'color': color
          };

          json['instructions']['draw'].push({'rectangle': rectangle});
          break;
        default:
      }
    }

    json['sourceFile'] = infile;
    json['destinationFile'] = outfile;

    processImage(json);
  });
}

function processImage(json) {
  var canvas = Fabric.createCanvasForNode(json['finalDimensions']['width'],json['finalDimensions']['height']);

  var imagePath = json['sourceFile'];

  var crop = json['instructions']['crop'];
  var imageOffset = crop != "undefined" ?
   {'x':crop['from']['x'],
    'y':crop['from']['y']} :
   {'x':0,'y':0};

  function applyBackground(canvas) {
    FS.readFile(imagePath, function (err, blob) {
      if (err) throw err;

      var dimensions = json['dimensions'];
      var finalDimensions = json['finalDimensions'];
      img = {'width': dimensions['width'], 'height': dimensions['height'], 'src':blob};

      Fabric.Image.fromObject(img, function(fimg) {
        top = img.height/2 - imageOffset['y'];
        if (top % 1 != 0) {
          top -= 0.5;
        }
        left = img.width / 2 - imageOffset['x'];
        if (left % 1 != 0) {
          left -= 0.5;
        }

        canvas.add(fimg.set('top', top).set('left', left));

        applyMarkup(canvas);
      });
    });
  }

  function applyMarkup(canvas) {
    var size = json['dimensions'];

    for (instruction in json['instructions']) {
      switch (instruction) {
        case 'draw':
          json['instructions']['draw'].forEach(function (e) {
            for (shapeName in e) {
              switch (shapeName) {
                case 'rectangle':
                  shape = e[shapeName];
                  drawRectangle(json, canvas, shape, imageOffset);
                  break;
                case 'circle':
                  shape = e[shapeName];
                  drawCircle(json, canvas,shape, imageOffset);
                  break;
                default:
                  console.error('Unsupported Shape: ' + shapeName);
              }
            }

            writeCanvas(canvas);
          });
          break;
        case 'crop':
          //もう処理していたから無視する
          break;
        default:
          console.error('Unsupported Instruction: ' + instruction);
      }
    }
  }

  function writeCanvas(canvas) {
    var outstream = FS.createWriteStream(json['destinationFile']),
        stream = canvas.createJPEGStream({
          quality: 93
        });

    stream.on('data', function(chunk) {
      outstream.write(chunk);
    });
  }

  applyBackground(canvas);
}

function drawRectangle(json, canvas, shape, imageOffset) {
  shape['stroke'] = Math.max(Math.round(json['finalDimensions']['width'] / 300 * 2), 2);
  whiteStroke = Math.max(Math.round(shape['stroke'] / 4), 1);

  var rect = {
    left: shape['from']['x']-imageOffset['x'],
    top: shape['from']['y']-imageOffset['y'],
    width: shape['size']['width'],
    height: shape['size']['height'],
    rx: 1,
    ry: 1,
    strokeWidth: shape['stroke'],
    stroke: colorValues[shape['color']],
    fill: 'transparent'
  }

  //Fabric調整
  rect['top'] = rect['top'] + rect['height'] / 2 + rect['strokeWidth'] / 2;
  rect['left'] = rect['left'] + rect['width'] / 2 + rect['strokeWidth'] / 2;

  var fillRect = Cloner.clone(rect);

  fillRect['fill'] = fillRect['stroke'].replace(/\d\.\d\d\)/, '0.05\)');
  fillRect['stroke'] = 'transparent';

  var rectBorder = Cloner.clone(rect);
  rectBorder['width'] += rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectBorder['height'] += rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectBorder['rx'] = 5;
  rectBorder['ry'] = 5;
  rectBorder['strokeWidth'] = whiteStroke;
  rectBorder['stroke'] = 'rgba(255,255,255,0.5)';

  var rectShadow = Cloner.clone(rect);
  rectShadow['left'] += 7;
  rectShadow['top'] += 7;
  rectShadow['stroke'] = 'rgba(0,0,0,0.5)';

  canvas.add(new Fabric.Rect(rectShadow));
  canvas.add(new Fabric.Rect(fillRect));
  canvas.add(new Fabric.Rect(rect));
  canvas.add(new Fabric.Rect(rectBorder));
}

function drawCircle(json, canvas, shape, imageOffset) {
  shape['stroke'] = Math.max(Math.round(shape['radius'] / 4), 5);
  whiteStroke = 1;

  var circle = {
    left: shape['from']['x']-imageOffset['x'],
    top: shape['from']['y']-imageOffset['y'],
    radius: shape['radius'],
    strokeWidth: shape['stroke'],
    stroke: colorValues[shape['color']],
    fill: 'transparent'
  };
  var circleBorder = Cloner.clone(circle);
  circleBorder['radius'] = circle['radius'] + circle['strokeWidth'] / 2;
  circleBorder['strokeWidth'] = whiteStroke;
  circleBorder['stroke'] = 'rgba(255,255,255,0.95';
  var circleShadow = Cloner.clone(circle);
  circleShadow['left'] += 7;
  circleShadow['top'] += 7;
  circleShadow['stroke'] = 'rgba(0,0,0,0.5)';

  canvas.add(new Fabric.Circle(circleShadow));
  canvas.add(new Fabric.Circle(circle));
  canvas.add(new Fabric.Circle(circleBorder));
}

processArgs();
<commit_msg>Fix instruction with only crop command<commit_after>var FS = require('fs');
var GM = require('gm');
var Fabric = require('fabric').fabric;
var Cloner = require('cloneextend');
var argv = require('optimist').argv;

//Values obtained from contemporary image markup dialog, 2013-02-18
var colorValues = {
  'red': 'rgba(193,40,11,0.93)',
  'orange': 'rgba(355,144,36,0.93)',
  'yellow': 'rgba(243,224,14,0.93)',
  'green': 'rgba(22,220,129,0.93)',
  'blue': 'rgba(35,67,232,0.93)',
  'violet': 'rgba(220,84,183,0.93)',
  'black': 'rgba(0,0,0,0.93)'
};

function usage(err) {
  console.log('Example Usage:');
  console.log('node imagemarkup.js [--help|-h] - Show this information');
  console.log('node imagemarkup.js --input infile.jpg --output outfile.jpg --markup \'[markup_string]\'');
  console.log('node imagemarkup.js --json \'[json_string]\'');
  //TODO: "See README file for specification"
  if (err)
    process.exit(err);
  else
    process.exit(0);
}

function processArgs() {
  if (argv.help || argv.h) {
    usage();
  }

  var json;
  if (argv.json && argv.markup) {
    console.error('Invalid usage.');
    usage(-1);
  } else if (argv.json) {
    json = JSON.parse(argv.json);
    cleanJSON(json);
    processImage(json);
  } else if (argv.markup) {
    if (!argv.input || !argv.output) {
      console.error('Invalid usage. Input or output path missing.');
      usage(-1);
    }
    convertMarkupToJSON(argv.markup, argv.input, argv.output);
  } else {
    console.error('Invalid uage.');
    usage(-1);
  }
}

function validateJSON(json, level) {
  String.prototype.repeat = function (num) {
    return new Array(num+1).join(this);
  }
  if (!level) {
    console.log("{");
    validateJSON(json, 1);
    console.log("}");
  } else {
    for (property in json) {
      console.log("\t".repeat(level) + property + ": " + json[property] + " [" + typeof(json[property]) + "]");
      if (typeof(json[property]) == 'object') {
        console.log("\t".repeat(level) + "{");
        validateJSON(json[property], level+1);
        console.log("\t".repeat(level) + "}");
      }
    }
  }
}

/**
 * Cycles through an object and changes all numeric fields to ints
 * where necessary.
 */
function cleanJSON(json) {
  for (property in json) {

    if (typeof(json[property]) == 'object') {
      cleanJSON(json[property]);
    }
    else if (property == 'x' || property == 'y' ||
             property == 'width' || property == 'height' ||
             property == 'radius') {
      if (typeof(json[property]) == 'string') {
        json[property] = parseInt(json[property]);
      }
    }
  }
}

function convertMarkupToJSON(markup, infile, outfile) {
  var json = {};

  GM(infile).size(function (err, size) {
    if (err) throw err;

    json['dimensions'] = size;
    json['finalDimensions'] = size;

    json['instructions'] = {};

    var instructions = markup.split(";");
    for (var i = 0; i < instructions.length; ++i) {
      if (instructions[i] == '') continue;

      var args = instructions[i].split(",");
      var command = args[0];
      switch (command) {
        case 'crop':
          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var dimensions = args[2].split("x");
          dimensions[0] = parseInt(dimensions[0]);
          dimensions[1] = parseInt(dimensions[1]);
          var size = {
            'width': dimensions[0],
            'height': dimensions[1]
          };

          var crop = {};
          crop['from'] = from;
          crop['size'] = size;

          json['instructions']['crop'] = crop;
          json['finalDimensions'] = crop['size'];
          break;
        case 'circle':
          if (!json['instructions']['draw']) json['instructions']['draw'] = new Array();

          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var radius = parseInt(args[2]);
          var color = args[3];

          var circle = {};
          circle['from'] = from;
          circle['radius'] = radius;
          circle['color'] = color;

          json['instructions']['draw'].push({'circle': circle});
          break;
        case 'rectangle':
          if (!json['instructions']['draw']) json['instructions']['draw'] = new Array();

          var position = args[1].split("x");
          position[0] = parseInt(position[0]);
          position[1] = parseInt(position[1]);
          var from = {
            'x': position[0],
            'y': position[1]
          };

          var dimensions = args[2].split("x");
          dimensions[0] = parseInt(dimensions[0]);
          dimensions[1] = parseInt(dimensions[1]);
          var size = {
            'width': dimensions[0],
            'height': dimensions[1]
          };

          var color = args[3];

          var rectangle = {
            'from': from,
            'size': size,
            'color': color
          };

          json['instructions']['draw'].push({'rectangle': rectangle});
          break;
        default:
      }
    }

    json['sourceFile'] = infile;
    json['destinationFile'] = outfile;

    processImage(json);
  });
}

function processImage(json) {
  var canvas = Fabric.createCanvasForNode(json['finalDimensions']['width'],json['finalDimensions']['height']);

  var imagePath = json['sourceFile'];

  var crop = json['instructions']['crop'];
  var imageOffset = crop != "undefined" ?
   {'x':crop['from']['x'],
    'y':crop['from']['y']} :
   {'x':0,'y':0};

  function applyBackground(canvas) {
    FS.readFile(imagePath, function (err, blob) {
      if (err) throw err;

      var dimensions = json['dimensions'];
      var finalDimensions = json['finalDimensions'];
      img = {'width': dimensions['width'], 'height': dimensions['height'], 'src':blob};

      Fabric.Image.fromObject(img, function(fimg) {
        top = img.height/2 - imageOffset['y'];
        if (top % 1 != 0) {
          top -= 0.5;
        }
        left = img.width / 2 - imageOffset['x'];
        if (left % 1 != 0) {
          left -= 0.5;
        }

        canvas.add(fimg.set('top', top).set('left', left));

        applyMarkup(canvas);
      });
    });
  }

  function applyMarkup(canvas) {
    var size = json['dimensions'];

    for (instruction in json['instructions']) {
      switch (instruction) {
        case 'draw':
          json['instructions']['draw'].forEach(function (e) {
            for (shapeName in e) {
              switch (shapeName) {
                case 'rectangle':
                  shape = e[shapeName];
                  drawRectangle(json, canvas, shape, imageOffset);
                  break;
                case 'circle':
                  shape = e[shapeName];
                  drawCircle(json, canvas,shape, imageOffset);
                  break;
                default:
                  console.error('Unsupported Shape: ' + shapeName);
              }
            }
          });
          break;
        case 'crop':
          //もう処理していたから無視する
          break;
        default:
          console.error('Unsupported Instruction: ' + instruction);
      }
    }

    writeCanvas(canvas);
  }

  function writeCanvas(canvas) {
    var outstream = FS.createWriteStream(json['destinationFile']),
        stream = canvas.createJPEGStream({
          quality: 93
        });

    stream.on('data', function(chunk) {
      outstream.write(chunk);
    });
  }

  applyBackground(canvas);
}

function drawRectangle(json, canvas, shape, imageOffset) {
  shape['stroke'] = Math.max(Math.round(json['finalDimensions']['width'] / 300 * 2), 2);
  whiteStroke = Math.max(Math.round(shape['stroke'] / 4), 1);

  var rect = {
    left: shape['from']['x']-imageOffset['x'],
    top: shape['from']['y']-imageOffset['y'],
    width: shape['size']['width'],
    height: shape['size']['height'],
    rx: 1,
    ry: 1,
    strokeWidth: shape['stroke'],
    stroke: colorValues[shape['color']],
    fill: 'transparent'
  }

  //Fabric調整
  rect['top'] = rect['top'] + rect['height'] / 2 + rect['strokeWidth'] / 2;
  rect['left'] = rect['left'] + rect['width'] / 2 + rect['strokeWidth'] / 2;

  var fillRect = Cloner.clone(rect);

  fillRect['fill'] = fillRect['stroke'].replace(/\d\.\d\d\)/, '0.05\)');
  fillRect['stroke'] = 'transparent';

  var rectBorder = Cloner.clone(rect);
  rectBorder['width'] += rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectBorder['height'] += rect['strokeWidth'] / 2 + whiteStroke / 2 + 4;
  rectBorder['rx'] = 5;
  rectBorder['ry'] = 5;
  rectBorder['strokeWidth'] = whiteStroke;
  rectBorder['stroke'] = 'rgba(255,255,255,0.5)';

  var rectShadow = Cloner.clone(rect);
  rectShadow['left'] += 7;
  rectShadow['top'] += 7;
  rectShadow['stroke'] = 'rgba(0,0,0,0.5)';

  canvas.add(new Fabric.Rect(rectShadow));
  canvas.add(new Fabric.Rect(fillRect));
  canvas.add(new Fabric.Rect(rect));
  canvas.add(new Fabric.Rect(rectBorder));
}

function drawCircle(json, canvas, shape, imageOffset) {
  shape['stroke'] = Math.max(Math.round(shape['radius'] / 4), 5);
  whiteStroke = 1;

  var circle = {
    left: shape['from']['x']-imageOffset['x'],
    top: shape['from']['y']-imageOffset['y'],
    radius: shape['radius'],
    strokeWidth: shape['stroke'],
    stroke: colorValues[shape['color']],
    fill: 'transparent'
  };
  var circleBorder = Cloner.clone(circle);
  circleBorder['radius'] = circle['radius'] + circle['strokeWidth'] / 2;
  circleBorder['strokeWidth'] = whiteStroke;
  circleBorder['stroke'] = 'rgba(255,255,255,0.95';
  var circleShadow = Cloner.clone(circle);
  circleShadow['left'] += 7;
  circleShadow['top'] += 7;
  circleShadow['stroke'] = 'rgba(0,0,0,0.5)';

  canvas.add(new Fabric.Circle(circleShadow));
  canvas.add(new Fabric.Circle(circle));
  canvas.add(new Fabric.Circle(circleBorder));
}

processArgs();
<|endoftext|>


<commit_before>export default {
  web: [
  { rank: "I",
    project: {
      id: 1,
      name: "Quizrl",
      authors: [{id: 1, name: "Lacătușu Casian"}],
      school: "Colegiul Național Traian",
      score: 68.43,
      open: 78.96,
      total: 73.70
    }
  },
  { rank: "II",
    project: {
      id: 2,
      name: "Nexus Play",
      authors: [{id: 2, name: "Cristian Alexandru"},
      {id: 3, name: "Ghinea Diana-Elena"}],
      school: "Colegiul Național de Informatică „Tudor Vianu”",
      score: 68.21,
      open: 78.11,
      total: 73.16
    }
  },
  { rank: "III",
    project: {
      id: 3,
      name: "Locatia Mea",
      authors: [{id: 4, name: "Cozloschi Florin"}],
      school: "Liceul Teoretic 'Traian Vuia' Faget",
      score: 67.93,
      open: 75.54,
      total: 71.74
    }
  },
  { rank: "M",
    project: {
      id: 4,
      name: "Idea Storm",
      authors: [{id: 5, name: "Stoian Ioan-Catalin"}],
      school: "Liceul Teoretic \"Dunarea\"",
      score: 62.86,
      open: 76.56,
      total: 69.71
    }
  },
  { rank: "M",
      project: {
        id: 4,
        name: "Idea Storm",
        authors: [{id: 6, name: "Chitu-Costinean Paul Luchian"},
                  {id: 7, name: "Zugravu-Herciu Alexandru Ioan"}],
        school: "Liceul Teoretic \"Grigore Moisil\"",
        score: 69.93,
        open: 64.5,
        total: 67.22
      }
    }
  ],
    robots: [
    {"rank": "I",
      "project": {"id": 1,
        "name": "RoboLine",
        "authors": [{"id": 1,
          "name": "Trett Stefan-Michael"}],
        "school": "Colegiul Național de Informatică \"Moisil\"",
        "score": "66.4",
        "open": "98",
        "total": "82.2"}},
    {"rank": "II",
      "project": {"id": 2,
        "name": "Controlul automat la distanta al autovehiculelor cu roti independente",
        "authors": [{"id": 1,
          "name": "Bejan Paul-Daniel"}, {"id": 2, "name": "Albis Alin-Adrian"}],
        "school": "Liceul \"Mihai Viteazul\" Ineu",
        "score": "78.8",
        "open": "77",
        "total": "77.9"}},
    {"rank": "III",
      "project": {"id": 4,
        "name": "Maze Solver & Line Follower",
        "authors": [{"id": 1,
          "name": "Stoenescu Marius"}],
        "school": "Liceul Voievodul Mircea Targoviste",
        "score": "78.6",
        "open": "61",
        "total": "69.8"}},
    {"rank": "M",
      "project": {"id": 5,
        "name": "O.D.M.E (Omni Directional Mobility Enhancer) Mk.2",
        "authors": [{"id": 1,
          "name": "Visan Alexandru Andrei"}],
        "school": "Colegiul National \"Al. I. Cuza\"",
        "score": "76.6",
        "open": "46",
        "total": "61.3"}},
    {"rank": "M",
      "project": {"id": 6,
        "name": "T-600",
        "authors": [{"id": 1,
          "name": "Druta Nicolae Mircea"}, {"id": 2, "name": "Balan Adrian"}],
        "school": "Colegiul Naţional \"Mihai Viteazul\" Bucuresti",
        "score": "67.8",
        "open": "54",
        "total": "60.9"}},
    {"rank": "M",
      "project": {"id": 8,
        "name": "Fight of robots- MiniSumo",
        "authors": [{"id": 1, "name": "Szymon Gula"},
        {"id": 2, "name": "Aleksandra Kielar"},
        {"id": 3, "name": "Tomasz Sala"},
        {"id": 4, "name": "Katarzyna Banaszak"},
        {"id": 5, "name": "Patryk Galczynski"}
        ],
        "school": "V LO Krakow",
        "score": "82",
        "open": "35",
        "total": "58.5"}},
    {"rank": "M",
      "project": {"id": 13,
        "name": "Wi-Bot Tilt EVO",
        "authors": [{"id": 1,
          "name": "Nicolescu Vlad"}],
        "school": "Colegiul National Carol I Craiova",
        "score": "57.6",
        "open": "",
        "total": "57.6"}}
  ],
    educational: [
    {"rank": "I",
      "project": {"id": 1,
        "name": "Pwnthecode",
        "authors": [{"id": 1,
          "name": "Horătău Marius-Sebastian"}],
        "school": "Colegiul Naţional \"Mihai Eminescu\" Suceava",
        "score": "83.29",
        "open": "90.14",
        "total": "86.72"}},
  {"rank": "II",
    "project": {"id": 2,
      "name": "Corpuri Geometrice",
      "authors": [{"id": 1,
        "name": "Conache Cristian"}],
      "school": "Liceul Teoretic \"Ioan Slavici\" Panciu Focsani",
      "score": "83.43",
      "open": "89.14",
      "total": "86.29"}},
  {"rank": "III",
    "project": {"id": 3,
      "name": "ABC - Mathematical Analysis",
      "authors": [{"id": 1,
        "name": "Ghinea Diana-Elena"}],
      "school": "Colegiul National de Informatica Tudor Vianu",
      "score": "79.71",
      "open": "89.86",
      "total": "84.79"}},
  {"rank": "M",
    "project": {"id": 4,
      "name": "Tehnica Backtracking",
      "authors": [{"id": 1, "name": "Ivan Diana"},
                  {"id": 2, "name": "Marton Adrian"}],
      "school": "Liceul Teoretic \"Alexandru Papiu Ilarian\" Dej",
      "score": "81.86",
      "open": "85.29",
      "total": "83.58"}},
  {"rank": "M",
    "project": {"id": 6,
      "name": "Pseudocode",
      "authors": [{"id": 1, "name": "Radu Valentin"},
                  {"id": 2, "name": "Dudescu Alexandru"}],
      "school": "Colegiul National \"Mihai Viteazul\" Ploiesti",
      "score": "78.86",
      "open": "79.71",
      "total": "78.86"}},
  {"rank": "M",
    "project": {"id": 8,
      "name": "SolveIT",
      "authors": [{"id": 1,
        "name": "Ungurean Cristina"}],
      "school": "Colegiul Economic Dimitrie Cantemir",
      "score": "77.71",
      "open": "",
      "total": "77.71"}}
  ]
}
<commit_msg>Add data for utilitar and multimedia. #43<commit_after>export default {
  web: [
  { rank: "I",
    project: {
      id: 1,
      name: "Quizrl",
      authors: [{id: 1, name: "Lacătușu Casian"}],
      school: "Colegiul Național Traian",
      score: 68.43,
      open: 78.96,
      total: 73.70
    }
  },
  { rank: "II",
    project: {
      id: 2,
      name: "Nexus Play",
      authors: [{id: 2, name: "Cristian Alexandru"},
      {id: 3, name: "Ghinea Diana-Elena"}],
      school: "Colegiul Național de Informatică „Tudor Vianu”",
      score: 68.21,
      open: 78.11,
      total: 73.16
    }
  },
  { rank: "III",
    project: {
      id: 3,
      name: "Locatia Mea",
      authors: [{id: 4, name: "Cozloschi Florin"}],
      school: "Liceul Teoretic 'Traian Vuia' Faget",
      score: 67.93,
      open: 75.54,
      total: 71.74
    }
  },
  { rank: "M",
    project: {
      id: 4,
      name: "Idea Storm",
      authors: [{id: 5, name: "Stoian Ioan-Catalin"}],
      school: "Liceul Teoretic \"Dunarea\"",
      score: 62.86,
      open: 76.56,
      total: 69.71
    }
  },
  { rank: "M",
    project: {
      id: 5,
      name: "Snackhunt",
      authors: [{id: 6, name: "Chitu-Costinean Paul Luchian"},
      {id: 7, name: "Zugravu-Herciu Alexandru Ioan"}],
      school: "Liceul Teoretic \"Grigore Moisil\"",
      score: 69.93,
      open: 64.5,
      total: 67.22
    }
  },
  { rank: "M",
    project: {
      id: 6,
      name: "CNRV",
      authors: [{id: 7, name: "Rosianu Alexandru"}],
      school: "Colegiul National \"Roman-Voda\"",
      score: 64.82,
      open: 69.32,
      total: 67.07
    }
  }

  ],
    robots: [
    {"rank": "I",
      "project": {"id": 1,
        "name": "RoboLine",
        "authors": [{"id": 1,
          "name": "Trett Stefan-Michael"}],
        "school": "Colegiul Național de Informatică \"Moisil\"",
        "score": "66.4",
        "open": "98",
        "total": "82.2"}},
    {"rank": "II",
      "project": {"id": 2,
        "name": "Controlul automat la distanta al autovehiculelor cu roti independente",
        "authors": [{"id": 1,
          "name": "Bejan Paul-Daniel"}, {"id": 2, "name": "Albis Alin-Adrian"}],
        "school": "Liceul \"Mihai Viteazul\" Ineu",
        "score": "78.8",
        "open": "77",
        "total": "77.9"}},
    {"rank": "III",
      "project": {"id": 4,
        "name": "Maze Solver & Line Follower",
        "authors": [{"id": 1,
          "name": "Stoenescu Marius"}],
        "school": "Liceul Voievodul Mircea Targoviste",
        "score": "78.6",
        "open": "61",
        "total": "69.8"}},
    {"rank": "M",
      "project": {"id": 5,
        "name": "O.D.M.E (Omni Directional Mobility Enhancer) Mk.2",
        "authors": [{"id": 1,
          "name": "Visan Alexandru Andrei"}],
        "school": "Colegiul National \"Al. I. Cuza\"",
        "score": "76.6",
        "open": "46",
        "total": "61.3"}},
    {"rank": "M",
      "project": {"id": 6,
        "name": "T-600",
        "authors": [{"id": 1,
          "name": "Druta Nicolae Mircea"}, {"id": 2, "name": "Balan Adrian"}],
        "school": "Colegiul Naţional \"Mihai Viteazul\" Bucuresti",
        "score": "67.8",
        "open": "54",
        "total": "60.9"}},
    {"rank": "M",
      "project": {"id": 8,
        "name": "Fight of robots- MiniSumo",
        "authors": [{"id": 1, "name": "Szymon Gula"},
        {"id": 2, "name": "Aleksandra Kielar"},
        {"id": 3, "name": "Tomasz Sala"},
        {"id": 4, "name": "Katarzyna Banaszak"},
        {"id": 5, "name": "Patryk Galczynski"}
        ],
          "school": "V LO Krakow",
          "score": "82",
          "open": "35",
          "total": "58.5"}},
    {"rank": "M",
      "project": {"id": 13,
        "name": "Wi-Bot Tilt EVO",
        "authors": [{"id": 1,
          "name": "Nicolescu Vlad"}],
        "school": "Colegiul National Carol I Craiova",
        "score": "57.6",
        "open": "",
        "total": "57.6"}}
  ],
    educational: [
    {"rank": "I",
      "project": {"id": 1,
        "name": "Pwnthecode",
        "authors": [{"id": 1,
          "name": "Horătău Marius-Sebastian"}],
        "school": "Colegiul Naţional \"Mihai Eminescu\" Suceava",
        "score": "83.29",
        "open": "90.14",
        "total": "86.72"}},
    {"rank": "II",
      "project": {"id": 2,
        "name": "Corpuri Geometrice",
        "authors": [{"id": 1,
          "name": "Conache Cristian"}],
        "school": "Liceul Teoretic \"Ioan Slavici\" Panciu Focsani",
        "score": "83.43",
        "open": "89.14",
        "total": "86.29"}},
    {"rank": "III",
      "project": {"id": 3,
        "name": "ABC - Mathematical Analysis",
        "authors": [{"id": 1,
          "name": "Ghinea Diana-Elena"}],
        "school": "Colegiul National de Informatica Tudor Vianu",
        "score": "79.71",
        "open": "89.86",
        "total": "84.79"}},
    {"rank": "M",
      "project": {"id": 4,
        "name": "Tehnica Backtracking",
        "authors": [{"id": 1, "name": "Ivan Diana"},
        {"id": 2, "name": "Marton Adrian"}],
        "school": "Liceul Teoretic \"Alexandru Papiu Ilarian\" Dej",
        "score": "81.86",
        "open": "85.29",
        "total": "83.58"}},
    {"rank": "M",
      "project": {"id": 6,
        "name": "Pseudocode",
        "authors": [{"id": 1, "name": "Radu Valentin"},
        {"id": 2, "name": "Dudescu Alexandru"}],
        "school": "Colegiul National \"Mihai Viteazul\" Ploiesti",
        "score": "78.86",
        "open": "79.71",
        "total": "78.86"}},
    {"rank": "M",
      "project": {"id": 8,
        "name": "SolveIT",
        "authors": [{"id": 1,
          "name": "Ungurean Cristina"}],
        "school": "Colegiul Economic Dimitrie Cantemir",
        "score": "77.71",
        "open": "",
        "total": "77.71"}}
  ],
    media: [
    {"rank": "I",
      "project": {"id": 1,
        "name": "Vise in umbra",
        "authors": [{"id": 1, "name": "Vlad Victor"}],
        "school": "Liceul Teoretic de Informatică „Grigore Moisil” Iasi",
        "score": "88",
        "open": "90",
        "total": "89"}},
    {"rank": "II",
      "project": {"id": 2,
        "name": "Carpe Diem!",
        "authors": [{"id": 1, "name": "Bubatu Luiza Madalina"},
        {"id": 2, "name": "Tomegea Roberta"},
        {"id": 3, "name": "Sendroiu Vlad"}],
        "school": "Colegiul National \"Aurel Vlaicu\" Orastie",
        "score": "71.6",
        "open": "87",
        "total": "79.3"}},
    {"rank": "III",
      "project": {"id": 5,
        "name": "Empty hearts",
        "authors": [{"id": 1,
          "name": "Chiar Bianca Raluca"}],
        "school": "Liceul pedagogic \"Mircea Scarlat\" Alexandria",
        "score": "73.6",
        "open": "83",
        "total": "78.3"}},
    {"rank": "M",
      "project": {"id": 6,
        "name": "Urban Force - The family",
        "authors": [{"id": 1,
          "name": "Zvinca Alin - Ionu"}],
        "school": "Colegiul National Grigore Ghica Dorohoi",
        "score": "71.4",
        "open": "82",
        "total": "76.7"}},
    {"rank": "M",
      "project": {"id": 7,
        "name": "Tudor",
        "authors": [{"id": 1, "name": "Cristescu Andreea"},
        {"id": 2, "name": "Cimpianu Dan"}],
        "school": "Liceul Pedagogic \"Carmen Sylva\" Timisoara",
        "score": "66.4",
        "open": "78",
        "total": "72.2"}},
    {"rank": "M",
      "project": {"id": 9,
        "name": "Miscarea contra statului",
        "authors": [{"id": 1,
          "name": "Durnea Vlad - Mihai"}],
        "school": "Liceul Teoretic \"Dante Alighieri\" Bucuresti",
        "score": "64.8",
        "open": "82",
        "total": "73.4"}},
    {"rank": "M",
      "project": {"id": 10,
        "name": "Impactul tehnologiei",
        "authors": [{"id": 1,
          "name": "Buruiana Alexandru"}],
        "school": "Liceul Teoretic \"Dunarea\"",
        "score": "58.2",
        "open": "",
        "total": "58.2"}},
    {"rank": "",
      "project": {"id": 11,
        "name": "La rascruce de drumuri",
        "authors": [{"id": 1,
          "name": "Popa Diana Fabiola"}],
        "school": "Colegiul National \"Iancu de Hunedoara\"",
        "score": "56.8",
        "open": "",
        "total": "56.8"}}
  ],
    utility: [ {"rank": "I",
      "project": {"id": 1,
        "name": "Noriy",
        "authors": [{"id": 1, "name": "Ionascu Alexandru"},
                    {"id": 2, "name": "Frunza Roxana"}],
        "school": "Colegiul National Gheorghe Rosca Codreanu Barlad",
        "score": "51",
        "open": "98.92",
        "total": "74.96"}},
  {"rank": "II",
    "project": {"id": 3,
      "name": "Sybilai",
      "authors": [{"id": 1, "name": "Cucu George"},
                  {"id": 2, "name": "Andrici Cezar"},
                  {"id": 3, "name": "Stolniceanu Stefan"},
                  {"id": 4, "name": "Miron Alexandru"}],
      "school": "Liceul de Informatica \"Grigore Moisil\" Iasi",
      "score": "47.25",
      "open": "72.99",
      "total": "60.12"}},
  {"rank": "III",
    "project": {"id": 7,
      "name": "Soldr",
      "authors": [{"id": 1, "name": "Budaca Eduard"}],
      "school": "Colegiul Național \"Emil Racoviță\" Iași",
      "score": "65.33",
      "open": "40.35",
      "total": "52.84"}},
  {"rank": "M",
    "project": {"id": 8,
      "name": "Dark Energy",
      "authors": [{"id": 1, "name": "Muntean Andrei-Alexandru"},
                  {"id": 2, "name": "Ghinea Diana-Elena"}],
      "school": "Colegiul National \"Vasile Goldis\" Arad",
      "score": "59.16",
      "open": "40.57",
      "total": "49.87"}},
  {"rank": "M",
    "project": {"id": 10,
      "name": "Cube World (Run)",
      "authors": [{"id": 1, "name": "Platon Vlad"}],
      "school": "Liceul Teoretic Nicolae Balcescu",
      "score": "47.08",
      "open": "30.36",
      "total": "38.72"}}
  ]
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

'use strict';

const commandLineCommands = require('command-line-commands')
const jsonfile = require('jsonfile')
const sh = require("shelljs");

function getUserHome() {
  return process.env.HOME || process.env.USERPROFILE;
}

const ubahnFilePath = getUserHome() + "/.ubahnfile.json";

const validCommands = [ null, 'list', 'goto', 'add', 'rm' ];
const commandAndArgs = commandLineCommands(validCommands);
const command = commandAndArgs.command;
const args = commandAndArgs.argv;

var ubahn = {};
ubahn.package = require('./package.json');
ubahn.version = ubahn.package.version;

if(command == "list") {

  jsonfile.readFile(ubahnFilePath, function(err, obj) {
    if (obj && obj.length > 0) {
      obj.map(function(entry) {
        console.log(entry.shortname + ": " + entry.path);
      });
    } else {
      console.log("ubahn is empty");
    }
  });

} else if (command == "add") {
  var shortname = args[0];
  var path = args[1];

  if(!path) {
    path = sh.pwd();
  }

  if(shortname) {
    jsonfile.readFile(ubahnFilePath, function(err, obj) {
      if (!obj) {
        obj = [];
      }
        var entry = {};
        entry.shortname = shortname;
        entry.path = path;
        obj.push(entry);

        jsonfile.writeFile(ubahnFilePath, obj, function (err) {
          if(err) {
            console.error(err);
          } else {
            console.log("Added " + shortname + " to ubahn")
}
        });
    });
  } else {
    console.log("You must specify a directory shortname.");
  }

} else if (command == "goto") {
  var shortname = args[0];

  if(shortname) {
    jsonfile.readFile(ubahnFilePath, function(err, obj) {
      if (obj) {
        var entry = obj.find(function(element, index, array) {
          return element.shortname == shortname;
        });

        if(entry) {
          console.log(entry.path);
          process.exit(42);
        } else {
          console.log(shortname + " not found");
        }
      } else {
        console.log("ubahn is empty");
      }
    });
  } else {
    console.log("You must specify a directory shortname.");
  }

} else if (command == "rm") {
  var shortname = args[0];

  if(shortname) {
    jsonfile.readFile(ubahnFilePath, function(err, obj) {
      if (!obj) {
        obj = [];
      }

      obj = obj.filter(function(entry) {
        return entry.shortname != shortname;
      });

      jsonfile.writeFile(ubahnFilePath, obj, function (err) {
        if(err) {
          console.error(err);
        } else {
          console.log("Removed " + shortname + " from ubahn")
        }
      });
    });
  } else {
    console.log("You must specify a directory shortname.");
  }

} else {
  console.log("ubahn " + ubahn.version);
}
<commit_msg>add clear command<commit_after>#!/usr/bin/env node

'use strict';

const commandLineCommands = require('command-line-commands')
const jsonfile = require('jsonfile')
const sh = require("shelljs");

function getUserHome() {
  return process.env.HOME || process.env.USERPROFILE;
}

const ubahnFilePath = getUserHome() + "/.ubahnfile.json";

const validCommands = [ null, 'list', 'goto', 'add', 'rm', 'clear' ];
const commandAndArgs = commandLineCommands(validCommands);
const command = commandAndArgs.command;
const args = commandAndArgs.argv;

var ubahn = {};
ubahn.package = require('./package.json');
ubahn.version = ubahn.package.version;

if(command == "list") {

  jsonfile.readFile(ubahnFilePath, function(err, obj) {
    if (obj && obj.length > 0) {
      obj.map(function(entry) {
        console.log(entry.shortname + ": " + entry.path);
      });
    } else {
      console.log("ubahn is empty");
    }
  });

} else if (command == "add") {
  var shortname = args[0];
  var path = args[1];

  if(!path) {
    path = sh.pwd();
  }

  if(shortname) {
    jsonfile.readFile(ubahnFilePath, function(err, obj) {
      if (!obj) {
        obj = [];
      }
        var entry = {};
        entry.shortname = shortname;
        entry.path = path;
        obj.push(entry);

        jsonfile.writeFile(ubahnFilePath, obj, function (err) {
          if(err) {
            console.error(err);
          } else {
            console.log("Added " + shortname + " to ubahn")
}
        });
    });
  } else {
    console.log("You must specify a directory shortname.");
  }

} else if (command == "goto") {
  var shortname = args[0];

  if(shortname) {
    jsonfile.readFile(ubahnFilePath, function(err, obj) {
      if (obj) {
        var entry = obj.find(function(element, index, array) {
          return element.shortname == shortname;
        });

        if(entry) {
          console.log(entry.path);
          process.exit(42);
        } else {
          console.log(shortname + " not found");
        }
      } else {
        console.log("ubahn is empty");
      }
    });
  } else {
    console.log("You must specify a directory shortname.");
  }

} else if (command == "rm") {
  var shortname = args[0];

  if(shortname) {
    jsonfile.readFile(ubahnFilePath, function(err, obj) {
      if (!obj) {
        obj = [];
      }

      obj = obj.filter(function(entry) {
        return entry.shortname != shortname;
      });

      jsonfile.writeFile(ubahnFilePath, obj, function (err) {
        if(err) {
          console.error(err);
        } else {
          console.log("Removed " + shortname + " from ubahn")
        }
      });
    });
  } else {
    console.log("You must specify a directory shortname.");
  }

} else if(command == "clear") {
  jsonfile.writeFile(ubahnFilePath, [], function (err) {
    if(err) {
      console.error(err);
    } else {
      console.log("Removed all stations from ubahn");
    }
  });
} else {
  console.log("ubahn " + ubahn.version);
}
<|endoftext|>


<commit_before>var Stremio = require("stremio-addons");
var needle = require("needle");
var _ = require("lodash");
var bagpipe = require("bagpipe");

var twitch_chans = [];
var chanName = [];

var stremioCentral = "http://api9.strem.io";

var pkg = require("./package");
var manifest = {
    "id": "org.stremio.twitch",
    "types": ["tv"],
    "filter": { "query.twitch_id": { "$exists": true }, "query.type": { "$in":["tv"] } },
    icon: "http://s.jtvnw.net/jtv_user_pictures/hosted_images/GlitchIcon_purple.png",
    logo: "https://rack1.chipmeup.com/assets/twitch/logo-cd148048b88ce417a0c815548e7e4681.png",
    posterShape: { tv: "square" },
    repository: "http://github.com/jaruba/stremio-twitch",
    endpoint: "http://twitch.strem.io/stremioget/stremio/v1",
    name: pkg.displayName, version: pkg.version, description: pkg.description,
    isFree: true,
    sorts: [{prop: "popularities.twitch", name: "Twitch.tv", types:["tv"]}]
};

var pipe = new bagpipe(1);

// Get all channels
function twitchStreams(cb) {
    if (twitch_chans.length && cb) {
        cb(null, twitch_chans);
        return;
    }
    needle.get('https://api.twitch.tv/kraken/streams', function(err, res) {
        if (err) {
            cb && cb(err);
            return;
        }
        twitch_chans = [];
        chanName = [];
        if (res && res.body && res.body.streams && res.body.streams.length) {
            var channel = res.body.streams[0].channel.name;
            res.body.streams.forEach( function(el, ij) {
                chanName[el.channel._id] = el.channel.name;
                twitch_chans.push({
                    twitch_id: el.channel._id,
                    name: el.channel.status,
                    poster: el.preview.medium,
                    backgroundShape: 'contain',
                    logoShape: 'hidden',
                    banner: el.channel.video_banner || el.preview.template.replace('{width}', '1920').replace('{height}', '1080'),
                    genre: [ 'Entertainment' ],
                    isFree: 1,
                    popularity: el.viewers,
                    popularities: { twitch: el.viewers },
                    type: 'tv'
                });
            });
            cb && cb(null, twitch_chans);
        }
    });
}

pipe.push(twitchStreams);

function getStream(args, callback) {
    var channel = chanName[args.query.twitch_id];
    if (channel) {
        needle.get('http://api.twitch.tv/api/channels/' + channel + '/access_token', function(err, res) {
            if (err) {
                callback(err);
                return;
            }
            if (res && res.body && res.body.sig && res.body.token) {
                var title = res.body.status;
                var token = res.body.token;
                var sig = res.body.sig;
                var rand = Math.floor((Math.random() * 999999) + 1);
                var mrl = 'http://usher.twitch.tv/api/channel/hls/' + channel + '.m3u8?player=twitchweb&token=' + token + '&sig=' + sig + '&allow_audio_only=true&allow_source=true&type=any&p=' + rand;
                callback(null, [{
                    availability: 1,
                    url: mrl,
                    tag: ['hls'],
                    twitch_id: args.query.twitch_id
                }]);
            }
        });
    } else
        callback(new Error('Stream Missing'))
}

function getMeta(args, callback) {
    twitchStreams(function(err, chans) {
        if (err) cb(err);
        else {
            callback(null, args.limit ? chans.slice(0, args.limit) : chans);
        }
    });
}
var addon = new Stremio.Server({
    "stream.get": function(args, callback, user) {
        pipe.push(getStream, args, function(err, resp) { callback(err, resp ? (resp[0] || null) : undefined) })
    },
    "stream.find": function(args, callback, user) {
        pipe.push(getStream, args, function(err, resp) { callback(err, resp ? resp.slice(0, 4) : undefined) });
    },
    "meta.get": function(args, callback, user) {
        args.projection = args.projection || { }; // full
        pipe.push(getMeta, _.extend(args, { limit: 1 }), function(err, res) {
            if (err) return callback(err);

            res = res && res[0];
            if (! res) return callback(null, null);

            callback(null, res);
        });
    },
    "meta.find": function(args, callback, user) {
        pipe.push(getMeta, args, callback); // push to pipe so we wait for channels to be crawled
    }
}, { stremioget: true, cacheTTL: { "meta.find": 30*60, "stream.find": 30*60, "meta.get": 4*60*60 }, allow: ["http://api8.herokuapp.com","http://api9.strem.io"] /* secret: mySecret */ }, manifest);

var server = require("http").createServer(function (req, res) {
    addon.middleware(req, res, function() { res.end() }); // wire the middleware - also compatible with connect / express
}).on("listening", function()
{
    console.log("Twitch.tv Stremio Addon listening on "+server.address().port);
})
if (module.parent) module.exports = server;
else server.listen(process.env.PORT || 9005);

var catchMyExceptions = require('catch-my-exceptions');
if (process.env.SLACK_HOOK) catchMyExceptions(process.env.SLACK_HOOK, { slackUsername: "twitch" });
<commit_msg>Implement Meta Get Correctly<commit_after>var Stremio = require("stremio-addons");
var needle = require("needle");
var _ = require("lodash");
var bagpipe = require("bagpipe");

var twitch_chans = [];
var chanName = [];

var stremioCentral = "http://api9.strem.io";

var pkg = require("./package");
var manifest = {
    "id": "org.stremio.twitch",
    "types": ["tv"],
    "filter": { "query.twitch_id": { "$exists": true }, "query.type": { "$in":["tv"] } },
    icon: "http://s.jtvnw.net/jtv_user_pictures/hosted_images/GlitchIcon_purple.png",
    logo: "https://rack1.chipmeup.com/assets/twitch/logo-cd148048b88ce417a0c815548e7e4681.png",
    repository: "http://github.com/jaruba/stremio-twitch",
    endpoint: "http://twitch.strem.io/stremioget/stremio/v1",
    name: pkg.displayName, version: pkg.version, description: pkg.description,
    isFree: true,
    sorts: [{prop: "popularities.twitch", name: "Twitch.tv", types:["tv"]}]
};

var pipe = new bagpipe(1);

// Get all channels
function twitchStreams(cb) {
    if (twitch_chans.length && cb) {
        cb(null, twitch_chans);
        return;
    }
    needle.get('https://api.twitch.tv/kraken/streams', function(err, res) {
        if (err) {
            cb && cb(err);
            return;
        }
        twitch_chans = [];
        chanName = [];
        if (res && res.body && res.body.streams && res.body.streams.length) {
            var channel = res.body.streams[0].channel.name;
            res.body.streams.forEach( function(el, ij) {
                chanName[el.channel._id] = el.channel.name;
                twitch_chans.push({
                    twitch_id: el.channel._id,
                    name: el.channel.status,
                    poster: el.preview.medium,
                    backgroundShape: 'contain',
                    logoShape: 'hidden',
                    banner: el.channel.video_banner || el.preview.template.replace('{width}', '1920').replace('{height}', '1080'),
                    genre: [ 'Entertainment' ],
                    isFree: 1,
                    popularity: el.viewers,
                    popularities: { twitch: el.viewers },
                    type: 'tv'
                });
            });
            cb && cb(null, twitch_chans);
        }
    });
}

pipe.push(twitchStreams);

function getStream(args, callback) {
    var channel = chanName[args.query.twitch_id];
    if (channel) {
        needle.get('http://api.twitch.tv/api/channels/' + channel + '/access_token', function(err, res) {
            if (err) {
                callback(err);
                return;
            }
            if (res && res.body && res.body.sig && res.body.token) {
                var title = res.body.status;
                var token = res.body.token;
                var sig = res.body.sig;
                var rand = Math.floor((Math.random() * 999999) + 1);
                var mrl = 'http://usher.twitch.tv/api/channel/hls/' + channel + '.m3u8?player=twitchweb&token=' + token + '&sig=' + sig + '&allow_audio_only=true&allow_source=true&type=any&p=' + rand;
                callback(null, [{
                    availability: 1,
                    url: mrl,
                    tag: ['hls'],
                    twitch_id: args.query.twitch_id
                }]);
            }
        });
    } else
        callback(new Error('Stream Missing'))
}

function getMeta(args, callback) {
    if (args.query.twitch_id) {
        var found = twitch_chans.some( function(el) {
            if (el.twitch_id == args.query.twitch_id) {
                callback(null, [el]);
                return true;
            }
        });
        !found && callback(new Error("Item Not Found"));
    } else {
        twitchStreams(function(err, chans) {
            if (err) cb(err);
            else {
                callback(null, args.limit ? chans.slice(0, args.limit) : chans);
            }
        });
    }
}
var addon = new Stremio.Server({
    "stream.get": function(args, callback, user) {
        pipe.push(getStream, args, function(err, resp) { callback(err, resp ? (resp[0] || null) : undefined) })
    },
    "stream.find": function(args, callback, user) {
        pipe.push(getStream, args, function(err, resp) { callback(err, resp || undefined) })
    },
    "meta.get": function(args, callback, user) {
        args.projection = args.projection || { }; // full
        pipe.push(getMeta, _.extend(args, { limit: 1 }), function(err, res) {
            if (err) return callback(err);

            res = res && res[0];
            if (! res) return callback(null, null);

            callback(null, res);
        });
    },
    "meta.find": function(args, callback, user) {
        pipe.push(getMeta, args, callback); // push to pipe so we wait for channels to be crawled
    }
}, { stremioget: true, cacheTTL: { "meta.find": 30*60, "stream.find": 30*60, "meta.get": 4*60*60 }, allow: ["http://api8.herokuapp.com","http://api9.strem.io"] /* secret: mySecret */ }, manifest);

var server = require("http").createServer(function (req, res) {
    addon.middleware(req, res, function() { res.end() }); // wire the middleware - also compatible with connect / express
}).on("listening", function()
{
    console.log("Twitch.tv Stremio Addon listening on "+server.address().port);
})
if (module.parent) module.exports = server;
else server.listen(process.env.PORT || 9005);

var catchMyExceptions = require('catch-my-exceptions');
if (process.env.SLACK_HOOK) catchMyExceptions(process.env.SLACK_HOOK, { slackUsername: "twitch" });
<|endoftext|>


<commit_before>var entityCharCodes = {
  'nbsp': '160',
  'iexcl': '161',
  'cent': '162',
  'pound': '163',
  'curren': '164',
  'yen': '165',
  'brvbar': '166',
  'sect': '167',
  'uml': '168',
  'copy': '169',
  'ordf': '170',
  'laquo': '171',
  'not': '172',
  'shy': '173',
  'reg': '174',
  'macr': '175',
  'deg': '176',
  'plusmn': '177',
  'sup2': '178',
  'sup3': '179',
  'acute': '180',
  'micro': '181',
  'para': '182',
  'middot': '183',
  'cedil': '184',
  'sup1': '185',
  'ordm': '186',
  'raquo': '187',
  'frac14': '188',
  'frac12': '189',
  'frac34': '190',
  'iquest': '191',
  'Agrave': '192',
  'Aacute': '193',
  'Acirc': '194',
  'Atilde': '195',
  'Auml': '196',
  'Aring': '197',
  'AElig': '198',
  'Ccedil': '199',
  'Egrave': '200',
  'Eacute': '201',
  'Ecirc': '202',
  'Euml': '203',
  'Igrave': '204',
  'Iacute': '205',
  'Icirc': '206',
  'Iuml': '207',
  'ETH': '208',
  'Ntilde': '209',
  'Ograve': '210',
  'Oacute': '211',
  'Ocirc': '212',
  'Otilde': '213',
  'Ouml': '214',
  'times': '215',
  'Oslash': '216',
  'Ugrave': '217',
  'Uacute': '218',
  'Ucirc': '219',
  'Uuml': '220',
  'Yacute': '221',
  'THORN': '222',
  'szlig': '223',
  'agrave': '224',
  'aacute': '225',
  'acirc': '226',
  'atilde': '227',
  'auml': '228',
  'aring': '229',
  'aelig': '230',
  'ccedil': '231',
  'egrave': '232',
  'eacute': '233',
  'ecirc': '234',
  'euml': '235',
  'igrave': '236',
  'iacute': '237',
  'icirc': '238',
  'iuml': '239',
  'eth': '240',
  'ntilde': '241',
  'ograve': '242',
  'oacute': '243',
  'ocirc': '244',
  'otilde': '245',
  'ouml': '246',
  'divide': '247',
  'oslash': '248',
  'ugrave': '249',
  'uacute': '250',
  'ucirc': '251',
  'uuml': '252',
  'yacute': '253',
  'thorn': '254',
  '255': 'yuml'
};

specialCharEntities = {
  '&': '&',
  '"': '"',
  '<': '<',
  '>': '>'
};

var entityRegExp = /&([#a-z0-9]+);/gi;
var specialCharRegExp = /[&"<>]/g;

function entityToChar(s, name) {
  var code = entityCharCodes[name];
  //if (!code && name === 'apos') char = 39;
  if (!code && name[0] === '#') {
    if (name[1] === 'x' || name[1] === 'X') {
      code = parseInt(name.substring(2), 16);
    }
    else {
      code = parseInt(name.substring(1), 10);
    }
    if (isNaN(code)) {
      code = undefined;
    }
  }
  return code ? String.fromCharCode(code) : s;
}

exports.HTMLDecode = function decode(s) {
    if (s) {
      s = s.replace(entityRegExp, entityToChar);
    }
    return s || '';
};

function specialCharToEntity(s) {
  var entity = specialCharEntities[s];
  return entity ? entity : s;
}

exports.HTMLEncode = function encode(s) {
  if (s) {
    s = s.replace(specialCharRegExp, specialCharToEntity);
  }
  return s || '';
};
<commit_msg>Added missing var<commit_after>var entityCharCodes = {
  'nbsp': '160',
  'iexcl': '161',
  'cent': '162',
  'pound': '163',
  'curren': '164',
  'yen': '165',
  'brvbar': '166',
  'sect': '167',
  'uml': '168',
  'copy': '169',
  'ordf': '170',
  'laquo': '171',
  'not': '172',
  'shy': '173',
  'reg': '174',
  'macr': '175',
  'deg': '176',
  'plusmn': '177',
  'sup2': '178',
  'sup3': '179',
  'acute': '180',
  'micro': '181',
  'para': '182',
  'middot': '183',
  'cedil': '184',
  'sup1': '185',
  'ordm': '186',
  'raquo': '187',
  'frac14': '188',
  'frac12': '189',
  'frac34': '190',
  'iquest': '191',
  'Agrave': '192',
  'Aacute': '193',
  'Acirc': '194',
  'Atilde': '195',
  'Auml': '196',
  'Aring': '197',
  'AElig': '198',
  'Ccedil': '199',
  'Egrave': '200',
  'Eacute': '201',
  'Ecirc': '202',
  'Euml': '203',
  'Igrave': '204',
  'Iacute': '205',
  'Icirc': '206',
  'Iuml': '207',
  'ETH': '208',
  'Ntilde': '209',
  'Ograve': '210',
  'Oacute': '211',
  'Ocirc': '212',
  'Otilde': '213',
  'Ouml': '214',
  'times': '215',
  'Oslash': '216',
  'Ugrave': '217',
  'Uacute': '218',
  'Ucirc': '219',
  'Uuml': '220',
  'Yacute': '221',
  'THORN': '222',
  'szlig': '223',
  'agrave': '224',
  'aacute': '225',
  'acirc': '226',
  'atilde': '227',
  'auml': '228',
  'aring': '229',
  'aelig': '230',
  'ccedil': '231',
  'egrave': '232',
  'eacute': '233',
  'ecirc': '234',
  'euml': '235',
  'igrave': '236',
  'iacute': '237',
  'icirc': '238',
  'iuml': '239',
  'eth': '240',
  'ntilde': '241',
  'ograve': '242',
  'oacute': '243',
  'ocirc': '244',
  'otilde': '245',
  'ouml': '246',
  'divide': '247',
  'oslash': '248',
  'ugrave': '249',
  'uacute': '250',
  'ucirc': '251',
  'uuml': '252',
  'yacute': '253',
  'thorn': '254',
  '255': 'yuml'
};

var specialCharEntities = {
  '&': '&',
  '"': '"',
  '<': '<',
  '>': '>'
};

var entityRegExp = /&([#a-z0-9]+);/gi;
var specialCharRegExp = /[&"<>]/g;

function entityToChar(s, name) {
  var code = entityCharCodes[name];
  //if (!code && name === 'apos') char = 39;
  if (!code && name[0] === '#') {
    if (name[1] === 'x' || name[1] === 'X') {
      code = parseInt(name.substring(2), 16);
    }
    else {
      code = parseInt(name.substring(1), 10);
    }
    if (isNaN(code)) {
      code = undefined;
    }
  }
  return code ? String.fromCharCode(code) : s;
}

exports.HTMLDecode = function decode(s) {
    if (s) {
      s = s.replace(entityRegExp, entityToChar);
    }
    return s || '';
};

function specialCharToEntity(s) {
  var entity = specialCharEntities[s];
  return entity ? entity : s;
}

exports.HTMLEncode = function encode(s) {
  if (s) {
    s = s.replace(specialCharRegExp, specialCharToEntity);
  }
  return s || '';
};
<|endoftext|>


<commit_before>'use strict';

/**
 * Load mongo models into mongoose
 */

require('../models/models.js');

/**
 * Module dependencies
 */

var mongoose = require('mongoose'),
    tvDB = require("node-tvdb"),
    key = require("../../../config/config").TVDBAPIKEY,
    proxy = require("../../../config/config").proxy,
    rootPath = require("../../../config/config").root,
    _ = require("underscore"),
    http = require('http'),
    fs = require('fs'),
    async = require("async"),
    Collection = mongoose.model('Collection');

var tvdb = new tvDB(key);

exports.search = function(req, res) {
    var query = req.query.query;

    if (query) {
        tvdb.getSeriesByName(query, function(err, tvdbres) {
            if (err) {
                console.log(err);
                res.status(500).send(err);
            } else {
                prepareShowsSearchJSON(tvdbres, function(shows) {
                    res.send(shows);
                });
            }
        });
    } else {
        res.status(500).send('No query received from client');
    }
};

exports.thumb = function(req, res) {
    var file = '/var/tmp/' + req.params.seriesid + ".jpg";
    fs.exists(file, function(exists) {
        if (exists) {
            res.sendfile(file);
        } else {
            res.sendfile('/client/img/error.jpg', {
                'root': rootPath
            });
        }
    });
};

function prepareShowsSearchJSON(tvdbdata, allShowsPreparedCallback) {
    /*[ { seriesid: '79349',
language: 'en',
SeriesName: 'Dexter',
AliasNames: 'Dexter: Early Cuts',
banner: 'graphical/79349-g28.jpg',
Overview: 'He\'s smart, he\'s good looking, and he\'s got a great sense of humor. He\'s Dexter Morgan, everyone\'s favorite serial killer. As a Miami forensics expert, he spends his days solving crimes, & nights committing them. But Dexter lives by a strict code of honor that is both his saving grace and lifelong burden. Torn between his deadly compulsion and his desire for true happiness, Dexter is a man in profound conflict with the world and himself.',
FirstAired: '2006-10-01',
Network: 'Showtime',
IMDB_ID: 'tt0773262',
zap2it_id: 'EP00859795',
id: '79349' } ]*/
    var showArray = [];
    if (!_.isArray(tvdbdata)) tvdbdata = [tvdbdata]; //tvdb returns object if only one result

    _.each(tvdbdata, function(show) {
        //I only accept results that are featured on IMDB
        if (_.has(show, 'IMDB_ID')) {
            //TODO: check if most shows have a banner in the getSeries data. otherwise make a more complicated request to getBanners
            var preparedShow = _.pick(show, 'seriesid', 'SeriesName', 'banner', 'Overview', 'FirstAired', 'Network', 'IMDB_ID');
            preparedShow.FirstAired = preparedShow.FirstAired || 'NA';
            preparedShow.Network = preparedShow.Network || 'NA';
            showArray.push(preparedShow);
        }
    });
    async.map(showArray, saveImageFromTVDB, function(err, results) {
        // Give back the inital collect, because we did not change it.
        // we just waited for all images to be saved
        allShowsPreparedCallback(showArray);
    });
}

var saveImageFromTVDB = function(show, imageSavedCallback) {
    var seriesid = show.seriesid,
        imageurl = show.banner;
    var dest = "/var/tmp/" + seriesid + ".jpg";

    //Check if file exists
    fs.exists(dest, function(exists) {
        if (exists) {
            return imageSavedCallback(null, null);
        } else {
            //It does not. Download it
            try {
                var file = fs.createWriteStream(dest);
                var request = http.get("http://thetvdb.com/banners/" + imageurl, function(response) {
                    response.pipe(file);
                    file.on('finish', function() {
                        return imageSavedCallback(null, null);
                    });
                });
            } catch (err) {
                console.log(err);
                return imageSavedCallback(err, null);
            }
        }
    });
};

exports.addseries = function(req, res) {
    var showid = req.body.showid;

    //Get current user collection
    var currentuser_id = req.session.passport.user;
    Collection.findOne({
        userid: currentuser_id
    }, function(err, collection) {
        if (err) {
            console.log(err);
        }

        if (!collection) {
            var collection = new Collection({
                userid: currentuser_id
            });
            collection.save(function() {
                //We cereated a collection
                addShow(showid, collection, function(show) {
                    prepareShowsCollectionJSON([show], function(results) {
                        res.status(201).send(results[0]);
                    });
                });
            });
        } else {
            //We have a collection
            addShow(showid, collection, function(show) {
                prepareShowsCollectionJSON([show], function(results) {
                    res.status(201).send(results[0]);
                });
            });
        }
    });

    function addShow(thisshowid, collection, cb) {
        // check if show already added, use unique in model
        // and catch error, actually client already does it
        collection.shows.push({
            tvdbid: thisshowid
        });
        collection.save(function(err) {
            //TODO: handle error when unique or other
            cb({
                tvdbid: thisshowid
            });
        });
    }
};

exports.getcollection = function(req, res) {
    var currentuser_id = req.session.passport.user;
    Collection.findOne({
        userid: currentuser_id
    }, function(err, collection) {
        if (err) {
            console.log(err);
        }

        if (!collection) {
            //TODO: what to do here?
            res.send('[]');
        } else {
            //We have a collection
            prepareShowsCollectionJSON(collection.shows, function(shows) {
                res.status(200).send(shows);
            });
        }
    });
};

function prepareShowsCollectionJSON(showsInCollection, allShowsPreparedCallback) {
    async.map(showsInCollection, getSeriesFromTVDB, function(err, results) {
        var showArray = [];
        _.each(results, function(show) {
            var preparedShow = compileShowDetails(show);
            showArray.push(preparedShow);
        });

        async.map(showArray, saveImageFromTVDB, function(err, results) {
            allShowsPreparedCallback(showArray); //Give back the inital collect, because we did not change it. we just waited for all images to be saved
        });

    });

}

var getSeriesFromTVDB = function(showInCollection, showFoundCallback) {
    var showid = showInCollection.tvdbid;
    tvdb.getSeriesAllById(showid, function(err, tvdbres) {
        if (err) {
            console.log('ERROR');
            console.log(err);
            return showFoundCallback(null, null);
        } else {
            if (tvdbres) {
                //TODO: Hackedy-hack: keep mongo data
                tvdbres.lastwatched = showInCollection.lastwatched;
                tvdbres.lastdownloaded = showInCollection.lastdownloaded;
                return showFoundCallback(null, tvdbres);
            } else {
                // TODO: never called? since error if none?
                return showFoundCallback(null, null);
            }
        }
    });
};

function compileShowDetails(show) {
    var preparedShow = _.pick(show.Series, 'id', 'SeriesName', 'IMDB_ID', 'banner', 'Status');
    preparedShow.seriesid = preparedShow.id;

    //User settings
    preparedShow.lastwatched = show.lastwatched;
    preparedShow.lastdownloaded = show.lastdownloaded;

    var stringArray = [],
        found = false,
        now = getDate(),
        nextEpisode;

    _.each(show.Episode, function(episode) {
        if (!_.isString(episode.FirstAired)) {
            episode.FirstAired = 'NA'; //Sometimes empty dates are {}
        }
        if (!_.isString(episode.EpisodeName)) {
            episode.EpisodeName = 'NA'; //Sometimes empty names are {}
        }
        stringArray.push("S" + episode.SeasonNumber + "E" + episode.EpisodeNumber + " " + episode.FirstAired + " " + episode.EpisodeName);

        if (episode.FirstAired != 'NA' && episode.FirstAired >= now && !found) {
            nextEpisode = episode.FirstAired + ", S" + episode.SeasonNumber + "E" + episode.EpisodeNumber;
            found = true;
        }

    });
    //TODO:::
    /*var lastEpisode = _.last(show.Episode);
    preparedShow.totalSeasons = lastEpisode.SeasonNumber;
    preparedShow.episodeList = stringArray;
    preparedShow.nextEpisode = nextEpisode;*/

    return preparedShow;
}

function getDate() {
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    month = (month < 10 ? "0" : "") + month;
    var day = date.getDate();
    day = (day < 10 ? "0" : "") + day;
    return year + "-" + month + "-" + day;
}

exports.updateSeries = function(req, res) {
    var currentuser_id = req.session.passport.user,
        showid = req.body.showid,
        lastWatched = req.body.lastwatched,
        lastDownloaded = req.body.lastdownloaded;

    Collection.findOne({
        userid: currentuser_id
    }, function(err, collection) {
        if (err) {
            console.log(err);
        }

        if (!collection) {
            //TODO: what to do here?
            res.send('[]');
        } else {
            //We have a collection
            _.each(collection.shows, function(show) {
                if (show.tvdbid == showid) {
                    //We have a show
                    if (lastWatched) {
                        show.lastwatched = lastWatched;
                    }
                    if (lastDownloaded) {
                        show.lastdownloaded = lastDownloaded;
                    }
                }
            });
            collection.save(function(err) { //TODO: why no need to save show before saving collection?
                res.send('OK');
            });
        }
    });
};

exports.removeSeries = function(req, res) {
    //TODO: a speial embedded doc id exists, use that to remove instead of cycling
    //http://mongoosejs.com/docs/2.8.x/docs/embedded-documents.html
    var currentuser_id = req.session.passport.user,
        showid = req.body.showid;

    Collection.findOne({
        userid: currentuser_id
    }, function(err, collection) {
        if (err) {
            console.log(err);
        }

        if (!collection) {
            //TODO: what to do here?
            res.send('[]');
        } else {
            //We have a collection
            _.each(collection.shows, function(show) {
                if (show.tvdbid == showid) {
                    //We have a show
                    collection.shows[collection.shows.indexOf(show)].remove();
                }
            });
            collection.save(function(err) {
                res.send('OK');
            });
        }
    });
};
<commit_msg>repair series<commit_after>'use strict';

/**
 * Load mongo models into mongoose
 */

require('../models/models.js');

/**
 * Module dependencies
 */

var mongoose = require('mongoose'),
    tvDB = require("node-tvdb"),
    key = require("../../../config/config").TVDBAPIKEY,
    proxy = require("../../../config/config").proxy,
    rootPath = require("../../../config/config").root,
    _ = require("underscore"),
    http = require('http'),
    fs = require('fs'),
    async = require("async"),
    Collection = mongoose.model('Collection');

var tvdb = new tvDB(key);

exports.search = function(req, res) {
    var query = req.query.query;

    if (query) {
        tvdb.getSeriesByName(query, function(err, tvdbres) {
            if (err) {
                console.log(err);
                res.status(500).send(err);
            } else {
                prepareShowsSearchJSON(tvdbres, function(shows) {
                    res.send(shows);
                });
            }
        });
    } else {
        res.status(500).send('No query received from client');
    }
};

exports.thumb = function(req, res) {
    var file = '/var/tmp/' + req.params.seriesid + ".jpg";
    fs.exists(file, function(exists) {
        if (exists) {
            res.sendfile(file);
        } else {
            res.sendfile('/client/img/error.jpg', {
                'root': rootPath
            });
        }
    });
};

function prepareShowsSearchJSON(tvdbdata, allShowsPreparedCallback) {
    var showArray = [];
    if (!_.isArray(tvdbdata)) tvdbdata = [tvdbdata]; //tvdb returns object if only one result

    _.each(tvdbdata, function(show) {
        //I only accept results that are featured on IMDB
        if (_.has(show, 'IMDB_ID')) {
            //TODO: check if most shows have a banner in the getSeries data. otherwise make a more complicated request to getBanners
            var preparedShow = _.pick(show, 'seriesid', 'SeriesName', 'banner', 'Overview', 'FirstAired', 'Network', 'IMDB_ID');
            preparedShow.FirstAired = preparedShow.FirstAired || 'NA';
            preparedShow.Network = preparedShow.Network || 'NA';
            showArray.push(preparedShow);
        }
    });
    async.map(showArray, saveImageFromTVDB, function(err, results) {
        // Give back the inital collect, because we did not change it.
        // we just waited for all images to be saved
        allShowsPreparedCallback(showArray);
    });
}

var saveImageFromTVDB = function(show, imageSavedCallback) {
    var seriesid = show.seriesid,
        imageurl = show.banner;
    var dest = "/var/tmp/" + seriesid + ".jpg";

    //Check if file exists
    fs.exists(dest, function(exists) {
        if (exists) {
            return imageSavedCallback(null, null);
        } else {
            //It does not. Download it
            try {
                var file = fs.createWriteStream(dest);
                var request = http.get("http://thetvdb.com/banners/" + imageurl, function(response) {
                    response.pipe(file);
                    file.on('finish', function() {
                        return imageSavedCallback(null, null);
                    });
                });
            } catch (err) {
                console.log(err);
                return imageSavedCallback(err, null);
            }
        }
    });
};

exports.addseries = function(req, res) {
    var showid = req.body.showid;

    //Get current user collection
    var currentuser_id = req.session.passport.user;
    Collection.findOne({
        userid: currentuser_id
    }, function(err, collection) {
        if (err) {
            console.log(err);
        }

        if (!collection) {
            var collection = new Collection({
                userid: currentuser_id
            });
            collection.save(function() {
                //We cereated a collection
                addShow(showid, collection, function(show) {
                    prepareShowsCollectionJSON([show], function(results) {
                        res.status(201).send(results[0]);
                    });
                });
            });
        } else {
            //We have a collection
            addShow(showid, collection, function(show) {
                prepareShowsCollectionJSON([show], function(results) {
                    res.status(201).send(results[0]);
                });
            });
        }
    });

    function addShow(thisshowid, collection, cb) {
        // check if show already added, use unique in model
        // and catch error, actually client already does it
        collection.shows.push({
            tvdbid: thisshowid
        });
        collection.save(function(err) {
            //TODO: handle error when unique or other
            cb({
                tvdbid: thisshowid
            });
        });
    }
};

exports.getcollection = function(req, res) {
    var currentuser_id = req.session.passport.user;
    Collection.findOne({
        userid: currentuser_id
    }, function(err, collection) {
        if (err) {
            console.log(err);
        }

        if (!collection) {
            //TODO: what to do here?
            res.send('[]');
        } else {
            //We have a collection
            prepareShowsCollectionJSON(collection.shows, function(shows) {
                res.status(200).send(shows);
            });
        }
    });
};

function prepareShowsCollectionJSON(showsInCollection, allShowsPreparedCallback) {
    async.map(showsInCollection, getSeriesFromTVDB, function(err, results) {
        var showArray = [];
        _.each(results, function(show) {
            var preparedShow = compileShowDetails(show);
            showArray.push(preparedShow);
        });

        async.map(showArray, saveImageFromTVDB, function(err, results) {
            allShowsPreparedCallback(showArray); //Give back the inital collect, because we did not change it. we just waited for all images to be saved
        });

    });

}

var getSeriesFromTVDB = function(showInCollection, showFoundCallback) {
    var showid = showInCollection.tvdbid;
    tvdb.getSeriesAllById(showid, function(err, tvdbres) {
        if (err) {
            console.log('ERROR');
            console.log(err);
            return showFoundCallback(null, null);
        } else {
            if (tvdbres) {
                //TODO: Hackedy-hack: keep mongo data
                tvdbres.lastwatched = showInCollection.lastwatched;
                tvdbres.lastdownloaded = showInCollection.lastdownloaded;
                return showFoundCallback(null, tvdbres);
            } else {
                // TODO: never called? since error if none?
                return showFoundCallback(null, null);
            }
        }
    });
};

function compileShowDetails(show) {
    var preparedShow = _.pick(show, 'id', 'SeriesName', 'IMDB_ID', 'banner', 'Status');
    preparedShow.seriesid = preparedShow.id;

    //User settings
    preparedShow.lastwatched = show.lastwatched;
    preparedShow.lastdownloaded = show.lastdownloaded;

    var stringArray = [],
        found = false,
        now = getDate(),
        nextEpisode;

    _.each(show.Episodes, function(episode) {
        if (!_.isString(episode.FirstAired)) {
            episode.FirstAired = 'NA'; //Sometimes empty dates are {}
        }
        if (!_.isString(episode.EpisodeName)) {
            episode.EpisodeName = 'NA'; //Sometimes empty names are {}
        }
        stringArray.push("S" + episode.SeasonNumber + "E" + episode.EpisodeNumber + " " + episode.FirstAired + " " + episode.EpisodeName);

        if (episode.FirstAired != 'NA' && episode.FirstAired >= now && !found) {
            nextEpisode = episode.FirstAired + ", S" + episode.SeasonNumber + "E" + episode.EpisodeNumber;
            found = true;
        }

    });
    //TODO:::
    var lastEpisode = _.last(show.Episodes);
    preparedShow.totalSeasons = lastEpisode.SeasonNumber;
    preparedShow.episodeList = stringArray;
    preparedShow.nextEpisode = nextEpisode;

    return preparedShow;
}

function getDate() {
    var date = new Date();
    var year = date.getFullYear();
    var month = date.getMonth() + 1;
    month = (month < 10 ? "0" : "") + month;
    var day = date.getDate();
    day = (day < 10 ? "0" : "") + day;
    return year + "-" + month + "-" + day;
}

exports.updateSeries = function(req, res) {
    var currentuser_id = req.session.passport.user,
        showid = req.body.showid,
        lastWatched = req.body.lastwatched,
        lastDownloaded = req.body.lastdownloaded;

    Collection.findOne({
        userid: currentuser_id
    }, function(err, collection) {
        if (err) {
            console.log(err);
        }

        if (!collection) {
            //TODO: what to do here?
            res.send('[]');
        } else {
            //We have a collection
            _.each(collection.shows, function(show) {
                if (show.tvdbid == showid) {
                    //We have a show
                    if (lastWatched) {
                        show.lastwatched = lastWatched;
                    }
                    if (lastDownloaded) {
                        show.lastdownloaded = lastDownloaded;
                    }
                }
            });
            collection.save(function(err) { //TODO: why no need to save show before saving collection?
                res.send('OK');
            });
        }
    });
};

exports.removeSeries = function(req, res) {
    //TODO: a speial embedded doc id exists, use that to remove instead of cycling
    //http://mongoosejs.com/docs/2.8.x/docs/embedded-documents.html

    //TODO, save shows individually, no sub schemas
    var currentuser_id = req.session.passport.user,
        showid = req.body.showid;

    Collection.findOne({
        userid: currentuser_id
    }, function(err, collection) {
        if (err) {
            console.log(err);
        }

        if (!collection) {
            //TODO: what to do here?
            res.send('[]');
        } else {
            //We have a collection
            _.each(collection.shows, function(show) {
                if (show && show.tvdbid == showid) {
                    //We have a show
                    collection.shows[collection.shows.indexOf(show)].remove();
                }
            });
            collection.save(function(err) {
                res.send('OK');
            });
        }
    });
};
<|endoftext|>


<commit_before>var TradePartnerTable = function () {
    this.tableId = '#trade-partner-table';

    this.table = $(this.tableId).DataTable({
        "dom": '<"rankings-control">frtip',
        "sAjaxSource": "/secex/all-0/all/all/show.5/?order=export_val.desc",
        "sAjaxDataProp": "data",
        "order": [],
        "aoColumns": [
            null,
            { "bVisible": false },
            null,
            null,
            { "bVisible": false },
            { "bVisible": false },
            { "bVisible": false },
            { "bVisible": false },
            { "bVisible": false },
            { "bVisible": false },
            { "bVisible": false },
            { "bVisible": false },
            null,
            null,
            null,
            null
        ],
        "deferRender": true,
        "scrollY": 500,
        "scrollCollapse": true,
        "scroller": true,
        initComplete: function () {
            var select = $("<select></select>").attr("id", 'year-selector').addClass("year-selector form-control"),
                buttons = $("

").addClass("btn-group");

            select.append( $('<option value="">Ano</option>') );
            buttons.append($("<button>Regiões</button>").attr("id", 'trade-partner-continents').addClass("btn btn-white"));
            buttons.append($("<button>Estados</button>").attr("id", 'trade-partner-countries').addClass("btn btn-white"));

            $('.rankings-content .rankings-control').append(buttons);
            $('.rankings-content .rankings-control').append(select);

            tradePartnerTable.table
                .column( 0 )
                .cache( 'search' )
                .sort()
                .unique()
                .each( function ( d ) {
                    select.append( $('<option value="'+d+'">'+d+'</option>') );
                } );

            select.on( 'change', function () {
               tradePartnerTable.table
                    .column( 0 )
                    .search( $(this).val() )
                    .draw();
            });

            $('#trade-partner-table_filter input').removeClass('input-sm');
            $('#trade-partner-table_filter').addClass('pull-right');

            $('#trade-partner-countries').click(function() {
                tradePartnerCountries();
            });

            $('#trade-partner-continents').click(function() {
                tradePartnerContinents();
            });
        }
    });
};

var tradePartnerTable = new TradePartnerTable();

var tradePartnerCountries = function() {
    tradePartnerTable.table.ajax.url("/secex/all-0/all/all/show.5/?order=eci.desc").load();
};

var tradePartnerContinents = function() {
    tradePartnerTable.table.ajax.url("/secex/all-0/all/all/show.2/?order=export_val.desc").load();
};
<commit_msg>Refactores js to reorder and get columns names.<commit_after>var TradePartnerTable = function () {
    this.tableId = '#trade-partner-table';

    this.table = $(this.tableId).DataTable({
        "dom": '<"rankings-control">frtip',
        "sAjaxSource": "/secex/all-0/all/all/show.5/?order=export_val.desc",
        "sAjaxDataProp": "data",
        "order": [],
        "columns": [
            {data: 0},
            {data: 15},
            null,
            {data: 3},
            {data: 4},
            {data: 14},
            {data: 12},
            {data: 13}
        ],
        "columnDefs": [
            {
                "targets": 2,
                "render": function (data, type, row, meta){
                    return dataviva.wld[row[15]].name
                }
            },
        ],
        "deferRender": true,
        "scrollY": 500,
        "scrollCollapse": true,
        "scroller": true,
        initComplete: function () {
            var select = $("<select></select>").attr("id", 'year-selector').addClass("year-selector form-control"),
                buttons = $("

").addClass("btn-group");

            var wld_2 = dataviva.dictionary['wld_2'],
                wld_5 = dataviva.dictionary['wld_5'],
                year = dataviva.dictionary['year'];

            select.append($('<option value="">'+year+'</option>'));
            buttons.append($("<button>"+wld_2+"</button>").attr("id", 'trade-partner-continents').addClass("btn btn-white"));
            buttons.append($("<button>"+wld_5+"</button>").attr("id", 'trade-partner-countries').addClass("btn btn-white"));

            $('.rankings-content .rankings-control').append(buttons);
            $('.rankings-content .rankings-control').append(select);

            tradePartnerTable.table
                .column( 0 )
                .cache( 'search' )
                .sort()
                .unique()
                .each( function ( d ) {
                    select.append( $('<option value="'+d+'">'+d+'</option>') );
                } );

            select.on( 'change', function () {
               tradePartnerTable.table
                    .column( 0 )
                    .search( $(this).val() )
                    .draw();
            });

            $('#trade-partner-table_filter input').removeClass('input-sm');
            $('#trade-partner-table_filter').addClass('pull-right');

            $('#trade-partner-continents').click(function() {
                tradePartnerTable.table.ajax.url("/secex/all-0/all/all/show.2/?order=export_val.desc").load();
            });

            $('#trade-partner-countries').click(function() {
                tradePartnerTable.table.ajax.url("/secex/all-0/all/all/show.5/?order=eci.desc").load();
            });
        }
    });
};

$(document).ready(function() {
    dataviva.requireAttrs(['wld'], function() {
        window.tradePartnerTable = new TradePartnerTable();
    });
});

<|endoftext|>


<commit_before>//
//   Copyright 2009-2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

/* globals URI, moment, emojify */

import Ember from 'ember';

export default Ember.Object.extend({
    store: null,

    gid: 0,
    body: null,
    cat: null,
    ts: null,
    userId: null,
    window: null,
    hideImages: false,

    mentionedRegEx: Ember.computed.alias('window.userNickHighlightRegex'),

    ircMotd: false,

    nick: Ember.computed('userId', 'window', function() {
        return this.get('store.users').getNick(this.get('userId'), this.get('window.network'));
    }),

    decoratedCat: Ember.computed('cat', 'nick', 'body', 'mentionedRegEx', function() {
        let cat = this.get('cat');
        let body = this.get('body');
        let userId = this.get('userId');
        let mentionedRegEx = this.get('mentionedRegEx');

        // TBD: Network === flowdock check is missing
        if (this.get('body').indexOf('Show in Flowdock:') > -1) {
            return 'flowdock-ignore';
        } else if (mentionedRegEx && mentionedRegEx.test(body) && cat === 'msg') {
            return 'mention';
        } else if (userId == this.get('store.userId')) {
            return 'mymsg';
        }

        return this.get('nick') === 'Flowdock' ? 'flowdock' : cat;
    }),

    decoratedTs: Ember.computed('ts', function() {
        return moment.unix(this.get('ts')).format('HH:mm');
    }),

    channelAction: Ember.computed('cat', function() {
        let category = this.get('cat');
        let nick = this.get('nick');
        let groupName = this.get('window.name');
        let body = this.get('body');

        switch (category) {
            case 'join':
                return `${nick} has joined ${groupName}.`;
            case 'part':
                return `${nick} has left ${groupName}. ${body}`;
            case 'quit':
                return `${nick} has quit irc. Reason: ${body}`;
            case 'kick':
                return `${nick} was kicked from ${groupName}. Reason: ${body}`;
        }
    }),

    bodyParts: Ember.computed('body', function() {
        let result = Ember.A([]);
        let pos = 0;
        let imgSuffixes = [ 'png', 'jpg', 'jpeg', 'gif' ];
        let body = this.get('body');
        let network = this.get('network');
        let cat = this.get('cat');

        // TODO: Security review the whole algorithm
        body = this._escapeHTMLStartTag(body);

        body = URI.withinString(body, (url, start, end, source) => {
            let urlObj = new URI(url);
            let visibleLink;
            let type = 'generic';
            let domain = urlObj.domain();

            if (imgSuffixes.indexOf(urlObj.suffix().toLowerCase()) !== -1) {
                visibleLink = urlObj.filename();
                type = 'image';
            } else if ((domain === 'youtube.com' && urlObj.search(true).v) ||
                domain === 'youtu.be') {
                visibleLink = urlObj.toString();
                type = 'youtubelink';
            } else {
                visibleLink = urlObj.readable();
            }

            if (urlObj.protocol() === '') {
                urlObj.protocol('http');
            }

            if (type === 'image' || type === 'youtubelink') {
                this._pushPart(result, {
                    type: type,
                    url: urlObj.toString()
                });
            }

            return this._renderLink(urlObj.normalize().toString(), visibleLink);
        });

        body = marked(body);

        this._pushPart(result, {
            type: 'text',
            text: this._parseCustomFormatting(body, network, cat)
        });

        return result;
    }),

    hasMedia: Ember.computed('bodyParts', function() {
        return !this.get('bodyParts').isEvery('type', 'text');
    }),

    hasImages: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('type', 'image');
    }),

    hasYoutubeVideo: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('type', 'youtubelink');
    }),

    videoId: Ember.computed('bodyParts', function() {
        let video = this.get('bodyParts').findBy('type', 'youtubelink');

        if (video) {
            let urlObj = new URI(video.url);
            // Format is https://www.youtube.com/watch?v=0P7O69GuCII or https://youtu.be/0P7O69GuCII
            return urlObj.search(true).v || urlObj.pathname().substring(1).split('/')[0];
        } else {
            return null;
        }
    }),

    images: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').filterBy('type', 'image');
    }),

    _pushPart(array, params) {
        array.pushObject(Ember.Object.create(params));
    },

    _parseCustomFormatting(text, network, cat) {
        if (network === 'Flowdock') {
            text = text.replace(/^\[(.*?)\] << (.*)/,
                (match, p1, p2) => `[${p1.substring(0, 9)}] ${p2}`);
        }

        text = text.replace(/  /g, '  '); // Preserve whitespace.

        // Find @ character 1) after space, 2) in the beginning of string, 3) after HTML tag (>)
        text = text.replace(/(^| |>)(@\S+)(?=( |$))/g,
            (match, p1, p2) => this._renderMention(p1, p2));

        text = text.replace(/:(.+?):/g, (match, p1) => {
            if (!emojify.emojiNames.includes(p1)) {
                return match;
            } else {
                return this._renderEmoji(match, `/app/assets/images/emoji/${p1}.png`);
            }
        });

        let keywords = text.match(/<(p|br)>/g);

        // Assumes that marked is used which inserts at least one
,
, or

        let multiLine = !keywords || keywords.length > 1;

        if (!multiLine) {
            text = text.replace(/(\s*
|<\/p>\s*)/g, '');
        }

        return text;
    },

    _renderLink(url, label) {
        return `<a href="${url}" target="_blank">${label}</a>`;
    },

    _renderEmoji(name, src) {
        return `<img align="absmiddle" alt="${name}" class="emoji" src="${src}"/>`;
    },

    _renderMention(beforeCharacter, nick) {
        return `${beforeCharacter}<span class="nick-mention">${nick}</span>`;
    },

    _escapeHTMLStartTag(text) {
        return text.replace(/<]/g, '<');
    }
});
<commit_msg>Refactor message bodyParts()<commit_after>//
//   Copyright 2009-2014 Ilkka Oksanen <iao@iki.fi>
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing,
//   software distributed under the License is distributed on an "AS
//   IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
//   express or implied.  See the License for the specific language
//   governing permissions and limitations under the License.
//

'use strict';

/* globals URI, moment, emojify */

import Ember from 'ember';

export default Ember.Object.extend({
    store: null,

    gid: 0,
    body: null,
    cat: null,
    ts: null,
    userId: null,
    window: null,
    hideImages: false,

    mentionedRegEx: Ember.computed.alias('window.userNickHighlightRegex'),

    ircMotd: false,

    nick: Ember.computed('userId', 'window', function() {
        return this.get('store.users').getNick(this.get('userId'), this.get('window.network'));
    }),

    decoratedCat: Ember.computed('cat', 'nick', 'body', 'mentionedRegEx', function() {
        let cat = this.get('cat');
        let body = this.get('body');
        let userId = this.get('userId');
        let mentionedRegEx = this.get('mentionedRegEx');

        // TBD: Network === flowdock check is missing
        if (this.get('body').indexOf('Show in Flowdock:') > -1) {
            return 'flowdock-ignore';
        } else if (mentionedRegEx && mentionedRegEx.test(body) && cat === 'msg') {
            return 'mention';
        } else if (userId == this.get('store.userId')) {
            return 'mymsg';
        }

        return this.get('nick') === 'Flowdock' ? 'flowdock' : cat;
    }),

    decoratedTs: Ember.computed('ts', function() {
        return moment.unix(this.get('ts')).format('HH:mm');
    }),

    channelAction: Ember.computed('cat', function() {
        let category = this.get('cat');
        let nick = this.get('nick');
        let groupName = this.get('window.name');
        let body = this.get('body');

        switch (category) {
            case 'join':
                return `${nick} has joined ${groupName}.`;
            case 'part':
                return `${nick} has left ${groupName}. ${body}`;
            case 'quit':
                return `${nick} has quit irc. Reason: ${body}`;
            case 'kick':
                return `${nick} was kicked from ${groupName}. Reason: ${body}`;
        }
    }),

    bodyParts: Ember.computed('body', function() {
        let body = this.get('body');
        let cat = this.get('cat');

        let parts = [];

        body = this._escapeHTMLStartTag(body);

        if (cat === 'msg') {
            ({ body, parts } = this._parseLinks(body));

            body = marked(body);
            body = this._parseCustomFormatting(body, cat);
        }

        body = this._parseWhiteSpace(body);

        parts.push({ type: 'text', text: body });

        return Ember.A(parts.map(item => Ember.Object.create(item)));
    }),

    hasMedia: Ember.computed('bodyParts', function() {
        return !this.get('bodyParts').isEvery('type', 'text');
    }),

    hasImages: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('type', 'image');
    }),

    hasYoutubeVideo: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').isAny('type', 'youtubelink');
    }),

    videoId: Ember.computed('bodyParts', function() {
        let video = this.get('bodyParts').findBy('type', 'youtubelink');

        if (video) {
            let urlObj = new URI(video.url);
            // Format is https://www.youtube.com/watch?v=0P7O69GuCII or https://youtu.be/0P7O69GuCII
            return urlObj.search(true).v || urlObj.pathname().substring(1).split('/')[0];
        } else {
            return null;
        }
    }),

    images: Ember.computed('bodyParts', function() {
        return this.get('bodyParts').filterBy('type', 'image');
    }),

    _parseLinks(text) {
        let imgSuffixes = [ 'png', 'jpg', 'jpeg', 'gif' ];
        let media = [];

        text = URI.withinString(text, (url, start, end, source) => {
            let urlObj = new URI(url);
            let visibleLink;
            let type = 'generic';
            let domain = urlObj.domain();

            if (imgSuffixes.indexOf(urlObj.suffix().toLowerCase()) !== -1) {
                visibleLink = urlObj.filename();
                type = 'image';
            } else if ((domain === 'youtube.com' && urlObj.search(true).v) ||
                domain === 'youtu.be') {
                visibleLink = urlObj.toString();
                type = 'youtubelink';
            } else {
                visibleLink = urlObj.readable();
            }

            if (urlObj.protocol() === '') {
                urlObj.protocol('http');
            }

            if (type === 'image' || type === 'youtubelink') {
                media.push({
                    type: type,
                    url: urlObj.toString()
                });
            }

            return this._renderLink(urlObj.normalize().toString(), visibleLink);
        });

        return { body: text, parts: media };
    },

    _parseWhiteSpace(text) {
        return text.replace(/  /g, '  '); // Preserve whitespace.
    },

    _parseCustomFormatting(text, cat) {
        let network = this.get('network');

        if (network === 'Flowdock') {
            text = text.replace(/^\[(.*?)\] << (.*)/,
                (match, p1, p2) => `[${p1.substring(0, 9)}] ${p2}`);
        }

        // Find @ character 1) after space, 2) in the beginning of string, 3) after HTML tag (>)
        text = text.replace(/(^| |>)(@\S+)(?=( |$))/g,
            (match, p1, p2) => this._renderMention(p1, p2));

        text = text.replace(/:(.+?):/g, (match, p1) => {
            if (!emojify.emojiNames.includes(p1)) {
                return match;
            } else {
                return this._renderEmoji(match, `/app/assets/images/emoji/${p1}.png`);
            }
        });

        let keywords = text.match(/<(p|br)>/g);

        // Assumes that marked is used which inserts at least one
,
, or

        let multiLine = !keywords || keywords.length > 1;

        if (!multiLine) {
            text = text.replace(/(\s*
|<\/p>\s*)/g, '');
        }

        return text;
    },

    _renderLink(url, label) {
        return `<a href="${url}" target="_blank">${label}</a>`;
    },

    _renderEmoji(name, src) {
        return `<img align="absmiddle" alt="${name}" class="emoji" src="${src}"/>`;
    },

    _renderMention(beforeCharacter, nick) {
        return `${beforeCharacter}<span class="nick-mention">${nick}</span>`;
    },

    _escapeHTMLStartTag(text) {
        return text.replace(/<]/g, '<');
    }
});
<|endoftext|>