Skip to content

Commit

Permalink
feat: add YoutubeParsingHelper for visitor data generation and update…
Browse files Browse the repository at this point in the history
… client handling

Don't ask what I'm doing.
  • Loading branch information
gokadzev committed Jan 28, 2025
1 parent 22e9ad3 commit 6d14930
Show file tree
Hide file tree
Showing 2 changed files with 109 additions and 6 deletions.
9 changes: 3 additions & 6 deletions lib/API/musify.dart
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import 'package:musify/services/lyrics_manager.dart';
import 'package:musify/services/settings_manager.dart';
import 'package:musify/utilities/flutter_toast.dart';
import 'package:musify/utilities/formatter.dart';
import 'package:musify/utilities/parsing_helper.dart';
import 'package:path_provider/path_provider.dart';
import 'package:youtube_explode_dart/youtube_explode_dart.dart';

Expand Down Expand Up @@ -63,11 +64,7 @@ Map activePlaylist = {
'list': [],
};

List<YoutubeApiClient> userChosenClients = [
YoutubeApiClient.tv,
YoutubeApiClient.androidVr,
YoutubeApiClient.safari,
];
List<YoutubeApiClient> userChosenClients = [];

dynamic nextRecommendedSong;

Expand Down Expand Up @@ -635,7 +632,7 @@ Future<AudioOnlyStreamInfo> getSongManifest(String songId) async {
try {
final manifest = await _yt.videos.streams.getManifest(
songId,
ytClients: userChosenClients,
ytClients: userChosenClients.isEmpty ? [iosNew] : userChosenClients,
);
final audioStream = manifest.audioOnly.withHighestBitrate();
return audioStream;
Expand Down
106 changes: 106 additions & 0 deletions lib/utilities/parsing_helper.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import 'dart:convert';
import 'dart:math';

import 'package:youtube_explode_dart/youtube_explode_dart.dart';

var _visitorData = YoutubeParsingHelper.getRandomVisitorData();

class YoutubeParsingHelper {
static const String _contentPlaybackNonceAlphabet =
'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_';

static String getRandomVisitorData() {
final random = Random();
final pbE2 = ProtoBuilder()
..string(2, '')
..varint(4, random.nextInt(255) + 1);

final pbE = ProtoBuilder()
..string(1, 'US')
..bytes(2, pbE2.toBytes());

final pb = ProtoBuilder()
..string(
1,
_generateRandomStringFromAlphabet(
_contentPlaybackNonceAlphabet,
11,
random,
),
)
..varint(
5,
DateTime.now().millisecondsSinceEpoch ~/ 1000 - random.nextInt(600000),
)
..bytes(6, pbE.toBytes());
return pb.toUrlencodedBase64();
}

static String _generateRandomStringFromAlphabet(
String alphabet,
int length,
Random random,
) {
final sb = StringBuffer();
for (var i = 0; i < length; i++) {
sb.write(alphabet[random.nextInt(alphabet.length)]);
}
return sb.toString();
}
}

class ProtoBuilder {
final Map<int, dynamic> _fields = {};

void string(int fieldNumber, String value) {
_fields[fieldNumber] = value;
}

void varint(int fieldNumber, int value) {
_fields[fieldNumber] = value;
}

void bytes(int fieldNumber, List<int> value) {
_fields[fieldNumber] = value;
}

List<int> toBytes() {
final bytes = <int>[];
_fields.forEach((key, value) {
if (value is String) {
bytes.addAll(utf8.encode(value));
} else if (value is int) {
bytes.add(value);
} else if (value is List<int>) {
bytes.addAll(value);
}
});
return bytes;
}

String toUrlencodedBase64() {
final bytes = toBytes();
return base64Url.encode(bytes);
}
}

var iosNew = YoutubeApiClient(
{
'context': {
'client': {
'clientName': 'IOS',
'clientVersion': '19.45.4',
'deviceMake': 'Apple',
'deviceModel': 'iPhone16,2',
'hl': 'en',
'platform': 'MOBILE',
'osName': 'IOS',
'osVersion': '18.1.0.22B83',
'visitorData': _visitorData,
},
'gl': 'US',
'utcOffsetMinutes': 0,
},
},
'https://www.youtube.com/youtubei/v1/player?key=AIzaSyB-63vPrdThhKuerbB2N_l7Kwwcxj6yUAc&prettyPrint=false',
);

0 comments on commit 6d14930

Please sign in to comment.