6

テキスト入力とボタンのあるページがあります。YouTube ビデオへのリンクをテキスト フィールドに挿入してボタンを押すと、ビデオがローカル フォルダにダウンロードされます。

問題:ダウンロードしたビデオのローカル コピーへのリンクをクライアントに送信するにはどうすればよいですか?

より一般的な質問:サーバーからクライアントに変数を送信するにはどうすればよいですか (この変数は一時的なものであり、どこにも保存されません)。

私が今持っているコード:

クライアントコード

if (Meteor.isClient) {
    Path = new Meteor.Collection("path");
    Meteor.subscribe("path");

    Template.hello.events(
        {
            'submit .form' : function() {
                var link = document.getElementById("youtube-url").value;
                Meteor.call('download', link);
                event.preventDefault();
            }
        }
    );
}

サーバー コード (「コレクション」部分が機能していません)

if (Meteor.isServer) {
    Meteor.startup(function () {

        Meteor.methods({
            download: function (link) {
                var youtubedl = Npm.require('youtube-dl');
                var Fiber = Npm.require("fibers");
                var dl = youtubedl.download(link, './videos');

                // called when youtube-dl finishes
                dl.on('end', function(data) {
                  console.log('\nDownload finished!');
                  Fiber(function() { 
                      Path = new Meteor.Collection("path");
                      Path.insert({path: './videos/' + data.filename});
                  })
                });              
            }
        });
    });
}

ありがとう!

4

4 に答える 4

3

youtube-dl質問に対する答えは、(a) Meteor のメソッド内で非同期関数を処理することと、(b)パッケージを使用することの 2 つの部分に分かれます。

Meteor のメソッド内の非同期関数

Meteor のメソッド内で非同期関数を操作するには、基本的に 2 つ以上の方法がありfutureますwrapAsync。Meteor のソースを調べると、https://github.com/meteor/meteor/blob/master/packages/meteor/helpers.js#L90wrapAsyncを使用していることがわかります。直接使用することもできますが、お勧めしませんfuturefibers

以下は、それらの使用方法の一般的な例です。

'use strict';

if (Meteor.isClient) {

   Template.methods.events({

    'click #btnAsync' : function() {
      console.log('Meteor.call(asyncMethod)');
      Meteor.call('asyncMethod', 1000, function(error, result) {
        if (error) {
          console.log('Meteor.call(asyncMethod): error:', error);
        } else {
          console.log('Meteor.call(asyncMethod): result:', result);
        }
      });
    },

    'click #btnFuture' : function() {
      console.log('Meteor.call(futureMethod)');
      Meteor.call('futureMethod', 1000, function(error, result) {
        if (error) {
          console.log('Meteor.call(futureMethod): error:', error);
        } else {
          console.log('Meteor.call(futureMethod): result:', result);
        }
      });
    },

    'click #btnFiber' : function() {
      console.log('Meteor.call(fiberMethod)');
      Meteor.call('fiberMethod', 1000, function(error, result) {
        if (error) {
          console.log('Meteor.call(fiberMethod): error:', error);
        } else {
          console.log('Meteor.call(fiberMethod): result:', result);
        }
      });
    }

  });

}

if (Meteor.isServer) {

  var demoFunction = function(duration, callback) {
    console.log('asyncDemoFunction: enter.');
    setTimeout(function() {
      console.log('asyncDemoFunction: finish.');
      callback(null, { result: 'this is result' });
    }, duration);
    console.log('asyncDemoFunction: exit.');
  };

  var asyncDemoFunction = Meteor.wrapAsync(demoFunction);

  var futureDemoFunction = function(duration) {
    var Future = Npm.require('fibers/future');
    var future = new Future();

    demoFunction(duration, function(error, result) {
      if (error) {
        future.throw(error);
      } else {
        future.return(result);
      }
    });
    return future.wait();
  };

  var fiberDemoFunction = function(duration) {
    var Fiber = Npm.require('fibers');
    var fiber = Fiber.current;

    demoFunction(duration, function(error, result) {
      if (error) {
        fiber.throwInto(new Meteor.Error(error));
      } else {
        fiber.run(result);
      }
    });

    return Fiber.yield();
  };

  Meteor.methods({

    asyncMethod: function (duration) {
      return asyncDemoFunction(duration);
    },
    futureMethod: function (duration) {
      return futureDemoFunction(duration);
    },
    fiberMethod: function (duration) {
      return fiberDemoFunction(duration);
    }

  });
}

より複雑なケースを調べたりMeteor.bindEnvironment()、探したりすることもできます。future.resolver()

Christian Fritzは正しい使用パターンを提供wrapAsyncしましたが、最初の質問が行われた時点から 2 年間で、youtube-dlパッケージの API が変更されました。

youtube-dlパッケージの使用

API の変更により、彼のコードを実行すると、サーバーはコンソールに表示される例外をスローします。

Exception while invoking method 'download' TypeError: Object function (videoUrl, args, options) {
...
} has no method 'download'

undefinedMeteor はクライアントの値に戻ります。

here is the path: undefined

以下のコードは機能しており (downloadDir をパスに置き換えるだけです)、ファイル名をクライアントに返します。

here is the path: test.mp4


ファイルindex.html

<head>
  <title>meteor-methods</title>
</head>
<body>
  {{> hello}}
</body>

<template name="hello">
  <form>
    <input type="text" id="youtube-url" value="https://www.youtube.com/watch?v=alIq_wG9FNk">
    <input type="button" id="downloadBtn" value="Download by click">
    <input type="submit" value="Download by submit">
  </form>
</template>

ファイルindex.js:

'use strict';

if (Meteor.isClient) {
  //Path = new Meteor.Collection("path");
  //Meteor.subscribe("path");

  Template.hello.events(
    {
      'submit .form, click #downloadBtn' : function() {
        var link = document.getElementById("youtube-url").value;

        //Meteor.call('download', link);
        Meteor.call('download', link, function(err, path) {
          if (err) { 
            console.log('Error:', err); 
          } else {
            console.log("here is the path:", path);
          }
        });

        event.preventDefault();
      }
    }
  );
}

if (Meteor.isServer) {

  var fs = Npm.require('fs');
  var youtubedl = Npm.require('youtube-dl');

  var downloadSync = Meteor.wrapAsync(function(link, callback) {
    var fname = 'test.mp4';
    // by default it will be downloaded to 
    // <project-root>/.meteor/local/build/programs/server/ 
    var downloadDir = './'; 
    console.log('\nStarting download...');

    // var dl = youtubedl.download(link, './videos');
    var dl = youtubedl(link, [], []);
    dl.on('info', function(info) {
      console.log('\nDownload started: ' + info._filename);
    });
    // dl.on('end', function(data) {
    dl.on('end', function() {
      console.log('\nDownload finished!');
      //callback(null, './videos/' + data.filename);
      callback(null, fname);
    });
    dl.on('error', function(error) {
      console.log('\nDownload error:', error);
      callback(new Meteor.Error(error.message) );
    });
    dl.pipe(fs.createWriteStream(downloadDir + fname));
  });

  Meteor.methods({
    download: function (link) {
      return downloadSync(link);
    }
  });

}

現在の API では、ファイルを保存するときに YouTube のファイル名を取得できません。YouTube のファイル名 (最初の質問で提供されている) でファイルを保存する場合は、パッケージgetInfo()のメソッドを使用する必要があります。youtube-dl

于 2015-12-26T21:18:57.720 に答える
2

この小さなパッケージを使用できます: https://atmosphere.meteor.com/package/client-call。他の方法と同じように、サーバーからクライアント側のメソッドを呼び出すことができますMeteor.methods

于 2013-08-02T10:52:50.950 に答える
0

メソッド呼び出しから必要なパスを返すだけであれば、はるかに簡単になると思います。これを達成するために必要なことは、YouTube のダウンロードを同期させることだけです。これは流星のようなやり方です。

これはうまくいくはずです:

if (Meteor.isServer) {
    var youtubedl = Npm.require('youtube-dl');
    var sync = Meteor.wrapAsync(function(url, callback) {
        var dl = youtubedl.download(link, './videos');
        dl.on('end', function(data) {
            console.log('\nDownload finished!');
            callback(null, './videos/' + data.filename);
        });
    });

    Meteor.methods({
        download: function (link) {
            return sync(link);
        }
    });
}

次に、クライアントで次を使用します。

Meteor.call('download', link, function(err, path) {
    console.log("here is the path:", path);
});
于 2015-12-21T17:28:32.623 に答える
0

この回答で説明されているように、メソッド定義で async Future を使用する必要があります。次に、非同期ダウンロード操作が完了した後にのみ、クライアントへのコールバックで待機できます

于 2015-12-21T20:45:23.727 に答える